Fix PR77933: stack corruption on ARM when using high registers and LR
authorThomas Preud'homme <thomas.preudhomme@arm.com>
Thu, 17 Nov 2016 20:12:13 +0000 (20:12 +0000)
committerThomas Preud'homme <thopre01@gcc.gnu.org>
Thu, 17 Nov 2016 20:12:13 +0000 (20:12 +0000)
2016-11-17  Thomas Preud'homme  <thomas.preudhomme@arm.com>

    gcc/
    PR target/77933
    * config/arm/arm.c (thumb1_expand_prologue): Distinguish between lr
    being live in the function and lr needing to be saved.  Distinguish
    between already saved pushable registers and registers to push.
    Check for LR being an available pushable register.

    gcc/testsuite/
    PR target/77933
    * gcc.target/arm/pr77933-1.c: New test.
    * gcc.target/arm/pr77933-2.c: Likewise.

From-SVN: r242559

gcc/ChangeLog
gcc/config/arm/arm.c
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.target/arm/pr77933-1.c [new file with mode: 0644]
gcc/testsuite/gcc.target/arm/pr77933-2.c [new file with mode: 0644]

index 86c664bf7a99931a741de4373511c459a4534c01..1cf436d161276282d924664e217ccd3409131712 100644 (file)
@@ -1,3 +1,11 @@
+2016-11-17  Thomas Preud'homme  <thomas.preudhomme@arm.com>
+
+       PR target/77933
+       * config/arm/arm.c (thumb1_expand_prologue): Distinguish between lr
+       being live in the function and lr needing to be saved.  Distinguish
+       between already saved pushable registers and registers to push.
+       Check for LR being an available pushable register.
+
 2016-11-17  Aaron Sawdey  <acsawdey@linux.vnet.ibm.com>
 
        * config/i386/i386.md (cmpstrnsi): New test to bail out if neither
index 239117ff7da1667c126c73dcc61536fec33f184c..d7ce87c27ee33b0f14c1a693a81e1de19b610d41 100644 (file)
@@ -23593,6 +23593,7 @@ thumb1_expand_prologue (void)
   unsigned long live_regs_mask;
   unsigned long l_mask;
   unsigned high_regs_pushed = 0;
+  bool lr_needs_saving;
 
   func_type = arm_current_func_type ();
 
@@ -23615,6 +23616,7 @@ thumb1_expand_prologue (void)
 
   offsets = arm_get_frame_offsets ();
   live_regs_mask = offsets->saved_regs_mask;
+  lr_needs_saving = live_regs_mask & (1 << LR_REGNUM);
 
   /* Extract a mask of the ones we can give to the Thumb's push instruction.  */
   l_mask = live_regs_mask & 0x40ff;
@@ -23681,6 +23683,7 @@ thumb1_expand_prologue (void)
        {
          insn = thumb1_emit_multi_reg_push (l_mask, l_mask);
          RTX_FRAME_RELATED_P (insn) = 1;
+         lr_needs_saving = false;
 
          offset = bit_count (l_mask) * UNITS_PER_WORD;
        }
@@ -23745,12 +23748,13 @@ thumb1_expand_prologue (void)
      be a push of LR and we can combine it with the push of the first high
      register.  */
   else if ((l_mask & 0xff) != 0
-          || (high_regs_pushed == 0 && l_mask))
+          || (high_regs_pushed == 0 && lr_needs_saving))
     {
       unsigned long mask = l_mask;
       mask |= (1 << thumb1_extra_regs_pushed (offsets, true)) - 1;
       insn = thumb1_emit_multi_reg_push (mask, mask);
       RTX_FRAME_RELATED_P (insn) = 1;
+      lr_needs_saving = false;
     }
 
   if (high_regs_pushed)
@@ -23768,7 +23772,9 @@ thumb1_expand_prologue (void)
       /* Here we need to mask out registers used for passing arguments
         even if they can be pushed.  This is to avoid using them to stash the high
         registers.  Such kind of stash may clobber the use of arguments.  */
-      pushable_regs = l_mask & (~arg_regs_mask) & 0xff;
+      pushable_regs = l_mask & (~arg_regs_mask);
+      if (lr_needs_saving)
+       pushable_regs &= ~(1 << LR_REGNUM);
 
       if (pushable_regs == 0)
        pushable_regs = 1 << thumb_find_work_register (live_regs_mask);
@@ -23776,8 +23782,9 @@ thumb1_expand_prologue (void)
       while (high_regs_pushed > 0)
        {
          unsigned long real_regs_mask = 0;
+         unsigned long push_mask = 0;
 
-         for (regno = LAST_LO_REGNUM; regno >= 0; regno --)
+         for (regno = LR_REGNUM; regno >= 0; regno --)
            {
              if (pushable_regs & (1 << regno))
                {
@@ -23786,6 +23793,7 @@ thumb1_expand_prologue (void)
 
                  high_regs_pushed --;
                  real_regs_mask |= (1 << next_hi_reg);
+                 push_mask |= (1 << regno);
 
                  if (high_regs_pushed)
                    {
@@ -23795,23 +23803,20 @@ thumb1_expand_prologue (void)
                          break;
                    }
                  else
-                   {
-                     pushable_regs &= ~((1 << regno) - 1);
-                     break;
-                   }
+                   break;
                }
            }
 
          /* If we had to find a work register and we have not yet
             saved the LR then add it to the list of regs to push.  */
-         if (l_mask == (1 << LR_REGNUM))
+         if (lr_needs_saving)
            {
-             pushable_regs |= l_mask;
-             real_regs_mask |= l_mask;
-             l_mask = 0;
+             push_mask |= 1 << LR_REGNUM;
+             real_regs_mask |= 1 << LR_REGNUM;
+             lr_needs_saving = false;
            }
 
-         insn = thumb1_emit_multi_reg_push (pushable_regs, real_regs_mask);
+         insn = thumb1_emit_multi_reg_push (push_mask, real_regs_mask);
          RTX_FRAME_RELATED_P (insn) = 1;
        }
     }
index cce390bb7448d9dca7a0ab1159804afeb64bcefd..55f7d837aad2f735dc4bc30478040598f183fd4e 100644 (file)
@@ -1,3 +1,9 @@
+2016-11-17  Thomas Preud'homme  <thomas.preudhomme@arm.com>
+
+       PR target/77933
+       * gcc.target/arm/pr77933-1.c: New test.
+       * gcc.target/arm/pr77933-2.c: Likewise.
+
 2016-11-17  Jakub Jelinek  <jakub@redhat.com>
 
        PR middle-end/78201
diff --git a/gcc/testsuite/gcc.target/arm/pr77933-1.c b/gcc/testsuite/gcc.target/arm/pr77933-1.c
new file mode 100644 (file)
index 0000000..95cf68e
--- /dev/null
@@ -0,0 +1,46 @@
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+__attribute__ ((noinline, noclone)) void
+clobber_lr_and_highregs (void)
+{
+  __asm__ volatile ("" : : : "r8", "r9", "lr");
+}
+
+int
+main (void)
+{
+  int ret;
+
+  __asm volatile ("mov\tr4, #0xf4\n\t"
+                 "mov\tr5, #0xf5\n\t"
+                 "mov\tr6, #0xf6\n\t"
+                 "mov\tr7, #0xf7\n\t"
+                 "mov\tr0, #0xf8\n\t"
+                 "mov\tr8, r0\n\t"
+                 "mov\tr0, #0xfa\n\t"
+                 "mov\tr10, r0"
+                 : : : "r0", "r4", "r5", "r6", "r7", "r8", "r10");
+
+  clobber_lr_and_highregs ();
+
+  __asm volatile ("cmp\tr4, #0xf4\n\t"
+                 "bne\tfail\n\t"
+                 "cmp\tr5, #0xf5\n\t"
+                 "bne\tfail\n\t"
+                 "cmp\tr6, #0xf6\n\t"
+                 "bne\tfail\n\t"
+                 "cmp\tr7, #0xf7\n\t"
+                 "bne\tfail\n\t"
+                 "mov\tr0, r8\n\t"
+                 "cmp\tr0, #0xf8\n\t"
+                 "bne\tfail\n\t"
+                 "mov\tr0, r10\n\t"
+                 "cmp\tr0, #0xfa\n\t"
+                 "bne\tfail\n\t"
+                 "mov\t%0, #1\n"
+                 "fail:\n\t"
+                 "sub\tr0, #1"
+                 : "=r" (ret) : :);
+  return ret;
+}
diff --git a/gcc/testsuite/gcc.target/arm/pr77933-2.c b/gcc/testsuite/gcc.target/arm/pr77933-2.c
new file mode 100644 (file)
index 0000000..9028c4f
--- /dev/null
@@ -0,0 +1,47 @@
+/* { dg-do run } */
+/* { dg-skip-if "" { ! { arm_thumb1_ok || arm_thumb2_ok } } } */
+/* { dg-options "-mthumb -O2 -mtpcs-leaf-frame" } */
+
+__attribute__ ((noinline, noclone)) void
+clobber_lr_and_highregs (void)
+{
+  __asm__ volatile ("" : : : "r8", "r9", "lr");
+}
+
+int
+main (void)
+{
+  int ret;
+
+  __asm volatile ("mov\tr4, #0xf4\n\t"
+                 "mov\tr5, #0xf5\n\t"
+                 "mov\tr6, #0xf6\n\t"
+                 "mov\tr7, #0xf7\n\t"
+                 "mov\tr0, #0xf8\n\t"
+                 "mov\tr8, r0\n\t"
+                 "mov\tr0, #0xfa\n\t"
+                 "mov\tr10, r0"
+                 : : : "r0", "r4", "r5", "r6", "r7", "r8", "r10");
+
+  clobber_lr_and_highregs ();
+
+  __asm volatile ("cmp\tr4, #0xf4\n\t"
+                 "bne\tfail\n\t"
+                 "cmp\tr5, #0xf5\n\t"
+                 "bne\tfail\n\t"
+                 "cmp\tr6, #0xf6\n\t"
+                 "bne\tfail\n\t"
+                 "cmp\tr7, #0xf7\n\t"
+                 "bne\tfail\n\t"
+                 "mov\tr0, r8\n\t"
+                 "cmp\tr0, #0xf8\n\t"
+                 "bne\tfail\n\t"
+                 "mov\tr0, r10\n\t"
+                 "cmp\tr0, #0xfa\n\t"
+                 "bne\tfail\n\t"
+                 "mov\t%0, #1\n"
+                 "fail:\n\t"
+                 "sub\tr0, #1"
+                 : "=r" (ret) : :);
+  return ret;
+}