| /* Support routines for Splitting Paths to loop backedges |
| Copyright (C) 2015-2022 Free Software Foundation, Inc. |
| Contributed by Ajit Kumar Agarwal <ajitkum@xilinx.com>. |
| |
| 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 "tree.h" |
| #include "gimple.h" |
| #include "tree-pass.h" |
| #include "tree-cfg.h" |
| #include "cfganal.h" |
| #include "cfgloop.h" |
| #include "gimple-iterator.h" |
| #include "tracer.h" |
| #include "predict.h" |
| #include "gimple-ssa.h" |
| #include "tree-phinodes.h" |
| #include "ssa-iterators.h" |
| #include "fold-const.h" |
| |
| /* Given LATCH, the latch block in a loop, see if the shape of the |
| path reaching LATCH is suitable for being split by duplication. |
| If so, return the block that will be duplicated into its predecessor |
| paths. Else return NULL. */ |
| |
| static basic_block |
| find_block_to_duplicate_for_splitting_paths (basic_block latch) |
| { |
| /* We should have simple latches at this point. So the latch should |
| have a single successor. This implies the predecessor of the latch |
| likely has the loop exit. And it's that predecessor we're most |
| interested in. To keep things simple, we're going to require that |
| the latch have a single predecessor too. */ |
| if (single_succ_p (latch) && single_pred_p (latch)) |
| { |
| basic_block bb = get_immediate_dominator (CDI_DOMINATORS, latch); |
| gcc_assert (single_pred_edge (latch)->src == bb); |
| |
| /* If BB has been marked as not to be duplicated, then honor that |
| request. */ |
| if (ignore_bb_p (bb)) |
| return NULL; |
| |
| gimple *last = gsi_stmt (gsi_last_nondebug_bb (bb)); |
| /* The immediate dominator of the latch must end in a conditional. */ |
| if (!last || gimple_code (last) != GIMPLE_COND) |
| return NULL; |
| |
| /* We're hoping that BB is a join point for an IF-THEN-ELSE diamond |
| region. Verify that it is. |
| |
| First, verify that BB has two predecessors (each arm of the |
| IF-THEN-ELSE) and two successors (the latch and exit) and that |
| all edges are normal. */ |
| if (EDGE_COUNT (bb->preds) == 2 |
| && !(EDGE_PRED (bb, 0)->flags & EDGE_COMPLEX) |
| && !(EDGE_PRED (bb, 1)->flags & EDGE_COMPLEX) |
| && EDGE_COUNT (bb->succs) == 2 |
| && !(EDGE_SUCC (bb, 0)->flags & EDGE_COMPLEX) |
| && !(EDGE_SUCC (bb, 1)->flags & EDGE_COMPLEX)) |
| { |
| /* Now verify that BB's immediate dominator ends in a |
| conditional as well. */ |
| basic_block bb_idom = get_immediate_dominator (CDI_DOMINATORS, bb); |
| gimple *last = gsi_stmt (gsi_last_nondebug_bb (bb_idom)); |
| if (!last || gimple_code (last) != GIMPLE_COND) |
| return NULL; |
| |
| /* And that BB's immediate dominator's successors are the |
| predecessors of BB or BB itself. */ |
| if (!(EDGE_PRED (bb, 0)->src == bb_idom |
| || find_edge (bb_idom, EDGE_PRED (bb, 0)->src)) |
| || !(EDGE_PRED (bb, 1)->src == bb_idom |
| || find_edge (bb_idom, EDGE_PRED (bb, 1)->src))) |
| return NULL; |
| |
| /* And that the predecessors of BB each have a single successor |
| or are BB's immediate domiator itself. */ |
| if (!(EDGE_PRED (bb, 0)->src == bb_idom |
| || single_succ_p (EDGE_PRED (bb, 0)->src)) |
| || !(EDGE_PRED (bb, 1)->src == bb_idom |
| || single_succ_p (EDGE_PRED (bb, 1)->src))) |
| return NULL; |
| |
| /* So at this point we have a simple diamond for an IF-THEN-ELSE |
| construct starting at BB_IDOM, with a join point at BB. BB |
| pass control outside the loop or to the loop latch. |
| |
| We're going to want to create two duplicates of BB, one for |
| each successor of BB_IDOM. */ |
| return bb; |
| } |
| } |
| return NULL; |
| } |
| |
| /* Return the number of non-debug statements in a block. */ |
| static unsigned int |
| count_stmts_in_block (basic_block bb) |
| { |
| gimple_stmt_iterator gsi; |
| unsigned int num_stmts = 0; |
| |
| for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi)) |
| { |
| gimple *stmt = gsi_stmt (gsi); |
| if (!is_gimple_debug (stmt)) |
| num_stmts++; |
| } |
| return num_stmts; |
| } |
| |
| /* Return TRUE if CODE represents a tree code that is not likely to |
| be easily if-convertable because it likely expands into multiple |
| insns, FALSE otherwise. */ |
| static bool |
| poor_ifcvt_candidate_code (enum tree_code code) |
| { |
| return (code == MIN_EXPR |
| || code == MAX_EXPR |
| || code == ABS_EXPR |
| || code == COND_EXPR |
| || code == CALL_EXPR); |
| } |
| |
| /* Return TRUE if BB is a reasonable block to duplicate by examining |
| its size, false otherwise. BB will always be a loop latch block. |
| |
| Things to consider: |
| |
| We do not want to spoil if-conversion if at all possible. |
| |
| Most of the benefit seems to be from eliminating the unconditional |
| jump rather than CSE/DCE opportunities. So favor duplicating |
| small latches. A latch with just a conditional branch is ideal. |
| |
| CSE/DCE opportunties crop up when statements from the predecessors |
| feed statements in the latch and allow statements in the latch to |
| simplify. */ |
| |
| static bool |
| is_feasible_trace (basic_block bb) |
| { |
| basic_block pred1 = EDGE_PRED (bb, 0)->src; |
| basic_block pred2 = EDGE_PRED (bb, 1)->src; |
| int num_stmts_in_join = count_stmts_in_block (bb); |
| int num_stmts_in_pred1 |
| = EDGE_COUNT (pred1->succs) == 1 ? count_stmts_in_block (pred1) : 0; |
| int num_stmts_in_pred2 |
| = EDGE_COUNT (pred2->succs) == 1 ? count_stmts_in_block (pred2) : 0; |
| |
| /* This is meant to catch cases that are likely opportunities for |
| if-conversion. Essentially we look for the case where |
| BB's predecessors are both single statement blocks where |
| the output of that statement feed the same PHI in BB. */ |
| if (num_stmts_in_pred1 == 1 && num_stmts_in_pred2 == 1) |
| { |
| gimple *stmt1 = last_and_only_stmt (pred1); |
| gimple *stmt2 = last_and_only_stmt (pred2); |
| |
| if (stmt1 && stmt2 |
| && gimple_code (stmt1) == GIMPLE_ASSIGN |
| && gimple_code (stmt2) == GIMPLE_ASSIGN) |
| { |
| enum tree_code code1 = gimple_assign_rhs_code (stmt1); |
| enum tree_code code2 = gimple_assign_rhs_code (stmt2); |
| |
| if (!poor_ifcvt_candidate_code (code1) |
| && !poor_ifcvt_candidate_code (code2)) |
| { |
| tree lhs1 = gimple_assign_lhs (stmt1); |
| tree lhs2 = gimple_assign_lhs (stmt2); |
| gimple_stmt_iterator gsi; |
| for (gsi = gsi_start_phis (bb); !gsi_end_p (gsi); gsi_next (&gsi)) |
| { |
| gimple *phi = gsi_stmt (gsi); |
| if ((gimple_phi_arg_def (phi, 0) == lhs1 |
| && gimple_phi_arg_def (phi, 1) == lhs2) |
| || (gimple_phi_arg_def (phi, 1) == lhs1 |
| && gimple_phi_arg_def (phi, 0) == lhs2)) |
| { |
| if (dump_file && (dump_flags & TDF_DETAILS)) |
| fprintf (dump_file, |
| "Block %d appears to be a join point for " |
| "if-convertable diamond.\n", |
| bb->index); |
| return false; |
| } |
| } |
| } |
| } |
| } |
| |
| /* Canonicalize the form. */ |
| if (num_stmts_in_pred1 == 0 && num_stmts_in_pred2 == 1) |
| { |
| std::swap (pred1, pred2); |
| std::swap (num_stmts_in_pred1, num_stmts_in_pred2); |
| } |
| |
| /* Another variant. This one is half-diamond. */ |
| if (num_stmts_in_pred1 == 1 && num_stmts_in_pred2 == 0 |
| && dominated_by_p (CDI_DOMINATORS, pred1, pred2)) |
| { |
| gimple *stmt1 = last_and_only_stmt (pred1); |
| |
| /* The only statement in PRED1 must be an assignment that is |
| not a good candidate for if-conversion. This may need some |
| generalization. */ |
| if (stmt1 && gimple_code (stmt1) == GIMPLE_ASSIGN) |
| { |
| enum tree_code code1 = gimple_assign_rhs_code (stmt1); |
| |
| if (!poor_ifcvt_candidate_code (code1)) |
| { |
| tree lhs1 = gimple_assign_lhs (stmt1); |
| tree rhs1 = gimple_assign_rhs1 (stmt1); |
| |
| gimple_stmt_iterator gsi; |
| for (gsi = gsi_start_phis (bb); !gsi_end_p (gsi); gsi_next (&gsi)) |
| { |
| gimple *phi = gsi_stmt (gsi); |
| if ((gimple_phi_arg_def (phi, 0) == lhs1 |
| && gimple_phi_arg_def (phi, 1) == rhs1) |
| || (gimple_phi_arg_def (phi, 1) == lhs1 |
| && gimple_phi_arg_def (phi, 0) == rhs1)) |
| { |
| if (dump_file && (dump_flags & TDF_DETAILS)) |
| fprintf (dump_file, |
| "Block %d appears to be a join point for " |
| "if-convertable half-diamond.\n", |
| bb->index); |
| return false; |
| } |
| } |
| } |
| } |
| } |
| |
| /* Canonicalize the form. */ |
| if (single_pred_p (pred1) && single_pred (pred1) == pred2 |
| && num_stmts_in_pred1 == 0) |
| std::swap (pred1, pred2); |
| |
| /* This is meant to catch another kind of cases that are likely opportunities |
| for if-conversion. After canonicalizing, PRED2 must be an empty block and |
| PRED1 must be the only predecessor of PRED2. Moreover, PRED1 is supposed |
| to end with a cond_stmt which has the same args with the PHI in BB. */ |
| if (single_pred_p (pred2) && single_pred (pred2) == pred1 |
| && num_stmts_in_pred2 == 0) |
| { |
| gimple *cond_stmt = last_stmt (pred1); |
| if (cond_stmt && gimple_code (cond_stmt) == GIMPLE_COND) |
| { |
| tree lhs = gimple_cond_lhs (cond_stmt); |
| tree rhs = gimple_cond_rhs (cond_stmt); |
| |
| gimple_stmt_iterator gsi; |
| for (gsi = gsi_start_phis (bb); !gsi_end_p (gsi); gsi_next (&gsi)) |
| { |
| gimple *phi = gsi_stmt (gsi); |
| if ((operand_equal_p (gimple_phi_arg_def (phi, 0), lhs) |
| && operand_equal_p (gimple_phi_arg_def (phi, 1), rhs)) |
| || (operand_equal_p (gimple_phi_arg_def (phi, 0), rhs) |
| && (operand_equal_p (gimple_phi_arg_def (phi, 1), lhs)))) |
| { |
| if (dump_file && (dump_flags & TDF_DETAILS)) |
| fprintf (dump_file, |
| "Block %d appears to be optimized to a join " |
| "point for if-convertable half-diamond.\n", |
| bb->index); |
| return false; |
| } |
| } |
| } |
| } |
| |
| /* If the joiner has no PHIs with useful uses there is zero chance |
| of CSE/DCE/jump-threading possibilities exposed by duplicating it. */ |
| bool found_useful_phi = false; |
| for (gphi_iterator si = gsi_start_phis (bb); ! gsi_end_p (si); |
| gsi_next (&si)) |
| { |
| gphi *phi = si.phi (); |
| use_operand_p use_p; |
| imm_use_iterator iter; |
| FOR_EACH_IMM_USE_FAST (use_p, iter, gimple_phi_result (phi)) |
| { |
| gimple *stmt = USE_STMT (use_p); |
| if (is_gimple_debug (stmt)) |
| continue; |
| /* If there's a use in the joiner this might be a CSE/DCE |
| opportunity, but not if the use is in a conditional |
| which makes this a likely if-conversion candidate. */ |
| if (gimple_bb (stmt) == bb |
| && (!is_gimple_assign (stmt) |
| || (TREE_CODE_CLASS (gimple_assign_rhs_code (stmt)) |
| != tcc_comparison))) |
| { |
| found_useful_phi = true; |
| break; |
| } |
| /* If the use is on a loop header PHI and on one path the |
| value is unchanged this might expose a jump threading |
| opportunity. */ |
| if (gimple_code (stmt) == GIMPLE_PHI |
| && gimple_bb (stmt) == bb->loop_father->header |
| /* But for memory the PHI alone isn't good enough. */ |
| && ! virtual_operand_p (gimple_phi_result (stmt))) |
| { |
| bool found_unchanged_path = false; |
| for (unsigned i = 0; i < gimple_phi_num_args (phi); ++i) |
| if (gimple_phi_arg_def (phi, i) == gimple_phi_result (stmt)) |
| { |
| found_unchanged_path = true; |
| break; |
| } |
| /* If we found an unchanged path this can only be a threading |
| opportunity if we have uses of the loop header PHI result |
| in a stmt dominating the merge block. Otherwise the |
| splitting may prevent if-conversion. */ |
| if (found_unchanged_path) |
| { |
| use_operand_p use2_p; |
| imm_use_iterator iter2; |
| FOR_EACH_IMM_USE_FAST (use2_p, iter2, gimple_phi_result (stmt)) |
| { |
| gimple *use_stmt = USE_STMT (use2_p); |
| if (is_gimple_debug (use_stmt)) |
| continue; |
| basic_block use_bb = gimple_bb (use_stmt); |
| if (use_bb != bb |
| && dominated_by_p (CDI_DOMINATORS, bb, use_bb)) |
| { |
| if (gcond *cond = dyn_cast <gcond *> (use_stmt)) |
| if (gimple_cond_code (cond) == EQ_EXPR |
| || gimple_cond_code (cond) == NE_EXPR) |
| found_useful_phi = true; |
| break; |
| } |
| } |
| } |
| if (found_useful_phi) |
| break; |
| } |
| } |
| if (found_useful_phi) |
| break; |
| } |
| /* There is one exception namely a controlling condition we can propagate |
| an equivalence from to the joiner. */ |
| bool found_cprop_opportunity = false; |
| basic_block dom = get_immediate_dominator (CDI_DOMINATORS, bb); |
| gcond *cond = as_a <gcond *> (last_stmt (dom)); |
| if (gimple_cond_code (cond) == EQ_EXPR |
| || gimple_cond_code (cond) == NE_EXPR) |
| for (unsigned i = 0; i < 2; ++i) |
| { |
| tree op = gimple_op (cond, i); |
| if (TREE_CODE (op) == SSA_NAME) |
| { |
| use_operand_p use_p; |
| imm_use_iterator iter; |
| FOR_EACH_IMM_USE_FAST (use_p, iter, op) |
| { |
| if (is_gimple_debug (USE_STMT (use_p))) |
| continue; |
| if (gimple_bb (USE_STMT (use_p)) == bb) |
| { |
| found_cprop_opportunity = true; |
| break; |
| } |
| } |
| } |
| if (found_cprop_opportunity) |
| break; |
| } |
| |
| if (! found_useful_phi && ! found_cprop_opportunity) |
| { |
| if (dump_file && (dump_flags & TDF_DETAILS)) |
| fprintf (dump_file, |
| "Block %d is a join that does not expose CSE/DCE/jump-thread " |
| "opportunities when duplicated.\n", |
| bb->index); |
| return false; |
| } |
| |
| /* We may want something here which looks at dataflow and tries |
| to guess if duplication of BB is likely to result in simplification |
| of instructions in BB in either the original or the duplicate. */ |
| |
| /* Upper Hard limit on the number statements to copy. */ |
| if (num_stmts_in_join |
| >= param_max_jump_thread_duplication_stmts) |
| return false; |
| |
| return true; |
| } |
| |
| /* If the immediate dominator of the latch of the loop is |
| block with conditional branch, then the loop latch is |
| duplicated to its predecessors path preserving the SSA |
| semantics. |
| |
| CFG before transformation. |
| |
| 2 |
| | |
| | |
| +---->3 |
| | / \ |
| | / \ |
| | 4 5 |
| | \ / |
| | \ / |
| | 6 |
| | / \ |
| | / \ |
| | 8 7 |
| | | | |
| ---+ E |
| |
| |
| |
| Block 8 is the latch. We're going to make copies of block 6 (9 & 10) |
| and wire things up so they look like this: |
| |
| 2 |
| | |
| | |
| +---->3 |
| | / \ |
| | / \ |
| | 4 5 |
| | | | |
| | | | |
| | 9 10 |
| | |\ /| |
| | | \ / | |
| | | 7 | |
| | | | | |
| | | E | |
| | | | |
| | \ / |
| | \ / |
| +-----8 |
| |
| |
| Blocks 9 and 10 will get merged into blocks 4 & 5 respectively which |
| enables CSE, DCE and other optimizations to occur on a larger block |
| of code. */ |
| |
| static bool |
| split_paths () |
| { |
| bool changed = false; |
| |
| loop_optimizer_init (LOOPS_NORMAL | LOOPS_HAVE_RECORDED_EXITS); |
| initialize_original_copy_tables (); |
| calculate_dominance_info (CDI_DOMINATORS); |
| |
| for (auto loop : loops_list (cfun, LI_FROM_INNERMOST)) |
| { |
| /* Only split paths if we are optimizing this loop for speed. */ |
| if (!optimize_loop_for_speed_p (loop)) |
| continue; |
| |
| /* See if there is a block that we can duplicate to split the |
| path to the loop latch. */ |
| basic_block bb |
| = find_block_to_duplicate_for_splitting_paths (loop->latch); |
| |
| /* BB is the merge point for an IF-THEN-ELSE we want to transform. |
| |
| Essentially we want to create a duplicate of bb and redirect the |
| first predecessor of BB to the duplicate (leaving the second |
| predecessor as is. This will split the path leading to the latch |
| re-using BB to avoid useless copying. */ |
| if (bb && is_feasible_trace (bb)) |
| { |
| if (dump_file && (dump_flags & TDF_DETAILS)) |
| fprintf (dump_file, |
| "Duplicating join block %d into predecessor paths\n", |
| bb->index); |
| basic_block pred0 = EDGE_PRED (bb, 0)->src; |
| if (EDGE_COUNT (pred0->succs) != 1) |
| pred0 = EDGE_PRED (bb, 1)->src; |
| transform_duplicate (pred0, bb); |
| changed = true; |
| |
| /* If BB has an outgoing edge marked as IRREDUCIBLE, then |
| duplicating BB may result in an irreducible region turning |
| into a natural loop. |
| |
| Long term we might want to hook this into the block |
| duplication code, but as we've seen with similar changes |
| for edge removal, that can be somewhat risky. */ |
| if (EDGE_SUCC (bb, 0)->flags & EDGE_IRREDUCIBLE_LOOP |
| || EDGE_SUCC (bb, 1)->flags & EDGE_IRREDUCIBLE_LOOP) |
| { |
| if (dump_file && (dump_flags & TDF_DETAILS)) |
| fprintf (dump_file, |
| "Join block %d has EDGE_IRREDUCIBLE_LOOP set. " |
| "Scheduling loop fixups.\n", |
| bb->index); |
| loops_state_set (LOOPS_NEED_FIXUP); |
| } |
| } |
| } |
| |
| loop_optimizer_finalize (); |
| free_original_copy_tables (); |
| return changed; |
| } |
| |
| /* Main entry point for splitting paths. Returns TODO_cleanup_cfg if any |
| paths where split, otherwise return zero. */ |
| |
| static unsigned int |
| execute_split_paths () |
| { |
| /* If we don't have at least 2 real blocks and backedges in the |
| CFG, then there's no point in trying to perform path splitting. */ |
| if (n_basic_blocks_for_fn (cfun) <= NUM_FIXED_BLOCKS + 1 |
| || !mark_dfs_back_edges ()) |
| return 0; |
| |
| bool changed = split_paths(); |
| if (changed) |
| free_dominance_info (CDI_DOMINATORS); |
| |
| return changed ? TODO_cleanup_cfg : 0; |
| } |
| |
| static bool |
| gate_split_paths () |
| { |
| return flag_split_paths; |
| } |
| |
| namespace { |
| |
| const pass_data pass_data_split_paths = |
| { |
| GIMPLE_PASS, /* type */ |
| "split-paths", /* name */ |
| OPTGROUP_NONE, /* optinfo_flags */ |
| TV_SPLIT_PATHS, /* tv_id */ |
| PROP_ssa, /* properties_required */ |
| 0, /* properties_provided */ |
| 0, /* properties_destroyed */ |
| 0, /* todo_flags_start */ |
| TODO_update_ssa, /* todo_flags_finish */ |
| }; |
| |
| class pass_split_paths : public gimple_opt_pass |
| { |
| public: |
| pass_split_paths (gcc::context *ctxt) |
| : gimple_opt_pass (pass_data_split_paths, ctxt) |
| {} |
| /* opt_pass methods: */ |
| opt_pass * clone () final override { return new pass_split_paths (m_ctxt); } |
| bool gate (function *) final override { return gate_split_paths (); } |
| unsigned int execute (function *) final override |
| { |
| return execute_split_paths (); |
| } |
| |
| }; // class pass_split_paths |
| |
| } // anon namespace |
| |
| gimple_opt_pass * |
| make_pass_split_paths (gcc::context *ctxt) |
| { |
| return new pass_split_paths (ctxt); |
| } |