#include "toplev.h"
#include "except.h"
#include "tree.h"
+#include "target.h"
#include "timevar.h"
#include "tree-pass.h"
#include "df.h"
if (! TEST_HARD_REG_BIT (equiv_regs[i], regno))
continue;
- SET_REGNO (testreg, regno);
+ SET_REGNO_RAW (testreg, regno);
PUT_MODE (testreg, mode);
/* We found a register equal to this operand. Now look for all
alternative yet and the operand being replaced is not
a cheap CONST_INT. */
if (op_alt_regno[i][j] == -1
+ && recog_data.alternative_enabled_p[j]
&& reg_fits_class_p (testreg, rclass, 0, mode)
&& (!CONST_INT_P (recog_data.operand[i])
|| (rtx_cost (recog_data.operand[i], SET,
/* Find the use of REGNO with the ruid that is highest among those
lower than RUID_LIMIT, and return it if it is the only use of this
- reg in the insn (or if the insn is a debug insn). Return NULL
- otherwise. */
+ reg in the insn. Return NULL otherwise. */
static struct reg_use *
reload_combine_closest_single_use (unsigned regno, int ruid_limit)
if (this_ruid > best_ruid)
{
best_ruid = this_ruid;
- retval = reg_state[regno].reg_use + i;
+ retval = use;
}
- else if (this_ruid == best_ruid && !DEBUG_INSN_P (use->insn))
+ else if (this_ruid == best_ruid)
retval = NULL;
}
if (last_label_ruid >= best_ruid)
return retval;
}
-/* After we've moved an add insn, fix up any debug insns that occur between
- the old location of the add and the new location. REGNO is the destination
- register of the add insn; REG is the corresponding RTX. REPLACEMENT is
- the SET_SRC of the add. MIN_RUID specifies the ruid of the insn after
- which we've placed the add, we ignore any debug insns after it. */
+/* After we've moved an add insn, fix up any debug insns that occur
+ between the old location of the add and the new location. REG is
+ the destination register of the add insn; REPLACEMENT is the
+ SET_SRC of the add. FROM and TO specify the range in which we
+ should make this change on debug insns. */
static void
-fixup_debug_insns (unsigned regno, rtx reg, rtx replacement, int min_ruid)
+fixup_debug_insns (rtx reg, rtx replacement, rtx from, rtx to)
{
- struct reg_use *use;
- int from = reload_combine_ruid;
- for (;;)
+ rtx insn;
+ for (insn = from; insn != to; insn = NEXT_INSN (insn))
{
rtx t;
- rtx use_insn = NULL_RTX;
- if (from < min_ruid)
- break;
- use = reload_combine_closest_single_use (regno, from);
- if (use)
+
+ if (!DEBUG_INSN_P (insn))
+ continue;
+
+ t = INSN_VAR_LOCATION_LOC (insn);
+ t = simplify_replace_rtx (t, reg, replacement);
+ validate_change (insn, &INSN_VAR_LOCATION_LOC (insn), t, 0);
+ }
+}
+
+/* Subroutine of reload_combine_recognize_const_pattern. Try to replace REG
+ with SRC in the insn described by USE, taking costs into account. Return
+ true if we made the replacement. */
+
+static bool
+try_replace_in_use (struct reg_use *use, rtx reg, rtx src)
+{
+ rtx use_insn = use->insn;
+ rtx mem = use->containing_mem;
+ bool speed = optimize_bb_for_speed_p (BLOCK_FOR_INSN (use_insn));
+
+ if (mem != NULL_RTX)
+ {
+ addr_space_t as = MEM_ADDR_SPACE (mem);
+ rtx oldaddr = XEXP (mem, 0);
+ rtx newaddr = NULL_RTX;
+ int old_cost = address_cost (oldaddr, GET_MODE (mem), as, speed);
+ int new_cost;
+
+ newaddr = simplify_replace_rtx (oldaddr, reg, src);
+ if (memory_address_addr_space_p (GET_MODE (mem), newaddr, as))
{
- from = use->ruid;
- use_insn = use->insn;
+ XEXP (mem, 0) = newaddr;
+ new_cost = address_cost (newaddr, GET_MODE (mem), as, speed);
+ XEXP (mem, 0) = oldaddr;
+ if (new_cost <= old_cost
+ && validate_change (use_insn,
+ &XEXP (mem, 0), newaddr, 0))
+ return true;
+ }
+ }
+ else
+ {
+ rtx new_set = single_set (use_insn);
+ if (new_set
+ && REG_P (SET_DEST (new_set))
+ && GET_CODE (SET_SRC (new_set)) == PLUS
+ && REG_P (XEXP (SET_SRC (new_set), 0))
+ && CONSTANT_P (XEXP (SET_SRC (new_set), 1)))
+ {
+ rtx new_src;
+ int old_cost = rtx_cost (SET_SRC (new_set), SET, speed);
+
+ gcc_assert (rtx_equal_p (XEXP (SET_SRC (new_set), 0), reg));
+ new_src = simplify_replace_rtx (SET_SRC (new_set), reg, src);
+
+ if (rtx_cost (new_src, SET, speed) <= old_cost
+ && validate_change (use_insn, &SET_SRC (new_set),
+ new_src, 0))
+ return true;
}
- else
- break;
-
- if (NONDEBUG_INSN_P (use->insn))
- continue;
- t = INSN_VAR_LOCATION_LOC (use_insn);
- t = simplify_replace_rtx (t, reg, copy_rtx (replacement));
- validate_change (use->insn,
- &INSN_VAR_LOCATION_LOC (use->insn), t, 0);
- reload_combine_purge_insn_uses (use_insn);
- reload_combine_note_use (&PATTERN (use_insn), use_insn,
- use->ruid, NULL_RTX);
}
+ return false;
}
/* Called by reload_combine when scanning INSN. This function tries to detect
/* Start the search for the next use from here. */
from_ruid = use->ruid;
- /* We'll fix up DEBUG_INSNs after we're done. */
- if (use && DEBUG_INSN_P (use->insn))
- continue;
-
if (use && GET_MODE (*use->usep) == Pmode)
{
+ bool delete_add = false;
rtx use_insn = use->insn;
int use_ruid = use->ruid;
- rtx mem = use->containing_mem;
- bool speed = optimize_bb_for_speed_p (BLOCK_FOR_INSN (use_insn));
/* Avoid moving the add insn past a jump. */
if (must_move_add && use_ruid <= last_jump_ruid)
&& reg_state[clobbered_regno].real_store_ruid >= use_ruid)
break;
- /* Avoid moving a use of ADDREG past a point where it
- is stored. */
- if (reg_state[REGNO (addreg)].store_ruid >= use_ruid)
+ gcc_assert (reg_state[regno].store_ruid <= use_ruid);
+ /* Avoid moving a use of ADDREG past a point where it is stored. */
+ if (reg_state[REGNO (addreg)].store_ruid > use_ruid)
break;
- if (mem != NULL_RTX)
+ /* We also must not move the addition past an insn that sets
+ the same register, unless we can combine two add insns. */
+ if (must_move_add && reg_state[regno].store_ruid == use_ruid)
{
- addr_space_t as = MEM_ADDR_SPACE (mem);
- rtx oldaddr = XEXP (mem, 0);
- rtx newaddr = NULL_RTX;
- int old_cost = address_cost (oldaddr, GET_MODE (mem), as, speed);
- int new_cost;
-
- newaddr = simplify_replace_rtx (oldaddr, reg, copy_rtx (src));
- if (memory_address_addr_space_p (GET_MODE (mem), newaddr, as))
- {
- XEXP (mem, 0) = newaddr;
- new_cost = address_cost (newaddr, GET_MODE (mem), as, speed);
- XEXP (mem, 0) = oldaddr;
- if (new_cost <= old_cost
- && validate_change (use_insn,
- &XEXP (mem, 0), newaddr, 0))
- {
- reload_combine_purge_insn_uses (use_insn);
- reload_combine_note_use (&PATTERN (use_insn), use_insn,
- use_ruid, NULL_RTX);
-
- if (must_move_add)
- {
- add_moved_after_insn = use_insn;
- add_moved_after_ruid = use_ruid;
- }
- continue;
- }
- }
+ if (use->containing_mem == NULL_RTX)
+ delete_add = true;
+ else
+ break;
}
- else
- {
- rtx new_set = single_set (use_insn);
- if (new_set
- && REG_P (SET_DEST (new_set))
- && GET_CODE (SET_SRC (new_set)) == PLUS
- && REG_P (XEXP (SET_SRC (new_set), 0))
- && CONSTANT_P (XEXP (SET_SRC (new_set), 1)))
- {
- rtx new_src;
- int old_cost = rtx_cost (SET_SRC (new_set), SET, speed);
- gcc_assert (rtx_equal_p (XEXP (SET_SRC (new_set), 0), reg));
- new_src = simplify_replace_rtx (SET_SRC (new_set), reg,
- copy_rtx (src));
-
- if (rtx_cost (new_src, SET, speed) <= old_cost
- && validate_change (use_insn, &SET_SRC (new_set),
- new_src, 0))
- {
- reload_combine_purge_insn_uses (use_insn);
- reload_combine_note_use (&SET_SRC (new_set), use_insn,
- use_ruid, NULL_RTX);
+ if (try_replace_in_use (use, reg, src))
+ {
+ reload_combine_purge_insn_uses (use_insn);
+ reload_combine_note_use (&PATTERN (use_insn), use_insn,
+ use_ruid, NULL_RTX);
- if (must_move_add)
- {
- /* See if that took care of the add insn. */
- if (rtx_equal_p (SET_DEST (new_set), reg))
- {
- delete_insn (insn);
- return true;
- }
- else
- {
- add_moved_after_insn = use_insn;
- add_moved_after_ruid = use_ruid;
- }
- }
- continue;
- }
+ if (delete_add)
+ {
+ fixup_debug_insns (reg, src, insn, use_insn);
+ delete_insn (insn);
+ return true;
+ }
+ if (must_move_add)
+ {
+ add_moved_after_insn = use_insn;
+ add_moved_after_ruid = use_ruid;
}
+ continue;
}
- /* If we get here, we couldn't handle this use. */
- if (must_move_add)
- break;
}
+ /* If we get here, we couldn't handle this use. */
+ if (must_move_add)
+ break;
}
while (use);
/* Process the add normally. */
return false;
- fixup_debug_insns (regno, reg, src, add_moved_after_ruid);
-
+ fixup_debug_insns (reg, src, insn, add_moved_after_insn);
+
reorder_insns (insn, insn, add_moved_after_insn);
reload_combine_purge_reg_uses_after_ruid (regno, add_moved_after_ruid);
reload_combine_split_ruids (add_moved_after_ruid - 1);
&& REG_P (XEXP (src, 1))
&& rtx_equal_p (XEXP (src, 0), reg)
&& !rtx_equal_p (XEXP (src, 1), reg)
+ && reg_state[regno].use_index >= 0
+ && reg_state[regno].use_index < RELOAD_COMBINE_MAX_USES
&& last_label_ruid < reg_state[regno].use_ruid)
{
rtx base = XEXP (src, 1);
- rtx prev = prev_nonnote_insn (insn);
+ rtx prev = prev_nonnote_nondebug_insn (insn);
rtx prev_set = prev ? single_set (prev) : NULL_RTX;
rtx index_reg = NULL_RTX;
rtx reg_sum = NULL_RTX;
if (TEST_HARD_REG_BIT (reg_class_contents[INDEX_REG_CLASS], i)
&& reg_state[i].use_index == RELOAD_COMBINE_MAX_USES
&& reg_state[i].store_ruid <= reg_state[regno].use_ruid
- && hard_regno_nregs[i][GET_MODE (reg)] == 1)
+ && (call_used_regs[i] || df_regs_ever_live_p (i))
+ && (!frame_pointer_needed || i != HARD_FRAME_POINTER_REGNUM)
+ && !fixed_regs[i] && !global_regs[i]
+ && hard_regno_nregs[i][GET_MODE (reg)] == 1
+ && targetm.hard_regno_scratch_ok (i))
{
index_reg = gen_rtx_REG (GET_MODE (reg), i);
reg_sum = gen_rtx_PLUS (GET_MODE (reg), index_reg, base);
&& prev_set
&& CONST_INT_P (SET_SRC (prev_set))
&& rtx_equal_p (SET_DEST (prev_set), reg)
- && reg_state[regno].use_index >= 0
&& (reg_state[REGNO (base)].store_ruid
<= reg_state[regno].use_ruid))
{
if (apply_change_group ())
{
+ struct reg_use *lowest_ruid = NULL;
+
/* For every new use of REG_SUM, we have to record the use
of BASE therein, i.e. operand 1. */
for (i = reg_state[regno].use_index;
i < RELOAD_COMBINE_MAX_USES; i++)
- reload_combine_note_use
- (&XEXP (*reg_state[regno].reg_use[i].usep, 1),
- reg_state[regno].reg_use[i].insn,
- reg_state[regno].reg_use[i].ruid,
- reg_state[regno].reg_use[i].containing_mem);
+ {
+ struct reg_use *use = reg_state[regno].reg_use + i;
+ reload_combine_note_use (&XEXP (*use->usep, 1), use->insn,
+ use->ruid, use->containing_mem);
+ if (lowest_ruid == NULL || use->ruid < lowest_ruid->ruid)
+ lowest_ruid = use;
+ }
+
+ fixup_debug_insns (reg, reg_sum, insn, lowest_ruid->insn);
/* Delete the reg-reg addition. */
delete_insn (insn);
remove_reg_equal_equiv_notes (prev);
reg_state[regno].use_index = RELOAD_COMBINE_MAX_USES;
- reg_state[REGNO (index_reg)].store_ruid
- = reload_combine_ruid;
return true;
}
}
}
}
-#if 0
- /* If reg+reg can be used in offsetable memory addresses, the main chunk of
- reload has already used it where appropriate, so there is no use in
- trying to generate it now. */
- if (double_reg_address_ok || last_index_reg == -1)
- return;
-#endif
-
/* Set up LABEL_LIVE and EVER_LIVE_AT_START. The register lifetime
information is a bit fuzzy immediately after reload, but it's
still good enough to determine which registers are live at a jump
if (! fixed_regs[r])
reg_state[r].use_index = RELOAD_COMBINE_MAX_USES;
- if (! INSN_P (insn))
+ if (! NONDEBUG_INSN_P (insn))
continue;
reload_combine_ruid++;
return;
}
+ /* We may be called to update uses in previously seen insns.
+ Don't add uses beyond the last store we saw. */
+ if (ruid < reg_state[regno].store_ruid)
+ return;
+
/* If this register is already used in some unknown fashion, we
can't do anything.
If we decrement the index from zero to -1, we can't store more
if (INTVAL (off) == reg_offset [regno])
changed = validate_change (insn, &SET_SRC (pat), reg, 0);
}
- else if (rtx_cost (new_src, PLUS, speed) < rtx_cost (src, SET, speed)
- && have_add2_insn (reg, new_src))
+ else
{
+ struct full_rtx_costs oldcst, newcst;
rtx tem = gen_rtx_PLUS (GET_MODE (reg), reg, new_src);
- changed = validate_change (insn, &SET_SRC (pat), tem, 0);
- }
- else if (sym == NULL_RTX && GET_MODE (reg) != BImode)
- {
- enum machine_mode narrow_mode;
- for (narrow_mode = GET_CLASS_NARROWEST_MODE (MODE_INT);
- narrow_mode != VOIDmode
- && narrow_mode != GET_MODE (reg);
- narrow_mode = GET_MODE_WIDER_MODE (narrow_mode))
+
+ get_full_rtx_cost (pat, SET, &oldcst);
+ SET_SRC (pat) = tem;
+ get_full_rtx_cost (pat, SET, &newcst);
+ SET_SRC (pat) = src;
+
+ if (costs_lt_p (&newcst, &oldcst, speed)
+ && have_add2_insn (reg, new_src))
+ changed = validate_change (insn, &SET_SRC (pat), tem, 0);
+ else if (sym == NULL_RTX && GET_MODE (reg) != BImode)
{
- if (have_insn_for (STRICT_LOW_PART, narrow_mode)
- && ((reg_offset[regno]
- & ~GET_MODE_MASK (narrow_mode))
- == (INTVAL (off)
- & ~GET_MODE_MASK (narrow_mode))))
+ enum machine_mode narrow_mode;
+ for (narrow_mode = GET_CLASS_NARROWEST_MODE (MODE_INT);
+ narrow_mode != VOIDmode
+ && narrow_mode != GET_MODE (reg);
+ narrow_mode = GET_MODE_WIDER_MODE (narrow_mode))
{
- rtx narrow_reg = gen_rtx_REG (narrow_mode,
- REGNO (reg));
- rtx narrow_src = gen_int_mode (INTVAL (off),
- narrow_mode);
- rtx new_set =
- gen_rtx_SET (VOIDmode,
- gen_rtx_STRICT_LOW_PART (VOIDmode,
- narrow_reg),
- narrow_src);
- changed = validate_change (insn, &PATTERN (insn),
- new_set, 0);
- if (changed)
- break;
+ if (have_insn_for (STRICT_LOW_PART, narrow_mode)
+ && ((reg_offset[regno] & ~GET_MODE_MASK (narrow_mode))
+ == (INTVAL (off) & ~GET_MODE_MASK (narrow_mode))))
+ {
+ rtx narrow_reg = gen_rtx_REG (narrow_mode,
+ REGNO (reg));
+ rtx narrow_src = gen_int_mode (INTVAL (off),
+ narrow_mode);
+ rtx new_set
+ = gen_rtx_SET (VOIDmode,
+ gen_rtx_STRICT_LOW_PART (VOIDmode,
+ narrow_reg),
+ narrow_src);
+ changed = validate_change (insn, &PATTERN (insn),
+ new_set, 0);
+ if (changed)
+ break;
+ }
}
}
}
rtx pat = PATTERN (insn);
rtx src = SET_SRC (pat);
int regno = REGNO (reg);
- int min_cost = INT_MAX;
int min_regno = 0;
bool speed = optimize_bb_for_speed_p (BLOCK_FOR_INSN (insn));
int i;
bool changed = false;
+ struct full_rtx_costs oldcst, newcst, mincst;
+ rtx plus_expr;
+
+ init_costs_to_max (&mincst);
+ get_full_rtx_cost (pat, SET, &oldcst);
+
+ plus_expr = gen_rtx_PLUS (GET_MODE (reg), reg, const0_rtx);
+ SET_SRC (pat) = plus_expr;
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
if (reg_set_luid[i] > move2add_last_label_luid
no-op moves. */
if (new_src == const0_rtx)
{
- min_cost = 0;
+ init_costs_to_zero (&mincst);
min_regno = i;
break;
}
else
{
- int cost = rtx_cost (new_src, PLUS, speed);
- if (cost < min_cost)
+ XEXP (plus_expr, 1) = new_src;
+ get_full_rtx_cost (pat, SET, &newcst);
+
+ if (costs_lt_p (&newcst, &mincst, speed))
{
- min_cost = cost;
+ mincst = newcst;
min_regno = i;
}
}
}
+ SET_SRC (pat) = src;
- if (min_cost < rtx_cost (src, SET, speed))
+ if (costs_lt_p (&mincst, &oldcst, speed))
{
rtx tem;
&& MODES_OK_FOR_MOVE2ADD (GET_MODE (reg),
reg_mode[REGNO (src)]))
{
- rtx next = next_nonnote_insn (insn);
+ rtx next = next_nonnote_nondebug_insn (insn);
rtx set = NULL_RTX;
if (next)
set = single_set (next);
/* See above why we create (set (reg) (reg)) here. */
success
= validate_change (next, &SET_SRC (set), reg, 0);
- else if ((rtx_cost (new_src, PLUS, speed)
- < COSTS_N_INSNS (1) + rtx_cost (src3, SET, speed))
- && have_add2_insn (reg, new_src))
+ else
{
- rtx newpat = gen_rtx_SET (VOIDmode,
- reg,
- gen_rtx_PLUS (GET_MODE (reg),
- reg,
- new_src));
- success
- = validate_change (next, &PATTERN (next),
- newpat, 0);
+ rtx old_src = SET_SRC (set);
+ struct full_rtx_costs oldcst, newcst;
+ rtx tem = gen_rtx_PLUS (GET_MODE (reg), reg, new_src);
+
+ get_full_rtx_cost (set, SET, &oldcst);
+ SET_SRC (set) = tem;
+ get_full_rtx_cost (tem, SET, &newcst);
+ SET_SRC (set) = old_src;
+ costs_add_n_insns (&oldcst, 1);
+
+ if (costs_lt_p (&newcst, &oldcst, speed)
+ && have_add2_insn (reg, new_src))
+ {
+ rtx newpat = gen_rtx_SET (VOIDmode, reg, tem);
+ success
+ = validate_change (next, &PATTERN (next),
+ newpat, 0);
+ }
}
if (success)
delete_insn (insn);
&& (MODES_OK_FOR_MOVE2ADD
(dst_mode, reg_mode[REGNO (XEXP (src, 1))])))
{
- if (reg_base_reg[REGNO (XEXP (src, 1))] < 0)
+ if (reg_base_reg[REGNO (XEXP (src, 1))] < 0
+ && reg_symbol_ref[REGNO (XEXP (src, 1))] == NULL_RTX)
offset = reg_offset[REGNO (XEXP (src, 1))];
/* Maybe the first register is known to be a
constant. */
else if (reg_set_luid[REGNO (base_reg)]
> move2add_last_label_luid
&& (MODES_OK_FOR_MOVE2ADD
- (dst_mode, reg_mode[REGNO (XEXP (src, 1))]))
- && reg_base_reg[REGNO (base_reg)] < 0)
+ (dst_mode, reg_mode[REGNO (base_reg)]))
+ && reg_base_reg[REGNO (base_reg)] < 0
+ && reg_symbol_ref[REGNO (base_reg)] == NULL_RTX)
{
offset = reg_offset[REGNO (base_reg)];
base_reg = XEXP (src, 1);