/* Common subexpression elimination for GNU compiler.
- Copyright (C) 1987-2015 Free Software Foundation, Inc.
+ Copyright (C) 1987-2020 Free Software Foundation, Inc.
This file is part of GCC.
#include "config.h"
#include "system.h"
#include "coretypes.h"
-#include "tm.h"
+#include "backend.h"
+#include "target.h"
#include "rtl.h"
+#include "tree.h"
+#include "cfghooks.h"
+#include "df.h"
+#include "memmodel.h"
#include "tm_p.h"
-#include "hard-reg-set.h"
+#include "insn-config.h"
#include "regs.h"
-#include "predict.h"
-#include "vec.h"
-#include "hashtab.h"
-#include "hash-set.h"
-#include "machmode.h"
-#include "input.h"
-#include "function.h"
-#include "dominance.h"
-#include "cfg.h"
+#include "emit-rtl.h"
+#include "recog.h"
#include "cfgrtl.h"
#include "cfganal.h"
#include "cfgcleanup.h"
-#include "basic-block.h"
-#include "flags.h"
-#include "insn-config.h"
-#include "recog.h"
-#include "symtab.h"
-#include "statistics.h"
-#include "double-int.h"
-#include "real.h"
-#include "fixed-value.h"
#include "alias.h"
-#include "wide-int.h"
-#include "inchash.h"
-#include "tree.h"
-#include "expmed.h"
-#include "dojump.h"
-#include "explow.h"
-#include "calls.h"
-#include "emit-rtl.h"
-#include "varasm.h"
-#include "stmt.h"
-#include "expr.h"
-#include "diagnostic-core.h"
#include "toplev.h"
-#include "ggc.h"
-#include "except.h"
-#include "target.h"
-#include "params.h"
#include "rtlhooks-def.h"
#include "tree-pass.h"
-#include "df.h"
#include "dbgcnt.h"
#include "rtl-iter.h"
+#include "regs.h"
+#include "function-abi.h"
/* The basic idea of common subexpression elimination is to go
through the code, keeping a record of expressions that would
|| (HARD_REGISTER_NUM_P (N) \
&& FIXED_REGNO_P (N) && REGNO_REG_CLASS (N) != NO_REGS))
-#define COST(X) (REG_P (X) ? 0 : notreg_cost (X, SET, 1))
-#define COST_IN(X, OUTER, OPNO) (REG_P (X) ? 0 : notreg_cost (X, OUTER, OPNO))
+#define COST(X, MODE) \
+ (REG_P (X) ? 0 : notreg_cost (X, MODE, SET, 1))
+#define COST_IN(X, MODE, OUTER, OPNO) \
+ (REG_P (X) ? 0 : notreg_cost (X, MODE, OUTER, OPNO))
/* Get the number of times this register has been updated in this
basic block. */
static sbitmap cse_visited_basic_blocks;
static bool fixed_base_plus_p (rtx x);
-static int notreg_cost (rtx, enum rtx_code, int);
+static int notreg_cost (rtx, machine_mode, enum rtx_code, int);
static int preferable (int, int, int, int);
static void new_basic_block (void);
static void make_new_qty (unsigned int, machine_mode);
static void merge_equiv_classes (struct table_elt *, struct table_elt *);
static void invalidate (rtx, machine_mode);
static void remove_invalid_refs (unsigned int);
-static void remove_invalid_subreg_refs (unsigned int, unsigned int,
+static void remove_invalid_subreg_refs (unsigned int, poly_uint64,
machine_mode);
static void rehash_using_reg (rtx);
static void invalidate_memory (void);
-static void invalidate_for_call (void);
static rtx use_related_value (rtx, struct table_elt *);
static inline unsigned canon_hash (rtx, machine_mode);
static void cse_prescan_path (struct cse_basic_block_data *);
static void invalidate_from_clobbers (rtx_insn *);
static void invalidate_from_sets_and_clobbers (rtx_insn *);
-static rtx cse_process_notes (rtx, rtx, bool *);
static void cse_extended_basic_block (struct cse_basic_block_data *);
extern void dump_class (struct table_elt*);
static void get_cse_reg_info_1 (unsigned int regno);
from COST macro to keep it simple. */
static int
-notreg_cost (rtx x, enum rtx_code outer, int opno)
+notreg_cost (rtx x, machine_mode mode, enum rtx_code outer, int opno)
{
+ scalar_int_mode int_mode, inner_mode;
return ((GET_CODE (x) == SUBREG
&& REG_P (SUBREG_REG (x))
- && GET_MODE_CLASS (GET_MODE (x)) == MODE_INT
- && GET_MODE_CLASS (GET_MODE (SUBREG_REG (x))) == MODE_INT
- && (GET_MODE_SIZE (GET_MODE (x))
- < GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))))
+ && is_int_mode (mode, &int_mode)
+ && is_int_mode (GET_MODE (SUBREG_REG (x)), &inner_mode)
+ && GET_MODE_SIZE (int_mode) < GET_MODE_SIZE (inner_mode)
&& subreg_lowpart_p (x)
- && TRULY_NOOP_TRUNCATION_MODES_P (GET_MODE (x),
- GET_MODE (SUBREG_REG (x))))
+ && TRULY_NOOP_TRUNCATION_MODES_P (int_mode, inner_mode))
? 0
- : rtx_cost (x, outer, opno, optimize_this_for_speed_p) * 2);
+ : rtx_cost (x, mode, outer, opno, optimize_this_for_speed_p) * 2);
}
\f
don't prefer pseudos over hard regs so that we derive constants in
argument registers from other argument registers rather than from the
original pseudo that was used to synthesize the constant. */
- insert_with_costs (exp, elt, hash, mode, COST (reg), 1);
+ insert_with_costs (exp, elt, hash, mode, COST (reg, mode), 1);
}
/* The constant CST is equivalent to the register REG. Create
insert (rtx x, struct table_elt *classp, unsigned int hash,
machine_mode mode)
{
- return
- insert_with_costs (x, classp, hash, mode, COST (x), approx_reg_cost (x));
+ return insert_with_costs (x, classp, hash, mode,
+ COST (x, mode), approx_reg_cost (x));
}
\f
}
return false;
}
-\f
+
+/* Remove from the hash table, or mark as invalid, all expressions whose
+ values could be altered by storing in register X. */
+
+static void
+invalidate_reg (rtx x)
+{
+ gcc_assert (GET_CODE (x) == REG);
+
+ /* If X is a register, dependencies on its contents are recorded
+ through the qty number mechanism. Just change the qty number of
+ the register, mark it as invalid for expressions that refer to it,
+ and remove it itself. */
+ unsigned int regno = REGNO (x);
+ unsigned int hash = HASH (x, GET_MODE (x));
+
+ /* Remove REGNO from any quantity list it might be on and indicate
+ that its value might have changed. If it is a pseudo, remove its
+ entry from the hash table.
+
+ For a hard register, we do the first two actions above for any
+ additional hard registers corresponding to X. Then, if any of these
+ registers are in the table, we must remove any REG entries that
+ overlap these registers. */
+
+ delete_reg_equiv (regno);
+ REG_TICK (regno)++;
+ SUBREG_TICKED (regno) = -1;
+
+ if (regno >= FIRST_PSEUDO_REGISTER)
+ remove_pseudo_from_table (x, hash);
+ else
+ {
+ HOST_WIDE_INT in_table = TEST_HARD_REG_BIT (hard_regs_in_table, regno);
+ unsigned int endregno = END_REGNO (x);
+ unsigned int rn;
+ struct table_elt *p, *next;
+
+ CLEAR_HARD_REG_BIT (hard_regs_in_table, regno);
+
+ for (rn = regno + 1; rn < endregno; rn++)
+ {
+ in_table |= TEST_HARD_REG_BIT (hard_regs_in_table, rn);
+ CLEAR_HARD_REG_BIT (hard_regs_in_table, rn);
+ delete_reg_equiv (rn);
+ REG_TICK (rn)++;
+ SUBREG_TICKED (rn) = -1;
+ }
+
+ if (in_table)
+ for (hash = 0; hash < HASH_SIZE; hash++)
+ for (p = table[hash]; p; p = next)
+ {
+ next = p->next_same_hash;
+
+ if (!REG_P (p->exp) || REGNO (p->exp) >= FIRST_PSEUDO_REGISTER)
+ continue;
+
+ unsigned int tregno = REGNO (p->exp);
+ unsigned int tendregno = END_REGNO (p->exp);
+ if (tendregno > regno && tregno < endregno)
+ remove_from_table (p, hash);
+ }
+ }
+}
+
/* Remove from the hash table, or mark as invalid, all expressions whose
values could be altered by storing in X. X is a register, a subreg, or
a memory reference with nonvarying address (because, when a memory
switch (GET_CODE (x))
{
case REG:
- {
- /* If X is a register, dependencies on its contents are recorded
- through the qty number mechanism. Just change the qty number of
- the register, mark it as invalid for expressions that refer to it,
- and remove it itself. */
- unsigned int regno = REGNO (x);
- unsigned int hash = HASH (x, GET_MODE (x));
-
- /* Remove REGNO from any quantity list it might be on and indicate
- that its value might have changed. If it is a pseudo, remove its
- entry from the hash table.
-
- For a hard register, we do the first two actions above for any
- additional hard registers corresponding to X. Then, if any of these
- registers are in the table, we must remove any REG entries that
- overlap these registers. */
-
- delete_reg_equiv (regno);
- REG_TICK (regno)++;
- SUBREG_TICKED (regno) = -1;
-
- if (regno >= FIRST_PSEUDO_REGISTER)
- remove_pseudo_from_table (x, hash);
- else
- {
- HOST_WIDE_INT in_table
- = TEST_HARD_REG_BIT (hard_regs_in_table, regno);
- unsigned int endregno = END_HARD_REGNO (x);
- unsigned int tregno, tendregno, rn;
- struct table_elt *p, *next;
-
- CLEAR_HARD_REG_BIT (hard_regs_in_table, regno);
-
- for (rn = regno + 1; rn < endregno; rn++)
- {
- in_table |= TEST_HARD_REG_BIT (hard_regs_in_table, rn);
- CLEAR_HARD_REG_BIT (hard_regs_in_table, rn);
- delete_reg_equiv (rn);
- REG_TICK (rn)++;
- SUBREG_TICKED (rn) = -1;
- }
-
- if (in_table)
- for (hash = 0; hash < HASH_SIZE; hash++)
- for (p = table[hash]; p; p = next)
- {
- next = p->next_same_hash;
-
- if (!REG_P (p->exp)
- || REGNO (p->exp) >= FIRST_PSEUDO_REGISTER)
- continue;
-
- tregno = REGNO (p->exp);
- tendregno = END_HARD_REGNO (p->exp);
- if (tendregno > regno && tregno < endregno)
- remove_from_table (p, hash);
- }
- }
- }
+ invalidate_reg (x);
return;
case SUBREG:
/* Likewise for a subreg with subreg_reg REGNO, subreg_byte OFFSET,
and mode MODE. */
static void
-remove_invalid_subreg_refs (unsigned int regno, unsigned int offset,
+remove_invalid_subreg_refs (unsigned int regno, poly_uint64 offset,
machine_mode mode)
{
unsigned int i;
struct table_elt *p, *next;
- unsigned int end = offset + (GET_MODE_SIZE (mode) - 1);
for (i = 0; i < HASH_SIZE; i++)
for (p = table[i]; p; p = next)
&& (GET_CODE (exp) != SUBREG
|| !REG_P (SUBREG_REG (exp))
|| REGNO (SUBREG_REG (exp)) != regno
- || (((SUBREG_BYTE (exp)
- + (GET_MODE_SIZE (GET_MODE (exp)) - 1)) >= offset)
- && SUBREG_BYTE (exp) <= end))
+ || ranges_maybe_overlap_p (SUBREG_BYTE (exp),
+ GET_MODE_SIZE (GET_MODE (exp)),
+ offset, GET_MODE_SIZE (mode)))
&& refers_to_regno_p (regno, p->exp))
remove_from_table (p, i);
}
}
\f
/* Remove from the hash table any expression that is a call-clobbered
- register. Also update their TICK values. */
+ register in INSN. Also update their TICK values. */
static void
-invalidate_for_call (void)
+invalidate_for_call (rtx_insn *insn)
{
- unsigned int regno, endregno;
- unsigned int i;
+ unsigned int regno;
unsigned hash;
struct table_elt *p, *next;
int in_table = 0;
hard_reg_set_iterator hrsi;
- /* Go through all the hard registers. For each that is clobbered in
- a CALL_INSN, remove the register from quantity chains and update
+ /* Go through all the hard registers. For each that might be clobbered
+ in call insn INSN, remove the register from quantity chains and update
reg_tick if defined. Also see if any of these registers is currently
- in the table. */
- EXECUTE_IF_SET_IN_HARD_REG_SET (regs_invalidated_by_call, 0, regno, hrsi)
+ in the table.
+
+ ??? We could be more precise for partially-clobbered registers,
+ and only invalidate values that actually occupy the clobbered part
+ of the registers. It doesn't seem worth the effort though, since
+ we shouldn't see this situation much before RA. Whatever choice
+ we make here has to be consistent with the table walk below,
+ so any change to this test will require a change there too. */
+ HARD_REG_SET callee_clobbers
+ = insn_callee_abi (insn).full_and_partial_reg_clobbers ();
+ EXECUTE_IF_SET_IN_HARD_REG_SET (callee_clobbers, 0, regno, hrsi)
{
delete_reg_equiv (regno);
if (REG_TICK (regno) >= 0)
|| REGNO (p->exp) >= FIRST_PSEUDO_REGISTER)
continue;
- regno = REGNO (p->exp);
- endregno = END_HARD_REGNO (p->exp);
-
- for (i = regno; i < endregno; i++)
- if (TEST_HARD_REG_BIT (regs_invalidated_by_call, i))
- {
- remove_from_table (p, hash);
- break;
- }
+ /* This must use the same test as above rather than the
+ more accurate clobbers_reg_p. */
+ if (overlaps_hard_reg_set_p (callee_clobbers, GET_MODE (p->exp),
+ REGNO (p->exp)))
+ remove_from_table (p, hash);
}
}
\f
{
hash += (((unsigned int) SUBREG << 7)
+ REGNO (SUBREG_REG (x))
- + (SUBREG_BYTE (x) / UNITS_PER_WORD));
+ + (constant_lower_bound (SUBREG_BYTE (x))
+ / UNITS_PER_WORD));
return hash;
}
break;
hash += CONST_WIDE_INT_ELT (x, i);
return hash;
+ case CONST_POLY_INT:
+ {
+ inchash::hash h;
+ h.add_int (hash);
+ for (unsigned int i = 0; i < NUM_POLY_INT_COEFFS; ++i)
+ h.add_wide_int (CONST_POLY_INT_COEFFS (x)[i]);
+ return h.end ();
+ }
+
case CONST_DOUBLE:
/* This is like the general case, except that it only counts
the integers representing the constant. */
int units;
rtx elt;
- units = CONST_VECTOR_NUNITS (x);
+ units = const_vector_encoded_nelts (x);
for (i = 0; i < units; ++i)
{
- elt = CONST_VECTOR_ELT (x, i);
+ elt = CONST_VECTOR_ENCODED_ELT (x, i);
hash += hash_rtx_cb (elt, GET_MODE (elt),
do_not_record_p, hash_arg_in_memory_p,
have_reg_qty, cb);
/* We don't hash on the address of the CODE_LABEL to avoid bootstrap
differences and differences between each stage's debugging dumps. */
hash += (((unsigned int) LABEL_REF << 7)
- + CODE_LABEL_NUMBER (LABEL_REF_LABEL (x)));
+ + CODE_LABEL_NUMBER (label_ref_label (x)));
return hash;
case SYMBOL_REF:
hash += (unsigned int) XINT (x, i);
break;
+ case 'p':
+ hash += constant_lower_bound (SUBREG_BYTE (x));
+ break;
+
case '0': case 't':
/* Unused. */
break;
return x == y;
case LABEL_REF:
- return LABEL_REF_LABEL (x) == LABEL_REF_LABEL (y);
+ return label_ref_label (x) == label_ref_label (y);
case SYMBOL_REF:
return XSTR (x, 0) == XSTR (y, 0);
return 0;
break;
+ case 'p':
+ if (maybe_ne (SUBREG_BYTE (x), SUBREG_BYTE (y)))
+ return 0;
+ break;
+
case '0':
case 't':
break;
if (x == 0)
break;
- /* If we need to reverse the comparison, make sure that that is
+ /* If we need to reverse the comparison, make sure that is
possible -- we can't necessarily infer the value of GE from LT
with floating-point operands. */
if (reverse_code)
{
- enum rtx_code reversed = reversed_comparison_code (x, NULL_RTX);
+ enum rtx_code reversed = reversed_comparison_code (x, NULL);
if (reversed == UNKNOWN)
break;
else
int i;
rtx new_rtx = 0;
int changed = 0;
+ poly_int64 xval;
/* Operands of X. */
/* Workaround -Wmaybe-uninitialized false positive during
}
return x;
-#ifdef NO_FUNCTION_CSE
case CALL:
- if (CONSTANT_P (XEXP (XEXP (x, 0), 0)))
+ if (NO_FUNCTION_CSE && CONSTANT_P (XEXP (XEXP (x, 0), 0)))
return x;
break;
-#endif
/* Anything else goes through the loop below. */
default:
argument. */
if (const_arg != 0
&& const_arg != folded_arg
- && COST_IN (const_arg, code, i) <= COST_IN (folded_arg, code, i)
+ && (COST_IN (const_arg, mode_arg, code, i)
+ <= COST_IN (folded_arg, mode_arg, code, i))
/* It's not safe to substitute the operand of a conversion
operator with a constant, as the conversion's identity
consistent with the order in X. */
if (canonicalize_change_group (insn, x))
{
- rtx tem;
- tem = const_arg0, const_arg0 = const_arg1, const_arg1 = tem;
- tem = folded_arg0, folded_arg0 = folded_arg1, folded_arg1 = tem;
+ std::swap (const_arg0, const_arg1);
+ std::swap (folded_arg0, folded_arg1);
}
apply_change_group ();
if (SCALAR_FLOAT_MODE_P (mode))
{
#ifdef FLOAT_STORE_FLAG_VALUE
- true_rtx = (CONST_DOUBLE_FROM_REAL_VALUE
+ true_rtx = (const_double_from_real_value
(FLOAT_STORE_FLAG_VALUE (mode), mode));
#else
true_rtx = NULL_RTX;
if (p != NULL)
{
cheapest_simplification = x;
- cheapest_cost = COST (x);
+ cheapest_cost = COST (x, mode);
for (p = p->first_same_value; p != NULL; p = p->next_same_value)
{
if (simp_result == NULL)
continue;
- cost = COST (simp_result);
+ cost = COST (simp_result, mode);
if (cost < cheapest_cost)
{
cheapest_cost = cost;
: lookup_as_function (folded_arg0, MINUS);
if (y != 0 && GET_CODE (XEXP (y, 1)) == LABEL_REF
- && LABEL_REF_LABEL (XEXP (y, 1)) == LABEL_REF_LABEL (const_arg1))
+ && label_ref_label (XEXP (y, 1)) == label_ref_label (const_arg1))
return XEXP (y, 0);
/* Now try for a CONST of a MINUS like the above. */
: lookup_as_function (folded_arg0, CONST))) != 0
&& GET_CODE (XEXP (y, 0)) == MINUS
&& GET_CODE (XEXP (XEXP (y, 0), 1)) == LABEL_REF
- && LABEL_REF_LABEL (XEXP (XEXP (y, 0), 1)) == LABEL_REF_LABEL (const_arg1))
+ && label_ref_label (XEXP (XEXP (y, 0), 1)) == label_ref_label (const_arg1))
return XEXP (XEXP (y, 0), 0);
}
: lookup_as_function (folded_arg1, MINUS);
if (y != 0 && GET_CODE (XEXP (y, 1)) == LABEL_REF
- && LABEL_REF_LABEL (XEXP (y, 1)) == LABEL_REF_LABEL (const_arg0))
+ && label_ref_label (XEXP (y, 1)) == label_ref_label (const_arg0))
return XEXP (y, 0);
/* Now try for a CONST of a MINUS like the above. */
: lookup_as_function (folded_arg1, CONST))) != 0
&& GET_CODE (XEXP (y, 0)) == MINUS
&& GET_CODE (XEXP (XEXP (y, 0), 1)) == LABEL_REF
- && LABEL_REF_LABEL (XEXP (XEXP (y, 0), 1)) == LABEL_REF_LABEL (const_arg0))
+ && label_ref_label (XEXP (XEXP (y, 0), 1)) == label_ref_label (const_arg0))
return XEXP (XEXP (y, 0), 0);
}
instead we test for the problematic value in a more direct
manner and hope the Sun compilers get it correct. */
&& INTVAL (const_arg1) !=
- ((HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT - 1))
+ (HOST_WIDE_INT_1 << (HOST_BITS_PER_WIDE_INT - 1))
&& REG_P (folded_arg1))
{
rtx new_const = GEN_INT (-INTVAL (const_arg1));
case MINUS:
/* If we have (MINUS Y C), see if Y is known to be (PLUS Z C2).
If so, produce (PLUS Z C2-C). */
- if (const_arg1 != 0 && CONST_INT_P (const_arg1))
+ if (const_arg1 != 0 && poly_int_rtx_p (const_arg1, &xval))
{
rtx y = lookup_as_function (XEXP (x, 0), PLUS);
- if (y && CONST_INT_P (XEXP (y, 1)))
- return fold_rtx (plus_constant (mode, copy_rtx (y),
- -INTVAL (const_arg1)),
+ if (y && poly_int_rtx_p (XEXP (y, 1)))
+ return fold_rtx (plus_constant (mode, copy_rtx (y), -xval),
NULL);
}
enum rtx_code associate_code;
if (is_shift
- && (INTVAL (const_arg1) >= GET_MODE_PRECISION (mode)
+ && (INTVAL (const_arg1) >= GET_MODE_UNIT_PRECISION (mode)
|| INTVAL (const_arg1) < 0))
{
if (SHIFT_COUNT_TRUNCATED)
- canon_const_arg1 = GEN_INT (INTVAL (const_arg1)
- & (GET_MODE_BITSIZE (mode)
- - 1));
+ canon_const_arg1 = gen_int_shift_amount
+ (mode, (INTVAL (const_arg1)
+ & (GET_MODE_UNIT_BITSIZE (mode) - 1)));
else
break;
}
if (code == PLUS && const_arg1 == inner_const
&& ((HAVE_PRE_INCREMENT
- && exact_log2 (INTVAL (const_arg1)) >= 0)
+ && pow2p_hwi (INTVAL (const_arg1)))
|| (HAVE_POST_INCREMENT
- && exact_log2 (INTVAL (const_arg1)) >= 0)
+ && pow2p_hwi (INTVAL (const_arg1)))
|| (HAVE_PRE_DECREMENT
- && exact_log2 (- INTVAL (const_arg1)) >= 0)
+ && pow2p_hwi (- INTVAL (const_arg1)))
|| (HAVE_POST_DECREMENT
- && exact_log2 (- INTVAL (const_arg1)) >= 0)))
+ && pow2p_hwi (- INTVAL (const_arg1)))))
break;
/* ??? Vector mode shifts by scalar
break;
if (is_shift
- && (INTVAL (inner_const) >= GET_MODE_PRECISION (mode)
+ && (INTVAL (inner_const) >= GET_MODE_UNIT_PRECISION (mode)
|| INTVAL (inner_const) < 0))
{
if (SHIFT_COUNT_TRUNCATED)
- inner_const = GEN_INT (INTVAL (inner_const)
- & (GET_MODE_BITSIZE (mode) - 1));
+ inner_const = gen_int_shift_amount
+ (mode, (INTVAL (inner_const)
+ & (GET_MODE_UNIT_BITSIZE (mode) - 1)));
else
break;
}
if (is_shift
&& CONST_INT_P (new_const)
- && INTVAL (new_const) >= GET_MODE_PRECISION (mode))
+ && INTVAL (new_const) >= GET_MODE_UNIT_PRECISION (mode))
{
/* As an exception, we can turn an ASHIFTRT of this
form into a shift of the number of bits - 1. */
if (code == ASHIFTRT)
- new_const = GEN_INT (GET_MODE_BITSIZE (mode) - 1);
+ new_const = gen_int_shift_amount
+ (mode, GET_MODE_UNIT_BITSIZE (mode) - 1);
else if (!side_effects_p (XEXP (y, 0)))
return CONST0_RTX (mode);
else
/* See if we previously assigned a constant value to this SUBREG. */
if ((new_rtx = lookup_as_function (x, CONST_INT)) != 0
|| (new_rtx = lookup_as_function (x, CONST_WIDE_INT)) != 0
+ || (NUM_POLY_INT_COEFFS > 1
+ && (new_rtx = lookup_as_function (x, CONST_POLY_INT)) != 0)
|| (new_rtx = lookup_as_function (x, CONST_DOUBLE)) != 0
|| (new_rtx = lookup_as_function (x, CONST_FIXED)) != 0)
return new_rtx;
/* If we didn't and if doing so makes sense, see if we previously
assigned a constant value to the enclosing word mode SUBREG. */
- if (GET_MODE_SIZE (mode) < GET_MODE_SIZE (word_mode)
- && GET_MODE_SIZE (word_mode) < GET_MODE_SIZE (imode))
+ if (known_lt (GET_MODE_SIZE (mode), UNITS_PER_WORD)
+ && known_lt (UNITS_PER_WORD, GET_MODE_SIZE (imode)))
{
- int byte = SUBREG_BYTE (x) - subreg_lowpart_offset (mode, word_mode);
- if (byte >= 0 && (byte % UNITS_PER_WORD) == 0)
+ poly_int64 byte = (SUBREG_BYTE (x)
+ - subreg_lowpart_offset (mode, word_mode));
+ if (known_ge (byte, 0) && multiple_p (byte, UNITS_PER_WORD))
{
rtx y = gen_rtx_SUBREG (word_mode, SUBREG_REG (x), byte);
new_rtx = lookup_as_function (y, CONST_INT);
the subreg. Note that the upper bits of paradoxical subregs
are undefined, so they cannot be said to equal anything. */
if (REG_P (SUBREG_REG (x))
- && GET_MODE_SIZE (mode) <= GET_MODE_SIZE (imode)
+ && !paradoxical_subreg_p (x)
&& (new_rtx = equiv_constant (SUBREG_REG (x))) != 0)
return simplify_subreg (mode, new_rtx, imode, SUBREG_BYTE (x));
op0 = fold_rtx (XEXP (XEXP (SET_SRC (set), 0), 0), insn);
op1 = fold_rtx (XEXP (XEXP (SET_SRC (set), 0), 1), insn);
+ /* On a cc0 target the cc0-setter and cc0-user may end up in different
+ blocks. When that happens the tracking of the cc0-setter via
+ PREV_INSN_CC0 is spoiled. That means that fold_rtx may return
+ NULL_RTX. In those cases, there's nothing to record. */
+ if (op0 == NULL_RTX || op1 == NULL_RTX)
+ return;
+
code = find_comparison_args (code, &op0, &op1, &mode0, &mode1);
if (! cond_known_true)
{
if we test MODE instead, we can get an infinite recursion
alternating between two modes each wider than MODE. */
- if (code == NE && GET_CODE (op0) == SUBREG
- && subreg_lowpart_p (op0)
- && (GET_MODE_SIZE (GET_MODE (op0))
- < GET_MODE_SIZE (GET_MODE (SUBREG_REG (op0)))))
+ if (code == NE
+ && partial_subreg_p (op0)
+ && subreg_lowpart_p (op0))
{
machine_mode inner_mode = GET_MODE (SUBREG_REG (op0));
rtx tem = record_jump_cond_subreg (inner_mode, op1);
reversed_nonequality);
}
- if (code == NE && GET_CODE (op1) == SUBREG
- && subreg_lowpart_p (op1)
- && (GET_MODE_SIZE (GET_MODE (op1))
- < GET_MODE_SIZE (GET_MODE (SUBREG_REG (op1)))))
+ if (code == NE
+ && partial_subreg_p (op1)
+ && subreg_lowpart_p (op1))
{
machine_mode inner_mode = GET_MODE (SUBREG_REG (op1));
rtx tem = record_jump_cond_subreg (inner_mode, op0);
The size of this field should match the size of the mode
field of struct rtx_def (see rtl.h). */
ENUM_BITFIELD(machine_mode) mode : 8;
- /* A constant equivalent for SET_SRC, if any. */
- rtx src_const;
/* Hash value of constant equivalent for SET_SRC. */
unsigned src_const_hash;
+ /* A constant equivalent for SET_SRC, if any. */
+ rtx src_const;
/* Table entry for constant equivalent for SET_SRC, if any. */
struct table_elt *src_const_elt;
/* Table entry for the destination address. */
&& (reg_mentioned_p (dest, XEXP (note, 0))
|| rtx_equal_p (src, XEXP (note, 0))))
remove_note (insn, note);
+
+ /* If INSN has a REG_ARGS_SIZE note, move it to PREV. */
+ note = find_reg_note (insn, REG_ARGS_SIZE, NULL_RTX);
+ if (note != 0)
+ {
+ remove_note (insn, note);
+ gcc_assert (!find_reg_note (prev, REG_ARGS_SIZE, NULL_RTX));
+ set_unique_reg_note (prev, REG_ARGS_SIZE, XEXP (note, 0));
+ }
}
}
}
return n_sets;
}
\f
+/* Subroutine of canonicalize_insn. X is an ASM_OPERANDS in INSN. */
+
+static void
+canon_asm_operands (rtx x, rtx_insn *insn)
+{
+ for (int i = ASM_OPERANDS_INPUT_LENGTH (x) - 1; i >= 0; i--)
+ {
+ rtx input = ASM_OPERANDS_INPUT (x, i);
+ if (!(REG_P (input) && HARD_REGISTER_P (input)))
+ {
+ input = canon_reg (input, insn);
+ validate_change (insn, &ASM_OPERANDS_INPUT (x, i), input, 1);
+ }
+ }
+}
+
/* Where possible, substitute every register reference in the N_SETS
- number of SETS in INSN with the the canonical register.
+ number of SETS in INSN with the canonical register.
Register canonicalization propagatest the earliest register (i.e.
one that is set before INSN) with the same value. This is a very
/* Canonicalize a USE of a pseudo register or memory location. */
canon_reg (x, insn);
else if (GET_CODE (x) == ASM_OPERANDS)
- {
- for (i = ASM_OPERANDS_INPUT_LENGTH (x) - 1; i >= 0; i--)
- {
- rtx input = ASM_OPERANDS_INPUT (x, i);
- if (!(REG_P (input) && REGNO (input) < FIRST_PSEUDO_REGISTER))
- {
- input = canon_reg (input, insn);
- validate_change (insn, &ASM_OPERANDS_INPUT (x, i), input, 1);
- }
- }
- }
+ canon_asm_operands (x, insn);
else if (GET_CODE (x) == CALL)
{
canon_reg (x, insn);
&& ! (REG_P (XEXP (y, 0))
&& REGNO (XEXP (y, 0)) < FIRST_PSEUDO_REGISTER))
canon_reg (y, insn);
+ else if (GET_CODE (y) == ASM_OPERANDS)
+ canon_asm_operands (y, insn);
else if (GET_CODE (y) == CALL)
{
canon_reg (y, insn);
canonicalize_insn (insn, &sets, n_sets);
/* If this insn has a REG_EQUAL note, store the equivalent value in SRC_EQV,
- if different, or if the DEST is a STRICT_LOW_PART. The latter condition
- is necessary because SRC_EQV is handled specially for this case, and if
- it isn't set, then there will be no equivalence for the destination. */
+ if different, or if the DEST is a STRICT_LOW_PART/ZERO_EXTRACT. The
+ latter condition is necessary because SRC_EQV is handled specially for
+ this case, and if it isn't set, then there will be no equivalence
+ for the destination. */
if (n_sets == 1 && REG_NOTES (insn) != 0
- && (tem = find_reg_note (insn, REG_EQUAL, NULL_RTX)) != 0
- && (! rtx_equal_p (XEXP (tem, 0), SET_SRC (sets[0].rtl))
- || GET_CODE (SET_DEST (sets[0].rtl)) == STRICT_LOW_PART))
- src_eqv = copy_rtx (XEXP (tem, 0));
+ && (tem = find_reg_note (insn, REG_EQUAL, NULL_RTX)) != 0)
+ {
+
+ if (GET_CODE (SET_DEST (sets[0].rtl)) != ZERO_EXTRACT
+ && (! rtx_equal_p (XEXP (tem, 0), SET_SRC (sets[0].rtl))
+ || GET_CODE (SET_DEST (sets[0].rtl)) == STRICT_LOW_PART))
+ src_eqv = copy_rtx (XEXP (tem, 0));
+ /* If DEST is of the form ZERO_EXTACT, as in:
+ (set (zero_extract:SI (reg:SI 119)
+ (const_int 16 [0x10])
+ (const_int 16 [0x10]))
+ (const_int 51154 [0xc7d2]))
+ REG_EQUAL note will specify the value of register (reg:SI 119) at this
+ point. Note that this is different from SRC_EQV. We can however
+ calculate SRC_EQV with the position and width of ZERO_EXTRACT. */
+ else if (GET_CODE (SET_DEST (sets[0].rtl)) == ZERO_EXTRACT
+ && CONST_INT_P (XEXP (tem, 0))
+ && CONST_INT_P (XEXP (SET_DEST (sets[0].rtl), 1))
+ && CONST_INT_P (XEXP (SET_DEST (sets[0].rtl), 2)))
+ {
+ rtx dest_reg = XEXP (SET_DEST (sets[0].rtl), 0);
+ /* This is the mode of XEXP (tem, 0) as well. */
+ scalar_int_mode dest_mode
+ = as_a <scalar_int_mode> (GET_MODE (dest_reg));
+ rtx width = XEXP (SET_DEST (sets[0].rtl), 1);
+ rtx pos = XEXP (SET_DEST (sets[0].rtl), 2);
+ HOST_WIDE_INT val = INTVAL (XEXP (tem, 0));
+ HOST_WIDE_INT mask;
+ unsigned int shift;
+ if (BITS_BIG_ENDIAN)
+ shift = (GET_MODE_PRECISION (dest_mode)
+ - INTVAL (pos) - INTVAL (width));
+ else
+ shift = INTVAL (pos);
+ if (INTVAL (width) == HOST_BITS_PER_WIDE_INT)
+ mask = HOST_WIDE_INT_M1;
+ else
+ mask = (HOST_WIDE_INT_1 << INTVAL (width)) - 1;
+ val = (val >> shift) & mask;
+ src_eqv = GEN_INT (val);
+ }
+ }
/* Set sets[i].src_elt to the class each source belongs to.
Detect assignments from or to volatile things
for (i = 0; i < n_sets; i++)
{
bool repeat = false;
+ bool noop_insn = false;
rtx src, dest;
rtx src_folded;
struct table_elt *elt = 0, *p;
/* Set nonzero if we need to call force_const_mem on with the
contents of src_folded before using it. */
int src_folded_force_flag = 0;
+ scalar_int_mode int_mode;
dest = SET_DEST (sets[i].rtl);
src = SET_SRC (sets[i].rtl);
/* Simplify and foldable subexpressions in SRC. Then get the fully-
simplified result, which may not necessarily be valid. */
- src_folded = fold_rtx (src, insn);
+ src_folded = fold_rtx (src, NULL);
#if 0
/* ??? This caused bad code to be generated for the m68k port with -O2.
&& INTVAL (width) < HOST_BITS_PER_WIDE_INT
&& (INTVAL (src) & ((HOST_WIDE_INT) (-1) << INTVAL (width))))
src_folded
- = GEN_INT (INTVAL (src) & (((HOST_WIDE_INT) 1
+ = GEN_INT (INTVAL (src) & ((HOST_WIDE_INT_1
<< INTVAL (width)) - 1));
}
#endif
/* Compute SRC's hash code, and also notice if it
should not be recorded at all. In that case,
- prevent any further processing of this assignment. */
- do_not_record = 0;
+ prevent any further processing of this assignment.
+
+ We set DO_NOT_RECORD if the destination has a REG_UNUSED note.
+ This avoids getting the source register into the tables, where it
+ may be invalidated later (via REG_QTY), then trigger an ICE upon
+ re-insertion.
+
+ This is only a problem in multi-set insns. If it were a single
+ set the dead copy would have been removed. If the RHS were anything
+ but a simple REG, then we won't call insert_regs and thus there's
+ no potential for triggering the ICE. */
+ do_not_record = (REG_P (dest)
+ && REG_P (src)
+ && find_reg_note (insn, REG_UNUSED, dest));
hash_arg_in_memory = 0;
sets[i].src = src;
wider mode. */
if (src_const && src_related == 0 && CONST_INT_P (src_const)
- && GET_MODE_CLASS (mode) == MODE_INT
- && GET_MODE_PRECISION (mode) < BITS_PER_WORD)
+ && is_int_mode (mode, &int_mode)
+ && GET_MODE_PRECISION (int_mode) < BITS_PER_WORD)
{
- machine_mode wider_mode;
-
- for (wider_mode = GET_MODE_WIDER_MODE (mode);
- wider_mode != VOIDmode
- && GET_MODE_PRECISION (wider_mode) <= BITS_PER_WORD
- && src_related == 0;
- wider_mode = GET_MODE_WIDER_MODE (wider_mode))
+ opt_scalar_int_mode wider_mode_iter;
+ FOR_EACH_WIDER_MODE (wider_mode_iter, int_mode)
{
+ scalar_int_mode wider_mode = wider_mode_iter.require ();
+ if (GET_MODE_PRECISION (wider_mode) > BITS_PER_WORD)
+ break;
+
struct table_elt *const_elt
= lookup (src_const, HASH (src_const, wider_mode), wider_mode);
const_elt; const_elt = const_elt->next_same_value)
if (REG_P (const_elt->exp))
{
- src_related = gen_lowpart (mode, const_elt->exp);
+ src_related = gen_lowpart (int_mode, const_elt->exp);
break;
}
+
+ if (src_related != 0)
+ break;
}
}
value. */
if (flag_expensive_optimizations && ! src_related
+ && is_a <scalar_int_mode> (mode, &int_mode)
&& GET_CODE (src) == AND && CONST_INT_P (XEXP (src, 1))
- && GET_MODE_SIZE (mode) < UNITS_PER_WORD)
+ && GET_MODE_SIZE (int_mode) < UNITS_PER_WORD)
{
- machine_mode tmode;
+ opt_scalar_int_mode tmode_iter;
rtx new_and = gen_rtx_AND (VOIDmode, NULL_RTX, XEXP (src, 1));
- for (tmode = GET_MODE_WIDER_MODE (mode);
- GET_MODE_SIZE (tmode) <= UNITS_PER_WORD;
- tmode = GET_MODE_WIDER_MODE (tmode))
+ FOR_EACH_WIDER_MODE (tmode_iter, int_mode)
{
+ scalar_int_mode tmode = tmode_iter.require ();
+ if (GET_MODE_SIZE (tmode) > UNITS_PER_WORD)
+ break;
+
rtx inner = gen_lowpart (tmode, XEXP (src, 0));
struct table_elt *larger_elt;
if (REG_P (larger_elt->exp))
{
src_related
- = gen_lowpart (mode, larger_elt->exp);
+ = gen_lowpart (int_mode, larger_elt->exp);
break;
}
}
}
-#ifdef LOAD_EXTEND_OP
/* See if a MEM has already been loaded with a widening operation;
if it has, we can use a subreg of that. Many CISC machines
also have such operations, but this is only likely to be
beneficial on these machines. */
+ rtx_code extend_op;
if (flag_expensive_optimizations && src_related == 0
- && (GET_MODE_SIZE (mode) < UNITS_PER_WORD)
- && GET_MODE_CLASS (mode) == MODE_INT
&& MEM_P (src) && ! do_not_record
- && LOAD_EXTEND_OP (mode) != UNKNOWN)
+ && is_a <scalar_int_mode> (mode, &int_mode)
+ && (extend_op = load_extend_op (int_mode)) != UNKNOWN)
{
struct rtx_def memory_extend_buf;
rtx memory_extend_rtx = &memory_extend_buf;
- machine_mode tmode;
/* Set what we are trying to extend and the operation it might
have been extended with. */
memset (memory_extend_rtx, 0, sizeof (*memory_extend_rtx));
- PUT_CODE (memory_extend_rtx, LOAD_EXTEND_OP (mode));
+ PUT_CODE (memory_extend_rtx, extend_op);
XEXP (memory_extend_rtx, 0) = src;
- for (tmode = GET_MODE_WIDER_MODE (mode);
- GET_MODE_SIZE (tmode) <= UNITS_PER_WORD;
- tmode = GET_MODE_WIDER_MODE (tmode))
+ opt_scalar_int_mode tmode_iter;
+ FOR_EACH_WIDER_MODE (tmode_iter, int_mode)
{
struct table_elt *larger_elt;
+ scalar_int_mode tmode = tmode_iter.require ();
+ if (GET_MODE_SIZE (tmode) > UNITS_PER_WORD)
+ break;
+
PUT_MODE (memory_extend_rtx, tmode);
larger_elt = lookup (memory_extend_rtx,
HASH (memory_extend_rtx, tmode), tmode);
larger_elt; larger_elt = larger_elt->next_same_value)
if (REG_P (larger_elt->exp))
{
- src_related = gen_lowpart (mode, larger_elt->exp);
+ src_related = gen_lowpart (int_mode, larger_elt->exp);
break;
}
break;
}
}
-#endif /* LOAD_EXTEND_OP */
/* Try to express the constant using a register+offset expression
derived from a constant anchor. */
&& ! (src != 0
&& GET_CODE (src) == SUBREG
&& GET_MODE (src) == GET_MODE (p->exp)
- && (GET_MODE_SIZE (GET_MODE (SUBREG_REG (src)))
- < GET_MODE_SIZE (GET_MODE (SUBREG_REG (p->exp))))))
+ && partial_subreg_p (GET_MODE (SUBREG_REG (src)),
+ GET_MODE (SUBREG_REG (p->exp)))))
continue;
if (src && GET_CODE (src) == code && rtx_equal_p (src, p->exp))
to prefer it. Copy it to src_related. The code below will
then give it a negative cost. */
if (GET_CODE (dest) == code && rtx_equal_p (p->exp, dest))
- src_related = dest;
+ src_related = p->exp;
}
/* Find the cheapest valid equivalent, trying all the available
src_cost = src_regcost = -1;
else
{
- src_cost = COST (src);
+ src_cost = COST (src, mode);
src_regcost = approx_reg_cost (src);
}
}
src_eqv_cost = src_eqv_regcost = -1;
else
{
- src_eqv_cost = COST (src_eqv_here);
+ src_eqv_cost = COST (src_eqv_here, mode);
src_eqv_regcost = approx_reg_cost (src_eqv_here);
}
}
src_folded_cost = src_folded_regcost = -1;
else
{
- src_folded_cost = COST (src_folded);
+ src_folded_cost = COST (src_folded, mode);
src_folded_regcost = approx_reg_cost (src_folded);
}
}
src_related_cost = src_related_regcost = -1;
else
{
- src_related_cost = COST (src_related);
+ src_related_cost = COST (src_related, mode);
src_related_regcost = approx_reg_cost (src_related);
/* If a const-anchor is used to synthesize a constant that
&& ! (src != 0
&& GET_CODE (src) == SUBREG
&& GET_MODE (src) == GET_MODE (elt->exp)
- && (GET_MODE_SIZE (GET_MODE (SUBREG_REG (src)))
- < GET_MODE_SIZE (GET_MODE (SUBREG_REG (elt->exp))))))
+ && partial_subreg_p (GET_MODE (SUBREG_REG (src)),
+ GET_MODE (SUBREG_REG (elt->exp)))))
{
elt = elt->next_same_value;
continue;
src_elt_cost = MAX_COST;
}
- /* Avoid creation of overlapping memory moves. */
- if (MEM_P (trial) && MEM_P (SET_DEST (sets[i].rtl)))
- {
- rtx src, dest;
-
- /* BLKmode moves are not handled by cse anyway. */
- if (GET_MODE (trial) == BLKmode)
- break;
-
- src = canon_rtx (trial);
- dest = canon_rtx (SET_DEST (sets[i].rtl));
-
- if (!MEM_P (src) || !MEM_P (dest)
- || !nonoverlapping_memrefs_p (src, dest, false))
- break;
- }
-
/* Try to optimize
(set (reg:M N) (const_int A))
(set (reg:M2 O) (const_int B))
&& CONST_INT_P (XEXP (SET_DEST (sets[i].rtl), 1))
&& CONST_INT_P (XEXP (SET_DEST (sets[i].rtl), 2))
&& REG_P (XEXP (SET_DEST (sets[i].rtl), 0))
- && (GET_MODE_PRECISION (GET_MODE (SET_DEST (sets[i].rtl)))
- >= INTVAL (XEXP (SET_DEST (sets[i].rtl), 1)))
+ && (known_ge
+ (GET_MODE_PRECISION (GET_MODE (SET_DEST (sets[i].rtl))),
+ INTVAL (XEXP (SET_DEST (sets[i].rtl), 1))))
&& ((unsigned) INTVAL (XEXP (SET_DEST (sets[i].rtl), 1))
+ (unsigned) INTVAL (XEXP (SET_DEST (sets[i].rtl), 2))
<= HOST_BITS_PER_WIDE_INT))
HOST_WIDE_INT val = INTVAL (dest_cst);
HOST_WIDE_INT mask;
unsigned int shift;
+ /* This is the mode of DEST_CST as well. */
+ scalar_int_mode dest_mode
+ = as_a <scalar_int_mode> (GET_MODE (dest_reg));
if (BITS_BIG_ENDIAN)
- shift = GET_MODE_PRECISION (GET_MODE (dest_reg))
+ shift = GET_MODE_PRECISION (dest_mode)
- INTVAL (pos) - INTVAL (width);
else
shift = INTVAL (pos);
if (INTVAL (width) == HOST_BITS_PER_WIDE_INT)
- mask = ~(HOST_WIDE_INT) 0;
+ mask = HOST_WIDE_INT_M1;
else
- mask = ((HOST_WIDE_INT) 1 << INTVAL (width)) - 1;
+ mask = (HOST_WIDE_INT_1 << INTVAL (width)) - 1;
val &= ~(mask << shift);
val |= (INTVAL (trial) & mask) << shift;
- val = trunc_int_for_mode (val, GET_MODE (dest_reg));
+ val = trunc_int_for_mode (val, dest_mode);
validate_unshare_change (insn, &SET_DEST (sets[i].rtl),
dest_reg, 1);
validate_unshare_change (insn, &SET_SRC (sets[i].rtl),
break;
}
+ /* Similarly, lots of targets don't allow no-op
+ (set (mem x) (mem x)) moves. Even (set (reg x) (reg x))
+ might be impossible for certain registers (like CC registers). */
+ else if (n_sets == 1
+ && !CALL_P (insn)
+ && (MEM_P (trial) || REG_P (trial))
+ && rtx_equal_p (trial, dest)
+ && !side_effects_p (dest)
+ && (cfun->can_delete_dead_exceptions
+ || insn_nothrow_p (insn))
+ /* We can only remove the later store if the earlier aliases
+ at least all accesses the later one. */
+ && (!MEM_P (trial)
+ || ((MEM_ALIAS_SET (dest) == MEM_ALIAS_SET (trial)
+ || alias_set_subset_of (MEM_ALIAS_SET (dest),
+ MEM_ALIAS_SET (trial)))
+ && (!MEM_EXPR (trial)
+ || refs_same_for_tbaa_p (MEM_EXPR (trial),
+ MEM_EXPR (dest))))))
+ {
+ SET_SRC (sets[i].rtl) = trial;
+ noop_insn = true;
+ break;
+ }
+
/* Reject certain invalid forms of CONST that we create. */
else if (CONSTANT_P (trial)
&& GET_CODE (trial) == CONST
/* Do nothing for this case. */
;
+ /* Do not replace anything with a MEM, except the replacement
+ is a no-op. This allows this loop to terminate. */
+ else if (MEM_P (trial) && !rtx_equal_p (trial, SET_SRC(sets[i].rtl)))
+ /* Do nothing for this case. */
+ ;
+
/* Look for a substitution that makes a valid insn. */
else if (validate_unshare_change (insn, &SET_SRC (sets[i].rtl),
trial, 0))
/* If we had a constant that is cheaper than what we are now
setting SRC to, use that constant. We ignored it when we
thought we could make this into a no-op. */
- if (src_const && COST (src_const) < COST (src)
+ if (src_const && COST (src_const, mode) < COST (src, mode)
&& validate_change (insn, &SET_SRC (sets[i].rtl),
src_const, 0))
src = src_const;
else if (n_sets == 1 && dest == pc_rtx && src == pc_rtx)
{
/* One less use of the label this insn used to jump to. */
- delete_insn_and_edges (insn);
+ cse_cfg_altered |= delete_insn_and_edges (insn);
cse_jumps_altered = true;
/* No more processing for this set. */
sets[i].rtl = 0;
}
+ /* Similarly for no-op moves. */
+ else if (noop_insn)
+ {
+ if (cfun->can_throw_non_call_exceptions && can_throw_internal (insn))
+ cse_cfg_altered = true;
+ cse_cfg_altered |= delete_insn_and_edges (insn);
+ /* No more processing for this set. */
+ sets[i].rtl = 0;
+ }
+
/* If this SET is now setting PC to a label, we know it used to
be a conditional or computed branch. */
else if (dest == pc_rtx && GET_CODE (src) == LABEL_REF
and hope for the best. */
if (n_sets == 1)
{
- rtx_insn *new_rtx;
+ rtx_jump_insn *new_rtx;
rtx note;
- new_rtx = emit_jump_insn_before (gen_jump (XEXP (src, 0)), insn);
+ rtx_insn *seq = targetm.gen_jump (XEXP (src, 0));
+ new_rtx = emit_jump_insn_before (seq, insn);
JUMP_LABEL (new_rtx) = XEXP (src, 0);
LABEL_NUSES (XEXP (src, 0))++;
REG_NOTES (new_rtx) = note;
}
- delete_insn_and_edges (insn);
+ cse_cfg_altered |= delete_insn_and_edges (insn);
insn = new_rtx;
}
else
{
if (!(RTL_CONST_OR_PURE_CALL_P (insn)))
invalidate_memory ();
- invalidate_for_call ();
+ else
+ /* For const/pure calls, invalidate any argument slots, because
+ those are owned by the callee. */
+ for (tem = CALL_INSN_FUNCTION_USAGE (insn); tem; tem = XEXP (tem, 1))
+ if (GET_CODE (XEXP (tem, 0)) == USE
+ && MEM_P (XEXP (XEXP (tem, 0), 0)))
+ invalidate (XEXP (XEXP (tem, 0), 0), VOIDmode);
+ invalidate_for_call (insn);
}
/* Now invalidate everything set by this instruction.
|| GET_MODE (dest) == BLKmode
/* If we didn't put a REG_EQUAL value or a source into the hash
table, there is no point is recording DEST. */
- || sets[i].src_elt == 0
- /* If DEST is a paradoxical SUBREG and SRC is a ZERO_EXTEND
- or SIGN_EXTEND, don't record DEST since it can cause
- some tracking to be wrong.
-
- ??? Think about this more later. */
- || (paradoxical_subreg_p (dest)
- && (GET_CODE (sets[i].src) == SIGN_EXTEND
- || GET_CODE (sets[i].src) == ZERO_EXTEND)))
+ || sets[i].src_elt == 0)
continue;
/* STRICT_LOW_PART isn't part of the value BEING set,
sets[i].dest_hash = HASH (dest, GET_MODE (dest));
}
+ /* If DEST is a paradoxical SUBREG, don't record DEST since the bits
+ outside the mode of GET_MODE (SUBREG_REG (dest)) are undefined. */
+ if (paradoxical_subreg_p (dest))
+ continue;
+
elt = insert (dest, sets[i].src_elt,
sets[i].dest_hash, GET_MODE (dest));
already entered SRC and DEST of the SET in the table. */
if (GET_CODE (dest) == SUBREG
- && (((GET_MODE_SIZE (GET_MODE (SUBREG_REG (dest))) - 1)
- / UNITS_PER_WORD)
- == (GET_MODE_SIZE (GET_MODE (dest)) - 1) / UNITS_PER_WORD)
- && (GET_MODE_SIZE (GET_MODE (dest))
- >= GET_MODE_SIZE (GET_MODE (SUBREG_REG (dest))))
+ && (known_equal_after_align_down
+ (GET_MODE_SIZE (GET_MODE (SUBREG_REG (dest))) - 1,
+ GET_MODE_SIZE (GET_MODE (dest)) - 1,
+ UNITS_PER_WORD))
+ && !partial_subreg_p (dest)
&& sets[i].src_elt != 0)
{
machine_mode new_mode = GET_MODE (SUBREG_REG (dest));
rtx new_src = 0;
unsigned src_hash;
struct table_elt *src_elt;
- int byte = 0;
/* Ignore invalid entries. */
if (!REG_P (elt->exp)
new_src = elt->exp;
else
{
- /* Calculate big endian correction for the SUBREG_BYTE.
- We have already checked that M1 (GET_MODE (dest))
- is not narrower than M2 (new_mode). */
- if (BYTES_BIG_ENDIAN)
- byte = (GET_MODE_SIZE (GET_MODE (dest))
- - GET_MODE_SIZE (new_mode));
-
+ poly_uint64 byte
+ = subreg_lowpart_offset (new_mode, GET_MODE (dest));
new_src = simplify_gen_subreg (new_mode, elt->exp,
GET_MODE (dest), byte);
}
if (CALL_P (insn))
{
for (tem = CALL_INSN_FUNCTION_USAGE (insn); tem; tem = XEXP (tem, 1))
- if (GET_CODE (XEXP (tem, 0)) == CLOBBER)
- invalidate (SET_DEST (XEXP (tem, 0)), VOIDmode);
+ {
+ rtx temx = XEXP (tem, 0);
+ if (GET_CODE (temx) == CLOBBER)
+ invalidate (SET_DEST (temx), VOIDmode);
+ }
}
/* Ensure we invalidate the destination register of a CALL insn.
}
}
\f
-/* Process X, part of the REG_NOTES of an insn. Look at any REG_EQUAL notes
- and replace any registers in them with either an equivalent constant
- or the canonical form of the register. If we are inside an address,
- only do this if the address remains valid.
+static rtx cse_process_note (rtx);
- OBJECT is 0 except when within a MEM in which case it is the MEM.
+/* A simplify_replace_fn_rtx callback for cse_process_note. Process X,
+ part of the REG_NOTES of an insn. Replace any registers with either
+ an equivalent constant or the canonical form of the register.
+ Only replace addresses if the containing MEM remains valid.
- Return the replacement for X. */
+ Return the replacement for X, or null if it should be simplified
+ recursively. */
static rtx
-cse_process_notes_1 (rtx x, rtx object, bool *changed)
+cse_process_note_1 (rtx x, const_rtx, void *)
{
- enum rtx_code code = GET_CODE (x);
- const char *fmt = GET_RTX_FORMAT (code);
- int i;
-
- switch (code)
+ if (MEM_P (x))
{
- case CONST:
- case SYMBOL_REF:
- case LABEL_REF:
- CASE_CONST_ANY:
- case PC:
- case CC0:
- case LO_SUM:
+ validate_change (x, &XEXP (x, 0), cse_process_note (XEXP (x, 0)), false);
return x;
+ }
- case MEM:
- validate_change (x, &XEXP (x, 0),
- cse_process_notes (XEXP (x, 0), x, changed), 0);
- return x;
-
- case EXPR_LIST:
- if (REG_NOTE_KIND (x) == REG_EQUAL)
- XEXP (x, 0) = cse_process_notes (XEXP (x, 0), NULL_RTX, changed);
- /* Fall through. */
-
- case INSN_LIST:
- case INT_LIST:
- if (XEXP (x, 1))
- XEXP (x, 1) = cse_process_notes (XEXP (x, 1), NULL_RTX, changed);
- return x;
-
- case SIGN_EXTEND:
- case ZERO_EXTEND:
- case SUBREG:
- {
- rtx new_rtx = cse_process_notes (XEXP (x, 0), object, changed);
- /* We don't substitute VOIDmode constants into these rtx,
- since they would impede folding. */
- if (GET_MODE (new_rtx) != VOIDmode)
- validate_change (object, &XEXP (x, 0), new_rtx, 0);
- return x;
- }
-
- case UNSIGNED_FLOAT:
- {
- rtx new_rtx = cse_process_notes (XEXP (x, 0), object, changed);
- /* We don't substitute negative VOIDmode constants into these rtx,
- since they would impede folding. */
- if (GET_MODE (new_rtx) != VOIDmode
- || (CONST_INT_P (new_rtx) && INTVAL (new_rtx) >= 0)
- || (CONST_DOUBLE_P (new_rtx) && CONST_DOUBLE_HIGH (new_rtx) >= 0))
- validate_change (object, &XEXP (x, 0), new_rtx, 0);
- return x;
- }
-
- case REG:
- i = REG_QTY (REGNO (x));
+ if (REG_P (x))
+ {
+ int i = REG_QTY (REGNO (x));
/* Return a constant or a constant register. */
if (REGNO_QTY_VALID_P (REGNO (x)))
/* Otherwise, canonicalize this register. */
return canon_reg (x, NULL);
-
- default:
- break;
}
- for (i = 0; i < GET_RTX_LENGTH (code); i++)
- if (fmt[i] == 'e')
- validate_change (object, &XEXP (x, i),
- cse_process_notes (XEXP (x, i), object, changed), 0);
-
- return x;
+ return NULL_RTX;
}
+/* Process X, part of the REG_NOTES of an insn. Replace any registers in it
+ with either an equivalent constant or the canonical form of the register.
+ Only replace addresses if the containing MEM remains valid. */
+
static rtx
-cse_process_notes (rtx x, rtx object, bool *changed)
+cse_process_note (rtx x)
{
- rtx new_rtx = cse_process_notes_1 (x, object, changed);
- if (new_rtx != x)
- *changed = true;
- return new_rtx;
+ return simplify_replace_fn_rtx (x, NULL_RTX, cse_process_note_1, NULL);
}
\f
if (follow_jumps)
{
bb = data->path[path_size - 1].bb;
- while (bb && path_size < PARAM_VALUE (PARAM_MAX_CSE_PATH_LENGTH))
+ while (bb && path_size < param_max_cse_path_length)
{
if (single_succ_p (bb))
e = single_succ_edge (bb);
fprintf (f, ";; Following path with %d sets: ", nsets);
for (path_entry = 0; path_entry < data->path_size; path_entry++)
fprintf (f, "%d ", (data->path[path_entry].bb)->index);
- fputc ('\n', dump_file);
+ fputc ('\n', f);
fflush (f);
}
if (GET_CODE (x) == LABEL_REF
&& !LABEL_REF_NONLOCAL_P (x)
&& (!JUMP_P (insn)
- || !label_is_jump_target_p (LABEL_REF_LABEL (x), insn))
- && LABEL_P (LABEL_REF_LABEL (x))
- && INSN_UID (LABEL_REF_LABEL (x)) != 0
- && !find_reg_note (insn, REG_LABEL_OPERAND, LABEL_REF_LABEL (x)))
+ || !label_is_jump_target_p (label_ref_label (x), insn))
+ && LABEL_P (label_ref_label (x))
+ && INSN_UID (label_ref_label (x)) != 0
+ && !find_reg_note (insn, REG_LABEL_OPERAND, label_ref_label (x)))
return true;
}
return false;
FIXME: This is a real kludge and needs to be done some other
way. */
if (NONDEBUG_INSN_P (insn)
- && num_insns++ > PARAM_VALUE (PARAM_MAX_CSE_INSNS))
+ && num_insns++ > param_max_cse_insns)
{
flush_hash_table ();
num_insns = 0;
{
/* Process notes first so we have all notes in canonical forms
when looking for duplicate operations. */
- if (REG_NOTES (insn))
- {
- bool changed = false;
- REG_NOTES (insn) = cse_process_notes (REG_NOTES (insn),
- NULL_RTX, &changed);
- if (changed)
- df_notes_rescan (insn);
- }
+ bool changed = false;
+ for (rtx note = REG_NOTES (insn); note; note = XEXP (note, 1))
+ if (REG_NOTE_KIND (note) == REG_EQUAL)
+ {
+ rtx newval = cse_process_note (XEXP (note, 0));
+ if (newval != XEXP (note, 0))
+ {
+ XEXP (note, 0) = newval;
+ changed = true;
+ }
+ }
+ if (changed)
+ df_notes_rescan (insn);
cse_insn (insn);
&& check_for_label_ref (insn))
recorded_label_ref = true;
-#ifdef HAVE_cc0
- if (NONDEBUG_INSN_P (insn))
+ if (HAVE_cc0 && NONDEBUG_INSN_P (insn))
{
/* If the previous insn sets CC0 and this insn no
longer references CC0, delete the previous insn.
prev_insn_cc0_mode = this_insn_cc0_mode;
}
}
-#endif
}
}
equivalences due to the condition being tested. */
insn = BB_END (bb);
if (path_entry < path_size - 1
+ && EDGE_COUNT (bb->succs) == 2
&& JUMP_P (insn)
&& single_set (insn)
&& any_condjump_p (insn))
int *rc_order = XNEWVEC (int, last_basic_block_for_fn (cfun));
int i, n_blocks;
+ /* CSE doesn't use dominane info but can invalidate it in different ways.
+ For simplicity free dominance info here. */
+ free_dominance_info (CDI_DOMINATORS);
+
df_set_flags (DF_LR_RUN_DCE);
df_note_add_problem ();
df_analyze ();
init_cse_reg_info (nregs);
ebb_data.path = XNEWVEC (struct branch_path,
- PARAM_VALUE (PARAM_MAX_CSE_PATH_LENGTH));
+ param_max_cse_path_length);
cse_cfg_altered = false;
cse_jumps_altered = false;
set_live_p (rtx set, rtx_insn *insn ATTRIBUTE_UNUSED, /* Only used with HAVE_cc0. */
int *counts)
{
- rtx tem;
+ rtx_insn *tem;
if (set_noop_p (set))
;
{
rtx_insn *next;
+ if (DEBUG_MARKER_INSN_P (insn))
+ return true;
+
for (next = NEXT_INSN (insn); next; next = NEXT_INSN (next))
if (NOTE_P (next))
continue;
else if (!DEBUG_INSN_P (next))
return true;
+ /* If we find an inspection point, such as a debug begin stmt,
+ we want to keep the earlier debug insn. */
+ else if (DEBUG_MARKER_INSN_P (next))
+ return true;
else if (INSN_VAR_LOCATION_DECL (insn) == INSN_VAR_LOCATION_DECL (next))
return false;
timevar_push (TV_DELETE_TRIVIALLY_DEAD);
/* First count the number of times each register is used. */
- if (MAY_HAVE_DEBUG_INSNS)
+ if (MAY_HAVE_DEBUG_BIND_INSNS)
{
counts = XCNEWVEC (int, nreg * 3);
for (insn = insns; insn; insn = NEXT_INSN (insn))
- if (DEBUG_INSN_P (insn))
+ if (DEBUG_BIND_INSN_P (insn))
count_reg_usage (INSN_VAR_LOCATION_LOC (insn), counts + nreg,
NULL_RTX, 1);
else if (INSN_P (insn))
{
count_reg_usage (insn, counts, NULL_RTX, 1);
- note_stores (PATTERN (insn), count_stores, counts + nreg * 2);
+ note_stores (insn, count_stores, counts + nreg * 2);
}
/* If there can be debug insns, COUNTS are 3 consecutive arrays.
First one counts how many times each pseudo is used outside
if (! live_insn && dbg_cnt (delete_trivial_dead))
{
if (DEBUG_INSN_P (insn))
- count_reg_usage (INSN_VAR_LOCATION_LOC (insn), counts + nreg,
- NULL_RTX, -1);
+ {
+ if (DEBUG_BIND_INSN_P (insn))
+ count_reg_usage (INSN_VAR_LOCATION_LOC (insn), counts + nreg,
+ NULL_RTX, -1);
+ }
else
{
rtx set;
- if (MAY_HAVE_DEBUG_INSNS
+ if (MAY_HAVE_DEBUG_BIND_INSNS
&& (set = single_set (insn)) != NULL_RTX
&& is_dead_reg (SET_DEST (set), counts)
/* Used at least once in some DEBUG_INSN. */
count_reg_usage (insn, counts, NULL_RTX, -1);
ndead++;
}
- delete_insn_and_edges (insn);
+ cse_cfg_altered |= delete_insn_and_edges (insn);
}
}
- if (MAY_HAVE_DEBUG_INSNS)
+ if (MAY_HAVE_DEBUG_BIND_INSNS)
{
for (insn = get_last_insn (); insn; insn = PREV_INSN (insn))
- if (DEBUG_INSN_P (insn))
+ if (DEBUG_BIND_INSN_P (insn))
{
/* If this debug insn references a dead register that wasn't replaced
with an DEBUG_EXPR, reset the DEBUG_INSN. */
static void
cse_change_cc_mode (subrtx_ptr_iterator::array_type &array,
- rtx *loc, rtx insn, rtx newreg)
+ rtx *loc, rtx_insn *insn, rtx newreg)
{
FOR_EACH_SUBRTX_PTR (iter, array, loc, NONCONST)
{
newreg);
}
- delete_insn_and_edges (insns[i]);
+ cse_cfg_altered |= delete_insn_and_edges (insns[i]);
}
return mode;
{
timevar_push (TV_JUMP);
rebuild_jump_labels (get_insns ());
- cleanup_cfg (CLEANUP_CFG_CHANGED);
+ cse_cfg_altered |= cleanup_cfg (CLEANUP_CFG_CHANGED);
timevar_pop (TV_JUMP);
}
else if (tem == 1 || optimize > 1)
- cleanup_cfg (0);
+ cse_cfg_altered |= cleanup_cfg (0);
return 0;
}
{
timevar_push (TV_JUMP);
rebuild_jump_labels (get_insns ());
- cleanup_cfg (CLEANUP_CFG_CHANGED);
+ cse_cfg_altered |= cleanup_cfg (CLEANUP_CFG_CHANGED);
timevar_pop (TV_JUMP);
}
- else if (tem == 1)
- cleanup_cfg (0);
+ else if (tem == 1 || cse_cfg_altered)
+ cse_cfg_altered |= cleanup_cfg (0);
cse_not_expected = 1;
return 0;
rebuild_jump_labels (get_insns ());
tem = cse_main (get_insns (), max_reg_num ());
- purge_all_dead_edges ();
+ cse_cfg_altered |= purge_all_dead_edges ();
delete_trivially_dead_insns (get_insns (), max_reg_num ());
cse_not_expected = !flag_rerun_cse_after_loop;
{
timevar_push (TV_JUMP);
rebuild_jump_labels (get_insns ());
- cleanup_cfg (CLEANUP_CFG_CHANGED);
+ cse_cfg_altered |= cleanup_cfg (CLEANUP_CFG_CHANGED);
timevar_pop (TV_JUMP);
}
- else if (tem == 1)
- cleanup_cfg (0);
+ else if (tem == 1 || cse_cfg_altered)
+ cse_cfg_altered |= cleanup_cfg (0);
flag_cse_follow_jumps = save_cfj;
return 0;