/* Copy propagation on hard registers for the GNU compiler.
- Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
- Free Software Foundation, Inc.
+ Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
+ 2010 Free Software Foundation, Inc.
This file is part of GCC.
#include "function.h"
#include "recog.h"
#include "flags.h"
-#include "toplev.h"
+#include "diagnostic-core.h"
#include "obstack.h"
#include "timevar.h"
#include "tree-pass.h"
up some silly register allocation decisions made by reload. This
code may be obsoleted by a new register allocator. */
+/* DEBUG_INSNs aren't changed right away, as doing so might extend the
+ lifetime of a register and get the DEBUG_INSN subsequently reset.
+ So they are queued instead, and updated only when the register is
+ used in some subsequent real insn before it is set. */
+struct queued_debug_insn_change
+{
+ struct queued_debug_insn_change *next;
+ rtx insn;
+ rtx *loc;
+ rtx new_rtx;
+};
+
/* For each register, we have a list of registers that contain the same
value. The OLDEST_REGNO field points to the head of the list, and
the NEXT_REGNO field runs through the list. The MODE field indicates
enum machine_mode mode;
unsigned int oldest_regno;
unsigned int next_regno;
+ struct queued_debug_insn_change *debug_insn_changes;
};
struct value_data
{
struct value_data_entry e[FIRST_PSEUDO_REGISTER];
unsigned int max_value_regs;
+ unsigned int n_debug_insn_changes;
};
+static alloc_pool debug_insn_changes_pool;
+
static void kill_value_one_regno (unsigned, struct value_data *);
static void kill_value_regno (unsigned, unsigned, struct value_data *);
static void kill_value (rtx, struct value_data *);
static bool replace_oldest_value_reg (rtx *, enum reg_class, rtx,
struct value_data *);
static bool replace_oldest_value_addr (rtx *, enum reg_class,
- enum machine_mode, rtx,
+ enum machine_mode, addr_space_t, rtx,
struct value_data *);
static bool replace_oldest_value_mem (rtx, rtx, struct value_data *);
static bool copyprop_hardreg_forward_1 (basic_block, struct value_data *);
static void validate_value_data (struct value_data *);
#endif
+/* Free all queued updates for DEBUG_INSNs that change some reg to
+ register REGNO. */
+
+static void
+free_debug_insn_changes (struct value_data *vd, unsigned int regno)
+{
+ struct queued_debug_insn_change *cur, *next;
+ for (cur = vd->e[regno].debug_insn_changes; cur; cur = next)
+ {
+ next = cur->next;
+ --vd->n_debug_insn_changes;
+ pool_free (debug_insn_changes_pool, cur);
+ }
+ vd->e[regno].debug_insn_changes = NULL;
+}
+
/* Kill register REGNO. This involves removing it from any value
lists, and resetting the value mode to VOIDmode. This is only a
helper function; it does not handle any hard registers overlapping
vd->e[regno].mode = VOIDmode;
vd->e[regno].oldest_regno = regno;
vd->e[regno].next_regno = INVALID_REGNUM;
+ if (vd->e[regno].debug_insn_changes)
+ free_debug_insn_changes (vd, regno);
#ifdef ENABLE_CHECKING
validate_value_data (vd);
vd->e[i].mode = VOIDmode;
vd->e[i].oldest_regno = i;
vd->e[i].next_regno = INVALID_REGNUM;
+ vd->e[i].debug_insn_changes = NULL;
}
vd->max_value_regs = 0;
+ vd->n_debug_insn_changes = 0;
}
/* Called through note_stores. If X is clobbered, kill its value. */
offset = ((WORDS_BIG_ENDIAN ? wordoffset : 0)
+ (BYTES_BIG_ENDIAN ? byteoffset : 0));
- return gen_rtx_raw_REG (new_mode,
- regno + subreg_regno_offset (regno, orig_mode,
- offset,
- new_mode));
+ regno += subreg_regno_offset (regno, orig_mode, offset, new_mode);
+ if (HARD_REGNO_MODE_OK (regno, new_mode))
+ return gen_rtx_raw_REG (new_mode, regno);
}
return NULL_RTX;
}
rtx new_rtx;
if (!in_hard_reg_set_p (reg_class_contents[cl], mode, i))
- return NULL_RTX;
+ continue;
new_rtx = maybe_mode_change (oldmode, vd->e[regno].mode, mode, i, regno);
if (new_rtx)
rtx new_rtx = find_oldest_value_reg (cl, *loc, vd);
if (new_rtx)
{
+ if (DEBUG_INSN_P (insn))
+ {
+ struct queued_debug_insn_change *change;
+
+ if (dump_file)
+ fprintf (dump_file, "debug_insn %u: queued replacing reg %u with %u\n",
+ INSN_UID (insn), REGNO (*loc), REGNO (new_rtx));
+
+ change = (struct queued_debug_insn_change *)
+ pool_alloc (debug_insn_changes_pool);
+ change->next = vd->e[REGNO (new_rtx)].debug_insn_changes;
+ change->insn = insn;
+ change->loc = loc;
+ change->new_rtx = new_rtx;
+ vd->e[REGNO (new_rtx)].debug_insn_changes = change;
+ ++vd->n_debug_insn_changes;
+ return true;
+ }
if (dump_file)
fprintf (dump_file, "insn %u: replaced reg %u with %u\n",
INSN_UID (insn), REGNO (*loc), REGNO (new_rtx));
static bool
replace_oldest_value_addr (rtx *loc, enum reg_class cl,
- enum machine_mode mode, rtx insn,
- struct value_data *vd)
+ enum machine_mode mode, addr_space_t as,
+ rtx insn, struct value_data *vd)
{
rtx x = *loc;
RTX_CODE code = GET_CODE (x);
unsigned regno0 = REGNO (op0), regno1 = REGNO (op1);
if (REGNO_OK_FOR_INDEX_P (regno1)
- && regno_ok_for_base_p (regno0, mode, PLUS, REG))
+ && regno_ok_for_base_p (regno0, mode, as, PLUS, REG))
index_op = 1;
else if (REGNO_OK_FOR_INDEX_P (regno0)
- && regno_ok_for_base_p (regno1, mode, PLUS, REG))
+ && regno_ok_for_base_p (regno1, mode, as, PLUS, REG))
index_op = 0;
- else if (regno_ok_for_base_p (regno0, mode, PLUS, REG)
+ else if (regno_ok_for_base_p (regno0, mode, as, PLUS, REG)
|| REGNO_OK_FOR_INDEX_P (regno1))
index_op = 1;
- else if (regno_ok_for_base_p (regno1, mode, PLUS, REG))
+ else if (regno_ok_for_base_p (regno1, mode, as, PLUS, REG))
index_op = 0;
else
index_op = 1;
}
if (locI)
- changed |= replace_oldest_value_addr (locI, INDEX_REG_CLASS, mode,
- insn, vd);
+ changed |= replace_oldest_value_addr (locI, INDEX_REG_CLASS,
+ mode, as, insn, vd);
if (locB)
changed |= replace_oldest_value_addr (locB,
- base_reg_class (mode, PLUS,
+ base_reg_class (mode, as, PLUS,
index_code),
- mode, insn, vd);
+ mode, as, insn, vd);
return changed;
}
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
if (fmt[i] == 'e')
- changed |= replace_oldest_value_addr (&XEXP (x, i), cl, mode,
+ changed |= replace_oldest_value_addr (&XEXP (x, i), cl, mode, as,
insn, vd);
else if (fmt[i] == 'E')
for (j = XVECLEN (x, i) - 1; j >= 0; j--)
changed |= replace_oldest_value_addr (&XVECEXP (x, i, j), cl,
- mode, insn, vd);
+ mode, as, insn, vd);
}
return changed;
if (DEBUG_INSN_P (insn))
cl = ALL_REGS;
else
- cl = base_reg_class (GET_MODE (x), MEM, SCRATCH);
+ cl = base_reg_class (GET_MODE (x), MEM_ADDR_SPACE (x), MEM, SCRATCH);
return replace_oldest_value_addr (&XEXP (x, 0), cl,
- GET_MODE (x), insn, vd);
+ GET_MODE (x), MEM_ADDR_SPACE (x),
+ insn, vd);
+}
+
+/* Apply all queued updates for DEBUG_INSNs that change some reg to
+ register REGNO. */
+
+static void
+apply_debug_insn_changes (struct value_data *vd, unsigned int regno)
+{
+ struct queued_debug_insn_change *change;
+ rtx last_insn = vd->e[regno].debug_insn_changes->insn;
+
+ for (change = vd->e[regno].debug_insn_changes;
+ change;
+ change = change->next)
+ {
+ if (last_insn != change->insn)
+ {
+ apply_change_group ();
+ last_insn = change->insn;
+ }
+ validate_change (change->insn, change->loc, change->new_rtx, 1);
+ }
+ apply_change_group ();
+}
+
+/* Called via for_each_rtx, for all used registers in a real
+ insn apply DEBUG_INSN changes that change registers to the
+ used register. */
+
+static int
+cprop_find_used_regs_1 (rtx *loc, void *data)
+{
+ if (REG_P (*loc))
+ {
+ struct value_data *vd = (struct value_data *) data;
+ if (vd->e[REGNO (*loc)].debug_insn_changes)
+ {
+ apply_debug_insn_changes (vd, REGNO (*loc));
+ free_debug_insn_changes (vd, REGNO (*loc));
+ }
+ }
+ return 0;
+}
+
+/* Called via note_uses, for all used registers in a real insn
+ apply DEBUG_INSN changes that change registers to the used
+ registers. */
+
+static void
+cprop_find_used_regs (rtx *loc, void *vd)
+{
+ for_each_rtx (loc, cprop_find_used_regs_1, vd);
}
/* Perform the forward copy propagation on basic block BB. */
if (DEBUG_INSN_P (insn))
{
rtx loc = INSN_VAR_LOCATION_LOC (insn);
- if (!VAR_LOC_UNKNOWN_P (loc)
- && replace_oldest_value_addr (&INSN_VAR_LOCATION_LOC (insn),
- ALL_REGS, GET_MODE (loc),
- insn, vd))
- {
- changed = apply_change_group ();
- gcc_assert (changed);
- df_insn_rescan (insn);
- anything_changed = true;
- }
+ if (!VAR_LOC_UNKNOWN_P (loc))
+ replace_oldest_value_addr (&INSN_VAR_LOCATION_LOC (insn),
+ ALL_REGS, GET_MODE (loc),
+ ADDR_SPACE_GENERIC, insn, vd);
}
if (insn == BB_END (bb))
recog_data.operand_type[i] = OP_INOUT;
}
+ /* Apply changes to earlier DEBUG_INSNs if possible. */
+ if (vd->n_debug_insn_changes)
+ note_uses (&PATTERN (insn), cprop_find_used_regs, vd);
+
/* For each earlyclobber operand, zap the value data. */
for (i = 0; i < n_ops; i++)
if (recog_op_alt[i][alt].earlyclobber)
if (hard_regno_nregs[regno][mode]
> hard_regno_nregs[regno][vd->e[regno].mode])
goto no_move_special_case;
+
+ /* And likewise, if we are narrowing on big endian the transformation
+ is also invalid. */
+ if (hard_regno_nregs[regno][mode]
+ < hard_regno_nregs[regno][vd->e[regno].mode]
+ && (GET_MODE_SIZE (vd->e[regno].mode) > UNITS_PER_WORD
+ ? WORDS_BIG_ENDIAN : BYTES_BIG_ENDIAN))
+ goto no_move_special_case;
}
/* If the destination is also a register, try to find a source
changed = true;
goto did_replacement;
}
+ /* We need to re-extract as validate_change clobbers
+ recog_data. */
+ extract_insn (insn);
+ if (! constrain_operands (1))
+ fatal_insn_not_found (insn);
+ preprocess_constraints ();
}
/* Otherwise, try all valid registers and see if its valid. */
changed = true;
goto did_replacement;
}
+ /* We need to re-extract as validate_change clobbers
+ recog_data. */
+ extract_insn (insn);
+ if (! constrain_operands (1))
+ fatal_insn_not_found (insn);
+ preprocess_constraints ();
}
}
}
replaced[i]
= replace_oldest_value_addr (recog_data.operand_loc[i],
recog_op_alt[i][alt].cl,
- VOIDmode, insn, vd);
+ VOIDmode, ADDR_SPACE_GENERIC,
+ insn, vd);
else if (REG_P (recog_data.operand[i]))
replaced[i]
= replace_oldest_value_reg (recog_data.operand_loc[i],
did_replacement:
if (changed)
{
- df_insn_rescan (insn);
anything_changed = true;
+
+ /* If something changed, perhaps further changes to earlier
+ DEBUG_INSNs can be applied. */
+ if (vd->n_debug_insn_changes)
+ note_uses (&PATTERN (insn), cprop_find_used_regs, vd);
}
/* Clobber call-clobbered registers. */
struct value_data *all_vd;
basic_block bb;
sbitmap visited;
+ bool analyze_called = false;
all_vd = XNEWVEC (struct value_data, last_basic_block);
visited = sbitmap_alloc (last_basic_block);
sbitmap_zero (visited);
+ if (MAY_HAVE_DEBUG_INSNS)
+ debug_insn_changes_pool
+ = create_alloc_pool ("debug insn changes pool",
+ sizeof (struct queued_debug_insn_change), 256);
+
FOR_EACH_BB (bb)
{
SET_BIT (visited, bb->index);
if (single_pred_p (bb)
&& TEST_BIT (visited, single_pred (bb)->index)
&& ! (single_pred_edge (bb)->flags & (EDGE_ABNORMAL_CALL | EDGE_EH)))
- all_vd[bb->index] = all_vd[single_pred (bb)->index];
+ {
+ all_vd[bb->index] = all_vd[single_pred (bb)->index];
+ if (all_vd[bb->index].n_debug_insn_changes)
+ {
+ unsigned int regno;
+
+ for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+ {
+ if (all_vd[bb->index].e[regno].debug_insn_changes)
+ {
+ all_vd[bb->index].e[regno].debug_insn_changes = NULL;
+ if (--all_vd[bb->index].n_debug_insn_changes == 0)
+ break;
+ }
+ }
+ }
+ }
else
init_value_data (all_vd + bb->index);
copyprop_hardreg_forward_1 (bb, all_vd + bb->index);
}
+ if (MAY_HAVE_DEBUG_INSNS)
+ {
+ FOR_EACH_BB (bb)
+ if (TEST_BIT (visited, bb->index)
+ && all_vd[bb->index].n_debug_insn_changes)
+ {
+ unsigned int regno;
+ bitmap live;
+
+ if (!analyze_called)
+ {
+ df_analyze ();
+ analyze_called = true;
+ }
+ live = df_get_live_out (bb);
+ for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+ if (all_vd[bb->index].e[regno].debug_insn_changes)
+ {
+ if (REGNO_REG_SET_P (live, regno))
+ apply_debug_insn_changes (all_vd + bb->index, regno);
+ if (all_vd[bb->index].n_debug_insn_changes == 0)
+ break;
+ }
+ }
+
+ free_alloc_pool (debug_insn_changes_pool);
+ }
+
sbitmap_free (visited);
free (all_vd);
return 0;
/* Dump the value chain data to stderr. */
-void
+DEBUG_FUNCTION void
debug_value_data (struct value_data *vd)
{
HARD_REG_SET set;
0, /* properties_provided */
0, /* properties_destroyed */
0, /* todo_flags_start */
- TODO_dump_func | TODO_verify_rtl_sharing /* todo_flags_finish */
+ TODO_df_finish
+ | TODO_verify_rtl_sharing /* todo_flags_finish */
}
};