X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=gcc%2Fcprop.c;h=169ca804e33e89086f944ed730f0d8967bd19866;hb=0cd74f3588928e22c08003c643c91340f555785e;hp=b1caabb09fc6f51a68b90acc29b492daa0c71a38;hpb=f1e52ed6b2a91ff953156a63a5c4af70e17fb6a8;p=gcc.git diff --git a/gcc/cprop.c b/gcc/cprop.c index b1caabb09fc..169ca804e33 100644 --- a/gcc/cprop.c +++ b/gcc/cprop.c @@ -1,5 +1,5 @@ /* Global constant/copy propagation for RTL. - Copyright (C) 1997-2015 Free Software Foundation, Inc. + Copyright (C) 1997-2020 Free Software Foundation, Inc. This file is part of GCC. @@ -20,57 +20,26 @@ along with GCC; see the file COPYING3. If not see #include "config.h" #include "system.h" #include "coretypes.h" -#include "tm.h" -#include "diagnostic-core.h" -#include "toplev.h" +#include "backend.h" #include "rtl.h" -#include "hash-set.h" -#include "machmode.h" -#include "vec.h" -#include "double-int.h" -#include "input.h" -#include "alias.h" -#include "symtab.h" -#include "wide-int.h" -#include "inchash.h" -#include "tree.h" -#include "tm_p.h" -#include "regs.h" -#include "hard-reg-set.h" -#include "flags.h" +#include "cfghooks.h" +#include "df.h" #include "insn-config.h" +#include "memmodel.h" +#include "emit-rtl.h" #include "recog.h" -#include "predict.h" -#include "hashtab.h" -#include "function.h" -#include "dominance.h" -#include "cfg.h" +#include "diagnostic-core.h" +#include "toplev.h" #include "cfgrtl.h" #include "cfganal.h" #include "lcm.h" #include "cfgcleanup.h" -#include "basic-block.h" -#include "statistics.h" -#include "real.h" -#include "fixed-value.h" -#include "expmed.h" -#include "dojump.h" -#include "explow.h" -#include "calls.h" -#include "emit-rtl.h" -#include "varasm.h" -#include "stmt.h" -#include "expr.h" -#include "except.h" -#include "params.h" #include "cselib.h" #include "intl.h" -#include "obstack.h" #include "tree-pass.h" -#include "df.h" #include "dbgcnt.h" -#include "target.h" #include "cfgloop.h" +#include "gcse.h" /* An obstack for our working variables. */ @@ -88,8 +57,6 @@ struct cprop_occr rtx_insn *insn; }; -typedef struct cprop_occr *occr_t; - /* Hash table entry for assignment expressions. */ struct cprop_expr @@ -285,6 +252,15 @@ cprop_constant_p (const_rtx x) return CONSTANT_P (x) && (GET_CODE (x) != CONST || shared_const_p (x)); } +/* Determine whether the rtx X should be treated as a register that can + be propagated. Any pseudo-register is fine. */ + +static bool +cprop_reg_p (const_rtx x) +{ + return REG_P (x) && !HARD_REGISTER_P (x); +} + /* Scan SET present in INSN and add an entry to the hash TABLE. IMPLICIT is true if it's an implicit set, false otherwise. */ @@ -295,8 +271,7 @@ hash_scan_set (rtx set, rtx_insn *insn, struct hash_table_d *table, rtx src = SET_SRC (set); rtx dest = SET_DEST (set); - if (REG_P (dest) - && ! HARD_REGISTER_P (dest) + if (cprop_reg_p (dest) && reg_available_p (dest, insn) && can_copy_p (GET_MODE (dest))) { @@ -318,12 +293,11 @@ hash_scan_set (rtx set, rtx_insn *insn, struct hash_table_d *table, && REG_NOTE_KIND (note) == REG_EQUAL && !REG_P (src) && cprop_constant_p (XEXP (note, 0))) - src = XEXP (note, 0), set = gen_rtx_SET (VOIDmode, dest, src); + src = XEXP (note, 0), set = gen_rtx_SET (dest, src); /* Record sets for constant/copy propagation. */ - if ((REG_P (src) + if ((cprop_reg_p (src) && src != dest - && ! HARD_REGISTER_P (src) && reg_available_p (src, insn)) || cprop_constant_p (src)) insert_set_in_table (dest, src, insn, table, implicit); @@ -758,12 +732,38 @@ try_replace_reg (rtx from, rtx to, rtx_insn *insn) int success = 0; rtx set = single_set (insn); + bool check_rtx_costs = true; + bool speed = optimize_bb_for_speed_p (BLOCK_FOR_INSN (insn)); + int old_cost = set ? set_rtx_cost (set, speed) : 0; + + if (!set + || CONSTANT_P (SET_SRC (set)) + || (note != 0 + && REG_NOTE_KIND (note) == REG_EQUAL + && (GET_CODE (XEXP (note, 0)) == CONST + || CONSTANT_P (XEXP (note, 0))))) + check_rtx_costs = false; + /* Usually we substitute easy stuff, so we won't copy everything. We however need to take care to not duplicate non-trivial CONST expressions. */ to = copy_rtx (to); validate_replace_src_group (from, to, insn); + + /* If TO is a constant, check the cost of the set after propagation + to the cost of the set before the propagation. If the cost is + higher, then do not replace FROM with TO. */ + + if (check_rtx_costs + && CONSTANT_P (to) + && set_rtx_cost (set, speed) > old_cost) + { + cancel_changes (0); + return false; + } + + if (num_changes_pending () && apply_change_group ()) success = 1; @@ -821,15 +821,15 @@ try_replace_reg (rtx from, rtx to, rtx_insn *insn) return success; } -/* Find a set of REGNOs that are available on entry to INSN's block. Return - NULL no such set is found. */ +/* Find a set of REGNOs that are available on entry to INSN's block. If found, + SET_RET[0] will be assigned a set with a register source and SET_RET[1] a + set with a constant source. If not found the corresponding entry is set to + NULL. */ -static struct cprop_expr * -find_avail_set (int regno, rtx_insn *insn) +static void +find_avail_set (int regno, rtx_insn *insn, struct cprop_expr *set_ret[2]) { - /* SET1 contains the last set found that can be returned to the caller for - use in a substitution. */ - struct cprop_expr *set1 = 0; + set_ret[0] = set_ret[1] = NULL; /* Loops are not possible here. To get a loop we would need two sets available at the start of the block containing INSN. i.e. we would @@ -838,7 +838,7 @@ find_avail_set (int regno, rtx_insn *insn) (set (reg X) (reg Y)) (set (reg Y) (reg X)) - This can not happen since the set of (reg Y) would have killed the + This cannot happen since the set of (reg Y) would have killed the set of (reg X) making it unavailable at the start of this block. */ while (1) { @@ -869,8 +869,10 @@ find_avail_set (int regno, rtx_insn *insn) If the source operand changed, we may still use it for the next iteration of this loop, but we may not use it for substitutions. */ - if (cprop_constant_p (src) || reg_not_set_p (src, insn)) - set1 = set; + if (cprop_constant_p (src)) + set_ret[1] = set; + else if (reg_not_set_p (src, insn)) + set_ret[0] = set; /* If the source of the set is anything except a register, then we have reached the end of the copy chain. */ @@ -881,10 +883,6 @@ find_avail_set (int regno, rtx_insn *insn) and see if we have an available copy into SRC. */ regno = REGNO (src); } - - /* SET1 holds the last set that was available and anticipatable at - INSN. */ - return set1; } /* Subroutine of cprop_insn that tries to propagate constants into @@ -965,17 +963,15 @@ cprop_jump (basic_block bb, rtx_insn *setcc, rtx_insn *jump, rtx from, rtx src) remove_note (jump, note); } -#if HAVE_cc0 /* Delete the cc0 setter. */ - if (setcc != NULL && CC0_P (SET_DEST (single_set (setcc)))) + if (HAVE_cc0 && setcc != NULL && CC0_P (SET_DEST (single_set (setcc)))) delete_insn (setcc); -#endif global_const_prop_count++; if (dump_file != NULL) { fprintf (dump_file, - "GLOBAL CONST-PROP: Replacing reg %d in jump_insn %d with" + "GLOBAL CONST-PROP: Replacing reg %d in jump_insn %d with " "constant ", REGNO (from), INSN_UID (jump)); print_rtl (dump_file, src); fprintf (dump_file, "\n"); @@ -1050,40 +1046,40 @@ cprop_insn (rtx_insn *insn) int changed = 0, changed_this_round; rtx note; -retry: - changed_this_round = 0; - reg_use_count = 0; - note_uses (&PATTERN (insn), find_used_regs, NULL); - - /* We may win even when propagating constants into notes. */ - note = find_reg_equal_equiv_note (insn); - if (note) - find_used_regs (&XEXP (note, 0), NULL); - - for (i = 0; i < reg_use_count; i++) + do { - rtx reg_used = reg_use_table[i]; - unsigned int regno = REGNO (reg_used); - rtx src; - struct cprop_expr *set; + changed_this_round = 0; + reg_use_count = 0; + note_uses (&PATTERN (insn), find_used_regs, NULL); - /* If the register has already been set in this block, there's - nothing we can do. */ - if (! reg_not_set_p (reg_used, insn)) - continue; + /* We may win even when propagating constants into notes. */ + note = find_reg_equal_equiv_note (insn); + if (note) + find_used_regs (&XEXP (note, 0), NULL); - /* Find an assignment that sets reg_used and is available - at the start of the block. */ - set = find_avail_set (regno, insn); - if (! set) - continue; + for (i = 0; i < reg_use_count; i++) + { + rtx reg_used = reg_use_table[i]; + unsigned int regno = REGNO (reg_used); + rtx src_cst = NULL, src_reg = NULL; + struct cprop_expr *set[2]; - src = set->src; + /* If the register has already been set in this block, there's + nothing we can do. */ + if (! reg_not_set_p (reg_used, insn)) + continue; - /* Constant propagation. */ - if (cprop_constant_p (src)) - { - if (constprop_register (reg_used, src, insn)) + /* Find an assignment that sets reg_used and is available + at the start of the block. */ + find_avail_set (regno, insn, set); + if (set[0]) + src_reg = set[0]->src; + if (set[1]) + src_cst = set[1]->src; + + /* Constant propagation. */ + if (src_cst && cprop_constant_p (src_cst) + && constprop_register (reg_used, src_cst, insn)) { changed_this_round = changed = 1; global_const_prop_count++; @@ -1093,18 +1089,16 @@ retry: "GLOBAL CONST-PROP: Replacing reg %d in ", regno); fprintf (dump_file, "insn %d with constant ", INSN_UID (insn)); - print_rtl (dump_file, src); + print_rtl (dump_file, src_cst); fprintf (dump_file, "\n"); } if (insn->deleted ()) return 1; } - } - else if (REG_P (src) - && REGNO (src) >= FIRST_PSEUDO_REGISTER - && REGNO (src) != regno) - { - if (try_replace_reg (reg_used, src, insn)) + /* Copy propagation. */ + else if (src_reg && cprop_reg_p (src_reg) + && REGNO (src_reg) != regno + && try_replace_reg (reg_used, src_reg, insn)) { changed_this_round = changed = 1; global_copy_prop_count++; @@ -1113,7 +1107,7 @@ retry: fprintf (dump_file, "GLOBAL COPY-PROP: Replacing reg %d in insn %d", regno, INSN_UID (insn)); - fprintf (dump_file, " with reg %d\n", REGNO (src)); + fprintf (dump_file, " with reg %d\n", REGNO (src_reg)); } /* The original insn setting reg_used may or may not now be @@ -1123,12 +1117,10 @@ retry: and made things worse. */ } } - - /* If try_replace_reg simplified the insn, the regs found - by find_used_regs may not be valid anymore. Start over. */ - if (changed_this_round) - goto retry; } + /* If try_replace_reg simplified the insn, the regs found by find_used_regs + may not be valid anymore. Start over. */ + while (changed_this_round); if (changed && DEBUG_INSN_P (insn)) return 0; @@ -1168,9 +1160,7 @@ local_cprop_find_used_regs (rtx *xptr, void *data) return; case SUBREG: - /* Setting a subreg of a register larger than word_mode leaves - the non-written words unchanged. */ - if (GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (x))) > BITS_PER_WORD) + if (read_modify_subreg_p (x)) return; break; @@ -1191,7 +1181,7 @@ do_local_cprop (rtx x, rtx_insn *insn) /* Rule out USE instructions and ASM statements as we don't want to change the hard registers mentioned. */ if (REG_P (x) - && (REGNO (x) >= FIRST_PSEUDO_REGISTER + && (cprop_reg_p (x) || (GET_CODE (PATTERN (insn)) != USE && asm_noperands (PATTERN (insn)) < 0))) { @@ -1207,7 +1197,7 @@ do_local_cprop (rtx x, rtx_insn *insn) if (cprop_constant_p (this_rtx)) newcnst = this_rtx; - if (REG_P (this_rtx) && REGNO (this_rtx) >= FIRST_PSEUDO_REGISTER + if (cprop_reg_p (this_rtx) /* Don't copy propagate if it has attached REG_EQUIV note. At this point this only function parameters should have REG_EQUIV notes and if the argument slot is used somewhere @@ -1257,6 +1247,8 @@ local_cprop_pass (void) bool changed = false; unsigned i; + auto_vec uncond_traps; + cselib_init (0); FOR_EACH_BB_FN (bb, cfun) { @@ -1264,6 +1256,9 @@ local_cprop_pass (void) { if (INSN_P (insn)) { + bool was_uncond_trap + = (GET_CODE (PATTERN (insn)) == TRAP_IF + && XEXP (PATTERN (insn), 0) == const1_rtx); rtx note = find_reg_equal_equiv_note (insn); do { @@ -1282,6 +1277,13 @@ local_cprop_pass (void) break; } } + if (!was_uncond_trap + && GET_CODE (PATTERN (insn)) == TRAP_IF + && XEXP (PATTERN (insn), 0) == const1_rtx) + { + uncond_traps.safe_push (insn); + break; + } if (insn->deleted ()) break; } @@ -1296,6 +1298,14 @@ local_cprop_pass (void) cselib_finish (); + while (!uncond_traps.is_empty ()) + { + rtx_insn *insn = uncond_traps.pop (); + basic_block to_split = BLOCK_FOR_INSN (insn); + remove_edge (split_block (to_split, insn)); + emit_barrier_after_bb (to_split); + } + return changed; } @@ -1328,9 +1338,8 @@ implicit_set_cond_p (const_rtx cond) if (GET_CODE (cond) != EQ && GET_CODE (cond) != NE) return false; - /* The first operand of COND must be a pseudo-reg. */ - if (! REG_P (XEXP (cond, 0)) - || HARD_REGISTER_P (XEXP (cond, 0))) + /* The first operand of COND must be a register we can propagate. */ + if (!cprop_reg_p (XEXP (cond, 0))) return false; /* The second operand of COND must be a suitable constant. */ @@ -1346,13 +1355,9 @@ implicit_set_cond_p (const_rtx cond) the optimization can't be performed. */ /* ??? The complex and vector checks are not implemented yet. We just always return zero for them. */ - if (CONST_DOUBLE_AS_FLOAT_P (cst)) - { - REAL_VALUE_TYPE d; - REAL_VALUE_FROM_CONST_DOUBLE (d, cst); - if (REAL_VALUES_EQUAL (d, dconst0)) - return 0; - } + if (CONST_DOUBLE_AS_FLOAT_P (cst) + && real_equal (CONST_DOUBLE_REAL_VALUE (cst), &dconst0)) + return 0; else return 0; } @@ -1422,8 +1427,7 @@ find_implicit_sets (void) (implicit_sets_size - old_implicit_sets_size) * sizeof (rtx)); } - new_rtx = gen_rtx_SET (VOIDmode, XEXP (cond, 0), - XEXP (cond, 1)); + new_rtx = gen_rtx_SET (XEXP (cond, 0), XEXP (cond, 1)); implicit_sets[dest->index] = new_rtx; if (dump_file) { @@ -1692,7 +1696,6 @@ bypass_conditional_jumps (void) if (ENTRY_BLOCK_PTR_FOR_FN (cfun)->next_bb == EXIT_BLOCK_PTR_FOR_FN (cfun)) return 0; - bypass_last_basic_block = last_basic_block_for_fn (cfun); mark_dfs_back_edges (); changed = 0; @@ -1739,47 +1742,6 @@ bypass_conditional_jumps (void) return changed; } -/* Return true if the graph is too expensive to optimize. PASS is the - optimization about to be performed. */ - -static bool -is_too_expensive (const char *pass) -{ - /* Trying to perform global optimizations on flow graphs which have - a high connectivity will take a long time and is unlikely to be - particularly useful. - - In normal circumstances a cfg should have about twice as many - edges as blocks. But we do not want to punish small functions - which have a couple switch statements. Rather than simply - threshold the number of blocks, uses something with a more - graceful degradation. */ - if (n_edges_for_fn (cfun) > 20000 + n_basic_blocks_for_fn (cfun) * 4) - { - warning (OPT_Wdisabled_optimization, - "%s: %d basic blocks and %d edges/basic block", - pass, n_basic_blocks_for_fn (cfun), - n_edges_for_fn (cfun) / n_basic_blocks_for_fn (cfun)); - - return true; - } - - /* If allocating memory for the cprop bitmap would take up too much - storage it's better just to disable the optimization. */ - if ((n_basic_blocks_for_fn (cfun) - * SBITMAP_SET_SIZE (max_reg_num ()) - * sizeof (SBITMAP_ELT_TYPE)) > MAX_GCSE_MEMORY) - { - warning (OPT_Wdisabled_optimization, - "%s: %d basic blocks and %d registers", - pass, n_basic_blocks_for_fn (cfun), max_reg_num ()); - - return true; - } - - return false; -} - /* Main function for the CPROP pass. */ static int @@ -1790,7 +1752,7 @@ one_cprop_pass (void) /* Return if there's nothing to do, or it is too expensive. */ if (n_basic_blocks_for_fn (cfun) <= NUM_FIXED_BLOCKS + 1 - || is_too_expensive (_ ("const/copy propagation disabled"))) + || gcse_or_cprop_is_too_expensive (_ ("const/copy propagation disabled"))) return 0; global_const_prop_count = local_const_prop_count = 0; @@ -1850,7 +1812,7 @@ one_cprop_pass (void) if (set_hash_table.n_elems > 0) { basic_block bb; - rtx_insn *insn; + auto_vec uncond_traps; alloc_cprop_mem (last_basic_block_for_fn (cfun), set_hash_table.n_elems); @@ -1866,6 +1828,9 @@ one_cprop_pass (void) EXIT_BLOCK_PTR_FOR_FN (cfun), next_bb) { + bool seen_uncond_trap = false; + rtx_insn *insn; + /* Reset tables used to keep track of what's still valid [since the start of the block]. */ reset_opr_set_tables (); @@ -1873,6 +1838,10 @@ one_cprop_pass (void) FOR_BB_INSNS (bb, insn) if (INSN_P (insn)) { + bool was_uncond_trap + = (GET_CODE (PATTERN (insn)) == TRAP_IF + && XEXP (PATTERN (insn), 0) == const1_rtx); + changed |= cprop_insn (insn); /* Keep track of everything modified by this insn. */ @@ -1881,9 +1850,40 @@ one_cprop_pass (void) insn into a NOTE, or deleted the insn. */ if (! NOTE_P (insn) && ! insn->deleted ()) mark_oprs_set (insn); + + if (!was_uncond_trap + && GET_CODE (PATTERN (insn)) == TRAP_IF + && XEXP (PATTERN (insn), 0) == const1_rtx) + { + /* If we have already seen an unconditional trap + earlier, the rest of the bb is going to be removed + as unreachable. Just turn it into a note, so that + RTL verification doesn't complain about it before + it is finally removed. */ + if (seen_uncond_trap) + set_insn_deleted (insn); + else + { + seen_uncond_trap = true; + uncond_traps.safe_push (insn); + } + } } } + /* Make sure bypass_conditional_jumps will ignore not just its new + basic blocks, but also the ones after unconditional traps (those are + unreachable and will be eventually removed as such). */ + bypass_last_basic_block = last_basic_block_for_fn (cfun); + + while (!uncond_traps.is_empty ()) + { + rtx_insn *insn = uncond_traps.pop (); + basic_block to_split = BLOCK_FOR_INSN (insn); + remove_edge (split_block (to_split, insn)); + emit_barrier_after_bb (to_split); + } + changed |= bypass_conditional_jumps (); FREE_REG_SET (reg_set_bitmap);