From a502f7b6e47d3f790e01e91ab5c480ee4b261486 Mon Sep 17 00:00:00 2001 From: Jeff Law Date: Fri, 18 Sep 2015 09:29:01 -0600 Subject: [PATCH] [PATCH] Break out phi-only cprop into its own file PR tree-optimization/47679 * Makefile.in (OBJS): Add tree-ssa-phionlycprop.o * tree-ssa-dom.c: Remove unnecessary header includes. (remove_stmt_or_phi): Moved from here into tree-ssa-phionlycprop.c (get_rhs_or_phi_arg, get_lhs_or_phi_result): Likewise. (propagate_rhs_into_lhs, eliminate_const_or_copy): Likewise. (eliminate_degenerate_phis_1, pass_phi_only_cprop): Likewise. (pass_phi_only_cprop::execute): Likewise. (make_pass_phi_only_cprop): Likewise. * tree-ssa-phionlycprop.c: New file with moved code. Eliminate uses of file scoped statics by passing the required objects as parameters wherever needed. From-SVN: r227908 --- gcc/ChangeLog | 15 + gcc/Makefile.in | 1 + gcc/tree-ssa-dom.c | 528 -------------------------------- gcc/tree-ssa-phionlycprop.c | 589 ++++++++++++++++++++++++++++++++++++ 4 files changed, 605 insertions(+), 528 deletions(-) create mode 100644 gcc/tree-ssa-phionlycprop.c diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 809f6bf2e2e..469abad356f 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,18 @@ +2015-09-16 Jeff Law + + PR tree-optimization/47679 + * Makefile.in (OBJS): Add tree-ssa-phionlycprop.o + * tree-ssa-dom.c: Remove unnecessary header includes. + (remove_stmt_or_phi): Moved from here into tree-ssa-phionlycprop.c + (get_rhs_or_phi_arg, get_lhs_or_phi_result): Likewise. + (propagate_rhs_into_lhs, eliminate_const_or_copy): Likewise. + (eliminate_degenerate_phis_1, pass_phi_only_cprop): Likewise. + (pass_phi_only_cprop::execute): Likewise. + (make_pass_phi_only_cprop): Likewise. + * tree-ssa-phionlycprop.c: New file with moved code. Eliminate + uses of file scoped statics by passing the required objects + as parameters wherever needed. + 2015-09-18 Andrew Dixie David Edelsohn diff --git a/gcc/Makefile.in b/gcc/Makefile.in index c2df21db7b3..440c4515aec 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -1465,6 +1465,7 @@ OBJS = \ tree-ssa-loop.o \ tree-ssa-math-opts.o \ tree-ssa-operands.o \ + tree-ssa-phionlycprop.o \ tree-ssa-phiopt.o \ tree-ssa-phiprop.o \ tree-ssa-pre.o \ diff --git a/gcc/tree-ssa-dom.c b/gcc/tree-ssa-dom.c index 1b44bd111af..963dea9b711 100644 --- a/gcc/tree-ssa-dom.c +++ b/gcc/tree-ssa-dom.c @@ -22,20 +22,13 @@ along with GCC; see the file COPYING3. If not see #include "system.h" #include "coretypes.h" #include "backend.h" -#include "cfghooks.h" #include "tree.h" #include "gimple.h" -#include "hard-reg-set.h" #include "ssa.h" -#include "alias.h" #include "fold-const.h" -#include "stor-layout.h" -#include "flags.h" -#include "tm_p.h" #include "cfganal.h" #include "cfgloop.h" #include "gimple-pretty-print.h" -#include "internal-fn.h" #include "gimple-fold.h" #include "tree-eh.h" #include "gimple-iterator.h" @@ -45,7 +38,6 @@ along with GCC; see the file COPYING3. If not see #include "tree-pass.h" #include "tree-ssa-propagate.h" #include "tree-ssa-threadupdate.h" -#include "langhooks.h" #include "params.h" #include "tree-ssa-scopedtables.h" #include "tree-ssa-threadedge.h" @@ -1986,523 +1978,3 @@ lookup_avail_expr (gimple stmt, bool insert) return lhs; } - -/* PHI-ONLY copy and constant propagation. This pass is meant to clean - up degenerate PHIs created by or exposed by jump threading. */ - -/* Given a statement STMT, which is either a PHI node or an assignment, - remove it from the IL. */ - -static void -remove_stmt_or_phi (gimple stmt) -{ - gimple_stmt_iterator gsi = gsi_for_stmt (stmt); - - if (gimple_code (stmt) == GIMPLE_PHI) - remove_phi_node (&gsi, true); - else - { - gsi_remove (&gsi, true); - release_defs (stmt); - } -} - -/* Given a statement STMT, which is either a PHI node or an assignment, - return the "rhs" of the node, in the case of a non-degenerate - phi, NULL is returned. */ - -static tree -get_rhs_or_phi_arg (gimple stmt) -{ - if (gimple_code (stmt) == GIMPLE_PHI) - return degenerate_phi_result (as_a (stmt)); - else if (gimple_assign_single_p (stmt)) - return gimple_assign_rhs1 (stmt); - else - gcc_unreachable (); -} - - -/* Given a statement STMT, which is either a PHI node or an assignment, - return the "lhs" of the node. */ - -static tree -get_lhs_or_phi_result (gimple stmt) -{ - if (gimple_code (stmt) == GIMPLE_PHI) - return gimple_phi_result (stmt); - else if (is_gimple_assign (stmt)) - return gimple_assign_lhs (stmt); - else - gcc_unreachable (); -} - -/* Propagate RHS into all uses of LHS (when possible). - - RHS and LHS are derived from STMT, which is passed in solely so - that we can remove it if propagation is successful. - - When propagating into a PHI node or into a statement which turns - into a trivial copy or constant initialization, set the - appropriate bit in INTERESTING_NAMEs so that we will visit those - nodes as well in an effort to pick up secondary optimization - opportunities. */ - -static void -propagate_rhs_into_lhs (gimple stmt, tree lhs, tree rhs, bitmap interesting_names) -{ - /* First verify that propagation is valid. */ - if (may_propagate_copy (lhs, rhs)) - { - use_operand_p use_p; - imm_use_iterator iter; - gimple use_stmt; - bool all = true; - - /* Dump details. */ - if (dump_file && (dump_flags & TDF_DETAILS)) - { - fprintf (dump_file, " Replacing '"); - print_generic_expr (dump_file, lhs, dump_flags); - fprintf (dump_file, "' with %s '", - (TREE_CODE (rhs) != SSA_NAME ? "constant" : "variable")); - print_generic_expr (dump_file, rhs, dump_flags); - fprintf (dump_file, "'\n"); - } - - /* Walk over every use of LHS and try to replace the use with RHS. - At this point the only reason why such a propagation would not - be successful would be if the use occurs in an ASM_EXPR. */ - FOR_EACH_IMM_USE_STMT (use_stmt, iter, lhs) - { - /* Leave debug stmts alone. If we succeed in propagating - all non-debug uses, we'll drop the DEF, and propagation - into debug stmts will occur then. */ - if (gimple_debug_bind_p (use_stmt)) - continue; - - /* It's not always safe to propagate into an ASM_EXPR. */ - if (gimple_code (use_stmt) == GIMPLE_ASM - && ! may_propagate_copy_into_asm (lhs)) - { - all = false; - continue; - } - - /* It's not ok to propagate into the definition stmt of RHS. - : - # prephitmp.12_36 = PHI - g_67.1_6 = prephitmp.12_36; - goto ; - While this is strictly all dead code we do not want to - deal with this here. */ - if (TREE_CODE (rhs) == SSA_NAME - && SSA_NAME_DEF_STMT (rhs) == use_stmt) - { - all = false; - continue; - } - - /* Dump details. */ - if (dump_file && (dump_flags & TDF_DETAILS)) - { - fprintf (dump_file, " Original statement:"); - print_gimple_stmt (dump_file, use_stmt, 0, dump_flags); - } - - /* Propagate the RHS into this use of the LHS. */ - FOR_EACH_IMM_USE_ON_STMT (use_p, iter) - propagate_value (use_p, rhs); - - /* Special cases to avoid useless calls into the folding - routines, operand scanning, etc. - - Propagation into a PHI may cause the PHI to become - a degenerate, so mark the PHI as interesting. No other - actions are necessary. */ - if (gimple_code (use_stmt) == GIMPLE_PHI) - { - tree result; - - /* Dump details. */ - if (dump_file && (dump_flags & TDF_DETAILS)) - { - fprintf (dump_file, " Updated statement:"); - print_gimple_stmt (dump_file, use_stmt, 0, dump_flags); - } - - result = get_lhs_or_phi_result (use_stmt); - bitmap_set_bit (interesting_names, SSA_NAME_VERSION (result)); - continue; - } - - /* From this point onward we are propagating into a - real statement. Folding may (or may not) be possible, - we may expose new operands, expose dead EH edges, - etc. */ - /* NOTE tuples. In the tuples world, fold_stmt_inplace - cannot fold a call that simplifies to a constant, - because the GIMPLE_CALL must be replaced by a - GIMPLE_ASSIGN, and there is no way to effect such a - transformation in-place. We might want to consider - using the more general fold_stmt here. */ - { - gimple_stmt_iterator gsi = gsi_for_stmt (use_stmt); - fold_stmt_inplace (&gsi); - } - - /* Sometimes propagation can expose new operands to the - renamer. */ - update_stmt (use_stmt); - - /* Dump details. */ - if (dump_file && (dump_flags & TDF_DETAILS)) - { - fprintf (dump_file, " Updated statement:"); - print_gimple_stmt (dump_file, use_stmt, 0, dump_flags); - } - - /* If we replaced a variable index with a constant, then - we would need to update the invariant flag for ADDR_EXPRs. */ - if (gimple_assign_single_p (use_stmt) - && TREE_CODE (gimple_assign_rhs1 (use_stmt)) == ADDR_EXPR) - recompute_tree_invariant_for_addr_expr - (gimple_assign_rhs1 (use_stmt)); - - /* If we cleaned up EH information from the statement, - mark its containing block as needing EH cleanups. */ - if (maybe_clean_or_replace_eh_stmt (use_stmt, use_stmt)) - { - bitmap_set_bit (need_eh_cleanup, gimple_bb (use_stmt)->index); - if (dump_file && (dump_flags & TDF_DETAILS)) - fprintf (dump_file, " Flagged to clear EH edges.\n"); - } - - /* Propagation may expose new trivial copy/constant propagation - opportunities. */ - if (gimple_assign_single_p (use_stmt) - && TREE_CODE (gimple_assign_lhs (use_stmt)) == SSA_NAME - && (TREE_CODE (gimple_assign_rhs1 (use_stmt)) == SSA_NAME - || is_gimple_min_invariant (gimple_assign_rhs1 (use_stmt)))) - { - tree result = get_lhs_or_phi_result (use_stmt); - bitmap_set_bit (interesting_names, SSA_NAME_VERSION (result)); - } - - /* Propagation into these nodes may make certain edges in - the CFG unexecutable. We want to identify them as PHI nodes - at the destination of those unexecutable edges may become - degenerates. */ - else if (gimple_code (use_stmt) == GIMPLE_COND - || gimple_code (use_stmt) == GIMPLE_SWITCH - || gimple_code (use_stmt) == GIMPLE_GOTO) - { - tree val; - - if (gimple_code (use_stmt) == GIMPLE_COND) - val = fold_binary_loc (gimple_location (use_stmt), - gimple_cond_code (use_stmt), - boolean_type_node, - gimple_cond_lhs (use_stmt), - gimple_cond_rhs (use_stmt)); - else if (gimple_code (use_stmt) == GIMPLE_SWITCH) - val = gimple_switch_index (as_a (use_stmt)); - else - val = gimple_goto_dest (use_stmt); - - if (val && is_gimple_min_invariant (val)) - { - basic_block bb = gimple_bb (use_stmt); - edge te = find_taken_edge (bb, val); - if (!te) - continue; - - edge_iterator ei; - edge e; - gimple_stmt_iterator gsi; - gphi_iterator psi; - - /* Remove all outgoing edges except TE. */ - for (ei = ei_start (bb->succs); (e = ei_safe_edge (ei));) - { - if (e != te) - { - /* Mark all the PHI nodes at the destination of - the unexecutable edge as interesting. */ - for (psi = gsi_start_phis (e->dest); - !gsi_end_p (psi); - gsi_next (&psi)) - { - gphi *phi = psi.phi (); - - tree result = gimple_phi_result (phi); - int version = SSA_NAME_VERSION (result); - - bitmap_set_bit (interesting_names, version); - } - - te->probability += e->probability; - - te->count += e->count; - remove_edge (e); - cfg_altered = true; - } - else - ei_next (&ei); - } - - gsi = gsi_last_bb (gimple_bb (use_stmt)); - gsi_remove (&gsi, true); - - /* And fixup the flags on the single remaining edge. */ - te->flags &= ~(EDGE_TRUE_VALUE | EDGE_FALSE_VALUE); - te->flags &= ~EDGE_ABNORMAL; - te->flags |= EDGE_FALLTHRU; - if (te->probability > REG_BR_PROB_BASE) - te->probability = REG_BR_PROB_BASE; - } - } - } - - /* Ensure there is nothing else to do. */ - gcc_assert (!all || has_zero_uses (lhs)); - - /* If we were able to propagate away all uses of LHS, then - we can remove STMT. */ - if (all) - remove_stmt_or_phi (stmt); - } -} - -/* STMT is either a PHI node (potentially a degenerate PHI node) or - a statement that is a trivial copy or constant initialization. - - Attempt to eliminate T by propagating its RHS into all uses of - its LHS. This may in turn set new bits in INTERESTING_NAMES - for nodes we want to revisit later. - - All exit paths should clear INTERESTING_NAMES for the result - of STMT. */ - -static void -eliminate_const_or_copy (gimple stmt, bitmap interesting_names) -{ - tree lhs = get_lhs_or_phi_result (stmt); - tree rhs; - int version = SSA_NAME_VERSION (lhs); - - /* If the LHS of this statement or PHI has no uses, then we can - just eliminate it. This can occur if, for example, the PHI - was created by block duplication due to threading and its only - use was in the conditional at the end of the block which was - deleted. */ - if (has_zero_uses (lhs)) - { - bitmap_clear_bit (interesting_names, version); - remove_stmt_or_phi (stmt); - return; - } - - /* Get the RHS of the assignment or PHI node if the PHI is a - degenerate. */ - rhs = get_rhs_or_phi_arg (stmt); - if (!rhs) - { - bitmap_clear_bit (interesting_names, version); - return; - } - - if (!virtual_operand_p (lhs)) - propagate_rhs_into_lhs (stmt, lhs, rhs, interesting_names); - else - { - gimple use_stmt; - imm_use_iterator iter; - use_operand_p use_p; - /* For virtual operands we have to propagate into all uses as - otherwise we will create overlapping life-ranges. */ - FOR_EACH_IMM_USE_STMT (use_stmt, iter, lhs) - FOR_EACH_IMM_USE_ON_STMT (use_p, iter) - SET_USE (use_p, rhs); - if (SSA_NAME_OCCURS_IN_ABNORMAL_PHI (lhs)) - SSA_NAME_OCCURS_IN_ABNORMAL_PHI (rhs) = 1; - remove_stmt_or_phi (stmt); - } - - /* Note that STMT may well have been deleted by now, so do - not access it, instead use the saved version # to clear - T's entry in the worklist. */ - bitmap_clear_bit (interesting_names, version); -} - -/* The first phase in degenerate PHI elimination. - - Eliminate the degenerate PHIs in BB, then recurse on the - dominator children of BB. */ - -static void -eliminate_degenerate_phis_1 (basic_block bb, bitmap interesting_names) -{ - gphi_iterator gsi; - basic_block son; - - for (gsi = gsi_start_phis (bb); !gsi_end_p (gsi); gsi_next (&gsi)) - { - gphi *phi = gsi.phi (); - - eliminate_const_or_copy (phi, interesting_names); - } - - /* Recurse into the dominator children of BB. */ - for (son = first_dom_son (CDI_DOMINATORS, bb); - son; - son = next_dom_son (CDI_DOMINATORS, son)) - eliminate_degenerate_phis_1 (son, interesting_names); -} - - -/* A very simple pass to eliminate degenerate PHI nodes from the - IL. This is meant to be fast enough to be able to be run several - times in the optimization pipeline. - - Certain optimizations, particularly those which duplicate blocks - or remove edges from the CFG can create or expose PHIs which are - trivial copies or constant initializations. - - While we could pick up these optimizations in DOM or with the - combination of copy-prop and CCP, those solutions are far too - heavy-weight for our needs. - - This implementation has two phases so that we can efficiently - eliminate the first order degenerate PHIs and second order - degenerate PHIs. - - The first phase performs a dominator walk to identify and eliminate - the vast majority of the degenerate PHIs. When a degenerate PHI - is identified and eliminated any affected statements or PHIs - are put on a worklist. - - The second phase eliminates degenerate PHIs and trivial copies - or constant initializations using the worklist. This is how we - pick up the secondary optimization opportunities with minimal - cost. */ - -namespace { - -const pass_data pass_data_phi_only_cprop = -{ - GIMPLE_PASS, /* type */ - "phicprop", /* name */ - OPTGROUP_NONE, /* optinfo_flags */ - TV_TREE_PHI_CPROP, /* tv_id */ - ( PROP_cfg | PROP_ssa ), /* properties_required */ - 0, /* properties_provided */ - 0, /* properties_destroyed */ - 0, /* todo_flags_start */ - ( TODO_cleanup_cfg | TODO_update_ssa ), /* todo_flags_finish */ -}; - -class pass_phi_only_cprop : public gimple_opt_pass -{ -public: - pass_phi_only_cprop (gcc::context *ctxt) - : gimple_opt_pass (pass_data_phi_only_cprop, ctxt) - {} - - /* opt_pass methods: */ - opt_pass * clone () { return new pass_phi_only_cprop (m_ctxt); } - virtual bool gate (function *) { return flag_tree_dom != 0; } - virtual unsigned int execute (function *); - -}; // class pass_phi_only_cprop - -unsigned int -pass_phi_only_cprop::execute (function *fun) -{ - bitmap interesting_names; - bitmap interesting_names1; - - /* Bitmap of blocks which need EH information updated. We can not - update it on-the-fly as doing so invalidates the dominator tree. */ - need_eh_cleanup = BITMAP_ALLOC (NULL); - - /* INTERESTING_NAMES is effectively our worklist, indexed by - SSA_NAME_VERSION. - - A set bit indicates that the statement or PHI node which - defines the SSA_NAME should be (re)examined to determine if - it has become a degenerate PHI or trivial const/copy propagation - opportunity. - - Experiments have show we generally get better compilation - time behavior with bitmaps rather than sbitmaps. */ - interesting_names = BITMAP_ALLOC (NULL); - interesting_names1 = BITMAP_ALLOC (NULL); - - calculate_dominance_info (CDI_DOMINATORS); - cfg_altered = false; - - /* First phase. Eliminate degenerate PHIs via a dominator - walk of the CFG. - - Experiments have indicated that we generally get better - compile-time behavior by visiting blocks in the first - phase in dominator order. Presumably this is because walking - in dominator order leaves fewer PHIs for later examination - by the worklist phase. */ - eliminate_degenerate_phis_1 (ENTRY_BLOCK_PTR_FOR_FN (fun), - interesting_names); - - /* Second phase. Eliminate second order degenerate PHIs as well - as trivial copies or constant initializations identified by - the first phase or this phase. Basically we keep iterating - until our set of INTERESTING_NAMEs is empty. */ - while (!bitmap_empty_p (interesting_names)) - { - unsigned int i; - bitmap_iterator bi; - - /* EXECUTE_IF_SET_IN_BITMAP does not like its bitmap - changed during the loop. Copy it to another bitmap and - use that. */ - bitmap_copy (interesting_names1, interesting_names); - - EXECUTE_IF_SET_IN_BITMAP (interesting_names1, 0, i, bi) - { - tree name = ssa_name (i); - - /* Ignore SSA_NAMEs that have been released because - their defining statement was deleted (unreachable). */ - if (name) - eliminate_const_or_copy (SSA_NAME_DEF_STMT (ssa_name (i)), - interesting_names); - } - } - - if (cfg_altered) - { - free_dominance_info (CDI_DOMINATORS); - /* If we changed the CFG schedule loops for fixup by cfgcleanup. */ - loops_state_set (LOOPS_NEED_FIXUP); - } - - /* Propagation of const and copies may make some EH edges dead. Purge - such edges from the CFG as needed. */ - if (!bitmap_empty_p (need_eh_cleanup)) - { - gimple_purge_all_dead_eh_edges (need_eh_cleanup); - BITMAP_FREE (need_eh_cleanup); - } - - BITMAP_FREE (interesting_names); - BITMAP_FREE (interesting_names1); - return 0; -} - -} // anon namespace - -gimple_opt_pass * -make_pass_phi_only_cprop (gcc::context *ctxt) -{ - return new pass_phi_only_cprop (ctxt); -} diff --git a/gcc/tree-ssa-phionlycprop.c b/gcc/tree-ssa-phionlycprop.c new file mode 100644 index 00000000000..2093273e57c --- /dev/null +++ b/gcc/tree-ssa-phionlycprop.c @@ -0,0 +1,589 @@ +/* Const/Copy propagation originating from degenerate PHIs + Copyright (C) 2001-2015 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 3, or (at your option) +any later version. + +GCC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or 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 COPYING3. If not see +. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "backend.h" +#include "cfghooks.h" +#include "tree.h" +#include "gimple.h" +#include "ssa.h" +#include "fold-const.h" +#include "cfgloop.h" +#include "gimple-pretty-print.h" +#include "gimple-fold.h" +#include "tree-eh.h" +#include "gimple-iterator.h" +#include "tree-cfg.h" +#include "tree-pass.h" +#include "tree-ssa-propagate.h" + + +/* PHI-ONLY copy and constant propagation. This pass is meant to clean + up degenerate PHIs created by or exposed by jump threading. */ + +/* Given a statement STMT, which is either a PHI node or an assignment, + remove it from the IL. */ + +static void +remove_stmt_or_phi (gimple stmt) +{ + gimple_stmt_iterator gsi = gsi_for_stmt (stmt); + + if (gimple_code (stmt) == GIMPLE_PHI) + remove_phi_node (&gsi, true); + else + { + gsi_remove (&gsi, true); + release_defs (stmt); + } +} + +/* Given a statement STMT, which is either a PHI node or an assignment, + return the "rhs" of the node, in the case of a non-degenerate + phi, NULL is returned. */ + +static tree +get_rhs_or_phi_arg (gimple stmt) +{ + if (gimple_code (stmt) == GIMPLE_PHI) + return degenerate_phi_result (as_a (stmt)); + else if (gimple_assign_single_p (stmt)) + return gimple_assign_rhs1 (stmt); + else + gcc_unreachable (); +} + + +/* Given a statement STMT, which is either a PHI node or an assignment, + return the "lhs" of the node. */ + +static tree +get_lhs_or_phi_result (gimple stmt) +{ + if (gimple_code (stmt) == GIMPLE_PHI) + return gimple_phi_result (stmt); + else if (is_gimple_assign (stmt)) + return gimple_assign_lhs (stmt); + else + gcc_unreachable (); +} + +/* Propagate RHS into all uses of LHS (when possible). + + RHS and LHS are derived from STMT, which is passed in solely so + that we can remove it if propagation is successful. + + When propagating into a PHI node or into a statement which turns + into a trivial copy or constant initialization, set the + appropriate bit in INTERESTING_NAMEs so that we will visit those + nodes as well in an effort to pick up secondary optimization + opportunities. + + NEED_EH_CLEANUP tracks blocks that need their EH information + cleaned up after changing EH information on a statement. */ + +static bool +propagate_rhs_into_lhs (gimple stmt, tree lhs, tree rhs, + bitmap interesting_names, bitmap need_eh_cleanup) +{ + bool cfg_altered = false; + + /* First verify that propagation is valid. */ + if (may_propagate_copy (lhs, rhs)) + { + use_operand_p use_p; + imm_use_iterator iter; + gimple use_stmt; + bool all = true; + + /* Dump details. */ + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, " Replacing '"); + print_generic_expr (dump_file, lhs, dump_flags); + fprintf (dump_file, "' with %s '", + (TREE_CODE (rhs) != SSA_NAME ? "constant" : "variable")); + print_generic_expr (dump_file, rhs, dump_flags); + fprintf (dump_file, "'\n"); + } + + /* Walk over every use of LHS and try to replace the use with RHS. + At this point the only reason why such a propagation would not + be successful would be if the use occurs in an ASM_EXPR. */ + FOR_EACH_IMM_USE_STMT (use_stmt, iter, lhs) + { + /* Leave debug stmts alone. If we succeed in propagating + all non-debug uses, we'll drop the DEF, and propagation + into debug stmts will occur then. */ + if (gimple_debug_bind_p (use_stmt)) + continue; + + /* It's not always safe to propagate into an ASM_EXPR. */ + if (gimple_code (use_stmt) == GIMPLE_ASM + && ! may_propagate_copy_into_asm (lhs)) + { + all = false; + continue; + } + + /* It's not ok to propagate into the definition stmt of RHS. + : + # prephitmp.12_36 = PHI + g_67.1_6 = prephitmp.12_36; + goto ; + While this is strictly all dead code we do not want to + deal with this here. */ + if (TREE_CODE (rhs) == SSA_NAME + && SSA_NAME_DEF_STMT (rhs) == use_stmt) + { + all = false; + continue; + } + + /* Dump details. */ + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, " Original statement:"); + print_gimple_stmt (dump_file, use_stmt, 0, dump_flags); + } + + /* Propagate the RHS into this use of the LHS. */ + FOR_EACH_IMM_USE_ON_STMT (use_p, iter) + propagate_value (use_p, rhs); + + /* Special cases to avoid useless calls into the folding + routines, operand scanning, etc. + + Propagation into a PHI may cause the PHI to become + a degenerate, so mark the PHI as interesting. No other + actions are necessary. */ + if (gimple_code (use_stmt) == GIMPLE_PHI) + { + tree result; + + /* Dump details. */ + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, " Updated statement:"); + print_gimple_stmt (dump_file, use_stmt, 0, dump_flags); + } + + result = get_lhs_or_phi_result (use_stmt); + bitmap_set_bit (interesting_names, SSA_NAME_VERSION (result)); + continue; + } + + /* From this point onward we are propagating into a + real statement. Folding may (or may not) be possible, + we may expose new operands, expose dead EH edges, + etc. */ + /* NOTE tuples. In the tuples world, fold_stmt_inplace + cannot fold a call that simplifies to a constant, + because the GIMPLE_CALL must be replaced by a + GIMPLE_ASSIGN, and there is no way to effect such a + transformation in-place. We might want to consider + using the more general fold_stmt here. */ + { + gimple_stmt_iterator gsi = gsi_for_stmt (use_stmt); + fold_stmt_inplace (&gsi); + } + + /* Sometimes propagation can expose new operands to the + renamer. */ + update_stmt (use_stmt); + + /* Dump details. */ + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, " Updated statement:"); + print_gimple_stmt (dump_file, use_stmt, 0, dump_flags); + } + + /* If we replaced a variable index with a constant, then + we would need to update the invariant flag for ADDR_EXPRs. */ + if (gimple_assign_single_p (use_stmt) + && TREE_CODE (gimple_assign_rhs1 (use_stmt)) == ADDR_EXPR) + recompute_tree_invariant_for_addr_expr + (gimple_assign_rhs1 (use_stmt)); + + /* If we cleaned up EH information from the statement, + mark its containing block as needing EH cleanups. */ + if (maybe_clean_or_replace_eh_stmt (use_stmt, use_stmt)) + { + bitmap_set_bit (need_eh_cleanup, gimple_bb (use_stmt)->index); + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, " Flagged to clear EH edges.\n"); + } + + /* Propagation may expose new trivial copy/constant propagation + opportunities. */ + if (gimple_assign_single_p (use_stmt) + && TREE_CODE (gimple_assign_lhs (use_stmt)) == SSA_NAME + && (TREE_CODE (gimple_assign_rhs1 (use_stmt)) == SSA_NAME + || is_gimple_min_invariant (gimple_assign_rhs1 (use_stmt)))) + { + tree result = get_lhs_or_phi_result (use_stmt); + bitmap_set_bit (interesting_names, SSA_NAME_VERSION (result)); + } + + /* Propagation into these nodes may make certain edges in + the CFG unexecutable. We want to identify them as PHI nodes + at the destination of those unexecutable edges may become + degenerates. */ + else if (gimple_code (use_stmt) == GIMPLE_COND + || gimple_code (use_stmt) == GIMPLE_SWITCH + || gimple_code (use_stmt) == GIMPLE_GOTO) + { + tree val; + + if (gimple_code (use_stmt) == GIMPLE_COND) + val = fold_binary_loc (gimple_location (use_stmt), + gimple_cond_code (use_stmt), + boolean_type_node, + gimple_cond_lhs (use_stmt), + gimple_cond_rhs (use_stmt)); + else if (gimple_code (use_stmt) == GIMPLE_SWITCH) + val = gimple_switch_index (as_a (use_stmt)); + else + val = gimple_goto_dest (use_stmt); + + if (val && is_gimple_min_invariant (val)) + { + basic_block bb = gimple_bb (use_stmt); + edge te = find_taken_edge (bb, val); + if (!te) + continue; + + edge_iterator ei; + edge e; + gimple_stmt_iterator gsi; + gphi_iterator psi; + + /* Remove all outgoing edges except TE. */ + for (ei = ei_start (bb->succs); (e = ei_safe_edge (ei));) + { + if (e != te) + { + /* Mark all the PHI nodes at the destination of + the unexecutable edge as interesting. */ + for (psi = gsi_start_phis (e->dest); + !gsi_end_p (psi); + gsi_next (&psi)) + { + gphi *phi = psi.phi (); + + tree result = gimple_phi_result (phi); + int version = SSA_NAME_VERSION (result); + + bitmap_set_bit (interesting_names, version); + } + + te->probability += e->probability; + + te->count += e->count; + remove_edge (e); + cfg_altered = true; + } + else + ei_next (&ei); + } + + gsi = gsi_last_bb (gimple_bb (use_stmt)); + gsi_remove (&gsi, true); + + /* And fixup the flags on the single remaining edge. */ + te->flags &= ~(EDGE_TRUE_VALUE | EDGE_FALSE_VALUE); + te->flags &= ~EDGE_ABNORMAL; + te->flags |= EDGE_FALLTHRU; + if (te->probability > REG_BR_PROB_BASE) + te->probability = REG_BR_PROB_BASE; + } + } + } + + /* Ensure there is nothing else to do. */ + gcc_assert (!all || has_zero_uses (lhs)); + + /* If we were able to propagate away all uses of LHS, then + we can remove STMT. */ + if (all) + remove_stmt_or_phi (stmt); + } + return cfg_altered; +} + +/* STMT is either a PHI node (potentially a degenerate PHI node) or + a statement that is a trivial copy or constant initialization. + + Attempt to eliminate STMT by propagating its RHS into all uses of + its LHS. This may in turn set new bits in INTERESTING_NAMES + for nodes we want to revisit later. + + All exit paths should clear INTERESTING_NAMES for the result + of STMT. + + NEED_EH_CLEANUP tracks blocks that need their EH information + cleaned up after changing EH information on a statement. It is + not set or queried here, but passed along to children. */ + +static bool +eliminate_const_or_copy (gimple stmt, bitmap interesting_names, + bitmap need_eh_cleanup) +{ + tree lhs = get_lhs_or_phi_result (stmt); + tree rhs; + int version = SSA_NAME_VERSION (lhs); + bool cfg_altered = false; + + /* If the LHS of this statement or PHI has no uses, then we can + just eliminate it. This can occur if, for example, the PHI + was created by block duplication due to threading and its only + use was in the conditional at the end of the block which was + deleted. */ + if (has_zero_uses (lhs)) + { + bitmap_clear_bit (interesting_names, version); + remove_stmt_or_phi (stmt); + return cfg_altered; + } + + /* Get the RHS of the assignment or PHI node if the PHI is a + degenerate. */ + rhs = get_rhs_or_phi_arg (stmt); + if (!rhs) + { + bitmap_clear_bit (interesting_names, version); + return cfg_altered; + } + + if (!virtual_operand_p (lhs)) + cfg_altered = propagate_rhs_into_lhs (stmt, lhs, rhs, + interesting_names, need_eh_cleanup); + else + { + gimple use_stmt; + imm_use_iterator iter; + use_operand_p use_p; + /* For virtual operands we have to propagate into all uses as + otherwise we will create overlapping life-ranges. */ + FOR_EACH_IMM_USE_STMT (use_stmt, iter, lhs) + FOR_EACH_IMM_USE_ON_STMT (use_p, iter) + SET_USE (use_p, rhs); + if (SSA_NAME_OCCURS_IN_ABNORMAL_PHI (lhs)) + SSA_NAME_OCCURS_IN_ABNORMAL_PHI (rhs) = 1; + remove_stmt_or_phi (stmt); + } + + /* Note that STMT may well have been deleted by now, so do + not access it, instead use the saved version # to clear + T's entry in the worklist. */ + bitmap_clear_bit (interesting_names, version); + return cfg_altered; +} + +/* The first phase in degenerate PHI elimination. + + Eliminate the degenerate PHIs in BB, then recurse on the + dominator children of BB. + + INTERESTING_NAMES tracks SSA_NAMEs that we may want to revisit + in the future. It is not set or queried here, but passed along + to children. + + NEED_EH_CLEANUP tracks blocks that need their EH information + cleaned up after changing EH information on a statement. It is + not set or queried here, but passed along to children. */ + +static bool +eliminate_degenerate_phis_1 (basic_block bb, bitmap interesting_names, + bitmap need_eh_cleanup) +{ + gphi_iterator gsi; + basic_block son; + bool cfg_altered = false; + + for (gsi = gsi_start_phis (bb); !gsi_end_p (gsi); gsi_next (&gsi)) + { + gphi *phi = gsi.phi (); + + cfg_altered |= eliminate_const_or_copy (phi, interesting_names, + need_eh_cleanup); + } + + /* Recurse into the dominator children of BB. */ + for (son = first_dom_son (CDI_DOMINATORS, bb); + son; + son = next_dom_son (CDI_DOMINATORS, son)) + cfg_altered |= eliminate_degenerate_phis_1 (son, interesting_names, + need_eh_cleanup); + + return cfg_altered; +} + + +/* A very simple pass to eliminate degenerate PHI nodes from the + IL. This is meant to be fast enough to be able to be run several + times in the optimization pipeline. + + Certain optimizations, particularly those which duplicate blocks + or remove edges from the CFG can create or expose PHIs which are + trivial copies or constant initializations. + + While we could pick up these optimizations in DOM or with the + combination of copy-prop and CCP, those solutions are far too + heavy-weight for our needs. + + This implementation has two phases so that we can efficiently + eliminate the first order degenerate PHIs and second order + degenerate PHIs. + + The first phase performs a dominator walk to identify and eliminate + the vast majority of the degenerate PHIs. When a degenerate PHI + is identified and eliminated any affected statements or PHIs + are put on a worklist. + + The second phase eliminates degenerate PHIs and trivial copies + or constant initializations using the worklist. This is how we + pick up the secondary optimization opportunities with minimal + cost. */ + +namespace { + +const pass_data pass_data_phi_only_cprop = +{ + GIMPLE_PASS, /* type */ + "phicprop", /* name */ + OPTGROUP_NONE, /* optinfo_flags */ + TV_TREE_PHI_CPROP, /* tv_id */ + ( PROP_cfg | PROP_ssa ), /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + ( TODO_cleanup_cfg | TODO_update_ssa ), /* todo_flags_finish */ +}; + +class pass_phi_only_cprop : public gimple_opt_pass +{ +public: + pass_phi_only_cprop (gcc::context *ctxt) + : gimple_opt_pass (pass_data_phi_only_cprop, ctxt) + {} + + /* opt_pass methods: */ + opt_pass * clone () { return new pass_phi_only_cprop (m_ctxt); } + virtual bool gate (function *) { return flag_tree_dom != 0; } + virtual unsigned int execute (function *); + +}; // class pass_phi_only_cprop + +unsigned int +pass_phi_only_cprop::execute (function *fun) +{ + bitmap interesting_names; + bitmap interesting_names1; + bool cfg_altered = false; + + /* Bitmap of blocks which need EH information updated. We can not + update it on-the-fly as doing so invalidates the dominator tree. */ + bitmap need_eh_cleanup = BITMAP_ALLOC (NULL); + + /* INTERESTING_NAMES is effectively our worklist, indexed by + SSA_NAME_VERSION. + + A set bit indicates that the statement or PHI node which + defines the SSA_NAME should be (re)examined to determine if + it has become a degenerate PHI or trivial const/copy propagation + opportunity. + + Experiments have show we generally get better compilation + time behavior with bitmaps rather than sbitmaps. */ + interesting_names = BITMAP_ALLOC (NULL); + interesting_names1 = BITMAP_ALLOC (NULL); + + calculate_dominance_info (CDI_DOMINATORS); + cfg_altered = false; + + /* First phase. Eliminate degenerate PHIs via a dominator + walk of the CFG. + + Experiments have indicated that we generally get better + compile-time behavior by visiting blocks in the first + phase in dominator order. Presumably this is because walking + in dominator order leaves fewer PHIs for later examination + by the worklist phase. */ + cfg_altered = eliminate_degenerate_phis_1 (ENTRY_BLOCK_PTR_FOR_FN (fun), + interesting_names, + need_eh_cleanup); + + /* Second phase. Eliminate second order degenerate PHIs as well + as trivial copies or constant initializations identified by + the first phase or this phase. Basically we keep iterating + until our set of INTERESTING_NAMEs is empty. */ + while (!bitmap_empty_p (interesting_names)) + { + unsigned int i; + bitmap_iterator bi; + + /* EXECUTE_IF_SET_IN_BITMAP does not like its bitmap + changed during the loop. Copy it to another bitmap and + use that. */ + bitmap_copy (interesting_names1, interesting_names); + + EXECUTE_IF_SET_IN_BITMAP (interesting_names1, 0, i, bi) + { + tree name = ssa_name (i); + + /* Ignore SSA_NAMEs that have been released because + their defining statement was deleted (unreachable). */ + if (name) + cfg_altered + |= eliminate_const_or_copy (SSA_NAME_DEF_STMT (ssa_name (i)), + interesting_names, need_eh_cleanup); + } + } + + if (cfg_altered) + { + free_dominance_info (CDI_DOMINATORS); + /* If we changed the CFG schedule loops for fixup by cfgcleanup. */ + loops_state_set (LOOPS_NEED_FIXUP); + } + + /* Propagation of const and copies may make some EH edges dead. Purge + such edges from the CFG as needed. */ + if (!bitmap_empty_p (need_eh_cleanup)) + { + gimple_purge_all_dead_eh_edges (need_eh_cleanup); + BITMAP_FREE (need_eh_cleanup); + } + + BITMAP_FREE (interesting_names); + BITMAP_FREE (interesting_names1); + return 0; +} + +} // anon namespace + +gimple_opt_pass * +make_pass_phi_only_cprop (gcc::context *ctxt) +{ + return new pass_phi_only_cprop (ctxt); +} -- 2.30.2