/* Reload pseudo regs into hard regs for insns that require hard regs.
- Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
- 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
- 2011 Free Software Foundation, Inc.
+ Copyright (C) 1987-2014 Free Software Foundation, Inc.
This file is part of GCC.
#include "insn-config.h"
#include "ggc.h"
#include "flags.h"
+#include "hashtab.h"
+#include "hash-set.h"
+#include "vec.h"
+#include "input.h"
#include "function.h"
#include "expr.h"
+#include "insn-codes.h"
#include "optabs.h"
#include "regs.h"
#include "addresses.h"
+#include "predict.h"
+#include "dominance.h"
+#include "cfg.h"
+#include "cfgrtl.h"
+#include "cfgbuild.h"
#include "basic-block.h"
#include "df.h"
#include "reload.h"
#include "recog.h"
-#include "output.h"
#include "except.h"
#include "tree.h"
#include "ira.h"
#include "target.h"
#include "emit-rtl.h"
+#include "dumpfile.h"
+#include "rtl-iter.h"
/* This file contains the reload pass of the compiler, which is
run after register allocation has been done. It checks that
/* During reload_as_needed, element N contains the insn for which
hard register N was last used. Its contents are significant only
when reg_reloaded_valid is set for this register. */
-static rtx reg_reloaded_insn[FIRST_PSEUDO_REGISTER];
+static rtx_insn *reg_reloaded_insn[FIRST_PSEUDO_REGISTER];
/* Indicate if reg_reloaded_insn / reg_reloaded_contents is valid. */
static HARD_REG_SET reg_reloaded_valid;
/* In parallel with spill_regs, contains nonzero for a spill reg
that was stored after the last time it was used.
The precise value is the insn generated to do the store. */
-static rtx spill_reg_store[FIRST_PSEUDO_REGISTER];
+static rtx_insn *spill_reg_store[FIRST_PSEUDO_REGISTER];
/* This is the register that was stored with spill_reg_store. This is a
copy of reload_out / reload_out_reg when the value was stored; if
examine. */
struct insn_chain *reload_insn_chain;
+/* TRUE if we potentially left dead insns in the insn stream and want to
+ run DCE immediately after reload, FALSE otherwise. */
+static bool need_dce;
+
/* List of all insns needing reloads. */
static struct insn_chain *insns_need_reload;
\f
static char *offsets_known_at;
static HOST_WIDE_INT (*offsets_at)[NUM_ELIMINABLE_REGS];
-VEC(reg_equivs_t,gc) *reg_equivs;
+vec<reg_equivs_t, va_gc> *reg_equivs;
/* Stack of addresses where an rtx has been changed. We can undo the
changes by popping items off the stack and restoring the original
rtx expression would be changed. See PR 42431. */
typedef rtx *rtx_p;
-DEF_VEC_P(rtx_p);
-DEF_VEC_ALLOC_P(rtx_p,heap);
-static VEC(rtx_p,heap) *substitute_stack;
+static vec<rtx_p> substitute_stack;
/* Number of labels in the current function. */
static int num_labels;
\f
-static void replace_pseudos_in (rtx *, enum machine_mode, rtx);
+static void replace_pseudos_in (rtx *, machine_mode, rtx);
static void maybe_fix_stack_asms (void);
static void copy_reloads (struct insn_chain *);
static void calculate_needs_all_insns (int);
static void select_reload_regs (void);
static void delete_caller_save_insns (void);
-static void spill_failure (rtx, enum reg_class);
+static void spill_failure (rtx_insn *, enum reg_class);
static void count_spilled_pseudo (int, int, int);
-static void delete_dead_insn (rtx);
+static void delete_dead_insn (rtx_insn *);
static void alter_reg (int, int, bool);
-static void set_label_offsets (rtx, rtx, int);
+static void set_label_offsets (rtx, rtx_insn *, int);
static void check_eliminable_occurrences (rtx);
-static void elimination_effects (rtx, enum machine_mode);
-static rtx eliminate_regs_1 (rtx, enum machine_mode, rtx, bool, bool);
-static int eliminate_regs_in_insn (rtx, int);
+static void elimination_effects (rtx, machine_mode);
+static rtx eliminate_regs_1 (rtx, machine_mode, rtx, bool, bool);
+static int eliminate_regs_in_insn (rtx_insn *, int);
static void update_eliminable_offsets (void);
static void mark_not_eliminable (rtx, const_rtx, void *);
static void set_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_eliminable_invariants (rtx, bool);
+static void set_offsets_for_label (rtx_insn *);
+static void init_eliminable_invariants (rtx_insn *, bool);
static void init_elim_table (void);
static void free_reg_equiv (void);
static void update_eliminables (HARD_REG_SET *);
-static void elimination_costs_in_insn (rtx);
+static bool update_eliminables_and_spill (void);
+static void elimination_costs_in_insn (rtx_insn *);
static void spill_hard_reg (unsigned int, int);
static int finish_spills (int);
static void scan_paradoxical_subregs (rtx);
static void forget_marked_reloads (regset);
static int reload_reg_class_lower (const void *, const void *);
static void mark_reload_reg_in_use (unsigned int, int, enum reload_type,
- enum machine_mode);
+ machine_mode);
static void clear_reload_reg_in_use (unsigned int, int, enum reload_type,
- enum machine_mode);
+ machine_mode);
static int reload_reg_free_p (unsigned int, int, enum reload_type);
static int reload_reg_free_for_value_p (int, int, int, enum reload_type,
rtx, rtx, int, int);
-static int free_for_value_p (int, enum machine_mode, int, enum reload_type,
+static int free_for_value_p (int, machine_mode, int, enum reload_type,
rtx, rtx, int, int);
-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);
-static void failed_reload (rtx, int);
+static void failed_reload (rtx_insn *, int);
static int set_reload_reg (int, int);
static void choose_reload_regs_init (struct insn_chain *, rtx *);
static void choose_reload_regs (struct insn_chain *);
static void do_input_reload (struct insn_chain *, struct reload *, int);
static void do_output_reload (struct insn_chain *, struct reload *, int);
static void emit_reload_insns (struct insn_chain *);
-static void delete_output_reload (rtx, int, int, rtx);
-static void delete_address_reloads (rtx, rtx);
-static void delete_address_reloads_1 (rtx, rtx, rtx);
-static rtx inc_for_reload (rtx, rtx, rtx, int);
+static void delete_output_reload (rtx_insn *, int, int, rtx);
+static void delete_address_reloads (rtx_insn *, rtx_insn *);
+static void delete_address_reloads_1 (rtx_insn *, rtx, rtx_insn *);
+static void inc_for_reload (rtx, rtx, rtx, int);
#ifdef AUTO_INC_DEC
-static void add_auto_inc_notes (rtx, rtx);
+static void add_auto_inc_notes (rtx_insn *, rtx);
#endif
static void substitute (rtx *, const_rtx, rtx);
static bool gen_reload_chain_without_interm_reg_p (int, int);
static int reloads_conflict (int, int);
-static rtx gen_reload (rtx, rtx, int, enum reload_type);
-static rtx emit_insn_if_valid_for_reload (rtx);
+static rtx_insn *gen_reload (rtx, rtx, int, enum reload_type);
+static rtx_insn *emit_insn_if_valid_for_reload (rtx);
\f
/* Initialize the reload pass. This is called at the beginning of compilation
and may be called again if the target is reinitialized. */
gen_rtx_PLUS (Pmode,
gen_rtx_REG (Pmode,
LAST_VIRTUAL_REGISTER + 1),
- GEN_INT (4)));
+ gen_int_mode (4, Pmode)));
spill_indirect_levels = 0;
while (memory_address_p (QImode, tem))
gen_rtx_REG (Pmode, i));
/* This way, we make sure that reg+reg is an offsettable address. */
- tem = plus_constant (tem, 4);
+ tem = plus_constant (Pmode, tem, 4);
if (memory_address_p (QImode, tem))
{
}
/* Initialize obstack for our rtl allocation. */
- gcc_obstack_init (&reload_obstack);
- reload_startobj = XOBNEWVAR (&reload_obstack, char, 0);
+ if (reload_startobj == NULL)
+ {
+ gcc_obstack_init (&reload_obstack);
+ reload_startobj = XOBNEWVAR (&reload_obstack, char, 0);
+ }
INIT_REG_SET (&spilled_pseudos);
INIT_REG_SET (&changed_allocation_pseudos);
equivalences. */
static void
-replace_pseudos_in (rtx *loc, enum machine_mode mem_mode, rtx usage)
+replace_pseudos_in (rtx *loc, machine_mode mem_mode, rtx usage)
{
rtx x = *loc;
enum rtx_code code;
return true;
/* First determine which blocks can reach exit via normal paths. */
- tos = worklist = XNEWVEC (basic_block, n_basic_blocks + 1);
+ tos = worklist = XNEWVEC (basic_block, n_basic_blocks_for_fn (cfun) + 1);
- FOR_EACH_BB (bb)
+ FOR_EACH_BB_FN (bb, cfun)
bb->flags &= ~BB_REACHABLE;
/* Place the exit block on our worklist. */
- EXIT_BLOCK_PTR->flags |= BB_REACHABLE;
- *tos++ = EXIT_BLOCK_PTR;
+ EXIT_BLOCK_PTR_FOR_FN (cfun)->flags |= BB_REACHABLE;
+ *tos++ = EXIT_BLOCK_PTR_FOR_FN (cfun);
/* Iterate: find everything reachable from what we've already seen. */
while (tos != worklist)
/* Now see if there's a reachable block with an exceptional incoming
edge. */
- FOR_EACH_BB (bb)
+ FOR_EACH_BB_FN (bb, cfun)
if (bb->flags & BB_REACHABLE && bb_has_abnormal_pred (bb))
return true;
void
grow_reg_equivs (void)
{
- int old_size = VEC_length (reg_equivs_t, reg_equivs);
+ int old_size = vec_safe_length (reg_equivs);
int max_regno = max_reg_num ();
int i;
+ reg_equivs_t ze;
- VEC_reserve (reg_equivs_t, gc, reg_equivs, max_regno);
+ memset (&ze, 0, sizeof (reg_equivs_t));
+ vec_safe_reserve (reg_equivs, max_regno);
for (i = old_size; i < max_regno; i++)
- {
- VEC_quick_insert (reg_equivs_t, reg_equivs, i, 0);
- memset (VEC_index (reg_equivs_t, reg_equivs, i), 0, sizeof (reg_equivs_t));
- }
-
+ reg_equivs->quick_insert (i, ze);
}
\f
/* Temporary array of pseudo-register number. */
static int *temp_pseudo_reg_arr;
+/* If a pseudo has no hard reg, delete the insns that made the equivalence.
+ If that insn didn't set the register (i.e., it copied the register to
+ memory), just delete that insn instead of the equivalencing insn plus
+ anything now dead. If we call delete_dead_insn on that insn, we may
+ delete the insn that actually sets the register if the register dies
+ there and that is incorrect. */
+static void
+remove_init_insns ()
+{
+ for (int i = FIRST_PSEUDO_REGISTER; i < max_regno; i++)
+ {
+ if (reg_renumber[i] < 0 && reg_equiv_init (i) != 0)
+ {
+ rtx list;
+ for (list = reg_equiv_init (i); list; list = XEXP (list, 1))
+ {
+ rtx_insn *equiv_insn = as_a <rtx_insn *> (XEXP (list, 0));
+
+ /* If we already deleted the insn or if it may trap, we can't
+ delete it. The latter case shouldn't happen, but can
+ if an insn has a variable address, gets a REG_EH_REGION
+ note added to it, and then gets converted into a load
+ from a constant address. */
+ if (NOTE_P (equiv_insn)
+ || can_throw_internal (equiv_insn))
+ ;
+ else if (reg_set_p (regno_reg_rtx[i], PATTERN (equiv_insn)))
+ delete_dead_insn (equiv_insn);
+ else
+ SET_INSN_DELETED (equiv_insn);
+ }
+ }
+ }
+}
+
+/* Return true if remove_init_insns will delete INSN. */
+static bool
+will_delete_init_insn_p (rtx_insn *insn)
+{
+ rtx set = single_set (insn);
+ if (!set || !REG_P (SET_DEST (set)))
+ return false;
+ unsigned regno = REGNO (SET_DEST (set));
+
+ if (can_throw_internal (insn))
+ return false;
+
+ if (regno < FIRST_PSEUDO_REGISTER || reg_renumber[regno] >= 0)
+ return false;
+
+ for (rtx list = reg_equiv_init (regno); list; list = XEXP (list, 1))
+ {
+ rtx equiv_insn = XEXP (list, 0);
+ if (equiv_insn == insn)
+ return true;
+ }
+ return false;
+}
+
/* Main entry point for the reload pass.
FIRST is the first insn of the function being compiled.
If GLOBAL is zero, we do not have enough information to do that,
so any pseudo reg that is spilled must go to the stack.
- Return value is nonzero if reload failed
- and we must not do any more for this function. */
+ Return value is TRUE if reload likely left dead insns in the
+ stream and a DCE pass should be run to elimiante them. Else the
+ return value is FALSE. */
-int
-reload (rtx first, int global)
+bool
+reload (rtx_insn *first, int global)
{
int i, n;
- rtx insn;
+ rtx_insn *insn;
struct elim_table *ep;
basic_block bb;
bool inserted;
be substituted eventually by altering the REG-rtx's. */
grow_reg_equivs ();
- reg_max_ref_width = XCNEWVEC (unsigned int, max_regno);
reg_old_renumber = XCNEWVEC (short, max_regno);
memcpy (reg_old_renumber, reg_renumber, max_regno * sizeof (short));
pseudo_forbidden_regs = XNEWVEC (HARD_REG_SET, max_regno);
if (caller_save_needed)
setup_save_areas ();
- /* If we allocated another stack slot, redo elimination bookkeeping. */
- if (something_was_spilled || starting_frame_size != get_frame_size ())
- continue;
if (starting_frame_size && crtl->stack_alignment_needed)
{
/* If we have a stack frame, we must align it now. The
STARTING_FRAME_OFFSET not be already aligned to
STACK_BOUNDARY. */
assign_stack_local (BLKmode, 0, crtl->stack_alignment_needed);
- if (starting_frame_size != get_frame_size ())
- continue;
+ }
+ /* If we allocated another stack slot, redo elimination bookkeeping. */
+ if (something_was_spilled || starting_frame_size != get_frame_size ())
+ {
+ update_eliminables_and_spill ();
+ continue;
}
if (caller_save_needed)
else if (!verify_initial_elim_offsets ())
something_changed = 1;
- {
- HARD_REG_SET to_spill;
- CLEAR_HARD_REG_SET (to_spill);
- update_eliminables (&to_spill);
- AND_COMPL_HARD_REG_SET (used_spill_regs, to_spill);
-
- for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
- if (TEST_HARD_REG_BIT (to_spill, i))
- {
- spill_hard_reg (i, 1);
- did_spill = 1;
-
- /* Regardless of the state of spills, if we previously had
- a register that we thought we could eliminate, but now can
- not eliminate, we must run another pass.
-
- Consider pseudos which have an entry in reg_equiv_* which
- reference an eliminable register. We must make another pass
- to update reg_equiv_* so that we do not substitute in the
- old value from when we thought the elimination could be
- performed. */
- something_changed = 1;
- }
- }
+ if (update_eliminables_and_spill ())
+ {
+ did_spill = 1;
+ something_changed = 1;
+ }
select_reload_regs ();
if (failure)
if (ep->can_eliminate)
mark_elimination (ep->from, ep->to);
- /* If a pseudo has no hard reg, delete the insns that made the equivalence.
- If that insn didn't set the register (i.e., it copied the register to
- memory), just delete that insn instead of the equivalencing insn plus
- anything now dead. If we call delete_dead_insn on that insn, we may
- delete the insn that actually sets the register if the register dies
- there and that is incorrect. */
-
- for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++)
- {
- if (reg_renumber[i] < 0 && reg_equiv_init (i) != 0)
- {
- rtx list;
- for (list = reg_equiv_init (i); list; list = XEXP (list, 1))
- {
- rtx equiv_insn = XEXP (list, 0);
-
- /* If we already deleted the insn or if it may trap, we can't
- delete it. The latter case shouldn't happen, but can
- if an insn has a variable address, gets a REG_EH_REGION
- note added to it, and then gets converted into a load
- from a constant address. */
- if (NOTE_P (equiv_insn)
- || can_throw_internal (equiv_insn))
- ;
- else if (reg_set_p (regno_reg_rtx[i], PATTERN (equiv_insn)))
- delete_dead_insn (equiv_insn);
- else
- SET_INSN_DELETED (equiv_insn);
- }
- }
- }
+ remove_init_insns ();
/* Use the reload registers where necessary
by generating move instructions to move the must-be-register
pseudo. */
if (! frame_pointer_needed)
- FOR_EACH_BB (bb)
+ FOR_EACH_BB_FN (bb, cfun)
bitmap_clear_bit (df_get_live_in (bb), HARD_FRAME_POINTER_REGNUM);
/* Come here (with failure set nonzero) if we can't get enough spill
if (reg_equiv_memory_loc (i))
MEM_COPY_ATTRIBUTES (reg, reg_equiv_memory_loc (i));
else
- {
- MEM_IN_STRUCT_P (reg) = MEM_SCALAR_P (reg) = 0;
- MEM_ATTRS (reg) = 0;
- }
+ MEM_ATTRS (reg) = 0;
MEM_NOTRAP_P (reg) = 1;
}
else if (reg_equiv_mem (i))
if (asm_noperands (PATTERN (insn)) >= 0)
{
extract_insn (insn);
- if (!constrain_operands (1))
+ if (!constrain_operands (1, get_enabled_alternatives (insn)))
{
error_for_asm (insn,
"%<asm%> operand has impossible constraints");
if (cfun->can_throw_non_call_exceptions)
{
sbitmap blocks;
- blocks = sbitmap_alloc (last_basic_block);
- sbitmap_ones (blocks);
+ blocks = sbitmap_alloc (last_basic_block_for_fn (cfun));
+ bitmap_ones (blocks);
find_many_sub_basic_blocks (blocks);
sbitmap_free (blocks);
}
REGNO_POINTER_ALIGN (HARD_FRAME_POINTER_REGNUM) = BITS_PER_UNIT;
#endif
- VEC_free (rtx_p, heap, substitute_stack);
+ substitute_stack.release ();
gcc_assert (bitmap_empty_p (&spilled_pseudos));
- return failure;
+ reload_completed = !failure;
+
+ return need_dce;
}
/* Yet another special case. Unfortunately, reg-stack forces people to
{
#ifdef STACK_REGS
const char *constraints[MAX_RECOG_OPERANDS];
- enum machine_mode operand_mode[MAX_RECOG_OPERANDS];
+ machine_mode operand_mode[MAX_RECOG_OPERANDS];
struct insn_chain *chain;
for (chain = reload_insn_chain; chain != 0; chain = chain->next)
switch (c)
{
- case '=': case '+': case '*': case '%': case '?': case '!':
- case '0': case '1': case '2': case '3': case '4': case '<':
- case '>': case 'V': case 'o': case '&': case 'E': case 'F':
- case 's': case 'i': case 'n': case 'X': case 'I': case 'J':
- case 'K': case 'L': case 'M': case 'N': case 'O': case 'P':
- case TARGET_MEM_CONSTRAINT:
- break;
-
- case 'p':
- cls = (int) reg_class_subunion[cls]
- [(int) base_reg_class (VOIDmode, ADDRESS, SCRATCH)];
- break;
-
case 'g':
- case 'r':
cls = (int) reg_class_subunion[cls][(int) GENERAL_REGS];
break;
default:
- if (EXTRA_ADDRESS_CONSTRAINT (c, p))
+ enum constraint_num cn = lookup_constraint (p);
+ if (insn_extra_address_constraint (cn))
cls = (int) reg_class_subunion[cls]
- [(int) base_reg_class (VOIDmode, ADDRESS, SCRATCH)];
+ [(int) base_reg_class (VOIDmode, ADDR_SPACE_GENERIC,
+ ADDRESS, SCRATCH)];
else
cls = (int) reg_class_subunion[cls]
- [(int) REG_CLASS_FROM_CONSTRAINT (c, p)];
+ [reg_class_for_constraint (cn)];
+ break;
}
p += CONSTRAINT_LEN (c, p);
}
reload_insn_firstobj = XOBNEWVAR (&reload_obstack, char, 0);
for (chain = reload_insn_chain; chain != 0; chain = next)
{
- rtx insn = chain->insn;
+ rtx_insn *insn = chain->insn;
next = chain->next;
include REG_LABEL_OPERAND and REG_LABEL_TARGET), we need to see
what effects this has on the known offsets at labels. */
- if (LABEL_P (insn) || JUMP_P (insn)
+ if (LABEL_P (insn) || JUMP_P (insn) || JUMP_TABLE_DATA_P (insn)
|| (INSN_P (insn) && REG_NOTES (insn) != 0))
set_label_offsets (insn, insn, 0);
rtx old_notes = REG_NOTES (insn);
int did_elimination = 0;
int operands_changed = 0;
- rtx set = single_set (insn);
/* Skip insns that only set an equivalence. */
- if (set && REG_P (SET_DEST (set))
- && reg_renumber[REGNO (SET_DEST (set))] < 0
- && (reg_equiv_constant (REGNO (SET_DEST (set)))
- || (reg_equiv_invariant (REGNO (SET_DEST (set)))))
- && reg_equiv_init (REGNO (SET_DEST (set))))
+ if (will_delete_init_insn_p (insn))
continue;
/* If needed, eliminate any eliminable registers. */
set_initial_elim_offsets ();
set_initial_label_offsets ();
- FOR_EACH_BB (bb)
+ FOR_EACH_BB_FN (bb, cfun)
{
- rtx insn;
+ rtx_insn *insn;
elim_bb = bb;
FOR_BB_INSNS (bb, insn)
include REG_LABEL_OPERAND and REG_LABEL_TARGET), we need to see
what effects this has on the known offsets at labels. */
- if (LABEL_P (insn) || JUMP_P (insn)
+ if (LABEL_P (insn) || JUMP_P (insn) || JUMP_TABLE_DATA_P (insn)
|| (INSN_P (insn) && REG_NOTES (insn) != 0))
set_label_offsets (insn, insn, 0);
{
rtx t = eliminate_regs_1 (SET_SRC (set), VOIDmode, insn,
false, true);
- int cost = rtx_cost (t, SET,
- optimize_bb_for_speed_p (bb));
+ int cost = set_src_cost (t, optimize_bb_for_speed_p (bb));
int freq = REG_FREQ_FROM_BB (bb);
reg_equiv_init_cost[regno] = cost * freq;
}
free (reg_equiv_init_cost);
+ free (offsets_known_at);
+ free (offsets_at);
+ offsets_at = NULL;
+ offsets_known_at = NULL;
}
\f
/* Comparison function for qsort to decide which of two reloads
int r = reg_renumber[reg];
int nregs;
+ /* Ignore spilled pseudo-registers which can be here only if IRA is used. */
+ if (ira_conflicts_p && r < 0)
+ return;
+
if (REGNO_REG_SET_P (&pseudos_counted, reg)
- || REGNO_REG_SET_P (&spilled_pseudos, reg)
- /* Ignore spilled pseudo-registers which can be here only if IRA
- is used. */
- || (ira_conflicts_p && r < 0))
+ || REGNO_REG_SET_P (&spilled_pseudos, reg))
return;
SET_REGNO_REG_SET (&pseudos_counted, reg);
{
int freq = REG_FREQ (reg);
int r = reg_renumber[reg];
- int nregs = hard_regno_nregs[r][PSEUDO_REGNO_MODE (reg)];
+ int nregs;
- /* Ignore spilled pseudo-registers which can be here only if IRA is
- used. */
- if ((ira_conflicts_p && r < 0)
- || REGNO_REG_SET_P (&spilled_pseudos, reg)
+ /* Ignore spilled pseudo-registers which can be here only if IRA is used. */
+ if (ira_conflicts_p && r < 0)
+ return;
+
+ gcc_assert (r >= 0);
+
+ nregs = hard_regno_nregs[r][PSEUDO_REGNO_MODE (reg)];
+
+ if (REGNO_REG_SET_P (&spilled_pseudos, reg)
|| spilled + spilled_nregs <= r || r + nregs <= spilled)
return;
while (c != 0 && c->is_caller_save_insn)
{
struct insn_chain *next = c->next;
- rtx insn = c->insn;
+ rtx_insn *insn = c->insn;
if (c == reload_insn_chain)
reload_insn_chain = next;
INSN should be one of the insns which needed this particular spill reg. */
static void
-spill_failure (rtx insn, enum reg_class rclass)
+spill_failure (rtx_insn *insn, enum reg_class rclass)
{
if (asm_noperands (PATTERN (insn)) >= 0)
error_for_asm (insn, "can%'t find a register in class %qs while "
data that is dead in INSN. */
static void
-delete_dead_insn (rtx insn)
+delete_dead_insn (rtx_insn *insn)
{
- rtx prev = prev_active_insn (insn);
+ rtx_insn *prev = prev_active_insn (insn);
rtx prev_dest;
- /* If the previous insn sets a register that dies in our insn, delete it
- too. */
- if (prev && GET_CODE (PATTERN (prev)) == SET
+ /* If the previous insn sets a register that dies in our insn make
+ a note that we want to run DCE immediately after reload.
+
+ We used to delete the previous insn & recurse, but that's wrong for
+ block local equivalences. Instead of trying to figure out the exact
+ circumstances where we can delete the potentially dead insns, just
+ let DCE do the job. */
+ if (prev && BLOCK_FOR_INSN (prev) == BLOCK_FOR_INSN (insn)
+ && GET_CODE (PATTERN (prev)) == SET
&& (prev_dest = SET_DEST (PATTERN (prev)), REG_P (prev_dest))
&& reg_mentioned_p (prev_dest, PATTERN (insn))
&& find_regno_note (insn, REG_DEAD, REGNO (prev_dest))
&& ! side_effects_p (SET_SRC (PATTERN (prev))))
- delete_dead_insn (prev);
+ need_dce = 1;
SET_INSN_DELETED (insn);
}
&& reg_equiv_memory_loc (i) == 0)
{
rtx x = NULL_RTX;
- enum machine_mode mode = GET_MODE (regno_reg_rtx[i]);
+ machine_mode mode = GET_MODE (regno_reg_rtx[i]);
unsigned int inherent_size = PSEUDO_REGNO_BYTES (i);
unsigned int inherent_align = GET_MODE_ALIGNMENT (mode);
unsigned int total_size = MAX (inherent_size, reg_max_ref_width[i]);
pseudo-reg number REGNO, accessed in MODE. */
static void
-mark_home_live_1 (int regno, enum machine_mode mode)
+mark_home_live_1 (int regno, machine_mode mode)
{
int i, lim;
return;
lim = end_hard_regno (mode, i);
while (i < lim)
- df_set_regs_ever_live(i++, true);
+ df_set_regs_ever_live (i++, true);
}
/* Mark the slots in regs_ever_live for the hard regs
current offset. */
static void
-set_label_offsets (rtx x, rtx insn, int initial_p)
+set_label_offsets (rtx x, rtx_insn *insn, int initial_p)
{
enum rtx_code code = GET_CODE (x);
rtx tem;
if (LABEL_REF_NONLOCAL_P (x))
return;
- x = XEXP (x, 0);
+ x = LABEL_REF_LABEL (x);
/* ... fall through ... */
return;
+ case JUMP_TABLE_DATA:
+ set_label_offsets (PATTERN (insn), insn, initial_p);
+ return;
+
case JUMP_INSN:
set_label_offsets (PATTERN (insn), insn, initial_p);
case IF_THEN_ELSE:
tem = XEXP (SET_SRC (x), 1);
if (GET_CODE (tem) == LABEL_REF)
- set_label_offsets (XEXP (tem, 0), insn, initial_p);
+ set_label_offsets (LABEL_REF_LABEL (tem), insn, initial_p);
else if (GET_CODE (tem) != PC && GET_CODE (tem) != RETURN)
break;
tem = XEXP (SET_SRC (x), 2);
if (GET_CODE (tem) == LABEL_REF)
- set_label_offsets (XEXP (tem, 0), insn, initial_p);
+ set_label_offsets (LABEL_REF_LABEL (tem), insn, initial_p);
else if (GET_CODE (tem) != PC && GET_CODE (tem) != RETURN)
break;
return;
}
}
\f
-/* Called through for_each_rtx, this function examines every reg that occurs
- in PX and adjusts the costs for its elimination which are gathered by IRA.
- DATA is the insn in which PX occurs. We do not recurse into MEM
- expressions. */
+/* This function examines every reg that occurs in X and adjusts the
+ costs for its elimination which are gathered by IRA. INSN is the
+ insn in which X occurs. We do not recurse into MEM expressions. */
-static int
-note_reg_elim_costly (rtx *px, void *data)
+static void
+note_reg_elim_costly (const_rtx x, rtx insn)
{
- rtx insn = (rtx)data;
- rtx x = *px;
-
- if (MEM_P (x))
- return -1;
-
- if (REG_P (x)
- && REGNO (x) >= FIRST_PSEUDO_REGISTER
- && reg_equiv_init (REGNO (x))
- && reg_equiv_invariant (REGNO (x)))
- {
- rtx t = reg_equiv_invariant (REGNO (x));
- rtx new_rtx = eliminate_regs_1 (t, Pmode, insn, true, true);
- int cost = rtx_cost (new_rtx, SET, optimize_bb_for_speed_p (elim_bb));
- int freq = REG_FREQ_FROM_BB (elim_bb);
+ subrtx_iterator::array_type array;
+ FOR_EACH_SUBRTX (iter, array, x, NONCONST)
+ {
+ const_rtx x = *iter;
+ if (MEM_P (x))
+ iter.skip_subrtxes ();
+ else if (REG_P (x)
+ && REGNO (x) >= FIRST_PSEUDO_REGISTER
+ && reg_equiv_init (REGNO (x))
+ && reg_equiv_invariant (REGNO (x)))
+ {
+ rtx t = reg_equiv_invariant (REGNO (x));
+ rtx new_rtx = eliminate_regs_1 (t, Pmode, insn, true, true);
+ int cost = set_src_cost (new_rtx, optimize_bb_for_speed_p (elim_bb));
+ int freq = REG_FREQ_FROM_BB (elim_bb);
- if (cost != 0)
- ira_adjust_equiv_reg_cost (REGNO (x), -cost * freq);
+ if (cost != 0)
+ ira_adjust_equiv_reg_cost (REGNO (x), -cost * freq);
+ }
}
- return 0;
}
/* Scan X and replace any eliminable registers (such as fp) with a
the proper thing. */
static rtx
-eliminate_regs_1 (rtx x, enum machine_mode mem_mode, rtx insn,
+eliminate_regs_1 (rtx x, machine_mode mem_mode, rtx insn,
bool may_use_invariant, bool for_costs)
{
enum rtx_code code = GET_CODE (x);
switch (code)
{
- case CONST_INT:
- case CONST_DOUBLE:
- case CONST_FIXED:
- case CONST_VECTOR:
+ CASE_CONST_ANY:
case CONST:
case SYMBOL_REF:
case CODE_LABEL:
for (ep = reg_eliminate; ep < ®_eliminate[NUM_ELIMINABLE_REGS];
ep++)
if (ep->from_rtx == x && ep->can_eliminate)
- return plus_constant (ep->to_rtx, ep->previous_offset);
+ return plus_constant (Pmode, ep->to_rtx, ep->previous_offset);
}
else if (reg_renumber && reg_renumber[regno] < 0
return ep->to_rtx;
else
return gen_rtx_PLUS (Pmode, ep->to_rtx,
- plus_constant (XEXP (x, 1),
+ plus_constant (Pmode, XEXP (x, 1),
ep->previous_offset));
}
ep->ref_outside_mem = 1;
return
- plus_constant (gen_rtx_MULT (Pmode, ep->to_rtx, XEXP (x, 1)),
+ plus_constant (Pmode,
+ gen_rtx_MULT (Pmode, ep->to_rtx, XEXP (x, 1)),
ep->previous_offset * INTVAL (XEXP (x, 1)));
}
/* ... fall through ... */
case INSN_LIST:
+ case INT_LIST:
/* Now do eliminations in the rest of the chain. If this was
an EXPR_LIST, this might result in allocating more memory than is
strictly needed, but it simplifies the code. */
eliminated version of the memory location because push_reload
may do the replacement in certain circumstances. */
if (REG_P (SUBREG_REG (x))
- && (GET_MODE_SIZE (GET_MODE (x))
- <= GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))))
+ && !paradoxical_subreg_p (x)
&& reg_equivs
&& reg_equiv_memory_loc (REGNO (SUBREG_REG (x))) != 0)
{
if (for_costs
&& memory_address_p (GET_MODE (x), XEXP (x, 0))
&& !memory_address_p (GET_MODE (x), new_rtx))
- for_each_rtx (&XEXP (x, 0), note_reg_elim_costly, insn);
+ note_reg_elim_costly (XEXP (x, 0), insn);
return replace_equiv_address_nv (x, new_rtx);
}
rtx
-eliminate_regs (rtx x, enum machine_mode mem_mode, rtx insn)
+eliminate_regs (rtx x, machine_mode mem_mode, rtx insn)
{
+ if (reg_eliminate == NULL)
+ {
+ gcc_assert (targetm.no_register_allocation);
+ return x;
+ }
return eliminate_regs_1 (x, mem_mode, insn, false, false);
}
the mode of an enclosing MEM rtx, or VOIDmode if not within a MEM. */
static void
-elimination_effects (rtx x, enum machine_mode mem_mode)
+elimination_effects (rtx x, machine_mode mem_mode)
{
enum rtx_code code = GET_CODE (x);
struct elim_table *ep;
switch (code)
{
- case CONST_INT:
- case CONST_DOUBLE:
- case CONST_FIXED:
- case CONST_VECTOR:
+ CASE_CONST_ANY:
case CONST:
case SYMBOL_REF:
case CODE_LABEL:
}
else if (reg_renumber[regno] < 0
- && reg_equiv_constant (0)
+ && reg_equivs
&& reg_equiv_constant (regno)
&& ! function_invariant_p (reg_equiv_constant (regno)))
elimination_effects (reg_equiv_constant (regno), mem_mode);
if (REG_P (SUBREG_REG (x))
&& (GET_MODE_SIZE (GET_MODE (x))
<= GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))))
- && reg_equivs != 0
+ && reg_equivs
&& reg_equiv_memory_loc (REGNO (SUBREG_REG (x))) != 0)
return;
is returned. Otherwise, 1 is returned. */
static int
-eliminate_regs_in_insn (rtx insn, int replace)
+eliminate_regs_in_insn (rtx_insn *insn, int replace)
{
int icode = recog_memoized (insn);
rtx old_body = PATTERN (insn);
if (! insn_is_asm && icode < 0)
{
- gcc_assert (GET_CODE (PATTERN (insn)) == USE
+ gcc_assert (DEBUG_INSN_P (insn)
+ || GET_CODE (PATTERN (insn)) == USE
|| GET_CODE (PATTERN (insn)) == CLOBBER
- || GET_CODE (PATTERN (insn)) == ADDR_VEC
- || GET_CODE (PATTERN (insn)) == ADDR_DIFF_VEC
- || GET_CODE (PATTERN (insn)) == ASM_INPUT
- || DEBUG_INSN_P (insn));
+ || GET_CODE (PATTERN (insn)) == ASM_INPUT);
if (DEBUG_INSN_P (insn))
INSN_VAR_LOCATION_LOC (insn)
= eliminate_regs (INSN_VAR_LOCATION_LOC (insn), VOIDmode, insn);
&& ep->to == HARD_FRAME_POINTER_REGNUM)
{
rtx base = SET_SRC (old_set);
- rtx base_insn = insn;
+ rtx_insn *base_insn = insn;
HOST_WIDE_INT offset = 0;
while (base != ep->to_rtx)
{
- rtx prev_insn, prev_set;
+ rtx_insn *prev_insn;
+ rtx prev_set;
if (GET_CODE (base) == PLUS
&& CONST_INT_P (XEXP (base, 1)))
if (base == ep->to_rtx)
{
- rtx src
- = plus_constant (ep->to_rtx, offset - ep->offset);
+ rtx src = plus_constant (Pmode, ep->to_rtx,
+ offset - ep->offset);
new_body = old_body;
if (! replace)
had a PLUS before. */
if (offset == 0 || plus_src)
{
- rtx new_src = plus_constant (to_rtx, offset);
+ rtx new_src = plus_constant (GET_MODE (to_rtx),
+ to_rtx, offset);
new_body = old_body;
if (! replace)
an invariant equivalence would add extra cost. */
static void
-elimination_costs_in_insn (rtx insn)
+elimination_costs_in_insn (rtx_insn *insn)
{
int icode = recog_memoized (insn);
rtx old_body = PATTERN (insn);
if (! insn_is_asm && icode < 0)
{
- gcc_assert (GET_CODE (PATTERN (insn)) == USE
+ gcc_assert (DEBUG_INSN_P (insn)
+ || GET_CODE (PATTERN (insn)) == USE
|| GET_CODE (PATTERN (insn)) == CLOBBER
- || GET_CODE (PATTERN (insn)) == ADDR_VEC
- || GET_CODE (PATTERN (insn)) == ADDR_DIFF_VEC
- || GET_CODE (PATTERN (insn)) == ASM_INPUT
- || DEBUG_INSN_P (insn));
+ || GET_CODE (PATTERN (insn)) == ASM_INPUT);
return;
}
if (old_set && recog_data.operand_loc[i] == &SET_SRC (old_set))
is_set_src = true;
if (is_set_src && !sets_reg_p)
- note_reg_elim_costly (&SET_SRC (old_set), insn);
+ note_reg_elim_costly (SET_SRC (old_set), insn);
in_plus = false;
if (plus_src && sets_reg_p
&& (recog_data.operand_loc[i] == &XEXP (plus_src, 0)
static void
set_initial_eh_label_offset (rtx label)
{
- set_label_offsets (label, NULL_RTX, 1);
+ set_label_offsets (label, NULL, 1);
}
/* Initialize the known label offsets.
static void
set_initial_label_offsets (void)
{
- rtx x;
memset (offsets_known_at, 0, num_labels);
- for (x = forced_labels; x; x = XEXP (x, 1))
- if (XEXP (x, 0))
- set_label_offsets (XEXP (x, 0), NULL_RTX, 1);
+ for (rtx_insn_list *x = forced_labels; x; x = x->next ())
+ if (x->insn ())
+ set_label_offsets (x->insn (), NULL, 1);
+
+ for (rtx_insn_list *x = nonlocal_goto_handler_labels; x; x = x->next ())
+ if (x->insn ())
+ set_label_offsets (x->insn (), NULL, 1);
for_each_eh_label (set_initial_eh_label_offset);
}
by INSN. */
static void
-set_offsets_for_label (rtx insn)
+set_offsets_for_label (rtx_insn *insn)
{
unsigned int i;
int label_nr = CODE_LABEL_NUMBER (insn);
SET_HARD_REG_BIT (*pset, HARD_FRAME_POINTER_REGNUM);
}
+/* Call update_eliminables an spill any registers we can't eliminate anymore.
+ Return true iff a register was spilled. */
+
+static bool
+update_eliminables_and_spill (void)
+{
+ int i;
+ bool did_spill = false;
+ HARD_REG_SET to_spill;
+ CLEAR_HARD_REG_SET (to_spill);
+ update_eliminables (&to_spill);
+ AND_COMPL_HARD_REG_SET (used_spill_regs, to_spill);
+
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ if (TEST_HARD_REG_BIT (to_spill, i))
+ {
+ spill_hard_reg (i, 1);
+ did_spill = true;
+
+ /* Regardless of the state of spills, if we previously had
+ a register that we thought we could eliminate, but now can
+ not eliminate, we must run another pass.
+
+ Consider pseudos which have an entry in reg_equiv_* which
+ reference an eliminable register. We must make another pass
+ to update reg_equiv_* so that we do not substitute in the
+ old value from when we thought the elimination could be
+ performed. */
+ }
+ return did_spill;
+}
+
/* Return true if X is used as the target register of an elimination. */
bool
be substituted eventually by altering the REG-rtx's. */
static void
-init_eliminable_invariants (rtx first, bool do_subregs)
+init_eliminable_invariants (rtx_insn *first, bool do_subregs)
{
int i;
- rtx insn;
+ rtx_insn *insn;
grow_reg_equivs ();
if (do_subregs)
}
else if (function_invariant_p (x))
{
+ machine_mode mode;
+
+ mode = GET_MODE (SET_DEST (set));
if (GET_CODE (x) == PLUS)
{
/* This is PLUS of frame pointer and a constant,
reg_equiv_invariant (i) = x;
num_eliminable_invariants++;
}
- else if (LEGITIMATE_CONSTANT_P (x))
+ else if (targetm.legitimate_constant_p (mode, x))
reg_equiv_constant (i) = x;
else
{
- reg_equiv_memory_loc (i)
- = force_const_mem (GET_MODE (SET_DEST (set)), x);
+ reg_equiv_memory_loc (i) = force_const_mem (mode, x);
if (! reg_equiv_memory_loc (i))
reg_equiv_init (i) = NULL_RTX;
}
{
int i;
-
- if (offsets_known_at)
- free (offsets_known_at);
- if (offsets_at)
- free (offsets_at);
+ free (offsets_known_at);
+ free (offsets_at);
offsets_at = 0;
offsets_known_at = 0;
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
if (reg_equiv_alt_mem_list (i))
free_EXPR_LIST_list (®_equiv_alt_mem_list (i));
- VEC_free (reg_equivs_t, gc, reg_equivs);
- reg_equivs = NULL;
-
+ vec_free (reg_equivs);
}
\f
/* Kick all pseudos out of hard register REGNO.
switch (code)
{
case REG:
- case CONST_INT:
case CONST:
case SYMBOL_REF:
case LABEL_REF:
- case CONST_DOUBLE:
- case CONST_FIXED:
- case CONST_VECTOR: /* shouldn't happen, but just in case. */
+ CASE_CONST_ANY:
case CC0:
case PC:
case USE:
}
}
}
+
+/* *OP_PTR and *OTHER_PTR are two operands to a conceptual reload.
+ If *OP_PTR is a paradoxical subreg, try to remove that subreg
+ and apply the corresponding narrowing subreg to *OTHER_PTR.
+ Return true if the operands were changed, false otherwise. */
+
+static bool
+strip_paradoxical_subreg (rtx *op_ptr, rtx *other_ptr)
+{
+ rtx op, inner, other, tem;
+
+ op = *op_ptr;
+ if (!paradoxical_subreg_p (op))
+ return false;
+ inner = SUBREG_REG (op);
+
+ other = *other_ptr;
+ tem = gen_lowpart_common (GET_MODE (inner), other);
+ if (!tem)
+ return false;
+
+ /* If the lowpart operation turned a hard register into a subreg,
+ rather than simplifying it to another hard register, then the
+ mode change cannot be properly represented. For example, OTHER
+ might be valid in its current mode, but not in the new one. */
+ if (GET_CODE (tem) == SUBREG
+ && REG_P (other)
+ && HARD_REGISTER_P (other))
+ return false;
+
+ *op_ptr = inner;
+ *other_ptr = tem;
+ return true;
+}
\f
/* A subroutine of reload_as_needed. If INSN has a REG_EH_REGION note,
examine all of the reload insns between PREV and NEXT exclusive, and
annotate all that may trap. */
static void
-fixup_eh_region_note (rtx insn, rtx prev, rtx next)
+fixup_eh_region_note (rtx_insn *insn, rtx_insn *prev, rtx_insn *next)
{
rtx note = find_reg_note (insn, REG_EH_REGION, NULL_RTX);
if (note == NULL)
#if defined (AUTO_INC_DEC)
int i;
#endif
- rtx x;
+ rtx_note *marker;
memset (spill_reg_rtx, 0, sizeof spill_reg_rtx);
memset (spill_reg_store, 0, sizeof spill_reg_store);
set_initial_elim_offsets ();
+ /* Generate a marker insn that we will move around. */
+ marker = emit_note (NOTE_INSN_DELETED);
+ unlink_insn_chain (marker, marker);
+
for (chain = reload_insn_chain; chain; chain = chain->next)
{
- rtx prev = 0;
- rtx insn = chain->insn;
- rtx old_next = NEXT_INSN (insn);
+ rtx_insn *prev = 0;
+ rtx_insn *insn = chain->insn;
+ rtx_insn *old_next = NEXT_INSN (insn);
#ifdef AUTO_INC_DEC
- rtx old_prev = PREV_INSN (insn);
+ rtx_insn *old_prev = PREV_INSN (insn);
#endif
+ if (will_delete_init_insn_p (insn))
+ continue;
+
/* If we pass a label, copy the offsets from the label information
into the current offsets of each elimination. */
if (LABEL_P (insn))
if (n_reloads > 0)
{
- rtx next = NEXT_INSN (insn);
- rtx p;
+ rtx_insn *next = NEXT_INSN (insn);
+ /* ??? PREV can get deleted by reload inheritance.
+ Work around this by emitting a marker note. */
prev = PREV_INSN (insn);
+ reorder_insns_nobb (marker, marker, prev);
/* Now compute which reload regs to reload them into. Perhaps
reusing reload regs from previous insns, or else output
and that we moved the structure into). */
subst_reloads (insn);
+ prev = PREV_INSN (marker);
+ unlink_insn_chain (marker, marker);
+
/* Adjust the exception region notes for loads and stores. */
if (cfun->can_throw_non_call_exceptions && !CALL_P (insn))
fixup_eh_region_note (insn, prev, next);
+ /* Adjust the location of REG_ARGS_SIZE. */
+ rtx p = find_reg_note (insn, REG_ARGS_SIZE, NULL_RTX);
+ if (p)
+ {
+ remove_note (insn, p);
+ fixup_args_size_notes (prev, PREV_INSN (next),
+ INTVAL (XEXP (p, 0)));
+ }
+
/* If this was an ASM, make sure that all the reload insns
we have generated are valid. If not, give an error
and delete them. */
if (asm_noperands (PATTERN (insn)) >= 0)
- for (p = NEXT_INSN (prev); p != next; p = NEXT_INSN (p))
+ for (rtx_insn *p = NEXT_INSN (prev);
+ p != next;
+ p = NEXT_INSN (p))
if (p != insn && INSN_P (p)
&& GET_CODE (PATTERN (p)) != USE
&& (recog_memoized (p) < 0
- || (extract_insn (p), ! constrain_operands (1))))
+ || (extract_insn (p),
+ !(constrain_operands (1,
+ get_enabled_alternatives (p))))))
{
error_for_asm (insn,
"%<asm%> operand requires "
/* There may have been CLOBBER insns placed after INSN. So scan
between INSN and NEXT and use them to forget old reloads. */
- for (x = NEXT_INSN (insn); x != old_next; x = NEXT_INSN (x))
+ for (rtx_insn *x = NEXT_INSN (insn); x != old_next; x = NEXT_INSN (x))
if (NONJUMP_INSN_P (x) && GET_CODE (PATTERN (x)) == CLOBBER)
note_stores (PATTERN (x), forget_old_reloads_1, NULL);
{
rtx reload_reg = rld[i].reg_rtx;
- enum machine_mode mode = GET_MODE (reload_reg);
+ machine_mode mode = GET_MODE (reload_reg);
int n = 0;
- rtx p;
+ rtx_insn *p;
for (p = PREV_INSN (old_next); p != prev; p = PREV_INSN (p))
{
if (n)
{
extract_insn (p);
- n = constrain_operands (1);
+ n = constrain_operands (1,
+ get_enabled_alternatives (p));
}
/* If the constraints were not met, then
if (TEST_HARD_REG_BIT (reg_reloaded_valid,
in_hard_regno))
{
- for (x = old_prev ? NEXT_INSN (old_prev) : insn;
+ for (rtx_insn *x = (old_prev ?
+ NEXT_INSN (old_prev) : insn);
x != old_next;
x = NEXT_INSN (x))
if (x == reg_reloaded_insn[in_hard_regno])
/* If a pseudo that got a hard register is auto-incremented,
we must purge records of copying it into pseudos without
hard registers. */
- for (x = REG_NOTES (insn); x; x = XEXP (x, 1))
+ for (rtx x = REG_NOTES (insn); x; x = XEXP (x, 1))
if (REG_NOTE_KIND (x) == REG_INC)
{
/* See if this pseudo reg was reloaded in this insn.
{
AND_COMPL_HARD_REG_SET (reg_reloaded_valid, call_used_reg_set);
AND_COMPL_HARD_REG_SET (reg_reloaded_valid, reg_reloaded_call_part_clobbered);
+
+ /* If this is a call to a setjmp-type function, we must not
+ reuse any reload reg contents across the call; that will
+ just be clobbered by other uses of the register in later
+ code, before the longjmp. */
+ if (find_reg_note (insn, REG_SETJMP, NULL_RTX))
+ CLEAR_HARD_REG_SET (reg_reloaded_valid);
}
}
static void
mark_reload_reg_in_use (unsigned int regno, int opnum, enum reload_type type,
- enum machine_mode mode)
+ machine_mode mode)
{
- unsigned int nregs = hard_regno_nregs[regno][mode];
- unsigned int i;
-
- for (i = regno; i < nregs + regno; i++)
+ switch (type)
{
- switch (type)
- {
- case RELOAD_OTHER:
- SET_HARD_REG_BIT (reload_reg_used, i);
- break;
-
- case RELOAD_FOR_INPUT_ADDRESS:
- SET_HARD_REG_BIT (reload_reg_used_in_input_addr[opnum], i);
- break;
+ case RELOAD_OTHER:
+ add_to_hard_reg_set (&reload_reg_used, mode, regno);
+ break;
- case RELOAD_FOR_INPADDR_ADDRESS:
- SET_HARD_REG_BIT (reload_reg_used_in_inpaddr_addr[opnum], i);
- break;
+ case RELOAD_FOR_INPUT_ADDRESS:
+ add_to_hard_reg_set (&reload_reg_used_in_input_addr[opnum], mode, regno);
+ break;
- case RELOAD_FOR_OUTPUT_ADDRESS:
- SET_HARD_REG_BIT (reload_reg_used_in_output_addr[opnum], i);
- break;
+ case RELOAD_FOR_INPADDR_ADDRESS:
+ add_to_hard_reg_set (&reload_reg_used_in_inpaddr_addr[opnum], mode, regno);
+ break;
- case RELOAD_FOR_OUTADDR_ADDRESS:
- SET_HARD_REG_BIT (reload_reg_used_in_outaddr_addr[opnum], i);
- break;
+ case RELOAD_FOR_OUTPUT_ADDRESS:
+ add_to_hard_reg_set (&reload_reg_used_in_output_addr[opnum], mode, regno);
+ break;
- case RELOAD_FOR_OPERAND_ADDRESS:
- SET_HARD_REG_BIT (reload_reg_used_in_op_addr, i);
- break;
+ case RELOAD_FOR_OUTADDR_ADDRESS:
+ add_to_hard_reg_set (&reload_reg_used_in_outaddr_addr[opnum], mode, regno);
+ break;
- case RELOAD_FOR_OPADDR_ADDR:
- SET_HARD_REG_BIT (reload_reg_used_in_op_addr_reload, i);
- break;
+ case RELOAD_FOR_OPERAND_ADDRESS:
+ add_to_hard_reg_set (&reload_reg_used_in_op_addr, mode, regno);
+ break;
- case RELOAD_FOR_OTHER_ADDRESS:
- SET_HARD_REG_BIT (reload_reg_used_in_other_addr, i);
- break;
+ case RELOAD_FOR_OPADDR_ADDR:
+ add_to_hard_reg_set (&reload_reg_used_in_op_addr_reload, mode, regno);
+ break;
- case RELOAD_FOR_INPUT:
- SET_HARD_REG_BIT (reload_reg_used_in_input[opnum], i);
- break;
+ case RELOAD_FOR_OTHER_ADDRESS:
+ add_to_hard_reg_set (&reload_reg_used_in_other_addr, mode, regno);
+ break;
- case RELOAD_FOR_OUTPUT:
- SET_HARD_REG_BIT (reload_reg_used_in_output[opnum], i);
- break;
+ case RELOAD_FOR_INPUT:
+ add_to_hard_reg_set (&reload_reg_used_in_input[opnum], mode, regno);
+ break;
- case RELOAD_FOR_INSN:
- SET_HARD_REG_BIT (reload_reg_used_in_insn, i);
- break;
- }
+ case RELOAD_FOR_OUTPUT:
+ add_to_hard_reg_set (&reload_reg_used_in_output[opnum], mode, regno);
+ break;
- SET_HARD_REG_BIT (reload_reg_used_at_all, i);
+ case RELOAD_FOR_INSN:
+ add_to_hard_reg_set (&reload_reg_used_in_insn, mode, regno);
+ break;
}
+
+ add_to_hard_reg_set (&reload_reg_used_at_all, mode, regno);
}
/* Similarly, but show REGNO is no longer in use for a reload. */
static void
clear_reload_reg_in_use (unsigned int regno, int opnum,
- enum reload_type type, enum machine_mode mode)
+ enum reload_type type, machine_mode mode)
{
unsigned int nregs = hard_regno_nregs[regno][mode];
unsigned int start_regno, end_regno, r;
}
}
-/* Return 1 if the value in reload reg REGNO, as used by a reload
- needed for the part of the insn specified by OPNUM and TYPE,
- is still available in REGNO at the end of the insn.
+/* Return 1 if the value in reload reg REGNO, as used by the reload with
+ the number RELOADNUM, is still available in REGNO at the end of the insn.
We can assume that the reload reg was already tested for availability
at the time it is needed, and we should not check this again,
in case the reg has already been marked in use. */
static int
-reload_reg_reaches_end_p (unsigned int regno, int opnum, enum reload_type type)
+reload_reg_reaches_end_p (unsigned int regno, int reloadnum)
{
+ int opnum = rld[reloadnum].opnum;
+ enum reload_type type = rld[reloadnum].when_needed;
int i;
+ /* See if there is a reload with the same type for this operand, using
+ the same register. This case is not handled by the code below. */
+ for (i = reloadnum + 1; i < n_reloads; i++)
+ {
+ rtx reg;
+ int nregs;
+
+ if (rld[i].opnum != opnum || rld[i].when_needed != type)
+ continue;
+ reg = rld[i].reg_rtx;
+ if (reg == NULL_RTX)
+ continue;
+ nregs = hard_regno_nregs[REGNO (reg)][GET_MODE (reg)];
+ if (regno >= REGNO (reg) && regno < REGNO (reg) + nregs)
+ return 0;
+ }
+
switch (type)
{
case RELOAD_OTHER:
if (TEST_HARD_REG_BIT (reload_reg_used_in_input[i], regno))
return 0;
+ /* Reload register of reload with type RELOAD_FOR_INPADDR_ADDRESS
+ could be killed if the register is also used by reload with type
+ RELOAD_FOR_INPUT_ADDRESS, so check it. */
+ if (type == RELOAD_FOR_INPADDR_ADDRESS
+ && TEST_HARD_REG_BIT (reload_reg_used_in_input_addr[opnum], regno))
+ return 0;
+
for (i = opnum + 1; i < reload_n_operands; i++)
if (TEST_HARD_REG_BIT (reload_reg_used_in_input_addr[i], regno)
|| TEST_HARD_REG_BIT (reload_reg_used_in_inpaddr_addr[i], regno))
|| TEST_HARD_REG_BIT (reload_reg_used_in_outaddr_addr[i], regno))
return 0;
+ /* Reload register of reload with type RELOAD_FOR_OUTADDR_ADDRESS
+ could be killed if the register is also used by reload with type
+ RELOAD_FOR_OUTPUT_ADDRESS, so check it. */
+ if (type == RELOAD_FOR_OUTADDR_ADDRESS
+ && TEST_HARD_REG_BIT (reload_reg_used_in_outaddr_addr[opnum], regno))
+ return 0;
+
return 1;
default:
}
/* Like reload_reg_reaches_end_p, but check that the condition holds for
- every register in the range [REGNO, REGNO + NREGS). */
+ every register in REG. */
static bool
-reload_regs_reach_end_p (unsigned int regno, int nregs,
- int opnum, enum reload_type type)
+reload_reg_rtx_reaches_end_p (rtx reg, int reloadnum)
{
- int i;
+ unsigned int i;
- for (i = 0; i < nregs; i++)
- if (!reload_reg_reaches_end_p (regno + i, opnum, type))
+ for (i = REGNO (reg); i < END_REGNO (reg); i++)
+ if (!reload_reg_reaches_end_p (i, reloadnum))
return false;
return true;
}
|| reg_mentioned_p (rld[r2].in, rld[r1].in)))
return false;
+ /* The following loop assumes that r1 is the reload that feeds r2. */
+ if (r1 > r2)
+ {
+ int tmp = r2;
+ r2 = r1;
+ r1 = tmp;
+ }
+
for (i = 0; i < n_reloads; i ++)
/* Look for input reloads that aren't our two */
if (i != r1 && i != r2 && rld[i].in)
if (*where == what || rtx_equal_p (*where, what))
{
/* Record the location of the changed rtx. */
- VEC_safe_push (rtx_p, heap, substitute_stack, where);
+ substitute_stack.safe_push (where);
*where = repl;
return;
}
chain reloads or do need an intermediate hard registers. */
bool result = true;
int regno, n, code;
- rtx out, in, tem, insn;
- rtx last = get_last_insn ();
+ rtx out, in;
+ rtx_insn *insn;
+ rtx_insn *last = get_last_insn ();
/* Make r2 a component of r1. */
if (reg_mentioned_p (rld[r1].in, rld[r2].in))
/* If IN is a paradoxical SUBREG, remove it and try to put the
opposite SUBREG on OUT. Likewise for a paradoxical SUBREG on OUT. */
- if (GET_CODE (in) == SUBREG
- && (GET_MODE_SIZE (GET_MODE (in))
- > GET_MODE_SIZE (GET_MODE (SUBREG_REG (in))))
- && (tem = gen_lowpart_common (GET_MODE (SUBREG_REG (in)), out)) != 0)
- in = SUBREG_REG (in), out = tem;
+ strip_paradoxical_subreg (&in, &out);
if (GET_CODE (in) == PLUS
&& (REG_P (XEXP (in, 0))
/* We want constrain operands to treat this insn strictly in
its validity determination, i.e., the way it would after
reload has completed. */
- result = constrain_operands (1);
+ result = constrain_operands (1, get_enabled_alternatives (insn));
}
delete_insns_since (last);
}
/* Restore the original value at each changed address within R1. */
- while (!VEC_empty (rtx_p, substitute_stack))
+ while (!substitute_stack.is_empty ())
{
- rtx *where = VEC_pop (rtx_p, substitute_stack);
+ rtx *where = substitute_stack.pop ();
*where = rld[r2].in;
}
/* For an inherited reload, this is the insn the reload was inherited from,
if we know it. Otherwise, this is 0. */
-static rtx reload_inheritance_insn[MAX_RELOADS];
+static rtx_insn *reload_inheritance_insn[MAX_RELOADS];
/* If nonzero, this is a place to get the value of the reload,
rather than using reload_in. */
register. */
static int
-free_for_value_p (int regno, enum machine_mode mode, int opnum,
+free_for_value_p (int regno, machine_mode mode, int opnum,
enum reload_type type, rtx value, rtx out, int reloadnum,
int ignore_address_reloads)
{
/* Give an error message saying we failed to find a reload for INSN,
and clear out reload R. */
static void
-failed_reload (rtx insn, int r)
+failed_reload (rtx_insn *insn, int r)
{
if (asm_noperands (PATTERN (insn)) < 0)
/* It's the compiler's fault. */
This used to be one `if', but Sequent compiler can't handle that. */
if (HARD_REGNO_MODE_OK (regno, rld[r].mode))
{
- enum machine_mode test_mode = VOIDmode;
+ machine_mode test_mode = VOIDmode;
if (rld[r].in)
test_mode = GET_MODE (rld[r].in);
/* If rld[r].in has VOIDmode, it means we will load it
rld[i].when_needed, rld[i].mode);
}
+#ifdef SECONDARY_MEMORY_NEEDED
+/* If X is not a subreg, return it unmodified. If it is a subreg,
+ look up whether we made a replacement for the SUBREG_REG. Return
+ either the replacement or the SUBREG_REG. */
+
+static rtx
+replaced_subreg (rtx x)
+{
+ if (GET_CODE (x) == SUBREG)
+ return find_replacement (&SUBREG_REG (x));
+ return x;
+}
+#endif
+
+/* Compute the offset to pass to subreg_regno_offset, for a pseudo of
+ mode OUTERMODE that is available in a hard reg of mode INNERMODE.
+ SUBREG is non-NULL if the pseudo is a subreg whose reg is a pseudo,
+ otherwise it is NULL. */
+
+static int
+compute_reload_subreg_offset (machine_mode outermode,
+ rtx subreg,
+ machine_mode innermode)
+{
+ int outer_offset;
+ machine_mode middlemode;
+
+ if (!subreg)
+ return subreg_lowpart_offset (outermode, innermode);
+
+ outer_offset = SUBREG_BYTE (subreg);
+ middlemode = GET_MODE (SUBREG_REG (subreg));
+
+ /* If SUBREG is paradoxical then return the normal lowpart offset
+ for OUTERMODE and INNERMODE. Our caller has already checked
+ that OUTERMODE fits in INNERMODE. */
+ if (outer_offset == 0
+ && GET_MODE_SIZE (outermode) > GET_MODE_SIZE (middlemode))
+ return subreg_lowpart_offset (outermode, innermode);
+
+ /* SUBREG is normal, but may not be lowpart; return OUTER_OFFSET
+ plus the normal lowpart offset for MIDDLEMODE and INNERMODE. */
+ return outer_offset + subreg_lowpart_offset (middlemode, innermode);
+}
+
/* Assign hard reg targets for the pseudo-registers we must reload
into hard regs for this insn.
Also output the instructions to copy them in and out of the hard regs.
static void
choose_reload_regs (struct insn_chain *chain)
{
- rtx insn = chain->insn;
+ rtx_insn *insn = chain->insn;
int i, j;
unsigned int max_group_size = 1;
enum reg_class group_class = NO_REGS;
{
int byte = 0;
int regno = -1;
- enum machine_mode mode = VOIDmode;
+ machine_mode mode = VOIDmode;
+ rtx subreg = NULL_RTX;
if (rld[r].in == 0)
;
if (regno < FIRST_PSEUDO_REGISTER)
regno = subreg_regno (rld[r].in_reg);
else
- byte = SUBREG_BYTE (rld[r].in_reg);
+ {
+ subreg = rld[r].in_reg;
+ byte = SUBREG_BYTE (subreg);
+ }
mode = GET_MODE (rld[r].in_reg);
}
#ifdef AUTO_INC_DEC
if (regno >= 0
&& reg_last_reload_reg[regno] != 0
+ && (GET_MODE_SIZE (GET_MODE (reg_last_reload_reg[regno]))
+ >= GET_MODE_SIZE (mode) + byte)
#ifdef CANNOT_CHANGE_MODE_CLASS
/* Verify that the register it's in can be used in
mode MODE. */
{
enum reg_class rclass = rld[r].rclass, last_class;
rtx last_reg = reg_last_reload_reg[regno];
- enum machine_mode need_mode;
i = REGNO (last_reg);
+ byte = compute_reload_subreg_offset (mode,
+ subreg,
+ GET_MODE (last_reg));
i += subreg_regno_offset (i, GET_MODE (last_reg), byte, mode);
last_class = REGNO_REG_CLASS (i);
- if (byte == 0)
- need_mode = mode;
- else
- need_mode
- = smallest_mode_for_size
- (GET_MODE_BITSIZE (mode) + byte * BITS_PER_UNIT,
- GET_MODE_CLASS (mode) == MODE_PARTIAL_INT
- ? MODE_INT : GET_MODE_CLASS (mode));
-
- if ((GET_MODE_SIZE (GET_MODE (last_reg))
- >= GET_MODE_SIZE (need_mode))
- && reg_reloaded_contents[i] == regno
+ if (reg_reloaded_contents[i] == regno
&& TEST_HARD_REG_BIT (reg_reloaded_valid, i)
&& HARD_REGNO_MODE_OK (i, rld[r].mode)
&& (TEST_HARD_REG_BIT (reg_class_contents[(int) rclass], i)
We must clear it, since otherwise emit_reload_insns
might delete the store. */
if (! TEST_HARD_REG_BIT (reg_reloaded_valid, regno))
- spill_reg_store[regno] = NULL_RTX;
+ spill_reg_store[regno] = NULL;
/* If any of the hard registers in EQUIV are spill
registers, mark them as in use for this insn. */
for (k = 0; k < nr; k++)
{
int r = reload_order[j];
rtx check_reg;
+#ifdef SECONDARY_MEMORY_NEEDED
+ rtx tem;
+#endif
if (reload_inherited[r] && rld[r].reg_rtx)
check_reg = rld[r].reg_rtx;
else if (reload_override_in[r]
removal of one reload might allow us to inherit another one. */
else if (rld[r].in
&& rld[r].out != rld[r].in
- && remove_address_replacements (rld[r].in) && pass)
- pass = 2;
+ && remove_address_replacements (rld[r].in))
+ {
+ if (pass)
+ pass = 2;
+ }
+#ifdef SECONDARY_MEMORY_NEEDED
+ /* If we needed a memory location for the reload, we also have to
+ remove its related reloads. */
+ else if (rld[r].in
+ && rld[r].out != rld[r].in
+ && (tem = replaced_subreg (rld[r].in), REG_P (tem))
+ && REGNO (tem) < FIRST_PSEUDO_REGISTER
+ && SECONDARY_MEMORY_NEEDED (REGNO_REG_CLASS (REGNO (tem)),
+ rld[r].rclass, rld[r].inmode)
+ && remove_address_replacements
+ (get_secondary_mem (tem, rld[r].inmode, rld[r].opnum,
+ rld[r].when_needed)))
+ {
+ if (pass)
+ pass = 2;
+ }
+#endif
}
}
nregno + nr);
if (i >= 0)
- {
- nr = hard_regno_nregs[i][rld[r].mode];
- while (--nr >= 0)
- SET_HARD_REG_BIT (reg_is_output_reload, i + nr);
- }
+ add_to_hard_reg_set (®_is_output_reload, rld[r].mode, i);
gcc_assert (rld[r].when_needed == RELOAD_OTHER
|| rld[r].when_needed == RELOAD_FOR_OUTPUT
}
\f
/* These arrays are filled by emit_reload_insns and its subroutines. */
-static rtx input_reload_insns[MAX_RECOG_OPERANDS];
-static rtx other_input_address_reload_insns = 0;
-static rtx other_input_reload_insns = 0;
-static rtx input_address_reload_insns[MAX_RECOG_OPERANDS];
-static rtx inpaddr_address_reload_insns[MAX_RECOG_OPERANDS];
-static rtx output_reload_insns[MAX_RECOG_OPERANDS];
-static rtx output_address_reload_insns[MAX_RECOG_OPERANDS];
-static rtx outaddr_address_reload_insns[MAX_RECOG_OPERANDS];
-static rtx operand_reload_insns = 0;
-static rtx other_operand_reload_insns = 0;
-static rtx other_output_reload_insns[MAX_RECOG_OPERANDS];
-
-/* Values to be put in spill_reg_store are put here first. */
-static rtx new_spill_reg_store[FIRST_PSEUDO_REGISTER];
+static rtx_insn *input_reload_insns[MAX_RECOG_OPERANDS];
+static rtx_insn *other_input_address_reload_insns = 0;
+static rtx_insn *other_input_reload_insns = 0;
+static rtx_insn *input_address_reload_insns[MAX_RECOG_OPERANDS];
+static rtx_insn *inpaddr_address_reload_insns[MAX_RECOG_OPERANDS];
+static rtx_insn *output_reload_insns[MAX_RECOG_OPERANDS];
+static rtx_insn *output_address_reload_insns[MAX_RECOG_OPERANDS];
+static rtx_insn *outaddr_address_reload_insns[MAX_RECOG_OPERANDS];
+static rtx_insn *operand_reload_insns = 0;
+static rtx_insn *other_operand_reload_insns = 0;
+static rtx_insn *other_output_reload_insns[MAX_RECOG_OPERANDS];
+
+/* Values to be put in spill_reg_store are put here first. Instructions
+ must only be placed here if the associated reload register reaches
+ the end of the instruction's reload sequence. */
+static rtx_insn *new_spill_reg_store[FIRST_PSEUDO_REGISTER];
static HARD_REG_SET reg_reloaded_died;
/* Check if *RELOAD_REG is suitable as an intermediate or scratch register
static bool
reload_adjust_reg_for_temp (rtx *reload_reg, rtx alt_reload_reg,
enum reg_class new_class,
- enum machine_mode new_mode)
+ machine_mode new_mode)
{
rtx reg;
{
enum reg_class new_class = scratch_reload_class (icode);
- enum machine_mode new_mode = insn_data[(int) icode].operand[2].mode;
+ machine_mode new_mode = insn_data[(int) icode].operand[2].mode;
return reload_adjust_reg_for_temp (reload_reg, alt_reload_reg,
new_class, new_mode);
emit_input_reload_insns (struct insn_chain *chain, struct reload *rl,
rtx old, int j)
{
- rtx insn = chain->insn;
+ rtx_insn *insn = chain->insn;
rtx reloadreg;
rtx oldequiv_reg = 0;
rtx oldequiv = 0;
int special = 0;
- enum machine_mode mode;
- rtx *where;
+ machine_mode mode;
+ rtx_insn **where;
/* delete_output_reload is only invoked properly if old contains
the original pseudo register. Since this is replaced with a
hard reg when RELOAD_OVERRIDE_IN is set, see if we can
- find the pseudo in RELOAD_IN_REG. */
+ find the pseudo in RELOAD_IN_REG. This is also used to
+ determine whether a secondary reload is needed. */
if (reload_override_in[j]
- && REG_P (rl->in_reg))
+ && (REG_P (rl->in_reg)
+ || (GET_CODE (rl->in_reg) == SUBREG
+ && REG_P (SUBREG_REG (rl->in_reg)))))
{
oldequiv = old;
old = rl->in_reg;
old = XEXP (rl->in_reg, 0);
- if (optimize && REG_P (oldequiv)
- && REGNO (oldequiv) < FIRST_PSEUDO_REGISTER
- && spill_reg_store[REGNO (oldequiv)]
- && REG_P (old)
- && (dead_or_set_p (insn,
- spill_reg_stored_to[REGNO (oldequiv)])
- || rtx_equal_p (spill_reg_stored_to[REGNO (oldequiv)],
- old)))
- delete_output_reload (insn, j, REGNO (oldequiv), reloadreg);
-
/* Prevent normal processing of this reload. */
special = 1;
/* Output a special code sequence for this case. */
- new_spill_reg_store[REGNO (reloadreg)]
- = inc_for_reload (reloadreg, oldequiv, rl->out,
- rl->inc);
+ inc_for_reload (reloadreg, oldequiv, rl->out, rl->inc);
}
/* If we are reloading a pseudo-register that was set by the previous
&& free_for_value_p (REGNO (reloadreg), rl->mode, rl->opnum,
rl->when_needed, old, rl->out, j, 0))
{
- rtx temp = PREV_INSN (insn);
+ rtx_insn *temp = PREV_INSN (insn);
while (temp && (NOTE_P (temp) || DEBUG_INSN_P (temp)))
temp = PREV_INSN (temp);
if (temp
/* Store into the reload register instead of the pseudo. */
SET_DEST (PATTERN (temp)) = reloadreg;
- /* Verify that resulting insn is valid. */
+ /* Verify that resulting insn is valid.
+
+ Note that we have replaced the destination of TEMP with
+ RELOADREG. If TEMP references RELOADREG within an
+ autoincrement addressing mode, then the resulting insn
+ is ill-formed and we must reject this optimization. */
extract_insn (temp);
- if (constrain_operands (1))
+ if (constrain_operands (1, get_enabled_alternatives (temp))
+#ifdef AUTO_INC_DEC
+ && ! find_reg_note (temp, REG_INC, reloadreg)
+#endif
+ )
{
/* If the previous insn is an output reload, the source is
a reload register, and its spill_reg_store entry will
int j)
{
rtx reloadreg;
- rtx insn = chain->insn;
+ rtx_insn *insn = chain->insn;
int special = 0;
rtx old = rl->out;
- enum machine_mode mode;
- rtx p;
+ machine_mode mode;
+ rtx_insn *p;
rtx rl_reg_rtx;
if (rl->when_needed == RELOAD_OTHER)
if (tertiary_icode != CODE_FOR_nothing)
{
rtx third_reloadreg = rld[tertiary_reload].reg_rtx;
- rtx tem;
/* Copy primary reload reg to secondary reload reg.
(Note that these have been swapped above, then
/* If REAL_OLD is a paradoxical SUBREG, remove it
and try to put the opposite SUBREG on
RELOADREG. */
- if (GET_CODE (real_old) == SUBREG
- && (GET_MODE_SIZE (GET_MODE (real_old))
- > GET_MODE_SIZE (GET_MODE (SUBREG_REG (real_old))))
- && 0 != (tem = gen_lowpart_common
- (GET_MODE (SUBREG_REG (real_old)),
- reloadreg)))
- real_old = SUBREG_REG (real_old), reloadreg = tem;
+ strip_paradoxical_subreg (&real_old, &reloadreg);
gen_reload (reloadreg, second_reloadreg,
rl->opnum, rl->when_needed);
that it is, setting new_spill_reg_store to
that insn will allow an extra optimization. */
rtx s_reg = rld[s].reg_rtx;
- rtx next = NEXT_INSN (p);
+ rtx_insn *next = NEXT_INSN (p);
rld[s].out = rl->out;
rld[s].out_reg = rl->out_reg;
set = single_set (next);
if (set && SET_SRC (set) == s_reg
- && ! new_spill_reg_store[REGNO (s_reg)])
+ && reload_reg_rtx_reaches_end_p (s_reg, s))
{
SET_HARD_REG_BIT (reg_is_output_reload,
REGNO (s_reg));
new_spill_reg_store[REGNO (s_reg)] = next;
}
}
- else
+ else if (reload_reg_rtx_reaches_end_p (rl_reg_rtx, j))
new_spill_reg_store[REGNO (rl_reg_rtx)] = p;
}
}
static void
do_input_reload (struct insn_chain *chain, struct reload *rl, int j)
{
- rtx insn = chain->insn;
+ rtx_insn *insn = chain->insn;
rtx old = (rl->in && MEM_P (rl->in)
? rl->in_reg : rl->in);
rtx reg_rtx = rl->reg_rtx;
if (old && reg_rtx)
{
- enum machine_mode mode;
+ machine_mode mode;
/* Determine the mode to reload in.
This is very tricky because we have three to choose from.
do_output_reload (struct insn_chain *chain, struct reload *rl, int j)
{
rtx note, old;
- rtx insn = chain->insn;
+ rtx_insn *insn = chain->insn;
/* If this is an output reload that stores something that is
not loaded in this same reload, see if we can eliminate a previous
store. */
if (rl->out && reg_rtx)
{
- enum machine_mode mode;
+ machine_mode mode;
/* Determine the mode to reload in.
See comments above (for input reloading). */
static bool
inherit_piecemeal_p (int dest ATTRIBUTE_UNUSED,
int src ATTRIBUTE_UNUSED,
- enum machine_mode mode ATTRIBUTE_UNUSED)
+ machine_mode mode ATTRIBUTE_UNUSED)
{
#ifdef CANNOT_CHANGE_MODE_CLASS
return (!REG_CANNOT_CHANGE_MODE_P (dest, mode, reg_raw_mode[dest])
static void
emit_reload_insns (struct insn_chain *chain)
{
- rtx insn = chain->insn;
+ rtx_insn *insn = chain->insn;
int j;
debug_reload_to_stream (dump_file);
}
+ for (j = 0; j < n_reloads; j++)
+ if (rld[j].reg_rtx && HARD_REGISTER_P (rld[j].reg_rtx))
+ {
+ unsigned int i;
+
+ for (i = REGNO (rld[j].reg_rtx); i < END_REGNO (rld[j].reg_rtx); i++)
+ new_spill_reg_store[i] = 0;
+ }
+
/* Now output the instructions to copy the data into and out of the
reload registers. Do these in the order that the reloads were reported,
since reloads of base and index registers precede reloads of operands
for (j = 0; j < n_reloads; j++)
{
- if (rld[j].reg_rtx && HARD_REGISTER_P (rld[j].reg_rtx))
- {
- unsigned int i;
-
- for (i = REGNO (rld[j].reg_rtx); i < END_REGNO (rld[j].reg_rtx); i++)
- new_spill_reg_store[i] = 0;
- }
-
do_input_reload (chain, rld + j, j);
do_output_reload (chain, rld + j, j);
}
/* For a multi register reload, we need to check if all or part
of the value lives to the end. */
for (k = 0; k < nr; k++)
- if (reload_reg_reaches_end_p (i + k, rld[r].opnum,
- rld[r].when_needed))
+ if (reload_reg_reaches_end_p (i + k, r))
CLEAR_HARD_REG_BIT (reg_reloaded_valid, i + k);
/* Maybe the spill reg contains a copy of reload_out. */
&& GET_CODE (rld[r].out) != PRE_MODIFY))))
{
rtx reg;
- enum machine_mode mode;
- int regno, nregs;
reg = reload_reg_rtx_for_output[r];
- mode = GET_MODE (reg);
- regno = REGNO (reg);
- nregs = hard_regno_nregs[regno][mode];
- if (reload_regs_reach_end_p (regno, nregs, rld[r].opnum,
- rld[r].when_needed))
+ if (reload_reg_rtx_reaches_end_p (reg, r))
{
+ machine_mode mode = GET_MODE (reg);
+ int regno = REGNO (reg);
+ int nregs = hard_regno_nregs[regno][mode];
rtx out = (REG_P (rld[r].out)
? rld[r].out
: rld[r].out_reg
&& !reg_set_p (reload_reg_rtx_for_input[r], PATTERN (insn)))
{
rtx reg;
- enum machine_mode mode;
- int regno, nregs;
reg = reload_reg_rtx_for_input[r];
- mode = GET_MODE (reg);
- regno = REGNO (reg);
- nregs = hard_regno_nregs[regno][mode];
- if (reload_regs_reach_end_p (regno, nregs, rld[r].opnum,
- rld[r].when_needed))
+ if (reload_reg_rtx_reaches_end_p (reg, r))
{
+ machine_mode mode;
+ int regno;
+ int nregs;
int in_regno;
int in_nregs;
rtx in;
bool piecemeal;
+ mode = GET_MODE (reg);
+ regno = REGNO (reg);
+ nregs = hard_regno_nregs[regno][mode];
if (REG_P (rld[r].in)
&& REGNO (rld[r].in) >= FIRST_PSEUDO_REGISTER)
in = rld[r].in;
rtx out = ((rld[r].out && REG_P (rld[r].out))
? rld[r].out : rld[r].out_reg);
int out_regno = REGNO (out);
- enum machine_mode mode = GET_MODE (out);
+ machine_mode mode = GET_MODE (out);
/* REG_RTX is now set or clobbered by the main instruction.
As the comment above explains, forget_old_reloads_1 only
if (!HARD_REGISTER_NUM_P (out_regno))
{
- rtx src_reg, store_insn = NULL_RTX;
+ rtx src_reg;
+ rtx_insn *store_insn = NULL;
reg_last_reload_reg[out_regno] = 0;
delete_output_reload. */
src_reg = reload_reg_rtx_for_output[r];
- /* If this is an optional reload, try to find the source reg
- from an input reload. */
- if (! src_reg)
+ if (src_reg)
{
+ if (reload_reg_rtx_reaches_end_p (src_reg, r))
+ store_insn = new_spill_reg_store[REGNO (src_reg)];
+ else
+ src_reg = NULL_RTX;
+ }
+ else
+ {
+ /* If this is an optional reload, try to find the
+ source reg from an input reload. */
rtx set = single_set (insn);
if (set && SET_DEST (set) == rld[r].out)
{
}
}
}
- else
- store_insn = new_spill_reg_store[REGNO (src_reg)];
if (src_reg && REG_P (src_reg)
&& REGNO (src_reg) < FIRST_PSEUDO_REGISTER)
{
/* Go through the motions to emit INSN and test if it is strictly valid.
Return the emitted insn if valid, else return NULL. */
-static rtx
-emit_insn_if_valid_for_reload (rtx insn)
+static rtx_insn *
+emit_insn_if_valid_for_reload (rtx pat)
{
- rtx last = get_last_insn ();
+ rtx_insn *last = get_last_insn ();
int code;
- insn = emit_insn (insn);
+ rtx_insn *insn = emit_insn (pat);
code = recog_memoized (insn);
if (code >= 0)
/* We want constrain operands to treat this insn strictly in its
validity determination, i.e., the way it would after reload has
completed. */
- if (constrain_operands (1))
+ if (constrain_operands (1, get_enabled_alternatives (insn)))
return insn;
}
Returns first insn emitted. */
-static rtx
+static rtx_insn *
gen_reload (rtx out, rtx in, int opnum, enum reload_type type)
{
- rtx last = get_last_insn ();
- rtx tem;
+ rtx_insn *last = get_last_insn ();
+ rtx_insn *tem;
+#ifdef SECONDARY_MEMORY_NEEDED
+ rtx tem1, tem2;
+#endif
/* If IN is a paradoxical SUBREG, remove it and try to put the
opposite SUBREG on OUT. Likewise for a paradoxical SUBREG on OUT. */
- if (GET_CODE (in) == SUBREG
- && (GET_MODE_SIZE (GET_MODE (in))
- > GET_MODE_SIZE (GET_MODE (SUBREG_REG (in))))
- && (tem = gen_lowpart_common (GET_MODE (SUBREG_REG (in)), out)) != 0)
- in = SUBREG_REG (in), out = tem;
- else if (GET_CODE (out) == SUBREG
- && (GET_MODE_SIZE (GET_MODE (out))
- > GET_MODE_SIZE (GET_MODE (SUBREG_REG (out))))
- && (tem = gen_lowpart_common (GET_MODE (SUBREG_REG (out)), in)) != 0)
- out = SUBREG_REG (out), in = tem;
+ if (!strip_paradoxical_subreg (&in, &out))
+ strip_paradoxical_subreg (&out, &in);
/* How to do this reload can get quite tricky. Normally, we are being
asked to reload a simple operand, such as a MEM, a constant, or a pseudo
`extract_insn' and it is simpler to emit and then delete the insn if
not valid than to dummy things up. */
- rtx op0, op1, tem, insn;
+ rtx op0, op1, tem;
+ rtx_insn *insn;
enum insn_code code;
op0 = find_replacement (&XEXP (in, 0));
if (insn)
{
/* Add a REG_EQUIV note so that find_equiv_reg can find it. */
- set_unique_reg_note (insn, REG_EQUIV, in);
+ set_dst_reg_note (insn, REG_EQUIV, in, out);
return insn;
}
gcc_assert (!reg_overlap_mentioned_p (out, op0));
gen_reload (out, op1, opnum, type);
insn = emit_insn (gen_add2_insn (out, op0));
- set_unique_reg_note (insn, REG_EQUIV, in);
+ set_dst_reg_note (insn, REG_EQUIV, in, out);
}
#ifdef SECONDARY_MEMORY_NEEDED
/* If we need a memory location to do the move, do it that way. */
- else if ((REG_P (in)
- || (GET_CODE (in) == SUBREG && REG_P (SUBREG_REG (in))))
- && reg_or_subregno (in) < FIRST_PSEUDO_REGISTER
- && (REG_P (out)
- || (GET_CODE (out) == SUBREG && REG_P (SUBREG_REG (out))))
- && reg_or_subregno (out) < FIRST_PSEUDO_REGISTER
- && SECONDARY_MEMORY_NEEDED (REGNO_REG_CLASS (reg_or_subregno (in)),
- REGNO_REG_CLASS (reg_or_subregno (out)),
+ else if ((tem1 = replaced_subreg (in), tem2 = replaced_subreg (out),
+ (REG_P (tem1) && REG_P (tem2)))
+ && REGNO (tem1) < FIRST_PSEUDO_REGISTER
+ && REGNO (tem2) < FIRST_PSEUDO_REGISTER
+ && SECONDARY_MEMORY_NEEDED (REGNO_REG_CLASS (REGNO (tem1)),
+ REGNO_REG_CLASS (REGNO (tem2)),
GET_MODE (out)))
{
/* Get the memory to use and rewrite both registers to its mode. */
rtx loc = get_secondary_mem (in, GET_MODE (out), opnum, type);
if (GET_MODE (loc) != GET_MODE (out))
- out = gen_rtx_REG (GET_MODE (loc), REGNO (out));
+ out = gen_rtx_REG (GET_MODE (loc), reg_or_subregno (out));
if (GET_MODE (loc) != GET_MODE (in))
- in = gen_rtx_REG (GET_MODE (loc), REGNO (in));
+ in = gen_rtx_REG (GET_MODE (loc), reg_or_subregno (in));
gen_reload (loc, in, opnum, type);
gen_reload (out, loc, opnum, type);
rtx insn;
rtx op1;
rtx out_moded;
- rtx set;
+ rtx_insn *set;
op1 = find_replacement (&XEXP (in, 0));
if (op1 != XEXP (in, 0))
if (insn)
{
set_unique_reg_note (insn, REG_EQUIV, in);
- return insn;
+ return as_a <rtx_insn *> (insn);
}
fatal_insn ("failure trying to reload:", set);
NEW_RELOAD_REG is reload register that reload J is using for REG. */
static void
-delete_output_reload (rtx insn, int j, int last_reload_reg, rtx new_reload_reg)
+delete_output_reload (rtx_insn *insn, int j, int last_reload_reg,
+ rtx new_reload_reg)
{
- rtx output_reload_insn = spill_reg_store[last_reload_reg];
+ rtx_insn *output_reload_insn = spill_reg_store[last_reload_reg];
rtx reg = spill_reg_stored_to[last_reload_reg];
int k;
int n_occurrences;
int n_inherited = 0;
- rtx i1;
rtx substed;
unsigned regno;
int nregs;
/* It is possible that this reload has been only used to set another reload
we eliminated earlier and thus deleted this instruction too. */
- if (INSN_DELETED_P (output_reload_insn))
+ if (output_reload_insn->deleted ())
return;
/* Get the raw pseudo-register referred to. */
n_occurrences += count_occurrences (PATTERN (insn),
eliminate_regs (substed, VOIDmode,
NULL_RTX), 0);
- for (i1 = reg_equiv_alt_mem_list (REGNO (reg)); i1; i1 = XEXP (i1, 1))
+ for (rtx i1 = reg_equiv_alt_mem_list (REGNO (reg)); i1; i1 = XEXP (i1, 1))
{
gcc_assert (!rtx_equal_p (XEXP (i1, 0), substed));
n_occurrences += count_occurrences (PATTERN (insn), XEXP (i1, 0), 0);
and we're within the same basic block, then the value can only
pass through the reload reg and end up here.
Otherwise, give up--return. */
- for (i1 = NEXT_INSN (output_reload_insn);
+ for (rtx_insn *i1 = NEXT_INSN (output_reload_insn);
i1 != insn; i1 = NEXT_INSN (i1))
{
if (NOTE_INSN_BASIC_BLOCK_P (i1))
&& REG_BASIC_BLOCK (REGNO (reg)) >= NUM_FIXED_BLOCKS
&& find_regno_note (insn, REG_DEAD, REGNO (reg)))
{
- rtx i2;
+ rtx_insn *i2;
/* We know that it was used only between here and the beginning of
the current basic block. (We also know that the last use before
since if they are the only uses, they are dead. */
if (set != 0 && SET_DEST (set) == reg)
continue;
- if (LABEL_P (i2)
- || JUMP_P (i2))
+ if (LABEL_P (i2) || JUMP_P (i2))
break;
if ((NONJUMP_INSN_P (i2) || CALL_P (i2))
&& reg_mentioned_p (reg, PATTERN (i2)))
delete_address_reloads (i2, insn);
delete_insn (i2);
}
- if (LABEL_P (i2)
- || JUMP_P (i2))
+ if (LABEL_P (i2) || JUMP_P (i2))
break;
}
reload registers used in DEAD_INSN that are not used till CURRENT_INSN.
CURRENT_INSN is being reloaded, so we have to check its reloads too. */
static void
-delete_address_reloads (rtx dead_insn, rtx current_insn)
+delete_address_reloads (rtx_insn *dead_insn, rtx_insn *current_insn)
{
rtx set = single_set (dead_insn);
- rtx set2, dst, prev, next;
+ rtx set2, dst;
+ rtx_insn *prev, *next;
if (set)
{
rtx dst = SET_DEST (set);
/* Subfunction of delete_address_reloads: process registers found in X. */
static void
-delete_address_reloads_1 (rtx dead_insn, rtx x, rtx current_insn)
+delete_address_reloads_1 (rtx_insn *dead_insn, rtx x, rtx_insn *current_insn)
{
- rtx prev, set, dst, i2;
+ rtx_insn *prev, *i2;
+ rtx set, dst;
int i, j;
enum rtx_code code = GET_CODE (x);
IN is either identical to VALUE, or some cheaper place to reload from.
INC_AMOUNT is the number to increment or decrement by (always positive).
- This cannot be deduced from VALUE.
+ This cannot be deduced from VALUE. */
- Return the instruction that stores into RELOADREG. */
-
-static rtx
+static void
inc_for_reload (rtx reloadreg, rtx in, rtx value, int inc_amount)
{
/* REG or MEM to be copied and incremented. */
/* Nonzero if increment after copying. */
int post = (GET_CODE (value) == POST_DEC || GET_CODE (value) == POST_INC
|| GET_CODE (value) == POST_MODIFY);
- rtx last;
+ rtx_insn *last;
rtx inc;
- rtx add_insn;
+ rtx_insn *add_insn;
int code;
- rtx store;
rtx real_in = in == value ? incloc : in;
/* No hard register is equivalent to this register after
if (code >= 0)
{
extract_insn (add_insn);
- if (constrain_operands (1))
+ if (constrain_operands (1, get_enabled_alternatives (add_insn)))
{
/* If this is a pre-increment and we have incremented the value
where it lives, copy the incremented value to RELOADREG to
be used as an address. */
if (! post)
- add_insn = emit_insn (gen_move_insn (reloadreg, incloc));
-
- return add_insn;
+ emit_insn (gen_move_insn (reloadreg, incloc));
+ return;
}
}
delete_insns_since (last);
if (in != reloadreg)
emit_insn (gen_move_insn (reloadreg, real_in));
emit_insn (gen_add2_insn (reloadreg, inc));
- store = emit_insn (gen_move_insn (incloc, reloadreg));
+ emit_insn (gen_move_insn (incloc, reloadreg));
}
else
{
the original value. */
emit_insn (gen_add2_insn (reloadreg, inc));
- store = emit_insn (gen_move_insn (incloc, reloadreg));
+ emit_insn (gen_move_insn (incloc, reloadreg));
if (CONST_INT_P (inc))
- emit_insn (gen_add2_insn (reloadreg, GEN_INT (-INTVAL (inc))));
+ emit_insn (gen_add2_insn (reloadreg,
+ gen_int_mode (-INTVAL (inc),
+ GET_MODE (reloadreg))));
else
emit_insn (gen_sub2_insn (reloadreg, inc));
}
-
- return store;
}
\f
#ifdef AUTO_INC_DEC
static void
-add_auto_inc_notes (rtx insn, rtx x)
+add_auto_inc_notes (rtx_insn *insn, rtx x)
{
enum rtx_code code = GET_CODE (x);
const char *fmt;