/* 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 Free Software Foundation, Inc.
+ 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation,
+ Inc.
This file is part of GCC.
#include "expr.h"
#include "optabs.h"
#include "regs.h"
+#include "addresses.h"
#include "basic-block.h"
#include "reload.h"
#include "recog.h"
#include "toplev.h"
#include "except.h"
#include "tree.h"
+#include "target.h"
/* This file contains the reload pass of the compiler, which is
run after register allocation has been done. It checks that
with the constant it stands for. */
rtx *reg_equiv_constant;
+/* Element N is an invariant value to which pseudo reg N is equivalent.
+ eliminate_regs_in_insn uses this to replace pseudos in particular
+ contexts. */
+rtx *reg_equiv_invariant;
+
/* Element N is a memory location to which pseudo reg N is equivalent,
prior to any register elimination (such as frame pointer to stack
pointer). Depending on whether or not it is a valid address, this value
/* We allocate reg_equiv_memory_loc inside a varray so that the garbage
collector can keep track of what is inside. */
-varray_type reg_equiv_memory_loc_varray;
+VEC(rtx,gc) *reg_equiv_memory_loc_vec;
/* Element N is the address of stack slot to which pseudo reg N is equivalent.
This is used when the address is not valid as a memory address
static void copy_eh_notes (rtx, rtx);
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);
\f
/* Initialize the reload pass once per compilation. */
INIT_REG_SET (&spilled_pseudos);
INIT_REG_SET (&pseudos_counted);
- VARRAY_RTX_INIT (reg_equiv_memory_loc_varray, 0, "reg_equiv_memory_loc");
}
/* List of insn chains that are currently unused. */
Record memory equivalents in reg_mem_equiv so they can
be substituted eventually by altering the REG-rtx's. */
- reg_equiv_constant = xcalloc (max_regno, sizeof (rtx));
- reg_equiv_mem = xcalloc (max_regno, sizeof (rtx));
- reg_equiv_address = xcalloc (max_regno, sizeof (rtx));
- reg_max_ref_width = xcalloc (max_regno, sizeof (int));
- reg_old_renumber = xcalloc (max_regno, sizeof (short));
+ reg_equiv_constant = XCNEWVEC (rtx, max_regno);
+ reg_equiv_invariant = XCNEWVEC (rtx, max_regno);
+ reg_equiv_mem = XCNEWVEC (rtx, max_regno);
+ reg_equiv_address = XCNEWVEC (rtx, max_regno);
+ 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 = xmalloc (max_regno * sizeof (HARD_REG_SET));
- pseudo_previous_regs = xcalloc (max_regno, sizeof (HARD_REG_SET));
+ pseudo_forbidden_regs = XNEWVEC (HARD_REG_SET, max_regno);
+ pseudo_previous_regs = XCNEWVEC (HARD_REG_SET, max_regno);
CLEAR_HARD_REG_SET (bad_spill_regs_global);
{
/* This is PLUS of frame pointer and a constant,
and might be shared. Unshare it. */
- reg_equiv_constant[i] = copy_rtx (x);
+ reg_equiv_invariant[i] = copy_rtx (x);
num_eliminable_invariants++;
}
- else if (x == frame_pointer_rtx
- || x == arg_pointer_rtx)
+ else if (x == frame_pointer_rtx || x == arg_pointer_rtx)
{
- reg_equiv_constant[i] = x;
+ reg_equiv_invariant[i] = x;
num_eliminable_invariants++;
}
else if (LEGITIMATE_CONSTANT_P (x))
/* We used to use alloca here, but the size of what it would try to
allocate would occasionally cause it to exceed the stack limit and
cause a core dump. */
- offsets_known_at = xmalloc (num_labels);
- offsets_at = xmalloc (num_labels * NUM_ELIMINABLE_REGS * sizeof (HOST_WIDE_INT));
+ offsets_known_at = XNEWVEC (char, num_labels);
+ offsets_at = (HOST_WIDE_INT (*)[NUM_ELIMINABLE_REGS]) xmalloc (num_labels * NUM_ELIMINABLE_REGS * sizeof (HOST_WIDE_INT));
/* Alter each pseudo-reg rtx to contain its hard reg number.
Assign stack slots to the pseudos that lack hard regs or equivalents.
MEM_IN_STRUCT_P (reg) = MEM_SCALAR_P (reg) = 0;
MEM_ATTRS (reg) = 0;
}
+ MEM_NOTRAP_P (reg) = 1;
}
else if (reg_equiv_mem[i])
XEXP (reg_equiv_mem[i], 0) = addr;
/* Indicate that we no longer have known memory locations or constants. */
if (reg_equiv_constant)
free (reg_equiv_constant);
+ if (reg_equiv_invariant)
+ free (reg_equiv_invariant);
reg_equiv_constant = 0;
- VARRAY_GROW (reg_equiv_memory_loc_varray, 0);
+ reg_equiv_invariant = 0;
+ VEC_free (rtx, gc, reg_equiv_memory_loc_vec);
reg_equiv_memory_loc = 0;
if (offsets_known_at)
case 'p':
cls = (int) reg_class_subunion[cls]
- [(int) MODE_BASE_REG_CLASS (VOIDmode)];
+ [(int) base_reg_class (VOIDmode, ADDRESS, SCRATCH)];
break;
case 'g':
default:
if (EXTRA_ADDRESS_CONSTRAINT (c, p))
cls = (int) reg_class_subunion[cls]
- [(int) MODE_BASE_REG_CLASS (VOIDmode)];
+ [(int) base_reg_class (VOIDmode, ADDRESS, SCRATCH)];
else
cls = (int) reg_class_subunion[cls]
[(int) REG_CLASS_FROM_CONSTRAINT (c, p)];
/* 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_constant[REGNO (SET_DEST (set))]
+ || (reg_equiv_invariant[REGNO (SET_DEST (set))]))
+ && reg_equiv_init[REGNO (SET_DEST (set))])
continue;
/* If needed, eliminate any eliminable registers. */
&& rld[r].regno == -1)
if (! find_reg (chain, i))
{
+ if (dump_file)
+ fprintf(dump_file, "reload failure for reload %d\n", r);
spill_failure (chain->insn, rld[r].class);
failure = 1;
return;
{
error ("unable to find a register to spill in class %qs",
reg_class_names[class]);
+
+ if (dump_file)
+ {
+ fprintf (dump_file, "\nReloads for insn # %d\n", INSN_UID (insn));
+ debug_reload_to_stream (dump_file);
+ }
fatal_insn ("this is the insn:", insn);
}
}
if (reg_renumber[i] < 0
&& REG_N_REFS (i) > 0
&& reg_equiv_constant[i] == 0
+ && (reg_equiv_invariant[i] == 0 || reg_equiv_init[i] == 0)
&& reg_equiv_memory_loc[i] == 0)
{
rtx x;
encounter, return the actual location so that find_reloads will do
the proper thing. */
-rtx
-eliminate_regs (rtx x, enum machine_mode mem_mode, rtx insn)
+static rtx
+eliminate_regs_1 (rtx x, enum machine_mode mem_mode, rtx insn,
+ bool may_use_invariant)
{
enum rtx_code code = GET_CODE (x);
struct elim_table *ep;
}
else if (reg_renumber && reg_renumber[regno] < 0
- && reg_equiv_constant && reg_equiv_constant[regno]
- && ! CONSTANT_P (reg_equiv_constant[regno]))
- return eliminate_regs (copy_rtx (reg_equiv_constant[regno]),
- mem_mode, insn);
+ && reg_equiv_invariant && reg_equiv_invariant[regno])
+ {
+ if (may_use_invariant)
+ return eliminate_regs_1 (copy_rtx (reg_equiv_invariant[regno]),
+ mem_mode, insn, true);
+ /* There exists at least one use of REGNO that cannot be
+ eliminated. Prevent the defining insn from being deleted. */
+ reg_equiv_init[regno] = NULL_RTX;
+ alter_reg (regno, -1);
+ }
return x;
/* You might think handling MINUS in a manner similar to PLUS is a
operand of a load-address insn. */
{
- rtx new0 = eliminate_regs (XEXP (x, 0), mem_mode, insn);
- rtx new1 = eliminate_regs (XEXP (x, 1), mem_mode, insn);
+ rtx new0 = eliminate_regs_1 (XEXP (x, 0), mem_mode, insn, true);
+ rtx new1 = eliminate_regs_1 (XEXP (x, 1), mem_mode, insn, true);
if (reg_renumber && (new0 != XEXP (x, 0) || new1 != XEXP (x, 1)))
{
case GE: case GT: case GEU: case GTU:
case LE: case LT: case LEU: case LTU:
{
- rtx new0 = eliminate_regs (XEXP (x, 0), mem_mode, insn);
- rtx new1
- = XEXP (x, 1) ? eliminate_regs (XEXP (x, 1), mem_mode, insn) : 0;
+ rtx new0 = eliminate_regs_1 (XEXP (x, 0), mem_mode, insn, false);
+ rtx new1 = XEXP (x, 1)
+ ? eliminate_regs_1 (XEXP (x, 1), mem_mode, insn, false) : 0;
if (new0 != XEXP (x, 0) || new1 != XEXP (x, 1))
return gen_rtx_fmt_ee (code, GET_MODE (x), new0, new1);
/* If we have something in XEXP (x, 0), the usual case, eliminate it. */
if (XEXP (x, 0))
{
- new = eliminate_regs (XEXP (x, 0), mem_mode, insn);
+ new = eliminate_regs_1 (XEXP (x, 0), mem_mode, insn, true);
if (new != XEXP (x, 0))
{
/* If this is a REG_DEAD note, it is not valid anymore.
REG_DEAD note for the stack or frame pointer. */
if (GET_MODE (x) == REG_DEAD)
return (XEXP (x, 1)
- ? eliminate_regs (XEXP (x, 1), mem_mode, insn)
+ ? eliminate_regs_1 (XEXP (x, 1), mem_mode, insn, true)
: NULL_RTX);
x = gen_rtx_EXPR_LIST (REG_NOTE_KIND (x), new, XEXP (x, 1));
strictly needed, but it simplifies the code. */
if (XEXP (x, 1))
{
- new = eliminate_regs (XEXP (x, 1), mem_mode, insn);
+ new = eliminate_regs_1 (XEXP (x, 1), mem_mode, insn, true);
if (new != XEXP (x, 1))
return
gen_rtx_fmt_ee (GET_CODE (x), GET_MODE (x), XEXP (x, 0), new);
case CTZ:
case POPCOUNT:
case PARITY:
- new = eliminate_regs (XEXP (x, 0), mem_mode, insn);
+ new = eliminate_regs_1 (XEXP (x, 0), mem_mode, insn, false);
if (new != XEXP (x, 0))
return gen_rtx_fmt_e (code, GET_MODE (x), new);
return x;
new = SUBREG_REG (x);
}
else
- new = eliminate_regs (SUBREG_REG (x), mem_mode, insn);
+ new = eliminate_regs_1 (SUBREG_REG (x), mem_mode, insn, false);
if (new != SUBREG_REG (x))
{
case more efficiently. */
return
replace_equiv_address_nv (x,
- eliminate_regs (XEXP (x, 0),
- GET_MODE (x), insn));
+ eliminate_regs_1 (XEXP (x, 0), GET_MODE (x),
+ insn, true));
case USE:
/* Handle insn_list USE that a call to a pure function may generate. */
- new = eliminate_regs (XEXP (x, 0), 0, insn);
+ new = eliminate_regs_1 (XEXP (x, 0), 0, insn, false);
if (new != XEXP (x, 0))
return gen_rtx_USE (GET_MODE (x), new);
return x;
{
if (*fmt == 'e')
{
- new = eliminate_regs (XEXP (x, i), mem_mode, insn);
+ new = eliminate_regs_1 (XEXP (x, i), mem_mode, insn, false);
if (new != XEXP (x, i) && ! copied)
{
- rtx new_x = rtx_alloc (code);
- memcpy (new_x, x, RTX_SIZE (code));
- x = new_x;
+ x = shallow_copy_rtx (x);
copied = 1;
}
XEXP (x, i) = new;
int copied_vec = 0;
for (j = 0; j < XVECLEN (x, i); j++)
{
- new = eliminate_regs (XVECEXP (x, i, j), mem_mode, insn);
+ new = eliminate_regs_1 (XVECEXP (x, i, j), mem_mode, insn, false);
if (new != XVECEXP (x, i, j) && ! copied_vec)
{
rtvec new_v = gen_rtvec_v (XVECLEN (x, i),
XVEC (x, i)->elem);
if (! copied)
{
- rtx new_x = rtx_alloc (code);
- memcpy (new_x, x, RTX_SIZE (code));
- x = new_x;
+ x = shallow_copy_rtx (x);
copied = 1;
}
XVEC (x, i) = new_v;
return x;
}
+rtx
+eliminate_regs (rtx x, enum machine_mode mem_mode, rtx insn)
+{
+ return eliminate_regs_1 (x, mem_mode, insn, false);
+}
+
/* Scan rtx X for modifications of elimination target registers. Update
the table of eliminables to reflect the changed state. MEM_MODE is
the mode of an enclosing MEM rtx, or VOIDmode if not within a MEM. */
rtx substed_operand[MAX_RECOG_OPERANDS];
rtx orig_operand[MAX_RECOG_OPERANDS];
struct elim_table *ep;
- rtx plus_src;
+ rtx plus_src, plus_cst_src;
if (! insn_is_asm && icode < 0)
{
/* We allow one special case which happens to work on all machines we
currently support: a single set with the source or a REG_EQUAL
note being a PLUS of an eliminable register and a constant. */
- plus_src = 0;
+ plus_src = plus_cst_src = 0;
if (old_set && REG_P (SET_DEST (old_set)))
{
- /* First see if the source is of the form (plus (reg) CST). */
- if (GET_CODE (SET_SRC (old_set)) == PLUS
- && REG_P (XEXP (SET_SRC (old_set), 0))
- && GET_CODE (XEXP (SET_SRC (old_set), 1)) == CONST_INT
- && REGNO (XEXP (SET_SRC (old_set), 0)) < FIRST_PSEUDO_REGISTER)
+ if (GET_CODE (SET_SRC (old_set)) == PLUS)
plus_src = SET_SRC (old_set);
- else if (REG_P (SET_SRC (old_set)))
+ /* First see if the source is of the form (plus (...) CST). */
+ if (plus_src
+ && GET_CODE (XEXP (plus_src, 1)) == CONST_INT)
+ plus_cst_src = plus_src;
+ else if (REG_P (SET_SRC (old_set))
+ || plus_src)
{
/* Otherwise, see if we have a REG_EQUAL note of the form
- (plus (reg) CST). */
+ (plus (...) CST). */
rtx links;
for (links = REG_NOTES (insn); links; links = XEXP (links, 1))
{
if (REG_NOTE_KIND (links) == REG_EQUAL
&& GET_CODE (XEXP (links, 0)) == PLUS
- && REG_P (XEXP (XEXP (links, 0), 0))
- && GET_CODE (XEXP (XEXP (links, 0), 1)) == CONST_INT
- && REGNO (XEXP (XEXP (links, 0), 0)) < FIRST_PSEUDO_REGISTER)
+ && GET_CODE (XEXP (XEXP (links, 0), 1)) == CONST_INT)
{
- plus_src = XEXP (links, 0);
+ plus_cst_src = XEXP (links, 0);
break;
}
}
}
+
+ /* Check that the first operand of the PLUS is a hard reg or
+ the lowpart subreg of one. */
+ if (plus_cst_src)
+ {
+ rtx reg = XEXP (plus_cst_src, 0);
+ if (GET_CODE (reg) == SUBREG && subreg_lowpart_p (reg))
+ reg = SUBREG_REG (reg);
+
+ if (!REG_P (reg) || REGNO (reg) >= FIRST_PSEUDO_REGISTER)
+ plus_cst_src = 0;
+ }
}
- if (plus_src)
+ if (plus_cst_src)
{
- rtx reg = XEXP (plus_src, 0);
- HOST_WIDE_INT offset = INTVAL (XEXP (plus_src, 1));
+ rtx reg = XEXP (plus_cst_src, 0);
+ HOST_WIDE_INT offset = INTVAL (XEXP (plus_cst_src, 1));
+
+ if (GET_CODE (reg) == SUBREG)
+ reg = SUBREG_REG (reg);
for (ep = reg_eliminate; ep < ®_eliminate[NUM_ELIMINABLE_REGS]; ep++)
if (ep->from_rtx == reg && ep->can_eliminate)
{
+ rtx to_rtx = ep->to_rtx;
offset += ep->offset;
+ if (GET_CODE (XEXP (plus_cst_src, 0)) == SUBREG)
+ to_rtx = gen_lowpart (GET_MODE (XEXP (plus_cst_src, 0)),
+ to_rtx);
if (offset == 0)
{
int num_clobbers;
There's not much we can do if that doesn't work. */
PATTERN (insn) = gen_rtx_SET (VOIDmode,
SET_DEST (old_set),
- ep->to_rtx);
+ to_rtx);
num_clobbers = 0;
INSN_CODE (insn) = recog (PATTERN (insn), insn, &num_clobbers);
if (num_clobbers)
/* If we have a nonzero offset, and the source is already
a simple REG, the following transformation would
increase the cost of the insn by replacing a simple REG
- with (plus (reg sp) CST). So try only when plus_src
- comes from old_set proper, not REG_NOTES. */
- else if (SET_SRC (old_set) == plus_src)
+ with (plus (reg sp) CST). So try only when we already
+ had a PLUS before. */
+ else if (plus_src)
{
new_body = old_body;
if (! replace)
PATTERN (insn) = new_body;
old_set = single_set (insn);
- XEXP (SET_SRC (old_set), 0) = ep->to_rtx;
+ XEXP (SET_SRC (old_set), 0) = to_rtx;
XEXP (SET_SRC (old_set), 1) = GEN_INT (offset);
}
else
/* For an asm statement, every operand is eliminable. */
if (insn_is_asm || insn_data[icode].operand[i].eliminable)
{
+ bool is_set_src, in_plus;
+
/* Check for setting a register that we know about. */
if (recog_data.operand_type[i] != OP_IN
&& REG_P (orig_operand[i]))
ep->can_eliminate = 0;
}
- substed_operand[i] = eliminate_regs (recog_data.operand[i], 0,
- replace ? insn : NULL_RTX);
+ /* Companion to the above plus substitution, we can allow
+ invariants as the source of a plain move. */
+ is_set_src = false;
+ if (old_set && recog_data.operand_loc[i] == &SET_SRC (old_set))
+ is_set_src = true;
+ in_plus = false;
+ if (plus_src
+ && (recog_data.operand_loc[i] == &XEXP (plus_src, 0)
+ || recog_data.operand_loc[i] == &XEXP (plus_src, 1)))
+ in_plus = true;
+
+ substed_operand[i]
+ = eliminate_regs_1 (recog_data.operand[i], 0,
+ replace ? insn : NULL_RTX,
+ is_set_src || in_plus);
if (substed_operand[i] != orig_operand[i])
val = 1;
/* Terminate the search in check_eliminable_occurrences at
|| GET_CODE (SET_SRC (old_set)) == PLUS))
{
int new_icode = recog (PATTERN (insn), insn, 0);
- if (new_icode < 0)
- INSN_CODE (insn) = icode;
+ if (new_icode >= 0)
+ INSN_CODE (insn) = new_icode;
}
}
of spill registers to be needed in the final reload pass than in
the pre-passes. */
if (val && REG_NOTES (insn) != 0)
- REG_NOTES (insn) = eliminate_regs (REG_NOTES (insn), 0, REG_NOTES (insn));
+ REG_NOTES (insn)
+ = eliminate_regs_1 (REG_NOTES (insn), 0, REG_NOTES (insn), true);
return val;
}
sp-adjusting insns for this case. */
|| (current_function_calls_alloca
&& EXIT_IGNORE_STACK)
+ || current_function_accesses_prior_frames
|| FRAME_POINTER_REQUIRED);
num_eliminable = 0;
}
}
\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)
+{
+ rtx note = find_reg_note (insn, REG_EH_REGION, NULL_RTX);
+ unsigned int trap_count;
+ rtx i;
+
+ if (note == NULL)
+ return;
+
+ if (may_trap_p (PATTERN (insn)))
+ trap_count = 1;
+ else
+ {
+ remove_note (insn, note);
+ trap_count = 0;
+ }
+
+ for (i = NEXT_INSN (prev); i != next; i = NEXT_INSN (i))
+ if (INSN_P (i) && i != insn && may_trap_p (PATTERN (i)))
+ {
+ trap_count++;
+ REG_NOTES (i)
+ = gen_rtx_EXPR_LIST (REG_EH_REGION, XEXP (note, 0), REG_NOTES (i));
+ }
+}
+
/* Reload pseudo-registers into hard regs around each insn as needed.
Additional register load insns are output before the insn that needs it
and perhaps store insns after insns that modify the reloaded pseudo reg.
memset (spill_reg_rtx, 0, sizeof spill_reg_rtx);
memset (spill_reg_store, 0, sizeof spill_reg_store);
- reg_last_reload_reg = xcalloc (max_regno, sizeof (rtx));
- reg_has_output_reload = xmalloc (max_regno);
+ reg_last_reload_reg = XCNEWVEC (rtx, max_regno);
+ reg_has_output_reload = XNEWVEC (char, max_regno);
CLEAR_HARD_REG_SET (reg_reloaded_valid);
CLEAR_HARD_REG_SET (reg_reloaded_call_part_clobbered);
and that we moved the structure into). */
subst_reloads (insn);
+ /* Adjust the exception region notes for loads and stores. */
+ if (flag_non_call_exceptions && !CALL_P (insn))
+ fixup_eh_region_note (insn, prev, next);
+
/* 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))
if (p != insn && INSN_P (p)
mode = GET_MODE (rld[r].in_reg);
}
#ifdef AUTO_INC_DEC
- else if ((GET_CODE (rld[r].in_reg) == PRE_INC
- || GET_CODE (rld[r].in_reg) == PRE_DEC
- || GET_CODE (rld[r].in_reg) == POST_INC
- || GET_CODE (rld[r].in_reg) == POST_DEC)
+ else if (GET_RTX_CLASS (GET_CODE (rld[r].in_reg)) == RTX_AUTOINC
&& REG_P (XEXP (rld[r].in_reg, 0)))
{
regno = REGNO (XEXP (rld[r].in_reg, 0));
enough. */
|| ((REGISTER_MOVE_COST (mode, last_class, class)
< MEMORY_MOVE_COST (mode, class, 1))
-#ifdef SECONDARY_INPUT_RELOAD_CLASS
- && (SECONDARY_INPUT_RELOAD_CLASS (class, mode,
- last_reg)
+ && (secondary_reload_class (1, class, mode,
+ last_reg)
== NO_REGS)
-#endif
#ifdef SECONDARY_MEMORY_NEEDED
&& ! SECONDARY_MEMORY_NEEDED (last_class, class,
mode)
if (equiv != 0)
{
- if (regno_clobbered_p (regno, insn, rld[r].mode, 0))
+ if (regno_clobbered_p (regno, insn, rld[r].mode, 2))
switch (rld[r].when_needed)
{
case RELOAD_FOR_OTHER_ADDRESS:
if (j == n_reloads
&& max_input_address_opnum <= min_conflicting_input_opnum)
{
+ gcc_assert (rld[i].when_needed != RELOAD_FOR_OUTPUT);
+
for (j = 0; j < n_reloads; j++)
if (i != j && rld[j].reg_rtx != 0
&& rtx_equal_p (rld[i].reg_rtx, rld[j].reg_rtx)
if they were for inputs, RELOAD_OTHER for outputs. Note that
this test is equivalent to looking for reloads for this operand
number. */
- /* We must take special care when there are two or more reloads to
- be merged and a RELOAD_FOR_OUTPUT_ADDRESS reload that loads the
- same value or a part of it; we must not change its type if there
- is a conflicting input. */
+ /* We must take special care with RELOAD_FOR_OUTPUT_ADDRESS; it may
+ share registers with a RELOAD_FOR_INPUT, so we can not change it
+ to RELOAD_FOR_OTHER_ADDRESS. We should never need to, since we
+ do not modify RELOAD_FOR_OUTPUT. */
if (rld[i].when_needed == RELOAD_OTHER)
for (j = 0; j < n_reloads; j++)
if (rld[j].in != 0
&& rld[j].when_needed != RELOAD_OTHER
&& rld[j].when_needed != RELOAD_FOR_OTHER_ADDRESS
+ && rld[j].when_needed != RELOAD_FOR_OUTPUT_ADDRESS
&& (! conflicting_input
|| rld[j].when_needed == RELOAD_FOR_INPUT_ADDRESS
|| rld[j].when_needed == RELOAD_FOR_INPADDR_ADDRESS)
static rtx 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
+ of class NEW_CLASS with mode NEW_MODE. Or alternatively, if alt_reload_reg
+ is nonzero, if that is suitable. On success, change *RELOAD_REG to the
+ adjusted register, and return true. Otherwise, return false. */
+static bool
+reload_adjust_reg_for_temp (rtx *reload_reg, rtx alt_reload_reg,
+ enum reg_class new_class,
+ enum machine_mode new_mode)
+
+{
+ rtx reg;
+
+ for (reg = *reload_reg; reg; reg = alt_reload_reg, alt_reload_reg = 0)
+ {
+ unsigned regno = REGNO (reg);
+
+ if (!TEST_HARD_REG_BIT (reg_class_contents[(int) new_class], regno))
+ continue;
+ if (GET_MODE (reg) != new_mode)
+ {
+ if (!HARD_REGNO_MODE_OK (regno, new_mode))
+ continue;
+ if (hard_regno_nregs[regno][new_mode]
+ > hard_regno_nregs[regno][GET_MODE (reg)])
+ continue;
+ reg = reload_adjust_reg_for_mode (reg, new_mode);
+ }
+ *reload_reg = reg;
+ return true;
+ }
+ return false;
+}
+
+/* Check if *RELOAD_REG is suitable as a scratch register for the reload
+ pattern with insn_code ICODE, or alternatively, if alt_reload_reg is
+ nonzero, if that is suitable. On success, change *RELOAD_REG to the
+ adjusted register, and return true. Otherwise, return false. */
+static bool
+reload_adjust_reg_for_icode (rtx *reload_reg, rtx alt_reload_reg,
+ enum insn_code icode)
+
+{
+ enum reg_class new_class = scratch_reload_class (icode);
+ enum 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);
+}
+
/* Generate insns to perform reload RL, which is for the insn in CHAIN and
has the number J. OLD contains the value to be used as input. */
if (mode == VOIDmode)
mode = rl->inmode;
-#ifdef SECONDARY_INPUT_RELOAD_CLASS
- /* If we need a secondary register for this operation, see if
- the value is already in a register in that class. Don't
- do this if the secondary register will be used as a scratch
- register. */
-
- if (rl->secondary_in_reload >= 0
- && rl->secondary_in_icode == CODE_FOR_nothing
- && optimize)
- oldequiv
- = find_equiv_reg (old, insn,
- rld[rl->secondary_in_reload].class,
- -1, NULL, 0, mode);
-#endif
-
- /* If reloading from memory, see if there is a register
- that already holds the same value. If so, reload from there.
- We can pass 0 as the reload_reg_p argument because
- any other reload has either already been emitted,
- in which case find_equiv_reg will see the reload-insn,
- or has yet to be emitted, in which case it doesn't matter
- because we will use this equiv reg right away. */
-
- if (oldequiv == 0 && optimize
- && (MEM_P (old)
- || (REG_P (old)
- && REGNO (old) >= FIRST_PSEUDO_REGISTER
- && reg_renumber[REGNO (old)] < 0)))
- oldequiv = find_equiv_reg (old, insn, ALL_REGS, -1, NULL, 0, mode);
-
- if (oldequiv)
- {
- unsigned int regno = true_regnum (oldequiv);
-
- /* Don't use OLDEQUIV if any other reload changes it at an
- earlier stage of this insn or at this stage. */
- if (! free_for_value_p (regno, rl->mode, rl->opnum, rl->when_needed,
- rl->in, const0_rtx, j, 0))
- oldequiv = 0;
-
- /* If it is no cheaper to copy from OLDEQUIV into the
- reload register than it would be to move from memory,
- don't use it. Likewise, if we need a secondary register
- or memory. */
-
- if (oldequiv != 0
- && (((enum reg_class) REGNO_REG_CLASS (regno) != rl->class
- && (REGISTER_MOVE_COST (mode, REGNO_REG_CLASS (regno),
- rl->class)
- >= MEMORY_MOVE_COST (mode, rl->class, 1)))
-#ifdef SECONDARY_INPUT_RELOAD_CLASS
- || (SECONDARY_INPUT_RELOAD_CLASS (rl->class,
- mode, oldequiv)
- != NO_REGS)
-#endif
-#ifdef SECONDARY_MEMORY_NEEDED
- || SECONDARY_MEMORY_NEEDED (REGNO_REG_CLASS (regno),
- rl->class,
- mode)
-#endif
- ))
- oldequiv = 0;
- }
-
/* 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. */
- if (oldequiv == 0
- && reload_override_in[j]
+ if (reload_override_in[j]
&& REG_P (rl->in_reg))
{
oldequiv = old;
/* We can't do that, so output an insn to load RELOADREG. */
-#ifdef SECONDARY_INPUT_RELOAD_CLASS
/* If we have a secondary reload, pick up the secondary register
and icode, if any. If OLDEQUIV and OLD are different or
if this is an in-out reload, recompute whether or not we
if (! special && rl->secondary_in_reload >= 0)
{
rtx second_reload_reg = 0;
+ rtx third_reload_reg = 0;
int secondary_reload = rl->secondary_in_reload;
rtx real_oldequiv = oldequiv;
rtx real_old = old;
rtx tmp;
enum insn_code icode;
+ enum insn_code tertiary_icode = CODE_FOR_nothing;
/* If OLDEQUIV is a pseudo with a MEM, get the real MEM
and similarly for OLD.
}
second_reload_reg = rld[secondary_reload].reg_rtx;
+ if (rld[secondary_reload].secondary_in_reload >= 0)
+ {
+ int tertiary_reload = rld[secondary_reload].secondary_in_reload;
+
+ third_reload_reg = rld[tertiary_reload].reg_rtx;
+ tertiary_icode = rld[secondary_reload].secondary_in_icode;
+ /* We'd have to add more code for quartary reloads. */
+ gcc_assert (rld[tertiary_reload].secondary_in_reload < 0);
+ }
icode = rl->secondary_in_icode;
if ((old != oldequiv && ! rtx_equal_p (old, oldequiv))
|| (rl->in != 0 && rl->out != 0))
{
- enum reg_class new_class
- = SECONDARY_INPUT_RELOAD_CLASS (rl->class,
- mode, real_oldequiv);
+ secondary_reload_info sri, sri2;
+ enum reg_class new_class, new_t_class;
+
+ sri.icode = CODE_FOR_nothing;
+ sri.prev_sri = NULL;
+ new_class = targetm.secondary_reload (1, real_oldequiv, rl->class,
+ mode, &sri);
- if (new_class == NO_REGS)
+ if (new_class == NO_REGS && sri.icode == CODE_FOR_nothing)
second_reload_reg = 0;
- else
+ else if (new_class == NO_REGS)
{
- enum insn_code new_icode;
- enum machine_mode new_mode;
-
- if (! TEST_HARD_REG_BIT (reg_class_contents[(int) new_class],
- REGNO (second_reload_reg)))
- oldequiv = old, real_oldequiv = real_old;
+ if (reload_adjust_reg_for_icode (&second_reload_reg,
+ third_reload_reg, sri.icode))
+ icode = sri.icode, third_reload_reg = 0;
else
+ oldequiv = old, real_oldequiv = real_old;
+ }
+ else if (sri.icode != CODE_FOR_nothing)
+ /* We currently lack a way to express this in reloads. */
+ gcc_unreachable ();
+ else
+ {
+ sri2.icode = CODE_FOR_nothing;
+ sri2.prev_sri = &sri;
+ new_t_class = targetm.secondary_reload (1, real_oldequiv,
+ new_class, mode, &sri);
+ if (new_t_class == NO_REGS && sri2.icode == CODE_FOR_nothing)
{
- new_icode = reload_in_optab[(int) mode];
- if (new_icode != CODE_FOR_nothing
- && ((insn_data[(int) new_icode].operand[0].predicate
- && ! ((*insn_data[(int) new_icode].operand[0].predicate)
- (reloadreg, mode)))
- || (insn_data[(int) new_icode].operand[1].predicate
- && ! ((*insn_data[(int) new_icode].operand[1].predicate)
- (real_oldequiv, mode)))))
- new_icode = CODE_FOR_nothing;
-
- if (new_icode == CODE_FOR_nothing)
- new_mode = mode;
+ if (reload_adjust_reg_for_temp (&second_reload_reg,
+ third_reload_reg,
+ new_class, mode))
+ third_reload_reg = 0, tertiary_icode = sri2.icode;
else
- new_mode = insn_data[(int) new_icode].operand[2].mode;
+ oldequiv = old, real_oldequiv = real_old;
+ }
+ else if (new_t_class == NO_REGS && sri2.icode != CODE_FOR_nothing)
+ {
+ rtx intermediate = second_reload_reg;
- if (GET_MODE (second_reload_reg) != new_mode)
+ if (reload_adjust_reg_for_temp (&intermediate, NULL,
+ new_class, mode)
+ && reload_adjust_reg_for_icode (&third_reload_reg, NULL,
+ sri2.icode))
{
- if (!HARD_REGNO_MODE_OK (REGNO (second_reload_reg),
- new_mode))
- oldequiv = old, real_oldequiv = real_old;
- else
- second_reload_reg
- = reload_adjust_reg_for_mode (second_reload_reg,
- new_mode);
+ second_reload_reg = intermediate;
+ tertiary_icode = sri2.icode;
+ }
+ else
+ oldequiv = old, real_oldequiv = real_old;
+ }
+ else if (new_t_class != NO_REGS && sri2.icode == CODE_FOR_nothing)
+ {
+ rtx intermediate = second_reload_reg;
+
+ if (reload_adjust_reg_for_temp (&intermediate, NULL,
+ new_class, mode)
+ && reload_adjust_reg_for_temp (&third_reload_reg, NULL,
+ new_t_class, mode))
+ {
+ second_reload_reg = intermediate;
+ tertiary_icode = sri2.icode;
}
+ else
+ oldequiv = old, real_oldequiv = real_old;
}
+ else
+ /* This could be handled more intelligently too. */
+ oldequiv = old, real_oldequiv = real_old;
}
}
{
if (icode != CODE_FOR_nothing)
{
+ /* We'd have to add extra code to handle this case. */
+ gcc_assert (!third_reload_reg);
+
emit_insn (GEN_FCN (icode) (reloadreg, real_oldequiv,
second_reload_reg));
special = 1;
{
/* See if we need a scratch register to load the
intermediate register (a tertiary reload). */
- enum insn_code tertiary_icode
- = rld[secondary_reload].secondary_in_icode;
-
if (tertiary_icode != CODE_FOR_nothing)
{
- rtx third_reload_reg
- = rld[rld[secondary_reload].secondary_in_reload].reg_rtx;
-
emit_insn ((GEN_FCN (tertiary_icode)
(second_reload_reg, real_oldequiv,
third_reload_reg)));
}
+ else if (third_reload_reg)
+ {
+ gen_reload (third_reload_reg, real_oldequiv,
+ rl->opnum,
+ rl->when_needed);
+ gen_reload (second_reload_reg, third_reload_reg,
+ rl->opnum,
+ rl->when_needed);
+ }
else
gen_reload (second_reload_reg, real_oldequiv,
rl->opnum,
}
}
}
-#endif
if (! special && ! rtx_equal_p (reloadreg, oldequiv))
{
if (GET_MODE (reloadreg) != mode)
reloadreg = reload_adjust_reg_for_mode (reloadreg, mode);
-#ifdef SECONDARY_OUTPUT_RELOAD_CLASS
-
/* If we need two reload regs, set RELOADREG to the intermediate
one, since it will be stored into OLD. We might need a secondary
register only for an input reload, so check again here. */
if (rl->secondary_out_reload >= 0)
{
rtx real_old = old;
+ int secondary_reload = rl->secondary_out_reload;
+ int tertiary_reload = rld[secondary_reload].secondary_out_reload;
if (REG_P (old) && REGNO (old) >= FIRST_PSEUDO_REGISTER
&& reg_equiv_mem[REGNO (old)] != 0)
real_old = reg_equiv_mem[REGNO (old)];
- if ((SECONDARY_OUTPUT_RELOAD_CLASS (rl->class,
- mode, real_old)
- != NO_REGS))
+ if (secondary_reload_class (0, rl->class, mode, real_old) != NO_REGS)
{
rtx second_reloadreg = reloadreg;
- reloadreg = rld[rl->secondary_out_reload].reg_rtx;
+ reloadreg = rld[secondary_reload].reg_rtx;
/* See if RELOADREG is to be used as a scratch register
or as an intermediate register. */
if (rl->secondary_out_icode != CODE_FOR_nothing)
{
+ /* We'd have to add extra code to handle this case. */
+ gcc_assert (tertiary_reload < 0);
+
emit_insn ((GEN_FCN (rl->secondary_out_icode)
(real_old, second_reloadreg, reloadreg)));
special = 1;
/* See if we need both a scratch and intermediate reload
register. */
- int secondary_reload = rl->secondary_out_reload;
enum insn_code tertiary_icode
= rld[secondary_reload].secondary_out_icode;
+ /* We'd have to add more code for quartary reloads. */
+ gcc_assert (tertiary_reload < 0
+ || rld[tertiary_reload].secondary_out_reload < 0);
+
if (GET_MODE (reloadreg) != mode)
reloadreg = reload_adjust_reg_for_mode (reloadreg, mode);
if (tertiary_icode != CODE_FOR_nothing)
{
- rtx third_reloadreg
- = rld[rld[secondary_reload].secondary_out_reload].reg_rtx;
+ rtx third_reloadreg = rld[tertiary_reload].reg_rtx;
rtx tem;
/* Copy primary reload reg to secondary reload reg.
}
else
- /* Copy between the reload regs here and then to
- OUT later. */
+ {
+ /* Copy between the reload regs here and then to
+ OUT later. */
- gen_reload (reloadreg, second_reloadreg,
- rl->opnum, rl->when_needed);
+ gen_reload (reloadreg, second_reloadreg,
+ rl->opnum, rl->when_needed);
+ if (tertiary_reload >= 0)
+ {
+ rtx third_reloadreg = rld[tertiary_reload].reg_rtx;
+
+ gen_reload (third_reloadreg, reloadreg,
+ rl->opnum, rl->when_needed);
+ reloadreg = third_reloadreg;
+ }
+ }
}
}
}
-#endif
/* Output the last reload insn. */
if (! special)
return;
/* If is a JUMP_INSN, we can't support output reloads yet. */
- gcc_assert (!JUMP_P (insn));
+ gcc_assert (NONJUMP_INSN_P (insn));
emit_output_reload_insns (chain, rld + j, j);
}
/* If a register gets output-reloaded from a non-spill register,
that invalidates any previous reloaded copy of it.
But forget_old_reloads_1 won't get to see it, because
- it thinks only about the original insn. So invalidate it here. */
- if (i < 0 && rld[r].out != 0
- && (REG_P (rld[r].out)
- || (MEM_P (rld[r].out)
+ it thinks only about the original insn. So invalidate it here.
+ Also do the same thing for RELOAD_OTHER constraints where the
+ output is discarded. */
+ if (i < 0
+ && ((rld[r].out != 0
+ && (REG_P (rld[r].out)
+ || (MEM_P (rld[r].out)
+ && REG_P (rld[r].out_reg))))
+ || (rld[r].out == 0 && rld[r].out_reg
&& REG_P (rld[r].out_reg))))
{
- rtx out = (REG_P (rld[r].out)
+ rtx out = ((rld[r].out && REG_P (rld[r].out))
? rld[r].out : rld[r].out_reg);
int nregno = REGNO (out);
if (nregno >= FIRST_PSEUDO_REGISTER)
}
else
{
- int num_regs = hard_regno_nregs[nregno][GET_MODE (rld[r].out)];
+ int num_regs = hard_regno_nregs[nregno][GET_MODE (out)];
while (num_regs-- > 0)
reg_last_reload_reg[nregno + num_regs] = 0;
IOR_HARD_REG_SET (reg_reloaded_dead, reg_reloaded_died);
}
\f
+/* 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)
+{
+ rtx last = get_last_insn ();
+ int code;
+
+ insn = emit_insn (insn);
+ code = recog_memoized (insn);
+
+ if (code >= 0)
+ {
+ extract_insn (insn);
+ /* 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))
+ return insn;
+ }
+
+ delete_insns_since (last);
+ return NULL;
+}
+
/* Emit code to perform a reload from IN (which may be a reload register) to
OUT (which may also be a reload register). IN or OUT is from operand
OPNUM with reload type TYPE.
trying to emit a single insn to perform the add. If it is not valid,
we use a two insn sequence.
+ Or we can be asked to reload an unary operand that was a fragment of
+ an addressing mode, into a register. If it isn't recognized as-is,
+ we try making the unop operand and the reload-register the same:
+ (set reg:X (unop:X expr:Y))
+ -> (set reg:Y expr:Y) (set reg:X (unop:X reg:Y)).
+
Finally, we could be called to handle an 'o' constraint by putting
an address into a register. In that case, we first try to do this
with a named pattern of "reload_load_address". If no such pattern
if (op0 != XEXP (in, 0) || op1 != XEXP (in, 1))
in = gen_rtx_PLUS (GET_MODE (in), op0, op1);
- insn = emit_insn (gen_rtx_SET (VOIDmode, out, in));
- code = recog_memoized (insn);
-
- if (code >= 0)
- {
- extract_insn (insn);
- /* 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))
- return insn;
- }
-
- delete_insns_since (last);
+ insn = emit_insn_if_valid_for_reload (gen_rtx_SET (VOIDmode, out, in));
+ if (insn)
+ return insn;
/* If that failed, we must use a conservative two-insn sequence.
if (rtx_equal_p (op0, op1))
op1 = out;
- insn = emit_insn (gen_add2_insn (out, op1));
-
- /* If that failed, copy the address register to the reload register.
- Then add the constant to the reload register. */
-
- code = recog_memoized (insn);
-
- if (code >= 0)
+ insn = emit_insn_if_valid_for_reload (gen_add2_insn (out, op1));
+ if (insn)
{
- extract_insn (insn);
- /* 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))
- {
- /* Add a REG_EQUIV note so that find_equiv_reg can find it. */
- REG_NOTES (insn)
- = gen_rtx_EXPR_LIST (REG_EQUIV, in, REG_NOTES (insn));
- return insn;
- }
+ /* Add a REG_EQUIV note so that find_equiv_reg can find it. */
+ REG_NOTES (insn)
+ = gen_rtx_EXPR_LIST (REG_EQUIV, in, REG_NOTES (insn));
+ return insn;
}
- delete_insns_since (last);
+ /* If that failed, copy the address register to the reload register.
+ Then add the constant to the reload register. */
gen_reload (out, op1, opnum, type);
insn = emit_insn (gen_add2_insn (out, op0));
gen_reload (out, loc, opnum, type);
}
#endif
+ else if (REG_P (out) && UNARY_P (in))
+ {
+ rtx insn;
+ rtx op1;
+ rtx out_moded;
+ rtx set;
+
+ op1 = find_replacement (&XEXP (in, 0));
+ if (op1 != XEXP (in, 0))
+ in = gen_rtx_fmt_e (GET_CODE (in), GET_MODE (in), op1);
+
+ /* First, try a plain SET. */
+ set = emit_insn_if_valid_for_reload (gen_rtx_SET (VOIDmode, out, in));
+ if (set)
+ return set;
+
+ /* If that failed, move the inner operand to the reload
+ register, and try the same unop with the inner expression
+ replaced with the reload register. */
+
+ if (GET_MODE (op1) != GET_MODE (out))
+ out_moded = gen_rtx_REG (GET_MODE (op1), REGNO (out));
+ else
+ out_moded = out;
+ gen_reload (out_moded, op1, opnum, type);
+
+ insn
+ = gen_rtx_SET (VOIDmode, out,
+ gen_rtx_fmt_e (GET_CODE (in), GET_MODE (in),
+ out_moded));
+ insn = emit_insn_if_valid_for_reload (insn);
+ if (insn)
+ {
+ REG_NOTES (insn)
+ = gen_rtx_EXPR_LIST (REG_EQUIV, in, REG_NOTES (insn));
+ return insn;
+ }
+
+ fatal_insn ("Failure trying to reload:", set);
+ }
/* If IN is a simple operand, use gen_move_insn. */
else if (OBJECT_P (in) || GET_CODE (in) == SUBREG)
emit_insn (gen_move_insn (out, in));
/* REG or MEM to be copied and incremented. */
rtx incloc = XEXP (value, 0);
/* Nonzero if increment after copying. */
- int post = (GET_CODE (value) == POST_DEC || GET_CODE (value) == POST_INC);
+ int post = (GET_CODE (value) == POST_DEC || GET_CODE (value) == POST_INC
+ || GET_CODE (value) == POST_MODIFY);
rtx last;
rtx inc;
rtx add_insn;
if (REG_P (incloc))
reg_last_reload_reg[REGNO (incloc)] = 0;
- if (GET_CODE (value) == PRE_DEC || GET_CODE (value) == POST_DEC)
- inc_amount = -inc_amount;
+ if (GET_CODE (value) == PRE_MODIFY || GET_CODE (value) == POST_MODIFY)
+ {
+ gcc_assert (GET_CODE (XEXP (value, 1)) == PLUS);
+ inc = XEXP (XEXP (value, 1), 1);
+ }
+ else
+ {
+ if (GET_CODE (value) == PRE_DEC || GET_CODE (value) == POST_DEC)
+ inc_amount = -inc_amount;
- inc = GEN_INT (inc_amount);
+ inc = GEN_INT (inc_amount);
+ }
/* If this is post-increment, first copy the location to the reload reg. */
if (post && real_in != reloadreg)
emit_insn (gen_add2_insn (reloadreg, inc));
store = emit_insn (gen_move_insn (incloc, reloadreg));
- emit_insn (gen_add2_insn (reloadreg, GEN_INT (-inc_amount)));
+ if (GET_CODE (inc) == CONST_INT)
+ emit_insn (gen_add2_insn (reloadreg, GEN_INT (-INTVAL(inc))));
+ else
+ emit_insn (gen_sub2_insn (reloadreg, inc));
}
return store;
if (e && !CALL_P (BB_END (bb))
&& !can_throw_internal (BB_END (bb)))
{
- rtx insn = BB_END (bb), stop = NEXT_INSN (BB_END (bb));
- rtx next;
- FOR_EACH_EDGE (e, ei, bb->succs)
- if (e->flags & EDGE_FALLTHRU)
- break;
- /* Get past the new insns generated. Allow notes, as the insns may
- be already deleted. */
+ rtx insn;
+
+ /* Get past the new insns generated. Allow notes, as the insns
+ may be already deleted. */
+ insn = BB_END (bb);
while ((NONJUMP_INSN_P (insn) || NOTE_P (insn))
&& !can_throw_internal (insn)
&& insn != BB_HEAD (bb))
insn = PREV_INSN (insn);
- gcc_assert (CALL_P (insn) || can_throw_internal (insn));
- BB_END (bb) = insn;
- inserted = true;
- insn = NEXT_INSN (insn);
- while (insn && insn != stop)
+
+ if (CALL_P (insn) || can_throw_internal (insn))
{
- next = NEXT_INSN (insn);
- if (INSN_P (insn))
+ rtx stop, next;
+
+ stop = NEXT_INSN (BB_END (bb));
+ BB_END (bb) = insn;
+ insn = NEXT_INSN (insn);
+
+ FOR_EACH_EDGE (e, ei, bb->succs)
+ if (e->flags & EDGE_FALLTHRU)
+ break;
+
+ while (insn && insn != stop)
{
- delete_insn (insn);
-
- /* Sometimes there's still the return value USE.
- If it's placed after a trapping call (i.e. that
- call is the last insn anyway), we have no fallthru
- edge. Simply delete this use and don't try to insert
- on the non-existent edge. */
- if (GET_CODE (PATTERN (insn)) != USE)
+ next = NEXT_INSN (insn);
+ if (INSN_P (insn))
{
- /* We're not deleting it, we're moving it. */
- INSN_DELETED_P (insn) = 0;
- PREV_INSN (insn) = NULL_RTX;
- NEXT_INSN (insn) = NULL_RTX;
+ delete_insn (insn);
+
+ /* Sometimes there's still the return value USE.
+ If it's placed after a trapping call (i.e. that
+ call is the last insn anyway), we have no fallthru
+ edge. Simply delete this use and don't try to insert
+ on the non-existent edge. */
+ if (GET_CODE (PATTERN (insn)) != USE)
+ {
+ /* We're not deleting it, we're moving it. */
+ INSN_DELETED_P (insn) = 0;
+ PREV_INSN (insn) = NULL_RTX;
+ NEXT_INSN (insn) = NULL_RTX;
- insert_insn_on_edge (insn, e);
+ insert_insn_on_edge (insn, e);
+ inserted = true;
+ }
}
+ insn = next;
}
- insn = next;
}
+
+ /* It may be that we don't find any such trapping insn. In this
+ case we discovered quite late that the insn that had been
+ marked as can_throw_internal in fact couldn't trap at all.
+ So we should in fact delete the EH edges out of the block. */
+ else
+ purge_dead_edges (bb);
}
}
+
/* We've possibly turned single trapping insn into multiple ones. */
if (flag_non_call_exceptions)
{
sbitmap_ones (blocks);
find_many_sub_basic_blocks (blocks);
}
+
if (inserted)
commit_edge_insertions ();
+
+#ifdef ENABLE_CHECKING
+ /* Verify that we didn't turn one trapping insn into many, and that
+ we found and corrected all of the problems wrt fixups on the
+ fallthru edge. */
+ verify_flow_info ();
+#endif
}