Commit acb37d29 by Richard Sandiford Committed by Richard Sandiford

re PR middle-end/37243 (IRA causes wrong code generation)

gcc/
	PR middle-end/37243
	* ira-build.c (form_loop_tree): Reverse BB walk.
	(create_bb_allocnos): Likewise.
	* ira-lives.c (make_regno_born_and_dead, regs_set): Delete.
	(mark_reg_store): Rename to...
	(mark_ref_live): ...this and take a df_ref argument instead of
	note_stores arguments.  Assert that we have a register.
	(mark_reg_clobber): Delete.
	(def_conflicts_with_inputs_p): New function.
	(mark_reg_conflicts): Delete.
	(mark_reg_death): Rename to...
	(mark_ref_dead): ...this and take a df_ref argument instead of
	a register.  Assert that we have a register.
	(process_bb_node_lives): Hoist frequency calculation out of
	instruction walk.  Convert from a forwards scan to a backwards scan.
	Use DF_REF_USES and DF_REF_DEFS instead of register notes and
	note_stores.  Remove EH_RETURN_DATA_REGNO and regs_set handling.
	(create_allocno_live_ranges): Don't create regs_set.

From-SVN: r139993
parent e0c68ce9
2008-09-04 Richard Sandiford <rdsandiford@googlemail.com>
PR middle-end/37243
* ira-build.c (form_loop_tree): Reverse BB walk.
(create_bb_allocnos): Likewise.
* ira-lives.c (make_regno_born_and_dead, regs_set): Delete.
(mark_reg_store): Rename to...
(mark_ref_live): ...this and take a df_ref argument instead of
note_stores arguments. Assert that we have a register.
(mark_reg_clobber): Delete.
(def_conflicts_with_inputs_p): New function.
(mark_reg_conflicts): Delete.
(mark_reg_death): Rename to...
(mark_ref_dead): ...this and take a df_ref argument instead of
a register. Assert that we have a register.
(process_bb_node_lives): Hoist frequency calculation out of
instruction walk. Convert from a forwards scan to a backwards scan.
Use DF_REF_USES and DF_REF_DEFS instead of register notes and
note_stores. Remove EH_RETURN_DATA_REGNO and regs_set handling.
(create_allocno_live_ranges): Don't create regs_set.
2008-09-04 Ian Lance Taylor <iant@google.com> 2008-09-04 Ian Lance Taylor <iant@google.com>
* rtl.h (LABEL_REF_NONLOCAL_P): Don't check for REG_LABEL_OPERAND * rtl.h (LABEL_REF_NONLOCAL_P): Don't check for REG_LABEL_OPERAND
......
...@@ -310,7 +310,7 @@ form_loop_tree (void) ...@@ -310,7 +310,7 @@ form_loop_tree (void)
ira_loop_nodes[i].children = NULL; ira_loop_nodes[i].children = NULL;
ira_loop_nodes[i].subloops = NULL; ira_loop_nodes[i].subloops = NULL;
} }
FOR_EACH_BB_REVERSE (bb) FOR_EACH_BB (bb)
{ {
bb_node = &ira_bb_nodes[bb->index]; bb_node = &ira_bb_nodes[bb->index];
bb_node->bb = bb; bb_node->bb = bb;
...@@ -1343,7 +1343,7 @@ create_bb_allocnos (ira_loop_tree_node_t bb_node) ...@@ -1343,7 +1343,7 @@ create_bb_allocnos (ira_loop_tree_node_t bb_node)
curr_bb = bb = bb_node->bb; curr_bb = bb = bb_node->bb;
ira_assert (bb != NULL); ira_assert (bb != NULL);
FOR_BB_INSNS (bb, insn) FOR_BB_INSNS_REVERSE (bb, insn)
if (INSN_P (insn)) if (INSN_P (insn))
create_insn_allocnos (PATTERN (insn), false); create_insn_allocnos (PATTERN (insn), false);
/* It might be a allocno living through from one subloop to /* It might be a allocno living through from one subloop to
......
...@@ -149,15 +149,6 @@ make_regno_dead (int regno) ...@@ -149,15 +149,6 @@ make_regno_dead (int regno)
update_allocno_pressure_excess_length (a); update_allocno_pressure_excess_length (a);
} }
/* Process the birth and, right after then, death of register
REGNO. */
static void
make_regno_born_and_dead (int regno)
{
make_regno_born (regno);
make_regno_dead (regno);
}
/* The current register pressures for each cover class for the current /* The current register pressures for each cover class for the current
basic block. */ basic block. */
static int curr_reg_pressure[N_REG_CLASSES]; static int curr_reg_pressure[N_REG_CLASSES];
...@@ -218,40 +209,21 @@ clear_allocno_live (ira_allocno_t a) ...@@ -218,40 +209,21 @@ clear_allocno_live (ira_allocno_t a)
sparseset_clear_bit (allocnos_live, ALLOCNO_NUM (a)); sparseset_clear_bit (allocnos_live, ALLOCNO_NUM (a));
} }
/* Record all regs that are set in any one insn. Communication from /* Mark the register referenced by use or def REF as live
mark_reg_{store,clobber}. */
static VEC(rtx, heap) *regs_set;
/* Handle the case where REG is set by the insn being scanned, during
the scan to build live ranges and calculate reg pressure info.
Store a 1 in hard_regs_live or allocnos_live for this register or Store a 1 in hard_regs_live or allocnos_live for this register or
the corresponding allocno, record how many consecutive hardware the corresponding allocno, record how many consecutive hardware
registers it actually needs. registers it actually needs. */
Note that even if REG does not remain alive after this insn, we
must mark it here as live, to ensure a conflict between REG and any
other reg allocnos set in this insn that really do live. This is
because those other allocnos could be considered after this.
REG might actually be something other than a register; if so, we do
nothing.
SETTER is 0 if this register was modified by an auto-increment
(i.e., a REG_INC note was found for it). */
static void static void
mark_reg_store (rtx reg, const_rtx setter ATTRIBUTE_UNUSED, mark_ref_live (struct df_ref *ref)
void *data ATTRIBUTE_UNUSED)
{ {
rtx reg;
int regno; int regno;
reg = DF_REF_REG (ref);
if (GET_CODE (reg) == SUBREG) if (GET_CODE (reg) == SUBREG)
reg = SUBREG_REG (reg); reg = SUBREG_REG (reg);
gcc_assert (REG_P (reg));
if (! REG_P (reg))
return;
VEC_safe_push (rtx, heap, regs_set, reg);
regno = REGNO (reg); regno = REGNO (reg);
if (regno >= FIRST_PSEUDO_REGISTER) if (regno >= FIRST_PSEUDO_REGISTER)
...@@ -297,55 +269,36 @@ mark_reg_store (rtx reg, const_rtx setter ATTRIBUTE_UNUSED, ...@@ -297,55 +269,36 @@ mark_reg_store (rtx reg, const_rtx setter ATTRIBUTE_UNUSED,
} }
} }
/* Like mark_reg_store except notice just CLOBBERs; ignore SETs. */ /* Return true if the definition described by DEF conflicts with the
static void instruction's inputs. */
mark_reg_clobber (rtx reg, const_rtx setter, void *data) static bool
def_conflicts_with_inputs_p (struct df_ref *def)
{ {
if (GET_CODE (setter) == CLOBBER) /* Conservatively assume that the condition is true for all clobbers. */
mark_reg_store (reg, setter, data); return DF_REF_FLAGS_IS_SET (def, DF_REF_MUST_CLOBBER);
} }
/* Record that hard register REG (if it is a hard register) has /* Mark the register referenced by definition DEF as dead, if the
conflicts with all the allocno currently live or the corresponding definition is a total one. Store a 0 in hard_regs_live or
allocno lives at just the current program point. Do not mark REG allocnos_live for the register. */
(or the allocno) itself as live. */
static void static void
mark_reg_conflicts (rtx reg) mark_ref_dead (struct df_ref *def)
{ {
unsigned int i;
rtx reg;
int regno; int regno;
if (GET_CODE (reg) == SUBREG) if (DF_REF_FLAGS_IS_SET (def, DF_REF_PARTIAL)
reg = SUBREG_REG (reg); || DF_REF_FLAGS_IS_SET (def, DF_REF_CONDITIONAL))
if (! REG_P (reg))
return; return;
reg = DF_REF_REG (def);
if (GET_CODE (reg) == SUBREG)
reg = SUBREG_REG (reg);
gcc_assert (REG_P (reg));
regno = REGNO (reg); regno = REGNO (reg);
if (regno >= FIRST_PSEUDO_REGISTER) if (regno >= FIRST_PSEUDO_REGISTER)
make_regno_born_and_dead (regno);
else if (! TEST_HARD_REG_BIT (ira_no_alloc_regs, regno))
{
int last = regno + hard_regno_nregs[regno][GET_MODE (reg)];
while (regno < last)
{
make_regno_born_and_dead (regno);
regno++;
}
}
}
/* Mark REG (or the corresponding allocno) as being dead (following
the insn being scanned now). Store a 0 in hard_regs_live or
allocnos_live for the register. */
static void
mark_reg_death (rtx reg)
{
unsigned int i;
int regno = REGNO (reg);
if (regno >= FIRST_PSEUDO_REGISTER)
{ {
ira_allocno_t a = ira_curr_regno_allocno_map[regno]; ira_allocno_t a = ira_curr_regno_allocno_map[regno];
...@@ -618,14 +571,14 @@ process_single_reg_class_operands (bool in_p, int freq) ...@@ -618,14 +571,14 @@ process_single_reg_class_operands (bool in_p, int freq)
static void static void
process_bb_node_lives (ira_loop_tree_node_t loop_tree_node) process_bb_node_lives (ira_loop_tree_node_t loop_tree_node)
{ {
int i; int i, freq;
unsigned int j; unsigned int j;
basic_block bb; basic_block bb;
rtx insn; rtx insn;
edge e; edge e;
edge_iterator ei; edge_iterator ei;
bitmap_iterator bi; bitmap_iterator bi;
bitmap reg_live_in; bitmap reg_live_out;
unsigned int px; unsigned int px;
bb = loop_tree_node->bb; bb = loop_tree_node->bb;
...@@ -637,9 +590,9 @@ process_bb_node_lives (ira_loop_tree_node_t loop_tree_node) ...@@ -637,9 +590,9 @@ process_bb_node_lives (ira_loop_tree_node_t loop_tree_node)
high_pressure_start_point[ira_reg_class_cover[i]] = -1; high_pressure_start_point[ira_reg_class_cover[i]] = -1;
} }
curr_bb_node = loop_tree_node; curr_bb_node = loop_tree_node;
reg_live_in = DF_LR_IN (bb); reg_live_out = DF_LR_OUT (bb);
sparseset_clear (allocnos_live); sparseset_clear (allocnos_live);
REG_SET_TO_HARD_REG_SET (hard_regs_live, reg_live_in); REG_SET_TO_HARD_REG_SET (hard_regs_live, reg_live_out);
AND_COMPL_HARD_REG_SET (hard_regs_live, eliminable_regset); AND_COMPL_HARD_REG_SET (hard_regs_live, eliminable_regset);
AND_COMPL_HARD_REG_SET (hard_regs_live, ira_no_alloc_regs); AND_COMPL_HARD_REG_SET (hard_regs_live, ira_no_alloc_regs);
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
...@@ -659,7 +612,7 @@ process_bb_node_lives (ira_loop_tree_node_t loop_tree_node) ...@@ -659,7 +612,7 @@ process_bb_node_lives (ira_loop_tree_node_t loop_tree_node)
ira_assert (curr_reg_pressure[cover_class] ira_assert (curr_reg_pressure[cover_class]
<= ira_available_class_regs[cover_class]); <= ira_available_class_regs[cover_class]);
} }
EXECUTE_IF_SET_IN_BITMAP (reg_live_in, FIRST_PSEUDO_REGISTER, j, bi) EXECUTE_IF_SET_IN_BITMAP (reg_live_out, FIRST_PSEUDO_REGISTER, j, bi)
{ {
ira_allocno_t a = ira_curr_regno_allocno_map[j]; ira_allocno_t a = ira_curr_regno_allocno_map[j];
...@@ -670,87 +623,91 @@ process_bb_node_lives (ira_loop_tree_node_t loop_tree_node) ...@@ -670,87 +623,91 @@ process_bb_node_lives (ira_loop_tree_node_t loop_tree_node)
make_regno_born (j); make_regno_born (j);
} }
#ifdef EH_RETURN_DATA_REGNO freq = REG_FREQ_FROM_BB (bb);
if (bb_has_eh_pred (bb)) if (freq == 0)
{ freq = 1;
for (j = 0; ; ++j)
{
unsigned int regno = EH_RETURN_DATA_REGNO (j);
if (regno == INVALID_REGNUM)
break;
make_regno_born_and_dead (regno);
}
}
#endif
/* Allocnos can't go in stack regs at the start of a basic block
that is reached by an abnormal edge. Likewise for call
clobbered regs, because caller-save, fixup_abnormal_edges and
possibly the table driven EH machinery are not quite ready to
handle such allocnos live across such edges. */
FOR_EACH_EDGE (e, ei, bb->preds)
if (e->flags & EDGE_ABNORMAL)
break;
if (e != NULL)
{
#ifdef STACK_REGS
EXECUTE_IF_SET_IN_SPARSESET (allocnos_live, px)
{
ALLOCNO_NO_STACK_REG_P (ira_allocnos[px]) = true;
ALLOCNO_TOTAL_NO_STACK_REG_P (ira_allocnos[px]) = true;
}
for (px = FIRST_STACK_REG; px <= LAST_STACK_REG; px++)
make_regno_born_and_dead (px);
#endif
/* No need to record conflicts for call clobbered regs if we
have nonlocal labels around, as we don't ever try to
allocate such regs in this case. */
if (!cfun->has_nonlocal_label)
for (px = 0; px < FIRST_PSEUDO_REGISTER; px++)
if (call_used_regs[px])
make_regno_born_and_dead (px);
}
/* Scan the code of this basic block, noting which allocnos and /* Scan the code of this basic block, noting which allocnos and
hard regs are born or die. */ hard regs are born or die.
FOR_BB_INSNS (bb, insn)
Note that this loop treats uninitialized values as live until
the beginning of the block. For example, if an instruction
uses (reg:DI foo), and only (subreg:SI (reg:DI foo) 0) is ever
set, FOO will remain live until the beginning of the block.
Likewise if FOO is not set at all. This is unnecessarily
pessimistic, but it probably doesn't matter much in practice. */
FOR_BB_INSNS_REVERSE (bb, insn)
{ {
rtx link; struct df_ref **def_rec, **use_rec;
int freq; bool call_p;
if (! INSN_P (insn)) if (! INSN_P (insn))
continue; continue;
freq = REG_FREQ_FROM_BB (BLOCK_FOR_INSN (insn));
if (freq == 0)
freq = 1;
if (internal_flag_ira_verbose > 2 && ira_dump_file != NULL) if (internal_flag_ira_verbose > 2 && ira_dump_file != NULL)
fprintf (ira_dump_file, " Insn %u(l%d): point = %d\n", fprintf (ira_dump_file, " Insn %u(l%d): point = %d\n",
INSN_UID (insn), loop_tree_node->parent->loop->num, INSN_UID (insn), loop_tree_node->parent->loop->num,
curr_point); curr_point);
/* Check regs_set is an empty set. */ /* Mark each defined value as live. We need to do this for
gcc_assert (VEC_empty (rtx, regs_set)); unused values because they still conflict with quantities
that are live at the time of the definition.
/* Mark any allocnos clobbered by INSN as live, so they
conflict with the inputs. */ Ignore DF_REF_MAY_CLOBBERs on a call instruction. Such
note_stores (PATTERN (insn), mark_reg_clobber, NULL); references represent the effect of the called function
on a call-clobbered register. Marking the register as
live would stop us from allocating it to a call-crossing
allocno. */
call_p = CALL_P (insn);
for (def_rec = DF_INSN_DEFS (insn); *def_rec; def_rec++)
if (!call_p || !DF_REF_FLAGS_IS_SET (*def_rec, DF_REF_MAY_CLOBBER))
mark_ref_live (*def_rec);
/* If INSN has multiple outputs, then any value used in one
of the outputs conflicts with the other outputs. Model this
by making the used value live during the output phase.
It is unsafe to use !single_set here since it will ignore
an unused output. Just because an output is unused does
not mean the compiler can assume the side effect will not
occur. Consider if ALLOCNO appears in the address of an
output and we reload the output. If we allocate ALLOCNO
to the same hard register as an unused output we could
set the hard register before the output reload insn. */
if (GET_CODE (PATTERN (insn)) == PARALLEL && multiple_sets (insn))
for (use_rec = DF_INSN_USES (insn); *use_rec; use_rec++)
{
int i;
rtx reg;
reg = DF_REF_REG (*use_rec);
for (i = XVECLEN (PATTERN (insn), 0) - 1; i >= 0; i--)
{
rtx set;
set = XVECEXP (PATTERN (insn), 0, i);
if (GET_CODE (set) == SET
&& reg_overlap_mentioned_p (reg, SET_DEST (set)))
{
/* After the previous loop, this is a no-op if
REG is contained within SET_DEST (SET). */
mark_ref_live (*use_rec);
break;
}
}
}
extract_insn (insn); extract_insn (insn);
process_single_reg_class_operands (true, freq); process_single_reg_class_operands (false, freq);
/* Mark any allocnos dead after INSN as dead now. */
for (link = REG_NOTES (insn); link; link = XEXP (link, 1))
if (REG_NOTE_KIND (link) == REG_DEAD)
mark_reg_death (XEXP (link, 0));
curr_point++; /* See which defined values die here. */
for (def_rec = DF_INSN_DEFS (insn); *def_rec; def_rec++)
if (!call_p || !DF_REF_FLAGS_IS_SET (*def_rec, DF_REF_MAY_CLOBBER))
mark_ref_dead (*def_rec);
if (CALL_P (insn)) if (call_p)
{ {
/* The current set of live allocnos are live across the call. */
EXECUTE_IF_SET_IN_SPARSESET (allocnos_live, i) EXECUTE_IF_SET_IN_SPARSESET (allocnos_live, i)
{ {
ira_allocno_t a = ira_allocnos[i]; ira_allocno_t a = ira_allocnos[i];
...@@ -767,67 +724,62 @@ process_bb_node_lives (ira_loop_tree_node_t loop_tree_node) ...@@ -767,67 +724,62 @@ process_bb_node_lives (ira_loop_tree_node_t loop_tree_node)
} }
} }
/* Mark any allocnos set in INSN as live. Clobbers are curr_point++;
processed again, so they will conflict with the reg
allocnos that are set. */ /* Mark each used value as live. */
note_stores (PATTERN (insn), mark_reg_store, NULL); for (use_rec = DF_INSN_USES (insn); *use_rec; use_rec++)
mark_ref_live (*use_rec);
#ifdef AUTO_INC_DEC
for (link = REG_NOTES (insn); link; link = XEXP (link, 1)) /* If any defined values conflict with the inputs, mark those
if (REG_NOTE_KIND (link) == REG_INC) defined values as live. */
mark_reg_store (XEXP (link, 0), NULL_RTX, NULL); for (def_rec = DF_INSN_DEFS (insn); *def_rec; def_rec++)
#endif if (def_conflicts_with_inputs_p (*def_rec))
mark_ref_live (*def_rec);
/* If INSN has multiple outputs, then any allocno that dies
here and is used inside of an output must conflict with process_single_reg_class_operands (true, freq);
the other outputs.
It is unsafe to use !single_set here since it will ignore
an unused output. Just because an output is unused does
not mean the compiler can assume the side effect will not
occur. Consider if ALLOCNO appears in the address of an
output and we reload the output. If we allocate ALLOCNO
to the same hard register as an unused output we could
set the hard register before the output reload insn. */
if (GET_CODE (PATTERN (insn)) == PARALLEL && multiple_sets (insn))
for (link = REG_NOTES (insn); link; link = XEXP (link, 1))
if (REG_NOTE_KIND (link) == REG_DEAD)
{
int i;
int used_in_output = 0;
rtx reg = XEXP (link, 0);
for (i = XVECLEN (PATTERN (insn), 0) - 1; i >= 0; i--)
{
rtx set = XVECEXP (PATTERN (insn), 0, i);
if (GET_CODE (set) == SET
&& ! REG_P (SET_DEST (set))
&& ! rtx_equal_p (reg, SET_DEST (set))
&& reg_overlap_mentioned_p (reg, SET_DEST (set)))
used_in_output = 1;
}
if (used_in_output)
mark_reg_conflicts (reg);
}
process_single_reg_class_operands (false, freq);
/* Mark any allocnos set in INSN and then never used. */ /* See which of the defined values we marked as live are dead
while (! VEC_empty (rtx, regs_set)) before the instruction. */
{ for (def_rec = DF_INSN_DEFS (insn); *def_rec; def_rec++)
rtx reg = VEC_pop (rtx, regs_set); if (def_conflicts_with_inputs_p (*def_rec))
rtx note = find_regno_note (insn, REG_UNUSED, REGNO (reg)); mark_ref_dead (*def_rec);
if (note)
mark_reg_death (XEXP (note, 0));
}
curr_point++; curr_point++;
} }
/* Allocnos can't go in stack regs at the start of a basic block
that is reached by an abnormal edge. Likewise for call
clobbered regs, because caller-save, fixup_abnormal_edges and
possibly the table driven EH machinery are not quite ready to
handle such allocnos live across such edges. */
FOR_EACH_EDGE (e, ei, bb->preds)
if (e->flags & EDGE_ABNORMAL)
break;
if (e != NULL)
{
#ifdef STACK_REGS
EXECUTE_IF_SET_IN_SPARSESET (allocnos_live, px)
{
ALLOCNO_NO_STACK_REG_P (ira_allocnos[px]) = true;
ALLOCNO_TOTAL_NO_STACK_REG_P (ira_allocnos[px]) = true;
}
for (px = FIRST_STACK_REG; px <= LAST_STACK_REG; px++)
make_regno_born (px);
#endif
/* No need to record conflicts for call clobbered regs if we
have nonlocal labels around, as we don't ever try to
allocate such regs in this case. */
if (!cfun->has_nonlocal_label)
for (px = 0; px < FIRST_PSEUDO_REGISTER; px++)
if (call_used_regs[px])
make_regno_born (px);
}
EXECUTE_IF_SET_IN_SPARSESET (allocnos_live, i) EXECUTE_IF_SET_IN_SPARSESET (allocnos_live, i)
{ {
make_regno_dead (ALLOCNO_REGNO (ira_allocnos[i])); make_regno_dead (ALLOCNO_REGNO (ira_allocnos[i]));
} }
curr_point++; curr_point++;
...@@ -944,9 +896,6 @@ void ...@@ -944,9 +896,6 @@ void
ira_create_allocno_live_ranges (void) ira_create_allocno_live_ranges (void)
{ {
allocnos_live = sparseset_alloc (ira_allocnos_num); allocnos_live = sparseset_alloc (ira_allocnos_num);
/* Make a vector that mark_reg_{store,clobber} will store in. */
if (!regs_set)
regs_set = VEC_alloc (rtx, heap, 10);
curr_point = 0; curr_point = 0;
ira_traverse_loop_tree (true, ira_loop_tree_root, NULL, ira_traverse_loop_tree (true, ira_loop_tree_root, NULL,
process_bb_node_lives); process_bb_node_lives);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment