| /* Const/Copy propagation originating from degenerate PHIs |
| Copyright (C) 2001-2018 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 |
| <http://www.gnu.org/licenses/>. */ |
| |
| #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 <gphi *> (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. |
| <bb 9>: |
| # prephitmp.12_36 = PHI <g_67.1_6(9)> |
| g_67.1_6 = prephitmp.12_36; |
| goto <bb 9>; |
| 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 <gswitch *> (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; |
| |
| 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; |
| } |
| } |
| } |
| |
| /* 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);) |
| { |
| gphi *phi = gsi.phi (); |
| /* We might end up removing PHI so advance the iterator now. */ |
| gsi_next (&gsi); |
| 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) |
| { |
| 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. */ |
| auto_bitmap need_eh_cleanup; |
| |
| /* 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. */ |
| auto_bitmap interesting_names; |
| auto_bitmap interesting_names1; |
| |
| 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); |
| |
| return 0; |
| } |
| |
| } // anon namespace |
| |
| gimple_opt_pass * |
| make_pass_phi_only_cprop (gcc::context *ctxt) |
| { |
| return new pass_phi_only_cprop (ctxt); |
| } |