/* 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 "toplev.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;
a cost of 2. Aside from these special cases, call `rtx_cost'. */
#define CHEAP_REGNO(N) \
- ((N) == FRAME_POINTER_REGNUM || (N) == HARD_FRAME_POINTER_REGNUM \
- || (N) == STACK_POINTER_REGNUM || (N) == ARG_POINTER_REGNUM \
- || ((N) >= FIRST_VIRTUAL_REGISTER && (N) <= LAST_VIRTUAL_REGISTER) \
+ ((N) == FRAME_POINTER_REGNUM || (N) == HARD_FRAME_POINTER_REGNUM \
+ || (N) == STACK_POINTER_REGNUM || (N) == ARG_POINTER_REGNUM \
+ || ((N) >= FIRST_VIRTUAL_REGISTER && (N) <= LAST_VIRTUAL_REGISTER) \
|| ((N) < FIRST_PSEUDO_REGISTER \
&& FIXED_REGNO_P (N) && REGNO_REG_CLASS (N) != NO_REGS))
/* Get the info associated with register N. */
-#define GET_CSE_REG_INFO(N) \
+#define GET_CSE_REG_INFO(N) \
(((N) == cached_regno && cached_cse_reg_info) \
? cached_cse_reg_info : get_cse_reg_info ((N)))
#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)
the insn. */
static int constant_pool_entries_cost;
-
-/* Define maximum length of a branch path. */
-
-#define PATHLENGTH 10
+static int constant_pool_entries_regcost;
/* This data describes a block that will be processed by cse_basic_block. */
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;
};
+static bool fixed_base_plus_p (rtx x);
+static int notreg_cost (rtx, enum rtx_code);
+static int approx_reg_cost_1 (rtx *, void *);
+static int approx_reg_cost (rtx);
+static int preferrable (int, int, int, int);
+static void new_basic_block (void);
+static void make_new_qty (unsigned int, enum machine_mode);
+static void make_regs_eqv (unsigned int, unsigned int);
+static void delete_reg_equiv (unsigned int);
+static int mention_regs (rtx);
+static int insert_regs (rtx, struct table_elt *, int);
+static void remove_from_table (struct table_elt *, unsigned);
+static struct table_elt *lookup (rtx, unsigned, enum machine_mode);
+static struct table_elt *lookup_for_remove (rtx, unsigned, enum machine_mode);
+static rtx lookup_as_function (rtx, enum rtx_code);
+static struct table_elt *insert (rtx, struct table_elt *, unsigned,
+ enum machine_mode);
+static void merge_equiv_classes (struct table_elt *, struct table_elt *);
+static void invalidate (rtx, enum machine_mode);
+static int cse_rtx_varies_p (rtx, int);
+static void remove_invalid_refs (unsigned int);
+static void remove_invalid_subreg_refs (unsigned int, unsigned int,
+ enum machine_mode);
+static void rehash_using_reg (rtx);
+static void invalidate_memory (void);
+static void invalidate_for_call (void);
+static rtx use_related_value (rtx, struct table_elt *);
+static 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 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 *,
+ enum machine_mode *,
+ enum machine_mode *);
+static rtx fold_rtx (rtx, rtx);
+static rtx equiv_constant (rtx);
+static void record_jump_equiv (rtx, int);
+static void record_jump_cond (enum rtx_code, enum machine_mode, rtx, rtx,
+ int);
+static void cse_insn (rtx, rtx);
+static int addr_affects_sp_p (rtx);
+static void invalidate_from_clobbers (rtx);
+static rtx cse_process_notes (rtx, rtx);
+static void cse_around_loop (rtx);
+static void invalidate_skipped_set (rtx, rtx, void *);
+static void invalidate_skipped_block (rtx);
+static void cse_check_loop_start (rtx, rtx, void *);
+static void cse_set_around_loop (rtx, rtx, rtx);
+static rtx cse_basic_block (rtx, rtx, struct branch_path *, int);
+static void count_reg_usage (rtx, int *, int);
+static int check_for_label_ref (rtx *, void *);
+extern void dump_class (struct table_elt*);
+static struct cse_reg_info * get_cse_reg_info (unsigned int);
+static int check_dependence (rtx *, void *);
+
+static void flush_hash_table (void);
+static bool insn_live_p (rtx, int *);
+static bool set_live_p (rtx, rtx, int *);
+static bool dead_libcall_p (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.
-
- ?!? 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 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 int preferrable PARAMS ((int, int, int, int));
-static void new_basic_block PARAMS ((void));
-static void make_new_qty PARAMS ((unsigned int, enum machine_mode));
-static void make_regs_eqv PARAMS ((unsigned int, unsigned int));
-static void delete_reg_equiv PARAMS ((unsigned int));
-static int mention_regs PARAMS ((rtx));
-static int insert_regs PARAMS ((rtx, struct table_elt *, int));
-static void remove_from_table PARAMS ((struct table_elt *, unsigned));
-static struct table_elt *lookup PARAMS ((rtx, unsigned, enum machine_mode)),
- *lookup_for_remove PARAMS ((rtx, unsigned, enum machine_mode));
-static rtx lookup_as_function PARAMS ((rtx, enum rtx_code));
-static struct table_elt *insert PARAMS ((rtx, struct table_elt *, unsigned,
- enum machine_mode));
-static void merge_equiv_classes PARAMS ((struct table_elt *,
- struct table_elt *));
-static void invalidate PARAMS ((rtx, enum machine_mode));
-static int cse_rtx_varies_p PARAMS ((rtx, int));
-static void remove_invalid_refs PARAMS ((unsigned int));
-static void remove_invalid_subreg_refs PARAMS ((unsigned int, unsigned int,
- enum machine_mode));
-static void rehash_using_reg PARAMS ((rtx));
-static void invalidate_memory PARAMS ((void));
-static void invalidate_for_call PARAMS ((void));
-static rtx use_related_value PARAMS ((rtx, struct table_elt *));
-static unsigned canon_hash PARAMS ((rtx, enum machine_mode));
-static unsigned canon_hash_string PARAMS ((const char *));
-static unsigned safe_hash PARAMS ((rtx, enum machine_mode));
-static int exp_equiv_p PARAMS ((rtx, rtx, int, int));
-static rtx canon_reg PARAMS ((rtx, rtx));
-static void find_best_addr PARAMS ((rtx, rtx *, enum machine_mode));
-static enum rtx_code find_comparison_args PARAMS ((enum rtx_code, rtx *, rtx *,
- enum machine_mode *,
- enum machine_mode *));
-static rtx fold_rtx PARAMS ((rtx, rtx));
-static rtx equiv_constant PARAMS ((rtx));
-static void record_jump_equiv PARAMS ((rtx, int));
-static void record_jump_cond PARAMS ((enum rtx_code, enum machine_mode,
- rtx, rtx, int));
-static void cse_insn PARAMS ((rtx, rtx));
-static int addr_affects_sp_p PARAMS ((rtx));
-static void invalidate_from_clobbers PARAMS ((rtx));
-static rtx cse_process_notes PARAMS ((rtx, rtx));
-static void cse_around_loop PARAMS ((rtx));
-static void invalidate_skipped_set PARAMS ((rtx, rtx, void *));
-static void invalidate_skipped_block PARAMS ((rtx));
-static void cse_check_loop_start PARAMS ((rtx, rtx, void *));
-static void cse_set_around_loop PARAMS ((rtx, rtx, rtx));
-static rtx cse_basic_block PARAMS ((rtx, rtx, struct branch_path *, int));
-static void count_reg_usage PARAMS ((rtx, int *, rtx, int));
-static int check_for_label_ref PARAMS ((rtx *, void *));
-extern void dump_class PARAMS ((struct table_elt*));
-static struct cse_reg_info * get_cse_reg_info PARAMS ((unsigned int));
-static int check_dependence PARAMS ((rtx *, void *));
-
-static void flush_hash_table PARAMS ((void));
-static bool insn_live_p PARAMS ((rtx, int *));
-static bool set_live_p PARAMS ((rtx, rtx, int *));
-static bool dead_libcall_p PARAMS ((rtx));
-\f
+ by integrate.c, which is called before virtual register instantiation. */
+
+static bool
+fixed_base_plus_p (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
-dump_class (classp)
- struct table_elt *classp;
+dump_class (struct table_elt *classp)
{
struct table_elt *elt;
/* Subroutine of approx_reg_cost; called through for_each_rtx. */
static int
-approx_reg_cost_1 (xp, data)
- rtx *xp;
- void *data;
+approx_reg_cost_1 (rtx *xp, void *data)
{
rtx x = *xp;
- regset set = (regset) data;
+ int *cost_p = data;
if (x && GET_CODE (x) == REG)
- SET_REGNO_REG_SET (set, REGNO (x));
+ {
+ unsigned int regno = REGNO (x);
+
+ if (! CHEAP_REGNO (regno))
+ {
+ if (regno < FIRST_PSEUDO_REGISTER)
+ {
+ if (SMALL_REGISTER_CLASSES)
+ return 1;
+ *cost_p += 2;
+ }
+ else
+ *cost_p += 1;
+ }
+ }
+
return 0;
}
0. If any other hard register reference occurs, return MAX_COST. */
static int
-approx_reg_cost (x)
- rtx x;
+approx_reg_cost (rtx x)
{
- regset_head set;
- int i;
int cost = 0;
- int hardregs = 0;
- INIT_REG_SET (&set);
- for_each_rtx (&x, approx_reg_cost_1, (void *)&set);
+ if (for_each_rtx (&x, approx_reg_cost_1, (void *) &cost))
+ return MAX_COST;
- EXECUTE_IF_SET_IN_REG_SET
- (&set, 0, i,
- {
- if (! CHEAP_REGNO (i))
- {
- if (i < FIRST_PSEUDO_REGISTER)
- hardregs++;
-
- cost += i < FIRST_PSEUDO_REGISTER ? 2 : 1;
- }
- });
-
- CLEAR_REG_SET (&set);
- return hardregs && SMALL_REGISTER_CLASSES ? MAX_COST : cost;
+ return cost;
}
/* Return a negative value if an rtx A, whose costs are given by COST_A
Return a positive value if A is less desirable, or 0 if the two are
equally good. */
static int
-preferrable (cost_a, regcost_a, cost_b, regcost_b)
- int cost_a, regcost_a, cost_b, regcost_b;
+preferrable (int cost_a, int regcost_a, int cost_b, int 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)
{
from COST macro to keep it simple. */
static int
-notreg_cost (x, outer)
- rtx x;
- enum rtx_code outer;
+notreg_cost (rtx x, enum rtx_code outer)
{
return ((GET_CODE (x) == SUBREG
&& GET_CODE (SUBREG_REG (x)) == REG
Other uses like the latter are expected in the future. */
int
-rtx_cost (x, outer_code)
- rtx x;
- enum rtx_code outer_code ATTRIBUTE_UNUSED;
+rtx_cost (rtx x, enum rtx_code outer_code ATTRIBUTE_UNUSED)
{
int i, j;
enum rtx_code code;
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)
{
case MULT:
- /* Count multiplication by 2**n as a shift,
- because if we are considering it, we would output it as a shift. */
- if (GET_CODE (XEXP (x, 1)) == CONST_INT
- && exact_log2 (INTVAL (XEXP (x, 1))) >= 0)
- total = 2;
- else
- total = COSTS_N_INSNS (5);
+ total = COSTS_N_INSNS (5);
break;
case DIV:
case UDIV:
+ 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;
}
Expect that X is properly formed address reference. */
int
-address_cost (x, mode)
- rtx x;
- enum machine_mode mode;
+address_cost (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 (rtx x)
+{
+ return rtx_cost (x, MEM);
+}
\f
static struct cse_reg_info *
-get_cse_reg_info (regno)
- unsigned int regno;
+get_cse_reg_info (unsigned int regno)
{
struct cse_reg_info **hash_head = ®_hash[REGHASH_FN (regno)];
struct cse_reg_info *p;
cse_reg_info_free_list = p->next;
}
else
- p = (struct cse_reg_info *) xmalloc (sizeof (struct cse_reg_info));
+ p = xmalloc (sizeof (struct cse_reg_info));
/* Insert into hash table. */
p->hash_next = *hash_head;
/* 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;
for a new basic block. */
static void
-new_basic_block ()
+new_basic_block (void)
{
int i;
/* Clear out hash table state for this pass. */
- memset ((char *) reg_hash, 0, sizeof reg_hash);
+ memset (reg_hash, 0, sizeof reg_hash);
if (cse_reg_info_used_list)
{
}
}
- prev_insn = 0;
-
#ifdef HAVE_cc0
+ prev_insn = 0;
prev_insn_cc0 = 0;
#endif
}
register before and initialize that quantity. */
static void
-make_new_qty (reg, mode)
- unsigned int reg;
- enum machine_mode mode;
+make_new_qty (unsigned int reg, enum machine_mode mode)
{
int q;
struct qty_table_elem *ent;
OLD is not changing; NEW is. */
static void
-make_regs_eqv (new, old)
- unsigned int new, old;
+make_regs_eqv (unsigned int new, unsigned int old)
{
unsigned int lastr, firstr;
int q = REG_QTY (old);
/* Remove REG from its equivalence class. */
static void
-delete_reg_equiv (reg)
- unsigned int reg;
+delete_reg_equiv (unsigned int reg)
{
struct qty_table_elem *ent;
int q = REG_QTY (reg);
of X. */
static int
-mention_regs (x)
- rtx x;
+mention_regs (rtx x)
{
enum rtx_code code;
int i, j;
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;
}
so X's hash code may be different. */
static int
-insert_regs (x, classp, modified)
- rtx x;
- struct table_elt *classp;
- int modified;
+insert_regs (rtx x, struct table_elt *classp, int modified)
{
if (GET_CODE (x) == REG)
{
and we save much time not recomputing it. */
static void
-remove_from_table (elt, hash)
- struct table_elt *elt;
- unsigned hash;
+remove_from_table (struct table_elt *elt, unsigned int hash)
{
if (elt == 0)
return;
looks like X. */
static struct table_elt *
-lookup (x, hash, mode)
- rtx x;
- unsigned hash;
- enum machine_mode mode;
+lookup (rtx x, unsigned int hash, enum machine_mode mode)
{
struct table_elt *p;
Also ignore discrepancies in the machine mode of a register. */
static struct table_elt *
-lookup_for_remove (x, hash, mode)
- rtx x;
- unsigned hash;
- enum machine_mode mode;
+lookup_for_remove (rtx x, unsigned int hash, enum machine_mode mode)
{
struct table_elt *p;
If one is found, return that expression. */
static rtx
-lookup_as_function (x, code)
- rtx x;
- enum rtx_code code;
+lookup_as_function (rtx x, enum rtx_code code)
{
struct table_elt *p
= lookup (x, safe_hash (x, VOIDmode) & HASH_MASK, GET_MODE (x));
(preferrable ((X)->cost, (X)->regcost, (Y)->cost, (Y)->regcost) < 0)
static struct table_elt *
-insert (x, classp, hash, mode)
- rtx x;
- struct table_elt *classp;
- unsigned hash;
- enum machine_mode mode;
+insert (rtx x, struct table_elt *classp, unsigned int hash, enum machine_mode mode)
{
struct table_elt *elt;
else
{
n_elements_made++;
- elt = (struct table_elt *) xmalloc (sizeof (struct table_elt));
+ elt = xmalloc (sizeof (struct table_elt));
}
elt->exp = x;
elt->is_const = (CONSTANT_P (x)
/* GNU C++ takes advantage of this for `this'
(and other const values). */
- || (RTX_UNCHANGING_P (x)
- && GET_CODE (x) == REG
+ || (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;
{
classp = classp->first_same_value;
if (CHEAPER (elt, classp))
- /* Insert at the head of the class */
+ /* Insert at the head of the class. */
{
struct table_elt *p;
elt->next_same_value = classp;
Any invalid entries in CLASS2 will not be copied. */
static void
-merge_equiv_classes (class1, class2)
- struct table_elt *class1, *class2;
+merge_equiv_classes (struct table_elt *class1, struct table_elt *class2)
{
struct table_elt *elt, *next, *new;
/* Flush the entire hash table. */
static void
-flush_hash_table ()
+flush_hash_table (void)
{
int i;
struct table_elt *p;
};
static int
-check_dependence (x, data)
- rtx *x;
- void *data;
+check_dependence (rtx *x, void *data)
{
struct check_dependence_data *d = (struct check_dependence_data *) data;
if (*x && GET_CODE (*x) == MEM)
or it may be either of those plus a numeric offset. */
static void
-invalidate (x, full_mode)
- rtx x;
- enum machine_mode full_mode;
+invalidate (rtx x, enum machine_mode full_mode)
{
int i;
struct table_elt *p;
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)
expressions to reappear as valid. */
static void
-remove_invalid_refs (regno)
- unsigned int regno;
+remove_invalid_refs (unsigned int regno)
{
unsigned int i;
struct table_elt *p, *next;
{
next = p->next_same_hash;
if (GET_CODE (p->exp) != REG
- && refers_to_regno_p (regno, regno + 1, p->exp, (rtx*) 0))
+ && refers_to_regno_p (regno, regno + 1, p->exp, (rtx *) 0))
remove_from_table (p, i);
}
}
/* Likewise for a subreg with subreg_reg REGNO, subreg_byte OFFSET,
and mode MODE. */
static void
-remove_invalid_subreg_refs (regno, offset, mode)
- unsigned int regno;
- unsigned int offset;
- enum machine_mode mode;
+remove_invalid_subreg_refs (unsigned int regno, unsigned int offset,
+ enum machine_mode mode)
{
unsigned int i;
struct table_elt *p, *next;
|| (((SUBREG_BYTE (exp)
+ (GET_MODE_SIZE (GET_MODE (exp)) - 1)) >= offset)
&& SUBREG_BYTE (exp) <= end))
- && refers_to_regno_p (regno, regno + 1, p->exp, (rtx*) 0))
+ && refers_to_regno_p (regno, regno + 1, p->exp, (rtx *) 0))
remove_from_table (p, i);
}
}
This is called when we make a jump equivalence. */
static void
-rehash_using_reg (x)
- rtx x;
+rehash_using_reg (rtx x)
{
unsigned int i;
struct table_elt *p, *next;
register. Also update their TICK values. */
static void
-invalidate_for_call ()
+invalidate_for_call (void)
{
unsigned int regno, endregno;
unsigned int i;
{
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);
}
If none can be found, return 0. */
static rtx
-use_related_value (x, elt)
- rtx x;
- struct table_elt *elt;
+use_related_value (rtx x, struct table_elt *elt)
{
struct table_elt *relt = 0;
struct table_elt *p, *q;
\f
/* Hash a string. Just add its bytes up. */
static inline unsigned
-canon_hash_string (ps)
- const char *ps;
+canon_hash_string (const char *ps)
{
unsigned hash = 0;
- const unsigned char *p = (const unsigned char *)ps;
-
+ const unsigned char *p = (const unsigned char *) ps;
+
if (p)
while (*p)
hash += *p++;
is just (int) MEM plus the hash code of the address. */
static unsigned
-canon_hash (x, mode)
- rtx x;
- enum machine_mode mode;
+canon_hash (rtx x, enum machine_mode mode)
{
int i, j;
unsigned hash = 0;
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, and sp to be fixed for this purpose.
+ 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.
+ 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
- && (global_regs[regno]
- || CLASS_LIKELY_SPILLED_P (REGNO_REG_CLASS (regno))
- || (SMALL_REGISTER_CLASSES
- && ! fixed_regs[regno]
- && x != frame_pointer_rtx
- && x != hard_frame_pointer_rtx
- && x != arg_pointer_rtx
- && x != stack_pointer_rtx
- && GET_MODE_CLASS (GET_MODE (x)) != MODE_CC)))
+ 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 = 1;
return 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));
return hash;
+ case CONST_VECTOR:
+ {
+ int units;
+ rtx elt;
+
+ units = CONST_VECTOR_NUNITS (x);
+
+ for (i = 0; i < units; ++i)
+ {
+ elt = CONST_VECTOR_ELT (x, i);
+ hash += canon_hash (elt, GET_MODE (elt));
+ }
+
+ return hash;
+ }
+
/* Assume there is only one rtx object for any given label. */
case LABEL_REF:
hash += ((unsigned) LABEL_REF << 7) + (unsigned long) XEXP (x, 0);
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;
if (GET_CODE (XEXP (x, 0)) == MEM
&& ! MEM_VOLATILE_P (XEXP (x, 0)))
{
- hash += (unsigned)USE;
+ 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,
/* Like canon_hash but with no side effects. */
static unsigned
-safe_hash (x, mode)
- rtx x;
- enum machine_mode mode;
+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;
is the same as that of the given value is pure luck. */
static int
-exp_equiv_p (x, y, validate, equal_values)
- rtx x, y;
- int validate;
- int equal_values;
+exp_equiv_p (rtx x, rtx y, int validate, int equal_values)
{
int i, j;
enum rtx_code code;
against certain constants or near-constants. */
static int
-cse_rtx_varies_p (x, from_alias)
- rtx x;
- int from_alias;
+cse_rtx_varies_p (rtx x, int from_alias)
{
/* We need not check for X and the equivalence class being of the same
mode because if X is equivalent to a constant in some mode, it
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. */
static rtx
-canon_reg (x, insn)
- rtx x;
- rtx insn;
+canon_reg (rtx x, rtx insn)
{
int i;
enum rtx_code code;
case CONST:
case CONST_INT:
case CONST_DOUBLE:
+ case CONST_VECTOR:
case SYMBOL_REF:
case LABEL_REF:
case ADDR_VEC:
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)
- rtx insn;
- rtx *loc;
- enum machine_mode mode;
+find_best_addr (rtx insn, rtx *loc, enum machine_mode 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
A or the code corresponding to the inverse of the comparison. */
static enum rtx_code
-find_comparison_args (code, parg1, parg2, pmode1, pmode2)
- enum rtx_code code;
- rtx *parg1, *parg2;
- enum machine_mode *pmode1, *pmode2;
+find_comparison_args (enum rtx_code code, rtx *parg1, rtx *parg2,
+ enum machine_mode *pmode1, enum machine_mode *pmode2)
{
rtx arg1, arg2;
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;
enum rtx_code reversed = reversed_comparison_code (x, NULL_RTX);
if (reversed == UNKNOWN)
break;
- else code = reversed;
+ else
+ code = reversed;
}
else if (GET_RTX_CLASS (GET_CODE (x)) == '<')
code = GET_CODE (x);
of X before modifying it. */
static rtx
-fold_rtx (x, insn)
- rtx x;
- rtx insn;
+fold_rtx (rtx x, rtx insn)
{
enum rtx_code code;
enum machine_mode mode;
case CONST:
case CONST_INT:
case CONST_DOUBLE:
+ case CONST_VECTOR:
case SYMBOL_REF:
case LABEL_REF:
case REG:
/* 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:
&& GET_CODE (elt->exp) != SIGN_EXTEND
&& GET_CODE (elt->exp) != ZERO_EXTEND
&& GET_CODE (XEXP (elt->exp, 0)) == SUBREG
- && GET_MODE (SUBREG_REG (XEXP (elt->exp, 0))) == mode)
+ && GET_MODE (SUBREG_REG (XEXP (elt->exp, 0))) == mode
+ && (GET_MODE_CLASS (mode)
+ == GET_MODE_CLASS (GET_MODE (XEXP (elt->exp, 0)))))
{
rtx op0 = SUBREG_REG (XEXP (elt->exp, 0));
rtx new;
if (CONSTANT_P (constant) && GET_CODE (constant) != CONST_INT)
- constant_pool_entries_cost = COST (constant);
+ {
+ constant_pool_entries_cost = COST (constant);
+ constant_pool_entries_regcost = approx_reg_cost (constant);
+ }
/* If we are loading the full constant, we have an equivalence. */
if (offset == 0 && mode == const_mode)
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))
case SYMBOL_REF:
case LABEL_REF:
case CONST_DOUBLE:
+ case CONST_VECTOR:
const_arg = arg;
break;
replacements[0] = cheap_arg;
replacements[1] = expensive_arg;
- for (j = 0; j < 2 && replacements[j]; j++)
+ 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
|| code == LTGT || code == UNEQ || code == ORDERED
|| code == UNORDERED)
{
- if (must_swap || (const_arg0
- && (const_arg1 == 0
- || (GET_CODE (const_arg0) == CONST_INT
- && GET_CODE (const_arg1) != CONST_INT))))
+ if (must_swap
+ || swap_commutative_operands_p (const_arg0 ? const_arg0
+ : XEXP (x, 0),
+ const_arg1 ? const_arg1
+ : XEXP (x, 1)))
{
rtx tem = XEXP (x, 0);
if (GET_MODE_CLASS (mode) == MODE_FLOAT)
{
true_rtx = (CONST_DOUBLE_FROM_REAL_VALUE
- (FLOAT_STORE_FLAG_VALUE (mode), mode));
+ (FLOAT_STORE_FLAG_VALUE (mode), mode));
false_rtx = CONST0_RTX (mode);
}
#endif
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;
& HASH_MASK), mode_arg0))
&& p0->first_same_value == p1->first_same_value))
{
- /* Sadly two equal NaNs are not equivalent. */
- if (TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT
- || ! FLOAT_MODE_P (mode_arg0)
- || flag_unsafe_math_optimizations)
- return ((code == EQ || code == LE || code == GE
- || code == LEU || code == GEU || code == UNEQ
- || code == UNLE || code == UNGE || code == ORDERED)
- ? true_rtx : false_rtx);
- /* Take care for the FP compares we can resolve. */
- if (code == UNEQ || code == UNLE || code == UNGE)
- return true_rtx;
- if (code == LTGT || code == LT || code == GT)
- return false_rtx;
+ /* Sadly two equal NaNs are not equivalent. */
+ if (!HONOR_NANS (mode_arg0))
+ return ((code == EQ || code == LE || code == GE
+ || code == LEU || code == GEU || code == UNEQ
+ || code == UNLE || code == UNGE
+ || code == ORDERED)
+ ? true_rtx : false_rtx);
+ /* Take care for the FP compares we can resolve. */
+ if (code == UNEQ || code == UNLE || code == UNGE)
+ return true_rtx;
+ if (code == LTGT || code == LT || code == GT)
+ return false_rtx;
}
/* If FOLDED_ARG0 is a register, see if the comparison we are
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
with a pre- or post-increment. Similarly for two subtracts of
identical powers of two with post decrement. */
- if (code == PLUS && INTVAL (const_arg1) == INTVAL (inner_const)
+ if (code == PLUS && const_arg1 == inner_const
&& ((HAVE_PRE_INCREMENT
&& exact_log2 (INTVAL (const_arg1)) >= 0)
|| (HAVE_POST_INCREMENT
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;
}
Return 0 if we don't know one. */
static rtx
-equiv_constant (x)
- rtx x;
+equiv_constant (rtx x)
{
if (GET_CODE (x) == REG
&& REGNO_QTY_VALID_P (REGNO (x)))
This is similar to gen_lowpart in emit-rtl.c. */
rtx
-gen_lowpart_if_possible (mode, x)
- enum machine_mode mode;
- rtx x;
+gen_lowpart_if_possible (enum machine_mode mode, rtx x)
{
rtx result = gen_lowpart_common (mode, x);
In certain cases, this can cause us to add an equivalence. For example,
if we are following the taken case of
- if (i == 2)
+ if (i == 2)
we can add the fact that `i' and '2' are now equivalent.
In any case, we can record that this comparison was passed. If the same
comparison is seen later, we will know its value. */
static void
-record_jump_equiv (insn, taken)
- rtx insn;
- int taken;
+record_jump_equiv (rtx insn, int taken)
{
int cond_known_true;
rtx op0, op1;
above function and called recursively. */
static void
-record_jump_cond (code, mode, op0, op1, reversed_nonequality)
- enum rtx_code code;
- enum machine_mode mode;
- rtx op0, op1;
- int reversed_nonequality;
+record_jump_cond (enum rtx_code code, enum machine_mode mode, rtx op0,
+ rtx op1, int reversed_nonequality)
{
unsigned op0_hash, op1_hash;
int op0_in_memory, op1_in_memory;
/* 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. */
};
static void
-cse_insn (insn, libcall_insn)
- rtx insn;
- rtx libcall_insn;
+cse_insn (rtx insn, rtx libcall_insn)
{
rtx x = PATTERN (insn);
int i;
if (GET_CODE (x) == SET)
{
- sets = (struct set *) alloca (sizeof (struct set));
+ sets = alloca (sizeof (struct set));
sets[0].rtl = x;
/* Ignore SETs that are unconditional jumps.
{
int lim = XVECLEN (x, 0);
- sets = (struct set *) alloca (lim * sizeof (struct set));
+ sets = alloca (lim * sizeof (struct set));
/* Find all regs explicitly clobbered in this insn,
and ensure they are not replaced with any other regs
&& (tem = find_reg_note (insn, REG_EQUAL, NULL_RTX)) != 0
&& (! rtx_equal_p (XEXP (tem, 0), SET_SRC (sets[0].rtl))
|| GET_CODE (SET_DEST (sets[0].rtl)) == STRICT_LOW_PART))
- src_eqv = canon_reg (XEXP (tem, 0), NULL_RTX);
+ {
+ src_eqv = fold_rtx (canon_reg (XEXP (tem, 0), NULL_RTX), insn);
+ XEXP (tem, 0) = src_eqv;
+ }
/* Canonicalize sources and addresses of destinations.
We do this in a separate pass to avoid problems when a MATCH_DUP is
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;
eqvmode = GET_MODE (SUBREG_REG (XEXP (dest, 0)));
do_not_record = 0;
hash_arg_in_memory = 0;
- src_eqv = fold_rtx (src_eqv, insn);
src_eqv_hash = HASH (src_eqv, eqvmode);
/* Find the equivalence class for the equivalent expression. */
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.
continue;
}
- if (elt)
+ if (elt)
{
src_elt_cost = elt->cost;
src_elt_regcost = elt->regcost;
}
- /* Find cheapest and skip it for the next time. For items
+ /* Find cheapest and skip it for the next time. For items
of equal cost, use this order:
src_folded, src, src_eqv, src_related and hash table entry. */
if (src_folded
{
trial = src_folded, src_folded_cost = MAX_COST;
if (src_folded_force_flag)
- trial = force_const_mem (mode, trial);
+ {
+ rtx forced = force_const_mem (mode, trial);
+ if (forced)
+ trial = forced;
+ }
}
else if (src
&& preferrable (src_cost, src_regcost,
else if (src_related
&& preferrable (src_related_cost, src_related_regcost,
src_elt_cost, src_elt_regcost) <= 0)
- trial = copy_rtx (src_related), src_related_cost = MAX_COST;
+ trial = copy_rtx (src_related), src_related_cost = MAX_COST;
else
{
trial = copy_rtx (elt->exp);
/* 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;
}
src_folded_force_flag = 1;
src_folded = trial;
src_folded_cost = constant_pool_entries_cost;
+ src_folded_regcost = constant_pool_entries_regcost;
}
}
&& 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);
-
- /* 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,
- record this fact with a REG_WAS_0 note on this insn.
-
- Note that the *register* is required to have previously held 0,
- not just any register in the quantity and we must point to the
- insn that set that register to zero.
-
- Rather than track each register individually, we just see if
- the last set for this quantity was for this register. */
-
- if (REGNO_QTY_VALID_P (REGNO (dest)))
+ /* We only want a REG_EQUAL note if src_const != src. */
+ if (! rtx_equal_p (src, src_const))
{
- int dest_q = REG_QTY (REGNO (dest));
- struct qty_table_elem *dest_ent = &qty_table[dest_q];
-
- if (dest_ent->const_rtx == const0_rtx)
- {
- /* See if we previously had a REG_WAS_0 note. */
- rtx note = find_reg_note (insn, REG_WAS_0, NULL_RTX);
- rtx const_insn = dest_ent->const_insn;
+ /* Make sure that the rtx is not shared. */
+ src_const = copy_rtx (src_const);
- if ((tem = single_set (const_insn)) != 0
- && rtx_equal_p (SET_DEST (tem), dest))
- {
- if (note)
- XEXP (note, 0) = const_insn;
- else
- REG_NOTES (insn)
- = gen_rtx_INSN_LIST (REG_WAS_0, const_insn,
- REG_NOTES (insn));
- }
- }
+ /* 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);
}
}
and hope for the best. */
if (n_sets == 1)
{
- rtx new = emit_jump_insn_before (gen_jump (XEXP (src, 0)), insn);
+ rtx new = emit_jump_insn_after (gen_jump (XEXP (src, 0)), insn);
JUMP_LABEL (new) = XEXP (src, 0);
LABEL_NUSES (XEXP (src, 0))++;
+ delete_insn (insn);
insn = new;
/* Now emit a BARRIER after the unconditional jump. */
else
INSN_CODE (insn) = -1;
- never_reached_warning (insn);
+ never_reached_warning (insn, NULL);
/* Do not bother deleting any unreachable code,
let jump/flow do that. */
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
rtx new_src = 0;
unsigned src_hash;
struct table_elt *src_elt;
+ int byte = 0;
/* Ignore invalid entries. */
if (GET_CODE (elt->exp) != REG
&& ! exp_equiv_p (elt->exp, elt->exp, 1, 0))
continue;
- new_src = gen_lowpart_if_possible (new_mode, elt->exp);
- if (new_src == 0)
- new_src = gen_rtx_SUBREG (new_mode, elt->exp, 0);
+ /* 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.
+ It is invalid to construct a SUBREG with a VOIDmode
+ SUBREG_REG, hence a zero new_src means we can't do
+ this substitution. */
+ if (! new_src)
+ continue;
src_hash = HASH (new_src, new_mode);
src_elt = lookup (new_src, src_hash, new_mode);
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
validate_change (insn, &SET_SRC (sets[0].rtl), dest, 1);
apply_change_group ();
- /* If there was a REG_WAS_0 note on PREV, remove it. Move
- any REG_WAS_0 note on INSN to PREV. */
- note = find_reg_note (prev, REG_WAS_0, NULL_RTX);
- if (note)
- remove_note (prev, note);
-
- note = find_reg_note (insn, REG_WAS_0, NULL_RTX);
- if (note)
- {
- remove_note (insn, note);
- XEXP (note, 1) = REG_NOTES (prev);
- REG_NOTES (prev) = note;
- }
-
/* If INSN has a REG_EQUAL note, and this note mentions
REG0, then we must delete it, because the value in
REG0 has changed. If the note's value is REG1, we must
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. */
static void
-invalidate_memory ()
+invalidate_memory (void)
{
int i;
struct table_elt *p, *next;
1 and update the register tables to show the effect. Else, return 0. */
static int
-addr_affects_sp_p (addr)
- rtx addr;
+addr_affects_sp_p (rtx addr)
{
if (GET_RTX_CLASS (GET_CODE (addr)) == 'a'
&& GET_CODE (XEXP (addr, 0)) == REG
&& 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))
X is the pattern of the insn. */
static void
-invalidate_from_clobbers (x)
- rtx x;
+invalidate_from_clobbers (rtx x)
{
if (GET_CODE (x) == CLOBBER)
{
Return the replacement for X. */
static rtx
-cse_process_notes (x, object)
- rtx x;
- rtx object;
+cse_process_notes (rtx x, rtx object)
{
enum rtx_code code = GET_CODE (x);
const char *fmt = GET_RTX_FORMAT (code);
case SYMBOL_REF:
case LABEL_REF:
case CONST_DOUBLE:
+ case CONST_VECTOR:
case PC:
case CC0:
case LO_SUM:
jumps to a label used only once. */
static void
-cse_around_loop (loop_start)
- rtx loop_start;
+cse_around_loop (rtx loop_start)
{
rtx insn;
int i;
since they are done elsewhere. This function is called via note_stores. */
static void
-invalidate_skipped_set (dest, set, data)
- rtx set;
- rtx dest;
- void *data ATTRIBUTE_UNUSED;
+invalidate_skipped_set (rtx dest, rtx set, void *data ATTRIBUTE_UNUSED)
{
enum rtx_code code = GET_CODE (dest);
}
if (GET_CODE (set) == CLOBBER
-#ifdef HAVE_cc0
- || dest == cc0_rtx
-#endif
+ || CC0_P (dest)
|| dest == pc_rtx)
return;
conditionally executed. */
static void
-invalidate_skipped_block (start)
- rtx start;
+invalidate_skipped_block (rtx start)
{
rtx insn;
NULL_RTX. */
static void
-cse_check_loop_start (x, set, data)
- rtx x;
- rtx set ATTRIBUTE_UNUSED;
- void *data;
+cse_check_loop_start (rtx x, rtx set ATTRIBUTE_UNUSED, void *data)
{
rtx *cse_check_loop_start_value = (rtx *) data;
In any event, we invalidate whatever this SET or CLOBBER modifies. */
static void
-cse_set_around_loop (x, insn, loop_start)
- rtx x;
- rtx insn;
- rtx loop_start;
+cse_set_around_loop (rtx x, rtx insn, rtx loop_start)
{
struct table_elt *src_elt;
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))
{
abort ();
}
else
- emit_insn_after (move, p);
+ {
+ if (CONSTANT_P (SET_SRC (set))
+ && ! find_reg_equal_equiv_note (insn))
+ set_unique_reg_note (insn, REG_EQUAL,
+ SET_SRC (set));
+ if (control_flow_insn_p (p))
+ /* p can cause a control flow transfer so it
+ is the last insn of a basic block. We can't
+ therefore use emit_insn_after. */
+ emit_insn_before (move, next_nonnote_insn (p));
+ else
+ emit_insn_after (move, p);
+ }
}
break;
}
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
to construct the output branch path. */
void
-cse_end_of_basic_block (insn, data, follow_jumps, after_loop, skip_blocks)
- rtx insn;
- struct cse_basic_block_data *data;
- int follow_jumps;
- int after_loop;
- int skip_blocks;
+cse_end_of_basic_block (rtx insn, struct cse_basic_block_data *data,
+ int follow_jumps, int after_loop, int skip_blocks)
{
rtx p = insn, q;
int nsets = 0;
/* 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
in conditional jump instructions. */
int
-cse_main (f, nregs, after_loop, file)
- rtx f;
- int nregs;
- int after_loop;
- FILE *file;
+cse_main (rtx f, int nregs, int after_loop, FILE *file)
{
struct cse_basic_block_data val;
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;
+ constant_pool_entries_regcost = 0;
val.path_size = 0;
init_recog ();
max_insn_uid = get_max_uid ();
- reg_eqv_table = (struct reg_eqv_elem *)
- xmalloc (nregs * sizeof (struct reg_eqv_elem));
+ reg_eqv_table = xmalloc (nregs * sizeof (struct reg_eqv_elem));
#ifdef LOAD_EXTEND_OP
/* Find the largest uid. */
max_uid = get_max_uid ();
- uid_cuid = (int *) xcalloc (max_uid + 1, sizeof (int));
+ uid_cuid = xcalloc (max_uid + 1, sizeof (int));
/* Compute the mapping from uids to cuids.
CUIDs are numbers assigned to insns, like uids,
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. */
static rtx
-cse_basic_block (from, to, next_branch, around_loop)
- rtx from, to;
- struct branch_path *next_branch;
- int around_loop;
+cse_basic_block (rtx from, rtx to, struct branch_path *next_branch,
+ int around_loop)
{
rtx insn;
int to_usage = 0;
/* This array is undefined before max_reg, so only allocate
the space actually needed and adjust the start. */
- qty_table
- = (struct qty_table_elem *) xmalloc ((max_qty - max_reg)
- * sizeof (struct qty_table_elem));
+ qty_table = xmalloc ((max_qty - max_reg) * sizeof (struct qty_table_elem));
qty_table -= max_reg;
new_basic_block ();
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,
we can cse into the loop. Don't do this if we changed the jump
structure of a loop unless we aren't going to be following jumps. */
- insn = prev_nonnote_insn(to);
+ insn = prev_nonnote_insn (to);
if ((cse_jumps_altered == 0
|| (flag_cse_follow_jumps == 0 && flag_cse_skip_blocks == 0))
&& around_loop && to != 0
there isn't a REG_LABEL note. Return one if so. DATA is the insn. */
static int
-check_for_label_ref (rtl, data)
- rtx *rtl;
- void *data;
+check_for_label_ref (rtx *rtl, void *data)
{
rtx insn = (rtx) data;
\f
/* Count the number of times registers are used (not set) in X.
COUNTS is an array in which we accumulate the count, INCR is how much
- we count each register usage.
-
- Don't count a usage of DEST, which is the SET_DEST of a SET which
- contains X in its SET_SRC. This is because such a SET does not
- modify the liveness of DEST. */
+ we count each register usage. */
static void
-count_reg_usage (x, counts, dest, incr)
- rtx x;
- int *counts;
- rtx dest;
- int incr;
+count_reg_usage (rtx x, int *counts, int incr)
{
enum rtx_code code;
+ rtx note;
const char *fmt;
int i, j;
switch (code = GET_CODE (x))
{
case REG:
- if (x != dest)
- counts[REGNO (x)] += incr;
+ counts[REGNO (x)] += incr;
return;
case PC:
case CONST:
case CONST_INT:
case CONST_DOUBLE:
+ case CONST_VECTOR:
case SYMBOL_REF:
case LABEL_REF:
return;
/* If we are clobbering a MEM, mark any registers inside the address
as being used. */
if (GET_CODE (XEXP (x, 0)) == MEM)
- count_reg_usage (XEXP (XEXP (x, 0), 0), counts, NULL_RTX, incr);
+ count_reg_usage (XEXP (XEXP (x, 0), 0), counts, incr);
return;
case SET:
/* 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),
- incr);
+ count_reg_usage (SET_DEST (x), counts, incr);
+ count_reg_usage (SET_SRC (x), counts, incr);
return;
case CALL_INSN:
- count_reg_usage (CALL_INSN_FUNCTION_USAGE (x), counts, NULL_RTX, incr);
+ count_reg_usage (CALL_INSN_FUNCTION_USAGE (x), counts, incr);
/* Fall through. */
case INSN:
case JUMP_INSN:
- count_reg_usage (PATTERN (x), counts, NULL_RTX, incr);
+ count_reg_usage (PATTERN (x), counts, incr);
/* 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, incr);
+ eqv = XEXP (eqv, 1);
+ }
+ while (eqv && GET_CODE (eqv) == EXPR_LIST);
+ else
+ count_reg_usage (eqv, counts, 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))
- count_reg_usage (XEXP (x, 0), counts, NULL_RTX, incr);
- count_reg_usage (XEXP (x, 1), counts, NULL_RTX, incr);
+ || (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, incr);
+
+ count_reg_usage (XEXP (x, 1), counts, incr);
return;
+ case ASM_OPERANDS:
+ /* Iterate over just the inputs, not the constraints as well. */
+ for (i = ASM_OPERANDS_INPUT_LENGTH (x) - 1; i >= 0; i--)
+ count_reg_usage (ASM_OPERANDS_INPUT (x, i), counts, incr);
+ return;
+
+ case INSN_LIST:
+ abort ();
+
default:
break;
}
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
if (fmt[i] == 'e')
- count_reg_usage (XEXP (x, i), counts, dest, incr);
+ count_reg_usage (XEXP (x, i), counts, incr);
else if (fmt[i] == 'E')
for (j = XVECLEN (x, i) - 1; j >= 0; j--)
- count_reg_usage (XVECEXP (x, i, j), counts, dest, incr);
+ count_reg_usage (XVECEXP (x, i, j), counts, incr);
}
}
\f
/* Return true if set is live. */
static bool
-set_live_p (set, insn, counts)
- rtx set;
- rtx insn ATTRIBUTE_UNUSED; /* Only used with HAVE_cc0. */
- int *counts;
+set_live_p (rtx set, rtx insn ATTRIBUTE_UNUSED, /* Only used with HAVE_cc0. */
+ int *counts)
{
#ifdef HAVE_cc0
rtx tem;
/* Return true if insn is live. */
static bool
-insn_live_p (insn, counts)
- rtx insn;
- int *counts;
+insn_live_p (rtx insn, 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)
{
/* Return true if libcall is dead as a whole. */
static bool
-dead_libcall_p (insn)
- rtx insn;
+dead_libcall_p (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);
- if (set && validate_change (insn, &SET_SRC (set), new, 0))
+ /* While changing insn, we must update the counts accordingly. */
+ count_reg_usage (insn, counts, -1);
+
+ if (validate_change (insn, &SET_SRC (set), new, 0))
+ {
+ count_reg_usage (insn, counts, 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, 1);
remove_note (insn, find_reg_note (insn, REG_RETVAL, NULL_RTX));
+ remove_note (insn, note);
return true;
}
}
+
+ count_reg_usage (insn, counts, 1);
return false;
}
move dead invariants out of loops or make givs for dead quantities. The
remaining passes of the compilation are also sped up. */
-void
-delete_trivially_dead_insns (insns, nreg, preserve_basic_blocks)
- rtx insns;
- int nreg;
- int preserve_basic_blocks;
+int
+delete_trivially_dead_insns (rtx insns, int nreg)
{
int *counts;
rtx insn, prev;
- int i;
int in_libcall = 0, dead_libcall = 0;
- basic_block bb;
+ int ndead = 0, nlastdead, niterations = 0;
+ timevar_push (TV_DELETE_TRIVIALLY_DEAD);
/* First count the number of times each register is used. */
- counts = (int *) xcalloc (nreg, sizeof (int));
+ counts = xcalloc (nreg, sizeof (int));
for (insn = next_real_insn (insns); insn; insn = next_real_insn (insn))
- count_reg_usage (insn, counts, NULL_RTX, 1);
-
- /* Go from the last insn to the first and delete insns that only set unused
- registers or copy a register to itself. As we delete an insn, remove
- usage counts for registers it uses.
-
- The first jump optimization pass may leave a real insn as the last
- insn in the function. We must not skip that insn or we may end
- up deleting code that is not really dead. */
- insn = get_last_insn ();
- if (! INSN_P (insn))
- insn = prev_real_insn (insn);
-
- if (!preserve_basic_blocks)
- for (; insn; insn = prev)
- {
- int live_insn = 0;
-
- prev = prev_real_insn (insn);
-
- /* Don't delete any insns that are part of a libcall block unless
- we can delete the whole libcall block.
-
- Flow or loop might get confused if we did that. Remember
- that we are scanning backwards. */
- if (find_reg_note (insn, REG_RETVAL, NULL_RTX))
- {
- in_libcall = 1;
- live_insn = 1;
- dead_libcall = dead_libcall_p (insn);
- }
- else if (in_libcall)
- live_insn = ! dead_libcall;
- else
- live_insn = insn_live_p (insn, counts);
+ count_reg_usage (insn, counts, 1);
- /* If this is a dead insn, delete it and show registers in it aren't
- being used. */
-
- if (! live_insn)
- {
- count_reg_usage (insn, counts, NULL_RTX, -1);
- delete_related_insns (insn);
- }
+ do
+ {
+ nlastdead = ndead;
+ niterations++;
+ /* Go from the last insn to the first and delete insns that only set unused
+ registers or copy a register to itself. As we delete an insn, remove
+ usage counts for registers it uses.
+
+ The first jump optimization pass may leave a real insn as the last
+ insn in the function. We must not skip that insn or we may end
+ up deleting code that is not really dead. */
+ insn = get_last_insn ();
+ if (! INSN_P (insn))
+ insn = prev_real_insn (insn);
- if (find_reg_note (insn, REG_LIBCALL, NULL_RTX))
- {
- in_libcall = 0;
- dead_libcall = 0;
- }
- }
- else
- for (i = 0; i < n_basic_blocks; i++)
- for (bb = BASIC_BLOCK (i), insn = bb->end; insn != bb->head; insn = prev)
+ for (; insn; insn = prev)
{
int live_insn = 0;
- prev = PREV_INSN (insn);
- if (!INSN_P (insn))
- continue;
+ prev = prev_real_insn (insn);
/* Don't delete any insns that are part of a libcall block unless
we can delete the whole libcall block.
{
in_libcall = 1;
live_insn = 1;
- dead_libcall = dead_libcall_p (insn);
+ dead_libcall = dead_libcall_p (insn, counts);
}
else if (in_libcall)
live_insn = ! dead_libcall;
if (! live_insn)
{
- count_reg_usage (insn, counts, NULL_RTX, -1);
- delete_insn (insn);
+ count_reg_usage (insn, counts, -1);
+ delete_insn_and_edges (insn);
+ ndead++;
}
if (find_reg_note (insn, REG_LIBCALL, NULL_RTX))
dead_libcall = 0;
}
}
+ }
+ while (ndead != nlastdead);
+ if (rtl_dump_file && ndead)
+ fprintf (rtl_dump_file, "Deleted %i trivially dead insns; %i iterations\n",
+ ndead, niterations);
/* Clean up. */
free (counts);
+ timevar_pop (TV_DELETE_TRIVIALLY_DEAD);
+ return ndead;
}