diff --git a/changes/int-min-literal.md b/changes/int-min-literal.md new file mode 100644 index 00000000..134ada1f --- /dev/null +++ b/changes/int-min-literal.md @@ -0,0 +1,6 @@ +bump: patch +type: fix +**The most negative int is emitted as itself.** The compiler's `itoa` negated a negative value before +taking its digits, and `-(-2147483648)` overflows back to itself, so a literal such as `0x80000000` in an +int (`b & 0x80000000`) was written into the IR as a bare `-` and the build failed in LLVM. The digits are +now taken off the value as it is. Needs a reseed to reach `bin/ludicc`. diff --git a/examples/lang/int_min_literal.ludic b/examples/lang/int_min_literal.ludic new file mode 100644 index 00000000..ba6bd055 --- /dev/null +++ b/examples/lang/int_min_literal.ludic @@ -0,0 +1,10 @@ +# int_min_literal.ludic - a literal that lands on the most negative int (0x80000000 in an int) is +# emitted as -2147483648. It once came out as a bare "-", invalid IR. Prints `-2147483648 -1 1`. +program IntMinLiteral { + entry { + let x = len(Os.args()) - 5 + let top = x & 0x80000000 + let low = x | 0x7FFFFFFF + print(`{top} {low} {(x & 0x80000000) != 0}`) + } +} diff --git a/selfhost/support/str.ludic b/selfhost/support/str.ludic index 850d3106..b648c53a 100644 --- a/selfhost/support/str.ludic +++ b/selfhost/support/str.ludic @@ -20,12 +20,19 @@ function str_starts(s: pointer, prefix: pointer) -> bool { # integer -> fresh decimal string function itoa(v: int) -> pointer { if v == 0 { let z = bytes(2); z[0] = '0'; z[1] = 0; return z } - var neg = false + # digits are taken off v as it is, never off -v: the most negative int has no positive twin, and + # -v overflowed back to itself, so a 0x80000000 in an i32 was emitted as a bare "-" + let neg = v < 0 var x = v - if x < 0 { neg = true; x = -x } - let tmp = bytes(16) + let tmp = bytes(24) var n = 0 - while x > 0 { tmp[n] = 48 + x % 10; x /= 10; n += 1 } + while x != 0 { + var d = x % 10 + if d < 0 { d = -d } + tmp[n] = 48 + d + x /= 10 + n += 1 + } var total = n if neg { total += 1 } let out = bytes(total + 1)