/* Common subexpression elimination for GNU compiler.
Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998
- 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
+ 1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
This file is part of GCC.
#include "config.h"
/* stdio.h must precede rtl.h for FFS. */
#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
#include "rtl.h"
#include "tm_p.h"
#include "output.h"
#include "ggc.h"
#include "timevar.h"
+#include "except.h"
+#include "target.h"
+#include "params.h"
/* The basic idea of common subexpression elimination is to go
through the code, keeping a record of expressions that would
rtx comparison_const;
int comparison_qty;
unsigned int first_reg, last_reg;
- enum machine_mode mode;
- enum rtx_code comparison_code;
+ /* The sizes of these fields should match the sizes of the
+ code and mode fields of struct rtx_def (see rtl.h). */
+ ENUM_BITFIELD(rtx_code) comparison_code : 16;
+ ENUM_BITFIELD(machine_mode) mode : 8;
};
/* The table of all qtys, indexed by qty number. */
static rtx prev_insn_cc0;
static enum machine_mode prev_insn_cc0_mode;
-#endif
/* Previous actual insn. 0 if at first insn of basic block. */
static rtx prev_insn;
+#endif
/* Insn being scanned. */
reg_tick value, such expressions existing in the hash table are
invalid. */
int reg_in_table;
+
+ /* The SUBREG that was set when REG_TICK was last incremented. Set
+ to -1 if the last store was to the whole register, not a subreg. */
+ unsigned int subreg_ticked;
};
/* A free list of cse_reg_info entries. */
struct table_elt *related_value;
int cost;
int regcost;
- enum machine_mode mode;
+ /* 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;
char in_memory;
char is_const;
char flag;
#define REG_IN_TABLE(N) ((GET_CSE_REG_INFO (N))->reg_in_table)
+/* Get the SUBREG set at the last increment to REG_TICK (-1 if not a
+ SUBREG). */
+
+#define SUBREG_TICKED(N) ((GET_CSE_REG_INFO (N))->subreg_ticked)
+
/* Get the quantity number for REG. */
#define REG_QTY(N) ((GET_CSE_REG_INFO (N))->reg_qty)
static int constant_pool_entries_cost;
-/* Define maximum length of a branch path. */
-
-#define PATHLENGTH 10
-
/* This data describes a block that will be processed by cse_basic_block. */
struct cse_basic_block_data
except that it is used when the destination label is not preceded
by a BARRIER. */
enum taken {TAKEN, NOT_TAKEN, AROUND} status;
- } path[PATHLENGTH];
+ } *path;
};
-/* Nonzero if X has the form (PLUS frame-pointer integer). We check for
- virtual regs here because the simplify_*_operation routines are called
- by integrate.c, which is called before virtual register instantiation.
-
- ?!? FIXED_BASE_PLUS_P and NONZERO_BASE_PLUS_P need to move into
- a header file so that their definitions can be shared with the
- simplification routines in simplify-rtx.c. Until then, do not
- change these macros without also changing the copy in simplify-rtx.c. */
-
-#define FIXED_BASE_PLUS_P(X) \
- ((X) == frame_pointer_rtx || (X) == hard_frame_pointer_rtx \
- || ((X) == arg_pointer_rtx && fixed_regs[ARG_POINTER_REGNUM])\
- || (X) == virtual_stack_vars_rtx \
- || (X) == virtual_incoming_args_rtx \
- || (GET_CODE (X) == PLUS && GET_CODE (XEXP (X, 1)) == CONST_INT \
- && (XEXP (X, 0) == frame_pointer_rtx \
- || XEXP (X, 0) == hard_frame_pointer_rtx \
- || ((X) == arg_pointer_rtx \
- && fixed_regs[ARG_POINTER_REGNUM]) \
- || XEXP (X, 0) == virtual_stack_vars_rtx \
- || XEXP (X, 0) == virtual_incoming_args_rtx)) \
- || GET_CODE (X) == ADDRESSOF)
-
-/* Similar, but also allows reference to the stack pointer.
-
- This used to include FIXED_BASE_PLUS_P, however, we can't assume that
- arg_pointer_rtx by itself is nonzero, because on at least one machine,
- the i960, the arg pointer is zero when it is unused. */
-
-#define NONZERO_BASE_PLUS_P(X) \
- ((X) == frame_pointer_rtx || (X) == hard_frame_pointer_rtx \
- || (X) == virtual_stack_vars_rtx \
- || (X) == virtual_incoming_args_rtx \
- || (GET_CODE (X) == PLUS && GET_CODE (XEXP (X, 1)) == CONST_INT \
- && (XEXP (X, 0) == frame_pointer_rtx \
- || XEXP (X, 0) == hard_frame_pointer_rtx \
- || ((X) == arg_pointer_rtx \
- && fixed_regs[ARG_POINTER_REGNUM]) \
- || XEXP (X, 0) == virtual_stack_vars_rtx \
- || XEXP (X, 0) == virtual_incoming_args_rtx)) \
- || (X) == stack_pointer_rtx \
- || (X) == virtual_stack_dynamic_rtx \
- || (X) == virtual_outgoing_args_rtx \
- || (GET_CODE (X) == PLUS && GET_CODE (XEXP (X, 1)) == CONST_INT \
- && (XEXP (X, 0) == stack_pointer_rtx \
- || XEXP (X, 0) == virtual_stack_dynamic_rtx \
- || XEXP (X, 0) == virtual_outgoing_args_rtx)) \
- || GET_CODE (X) == ADDRESSOF)
-
+static bool fixed_base_plus_p PARAMS ((rtx x));
static int notreg_cost PARAMS ((rtx, enum rtx_code));
static int approx_reg_cost_1 PARAMS ((rtx *, void *));
static int approx_reg_cost PARAMS ((rtx));
static bool set_live_p PARAMS ((rtx, rtx, int *));
static bool dead_libcall_p PARAMS ((rtx, int *));
\f
+/* Nonzero if X has the form (PLUS frame-pointer integer). We check for
+ virtual regs here because the simplify_*_operation routines are called
+ by integrate.c, which is called before virtual register instantiation. */
+
+static bool
+fixed_base_plus_p (x)
+ rtx x;
+{
+ switch (GET_CODE (x))
+ {
+ case REG:
+ if (x == frame_pointer_rtx || x == hard_frame_pointer_rtx)
+ return true;
+ if (x == arg_pointer_rtx && fixed_regs[ARG_POINTER_REGNUM])
+ return true;
+ if (REGNO (x) >= FIRST_VIRTUAL_REGISTER
+ && REGNO (x) <= LAST_VIRTUAL_REGISTER)
+ return true;
+ return false;
+
+ case PLUS:
+ if (GET_CODE (XEXP (x, 1)) != CONST_INT)
+ return false;
+ return fixed_base_plus_p (XEXP (x, 0));
+
+ case ADDRESSOF:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
/* Dump the expressions in the equivalence class indicated by CLASSP.
This function is used only for debugging. */
void
preferrable (cost_a, regcost_a, cost_b, regcost_b)
int cost_a, regcost_a, cost_b, regcost_b;
{
- /* First, get rid of a cases involving expressions that are entirely
+ /* First, get rid of cases involving expressions that are entirely
unwanted. */
if (cost_a != cost_b)
{
return 0;
/* Compute the default costs of certain things.
- Note that RTX_COSTS can override the defaults. */
+ Note that targetm.rtx_costs can override the defaults. */
code = GET_CODE (x);
switch (code)
+ GET_MODE_SIZE (GET_MODE (x)) / UNITS_PER_WORD);
break;
-#ifdef RTX_COSTS
- RTX_COSTS (x, code, outer_code);
-#endif
-#ifdef CONST_COSTS
- CONST_COSTS (x, code, outer_code);
-#endif
-
default:
-#ifdef DEFAULT_RTX_COSTS
- DEFAULT_RTX_COSTS (x, code, outer_code);
-#endif
+ if ((*targetm.rtx_costs) (x, code, outer_code, &total))
+ return total;
break;
}
rtx x;
enum machine_mode mode;
{
- /* The ADDRESS_COST macro does not deal with ADDRESSOF nodes. But,
+ /* The address_cost target hook does not deal with ADDRESSOF nodes. But,
during CSE, such nodes are present. Using an ADDRESSOF node which
refers to the address of a REG is a good thing because we can then
turn (MEM (ADDRESSSOF (REG))) into just plain REG. */
/* We may be asked for cost of various unusual addresses, such as operands
of push instruction. It is not worthwhile to complicate writing
- of ADDRESS_COST macro by such cases. */
+ of the target hook by such cases. */
if (!memory_address_p (mode, x))
return 1000;
-#ifdef ADDRESS_COST
- return ADDRESS_COST (x);
-#else
- return rtx_cost (x, MEM);
-#endif
+
+ return (*targetm.address_cost) (x);
}
+/* If the target doesn't override, compute the cost as with arithmetic. */
+
+int
+default_address_cost (x)
+ rtx x;
+{
+ return rtx_cost (x, MEM);
+}
\f
static struct cse_reg_info *
get_cse_reg_info (regno)
/* Initialize it. */
p->reg_tick = 1;
p->reg_in_table = -1;
+ p->subreg_ticked = -1;
p->reg_qty = regno;
p->regno = regno;
p->next = cse_reg_info_used_list;
}
}
- prev_insn = 0;
-
#ifdef HAVE_cc0
+ prev_insn = 0;
prev_insn_cc0 = 0;
#endif
}
remove_invalid_refs (i);
REG_IN_TABLE (i) = REG_TICK (i);
+ SUBREG_TICKED (i) = -1;
}
return 0;
if (REG_IN_TABLE (i) >= 0 && REG_IN_TABLE (i) != REG_TICK (i))
{
- /* If reg_tick has been incremented more than once since
- reg_in_table was last set, that means that the entire
- register has been set before, so discard anything memorized
- for the entire register, including all SUBREG expressions. */
- if (REG_IN_TABLE (i) != REG_TICK (i) - 1)
+ /* If REG_IN_TABLE (i) differs from REG_TICK (i) by one, and
+ the last store to this register really stored into this
+ subreg, then remove the memory of this subreg.
+ Otherwise, remove any memory of the entire register and
+ all its subregs from the table. */
+ if (REG_TICK (i) - REG_IN_TABLE (i) > 1
+ || SUBREG_TICKED (i) != REGNO (SUBREG_REG (x)))
remove_invalid_refs (i);
else
remove_invalid_subreg_refs (i, SUBREG_BYTE (x), GET_MODE (x));
}
REG_IN_TABLE (i) = REG_TICK (i);
+ SUBREG_TICKED (i) = REGNO (SUBREG_REG (x));
return 0;
}
|| (GET_CODE (x) == REG
&& RTX_UNCHANGING_P (x)
&& REGNO (x) >= FIRST_PSEUDO_REGISTER)
- || FIXED_BASE_PLUS_P (x));
+ || fixed_base_plus_p (x));
if (table[hash])
table[hash]->prev_same_hash = elt;
delete_reg_equiv (regno);
REG_TICK (regno)++;
+ SUBREG_TICKED (regno) = -1;
if (regno >= FIRST_PSEUDO_REGISTER)
{
CLEAR_HARD_REG_BIT (hard_regs_in_table, rn);
delete_reg_equiv (rn);
REG_TICK (rn)++;
+ SUBREG_TICKED (rn) = -1;
}
if (in_table)
{
delete_reg_equiv (regno);
if (REG_TICK (regno) >= 0)
- REG_TICK (regno)++;
+ {
+ REG_TICK (regno)++;
+ SUBREG_TICKED (regno) = -1;
+ }
in_table |= (TEST_HARD_REG_BIT (hard_regs_in_table, regno) != 0);
}
the integers representing the constant. */
hash += (unsigned) code + (unsigned) GET_MODE (x);
if (GET_MODE (x) != VOIDmode)
- for (i = 2; i < GET_RTX_LENGTH (CONST_DOUBLE); i++)
- {
- unsigned HOST_WIDE_INT tem = XWINT (x, i);
- hash += tem;
- }
+ hash += real_hash (CONST_DOUBLE_REAL_VALUE (x));
else
hash += ((unsigned) CONST_DOUBLE_LOW (x)
+ (unsigned) CONST_DOUBLE_HIGH (x));
do_not_record = 1;
return 0;
}
- if (! RTX_UNCHANGING_P (x) || FIXED_BASE_PLUS_P (XEXP (x, 0)))
- {
- hash_arg_in_memory = 1;
- }
+ if (! RTX_UNCHANGING_P (x) || fixed_base_plus_p (XEXP (x, 0)))
+ hash_arg_in_memory = 1;
+
/* Now that we have already found this special case,
might as well speed it up as much as possible. */
hash += (unsigned) MEM;
hash += (unsigned) USE;
x = XEXP (x, 0);
- if (! RTX_UNCHANGING_P (x) || FIXED_BASE_PLUS_P (XEXP (x, 0)))
+ if (! RTX_UNCHANGING_P (x) || fixed_base_plus_p (XEXP (x, 0)))
hash_arg_in_memory = 1;
/* Now that we have already found this special case,
replace each register reference inside it
with the "oldest" equivalent register.
- If INSN is non-zero and we are replacing a pseudo with a hard register
+ If INSN is nonzero and we are replacing a pseudo with a hard register
or vice versa, validate_change is used to ensure that INSN remains valid
- after we make our substitution. The calls are made with IN_GROUP non-zero
+ after we make our substitution. The calls are made with IN_GROUP nonzero
so apply_change_group must be called upon the outermost return from this
function (unless INSN is zero). The result of apply_change_group can
generally be discarded since the changes we are making are optional. */
is a good approximation for that cost. However, most RISC machines have
only a few (usually only one) memory reference formats. If an address is
valid at all, it is often just as cheap as any other address. Hence, for
- RISC machines, we use the configuration macro `ADDRESS_COST' to compare the
- costs of various addresses. For two addresses of equal cost, choose the one
- with the highest `rtx_cost' value as that has the potential of eliminating
- the most insns. For equal costs, we choose the first in the equivalence
- class. Note that we ignore the fact that pseudo registers are cheaper
- than hard registers here because we would also prefer the pseudo registers.
- */
+ RISC machines, we use `address_cost' to compare the costs of various
+ addresses. For two addresses of equal cost, choose the one with the
+ highest `rtx_cost' value as that has the potential of eliminating the
+ most insns. For equal costs, we choose the first in the equivalence
+ class. Note that we ignore the fact that pseudo registers are cheaper than
+ hard registers here because we would also prefer the pseudo registers. */
static void
find_best_addr (insn, loc, mode)
{
struct table_elt *elt;
rtx addr = *loc;
-#ifdef ADDRESS_COST
struct table_elt *p;
int found_better = 1;
-#endif
int save_do_not_record = do_not_record;
int save_hash_arg_in_memory = hash_arg_in_memory;
int addr_volatile;
elt = lookup (addr, hash, Pmode);
-#ifndef ADDRESS_COST
- if (elt)
- {
- int our_cost = elt->cost;
-
- /* Find the lowest cost below ours that works. */
- for (elt = elt->first_same_value; elt; elt = elt->next_same_value)
- if (elt->cost < our_cost
- && (GET_CODE (elt->exp) == REG
- || exp_equiv_p (elt->exp, elt->exp, 1, 0))
- && validate_change (insn, loc,
- canon_reg (copy_rtx (elt->exp), NULL_RTX), 0))
- return;
- }
-#else
-
if (elt)
{
/* We need to find the best (under the criteria documented above) entry
if (flag_expensive_optimizations
&& (GET_RTX_CLASS (GET_CODE (*loc)) == '2'
|| GET_RTX_CLASS (GET_CODE (*loc)) == 'c')
- && GET_CODE (XEXP (*loc, 0)) == REG
- && GET_CODE (XEXP (*loc, 1)) == CONST_INT)
+ && GET_CODE (XEXP (*loc, 0)) == REG)
{
- rtx c = XEXP (*loc, 1);
+ rtx op1 = XEXP (*loc, 1);
do_not_record = 0;
hash = HASH (XEXP (*loc, 0), Pmode);
|| exp_equiv_p (p->exp, p->exp, 1, 0)))
{
rtx new = simplify_gen_binary (GET_CODE (*loc), Pmode,
- p->exp, c);
+ p->exp, op1);
int new_cost;
new_cost = address_cost (new, mode);
}
}
}
-#endif
}
\f
/* Given an operation (CODE, *PARG1, *PARG2), where code is a comparison
while (arg2 == CONST0_RTX (GET_MODE (arg1)))
{
- /* Set non-zero when we find something of interest. */
+ /* Set nonzero when we find something of interest. */
rtx x = 0;
int reverse_code = 0;
struct table_elt *p = 0;
else if (GET_RTX_CLASS (GET_CODE (arg1)) == '<')
{
+#ifdef FLOAT_STORE_FLAG_VALUE
+ REAL_VALUE_TYPE fsfv;
+#endif
+
if (code == NE
|| (GET_MODE_CLASS (GET_MODE (arg1)) == MODE_INT
&& code == LT && STORE_FLAG_VALUE == -1)
#ifdef FLOAT_STORE_FLAG_VALUE
|| (GET_MODE_CLASS (GET_MODE (arg1)) == MODE_FLOAT
- && (REAL_VALUE_NEGATIVE
- (FLOAT_STORE_FLAG_VALUE (GET_MODE (arg1)))))
+ && (fsfv = FLOAT_STORE_FLAG_VALUE (GET_MODE (arg1)),
+ REAL_VALUE_NEGATIVE (fsfv)))
#endif
)
x = arg1;
&& code == GE && STORE_FLAG_VALUE == -1)
#ifdef FLOAT_STORE_FLAG_VALUE
|| (GET_MODE_CLASS (GET_MODE (arg1)) == MODE_FLOAT
- && (REAL_VALUE_NEGATIVE
- (FLOAT_STORE_FLAG_VALUE (GET_MODE (arg1)))))
+ && (fsfv = FLOAT_STORE_FLAG_VALUE (GET_MODE (arg1)),
+ REAL_VALUE_NEGATIVE (fsfv)))
#endif
)
x = arg1, reverse_code = 1;
for (; p; p = p->next_same_value)
{
enum machine_mode inner_mode = GET_MODE (p->exp);
+#ifdef FLOAT_STORE_FLAG_VALUE
+ REAL_VALUE_TYPE fsfv;
+#endif
/* If the entry isn't valid, skip it. */
if (! exp_equiv_p (p->exp, p->exp, 1, 0))
#ifdef FLOAT_STORE_FLAG_VALUE
|| (code == LT
&& GET_MODE_CLASS (inner_mode) == MODE_FLOAT
- && (REAL_VALUE_NEGATIVE
- (FLOAT_STORE_FLAG_VALUE (GET_MODE (arg1)))))
+ && (fsfv = FLOAT_STORE_FLAG_VALUE (GET_MODE (arg1)),
+ REAL_VALUE_NEGATIVE (fsfv)))
#endif
)
&& GET_RTX_CLASS (GET_CODE (p->exp)) == '<'))
#ifdef FLOAT_STORE_FLAG_VALUE
|| (code == GE
&& GET_MODE_CLASS (inner_mode) == MODE_FLOAT
- && (REAL_VALUE_NEGATIVE
- (FLOAT_STORE_FLAG_VALUE (GET_MODE (arg1)))))
+ && (fsfv = FLOAT_STORE_FLAG_VALUE (GET_MODE (arg1)),
+ REAL_VALUE_NEGATIVE (fsfv)))
#endif
)
&& GET_RTX_CLASS (GET_CODE (p->exp)) == '<')
break;
}
- /* If this is fp + constant, the equivalent is a better operand since
- it may let us predict the value of the comparison. */
- else if (NONZERO_BASE_PLUS_P (p->exp))
+ /* If this non-trapping address, e.g. fp + constant, the
+ equivalent is a better operand since it may let us predict
+ the value of the comparison. */
+ else if (!rtx_addr_can_trap_p (p->exp))
{
arg1 = p->exp;
continue;
/* If the next insn is a CODE_LABEL followed by a jump table,
PC's value is a LABEL_REF pointing to that label. That
lets us fold switch statements on the VAX. */
- if (insn && GET_CODE (insn) == JUMP_INSN)
- {
- rtx next = next_nonnote_insn (insn);
-
- if (next && GET_CODE (next) == CODE_LABEL
- && NEXT_INSN (next) != 0
- && GET_CODE (NEXT_INSN (next)) == JUMP_INSN
- && (GET_CODE (PATTERN (NEXT_INSN (next))) == ADDR_VEC
- || GET_CODE (PATTERN (NEXT_INSN (next))) == ADDR_DIFF_VEC))
- return gen_rtx_LABEL_REF (Pmode, next);
- }
+ {
+ rtx next;
+ if (insn && tablejump_p (insn, &next, NULL))
+ return gen_rtx_LABEL_REF (Pmode, next);
+ }
break;
case SUBREG:
rtx cheap_arg, expensive_arg;
rtx replacements[2];
int j;
+ int old_cost = COST_IN (XEXP (x, i), code);
/* Most arguments are cheap, so handle them specially. */
switch (GET_CODE (arg))
for (j = 0; j < 2 && replacements[j]; j++)
{
- int old_cost = COST_IN (XEXP (x, i), code);
int new_cost = COST_IN (replacements[j], code);
/* Stop if what existed before was cheaper. Prefer constants
comparison. */
if (const_arg0 == 0 || const_arg1 == 0)
{
- /* Is FOLDED_ARG0 frame-pointer plus a constant? Or
- non-explicit constant? These aren't zero, but we
- don't know their sign. */
+ /* Some addresses are known to be nonzero. We don't know
+ their sign, but equality comparisons are known. */
if (const_arg1 == const0_rtx
- && (NONZERO_BASE_PLUS_P (folded_arg0)
-#if 0 /* Sad to say, on sysvr4, #pragma weak can make a symbol address
- come out as 0. */
- || GET_CODE (folded_arg0) == SYMBOL_REF
-#endif
- || GET_CODE (folded_arg0) == LABEL_REF
- || GET_CODE (folded_arg0) == CONST))
+ && nonzero_address_p (folded_arg0))
{
if (code == EQ)
return false_rtx;
from_plus:
case SMIN: case SMAX: case UMIN: case UMAX:
case IOR: case AND: case XOR:
- case MULT: case DIV: case UDIV:
+ case MULT:
case ASHIFT: case LSHIFTRT: case ASHIFTRT:
/* If we have (<op> <reg> <const_int>) for an associative OP and REG
is known to be of similar form, we may be able to replace the
break;
/* Compute the code used to compose the constants. For example,
- A/C1/C2 is A/(C1 * C2), so if CODE == DIV, we want MULT. */
+ A-C1-C2 is A-(C1 + C2), so if CODE == MINUS, we want PLUS. */
- associate_code
- = (code == MULT || code == DIV || code == UDIV ? MULT
- : is_shift || code == PLUS || code == MINUS ? PLUS : code);
+ associate_code = (is_shift || code == MINUS ? PLUS : code);
new_const = simplify_binary_operation (associate_code, mode,
const_arg1, inner_const);
}
break;
+ case DIV: case UDIV:
+ /* ??? The associative optimization performed immediately above is
+ also possible for DIV and UDIV using associate_code of MULT.
+ However, we would need extra code to verify that the
+ multiplication does not overflow, that is, there is no overflow
+ in the calculation of new_const. */
+ break;
+
default:
break;
}
break;
case 'x':
- /* Always eliminate CONSTANT_P_RTX at this stage. */
+ /* Eliminate CONSTANT_P_RTX if its constant. */
if (code == CONSTANT_P_RTX)
- return (const_arg0 ? const1_rtx : const0_rtx);
+ {
+ if (const_arg0)
+ return const1_rtx;
+ if (optimize == 0 || !flag_gcse)
+ return const0_rtx;
+ }
break;
}
/* Nonzero if the SET_SRC contains something
whose value cannot be predicted and understood. */
char src_volatile;
- /* Original machine mode, in case it becomes a CONST_INT. */
- enum machine_mode mode;
+ /* Original machine mode, in case it becomes a CONST_INT.
+ 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;
/* Original SET_SRC value used for libcall notes. */
int src_folded_regcost = MAX_COST;
int src_related_regcost = MAX_COST;
int src_elt_regcost = MAX_COST;
- /* Set non-zero if we need to call force_const_mem on with the
+ /* 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;
if (src == src_folded)
src_folded = 0;
- /* At this point, ELT, if non-zero, points to a class of expressions
+ /* At this point, ELT, if nonzero, points to a class of expressions
equivalent to the source of this SET and SRC, SRC_EQV, SRC_FOLDED,
- and SRC_RELATED, if non-zero, each contain additional equivalent
+ and SRC_RELATED, if nonzero, each contain additional equivalent
expressions. Prune these latter expressions by deleting expressions
already in the equivalence class.
/* Look for a substitution that makes a valid insn. */
else if (validate_change (insn, &SET_SRC (sets[i].rtl), trial, 0))
{
+ rtx new = canon_reg (SET_SRC (sets[i].rtl), insn);
+
/* If we just made a substitution inside a libcall, then we
need to make the same substitution in any notes attached
to the RETVAL insn. */
&& (GET_CODE (sets[i].orig_src) == REG
|| GET_CODE (sets[i].orig_src) == SUBREG
|| GET_CODE (sets[i].orig_src) == MEM))
- replace_rtx (REG_NOTES (libcall_insn), sets[i].orig_src,
- canon_reg (SET_SRC (sets[i].rtl), insn));
+ simplify_replace_rtx (REG_NOTES (libcall_insn),
+ sets[i].orig_src, copy_rtx (new));
/* The result of apply_change_group can be ignored; see
canon_reg. */
- validate_change (insn, &SET_SRC (sets[i].rtl),
- canon_reg (SET_SRC (sets[i].rtl), insn),
- 1);
+ validate_change (insn, &SET_SRC (sets[i].rtl), new, 1);
apply_change_group ();
break;
}
&& GET_CODE (XEXP (XEXP (src_const, 0), 0)) == LABEL_REF
&& GET_CODE (XEXP (XEXP (src_const, 0), 1)) == LABEL_REF))
{
- /* Make sure that the rtx is not shared with any other insn. */
- src_const = copy_rtx (src_const);
+ /* We only want a REG_EQUAL note if src_const != src. */
+ if (! rtx_equal_p (src, src_const))
+ {
+ /* Make sure that the rtx is not shared. */
+ src_const = copy_rtx (src_const);
- /* Record the actual constant value in a REG_EQUAL note, making
- a new one if one does not already exist. */
- set_unique_reg_note (insn, REG_EQUAL, src_const);
+ /* Record the actual constant value in a REG_EQUAL note,
+ making a new one if one does not already exist. */
+ set_unique_reg_note (insn, REG_EQUAL, src_const);
+ }
/* If storing a constant value in a register that
previously held the constant value 0,
elt->in_memory = (GET_CODE (sets[i].inner_dest) == MEM
&& (! RTX_UNCHANGING_P (sets[i].inner_dest)
- || FIXED_BASE_PLUS_P (XEXP (sets[i].inner_dest,
+ || fixed_base_plus_p (XEXP (sets[i].inner_dest,
0))));
/* If we have (set (subreg:m1 (reg:m2 foo) 0) (bar:m1)), M1 is no
&& ! exp_equiv_p (elt->exp, elt->exp, 1, 0))
continue;
- /* Calculate big endian correction for the SUBREG_BYTE
- (or equivalent). 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));
- new_src = simplify_gen_subreg (new_mode, elt->exp,
- GET_MODE (dest), byte);
+ /* We may have already been playing subreg games. If the
+ mode is already correct for the destination, use it. */
+ if (GET_MODE (elt->exp) == new_mode)
+ 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));
+
+ new_src = simplify_gen_subreg (new_mode, elt->exp,
+ GET_MODE (dest), byte);
+ }
+
/* The call to simplify_gen_subreg fails if the value
is VOIDmode, yet we can't do any simplification, e.g.
for EXPR_LISTs denoting function call results.
if ((src_ent->first_reg == REGNO (SET_DEST (sets[0].rtl)))
&& ! find_reg_note (insn, REG_RETVAL, NULL_RTX))
{
- rtx prev = prev_nonnote_insn (insn);
-
+ rtx prev = insn;
+ /* Scan for the previous nonnote insn, but stop at a basic
+ block boundary. */
+ do
+ {
+ prev = PREV_INSN (prev);
+ }
+ while (prev && GET_CODE (prev) == NOTE
+ && NOTE_LINE_NUMBER (prev) != NOTE_INSN_BASIC_BLOCK);
+
/* Do not swap the registers around if the previous instruction
attaches a REG_EQUIV note to REG1.
This section previously turned the REG_EQUIV into a REG_EQUAL
note. We cannot do that because REG_EQUIV may provide an
- uninitialised stack slot when REG_PARM_STACK_SPACE is used. */
+ uninitialized stack slot when REG_PARM_STACK_SPACE is used. */
if (prev != 0 && GET_CODE (prev) == INSN
&& GET_CODE (PATTERN (prev)) == SET
prev_insn_cc0 = this_insn_cc0;
prev_insn_cc0_mode = this_insn_cc0_mode;
-#endif
-
prev_insn = insn;
+#endif
}
\f
/* Remove from the hash table all expressions that reference memory. */
&& REGNO (XEXP (addr, 0)) == STACK_POINTER_REGNUM)
{
if (REG_TICK (STACK_POINTER_REGNUM) >= 0)
- REG_TICK (STACK_POINTER_REGNUM)++;
+ {
+ REG_TICK (STACK_POINTER_REGNUM)++;
+ /* Is it possible to use a subreg of SP? */
+ SUBREG_TICKED (STACK_POINTER_REGNUM) = -1;
+ }
/* This should be *very* rare. */
if (TEST_HARD_REG_BIT (hard_regs_in_table, STACK_POINTER_REGNUM))
}
if (GET_CODE (set) == CLOBBER
-#ifdef HAVE_cc0
- || dest == cc0_rtx
-#endif
+ || CC0_P (dest)
|| dest == pc_rtx)
return;
SET_SRC, add an insn after P to copy its destination
to what we will be replacing SET_SRC with. */
if (cse_check_loop_start_value
+ && single_set (p)
+ && !can_throw_internal (insn)
&& validate_change (insn, &SET_SRC (x),
src_elt->exp, 0))
{
the total number of SETs in all the insns of the block, the last insn of the
block, and the branch path.
- The branch path indicates which branches should be followed. If a non-zero
+ The branch path indicates which branches should be followed. If a nonzero
path size is specified, the block should be rescanned and a different set
of branches will be taken. The branch path is only used if
- FLAG_CSE_FOLLOW_JUMPS or FLAG_CSE_SKIP_BLOCKS is non-zero.
+ FLAG_CSE_FOLLOW_JUMPS or FLAG_CSE_SKIP_BLOCKS is nonzero.
DATA is a pointer to a struct cse_basic_block_data, defined below, that is
used to describe the block. It is filled in with the information about
/* Update the previous branch path, if any. If the last branch was
previously TAKEN, mark it NOT_TAKEN. If it was previously NOT_TAKEN,
shorten the path by one and look at the previous branch. We know that
- at least one branch must have been taken if PATH_SIZE is non-zero. */
+ at least one branch must have been taken if PATH_SIZE is nonzero. */
while (path_size > 0)
{
if (data->path[path_size - 1].status != NOT_TAKEN)
In this case invalidate_skipped_block will be called to invalidate any
registers set in the block when following the jump. */
- else if ((follow_jumps || skip_blocks) && path_size < PATHLENGTH - 1
+ else if ((follow_jumps || skip_blocks) && path_size < PARAM_VALUE (PARAM_MAX_CSE_PATH_LENGTH) - 1
&& GET_CODE (p) == JUMP_INSN
&& GET_CODE (PATTERN (p)) == SET
&& GET_CODE (SET_SRC (PATTERN (p))) == IF_THEN_ELSE
rtx insn = f;
int i;
+ val.path = xmalloc (sizeof (struct branch_path)
+ * PARAM_VALUE (PARAM_MAX_CSE_PATH_LENGTH));
+
cse_jumps_altered = 0;
recorded_label_ref = 0;
constant_pool_entries_cost = 0;
end_alias_analysis ();
free (uid_cuid);
free (reg_eqv_table);
+ free (val.path);
return cse_jumps_altered || recorded_label_ref;
}
block. NEXT_BRANCH points to the branch path when following jumps or
a null path when not following jumps.
- AROUND_LOOP is non-zero if we are to try to cse around to the start of a
+ AROUND_LOOP is nonzero if we are to try to cse around to the start of a
loop. This is true when we are being called for the last time on a
block and this CSE pass is before loop.c. */
Then follow this branch. */
#ifdef HAVE_cc0
prev_insn_cc0 = 0;
-#endif
prev_insn = insn;
+#endif
insn = JUMP_LABEL (insn);
continue;
}
following branches in this case. */
to_usage = 0;
val.path_size = 0;
+ val.path = xmalloc (sizeof (struct branch_path)
+ * PARAM_VALUE (PARAM_MAX_CSE_PATH_LENGTH));
cse_end_of_basic_block (insn, &val, 0, 0, 0);
+ free (val.path);
/* If the tables we allocated have enough space left
to handle all the SETs in the next basic block,
int incr;
{
enum rtx_code code;
+ rtx note;
const char *fmt;
int i, j;
/* Unless we are setting a REG, count everything in SET_DEST. */
if (GET_CODE (SET_DEST (x)) != REG)
count_reg_usage (SET_DEST (x), counts, NULL_RTX, incr);
-
- /* If SRC has side-effects, then we can't delete this insn, so the
- usage of SET_DEST inside SRC counts.
-
- ??? Strictly-speaking, we might be preserving this insn
- because some other SET has side-effects, but that's hard
- to do and can't happen now. */
count_reg_usage (SET_SRC (x), counts,
- side_effects_p (SET_SRC (x)) ? NULL_RTX : SET_DEST (x),
+ SET_DEST (x),
incr);
return;
/* Things used in a REG_EQUAL note aren't dead since loop may try to
use them. */
- count_reg_usage (REG_NOTES (x), counts, NULL_RTX, incr);
+ note = find_reg_equal_equiv_note (x);
+ if (note)
+ {
+ rtx eqv = XEXP (note, 0);
+
+ if (GET_CODE (eqv) == EXPR_LIST)
+ /* This REG_EQUAL note describes the result of a function call.
+ Process all the arguments. */
+ do
+ {
+ count_reg_usage (XEXP (eqv, 0), counts, NULL_RTX, incr);
+ eqv = XEXP (eqv, 1);
+ }
+ while (eqv && GET_CODE (eqv) == EXPR_LIST);
+ else
+ count_reg_usage (eqv, counts, NULL_RTX, incr);
+ }
return;
case EXPR_LIST:
- case INSN_LIST:
if (REG_NOTE_KIND (x) == REG_EQUAL
- || (REG_NOTE_KIND (x) != REG_NONNEG && GET_CODE (XEXP (x,0)) == USE))
+ || (REG_NOTE_KIND (x) != REG_NONNEG && GET_CODE (XEXP (x,0)) == USE)
+ /* FUNCTION_USAGE expression lists may include (CLOBBER (mem /u)),
+ involving registers in the address. */
+ || GET_CODE (XEXP (x, 0)) == CLOBBER)
count_reg_usage (XEXP (x, 0), counts, NULL_RTX, incr);
+
count_reg_usage (XEXP (x, 1), counts, NULL_RTX, incr);
return;
+ case INSN_LIST:
+ abort ();
+
default:
break;
}
int *counts;
{
int i;
- if (GET_CODE (PATTERN (insn)) == SET)
+ if (flag_non_call_exceptions && may_trap_p (PATTERN (insn)))
+ return true;
+ else if (GET_CODE (PATTERN (insn)) == SET)
return set_live_p (PATTERN (insn), insn, counts);
else if (GET_CODE (PATTERN (insn)) == PARALLEL)
{
rtx insn;
int *counts;
{
- rtx note;
+ rtx note, set, new;
+
/* See if there's a REG_EQUAL note on this insn and try to
replace the source with the REG_EQUAL expression.
We assume that insns with REG_RETVALs can only be reg->reg
copies at this point. */
note = find_reg_note (insn, REG_EQUAL, NULL_RTX);
- if (note)
- {
- rtx set = single_set (insn);
- rtx new = simplify_rtx (XEXP (note, 0));
+ if (!note)
+ return false;
+
+ set = single_set (insn);
+ if (!set)
+ return false;
- if (!new)
- new = XEXP (note, 0);
+ new = simplify_rtx (XEXP (note, 0));
+ if (!new)
+ new = XEXP (note, 0);
- /* While changing insn, we must update the counts accordingly. */
- count_reg_usage (insn, counts, NULL_RTX, -1);
+ /* While changing insn, we must update the counts accordingly. */
+ count_reg_usage (insn, counts, NULL_RTX, -1);
- if (set && validate_change (insn, &SET_SRC (set), new, 0))
+ if (validate_change (insn, &SET_SRC (set), new, 0))
+ {
+ count_reg_usage (insn, counts, NULL_RTX, 1);
+ remove_note (insn, find_reg_note (insn, REG_RETVAL, NULL_RTX));
+ remove_note (insn, note);
+ return true;
+ }
+
+ if (CONSTANT_P (new))
+ {
+ new = force_const_mem (GET_MODE (SET_DEST (set)), new);
+ if (new && validate_change (insn, &SET_SRC (set), new, 0))
{
- count_reg_usage (insn, counts, NULL_RTX, 1);
+ count_reg_usage (insn, counts, NULL_RTX, 1);
remove_note (insn, find_reg_note (insn, REG_RETVAL, NULL_RTX));
remove_note (insn, note);
return true;
}
- count_reg_usage (insn, counts, NULL_RTX, 1);
}
+
+ count_reg_usage (insn, counts, NULL_RTX, 1);
return false;
}