64-bit multiply overflow is unchecked under {$Q+} on riscv32 and xtensa
The fact
{$Q+}
var x, y, z: Int64;
begin x := 4000000000000000000; y := 4; z := x * y; WriteLn(z); end.
| target | result |
|---|---|
| x86-64 | Runtime error 215 (arithmetic overflow) |
| riscv32 | 1553255926290448384 — wrapped, no trap |
| xtensa | 1553255926290448384 — wrapped, no trap |
Note both wrong answers are the same wrong answer, which is the tell that this is one shared mechanism and not two coincidences.
Why it is separate from the narrowing-store check
32-bit overflow under {$Q+} IS caught on both, because Pascal widens the
arithmetic to Int64 and the wrap is caught at the narrowing store
([[bug-a-xtensa-has-no-q-plus-overflow-check-emitter-so-it-wraps-silently]]).
This case has no narrowing — destination and value are both Int64 — so the
store-side check cannot see it by construction. It has to be detected where the
64-bit product is formed.
Where it would go
EmitBinop64RISCV32 already takes qchk/qchkUns and handles checked add and
subtract of pairs; tkStar is the arm it does not check, and its own comment
records the deferral ("unsigned checked mul stays deferred, like the 64-bit
pair path records"). EmitBinop64Xtensa takes no qchk parameter at all and
would need one.
A 64×64 checked multiply needs the full 128-bit product, or an equivalent
pre-check on the operand magnitudes. On xtensa that is harder again: muluh is
UNSIGNED, so the signed high word must be reconstructed as
hi_s = hi_u - ((l sar 31) and r) - ((r sar 31) and l) — and under
--xtensa-soft-mulhigh there is no muluh instruction at all, so it becomes a
helper call inside an arithmetic operation.
Ranking
Low deliberately. It needs an Int64 product past 2^63 with {$Q+} on, on a
32-bit cross target. Real, silent, and worth recording — but the 32-bit shapes
that reach it are the ones already covered. Do not raise the prio to make it
visible; it is reachable and correctly ranked, and both backends should be
done in one pass when someone takes it.