The gcc 3.4.0 still have problems on generating the entry/exit code for interrupts, if we supress the "apcs-frame" option. When the code is compiled with "-mno-apcs-frame" the entry code subtracts 4 from the "lr" before pushing it into stack, and do it again on the exit code. __attribute__ ((interrupt ("IRQ"))) void int_handler(void) { foo(); } arm-elf-gcc -mcpu=arm7tdmi -mno-apcs-frame -mthumb-interwork -Wall -O2 -c test.c 00000000 <int_handler>: 0: e24ee004 sub lr, lr, #4 ; 0x4 4: e92d500f stmdb sp!, {r0, r1, r2, r3, ip, lr} 8: ebfffffe bl 0 <int_handler> c: e8bd500f ldmia sp!, {r0, r1, r2, r3, ip, lr} 10: e25ef004 subs pc, lr, #4 ; 0x4 arm-elf-gcc -mcpu=arm7tdmi -mapcs-frame -mthumb-interwork -Wall -O2 -c test.c 00000000 <int_handler>: 0: e52dc004 str ip, [sp, -#4]! 4: e1a0c00d mov ip, sp 8: e92dd80f stmdb sp!, {r0, r1, r2, r3, fp, ip, lr, pc} c: e24cb004 sub fp, ip, #4 ; 0x4 10: ebfffffe bl 0 <int_handler> 14: e89d680f ldmia sp, {r0, r1, r2, r3, fp, sp, lr} 18: e8bd1000 ldmia sp!, {ip} 1c: e25ef004 subs pc, lr, #4 ; 0x4
*** Bug 25428 has been marked as a duplicate of this bug. ***
Patch here: http://gcc.gnu.org/ml/gcc-patches/2006-08/msg00230.html
*** Bug 29004 has been marked as a duplicate of this bug. ***
(In reply to comment #2) > Patch here: > http://gcc.gnu.org/ml/gcc-patches/2006-08/msg00230.html What's the status of this patch?
(In reply to comment #4) > What's the status of this patch? The bug is also present in arm-elf-gcc version 4.1.0 However, adding the -fno-omit-frame-pointer parameter, make the compiler emit the correct code in the mentioned code example.
Subject: Bug 16634 Author: pbrook Date: Thu Jan 4 00:09:48 2007 New Revision: 120413 URL: http://gcc.gnu.org/viewcvs?root=gcc&view=rev&rev=120413 Log: 2007-01-03 Paul Brook <paul@codesourcery.com> PR target/16634 gcc/ * config/arm/arm.c (output_return_instruction): Pop PC in interrupt functions. (use_return_insn): Return 0 for Thumb interrupt functions. (print_multi_reg): Add rfe argument for IRQ returns. (arm_output_epilogue): Pop interrupt return address directly into PC. (arm_expand_prologue): Only adjust IRQ return address in Arm mode. Modified: trunk/gcc/ChangeLog trunk/gcc/config/arm/arm.c
This still fails in gcc 4.1.2 under certain conditions. I can provide a test case if necessary.
also fails on 4.2.1
(In reply to comment #8) > also fails on 4.2.1 is this bug still present in 4.2.2? Acc. to my obervations the error only shows up when not all auto variables can be mapped to registers, and additional variables are located on stack. As a work-around, if a more complex interrupt handler is needed, place the code in a subfunction and call this function from the interrupt handler. In this case no stack is reserved in the interrupt handler, and the generated code is OK. Care must be taken, of course, that the optimizer does not inline the code for IntFunc(). Example: void IntFunc(void); __attribute__((interrupt("IRQ")) void IntHandler(void) { IntFunc(); } __attribute__(noinline)) void IntFunc(void) { // interrupt code here! }
(In reply to comment #9) > is this bug still present in 4.2.2? I can confirm this bug still exists in 4.2.2 and 4.2.3. > Acc. to my obervations the error only shows up when not all auto variables can > be mapped to registers, and additional variables are located on stack. As a > work-around, if a more complex interrupt handler is needed, place the code in a > subfunction and call this function from the interrupt handler. In this case no > stack is reserved in the interrupt handler, and the generated code is OK. Care > must be taken, of course, that the optimizer does not inline the code for > IntFunc(). Your subfunction workaround actually did the trick. The generated return instruction is "ldm sp!, {r0, r1, r2, r3, ip, pc}^". However, if a more complex interrupt handler is implemented the wrong return instruction generated. The results seem a bit unpredictable so I currently use plain assembler for my interrupts. Example: extern __attribute__ ((interrupt ("IRQ"))) void base_int_ctrl_top_irq_handler(void) { int_ctrl_status_t status; status.val = READ_REG_U32(ADDR_STATUS_NIRQ); if (status.bf.x) { puts("Spurious x IRQ!"); } else if (status.bf.y) { puts("Spurious y IRQ!"); } else if (status.bf.timer) { base_timer_interrupt(); } else if (status.bf.z) { puts("Spurious z IRQ!"); } else { puts("Spurious unknown IRQ!"); } } Generated assembly for return: pop {r0, r1, r2, r3, ip, lr} subs pc, lr, #4 ; 0x4 (and lr was already decremented by 4 when the handler was entered) So your observations are correct. Bug #27859 seems to be a duplicate of this bug and contains a patch. However, this patch is probably not optimal since it just replaces the final "subs" by a "movs" while the best solution seems to be the "ldmfd ..." instruction above.
Since this is not marked to be fixes I assume is hasn't been fixed in 4.3.3. Wat is wrong with Pauls patch since it hasn't been included ?
The code generated on trunk, 4.4 and 4.3 with arm-eabi-gcc at O2 for the test in Comment #0 is int_handler: @ Interrupt Service Routine. @ args = 0, pretend = 0, frame = 0 @ frame_needed = 0, uses_anonymous_args = 0 sub lr, lr, #4 stmfd sp!, {r0, r1, r2, r3, ip, lr} bl foo ldmfd sp!, {r0, r1, r2, r3, ip, pc}^ which appears to be the expected code generated. This appears to be fixed. If there is a problem please open a separate bug report. I tried the testcase in Comment #10 but that doesn't even compile . Attempting to reverse create a testcase using similar structs didn't show the problem.