On Tue, 7 May 2019, Andrew Pinski wrote: > 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.
The awkward part is that it changes the CFG so it doesn't fit forwprop well. I thought of ifcombine which kind-of does a reverse transform but in the end it is really duplicating <X> to get rid of the PHI and then applying forwprop. Richard. > 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 > > > -- Richard Biener <rguent...@suse.de> SUSE Linux GmbH, Maxfeldstrasse 5, 90409 Nuernberg, Germany; GF: Felix Imendörffer, Mary Higgins, Sri Rasiah; HRB 21284 (AG Nürnberg)