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
+. */
+
+#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 (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 (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 (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
+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
+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