This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[patch] Fix var-tracking with dynamic stack realignment on x86
- From: Eric Botcazou <ebotcazou at adacore dot com>
- To: gcc-patches at gcc dot gnu dot org
- Date: Sun, 22 May 2011 22:51:30 +0200
- Subject: [patch] Fix var-tracking with dynamic stack realignment on x86
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
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;
package Q1 is
function Func (A: Integer; B : Integer) return Integer;
pragma Export (C, Func);
end Q1;
package R1 is
function Diff (A : Integer; B : Integer) return Integer;
function Dummy return Long_Long_Integer;
end R1;
with Q1; use Q1;
procedure P1 is
begin
if Func (10, 20) /= 25 then
raise Program_Error;
end if;
end;
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;
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);
}