Consider the following C code:
extern void dummy(void);
void foo1(void) __attribute__(( interrupt("IRQ") ));
void foo2(void) __attribute__(( interrupt("FIQ") ));
void foo1() {
dummy();
return;
}
void foo2() {
dummy();
return;
}
The code produced by arm gnueabi gcc is basically this:
foo1:
sub lr, lr, #4
stmfd sp!, {r0, r1, r2, r3, ip, lr}
bl dummy
ldmfd sp!, {r0, r1, r2, r3, ip, pc}^
foo2:
sub lr, lr, #4
stmfd sp!, {r0, r1, r2, r3, r4, lr}
bl dummy
ldmfd sp!, {r0, r1, r2, r3, r4, pc}^
The code for foo1 does not hold any surprises. r0-r3 and ip are saved, because the call to dummy may change their value. Also, after correcting lr, it is pushed and popped into pc in the end. This is fairly standard.
However, the code for foo2 is surprising. Saving the value of ip is not required, as it is a banked register. But that gcc saves r4 is surprising.
So why does gcc save r4? I don't see any reason to do that, since the call to dummy will not corrupt this register.