/* { dg-do run } */ #ifdef __MSP430X__ #include "isr-push-pop-isr-430x.c" #include "isr-push-pop-leaf-isr-430x.c" #else #include "isr-push-pop-isr-430.c" #include "isr-push-pop-leaf-isr-430.c" #endif /* Test that ISRs which call other functions do not save extraneous registers. They only need to save the caller-saved regs R11->R15. We use a lot of asm statements to hide what is going on from the compiler to more accurately simulate an interrupt. */ /* Store the register number in each general register R4->R15, so they can be later checked their value has been kept. */ #define SETUP_REGS \ __asm__ ("mov #4, r4"); \ __asm__ ("mov #5, r5"); \ __asm__ ("mov #6, r6"); \ __asm__ ("mov #7, r7"); \ __asm__ ("mov #8, r8"); \ __asm__ ("mov #9, r9"); \ __asm__ ("mov #10, r10"); \ __asm__ ("mov #11, r11"); \ __asm__ ("mov #12, r12"); \ __asm__ ("mov #13, r13"); \ __asm__ ("mov #14, r14"); \ __asm__ ("mov #15, r15"); /* Write an arbitrary value to all general regs. */ #define TRASH_REGS \ __asm__ ("mov #0xFFFF, r4" : : : "R4"); \ __asm__ ("mov #0xFFFF, r5" : : : "R5"); \ __asm__ ("mov #0xFFFF, r6" : : : "R6"); \ __asm__ ("mov #0xFFFF, r7" : : : "R7"); \ __asm__ ("mov #0xFFFF, r8" : : : "R8"); \ __asm__ ("mov #0xFFFF, r9" : : : "R9"); \ __asm__ ("mov #0xFFFF, r10" : : : "R10"); \ __asm__ ("mov #0xFFFF, r11" : : : "R11"); \ __asm__ ("mov #0xFFFF, r12" : : : "R12"); \ __asm__ ("mov #0xFFFF, r13" : : : "R13"); \ __asm__ ("mov #0xFFFF, r14" : : : "R14"); \ __asm__ ("mov #0xFFFF, r15" : : : "R15"); /* Check the value in all general registers is the same as that set in SETUP_REGS. */ #define CHECK_REGS \ __asm__ ("cmp #4, r4 { jne ABORT"); \ __asm__ ("cmp #5, r5 { jne ABORT"); \ __asm__ ("cmp #6, r6 { jne ABORT"); \ __asm__ ("cmp #7, r7 { jne ABORT"); \ __asm__ ("cmp #8, r8 { jne ABORT"); \ __asm__ ("cmp #9, r9 { jne ABORT"); \ __asm__ ("cmp #10, r10 { jne ABORT"); \ __asm__ ("cmp #11, r11 { jne ABORT"); \ __asm__ ("cmp #12, r12 { jne ABORT"); \ __asm__ ("cmp #13, r13 { jne ABORT"); \ __asm__ ("cmp #14, r14 { jne ABORT"); \ __asm__ ("cmp #15, r15 { jne ABORT"); void __attribute__((noinline)) callee (void) { /* Here were modify all the regs, but tell the compiler that we are since this is just a way to simulate a function that happens to modify all the registers. */ TRASH_REGS } int #ifdef __MSP430X_LARGE__ __attribute__((lower)) #endif main (void) { SETUP_REGS /* A surprise branch to the ISR that the compiler cannot prepare for. We must first simulate the interrupt acceptance procedure that the hardware would normally take care of. So push the desired PC return address, and then the SR (R2). MSP430X expects the high bits 19:16 of the PC return address to be stored in bits 12:15 of the SR stack slot. This is hard to handle in hand-rolled assembly code, so we always place main() in lower memory so the return address is 16-bits. */ __asm__ ("push #CHECK1"); __asm__ ("push r2"); __asm__ ("br #isr"); __asm__ ("CHECK1:"); /* If any of the regs R4->R15 don't match their original value, this will jump to ABORT. */ CHECK_REGS /* Now test that an interrupt function that is a leaf also works correctly. */ __asm__ ("push #CHECK2"); __asm__ ("push r2"); __asm__ ("br #isr_leaf"); __asm__ ("CHECK2:"); CHECK_REGS /* The values in R4->R15 were successfully checked, now jump to FINISH to run the prologue generated by the compiler. */ __asm__ ("jmp FINISH"); /* CHECK_REGS will branch here if a register holds the wrong value. */ __asm__ ("ABORT:"); #ifdef __MSP430X_LARGE__ __asm__ ("calla #abort"); #else __asm__ ("call #abort"); #endif __asm__ ("FINISH:"); return 0; }