// Regression test: a longjmp must not hand a caller back IMG8..IMG15 // ($C0..$CE) words that a function it unwound overwrote. // // IMG8..IMG15 survive calls because every function that writes one gives // its caller the old value back on return (W65816ImgCalleeSave, and the // entry/return swaps of W65816StackRelToImg's IMG8..15 pointer homes). A // longjmp skips those returns. walk() keeps q and r in $C2..$C8 and // longjmps out of ext(); outer() keeps ITS q and r in the same words // across the call to f0(). Before the fix outer() came back from f0() // walking walk()'s r (bufF) instead of its own (bufE). // // Fix: a call that can return twice (setjmp) clobbers IMG8..IMG15 // (W65816TargetLowering::AdjustInstrPostInstrSelection), so f0() keeps // nothing in them across setjmp and saves and restores all eight for // outer(). // // outer(bufB, bufC, bufE, 2) = 2 + bufC[1] (5) + bufE[2] (9) = 16; the // broken build gave 9, 5, 8. #include static jmp_buf gJb; volatile int gSink; int bufA[4] = {5, 0, 0, 0}; int bufB[4] = {0, 1, 2, 3}; int bufC[4] = {0, 4, 5, 6}; int bufD[4] = {9, 9, 9, 9}; int bufE[4] = {0, 0, 8, 0}; int bufF[4] = {0, 0, 0, 0}; __attribute__((noinline)) void ext(int v) { gSink = v; if (v == 7) { longjmp(gJb, 1); } } // q and r live across the calls to ext in IMG8..15 homes. __attribute__((noinline)) void walk(int *p, int *q, int *r, int n) { int i = 0; do { ext(p[0]); q[1] += i; r[2] ^= q[1]; p[2] = r[2]; p[0]++; } while (++i != n); } __attribute__((noinline)) int f0(int *p, int *q, int *r) { if (setjmp(gJb)) { return 1; } walk(p, q, r, 3); return 0; } // Its own q and r in the same IMG8..15 words across the call to f0. __attribute__((noinline)) int outer(int *p, int *q, int *r, int n) { int i = 0; int t = 0; do { t += f0(bufA, bufD, bufF); q[1] += i; r[2] ^= q[1]; p[3] = r[2]; } while (++i != n); return t + q[1] + r[2]; } int main(void) { volatile int *out = (volatile int *)0x025000; out[1] = outer(bufB, bufC, bufE, 2); // 0x0010 out[2] = bufC[1]; // 0x0005 out[3] = bufE[2]; // 0x0009 out[0] = 0xC0DE; while (1) { } return 0; }