From 121308d427978857aade10a387e92f130d36cd75 Mon Sep 17 00:00:00 2001 From: Nick Clifton Date: Thu, 20 Dec 2001 19:03:37 +0000 Subject: [PATCH] Fix prologue/epilogue generation for ARM ISR routines. Add test case to catch bugs reported in prologue/epilogue generation for ARM ISR routines. From-SVN: r48210 --- gcc/ChangeLog | 22 +++++ gcc/config/arm/arm.c | 109 ++++++++++++++--------- gcc/testsuite/gcc.misc-tests/arm-isr.c | 48 ++++++++++ gcc/testsuite/gcc.misc-tests/arm-isr.exp | 27 ++++++ 4 files changed, 162 insertions(+), 44 deletions(-) create mode 100644 gcc/testsuite/gcc.misc-tests/arm-isr.c create mode 100644 gcc/testsuite/gcc.misc-tests/arm-isr.exp diff --git a/gcc/ChangeLog b/gcc/ChangeLog index a069113277b..5e68161996e 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,25 @@ +2001-12-20 Alan Shieh + + * config/arm/arm.c (arm_output_epilogue): Changed IP restore + to use ldmfd instead of ldmea. + * config/arm/arm.c (arm_compute_initial_elimination_offset): + Modified to reflect behavior of arm_expand_prologue when generating + interrupt handlers + +2001-12-20 Nick Clifton + + * config/arm/arm.c (arm_compute_save_reg0_reg12_mask): New + function. Compute which of registers r0 through r12 should be + saved onto the stack during a function's prologue. + (arm_compute_save_reg_mask): Use + arm_compute_save_reg0_reg12_mask. + (arm_compute_initial_elimination_offset): Use + arm_compute_save_reg0_reg12_mask. + + (arm_expand_prologue): Do not mark as save of the IP register + for an interrupt handler as being part of the frame creation + code. + 2001-12-20 Richard Henderson * varasm.c (assemble_real): Use REAL_VALUE_TO_x and assemble_integer diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c index 7a5f03ae4ec..d4a81f3d170 100644 --- a/gcc/config/arm/arm.c +++ b/gcc/config/arm/arm.c @@ -104,6 +104,7 @@ static void push_minipool_barrier PARAMS ((rtx, Hint)); static void push_minipool_fix PARAMS ((rtx, Hint, rtx *, Mmode, rtx)); static void note_invalid_constants PARAMS ((rtx, Hint)); static int current_file_function_operand PARAMS ((rtx)); +static Ulong arm_compute_save_reg0_reg12_mask PARAMS ((void)); static Ulong arm_compute_save_reg_mask PARAMS ((void)); static Ulong arm_isr_value PARAMS ((tree)); static Ulong arm_compute_func_type PARAMS ((void)); @@ -7001,38 +7002,20 @@ output_ascii_pseudo_op (stream, p, len) fputs ("\"\n", stream); } -/* Compute a bit mask of which registers need to be - saved on the stack for the current function. */ +/* Compute the register sabe mask for registers 0 through 12 + inclusive. This code is used by both arm_compute_save_reg_mask + and arm_compute_initial_elimination_offset. */ static unsigned long -arm_compute_save_reg_mask () +arm_compute_save_reg0_reg12_mask () { + unsigned long func_type = arm_current_func_type (); unsigned int save_reg_mask = 0; unsigned int reg; - unsigned long func_type = arm_current_func_type (); - - if (IS_NAKED (func_type)) - /* This should never really happen. */ - return 0; - - /* If we are creating a stack frame, then we must save the frame pointer, - IP (which will hold the old stack pointer), LR and the PC. */ - if (frame_pointer_needed) - save_reg_mask |= - (1 << ARM_HARD_FRAME_POINTER_REGNUM) - | (1 << IP_REGNUM) - | (1 << LR_REGNUM) - | (1 << PC_REGNUM); - - /* Volatile functions do not return, so there - is no need to save any other registers. */ - if (IS_VOLATILE (func_type)) - return save_reg_mask; if (IS_INTERRUPT (func_type)) { unsigned int max_reg; - /* Interrupt functions must not corrupt any registers, even call clobbered ones. If this is a leaf function we can just examine the registers used by the RTL, but @@ -7042,7 +7025,7 @@ arm_compute_save_reg_mask () if (ARM_FUNC_TYPE (func_type) == ARM_FT_FIQ) /* FIQ handlers have registers r8 - r12 banked, so we only need to check r0 - r7, Normal ISRs only - bank r14 and r15, so ew must check up to r12. + bank r14 and r15, so we must check up to r12. r13 is the stack pointer which is always preserved, so we do not need to consider it here. */ max_reg = 7; @@ -7077,6 +7060,38 @@ arm_compute_save_reg_mask () save_reg_mask |= 1 << PIC_OFFSET_TABLE_REGNUM; } + return save_reg_mask; +} + +/* Compute a bit mask of which registers need to be + saved on the stack for the current function. */ + +static unsigned long +arm_compute_save_reg_mask () +{ + unsigned int save_reg_mask = 0; + unsigned long func_type = arm_current_func_type (); + + if (IS_NAKED (func_type)) + /* This should never really happen. */ + return 0; + + /* If we are creating a stack frame, then we must save the frame pointer, + IP (which will hold the old stack pointer), LR and the PC. */ + if (frame_pointer_needed) + save_reg_mask |= + (1 << ARM_HARD_FRAME_POINTER_REGNUM) + | (1 << IP_REGNUM) + | (1 << LR_REGNUM) + | (1 << PC_REGNUM); + + /* Volatile functions do not return, so there + is no need to save any other registers. */ + if (IS_VOLATILE (func_type)) + return save_reg_mask; + + save_reg_mask |= arm_compute_save_reg0_reg12_mask (); + /* Decide if we need to save the link register. Interrupt routines have their own banked link register, so they never need to save it. @@ -7535,7 +7550,7 @@ arm_output_epilogue (really_return) if (IS_INTERRUPT (func_type)) /* Interrupt handlers will have pushed the IP onto the stack, so restore it now. */ - print_multi_reg (f, "ldmea\t%r", SP_REGNUM, 1 << IP_REGNUM); + print_multi_reg (f, "ldmfd\t%r", SP_REGNUM, 1 << IP_REGNUM); } else { @@ -7946,27 +7961,22 @@ arm_compute_initial_elimination_offset (from, to) call_saved_registers = 0; if (! IS_VOLATILE (func_type)) { + unsigned int reg_mask; unsigned int reg; - /* In theory we should check all of the hard registers to - see if they will be saved onto the stack. In practice - registers 11 upwards have special meanings and need to - be check individually. */ - for (reg = 0; reg <= 10; reg ++) - if (regs_ever_live[reg] && ! call_used_regs[reg]) + /* Makre sure that we compute which registers will be saved + on the stack using the same algorithm that is used by + arm_compute_save_reg_mask(). */ + reg_mask = arm_compute_save_reg0_reg12_mask (); + + /* Now count the number of bits set in save_reg_mask. + For each set bit we need 4 bytes of stack space. */ + + while (reg_mask) + { call_saved_registers += 4; - - /* Determine if register 11 will be clobbered. */ - if (! TARGET_APCS_FRAME - && ! frame_pointer_needed - && regs_ever_live[HARD_FRAME_POINTER_REGNUM] - && ! call_used_regs[HARD_FRAME_POINTER_REGNUM]) - call_saved_registers += 4; - - /* The PIC register is fixed, so if the function will - corrupt it, it has to be saved onto the stack. */ - if (flag_pic && regs_ever_live[PIC_OFFSET_TABLE_REGNUM]) - call_saved_registers += 4; + reg_mask = reg_mask & ~ (reg_mask & - reg_mask); + } if (regs_ever_live[LR_REGNUM] /* If a stack frame is going to be created, the LR will @@ -8097,7 +8107,18 @@ arm_expand_prologue () Creating a frame pointer however, corrupts the IP register, so we must push it first. */ insn = emit_multi_reg_push (1 << IP_REGNUM); - RTX_FRAME_RELATED_P (insn) = 1; + + /* Do not set RTX_FRAME_RELATED_P on this insn. + The dwarf stack unwinding code only wants to see one + stack decrement per function, and this is not it. If + this instruction is labeled as being part of the frame + creation sequence then dwarf2out_frame_debug_expr will + abort when it encounters the assignment of IP to FP + later on, since the use of SP here establishes SP as + the CFA register and not IP. + + Anyway this instruction is not really part of the stack + frame creation although it is part of the prologue. */ } else if (IS_NESTED (func_type)) { diff --git a/gcc/testsuite/gcc.misc-tests/arm-isr.c b/gcc/testsuite/gcc.misc-tests/arm-isr.c new file mode 100644 index 00000000000..f79e241633d --- /dev/null +++ b/gcc/testsuite/gcc.misc-tests/arm-isr.c @@ -0,0 +1,48 @@ +#ifndef __thumb__ +/* There used to be a couple of bugs in the ARM's prologue and epilogue + generation for ISR routines. The wrong epilogue instruction would be + generated to restore the IP register if it had to be pushed onto the + stack, and the wrong offset was being computed for local variables if + r0 - r3 had to be saved. This tests for both of these cases. */ + +int z = 9; + +int +bar (void) +{ + return z; +} + +int +foo (int a, int b, int c, int d, int e, int f, int g, int h) +{ + volatile int i = (a + b) - (g + h) + bar (); + volatile int j = (e + f) - (c + d); + + return a + b + c + d + e + f + g + h + i + j; +} + +int foo1 (int a, int b, int c, int d, int e, int f, int g, int h) __attribute__ ((interrupt ("IRQ"))); + +int +foo1 (int a, int b, int c, int d, int e, int f, int g, int h) +{ + volatile int i = (a + b) - (g + h) + bar (); + volatile int j = (e + f) - (c + d); + + return a + b + c + d + e + f + g + h + i + j; +} +#endif + +int +main (void) +{ +#ifndef __thumb__ + if (foo (1, 2, 3, 4, 5, 6, 7, 8) != 32) + abort (); + + if (foo1 (1, 2, 3, 4, 5, 6, 7, 8) != 32) + abort (); +#endif + exit (0); +} diff --git a/gcc/testsuite/gcc.misc-tests/arm-isr.exp b/gcc/testsuite/gcc.misc-tests/arm-isr.exp new file mode 100644 index 00000000000..9d4a1b5df4f --- /dev/null +++ b/gcc/testsuite/gcc.misc-tests/arm-isr.exp @@ -0,0 +1,27 @@ +# Copyright (C) 2001 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +# This file is based on a bug report submitted to gcc-bugs: + +# Load support procs. +load_lib gcc-dg.exp + +dg-init +if {[istarget "*arm-*-*"] || [istarget "xscale-*-*"]} { + dg-runtest "$srcdir/$subdir/arm-isr.c" "" "" +} +dg-finish +