Makefile.in (OBJS-common): Add postreload-gcse.c.

* Makefile.in (OBJS-common): Add postreload-gcse.c.
	Add new postreload-gcse.o.
	* cse.c (SAFE_HASH): Define as wrapper around safe_hash.
	(lookup_as_function, insert, rehash_using_reg, use_related_value,
	equiv_constant): Use SAFE_HASH instead of safe_hash.
	(exp_equiv_p): Export.  Add for_gcse argument when comparing
	for GCSE.
	(lookup, lookup_for_remove, merge_equiv_classes, find_best_addr,
	find_comparison_args, fold_rtx, cse_insn): Update callers.
	(hash_rtx): New function derived from old canon_hash and bits
	from gcse.c hash_expr_1.
	(canon_hash_string): Rename to hash_rtx_string.
	(canon_hash, safe_hash): Make static inline.  Call hash_rtx.
	* cselib.c (hash_rtx): Rename to cselib_hash_rtx.
	(cselib_lookup): Update this caller.
	* gcse.c (modify_mem_list_set, canon_modify_mem_list_set):
	Make static.
	(hash_expr): Call hash_rtx.
	(ldst_entry): Likewise.
	(expr_equiv_p): Call exp_equiv_p.
	(struct unoccr, hash_expr_1, hash_string_1, lookup_expr,
	reg_used_on_edge, reg_set_between_after_reload_p,
	reg_used_between_after_reload_p, get_avail_load_store_reg,
	is_jump_table_basic_block, bb_has_well_behaved_predecessors,
	get_bb_avail_insn, hash_scan_set_after_reload,
	compute_hash_table_after_reload,
	eliminate_partially_redundant_loads, gcse_after_reload,
	get_bb_avail_insn, gcse_after_reload_main): Remove.
	* postreload-gcse.c: New file, reincarnating most of the above.
	* rtl.h (exp_equiv_p, hash_rtx): New prototypes.
	(gcse_after_reload_main): Update prototype.
	* timevar.def (TV_GCSE_AFTER_RELOAD): New timevar.
	* passes.c (rest_of_handle_gcse2): Use it.

From-SVN: r86206
This commit is contained in:
Steven Bosscher 2004-08-18 20:53:59 +00:00 committed by Steven Bosscher
parent 95013377bd
commit 0516f6fe82
9 changed files with 1671 additions and 1226 deletions

View File

@ -1,3 +1,39 @@
2004-08-18 Steven Bosscher <stevenb@suse.de>
* Makefile.in (OBJS-common): Add postreload-gcse.c.
Add new postreload-gcse.o.
* cse.c (SAFE_HASH): Define as wrapper around safe_hash.
(lookup_as_function, insert, rehash_using_reg, use_related_value,
equiv_constant): Use SAFE_HASH instead of safe_hash.
(exp_equiv_p): Export. Add for_gcse argument when comparing
for GCSE.
(lookup, lookup_for_remove, merge_equiv_classes, find_best_addr,
find_comparison_args, fold_rtx, cse_insn): Update callers.
(hash_rtx): New function derived from old canon_hash and bits
from gcse.c hash_expr_1.
(canon_hash_string): Rename to hash_rtx_string.
(canon_hash, safe_hash): Make static inline. Call hash_rtx.
* cselib.c (hash_rtx): Rename to cselib_hash_rtx.
(cselib_lookup): Update this caller.
* gcse.c (modify_mem_list_set, canon_modify_mem_list_set):
Make static.
(hash_expr): Call hash_rtx.
(ldst_entry): Likewise.
(expr_equiv_p): Call exp_equiv_p.
(struct unoccr, hash_expr_1, hash_string_1, lookup_expr,
reg_used_on_edge, reg_set_between_after_reload_p,
reg_used_between_after_reload_p, get_avail_load_store_reg,
is_jump_table_basic_block, bb_has_well_behaved_predecessors,
get_bb_avail_insn, hash_scan_set_after_reload,
compute_hash_table_after_reload,
eliminate_partially_redundant_loads, gcse_after_reload,
get_bb_avail_insn, gcse_after_reload_main): Remove.
* postreload-gcse.c: New file, reincarnating most of the above.
* rtl.h (exp_equiv_p, hash_rtx): New prototypes.
(gcse_after_reload_main): Update prototype.
* timevar.def (TV_GCSE_AFTER_RELOAD): New timevar.
* passes.c (rest_of_handle_gcse2): Use it.
2004-08-18 Diego Novillo <dnovillo@redhat.com>
* tree-ssa-loop.c (pass_loop_init): Add TODO_dump_func.

View File

@ -899,15 +899,18 @@ OBJS-common = \
genrtl.o ggc-common.o global.o graph.o gtype-desc.o \
haifa-sched.o hooks.o ifcvt.o insn-attrtab.o insn-emit.o insn-modes.o \
insn-extract.o insn-opinit.o insn-output.o insn-peep.o insn-recog.o \
insn-preds.o integrate.o intl.o jump.o langhooks.o lcm.o lists.o \
local-alloc.o loop.o modulo-sched.o \
optabs.o options.o opts.o params.o postreload.o predict.o \
integrate.o intl.o jump.o langhooks.o lcm.o lists.o local-alloc.o \
loop.o modulo-sched.o optabs.o options.o opts.o \
params.o postreload.o postreload-gcse.o predict.o \
insn-preds.o integrate.o intl.o jump.o langhooks.o lcm.o lists.o \
local-alloc.o loop.o modulo-sched.o optabs.o options.o opts.o \
params.o postreload.o postreload-gcse.o predict.o \
print-rtl.o print-tree.o value-prof.o var-tracking.o \
profile.o ra.o ra-build.o ra-colorize.o ra-debug.o ra-rewrite.o \
real.o recog.o reg-stack.o regclass.o regmove.o regrename.o \
reload.o reload1.o reorg.o resource.o rtl.o rtlanal.o rtl-error.o \
sbitmap.o sched-deps.o sched-ebb.o sched-rgn.o sched-vis.o sdbout.o \
simplify-rtx.o sreal.o stmt.o stor-layout.o stringpool.o \
simplify-rtx.o sreal.o stmt.o stor-layout.o stringpool.o \
targhooks.o timevar.o toplev.o tracer.o tree.o tree-dump.o unroll.o \
varasm.o varray.o vec.o version.o vmsdbgout.o xcoffout.o alloc-pool.o \
et-forest.o cfghooks.o bt-load.o pretty-print.o $(GGC) web.o passes.o \
@ -2047,6 +2050,10 @@ postreload.o : postreload.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H)
$(EXPR_H) $(OPTABS_H) reload.h $(REGS_H) hard-reg-set.h insn-config.h \
$(BASIC_BLOCK_H) $(RECOG_H) output.h function.h toplev.h cselib.h $(TM_P_H) \
except.h $(TREE_H)
postreload-gcse.o : postreload-gcse.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \
$(RTL_H) $(REGS_H) hard-reg-set.h $(FLAGS_H) real.h insn-config.h $(GGC_H) \
$(RECOG_H) $(EXPR_H) $(BASIC_BLOCK_H) function.h output.h toplev.h $(TM_P_H) \
except.h $(TREE_H)
caller-save.o : caller-save.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \
$(FLAGS_H) $(REGS_H) hard-reg-set.h insn-config.h $(BASIC_BLOCK_H) function.h \
$(RECOG_H) reload.h $(EXPR_H) toplev.h $(TM_P_H)

410
gcc/cse.c
View File

@ -489,6 +489,12 @@ struct table_elt
? (((unsigned) REG << 7) + (unsigned) REG_QTY (REGNO (X))) \
: canon_hash (X, M)) & HASH_MASK)
/* Like HASH, but without side-effects. */
#define SAFE_HASH(X, M) \
((REG_P (X) && REGNO (X) >= FIRST_PSEUDO_REGISTER \
? (((unsigned) REG << 7) + (unsigned) REG_QTY (REGNO (X))) \
: safe_hash (X, M)) & HASH_MASK)
/* Determine whether register number N is considered a fixed register for the
purpose of approximating register costs.
It is desirable to replace other regs with fixed regs, to reduce need for
@ -625,10 +631,11 @@ 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 unsigned canon_hash (rtx, enum machine_mode);
static unsigned canon_hash_string (const char *);
static unsigned safe_hash (rtx, enum machine_mode);
static int exp_equiv_p (rtx, rtx, int, int);
static inline unsigned canon_hash (rtx, enum machine_mode);
static inline unsigned safe_hash (rtx, enum machine_mode);
static unsigned hash_rtx_string (const char *);
static rtx canon_reg (rtx, rtx);
static void find_best_addr (rtx, rtx *, enum machine_mode);
static enum rtx_code find_comparison_args (enum rtx_code, rtx *, rtx *,
@ -1324,7 +1331,7 @@ lookup (rtx x, unsigned int hash, enum machine_mode mode)
for (p = table[hash]; p; p = p->next_same_hash)
if (mode == p->mode && ((x == p->exp && REG_P (x))
|| exp_equiv_p (x, p->exp, !REG_P (x), 0)))
|| exp_equiv_p (x, p->exp, !REG_P (x), false)))
return p;
return 0;
@ -1352,7 +1359,8 @@ lookup_for_remove (rtx x, unsigned int hash, enum machine_mode mode)
else
{
for (p = table[hash]; p; p = p->next_same_hash)
if (mode == p->mode && (x == p->exp || exp_equiv_p (x, p->exp, 0, 0)))
if (mode == p->mode
&& (x == p->exp || exp_equiv_p (x, p->exp, 0, false)))
return p;
}
@ -1366,7 +1374,7 @@ static rtx
lookup_as_function (rtx x, enum rtx_code code)
{
struct table_elt *p
= lookup (x, safe_hash (x, VOIDmode) & HASH_MASK, GET_MODE (x));
= lookup (x, SAFE_HASH (x, VOIDmode), GET_MODE (x));
/* If we are looking for a CONST_INT, the mode doesn't really matter, as
long as we are narrowing. So if we looked in vain for a mode narrower
@ -1376,7 +1384,7 @@ lookup_as_function (rtx x, enum rtx_code code)
{
x = copy_rtx (x);
PUT_MODE (x, word_mode);
p = lookup (x, safe_hash (x, VOIDmode) & HASH_MASK, word_mode);
p = lookup (x, SAFE_HASH (x, VOIDmode), word_mode);
}
if (p == 0)
@ -1385,7 +1393,7 @@ lookup_as_function (rtx x, enum rtx_code code)
for (p = p->first_same_value; p; p = p->next_same_value)
if (GET_CODE (p->exp) == code
/* Make sure this is a valid entry in the table. */
&& exp_equiv_p (p->exp, p->exp, 1, 0))
&& exp_equiv_p (p->exp, p->exp, 1, false))
return p->exp;
return 0;
@ -1568,7 +1576,7 @@ insert (rtx x, struct table_elt *classp, unsigned int hash, enum machine_mode mo
if (subexp != 0)
{
/* Get the integer-free subexpression in the hash table. */
subhash = safe_hash (subexp, mode) & HASH_MASK;
subhash = SAFE_HASH (subexp, mode);
subelt = lookup (subexp, subhash, mode);
if (subelt == 0)
subelt = insert (subexp, NULL, subhash, mode);
@ -1622,7 +1630,7 @@ merge_equiv_classes (struct table_elt *class1, struct table_elt *class2)
/* Remove old entry, make a new one in CLASS1's class.
Don't do this for invalid entries as we cannot find their
hash code (it also isn't necessary). */
if (REG_P (exp) || exp_equiv_p (exp, exp, 1, 0))
if (REG_P (exp) || exp_equiv_p (exp, exp, 1, false))
{
bool need_rehash = false;
@ -1917,8 +1925,8 @@ rehash_using_reg (rtx x)
{
next = p->next_same_hash;
if (reg_mentioned_p (x, p->exp)
&& exp_equiv_p (p->exp, p->exp, 1, 0)
&& i != (hash = safe_hash (p->exp, p->mode) & HASH_MASK))
&& exp_equiv_p (p->exp, p->exp, 1, false)
&& i != (hash = SAFE_HASH (p->exp, p->mode)))
{
if (p->next_same_hash)
p->next_same_hash->prev_same_hash = p->prev_same_hash;
@ -2017,7 +2025,7 @@ use_related_value (rtx x, struct table_elt *elt)
rtx subexp = get_related_value (x);
if (subexp != 0)
relt = lookup (subexp,
safe_hash (subexp, GET_MODE (subexp)) & HASH_MASK,
SAFE_HASH (subexp, GET_MODE (subexp)),
GET_MODE (subexp));
}
@ -2068,7 +2076,7 @@ use_related_value (rtx x, struct table_elt *elt)
/* Hash a string. Just add its bytes up. */
static inline unsigned
canon_hash_string (const char *ps)
hash_rtx_string (const char *ps)
{
unsigned hash = 0;
const unsigned char *p = (const unsigned char *) ps;
@ -2085,23 +2093,26 @@ canon_hash_string (const char *ps)
MODE is used in hashing for CONST_INTs only;
otherwise the mode of X is used.
Store 1 in do_not_record if any subexpression is volatile.
Store 1 in DO_NOT_RECORD_P if any subexpression is volatile.
Store 1 in hash_arg_in_memory if X contains a MEM rtx
which does not have the MEM_READONLY_P bit set.
If HASH_ARG_IN_MEMORY_P is not NULL, store 1 in it if X contains
a MEM rtx which does not have the RTX_UNCHANGING_P bit set.
Note that cse_insn knows that the hash code of a MEM expression
is just (int) MEM plus the hash code of the address. */
static unsigned
canon_hash (rtx x, enum machine_mode mode)
unsigned
hash_rtx (rtx x, enum machine_mode mode, int *do_not_record_p,
int *hash_arg_in_memory_p, bool have_reg_qty)
{
int i, j;
unsigned hash = 0;
enum rtx_code code;
const char *fmt;
/* repeat is used to turn tail-recursion into iteration. */
/* Used to turn recursion into iteration. We can't rely on GCC's
tail-recursion elimination since we need to keep accumulating values
in HASH. */
repeat:
if (x == 0)
return hash;
@ -2112,48 +2123,52 @@ canon_hash (rtx x, enum machine_mode mode)
case REG:
{
unsigned int regno = REGNO (x);
bool record;
/* On some machines, we can't record any non-fixed hard register,
because extending its life will cause reload problems. We
consider ap, fp, sp, gp to be fixed for this purpose.
We also consider CCmode registers to be fixed for this purpose;
failure to do so leads to failure to simplify 0<100 type of
conditionals.
On all machines, we can't record any global registers.
Nor should we record any register that is in a small
class, as defined by CLASS_LIKELY_SPILLED_P. */
if (regno >= FIRST_PSEUDO_REGISTER)
record = true;
else if (x == frame_pointer_rtx
|| x == hard_frame_pointer_rtx
|| x == arg_pointer_rtx
|| x == stack_pointer_rtx
|| x == pic_offset_table_rtx)
record = true;
else if (global_regs[regno])
record = false;
else if (fixed_regs[regno])
record = true;
else if (GET_MODE_CLASS (GET_MODE (x)) == MODE_CC)
record = true;
else if (SMALL_REGISTER_CLASSES)
record = false;
else if (CLASS_LIKELY_SPILLED_P (REGNO_REG_CLASS (regno)))
record = false;
else
record = true;
if (!record)
if (!reload_completed)
{
do_not_record = 1;
return 0;
/* On some machines, we can't record any non-fixed hard register,
because extending its life will cause reload problems. We
consider ap, fp, sp, gp to be fixed for this purpose.
We also consider CCmode registers to be fixed for this purpose;
failure to do so leads to failure to simplify 0<100 type of
conditionals.
On all machines, we can't record any global registers.
Nor should we record any register that is in a small
class, as defined by CLASS_LIKELY_SPILLED_P. */
bool record;
if (regno >= FIRST_PSEUDO_REGISTER)
record = true;
else if (x == frame_pointer_rtx
|| x == hard_frame_pointer_rtx
|| x == arg_pointer_rtx
|| x == stack_pointer_rtx
|| x == pic_offset_table_rtx)
record = true;
else if (global_regs[regno])
record = false;
else if (fixed_regs[regno])
record = true;
else if (GET_MODE_CLASS (GET_MODE (x)) == MODE_CC)
record = true;
else if (SMALL_REGISTER_CLASSES)
record = false;
else if (CLASS_LIKELY_SPILLED_P (REGNO_REG_CLASS (regno)))
record = false;
else
record = true;
if (!record)
{
*do_not_record_p = 1;
return 0;
}
}
hash += ((unsigned) REG << 7) + (unsigned) REG_QTY (regno);
hash += ((unsigned int) REG << 7);
hash += (have_reg_qty ? (unsigned) REG_QTY (regno) : regno);
return hash;
}
@ -2164,7 +2179,7 @@ canon_hash (rtx x, enum machine_mode mode)
{
if (REG_P (SUBREG_REG (x)))
{
hash += (((unsigned) SUBREG << 7)
hash += (((unsigned int) SUBREG << 7)
+ REGNO (SUBREG_REG (x))
+ (SUBREG_BYTE (x) / UNITS_PER_WORD));
return hash;
@ -2173,21 +2188,19 @@ canon_hash (rtx x, enum machine_mode mode)
}
case CONST_INT:
{
unsigned HOST_WIDE_INT tem = INTVAL (x);
hash += ((unsigned) CONST_INT << 7) + (unsigned) mode + tem;
return hash;
}
hash += (((unsigned int) CONST_INT << 7) + (unsigned int) mode
+ (unsigned int) INTVAL (x));
return hash;
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);
hash += (unsigned int) code + (unsigned int) GET_MODE (x);
if (GET_MODE (x) != VOIDmode)
hash += real_hash (CONST_DOUBLE_REAL_VALUE (x));
else
hash += ((unsigned) CONST_DOUBLE_LOW (x)
+ (unsigned) CONST_DOUBLE_HIGH (x));
hash += ((unsigned int) CONST_DOUBLE_LOW (x)
+ (unsigned int) CONST_DOUBLE_HIGH (x));
return hash;
case CONST_VECTOR:
@ -2200,7 +2213,8 @@ canon_hash (rtx x, enum machine_mode mode)
for (i = 0; i < units; ++i)
{
elt = CONST_VECTOR_ELT (x, i);
hash += canon_hash (elt, GET_MODE (elt));
hash += hash_rtx (elt, GET_MODE (elt), do_not_record_p,
hash_arg_in_memory_p, have_reg_qty);
}
return hash;
@ -2208,23 +2222,39 @@ canon_hash (rtx x, enum machine_mode mode)
/* Assume there is only one rtx object for any given label. */
case LABEL_REF:
hash += ((unsigned) LABEL_REF << 7) + (unsigned long) XEXP (x, 0);
/* 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)));
return hash;
case SYMBOL_REF:
hash += ((unsigned) SYMBOL_REF << 7) + (unsigned long) XSTR (x, 0);
return hash;
{
/* Don't hash on the symbol's address to avoid bootstrap differences.
Different hash values may cause expressions to be recorded in
different orders and thus different registers to be used in the
final assembler. This also avoids differences in the dump files
between various stages. */
unsigned int h = 0;
const unsigned char *p = (const unsigned char *) XSTR (x, 0);
while (*p)
h += (h << 7) + *p++; /* ??? revisit */
hash += ((unsigned int) SYMBOL_REF << 7) + h;
return hash;
}
case MEM:
/* We don't record if marked volatile or if BLKmode since we don't
know the size of the move. */
if (MEM_VOLATILE_P (x) || GET_MODE (x) == BLKmode)
{
do_not_record = 1;
*do_not_record_p = 1;
return 0;
}
if (!MEM_READONLY_P (x))
hash_arg_in_memory = 1;
if (hash_arg_in_memory_p && !MEM_READONLY_P (x))
*hash_arg_in_memory_p = 1;
/* Now that we have already found this special case,
might as well speed it up as much as possible. */
@ -2236,15 +2266,16 @@ canon_hash (rtx x, enum machine_mode mode)
/* A USE that mentions non-volatile memory needs special
handling since the MEM may be BLKmode which normally
prevents an entry from being made. Pure calls are
marked by a USE which mentions BLKmode memory. */
marked by a USE which mentions BLKmode memory.
See calls.c:emit_call_1. */
if (MEM_P (XEXP (x, 0))
&& ! MEM_VOLATILE_P (XEXP (x, 0)))
{
hash += (unsigned) USE;
x = XEXP (x, 0);
if (!MEM_READONLY_P (x))
hash_arg_in_memory = 1;
if (hash_arg_in_memory_p && !MEM_READONLY_P (x))
*hash_arg_in_memory_p = 1;
/* Now that we have already found this special case,
might as well speed it up as much as possible. */
@ -2264,34 +2295,36 @@ canon_hash (rtx x, enum machine_mode mode)
case CC0:
case CALL:
case UNSPEC_VOLATILE:
do_not_record = 1;
*do_not_record_p = 1;
return 0;
case ASM_OPERANDS:
if (MEM_VOLATILE_P (x))
{
do_not_record = 1;
*do_not_record_p = 1;
return 0;
}
else
{
/* We don't want to take the filename and line into account. */
hash += (unsigned) code + (unsigned) GET_MODE (x)
+ canon_hash_string (ASM_OPERANDS_TEMPLATE (x))
+ canon_hash_string (ASM_OPERANDS_OUTPUT_CONSTRAINT (x))
+ hash_rtx_string (ASM_OPERANDS_TEMPLATE (x))
+ hash_rtx_string (ASM_OPERANDS_OUTPUT_CONSTRAINT (x))
+ (unsigned) ASM_OPERANDS_OUTPUT_IDX (x);
if (ASM_OPERANDS_INPUT_LENGTH (x))
{
for (i = 1; i < ASM_OPERANDS_INPUT_LENGTH (x); i++)
{
hash += (canon_hash (ASM_OPERANDS_INPUT (x, i),
GET_MODE (ASM_OPERANDS_INPUT (x, i)))
+ canon_hash_string (ASM_OPERANDS_INPUT_CONSTRAINT
(x, i)));
hash += (hash_rtx (ASM_OPERANDS_INPUT (x, i),
GET_MODE (ASM_OPERANDS_INPUT (x, i)),
do_not_record_p, hash_arg_in_memory_p,
have_reg_qty)
+ hash_rtx_string
(ASM_OPERANDS_INPUT_CONSTRAINT (x, i)));
}
hash += canon_hash_string (ASM_OPERANDS_INPUT_CONSTRAINT (x, 0));
hash += hash_rtx_string (ASM_OPERANDS_INPUT_CONSTRAINT (x, 0));
x = ASM_OPERANDS_INPUT (x, 0);
mode = GET_MODE (x);
goto repeat;
@ -2312,48 +2345,59 @@ canon_hash (rtx x, enum machine_mode mode)
{
if (fmt[i] == 'e')
{
rtx tem = XEXP (x, i);
/* If we are about to do the last recursive call
needed at this level, change it into iteration.
This function is called enough to be worth it. */
if (i == 0)
{
x = tem;
x = XEXP (x, i);
goto repeat;
}
hash += canon_hash (tem, 0);
hash += hash_rtx (XEXP (x, i), 0, do_not_record_p,
hash_arg_in_memory_p, have_reg_qty);
}
else if (fmt[i] == 'E')
for (j = 0; j < XVECLEN (x, i); j++)
hash += canon_hash (XVECEXP (x, i, j), 0);
{
hash += hash_rtx (XVECEXP (x, i, j), 0, do_not_record_p,
hash_arg_in_memory_p, have_reg_qty);
}
else if (fmt[i] == 's')
hash += canon_hash_string (XSTR (x, i));
hash += hash_rtx_string (XSTR (x, i));
else if (fmt[i] == 'i')
{
unsigned tem = XINT (x, i);
hash += tem;
}
hash += (unsigned int) XINT (x, i);
else if (fmt[i] == '0' || fmt[i] == 't')
/* Unused. */
;
else
abort ();
}
return hash;
}
/* Like canon_hash but with no side effects. */
/* Hash an rtx X for cse via hash_rtx.
Stores 1 in do_not_record if any subexpression is volatile.
Stores 1 in hash_arg_in_memory if X contains a mem rtx which
does not have the RTX_UNCHANGING_P bit set. */
static unsigned
static inline unsigned
canon_hash (rtx x, enum machine_mode mode)
{
return hash_rtx (x, mode, &do_not_record, &hash_arg_in_memory, true);
}
/* Like canon_hash but with no side effects, i.e. do_not_record
and hash_arg_in_memory are not changed. */
static inline unsigned
safe_hash (rtx x, enum machine_mode mode)
{
int save_do_not_record = do_not_record;
int save_hash_arg_in_memory = hash_arg_in_memory;
unsigned hash = canon_hash (x, mode);
hash_arg_in_memory = save_hash_arg_in_memory;
do_not_record = save_do_not_record;
return hash;
int dummy_do_not_record;
return hash_rtx (x, mode, &dummy_do_not_record, NULL, true);
}
/* Return 1 iff X and Y would canonicalize into the same thing,
@ -2363,16 +2407,10 @@ safe_hash (rtx x, enum machine_mode mode)
and Y was found in the hash table. We check register refs
in Y for being marked as valid.
If EQUAL_VALUES is nonzero, we allow a register to match a constant value
that is known to be in the register. Ordinarily, we don't allow them
to match, because letting them match would cause unpredictable results
in all the places that search a hash table chain for an equivalent
for a given value. A possible equivalent that has different structure
has its hash code computed from different data. Whether the hash code
is the same as that of the given value is pure luck. */
If FOR_GCSE is true, we compare X and Y for equivalence for GCSE. */
static int
exp_equiv_p (rtx x, rtx y, int validate, int equal_values)
int
exp_equiv_p (rtx x, rtx y, int validate, bool for_gcse)
{
int i, j;
enum rtx_code code;
@ -2382,42 +2420,13 @@ exp_equiv_p (rtx x, rtx y, int validate, int equal_values)
if VALIDATE is nonzero. */
if (x == y && !validate)
return 1;
if (x == 0 || y == 0)
return x == y;
code = GET_CODE (x);
if (code != GET_CODE (y))
{
if (!equal_values)
return 0;
/* If X is a constant and Y is a register or vice versa, they may be
equivalent. We only have to validate if Y is a register. */
if (CONSTANT_P (x) && REG_P (y)
&& REGNO_QTY_VALID_P (REGNO (y)))
{
int y_q = REG_QTY (REGNO (y));
struct qty_table_elem *y_ent = &qty_table[y_q];
if (GET_MODE (y) == y_ent->mode
&& rtx_equal_p (x, y_ent->const_rtx)
&& (! validate || REG_IN_TABLE (REGNO (y)) == REG_TICK (REGNO (y))))
return 1;
}
if (CONSTANT_P (y) && code == REG
&& REGNO_QTY_VALID_P (REGNO (x)))
{
int x_q = REG_QTY (REGNO (x));
struct qty_table_elem *x_ent = &qty_table[x_q];
if (GET_MODE (x) == x_ent->mode
&& rtx_equal_p (y, x_ent->const_rtx))
return 1;
}
return 0;
}
return 0;
/* (MULT:SI x y) and (MULT:HI x y) are NOT equivalent. */
if (GET_MODE (x) != GET_MODE (y))
@ -2437,29 +2446,48 @@ exp_equiv_p (rtx x, rtx y, int validate, int equal_values)
return XSTR (x, 0) == XSTR (y, 0);
case REG:
{
unsigned int regno = REGNO (y);
unsigned int endregno
= regno + (regno >= FIRST_PSEUDO_REGISTER ? 1
: hard_regno_nregs[regno][GET_MODE (y)]);
unsigned int i;
if (for_gcse)
return REGNO (x) == REGNO (y);
else
{
unsigned int regno = REGNO (y);
unsigned int i;
unsigned int endregno
= regno + (regno >= FIRST_PSEUDO_REGISTER ? 1
: hard_regno_nregs[regno][GET_MODE (y)]);
/* If the quantities are not the same, the expressions are not
equivalent. If there are and we are not to validate, they
are equivalent. Otherwise, ensure all regs are up-to-date. */
/* If the quantities are not the same, the expressions are not
equivalent. If there are and we are not to validate, they
are equivalent. Otherwise, ensure all regs are up-to-date. */
if (REG_QTY (REGNO (x)) != REG_QTY (regno))
return 0;
if (! validate)
return 1;
for (i = regno; i < endregno; i++)
if (REG_IN_TABLE (i) != REG_TICK (i))
if (REG_QTY (REGNO (x)) != REG_QTY (regno))
return 0;
return 1;
}
if (! validate)
return 1;
for (i = regno; i < endregno; i++)
if (REG_IN_TABLE (i) != REG_TICK (i))
return 0;
return 1;
}
case MEM:
if (for_gcse)
{
/* Can't merge two expressions in different alias sets, since we
can decide that the expression is transparent in a block when
it isn't, due to it being set with the different alias set. */
if (MEM_ALIAS_SET (x) != MEM_ALIAS_SET (y))
return 0;
/* A volatile mem should not be considered equivalent to any
other. */
if (MEM_VOLATILE_P (x) || MEM_VOLATILE_P (y))
return 0;
}
break;
/* For commutative operations, check both orders. */
case PLUS:
@ -2469,13 +2497,14 @@ exp_equiv_p (rtx x, rtx y, int validate, int equal_values)
case XOR:
case NE:
case EQ:
return ((exp_equiv_p (XEXP (x, 0), XEXP (y, 0), validate, equal_values)
return ((exp_equiv_p (XEXP (x, 0), XEXP (y, 0),
validate, for_gcse)
&& exp_equiv_p (XEXP (x, 1), XEXP (y, 1),
validate, equal_values))
validate, for_gcse))
|| (exp_equiv_p (XEXP (x, 0), XEXP (y, 1),
validate, equal_values)
validate, for_gcse)
&& exp_equiv_p (XEXP (x, 1), XEXP (y, 0),
validate, equal_values)));
validate, for_gcse)));
case ASM_OPERANDS:
/* We don't use the generic code below because we want to
@ -2498,7 +2527,7 @@ exp_equiv_p (rtx x, rtx y, int validate, int equal_values)
for (i = ASM_OPERANDS_INPUT_LENGTH (x) - 1; i >= 0; i--)
if (! exp_equiv_p (ASM_OPERANDS_INPUT (x, i),
ASM_OPERANDS_INPUT (y, i),
validate, equal_values)
validate, for_gcse)
|| strcmp (ASM_OPERANDS_INPUT_CONSTRAINT (x, i),
ASM_OPERANDS_INPUT_CONSTRAINT (y, i)))
return 0;
@ -2511,7 +2540,7 @@ exp_equiv_p (rtx x, rtx y, int validate, int equal_values)
}
/* Compare the elements. If any pair of corresponding elements
fail to match, return 0 for the whole things. */
fail to match, return 0 for the whole thing. */
fmt = GET_RTX_FORMAT (code);
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
@ -2519,7 +2548,8 @@ exp_equiv_p (rtx x, rtx y, int validate, int equal_values)
switch (fmt[i])
{
case 'e':
if (! exp_equiv_p (XEXP (x, i), XEXP (y, i), validate, equal_values))
if (! exp_equiv_p (XEXP (x, i), XEXP (y, i),
validate, for_gcse))
return 0;
break;
@ -2528,7 +2558,7 @@ exp_equiv_p (rtx x, rtx y, int validate, int equal_values)
return 0;
for (j = 0; j < XVECLEN (x, i); j++)
if (! exp_equiv_p (XVECEXP (x, i, j), XVECEXP (y, i, j),
validate, equal_values))
validate, for_gcse))
return 0;
break;
@ -2827,7 +2857,7 @@ find_best_addr (rtx insn, rtx *loc, enum machine_mode mode)
if (! p->flag)
{
if ((REG_P (p->exp)
|| exp_equiv_p (p->exp, p->exp, 1, 0))
|| exp_equiv_p (p->exp, p->exp, 1, false))
&& ((exp_cost = address_cost (p->exp, mode)) < best_addr_cost
|| (exp_cost == best_addr_cost
&& ((p->cost + 1) >> 1) > best_rtx_cost)))
@ -2903,7 +2933,7 @@ find_best_addr (rtx insn, rtx *loc, enum machine_mode mode)
p = p->next_same_value, count++)
if (! p->flag
&& (REG_P (p->exp)
|| exp_equiv_p (p->exp, p->exp, 1, 0)))
|| exp_equiv_p (p->exp, p->exp, 1, false)))
{
rtx new = simplify_gen_binary (GET_CODE (*loc), Pmode,
p->exp, op1);
@ -3012,8 +3042,7 @@ find_comparison_args (enum rtx_code code, rtx *parg1, rtx *parg2,
if (x == 0)
/* Look up ARG1 in the hash table and see if it has an equivalence
that lets us see what is being compared. */
p = lookup (arg1, safe_hash (arg1, GET_MODE (arg1)) & HASH_MASK,
GET_MODE (arg1));
p = lookup (arg1, SAFE_HASH (arg1, GET_MODE (arg1)), GET_MODE (arg1));
if (p)
{
p = p->first_same_value;
@ -3038,7 +3067,7 @@ find_comparison_args (enum rtx_code code, rtx *parg1, rtx *parg2,
#endif
/* If the entry isn't valid, skip it. */
if (! exp_equiv_p (p->exp, p->exp, 1, 0))
if (! exp_equiv_p (p->exp, p->exp, 1, false))
continue;
if (GET_CODE (p->exp) == COMPARE
@ -3235,7 +3264,7 @@ fold_rtx (rtx x, rtx insn)
if (GET_CODE (elt->exp) == SUBREG
&& GET_MODE (SUBREG_REG (elt->exp)) == mode
&& exp_equiv_p (elt->exp, elt->exp, 1, 0))
&& exp_equiv_p (elt->exp, elt->exp, 1, false))
return copy_rtx (SUBREG_REG (elt->exp));
}
@ -3264,8 +3293,6 @@ fold_rtx (rtx x, rtx insn)
{
struct table_elt *elt;
/* We can use HASH here since we know that canon_hash won't be
called. */
elt = lookup (folded_arg0,
HASH (folded_arg0, GET_MODE (folded_arg0)),
GET_MODE (folded_arg0));
@ -3370,7 +3397,7 @@ fold_rtx (rtx x, rtx insn)
&& GET_MODE (SUBREG_REG (elt->exp)) == mode
&& (GET_MODE_SIZE (GET_MODE (folded_arg0))
<= UNITS_PER_WORD)
&& exp_equiv_p (elt->exp, elt->exp, 1, 0))
&& exp_equiv_p (elt->exp, elt->exp, 1, false))
new = copy_rtx (SUBREG_REG (elt->exp));
if (new)
@ -3829,11 +3856,11 @@ fold_rtx (rtx x, rtx insn)
&& (REG_QTY (REGNO (folded_arg0))
== REG_QTY (REGNO (folded_arg1))))
|| ((p0 = lookup (folded_arg0,
(safe_hash (folded_arg0, mode_arg0)
& HASH_MASK), mode_arg0))
SAFE_HASH (folded_arg0, mode_arg0),
mode_arg0))
&& (p1 = lookup (folded_arg1,
(safe_hash (folded_arg1, mode_arg0)
& HASH_MASK), mode_arg0))
SAFE_HASH (folded_arg1, mode_arg0),
mode_arg0))
&& p0->first_same_value == p1->first_same_value))
{
/* Sadly two equal NaNs are not equivalent. */
@ -4007,8 +4034,7 @@ fold_rtx (rtx x, rtx insn)
{
rtx new_const = GEN_INT (-INTVAL (const_arg1));
struct table_elt *p
= lookup (new_const, safe_hash (new_const, mode) & HASH_MASK,
mode);
= lookup (new_const, SAFE_HASH (new_const, mode), mode);
if (p)
for (p = p->first_same_value; p; p = p->next_same_value)
@ -4195,7 +4221,7 @@ equiv_constant (rtx x)
if (CONSTANT_P (x))
return x;
elt = lookup (x, safe_hash (x, GET_MODE (x)) & HASH_MASK, GET_MODE (x));
elt = lookup (x, SAFE_HASH (x, GET_MODE (x)), GET_MODE (x));
if (elt == 0)
return 0;
@ -5182,7 +5208,7 @@ cse_insn (rtx insn, rtx libcall_insn)
/* If the expression is not valid, ignore it. Then we do not
have to check for validity below. In most cases, we can use
`rtx_equal_p', since canonicalization has already been done. */
if (code != REG && ! exp_equiv_p (p->exp, p->exp, 1, 0))
if (code != REG && ! exp_equiv_p (p->exp, p->exp, 1, false))
continue;
/* Also skip paradoxical subregs, unless that's what we're
@ -5279,7 +5305,7 @@ cse_insn (rtx insn, rtx libcall_insn)
/* Skip invalid entries. */
while (elt && !REG_P (elt->exp)
&& ! exp_equiv_p (elt->exp, elt->exp, 1, 0))
&& ! exp_equiv_p (elt->exp, elt->exp, 1, false))
elt = elt->next_same_value;
/* A paradoxical subreg would be bad here: it'll be the right
@ -6006,7 +6032,7 @@ cse_insn (rtx insn, rtx libcall_insn)
/* Ignore invalid entries. */
if (!REG_P (elt->exp)
&& ! exp_equiv_p (elt->exp, elt->exp, 1, 0))
&& ! exp_equiv_p (elt->exp, elt->exp, 1, false))
continue;
/* We may have already been playing subreg games. If the
@ -6059,7 +6085,7 @@ cse_insn (rtx insn, rtx libcall_insn)
/* Ignore invalid entries. */
while (classp
&& !REG_P (classp->exp)
&& ! exp_equiv_p (classp->exp, classp->exp, 1, 0))
&& ! exp_equiv_p (classp->exp, classp->exp, 1, false))
classp = classp->next_same_value;
}
}

View File

@ -55,7 +55,7 @@ static int discard_useless_locs (void **, void *);
static int discard_useless_values (void **, void *);
static void remove_useless_values (void);
static rtx wrap_constant (enum machine_mode, rtx);
static unsigned int hash_rtx (rtx, enum machine_mode, int);
static unsigned int cselib_hash_rtx (rtx, enum machine_mode, int);
static cselib_val *new_cselib_val (unsigned int, enum machine_mode);
static void add_mem_for_addr (cselib_val *, cselib_val *, rtx);
static cselib_val *cselib_lookup_mem (rtx, int);
@ -257,8 +257,8 @@ entry_and_rtx_equal_p (const void *entry, const void *x_arg)
}
/* The hash function for our hash table. The value is always computed with
hash_rtx when adding an element; this function just extracts the hash
value from a cselib_val structure. */
cselib_hash_rtx when adding an element; this function just extracts the
hash value from a cselib_val structure. */
static hashval_t
get_value_hash (const void *entry)
@ -554,7 +554,7 @@ wrap_constant (enum machine_mode mode, rtx x)
otherwise the mode of X is used. */
static unsigned int
hash_rtx (rtx x, enum machine_mode mode, int create)
cselib_hash_rtx (rtx x, enum machine_mode mode, int create)
{
cselib_val *e;
int i, j;
@ -600,7 +600,7 @@ hash_rtx (rtx x, enum machine_mode mode, int create)
for (i = 0; i < units; ++i)
{
elt = CONST_VECTOR_ELT (x, i);
hash += hash_rtx (elt, GET_MODE (elt), 0);
hash += cselib_hash_rtx (elt, GET_MODE (elt), 0);
}
return hash;
@ -646,7 +646,7 @@ hash_rtx (rtx x, enum machine_mode mode, int create)
if (fmt[i] == 'e')
{
rtx tem = XEXP (x, i);
unsigned int tem_hash = hash_rtx (tem, 0, create);
unsigned int tem_hash = cselib_hash_rtx (tem, 0, create);
if (tem_hash == 0)
return 0;
@ -656,7 +656,7 @@ hash_rtx (rtx x, enum machine_mode mode, int create)
else if (fmt[i] == 'E')
for (j = 0; j < XVECLEN (x, i); j++)
{
unsigned int tem_hash = hash_rtx (XVECEXP (x, i, j), 0, create);
unsigned int tem_hash = cselib_hash_rtx (XVECEXP (x, i, j), 0, create);
if (tem_hash == 0)
return 0;
@ -926,7 +926,7 @@ cselib_lookup (rtx x, enum machine_mode mode, int create)
if (MEM_P (x))
return cselib_lookup_mem (x, create);
hashval = hash_rtx (x, mode, create);
hashval = cselib_hash_rtx (x, mode, create);
/* Can't even create if hashing is not possible. */
if (! hashval)
return 0;

1028
gcc/gcse.c

File diff suppressed because it is too large Load Diff

View File

@ -841,10 +841,10 @@ rest_of_handle_sched2 (void)
static void
rest_of_handle_gcse2 (void)
{
timevar_push (TV_RELOAD_CSE_REGS);
timevar_push (TV_GCSE_AFTER_RELOAD);
open_dump_file (DFI_gcse2, current_function_decl);
gcse_after_reload_main (get_insns (), dump_file);
gcse_after_reload_main (get_insns ());
rebuild_jump_labels (get_insns ());
delete_trivially_dead_insns (get_insns (), max_reg_num ());
close_dump_file (DFI_gcse2, print_rtl_with_bb, get_insns ());
@ -855,7 +855,7 @@ rest_of_handle_gcse2 (void)
verify_flow_info ();
#endif
timevar_pop (TV_RELOAD_CSE_REGS);
timevar_pop (TV_GCSE_AFTER_RELOAD);
}
/* Register allocation pre-pass, to reduce number of moves necessary

1379
gcc/postreload-gcse.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -2119,6 +2119,8 @@ extern int rtx_to_tree_code (enum rtx_code);
extern int delete_trivially_dead_insns (rtx, int);
extern int cse_main (rtx, int, int, FILE *);
extern void cse_condition_code_reg (void);
extern int exp_equiv_p (rtx, rtx, int, bool);
extern unsigned hash_rtx (rtx x, enum machine_mode, int *, int *, bool);
/* In jump.c */
extern int comparison_dominates_p (enum rtx_code, enum rtx_code);
@ -2265,7 +2267,9 @@ extern bool can_copy_p (enum machine_mode);
extern rtx fis_get_condition (rtx);
extern int gcse_main (rtx, FILE *);
extern int bypass_jumps (FILE *);
extern void gcse_after_reload_main (rtx, FILE *);
/* In postreload-gcse.c */
extern void gcse_after_reload_main (rtx);
/* In global.c */
extern void mark_elimination (int, int);

View File

@ -122,6 +122,7 @@ DEFTIMEVAR (TV_SCHED , "scheduling")
DEFTIMEVAR (TV_LOCAL_ALLOC , "local alloc")
DEFTIMEVAR (TV_GLOBAL_ALLOC , "global alloc")
DEFTIMEVAR (TV_RELOAD_CSE_REGS , "reload CSE regs")
DEFTIMEVAR (TV_GCSE_AFTER_RELOAD , "load CSE after reload")
DEFTIMEVAR (TV_FLOW2 , "flow 2")
DEFTIMEVAR (TV_IFCVT2 , "if-conversion 2")
DEFTIMEVAR (TV_PEEPHOLE2 , "peephole 2")