Skip to content

Avoid int literals that overflow a 31-bit target - #31

Open
samoht wants to merge 1 commit into
mirage:masterfrom
samoht:fix-wasm-integer-overflow
Open

Avoid int literals that overflow a 31-bit target#31
samoht wants to merge 1 commit into
mirage:masterfrom
samoht:fix-wasm-integer-overflow

Conversation

@samoht

@samoht samoht commented Jul 30, 2026

Copy link
Copy Markdown
Member

A wasm_of_ocaml build that depends on optint gets three integer-overflow warnings:

Warning [integer-overflow]: integer 0x7fffffff (2147483647) truncated to 0xffffffff (-1); the generated code might be incorrect.
Warning [integer-overflow]: integer 0x40000000 (1073741824) truncated to 0xc0000000 (-1073741824); the generated code might be incorrect.

None of them is a bug, but a consumer cannot silence them either: they are emitted when optint's own .wasma is built, so flags on the consuming executable never reach them.

In optint_emul.ml and int63_emul.ml the warning is a false positive. On a 31-bit int the sign bit is 0x40000000, so the literal truncates to exactly the value without_bit_sign masks off, on the one backend that selects those modules. Building it from an int32/int64 literal keeps that value and drops the warning.

optint_native.ml is a different case: int32_max really does wrap there. But the module is reached only when Sys.word_size = 64, and its neighbours wrap silently already (uint32_max is -1 on a 31-bit int; 1 lsl 31 and the lsl 32 in of_int32 are unspecified). So instead of converting a literal that cannot fit, this PR derives int32_max from uint32_max and records the 64-bit assumption in a comment.

To check the values are unchanged I ran a driver over of_int, of_unsigned_int, to_int32 and the Int63 equivalents at the boundaries (max_int, min_int, +/-2^30, small negatives) on 63-bit native, 32-bit js_of_ocaml and 31-bit wasm_of_ocaml. Output is identical before and after, dune runtest passes, and the three warnings are gone.

wasm_of_ocaml compiles OCaml int to 31 bits, where 0x7fffffff and
0x40000000 do not fit:

    Warning [integer-overflow]: integer 0x40000000 (1073741824) truncated
    to 0xc0000000 (-1073741824); the generated code might be incorrect.

Neither site is wrong. In the emulation modules 0x40000000 truncates to
the 31-bit sign bit, which is what without_bit_sign masks off, so the
code is correct on the backend that selects them. optint_native is only
reached when Sys.word_size = 64, and its neighbouring constants already
wrap on a narrower int without warning.

Build the emulation masks from wider literals so nothing is truncated at
compile time, and derive int32_max from uint32_max rather than laundering
a literal that cannot fit. Values are unchanged on 63-bit native, 32-bit
js_of_ocaml and 31-bit wasm_of_ocaml.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant