]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
Describe unwinding for realigned frames explicitly.
authorRichard Henderson <rth@redhat.com>
Wed, 4 Aug 2010 23:58:26 +0000 (16:58 -0700)
committerRichard Henderson <rth@gcc.gnu.org>
Wed, 4 Aug 2010 23:58:26 +0000 (16:58 -0700)
We had been relying on some extremely fragile code within
dwarf2out in order to guess what to do with aligned stack
frames, which broke when we decided to perform the stores
to the aligned stack frame via EBP instead of ESP.

Instead, emit the appropriate unwinding instructions from
the backend.  This requires adding a new reg-note in order
to describe a register save at an arbitrary address.

From-SVN: r162889

gcc/ChangeLog
gcc/config/i386/i386.c
gcc/config/i386/i386.h
gcc/dwarf2out.c
gcc/reg-notes.def

index cc867ffccf544a666f5af310bfb81701e3baefdb..9866d8e8e749af005ec2af0063ba91ca23e517ce 100644 (file)
@@ -1,5 +1,16 @@
 2010-08-04  Richard Henderson  <rth@redhat.com>
 
+       * reg-notes.def (CFA_EXPRESSION): New.
+       * dwarf2out.c (dwarf2out_frame_debug): Handle it.
+       (dwarf2out_frame_debug_cfa_expression): New.
+       (dwarf2out_frame_debug_def_cfa): Handle simple MEMs.
+
+       * config/i386/i386.h (struct machine_frame_state): Add realigned flag.
+       * config/i386/i386.c (ix86_expand_prologue): Set it.
+       (ix86_expand_epilogue): Clear it.
+       (ix86_emit_save_reg_using_mov): For registers saved in a realigned
+       context, add REG_CFA_EXPRESSION notes.
+
        * config/i386/i386.h (struct machine_frame_state): Rename from
        machine_cfa_state.  Add members tracking SP and FP regardless
        of the current CFA register.  
index 77dbef36ddc0ed052249a06c24bcac083511bbc9..96897be8d94ee0a435b7a11959d1745f2de83f6f 100644 (file)
@@ -8616,8 +8616,9 @@ static void
 ix86_emit_save_reg_using_mov (enum machine_mode mode, unsigned int regno,
                              HOST_WIDE_INT cfa_offset)
 {
+  struct machine_function *m = cfun->machine;
   rtx reg = gen_rtx_REG (mode, regno);
-  rtx mem, addr, insn;
+  rtx mem, addr, base, insn;
 
   addr = choose_baseaddr (cfa_offset);
   mem = gen_frame_mem (mode, addr);
@@ -8626,20 +8627,64 @@ ix86_emit_save_reg_using_mov (enum machine_mode mode, unsigned int regno,
   set_mem_align (mem, GET_MODE_ALIGNMENT (mode));
 
   insn = emit_move_insn (mem, reg);
+  RTX_FRAME_RELATED_P (insn) = 1;
+
+  base = addr;
+  if (GET_CODE (base) == PLUS)
+    base = XEXP (base, 0);
+  gcc_checking_assert (REG_P (base));
+
+  /* When saving registers into a re-aligned local stack frame, avoid
+     any tricky guessing by dwarf2out.  */
+  if (m->fs.realigned)
+    {
+      if (stack_realign_drap && regno == REGNO (crtl->drap_reg))
+       {
+         /* A bit of a hack.  We force the DRAP register to be saved in
+            the re-aligned stack frame, which provides us with a copy
+            of the CFA that will last past the prologue.  Install it.  */
+         gcc_checking_assert (cfun->machine->fs.fp_valid);
+         addr = plus_constant (hard_frame_pointer_rtx,
+                               cfun->machine->fs.fp_offset - cfa_offset);
+         mem = gen_rtx_MEM (mode, addr);
+         add_reg_note (insn, REG_CFA_DEF_CFA, mem);
+       }
+      else if (stack_realign_fp)
+       {
+         /* The stack pointer may or may not be varying within the
+            function.  If it is, then we can't use it as a stable
+            reference to the locations within the frame.  Instead,
+            simply compute the location of the aligned frame from
+            the frame pointer.  */
+         addr = GEN_INT (-crtl->stack_alignment_needed / BITS_PER_UNIT);
+         addr = gen_rtx_AND (Pmode, hard_frame_pointer_rtx, addr);
+         addr = plus_constant (addr, -cfa_offset);
+         mem = gen_rtx_MEM (mode, addr);
+         add_reg_note (insn, REG_CFA_EXPRESSION,
+                       gen_rtx_SET (VOIDmode, mem, reg));
+       }
+      else
+       {
+         /* The frame pointer is a stable reference within the
+            aligned frame.  Use it.  */
+         gcc_checking_assert (cfun->machine->fs.fp_valid);
+         addr = plus_constant (hard_frame_pointer_rtx,
+                               cfun->machine->fs.fp_offset - cfa_offset);
+         mem = gen_rtx_MEM (mode, addr);
+         add_reg_note (insn, REG_CFA_EXPRESSION,
+                       gen_rtx_SET (VOIDmode, mem, reg));
+       }
+    }
 
   /* The memory may not be relative to the current CFA register,
      which means that we may need to generate a new pattern for
      use by the unwind info.  */
-  if (GET_CODE (addr) == PLUS)
-    addr = XEXP (addr, 0);
-  if (addr != cfun->machine->fs.cfa_reg)
+  else if (base != m->fs.cfa_reg)
     {
-      addr = plus_constant (cfun->machine->fs.cfa_reg,
-                           cfun->machine->fs.cfa_offset - cfa_offset);
+      addr = plus_constant (m->fs.cfa_reg, m->fs.cfa_offset - cfa_offset);
       mem = gen_rtx_MEM (mode, addr);
       add_reg_note (insn, REG_CFA_OFFSET, gen_rtx_SET (VOIDmode, mem, reg));
     }
-  RTX_FRAME_RELATED_P (insn) = 1;
 }
 
 /* Emit code to save registers using MOV insns.
@@ -9511,6 +9556,7 @@ ix86_expand_prologue (void)
       /* For the purposes of frame and register save area addressing,
         we've started over with a new frame.  */
       m->fs.sp_offset = INCOMING_FRAME_SP_OFFSET;
+      m->fs.realigned = true;
     }
 
   if (frame_pointer_needed && !m->fs.fp_valid)
@@ -9546,8 +9592,9 @@ ix86_expand_prologue (void)
       /* ??? There's no need to place the register save area into the
         aligned local stack frame.  We should do this later, after
         the register saves.  */
-      m->fs.fp_valid = false;
       m->fs.sp_offset = (m->fs.sp_offset + align_bytes - 1) & -align_bytes;
+      m->fs.fp_valid = false;
+      m->fs.realigned = true;
     }
 
   allocate = frame.to_allocate + frame.nsseregs * 16 + frame.padding0;
@@ -10070,6 +10117,7 @@ ix86_expand_epilogue (int style)
                 frame.  Thus the FP is suddenly valid and the SP isn't.  */
              m->fs.fp_valid = true;
              m->fs.sp_valid = false;
+             m->fs.realigned = false;
            }
 
          /* Leave results in shorter dependency chains on CPUs that are
@@ -10122,6 +10170,7 @@ ix86_expand_epilogue (int style)
                 frame.  Thus the FP is suddenly valid and the SP isn't.  */
              m->fs.fp_valid = true;
              m->fs.sp_valid = false;
+             m->fs.realigned = false;
            }
 
          /* Leave results in shorter dependency chains on CPUs that are
@@ -10162,6 +10211,7 @@ ix86_expand_epilogue (int style)
       m->fs.cfa_reg = stack_pointer_rtx;
       m->fs.cfa_offset = param_ptr_offset;
       m->fs.sp_offset = param_ptr_offset;
+      m->fs.realigned = false;
 
       add_reg_note (insn, REG_CFA_DEF_CFA,
                    gen_rtx_PLUS (Pmode, stack_pointer_rtx,
@@ -10196,6 +10246,7 @@ ix86_expand_epilogue (int style)
   gcc_assert (m->fs.sp_offset == UNITS_PER_WORD);
   gcc_assert (m->fs.sp_valid);
   gcc_assert (!m->fs.fp_valid);
+  gcc_assert (!m->fs.realigned);
 
   /* Sibcall epilogues don't want a return instruction.  */
   if (style == 0)
index 300f22e58c7530823aa6ac36b4c01f984d9cad07..a2acc7131f7ee219de6ae1a19e0c64c7ea0e13e1 100644 (file)
@@ -2323,6 +2323,11 @@ struct GTY(()) machine_frame_state
   BOOL_BITFIELD sp_valid : 1;
   BOOL_BITFIELD fp_valid : 1;
   BOOL_BITFIELD drap_valid : 1;
+
+  /* Indicate whether the local stack frame has been re-aligned.  When
+     set, the SP/FP offsets above are relative to the aligned frame
+     and not the CFA.  */
+  BOOL_BITFIELD realigned : 1;
 };
 
 struct GTY(()) machine_function {
index 54585e91d9f5b0d1f229dc8680f92176b74ae59c..2c79c6941377d44345350faf6a0754105a3cf18a 100644 (file)
@@ -479,6 +479,8 @@ static struct dw_loc_descr_struct *build_cfa_loc
 static struct dw_loc_descr_struct *build_cfa_aligned_loc
   (HOST_WIDE_INT, HOST_WIDE_INT);
 static void def_cfa_1 (const char *, dw_cfa_location *);
+static struct dw_loc_descr_struct *mem_loc_descriptor
+  (rtx, enum machine_mode mode, enum var_init_status);
 
 /* How to start an assembler comment.  */
 #ifndef ASM_COMMENT_START
@@ -1833,6 +1835,17 @@ dwarf2out_frame_debug_def_cfa (rtx pat, const char *label)
       cfa.reg = REGNO (pat);
       break;
 
+    case MEM:
+      cfa.indirect = 1;
+      pat = XEXP (pat, 0);
+      if (GET_CODE (pat) == PLUS)
+       {
+         cfa.base_offset = INTVAL (XEXP (pat, 1));
+         pat = XEXP (pat, 0);
+       }
+      cfa.reg = REGNO (pat);
+      break;
+
     default:
       /* Recurse and define an expression.  */
       gcc_unreachable ();
@@ -1951,6 +1964,34 @@ dwarf2out_frame_debug_cfa_register (rtx set, const char *label)
   reg_save (label, sregno, dregno, 0);
 }
 
+/* A subroutine of dwarf2out_frame_debug, process a REG_CFA_EXPRESSION note. */
+
+static void
+dwarf2out_frame_debug_cfa_expression (rtx set, const char *label)
+{
+  rtx src, dest, span;
+  dw_cfi_ref cfi = new_cfi ();
+
+  dest = SET_DEST (set);
+  src = SET_SRC (set);
+
+  gcc_assert (REG_P (src));
+  gcc_assert (MEM_P (dest));
+
+  span = targetm.dwarf_register_span (src);
+  gcc_assert (!span);
+
+  cfi->dw_cfi_opc = DW_CFA_expression;
+  cfi->dw_cfi_oprnd1.dw_cfi_reg_num = DWARF_FRAME_REGNUM (REGNO (src));
+  cfi->dw_cfi_oprnd2.dw_cfi_loc
+    = mem_loc_descriptor (XEXP (dest, 0), GET_MODE (dest),
+                         VAR_INIT_STATUS_INITIALIZED);
+
+  /* ??? We'd like to use queue_reg_save, were the interface different,
+     and, as above, we could manage flushing for epilogues.  */
+  add_fde_cfi (label, cfi);
+}
+
 /* A subroutine of dwarf2out_frame_debug, process a REG_CFA_RESTORE note.  */
 
 static void
@@ -2740,6 +2781,14 @@ dwarf2out_frame_debug (rtx insn, bool after_p)
        handled_one = true;
        break;
 
+      case REG_CFA_EXPRESSION:
+       n = XEXP (note, 0);
+       if (n == NULL)
+         n = single_set (insn);
+       dwarf2out_frame_debug_cfa_expression (n, label);
+       handled_one = true;
+       break;
+
       case REG_CFA_RESTORE:
        n = XEXP (note, 0);
        if (n == NULL)
@@ -6181,8 +6230,6 @@ static dw_loc_descr_ref based_loc_descr (rtx, HOST_WIDE_INT,
                                         enum var_init_status);
 static int is_based_loc (const_rtx);
 static int resolve_one_addr (rtx *, void *);
-static dw_loc_descr_ref mem_loc_descriptor (rtx, enum machine_mode mode,
-                                           enum var_init_status);
 static dw_loc_descr_ref concat_loc_descriptor (rtx, rtx,
                                               enum var_init_status);
 static dw_loc_descr_ref loc_descriptor (rtx, enum machine_mode mode,
index aa2daeac0071af85d9baf73c5bf8203a4bf52ac7..f82e7b741a03ec8b1e1c21868f144104282e11ae 100644 (file)
@@ -148,6 +148,12 @@ REG_NOTE (CFA_OFFSET)
    or the pattern should be simple reg-reg move.  */
 REG_NOTE (CFA_REGISTER)
 
+/* Attached to insns that are RTX_FRAME_RELATED_P, but are too complex
+   for FRAME_RELATED_EXPR intuition.  This is a save to memory, i.e. will
+   result in a DW_CFA_expression.  The pattern or the insn should be a
+   store of a register to an arbitrary (non-validated) memory address.  */
+REG_NOTE (CFA_EXPRESSION)
+
 /* Attached to insns that are RTX_FRAME_RELATED_P, with the information
    that this is a restore operation, i.e. will result in DW_CFA_restore
    or the like.  Either the attached rtx, or the destination of the insn's