bug: arm32 writeln(LongWord) mangles a value with the high bit set
- Type: bug (codegen — arm32 unsigned-32 write formatting)
- Status: DONE (2026-06-26, commit on master)
- Track: A
- Opened: 2026-06-25
- Found-by: Track A, verifying bug-not-on-int64-is-boolean
(
test_not_int64_exprLongWord lines) on arm32.
Symptom
On arm32, writeln of a LongWord/Cardinal (32-bit unsigned) whose value
has bit 31 set prints garbage; small values are fine. x86-64 / i386 / aarch64 are
all correct.
var c: LongWord;
begin
c := 4294967295; writeln(c); { arm32 prints `/` ; want 4294967295 }
c := 100; writeln(c); { arm32 prints 100 (OK) }
end.
Independent of not — a plain assignment + writeln reproduces it. The bitwise
not work only surfaced it (not LongWord(0) = 4294967295).
Likely cause
arm32's unsigned-32 write path (the writeln/Str integer formatter for
tyUInt32) probably treats the value as signed, or the high-bit value collapses
to a wrong digit/char — / is ASCII 47, suggesting a single mis-derived digit
rather than a full number. Compare with the i386/aarch64 unsigned-write paths,
which handle the same value correctly (so the fix is arm32-local).
Workaround
test_not_int64_expr.pas checks the LongWord complement by value comparison
(if c = 4294967295) instead of writeln(c), so it stays portable across
targets. Remove the workaround once this is fixed.
Acceptance
writeln(c)forc: LongWordwith bit 31 set prints the correct unsigned decimal on arm32 (matching the x86-64 oracle).- Regression test (cross compare arm32 vs x86-64).
Resolution (2026-06-26, Track A)
arm32's write/Str integer loops divided with SDIV; the unsigned writers mangled
LongWord >= 2^31, and the signed writers also mangled INT_MIN (negation overflows
to 0x80000000, SDIV mis-divides). Every loop divides a NON-NEGATIVE magnitude
(unsigned value, or post-negation magnitude), so switched all four sdiv
(0xE714F210) -> udiv (0xE734F210) in emit.inc. Fixes both LongWord-high-bit AND
INT_MIN as a bonus. Regression: test/test_uint32_write.pas, cross-checked arm32
vs x86-64 in make test. Self-host byte-identical. (test_not_int64_expr's
value-comparison workaround can stay; the new test covers the writeln path.)