65816-llvm-mos/tests/regress/byteStoreO0.c
2026-07-03 17:57:10 -05:00

37 lines
1.7 KiB
C

// Regression test for the -O0 byte-store-through-pointer miscompile
// (W65816 STB_DPIndLongY atomic pseudo).
//
// The i8 store `*pcout = ...` through a ptr32 lowers to a [dp],Y store
// wrapped in SEP #$20 / STA / REP #$20 (M=8 so only one byte is written).
// The custom inserter used to emit those as THREE separate instructions,
// so the -O0 fast register allocator (which spills everything and reloads
// before each use) could wedge a 16-bit reload BETWEEN the SEP and REP —
// running it in M=8, it loaded only the low byte of a 16-bit value. That
// truncated `probe`'s `px + 1` pointer reload, so a value carried across
// the call came back wrong (reported by an external SNES user: an
// `unsigned char pid` packed via `<< 16` returned 0).
//
// Fix: emit ONE atomic STB_DPIndLongY pseudo, expanded to SEP/STA/REP at
// the AsmPrinter, so nothing can be inserted inside the M=8 window.
//
// caller(34): probe writes pc = 34 & 7 = 2 through the pointer and returns
// 34 + 1 = 35; caller returns 35 + 2 = 37 = 0x25. The buggy -O0 build
// returned 3 (probe's return truncated to 1, + pc 2). MUST be compiled
// at -O0 (the bug does not occur at -O1+).
__attribute__((noinline)) static short probe(short px, unsigned char *pcout) {
*pcout = (unsigned char)(px & 7);
return (short)(px + 1);
}
__attribute__((noinline)) static short caller(short px) {
unsigned char pc;
short y = probe(px, &pc); // y (probe's return) is live across the call
return (short)(y + pc); // + pc, the byte written through the pointer
}
int main(void) {
*(volatile unsigned short *)0x025000 = (unsigned short)caller(34); // 0x0025
while (1) {}
return 0;
}