diff options
Diffstat (limited to 'contrib/gcc/cse.c')
-rw-r--r-- | contrib/gcc/cse.c | 1289 |
1 files changed, 770 insertions, 519 deletions
diff --git a/contrib/gcc/cse.c b/contrib/gcc/cse.c index 9566865..5081926 100644 --- a/contrib/gcc/cse.c +++ b/contrib/gcc/cse.c @@ -1,6 +1,6 @@ /* 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, 2004 Free Software Foundation, Inc. This file is part of GCC. @@ -22,6 +22,8 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA #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" @@ -38,6 +40,9 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA #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 @@ -247,8 +252,10 @@ struct qty_table_elem 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. */ @@ -266,11 +273,11 @@ static struct qty_table_elem *qty_table; 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. */ @@ -458,7 +465,9 @@ struct table_elt 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; @@ -494,9 +503,9 @@ struct table_elt 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)) @@ -505,7 +514,7 @@ struct table_elt /* 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))) @@ -560,10 +569,7 @@ static struct table_elt *last_jump_equiv_class; 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. */ @@ -588,125 +594,110 @@ 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; }; +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 *); +static int cse_change_cc_mode (rtx *, void *); +static void cse_change_cc_mode_insns (rtx, rtx, rtx); +static enum machine_mode cse_cc_succs (basic_block, rtx, rtx, bool); + /* 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, int *)); - + 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; @@ -724,9 +715,7 @@ dump_class (classp) /* 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; int *cost_p = data; @@ -757,8 +746,7 @@ approx_reg_cost_1 (xp, data) 0. If any other hard register reference occurs, return MAX_COST. */ static int -approx_reg_cost (x) - rtx x; +approx_reg_cost (rtx x) { int cost = 0; @@ -773,8 +761,7 @@ approx_reg_cost (x) 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 cases involving expressions that are entirely unwanted. */ @@ -808,9 +795,7 @@ preferrable (cost_a, regcost_a, cost_b, regcost_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 @@ -831,9 +816,7 @@ notreg_cost (x, outer) 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; @@ -844,7 +827,7 @@ rtx_cost (x, outer_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) @@ -879,17 +862,9 @@ rtx_cost (x, outer_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; } @@ -911,11 +886,9 @@ rtx_cost (x, outer_code) 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. */ @@ -925,21 +898,24 @@ address_cost (x, mode) /* 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); +} 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; @@ -957,7 +933,7 @@ get_cse_reg_info (regno) 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; @@ -987,7 +963,7 @@ get_cse_reg_info (regno) for a new basic block. */ static void -new_basic_block () +new_basic_block (void) { int i; @@ -995,7 +971,7 @@ new_basic_block () /* 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) { @@ -1032,9 +1008,8 @@ new_basic_block () } } - prev_insn = 0; - #ifdef HAVE_cc0 + prev_insn = 0; prev_insn_cc0 = 0; #endif } @@ -1043,9 +1018,7 @@ new_basic_block () 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; @@ -1070,8 +1043,7 @@ make_new_qty (reg, mode) 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); @@ -1134,8 +1106,7 @@ make_regs_eqv (new, 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); @@ -1175,8 +1146,7 @@ delete_reg_equiv (reg) of X. */ static int -mention_regs (x) - rtx x; +mention_regs (rtx x) { enum rtx_code code; int i, j; @@ -1285,10 +1255,7 @@ mention_regs (x) 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) { @@ -1365,9 +1332,7 @@ insert_regs (x, classp, modified) 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; @@ -1450,10 +1415,7 @@ remove_from_table (elt, hash) 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; @@ -1469,10 +1431,7 @@ lookup (x, hash, mode) 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; @@ -1501,9 +1460,7 @@ lookup_for_remove (x, hash, mode) 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)); @@ -1559,11 +1516,7 @@ lookup_as_function (x, code) (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; @@ -1591,7 +1544,7 @@ insert (x, classp, hash, mode) else { n_elements_made++; - elt = (struct table_elt *) xmalloc (sizeof (struct table_elt)); + elt = xmalloc (sizeof (struct table_elt)); } elt->exp = x; @@ -1611,7 +1564,7 @@ insert (x, classp, hash, mode) || (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; @@ -1622,7 +1575,7 @@ insert (x, classp, hash, mode) { 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; @@ -1749,8 +1702,7 @@ insert (x, classp, hash, mode) 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; @@ -1775,15 +1727,20 @@ merge_equiv_classes (class1, class2) hash code (it also isn't necessary). */ if (GET_CODE (exp) == REG || exp_equiv_p (exp, exp, 1, 0)) { + bool need_rehash = false; + hash_arg_in_memory = 0; hash = HASH (exp, mode); if (GET_CODE (exp) == REG) - delete_reg_equiv (REGNO (exp)); + { + need_rehash = (unsigned) REG_QTY (REGNO (exp)) != REGNO (exp); + delete_reg_equiv (REGNO (exp)); + } remove_from_table (elt, hash); - if (insert_regs (exp, class1, 0)) + if (insert_regs (exp, class1, 0) || need_rehash) { rehash_using_reg (exp); hash = HASH (exp, mode); @@ -1797,7 +1754,7 @@ merge_equiv_classes (class1, class2) /* Flush the entire hash table. */ static void -flush_hash_table () +flush_hash_table (void) { int i; struct table_elt *p; @@ -1819,16 +1776,16 @@ struct check_dependence_data { enum machine_mode mode; rtx exp; + rtx addr; }; 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) - return true_dependence (d->exp, d->mode, *x, cse_rtx_varies_p); + return canon_true_dependence (d->exp, d->mode, d->addr, *x, + cse_rtx_varies_p); else return 0; } @@ -1846,12 +1803,11 @@ check_dependence (x, data) 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; + rtx addr; switch (GET_CODE (x)) { @@ -1942,6 +1898,7 @@ invalidate (x, full_mode) return; case MEM: + addr = canon_rtx (get_addr (XEXP (x, 0))); /* Calculate the canonical version of X here so that true_dependence doesn't generate new RTL for X on each call. */ x = canon_rtx (x); @@ -1969,6 +1926,7 @@ invalidate (x, full_mode) if (!p->canon_exp) p->canon_exp = canon_rtx (p->exp); d.exp = x; + d.addr = addr; d.mode = full_mode; if (for_each_rtx (&p->canon_exp, check_dependence, &d)) remove_from_table (p, i); @@ -1988,8 +1946,7 @@ invalidate (x, full_mode) 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; @@ -2007,10 +1964,8 @@ remove_invalid_refs (regno) /* 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; @@ -2040,8 +1995,7 @@ remove_invalid_subreg_refs (regno, offset, mode) 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; @@ -2059,14 +2013,13 @@ rehash_using_reg (x) return; /* Scan all hash chains looking for valid entries that mention X. - If we find one and it is in the wrong hash chain, move it. We can skip - objects that are registers, since they are handled specially. */ + If we find one and it is in the wrong hash chain, move it. */ for (i = 0; i < HASH_SIZE; i++) for (p = table[i]; p; p = next) { next = p->next_same_hash; - if (GET_CODE (p->exp) != REG && reg_mentioned_p (x, p->exp) + if (reg_mentioned_p (x, p->exp) && exp_equiv_p (p->exp, p->exp, 1, 0) && i != (hash = safe_hash (p->exp, p->mode) & HASH_MASK)) { @@ -2091,7 +2044,7 @@ rehash_using_reg (x) register. Also update their TICK values. */ static void -invalidate_for_call () +invalidate_for_call (void) { unsigned int regno, endregno; unsigned int i; @@ -2150,9 +2103,7 @@ invalidate_for_call () 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; @@ -2220,8 +2171,7 @@ use_related_value (x, elt) /* 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; @@ -2247,9 +2197,7 @@ canon_hash_string (ps) 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; @@ -2301,7 +2249,7 @@ canon_hash (x, mode) record = false; else record = true; - + if (!record) { do_not_record = 1; @@ -2378,10 +2326,9 @@ canon_hash (x, mode) 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; @@ -2399,7 +2346,7 @@ canon_hash (x, mode) 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, @@ -2502,9 +2449,7 @@ canon_hash (x, mode) /* 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; @@ -2530,10 +2475,7 @@ safe_hash (x, mode) 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; @@ -2725,9 +2667,7 @@ exp_equiv_p (x, y, validate, equal_values) 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 @@ -2795,9 +2735,7 @@ cse_rtx_varies_p (x, from_alias) 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; @@ -2886,26 +2824,20 @@ canon_reg (x, insn) 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; @@ -2969,22 +2901,6 @@ find_best_addr (insn, loc, mode) 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 @@ -3043,10 +2959,9 @@ find_best_addr (insn, loc, mode) 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); @@ -3088,7 +3003,7 @@ find_best_addr (insn, loc, mode) || 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); @@ -3115,7 +3030,6 @@ find_best_addr (insn, loc, mode) } } } -#endif } /* Given an operation (CODE, *PARG1, *PARG2), where code is a comparison @@ -3131,10 +3045,8 @@ find_best_addr (insn, loc, mode) 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; @@ -3276,9 +3188,10 @@ find_comparison_args (code, parg1, parg2, pmode1, pmode2) 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; @@ -3328,9 +3241,7 @@ find_comparison_args (code, parg1, parg2, pmode1, pmode2) 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; @@ -3387,17 +3298,11 @@ fold_rtx (x, insn) /* 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: @@ -3637,7 +3542,10 @@ fold_rtx (x, insn) 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) @@ -3850,6 +3758,23 @@ fold_rtx (x, insn) || (new_cost == old_cost && CONSTANT_P (XEXP (x, i)))) break; + /* It's not safe to substitute the operand of a conversion + operator with a constant, as the conversion's identity + depends upon the mode of it's operand. This optimization + is handled by the call to simplify_unary_operation. */ + if (GET_RTX_CLASS (code) == '1' + && GET_MODE (replacements[j]) != mode_arg0 + && (code == ZERO_EXTEND + || code == SIGN_EXTEND + || code == TRUNCATE + || code == FLOAT_TRUNCATE + || code == FLOAT_EXTEND + || code == FLOAT + || code == FIX + || code == UNSIGNED_FLOAT + || code == UNSIGNED_FIX)) + continue; + if (validate_change (insn, &XEXP (x, i), replacements[j], 0)) break; @@ -3891,10 +3816,11 @@ fold_rtx (x, insn) || 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); @@ -3979,17 +3905,10 @@ fold_rtx (x, insn) 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; @@ -4267,7 +4186,7 @@ fold_rtx (x, insn) 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 @@ -4353,9 +4272,14 @@ fold_rtx (x, insn) 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; } @@ -4366,8 +4290,7 @@ fold_rtx (x, insn) 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))) @@ -4417,9 +4340,7 @@ equiv_constant (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); @@ -4455,16 +4376,14 @@ gen_lowpart_if_possible (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; @@ -4515,11 +4434,8 @@ record_jump_equiv (insn, taken) 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; @@ -4755,8 +4671,10 @@ struct set /* 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. */ @@ -4768,9 +4686,7 @@ struct set }; 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; @@ -4810,7 +4726,7 @@ cse_insn (insn, libcall_insn) 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. @@ -4845,7 +4761,7 @@ cse_insn (insn, libcall_insn) { 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 @@ -5529,7 +5445,11 @@ cse_insn (insn, libcall_insn) { 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, @@ -5578,6 +5498,8 @@ cse_insn (insn, libcall_insn) /* 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. */ @@ -5585,15 +5507,13 @@ cse_insn (insn, libcall_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; } @@ -5624,6 +5544,7 @@ cse_insn (insn, libcall_insn) src_folded_force_flag = 1; src_folded = trial; src_folded_cost = constant_pool_entries_cost; + src_folded_regcost = constant_pool_entries_regcost; } } @@ -5706,46 +5627,15 @@ cse_insn (insn, libcall_insn) && 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); } } @@ -5966,6 +5856,16 @@ cse_insn (insn, libcall_insn) enum machine_mode mode = GET_MODE (src) == VOIDmode ? GET_MODE (dest) : GET_MODE (src); + /* It's possible that we have a source value known to be + constant but don't have a REG_EQUAL note on the insn. + Lack of a note will mean src_eqv_elt will be NULL. This + can happen where we've generated a SUBREG to access a + CONST_INT that is already in a register in a wider mode. + Ensure that the source expression is put in the proper + constant class. */ + if (!classp) + classp = sets[i].src_const_elt; + if (sets[i].src_elt == 0) { /* Don't put a hard register source into the table if this is @@ -6189,7 +6089,7 @@ cse_insn (insn, libcall_insn) 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 @@ -6325,7 +6225,7 @@ cse_insn (insn, libcall_insn) } 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. @@ -6352,20 +6252,6 @@ cse_insn (insn, libcall_insn) 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 @@ -6400,15 +6286,14 @@ cse_insn (insn, libcall_insn) prev_insn_cc0 = this_insn_cc0; prev_insn_cc0_mode = this_insn_cc0_mode; -#endif - prev_insn = insn; +#endif } /* Remove from the hash table all expressions that reference memory. */ static void -invalidate_memory () +invalidate_memory (void) { int i; struct table_elt *p, *next; @@ -6426,8 +6311,7 @@ invalidate_memory () 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 @@ -6458,8 +6342,7 @@ addr_affects_sp_p (addr) 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) { @@ -6504,9 +6387,7 @@ invalidate_from_clobbers (x) 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); @@ -6598,8 +6479,7 @@ cse_process_notes (x, object) 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; @@ -6670,10 +6550,7 @@ cse_around_loop (loop_start) 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); @@ -6692,9 +6569,7 @@ invalidate_skipped_set (dest, set, data) } if (GET_CODE (set) == CLOBBER -#ifdef HAVE_cc0 - || dest == cc0_rtx -#endif + || CC0_P (dest) || dest == pc_rtx) return; @@ -6709,8 +6584,7 @@ invalidate_skipped_set (dest, set, data) conditionally executed. */ static void -invalidate_skipped_block (start) - rtx start; +invalidate_skipped_block (rtx start) { rtx insn; @@ -6737,10 +6611,7 @@ invalidate_skipped_block (start) 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; @@ -6771,10 +6642,7 @@ cse_check_loop_start (x, set, 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; @@ -6826,6 +6694,8 @@ cse_set_around_loop (x, insn, loop_start) 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)) { @@ -6845,6 +6715,10 @@ cse_set_around_loop (x, insn, loop_start) } else { + 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 @@ -6887,12 +6761,8 @@ cse_set_around_loop (x, insn, loop_start) 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; @@ -6989,7 +6859,7 @@ cse_end_of_basic_block (insn, data, follow_jumps, after_loop, skip_blocks) 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 @@ -7109,19 +6979,19 @@ cse_end_of_basic_block (insn, data, follow_jumps, after_loop, skip_blocks) 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 (); @@ -7131,8 +7001,7 @@ cse_main (f, nregs, after_loop, file) 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 @@ -7148,7 +7017,7 @@ cse_main (f, nregs, after_loop, file) /* 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, @@ -7242,6 +7111,7 @@ cse_main (f, nregs, after_loop, file) end_alias_analysis (); free (uid_cuid); free (reg_eqv_table); + free (val.path); return cse_jumps_altered || recorded_label_ref; } @@ -7255,22 +7125,19 @@ cse_main (f, nregs, after_loop, file) 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; rtx libcall_insn = NULL_RTX; int num_insns = 0; + int no_conflict = 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 (); @@ -7314,8 +7181,8 @@ cse_basic_block (from, to, next_branch, around_loop) Then follow this branch. */ #ifdef HAVE_cc0 prev_insn_cc0 = 0; -#endif prev_insn = insn; +#endif insn = JUMP_LABEL (insn); continue; } @@ -7345,11 +7212,26 @@ cse_basic_block (from, to, next_branch, around_loop) if ((p = find_reg_note (insn, REG_LIBCALL, NULL_RTX))) libcall_insn = XEXP (p, 0); else if (find_reg_note (insn, REG_RETVAL, NULL_RTX)) - libcall_insn = 0; + { + /* Keep libcall_insn for the last SET insn of a no-conflict + block to prevent changing the destination. */ + if (! no_conflict) + libcall_insn = 0; + else + no_conflict = -1; + } + else if (find_reg_note (insn, REG_NO_CONFLICT, NULL_RTX)) + no_conflict = 1; } cse_insn (insn, libcall_insn); + if (no_conflict == -1) + { + libcall_insn = 0; + no_conflict = 0; + } + /* If we haven't already found an insn where we added a LABEL_REF, check this one. */ if (GET_CODE (insn) == INSN && ! recorded_label_ref @@ -7419,7 +7301,10 @@ cse_basic_block (from, to, next_branch, around_loop) 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, @@ -7468,9 +7353,7 @@ cse_basic_block (from, to, next_branch, around_loop) 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; @@ -7487,18 +7370,10 @@ check_for_label_ref (rtl, data) /* 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; @@ -7511,8 +7386,7 @@ count_reg_usage (x, counts, dest, incr) switch (code = GET_CODE (x)) { case REG: - if (x != dest) - counts[REGNO (x)] += incr; + counts[REGNO (x)] += incr; return; case PC: @@ -7529,39 +7403,61 @@ count_reg_usage (x, counts, dest, incr) /* 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. */ note = find_reg_equal_equiv_note (x); if (note) - count_reg_usage (XEXP (note, 0), counts, NULL_RTX, incr); + { + 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: + if (REG_NOTE_KIND (x) == REG_EQUAL + || (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: @@ -7575,19 +7471,17 @@ count_reg_usage (x, counts, dest, incr) 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); } } /* 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; @@ -7620,9 +7514,7 @@ set_live_p (set, insn, counts) /* 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 (flag_non_call_exceptions && may_trap_p (PATTERN (insn))) @@ -7652,37 +7544,51 @@ insn_live_p (insn, counts) /* Return true if libcall is dead as a whole. */ static bool -dead_libcall_p (insn, counts) - rtx insn; - int *counts; +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; - if (!new) - new = XEXP (note, 0); + set = single_set (insn); + if (!set) + return false; + + 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, -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 (set && validate_change (insn, &SET_SRC (set), new, 0)) + 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, 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, 1); return false; } @@ -7695,9 +7601,7 @@ dead_libcall_p (insn, counts) remaining passes of the compilation are also sped up. */ int -delete_trivially_dead_insns (insns, nreg) - rtx insns; - int nreg; +delete_trivially_dead_insns (rtx insns, int nreg) { int *counts; rtx insn, prev; @@ -7706,9 +7610,9 @@ delete_trivially_dead_insns (insns, nreg) 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); + count_reg_usage (insn, counts, 1); do { @@ -7752,7 +7656,7 @@ delete_trivially_dead_insns (insns, nreg) if (! live_insn) { - count_reg_usage (insn, counts, NULL_RTX, -1); + count_reg_usage (insn, counts, -1); delete_insn_and_edges (insn); ndead++; } @@ -7774,3 +7678,350 @@ delete_trivially_dead_insns (insns, nreg) timevar_pop (TV_DELETE_TRIVIALLY_DEAD); return ndead; } + +/* This function is called via for_each_rtx. The argument, NEWREG, is + a condition code register with the desired mode. If we are looking + at the same register in a different mode, replace it with + NEWREG. */ + +static int +cse_change_cc_mode (rtx *loc, void *data) +{ + rtx newreg = (rtx) data; + + if (*loc + && GET_CODE (*loc) == REG + && REGNO (*loc) == REGNO (newreg) + && GET_MODE (*loc) != GET_MODE (newreg)) + { + *loc = newreg; + return -1; + } + return 0; +} + +/* Change the mode of any reference to the register REGNO (NEWREG) to + GET_MODE (NEWREG), starting at START. Stop before END. Stop at + any instruction which modifies NEWREG. */ + +static void +cse_change_cc_mode_insns (rtx start, rtx end, rtx newreg) +{ + rtx insn; + + for (insn = start; insn != end; insn = NEXT_INSN (insn)) + { + if (! INSN_P (insn)) + continue; + + if (reg_set_p (newreg, insn)) + return; + + for_each_rtx (&PATTERN (insn), cse_change_cc_mode, newreg); + for_each_rtx (®_NOTES (insn), cse_change_cc_mode, newreg); + } +} + +/* BB is a basic block which finishes with CC_REG as a condition code + register which is set to CC_SRC. Look through the successors of BB + to find blocks which have a single predecessor (i.e., this one), + and look through those blocks for an assignment to CC_REG which is + equivalent to CC_SRC. CAN_CHANGE_MODE indicates whether we are + permitted to change the mode of CC_SRC to a compatible mode. This + returns VOIDmode if no equivalent assignments were found. + Otherwise it returns the mode which CC_SRC should wind up with. + + The main complexity in this function is handling the mode issues. + We may have more than one duplicate which we can eliminate, and we + try to find a mode which will work for multiple duplicates. */ + +static enum machine_mode +cse_cc_succs (basic_block bb, rtx cc_reg, rtx cc_src, bool can_change_mode) +{ + bool found_equiv; + enum machine_mode mode; + unsigned int insn_count; + edge e; + rtx insns[2]; + enum machine_mode modes[2]; + rtx last_insns[2]; + unsigned int i; + rtx newreg; + + /* We expect to have two successors. Look at both before picking + the final mode for the comparison. If we have more successors + (i.e., some sort of table jump, although that seems unlikely), + then we require all beyond the first two to use the same + mode. */ + + found_equiv = false; + mode = GET_MODE (cc_src); + insn_count = 0; + for (e = bb->succ; e; e = e->succ_next) + { + rtx insn; + rtx end; + + if (e->flags & EDGE_COMPLEX) + continue; + + if (! e->dest->pred + || e->dest->pred->pred_next + || e->dest == EXIT_BLOCK_PTR) + continue; + + end = NEXT_INSN (BB_END (e->dest)); + for (insn = BB_HEAD (e->dest); insn != end; insn = NEXT_INSN (insn)) + { + rtx set; + + if (! INSN_P (insn)) + continue; + + /* If CC_SRC is modified, we have to stop looking for + something which uses it. */ + if (modified_in_p (cc_src, insn)) + break; + + /* Check whether INSN sets CC_REG to CC_SRC. */ + set = single_set (insn); + if (set + && GET_CODE (SET_DEST (set)) == REG + && REGNO (SET_DEST (set)) == REGNO (cc_reg)) + { + bool found; + enum machine_mode set_mode; + enum machine_mode comp_mode; + + found = false; + set_mode = GET_MODE (SET_SRC (set)); + comp_mode = set_mode; + if (rtx_equal_p (cc_src, SET_SRC (set))) + found = true; + else if (GET_CODE (cc_src) == COMPARE + && GET_CODE (SET_SRC (set)) == COMPARE + && mode != set_mode + && rtx_equal_p (XEXP (cc_src, 0), + XEXP (SET_SRC (set), 0)) + && rtx_equal_p (XEXP (cc_src, 1), + XEXP (SET_SRC (set), 1))) + + { + comp_mode = (*targetm.cc_modes_compatible) (mode, set_mode); + if (comp_mode != VOIDmode + && (can_change_mode || comp_mode == mode)) + found = true; + } + + if (found) + { + found_equiv = true; + if (insn_count < ARRAY_SIZE (insns)) + { + insns[insn_count] = insn; + modes[insn_count] = set_mode; + last_insns[insn_count] = end; + ++insn_count; + + if (mode != comp_mode) + { + if (! can_change_mode) + abort (); + mode = comp_mode; + PUT_MODE (cc_src, mode); + } + } + else + { + if (set_mode != mode) + { + /* We found a matching expression in the + wrong mode, but we don't have room to + store it in the array. Punt. This case + should be rare. */ + break; + } + /* INSN sets CC_REG to a value equal to CC_SRC + with the right mode. We can simply delete + it. */ + delete_insn (insn); + } + + /* We found an instruction to delete. Keep looking, + in the hopes of finding a three-way jump. */ + continue; + } + + /* We found an instruction which sets the condition + code, so don't look any farther. */ + break; + } + + /* If INSN sets CC_REG in some other way, don't look any + farther. */ + if (reg_set_p (cc_reg, insn)) + break; + } + + /* If we fell off the bottom of the block, we can keep looking + through successors. We pass CAN_CHANGE_MODE as false because + we aren't prepared to handle compatibility between the + further blocks and this block. */ + if (insn == end) + { + enum machine_mode submode; + + submode = cse_cc_succs (e->dest, cc_reg, cc_src, false); + if (submode != VOIDmode) + { + if (submode != mode) + abort (); + found_equiv = true; + can_change_mode = false; + } + } + } + + if (! found_equiv) + return VOIDmode; + + /* Now INSN_COUNT is the number of instructions we found which set + CC_REG to a value equivalent to CC_SRC. The instructions are in + INSNS. The modes used by those instructions are in MODES. */ + + newreg = NULL_RTX; + for (i = 0; i < insn_count; ++i) + { + if (modes[i] != mode) + { + /* We need to change the mode of CC_REG in INSNS[i] and + subsequent instructions. */ + if (! newreg) + { + if (GET_MODE (cc_reg) == mode) + newreg = cc_reg; + else + newreg = gen_rtx_REG (mode, REGNO (cc_reg)); + } + cse_change_cc_mode_insns (NEXT_INSN (insns[i]), last_insns[i], + newreg); + } + + delete_insn (insns[i]); + } + + return mode; +} + +/* If we have a fixed condition code register (or two), walk through + the instructions and try to eliminate duplicate assignments. */ + +void +cse_condition_code_reg (void) +{ + unsigned int cc_regno_1; + unsigned int cc_regno_2; + rtx cc_reg_1; + rtx cc_reg_2; + basic_block bb; + + if (! (*targetm.fixed_condition_code_regs) (&cc_regno_1, &cc_regno_2)) + return; + + cc_reg_1 = gen_rtx_REG (CCmode, cc_regno_1); + if (cc_regno_2 != INVALID_REGNUM) + cc_reg_2 = gen_rtx_REG (CCmode, cc_regno_2); + else + cc_reg_2 = NULL_RTX; + + FOR_EACH_BB (bb) + { + rtx last_insn; + rtx cc_reg; + rtx insn; + rtx cc_src_insn; + rtx cc_src; + enum machine_mode mode; + enum machine_mode orig_mode; + + /* Look for blocks which end with a conditional jump based on a + condition code register. Then look for the instruction which + sets the condition code register. Then look through the + successor blocks for instructions which set the condition + code register to the same value. There are other possible + uses of the condition code register, but these are by far the + most common and the ones which we are most likely to be able + to optimize. */ + + last_insn = BB_END (bb); + if (GET_CODE (last_insn) != JUMP_INSN) + continue; + + if (reg_referenced_p (cc_reg_1, PATTERN (last_insn))) + cc_reg = cc_reg_1; + else if (cc_reg_2 && reg_referenced_p (cc_reg_2, PATTERN (last_insn))) + cc_reg = cc_reg_2; + else + continue; + + cc_src_insn = NULL_RTX; + cc_src = NULL_RTX; + for (insn = PREV_INSN (last_insn); + insn && insn != PREV_INSN (BB_HEAD (bb)); + insn = PREV_INSN (insn)) + { + rtx set; + + if (! INSN_P (insn)) + continue; + set = single_set (insn); + if (set + && GET_CODE (SET_DEST (set)) == REG + && REGNO (SET_DEST (set)) == REGNO (cc_reg)) + { + cc_src_insn = insn; + cc_src = SET_SRC (set); + break; + } + else if (reg_set_p (cc_reg, insn)) + break; + } + + if (! cc_src_insn) + continue; + + if (modified_between_p (cc_src, cc_src_insn, NEXT_INSN (last_insn))) + continue; + + /* Now CC_REG is a condition code register used for a + conditional jump at the end of the block, and CC_SRC, in + CC_SRC_INSN, is the value to which that condition code + register is set, and CC_SRC is still meaningful at the end of + the basic block. */ + + orig_mode = GET_MODE (cc_src); + mode = cse_cc_succs (bb, cc_reg, cc_src, true); + if (mode != VOIDmode) + { + if (mode != GET_MODE (cc_src)) + abort (); + if (mode != orig_mode) + { + rtx newreg = gen_rtx_REG (mode, REGNO (cc_reg)); + + /* Change the mode of CC_REG in CC_SRC_INSN to + GET_MODE (NEWREG). */ + for_each_rtx (&PATTERN (cc_src_insn), cse_change_cc_mode, + newreg); + for_each_rtx (®_NOTES (cc_src_insn), cse_change_cc_mode, + newreg); + + /* Do the same in the following insns that use the + current value of CC_REG within BB. */ + cse_change_cc_mode_insns (NEXT_INSN (cc_src_insn), + NEXT_INSN (last_insn), + newreg); + } + } + } +} |