A5 ignored on asm() register-clobber list
Given an asm() block such as:
void test_a5(void) {
asm volatile(
"clr.l %%a3\n\t"
"clr.l %%a4\n\t"
"clr.l %%a5\n\t"
:
:
: "a3", "a4", "a5"
);
}
Registers A3, A4, and A5 are marked as 'clobbered' and should be saved and restored. However, the generated code (from m68k-apple-macos-gcc -S a5test.c) preserves A3 and A4, but not A5:
test_a5:
link.w %fp,#0
move.l %a4,-(%sp)
move.l %a3,-(%sp)
#APP
| 2 "a5test.c" 1
clr.l %a3
clr.l %a4
clr.l %a5
| 0 "" 2
#NO_APP
nop
move.l (%sp)+,%a3
move.l (%sp)+,%a4
unlk %fp
rts
I don't have a copy of mainline GCC 9 to test this against, but m68k GCC 13.2 does not exhibit this behavior, saving and restoring A5 correctly along with the others: https://godbolt.org/z/r5zese79j
I'm assuming that this is a side effect of modifying GCC to avoid using A5 due to its special nature on the Mac OS, but in this case it has the opposite effect!
I should add, this is easy enough to work around by just 'manually' saving and restoring A5 in the assembly block, but it was definitely a fun issue to track down! Mostly just putting this here to document it - I wouldn't consider it a high priority to fix.
That's an interesting... consequence.
I guess that %a5 is reserved as a fixed register, but it's not actually considered 'used', so it doesn't get saved & restored.
GCC seems to behave similarly on other platforms... on ARM, %x29 is the frame pointer, and....
int foo();
void useFramePointer()
{
foo();
}
void clobberFramePointer()
{
asm volatile(
"add x29, x29, x29"
: : : "x29"
);
}
gives me
useFramePointer():
stp x29, x30, [sp, -16]!
mov x29, sp
bl foo()
ldp x29, x30, [sp], 16
ret
clobberFramePointer():
add x29, x29, x29
ret
... in other words - it's carefully saving and restoring %x29 before it does a function call, but it does nothing to protect it from the asm statement.
So let's leave this ticket here as a warning until there's a piece of documentation that mentions it...