65816-llvm-mos/tests/regress/setjmpImgSave.c
2026-10-06 17:01:51 -05:00

88 lines
2.3 KiB
C

// 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 <setjmp.h>
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;
}