Patchwork [7/9] Describe unwinding for realigned frames explicitly.

login
register
mail settings
Submitter Richard Henderson
Date Aug. 3, 2010, 11:53 p.m.
Message ID <1280879596-1089-8-git-send-email-rth@twiddle.net>
Download mbox | patch
Permalink /patch/60809/
State New
Headers show

Comments

Richard Henderson - Aug. 3, 2010, 11:53 p.m.
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.
---
 gcc/config/i386/i386.c |   67 ++++++++++++++++++++++++++++++++++++++++++-----
 gcc/config/i386/i386.h |    5 +++
 gcc/dwarf2out.c        |   51 +++++++++++++++++++++++++++++++++++-
 gcc/reg-notes.def      |    6 ++++
 4 files changed, 119 insertions(+), 10 deletions(-)


	* 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.

Patch

diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index f465600..b4fddfc 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -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;
@@ -10064,6 +10111,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
@@ -10116,6 +10164,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
@@ -10156,6 +10205,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,
@@ -10190,6 +10240,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)
diff --git a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h
index 300f22e..a2acc71 100644
--- a/gcc/config/i386/i386.h
+++ b/gcc/config/i386/i386.h
@@ -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 {
diff --git a/gcc/dwarf2out.c b/gcc/dwarf2out.c
index d8d3851..1ac874b 100644
--- a/gcc/dwarf2out.c
+++ b/gcc/dwarf2out.c
@@ -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,
diff --git a/gcc/reg-notes.def b/gcc/reg-notes.def
index aa2daea..f82e7b7 100644
--- a/gcc/reg-notes.def
+++ b/gcc/reg-notes.def
@@ -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