X-Git-Url: http://git.sourceforge.jp/view?a=blobdiff_plain;f=gcc%2Fcse.c;h=3ab6b37a8ea99f51cad920714aa7f161ceff7884;hb=58c5f008ca19c1255dacf9ccd88f5eee28d00a5d;hp=590adfad7b4b44c3df95fe92e83cb29fcc63cad5;hpb=045ed337d9a8d9ffba671a3877b1d518eee2ea35;p=pf3gnuchains%2Fgcc-fork.git diff --git a/gcc/cse.c b/gcc/cse.c index 590adfad7b4..3ab6b37a8ea 100644 --- a/gcc/cse.c +++ b/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, 2003, 2004, 2005, 2006, 2007 + 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc. This file is part of GCC. @@ -20,7 +20,6 @@ along with GCC; see the file COPYING3. If not see . */ #include "config.h" -/* stdio.h must precede rtl.h for FFS. */ #include "system.h" #include "coretypes.h" #include "tm.h" @@ -30,11 +29,11 @@ along with GCC; see the file COPYING3. If not see #include "regs.h" #include "basic-block.h" #include "flags.h" -#include "real.h" #include "insn-config.h" #include "recog.h" #include "function.h" #include "expr.h" +#include "diagnostic-core.h" #include "toplev.h" #include "output.h" #include "ggc.h" @@ -283,6 +282,7 @@ static enum machine_mode this_insn_cc0_mode, prev_insn_cc0_mode; /* Insn being scanned. */ static rtx this_insn; +static bool optimize_this_for_speed_p; /* Index by register number, gives the number of the next (or previous) register in the chain of registers sharing the same @@ -348,14 +348,17 @@ static unsigned int cse_reg_info_timestamp; static HARD_REG_SET hard_regs_in_table; -/* Nonzero if cse has altered conditional jump insns - in such a way that jump optimization should be redone. */ +/* True if CSE has altered the CFG. */ +static bool cse_cfg_altered; -static int cse_jumps_altered; +/* True if CSE has altered conditional jump insns in such a way + that jump optimization should be redone. */ +static bool cse_jumps_altered; -/* Nonzero if we put a LABEL_REF into the hash table for an INSN without a - REG_LABEL, we have to rerun jump after CSE to put in the note. */ -static int recorded_label_ref; +/* True if we put a LABEL_REF into the hash table for an INSN + without a REG_LABEL_OPERAND, we have to rerun jump after CSE + to put in the note. */ +static bool recorded_label_ref; /* canon_hash stores 1 in do_not_record if it notices a reference to CC0, PC, or some other volatile @@ -498,6 +501,11 @@ struct table_elt #define REGNO_QTY_VALID_P(N) (REG_QTY (N) >= 0) +/* Compare table_elt X and Y and return true iff X is cheaper than Y. */ + +#define CHEAPER(X, Y) \ + (preferable ((X)->cost, (X)->regcost, (Y)->cost, (Y)->regcost) < 0) + static struct table_elt *table[HASH_SIZE]; /* Chain of `struct table_elt's made so far for this function @@ -513,6 +521,14 @@ static struct table_elt *free_element_chain; static int constant_pool_entries_cost; static int constant_pool_entries_regcost; +/* Trace a patch through the CFG. */ + +struct branch_path +{ + /* The basic block for this path entry. */ + basic_block bb; +}; + /* This data describes a block that will be processed by cse_extended_basic_block. */ @@ -523,11 +539,7 @@ struct cse_basic_block_data /* Size of current branch path, if any. */ int path_size; /* Current path, indicating which basic_blocks will be processed. */ - struct branch_path - { - /* The basic block for this path entry. */ - basic_block bb; - } *path; + struct branch_path *path; }; @@ -551,9 +563,12 @@ 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 void remove_pseudo_from_table (rtx, 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_with_costs (rtx, struct table_elt *, unsigned, + enum machine_mode, int, int); static struct table_elt *insert (rtx, struct table_elt *, unsigned, enum machine_mode); static void merge_equiv_classes (struct table_elt *, struct table_elt *); @@ -569,7 +584,7 @@ static rtx use_related_value (rtx, struct table_elt *); static inline unsigned canon_hash (rtx, enum machine_mode); static inline unsigned safe_hash (rtx, enum machine_mode); -static unsigned hash_rtx_string (const char *); +static inline unsigned hash_rtx_string (const char *); static rtx canon_reg (rtx, rtx); static enum rtx_code find_comparison_args (enum rtx_code, rtx *, rtx *, @@ -580,7 +595,7 @@ static rtx equiv_constant (rtx); static void record_jump_equiv (rtx, bool); static void record_jump_cond (enum rtx_code, enum machine_mode, rtx, rtx, int); -static void cse_insn (rtx, rtx); +static void cse_insn (rtx); static void cse_prescan_path (struct cse_basic_block_data *); static void invalidate_from_clobbers (rtx); static rtx cse_process_notes (rtx, rtx, bool *); @@ -595,11 +610,11 @@ 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_insn (rtx, rtx); static void cse_change_cc_mode_insns (rtx, rtx, rtx); -static enum machine_mode cse_cc_succs (basic_block, rtx, rtx, bool); +static enum machine_mode cse_cc_succs (basic_block, basic_block, rtx, rtx, + bool); #undef RTL_HOOKS_GEN_LOWPART @@ -627,7 +642,7 @@ fixed_base_plus_p (rtx x) return false; case PLUS: - if (GET_CODE (XEXP (x, 1)) != CONST_INT) + if (!CONST_INT_P (XEXP (x, 1))) return false; return fixed_base_plus_p (XEXP (x, 0)); @@ -660,7 +675,7 @@ static int approx_reg_cost_1 (rtx *xp, void *data) { rtx x = *xp; - int *cost_p = data; + int *cost_p = (int *) data; if (x && REG_P (x)) { @@ -670,7 +685,7 @@ approx_reg_cost_1 (rtx *xp, void *data) { if (regno < FIRST_PSEUDO_REGISTER) { - if (SMALL_REGISTER_CLASSES) + if (targetm.small_register_classes_for_mode_p (GET_MODE (x))) return 1; *cost_p += 2; } @@ -749,7 +764,7 @@ notreg_cost (rtx x, enum rtx_code outer) && TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (GET_MODE (x)), GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (x))))) ? 0 - : rtx_cost (x, outer) * 2); + : rtx_cost (x, outer, optimize_this_for_speed_p) * 2); } @@ -910,18 +925,18 @@ make_new_qty (unsigned int reg, enum machine_mode mode) OLD is not changing; NEW is. */ static void -make_regs_eqv (unsigned int new, unsigned int old) +make_regs_eqv (unsigned int new_reg, unsigned int old_reg) { unsigned int lastr, firstr; - int q = REG_QTY (old); + int q = REG_QTY (old_reg); struct qty_table_elem *ent; ent = &qty_table[q]; /* Nothing should become eqv until it has a "non-invalid" qty number. */ - gcc_assert (REGNO_QTY_VALID_P (old)); + gcc_assert (REGNO_QTY_VALID_P (old_reg)); - REG_QTY (new) = q; + REG_QTY (new_reg) = q; firstr = ent->first_reg; lastr = ent->last_reg; @@ -934,19 +949,19 @@ make_regs_eqv (unsigned int new, unsigned int old) that not only can they not be allocated by the compiler, but they cannot be used in substitutions or canonicalizations either. */ - && (new >= FIRST_PSEUDO_REGISTER || REGNO_REG_CLASS (new) != NO_REGS) - && ((new < FIRST_PSEUDO_REGISTER && FIXED_REGNO_P (new)) - || (new >= FIRST_PSEUDO_REGISTER + && (new_reg >= FIRST_PSEUDO_REGISTER || REGNO_REG_CLASS (new_reg) != NO_REGS) + && ((new_reg < FIRST_PSEUDO_REGISTER && FIXED_REGNO_P (new_reg)) + || (new_reg >= FIRST_PSEUDO_REGISTER && (firstr < FIRST_PSEUDO_REGISTER - || (bitmap_bit_p (cse_ebb_live_out, new) + || (bitmap_bit_p (cse_ebb_live_out, new_reg) && !bitmap_bit_p (cse_ebb_live_out, firstr)) - || (bitmap_bit_p (cse_ebb_live_in, new) + || (bitmap_bit_p (cse_ebb_live_in, new_reg) && !bitmap_bit_p (cse_ebb_live_in, firstr)))))) { - reg_eqv_table[firstr].prev = new; - reg_eqv_table[new].next = firstr; - reg_eqv_table[new].prev = -1; - ent->first_reg = new; + reg_eqv_table[firstr].prev = new_reg; + reg_eqv_table[new_reg].next = firstr; + reg_eqv_table[new_reg].prev = -1; + ent->first_reg = new_reg; } else { @@ -956,15 +971,15 @@ make_regs_eqv (unsigned int new, unsigned int old) equivalent for anything. */ while (lastr < FIRST_PSEUDO_REGISTER && reg_eqv_table[lastr].prev >= 0 && (REGNO_REG_CLASS (lastr) == NO_REGS || ! FIXED_REGNO_P (lastr)) - && new >= FIRST_PSEUDO_REGISTER) + && new_reg >= FIRST_PSEUDO_REGISTER) lastr = reg_eqv_table[lastr].prev; - reg_eqv_table[new].next = reg_eqv_table[lastr].next; + reg_eqv_table[new_reg].next = reg_eqv_table[lastr].next; if (reg_eqv_table[lastr].next >= 0) - reg_eqv_table[reg_eqv_table[lastr].next].prev = new; + reg_eqv_table[reg_eqv_table[lastr].next].prev = new_reg; else - qty_table[q].last_reg = new; - reg_eqv_table[lastr].next = new; - reg_eqv_table[new].prev = lastr; + qty_table[q].last_reg = new_reg; + reg_eqv_table[lastr].next = new_reg; + reg_eqv_table[new_reg].prev = lastr; } } @@ -1204,6 +1219,174 @@ insert_regs (rtx x, struct table_elt *classp, int modified) return mention_regs (x); } + +/* Compute upper and lower anchors for CST. Also compute the offset of CST + from these anchors/bases such that *_BASE + *_OFFS = CST. Return false iff + CST is equal to an anchor. */ + +static bool +compute_const_anchors (rtx cst, + HOST_WIDE_INT *lower_base, HOST_WIDE_INT *lower_offs, + HOST_WIDE_INT *upper_base, HOST_WIDE_INT *upper_offs) +{ + HOST_WIDE_INT n = INTVAL (cst); + + *lower_base = n & ~(targetm.const_anchor - 1); + if (*lower_base == n) + return false; + + *upper_base = + (n + (targetm.const_anchor - 1)) & ~(targetm.const_anchor - 1); + *upper_offs = n - *upper_base; + *lower_offs = n - *lower_base; + return true; +} + +/* Insert the equivalence between ANCHOR and (REG + OFF) in mode MODE. */ + +static void +insert_const_anchor (HOST_WIDE_INT anchor, rtx reg, HOST_WIDE_INT offs, + enum machine_mode mode) +{ + struct table_elt *elt; + unsigned hash; + rtx anchor_exp; + rtx exp; + + anchor_exp = GEN_INT (anchor); + hash = HASH (anchor_exp, mode); + elt = lookup (anchor_exp, hash, mode); + if (!elt) + elt = insert (anchor_exp, NULL, hash, mode); + + exp = plus_constant (reg, offs); + /* REG has just been inserted and the hash codes recomputed. */ + mention_regs (exp); + hash = HASH (exp, mode); + + /* Use the cost of the register rather than the whole expression. When + looking up constant anchors we will further offset the corresponding + expression therefore it does not make sense to prefer REGs over + reg-immediate additions. Prefer instead the oldest expression. Also + don't prefer pseudos over hard regs so that we derive constants in + argument registers from other argument registers rather than from the + original pseudo that was used to synthesize the constant. */ + insert_with_costs (exp, elt, hash, mode, COST (reg), 1); +} + +/* The constant CST is equivalent to the register REG. Create + equivalences between the two anchors of CST and the corresponding + register-offset expressions using REG. */ + +static void +insert_const_anchors (rtx reg, rtx cst, enum machine_mode mode) +{ + HOST_WIDE_INT lower_base, lower_offs, upper_base, upper_offs; + + if (!compute_const_anchors (cst, &lower_base, &lower_offs, + &upper_base, &upper_offs)) + return; + + /* Ignore anchors of value 0. Constants accessible from zero are + simple. */ + if (lower_base != 0) + insert_const_anchor (lower_base, reg, -lower_offs, mode); + + if (upper_base != 0) + insert_const_anchor (upper_base, reg, -upper_offs, mode); +} + +/* We need to express ANCHOR_ELT->exp + OFFS. Walk the equivalence list of + ANCHOR_ELT and see if offsetting any of the entries by OFFS would create a + valid expression. Return the cheapest and oldest of such expressions. In + *OLD, return how old the resulting expression is compared to the other + equivalent expressions. */ + +static rtx +find_reg_offset_for_const (struct table_elt *anchor_elt, HOST_WIDE_INT offs, + unsigned *old) +{ + struct table_elt *elt; + unsigned idx; + struct table_elt *match_elt; + rtx match; + + /* Find the cheapest and *oldest* expression to maximize the chance of + reusing the same pseudo. */ + + match_elt = NULL; + match = NULL_RTX; + for (elt = anchor_elt->first_same_value, idx = 0; + elt; + elt = elt->next_same_value, idx++) + { + if (match_elt && CHEAPER (match_elt, elt)) + return match; + + if (REG_P (elt->exp) + || (GET_CODE (elt->exp) == PLUS + && REG_P (XEXP (elt->exp, 0)) + && GET_CODE (XEXP (elt->exp, 1)) == CONST_INT)) + { + rtx x; + + /* Ignore expressions that are no longer valid. */ + if (!REG_P (elt->exp) && !exp_equiv_p (elt->exp, elt->exp, 1, false)) + continue; + + x = plus_constant (elt->exp, offs); + if (REG_P (x) + || (GET_CODE (x) == PLUS + && IN_RANGE (INTVAL (XEXP (x, 1)), + -targetm.const_anchor, + targetm.const_anchor - 1))) + { + match = x; + match_elt = elt; + *old = idx; + } + } + } + + return match; +} + +/* Try to express the constant SRC_CONST using a register+offset expression + derived from a constant anchor. Return it if successful or NULL_RTX, + otherwise. */ + +static rtx +try_const_anchors (rtx src_const, enum machine_mode mode) +{ + struct table_elt *lower_elt, *upper_elt; + HOST_WIDE_INT lower_base, lower_offs, upper_base, upper_offs; + rtx lower_anchor_rtx, upper_anchor_rtx; + rtx lower_exp = NULL_RTX, upper_exp = NULL_RTX; + unsigned lower_old, upper_old; + + if (!compute_const_anchors (src_const, &lower_base, &lower_offs, + &upper_base, &upper_offs)) + return NULL_RTX; + + lower_anchor_rtx = GEN_INT (lower_base); + upper_anchor_rtx = GEN_INT (upper_base); + lower_elt = lookup (lower_anchor_rtx, HASH (lower_anchor_rtx, mode), mode); + upper_elt = lookup (upper_anchor_rtx, HASH (upper_anchor_rtx, mode), mode); + + if (lower_elt) + lower_exp = find_reg_offset_for_const (lower_elt, lower_offs, &lower_old); + if (upper_elt) + upper_exp = find_reg_offset_for_const (upper_elt, upper_offs, &upper_old); + + if (!lower_exp) + return upper_exp; + if (!upper_exp) + return lower_exp; + + /* Return the older expression. */ + return (upper_old > lower_old ? upper_exp : lower_exp); +} + /* Look in or update the hash table. */ /* Remove table element ELT from use in the table. @@ -1285,6 +1468,19 @@ remove_from_table (struct table_elt *elt, unsigned int hash) free_element_chain = elt; } +/* Same as above, but X is a pseudo-register. */ + +static void +remove_pseudo_from_table (rtx x, unsigned int hash) +{ + struct table_elt *elt; + + /* Because a pseudo-register can be referenced in more than one + mode, we might have to remove more than one table entry. */ + while ((elt = lookup_for_remove (x, hash, VOIDmode))) + remove_from_table (elt, hash); +} + /* Look up X in the hash table and return its table element, or 0 if X is not in the table. @@ -1346,17 +1542,6 @@ lookup_as_function (rtx x, enum rtx_code code) struct table_elt *p = lookup (x, SAFE_HASH (x, VOIDmode), GET_MODE (x)); - /* If we are looking for a CONST_INT, the mode doesn't really matter, as - long as we are narrowing. So if we looked in vain for a mode narrower - than word_mode before, look for word_mode now. */ - if (p == 0 && code == CONST_INT - && GET_MODE_SIZE (GET_MODE (x)) < GET_MODE_SIZE (word_mode)) - { - x = copy_rtx (x); - PUT_MODE (x, word_mode); - p = lookup (x, SAFE_HASH (x, VOIDmode), word_mode); - } - if (p == 0) return 0; @@ -1369,11 +1554,11 @@ lookup_as_function (rtx x, enum rtx_code code) return 0; } -/* Insert X in the hash table, assuming HASH is its hash code - and CLASSP is an element of the class it should go in - (or 0 if a new class should be made). - It is inserted at the proper position to keep the class in - the order cheapest first. +/* Insert X in the hash table, assuming HASH is its hash code and + CLASSP is an element of the class it should go in (or 0 if a new + class should be made). COST is the code of X and reg_cost is the + cost of registers in X. It is inserted at the proper position to + keep the class in the order cheapest first. MODE is the machine-mode of X, or if X is an integer constant with VOIDmode then MODE is the mode with which X will be used. @@ -1393,11 +1578,9 @@ lookup_as_function (rtx x, enum rtx_code code) If necessary, update table showing constant values of quantities. */ -#define CHEAPER(X, Y) \ - (preferable ((X)->cost, (X)->regcost, (Y)->cost, (Y)->regcost) < 0) - static struct table_elt * -insert (rtx x, struct table_elt *classp, unsigned int hash, enum machine_mode mode) +insert_with_costs (rtx x, struct table_elt *classp, unsigned int hash, + enum machine_mode mode, int cost, int reg_cost) { struct table_elt *elt; @@ -1419,8 +1602,8 @@ insert (rtx x, struct table_elt *classp, unsigned int hash, enum machine_mode mo elt->exp = x; elt->canon_exp = NULL_RTX; - elt->cost = COST (x); - elt->regcost = approx_reg_cost (x); + elt->cost = cost; + elt->regcost = reg_cost; elt->next_same_value = 0; elt->prev_same_value = 0; elt->next_same_hash = table[hash]; @@ -1555,6 +1738,17 @@ insert (rtx x, struct table_elt *classp, unsigned int hash, enum machine_mode mo return elt; } + +/* Wrap insert_with_costs by passing the default costs. */ + +static struct table_elt * +insert (rtx x, struct table_elt *classp, unsigned int hash, + enum machine_mode mode) +{ + return + insert_with_costs (x, classp, hash, mode, COST (x), approx_reg_cost (x)); +} + /* Given two equivalence classes, CLASS1 and CLASS2, put all the entries from CLASS2 into CLASS1. This is done when we have reached an insn which makes @@ -1568,7 +1762,7 @@ insert (rtx x, struct table_elt *classp, unsigned int hash, enum machine_mode mo static void merge_equiv_classes (struct table_elt *class1, struct table_elt *class2) { - struct table_elt *elt, *next, *new; + struct table_elt *elt, *next, *new_elt; /* Ensure we start with the head of the classes. */ class1 = class1->first_same_value; @@ -1602,15 +1796,18 @@ merge_equiv_classes (struct table_elt *class1, struct table_elt *class2) delete_reg_equiv (REGNO (exp)); } - remove_from_table (elt, hash); + if (REG_P (exp) && REGNO (exp) >= FIRST_PSEUDO_REGISTER) + remove_pseudo_from_table (exp, hash); + else + remove_from_table (elt, hash); if (insert_regs (exp, class1, 0) || need_rehash) { rehash_using_reg (exp); hash = HASH (exp, mode); } - new = insert (exp, class1, hash, mode); - new->in_memory = hash_arg_in_memory; + new_elt = insert (exp, class1, hash, mode); + new_elt->in_memory = hash_arg_in_memory; } } } @@ -1648,7 +1845,7 @@ check_dependence (rtx *x, void *data) { struct check_dependence_data *d = (struct check_dependence_data *) data; if (*x && MEM_P (*x)) - return canon_true_dependence (d->exp, d->mode, d->addr, *x, + return canon_true_dependence (d->exp, d->mode, d->addr, *x, NULL_RTX, cse_rtx_varies_p); else return 0; @@ -1698,14 +1895,7 @@ invalidate (rtx x, enum machine_mode full_mode) SUBREG_TICKED (regno) = -1; if (regno >= FIRST_PSEUDO_REGISTER) - { - /* Because a register can be referenced in more than one mode, - we might have to remove more than one table entry. */ - struct table_elt *elt; - - while ((elt = lookup_for_remove (x, hash, GET_MODE (x)))) - remove_from_table (elt, hash); - } + remove_pseudo_from_table (x, hash); else { HOST_WIDE_INT in_table @@ -2031,6 +2221,7 @@ use_related_value (rtx x, struct table_elt *elt) return plus_constant (q->exp, offset); } + /* Hash a string. Just add its bytes up. */ static inline unsigned hash_rtx_string (const char *ps) @@ -2045,27 +2236,20 @@ hash_rtx_string (const char *ps) return hash; } -/* Hash an rtx. We are careful to make sure the value is never negative. - Equivalent registers hash identically. - MODE is used in hashing for CONST_INTs only; - otherwise the mode of X is used. - - Store 1 in DO_NOT_RECORD_P if any subexpression is volatile. - - If HASH_ARG_IN_MEMORY_P is not NULL, store 1 in it if X contains - a MEM rtx which does not have the RTX_UNCHANGING_P bit set. - - Note that cse_insn knows that the hash code of a MEM expression - is just (int) MEM plus the hash code of the address. */ +/* Same as hash_rtx, but call CB on each rtx if it is not NULL. + When the callback returns true, we continue with the new rtx. */ unsigned -hash_rtx (const_rtx x, enum machine_mode mode, int *do_not_record_p, - int *hash_arg_in_memory_p, bool have_reg_qty) +hash_rtx_cb (const_rtx x, enum machine_mode mode, + int *do_not_record_p, int *hash_arg_in_memory_p, + bool have_reg_qty, hash_rtx_callback_function cb) { int i, j; unsigned hash = 0; enum rtx_code code; const char *fmt; + enum machine_mode newmode; + rtx newx; /* Used to turn recursion into iteration. We can't rely on GCC's tail-recursion elimination since we need to keep accumulating values @@ -2074,6 +2258,15 @@ hash_rtx (const_rtx x, enum machine_mode mode, int *do_not_record_p, if (x == 0) return hash; + /* Invoke the callback first. */ + if (cb != NULL + && ((*cb) (x, mode, &newx, &newmode))) + { + hash += hash_rtx_cb (newx, newmode, do_not_record_p, + hash_arg_in_memory_p, have_reg_qty, cb); + return hash; + } + code = GET_CODE (x); switch (code) { @@ -2081,7 +2274,7 @@ hash_rtx (const_rtx x, enum machine_mode mode, int *do_not_record_p, { unsigned int regno = REGNO (x); - if (!reload_completed) + if (do_not_record_p && !reload_completed) { /* On some machines, we can't record any non-fixed hard register, because extending its life will cause reload problems. We @@ -2093,7 +2286,7 @@ hash_rtx (const_rtx x, enum machine_mode mode, int *do_not_record_p, 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. */ + class, as defined by TARGET_CLASS_LIKELY_SPILLED_P. */ bool record; if (regno >= FIRST_PSEUDO_REGISTER) @@ -2110,9 +2303,9 @@ hash_rtx (const_rtx x, enum machine_mode mode, int *do_not_record_p, record = true; else if (GET_MODE_CLASS (GET_MODE (x)) == MODE_CC) record = true; - else if (SMALL_REGISTER_CLASSES) + else if (targetm.small_register_classes_for_mode_p (GET_MODE (x))) record = false; - else if (CLASS_LIKELY_SPILLED_P (REGNO_REG_CLASS (regno))) + else if (targetm.class_likely_spilled_p (REGNO_REG_CLASS (regno))) record = false; else record = true; @@ -2175,8 +2368,9 @@ hash_rtx (const_rtx x, enum machine_mode mode, int *do_not_record_p, for (i = 0; i < units; ++i) { elt = CONST_VECTOR_ELT (x, i); - hash += hash_rtx (elt, GET_MODE (elt), do_not_record_p, - hash_arg_in_memory_p, have_reg_qty); + hash += hash_rtx_cb (elt, GET_MODE (elt), + do_not_record_p, hash_arg_in_memory_p, + have_reg_qty, cb); } return hash; @@ -2210,7 +2404,7 @@ hash_rtx (const_rtx x, enum machine_mode mode, int *do_not_record_p, case MEM: /* We don't record if marked volatile or if BLKmode since we don't know the size of the move. */ - if (MEM_VOLATILE_P (x) || GET_MODE (x) == BLKmode) + if (do_not_record_p && (MEM_VOLATILE_P (x) || GET_MODE (x) == BLKmode)) { *do_not_record_p = 1; return 0; @@ -2257,11 +2451,16 @@ hash_rtx (const_rtx x, enum machine_mode mode, int *do_not_record_p, case CC0: case CALL: case UNSPEC_VOLATILE: - *do_not_record_p = 1; - return 0; + if (do_not_record_p) { + *do_not_record_p = 1; + return 0; + } + else + return hash; + break; case ASM_OPERANDS: - if (MEM_VOLATILE_P (x)) + if (do_not_record_p && MEM_VOLATILE_P (x)) { *do_not_record_p = 1; return 0; @@ -2278,12 +2477,12 @@ hash_rtx (const_rtx x, enum machine_mode mode, int *do_not_record_p, { for (i = 1; i < ASM_OPERANDS_INPUT_LENGTH (x); i++) { - hash += (hash_rtx (ASM_OPERANDS_INPUT (x, i), - GET_MODE (ASM_OPERANDS_INPUT (x, i)), - do_not_record_p, hash_arg_in_memory_p, - have_reg_qty) + hash += (hash_rtx_cb (ASM_OPERANDS_INPUT (x, i), + GET_MODE (ASM_OPERANDS_INPUT (x, i)), + do_not_record_p, hash_arg_in_memory_p, + have_reg_qty, cb) + hash_rtx_string - (ASM_OPERANDS_INPUT_CONSTRAINT (x, i))); + (ASM_OPERANDS_INPUT_CONSTRAINT (x, i))); } hash += hash_rtx_string (ASM_OPERANDS_INPUT_CONSTRAINT (x, 0)); @@ -2317,14 +2516,16 @@ hash_rtx (const_rtx x, enum machine_mode mode, int *do_not_record_p, goto repeat; } - hash += hash_rtx (XEXP (x, i), 0, do_not_record_p, - hash_arg_in_memory_p, have_reg_qty); + hash += hash_rtx_cb (XEXP (x, i), VOIDmode, do_not_record_p, + hash_arg_in_memory_p, + have_reg_qty, cb); break; case 'E': for (j = 0; j < XVECLEN (x, i); j++) - hash += hash_rtx (XVECEXP (x, i, j), 0, do_not_record_p, - hash_arg_in_memory_p, have_reg_qty); + hash += hash_rtx_cb (XVECEXP (x, i, j), VOIDmode, do_not_record_p, + hash_arg_in_memory_p, + have_reg_qty, cb); break; case 's': @@ -2347,6 +2548,27 @@ hash_rtx (const_rtx x, enum machine_mode mode, int *do_not_record_p, return hash; } +/* Hash an rtx. We are careful to make sure the value is never negative. + Equivalent registers hash identically. + MODE is used in hashing for CONST_INTs only; + otherwise the mode of X is used. + + Store 1 in DO_NOT_RECORD_P if any subexpression is volatile. + + If HASH_ARG_IN_MEMORY_P is not NULL, store 1 in it if X contains + a MEM rtx which does not have the RTX_UNCHANGING_P bit set. + + Note that cse_insn knows that the hash code of a MEM expression + is just (int) MEM plus the hash code of the address. */ + +unsigned +hash_rtx (const_rtx x, enum machine_mode mode, int *do_not_record_p, + int *hash_arg_in_memory_p, bool have_reg_qty) +{ + return hash_rtx_cb (x, mode, do_not_record_p, + hash_arg_in_memory_p, have_reg_qty, NULL); +} + /* Hash an rtx X for cse via hash_rtx. Stores 1 in do_not_record if any subexpression is volatile. Stores 1 in hash_arg_in_memory if X contains a mem rtx which @@ -2400,6 +2622,10 @@ exp_equiv_p (const_rtx x, const_rtx y, int validate, bool for_gcse) if (GET_MODE (x) != GET_MODE (y)) return 0; + /* MEMs refering to different address space are not equivalent. */ + if (code == MEM && MEM_ADDR_SPACE (x) != MEM_ADDR_SPACE (y)) + return 0; + switch (code) { case PC: @@ -2444,26 +2670,16 @@ exp_equiv_p (const_rtx x, const_rtx y, int validate, bool for_gcse) case MEM: if (for_gcse) { + /* Can't merge two expressions in different alias sets, since we + can decide that the expression is transparent in a block when + it isn't, due to it being set with the different alias set. */ + if (MEM_ALIAS_SET (x) != MEM_ALIAS_SET (y)) + return 0; + /* A volatile mem should not be considered equivalent to any other. */ if (MEM_VOLATILE_P (x) || MEM_VOLATILE_P (y)) return 0; - - /* Can't merge two expressions in different alias sets, since we - can decide that the expression is transparent in a block when - it isn't, due to it being set with the different alias set. - - Also, can't merge two expressions with different MEM_ATTRS. - They could e.g. be two different entities allocated into the - same space on the stack (see e.g. PR25130). In that case, the - MEM addresses can be the same, even though the two MEMs are - absolutely not equivalent. - - But because really all MEM attributes should be the same for - equivalent MEMs, we just use the invariant that MEMs that have - the same attributes share the same mem_attrs data structure. */ - if (MEM_ATTRS (x) != MEM_ATTRS (y)) - return 0; } break; @@ -2590,7 +2806,7 @@ cse_rtx_varies_p (const_rtx x, bool from_alias) } if (GET_CODE (x) == PLUS - && GET_CODE (XEXP (x, 1)) == CONST_INT + && CONST_INT_P (XEXP (x, 1)) && REG_P (XEXP (x, 0)) && REGNO_QTY_VALID_P (REGNO (XEXP (x, 0)))) { @@ -2636,12 +2852,12 @@ validate_canon_reg (rtx *xloc, rtx insn) { if (*xloc) { - rtx new = canon_reg (*xloc, insn); + rtx new_rtx = canon_reg (*xloc, insn); /* If replacing pseudo with hard reg or vice versa, ensure the insn remains valid. Likewise if the insn has MATCH_DUPs. */ - gcc_assert (insn && new); - validate_change (insn, xloc, new, 1); + gcc_assert (insn && new_rtx); + validate_change (insn, xloc, new_rtx, 1); } } @@ -2936,7 +3152,7 @@ fold_rtx (rtx x, rtx insn) enum machine_mode mode; const char *fmt; int i; - rtx new = 0; + rtx new_rtx = 0; int changed = 0; /* Operands of X. */ @@ -2962,8 +3178,8 @@ fold_rtx (rtx x, rtx insn) { case MEM: case SUBREG: - if ((new = equiv_constant (x)) != NULL_RTX) - return new; + if ((new_rtx = equiv_constant (x)) != NULL_RTX) + return new_rtx; return x; case CONST: @@ -3102,7 +3318,7 @@ fold_rtx (rtx x, rtx insn) if (insn == NULL_RTX && !changed) x = copy_rtx (x); changed = 1; - validate_change (insn, &XEXP (x, i), folded_arg, 1); + validate_unshare_change (insn, &XEXP (x, i), folded_arg, 1); } if (changed) @@ -3125,33 +3341,15 @@ fold_rtx (rtx x, rtx insn) { case RTX_UNARY: { - int is_const = 0; - /* We can't simplify extension ops unless we know the original mode. */ if ((code == ZERO_EXTEND || code == SIGN_EXTEND) && mode_arg0 == VOIDmode) break; - /* If we had a CONST, strip it off and put it back later if we - fold. */ - if (const_arg0 != 0 && GET_CODE (const_arg0) == CONST) - is_const = 1, const_arg0 = XEXP (const_arg0, 0); - - new = simplify_unary_operation (code, mode, + new_rtx = simplify_unary_operation (code, mode, const_arg0 ? const_arg0 : folded_arg0, mode_arg0); - /* NEG of PLUS could be converted into MINUS, but that causes - expressions of the form - (CONST (MINUS (CONST_INT) (SYMBOL_REF))) - which many ports mistakenly treat as LEGITIMATE_CONSTANT_P. - FIXME: those ports should be fixed. */ - if (new != 0 && is_const - && GET_CODE (new) == PLUS - && (GET_CODE (XEXP (new, 0)) == SYMBOL_REF - || GET_CODE (XEXP (new, 0)) == LABEL_REF) - && GET_CODE (XEXP (new, 1)) == CONST_INT) - new = gen_rtx_CONST (mode, new); } break; @@ -3169,17 +3367,24 @@ fold_rtx (rtx x, rtx insn) if (const_arg0 == 0 || const_arg1 == 0) { struct table_elt *p0, *p1; - rtx true_rtx = const_true_rtx, false_rtx = const0_rtx; + rtx true_rtx, false_rtx; enum machine_mode mode_arg1; -#ifdef FLOAT_STORE_FLAG_VALUE if (SCALAR_FLOAT_MODE_P (mode)) { +#ifdef FLOAT_STORE_FLAG_VALUE true_rtx = (CONST_DOUBLE_FROM_REAL_VALUE (FLOAT_STORE_FLAG_VALUE (mode), mode)); +#else + true_rtx = NULL_RTX; +#endif false_rtx = CONST0_RTX (mode); } -#endif + else + { + true_rtx = const_true_rtx; + false_rtx = const0_rtx; + } code = find_comparison_args (code, &folded_arg0, &folded_arg1, &mode_arg0, &mode_arg1); @@ -3211,7 +3416,7 @@ fold_rtx (rtx x, rtx insn) constant through simplifications. */ p = lookup (folded_arg0, SAFE_HASH (folded_arg0, mode_arg0), mode_arg0); - + if (p != NULL) { cheapest_simplification = x; @@ -3251,24 +3456,12 @@ fold_rtx (rtx x, rtx insn) } } - /* Some addresses are known to be nonzero. We don't know - their sign, but equality comparisons are known. */ - if (const_arg1 == const0_rtx - && nonzero_address_p (folded_arg0)) - { - if (code == EQ) - return false_rtx; - else if (code == NE) - return true_rtx; - } - /* See if the two operands are the same. */ - if (folded_arg0 == folded_arg1 - || (REG_P (folded_arg0) - && REG_P (folded_arg1) - && (REG_QTY (REGNO (folded_arg0)) - == REG_QTY (REGNO (folded_arg1)))) + if ((REG_P (folded_arg0) + && REG_P (folded_arg1) + && (REG_QTY (REGNO (folded_arg0)) + == REG_QTY (REGNO (folded_arg1)))) || ((p0 = lookup (folded_arg0, SAFE_HASH (folded_arg0, mode_arg0), mode_arg0)) @@ -3276,20 +3469,7 @@ fold_rtx (rtx x, rtx insn) SAFE_HASH (folded_arg1, mode_arg0), mode_arg0)) && p0->first_same_value == p1->first_same_value)) - { - /* 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; - } + folded_arg1 = folded_arg0; /* If FOLDED_ARG0 is a register, see if the comparison we are doing now is either the same as we did before or the reverse @@ -3312,8 +3492,17 @@ fold_rtx (rtx x, rtx insn) const_arg1)) || (REG_P (folded_arg1) && (REG_QTY (REGNO (folded_arg1)) == ent->comparison_qty)))) - return (comparison_dominates_p (ent->comparison_code, code) - ? true_rtx : false_rtx); + { + if (comparison_dominates_p (ent->comparison_code, code)) + { + if (true_rtx) + return true_rtx; + else + break; + } + else + return false_rtx; + } } } } @@ -3322,56 +3511,22 @@ fold_rtx (rtx x, rtx insn) /* If we are comparing against zero, see if the first operand is equivalent to an IOR with a constant. If so, we may be able to determine the result of this comparison. */ - - if (const_arg1 == const0_rtx) + if (const_arg1 == const0_rtx && !const_arg0) { rtx y = lookup_as_function (folded_arg0, IOR); rtx inner_const; if (y != 0 && (inner_const = equiv_constant (XEXP (y, 1))) != 0 - && GET_CODE (inner_const) == CONST_INT + && CONST_INT_P (inner_const) && INTVAL (inner_const) != 0) - { - int sign_bitnum = GET_MODE_BITSIZE (mode_arg0) - 1; - int has_sign = (HOST_BITS_PER_WIDE_INT >= sign_bitnum - && (INTVAL (inner_const) - & ((HOST_WIDE_INT) 1 << sign_bitnum))); - rtx true_rtx = const_true_rtx, false_rtx = const0_rtx; - -#ifdef FLOAT_STORE_FLAG_VALUE - if (SCALAR_FLOAT_MODE_P (mode)) - { - true_rtx = (CONST_DOUBLE_FROM_REAL_VALUE - (FLOAT_STORE_FLAG_VALUE (mode), mode)); - false_rtx = CONST0_RTX (mode); - } -#endif - - switch (code) - { - case EQ: - return false_rtx; - case NE: - return true_rtx; - case LT: case LE: - if (has_sign) - return true_rtx; - break; - case GT: case GE: - if (has_sign) - return false_rtx; - break; - default: - break; - } - } + folded_arg0 = gen_rtx_IOR (mode_arg0, XEXP (y, 0), inner_const); } { rtx op0 = const_arg0 ? const_arg0 : folded_arg0; rtx op1 = const_arg1 ? const_arg1 : folded_arg1; - new = simplify_relational_operation (code, mode, mode_arg0, op0, op1); + new_rtx = simplify_relational_operation (code, mode, mode_arg0, op0, op1); } break; @@ -3433,7 +3588,7 @@ fold_rtx (rtx x, rtx insn) the smallest negative number this would overflow: depending on the mode, this would either just be the same value (and hence not save anything) or be incorrect. */ - if (const_arg1 != 0 && GET_CODE (const_arg1) == CONST_INT + if (const_arg1 != 0 && CONST_INT_P (const_arg1) && INTVAL (const_arg1) < 0 /* This used to test @@ -3461,10 +3616,10 @@ fold_rtx (rtx x, rtx insn) case MINUS: /* If we have (MINUS Y C), see if Y is known to be (PLUS Z C2). If so, produce (PLUS Z C2-C). */ - if (const_arg1 != 0 && GET_CODE (const_arg1) == CONST_INT) + if (const_arg1 != 0 && CONST_INT_P (const_arg1)) { rtx y = lookup_as_function (XEXP (x, 0), PLUS); - if (y && GET_CODE (XEXP (y, 1)) == CONST_INT) + if (y && CONST_INT_P (XEXP (y, 1))) return fold_rtx (plus_constant (copy_rtx (y), -INTVAL (const_arg1)), NULL_RTX); @@ -3485,11 +3640,12 @@ fold_rtx (rtx x, rtx insn) if the intermediate operation's result has only one reference. */ if (REG_P (folded_arg0) - && const_arg1 && GET_CODE (const_arg1) == CONST_INT) + && const_arg1 && CONST_INT_P (const_arg1)) { int is_shift = (code == ASHIFT || code == ASHIFTRT || code == LSHIFTRT); rtx y, inner_const, new_const; + rtx canon_const_arg1 = const_arg1; enum rtx_code associate_code; if (is_shift @@ -3497,8 +3653,9 @@ fold_rtx (rtx x, rtx insn) || INTVAL (const_arg1) < 0)) { if (SHIFT_COUNT_TRUNCATED) - const_arg1 = GEN_INT (INTVAL (const_arg1) - & (GET_MODE_BITSIZE (mode) - 1)); + canon_const_arg1 = GEN_INT (INTVAL (const_arg1) + & (GET_MODE_BITSIZE (mode) + - 1)); else break; } @@ -3516,7 +3673,7 @@ fold_rtx (rtx x, rtx insn) break; inner_const = equiv_constant (fold_rtx (XEXP (y, 1), 0)); - if (!inner_const || GET_CODE (inner_const) != CONST_INT) + if (!inner_const || !CONST_INT_P (inner_const)) break; /* Don't associate these operations if they are a PLUS with the @@ -3535,6 +3692,11 @@ fold_rtx (rtx x, rtx insn) && exact_log2 (- INTVAL (const_arg1)) >= 0))) break; + /* ??? Vector mode shifts by scalar + shift operand are not supported yet. */ + if (is_shift && VECTOR_MODE_P (mode)) + break; + if (is_shift && (INTVAL (inner_const) >= GET_MODE_BITSIZE (mode) || INTVAL (inner_const) < 0)) @@ -3552,7 +3714,8 @@ fold_rtx (rtx x, rtx insn) associate_code = (is_shift || code == MINUS ? PLUS : code); new_const = simplify_binary_operation (associate_code, mode, - const_arg1, inner_const); + canon_const_arg1, + inner_const); if (new_const == 0) break; @@ -3564,7 +3727,7 @@ fold_rtx (rtx x, rtx insn) of shifts. */ if (is_shift - && GET_CODE (new_const) == CONST_INT + && CONST_INT_P (new_const) && INTVAL (new_const) >= GET_MODE_BITSIZE (mode)) { /* As an exception, we can turn an ASHIFTRT of this @@ -3602,7 +3765,7 @@ fold_rtx (rtx x, rtx insn) break; } - new = simplify_binary_operation (code, mode, + new_rtx = simplify_binary_operation (code, mode, const_arg0 ? const_arg0 : folded_arg0, const_arg1 ? const_arg1 : folded_arg1); break; @@ -3617,7 +3780,7 @@ fold_rtx (rtx x, rtx insn) case RTX_TERNARY: case RTX_BITFIELD_OPS: - new = simplify_ternary_operation (code, mode, mode_arg0, + new_rtx = simplify_ternary_operation (code, mode, mode_arg0, const_arg0 ? const_arg0 : folded_arg0, const_arg1 ? const_arg1 : folded_arg1, const_arg2 ? const_arg2 : XEXP (x, 2)); @@ -3627,7 +3790,7 @@ fold_rtx (rtx x, rtx insn) break; } - return new ? new : x; + return new_rtx ? new_rtx : x; } /* Return a constant value currently equivalent to X. @@ -3651,18 +3814,35 @@ equiv_constant (rtx x) if (GET_CODE (x) == SUBREG) { - rtx new; + enum machine_mode mode = GET_MODE (x); + enum machine_mode imode = GET_MODE (SUBREG_REG (x)); + rtx new_rtx; /* See if we previously assigned a constant value to this SUBREG. */ - if ((new = lookup_as_function (x, CONST_INT)) != 0 - || (new = lookup_as_function (x, CONST_DOUBLE)) != 0 - || (new = lookup_as_function (x, CONST_FIXED)) != 0) - return new; + if ((new_rtx = lookup_as_function (x, CONST_INT)) != 0 + || (new_rtx = lookup_as_function (x, CONST_DOUBLE)) != 0 + || (new_rtx = lookup_as_function (x, CONST_FIXED)) != 0) + return new_rtx; + + /* If we didn't and if doing so makes sense, see if we previously + assigned a constant value to the enclosing word mode SUBREG. */ + if (GET_MODE_SIZE (mode) < GET_MODE_SIZE (word_mode) + && GET_MODE_SIZE (word_mode) < GET_MODE_SIZE (imode)) + { + int byte = SUBREG_BYTE (x) - subreg_lowpart_offset (mode, word_mode); + if (byte >= 0 && (byte % UNITS_PER_WORD) == 0) + { + rtx y = gen_rtx_SUBREG (word_mode, SUBREG_REG (x), byte); + new_rtx = lookup_as_function (y, CONST_INT); + if (new_rtx) + return gen_lowpart (mode, new_rtx); + } + } + /* Otherwise see if we already have a constant for the inner REG. */ if (REG_P (SUBREG_REG (x)) - && (new = equiv_constant (SUBREG_REG (x))) != 0) - return simplify_subreg (GET_MODE (x), SUBREG_REG (x), - GET_MODE (SUBREG_REG (x)), SUBREG_BYTE (x)); + && (new_rtx = equiv_constant (SUBREG_REG (x))) != 0) + return simplify_subreg (mode, new_rtx, imode, SUBREG_BYTE (x)); return 0; } @@ -3970,11 +4150,7 @@ record_jump_cond (enum rtx_code code, enum machine_mode mode, rtx op0, First simplify sources and addresses of all assignments in the instruction, using previously-computed equivalents values. Then install the new sources and destinations in the table - of available values. - - If LIBCALL_INSN is nonzero, don't record any equivalence made in - the insn. It means that INSN is inside libcall block. In this - case LIBCALL_INSN is the corresponding insn with REG_LIBCALL. */ + of available values. */ /* Data on one SET contained in the instruction. */ @@ -4003,8 +4179,6 @@ struct set 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. */ - rtx orig_src; /* Hash value of constant equivalent for SET_SRC. */ unsigned src_const_hash; /* Table entry for constant equivalent for SET_SRC, if any. */ @@ -4014,7 +4188,7 @@ struct set }; static void -cse_insn (rtx insn, rtx libcall_insn) +cse_insn (rtx insn) { rtx x = PATTERN (insn); int i; @@ -4053,7 +4227,7 @@ cse_insn (rtx insn, rtx libcall_insn) if (GET_CODE (x) == SET) { - sets = alloca (sizeof (struct set)); + sets = XALLOCA (struct set); sets[0].rtl = x; /* Ignore SETs that are unconditional jumps. @@ -4088,7 +4262,7 @@ cse_insn (rtx insn, rtx libcall_insn) { int lim = XVECLEN (x, 0); - sets = alloca (lim * sizeof (struct set)); + sets = XALLOCAVEC (struct set, lim); /* Find all regs explicitly clobbered in this insn, and ensure they are not replaced with any other regs @@ -4164,12 +4338,23 @@ cse_insn (rtx insn, rtx libcall_insn) if (MEM_P (XEXP (x, 0))) canon_reg (XEXP (x, 0), insn); } - /* Canonicalize a USE of a pseudo register or memory location. */ else if (GET_CODE (x) == USE && ! (REG_P (XEXP (x, 0)) && REGNO (XEXP (x, 0)) < FIRST_PSEUDO_REGISTER)) - canon_reg (XEXP (x, 0), insn); + canon_reg (x, insn); + else if (GET_CODE (x) == ASM_OPERANDS) + { + for (i = ASM_OPERANDS_INPUT_LENGTH (x) - 1; i >= 0; i--) + { + rtx input = ASM_OPERANDS_INPUT (x, i); + if (!(REG_P (input) && REGNO (input) < FIRST_PSEUDO_REGISTER)) + { + input = canon_reg (input, insn); + validate_change (insn, &ASM_OPERANDS_INPUT (x, i), input, 1); + } + } + } else if (GET_CODE (x) == CALL) { /* The result of apply_change_group can be ignored; see canon_reg. */ @@ -4177,6 +4362,8 @@ cse_insn (rtx insn, rtx libcall_insn) apply_change_group (); fold_rtx (x, insn); } + else if (DEBUG_INSN_P (insn)) + canon_reg (PATTERN (insn), insn); /* Store the equivalent value in SRC_EQV, if different, or if the DEST is a STRICT_LOW_PART. The latter condition is necessary because SRC_EQV @@ -4209,10 +4396,9 @@ cse_insn (rtx insn, rtx libcall_insn) { rtx dest = SET_DEST (sets[i].rtl); rtx src = SET_SRC (sets[i].rtl); - rtx new = canon_reg (src, insn); + rtx new_rtx = canon_reg (src, insn); - sets[i].orig_src = src; - validate_change (insn, &SET_SRC (sets[i].rtl), new, 1); + validate_change (insn, &SET_SRC (sets[i].rtl), new_rtx, 1); if (GET_CODE (dest) == ZERO_EXTRACT) { @@ -4250,6 +4436,7 @@ cse_insn (rtx insn, rtx libcall_insn) for (i = 0; i < n_sets; i++) { + bool repeat = false; rtx src, dest; rtx src_folded; struct table_elt *elt = 0, *p; @@ -4257,6 +4444,7 @@ cse_insn (rtx insn, rtx libcall_insn) rtx src_eqv_here; rtx src_const = 0; rtx src_related = 0; + bool src_related_is_const_anchor = false; struct table_elt *src_const_elt = 0; int src_cost = MAX_COST; int src_eqv_cost = MAX_COST; @@ -4325,8 +4513,8 @@ cse_insn (rtx insn, rtx libcall_insn) { rtx width = XEXP (SET_DEST (sets[i].rtl), 1); - if (GET_CODE (src) == CONST_INT - && GET_CODE (width) == CONST_INT + if (CONST_INT_P (src) + && CONST_INT_P (width) && INTVAL (width) < HOST_BITS_PER_WIDE_INT && (INTVAL (src) & ((HOST_WIDE_INT) (-1) << INTVAL (width)))) src_folded @@ -4487,14 +4675,15 @@ cse_insn (rtx insn, rtx libcall_insn) /* See if we have a CONST_INT that is already in a register in a wider mode. */ - if (src_const && src_related == 0 && GET_CODE (src_const) == CONST_INT + if (src_const && src_related == 0 && CONST_INT_P (src_const) && GET_MODE_CLASS (mode) == MODE_INT && GET_MODE_BITSIZE (mode) < BITS_PER_WORD) { enum machine_mode wider_mode; for (wider_mode = GET_MODE_WIDER_MODE (mode); - GET_MODE_BITSIZE (wider_mode) <= BITS_PER_WORD + wider_mode != VOIDmode + && GET_MODE_BITSIZE (wider_mode) <= BITS_PER_WORD && src_related == 0; wider_mode = GET_MODE_WIDER_MODE (wider_mode)) { @@ -4521,7 +4710,7 @@ cse_insn (rtx insn, rtx libcall_insn) value. */ if (flag_expensive_optimizations && ! src_related - && GET_CODE (src) == AND && GET_CODE (XEXP (src, 1)) == CONST_INT + && GET_CODE (src) == AND && CONST_INT_P (XEXP (src, 1)) && GET_MODE_SIZE (mode) < UNITS_PER_WORD) { enum machine_mode tmode; @@ -4605,6 +4794,19 @@ cse_insn (rtx insn, rtx libcall_insn) } #endif /* LOAD_EXTEND_OP */ + /* Try to express the constant using a register+offset expression + derived from a constant anchor. */ + + if (targetm.const_anchor + && !src_related + && src_const + && GET_CODE (src_const) == CONST_INT) + { + src_related = try_const_anchors (src_const, mode); + src_related_is_const_anchor = src_related != NULL_RTX; + } + + if (src == src_folded) src_folded = 0; @@ -4709,6 +4911,18 @@ cse_insn (rtx insn, rtx libcall_insn) { src_related_cost = COST (src_related); src_related_regcost = approx_reg_cost (src_related); + + /* If a const-anchor is used to synthesize a constant that + normally requires multiple instructions then slightly prefer + it over the original sequence. These instructions are likely + to become redundant now. We can't compare against the cost + of src_eqv_here because, on MIPS for example, multi-insn + constants have zero cost; they are assumed to be hoisted from + loops. */ + if (src_related_is_const_anchor + && src_related_cost == src_cost + && src_eqv_here) + src_related_cost--; } } @@ -4799,6 +5013,94 @@ cse_insn (rtx insn, rtx libcall_insn) src_elt_cost = MAX_COST; } + /* Avoid creation of overlapping memory moves. */ + if (MEM_P (trial) && MEM_P (SET_DEST (sets[i].rtl))) + { + rtx src, dest; + + /* BLKmode moves are not handled by cse anyway. */ + if (GET_MODE (trial) == BLKmode) + break; + + src = canon_rtx (trial); + dest = canon_rtx (SET_DEST (sets[i].rtl)); + + if (!MEM_P (src) || !MEM_P (dest) + || !nonoverlapping_memrefs_p (src, dest, false)) + break; + } + + /* Try to optimize + (set (reg:M N) (const_int A)) + (set (reg:M2 O) (const_int B)) + (set (zero_extract:M2 (reg:M N) (const_int C) (const_int D)) + (reg:M2 O)). */ + if (GET_CODE (SET_DEST (sets[i].rtl)) == ZERO_EXTRACT + && CONST_INT_P (trial) + && CONST_INT_P (XEXP (SET_DEST (sets[i].rtl), 1)) + && CONST_INT_P (XEXP (SET_DEST (sets[i].rtl), 2)) + && REG_P (XEXP (SET_DEST (sets[i].rtl), 0)) + && (GET_MODE_BITSIZE (GET_MODE (SET_DEST (sets[i].rtl))) + >= INTVAL (XEXP (SET_DEST (sets[i].rtl), 1))) + && ((unsigned) INTVAL (XEXP (SET_DEST (sets[i].rtl), 1)) + + (unsigned) INTVAL (XEXP (SET_DEST (sets[i].rtl), 2)) + <= HOST_BITS_PER_WIDE_INT)) + { + rtx dest_reg = XEXP (SET_DEST (sets[i].rtl), 0); + rtx width = XEXP (SET_DEST (sets[i].rtl), 1); + rtx pos = XEXP (SET_DEST (sets[i].rtl), 2); + unsigned int dest_hash = HASH (dest_reg, GET_MODE (dest_reg)); + struct table_elt *dest_elt + = lookup (dest_reg, dest_hash, GET_MODE (dest_reg)); + rtx dest_cst = NULL; + + if (dest_elt) + for (p = dest_elt->first_same_value; p; p = p->next_same_value) + if (p->is_const && CONST_INT_P (p->exp)) + { + dest_cst = p->exp; + break; + } + if (dest_cst) + { + HOST_WIDE_INT val = INTVAL (dest_cst); + HOST_WIDE_INT mask; + unsigned int shift; + if (BITS_BIG_ENDIAN) + shift = GET_MODE_BITSIZE (GET_MODE (dest_reg)) + - INTVAL (pos) - INTVAL (width); + else + shift = INTVAL (pos); + if (INTVAL (width) == HOST_BITS_PER_WIDE_INT) + mask = ~(HOST_WIDE_INT) 0; + else + mask = ((HOST_WIDE_INT) 1 << INTVAL (width)) - 1; + val &= ~(mask << shift); + val |= (INTVAL (trial) & mask) << shift; + val = trunc_int_for_mode (val, GET_MODE (dest_reg)); + validate_unshare_change (insn, &SET_DEST (sets[i].rtl), + dest_reg, 1); + validate_unshare_change (insn, &SET_SRC (sets[i].rtl), + GEN_INT (val), 1); + if (apply_change_group ()) + { + rtx note = find_reg_note (insn, REG_EQUAL, NULL_RTX); + if (note) + { + remove_note (insn, note); + df_notes_rescan (insn); + } + src_eqv = NULL_RTX; + src_eqv_elt = NULL; + src_eqv_volatile = 0; + src_eqv_in_memory = 0; + src_eqv_hash = 0; + repeat = true; + break; + } + } + } + /* We don't normally have an insn matching (set (pc) (pc)), so check for this separately here. We will delete such an insn below. @@ -4819,7 +5121,7 @@ cse_insn (rtx insn, rtx libcall_insn) continue; SET_SRC (sets[i].rtl) = trial; - cse_jumps_altered = 1; + cse_jumps_altered = true; break; } @@ -4843,28 +5145,12 @@ cse_insn (rtx insn, rtx libcall_insn) else if (validate_unshare_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. */ - if (libcall_insn - && (REG_P (sets[i].orig_src) - || GET_CODE (sets[i].orig_src) == SUBREG - || MEM_P (sets[i].orig_src))) - { - rtx note = find_reg_equal_equiv_note (libcall_insn); - if (note != 0) - XEXP (note, 0) = simplify_replace_rtx (XEXP (note, 0), - sets[i].orig_src, - copy_rtx (new)); - df_notes_rescan (libcall_insn); - } + rtx new_rtx = canon_reg (SET_SRC (sets[i].rtl), insn); /* The result of apply_change_group can be ignored; see canon_reg. */ - validate_change (insn, &SET_SRC (sets[i].rtl), new, 1); + validate_change (insn, &SET_SRC (sets[i].rtl), new_rtx, 1); apply_change_group (); break; @@ -4890,6 +5176,13 @@ cse_insn (rtx insn, rtx libcall_insn) } } + /* If we changed the insn too much, handle this set from scratch. */ + if (repeat) + { + i--; + continue; + } + src = SET_SRC (sets[i].rtl); /* In general, it is good to have a SET with SET_SRC == SET_DEST. @@ -5018,8 +5311,8 @@ cse_insn (rtx insn, rtx libcall_insn) { rtx width = XEXP (SET_DEST (sets[i].rtl), 1); - if (src_const != 0 && GET_CODE (src_const) == CONST_INT - && GET_CODE (width) == CONST_INT + if (src_const != 0 && CONST_INT_P (src_const) + && CONST_INT_P (width) && INTVAL (width) < HOST_BITS_PER_WIDE_INT && ! (INTVAL (src_const) & ((HOST_WIDE_INT) (-1) << INTVAL (width)))) @@ -5045,7 +5338,7 @@ cse_insn (rtx insn, rtx libcall_insn) { /* One less use of the label this insn used to jump to. */ delete_insn_and_edges (insn); - cse_jumps_altered = 1; + cse_jumps_altered = true; /* No more processing for this set. */ sets[i].rtl = 0; } @@ -5064,10 +5357,10 @@ cse_insn (rtx insn, rtx libcall_insn) and hope for the best. */ if (n_sets == 1) { - rtx new, note; + rtx new_rtx, note; - new = emit_jump_insn_before (gen_jump (XEXP (src, 0)), insn); - JUMP_LABEL (new) = XEXP (src, 0); + new_rtx = emit_jump_insn_before (gen_jump (XEXP (src, 0)), insn); + JUMP_LABEL (new_rtx) = XEXP (src, 0); LABEL_NUSES (XEXP (src, 0))++; /* Make sure to copy over REG_NON_LOCAL_GOTO. */ @@ -5075,19 +5368,17 @@ cse_insn (rtx insn, rtx libcall_insn) if (note) { XEXP (note, 1) = NULL_RTX; - REG_NOTES (new) = note; + REG_NOTES (new_rtx) = note; } delete_insn_and_edges (insn); - insn = new; + insn = new_rtx; } else INSN_CODE (insn) = -1; - /* Do not bother deleting any unreachable code, - let jump/flow do that. */ - - cse_jumps_altered = 1; + /* Do not bother deleting any unreachable code, let jump do it. */ + cse_jumps_altered = true; sets[i].rtl = 0; } @@ -5201,27 +5492,19 @@ cse_insn (rtx insn, rtx libcall_insn) if (sets[i].src_elt == 0) { - /* Don't put a hard register source into the table if this is - the last insn of a libcall. In this case, we only need - to put src_eqv_elt in src_elt. */ - if (! find_reg_note (insn, REG_RETVAL, NULL_RTX)) - { - struct table_elt *elt; + struct table_elt *elt; - /* Note that these insert_regs calls cannot remove - any of the src_elt's, because they would have failed to - match if not still valid. */ - if (insert_regs (src, classp, 0)) - { - rehash_using_reg (src); - sets[i].src_hash = HASH (src, mode); - } - elt = insert (src, classp, sets[i].src_hash, mode); - elt->in_memory = sets[i].src_in_memory; - sets[i].src_elt = classp = elt; + /* Note that these insert_regs calls cannot remove + any of the src_elt's, because they would have failed to + match if not still valid. */ + if (insert_regs (src, classp, 0)) + { + rehash_using_reg (src); + sets[i].src_hash = HASH (src, mode); } - else - sets[i].src_elt = classp; + elt = insert (src, classp, sets[i].src_hash, mode); + elt->in_memory = sets[i].src_in_memory; + sets[i].src_elt = classp = elt; } if (sets[i].src_const && sets[i].src_const_elt == 0 && src != sets[i].src_const @@ -5280,7 +5563,7 @@ cse_insn (rtx insn, rtx libcall_insn) if (CALL_P (insn)) { - if (! CONST_OR_PURE_CALL_P (insn)) + if (!(RTL_CONST_OR_PURE_CALL_P (insn))) invalidate_memory (); invalidate_for_call (); } @@ -5418,11 +5701,6 @@ cse_insn (rtx insn, rtx libcall_insn) size of it, and can't be sure that other BLKmode values have the same or smaller size. */ || GET_MODE (dest) == BLKmode - /* Don't record values of destinations set inside a libcall block - since we might delete the libcall. Things should have been set - up so we won't want to reuse such a value, but we play it safe - here. */ - || libcall_insn /* If we didn't put a REG_EQUAL value or a source into the hash table, there is no point is recording DEST. */ || sets[i].src_elt == 0 @@ -5457,6 +5735,14 @@ cse_insn (rtx insn, rtx libcall_insn) elt = insert (dest, sets[i].src_elt, sets[i].dest_hash, GET_MODE (dest)); + /* If this is a constant, insert the constant anchors with the + equivalent register-offset expressions using register DEST. */ + if (targetm.const_anchor + && REG_P (dest) + && SCALAR_INT_MODE_P (GET_MODE (dest)) + && GET_CODE (sets[i].src_elt->exp) == CONST_INT) + insert_const_anchors (dest, sets[i].src_elt->exp, GET_MODE (dest)); + elt->in_memory = (MEM_P (sets[i].inner_dest) && !MEM_READONLY_P (sets[i].inner_dest)); @@ -5566,11 +5852,7 @@ cse_insn (rtx insn, rtx libcall_insn) then be used in the sequel and we may be changing a two-operand insn into a three-operand insn. - Also do not do this if we are operating on a copy of INSN. - - Also don't do this if INSN ends a libcall; this would cause an unrelated - register to be set in the middle of a libcall, and we then get bad code - if the libcall is deleted. */ + Also do not do this if we are operating on a copy of INSN. */ if (n_sets == 1 && sets[0].rtl && REG_P (SET_DEST (sets[0].rtl)) && NEXT_INSN (PREV_INSN (insn)) == insn @@ -5581,8 +5863,7 @@ cse_insn (rtx insn, rtx libcall_insn) int src_q = REG_QTY (REGNO (SET_SRC (sets[0].rtl))); struct qty_table_elem *src_ent = &qty_table[src_q]; - if ((src_ent->first_reg == REGNO (SET_DEST (sets[0].rtl))) - && ! find_reg_note (insn, REG_RETVAL, NULL_RTX)) + if (src_ent->first_reg == REGNO (SET_DEST (sets[0].rtl))) { /* Scan for the previous nonnote insn, but stop at a basic block boundary. */ @@ -5592,7 +5873,7 @@ cse_insn (rtx insn, rtx libcall_insn) { prev = PREV_INSN (prev); } - while (prev != bb_head && NOTE_P (prev)); + while (prev != bb_head && (NOTE_P (prev) || DEBUG_INSN_P (prev))); /* Do not swap the registers around if the previous instruction attaches a REG_EQUIV note to REG1. @@ -5742,11 +6023,11 @@ cse_process_notes_1 (rtx x, rtx object, bool *changed) case ZERO_EXTEND: case SUBREG: { - rtx new = cse_process_notes (XEXP (x, 0), object, changed); + rtx new_rtx = cse_process_notes (XEXP (x, 0), object, changed); /* We don't substitute VOIDmode constants into these rtx, since they would impede folding. */ - if (GET_MODE (new) != VOIDmode) - validate_change (object, &XEXP (x, 0), new, 0); + if (GET_MODE (new_rtx) != VOIDmode) + validate_change (object, &XEXP (x, 0), new_rtx, 0); return x; } @@ -5762,9 +6043,9 @@ cse_process_notes_1 (rtx x, rtx object, bool *changed) && (CONSTANT_P (ent->const_rtx) || REG_P (ent->const_rtx))) { - rtx new = gen_lowpart (GET_MODE (x), ent->const_rtx); - if (new) - return copy_rtx (new); + rtx new_rtx = gen_lowpart (GET_MODE (x), ent->const_rtx); + if (new_rtx) + return copy_rtx (new_rtx); } } @@ -5786,10 +6067,10 @@ cse_process_notes_1 (rtx x, rtx object, bool *changed) static rtx cse_process_notes (rtx x, rtx object, bool *changed) { - rtx new = cse_process_notes_1 (x, object, changed); - if (new != x) + rtx new_rtx = cse_process_notes_1 (x, object, changed); + if (new_rtx != x) *changed = true; - return new; + return new_rtx; } @@ -5799,7 +6080,7 @@ cse_process_notes (rtx x, rtx object, bool *changed) describe the path. It is filled with a queue of basic blocks, starting with FIRST_BB and following a trace through the CFG. - + If all paths starting at FIRST_BB have been followed, or no new path starting at FIRST_BB can be constructed, this function returns FALSE. Otherwise, DATA->path is filled and the function returns TRUE indicating @@ -5815,7 +6096,7 @@ cse_find_path (basic_block first_bb, struct cse_basic_block_data *data, basic_block bb; edge e; int path_size; - + SET_BIT (cse_visited_basic_blocks, first_bb->index); /* See if there is a previous path. */ @@ -5976,7 +6257,7 @@ cse_prescan_path (struct cse_basic_block_data *data) int path_entry; /* Scan to end of each basic block in the path. */ - for (path_entry = 0; path_entry < path_size; path_entry++) + for (path_entry = 0; path_entry < path_size; path_entry++) { basic_block bb; rtx insn; @@ -6019,10 +6300,24 @@ cse_extended_basic_block (struct cse_basic_block_data *ebb_data) { basic_block bb; rtx insn; - rtx libcall_insn = NULL_RTX; - int no_conflict = 0; bb = ebb_data->path[path_entry].bb; + + /* Invalidate recorded information for eh regs if there is an EH + edge pointing to that bb. */ + if (bb_has_eh_pred (bb)) + { + df_ref *def_rec; + + for (def_rec = df_get_artificial_defs (bb->index); *def_rec; def_rec++) + { + df_ref def = *def_rec; + if (DF_REF_FLAGS (def) & DF_REF_AT_TOP) + invalidate (DF_REF_REG (def), GET_MODE (DF_REF_REG (def))); + } + } + + optimize_this_for_speed_p = optimize_bb_for_speed_p (bb); FOR_BB_INSNS (bb, insn) { /* If we have processed 1,000 insns, flush the hash table to @@ -6034,7 +6329,7 @@ cse_extended_basic_block (struct cse_basic_block_data *ebb_data) FIXME: This is a real kludge and needs to be done some other way. */ - if (INSN_P (insn) + if (NONDEBUG_INSN_P (insn) && num_insns++ > PARAM_VALUE (PARAM_MAX_CSE_INSNS)) { flush_hash_table (); @@ -6054,85 +6349,51 @@ cse_extended_basic_block (struct cse_basic_block_data *ebb_data) df_notes_rescan (insn); } - /* Track when we are inside in LIBCALL block. Inside such - a block we do not want to record destinations. The last - insn of a LIBCALL block is not considered to be part of - the block, since its destination is the result of the - block and hence should be recorded. */ - if (REG_NOTES (insn) != 0) - { - rtx p; + cse_insn (insn); - 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)) - { - /* Keep libcall_insn for the last SET insn of - a no-conflict block to prevent changing the - destination. */ - if (!no_conflict) - libcall_insn = NULL_RTX; - else - no_conflict = -1; - } - else if (find_reg_note (insn, REG_NO_CONFLICT, NULL_RTX)) - no_conflict = 1; - } - - cse_insn (insn, libcall_insn); - - /* If we kept libcall_insn for a no-conflict bock, - clear it here. */ - if (no_conflict == -1) - { - libcall_insn = NULL_RTX; - no_conflict = 0; - } - /* If we haven't already found an insn where we added a LABEL_REF, check this one. */ - if (NONJUMP_INSN_P (insn) && ! recorded_label_ref + if (INSN_P (insn) && !recorded_label_ref && for_each_rtx (&PATTERN (insn), check_for_label_ref, (void *) insn)) - recorded_label_ref = 1; + recorded_label_ref = true; #ifdef HAVE_cc0 - /* If the previous insn set CC0 and this insn no longer - references CC0, delete the previous insn. Here we use - fact that nothing expects CC0 to be valid over an insn, - which is true until the final pass. */ - { - rtx prev_insn, tem; - - prev_insn = PREV_INSN (insn); - if (prev_insn && NONJUMP_INSN_P (prev_insn) - && (tem = single_set (prev_insn)) != 0 - && SET_DEST (tem) == cc0_rtx - && ! reg_mentioned_p (cc0_rtx, PATTERN (insn))) - delete_insn (prev_insn); - } - - /* If this insn is not the last insn in the basic block, - it will be PREV_INSN(insn) in the next iteration. If - we recorded any CC0-related information for this insn, - remember it. */ - if (insn != BB_END (bb)) + if (NONDEBUG_INSN_P (insn)) { - prev_insn_cc0 = this_insn_cc0; - prev_insn_cc0_mode = this_insn_cc0_mode; + /* If the previous insn sets CC0 and this insn no + longer references CC0, delete the previous insn. + Here we use fact that nothing expects CC0 to be + valid over an insn, which is true until the final + pass. */ + rtx prev_insn, tem; + + prev_insn = prev_nonnote_nondebug_insn (insn); + if (prev_insn && NONJUMP_INSN_P (prev_insn) + && (tem = single_set (prev_insn)) != NULL_RTX + && SET_DEST (tem) == cc0_rtx + && ! reg_mentioned_p (cc0_rtx, PATTERN (insn))) + delete_insn (prev_insn); + + /* If this insn is not the last insn in the basic + block, it will be PREV_INSN(insn) in the next + iteration. If we recorded any CC0-related + information for this insn, remember it. */ + if (insn != BB_END (bb)) + { + prev_insn_cc0 = this_insn_cc0; + prev_insn_cc0_mode = this_insn_cc0_mode; + } } #endif } } - /* Make sure that libcalls don't span multiple basic blocks. */ - gcc_assert (libcall_insn == NULL_RTX); - /* With non-call exceptions, we are not always able to update the CFG properly inside cse_insn. So clean up possibly redundant EH edges here. */ - if (flag_non_call_exceptions && have_eh_succ_edges (bb)) - purge_dead_edges (bb); + if (cfun->can_throw_non_call_exceptions && have_eh_succ_edges (bb)) + cse_cfg_altered |= purge_dead_edges (bb); /* If we changed a conditional jump, we may have terminated the path we are following. Check that by verifying that @@ -6190,8 +6451,10 @@ cse_extended_basic_block (struct cse_basic_block_data *ebb_data) F is the first instruction. NREGS is one plus the highest pseudo-reg number used in the instruction. - Returns 1 if jump_optimize should be redone due to simplifications - in conditional jump instructions. */ + Return 2 if jump optimizations should be redone due to simplifications + in conditional jump instructions. + Return 1 if the CFG should be cleaned up because it has been modified. + Return 0 otherwise. */ int cse_main (rtx f ATTRIBUTE_UNUSED, int nregs) @@ -6211,8 +6474,9 @@ cse_main (rtx f ATTRIBUTE_UNUSED, int nregs) ebb_data.path = XNEWVEC (struct branch_path, PARAM_VALUE (PARAM_MAX_CSE_PATH_LENGTH)); - cse_jumps_altered = 0; - recorded_label_ref = 0; + cse_cfg_altered = false; + cse_jumps_altered = false; + recorded_label_ref = false; constant_pool_entries_cost = 0; constant_pool_entries_regcost = 0; ebb_data.path_size = 0; @@ -6274,26 +6538,34 @@ cse_main (rtx f ATTRIBUTE_UNUSED, int nregs) free (rc_order); rtl_hooks = general_rtl_hooks; - return cse_jumps_altered || recorded_label_ref; + if (cse_jumps_altered || recorded_label_ref) + return 2; + else if (cse_cfg_altered) + return 1; + else + return 0; } -/* Called via for_each_rtx to see if an insn is using a LABEL_REF for which - there isn't a REG_LABEL note. Return one if so. DATA is the insn. */ +/* Called via for_each_rtx to see if an insn is using a LABEL_REF for + which there isn't a REG_LABEL_OPERAND note. + Return one if so. DATA is the insn. */ static int check_for_label_ref (rtx *rtl, void *data) { rtx insn = (rtx) data; - /* If this insn uses a LABEL_REF and there isn't a REG_LABEL note for it, - we must rerun jump since it needs to place the note. If this is a - LABEL_REF for a CODE_LABEL that isn't in the insn chain, don't do this - since no REG_LABEL will be added. */ + /* If this insn uses a LABEL_REF and there isn't a REG_LABEL_OPERAND + note for it, we must rerun jump since it needs to place the note. If + this is a LABEL_REF for a CODE_LABEL that isn't in the insn chain, + don't do this since no REG_LABEL_OPERAND will be added. */ return (GET_CODE (*rtl) == LABEL_REF && ! LABEL_REF_NONLOCAL_P (*rtl) + && (!JUMP_P (insn) + || !label_is_jump_target_p (XEXP (*rtl, 0), insn)) && LABEL_P (XEXP (*rtl, 0)) && INSN_UID (XEXP (*rtl, 0)) != 0 - && ! find_reg_note (insn, REG_LABEL, XEXP (*rtl, 0))); + && ! find_reg_note (insn, REG_LABEL_OPERAND, XEXP (*rtl, 0))); } /* Count the number of times registers are used (not set) in X. @@ -6351,12 +6623,15 @@ count_reg_usage (rtx x, int *counts, rtx dest, int incr) incr); return; + case DEBUG_INSN: + return; + case CALL_INSN: case INSN: case JUMP_INSN: - /* We expect dest to be NULL_RTX here. If the insn may trap, mark - this fact by setting DEST to pc_rtx. */ - if (flag_non_call_exceptions && may_trap_p (PATTERN (x))) + /* We expect dest to be NULL_RTX here. If the insn may trap, mark + this fact by setting DEST to pc_rtx. */ + if (insn_could_throw_p (x)) dest = pc_rtx; if (code == CALL_INSN) count_reg_usage (CALL_INSN_FUNCTION_USAGE (x), counts, dest, incr); @@ -6423,6 +6698,16 @@ count_reg_usage (rtx x, int *counts, rtx dest, int incr) } } +/* Return true if X is a dead register. */ + +static inline int +is_dead_reg (rtx x, int *counts) +{ + return (REG_P (x) + && REGNO (x) >= FIRST_PSEUDO_REGISTER + && counts[REGNO (x)] == 0); +} + /* Return true if set is live. */ static bool set_live_p (rtx set, rtx insn ATTRIBUTE_UNUSED, /* Only used with HAVE_cc0. */ @@ -6438,14 +6723,12 @@ set_live_p (rtx set, rtx insn ATTRIBUTE_UNUSED, /* Only used with HAVE_cc0. */ #ifdef HAVE_cc0 else if (GET_CODE (SET_DEST (set)) == CC0 && !side_effects_p (SET_SRC (set)) - && ((tem = next_nonnote_insn (insn)) == 0 + && ((tem = next_nonnote_nondebug_insn (insn)) == NULL_RTX || !INSN_P (tem) || !reg_referenced_p (cc0_rtx, PATTERN (tem)))) return false; #endif - else if (!REG_P (SET_DEST (set)) - || REGNO (SET_DEST (set)) < FIRST_PSEUDO_REGISTER - || counts[REGNO (SET_DEST (set))] != 0 + else if (!is_dead_reg (SET_DEST (set), counts) || side_effects_p (SET_SRC (set))) return true; return false; @@ -6457,7 +6740,7 @@ static bool insn_live_p (rtx insn, int *counts) { int i; - if (flag_non_call_exceptions && may_trap_p (PATTERN (insn))) + if (insn_could_throw_p (insn)) return true; else if (GET_CODE (PATTERN (insn)) == SET) return set_live_p (PATTERN (insn), insn, counts); @@ -6477,59 +6760,78 @@ insn_live_p (rtx insn, int *counts) } return false; } + else if (DEBUG_INSN_P (insn)) + { + rtx next; + + for (next = NEXT_INSN (insn); next; next = NEXT_INSN (next)) + if (NOTE_P (next)) + continue; + else if (!DEBUG_INSN_P (next)) + return true; + else if (INSN_VAR_LOCATION_DECL (insn) == INSN_VAR_LOCATION_DECL (next)) + return false; + + return true; + } else return true; } -/* Return true if libcall is dead as a whole. */ +/* Count the number of stores into pseudo. Callback for note_stores. */ -static bool -dead_libcall_p (rtx insn, int *counts) +static void +count_stores (rtx x, const_rtx set ATTRIBUTE_UNUSED, void *data) { - 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) - return false; + int *counts = (int *) data; + if (REG_P (x) && REGNO (x) >= FIRST_PSEUDO_REGISTER) + counts[REGNO (x)]++; +} - set = single_set (insn); - if (!set) - return false; +struct dead_debug_insn_data +{ + int *counts; + rtx *replacements; + bool seen_repl; +}; - new = simplify_rtx (XEXP (note, 0)); - if (!new) - new = XEXP (note, 0); +/* Return if a DEBUG_INSN needs to be reset because some dead + pseudo doesn't have a replacement. Callback for for_each_rtx. */ - /* While changing insn, we must update the counts accordingly. */ - count_reg_usage (insn, counts, NULL_RTX, -1); +static int +is_dead_debug_insn (rtx *loc, void *data) +{ + rtx x = *loc; + struct dead_debug_insn_data *ddid = (struct dead_debug_insn_data *) data; - if (validate_change (insn, &SET_SRC (set), new, 0)) + if (is_dead_reg (x, ddid->counts)) { - count_reg_usage (insn, counts, NULL_RTX, 1); - remove_note (insn, find_reg_note (insn, REG_RETVAL, NULL_RTX)); - remove_note (insn, note); - return true; + if (ddid->replacements && ddid->replacements[REGNO (x)] != NULL_RTX) + ddid->seen_repl = true; + else + return 1; } + return 0; +} + +/* Replace a dead pseudo in a DEBUG_INSN with replacement DEBUG_EXPR. + Callback for simplify_replace_fn_rtx. */ - if (CONSTANT_P (new)) +static rtx +replace_dead_reg (rtx x, const_rtx old_rtx ATTRIBUTE_UNUSED, void *data) +{ + rtx *replacements = (rtx *) data; + + if (REG_P (x) + && REGNO (x) >= FIRST_PSEUDO_REGISTER + && replacements[REGNO (x)] != NULL_RTX) { - 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); - remove_note (insn, find_reg_note (insn, REG_RETVAL, NULL_RTX)); - remove_note (insn, note); - return true; - } + if (GET_MODE (x) == GET_MODE (replacements[REGNO (x)])) + return replacements[REGNO (x)]; + return lowpart_subreg (GET_MODE (x), replacements[REGNO (x)], + GET_MODE (replacements[REGNO (x)])); } - - count_reg_usage (insn, counts, NULL_RTX, 1); - return false; + return NULL_RTX; } /* Scan all the insns and delete any that are dead; i.e., they store a register @@ -6545,23 +6847,51 @@ delete_trivially_dead_insns (rtx insns, int nreg) { int *counts; rtx insn, prev; - int in_libcall = 0, dead_libcall = 0; + rtx *replacements = NULL; int ndead = 0; timevar_push (TV_DELETE_TRIVIALLY_DEAD); /* First count the number of times each register is used. */ - counts = XCNEWVEC (int, nreg); - for (insn = insns; insn; insn = NEXT_INSN (insn)) - if (INSN_P (insn)) - count_reg_usage (insn, counts, NULL_RTX, 1); - + if (MAY_HAVE_DEBUG_INSNS) + { + counts = XCNEWVEC (int, nreg * 3); + for (insn = insns; insn; insn = NEXT_INSN (insn)) + if (DEBUG_INSN_P (insn)) + count_reg_usage (INSN_VAR_LOCATION_LOC (insn), counts + nreg, + NULL_RTX, 1); + else if (INSN_P (insn)) + { + count_reg_usage (insn, counts, NULL_RTX, 1); + note_stores (PATTERN (insn), count_stores, counts + nreg * 2); + } + /* If there can be debug insns, COUNTS are 3 consecutive arrays. + First one counts how many times each pseudo is used outside + of debug insns, second counts how many times each pseudo is + used in debug insns and third counts how many times a pseudo + is stored. */ + } + else + { + counts = XCNEWVEC (int, nreg); + for (insn = insns; insn; insn = NEXT_INSN (insn)) + if (INSN_P (insn)) + count_reg_usage (insn, counts, NULL_RTX, 1); + /* If no debug insns can be present, COUNTS is just an array + which counts how many times each pseudo is used. */ + } /* 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. */ + up deleting code that is not really dead. + + If some otherwise unused register is only used in DEBUG_INSNs, + try to create a DEBUG_EXPR temporary and emit a DEBUG_INSN before + the setter. Then go through DEBUG_INSNs and if a DEBUG_EXPR + has been created for the unused register, replace it with + the DEBUG_EXPR, otherwise reset the DEBUG_INSN. */ for (insn = get_last_insn (); insn; insn = prev) { int live_insn = 0; @@ -6570,37 +6900,85 @@ delete_trivially_dead_insns (rtx insns, int nreg) if (!INSN_P (insn)) continue; - /* 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, counts); - } - else if (in_libcall) - live_insn = ! dead_libcall; - else - live_insn = insn_live_p (insn, counts); + live_insn = insn_live_p (insn, counts); /* If this is a dead insn, delete it and show registers in it aren't being used. */ if (! live_insn && dbg_cnt (delete_trivial_dead)) { - count_reg_usage (insn, counts, NULL_RTX, -1); + if (DEBUG_INSN_P (insn)) + count_reg_usage (INSN_VAR_LOCATION_LOC (insn), counts + nreg, + NULL_RTX, -1); + else + { + rtx set; + if (MAY_HAVE_DEBUG_INSNS + && (set = single_set (insn)) != NULL_RTX + && is_dead_reg (SET_DEST (set), counts) + /* Used at least once in some DEBUG_INSN. */ + && counts[REGNO (SET_DEST (set)) + nreg] > 0 + /* And set exactly once. */ + && counts[REGNO (SET_DEST (set)) + nreg * 2] == 1 + && !side_effects_p (SET_SRC (set)) + && asm_noperands (PATTERN (insn)) < 0) + { + rtx dval, bind; + + /* Create DEBUG_EXPR (and DEBUG_EXPR_DECL). */ + dval = make_debug_expr_from_rtl (SET_DEST (set)); + + /* Emit a debug bind insn before the insn in which + reg dies. */ + bind = gen_rtx_VAR_LOCATION (GET_MODE (SET_DEST (set)), + DEBUG_EXPR_TREE_DECL (dval), + SET_SRC (set), + VAR_INIT_STATUS_INITIALIZED); + count_reg_usage (bind, counts + nreg, NULL_RTX, 1); + + bind = emit_debug_insn_before (bind, insn); + df_insn_rescan (bind); + + if (replacements == NULL) + replacements = XCNEWVEC (rtx, nreg); + replacements[REGNO (SET_DEST (set))] = dval; + } + + count_reg_usage (insn, counts, NULL_RTX, -1); + ndead++; + } delete_insn_and_edges (insn); - ndead++; } + } - if (in_libcall && find_reg_note (insn, REG_LIBCALL, NULL_RTX)) - { - in_libcall = 0; - dead_libcall = 0; - } + if (MAY_HAVE_DEBUG_INSNS) + { + struct dead_debug_insn_data ddid; + ddid.counts = counts; + ddid.replacements = replacements; + for (insn = get_last_insn (); insn; insn = PREV_INSN (insn)) + if (DEBUG_INSN_P (insn)) + { + /* If this debug insn references a dead register that wasn't replaced + with an DEBUG_EXPR, reset the DEBUG_INSN. */ + ddid.seen_repl = false; + if (for_each_rtx (&INSN_VAR_LOCATION_LOC (insn), + is_dead_debug_insn, &ddid)) + { + INSN_VAR_LOCATION_LOC (insn) = gen_rtx_UNKNOWN_VAR_LOC (); + df_insn_rescan (insn); + } + else if (ddid.seen_repl) + { + INSN_VAR_LOCATION_LOC (insn) + = simplify_replace_fn_rtx (INSN_VAR_LOCATION_LOC (insn), + NULL_RTX, replace_dead_reg, + replacements); + df_insn_rescan (insn); + } + } + if (replacements) + free (replacements); } if (dump_file && ndead) @@ -6628,7 +7006,7 @@ cse_change_cc_mode (rtx *loc, void *data) && GET_MODE (*loc) != GET_MODE (args->newreg)) { validate_change (args->insn, loc, args->newreg, 1); - + return -1; } return 0; @@ -6648,10 +7026,10 @@ cse_change_cc_mode_insn (rtx insn, rtx newreg) args.insn = insn; args.newreg = newreg; - + for_each_rtx (&PATTERN (insn), cse_change_cc_mode, &args); for_each_rtx (®_NOTES (insn), cse_change_cc_mode, &args); - + /* If the following assertion was triggered, there is most probably something wrong with the cc_modes_compatible back end function. CC modes only can be considered compatible if the insn - with the mode @@ -6689,13 +7067,17 @@ cse_change_cc_mode_insns (rtx start, rtx end, rtx newreg) 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. + ORIG_BB should be the same as BB in the outermost cse_cc_succs call, + but is passed unmodified down to recursive calls in order to prevent + endless recursion. 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) +cse_cc_succs (basic_block bb, basic_block orig_bb, rtx cc_reg, rtx cc_src, + bool can_change_mode) { bool found_equiv; enum machine_mode mode; @@ -6726,7 +7108,9 @@ cse_cc_succs (basic_block bb, rtx cc_reg, rtx cc_src, bool can_change_mode) continue; if (EDGE_COUNT (e->dest->preds) != 1 - || e->dest == EXIT_BLOCK_PTR) + || e->dest == EXIT_BLOCK_PTR + /* Avoid endless recursion on unreachable blocks. */ + || e->dest == orig_bb) continue; end = NEXT_INSN (BB_END (e->dest)); @@ -6764,7 +7148,7 @@ cse_cc_succs (basic_block bb, rtx cc_reg, rtx cc_src, bool can_change_mode) 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 @@ -6831,7 +7215,7 @@ cse_cc_succs (basic_block bb, rtx cc_reg, rtx cc_src, bool can_change_mode) { enum machine_mode submode; - submode = cse_cc_succs (e->dest, cc_reg, cc_src, false); + submode = cse_cc_succs (e->dest, orig_bb, cc_reg, cc_src, false); if (submode != VOIDmode) { gcc_assert (submode == mode); @@ -6866,7 +7250,7 @@ cse_cc_succs (basic_block bb, rtx cc_reg, rtx cc_src, bool can_change_mode) newreg); } - delete_insn (insns[i]); + delete_insn_and_edges (insns[i]); } return mode; @@ -6959,7 +7343,7 @@ cse_condition_code_reg (void) the basic block. */ orig_mode = GET_MODE (cc_src); - mode = cse_cc_succs (bb, cc_reg, cc_src, true); + mode = cse_cc_succs (bb, bb, cc_reg, cc_src, true); if (mode != VOIDmode) { gcc_assert (mode == GET_MODE (cc_src)); @@ -7003,20 +7387,26 @@ rest_of_handle_cse (void) expecting CSE to be run. But always rerun it in a cheap mode. */ cse_not_expected = !flag_rerun_cse_after_loop && !flag_gcse; - if (tem) - rebuild_jump_labels (get_insns ()); - - if (tem || optimize > 1) + if (tem == 2) + { + timevar_push (TV_JUMP); + rebuild_jump_labels (get_insns ()); + cleanup_cfg (0); + timevar_pop (TV_JUMP); + } + else if (tem == 1 || optimize > 1) cleanup_cfg (0); return 0; } -struct tree_opt_pass pass_cse = +struct rtl_opt_pass pass_cse = { + { + RTL_PASS, "cse1", /* name */ - gate_handle_cse, /* gate */ - rest_of_handle_cse, /* execute */ + gate_handle_cse, /* gate */ + rest_of_handle_cse, /* execute */ NULL, /* sub */ NULL, /* next */ 0, /* static_pass_number */ @@ -7029,7 +7419,7 @@ struct tree_opt_pass pass_cse = TODO_dump_func | TODO_ggc_collect | TODO_verify_flow, /* todo_flags_finish */ - 's' /* letter */ + } }; @@ -7058,23 +7448,28 @@ rest_of_handle_cse2 (void) delete_trivially_dead_insns (get_insns (), max_reg_num ()); - if (tem) + if (tem == 2) { timevar_push (TV_JUMP); rebuild_jump_labels (get_insns ()); cleanup_cfg (0); timevar_pop (TV_JUMP); } + else if (tem == 1) + cleanup_cfg (0); + cse_not_expected = 1; return 0; } -struct tree_opt_pass pass_cse2 = +struct rtl_opt_pass pass_cse2 = { + { + RTL_PASS, "cse2", /* name */ - gate_handle_cse2, /* gate */ - rest_of_handle_cse2, /* execute */ + gate_handle_cse2, /* gate */ + rest_of_handle_cse2, /* execute */ NULL, /* sub */ NULL, /* next */ 0, /* static_pass_number */ @@ -7086,7 +7481,67 @@ struct tree_opt_pass pass_cse2 = TODO_df_finish | TODO_verify_rtl_sharing | TODO_dump_func | TODO_ggc_collect | - TODO_verify_flow, /* todo_flags_finish */ - 't' /* letter */ + TODO_verify_flow /* todo_flags_finish */ + } }; +static bool +gate_handle_cse_after_global_opts (void) +{ + return optimize > 0 && flag_rerun_cse_after_global_opts; +} + +/* Run second CSE pass after loop optimizations. */ +static unsigned int +rest_of_handle_cse_after_global_opts (void) +{ + int save_cfj; + int tem; + + /* We only want to do local CSE, so don't follow jumps. */ + save_cfj = flag_cse_follow_jumps; + flag_cse_follow_jumps = 0; + + rebuild_jump_labels (get_insns ()); + tem = cse_main (get_insns (), max_reg_num ()); + purge_all_dead_edges (); + delete_trivially_dead_insns (get_insns (), max_reg_num ()); + + cse_not_expected = !flag_rerun_cse_after_loop; + + /* If cse altered any jumps, rerun jump opts to clean things up. */ + if (tem == 2) + { + timevar_push (TV_JUMP); + rebuild_jump_labels (get_insns ()); + cleanup_cfg (0); + timevar_pop (TV_JUMP); + } + else if (tem == 1) + cleanup_cfg (0); + + flag_cse_follow_jumps = save_cfj; + return 0; +} + +struct rtl_opt_pass pass_cse_after_global_opts = +{ + { + RTL_PASS, + "cse_local", /* name */ + gate_handle_cse_after_global_opts, /* gate */ + rest_of_handle_cse_after_global_opts, /* execute */ + NULL, /* sub */ + NULL, /* next */ + 0, /* static_pass_number */ + TV_CSE, /* tv_id */ + 0, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + TODO_df_finish | TODO_verify_rtl_sharing | + TODO_dump_func | + TODO_ggc_collect | + TODO_verify_flow /* todo_flags_finish */ + } +};