Signed Multiply-Overflow Orbit

Motion & Curves

Six orbiters trace looping paths on a Super Nintendo canvas. Each frame their velocity is scaled by a growing factor; when the product overflows a signed 16-bit integer the orbiter teleports to the mirror quadrant and flashes orange. This is compiler stress-test #76 — the first demo to exercise G_SMULO (signed multiply-overflow), which llvm-mos lowers by widening both operands to double width, multiplying, and comparing the halves.

loading core…

Self-running — trails accumulate and sparks fire when overflow fires.

Click the screen to focus, then watch. Tab away and it pauses.

What it is

Multiplying two signed integers whose product exceeds the range of the type is technically undefined behaviour in C — but __builtin_mul_overflow(a, b, &result) makes it well-defined: it stores the truncated product in result and returns true if the mathematical product didn't fit. The demo uses it with both int16_t (orbit velocity × growing scale) and int32_t (the same operands widened × 256/64) to exercise the legalizer at two different widths.

// int16_t signed multiply-overflow check (G_SMULO s16)
int16_t scaled;
bool ov = __builtin_mul_overflow(vx, scale_factor, &scaled);

// int32_t signed multiply-overflow check (G_SMULO s32 -> __muldi3)
int32_t p32;
bool ov32 = __builtin_mul_overflow(a32, b32, &p32);

// LLVM lowerMulo: widen a,b to double-width, multiply, compare
// high half to sign-extension of low half.
if (ov) { teleport_to_mirror(); emit_spark(); }

The scale_factor grows with the animation tick (t/4 + 1), so each orbiter hits the INT16_MAX boundary at a different moment, causing periodic teleports. As time advances the orbiters return to nearly the same paths (the mirror quadrant is the same orbit reflected), creating a symmetric scatter of trails punctuated by orange sparks.

Compiler stress-test

ItemWhat it exercises
G_SMULOThe signed multiply-with-overflow operation: check whether a×b overflows a signed integer of a given width. The LLVM legalizer at lowerMulo (LegalizerHelper.cpp:2693) expands it by widening both operands to double-width, multiplying (which gives a result that never overflows at double-width), then comparing the high half to the sign-extension of the low half. If they agree, no overflow; if they differ, the truncated product wrapped.
distinct from add-overflow (#44)__builtin_add_overflow (demo #44, G_UADDO) checks whether an ADDITION overflows — a single carry/overflow flag test, short-circuit branch. Multiply-overflow (#76, G_SMULO) is fundamentally different: the widen-multiply-sign-check sequence. The two paths share no lowering code.
distinct from mul-high (#56)Demo #56 rotozoom uses G_SMULH/G_UMULH to extract the HIGH half of a widening multiply (Q16.16 fixed-point descale). G_SMULO takes the LOW half and checks whether the HIGH half matches the sign extension — the complement operation, exercising different code in the legalizer.
measured finding: __muldi3 not __mulosi4The ideas doc predicted the int32 path would call compiler-rt __mulosi4 (the ACLE 32-bit signed mul-overflow helper). Measurement shows lowerMulo widens int32→int64 and calls __muldi3 instead — the LLVM widen-to-double-width strategy. The gate found the same CRC on host and both emulators, so the lowering is correct; the predicted symbol was simply not the one emitted.
orbit + teleport visualSix orbiters trace paths on the canvas. Each frame their velocity is scaled by a growing factor; when the int16_t product overflows, the orbiter teleports to the mirror quadrant (127−x, 127−y) and emits an orange spark that lingers for ~12 frames. The growing scale_factor causes each orbiter to overflow at a different time, giving a staggered spark pattern.

Written in C with the llvm-mos 65816 toolchain and verified against bsnes-jg and MAME. Hit Verify fidelity to reproduce the build gate's WRAM assert (gate CRC 0xD81B — a fold of 121 signed-overflow-check results) live in this tab. No far pointers — host == default == +mos-a16 == +mos-xy16, -verify clean.