summaryrefslogtreecommitdiff
path: root/0311-PATCH-Add-if-split-optimization-pass.patch
diff options
context:
space:
mode:
Diffstat (limited to '0311-PATCH-Add-if-split-optimization-pass.patch')
-rw-r--r--0311-PATCH-Add-if-split-optimization-pass.patch1203
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
+