public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [patch] Fix var-tracking with dynamic stack realignment on x86
@ 2011-05-22 23:15 Eric Botcazou
  2011-05-23 10:07 ` Jakub Jelinek
  0 siblings, 1 reply; 17+ messages in thread
From: Eric Botcazou @ 2011-05-22 23:15 UTC (permalink / raw)
  To: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 1924 bytes --]

var-tracking doesn't fully work with dynamic stack realignment on x86 when a 
DRAP register is used for a function: the value of the incoming parameters is 
very quickly wrong (as soon as the DRAP register is clobbered), in particular
in backtraces.  The reason is that var-tracking doesn't track parameters passed 
on the stack and, therefore, doesn't invalidate their location when the DRAP 
register is reused for something else.

Small Ada testcase attached, compile p1.adb with either -mforce-drap -O -g 
or -mforce-drap -O -g -fno-var-tracking-assignments, break on Diff and display 
a backtrace: the arguments for Func are wrong.

The fix is (1) to arrange for var-tracking to track parameters passed on the 
stack and (2) to invalidate their location where there is a DRAP register and 
it is clobbered.

Tested on x86_64-suse-linux and i586-suse-linux.  Is there an (unofficial) 
maintainter for the var-tracking code?  Jakub?


2011-05-22  Eric Botcazou  <ebotcazou@adacore.com>

	* var-tracking.c (var_regno_delete): Adjust prototype.
	(delete_mem_loc): New static function.
	(delete_mem_locs_on_entry): Likewise.
	(var_reg_delete_and_set): Add local variable REGNO.
	If the register is the DRAP register, invoke delete_mem_locs_on_entry.
	(var_reg_delete): Add local variable REGNO.
	If the register is the DRAP register and is also clobbered, invoke
	delete_mem_locs_on_entry.
	(var_regno_delete): Turn type of REGNO parameter to unsigned int.
	If the register is the DRAP register, invoke delete_mem_locs_on_entry.
	(drap_setter): New static function.
	(vt_init_drap_reg): Likewise.
	(vt_initialize): Set PROLOGUE_BB unconditionally.
	Use drap_setter to find out the SET that initializes the DRAP register
	in the prologue, if any, and call vt_init_drap_reg on it.
	Call vt_init_cfa_base only if FP_CFA_OFFSET isn't equal to -1.
	Call vt_add_function_parameters at the end of the prologue.


-- 
Eric Botcazou

[-- Attachment #2: q1.adb --]
[-- Type: text/x-adasrc, Size: 309 bytes --]

with R1; use R1;

package body Q1 is

  type Arr is array (1..4) of Long_Long_Integer;
  for Arr'alignment use 16;

  function Func (A : Integer; B : Integer) return Integer is
    C : Integer;
    My_Arr : Arr;
  begin
    My_Arr (1) := Dummy;
    C := Diff (A/2, B/2);
    return A + B + C;
  end;

end Q1;

[-- Attachment #3: q1.ads --]
[-- Type: text/x-adasrc, Size: 109 bytes --]

package Q1 is

  function Func (A: Integer; B : Integer) return Integer;
  pragma Export (C, Func);

end Q1;

[-- Attachment #4: r1.ads --]
[-- Type: text/x-adasrc, Size: 127 bytes --]

package R1 is

  function Diff (A : Integer; B : Integer) return Integer;

  function Dummy return Long_Long_Integer;

end R1;

[-- Attachment #5: p1.adb --]
[-- Type: text/x-adasrc, Size: 110 bytes --]

with Q1; use Q1;

procedure P1 is
begin
  if Func (10, 20) /= 25 then
    raise Program_Error;
  end if;
end;

[-- Attachment #6: r1.adb --]
[-- Type: text/x-adasrc, Size: 317 bytes --]

with System.Machine_Code; use System.Machine_Code;

package body R1 is

  function Diff (A : Integer; B : Integer) return Integer is
  begin
    return A - B;
  end;

  function Dummy return Long_Long_Integer is
  begin
    Asm ("mov %%esp, %%ecx", Clobber => "%ecx", Volatile => true);
    return 1;
  end;

end R1;

[-- Attachment #7: p.diff --]
[-- Type: text/x-diff, Size: 8167 bytes --]

Index: var-tracking.c
===================================================================
--- var-tracking.c	(revision 174027)
+++ var-tracking.c	(working copy)
@@ -429,7 +429,7 @@ static void var_reg_set (dataflow_set *,
 static void var_reg_delete_and_set (dataflow_set *, rtx, bool,
 				    enum var_init_status, rtx);
 static void var_reg_delete (dataflow_set *, rtx, bool);
-static void var_regno_delete (dataflow_set *, int);
+static void var_regno_delete (dataflow_set *, unsigned int);
 static void var_mem_set (dataflow_set *, rtx, enum var_init_status, rtx);
 static void var_mem_delete_and_set (dataflow_set *, rtx, bool,
 				    enum var_init_status, rtx);
@@ -1690,6 +1690,36 @@ get_init_value (dataflow_set *set, rtx l
   return ret_val;
 }
 
+/* Remove the one-part variable pointed to by SLOT from the set passed as
+   INFO if its location is a MEM.  */
+
+static int
+delete_mem_loc (void **slot, void *info)
+{
+  dataflow_set *set = (dataflow_set *) info;
+  variable var = (variable) *slot;
+
+  gcc_assert (var->n_var_parts == 1);
+
+  if (MEM_P (var->var_part[0].loc_chain->loc))
+    delete_variable_part (set, var->var_part[0].loc_chain->loc, var->dv,
+			  var->var_part[0].offset);
+
+  return 1;
+}
+
+/* Remove all one-part variables from the entry block still live in SET
+   and whose location is a MEM.  */
+
+static void
+delete_mem_locs_on_entry (dataflow_set *set)
+{
+  set->traversed_vars = VTI (ENTRY_BLOCK_PTR)->out.vars;
+  htab_traverse (shared_hash_htab (VTI (ENTRY_BLOCK_PTR)->out.vars),
+		 delete_mem_loc, set);
+  set->traversed_vars = NULL;
+}
+
 /* Delete current content of register LOC in dataflow set SET and set
    the register to contain REG_EXPR (LOC), REG_OFFSET (LOC).  If
    MODIFY is true, any other live copies of the same variable part are
@@ -1701,6 +1731,7 @@ static void
 var_reg_delete_and_set (dataflow_set *set, rtx loc, bool modify,
 			enum var_init_status initialized, rtx set_src)
 {
+  unsigned int regno = REGNO (loc);
   tree decl = REG_EXPR (loc);
   HOST_WIDE_INT offset = REG_OFFSET (loc);
   attrs node, next;
@@ -1711,7 +1742,7 @@ var_reg_delete_and_set (dataflow_set *se
   if (initialized == VAR_INIT_STATUS_UNKNOWN)
     initialized = get_init_value (set, loc, dv_from_decl (decl));
 
-  nextp = &set->regs[REGNO (loc)];
+  nextp = &set->regs[regno];
   for (node = *nextp; node; node = next)
     {
       next = node->next;
@@ -1729,6 +1760,12 @@ var_reg_delete_and_set (dataflow_set *se
     }
   if (modify)
     clobber_variable_part (set, loc, dv_from_decl (decl), offset, set_src);
+
+  /* If the DRAP register is set, then the incoming location of all parameters
+     not passed in registers is no longer valid.  */
+  if (stack_realign_drap && crtl->drap_reg && REGNO (crtl->drap_reg) == regno)
+    delete_mem_locs_on_entry (set);
+
   var_reg_set (set, loc, initialized, set_src);
 }
 
@@ -1740,7 +1777,8 @@ var_reg_delete_and_set (dataflow_set *se
 static void
 var_reg_delete (dataflow_set *set, rtx loc, bool clobber)
 {
-  attrs *nextp = &set->regs[REGNO (loc)];
+  unsigned int regno = REGNO (loc);
+  attrs *nextp = &set->regs[regno];
   attrs node, next;
 
   if (clobber)
@@ -1751,6 +1789,13 @@ var_reg_delete (dataflow_set *set, rtx l
       decl = var_debug_decl (decl);
 
       clobber_variable_part (set, NULL, dv_from_decl (decl), offset, NULL);
+
+      /* If the DRAP register is clobbered, then the incoming location of all
+	 parameters not passed in registers is no longer valid.  */
+      if (stack_realign_drap
+	  && crtl->drap_reg
+	  && REGNO (crtl->drap_reg) == regno)
+	delete_mem_locs_on_entry (set);
     }
 
   for (node = *nextp; node; node = next)
@@ -1770,7 +1815,7 @@ var_reg_delete (dataflow_set *set, rtx l
 /* Delete content of register with number REGNO in dataflow set SET.  */
 
 static void
-var_regno_delete (dataflow_set *set, int regno)
+var_regno_delete (dataflow_set *set, unsigned int regno)
 {
   attrs *reg = &set->regs[regno];
   attrs node, next;
@@ -1782,6 +1827,11 @@ var_regno_delete (dataflow_set *set, int
       pool_free (attrs_pool, node);
     }
   *reg = NULL;
+
+  /* If the contents of the DRAP register are changed, then the incoming
+     location of parameters not passed in registers is no longer valid.  */
+  if (stack_realign_drap && crtl->drap_reg && REGNO (crtl->drap_reg) == regno)
+    delete_mem_locs_on_entry (set);
 }
 
 /* Set the location of DV, OFFSET as the MEM LOC.  */
@@ -8658,13 +8708,61 @@ vt_init_cfa_base (void)
 		    0, NULL_RTX, INSERT);
 }
 
+/* Return the SET that initializes the DRAP register in INSN, if any.  */
+
+static rtx *
+drap_setter (rtx insn)
+{
+  const unsigned int drap_reg = REGNO (crtl->drap_reg);
+  rtx pat = PATTERN (insn);
+  if (GET_CODE (pat) == SET
+      && REG_P (SET_DEST (pat))
+      && REGNO (SET_DEST (pat)) == drap_reg)
+    return &PATTERN (insn);
+  else if (GET_CODE (pat) == PARALLEL)
+    {
+      int i;
+      for (i = XVECLEN (pat, 0) - 1; i >= 0; i--)
+	if (GET_CODE (XVECEXP (pat, 0, i)) == SET
+	    && REG_P (SET_DEST (XVECEXP (pat, 0, i)))
+	    && REGNO (SET_DEST (XVECEXP (pat, 0, i))) == drap_reg)
+	  return &XVECEXP (pat, 0, i);
+    }
+  return NULL;
+}
+
+/* Mask the SET that initializes the DRAP register and create a preserved VALUE
+   for it.  We need to do this because the incoming location of parameters not
+   passed in registers references the DRAP register _before_ the SET, since the
+   parameters are attached to the entry block.  */
+
+static void
+vt_init_drap_reg (rtx *set)
+{
+  cselib_val *val;
+
+  validate_change (NULL_RTX, set, gen_rtx_USE (VOIDmode, SET_SRC (*set)),
+		   true);
+
+  if (!MAY_HAVE_DEBUG_INSNS)
+    return;
+
+  val = cselib_lookup_from_insn (crtl->drap_reg, GET_MODE (crtl->drap_reg),
+				 1, VOIDmode, get_insns ());
+  preserve_value (val);
+  var_reg_decl_set (&VTI (ENTRY_BLOCK_PTR)->out, crtl->drap_reg,
+		    VAR_INIT_STATUS_INITIALIZED, dv_from_value (val->val_rtx),
+		    0, NULL_RTX, INSERT);
+}
+
 /* Allocate and initialize the data structures for variable tracking
    and parse the RTL to get the micro operations.  */
 
 static bool
 vt_initialize (void)
 {
-  basic_block bb, prologue_bb = NULL;
+  basic_block prologue_bb = single_succ (ENTRY_BLOCK_PTR), bb;
+  unsigned int drap_reg = INVALID_REGNUM;
   HOST_WIDE_INT fp_cfa_offset = -1;
 
   alloc_aux_for_blocks (sizeof (struct variable_tracking_info_def));
@@ -8764,8 +8862,6 @@ vt_initialize (void)
 	    }
 	  if (elim != hard_frame_pointer_rtx)
 	    fp_cfa_offset = -1;
-	  else
-	    prologue_bb = single_succ (ENTRY_BLOCK_PTR);
 	}
     }
   if (frame_pointer_needed)
@@ -8778,11 +8874,9 @@ vt_initialize (void)
 
   hard_frame_pointer_adjustment = -1;
 
-  vt_add_function_parameters ();
-
   FOR_EACH_BB (bb)
     {
-      rtx insn;
+      rtx insn, *set;
       HOST_WIDE_INT pre, post = 0;
       basic_block first_bb, last_bb;
 
@@ -8836,6 +8930,17 @@ vt_initialize (void)
 			}
 		    }
 
+		  if (bb == prologue_bb
+		      && stack_realign_drap
+		      && crtl->drap_reg
+		      && drap_reg == INVALID_REGNUM
+		      && RTX_FRAME_RELATED_P (insn)
+		      && ((set = drap_setter (insn)) != NULL))
+		    {
+		       vt_init_drap_reg (set);
+		       drap_reg = REGNO (crtl->drap_reg);
+		    }
+
 		  cselib_hook_called = false;
 		  adjust_insn (bb, insn);
 		  if (MAY_HAVE_DEBUG_INSNS)
@@ -8869,6 +8974,7 @@ vt_initialize (void)
 
 		  if (bb == prologue_bb
 		      && hard_frame_pointer_adjustment == -1
+		      && fp_cfa_offset != -1
 		      && RTX_FRAME_RELATED_P (insn)
 		      && fp_setter (insn))
 		    {
@@ -8876,6 +8982,14 @@ vt_initialize (void)
 		      hard_frame_pointer_adjustment = fp_cfa_offset;
 		    }
 		}
+
+	      /* Initialize the parameters right at the end of the prologue.
+		 We can do this neither earlier because the insn establishing
+		 the frame might invalidate all the previous MEMs, nor later
+		 because the DRAP register might be clobbered in between.  */
+	      else if (NOTE_P (insn)
+		       && NOTE_KIND (insn) == NOTE_INSN_PROLOGUE_END)
+		vt_add_function_parameters ();
 	    }
 	  gcc_assert (offset == VTI (bb)->out.stack_adjust);
 	}

^ permalink raw reply	[flat|nested] 17+ messages in thread

end of thread, other threads:[~2011-05-30 15:17 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-05-22 23:15 [patch] Fix var-tracking with dynamic stack realignment on x86 Eric Botcazou
2011-05-23 10:07 ` Jakub Jelinek
2011-05-23 10:25   ` Eric Botcazou
2011-05-23 12:04     ` Jakub Jelinek
2011-05-23 18:17     ` Jakub Jelinek
2011-05-24 12:21       ` Eric Botcazou
2011-05-24 12:26         ` Jakub Jelinek
2011-05-24 14:20           ` Eric Botcazou
2011-05-24 14:59             ` Jakub Jelinek
2011-05-24 23:44               ` Eric Botcazou
2011-05-29 16:14       ` Eric Botcazou
2011-05-30  9:55         ` Jakub Jelinek
2011-05-30 13:10           ` Eric Botcazou
2011-05-30 13:31             ` Jakub Jelinek
2011-05-30 17:10               ` Eric Botcazou
2011-05-30 10:07         ` Alexandre Oliva
2011-05-30 11:21           ` Bernd Schmidt

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).