diff options
Diffstat (limited to '0311-PATCH-Add-if-split-optimization-pass.patch')
-rw-r--r-- | 0311-PATCH-Add-if-split-optimization-pass.patch | 1203 |
1 files changed, 1203 insertions, 0 deletions
diff --git a/0311-PATCH-Add-if-split-optimization-pass.patch b/0311-PATCH-Add-if-split-optimization-pass.patch new file mode 100644 index 0000000..5e3b75e --- /dev/null +++ b/0311-PATCH-Add-if-split-optimization-pass.patch @@ -0,0 +1,1203 @@ +From 899db9bca3c2ef3cd346814be761eed8b85f5e1e Mon Sep 17 00:00:00 2001 +From: liyancheng <412998149@qq.com> +Date: Wed, 27 Nov 2024 19:26:13 +0800 +Subject: [PATCH] [PATCH] Add if-split optimization pass + +This pass splits conditions like +if (cond1 or cond2) +to the sequense of separate conditions. + +This happens only if there is a function call under condition +Which depends on the condition variable. +--- + gcc/Makefile.in | 1 + + gcc/common.opt | 4 + + gcc/gimple-if-split.cc | 567 ++++++++++++++++++++ + gcc/opts.cc | 2 +- + gcc/passes.def | 1 + + gcc/testsuite/gcc.dg/tree-ssa/if-split-1.c | 24 + + gcc/testsuite/gcc.dg/tree-ssa/if-split-10.c | 45 ++ + gcc/testsuite/gcc.dg/tree-ssa/if-split-2.c | 36 ++ + gcc/testsuite/gcc.dg/tree-ssa/if-split-3.c | 36 ++ + gcc/testsuite/gcc.dg/tree-ssa/if-split-4.c | 42 ++ + gcc/testsuite/gcc.dg/tree-ssa/if-split-5.c | 42 ++ + gcc/testsuite/gcc.dg/tree-ssa/if-split-6.c | 45 ++ + gcc/testsuite/gcc.dg/tree-ssa/if-split-7.c | 45 ++ + gcc/testsuite/gcc.dg/tree-ssa/if-split-8.c | 42 ++ + gcc/testsuite/gcc.dg/tree-ssa/if-split-9.c | 44 ++ + gcc/timevar.def | 1 + + gcc/tree-cfg.h | 2 + + gcc/tree-pass.h | 1 + + gcc/tree-ssa-ifcombine.cc | 6 +- + 19 files changed, 981 insertions(+), 5 deletions(-) + create mode 100644 gcc/gimple-if-split.cc + create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/if-split-1.c + create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/if-split-10.c + create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/if-split-2.c + create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/if-split-3.c + create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/if-split-4.c + create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/if-split-5.c + create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/if-split-6.c + create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/if-split-7.c + create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/if-split-8.c + create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/if-split-9.c + +diff --git a/gcc/Makefile.in b/gcc/Makefile.in +index bb6197a8e..683b28896 100644 +--- a/gcc/Makefile.in ++++ b/gcc/Makefile.in +@@ -1393,6 +1393,7 @@ OBJS = \ + gimple-builder.o \ + gimple-expr.o \ + gimple-if-to-switch.o \ ++ gimple-if-split.o \ + gimple-iterator.o \ + gimple-fold.o \ + gimple-harden-conditionals.o \ +diff --git a/gcc/common.opt b/gcc/common.opt +index a45fbfe1b..a52fa9814 100644 +--- a/gcc/common.opt ++++ b/gcc/common.opt +@@ -1981,6 +1981,10 @@ finstrument-functions-exclude-file-list= + Common RejectNegative Joined + -finstrument-functions-exclude-file-list=filename,... Do not instrument functions listed in files. + ++fif-split ++Common Var(flag_if_split) Init(0) Optimization ++Perform splitting if complex conditions on separate ones with clonning their bodies (gimple version). ++ + fipa-cp + Common Var(flag_ipa_cp) Optimization + Perform interprocedural constant propagation. +diff --git a/gcc/gimple-if-split.cc b/gcc/gimple-if-split.cc +new file mode 100644 +index 000000000..3446204ea +--- /dev/null ++++ b/gcc/gimple-if-split.cc +@@ -0,0 +1,567 @@ ++/* If-split. ++ Copyright (C) 2024 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" ++#define INCLUDE_FUNCTIONAL ++#include "system.h" ++#include "coretypes.h" ++#include "tree.h" ++#include "tree-ssa.h" ++#include "tree-pass.h" ++#include "diagnostic-core.h" ++#include "function.h" ++#include "basic-block.h" ++#include "gimple.h" ++#include "gimple-pretty-print.h" ++#include "gimple-iterator.h" ++#include "cfg.h" ++#include "cfghooks.h" ++#include "ssa.h" ++#include "fold-const.h" ++#include "tree-into-ssa.h" ++#include "tree-cfg.h" ++#include "bitmap.h" ++#include "cfganal.h" ++ ++/* Perform splitting if-then-else patterns, whose complex OR condition in ++cond-bb contains comparison of some variable with constant and then-bb got ++function call, whose arg list contains this var (or this variable is a ++scalar of an aggregate which is an arg of this call). We split condition on ++two separate ones and duplicate then-bb for each one, thus help ipa const ++prop to propagate corresponding constant in function calls. ++Example: ++ Before: ++ if (n == const || some_cond) ++ func (n); ++ After: ++ if (n == const) ++ func (n); ++ else if (some_cond) ++ func (n); */ ++ ++//------------------------------------------------------------------------- ++// Auxiliary functions ++//------------------------------------------------------------------------- ++/* Check if arg list of call got n. */ ++bool ++got_in_args_p (gimple* call, tree n) ++{ ++ unsigned num_args = gimple_call_num_args (call); ++ ++ for (int i = 0; i < num_args; i++) ++ { ++ if (n == gimple_call_arg (call, i)) ++ return true; ++ } ++ ++ return false; ++} ++ ++#define SCALAR_NESTING 2 ++/* Check if call is "necessary" for n. Call is called "necessary" ++ * for n, if n is one of call args, or n is scalar of some aggregate, ++ * which is one of this call args. Nesting param determines how many ++ * levels of aggregate-scalar nesting we want to check. For example, ++ * if nesting == 2, we allow only 2 levels of nesting, like ++ * outer_aggr->inner_aggr->scalar. */ ++static bool ++necessary_call_p (gimple *call, tree n, unsigned nesting) ++{ ++ if (!call) ++ return false; ++ ++ if (got_in_args_p (call, n)) ++ return true; ++ ++ /* Else we need to check if n could be a scalar of some aggregate which ++ * is one of call args. */ ++ tree scalar = n; ++ tree aggregate = NULL_TREE; ++ ++ for (int i = 0; i < nesting; i++) ++ { ++ if (!scalar || TREE_CODE (scalar) != SSA_NAME) ++ return false; ++ ++ gimple *scalar_def = SSA_NAME_DEF_STMT (scalar); ++ ++ if (!is_gimple_assign (scalar_def) ++ || gimple_assign_rhs_code (scalar_def) != COMPONENT_REF) ++ return false; ++ ++ tree scalar_def_rhs = gimple_assign_rhs1 (scalar_def); ++ tree aggregate = TREE_OPERAND (scalar_def_rhs, 0); ++ ++ if (TREE_CODE (aggregate) == MEM_REF) ++ aggregate = TREE_OPERAND (aggregate, 0); ++ ++ if (aggregate && got_in_args_p (call, aggregate)) ++ return true; ++ ++ scalar = aggregate; ++ } ++ ++ return false; ++} ++ ++/* Check if bb got a "necessary" call statement. */ ++static bool ++bb_got_necessary_call_p (basic_block bb, tree n, unsigned nesting) ++{ ++ gimple *stmt = NULL; ++ ++ for (gimple_stmt_iterator gsi = gsi_start_bb (bb); !gsi_end_p (gsi); ++ gsi_next (&gsi)) ++ { ++ gimple *stmt = gsi_stmt (gsi); ++ ++ if (is_gimple_call (stmt) && necessary_call_p (stmt, n, nesting)) ++ return true; ++ } ++ ++ return false; ++} ++ ++//------------------------------------------------------------------------- ++// Complex conditions ++//------------------------------------------------------------------------- ++/* Auxiliary struct which contains var and its constant of comaprison ++ * of expr: n == cst. */ ++struct var_const ++{ ++ tree n = NULL_TREE; ++ tree cst = NULL_TREE; ++}; ++ ++/* Check if var_def stmt got this pattern: ++ * var = (n == const); ++ * If it does, we need to set var_cst struct. */ ++static bool ++comp_with_const_p (gimple *var_def, var_const *var_cst) ++{ ++ if (gimple_expr_code (var_def) != EQ_EXPR) ++ return false; ++ ++ tree var_def_rhs2 = gimple_assign_rhs2 (var_def); ++ ++ if (TREE_CODE (var_def_rhs2) != INTEGER_CST) ++ return false; ++ ++ var_cst->n = gimple_assign_rhs1 (var_def); ++ var_cst->cst = var_def_rhs2; ++ ++ return true; ++} ++ ++/* Auxiliary struct which contains defenition of each part of ++ * complex condition, like: ++ * a = ... <- a_def ++ * b = ... <- b_def ++ * c = a | b <- complex_cond. */ ++struct cond_parts_defs ++{ ++ gimple *a_def = NULL; ++ gimple *b_def = NULL; ++}; ++ ++/* Check if cond got this pattern: ++ * a = ...; <- a_def ++ * b = ...; <- b_def ++ * c = a | b; ++ * if (c != 0) ++ * and a_def or b_def is comparison with constant. If it does, ++ * we need to set a with a_def and b with b_def. */ ++static bool ++necessary_complex_cond_p (const gimple *cond, basic_block then_bb, ++ cond_parts_defs *defs) ++{ ++ tree lhs = gimple_cond_lhs (cond); ++ tree rhs = gimple_cond_rhs (cond); ++ ++ /* As we look for: if (c != 0). */ ++ if (gimple_cond_code (cond) != NE_EXPR || TREE_CODE (lhs) != SSA_NAME ++ || !integer_zerop (rhs)) ++ return false; ++ ++ gimple *c_def = SSA_NAME_DEF_STMT (lhs); ++ ++ /* As we look for: c = a | b. */ ++ if (!c_def || !is_gimple_assign (c_def) || gimple_num_ops (c_def) != 3 ++ || gimple_expr_code (c_def) != BIT_IOR_EXPR) ++ return false; ++ ++ tree a_var = gimple_assign_rhs1 (c_def); ++ tree b_var = gimple_assign_rhs2 (c_def); ++ gimple *a_def = SSA_NAME_DEF_STMT (a_var); ++ gimple *b_def = SSA_NAME_DEF_STMT (b_var); ++ ++ if (!a_def || !is_gimple_assign (a_def) || !b_def ++ || !is_gimple_assign (b_def)) ++ return false; ++ ++ var_const var_cst; ++ ++ if (!(comp_with_const_p (a_def, &var_cst) ++ && bb_got_necessary_call_p (then_bb, var_cst.n, SCALAR_NESTING)) ++ && !(comp_with_const_p (b_def, &var_cst) ++ && bb_got_necessary_call_p (then_bb, var_cst.n, SCALAR_NESTING))) ++ return false; ++ ++ defs->a_def = a_def; ++ defs->b_def = b_def; ++ ++ return true; ++} ++ ++/* Check if our complex condition seems to be "necessary" ++ * and if it does split it on two separate ones. Like: ++ * a = (n == const); <- a_def ++ * b = smth; <- b_def ++ * c = a | b ++ * if (c != 0) ++ * call func (n, ...) ++ * Transform this to: ++ * if (n == const) ++ * goto then ++ * else if (b != 0) ++ * goto then ++ * then: ++ * call func (n, ...). ++ * A complex condition is called "necessary", if it is OR of two ++ * conditions, one of them is comparison with constant and then_bb ++ * of this cond got "necessary" function_call. To know, what ++ * "necessary" function call means look at necessary_call_p (). */ ++static void ++process_complex_cond (basic_block cond_bb, basic_block then_bb, ++ basic_block else_bb) ++{ ++ gimple *cond = last_stmt (cond_bb); ++ cond_parts_defs defs; ++ ++ if (!can_duplicate_block_p (then_bb) ++ || !necessary_complex_cond_p (cond, then_bb, &defs)) ++ return; ++ ++ if (dump_file && (dump_flags & TDF_DETAILS)) ++ { ++ fprintf (dump_file, ++ "Recognized necessary complex condition: ", cond_bb->index); ++ print_gimple_stmt (dump_file, cond, 0, TDF_NONE); ++ } ++ ++ var_const var_cst; ++ ++ /* Setting cond. */ ++ if (comp_with_const_p (defs.a_def, &var_cst)) ++ /* Setting cond as: if (n == const). */ ++ gimple_cond_set_condition (as_a<gcond *> (cond), EQ_EXPR, var_cst.n, ++ var_cst.cst); ++ else ++ { ++ /* Setting cond as: if (a != 0). */ ++ tree cond_lhs = gimple_assign_lhs (defs.a_def); ++ gimple_cond_set_condition (as_a<gcond *> (cond), NE_EXPR, cond_lhs, ++ build_zero_cst (TREE_TYPE (cond_lhs))); ++ } ++ update_stmt (cond); ++ ++ /* Creating inner_cond_bb. */ ++ edge then_e = find_edge (cond_bb, then_bb); ++ edge else_e = find_edge (cond_bb, else_bb); ++ basic_block inner_cond_bb = split_edge (else_e); ++ ++ /* Setting inner_cond. */ ++ gcond *inner_cond = NULL; ++ if (comp_with_const_p (defs.b_def, &var_cst)) ++ { ++ /* Setting inner cond as: if (b == const). */ ++ inner_cond = gimple_build_cond (EQ_EXPR, var_cst.n, var_cst.cst, ++ NULL_TREE, NULL_TREE); ++ } ++ else ++ { ++ /* Setting inner cond as: if (b != 0). */ ++ tree inner_cond_lhs = gimple_assign_lhs (defs.b_def); ++ inner_cond = gimple_build_cond ( ++ NE_EXPR, inner_cond_lhs, build_zero_cst (TREE_TYPE (inner_cond_lhs)), ++ NULL_TREE, NULL_TREE); ++ } ++ gimple_stmt_iterator gsi = gsi_last_bb (inner_cond_bb); ++ gsi_insert_after (&gsi, inner_cond, GSI_NEW_STMT); ++ ++ /* Configuring edges. */ ++ edge inner_cond_then_e = make_edge (inner_cond_bb, then_bb, EDGE_TRUE_VALUE); ++ edge inner_cond_else_e = find_edge (inner_cond_bb, else_bb); ++ inner_cond_else_e->flags = EDGE_FALSE_VALUE; ++ ++ /* Setting phinode args in then_bb coming from inner_cond_bb the same as ++ * ones coming from cond_bb. */ ++ for (gphi_iterator psi = gsi_start_phis (then_bb); !gsi_end_p (psi); ++ gsi_next (&psi)) ++ { ++ gphi *phi = psi.phi (); ++ add_phi_arg (phi, PHI_ARG_DEF_FROM_EDGE (phi, then_e), inner_cond_then_e, ++ UNKNOWN_LOCATION); ++ } ++ ++ /* Updating dominators. */ ++ set_immediate_dominator (CDI_DOMINATORS, inner_cond_bb, cond_bb); ++ basic_block cond_bb_postdominator ++ = get_immediate_dominator (CDI_POST_DOMINATORS, cond_bb); ++ set_immediate_dominator (CDI_POST_DOMINATORS, inner_cond_bb, ++ cond_bb_postdominator); ++ ++ if (dump_file && (dump_flags & TDF_DETAILS)) ++ { ++ fprintf (dump_file, "Successfully transformed:\n (o_cond) ", ++ cond_bb->index); ++ print_gimple_stmt (dump_file, cond, 0, TDF_NONE); ++ fprintf (dump_file, " (i_cond) ", inner_cond_bb->index); ++ print_gimple_stmt (dump_file, inner_cond, 0, TDF_NONE); ++ } ++} ++ ++//------------------------------------------------------------------------- ++// Condition pairs ++//------------------------------------------------------------------------- ++/* Transforming cfg if we recognized patern in process_condition_pair (). */ ++static basic_block ++make_two_separate_calls (basic_block outer_cond_bb, basic_block inner_cond_bb, ++ basic_block then_bb) ++{ ++ if (!can_duplicate_block_p (then_bb) || EDGE_COUNT (then_bb->succs) != 1) ++ return NULL; ++ ++ edge outer_then_e = find_edge (outer_cond_bb, then_bb); ++ ++ /* Making duplication of then_bb. */ ++ basic_block then_bb_dom = get_immediate_dominator (CDI_DOMINATORS, then_bb); ++ basic_block merge_bb = split_edge (single_succ_edge (then_bb)); ++ basic_block then_bb1 = duplicate_block (then_bb, outer_then_e, outer_cond_bb); ++ edge outer_then1_e = find_edge (outer_cond_bb, then_bb1); ++ ++ /* Setting phinode args in then_bb1 coming from outer_cond_bb by previously ++ * collected args_from_outer_cond_bb. */ ++ flush_pending_stmts (outer_then1_e); ++ ++ /* Updating dominators. */ ++ if (then_bb_dom == outer_cond_bb) ++ set_immediate_dominator (CDI_DOMINATORS, then_bb, inner_cond_bb); ++ ++ set_immediate_dominator (CDI_DOMINATORS, merge_bb, then_bb_dom); ++ set_immediate_dominator (CDI_DOMINATORS, then_bb1, outer_cond_bb); ++ ++ set_immediate_dominator (CDI_POST_DOMINATORS, then_bb, merge_bb); ++ set_immediate_dominator (CDI_POST_DOMINATORS, then_bb1, merge_bb); ++ set_immediate_dominator (CDI_POST_DOMINATORS, merge_bb, ++ single_succ (merge_bb)); ++ ++ return then_bb1; ++} ++ ++/* Here we check if cond of bb got this pattern: ++ * if (n == const) ++ * And if it does we need to set n. */ ++static bool ++got_necessary_cond_p (basic_block bb, tree *n) ++{ ++ gimple *stmt = last_stmt (bb); ++ if (!stmt || gimple_code (stmt) != GIMPLE_COND) ++ return false; ++ ++ gcond *cond = as_a<gcond *> (stmt); ++ ++ if (gimple_cond_code (cond) != EQ_EXPR ++ || TREE_CODE (gimple_cond_lhs (cond)) != SSA_NAME ++ || TREE_CODE (gimple_cond_rhs (cond)) != INTEGER_CST) ++ return false; ++ ++ *n = gimple_cond_lhs (cond); ++ ++ return true; ++} ++ ++/* Recognize pattern: ++ * if (n == const) ++ * goto then ++ * else if (some_cond) ++ * goto then ++ * then: ++ * call func (n, ...) ++ * Transform this to: ++ * if (n == const) ++ * call func (n, ...) ++ * else if (some_cond) ++ * call func (n, ...). */ ++static void ++process_cond_pair (basic_block outer_cond_bb, basic_block inner_cond_bb, ++ basic_block then_bb) ++{ ++ tree n = NULL_TREE; ++ ++ if (inner_cond_bb == then_bb ++ || !recognize_if_then_else (outer_cond_bb, &then_bb, &inner_cond_bb) ++ || !same_phi_args_p (outer_cond_bb, inner_cond_bb, then_bb) ++ || (!got_necessary_cond_p (outer_cond_bb, &n) ++ && !got_necessary_cond_p (inner_cond_bb, &n)) ++ || !bb_got_necessary_call_p (then_bb, n, SCALAR_NESTING)) ++ return; ++ ++ if (dump_file && (dump_flags & TDF_DETAILS)) ++ { ++ fprintf (dump_file, "Recognized necessary condition pair: (o_cond) "); ++ print_gimple_stmt (dump_file, last_stmt (outer_cond_bb), 0, TDF_NONE); ++ fprintf (dump_file, " (i_cond) "); ++ print_gimple_stmt (dump_file, last_stmt (inner_cond_bb), 0, TDF_NONE); ++ } ++ ++ basic_block then_bb1 ++ = make_two_separate_calls (outer_cond_bb, inner_cond_bb, then_bb); ++ ++ if (dump_file && (dump_flags & TDF_DETAILS)) ++ { ++ if (then_bb1) ++ fprintf (dump_file, ++ "Successfully transformed: bb<%d> is a copy of bb<%d> \n", ++ then_bb1->index, then_bb->index); ++ else ++ fprintf (dump_file, "No transformation: bb<%d> cannot be duplicated \n", ++ then_bb->index); ++ } ++} ++ ++//------------------------------------------------------------------------- ++// Main logic ++//------------------------------------------------------------------------- ++/* If cond_bb suits if-then-else pattern and got single pred, execute func ++ * over it and its then, else basic blocks. */ ++template <typename F> ++static void ++process_bb (basic_block cond_bb, F func) ++{ ++ basic_block then_bb = NULL, else_bb = NULL; ++ ++ if (!recognize_if_then_else (cond_bb, &then_bb, &else_bb)) ++ return; ++ ++ func (cond_bb, then_bb, else_bb); ++} ++ ++/* For each block, if it has condition, execute function over it. We walk ++ * the blocks in order that guarantees that a block with a single predecessor ++ * is processed after the predecessor. */ ++template <typename F> ++static void ++execute_function_over_conditional_bbs (F func) ++{ ++ basic_block *bbs = single_pred_before_succ_order (); ++ for (int i = n_basic_blocks_for_fn (cfun) - NUM_FIXED_BLOCKS - 1; i >= 0; i--) ++ { ++ gimple *stmt = last_stmt (bbs[i]); ++ ++ if (stmt && gimple_code (stmt) == GIMPLE_COND) ++ { ++ process_bb (bbs[i], func); ++ } ++ } ++ update_ssa (TODO_update_ssa); ++ free (bbs); ++} ++ ++static void ++process_if_split_cfun () ++{ ++ /* First pass. Split complex conditions, so process_condition_pair_bb () ++ * will be able to recognize more necessary patterns. */ ++ execute_function_over_conditional_bbs (process_complex_cond); ++ ++ /* Second pass. Search each basic block for condition pair we may be ++ * able to optimize. */ ++ execute_function_over_conditional_bbs ( ++ [] (basic_block cond_bb, basic_block then_bb, basic_block else_bb) ++ { ++ if (!single_pred_p (cond_bb)) ++ return; ++ process_cond_pair (single_pred (cond_bb), cond_bb, then_bb); ++ }); ++} ++ ++namespace ++{ ++ ++const pass_data pass_data_if_split = { ++ GIMPLE_PASS, /* type. */ ++ "if-split", /* name. */ ++ OPTGROUP_NONE, /* optinfo_flags. */ ++ TV_TREE_IF_SPLIT, /* tv_id. */ ++ 0, /* properties_required. */ ++ 0, /* properties_provided. */ ++ 0, /* properties_destroyed. */ ++ 0, /* todo_flags_start. */ ++ 0 /* todo_flags_finish. */ ++}; ++ ++class pass_if_split : public gimple_opt_pass ++{ ++public: ++ pass_if_split (gcc::context *ctxt) ++ : gimple_opt_pass (pass_data_if_split, ctxt) ++ { ++ } ++ ++ /* opt_pass methods: */ ++ virtual bool ++ gate (function *) ++ { ++ /* Don't bother doing anything if the program has errors. */ ++ return (optimize >= 3 && flag_if_split && !seen_error ()); ++ } ++ ++ virtual unsigned int execute (function *); ++ ++}; // class pass_if_split ++ ++unsigned int ++pass_if_split::execute (function *fun) ++{ ++ calculate_dominance_info (CDI_DOMINATORS); ++ calculate_dominance_info (CDI_POST_DOMINATORS); ++ initialize_original_copy_tables (); ++ ++ process_if_split_cfun (); ++ ++ checking_verify_ssa (true, true); ++ checking_verify_flow_info (); ++ checking_verify_dominators (CDI_DOMINATORS); ++ checking_verify_dominators (CDI_POST_DOMINATORS); ++ ++ free_original_copy_tables (); ++ free_dominance_info (CDI_POST_DOMINATORS); ++ free_dominance_info (CDI_DOMINATORS); ++ ++ return 0; ++} ++ ++} // anon namespace ++ ++gimple_opt_pass * ++make_pass_if_split (gcc::context *ctxt) ++{ ++ return new pass_if_split (ctxt); ++} +\ No newline at end of file +diff --git a/gcc/opts.cc b/gcc/opts.cc +index 84dd8925a..4f3eb4bd4 100644 +--- a/gcc/opts.cc ++++ b/gcc/opts.cc +@@ -3145,7 +3145,7 @@ common_handle_option (struct gcc_options *opts, + SET_OPTION_IF_UNSET (opts, opts_set, flag_cfgo_profile_generate, + value); + } +- /* No break here - do -fcfgo-profile-generate processing. */ ++ /* No break here - do -fprofile-generate processing. */ + /* FALLTHRU */ + case OPT_fprofile_generate_: + opts->x_profile_data_prefix = xstrdup (arg); +diff --git a/gcc/passes.def b/gcc/passes.def +index 862ef0d8f..fbe828439 100644 +--- a/gcc/passes.def ++++ b/gcc/passes.def +@@ -100,6 +100,7 @@ along with GCC; see the file COPYING3. If not see + NEXT_PASS (pass_if_to_switch); + NEXT_PASS (pass_convert_switch); + NEXT_PASS (pass_cleanup_eh); ++ NEXT_PASS (pass_if_split); + NEXT_PASS (pass_profile); + NEXT_PASS (pass_local_pure_const); + NEXT_PASS (pass_modref); +diff --git a/gcc/testsuite/gcc.dg/tree-ssa/if-split-1.c b/gcc/testsuite/gcc.dg/tree-ssa/if-split-1.c +new file mode 100644 +index 000000000..5909dac41 +--- /dev/null ++++ b/gcc/testsuite/gcc.dg/tree-ssa/if-split-1.c +@@ -0,0 +1,24 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O3 -fif-split -fdump-tree-if-split-details" } */ ++ ++static __attribute__ ((noinline)) int foo (int b) ++{ ++ int res = 1; ++ for (int i = 0; i < b; i++) { ++ res*=3; ++ } ++ return res; ++} ++ ++int main(int argc, char** argv){ ++ int b = argc; ++ int res = 0; ++ ++ if (b == 5 || b == 52) ++ res = foo (b); ++ ++ return res; ++} ++ ++/* { dg-final { scan-tree-dump-times "Recognized necessary condition pair:" 1 "if-split" } } */ ++/* { dg-final { scan-tree-dump "Successfully transformed:" "if-split" } } */ +\ No newline at end of file +diff --git a/gcc/testsuite/gcc.dg/tree-ssa/if-split-10.c b/gcc/testsuite/gcc.dg/tree-ssa/if-split-10.c +new file mode 100644 +index 000000000..20a45116b +--- /dev/null ++++ b/gcc/testsuite/gcc.dg/tree-ssa/if-split-10.c +@@ -0,0 +1,45 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O3 -fif-split -fdump-tree-if-split-details" } */ ++ ++typedef struct Y ++{ ++ int b; ++} Y; ++ ++typedef struct X ++{ ++ Y y; ++ int a; ++} X; ++ ++ ++void __attribute__ ((noinline)) set_b (Y* y, int val) ++{ ++ y->b = val; ++} ++ ++static __attribute__ ((noinline)) int foo (int b) ++{ ++ int res = 1; ++ for (int i = 0; i < b; i++) { ++ res*=3; ++ } ++ return res; ++} ++ ++int foo2 (); ++ ++int main(int argc, char** argv){ ++ X data; ++ set_b (&data.y, argc); ++ int res = 0; ++ int foo2_res = foo2(); ++ ++ if (data.y.b == 5 || data.y.b == 52 || foo2_res == 25) ++ res = foo (data.y.b); ++ ++ return res; ++} ++ ++/* { dg-final { scan-tree-dump-times "Recognized necessary condition pair:" 2 "if-split" } } */ ++/* { dg-final { scan-tree-dump "Successfully transformed:" "if-split" } } */ +\ No newline at end of file +diff --git a/gcc/testsuite/gcc.dg/tree-ssa/if-split-2.c b/gcc/testsuite/gcc.dg/tree-ssa/if-split-2.c +new file mode 100644 +index 000000000..1370f9474 +--- /dev/null ++++ b/gcc/testsuite/gcc.dg/tree-ssa/if-split-2.c +@@ -0,0 +1,36 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O3 -fif-split -fdump-tree-if-split-details" } */ ++ ++typedef struct X ++{ ++ int a; ++} X; ++ ++ ++void __attribute__ ((noinline)) set_a (X* x, int val) ++{ ++ x->a = val; ++} ++ ++static __attribute__ ((noinline)) int foo (int b) ++{ ++ int res = 1; ++ for (int i = 0; i < b; i++) { ++ res*=3; ++ } ++ return res; ++} ++ ++int main(int argc, char** argv){ ++ X data; ++ set_a (&data, argc); ++ int res = 0; ++ ++ if (data.a == 5 || data.a == 52) ++ res = foo (data.a); ++ ++ return res; ++} ++ ++/* { dg-final { scan-tree-dump-times "Recognized necessary condition pair:" 1 "if-split" } } */ ++/* { dg-final { scan-tree-dump "Successfully transformed:" "if-split" } } */ +diff --git a/gcc/testsuite/gcc.dg/tree-ssa/if-split-3.c b/gcc/testsuite/gcc.dg/tree-ssa/if-split-3.c +new file mode 100644 +index 000000000..93a6eb6dd +--- /dev/null ++++ b/gcc/testsuite/gcc.dg/tree-ssa/if-split-3.c +@@ -0,0 +1,36 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O3 -fif-split -fdump-tree-if-split-details" } */ ++ ++typedef struct X ++{ ++ int a; ++} X; ++ ++ ++void __attribute__ ((noinline)) set_a (X* x, int val) ++{ ++ x->a = val; ++} ++ ++static __attribute__ ((noinline)) int foo (int b) ++{ ++ int res = 1; ++ for (int i = 0; i < b; i++) { ++ res*=3; ++ } ++ return res; ++} ++ ++int main(int argc, char** argv){ ++ X data; ++ set_a (&data, argc); ++ int res = 0; ++ ++ if (data.a == 5 || data.a == 52 || data.a == 25) ++ res = foo (data.a); ++ ++ return res; ++} ++ ++/* { dg-final { scan-tree-dump-times "Recognized necessary condition pair:" 2 "if-split" } } */ ++/* { dg-final { scan-tree-dump "Successfully transformed:" "if-split" } } */ +\ No newline at end of file +diff --git a/gcc/testsuite/gcc.dg/tree-ssa/if-split-4.c b/gcc/testsuite/gcc.dg/tree-ssa/if-split-4.c +new file mode 100644 +index 000000000..36f2a15b3 +--- /dev/null ++++ b/gcc/testsuite/gcc.dg/tree-ssa/if-split-4.c +@@ -0,0 +1,42 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O3 -fif-split -fdump-tree-if-split-details" } */ ++ ++typedef struct Y ++{ ++ int b; ++} Y; ++ ++typedef struct X ++{ ++ Y y; ++ int a; ++} X; ++ ++ ++void __attribute__ ((noinline)) set_b (Y* y, int val) ++{ ++ y->b = val; ++} ++ ++static __attribute__ ((noinline)) int foo (int b) ++{ ++ int res = 1; ++ for (int i = 0; i < b; i++) { ++ res*=3; ++ } ++ return res; ++} ++ ++int main(int argc, char** argv){ ++ X data; ++ set_b (&data.y, argc); ++ int res = 0; ++ ++ if (data.y.b == 5 || data.y.b == 52) ++ res = foo (data.y.b); ++ ++ return res; ++} ++ ++/* { dg-final { scan-tree-dump-times "Recognized necessary condition pair:" 1 "if-split" } } */ ++/* { dg-final { scan-tree-dump "Successfully transformed:" "if-split" } } */ +\ No newline at end of file +diff --git a/gcc/testsuite/gcc.dg/tree-ssa/if-split-5.c b/gcc/testsuite/gcc.dg/tree-ssa/if-split-5.c +new file mode 100644 +index 000000000..fbc3b0c19 +--- /dev/null ++++ b/gcc/testsuite/gcc.dg/tree-ssa/if-split-5.c +@@ -0,0 +1,42 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O3 -fif-split -fdump-tree-if-split-details" } */ ++ ++typedef struct Y ++{ ++ int b; ++} Y; ++ ++typedef struct X ++{ ++ Y y; ++ int a; ++} X; ++ ++ ++void __attribute__ ((noinline)) set_b (Y* y, int val) ++{ ++ y->b = val; ++} ++ ++static __attribute__ ((noinline)) int foo (int b) ++{ ++ int res = 1; ++ for (int i = 0; i < b; i++) { ++ res*=3; ++ } ++ return res; ++} ++ ++int main(int argc, char** argv){ ++ X data; ++ set_b (&data.y, argc); ++ int res = 0; ++ ++ if (data.y.b == 5 || data.y.b == 52 || data.y.b == 25) ++ res = foo (data.y.b); ++ ++ return res; ++} ++ ++/* { dg-final { scan-tree-dump-times "Recognized necessary condition pair:" 2 "if-split" } } */ ++/* { dg-final { scan-tree-dump "Successfully transformed:" "if-split" } } */ +diff --git a/gcc/testsuite/gcc.dg/tree-ssa/if-split-6.c b/gcc/testsuite/gcc.dg/tree-ssa/if-split-6.c +new file mode 100644 +index 000000000..185127c79 +--- /dev/null ++++ b/gcc/testsuite/gcc.dg/tree-ssa/if-split-6.c +@@ -0,0 +1,45 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O3 -fif-split -fdump-tree-if-split-details" } */ ++ ++typedef struct Y ++{ ++ int b; ++} Y; ++ ++typedef struct X ++{ ++ Y y; ++ int a; ++} X; ++ ++ ++void __attribute__ ((noinline)) set_b (Y* y, int val) ++{ ++ y->b = val; ++} ++ ++static __attribute__ ((noinline)) int foo (int b) ++{ ++ int res = 1; ++ for (int i = 0; i < b; i++) { ++ res*=3; ++ } ++ return res; ++} ++ ++int foo2 (); ++ ++int main(int argc, char** argv){ ++ X data; ++ set_b (&data.y, argc); ++ int res = 0; ++ int foo2_res = foo2(); ++ ++ if (data.y.b == 5 || foo2_res == 52) ++ res = foo (data.y.b); ++ ++ return res; ++} ++ ++/* { dg-final { scan-tree-dump-times "Recognized necessary condition pair:" 1 "if-split" } } */ ++/* { dg-final { scan-tree-dump "Successfully transformed:" "if-split" } } */ +\ No newline at end of file +diff --git a/gcc/testsuite/gcc.dg/tree-ssa/if-split-7.c b/gcc/testsuite/gcc.dg/tree-ssa/if-split-7.c +new file mode 100644 +index 000000000..23f1a8f04 +--- /dev/null ++++ b/gcc/testsuite/gcc.dg/tree-ssa/if-split-7.c +@@ -0,0 +1,45 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O3 -fif-split -fdump-tree-if-split-details" } */ ++ ++typedef struct Y ++{ ++ int b; ++} Y; ++ ++typedef struct X ++{ ++ Y y; ++ int a; ++} X; ++ ++ ++void __attribute__ ((noinline)) set_b (Y* y, int val) ++{ ++ y->b = val; ++} ++ ++static __attribute__ ((noinline)) int foo (int b) ++{ ++ int res = 1; ++ for (int i = 0; i < b; i++) { ++ res*=3; ++ } ++ return res; ++} ++ ++int foo2 (); ++ ++int main(int argc, char** argv){ ++ X data; ++ set_b (&data.y, argc); ++ int res = 0; ++ ++ if (data.y.b == 5 || foo2() == 52) ++ res = foo (data.y.b); ++ ++ return res; ++} ++ ++/* { dg-final { scan-tree-dump-times "Recognized necessary complex condition:" 0 "if-split" } } */ ++/* { dg-final { scan-tree-dump-times "Recognized necessary condition pair:" 0 "if-split" } } */ ++/* { dg-final { scan-tree-dump-times "Successfully transformed:" 0 "if-split" } } */ +\ No newline at end of file +diff --git a/gcc/testsuite/gcc.dg/tree-ssa/if-split-8.c b/gcc/testsuite/gcc.dg/tree-ssa/if-split-8.c +new file mode 100644 +index 000000000..028b6dc40 +--- /dev/null ++++ b/gcc/testsuite/gcc.dg/tree-ssa/if-split-8.c +@@ -0,0 +1,42 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O3 -fif-split -fdump-tree-if-split-details" } */ ++ ++typedef struct Y ++{ ++ int b; ++} Y; ++ ++typedef struct X ++{ ++ Y y; ++ int a; ++} X; ++ ++ ++void __attribute__ ((noinline)) set_b (Y* y, int val) ++{ ++ y->b = val; ++} ++ ++static __attribute__ ((noinline)) int foo (int b) ++{ ++ int res = 1; ++ for (int i = 0; i < b; i++) { ++ res*=3; ++ } ++ return res; ++} ++ ++int main(int argc, char** argv){ ++ X data; ++ set_b (&data.y, argc); ++ int res = 0; ++ ++ if (data.y.b == 5 || data.a == 52) ++ res = foo (data.y.b); ++ ++ return res; ++} ++ ++/* { dg-final { scan-tree-dump-times "Recognized necessary condition pair:" 1 "if-split" } } */ ++/* { dg-final { scan-tree-dump "Successfully transformed:" "if-split" } } */ +\ No newline at end of file +diff --git a/gcc/testsuite/gcc.dg/tree-ssa/if-split-9.c b/gcc/testsuite/gcc.dg/tree-ssa/if-split-9.c +new file mode 100644 +index 000000000..3ff7e2efc +--- /dev/null ++++ b/gcc/testsuite/gcc.dg/tree-ssa/if-split-9.c +@@ -0,0 +1,44 @@ ++/* { dg-do compile } */ ++/* { dg-options "-O3 -fif-split -fdump-tree-if-split-details" } */ ++ ++typedef struct Y ++{ ++ int b; ++} Y; ++ ++typedef struct X ++{ ++ Y y; ++ int a; ++} X; ++ ++ ++void __attribute__ ((noinline)) set_b (Y* y, int val) ++{ ++ y->b = val; ++} ++ ++static __attribute__ ((noinline)) int foo (int b) ++{ ++ int res = 1; ++ for (int i = 0; i < b; i++) { ++ res*=3; ++ } ++ return res; ++} ++ ++int foo2 (); ++ ++int main(int argc, char** argv){ ++ X data; ++ set_b (&data.y, argc); ++ int res = 0; ++ ++ if (data.y.b == 5 || data.y.b == 52 || foo2() == 25) ++ res = foo (data.y.b); ++ ++ return res; ++} ++ ++/* { dg-final { scan-tree-dump-times "Recognized necessary condition pair:" 1 "if-split" } } */ ++/* { dg-final { scan-tree-dump "Successfully transformed:" "if-split" } } */ +\ No newline at end of file +diff --git a/gcc/timevar.def b/gcc/timevar.def +index 6fdb2c767..b0d3d1188 100644 +--- a/gcc/timevar.def ++++ b/gcc/timevar.def +@@ -306,6 +306,7 @@ DEFTIMEVAR (TV_VAR_TRACKING_DATAFLOW , "var-tracking dataflow") + DEFTIMEVAR (TV_VAR_TRACKING_EMIT , "var-tracking emit") + DEFTIMEVAR (TV_TREE_IFCOMBINE , "tree if-combine") + DEFTIMEVAR (TV_TREE_IF_TO_SWITCH , "if to switch conversion") ++DEFTIMEVAR (TV_TREE_IF_SPLIT , "gimple if splitting") + DEFTIMEVAR (TV_TREE_UNINIT , "uninit var analysis") + DEFTIMEVAR (TV_PLUGIN_INIT , "plugin initialization") + DEFTIMEVAR (TV_PLUGIN_RUN , "plugin execution") +diff --git a/gcc/tree-cfg.h b/gcc/tree-cfg.h +index cb67cdf87..bfe44c073 100644 +--- a/gcc/tree-cfg.h ++++ b/gcc/tree-cfg.h +@@ -112,6 +112,8 @@ extern basic_block gimple_switch_default_bb (function *, gswitch *); + extern edge gimple_switch_edge (function *, gswitch *, unsigned); + extern edge gimple_switch_default_edge (function *, gswitch *); + extern bool cond_only_block_p (basic_block); ++extern bool recognize_if_then_else (basic_block, basic_block *, basic_block *); ++extern bool same_phi_args_p (basic_block, basic_block, basic_block); + + /* Return true if the LHS of a call should be removed. */ + +diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h +index f9c2eed8b..fb17b189c 100644 +--- a/gcc/tree-pass.h ++++ b/gcc/tree-pass.h +@@ -383,6 +383,7 @@ extern gimple_opt_pass *make_pass_graphite (gcc::context *ctxt); + extern gimple_opt_pass *make_pass_graphite_transforms (gcc::context *ctxt); + extern gimple_opt_pass *make_pass_if_conversion (gcc::context *ctxt); + extern gimple_opt_pass *make_pass_if_to_switch (gcc::context *ctxt); ++extern gimple_opt_pass *make_pass_if_split (gcc::context *ctxt); + extern gimple_opt_pass *make_pass_loop_distribution (gcc::context *ctxt); + extern gimple_opt_pass *make_pass_vectorize (gcc::context *ctxt); + extern gimple_opt_pass *make_pass_simduid_cleanup (gcc::context *ctxt); +diff --git a/gcc/tree-ssa-ifcombine.cc b/gcc/tree-ssa-ifcombine.cc +index 264a8bcae..3b50fc114 100644 +--- a/gcc/tree-ssa-ifcombine.cc ++++ b/gcc/tree-ssa-ifcombine.cc +@@ -76,8 +76,7 @@ along with GCC; see the file COPYING3. If not see + match the then and else basic-blocks to make the pattern match. + Returns true if the pattern matched, false otherwise. */ + +-static bool +-recognize_if_then_else (basic_block cond_bb, ++bool recognize_if_then_else (basic_block cond_bb, + basic_block *then_bb, basic_block *else_bb) + { + edge t, e; +@@ -168,8 +167,7 @@ forwarder_block_to (basic_block bb, basic_block to_bb) + BB2 to DEST are the same. This makes the CFG merge point + free from side-effects. Return true in this case, else false. */ + +-static bool +-same_phi_args_p (basic_block bb1, basic_block bb2, basic_block dest) ++bool same_phi_args_p (basic_block bb1, basic_block bb2, basic_block dest) + { + edge e1 = find_edge (bb1, dest); + edge e2 = find_edge (bb2, dest); +-- +2.25.1 + |