Sort Visualizer
Algorithms & Data
Bars reshuffle, then re-sort under libc qsort — with a function-pointer
comparator that qsort calls back into for every comparison (ascending, evens-first,
descending). Building this demo caught a real compiler crash: the ordinary
comparator idiom (x>y)-(x<y) tripped an unhandled case in the backend. Runs
automatically; no joypad needed. Written in C and compiled with the
llvm-mos-based 65816 toolchain
(+mos-a16, 16-bit accumulator mode).
Self-running demo — bars reshuffle then re-sort under rotating comparators.
Click the screen to focus, then watch. Tab away and it pauses.
What it is
qsort is C's built-in sort. You hand it your array and a comparator — a
little function it calls back for each pair it needs to order. Swap the comparator and the same
data sorts a different way:
int cmp(const void *a, const void *b) {
int16_t x = *(int16_t*)a, y = *(int16_t*)b;
return (x > y) - (x < y); // ← the spaceship idiom
}
qsort(bars, N, sizeof bars[0], cmp); // qsort calls cmp back, per compare
That comparator is the whole point of the stress-test: a library calling back into your
code through a pointer. And it found a bug — the compiler turned (x>y)-(x<y)
into a single three-way-compare instruction the 65816 backend didn't know how to lower, crashing
the build. A one-line fix in the backend legalizer resolved it; this demo is its regression test.
Compiler stress-test
| Item | What it exercises |
|---|---|
| qsort callback | The library sort receives a function pointer and calls it back for every comparison — an indirect call into our C, with two const void* arguments and an int return. A rotating pointer picks ascending, evens-first, or descending order. |
| the bug it found | That innocuous comparator return (x>y)-(x<y) is canonicalised by the compiler to a single "three-way compare" opcode (G_SCMP). The MOS backend had no rule for it and aborted: "unable to legalize G_SCMP" — so ANY program sorting with the standard comparator idiom failed to build. |
| the fix | One line in the backend legalizer lowers G_SCMP/G_UCMP to the compare+select primitives it already handles. It reproduced on plain 8-bit C (no exotic mode), so it is a general fix queued upstream to llvm-mos. |
| bit-exact | The gate sorts a 64-element array three ways and folds the sorted VALUES — so even if two qsort implementations break ties between equal elements differently, the result is identical. host == default == +mos-a16 == +mos-xy16 == bsnes-jg. |
Written in C with the llvm-mos 65816 toolchain and verified against bsnes-jg
(and MAME where the SPC700 IPL is present). Hit Verify fidelity to reproduce the build
gate's WRAM assert (gate hash 0x8EA5 — a fold of a 64-element array sorted three
ways) live in this tab. No far pointers — the same source passes every way: host == default ==
+mos-a16 == +mos-xy16 == bsnes-jg, -verify clean.