On Mon, May 6, 2019 at 7:24 AM Jiufu Guo <guoji...@linux.ibm.com> wrote: > > Hi, > > This patch implements the optimization in PR77820. The optimization > eliminates phi and phi's basic block, if the phi is used only by > condition branch, and the phi's incoming value in the result of a > CMP result. > > This optimization eliminates: > 1. saving CMP result: p0 = a CMP b. > 2. additional CMP on branch: if (phi != 0). > 3. converting CMP result if there is phi = (INT_CONV) p0 if there is. > > <P0> > p0 = a CMP b > goto <X>; > > <P1> > p1 = c CMP d > goto <X>; > > <X> > # phi = PHI <p0 (P0), p1 (P1)> > if (phi != 0) goto <Y>; else goto <Z>; > > Transform to: > > <P0> > p0 = a CMP b > if (p0 != 0) goto <Y>; else goto <Z>; > > <P1> > p1 = c CMP d > if (p1 != 0) goto <Y>; else goto <Z>; > > Bootstrapped and tested on powerpc64le with no regressions, and testcases were > saved. Is this ok for trunk?
forwprop was created orginally to something similar but this case is a specific case of backwards prop (almost). I wonder if it could be combined with that or as Richard mentioned, jump threading. Thanks, Andrew Pinski > > Thanks! > > [gcc] > 2019-05-06 Jiufu Guo <guoji...@linux.ibm.com> > Lijia He <heli...@linux.ibm.com> > > PR tree-optimization/77820 > * tree-ssa-mergephicmp.c: New file. > * Makefile.in (OBJS): Add tree-ssa-mergephicmp.o. > * common.opt (ftree-mergephicmp): New flag. > * passes.def (pass_mergephicmp): New pass. > * timevar.def (TV_TREE_MERGEPHICMP): New timevar. > * tree-pass.h: New file. > > [gcc/testsuite] > 2019-05-06 Jiufu Guo <guoji...@linux.ibm.com> > Lijia He <heli...@linux.ibm.com> > > PR tree-optimization/77820 > * gcc.dg/tree-ssa/phi_on_compare-1.c: New testcase. > * gcc.dg/tree-ssa/phi_on_compare-2.c: New testcase. > > > --- > gcc/Makefile.in | 1 + > gcc/common.opt | 4 + > gcc/passes.def | 1 + > gcc/testsuite/gcc.dg/tree-ssa/phi_on_compare-1.c | 31 +++ > gcc/testsuite/gcc.dg/tree-ssa/phi_on_compare-2.c | 31 +++ > gcc/timevar.def | 1 + > gcc/tree-pass.h | 1 + > gcc/tree-ssa-mergephicmp.c | 260 > +++++++++++++++++++++++ > 8 files changed, 330 insertions(+) > create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/phi_on_compare-1.c > create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/phi_on_compare-2.c > create mode 100644 gcc/tree-ssa-mergephicmp.c > > diff --git a/gcc/Makefile.in b/gcc/Makefile.in > index d186d71..9729501 100644 > --- a/gcc/Makefile.in > +++ b/gcc/Makefile.in > @@ -1567,6 +1567,7 @@ OBJS = \ > tree-ssa-reassoc.o \ > tree-ssa-sccvn.o \ > tree-ssa-scopedtables.o \ > + tree-ssa-mergephicmp.o \ > tree-ssa-sink.o \ > tree-ssa-strlen.o \ > tree-ssa-structalias.o \ > diff --git a/gcc/common.opt b/gcc/common.opt > index d342c4f..5ea5ed2 100644 > --- a/gcc/common.opt > +++ b/gcc/common.opt > @@ -2702,6 +2702,10 @@ ftree-salias > Common Ignore > Does nothing. Preserved for backward compatibility. > > +ftree-mergephicmp > +Common Report Var(flag_mergephicmp) Init(1) Optimization > +Enable optimization on branch phi compare on trees. > + > ftree-sink > Common Report Var(flag_tree_sink) Optimization > Enable SSA code sinking on trees. > diff --git a/gcc/passes.def b/gcc/passes.def > index 446a7c4..e3a3913 100644 > --- a/gcc/passes.def > +++ b/gcc/passes.def > @@ -249,6 +249,7 @@ along with GCC; see the file COPYING3. If not see > NEXT_PASS (pass_lim); > NEXT_PASS (pass_walloca, false); > NEXT_PASS (pass_pre); > + NEXT_PASS (pass_mergephicmp); > NEXT_PASS (pass_sink_code); > NEXT_PASS (pass_sancov); > NEXT_PASS (pass_asan); > diff --git a/gcc/testsuite/gcc.dg/tree-ssa/phi_on_compare-1.c > b/gcc/testsuite/gcc.dg/tree-ssa/phi_on_compare-1.c > new file mode 100644 > index 0000000..2e3f4f6 > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/tree-ssa/phi_on_compare-1.c > @@ -0,0 +1,31 @@ > +/* { dg-do compile } */ > +/* { dg-options "-O2 -fdump-tree-mergephicmp" } */ > + > +void g (void); > +void g1 (void); > + > +void > +f (long a, long b, long c, long d, int x) > +{ > + _Bool t; > + if (x) > + { > + t = a < b; > + } > + else if (d == a + b) > + { > + t = c < d; > + } > + else > + { > + t = a == c; > + } > + > + if (t) > + { > + g1 (); > + g (); > + } > +} > + > +/* { dg-final { scan-tree-dump-not "PHI" "mergephicmp" } } */ > diff --git a/gcc/testsuite/gcc.dg/tree-ssa/phi_on_compare-2.c > b/gcc/testsuite/gcc.dg/tree-ssa/phi_on_compare-2.c > new file mode 100644 > index 0000000..7c35417 > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/tree-ssa/phi_on_compare-2.c > @@ -0,0 +1,31 @@ > +/* { dg-do compile } */ > +/* { dg-options "-O2 -fdump-tree-mergephicmp" } */ > + > +void g (void); > +void g1 (void); > + > +void > +f (long a, long b, long c, long d, int x) > +{ > + int t; > + if (x) > + { > + t = a < b; > + } > + else if (d == a + b) > + { > + t = c < d; > + } > + else > + { > + t = a == c; > + } > + > + if (t) > + { > + g1 (); > + g (); > + } > +} > + > +/* { dg-final { scan-tree-dump-times "PHI" 0 "mergephicmp" } } */ > diff --git a/gcc/timevar.def b/gcc/timevar.def > index 5415446..91f92d7 100644 > --- a/gcc/timevar.def > +++ b/gcc/timevar.def > @@ -170,6 +170,7 @@ DEFTIMEVAR (TV_TREE_SPLIT_EDGES , "tree split crit > edges") > DEFTIMEVAR (TV_TREE_REASSOC , "tree reassociation") > DEFTIMEVAR (TV_TREE_PRE , "tree PRE") > DEFTIMEVAR (TV_TREE_FRE , "tree FRE") > +DEFTIMEVAR (TV_TREE_MERGEPHICMP , "tree branch on PHI compare") > DEFTIMEVAR (TV_TREE_SINK , "tree code sinking") > DEFTIMEVAR (TV_TREE_PHIOPT , "tree linearize phis") > DEFTIMEVAR (TV_TREE_BACKPROP , "tree backward propagate") > diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h > index 47be59b..e21e820 100644 > --- a/gcc/tree-pass.h > +++ b/gcc/tree-pass.h > @@ -441,6 +441,7 @@ extern gimple_opt_pass *make_pass_tree_ifcombine > (gcc::context *ctxt); > extern gimple_opt_pass *make_pass_dse (gcc::context *ctxt); > extern gimple_opt_pass *make_pass_nrv (gcc::context *ctxt); > extern gimple_opt_pass *make_pass_rename_ssa_copies (gcc::context *ctxt); > +extern gimple_opt_pass *make_pass_mergephicmp (gcc::context *ctxt); > extern gimple_opt_pass *make_pass_sink_code (gcc::context *ctxt); > extern gimple_opt_pass *make_pass_fre (gcc::context *ctxt); > extern gimple_opt_pass *make_pass_check_data_deps (gcc::context *ctxt); > diff --git a/gcc/tree-ssa-mergephicmp.c b/gcc/tree-ssa-mergephicmp.c > new file mode 100644 > index 0000000..7bb82d5 > --- /dev/null > +++ b/gcc/tree-ssa-mergephicmp.c > @@ -0,0 +1,260 @@ > +/* Elimiate PHI nodes which come from comparasion and used by branch. > + Copyright (C) 2004-2019 Free Software Foundation, Inc. > + > +This file is part of GCC. > + > +GCC is free software; you can redistribute it and/or modify it > +under the terms of the GNU General Public License as published by the > +Free Software Foundation; either version 3, or (at your option) any > +later version. > + > +GCC is distributed in the hope that it will be useful, but WITHOUT > +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or > +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License > +for more details. > + > +You should have received a copy of the GNU General Public License > +along with GCC; see the file COPYING3. If not see > +<http://www.gnu.org/licenses/>. */ > + > +#include "config.h" > +#include "system.h" > +#include "coretypes.h" > +#include "backend.h" > +#include "tree.h" > +#include "gimple.h" > +#include "cfghooks.h" > +#include "tree-pass.h" > +#include "ssa.h" > +#include "gimple-iterator.h" > +#include "tree-cfg.h" > + > +/* Return true if it is ok to merge phi's incoming value: > + - phi's incoming value is > + phi_arg = a CMP b > + or > + cmp = a CMP b > + phi_arg = (INT_CONV) cmp > + - phi's incoming value is defined in incoming basic block. > + > + * Parameter PHI: the phi to be checked. > + * Parameter INDEX: index'th incoming argument of phi to be checked. */ > +static bool > +could_incoming_merge (gphi *phi, int index) > +{ > + tree value = gimple_phi_arg_def (phi, index); > + if (!(TREE_CODE (value) == SSA_NAME && has_single_use (value))) > + return false; > + > + gimple *def = SSA_NAME_DEF_STMT (value); > + > + if (!is_gimple_assign (def)) > + return false; > + > + if (CONVERT_EXPR_CODE_P (gimple_assign_rhs_code (def))) > + { > + value = gimple_assign_rhs1 (def); > + if (!has_single_use (value)) > + return false; > + > + def = SSA_NAME_DEF_STMT (value); > + > + if (!is_gimple_assign (def)) > + return false; > + } > + > + if (TREE_CODE_CLASS (gimple_assign_rhs_code (def)) != tcc_comparison) > + return false; > + > + /* Check if phi's incoming value is defined in the incoming basic_block. > */ > + edge e = gimple_phi_arg_edge (phi, index); > + if (def->bb != e->src) > + return false; > + > + if (!single_succ_p (def->bb)) > + return false; > + > + return true; > +} > + > +/* Return true if the basic_block is ok to optimize: > + <X> > + res = PHI <v0(b0), v1(b1)...> > + if (res != 0) goto l_true; goto l_false; > + > + The phi stmt is saved to argument PHI. > + The gcond stmt is saved to argument GC. */ > +static bool > +could_optimize_phi_bb (basic_block bb, gphi **phi, gcond **gc) > +{ > + /* There should be only one phi. */ > + gphi_iterator pi = gsi_start_phis (bb); > + if (gsi_end_p (pi)) > + return false; > + > + *phi = pi.phi (); > + if (!has_single_use ((*phi)->result)) > + return false; > + > + gsi_next (&pi); > + if (!gsi_end_p (pi)) > + return false; > + > + /* There should be only one stmt which is gcond. */ > + gimple *gs = last_and_only_stmt (bb); > + if (gs == NULL) > + return false; > + > + if (!is_a<gcond *> (gs)) > + return false; > + > + *gc = as_a<gcond *> (gs); > + if (gimple_cond_lhs (*gc) != (*phi)->result) > + return false; > + > + /* Check if all incoming basic blocks can merge. */ > + for (unsigned int i = 0; i < (*phi)->nargs; i++) > + if (!could_incoming_merge (*phi, i)) > + return false; > + > + /* Check if there is no phi in any successors. */ > + edge e; > + edge_iterator ei; > + FOR_EACH_EDGE (e, ei, (*phi)->bb->succs) > + { > + if (!gsi_end_p (gsi_start_phis (e->dest))) > + return false; > + } > + > + return true; > +} > + > +/* Merge the phi and the gcond into the index'th incoming block. */ > +static void > +merge_incoming_bb (gcond *gc, gphi *phi, int index) > +{ > + tree value = gimple_phi_arg_def (phi, index); > + > + gcond *new_gc = gimple_build_cond (gimple_cond_code (gc), value, > + gimple_cond_rhs (gc), NULL, NULL); > + > + gimple *def = SSA_NAME_DEF_STMT (value); > + gimple_stmt_iterator last = gsi_last_bb (def->bb); > + gsi_insert_after (&last, new_gc, GSI_NEW_STMT); > + > + edge e; > + edge_iterator ei; > + FOR_EACH_EDGE (e, ei, phi->bb->succs) > + { > + edge new_e = make_edge (def->bb, e->dest, e->flags); > + new_e->probability = e->probability; > + } > +} > + > +/* If there are basic blocks look like: > + <P0> > + p0 = a CMP b > + goto <X>; > + > + <P1> > + p1 = c CMP d > + goto <X>; > + > + <X> > + # phi = PHI <p0 (P0), p1 (P1)> > + if (phi != 0) goto <Y>; else goto <Z>; > + > +Transform it to: > + > + <P0> > + p0 = a CMP b > + if (p0 != 0) goto <Y>; else goto <Z>; > + > + <P1> > + p1 = c CMP d > + if (p1 != 0) goto <Y>; else goto <Z>; */ > + > +static bool > +mergephicmp_once (function *fun) > +{ > + bool change = false; > + basic_block bb; > + basic_block prev_need_delete = NULL; > + > + FOR_EACH_BB_FN (bb, fun) > + { > + gphi *phi; > + gcond *gc; > + > + /* Prev bb can be deleted only after iterator move to next bb. */ > + if (prev_need_delete) > + delete_basic_block (prev_need_delete); > + prev_need_delete = NULL; > + > + if (could_optimize_phi_bb (bb, &phi, &gc)) > + { > + for (unsigned int i = 0; i < phi->nargs; i++) > + merge_incoming_bb (gc, phi, i); > + > + change = true; > + prev_need_delete = bb; > + } > + } > + > + return change; > +} > + > +namespace { > + > +const pass_data pass_data_mergephicmp = { > + GIMPLE_PASS, /* type */ > + "mergephicmp", /* name */ > + OPTGROUP_NONE, /* optinfo_flags */ > + TV_TREE_MERGEPHICMP, /* tv_id */ > + /* PROP_no_crit_edges is ensured by running split_critical_edges in > + pass_data_sink_code::execute (). */ > + (PROP_cfg | PROP_ssa), /* properties_required */ > + 0, /* properties_provided */ > + 0, /* properties_destroyed */ > + 0, /* todo_flags_start */ > + 0, /* todo_flags_finish */ > +}; > + > +class pass_mergephicmp : public gimple_opt_pass > +{ > +public: > + pass_mergephicmp (gcc::context *ctxt) > + : gimple_opt_pass (pass_data_mergephicmp, ctxt) > + { > + } > + > + /* opt_pass methods: */ > + virtual bool gate (function *) { return flag_mergephicmp != 0; } > + > + virtual unsigned int execute (function *); > + > +}; // class pass_sink_code > + > +unsigned int > +pass_mergephicmp::execute (function *fun) > +{ > + bool changed; > + > + if (n_basic_blocks_for_fn (fun) <= NUM_FIXED_BLOCKS + 1) > + return 0; > + > + changed = mergephicmp_once (fun); > + > + if (changed) > + free_dominance_info (CDI_DOMINATORS); > + > + return changed ? TODO_cleanup_cfg : 0; > +} > + > +} // anon namespace > + > +gimple_opt_pass * > +make_pass_mergephicmp (gcc::context *ctxt) > +{ > + return new pass_mergephicmp (ctxt); > +} > -- > 2.7.4 >