static void update_eliminable_offsets (void);
static void mark_not_eliminable (rtx, rtx, void *);
static void set_initial_elim_offsets (void);
-static void verify_initial_elim_offsets (void);
+static bool verify_initial_elim_offsets (void);
static void set_initial_label_offsets (void);
static void set_offsets_for_label (rtx);
static void init_elim_table (void);
rtx, rtx, int, int);
static int free_for_value_p (int, enum machine_mode, int, enum reload_type,
rtx, rtx, int, int);
-static int function_invariant_p (rtx);
static int reload_reg_reaches_end_p (unsigned int, int, enum reload_type);
static int allocate_reload_reg (struct insn_chain *, int, int);
static int conflicts_with_override (rtx);
that is not a legitimate memory operand. As later
stages of reload assume that all addresses found
in the reg_equiv_* arrays were originally legitimate,
- we ignore such REG_EQUIV notes. */
- if (memory_operand (x, VOIDmode))
+
+ It can also happen that a REG_EQUIV note contains a
+ readonly memory location. If the destination pseudo
+ is set from some other value (typically a different
+ pseudo), and the destination pseudo does not get a
+ hard reg, then reload will replace the destination
+ pseudo with its equivalent memory location. This
+ is horribly bad as it creates a store to a readonly
+ memory location and a runtime segfault. To avoid
+ this problem we reject readonly memory locations
+ for equivalences. This is overly conservative as
+ we could find all sets of the destination pseudo
+ and remove them as they should be redundant. */
+ if (memory_operand (x, VOIDmode) && ! MEM_READONLY_P (x))
{
/* Always unshare the equivalence, so we can
substitute into this insn without touching the
if (starting_frame_size != get_frame_size ())
something_changed = 1;
+ /* Even if the frame size remained the same, we might still have
+ changed elimination offsets, e.g. if find_reloads called
+ force_const_mem requiring the back end to allocate a constant
+ pool base register that needs to be saved on the stack. */
+ else if (!verify_initial_elim_offsets ())
+ something_changed = 1;
+
{
HARD_REG_SET to_spill;
CLEAR_HARD_REG_SET (to_spill);
gcc_assert (old_frame_size == get_frame_size ());
- if (num_eliminable)
- verify_initial_elim_offsets ();
+ gcc_assert (verify_initial_elim_offsets ());
}
/* If we were able to eliminate the frame pointer, show that it is no
CLEAR_REGNO_REG_SET (bb->global_live_at_start,
HARD_FRAME_POINTER_REGNUM);
- /* Come here (with failure set nonzero) if we can't get enough spill regs
- and we decide not to abort about it. */
+ /* Come here (with failure set nonzero) if we can't get enough spill
+ regs. */
failed:
CLEAR_REG_SET (&spilled_pseudos);
if (size > STACK_CHECK_MAX_FRAME_SIZE)
{
- warning ("frame size too large for reliable stack checking");
+ warning (0, "frame size too large for reliable stack checking");
if (! verbose_warned)
{
- warning ("try reducing the number of local variables");
+ warning (0, "try reducing the number of local variables");
verbose_warned = 1;
}
}
where something illegal happened during reload_as_needed that could
cause incorrect code to be generated if we did not check for it. */
-static void
+static bool
verify_initial_elim_offsets (void)
{
HOST_WIDE_INT t;
-#ifdef ELIMINABLE_REGS
- struct elim_table *ep;
+ if (!num_eliminable)
+ return true;
- for (ep = reg_eliminate; ep < ®_eliminate[NUM_ELIMINABLE_REGS]; ep++)
- {
- INITIAL_ELIMINATION_OFFSET (ep->from, ep->to, t);
- gcc_assert (t == ep->initial_offset);
- }
+#ifdef ELIMINABLE_REGS
+ {
+ struct elim_table *ep;
+
+ for (ep = reg_eliminate; ep < ®_eliminate[NUM_ELIMINABLE_REGS]; ep++)
+ {
+ INITIAL_ELIMINATION_OFFSET (ep->from, ep->to, t);
+ if (t != ep->initial_offset)
+ return false;
+ }
+ }
#else
INITIAL_FRAME_POINTER_OFFSET (t);
- gcc_assert (t == reg_eliminate[0].initial_offset);
+ if (t != reg_eliminate[0].initial_offset)
+ return false;
#endif
+
+ return true;
}
/* Reset all offsets on eliminable registers to their initial values. */
unsigned int nr;
/* note_stores does give us subregs of hard regs,
- subreg_regno_offset will abort if it is not a hard reg. */
+ subreg_regno_offset requires a hard reg. */
while (GET_CODE (x) == SUBREG)
{
/* We ignore the subreg offset when calculating the regno,
}
/* Return nonzero if the rtx X is invariant over the current function. */
-/* ??? Actually, the places where we use this expect exactly what
- * is tested here, and not everything that is function invariant. In
- * particular, the frame pointer and arg pointer are special cased;
- * pic_offset_table_rtx is not, and this will cause aborts when we
- * go to spill these things to memory. */
+/* ??? Actually, the places where we use this expect exactly what is
+ tested here, and not everything that is function invariant. In
+ particular, the frame pointer and arg pointer are special cased;
+ pic_offset_table_rtx is not, and we must not spill these things to
+ memory. */
-static int
+int
function_invariant_p (rtx x)
{
if (CONSTANT_P (x))
|| rld[j].when_needed == RELOAD_FOR_INPADDR_ADDRESS)
? RELOAD_FOR_OTHER_ADDRESS : RELOAD_OTHER);
- /* Check to see if we accidentally converted two reloads
- that use the same reload register with different inputs
- to the same type. If so, the resulting code won't work,
- so abort. */
+ /* Check to see if we accidentally converted two
+ reloads that use the same reload register with
+ different inputs to the same type. If so, the
+ resulting code won't work. */
if (rld[j].reg_rtx)
for (k = 0; k < j; k++)
gcc_assert (rld[k].in == 0 || rld[k].reg_rtx == 0