/* Common subexpression elimination library for GNU compiler.
- Copyright (C) 1987-2014 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"/* FIXME: For hashing DEBUG_EXPR & friends. */
+#include "tree.h"
+#include "df.h"
+#include "memmodel.h"
#include "tm_p.h"
#include "regs.h"
-#include "hard-reg-set.h"
-#include "flags.h"
-#include "insn-config.h"
-#include "recog.h"
-#include "function.h"
#include "emit-rtl.h"
-#include "diagnostic-core.h"
-#include "ggc.h"
-#include "hash-table.h"
#include "dumpfile.h"
#include "cselib.h"
-#include "valtrack.h"
-#include "params.h"
-#include "alloc-pool.h"
-#include "target.h"
-#include "bitmap.h"
+#include "function-abi.h"
/* A list of cselib_val structures. */
-struct elt_list {
- struct elt_list *next;
- cselib_val *elt;
+struct elt_list
+{
+ struct elt_list *next;
+ cselib_val *elt;
};
-/* See the documentation of cselib_find_slot below. */
-static enum machine_mode find_slot_memmode;
-
static bool cselib_record_memory;
static bool cselib_preserve_constants;
static bool cselib_any_perm_equivs;
static void unchain_one_elt_list (struct elt_list **);
static void unchain_one_elt_loc_list (struct elt_loc_list **);
static void remove_useless_values (void);
-static int rtx_equal_for_cselib_1 (rtx, rtx, enum machine_mode);
-static unsigned int cselib_hash_rtx (rtx, int, enum machine_mode);
-static cselib_val *new_cselib_val (unsigned int, enum machine_mode, rtx);
+static unsigned int cselib_hash_rtx (rtx, int, machine_mode);
+static cselib_val *new_cselib_val (unsigned int, machine_mode, rtx);
static void add_mem_for_addr (cselib_val *, cselib_val *, rtx);
static cselib_val *cselib_lookup_mem (rtx, int);
-static void cselib_invalidate_regno (unsigned int, enum machine_mode);
+static void cselib_invalidate_regno (unsigned int, machine_mode);
static void cselib_invalidate_mem (rtx);
static void cselib_record_set (rtx, cselib_val *, cselib_val *);
-static void cselib_record_sets (rtx);
+static void cselib_record_sets (rtx_insn *);
struct expand_value_data
{
this involves walking the table entries for a given value and comparing
the locations of the entries with the rtx we are looking up. */
-struct cselib_hasher : typed_noop_remove <cselib_val>
+struct cselib_hasher : nofree_ptr_hash <cselib_val>
{
- typedef cselib_val value_type;
- typedef rtx_def compare_type;
- static inline hashval_t hash (const value_type *);
- static inline bool equal (const value_type *, const compare_type *);
+ struct key {
+ /* The rtx value and its mode (needed separately for constant
+ integers). */
+ machine_mode mode;
+ rtx x;
+ /* The mode of the contaning MEM, if any, otherwise VOIDmode. */
+ machine_mode memmode;
+ };
+ typedef key *compare_type;
+ static inline hashval_t hash (const cselib_val *);
+ static inline bool equal (const cselib_val *, const key *);
};
/* The hash function for our hash table. The value is always computed with
hash value from a cselib_val structure. */
inline hashval_t
-cselib_hasher::hash (const value_type *v)
+cselib_hasher::hash (const cselib_val *v)
{
return v->hash;
}
CONST of an appropriate mode. */
inline bool
-cselib_hasher::equal (const value_type *v, const compare_type *x_arg)
+cselib_hasher::equal (const cselib_val *v, const key *x_arg)
{
struct elt_loc_list *l;
- rtx x = CONST_CAST_RTX (x_arg);
- enum machine_mode mode = GET_MODE (x);
-
- gcc_assert (!CONST_SCALAR_INT_P (x) && GET_CODE (x) != CONST_FIXED);
+ rtx x = x_arg->x;
+ machine_mode mode = x_arg->mode;
+ machine_mode memmode = x_arg->memmode;
if (mode != GET_MODE (v->val_rtx))
return false;
- /* Unwrap X if necessary. */
- if (GET_CODE (x) == CONST
- && (CONST_SCALAR_INT_P (XEXP (x, 0))
- || GET_CODE (XEXP (x, 0)) == CONST_FIXED))
- x = XEXP (x, 0);
-
if (GET_CODE (x) == VALUE)
return x == v->val_rtx;
/* We don't guarantee that distinct rtx's have different hash values,
so we need to do a comparison. */
for (l = v->locs; l; l = l->next)
- if (rtx_equal_for_cselib_1 (l->loc, x, find_slot_memmode))
+ if (rtx_equal_for_cselib_1 (l->loc, x, memmode, 0))
{
promote_debug_loc (l);
return true;
}
/* A table that enables us to look up elts by their value. */
-static hash_table <cselib_hasher> cselib_hash_table;
+static hash_table<cselib_hasher> *cselib_hash_table;
/* A table to hold preserved values. */
-static hash_table <cselib_hasher> cselib_preserved_hash_table;
+static hash_table<cselib_hasher> *cselib_preserved_hash_table;
/* This is a global so we don't have to pass this through every function.
It is used in new_elt_loc_list to set SETTING_INSN. */
-static rtx cselib_current_insn;
+static rtx_insn *cselib_current_insn;
/* The unique id that the next create value will take. */
static unsigned int next_uid;
May or may not contain the useless values - the list is compacted
each time memory is invalidated. */
static cselib_val *first_containing_mem = &dummy_val;
-static alloc_pool elt_loc_list_pool, elt_list_pool, cselib_val_pool, value_pool;
+
+static object_allocator<elt_list> elt_list_pool ("elt_list");
+static object_allocator<elt_loc_list> elt_loc_list_pool ("elt_loc_list");
+static object_allocator<cselib_val> cselib_val_pool ("cselib_val_list");
+
+static pool_allocator value_pool ("value", RTX_CODE_SIZE (VALUE));
/* If nonnull, cselib will call this function before freeing useless
VALUEs. A VALUE is deemed useless if its "locs" field is null. */
represented in the array sets[n_sets]. new_val_min can be used to
tell whether values present in sets are introduced by this
instruction. */
-void (*cselib_record_sets_hook) (rtx insn, struct cselib_set *sets,
+void (*cselib_record_sets_hook) (rtx_insn *insn, struct cselib_set *sets,
int n_sets);
#define PRESERVED_VALUE_P(RTX) \
static inline struct elt_list *
new_elt_list (struct elt_list *next, cselib_val *elt)
{
- struct elt_list *el;
- el = (struct elt_list *) pool_alloc (elt_list_pool);
+ elt_list *el = elt_list_pool.allocate ();
el->next = next;
el->elt = elt;
return el;
}
/* Chain LOC back to VAL. */
- el = (struct elt_loc_list *) pool_alloc (elt_loc_list_pool);
+ el = elt_loc_list_pool.allocate ();
el->loc = val->val_rtx;
el->setting_insn = cselib_current_insn;
el->next = NULL;
CSELIB_VAL_PTR (loc)->locs = el;
}
- el = (struct elt_loc_list *) pool_alloc (elt_loc_list_pool);
+ el = elt_loc_list_pool.allocate ();
el->loc = loc;
el->setting_insn = cselib_current_insn;
el->next = next;
struct elt_list *l = *pl;
*pl = l->next;
- pool_free (elt_list_pool, l);
+ elt_list_pool.remove (l);
}
/* Likewise for elt_loc_lists. */
struct elt_loc_list *l = *pl;
*pl = l->next;
- pool_free (elt_loc_list_pool, l);
+ elt_loc_list_pool.remove (l);
}
/* Likewise for cselib_vals. This also frees the addr_list associated with
while (v->addr_list)
unchain_one_elt_list (&v->addr_list);
- pool_free (cselib_val_pool, v);
+ cselib_val_pool.remove (v);
}
/* Remove all entries from the hash table. Also used during
if (invariant_or_equiv_p (v))
{
+ cselib_hasher::key lookup = {
+ GET_MODE (v->val_rtx), v->val_rtx, VOIDmode
+ };
cselib_val **slot
- = cselib_preserved_hash_table.find_slot_with_hash (v->val_rtx,
+ = cselib_preserved_hash_table->find_slot_with_hash (&lookup,
v->hash, INSERT);
gcc_assert (!*slot);
*slot = v;
}
- cselib_hash_table.clear_slot (x);
+ cselib_hash_table->clear_slot (x);
return 1;
}
n_used_regs = new_used_regs;
used_regs[0] = regno;
max_value_regs
- = hard_regno_nregs[regno][GET_MODE (cfa_base_preserved_val->locs->loc)];
+ = hard_regno_nregs (regno,
+ GET_MODE (cfa_base_preserved_val->locs->loc));
}
else
{
}
if (cselib_preserve_constants)
- cselib_hash_table.traverse <void *, preserve_constants_and_equivs> (NULL);
+ cselib_hash_table->traverse <void *, preserve_constants_and_equivs>
+ (NULL);
else
{
- cselib_hash_table.empty ();
+ cselib_hash_table->empty ();
gcc_checking_assert (!cselib_any_perm_equivs);
}
/* Search for X, whose hashcode is HASH, in CSELIB_HASH_TABLE,
INSERTing if requested. When X is part of the address of a MEM,
- MEMMODE should specify the mode of the MEM. While searching the
- table, MEMMODE is held in FIND_SLOT_MEMMODE, so that autoinc RTXs
- in X can be resolved. */
+ MEMMODE should specify the mode of the MEM. */
static cselib_val **
-cselib_find_slot (rtx x, hashval_t hash, enum insert_option insert,
- enum machine_mode memmode)
+cselib_find_slot (machine_mode mode, rtx x, hashval_t hash,
+ enum insert_option insert, machine_mode memmode)
{
cselib_val **slot = NULL;
- find_slot_memmode = memmode;
+ cselib_hasher::key lookup = { mode, x, memmode };
if (cselib_preserve_constants)
- slot = cselib_preserved_hash_table.find_slot_with_hash (x, hash,
- NO_INSERT);
+ slot = cselib_preserved_hash_table->find_slot_with_hash (&lookup, hash,
+ NO_INSERT);
if (!slot)
- slot = cselib_hash_table.find_slot_with_hash (x, hash, insert);
- find_slot_memmode = VOIDmode;
+ slot = cselib_hash_table->find_slot_with_hash (&lookup, hash, insert);
return slot;
}
cselib_val *v = *x;
struct elt_loc_list **p = &v->locs;
bool had_locs = v->locs != NULL;
- rtx setting_insn = v->locs ? v->locs->setting_insn : NULL;
+ rtx_insn *setting_insn = v->locs ? v->locs->setting_insn : NULL;
while (*p)
{
cselib_discard_hook (v);
CSELIB_VAL_PTR (v->val_rtx) = NULL;
- cselib_hash_table.clear_slot (x);
+ cselib_hash_table->clear_slot (x);
unchain_one_value (v);
n_useless_values--;
}
do
{
values_became_useless = 0;
- cselib_hash_table.traverse <void *, discard_useless_locs> (NULL);
+ cselib_hash_table->traverse <void *, discard_useless_locs> (NULL);
}
while (values_became_useless);
n_debug_values -= n_useless_debug_values;
n_useless_debug_values = 0;
- cselib_hash_table.traverse <void *, discard_useless_values> (NULL);
+ cselib_hash_table->traverse <void *, discard_useless_values> (NULL);
gcc_assert (!n_useless_values);
}
set is not known, or the value was already clobbered, return
VOIDmode. */
-enum machine_mode
+machine_mode
cselib_reg_set_mode (const_rtx x)
{
if (!REG_P (x))
return GET_MODE (REG_VALUES (REGNO (x))->elt->val_rtx);
}
-/* Return nonzero if we can prove that X and Y contain the same value, taking
- our gathered information into account. */
-
-int
-rtx_equal_for_cselib_p (rtx x, rtx y)
-{
- return rtx_equal_for_cselib_1 (x, y, VOIDmode);
-}
-
/* If x is a PLUS or an autoinc operation, expand the operation,
storing the offset, if any, in *OFF. */
static rtx
-autoinc_split (rtx x, rtx *off, enum machine_mode memmode)
+autoinc_split (rtx x, rtx *off, machine_mode memmode)
{
switch (GET_CODE (x))
{
if (memmode == VOIDmode)
return x;
- *off = GEN_INT (-GET_MODE_SIZE (memmode));
+ *off = gen_int_mode (-GET_MODE_SIZE (memmode), GET_MODE (x));
return XEXP (x, 0);
- break;
case PRE_INC:
if (memmode == VOIDmode)
return x;
- *off = GEN_INT (GET_MODE_SIZE (memmode));
+ *off = gen_int_mode (GET_MODE_SIZE (memmode), GET_MODE (x));
return XEXP (x, 0);
case PRE_MODIFY:
addressing modes. If X and Y are not (known to be) part of
addresses, MEMMODE should be VOIDmode. */
-static int
-rtx_equal_for_cselib_1 (rtx x, rtx y, enum machine_mode memmode)
+int
+rtx_equal_for_cselib_1 (rtx x, rtx y, machine_mode memmode, int depth)
{
enum rtx_code code;
const char *fmt;
if (GET_CODE (y) == VALUE)
return e == canonical_cselib_val (CSELIB_VAL_PTR (y));
+ if (depth == 128)
+ return 0;
+
for (l = e->locs; l; l = l->next)
{
rtx t = l->loc;
list. */
if (REG_P (t) || MEM_P (t) || GET_CODE (t) == VALUE)
continue;
- else if (rtx_equal_for_cselib_1 (t, y, memmode))
+ else if (rtx_equal_for_cselib_1 (t, y, memmode, depth + 1))
return 1;
}
cselib_val *e = canonical_cselib_val (CSELIB_VAL_PTR (y));
struct elt_loc_list *l;
+ if (depth == 128)
+ return 0;
+
for (l = e->locs; l; l = l->next)
{
rtx t = l->loc;
if (REG_P (t) || MEM_P (t) || GET_CODE (t) == VALUE)
continue;
- else if (rtx_equal_for_cselib_1 (x, t, memmode))
+ else if (rtx_equal_for_cselib_1 (x, t, memmode, depth + 1))
return 1;
}
if (!xoff != !yoff)
return 0;
- if (xoff && !rtx_equal_for_cselib_1 (xoff, yoff, memmode))
+ if (xoff && !rtx_equal_for_cselib_1 (xoff, yoff, memmode, depth))
return 0;
/* Don't recurse if nothing changed. */
if (x != xorig || y != yorig)
- return rtx_equal_for_cselib_1 (x, y, memmode);
+ return rtx_equal_for_cselib_1 (x, y, memmode, depth);
return 0;
}
/* These won't be handled correctly by the code below. */
switch (GET_CODE (x))
{
- case CONST_DOUBLE:
- case CONST_FIXED:
+ CASE_CONST_UNIQUE:
case DEBUG_EXPR:
return 0;
return rtx_equal_p (ENTRY_VALUE_EXP (x), ENTRY_VALUE_EXP (y));
case LABEL_REF:
- return XEXP (x, 0) == XEXP (y, 0);
+ return label_ref_label (x) == label_ref_label (y);
+
+ case REG:
+ return REGNO (x) == REGNO (y);
case MEM:
/* We have to compare any autoinc operations in the addresses
using this MEM's mode. */
- return rtx_equal_for_cselib_1 (XEXP (x, 0), XEXP (y, 0), GET_MODE (x));
+ return rtx_equal_for_cselib_1 (XEXP (x, 0), XEXP (y, 0), GET_MODE (x),
+ depth);
default:
break;
return 0;
break;
+ case 'p':
+ if (maybe_ne (SUBREG_BYTE (x), SUBREG_BYTE (y)))
+ return 0;
+ break;
+
case 'V':
case 'E':
/* Two vectors must have the same length. */
/* And the corresponding elements must match. */
for (j = 0; j < XVECLEN (x, i); j++)
if (! rtx_equal_for_cselib_1 (XVECEXP (x, i, j),
- XVECEXP (y, i, j), memmode))
+ XVECEXP (y, i, j), memmode, depth))
return 0;
break;
case 'e':
if (i == 1
&& targetm.commutative_p (x, UNKNOWN)
- && rtx_equal_for_cselib_1 (XEXP (x, 1), XEXP (y, 0), memmode)
- && rtx_equal_for_cselib_1 (XEXP (x, 0), XEXP (y, 1), memmode))
+ && rtx_equal_for_cselib_1 (XEXP (x, 1), XEXP (y, 0), memmode,
+ depth)
+ && rtx_equal_for_cselib_1 (XEXP (x, 0), XEXP (y, 1), memmode,
+ depth))
return 1;
- if (! rtx_equal_for_cselib_1 (XEXP (x, i), XEXP (y, i), memmode))
+ if (! rtx_equal_for_cselib_1 (XEXP (x, i), XEXP (y, i), memmode,
+ depth))
return 0;
break;
return 1;
}
-/* We need to pass down the mode of constants through the hash table
- functions. For that purpose, wrap them in a CONST of the appropriate
- mode. */
-static rtx
-wrap_constant (enum machine_mode mode, rtx x)
-{
- if (!CONST_SCALAR_INT_P (x) && GET_CODE (x) != CONST_FIXED)
- return x;
- gcc_assert (mode != VOIDmode);
- return gen_rtx_CONST (mode, x);
-}
-
/* Hash an rtx. Return 0 if we couldn't hash the rtx.
For registers and memory locations, we look up their cselib_val structure
and return its VALUE element.
in a comparison anyway, since relying on hash differences is unsafe. */
static unsigned int
-cselib_hash_rtx (rtx x, int create, enum machine_mode memmode)
+cselib_hash_rtx (rtx x, int create, machine_mode memmode)
{
cselib_val *e;
+ poly_int64 offset;
int i, j;
enum rtx_code code;
const char *fmt;
return hash ? hash : (unsigned int) ENTRY_VALUE;
case CONST_INT:
- hash += ((unsigned) CONST_INT << 7) + INTVAL (x);
+ hash += ((unsigned) CONST_INT << 7) + UINTVAL (x);
return hash ? hash : (unsigned int) CONST_INT;
+ case CONST_WIDE_INT:
+ for (i = 0; i < CONST_WIDE_INT_NUNITS (x); i++)
+ 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. */
hash += (unsigned) code + (unsigned) GET_MODE (x);
- if (GET_MODE (x) != VOIDmode)
- hash += real_hash (CONST_DOUBLE_REAL_VALUE (x));
- else
+ if (TARGET_SUPPORTS_WIDE_INT == 0 && GET_MODE (x) == VOIDmode)
hash += ((unsigned) CONST_DOUBLE_LOW (x)
+ (unsigned) CONST_DOUBLE_HIGH (x));
+ else
+ hash += real_hash (CONST_DOUBLE_REAL_VALUE (x));
return hash ? hash : (unsigned int) CONST_DOUBLE;
case CONST_FIXED:
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 += cselib_hash_rtx (elt, 0, memmode);
}
/* 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 (XEXP (x, 0)));
+ + CODE_LABEL_NUMBER (label_ref_label (x)));
return hash ? hash : (unsigned int) LABEL_REF;
case SYMBOL_REF:
case PRE_INC:
/* We can't compute these without knowing the MEM mode. */
gcc_assert (memmode != VOIDmode);
- i = GET_MODE_SIZE (memmode);
+ offset = GET_MODE_SIZE (memmode);
if (code == PRE_DEC)
- i = -i;
+ offset = -offset;
/* Adjust the hash so that (mem:MEMMODE (pre_* (reg))) hashes
like (mem:MEMMODE (plus (reg) (const_int I))). */
hash += (unsigned) PLUS - (unsigned)code
+ cselib_hash_rtx (XEXP (x, 0), create, memmode)
- + cselib_hash_rtx (GEN_INT (i), create, memmode);
+ + cselib_hash_rtx (gen_int_mode (offset, GET_MODE (x)),
+ create, memmode);
return hash ? hash : 1 + (unsigned) PLUS;
case PRE_MODIFY:
hash += XINT (x, i);
break;
+ case 'p':
+ hash += constant_lower_bound (SUBREG_BYTE (x));
+ break;
+
case '0':
case 't':
/* unused */
value is MODE. */
static inline cselib_val *
-new_cselib_val (unsigned int hash, enum machine_mode mode, rtx x)
+new_cselib_val (unsigned int hash, machine_mode mode, rtx x)
{
- cselib_val *e = (cselib_val *) pool_alloc (cselib_val_pool);
+ cselib_val *e = cselib_val_pool.allocate ();
gcc_assert (hash);
gcc_assert (next_uid);
precisely when we can have VALUE RTXen (when cselib is active)
so we don't need to put them in garbage collected memory.
??? Why should a VALUE be an RTX in the first place? */
- e->val_rtx = (rtx) pool_alloc (value_pool);
+ e->val_rtx = (rtx_def*) value_pool.allocate ();
memset (e->val_rtx, 0, RTX_HDR_SIZE);
PUT_CODE (e->val_rtx, VALUE);
PUT_MODE (e->val_rtx, mode);
static void
add_mem_for_addr (cselib_val *addr_elt, cselib_val *mem_elt, rtx x)
{
- struct elt_loc_list *l;
-
addr_elt = canonical_cselib_val (addr_elt);
mem_elt = canonical_cselib_val (mem_elt);
/* Avoid duplicates. */
- for (l = mem_elt->locs; l; l = l->next)
+ addr_space_t as = MEM_ADDR_SPACE (x);
+ for (elt_loc_list *l = mem_elt->locs; l; l = l->next)
if (MEM_P (l->loc)
- && CSELIB_VAL_PTR (XEXP (l->loc, 0)) == addr_elt)
+ && CSELIB_VAL_PTR (XEXP (l->loc, 0)) == addr_elt
+ && MEM_ADDR_SPACE (l->loc) == as)
{
promote_debug_loc (l);
return;
static cselib_val *
cselib_lookup_mem (rtx x, int create)
{
- enum machine_mode mode = GET_MODE (x);
- enum machine_mode addr_mode;
+ machine_mode mode = GET_MODE (x);
+ machine_mode addr_mode;
cselib_val **slot;
cselib_val *addr;
cselib_val *mem_elt;
- struct elt_list *l;
if (MEM_VOLATILE_P (x) || mode == BLKmode
|| !cselib_record_memory
addr = cselib_lookup (XEXP (x, 0), addr_mode, create, mode);
if (! addr)
return 0;
-
addr = canonical_cselib_val (addr);
+
/* Find a value that describes a value of our mode at that address. */
- for (l = addr->addr_list; l; l = l->next)
+ addr_space_t as = MEM_ADDR_SPACE (x);
+ for (elt_list *l = addr->addr_list; l; l = l->next)
if (GET_MODE (l->elt->val_rtx) == mode)
{
- promote_debug_loc (l->elt->locs);
- return l->elt;
+ for (elt_loc_list *l2 = l->elt->locs; l2; l2 = l2->next)
+ if (MEM_P (l2->loc) && MEM_ADDR_SPACE (l2->loc) == as)
+ {
+ promote_debug_loc (l->elt->locs);
+ return l->elt;
+ }
}
if (! create)
mem_elt = new_cselib_val (next_uid, mode, x);
add_mem_for_addr (addr, mem_elt, x);
- slot = cselib_find_slot (wrap_constant (mode, x), mem_elt->hash,
- INSERT, mode);
+ slot = cselib_find_slot (mode, x, mem_elt->hash, INSERT, VOIDmode);
*slot = mem_elt;
return mem_elt;
}
int i, j;
RTX_CODE code;
const char *format_ptr;
- enum machine_mode mode;
+ machine_mode mode;
code = GET_CODE (orig);
else
return orig;
}
+ return orig;
}
CASE_CONST_ANY:
If X is within a MEM, MEMMODE must be the mode of the MEM. */
rtx
-cselib_subst_to_values (rtx x, enum machine_mode memmode)
+cselib_subst_to_values (rtx x, machine_mode memmode)
{
enum rtx_code code = GET_CODE (x);
const char *fmt = GET_RTX_FORMAT (code);
struct elt_list *l;
rtx copy = x;
int i;
+ poly_int64 offset;
switch (code)
{
case PRE_DEC:
case PRE_INC:
gcc_assert (memmode != VOIDmode);
- i = GET_MODE_SIZE (memmode);
+ offset = GET_MODE_SIZE (memmode);
if (code == PRE_DEC)
- i = -i;
+ offset = -offset;
return cselib_subst_to_values (plus_constant (GET_MODE (x),
- XEXP (x, 0), i),
+ XEXP (x, 0), offset),
memmode);
case PRE_MODIFY:
/* Wrapper for cselib_subst_to_values, that indicates X is in INSN. */
rtx
-cselib_subst_to_values_from_insn (rtx x, enum machine_mode memmode, rtx insn)
+cselib_subst_to_values_from_insn (rtx x, machine_mode memmode, rtx_insn *insn)
{
rtx ret;
gcc_assert (!cselib_current_insn);
we're tracking autoinc expressions. */
static cselib_val *
-cselib_lookup_1 (rtx x, enum machine_mode mode,
- int create, enum machine_mode memmode)
+cselib_lookup_1 (rtx x, machine_mode mode,
+ int create, machine_mode memmode)
{
cselib_val **slot;
cselib_val *e;
if (i < FIRST_PSEUDO_REGISTER)
{
- unsigned int n = hard_regno_nregs[i][mode];
+ unsigned int n = hard_regno_nregs (i, mode);
if (n > max_value_regs)
max_value_regs = n;
e = new_cselib_val (next_uid, GET_MODE (x), x);
new_elt_loc_list (e, x);
+
+ scalar_int_mode int_mode;
if (REG_VALUES (i) == 0)
{
/* Maintain the invariant that the first entry of
REG_VALUES (i) = new_elt_list (REG_VALUES (i), NULL);
}
else if (cselib_preserve_constants
- && GET_MODE_CLASS (mode) == MODE_INT)
+ && is_int_mode (mode, &int_mode))
{
/* During var-tracking, try harder to find equivalences
for SUBREGs. If a setter sets say a DImode register
and user uses that register only in SImode, add a lowpart
subreg location. */
struct elt_list *lwider = NULL;
+ scalar_int_mode lmode;
l = REG_VALUES (i);
if (l && l->elt == NULL)
l = l->next;
for (; l; l = l->next)
- if (GET_MODE_CLASS (GET_MODE (l->elt->val_rtx)) == MODE_INT
- && GET_MODE_SIZE (GET_MODE (l->elt->val_rtx))
- > GET_MODE_SIZE (mode)
+ if (is_int_mode (GET_MODE (l->elt->val_rtx), &lmode)
+ && GET_MODE_SIZE (lmode) > GET_MODE_SIZE (int_mode)
&& (lwider == NULL
- || GET_MODE_SIZE (GET_MODE (l->elt->val_rtx))
- < GET_MODE_SIZE (GET_MODE (lwider->elt->val_rtx))))
+ || partial_subreg_p (lmode,
+ GET_MODE (lwider->elt->val_rtx))))
{
struct elt_loc_list *el;
if (i < FIRST_PSEUDO_REGISTER
- && hard_regno_nregs[i][GET_MODE (l->elt->val_rtx)] != 1)
+ && hard_regno_nregs (i, lmode) != 1)
continue;
for (el = l->elt->locs; el; el = el->next)
if (!REG_P (el->loc))
}
if (lwider)
{
- rtx sub = lowpart_subreg (mode, lwider->elt->val_rtx,
+ rtx sub = lowpart_subreg (int_mode, lwider->elt->val_rtx,
GET_MODE (lwider->elt->val_rtx));
if (sub)
new_elt_loc_list (e, sub);
}
}
REG_VALUES (i)->next = new_elt_list (REG_VALUES (i)->next, e);
- slot = cselib_find_slot (x, e->hash, INSERT, memmode);
+ slot = cselib_find_slot (mode, x, e->hash, INSERT, memmode);
*slot = e;
return e;
}
if (! hashval)
return 0;
- slot = cselib_find_slot (wrap_constant (mode, x), hashval,
+ slot = cselib_find_slot (mode, x, hashval,
create ? INSERT : NO_INSERT, memmode);
if (slot == 0)
return 0;
/* Wrapper for cselib_lookup, that indicates X is in INSN. */
cselib_val *
-cselib_lookup_from_insn (rtx x, enum machine_mode mode,
- int create, enum machine_mode memmode, rtx insn)
+cselib_lookup_from_insn (rtx x, machine_mode mode,
+ int create, machine_mode memmode, rtx_insn *insn)
{
cselib_val *ret;
maintains invariants related with debug insns. */
cselib_val *
-cselib_lookup (rtx x, enum machine_mode mode,
- int create, enum machine_mode memmode)
+cselib_lookup (rtx x, machine_mode mode,
+ int create, machine_mode memmode)
{
cselib_val *ret = cselib_lookup_1 (x, mode, create, memmode);
return ret;
}
+/* Invalidate the value at *L, which is part of REG_VALUES (REGNO). */
+
+static void
+cselib_invalidate_regno_val (unsigned int regno, struct elt_list **l)
+{
+ cselib_val *v = (*l)->elt;
+ if (*l == REG_VALUES (regno))
+ {
+ /* Maintain the invariant that the first entry of
+ REG_VALUES, if present, must be the value used to set
+ the register, or NULL. This is also nice because
+ then we won't push the same regno onto user_regs
+ multiple times. */
+ (*l)->elt = NULL;
+ l = &(*l)->next;
+ }
+ else
+ unchain_one_elt_list (l);
+
+ v = canonical_cselib_val (v);
+
+ bool had_locs = v->locs != NULL;
+ rtx_insn *setting_insn = v->locs ? v->locs->setting_insn : NULL;
+
+ /* Now, we clear the mapping from value to reg. It must exist, so
+ this code will crash intentionally if it doesn't. */
+ for (elt_loc_list **p = &v->locs; ; p = &(*p)->next)
+ {
+ rtx x = (*p)->loc;
+
+ if (REG_P (x) && REGNO (x) == regno)
+ {
+ unchain_one_elt_loc_list (p);
+ break;
+ }
+ }
+
+ if (had_locs && v->locs == 0 && !PRESERVED_VALUE_P (v->val_rtx))
+ {
+ if (setting_insn && DEBUG_INSN_P (setting_insn))
+ n_useless_debug_values++;
+ else
+ n_useless_values++;
+ }
+}
+
/* Invalidate any entries in reg_values that overlap REGNO. This is called
if REGNO is changing. MODE is the mode of the assignment to REGNO, which
is used to determine how many hard registers are being changed. If MODE
invalidating call clobbered registers across a call. */
static void
-cselib_invalidate_regno (unsigned int regno, enum machine_mode mode)
+cselib_invalidate_regno (unsigned int regno, machine_mode mode)
{
unsigned int endregno;
unsigned int i;
while (*l)
{
cselib_val *v = (*l)->elt;
- bool had_locs;
- rtx setting_insn;
- struct elt_loc_list **p;
unsigned int this_last = i;
if (i < FIRST_PSEUDO_REGISTER && v != NULL)
}
/* We have an overlap. */
- if (*l == REG_VALUES (i))
- {
- /* Maintain the invariant that the first entry of
- REG_VALUES, if present, must be the value used to set
- the register, or NULL. This is also nice because
- then we won't push the same regno onto user_regs
- multiple times. */
- (*l)->elt = NULL;
- l = &(*l)->next;
- }
- else
- unchain_one_elt_list (l);
-
- v = canonical_cselib_val (v);
-
- had_locs = v->locs != NULL;
- setting_insn = v->locs ? v->locs->setting_insn : NULL;
-
- /* Now, we clear the mapping from value to reg. It must exist, so
- this code will crash intentionally if it doesn't. */
- for (p = &v->locs; ; p = &(*p)->next)
- {
- rtx x = (*p)->loc;
-
- if (REG_P (x) && REGNO (x) == i)
- {
- unchain_one_elt_loc_list (p);
- break;
- }
- }
-
- if (had_locs && v->locs == 0 && !PRESERVED_VALUE_P (v->val_rtx))
- {
- if (setting_insn && DEBUG_INSN_P (setting_insn))
- n_useless_debug_values++;
- else
- n_useless_values++;
- }
+ cselib_invalidate_regno_val (i, l);
}
}
}
bool has_mem = false;
struct elt_loc_list **p = &v->locs;
bool had_locs = v->locs != NULL;
- rtx setting_insn = v->locs ? v->locs->setting_insn : NULL;
+ rtx_insn *setting_insn = v->locs ? v->locs->setting_insn : NULL;
while (*p)
{
p = &(*p)->next;
continue;
}
- if (num_mems < PARAM_VALUE (PARAM_MAX_CSELIB_MEMORY_LOCATIONS)
+ if (num_mems < param_max_cselib_memory_locations
&& ! canon_anti_dependence (x, false, mem_rtx,
GET_MODE (mem_rtx), mem_addr))
{
*vp = &dummy_val;
}
-/* Invalidate DEST, which is being assigned to or clobbered. */
+/* Invalidate DEST. */
void
cselib_invalidate_rtx (rtx dest)
/* A wrapper for cselib_invalidate_rtx to be called via note_stores. */
static void
-cselib_invalidate_rtx_note_stores (rtx dest, const_rtx ignore ATTRIBUTE_UNUSED,
+cselib_invalidate_rtx_note_stores (rtx dest, const_rtx,
void *data ATTRIBUTE_UNUSED)
{
cselib_invalidate_rtx (dest);
static void
cselib_record_set (rtx dest, cselib_val *src_elt, cselib_val *dest_addr_elt)
{
- int dreg = REG_P (dest) ? (int) REGNO (dest) : -1;
-
if (src_elt == 0 || side_effects_p (dest))
return;
- if (dreg >= 0)
+ if (REG_P (dest))
{
+ unsigned int dreg = REGNO (dest);
if (dreg < FIRST_PSEUDO_REGISTER)
{
- unsigned int n = hard_regno_nregs[dreg][GET_MODE (dest)];
+ unsigned int n = REG_NREGS (dest);
if (n > max_value_regs)
max_value_regs = n;
/* Make ELT and X's VALUE equivalent to each other at INSN. */
void
-cselib_add_permanent_equiv (cselib_val *elt, rtx x, rtx insn)
+cselib_add_permanent_equiv (cselib_val *elt, rtx x, rtx_insn *insn)
{
cselib_val *nelt;
- rtx save_cselib_current_insn = cselib_current_insn;
+ rtx_insn *save_cselib_current_insn = cselib_current_insn;
gcc_checking_assert (elt);
gcc_checking_assert (PRESERVED_VALUE_P (elt->val_rtx));
data->n_sets++;
- return -1;
+ return 0;
}
/* Record the effects of any sets and autoincs in INSN. */
static void
-cselib_record_sets (rtx insn)
+cselib_record_sets (rtx_insn *insn)
{
int n_sets = 0;
int i;
struct cselib_set sets[MAX_SETS];
- rtx body = PATTERN (insn);
rtx cond = 0;
int n_sets_before_autoinc;
+ int n_strict_low_parts = 0;
struct cselib_record_autoinc_data data;
- body = PATTERN (insn);
+ rtx body = PATTERN (insn);
if (GET_CODE (body) == COND_EXEC)
{
cond = COND_EXEC_TEST (body);
data.sets = sets;
data.n_sets = n_sets_before_autoinc = n_sets;
- for_each_inc_dec (&insn, cselib_record_autoinc_cb, &data);
+ for_each_inc_dec (PATTERN (insn), cselib_record_autoinc_cb, &data);
n_sets = data.n_sets;
/* Look up the values that are read. Do this before invalidating the
for (i = 0; i < n_sets; i++)
{
rtx dest = sets[i].dest;
+ rtx orig = dest;
/* A STRICT_LOW_PART can be ignored; we'll record the equivalence for
the low part after invalidating any knowledge about larger modes. */
sets[i].src_elt = cselib_lookup (src, GET_MODE (dest), 1, VOIDmode);
if (MEM_P (dest))
{
- enum machine_mode address_mode = get_address_mode (dest);
+ machine_mode address_mode = get_address_mode (dest);
sets[i].dest_addr_elt = cselib_lookup (XEXP (dest, 0),
address_mode, 1,
else
sets[i].dest_addr_elt = 0;
}
+
+ /* Improve handling of STRICT_LOW_PART if the current value is known
+ to be const0_rtx, then the low bits will be set to dest and higher
+ bits will remain zero. Used in code like:
+
+ {di:SI=0;clobber flags:CC;}
+ flags:CCNO=cmp(bx:SI,0)
+ strict_low_part(di:QI)=flags:CCNO<=0
+
+ where we can note both that di:QI=flags:CCNO<=0 and
+ also that because di:SI is known to be 0 and strict_low_part(di:QI)
+ preserves the upper bits that di:SI=zero_extend(flags:CCNO<=0). */
+ scalar_int_mode mode;
+ if (dest != orig
+ && cselib_record_sets_hook
+ && REG_P (dest)
+ && HARD_REGISTER_P (dest)
+ && sets[i].src_elt
+ && is_a <scalar_int_mode> (GET_MODE (dest), &mode)
+ && n_sets + n_strict_low_parts < MAX_SETS)
+ {
+ opt_scalar_int_mode wider_mode_iter;
+ FOR_EACH_WIDER_MODE (wider_mode_iter, mode)
+ {
+ scalar_int_mode wider_mode = wider_mode_iter.require ();
+ if (GET_MODE_PRECISION (wider_mode) > BITS_PER_WORD)
+ break;
+
+ rtx reg = gen_lowpart (wider_mode, dest);
+ if (!REG_P (reg))
+ break;
+
+ cselib_val *v = cselib_lookup (reg, wider_mode, 0, VOIDmode);
+ if (!v)
+ continue;
+
+ struct elt_loc_list *l;
+ for (l = v->locs; l; l = l->next)
+ if (l->loc == const0_rtx)
+ break;
+
+ if (!l)
+ continue;
+
+ sets[n_sets + n_strict_low_parts].dest = reg;
+ sets[n_sets + n_strict_low_parts].src = dest;
+ sets[n_sets + n_strict_low_parts++].src_elt = sets[i].src_elt;
+ break;
+ }
+ }
}
if (cselib_record_sets_hook)
/* Invalidate all locations written by this insn. Note that the elts we
looked up in the previous loop aren't affected, just some of their
locations may go away. */
- note_stores (body, cselib_invalidate_rtx_note_stores, NULL);
+ note_pattern_stores (body, cselib_invalidate_rtx_note_stores, NULL);
for (i = n_sets_before_autoinc; i < n_sets; i++)
cselib_invalidate_rtx (sets[i].dest);
|| (MEM_P (dest) && cselib_record_memory))
cselib_record_set (dest, sets[i].src_elt, sets[i].dest_addr_elt);
}
+
+ /* And deal with STRICT_LOW_PART. */
+ for (i = 0; i < n_strict_low_parts; i++)
+ {
+ if (! PRESERVED_VALUE_P (sets[n_sets + i].src_elt->val_rtx))
+ continue;
+ machine_mode dest_mode = GET_MODE (sets[n_sets + i].dest);
+ cselib_val *v
+ = cselib_lookup (sets[n_sets + i].dest, dest_mode, 1, VOIDmode);
+ cselib_preserve_value (v);
+ rtx r = gen_rtx_ZERO_EXTEND (dest_mode,
+ sets[n_sets + i].src_elt->val_rtx);
+ cselib_add_permanent_equiv (v, r, insn);
+ }
}
/* Return true if INSN in the prologue initializes hard_frame_pointer_rtx. */
bool
-fp_setter_insn (rtx insn)
+fp_setter_insn (rtx_insn *insn)
{
rtx expr, pat = NULL_RTX;
return true;
}
+/* V is one of the values in REG_VALUES (REGNO). Return true if it
+ would be invalidated by CALLEE_ABI. */
+
+static bool
+cselib_invalidated_by_call_p (const function_abi &callee_abi,
+ unsigned int regno, cselib_val *v)
+{
+ machine_mode mode = GET_MODE (v->val_rtx);
+ if (mode == VOIDmode)
+ {
+ v = REG_VALUES (regno)->elt;
+ if (!v)
+ /* If we don't know what the mode of the constant value is, and we
+ don't know what mode the register was set in, conservatively
+ assume that the register is clobbered. The value's going to be
+ essentially useless in this case anyway. */
+ return true;
+ mode = GET_MODE (v->val_rtx);
+ }
+ return callee_abi.clobbers_reg_p (mode, regno);
+}
+
/* Record the effects of INSN. */
void
-cselib_process_insn (rtx insn)
+cselib_process_insn (rtx_insn *insn)
{
int i;
rtx x;
cselib_current_insn = insn;
- /* Forget everything at a CODE_LABEL, a volatile insn, or a setjmp. */
+ /* Forget everything at a CODE_LABEL or a setjmp. */
if ((LABEL_P (insn)
|| (CALL_P (insn)
- && find_reg_note (insn, REG_SETJMP, NULL))
- || (NONJUMP_INSN_P (insn)
- && volatile_insn_p (PATTERN (insn))))
+ && find_reg_note (insn, REG_SETJMP, NULL)))
&& !cselib_preserve_constants)
{
cselib_reset_table (next_uid);
- cselib_current_insn = NULL_RTX;
+ cselib_current_insn = NULL;
return;
}
if (! INSN_P (insn))
{
- cselib_current_insn = NULL_RTX;
+ cselib_current_insn = NULL;
return;
}
memory. */
if (CALL_P (insn))
{
+ function_abi callee_abi = insn_callee_abi (insn);
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
- if (call_used_regs[i]
- || (REG_VALUES (i) && REG_VALUES (i)->elt
- && HARD_REGNO_CALL_PART_CLOBBERED (i,
- GET_MODE (REG_VALUES (i)->elt->val_rtx))))
- cselib_invalidate_regno (i, reg_raw_mode[i]);
+ {
+ elt_list **l = ®_VALUES (i);
+ while (*l)
+ {
+ cselib_val *v = (*l)->elt;
+ if (v && cselib_invalidated_by_call_p (callee_abi, i, v))
+ cselib_invalidate_regno_val (i, l);
+ else
+ l = &(*l)->next;
+ }
+ }
/* Since it is not clear how cselib is going to be used, be
conservative here and treat looping pure or const functions
if (RTL_LOOPING_CONST_OR_PURE_CALL_P (insn)
|| !(RTL_CONST_OR_PURE_CALL_P (insn)))
cselib_invalidate_mem (callmem);
+ else
+ /* For const/pure calls, invalidate any argument slots because
+ they are owned by the callee. */
+ for (x = CALL_INSN_FUNCTION_USAGE (insn); x; x = XEXP (x, 1))
+ if (GET_CODE (XEXP (x, 0)) == USE
+ && MEM_P (XEXP (XEXP (x, 0), 0)))
+ cselib_invalidate_mem (XEXP (XEXP (x, 0), 0));
}
cselib_record_sets (insn);
for (x = CALL_INSN_FUNCTION_USAGE (insn); x; x = XEXP (x, 1))
if (GET_CODE (XEXP (x, 0)) == CLOBBER)
cselib_invalidate_rtx (XEXP (XEXP (x, 0), 0));
- /* Flush evertything on setjmp. */
+
+ /* Flush everything on setjmp. */
if (cselib_preserve_constants
&& find_reg_note (insn, REG_SETJMP, NULL))
{
&& fp_setter_insn (insn))
cselib_invalidate_rtx (stack_pointer_rtx);
- cselib_current_insn = NULL_RTX;
+ cselib_current_insn = NULL;
if (n_useless_values > MAX_USELESS_VALUES
/* remove_useless_values is linear in the hash table size. Avoid
quadratic behavior for very large hashtables with very few
useless elements. */
&& ((unsigned int)n_useless_values
- > (cselib_hash_table.elements () - n_debug_values) / 4))
+ > (cselib_hash_table->elements () - n_debug_values) / 4))
remove_useless_values ();
}
void
cselib_init (int record_what)
{
- elt_list_pool = create_alloc_pool ("elt_list",
- sizeof (struct elt_list), 10);
- elt_loc_list_pool = create_alloc_pool ("elt_loc_list",
- sizeof (struct elt_loc_list), 10);
- cselib_val_pool = create_alloc_pool ("cselib_val_list",
- sizeof (cselib_val), 10);
- value_pool = create_alloc_pool ("value", RTX_CODE_SIZE (VALUE), 100);
cselib_record_memory = record_what & CSELIB_RECORD_MEMORY;
cselib_preserve_constants = record_what & CSELIB_PRESERVE_CONSTANTS;
cselib_any_perm_equivs = false;
}
used_regs = XNEWVEC (unsigned int, cselib_nregs);
n_used_regs = 0;
- cselib_hash_table.create (31);
+ /* FIXME: enable sanitization (PR87845) */
+ cselib_hash_table
+ = new hash_table<cselib_hasher> (31, /* ggc */ false,
+ /* sanitize_eq_and_hash */ false);
if (cselib_preserve_constants)
- cselib_preserved_hash_table.create (31);
+ cselib_preserved_hash_table
+ = new hash_table<cselib_hasher> (31, /* ggc */ false,
+ /* sanitize_eq_and_hash */ false);
next_uid = 1;
}
cselib_any_perm_equivs = false;
cfa_base_preserved_val = NULL;
cfa_base_preserved_regno = INVALID_REGNUM;
- free_alloc_pool (elt_list_pool);
- free_alloc_pool (elt_loc_list_pool);
- free_alloc_pool (cselib_val_pool);
- free_alloc_pool (value_pool);
+ elt_list_pool.release ();
+ elt_loc_list_pool.release ();
+ cselib_val_pool.release ();
+ value_pool.release ();
cselib_clear_table ();
- cselib_hash_table.dispose ();
+ delete cselib_hash_table;
+ cselib_hash_table = NULL;
if (preserved)
- cselib_preserved_hash_table.dispose ();
+ delete cselib_preserved_hash_table;
+ cselib_preserved_hash_table = NULL;
free (used_regs);
used_regs = 0;
n_useless_values = 0;
dump_cselib_table (FILE *out)
{
fprintf (out, "cselib hash table:\n");
- cselib_hash_table.traverse <FILE *, dump_cselib_val> (out);
+ cselib_hash_table->traverse <FILE *, dump_cselib_val> (out);
fprintf (out, "cselib preserved hash table:\n");
- cselib_preserved_hash_table.traverse <FILE *, dump_cselib_val> (out);
+ cselib_preserved_hash_table->traverse <FILE *, dump_cselib_val> (out);
if (first_containing_mem != &dummy_val)
{
fputs ("first mem ", out);