/* Perform simple optimizations to clean up the result of reload.
- Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
- 1999, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
+ Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997,
+ 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
+ 2010 Free Software Foundation, Inc.
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
-Software Foundation; either version 2, or (at your option) any later
+Software Foundation; either version 3, or (at your option) any later
version.
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
for more details.
You should have received a copy of the GNU General Public License
-along with GCC; see the file COPYING. If not, write to the Free
-Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
-02110-1301, USA. */
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
#include "config.h"
#include "system.h"
#include "recog.h"
#include "output.h"
#include "cselib.h"
-#include "real.h"
+#include "diagnostic-core.h"
#include "toplev.h"
#include "except.h"
#include "tree.h"
#include "timevar.h"
#include "tree-pass.h"
+#include "df.h"
+#include "dbgcnt.h"
static int reload_cse_noop_set_p (rtx);
static void reload_cse_simplify (rtx, rtx);
static void reload_combine (void);
static void reload_combine_note_use (rtx *, rtx);
-static void reload_combine_note_store (rtx, rtx, void *);
+static void reload_combine_note_store (rtx, const_rtx, void *);
static void reload_cse_move2add (rtx);
-static void move2add_note_store (rtx, rtx, void *);
+static void move2add_note_store (rtx, const_rtx, void *);
/* Call cse / combine like post-reload optimization phases.
FIRST is the first instruction. */
rtx insn;
rtx testreg = gen_rtx_REG (VOIDmode, -1);
- cselib_init (true);
+ cselib_init (CSELIB_RECORD_MEMORY);
init_alias_analysis ();
for (insn = first; insn; insn = NEXT_INSN (insn))
#ifdef LOAD_EXTEND_OP
enum rtx_code extend_op = UNKNOWN;
#endif
+ bool speed = optimize_bb_for_speed_p (BLOCK_FOR_INSN (insn));
dreg = true_regnum (SET_DEST (set));
if (dreg < 0)
/* If memory loads are cheaper than register copies, don't change them. */
if (MEM_P (src))
- old_cost = MEMORY_MOVE_COST (GET_MODE (src), dclass, 1);
+ old_cost = memory_move_cost (GET_MODE (src), dclass, true);
else if (REG_P (src))
- old_cost = REGISTER_MOVE_COST (GET_MODE (src),
+ old_cost = register_move_cost (GET_MODE (src),
REGNO_REG_CLASS (REGNO (src)), dclass);
else
- old_cost = rtx_cost (src, SET);
+ old_cost = rtx_cost (src, SET, speed);
for (l = val->locs; l; l = l->next)
{
/* ??? I'm lazy and don't wish to handle CONST_DOUBLE. Other
constants, such as SYMBOL_REF, cannot be extended. */
- if (GET_CODE (this_rtx) != CONST_INT)
+ if (!CONST_INT_P (this_rtx))
continue;
this_val = INTVAL (this_rtx);
this_rtx = GEN_INT (this_val);
}
#endif
- this_cost = rtx_cost (this_rtx, SET);
+ this_cost = rtx_cost (this_rtx, SET, speed);
}
else if (REG_P (this_rtx))
{
if (extend_op != UNKNOWN)
{
this_rtx = gen_rtx_fmt_e (extend_op, word_mode, this_rtx);
- this_cost = rtx_cost (this_rtx, SET);
+ this_cost = rtx_cost (this_rtx, SET, speed);
}
else
#endif
- this_cost = REGISTER_MOVE_COST (GET_MODE (this_rtx),
+ this_cost = register_move_cost (GET_MODE (this_rtx),
REGNO_REG_CLASS (REGNO (this_rtx)),
dclass);
}
}
#endif
- validate_change (insn, &SET_SRC (set), copy_rtx (this_rtx), 1);
+ validate_unshare_change (insn, &SET_SRC (set), this_rtx, 1);
old_cost = this_cost, did_change = 1;
}
}
if (! constrain_operands (1))
fatal_insn_not_found (insn);
- alternative_reject = alloca (recog_data.n_alternatives * sizeof (int));
- alternative_nregs = alloca (recog_data.n_alternatives * sizeof (int));
- alternative_order = alloca (recog_data.n_alternatives * sizeof (int));
+ alternative_reject = XALLOCAVEC (int, recog_data.n_alternatives);
+ alternative_nregs = XALLOCAVEC (int, recog_data.n_alternatives);
+ alternative_order = XALLOCAVEC (int, recog_data.n_alternatives);
memset (alternative_reject, 0, recog_data.n_alternatives * sizeof (int));
memset (alternative_nregs, 0, recog_data.n_alternatives * sizeof (int));
cselib_val *v;
struct elt_loc_list *l;
rtx op;
- enum machine_mode mode;
CLEAR_HARD_REG_SET (equiv_regs[i]);
continue;
op = recog_data.operand[i];
- mode = GET_MODE (op);
#ifdef LOAD_EXTEND_OP
if (MEM_P (op)
- && GET_MODE_BITSIZE (mode) < BITS_PER_WORD
- && LOAD_EXTEND_OP (mode) != UNKNOWN)
+ && GET_MODE_BITSIZE (GET_MODE (op)) < BITS_PER_WORD
+ && LOAD_EXTEND_OP (GET_MODE (op)) != UNKNOWN)
{
rtx set = single_set (insn);
&& SET_DEST (set) == recog_data.operand[1-i])
{
validate_change (insn, recog_data.operand_loc[i],
- gen_rtx_fmt_e (LOAD_EXTEND_OP (mode),
+ gen_rtx_fmt_e (LOAD_EXTEND_OP (GET_MODE (op)),
word_mode, op),
1);
validate_change (insn, recog_data.operand_loc[1-i],
int regno;
const char *p;
- op_alt_regno[i] = alloca (recog_data.n_alternatives * sizeof (int));
+ op_alt_regno[i] = XALLOCAVEC (int, recog_data.n_alternatives);
for (j = 0; j < recog_data.n_alternatives; j++)
op_alt_regno[i][j] = -1;
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
{
- int class = (int) NO_REGS;
+ enum reg_class rclass = NO_REGS;
if (! TEST_HARD_REG_BIT (equiv_regs[i], regno))
continue;
- REGNO (testreg) = regno;
+ SET_REGNO (testreg, regno);
PUT_MODE (testreg, mode);
/* We found a register equal to this operand. Now look for all
case '*': case '%':
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
- case 'm': case '<': case '>': case 'V': case 'o':
+ case '<': case '>': case 'V': case 'o':
case 'E': case 'F': case 'G': case 'H':
case 's': case 'i': case 'n':
case 'I': case 'J': case 'K': case 'L':
case 'M': case 'N': case 'O': case 'P':
- case 'p': case 'X':
+ case 'p': case 'X': case TARGET_MEM_CONSTRAINT:
/* These don't say anything we care about. */
break;
case 'g': case 'r':
- class = reg_class_subunion[(int) class][(int) GENERAL_REGS];
+ rclass = reg_class_subunion[(int) rclass][(int) GENERAL_REGS];
break;
default:
- class
+ rclass
= (reg_class_subunion
- [(int) class]
+ [(int) rclass]
[(int) REG_CLASS_FROM_CONSTRAINT ((unsigned char) c, p)]);
break;
alternative yet and the operand being replaced is not
a cheap CONST_INT. */
if (op_alt_regno[i][j] == -1
- && reg_fits_class_p (testreg, class, 0, mode)
- && (GET_CODE (recog_data.operand[i]) != CONST_INT
- || (rtx_cost (recog_data.operand[i], SET)
- > rtx_cost (testreg, SET))))
+ && reg_fits_class_p (testreg, rclass, 0, mode)
+ && (!CONST_INT_P (recog_data.operand[i])
+ || (rtx_cost (recog_data.operand[i], SET,
+ optimize_bb_for_speed_p (BLOCK_FOR_INSN (insn)))
+ > rtx_cost (testreg, SET,
+ optimize_bb_for_speed_p (BLOCK_FOR_INSN (insn))))))
{
alternative_nregs[j]++;
op_alt_regno[i][j] = regno;
}
j++;
+ rclass = NO_REGS;
break;
}
p += CONSTRAINT_LEN (c, p);
int this_nregs = alternative_nregs[alternative_order[j]];
if (this_reject < best_reject
- || (this_reject == best_reject && this_nregs < best_nregs))
+ || (this_reject == best_reject && this_nregs > best_nregs))
{
best = j;
best_reject = this_reject;
replace them with reg+reg addressing. */
#define RELOAD_COMBINE_MAX_USES 6
-/* INSN is the insn where a register has ben used, and USEP points to the
+/* INSN is the insn where a register has been used, and USEP points to the
location of the register within the rtl. */
struct reg_use { rtx insn, *usep; };
destination. */
min_labelno = get_first_label_num ();
n_labels = max_label_num () - min_labelno;
- label_live = xmalloc (n_labels * sizeof (HARD_REG_SET));
+ label_live = XNEWVEC (HARD_REG_SET, n_labels);
CLEAR_HARD_REG_SET (ever_live_at_start);
FOR_EACH_BB_REVERSE (bb)
if (LABEL_P (insn))
{
HARD_REG_SET live;
+ bitmap live_in = df_get_live_in (bb);
- REG_SET_TO_HARD_REG_SET (live,
- bb->il.rtl->global_live_at_start);
- compute_use_by_pseudos (&live,
- bb->il.rtl->global_live_at_start);
+ REG_SET_TO_HARD_REG_SET (live, live_in);
+ compute_use_by_pseudos (&live, live_in);
COPY_HARD_REG_SET (LABEL_LIVE (insn), live);
IOR_HARD_REG_SET (ever_live_at_start, live);
}
... (MEM (PLUS (REGZ) (REGY)))... .
First, check that we have (set (REGX) (PLUS (REGX) (REGY)))
- and that we know all uses of REGX before it dies.
+ and that we know all uses of REGX before it dies.
Also, explicitly check that REGX != REGY; our life information
does not yet show whether REGY changes in this insn. */
set = single_set (insn);
rtx prev = prev_nonnote_insn (insn);
rtx prev_set = prev ? single_set (prev) : NULL_RTX;
unsigned int regno = REGNO (reg);
- rtx const_reg = NULL_RTX;
+ rtx index_reg = NULL_RTX;
rtx reg_sum = NULL_RTX;
- /* Now, we need an index register.
- We'll set index_reg to this index register, const_reg to the
- register that is to be loaded with the constant
- (denoted as REGZ in the substitution illustration above),
- and reg_sum to the register-register that we want to use to
- substitute uses of REG (typically in MEMs) with.
- First check REG and BASE for being index registers;
- we can use them even if they are not dead. */
+ /* Now we need to set INDEX_REG to an index register (denoted as
+ REGZ in the illustration above) and REG_SUM to the expression
+ register+register that we want to use to substitute uses of REG
+ (typically in MEMs) with. First check REG and BASE for being
+ index registers; we can use them even if they are not dead. */
if (TEST_HARD_REG_BIT (reg_class_contents[INDEX_REG_CLASS], regno)
|| TEST_HARD_REG_BIT (reg_class_contents[INDEX_REG_CLASS],
REGNO (base)))
{
- const_reg = reg;
+ index_reg = reg;
reg_sum = plus;
}
else
&& reg_state[i].store_ruid <= reg_state[regno].use_ruid
&& hard_regno_nregs[i][GET_MODE (reg)] == 1)
{
- rtx index_reg = gen_rtx_REG (GET_MODE (reg), i);
-
- const_reg = index_reg;
+ index_reg = gen_rtx_REG (GET_MODE (reg), i);
reg_sum = gen_rtx_PLUS (GET_MODE (reg), index_reg, base);
break;
}
/* Check that PREV_SET is indeed (set (REGX) (CONST_INT)) and that
(REGY), i.e. BASE, is not clobbered before the last use we'll
create. */
- if (prev_set != 0
- && GET_CODE (SET_SRC (prev_set)) == CONST_INT
+ if (reg_sum
+ && 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)
- && reg_sum != 0)
+ <= reg_state[regno].use_ruid))
{
int i;
- /* Change destination register and, if necessary, the
- constant value in PREV, the constant loading instruction. */
- validate_change (prev, &SET_DEST (prev_set), const_reg, 1);
+ /* Change destination register and, if necessary, the constant
+ value in PREV, the constant loading instruction. */
+ validate_change (prev, &SET_DEST (prev_set), index_reg, 1);
if (reg_state[regno].offset != const0_rtx)
validate_change (prev,
&SET_SRC (prev_set),
with REG_SUM. */
for (i = reg_state[regno].use_index;
i < RELOAD_COMBINE_MAX_USES; i++)
- validate_change (reg_state[regno].reg_use[i].insn,
- reg_state[regno].reg_use[i].usep,
- /* Each change must have its own
- replacement. */
- copy_rtx (reg_sum), 1);
+ validate_unshare_change (reg_state[regno].reg_use[i].insn,
+ reg_state[regno].reg_use[i].usep,
+ /* Each change must have its own
+ replacement. */
+ reg_sum, 1);
if (apply_change_group ())
{
- rtx *np;
+ /* 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);
+
+ if (reg_state[REGNO (base)].use_ruid
+ > reg_state[regno].use_ruid)
+ reg_state[REGNO (base)].use_ruid
+ = reg_state[regno].use_ruid;
/* Delete the reg-reg addition. */
delete_insn (insn);
if (reg_state[regno].offset != const0_rtx)
/* Previous REG_EQUIV / REG_EQUAL notes for PREV
are now invalid. */
- for (np = ®_NOTES (prev); *np;)
- {
- if (REG_NOTE_KIND (*np) == REG_EQUAL
- || REG_NOTE_KIND (*np) == REG_EQUIV)
- *np = XEXP (*np, 1);
- else
- np = &XEXP (*np, 1);
- }
+ remove_reg_equal_equiv_notes (prev);
reg_state[regno].use_index = RELOAD_COMBINE_MAX_USES;
- reg_state[REGNO (const_reg)].store_ruid
+ reg_state[REGNO (index_reg)].store_ruid
= reload_combine_ruid;
continue;
}
accordingly. Called via note_stores from reload_combine. */
static void
-reload_combine_note_store (rtx dst, rtx set, void *data ATTRIBUTE_UNUSED)
+reload_combine_note_store (rtx dst, const_rtx set, void *data ATTRIBUTE_UNUSED)
{
int regno = 0;
int i;
case PLUS:
/* We are interested in (plus (reg) (const_int)) . */
if (!REG_P (XEXP (x, 0))
- || GET_CODE (XEXP (x, 1)) != CONST_INT)
+ || !CONST_INT_P (XEXP (x, 1)))
break;
offset = XEXP (x, 1);
x = XEXP (x, 0);
information about register contents we have would be costly, so we
use move2add_last_label_luid to note where the label is and then
later disable any optimization that would cross it.
- reg_offset[n] / reg_base_reg[n] / reg_mode[n] are only valid if
- reg_set_luid[n] is greater than move2add_last_label_luid. */
+ reg_offset[n] / reg_base_reg[n] / reg_symbol_ref[n] / reg_mode[n]
+ are only valid if reg_set_luid[n] is greater than
+ move2add_last_label_luid. */
static int reg_set_luid[FIRST_PSEUDO_REGISTER];
/* If reg_base_reg[n] is negative, register n has been set to
- reg_offset[n] in mode reg_mode[n] .
+ reg_offset[n] or reg_symbol_ref[n] + reg_offset[n] in mode reg_mode[n].
If reg_base_reg[n] is non-negative, register n has been set to the
sum of reg_offset[n] and the value of register reg_base_reg[n]
before reg_set_luid[n], calculated in mode reg_mode[n] . */
static HOST_WIDE_INT reg_offset[FIRST_PSEUDO_REGISTER];
static int reg_base_reg[FIRST_PSEUDO_REGISTER];
+static rtx reg_symbol_ref[FIRST_PSEUDO_REGISTER];
static enum machine_mode reg_mode[FIRST_PSEUDO_REGISTER];
/* move2add_luid is linearly increased while scanning the instructions
&& TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (OUTMODE), \
GET_MODE_BITSIZE (INMODE))))
+/* This function is called with INSN that sets REG to (SYM + OFF),
+ while REG is known to already have value (SYM + offset).
+ This function tries to change INSN into an add instruction
+ (set (REG) (plus (REG) (OFF - offset))) using the known value.
+ It also updates the information about REG's known value. */
+
+static void
+move2add_use_add2_insn (rtx reg, rtx sym, rtx off, rtx insn)
+{
+ rtx pat = PATTERN (insn);
+ rtx src = SET_SRC (pat);
+ int regno = REGNO (reg);
+ rtx new_src = gen_int_mode (INTVAL (off) - reg_offset[regno],
+ GET_MODE (reg));
+ bool speed = optimize_bb_for_speed_p (BLOCK_FOR_INSN (insn));
+
+ /* (set (reg) (plus (reg) (const_int 0))) is not canonical;
+ use (set (reg) (reg)) instead.
+ We don't delete this insn, nor do we convert it into a
+ note, to avoid losing register notes or the return
+ value flag. jump2 already knows how to get rid of
+ no-op moves. */
+ if (new_src == const0_rtx)
+ {
+ /* If the constants are different, this is a
+ truncation, that, if turned into (set (reg)
+ (reg)), would be discarded. Maybe we should
+ try a truncMN pattern? */
+ if (INTVAL (off) == reg_offset [regno])
+ 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))
+ {
+ rtx tem = gen_rtx_PLUS (GET_MODE (reg), reg, new_src);
+ 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))
+ {
+ 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);
+ if (validate_change (insn, &PATTERN (insn),
+ new_set, 0))
+ break;
+ }
+ }
+ }
+ reg_set_luid[regno] = move2add_luid;
+ reg_base_reg[regno] = -1;
+ reg_mode[regno] = GET_MODE (reg);
+ reg_symbol_ref[regno] = sym;
+ reg_offset[regno] = INTVAL (off);
+}
+
+
+/* This function is called with INSN that sets REG to (SYM + OFF),
+ but REG doesn't have known value (SYM + offset). This function
+ tries to find another register which is known to already have
+ value (SYM + offset) and change INSN into an add instruction
+ (set (REG) (plus (the found register) (OFF - offset))) if such
+ a register is found. It also updates the information about
+ REG's known value. */
+
+static void
+move2add_use_add3_insn (rtx reg, rtx sym, rtx off, rtx insn)
+{
+ 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;
+
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ if (reg_set_luid[i] > move2add_last_label_luid
+ && reg_mode[i] == GET_MODE (reg)
+ && reg_base_reg[i] < 0
+ && reg_symbol_ref[i] != NULL_RTX
+ && rtx_equal_p (sym, reg_symbol_ref[i]))
+ {
+ rtx new_src = gen_int_mode (INTVAL (off) - reg_offset[i],
+ GET_MODE (reg));
+ /* (set (reg) (plus (reg) (const_int 0))) is not canonical;
+ use (set (reg) (reg)) instead.
+ We don't delete this insn, nor do we convert it into a
+ note, to avoid losing register notes or the return
+ value flag. jump2 already knows how to get rid of
+ no-op moves. */
+ if (new_src == const0_rtx)
+ {
+ min_cost = 0;
+ min_regno = i;
+ break;
+ }
+ else
+ {
+ int cost = rtx_cost (new_src, PLUS, speed);
+ if (cost < min_cost)
+ {
+ min_cost = cost;
+ min_regno = i;
+ }
+ }
+ }
+
+ if (min_cost < rtx_cost (src, SET, speed))
+ {
+ rtx tem;
+
+ tem = gen_rtx_REG (GET_MODE (reg), min_regno);
+ if (i != min_regno)
+ {
+ rtx new_src = gen_int_mode (INTVAL (off) - reg_offset[min_regno],
+ GET_MODE (reg));
+ tem = gen_rtx_PLUS (GET_MODE (reg), tem, new_src);
+ }
+ validate_change (insn, &SET_SRC (pat), tem, 0);
+ }
+ reg_set_luid[regno] = move2add_luid;
+ reg_base_reg[regno] = -1;
+ reg_mode[regno] = GET_MODE (reg);
+ reg_symbol_ref[regno] = sym;
+ reg_offset[regno] = INTVAL (off);
+}
+
static void
reload_cse_move2add (rtx first)
{
rtx insn;
for (i = FIRST_PSEUDO_REGISTER - 1; i >= 0; i--)
- reg_set_luid[i] = 0;
+ {
+ reg_set_luid[i] = 0;
+ reg_offset[i] = 0;
+ reg_base_reg[i] = 0;
+ reg_symbol_ref[i] = NULL_RTX;
+ reg_mode[i] = VOIDmode;
+ }
move2add_last_label_luid = 0;
move2add_luid = 2;
/* Check if we have valid information on the contents of this
register in the mode of REG. */
if (reg_set_luid[regno] > move2add_last_label_luid
- && MODES_OK_FOR_MOVE2ADD (GET_MODE (reg), reg_mode[regno]))
+ && MODES_OK_FOR_MOVE2ADD (GET_MODE (reg), reg_mode[regno])
+ && dbg_cnt (cse2_move2add))
{
/* Try to transform (set (REGX) (CONST_INT A))
...
(set (STRICT_LOW_PART (REGX)) (CONST_INT B))
*/
- if (GET_CODE (src) == CONST_INT && reg_base_reg[regno] < 0)
+ if (CONST_INT_P (src)
+ && reg_base_reg[regno] < 0
+ && reg_symbol_ref[regno] == NULL_RTX)
{
- rtx new_src = gen_int_mode (INTVAL (src) - reg_offset[regno],
- GET_MODE (reg));
- /* (set (reg) (plus (reg) (const_int 0))) is not canonical;
- use (set (reg) (reg)) instead.
- We don't delete this insn, nor do we convert it into a
- note, to avoid losing register notes or the return
- value flag. jump2 already knows how to get rid of
- no-op moves. */
- if (new_src == const0_rtx)
- {
- /* If the constants are different, this is a
- truncation, that, if turned into (set (reg)
- (reg)), would be discarded. Maybe we should
- try a truncMN pattern? */
- if (INTVAL (src) == reg_offset [regno])
- validate_change (insn, &SET_SRC (pat), reg, 0);
- }
- else if (rtx_cost (new_src, PLUS) < rtx_cost (src, SET)
- && have_add2_insn (reg, new_src))
- {
- rtx tem = gen_rtx_PLUS (GET_MODE (reg), reg, new_src);
- validate_change (insn, &SET_SRC (pat), tem, 0);
- }
- else
- {
- 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))
- {
- if (have_insn_for (STRICT_LOW_PART, narrow_mode)
- && ((reg_offset[regno]
- & ~GET_MODE_MASK (narrow_mode))
- == (INTVAL (src)
- & ~GET_MODE_MASK (narrow_mode))))
- {
- rtx narrow_reg = gen_rtx_REG (narrow_mode,
- REGNO (reg));
- rtx narrow_src = gen_int_mode (INTVAL (src),
- narrow_mode);
- rtx new_set =
- gen_rtx_SET (VOIDmode,
- gen_rtx_STRICT_LOW_PART (VOIDmode,
- narrow_reg),
- narrow_src);
- if (validate_change (insn, &PATTERN (insn),
- new_set, 0))
- break;
- }
- }
- }
- reg_set_luid[regno] = move2add_luid;
- reg_mode[regno] = GET_MODE (reg);
- reg_offset[regno] = INTVAL (src);
+ move2add_use_add2_insn (reg, NULL_RTX, src, insn);
continue;
}
&& SET_DEST (set) == reg
&& GET_CODE (SET_SRC (set)) == PLUS
&& XEXP (SET_SRC (set), 0) == reg
- && GET_CODE (XEXP (SET_SRC (set), 1)) == CONST_INT)
+ && CONST_INT_P (XEXP (SET_SRC (set), 1)))
{
rtx src3 = XEXP (SET_SRC (set), 1);
HOST_WIDE_INT added_offset = INTVAL (src3);
+ base_offset
- regno_offset,
GET_MODE (reg));
- int success = 0;
+ bool success = false;
+ bool speed = optimize_bb_for_speed_p (BLOCK_FOR_INSN (insn));
if (new_src == const0_rtx)
/* 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)
- < COSTS_N_INSNS (1) + rtx_cost (src3, SET))
+ else if ((rtx_cost (new_src, PLUS, speed)
+ < COSTS_N_INSNS (1) + rtx_cost (src3, SET, speed))
&& have_add2_insn (reg, new_src))
{
rtx newpat = gen_rtx_SET (VOIDmode,
}
}
}
+
+ /* Try to transform
+ (set (REGX) (CONST (PLUS (SYMBOL_REF) (CONST_INT A))))
+ ...
+ (set (REGY) (CONST (PLUS (SYMBOL_REF) (CONST_INT B))))
+ to
+ (set (REGX) (CONST (PLUS (SYMBOL_REF) (CONST_INT A))))
+ ...
+ (set (REGY) (CONST (PLUS (REGX) (CONST_INT B-A)))) */
+ if ((GET_CODE (src) == SYMBOL_REF
+ || (GET_CODE (src) == CONST
+ && GET_CODE (XEXP (src, 0)) == PLUS
+ && GET_CODE (XEXP (XEXP (src, 0), 0)) == SYMBOL_REF
+ && CONST_INT_P (XEXP (XEXP (src, 0), 1))))
+ && dbg_cnt (cse2_move2add))
+ {
+ rtx sym, off;
+
+ if (GET_CODE (src) == SYMBOL_REF)
+ {
+ sym = src;
+ off = const0_rtx;
+ }
+ else
+ {
+ sym = XEXP (XEXP (src, 0), 0);
+ off = XEXP (XEXP (src, 0), 1);
+ }
+
+ /* If the reg already contains the value which is sum of
+ sym and some constant value, we can use an add2 insn. */
+ if (reg_set_luid[regno] > move2add_last_label_luid
+ && MODES_OK_FOR_MOVE2ADD (GET_MODE (reg), reg_mode[regno])
+ && reg_base_reg[regno] < 0
+ && reg_symbol_ref[regno] != NULL_RTX
+ && rtx_equal_p (sym, reg_symbol_ref[regno]))
+ move2add_use_add2_insn (reg, sym, off, insn);
+
+ /* Otherwise, we have to find a register whose value is sum
+ of sym and some constant value. */
+ else
+ move2add_use_add3_insn (reg, sym, off, insn);
+
+ continue;
+ }
}
for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
reg_set_luid[regno] = 0;
}
}
- note_stores (PATTERN (insn), move2add_note_store, NULL);
+ note_stores (PATTERN (insn), move2add_note_store, insn);
/* If INSN is a conditional branch, we try to extract an
implicit set out of it. */
allocation if possible. */
&& SCALAR_INT_MODE_P (GET_MODE (XEXP (cnd, 0)))
&& hard_regno_nregs[REGNO (XEXP (cnd, 0))][GET_MODE (XEXP (cnd, 0))] == 1
- && GET_CODE (XEXP (cnd, 1)) == CONST_INT)
+ && CONST_INT_P (XEXP (cnd, 1)))
{
rtx implicit_set =
gen_rtx_SET (VOIDmode, XEXP (cnd, 0), XEXP (cnd, 1));
- move2add_note_store (SET_DEST (implicit_set), implicit_set, 0);
+ move2add_note_store (SET_DEST (implicit_set), implicit_set, insn);
}
}
}
}
-/* SET is a SET or CLOBBER that sets DST.
+/* SET is a SET or CLOBBER that sets DST. DATA is the insn which
+ contains SET.
Update reg_set_luid, reg_offset and reg_base_reg accordingly.
Called from reload_cse_move2add via note_stores. */
static void
-move2add_note_store (rtx dst, rtx set, void *data ATTRIBUTE_UNUSED)
+move2add_note_store (rtx dst, const_rtx set, void *data)
{
+ rtx insn = (rtx) data;
unsigned int regno = 0;
+ unsigned int nregs = 0;
unsigned int i;
enum machine_mode mode = GET_MODE (dst);
GET_MODE (SUBREG_REG (dst)),
SUBREG_BYTE (dst),
GET_MODE (dst));
+ nregs = subreg_nregs (dst);
dst = SUBREG_REG (dst);
}
return;
regno += REGNO (dst);
+ if (!nregs)
+ nregs = hard_regno_nregs[regno][mode];
+
+ if (SCALAR_INT_MODE_P (GET_MODE (dst))
+ && nregs == 1 && GET_CODE (set) == SET)
+ {
+ rtx note, sym = NULL_RTX;
+ HOST_WIDE_INT off;
+
+ note = find_reg_equal_equiv_note (insn);
+ if (note && GET_CODE (XEXP (note, 0)) == SYMBOL_REF)
+ {
+ sym = XEXP (note, 0);
+ off = 0;
+ }
+ else if (note && GET_CODE (XEXP (note, 0)) == CONST
+ && GET_CODE (XEXP (XEXP (note, 0), 0)) == PLUS
+ && GET_CODE (XEXP (XEXP (XEXP (note, 0), 0), 0)) == SYMBOL_REF
+ && CONST_INT_P (XEXP (XEXP (XEXP (note, 0), 0), 1)))
+ {
+ sym = XEXP (XEXP (XEXP (note, 0), 0), 0);
+ off = INTVAL (XEXP (XEXP (XEXP (note, 0), 0), 1));
+ }
+
+ if (sym != NULL_RTX)
+ {
+ reg_base_reg[regno] = -1;
+ reg_symbol_ref[regno] = sym;
+ reg_offset[regno] = off;
+ reg_mode[regno] = mode;
+ reg_set_luid[regno] = move2add_luid;
+ return;
+ }
+ }
if (SCALAR_INT_MODE_P (GET_MODE (dst))
- && hard_regno_nregs[regno][mode] == 1 && GET_CODE (set) == SET
+ && nregs == 1 && GET_CODE (set) == SET
&& GET_CODE (SET_DEST (set)) != ZERO_EXTRACT
&& GET_CODE (SET_DEST (set)) != STRICT_LOW_PART)
{
{
base_reg = XEXP (src, 0);
- if (GET_CODE (XEXP (src, 1)) == CONST_INT)
+ if (CONST_INT_P (XEXP (src, 1)))
offset = INTVAL (XEXP (src, 1));
else if (REG_P (XEXP (src, 1))
&& (reg_set_luid[REGNO (XEXP (src, 1))]
case CONST_INT:
/* Start tracking the register as a constant. */
reg_base_reg[regno] = -1;
+ reg_symbol_ref[regno] = NULL_RTX;
reg_offset[regno] = INTVAL (SET_SRC (set));
/* We assign the same luid to all registers set to constants. */
reg_set_luid[regno] = move2add_last_label_luid + 1;
if (reg_set_luid[base_regno] <= move2add_last_label_luid)
{
reg_base_reg[base_regno] = base_regno;
+ reg_symbol_ref[base_regno] = NULL_RTX;
reg_offset[base_regno] = 0;
reg_set_luid[base_regno] = move2add_luid;
reg_mode[base_regno] = mode;
/* Copy base information from our base register. */
reg_set_luid[regno] = reg_set_luid[base_regno];
reg_base_reg[regno] = reg_base_reg[base_regno];
+ reg_symbol_ref[regno] = reg_symbol_ref[base_regno];
/* Compute the sum of the offsets or constants. */
reg_offset[regno] = trunc_int_for_mode (offset
}
else
{
- unsigned int endregno = regno + hard_regno_nregs[regno][mode];
+ unsigned int endregno = regno + nregs;
for (i = regno; i < endregno; i++)
/* Reset the information about this register. */
static bool
gate_handle_postreload (void)
{
- return (optimize > 0);
+ return (optimize > 0 && reload_completed);
}
-static void
+static unsigned int
rest_of_handle_postreload (void)
{
+ if (!dbg_cnt (postreload_cse))
+ return 0;
+
/* Do a very simple CSE pass over just the hard registers. */
reload_cse_regs (get_insns ());
- /* reload_cse_regs can eliminate potentially-trapping MEMs.
+ /* Reload_cse_regs can eliminate potentially-trapping MEMs.
Remove any EH edges associated with them. */
- if (flag_non_call_exceptions)
+ if (cfun->can_throw_non_call_exceptions)
purge_all_dead_edges ();
+
+ return 0;
}
-struct tree_opt_pass pass_postreload_cse =
+struct rtl_opt_pass pass_postreload_cse =
{
+ {
+ RTL_PASS,
"postreload", /* name */
gate_handle_postreload, /* gate */
rest_of_handle_postreload, /* execute */
0, /* properties_provided */
0, /* properties_destroyed */
0, /* todo_flags_start */
- TODO_dump_func, /* todo_flags_finish */
- 'o' /* letter */
+ TODO_df_finish | TODO_verify_rtl_sharing |
+ TODO_dump_func /* todo_flags_finish */
+ }
};
-