Fix prologue/epilogue generation for ARM ISR routines.
authorNick Clifton <nickc@gcc.gnu.org>
Thu, 20 Dec 2001 19:03:37 +0000 (19:03 +0000)
committerNick Clifton <nickc@gcc.gnu.org>
Thu, 20 Dec 2001 19:03:37 +0000 (19:03 +0000)
Add test case to catch bugs reported in prologue/epilogue generation for ARM ISR routines.

From-SVN: r48210

gcc/ChangeLog
gcc/config/arm/arm.c
gcc/testsuite/gcc.misc-tests/arm-isr.c [new file with mode: 0644]
gcc/testsuite/gcc.misc-tests/arm-isr.exp [new file with mode: 0644]

index a069113277b433eac12bb2f01b8109b37ca99494..5e68161996efe520642e91e1e89244f0d86ceb3f 100644 (file)
@@ -1,3 +1,25 @@
+2001-12-20 Alan Shieh <ashieh@hkn.eecs.berkeley.edu>
+
+       * 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  <nickc@cambridge.redhat.com>
+
+       * 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  <rth@redhat.com>
 
        * varasm.c (assemble_real): Use REAL_VALUE_TO_x and assemble_integer
index 7a5f03ae4ec8a275e8179be74311ba9587bba19f..d4a81f3d170cfbccfe393e76d808cbc492a4d6d8 100644 (file)
@@ -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);
 }
 \f
-/* 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])
-         call_saved_registers += 4;
+      /* 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 ();
 
-      /* 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;
+      /* Now count the number of bits set in save_reg_mask.
+        For each set bit we need 4 bytes of stack space.  */
 
-      /* 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;
+      while (reg_mask)
+       {
+         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 (file)
index 0000000..f79e241
--- /dev/null
@@ -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 (file)
index 0000000..9d4a1b5
--- /dev/null
@@ -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
+