Odd-Width Mask Sculptor
Algorithms & DataValues masked to odd widths — 20, 24, and 28 bits — then zero-extended to 64 bits and multiplied. This is compiler stress-test #103: it re-runs the exact legalization shape a real llvm-mos 64-bit crash once hit — an odd-width value with no clean 16-bit-lane decomposition, widened into 64-bit arithmetic — and confirms the fix (patch 0017) holds.
Self-running — four bands, each masked to a different width, terrace and flow.
Click the screen to focus, then watch. Tab away and it pauses.
What it is
Odd-width integers are the awkward case for a 16-bit machine: a 20- or 24- or 28-bit value has no neat split into 16-bit halves, yet the compiler must zero-extend it to 64 bits to do 64-bit math on it. This demo builds exactly those values and multiplies them wide.
// a 20/24/28-bit value, then widened to 64 bits and multiplied uint32_t x = (uint32_t)v * 2654435761u + rot; // 32-bit multiply uint64_t w20 = (uint64_t)((x ^ y) & 0x000FFFFF); // 20-bit → zero-extend to 64 uint64_t w24 = (uint64_t)((x + y) & 0x00FFFFFF); // 24-bit → zero-extend to 64 uint64_t a = w20 * w24; // 64-bit multiply // the compiler narrows each masked value to an ODD width (20, 24, 28 bits), // then must zero-extend that odd width to 64 — a shape with no clean // decomposition into 16-bit lanes, which once crashed the backend.
Each step folds the 64-bit result into a CRC. The picture is the proof: a wrong odd-width extend or a wrong 64-bit split would corrupt the arithmetic and change the CRC.
Compiler stress-test #103 — Round 6: hardening the fixes
| Item | What it exercises |
|---|---|
| odd-width integers | Most integers are 8, 16, 32, or 64 bits wide. But mask a value to 20 bits and the compiler tracks it as a 20-bit quantity — an “odd” width. When that narrow value is then widened to 64 bits and fed to 64-bit arithmetic, the backend must extend an odd width into four 16-bit lanes, which has no tidy decomposition. |
| the bug this guards (#61, patch 0017) | A 64-bit Diffie-Hellman demo once crashed the 16-bit-accumulator backend on exactly this: a 20-bit-masked value zero-extended to 64 bits (“unable to legalize”), plus splitting a 64-bit value into 16-bit lanes. Patch 0017 taught the legalizer both moves. This demo re-runs the odd-width extend at 20, 24, and 28 bits, threaded through a 64-bit multiply, and confirms the fix holds. |
| what actually forms the odd width | A measured detail: masking a 64-bit value directly (v & 0xFFFFF) does NOT create an odd-width type — it stays 64-bit with some bits known-zero. The odd width only appears when a 32-bit value is masked and an operation is applied, then the result is widened to 64 bits. So widths above 32 (40, 48) can’t arise this way; the reachable odd widths here are 20, 24, and 28. |
| a 64-bit multiply on top | The two zero-extended narrow values are multiplied at 64 bits, which exercises the other half of the fix — the 64-bit multiply glue that composes a product from 32-bit pieces without ever widening to 128 bits. So a single step touches both the odd-width extend and the 64-bit (un)merge legalization. |
| keeping it honest | The computation runs from a volatile seed so the compiler can’t precompute the whole thing to a constant and skip the code entirely. Without that, a pure function with fixed inputs folds away and tests nothing — a trap worth flagging. |
| result: bit-exact, and the visual | 64-bit integer arithmetic on masked values is exact, so the result is identical on host and SNES: host == default == +mos-a16 == +mos-xy16 on both MAME and bsnes-jg, -verify-machineinstrs clean (the modes that used to crash). On screen, a field is split into horizontal bands, each masking the running 64-bit state to a different width — so a band’s terrace granularity tracks its mask — and the terraces flow as the state mixes. |
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 0x1FD9 — a fold of 40 mix steps) live in this tab.
No far pointers — host == default == +mos-a16 == +mos-xy16,
-verify clean.