Hi.
I've been converting a project from TI's compiler to the GCC for MSP430, and I noticed that msp430-elf-gcc is pushing *all* of the general-purpose registers at the beginning of every interrupt service routine (ISR) that calls another function.
Doing that seems inefficient in terms of performance and stack use if saving them all is not necessary.
I investigated the EABI requirements, and slaa534 says the following in section 3.9:
Interrupt functions must save all the registers that are used, even those that are normally considered callee-saved, except for the special purpose registers PC, SP, and SR.
Now suppose my ISR calls my_func(). According to the EABI section 3.2.2, my_func() is already required to save R4-R10 if it uses them because they are callee-saved registers, so there is no reason the ISR has to save them if the ISR doesn't use them.
Also, my_func() is free to clobber R11-R15 without saving them according to the EABI section 3.2 because those are caller-save registers. So if an ISR calls my_func(), it is required to save R11-R15 in addition to any other registers it uses, but nothing more.
I have modified the GCC code to implement this behavior. I don't know if my reasoning is correct or not, but so far my interrupt handlers are working as expected. I am using this version of msp430-elf-gcc (build date of 04012016):
msp430-elf-gcc (GCC) 5.3.0 (GNUPro 15r1) (Based on: GCC 5.3 GDB 7.7 Binutils 2.25 Newlib 2.2.0)
Here is the patch. I hope it may be useful.
--- ./sources/tools/gcc/config/msp430/msp430.c.original»2016-02-17 19:31:21.000000000 -0500 +++ ./sources/tools/gcc/config/msp430/msp430.c»·2016-04-20 15:39:38.914055260 -0400 @@ -1749,8 +1749,25 @@ /* Interrupt handlers save all registers they use, even ones which are call saved. If they call other functions then *every* register is saved. */ +#if 0 if (msp430_is_interrupt_func ()) return ! crtl->is_leaf || df_regs_ever_live_p (regno); +#else +/* I don't see any reason why a call requires every register to be saved. The + * TI compiler does not do that. According to the EABI section 3.9, interrupt + * handlers must save any registers they use, and functions are required to + * save all the callee-saved registers (R4 - R10). R11-R15 are caller-saved + * registers, so the ISR must save R11-R15 if it calls a function of any kind. + * That's what is done here. */ + if (msp430_is_interrupt_func ()) { + if((!crtl->is_leaf) && (regno >= 11) && (regno <= 15)) { + /* We need to save all of the save-on-call (caller-save) registers if we + * call a function. */ + return true; + } + return df_regs_ever_live_p (regno); + } +#endif ␣ if (!call_used_regs [regno] && df_regs_ever_live_p (regno))
--- ./sources/tools/gcc/config/msp430/msp430.c.original 2016-02-17 19:31:21.000000000 -0500 +++ ./sources/tools/gcc/config/msp430/msp430.c 2016-04-20 15:39:38.914055260 -0400 @@ -1749,8 +1749,25 @@ /* Interrupt handlers save all registers they use, even ones which are call saved. If they call other functions then *every* register is saved. */ +#if 0 if (msp430_is_interrupt_func ()) return ! crtl->is_leaf || df_regs_ever_live_p (regno); +#else +/* I don't see any reason why a call requires every register to be saved. The + * TI compiler does not do that. According to the EABI section 3.9, interrupt + * handlers must save any registers they use, and functions are required to + * save all the callee-saved registers (R4 - R10). R11-R15 are caller-saved + * registers, so the ISR must save R11-R15 if it calls a function of any kind. + * That's what is done here. */ + if (msp430_is_interrupt_func ()) { + if((!crtl->is_leaf) && (regno >= 11) && (regno <= 15)) { + /* We need to save all of the save-on-call (caller-save) registers if we + * call a function. */ + return true; + } + return df_regs_ever_live_p (regno); + } +#endif if (!call_used_regs [regno] && df_regs_ever_live_p (regno))