[THUMB2] Frame pointer for arm with THUMB2 mode. 52/188052/2 accepted/tizen/base/20181102.125713 submit/tizen_base/20181018.065945 submit/tizen_base/20181101.054916
authorDenis Khalikov <d.khalikov@partner.samsung.com>
Thu, 30 Aug 2018 15:57:41 +0000 (18:57 +0300)
committerVyacheslav Barinov <v.barinov@samsung.com>
Mon, 8 Oct 2018 13:48:20 +0000 (13:48 +0000)
Set frame pointer to the predictable location in the stack frame
for arm with THUMB2 mode.

Denis Khalikov  <d.khalikov@partner.samsung.com>

  * config/arm/arm.c (arm_emit_multi_reg_pop_no_return): New function.
  (arm_compute_initial_elimination_offset): Add support for
  TARGET_THUMB_STACK_UNWIND.
  (arm_expand_prologue): Emit function prologue related to
  TARGET_THUMB_STACK_UNWIND.
  (thumb2_expand_return): Emit function epilogue related to
  TARGET_THUMB_STACK_UNWIND.
  (arm_expand_epilogue): Emit function epilogue related to
  TARGET_THUMB_STACK_UNWIND.
  * config/arm/arm.h (TARGET_THUMB_STACK_UNWIND): New define.
  (INITIAL_ELIMINATION_OFFSET): Add support for
  TARGET_THUMB_STACK_UNWIND.
  * config/arm/arm.opt: Add compile-time option THUMB_FP.
  * gcc/testsuite/c-c++-common/asan/fast-unwind-thumb.c: New tests.

Change-Id: I45a68a1216f3115c5199e5cea14c344fabe11fc1

gcc/config/arm/arm.c
gcc/config/arm/arm.h
gcc/config/arm/arm.opt
gcc/testsuite/c-c++-common/asan/fast-unwind-thumb.c [new file with mode: 0644]

index f48fe56..b199529 100644 (file)
@@ -20094,6 +20094,70 @@ arm_emit_multi_reg_pop (unsigned long saved_regs_mask)
                                 stack_pointer_rtx, stack_pointer_rtx);
 }
 
+/* This function hepls to handle situation when we need to make
+   multiple pop, but does not have lr or pc registers inside
+   saved_regs_mask and can not add cfa_notes.
+   This function is based on arm_emit_multi_reg_pop.  */
+static void
+arm_emit_multi_reg_pop_no_return (unsigned long saved_regs_mask)
+{
+  int num_regs = 0;
+  int i, j;
+  rtx par;
+  rtx dwarf = NULL_RTX;
+  rtx tmp, reg;
+  int emit_update = 1;
+
+  for (i = 0; i <= LAST_ARM_REGNUM; i++)
+    if (saved_regs_mask & (1 << i))
+      num_regs++;
+
+  gcc_assert (num_regs && num_regs <= 16);
+
+  /* If SP is in reglist, then we don't emit SP update insn.  */
+  emit_update = (saved_regs_mask & (1 << SP_REGNUM)) ? 0 : 1;
+
+  /* The parallel needs to hold num_regs SETs
+     and one SET for the stack update.  */
+  par = gen_rtx_PARALLEL (VOIDmode,
+                         rtvec_alloc (num_regs + emit_update));
+  if (emit_update)
+    {
+      /* Increment the stack pointer, based on there being
+        num_regs 4-byte registers to restore.  */
+      tmp
+       = gen_rtx_SET (stack_pointer_rtx,
+                      plus_constant (Pmode, stack_pointer_rtx, 4 * num_regs));
+      RTX_FRAME_RELATED_P (tmp) = 1;
+      XVECEXP (par, 0, 0) = tmp;
+    }
+
+  for (j = 0, i = 0; j < num_regs; i++)
+    if (saved_regs_mask & (1 << i))
+      {
+       reg = gen_rtx_REG (SImode, i);
+       if ((num_regs == 1) && emit_update)
+         {
+           /* Emit single load with writeback.  */
+           tmp = gen_frame_mem (SImode,
+                                gen_rtx_POST_INC (Pmode, stack_pointer_rtx));
+           tmp = emit_insn (gen_rtx_SET (reg, tmp));
+           REG_NOTES (tmp) = alloc_reg_note (REG_CFA_RESTORE, reg, dwarf);
+           return;
+         }
+
+       tmp = gen_rtx_SET (
+         reg, gen_frame_mem (SImode,
+                             plus_constant (Pmode, stack_pointer_rtx, 4 * j)));
+       RTX_FRAME_RELATED_P (tmp) = 1;
+       XVECEXP (par, 0, j + emit_update) = tmp;
+       dwarf = alloc_reg_note (REG_CFA_RESTORE, reg, dwarf);
+       j++;
+      }
+
+  par = emit_insn (par);
+}
+
 /* Generate and emit an insn pattern that we will recognize as a pop_multi
    of NUM_REGS consecutive VFP regs, starting at FIRST_REG.
 
@@ -20749,6 +20813,10 @@ arm_compute_initial_elimination_offset (unsigned int from, unsigned int to)
       switch (to)
        {
        case THUMB_HARD_FRAME_POINTER_REGNUM:
+         if (TARGET_THUMB_STACK_UNWIND)
+           /* In this case the hard frame pointer points to the lr in the stack
+              frame. So offset is frame - saved_args.  */
+           return offsets->frame - offsets->saved_args;
          return 0;
 
        case FRAME_POINTER_REGNUM:
@@ -20775,6 +20843,11 @@ arm_compute_initial_elimination_offset (unsigned int from, unsigned int to)
       switch (to)
        {
        case THUMB_HARD_FRAME_POINTER_REGNUM:
+         if (TARGET_THUMB_STACK_UNWIND)
+           /*  The hard frame_pointer points to the top entry in the
+               stack frame. The soft frame pointer points to the bottom
+               entry in the stack.  */
+           return offsets->frame - offsets->soft_frame;
          return 0;
 
        case ARM_HARD_FRAME_POINTER_REGNUM:
@@ -21342,7 +21415,7 @@ arm_expand_prologue (void)
   if ((func_type == ARM_FT_ISR || func_type == ARM_FT_FIQ)
       && (live_regs_mask & (1 << LR_REGNUM)) != 0
       && !(frame_pointer_needed && TARGET_APCS_FRAME)
-      && TARGET_ARM)
+      && (TARGET_ARM || TARGET_THUMB_STACK_UNWIND))
     {
       rtx lr = gen_rtx_REG (SImode, LR_REGNUM);
 
@@ -21387,22 +21460,59 @@ arm_expand_prologue (void)
                    && !IS_INTERRUPT (func_type))
            arm_emit_strd_push (live_regs_mask);
           else
-            {
+           {
              insn = emit_multi_reg_push (live_regs_mask, live_regs_mask);
-              RTX_FRAME_RELATED_P (insn) = 1;
-            }
-        }
+             RTX_FRAME_RELATED_P (insn) = 1;
+           }
+       }
       else
-        {
-         insn = emit_multi_reg_push (live_regs_mask, dwarf_regs_mask);
-          RTX_FRAME_RELATED_P (insn) = 1;
-        }
+       {
+         /* In case we want to have frame pointer register to follow
+            lr register on the stack, we need to modify stack layout.
+            For example we have to save on the stack following registers
+            {r4, ... r11, lr}, but with thumb mode frame pointer
+            register is r7, so this layout is not correct. We will modify it
+            from
+            push {r4, ... r11, lr}
+            to
+            push {r4, ... r7, lr}
+            push {r8, ..., r11}
+            Registers should be pushed in ascending order.  */
+         if (TARGET_THUMB_STACK_UNWIND && (live_regs_mask & (1 << 8))
+             /* Check for non-leaf fucntion.  */
+             && (live_regs_mask & (1 << LR_REGNUM)))
+           {
+             unsigned long first_regs_mask, dwarf_first_regs_mask,
+               second_regs_mask, dwarf_second_regs_mask;
+
+             /* Save info about first 8 registers from r0 to r7 and
+                add lr for this mask.  */
+             first_regs_mask = dwarf_first_regs_mask
+               = (live_regs_mask & 0xff) | (1 << LR_REGNUM);
+             /* Add all left registers from r8 and delete lr.  */
+             second_regs_mask = dwarf_second_regs_mask
+               = (live_regs_mask & ~0xff) & ~(1 << LR_REGNUM);
+             insn
+               = emit_multi_reg_push (first_regs_mask, dwarf_first_regs_mask);
+             RTX_FRAME_RELATED_P (insn) = 1;
+             insn = emit_multi_reg_push (second_regs_mask,
+                                         dwarf_second_regs_mask);
+             RTX_FRAME_RELATED_P (insn) = 1;
+           }
+         else
+           {
+             insn = emit_multi_reg_push (live_regs_mask, dwarf_regs_mask);
+             RTX_FRAME_RELATED_P (insn) = 1;
+           }
+       }
     }
 
   if (! IS_VOLATILE (func_type))
     saved_regs += arm_save_coproc_regs ();
 
-  if (frame_pointer_needed && TARGET_ARM)
+  /* Set frame pointer register (r7) to lr on the stack
+     for TARGET_THUMB_STACK_UNWIND.  */
+  if (frame_pointer_needed && (TARGET_ARM || TARGET_THUMB_STACK_UNWIND))
     {
       /* Create the new frame pointer.  */
       if (TARGET_APCS_FRAME)
@@ -21492,7 +21602,7 @@ arm_expand_prologue (void)
     }
 
 
-  if (frame_pointer_needed && TARGET_THUMB2)
+  if (frame_pointer_needed && (TARGET_THUMB2 && !TARGET_THUMB_FP))
     thumb_set_frame_pointer (offsets);
 
   if (flag_pic && arm_pic_register != INVALID_REGNUM)
@@ -24929,11 +25039,26 @@ thumb2_expand_return (bool simple_return)
           emit_jump_insn (par);
         }
       else
-        {
-          saved_regs_mask &= ~ (1 << LR_REGNUM);
-          saved_regs_mask |=   (1 << PC_REGNUM);
-          arm_emit_multi_reg_pop (saved_regs_mask);
-        }
+       {
+         /* We have made multiple push for thumb, when frame pointer
+            is needed. So, make multiple pop in the same order.  */
+         if (TARGET_THUMB_STACK_UNWIND && (saved_regs_mask & (1 << 8)))
+           {
+             unsigned long first_regs_mask = saved_regs_mask & 0xff;
+             unsigned long second_regs_mask = saved_regs_mask & ~0xff;
+
+             first_regs_mask |= (1 << PC_REGNUM);
+             second_regs_mask &= ~((1 << PC_REGNUM) | (1 << LR_REGNUM));
+             arm_emit_multi_reg_pop_no_return (second_regs_mask);
+             arm_emit_multi_reg_pop (first_regs_mask);
+           }
+         else
+           {
+             saved_regs_mask &= ~(1 << LR_REGNUM);
+             saved_regs_mask |= (1 << PC_REGNUM);
+             arm_emit_multi_reg_pop (saved_regs_mask);
+           }
+       }
     }
   else
     {
@@ -25233,9 +25358,15 @@ arm_expand_epilogue (bool really_return)
         }
       else
         {
-          /* In Thumb-2 mode, the frame pointer points to the last saved
-             register.  */
-         amount = offsets->locals_base - offsets->saved_regs;
+         /* For TARGET_THUMB_STACK_UNWIND the frame pointer points to the
+            bottom of the stack.  */
+         if (TARGET_THUMB_STACK_UNWIND)
+           amount = offsets->frame - offsets->saved_regs;
+         else
+           /* In Thumb-2 mode, the frame pointer points to the last saved
+               register.  */
+           amount = offsets->locals_base - offsets->saved_regs;
+
          if (amount)
            {
              insn = emit_insn (gen_addsi3 (hard_frame_pointer_rtx,
@@ -25384,10 +25515,10 @@ arm_expand_epilogue (bool really_return)
         }
       else
         {
-          if (TARGET_LDRD
+          if (TARGET_LDRD
              && current_tune->prefer_ldrd_strd
-              && !optimize_function_for_size_p (cfun))
-            {
+              && !optimize_function_for_size_p (cfun))
+            {
               if (TARGET_THUMB2)
                 thumb2_emit_ldrd_pop (saved_regs_mask);
               else if (TARGET_ARM && !IS_INTERRUPT (func_type))
@@ -25395,9 +25526,34 @@ arm_expand_epilogue (bool really_return)
               else
                 arm_emit_multi_reg_pop (saved_regs_mask);
             }
-          else
-            arm_emit_multi_reg_pop (saved_regs_mask);
-        }
+         else
+           {
+             /* Make multiple pop in the same order as we done multiple push.
+              */
+             if (TARGET_THUMB_STACK_UNWIND && (saved_regs_mask & (1 << 8))
+                 && ((saved_regs_mask & (1 << LR_REGNUM))
+                     || (saved_regs_mask & (1 << PC_REGNUM))))
+               {
+                 unsigned long first_regs_mask = saved_regs_mask & 0xff;
+                 unsigned long second_regs_mask = saved_regs_mask & ~0xff;
+
+                 if (saved_regs_mask & (1 << PC_REGNUM))
+                   {
+                     first_regs_mask |= (1 << PC_REGNUM);
+                     second_regs_mask &= ~(1 << PC_REGNUM);
+                   }
+                 else
+                   {
+                     first_regs_mask |= (1 << LR_REGNUM);
+                     second_regs_mask &= ~(1 << LR_REGNUM);
+                   }
+                 arm_emit_multi_reg_pop_no_return (second_regs_mask);
+                 arm_emit_multi_reg_pop (first_regs_mask);
+               }
+             else
+               arm_emit_multi_reg_pop (saved_regs_mask);
+           }
+       }
 
       if (return_in_pc)
         return;
index dea7720..3d9b926 100644 (file)
@@ -127,6 +127,8 @@ extern void (*arm_lang_output_object_attributes_hook)(void);
 #define TARGET_THUMB1_P(flags) (TARGET_THUMB_P (flags) && !arm_arch_thumb2)
 #define TARGET_THUMB2_P(flags) (TARGET_THUMB_P (flags) && arm_arch_thumb2)
 #define TARGET_32BIT_P(flags)  (TARGET_ARM_P (flags) || TARGET_THUMB2_P (flags))
+#define TARGET_THUMB_STACK_UNWIND                                              \
+  (TARGET_THUMB2 && TARGET_THUMB_FP && frame_pointer_needed && arm_arch7)
 
 /* Run-time Target Specification.  */
 #define TARGET_SOFT_FLOAT              (arm_float_abi == ARM_FLOAT_ABI_SOFT)
@@ -1644,10 +1646,10 @@ typedef struct
 
 /* Define the offset between two registers, one to be eliminated, and the
    other its replacement, at the start of a routine.  */
-#define INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET)                   \
-  if (TARGET_ARM)                                                      \
-    (OFFSET) = arm_compute_initial_elimination_offset (FROM, TO);      \
-  else                                                                 \
+#define INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET)                           \
+  if (TARGET_ARM || TARGET_THUMB_STACK_UNWIND)                                 \
+    (OFFSET) = arm_compute_initial_elimination_offset (FROM, TO);              \
+  else                                                                         \
     (OFFSET) = thumb_compute_initial_elimination_offset (FROM, TO)
 
 /* Special case handling of the location of arguments passed on the stack.  */
index c3c4735..73f40e8 100644 (file)
@@ -193,6 +193,10 @@ mthumb
 Target Report RejectNegative Mask(THUMB) Save
 Generate code for Thumb state.
 
+mthumb-fp
+Target Report Mask(THUMB_FP)
+Generate frame pointer which points to the lr register on the stack.
+
 mthumb-interwork
 Target Report Mask(INTERWORK)
 Support calls between Thumb and ARM instruction sets.
diff --git a/gcc/testsuite/c-c++-common/asan/fast-unwind-thumb.c b/gcc/testsuite/c-c++-common/asan/fast-unwind-thumb.c
new file mode 100644 (file)
index 0000000..c301ba8
--- /dev/null
@@ -0,0 +1,49 @@
+/* { dg-do compile { target arm*-*-*} } */
+/* { dg-options "-fno-builtin-malloc -fno-builtin-free -fno-builtin-memset
+ * -fno-omit-frame-pointer -mthumb -Wa,-mimplicit-it=thumb -mthumb-fp" } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O0" } } */
+/* { dg-shouldfail "asan" } */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void *memset (void *, int, __SIZE_TYPE__);
+void *malloc (__SIZE_TYPE__);
+void free (void *);
+
+const char *
+__asan_default_options () {
+  return "fast_unwind_on_fatal=true";
+}
+
+#ifdef __cplusplus
+}
+#endif
+volatile int ten = 10;
+__attribute__ ((noinline)) int
+foo ()
+{
+  char *x = (char *) malloc (10);
+  memset(x, 0, 10);
+  int res = x[ten]; /* BOOOM */
+  free(x);
+  return res;
+}
+
+__attribute__ ((noinline)) int
+bar ()
+{
+  return foo ();
+}
+
+int
+main (int argc, char **argv)
+{
+  return bar ();
+}
+
+/* { dg-output "READ of size 1 at 0x\[0-9a-f\]+ thread T0.*(\n|\r\n|\r)" } */
+/* { dg-output "    #0 0x\[0-9a-f\]+ +(in \[^\n\r]*foo\[^\n\r]*fast-unwind.c:29|\[(\]).*(\n|\r\n|\r)" } */
+/* { dg-output "    #1 0x\[0-9a-f\]+ +(in \[^\n\r]*bar\[^\n\r]*fast-unwind.c:37|\[(\]).*(\n|\r\n|\r)" } */
+/* { dg-output "    #2 0x\[0-9a-f\]+ +(in _*main (\[^\n\r]*fast-unwind.c:43|\[^\n\r]*:0)|\[(\]).*(\n|\r\n|\r)" } */