X-Git-Url: http://git.sourceforge.jp/view?a=blobdiff_plain;f=gcc%2Fcse.c;h=3ab6b37a8ea99f51cad920714aa7f161ceff7884;hb=54c0191177db8bc27fb599a7d0d7a5866c5046fb;hp=6976d455753c696a86045f875a9fdded9dc27603;hpb=06320855211a99597e4d4489a1816fb8712daf14;p=pf3gnuchains%2Fgcc-fork.git diff --git a/gcc/cse.c b/gcc/cse.c index 6976d455753..3ab6b37a8ea 100644 --- a/gcc/cse.c +++ b/gcc/cse.c @@ -1,13 +1,13 @@ /* 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. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free -Software Foundation; either version 2, or (at your option) any later +Software Foundation; either version 3, or (at your option) any later version. GCC is distributed in the hope that it will be useful, but WITHOUT ANY @@ -16,12 +16,10 @@ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License -along with GCC; see the file COPYING. If not, write to the Free -Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA -02110-1301, USA. */ +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" @@ -31,11 +29,11 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #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" @@ -45,6 +43,8 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #include "params.h" #include "rtlhooks-def.h" #include "tree-pass.h" +#include "df.h" +#include "dbgcnt.h" /* The basic idea of common subexpression elimination is to go through the code, keeping a record of expressions that would @@ -282,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 @@ -347,35 +348,17 @@ static unsigned int cse_reg_info_timestamp; static HARD_REG_SET hard_regs_in_table; -/* CUID of insn that starts the basic block currently being cse-processed. */ +/* True if CSE has altered the CFG. */ +static bool cse_cfg_altered; -static int cse_basic_block_start; +/* True if CSE has altered conditional jump insns in such a way + that jump optimization should be redone. */ +static bool cse_jumps_altered; -/* CUID of insn that ends the basic block currently being cse-processed. */ - -static int cse_basic_block_end; - -/* Vector mapping INSN_UIDs to cuids. - The cuids are like uids but increase monotonically always. - We use them to see whether a reg is used outside a given basic block. */ - -static int *uid_cuid; - -/* Highest UID in UID_CUID. */ -static int max_uid; - -/* Get the cuid of an insn. */ - -#define INSN_CUID(INSN) (uid_cuid[INSN_UID (INSN)]) - -/* Nonzero if cse has altered conditional jump insns - in such a way that jump optimization should be redone. */ - -static int 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 @@ -518,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 @@ -533,27 +521,32 @@ 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. */ struct cse_basic_block_data { - /* Lowest CUID value of insns in block. */ - int low_cuid; - /* Highest CUID value of insns in block. */ - int high_cuid; /* Total number of SETs in block. */ int nsets; /* 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; }; + +/* Pointers to the live in/live out bitmaps for the boundaries of the + current EBB. */ +static bitmap cse_ebb_live_in, cse_ebb_live_out; + /* A simple bitmap to track which basic blocks have been visited already as part of an already processed extended basic block. */ static sbitmap cse_visited_basic_blocks; @@ -570,14 +563,17 @@ 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 *); static void invalidate (rtx, enum machine_mode); -static int cse_rtx_varies_p (rtx, int); +static bool cse_rtx_varies_p (const_rtx, bool); static void remove_invalid_refs (unsigned int); static void remove_invalid_subreg_refs (unsigned int, unsigned int, enum machine_mode); @@ -588,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 *, @@ -599,10 +595,10 @@ 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); +static rtx cse_process_notes (rtx, rtx, bool *); static void cse_extended_basic_block (struct cse_basic_block_data *); static void count_reg_usage (rtx, int *, rtx, int); static int check_for_label_ref (rtx *, void *); @@ -614,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 @@ -646,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)); @@ -679,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)) { @@ -689,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; } @@ -768,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); } @@ -929,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; @@ -953,20 +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 - || ((uid_cuid[REGNO_LAST_UID (new)] > cse_basic_block_end - || (uid_cuid[REGNO_FIRST_UID (new)] - < cse_basic_block_start)) - && (uid_cuid[REGNO_LAST_UID (new)] - > uid_cuid[REGNO_LAST_UID (firstr)])))))) + || (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_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 { @@ -976,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; } } @@ -1045,9 +1040,7 @@ mention_regs (rtx x) if (code == REG) { unsigned int regno = REGNO (x); - unsigned int endregno - = regno + (regno >= FIRST_PSEUDO_REGISTER ? 1 - : hard_regno_nregs[regno][GET_MODE (x)]); + unsigned int endregno = END_REGNO (x); unsigned int i; for (i = regno; i < endregno; i++) @@ -1226,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. @@ -1307,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. @@ -1368,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; @@ -1391,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. @@ -1415,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; @@ -1429,14 +1590,7 @@ insert (rtx x, struct table_elt *classp, unsigned int hash, enum machine_mode mo /* If X is a hard register, show it is being put in the table. */ if (REG_P (x) && REGNO (x) < FIRST_PSEUDO_REGISTER) - { - unsigned int regno = REGNO (x); - unsigned int endregno = regno + hard_regno_nregs[regno][GET_MODE (x)]; - unsigned int i; - - for (i = regno; i < endregno; i++) - SET_HARD_REG_BIT (hard_regs_in_table, i); - } + add_to_hard_reg_set (&hard_regs_in_table, GET_MODE (x), REGNO (x)); /* Put an element for X into the right hash bucket. */ @@ -1448,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]; @@ -1584,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 @@ -1597,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; @@ -1631,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; } } } @@ -1677,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; @@ -1727,20 +1895,12 @@ 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 = TEST_HARD_REG_BIT (hard_regs_in_table, regno); - unsigned int endregno - = regno + hard_regno_nregs[regno][GET_MODE (x)]; + unsigned int endregno = END_HARD_REGNO (x); unsigned int tregno, tendregno, rn; struct table_elt *p, *next; @@ -1766,8 +1926,7 @@ invalidate (rtx x, enum machine_mode full_mode) continue; tregno = REGNO (p->exp); - tendregno - = tregno + hard_regno_nregs[tregno][GET_MODE (p->exp)]; + tendregno = END_HARD_REGNO (p->exp); if (tendregno > regno && tregno < endregno) remove_from_table (p, hash); } @@ -1978,7 +2137,7 @@ invalidate_for_call (void) continue; regno = REGNO (p->exp); - endregno = regno + hard_regno_nregs[regno][GET_MODE (p->exp)]; + endregno = END_HARD_REGNO (p->exp); for (i = regno; i < endregno; i++) if (TEST_HARD_REG_BIT (regs_invalidated_by_call, i)) @@ -2062,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) @@ -2076,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 (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 @@ -2105,6 +2258,15 @@ hash_rtx (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) { @@ -2112,7 +2274,7 @@ hash_rtx (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 @@ -2124,7 +2286,7 @@ hash_rtx (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) @@ -2141,9 +2303,9 @@ hash_rtx (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; @@ -2191,6 +2353,11 @@ hash_rtx (rtx x, enum machine_mode mode, int *do_not_record_p, + (unsigned int) CONST_DOUBLE_HIGH (x)); return hash; + case CONST_FIXED: + hash += (unsigned int) code + (unsigned int) GET_MODE (x); + hash += fixed_hash (CONST_FIXED_VALUE (x)); + return hash; + case CONST_VECTOR: { int units; @@ -2201,8 +2368,9 @@ hash_rtx (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; @@ -2236,7 +2404,7 @@ hash_rtx (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; @@ -2283,11 +2451,16 @@ hash_rtx (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; @@ -2304,12 +2477,12 @@ hash_rtx (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)); @@ -2343,14 +2516,16 @@ hash_rtx (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': @@ -2373,6 +2548,27 @@ hash_rtx (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 @@ -2404,7 +2600,7 @@ safe_hash (rtx x, enum machine_mode mode) If FOR_GCSE is true, we compare X and Y for equivalence for GCSE. */ int -exp_equiv_p (rtx x, rtx y, int validate, bool for_gcse) +exp_equiv_p (const_rtx x, const_rtx y, int validate, bool for_gcse) { int i, j; enum rtx_code code; @@ -2426,12 +2622,17 @@ exp_equiv_p (rtx x, 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: case CC0: case CONST_INT: case CONST_DOUBLE: + case CONST_FIXED: return x == y; case LABEL_REF: @@ -2447,9 +2648,7 @@ exp_equiv_p (rtx x, rtx y, int validate, bool for_gcse) { unsigned int regno = REGNO (y); unsigned int i; - unsigned int endregno - = regno + (regno >= FIRST_PSEUDO_REGISTER ? 1 - : hard_regno_nregs[regno][GET_MODE (y)]); + unsigned int endregno = END_REGNO (y); /* If the quantities are not the same, the expressions are not equivalent. If there are and we are not to validate, they @@ -2471,26 +2670,16 @@ exp_equiv_p (rtx x, 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; @@ -2598,8 +2787,8 @@ exp_equiv_p (rtx x, rtx y, int validate, bool for_gcse) executions of the program. 0 means X can be compared reliably against certain constants or near-constants. */ -static int -cse_rtx_varies_p (rtx x, int from_alias) +static bool +cse_rtx_varies_p (const_rtx x, bool 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 @@ -2617,7 +2806,7 @@ cse_rtx_varies_p (rtx x, int 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)))) { @@ -2661,14 +2850,15 @@ cse_rtx_varies_p (rtx x, int from_alias) static void validate_canon_reg (rtx *xloc, rtx insn) { - rtx new = canon_reg (*xloc, insn); + if (*xloc) + { + 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. */ - if (insn != 0 && new != 0) - validate_change (insn, xloc, new, 1); - else - *xloc = new; + /* 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_rtx); + validate_change (insn, xloc, new_rtx, 1); + } } /* Canonicalize an expression: @@ -2699,6 +2889,7 @@ canon_reg (rtx x, rtx insn) case CONST: case CONST_INT: case CONST_DOUBLE: + case CONST_FIXED: case CONST_VECTOR: case SYMBOL_REF: case LABEL_REF: @@ -2961,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. */ @@ -2987,13 +3178,14 @@ 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: case CONST_INT: case CONST_DOUBLE: + case CONST_FIXED: case CONST_VECTOR: case SYMBOL_REF: case LABEL_REF: @@ -3060,6 +3252,7 @@ fold_rtx (rtx x, rtx insn) case SYMBOL_REF: case LABEL_REF: case CONST_DOUBLE: + case CONST_FIXED: case CONST_VECTOR: const_arg = folded_arg; break; @@ -3125,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) @@ -3148,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; @@ -3192,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); @@ -3234,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; @@ -3269,28 +3451,17 @@ fold_rtx (rtx x, rtx insn) /* If we have a cheaper expression now, use that and try folding it further, from the top. */ if (cheapest_simplification != x) - return fold_rtx (cheapest_simplification, insn); + return fold_rtx (copy_rtx (cheapest_simplification), + 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)) @@ -3298,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 @@ -3334,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; + } } } } @@ -3344,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; @@ -3455,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 @@ -3483,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); @@ -3507,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 @@ -3519,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; } @@ -3538,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 @@ -3557,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)) @@ -3574,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; @@ -3586,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 @@ -3624,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; @@ -3639,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)); @@ -3649,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. @@ -3673,17 +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) - 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; } @@ -3991,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. */ @@ -4024,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. */ @@ -4035,7 +4188,7 @@ struct set }; static void -cse_insn (rtx insn, rtx libcall_insn) +cse_insn (rtx insn) { rtx x = PATTERN (insn); int i; @@ -4074,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. @@ -4109,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 +4317,12 @@ cse_insn (rtx insn, rtx libcall_insn) This does nothing when a register is clobbered because we have already invalidated the reg. */ if (MEM_P (XEXP (y, 0))) - canon_reg (XEXP (y, 0), NULL_RTX); + canon_reg (XEXP (y, 0), insn); } else if (GET_CODE (y) == USE && ! (REG_P (XEXP (y, 0)) && REGNO (XEXP (y, 0)) < FIRST_PSEUDO_REGISTER)) - canon_reg (y, NULL_RTX); + canon_reg (y, insn); else if (GET_CODE (y) == CALL) { /* The result of apply_change_group can be ignored; see @@ -4183,14 +4336,25 @@ cse_insn (rtx insn, rtx libcall_insn) else if (GET_CODE (x) == CLOBBER) { if (MEM_P (XEXP (x, 0))) - canon_reg (XEXP (x, 0), NULL_RTX); + 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), NULL_RTX); + 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. */ @@ -4198,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 @@ -4208,8 +4374,12 @@ cse_insn (rtx insn, rtx libcall_insn) && (! rtx_equal_p (XEXP (tem, 0), SET_SRC (sets[0].rtl)) || GET_CODE (SET_DEST (sets[0].rtl)) == STRICT_LOW_PART)) { - src_eqv = fold_rtx (canon_reg (XEXP (tem, 0), NULL_RTX), insn); - XEXP (tem, 0) = src_eqv; + /* The result of apply_change_group can be ignored; see canon_reg. */ + canon_reg (XEXP (tem, 0), insn); + apply_change_group (); + src_eqv = fold_rtx (XEXP (tem, 0), insn); + XEXP (tem, 0) = copy_rtx (src_eqv); + df_notes_rescan (insn); } /* Canonicalize sources and addresses of destinations. @@ -4226,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) { @@ -4267,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; @@ -4274,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; @@ -4342,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 @@ -4504,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)) { @@ -4538,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; @@ -4622,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; @@ -4726,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--; } } @@ -4804,18 +5001,106 @@ cse_insn (rtx insn, rtx libcall_insn) src_related_cost, src_related_regcost) <= 0 && preferable (src_eqv_cost, src_eqv_regcost, src_elt_cost, src_elt_regcost) <= 0) - trial = copy_rtx (src_eqv_here), src_eqv_cost = MAX_COST; + trial = src_eqv_here, src_eqv_cost = MAX_COST; else if (src_related && preferable (src_related_cost, src_related_regcost, src_elt_cost, src_elt_regcost) <= 0) - trial = copy_rtx (src_related), src_related_cost = MAX_COST; + trial = src_related, src_related_cost = MAX_COST; else { - trial = copy_rtx (elt->exp); + trial = elt->exp; elt = elt->next_same_value; 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. @@ -4836,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; } @@ -4857,29 +5142,15 @@ cse_insn (rtx insn, rtx libcall_insn) ; /* Look for a substitution that makes a valid insn. */ - else if (validate_change (insn, &SET_SRC (sets[i].rtl), trial, 0)) + 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)); - } + 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; @@ -4905,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. @@ -4992,6 +5270,7 @@ cse_insn (rtx insn, rtx libcall_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); + df_notes_rescan (insn); } } @@ -5032,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)))) @@ -5059,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; } @@ -5069,11 +5348,6 @@ cse_insn (rtx insn, rtx libcall_insn) else if (dest == pc_rtx && GET_CODE (src) == LABEL_REF && !LABEL_REF_NONLOCAL_P (src)) { - /* Now emit a BARRIER after the unconditional jump. */ - if (NEXT_INSN (insn) == 0 - || !BARRIER_P (NEXT_INSN (insn))) - emit_barrier_after (insn); - /* We reemit the jump in as many cases as possible just in case the form of an unconditional jump is significantly different than a computed jump or conditional jump. @@ -5083,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. */ @@ -5094,24 +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; - - /* Now emit a BARRIER after the unconditional jump. */ - if (NEXT_INSN (insn) == 0 - || !BARRIER_P (NEXT_INSN (insn))) - emit_barrier_after (insn); + 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; } @@ -5225,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 @@ -5304,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 (); } @@ -5380,9 +5639,7 @@ cse_insn (rtx insn, rtx libcall_insn) but it knows that reg_tick has been incremented, and it leaves reg_in_table as -1 . */ unsigned int regno = REGNO (x); - unsigned int endregno - = regno + (regno >= FIRST_PSEUDO_REGISTER ? 1 - : hard_regno_nregs[regno][GET_MODE (x)]); + unsigned int endregno = END_REGNO (x); unsigned int i; for (i = regno; i < endregno; i++) @@ -5444,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 @@ -5483,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)); @@ -5592,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 @@ -5607,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. */ @@ -5618,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. @@ -5731,7 +5986,7 @@ invalidate_from_clobbers (rtx x) Return the replacement for X. */ static rtx -cse_process_notes (rtx x, rtx object) +cse_process_notes_1 (rtx x, rtx object, bool *changed) { enum rtx_code code = GET_CODE (x); const char *fmt = GET_RTX_FORMAT (code); @@ -5744,6 +5999,7 @@ cse_process_notes (rtx x, rtx object) case SYMBOL_REF: case LABEL_REF: case CONST_DOUBLE: + case CONST_FIXED: case CONST_VECTOR: case PC: case CC0: @@ -5752,26 +6008,26 @@ cse_process_notes (rtx x, rtx object) case MEM: validate_change (x, &XEXP (x, 0), - cse_process_notes (XEXP (x, 0), x), 0); + cse_process_notes (XEXP (x, 0), x, changed), 0); return x; case EXPR_LIST: case INSN_LIST: if (REG_NOTE_KIND (x) == REG_EQUAL) - XEXP (x, 0) = cse_process_notes (XEXP (x, 0), NULL_RTX); + XEXP (x, 0) = cse_process_notes (XEXP (x, 0), NULL_RTX, changed); if (XEXP (x, 1)) - XEXP (x, 1) = cse_process_notes (XEXP (x, 1), NULL_RTX); + XEXP (x, 1) = cse_process_notes (XEXP (x, 1), NULL_RTX, changed); return x; case SIGN_EXTEND: case ZERO_EXTEND: case SUBREG: { - rtx new = cse_process_notes (XEXP (x, 0), object); + 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; } @@ -5787,9 +6043,9 @@ cse_process_notes (rtx x, rtx object) && (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); } } @@ -5803,10 +6059,20 @@ cse_process_notes (rtx x, rtx object) for (i = 0; i < GET_RTX_LENGTH (code); i++) if (fmt[i] == 'e') validate_change (object, &XEXP (x, i), - cse_process_notes (XEXP (x, i), object), 0); + cse_process_notes (XEXP (x, i), object, changed), 0); return x; } + +static rtx +cse_process_notes (rtx x, rtx object, bool *changed) +{ + rtx new_rtx = cse_process_notes_1 (x, object, changed); + if (new_rtx != x) + *changed = true; + return new_rtx; +} + /* Find a path in the CFG, starting with FIRST_BB to perform CSE on. @@ -5814,7 +6080,7 @@ cse_process_notes (rtx x, rtx object) 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 @@ -5830,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. */ @@ -5876,13 +6142,20 @@ cse_find_path (basic_block first_bb, struct cse_basic_block_data *data, { bb = FALLTHRU_EDGE (previous_bb_in_path)->dest; if (bb != EXIT_BLOCK_PTR - && single_pred_p (bb)) + && single_pred_p (bb) + /* We used to assert here that we would only see blocks + that we have not visited yet. But we may end up + visiting basic blocks twice if the CFG has changed + in this run of cse_main, because when the CFG changes + the topological sort of the CFG also changes. A basic + blocks that previously had more than two predecessors + may now have a single predecessor, and become part of + a path that starts at another basic block. + + We still want to visit each basic block only once, so + halt the path here if we have already visited BB. */ + && !TEST_BIT (cse_visited_basic_blocks, bb->index)) { -#if ENABLE_CHECKING - /* We should only see blocks here that we have not - visited yet. */ - gcc_assert (!TEST_BIT (cse_visited_basic_blocks, bb->index)); -#endif SET_BIT (cse_visited_basic_blocks, bb->index); data->path[path_size++].bb = bb; break; @@ -5921,14 +6194,12 @@ cse_find_path (basic_block first_bb, struct cse_basic_block_data *data, e = NULL; if (e && e->dest != EXIT_BLOCK_PTR - && single_pred_p (e->dest)) + && single_pred_p (e->dest) + /* Avoid visiting basic blocks twice. The large comment + above explains why this can happen. */ + && !TEST_BIT (cse_visited_basic_blocks, e->dest->index)) { basic_block bb2 = e->dest; - - /* We should only see blocks here that we have not - visited yet. */ - gcc_assert (!TEST_BIT (cse_visited_basic_blocks, bb2->index)); - SET_BIT (cse_visited_basic_blocks, bb2->index); data->path[path_size++].bb = bb2; bb = bb2; @@ -5976,19 +6247,17 @@ have_eh_succ_edges (basic_block bb) /* Scan to the end of the path described by DATA. Return an estimate of - the total number of SETs, and the lowest and highest insn CUID, of all - insns in the path. */ + the total number of SETs of all insns in the path. */ static void cse_prescan_path (struct cse_basic_block_data *data) { int nsets = 0; - int low_cuid = -1, high_cuid = -1; /* FIXME low_cuid not computed correctly */ int path_size = data->path_size; 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; @@ -6006,21 +6275,9 @@ cse_prescan_path (struct cse_basic_block_data *data) nsets += XVECLEN (PATTERN (insn), 0); else nsets += 1; - - /* Ignore insns made by CSE in a previous traversal of this - basic block. They cannot affect the boundaries of the - basic block. - FIXME: When we only visit each basic block at most once, - this can go away. */ - if (INSN_UID (insn) <= max_uid && INSN_CUID (insn) > high_cuid) - high_cuid = INSN_CUID (insn); - if (INSN_UID (insn) <= max_uid && INSN_CUID (insn) < low_cuid) - low_cuid = INSN_CUID (insn); } } - data->low_cuid = low_cuid; - data->high_cuid = high_cuid; data->nsets = nsets; } @@ -6037,14 +6294,30 @@ cse_extended_basic_block (struct cse_basic_block_data *ebb_data) qty_table = XNEWVEC (struct qty_table_elem, max_qty); new_basic_block (); + cse_ebb_live_in = df_get_live_in (ebb_data->path[0].bb); + cse_ebb_live_out = df_get_live_out (ebb_data->path[path_size - 1].bb); for (path_entry = 0; path_entry < path_size; path_entry++) { 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 @@ -6056,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 (); @@ -6068,88 +6341,59 @@ cse_extended_basic_block (struct cse_basic_block_data *ebb_data) /* Process notes first so we have all notes in canonical forms when looking for duplicate operations. */ if (REG_NOTES (insn)) - REG_NOTES (insn) = cse_process_notes (REG_NOTES (insn), - NULL_RTX); - - /* 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; - - 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; + bool changed = false; + REG_NOTES (insn) = cse_process_notes (REG_NOTES (insn), + NULL_RTX, &changed); + if (changed) + df_notes_rescan (insn); } - cse_insn (insn, libcall_insn); + cse_insn (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 @@ -6201,13 +6445,16 @@ cse_extended_basic_block (struct cse_basic_block_data *ebb_data) free (qty_table); } + /* Perform cse on the instructions of a function. 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) @@ -6217,13 +6464,19 @@ cse_main (rtx f ATTRIBUTE_UNUSED, int nregs) int *rc_order = XNEWVEC (int, last_basic_block); int i, n_blocks; + df_set_flags (DF_LR_RUN_DCE); + df_analyze (); + df_set_flags (DF_DEFER_INSN_RESCAN); + + reg_scan (get_insns (), max_reg_num ()); init_cse_reg_info (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; @@ -6239,19 +6492,6 @@ cse_main (rtx f ATTRIBUTE_UNUSED, int nregs) cse_visited_basic_blocks = sbitmap_alloc (last_basic_block); sbitmap_zero (cse_visited_basic_blocks); - /* Compute the mapping from uids to cuids. - CUIDs are numbers assigned to insns, like uids, except that - that cuids increase monotonically through the code. */ - max_uid = get_max_uid (); - uid_cuid = XCNEWVEC (int, max_uid + 1); - i = 0; - FOR_EACH_BB (bb) - { - rtx insn; - FOR_BB_INSNS (bb, insn) - INSN_CUID (insn) = ++i; - } - /* Loop over basic blocks in reverse completion order (RPO), excluding the ENTRY and EXIT blocks. */ n_blocks = pre_and_rev_post_order_compute (NULL, rc_order, false); @@ -6281,8 +6521,6 @@ cse_main (rtx f ATTRIBUTE_UNUSED, int nregs) needed for this path. For this, we take the number of sets and multiply that by MAX_RECOG_OPERANDS. */ max_qty = ebb_data.nsets * MAX_RECOG_OPERANDS; - cse_basic_block_start = ebb_data.low_cuid; - cse_basic_block_end = ebb_data.high_cuid; /* Dump the path we're about to process. */ if (dump_file) @@ -6294,33 +6532,40 @@ cse_main (rtx f ATTRIBUTE_UNUSED, int nregs) /* Clean up. */ end_alias_analysis (); - free (uid_cuid); free (reg_eqv_table); free (ebb_data.path); sbitmap_free (cse_visited_basic_blocks); 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. @@ -6356,6 +6601,7 @@ count_reg_usage (rtx x, int *counts, rtx dest, int incr) case CONST: case CONST_INT: case CONST_DOUBLE: + case CONST_FIXED: case CONST_VECTOR: case SYMBOL_REF: case LABEL_REF: @@ -6377,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); @@ -6449,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. */ @@ -6464,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; @@ -6483,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); @@ -6503,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; +} - if (CONSTANT_P (new)) +/* Replace a dead pseudo in a DEBUG_INSN with replacement DEBUG_EXPR. + Callback for simplify_replace_fn_rtx. */ + +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 @@ -6571,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; @@ -6596,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) + 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) @@ -6654,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; @@ -6674,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 @@ -6715,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; @@ -6752,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)); @@ -6790,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 @@ -6857,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); @@ -6892,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; @@ -6985,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)); @@ -7019,31 +7377,36 @@ static unsigned int rest_of_handle_cse (void) { int tem; + if (dump_file) dump_flow_info (dump_file, dump_flags); - reg_scan (get_insns (), max_reg_num ()); - tem = cse_main (get_insns (), max_reg_num ()); /* If we are not running more CSE passes, then we are no longer 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) - cleanup_cfg (CLEANUP_EXPENSIVE); + 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 */ @@ -7052,10 +7415,11 @@ struct tree_opt_pass pass_cse = 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 */ - 's' /* letter */ + } }; @@ -7084,25 +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 ()); - delete_dead_jumptables (); - cleanup_cfg (CLEANUP_EXPENSIVE); + cleanup_cfg (0); timevar_pop (TV_JUMP); } - reg_scan (get_insns (), max_reg_num ()); + 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 */ @@ -7111,9 +7478,70 @@ struct tree_opt_pass pass_cse2 = 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 */ - '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 */ + } +};