On Tue, Oct 13, 2026, 1:22 AM <[email protected]> wrote: > From: Kyrylo Tkachov <[email protected]> > > match_simplify_replacement only accepts middle blocks that are empty or > hold one cheap statement, which it moves before the condition. A middle > block with more statements prevents every match.pd simplification of the > COND_EXPR. For > > unsigned > f (unsigned m, unsigned a, unsigned b, unsigned d, _Bool c) > { > unsigned r = m; > if (c) > r = m | ((a + b) ^ d); > return r; > } > > phiopt2 sees > > if (c_5(D) != 0) goto <bb 3>; else goto <bb 4>; > <bb 3>: > _1 = a_6(D) + b_7(D); > _2 = _1 ^ d_8(D); > r_9 = _2 | m_4(D); > <bb 4>: > # r_3 = PHI <m_4(D)(2), r_9(3)> > > match.pd simplifies c ? m | t : m to m | t * (unsigned) c, but phiopt > does not try it. > > Accept middle blocks whose statements are all cheap in the same way as > the single statement that phiopt moves (no memory access, no trap, no > side effects, no maybe undefined operand, only some bit-operation calls) > and only compute the PHI argument. Hide the flow-sensitive information > of the blocks while simplifying. Then move the statements that the > replacement needs before the condition, in their order, and remove the > other ones, so that replace_phi_edge_with_variable removes the empty > blocks and resets their debug binds. f becomes > > _1 = a_6(D) + b_7(D); > _2 = _1 ^ d_8(D); > _11 = (unsigned int) c_5(D); > _12 = _2 * _11; > _13 = m_4(D) | _12; > > A block with one statement is handled as before. Statements moved out > of a block with several statements must be cheap integer operations > (conversions, bitwise operations, additions, subtractions, negations or > bit-operation calls), and at most --param=phiopt-max-moved-stmts of > them, 2 by default. Nop conversions are free. move_stmt now keeps GSI > on the insertion point, so that moved statements keep their order. > > This is based on the patch by Andrea Pinski at > https://gcc.gnu.org/pipermail/gcc-patches/2026-July/723433.html > with these changes: > > - The statements of a block do not have to form a chain, and phiopt > moves any of them that the replacement needs, not only the first one > or the ones up to the result. The other statements are removed. >
No. That seems to the wrong direction. - A --param limits the statements moved out of a block with several > statements, and they must be cheap integer operations, as asked in > https://gcc.gnu.org/pipermail/gcc-patches/2026-July/723865.html > - Both blocks become empty before they are removed, so that the debug > binds of one arm do not stay on both paths. > Even this seems like the wrong direction. - cond_removal_in_builtin_zero_pattern is removed in a separate patch. > - New tests. > > Bootstrapped and tested on aarch64-none-linux-gnu and x86_64-linux. > > gcc/ChangeLog: > > PR tree-optimization/126035 > * doc/params.texi (phiopt-max-moved-stmts): Document. > * gimple.cc (gimple_nop_conversion_p): Move from > tree-ssa-reassoc.cc. > * gimple.h (gimple_nop_conversion_p): Declare. > * params.opt (phiopt-max-moved-stmts): New param. > * tree-ssa-phiopt.cc: Include gimple-walk.h. > (cheap_middle_stmt_lhs): New function, split out from ... > (empty_bb_or_one_feeding_into_p): ... here. Rename to ... > (middle_bb_stmts): ... this. Return the number of statements > and accept blocks with several statements that only compute the > PHI argument. > (move_stmt): Do not accept a null STMT. Keep GSI on the > insertion point and rewrite STMT through its own iterator. > (auto_flow_sensitive::auto_flow_sensitive): Take a basic block > instead of a statement. > (mark_ssa_name_r, cheap_integer_stmt_p, mark_needed_stmts) > (move_needed_stmts): New functions. > (match_simplify_replacement): Move the statements of the middle > blocks that the replacement needs and remove the other ones. > * tree-ssa-reassoc.cc (gimple_nop_conversion_p): Move to > gimple.cc. > > gcc/testsuite/ChangeLog: > > PR tree-optimization/126035 > * gcc.dg/tree-ssa/phi-opt-53.c: New test. > * gcc.dg/tree-ssa/phi-opt-54.c: New test. > * gcc.dg/tree-ssa/phi-opt-55.c: New test. > * gcc.dg/tree-ssa/phi-opt-56.c: New test. > * gcc.dg/tree-ssa/phi-opt-57.c: New test. > * gcc.dg/tree-ssa/phi-opt-58.c: New test. > * gcc.dg/tree-ssa/phi-opt-59.c: New test. > > Co-authored-by: Andrea Pinski <[email protected]> > Signed-off-by: Andrea Pinski <[email protected]> > Signed-off-by: Kyrylo Tkachov <[email protected]> > --- > gcc/doc/params.texi | 7 + > gcc/gimple.cc | 15 + > gcc/gimple.h | 1 + > gcc/params.opt | 4 + > gcc/testsuite/gcc.dg/tree-ssa/phi-opt-53.c | 49 +++ > gcc/testsuite/gcc.dg/tree-ssa/phi-opt-54.c | 52 +++ > gcc/testsuite/gcc.dg/tree-ssa/phi-opt-55.c | 72 +++++ > gcc/testsuite/gcc.dg/tree-ssa/phi-opt-56.c | 17 + > gcc/testsuite/gcc.dg/tree-ssa/phi-opt-57.c | 35 +++ > gcc/testsuite/gcc.dg/tree-ssa/phi-opt-58.c | 29 ++ > gcc/testsuite/gcc.dg/tree-ssa/phi-opt-59.c | 35 +++ > gcc/tree-ssa-phiopt.cc | 349 ++++++++++++++------- > gcc/tree-ssa-reassoc.cc | 15 - > 13 files changed, 560 insertions(+), 120 deletions(-) > create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/phi-opt-53.c > create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/phi-opt-54.c > create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/phi-opt-55.c > create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/phi-opt-56.c > create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/phi-opt-57.c > create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/phi-opt-58.c > create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/phi-opt-59.c > > diff --git a/gcc/doc/params.texi b/gcc/doc/params.texi > index 090af64a7e7..82dfdb0e73d 100644 > --- a/gcc/doc/params.texi > +++ b/gcc/doc/params.texi > @@ -57,6 +57,13 @@ When factoring statements out of if/then/else, this is > the maximum number > of statements > after the defining statement to be allowed to extend the lifetime of a > name. > > +@paindex phiopt-max-moved-stmts > +@item phiopt-max-moved-stmts > +The maximum number of statements that phiopt moves out of conditional > +blocks with more than one statement when it removes a branch, not counting > +conversions that need no instruction. These statements must be cheap > +integer operations. > + > @paindex predictable-branch-outcome > @item predictable-branch-outcome > When branch is predicted to be taken with probability lower than this > threshold > diff --git a/gcc/gimple.cc b/gcc/gimple.cc > index 3b68902b610..1fda9676647 100644 > --- a/gcc/gimple.cc > +++ b/gcc/gimple.cc > @@ -1878,6 +1878,21 @@ gimple_assign_unary_nop_p (gimple *gs) > == TYPE_MODE (TREE_TYPE (gimple_assign_rhs1 (gs))))); > } > > +/* Return true if STMT is a nop-conversion. */ > + > +bool > +gimple_nop_conversion_p (gimple *stmt) > +{ > + if (gassign *ass = dyn_cast <gassign *> (stmt)) > + { > + if (CONVERT_EXPR_CODE_P (gimple_assign_rhs_code (ass)) > + && tree_nop_conversion_p (TREE_TYPE (gimple_assign_lhs (ass)), > + TREE_TYPE (gimple_assign_rhs1 (ass)))) > + return true; > + } > + return false; > +} > + > /* Return true if GS is an assignment that loads from its rhs1. */ > > bool > diff --git a/gcc/gimple.h b/gcc/gimple.h > index 0245e5e0934..08a8e32cf07 100644 > --- a/gcc/gimple.h > +++ b/gcc/gimple.h > @@ -1640,6 +1640,7 @@ tree gimple_call_nonnull_arg (gcall *); > bool gimple_assign_copy_p (gimple *); > bool gimple_assign_ssa_name_copy_p (gimple *); > bool gimple_assign_unary_nop_p (gimple *); > +bool gimple_nop_conversion_p (gimple *); > bool gimple_assign_load_p (const gimple *); > void gimple_set_bb (gimple *, basic_block); > void gimple_assign_set_rhs_from_tree (gimple_stmt_iterator *, tree); > diff --git a/gcc/params.opt b/gcc/params.opt > index 29a5c7018a8..fe109f59e92 100644 > --- a/gcc/params.opt > +++ b/gcc/params.opt > @@ -958,6 +958,10 @@ Maximum probability of the entry BB of split region > (as a percentage relative to > Common Joined UInteger Var(param_phiopt_factor_max_stmts_live) Init(5) > Optimization IntegerRange(0, 100) Param > Maximum number of statements allowed between a defining statement and the > end of the basic block before extending the live range is considered > profitable. > > +-param=phiopt-max-moved-stmts= > +Common Joined UInteger Var(param_phiopt_max_moved_stmts) Init(2) > Optimization IntegerRange(0, 100) Param > +Maximum number of statements, not counting conversions that need no > instruction, that phiopt moves out of conditional blocks with more than one > statement when it removes a branch. > + > -param=predictable-branch-outcome= > Common Joined UInteger Var(param_predictable_branch_outcome) Init(2) > IntegerRange(0, 50) Param Optimization > Maximal estimated outcome of branch considered predictable. > diff --git a/gcc/testsuite/gcc.dg/tree-ssa/phi-opt-53.c > b/gcc/testsuite/gcc.dg/tree-ssa/phi-opt-53.c > new file mode 100644 > index 00000000000..e3439791f7a > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/tree-ssa/phi-opt-53.c > @@ -0,0 +1,49 @@ > +/* { dg-do run } */ > +/* { dg-options "-O2 -fdump-tree-phiopt2-details" } */ > +/* { dg-require-effective-target int32plus } */ > + > +/* A conditional set, flip or add of one bit computes the shifted bit in > + the conditional block. phiopt removes the block, as > + c ? m | (1 << i) : m is m | ((T) c << i). */ > + > +__attribute__((noipa)) unsigned > +set_bit (unsigned m, _Bool c, int i) > +{ > + if (c) > + m |= 1u << i; > + return m; > +} > + > +__attribute__((noipa)) unsigned > +flip_bit (unsigned m, unsigned x, int i) > +{ > + if ((x >> i) & 1) > + m ^= 1u << i; > + return m; > +} > + > +__attribute__((noipa)) unsigned long long > +add_bit (unsigned long long m, _Bool c, int i) > +{ > + if (c) > + m += 1ULL << i; > + return m; > +} > + > +int > +main () > +{ > + for (int i = 0; i < 32; i++) > + { > + if (set_bit (0x10, 0, i) != 0x10 > + || set_bit (0x10, 1, i) != (0x10 | (1u << i)) > + || flip_bit (0x10, 0, i) != 0x10 > + || flip_bit (0x10, 1u << i, i) != (0x10 ^ (1u << i)) > + || add_bit (0x10, 0, i) != 0x10 > + || add_bit (0x10, 1, i) != 0x10 + (1ULL << i)) > + __builtin_abort (); > + } > + return 0; > +} > + > +/* { dg-final { scan-tree-dump-times "to straightline code" 3 "phiopt2" } > } */ > diff --git a/gcc/testsuite/gcc.dg/tree-ssa/phi-opt-54.c > b/gcc/testsuite/gcc.dg/tree-ssa/phi-opt-54.c > new file mode 100644 > index 00000000000..416c4db8b7a > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/tree-ssa/phi-opt-54.c > @@ -0,0 +1,52 @@ > +/* { dg-do run } */ > +/* { dg-options "-O2 -fgimple -fdump-tree-phiopt2-details" } */ > +/* { dg-require-effective-target int32 } */ > + > +/* The arm computes the PHI argument with three statements. The > + simplified COND_EXPR does not use them, so the arm is removed. */ > + > +unsigned char __GIMPLE (ssa,startwith("phiopt")) __attribute__((noipa)) > +clip (int x) > +{ > + unsigned u; > + unsigned char r0; > + int n; > + int s; > + unsigned char r1; > + unsigned char r; > + > + __BB(2): > + u_2 = (unsigned) x_1(D); > + r0_3 = (unsigned char) x_1(D); > + if (u_2 > 255u) > + goto __BB3; > + else > + goto __BB4; > + > + __BB(3): > + n_4 = -x_1(D); > + s_5 = n_4 >> 31; > + r1_6 = (unsigned char) s_5; > + goto __BB4; > + > + __BB(4): > + r_7 = __PHI (__BB2: r0_3, __BB3: r1_6); > + return r_7; > +} > + > +int > +main () > +{ > + static const int v[] = { -__INT_MAX__, -256, -255, -1, 0, 1, 77, 254, > 255, > + 256, 257, 1000, __INT_MAX__ }; > + static const unsigned char want[] = { 0, 0, 0, 0, 0, 1, 77, 254, 255, > + 255, 255, 255, 255 }; > + for (unsigned i = 0; i < sizeof (v) / sizeof (v[0]); i++) > + if (clip (v[i]) != want[i]) > + __builtin_abort (); > + return 0; > +} > + > +/* { dg-final { scan-tree-dump "converted to straightline code" "phiopt2" > } } */ > +/* { dg-final { scan-tree-dump "MAX_EXPR" "phiopt2" } } */ > +/* { dg-final { scan-tree-dump "MIN_EXPR" "phiopt2" } } */ > diff --git a/gcc/testsuite/gcc.dg/tree-ssa/phi-opt-55.c > b/gcc/testsuite/gcc.dg/tree-ssa/phi-opt-55.c > new file mode 100644 > index 00000000000..efb7e94e13c > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/tree-ssa/phi-opt-55.c > @@ -0,0 +1,72 @@ > +/* { dg-do run } */ > +/* { dg-options "-O2 -fchecking -fdump-tree-phiopt-details" } */ > + > +/* phiopt moves the statements of a conditional block that the simplified > + PHI needs before the condition and removes the other ones. */ > + > +#define N __attribute__((noipa)) > + > +/* c ? m | t : m is m | t * c, and t needs two statements. */ > +N unsigned > +f1 (unsigned m, unsigned a, unsigned b, unsigned d, _Bool c) > +{ > + unsigned r = m; > + if (c) > + r = m | ((a + b) ^ d); > + return r; > +} > + > +/* The signed addition can overflow when C is false, so it is rewritten to > + unsigned arithmetic. */ > +N int > +f2 (int m, int a, int b, int d, _Bool c) > +{ > + int r = m; > + if (c) > + r = m | ((a + b) ^ d); > + return r; > +} > + > +/* The PHI argument is the result. */ > +N unsigned > +f3 (unsigned a, unsigned b, unsigned c) > +{ > + unsigned r; > + if (a) > + r = a & (b | c); > + else > + r = 0; > + return r; > +} > + > +/* The nop conversion is not counted. */ > +N unsigned > +f4 (unsigned m, int a, int b, int d, _Bool c) > +{ > + unsigned r = m; > + if (c) > + r = m | (unsigned) ((a - b) ^ d); > + return r; > +} > + > +int > +main () > +{ > + if (f1 (0x10, 1, 2, 4, 0) != 0x10 || f1 (0x10, 1, 2, 4, 1) != 0x17) > + __builtin_abort (); > + if (f2 (0x10, __INT_MAX__, 1, 0, 0) != 0x10 > + || f2 (0x10, 1, 2, 4, 1) != 0x17) > + __builtin_abort (); > + if (f3 (0, 1, 2) != 0 || f3 (6, 1, 2) != 2) > + __builtin_abort (); > + if (f4 (0x10, -__INT_MAX__ - 1, 1, 0, 0) != 0x10 > + || f4 (0x10, 5, 2, 4, 1) != 0x17) > + __builtin_abort (); > + return 0; > +} > + > +/* { dg-final { scan-tree-dump-times "to straightline code" 1 "phiopt1" } > } */ > +/* { dg-final { scan-tree-dump-times "to straightline code" 3 "phiopt2" } > } */ > +/* { dg-final { scan-tree-dump-times "statement un-sinked" 2 "phiopt1" } > } */ > +/* { dg-final { scan-tree-dump-times "statement un-sinked" 7 "phiopt2" } > } */ > +/* { dg-final { scan-tree-dump-times "rewriting stmt" 2 "phiopt2" } } */ > diff --git a/gcc/testsuite/gcc.dg/tree-ssa/phi-opt-56.c > b/gcc/testsuite/gcc.dg/tree-ssa/phi-opt-56.c > new file mode 100644 > index 00000000000..827290e03a9 > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/tree-ssa/phi-opt-56.c > @@ -0,0 +1,17 @@ > +/* { dg-do compile } */ > +/* { dg-options "-O2 -fdump-tree-optimized" } */ > +/* { dg-additional-options "--param=phiopt-max-moved-stmts=1" } */ > + > +/* With a limit of one statement, phiopt keeps the branch, as t needs two > + statements. */ > + > +unsigned > +f1 (unsigned m, unsigned a, unsigned b, unsigned d, _Bool c) > +{ > + unsigned r = m; > + if (c) > + r = m | ((a + b) ^ d); > + return r; > +} > + > +/* { dg-final { scan-tree-dump "if \\(" "optimized" } } */ > diff --git a/gcc/testsuite/gcc.dg/tree-ssa/phi-opt-57.c > b/gcc/testsuite/gcc.dg/tree-ssa/phi-opt-57.c > new file mode 100644 > index 00000000000..4c911b8a0c3 > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/tree-ssa/phi-opt-57.c > @@ -0,0 +1,35 @@ > +/* { dg-do compile } */ > +/* { dg-options "-O2 -fdump-tree-optimized" } */ > + > +/* phiopt only moves cheap integer operations out of a conditional block > + with several statements. The only statement of a block can be any > + cheap one. */ > + > +int > +g1 (int a, int b, int c) > +{ > + int r = 0; > + if (a != 0) > + r = a * (b + c); > + return r; > +} > + > +unsigned > +g2 (unsigned m, unsigned a, unsigned b, _Bool c) > +{ > + unsigned r = m; > + if (c) > + r = m | (a * b); > + return r; > +} > + > +int > +g3 (int a, int t) > +{ > + int r = 0; > + if (a != 0) > + r = a * t; > + return r; > +} > + > +/* { dg-final { scan-tree-dump-times "if \\(" 2 "optimized" } } */ > diff --git a/gcc/testsuite/gcc.dg/tree-ssa/phi-opt-58.c > b/gcc/testsuite/gcc.dg/tree-ssa/phi-opt-58.c > new file mode 100644 > index 00000000000..2e4d83b3c39 > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/tree-ssa/phi-opt-58.c > @@ -0,0 +1,29 @@ > +/* { dg-do run } */ > +/* { dg-options "-O2 -fchecking" } */ > + > +/* The empty conditional block has another predecessor. */ > + > +__attribute__((noipa)) int > +h (int z) > +{ > + return z > 10; > +} > + > +__attribute__((noipa)) int > +k (int x, int y, int z) > +{ > + int r; > + if (h (z) || x > y) > + r = x; > + else > + r = y; > + return r; > +} > + > +int > +main () > +{ > + if (k (1, 2, 20) != 1 || k (1, 2, 0) != 2 || k (3, 2, 0) != 3) > + __builtin_abort (); > + return 0; > +} > diff --git a/gcc/testsuite/gcc.dg/tree-ssa/phi-opt-59.c > b/gcc/testsuite/gcc.dg/tree-ssa/phi-opt-59.c > new file mode 100644 > index 00000000000..6cff4e39abc > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/tree-ssa/phi-opt-59.c > @@ -0,0 +1,35 @@ > +/* { dg-do compile } */ > +/* { dg-options "-O2 -g -fcompare-debug" } */ > + > +/* Debug statements in the conditional blocks do not change what phiopt > + moves or removes. */ > + > +unsigned > +f1 (unsigned m, unsigned a, unsigned b, unsigned d, _Bool c) > +{ > + unsigned r = m; > + if (c) > + { > + unsigned t = a + b; > + unsigned u = t ^ d; > + r = m | u; > + } > + return r; > +} > + > +int > +f2 (int x, int y, int a) > +{ > + int r; > + if (x > y) > + { > + int t = a; > + r = t | 8; > + } > + else > + { > + int t = a; > + r = t & ~8; > + } > + return r; > +} > diff --git a/gcc/tree-ssa-phiopt.cc b/gcc/tree-ssa-phiopt.cc > index a93bc699be7..5f24314c3c9 100644 > --- a/gcc/tree-ssa-phiopt.cc > +++ b/gcc/tree-ssa-phiopt.cc > @@ -36,6 +36,7 @@ along with GCC; see the file COPYING3. If not see > #include "stor-layout.h" > #include "cfganal.h" > #include "gimple-iterator.h" > +#include "gimple-walk.h" > #include "tree-cfg.h" > #include "tree-dfa.h" > #include "domwalk.h" > @@ -917,63 +918,26 @@ one_feeding_comparison_into_p (basic_block bb, > return true; > } > > -/* empty_bb_or_one_feeding_into_p returns true if bb was empty basic block > - or it has one cheap preparation statement that feeds into the PHI > - statement and it sets STMT to that statement. */ > -static bool > -empty_bb_or_one_feeding_into_p (basic_block bb, > - gimple *phi, > - gimple *&stmt) > -{ > - stmt = nullptr; > - gimple *stmt_to_move = nullptr; > - tree lhs; > - > - if (empty_block_p (bb)) > - return true; > - > - if (!single_pred_p (bb)) > - return false; > - > - /* The middle bb cannot have phi nodes as we don't > - move those assignments yet. */ > - if (!gimple_seq_empty_p (phi_nodes (bb))) > - return false; > - > - gimple_stmt_iterator gsi; > +/* Return the SSA name defined by STMT, a statement in a middle basic > block, > + if STMT does not access memory, cannot trap, has no side effects and > does > + not use a maybe undefined value, so that it can be executed > + unconditionally or removed. Otherwise return NULL_TREE. */ > > - gsi = gsi_start_nondebug_after_labels_bb (bb); > - while (!gsi_end_p (gsi)) > - { > - gimple *s = gsi_stmt (gsi); > - gsi_next_nondebug (&gsi); > - /* Skip over Predict and nop statements. */ > - if (gimple_code (s) == GIMPLE_PREDICT > - || gimple_code (s) == GIMPLE_NOP) > - continue; > - /* If there is more one statement return false. */ > - if (stmt_to_move) > - return false; > - stmt_to_move = s; > - } > - > - /* The only statement here was a Predict or a nop statement > - so return true. */ > - if (!stmt_to_move) > - return true; > - > - if (gimple_vuse (stmt_to_move)) > - return false; > +static tree > +cheap_middle_stmt_lhs (gimple *stmt) > +{ > + if (gimple_vuse (stmt)) > + return NULL_TREE; > > - if (gimple_could_trap_p (stmt_to_move) > - || gimple_has_side_effects (stmt_to_move)) > - return false; > + if (gimple_could_trap_p (stmt) > + || gimple_has_side_effects (stmt)) > + return NULL_TREE; > > ssa_op_iter it; > tree use; > - FOR_EACH_SSA_TREE_OPERAND (use, stmt_to_move, it, SSA_OP_USE) > + FOR_EACH_SSA_TREE_OPERAND (use, stmt, it, SSA_OP_USE) > if (ssa_name_maybe_undef_p (use)) > - return false; > + return NULL_TREE; > > /* Allow assignments but allow some builtin/internal calls. > As const calls don't match any of the above, yet they could > @@ -984,15 +948,16 @@ empty_bb_or_one_feeding_into_p (basic_block bb, > should handle this. > Allow some known builtin/internal calls that are known not to > trap: logical functions (e.g. bswap and bit counting). */ > - if (!is_gimple_assign (stmt_to_move)) > + tree lhs; > + if (!is_gimple_assign (stmt)) > { > - if (!is_gimple_call (stmt_to_move)) > - return false; > - combined_fn cfn = gimple_call_combined_fn (stmt_to_move); > + if (!is_gimple_call (stmt)) > + return NULL_TREE; > + combined_fn cfn = gimple_call_combined_fn (stmt); > switch (cfn) > { > default: > - return false; > + return NULL_TREE; > CASE_CFN_BSWAP: > CASE_CFN_BITREVERSE: > CASE_CFN_FFS: > @@ -1003,34 +968,63 @@ empty_bb_or_one_feeding_into_p (basic_block bb, > case CFN_BUILT_IN_CLRSB: > case CFN_BUILT_IN_CLRSBL: > case CFN_BUILT_IN_CLRSBLL: > - lhs = gimple_call_lhs (stmt_to_move); > + lhs = gimple_call_lhs (stmt); > break; > } > } > else > - lhs = gimple_assign_lhs (stmt_to_move); > + lhs = gimple_assign_lhs (stmt); > > - gimple *use_stmt; > - use_operand_p use_p; > + if (!lhs || TREE_CODE (lhs) != SSA_NAME) > + return NULL_TREE; > + return lhs; > +} > > - /* Allow only a statement which feeds into the other stmt. */ > - if (!lhs || TREE_CODE (lhs) != SSA_NAME > - || !single_imm_use (lhs, &use_p, &use_stmt) > - || use_stmt != phi) > - return false; > +/* Return -1 if match_simplify_replacement cannot remove BB, a middle > basic > + block. Otherwise return the number of statements of BB, which are > cheap > + (see cheap_middle_stmt_lhs) and only compute the PHI argument. */ > > - stmt = stmt_to_move; > - return true; > +static int > +middle_bb_stmts (basic_block bb) > +{ > + if (empty_block_p (bb)) > + return 0; > + > + if (!single_pred_p (bb)) > + return -1; > + > + /* The middle bb cannot have phi nodes as we don't > + move those assignments yet. */ > + if (!gimple_seq_empty_p (phi_nodes (bb))) > + return -1; > + > + int n = 0; > + for (gimple_stmt_iterator gsi = gsi_start_nondebug_after_labels_bb (bb); > + !gsi_end_p (gsi); gsi_next_nondebug (&gsi)) > + { > + gimple *s = gsi_stmt (gsi); > + /* Skip over Predict and nop statements. */ > + if (gimple_code (s) == GIMPLE_PREDICT > + || gimple_code (s) == GIMPLE_NOP) > + continue; > + /* Every statement must be cheap and its result must be used. BB is > + a middle block and the PHI is the only PHI whose arguments differ, > + so the PHI is the only user outside BB. */ > + tree lhs = cheap_middle_stmt_lhs (s); > + if (!lhs || has_zero_uses (lhs)) > + return -1; > + n++; > + } > + return n; > } > > -/* Move STMT to before GSI and insert its defining > - name into INSERTED_EXPRS bitmap. > - Also rewrite its if it might be undefined when unconditionalized. */ > +/* Move STMT right before the statement at GSI, which GSI keeps pointing > to, > + so that statements moved one after the other keep their order, and > insert > + its defining name into INSERTED_EXPRS bitmap. > + Also rewrite it if it might be undefined when unconditionalized. */ > static void > move_stmt (gimple *stmt, gimple_stmt_iterator *gsi, auto_bitmap > &inserted_exprs) > { > - if (!stmt) > - return; > if (dump_file && (dump_flags & TDF_DETAILS)) > { > fprintf (dump_file, "statement un-sinked:\n"); > @@ -1042,21 +1036,24 @@ move_stmt (gimple *stmt, gimple_stmt_iterator > *gsi, auto_bitmap &inserted_exprs) > // Mark the name to be renamed if there is one. > bitmap_set_bit (inserted_exprs, SSA_NAME_VERSION (name)); > gimple_stmt_iterator gsi1 = gsi_for_stmt (stmt); > - gsi_move_before (&gsi1, gsi, GSI_NEW_STMT); > + gsi_move_before (&gsi1, gsi); > reset_flow_sensitive_info (name); > > /* Rewrite some code which might be undefined when > unconditionalized. */ > if (gimple_needing_rewrite_undefined (stmt)) > - rewrite_to_defined_unconditional (gsi); > + { > + gsi1 = gsi_for_stmt (stmt); > + rewrite_to_defined_unconditional (&gsi1); > + } > } > > -/* RAII style class to temporarily remove flow sensitive > - from ssa names defined by a gimple statement. */ > +/* RAII style class to temporarily remove the flow sensitive information > + of the SSA names defined in a basic block. */ > class auto_flow_sensitive > { > public: > - auto_flow_sensitive (gimple *s); > + auto_flow_sensitive (basic_block bb); > ~auto_flow_sensitive (); > private: > auto_vec<std::pair<tree, flow_sensitive_info_storage>, 2> stack; > @@ -1064,20 +1061,24 @@ private: > > /* Constructor for auto_flow_sensitive. Saves > off the ssa names' flow sensitive information > - that was defined by gimple statement S and > + that was defined in BB, if BB is non-null, and > resets it to be non-flow based ones. */ > > -auto_flow_sensitive::auto_flow_sensitive (gimple *s) > +auto_flow_sensitive::auto_flow_sensitive (basic_block bb) > { > - if (!s) > + if (!bb) > return; > - ssa_op_iter it; > - tree def; > - FOR_EACH_SSA_TREE_OPERAND (def, s, it, SSA_OP_DEF) > + for (gimple_stmt_iterator gsi = gsi_start_nondebug_after_labels_bb (bb); > + !gsi_end_p (gsi); gsi_next_nondebug (&gsi)) > { > - flow_sensitive_info_storage storage; > - storage.save_and_clear (def); > - stack.safe_push (std::make_pair (def, storage)); > + ssa_op_iter it; > + tree def; > + FOR_EACH_SSA_TREE_OPERAND (def, gsi_stmt (gsi), it, SSA_OP_DEF) > + { > + flow_sensitive_info_storage storage; > + storage.save_and_clear (def); > + stack.safe_push (std::make_pair (def, storage)); > + } > } > } > > @@ -1111,6 +1112,116 @@ contains_hot_cold_predict (basic_block bb) > return false; > } > > +/* Callback for walk_gimple_op. If *TP is an SSA name, set its bit in the > + bitmap that the info field of DATA, a walk_stmt_info, points to. Clear > + *WALK_SUBTREES for types, decls and constants. Return NULL_TREE. */ > + > +static tree > +mark_ssa_name_r (tree *tp, int *walk_subtrees, void *data) > +{ > + if (TREE_CODE (*tp) == SSA_NAME) > + bitmap_set_bit ((bitmap) ((walk_stmt_info *) data)->info, > + SSA_NAME_VERSION (*tp)); > + else if (IS_TYPE_OR_DECL_P (*tp) || CONSTANT_CLASS_P (*tp)) > + *walk_subtrees = 0; > + return NULL_TREE; > +} > + > +/* Return true if STMT, a statement that cheap_middle_stmt_lhs accepts, is > + a cheap integer operation: a conversion, a bitwise operation, an > + addition, a subtraction, a negation or a bit operation call. */ > + > +static bool > +cheap_integer_stmt_p (gimple *stmt) > +{ > + tree type = TREE_TYPE (gimple_get_lhs (stmt)); > + if (!INTEGRAL_TYPE_P (type) || !SCALAR_INT_MODE_P (TYPE_MODE (type))) > + return false; > + if (is_gimple_call (stmt)) > + return true; > + switch (gimple_assign_rhs_code (stmt)) > + { > + CASE_CONVERT: > + type = TREE_TYPE (gimple_assign_rhs1 (stmt)); > + return INTEGRAL_TYPE_P (type) || POINTER_TYPE_P (type); > + case BIT_AND_EXPR: > + case BIT_IOR_EXPR: > + case BIT_XOR_EXPR: > + case BIT_NOT_EXPR: > + case PLUS_EXPR: > + case MINUS_EXPR: > + case NEGATE_EXPR: > + return true; > + default: > + return false; > + } > +} > + > +/* Mark in NEEDED the SSA names that the statements of BB, a middle basic > + block with NSTMTS statements, use to compute the names marked in > NEEDED. > + If NSTMTS is more than one, these statements, apart from nop > + conversions, must be cheap integer operations (see > cheap_integer_stmt_p), > + and *NMOVED is increased by their number. Return false if one of them > + is not cheap. */ > + > +static bool > +mark_needed_stmts (basic_block bb, int nstmts, bitmap needed, int *nmoved) > +{ > + for (gimple_stmt_iterator gsi = gsi_last_nondebug_bb (bb); > + !gsi_end_p (gsi); gsi_prev_nondebug (&gsi)) > + { > + gimple *stmt = gsi_stmt (gsi); > + tree lhs = gimple_get_lhs (stmt); > + if (!lhs || !bitmap_bit_p (needed, SSA_NAME_VERSION (lhs))) > + continue; > + if (nstmts > 1 && !gimple_nop_conversion_p (stmt)) > + { > + if (!cheap_integer_stmt_p (stmt)) > + return false; > + ++*nmoved; > + } > + ssa_op_iter it; > + tree use; > + FOR_EACH_SSA_TREE_OPERAND (use, stmt, it, SSA_OP_USE) > + bitmap_set_bit (needed, SSA_NAME_VERSION (use)); > + } > + return true; > +} > + > +/* Move the statements of BB, a middle basic block, that define the SSA > + names marked in NEEDED right before GSI, in their order, inserting > their > + names into INSERTED_EXPRS. Then use RESULT as the argument of PHI for > + the edge E from BB and remove the other statements of BB, so that BB is > + empty. */ > + > +static void > +move_needed_stmts (basic_block bb, edge e, gphi *phi, tree result, > + bitmap needed, gimple_stmt_iterator *gsi, > + auto_bitmap &inserted_exprs) > +{ > + for (gimple_stmt_iterator gsi_bb = gsi_after_labels (bb); > + !gsi_end_p (gsi_bb);) > + { > + gimple *stmt = gsi_stmt (gsi_bb); > + gsi_next (&gsi_bb); > + tree lhs = gimple_get_lhs (stmt); > + if (lhs && bitmap_bit_p (needed, SSA_NAME_VERSION (lhs))) > + move_stmt (stmt, gsi, inserted_exprs); > + } > + > + /* The statements of BB compute the argument of PHI. */ > + tree arg = gimple_phi_arg_def (phi, e->dest_idx); > + gcc_checking_assert (TREE_CODE (arg) == SSA_NAME); > + SET_USE (PHI_ARG_DEF_PTR (phi, e->dest_idx), result); > + if (gimple_bb (SSA_NAME_DEF_STMT (arg)) == bb) > + { > + auto_bitmap dce; > + bitmap_set_bit (dce, SSA_NAME_VERSION (arg)); > + simple_dce_from_worklist (dce); > + } > + gcc_checking_assert (empty_block_p (bb)); > +} > + > /* The function match_simplify_replacement does the main work of doing > the > replacement using match and simplify. Return true if the replacement > is done. > Otherwise return false. > @@ -1129,23 +1240,22 @@ match_simplify_replacement (basic_block cond_bb, > basic_block middle_bb, > edge true_edge, false_edge; > gimple_seq seq = NULL; > tree result; > - gimple *stmt_to_move = NULL; > - gimple *stmt_to_move_alt = NULL; > tree arg_true, arg_false; > > /* Special case A ? B : B as this will always simplify to B. */ > if (operand_equal_for_phi_arg_p (arg0, arg1)) > return false; > > - /* If the basic block only has a cheap preparation statement, > - allow it and move it once the transformation is done. */ > - if (!empty_bb_or_one_feeding_into_p (middle_bb, phi, stmt_to_move)) > + /* The middle basic blocks can only have cheap statements that compute > + the PHI arguments. The statements that the replacement needs are > + moved before the condition and the other ones are removed. */ > + int nstmts = middle_bb_stmts (middle_bb); > + if (nstmts < 0) > return false; > > - if (threeway_p > - && middle_bb != middle_bb_alt > - && !empty_bb_or_one_feeding_into_p (middle_bb_alt, phi, > - stmt_to_move_alt)) > + bool alt_p = threeway_p && middle_bb != middle_bb_alt; > + int nstmts_alt = alt_p ? middle_bb_stmts (middle_bb_alt) : 0; > + if (nstmts_alt < 0) > return false; > > /* Do not make conditional undefs unconditional. */ > @@ -1196,14 +1306,39 @@ match_simplify_replacement (basic_block cond_bb, > basic_block middle_bb, > > tree type = TREE_TYPE (gimple_phi_result (phi)); > { > - auto_flow_sensitive s1(stmt_to_move); > - auto_flow_sensitive s_alt(stmt_to_move_alt); > + auto_flow_sensitive s1 (middle_bb); > + auto_flow_sensitive s_alt (alt_p ? middle_bb_alt : NULL); > > result = gimple_simplify_phiopt (early_p, type, stmt, > arg_true, arg_false, > &seq); > } > > + /* Mark the SSA names that the replacement needs and the statements of > + the middle basic blocks that compute them. Statements moved out of a > + block with several statements must be cheap integer operations, and > + at most param_phiopt_max_moved_stmts of them. */ > + auto_bitmap needed; > + if (result) > + { > + if (TREE_CODE (result) == SSA_NAME) > + bitmap_set_bit (needed, SSA_NAME_VERSION (result)); > + walk_stmt_info wi = {}; > + wi.info = (bitmap) needed; > + for (gsi = gsi_start (seq); !gsi_end_p (gsi); gsi_next (&gsi)) > + walk_gimple_op (gsi_stmt (gsi), mark_ssa_name_r, &wi); > + int nmoved = 0; > + if (!mark_needed_stmts (middle_bb, nstmts, needed, &nmoved) > + || (alt_p > + && !mark_needed_stmts (middle_bb_alt, nstmts_alt, needed, > + &nmoved)) > + || nmoved > param_phiopt_max_moved_stmts) > + { > + gimple_seq_discard (seq); > + return false; > + } > + } > + > /* For early phiopt, we don't want to lose user generated predictors > if the phiopt is converting `if (a)` into `a` as that might > be jump threaded later on so we want to keep around the > @@ -1228,9 +1363,7 @@ match_simplify_replacement (basic_block cond_bb, > basic_block middle_bb, > ; > else if (contains_hot_cold_predict (middle_bb)) > return false; > - else if (threeway_p > - && middle_bb != middle_bb_alt > - && contains_hot_cold_predict (middle_bb_alt)) > + else if (alt_p && contains_hot_cold_predict (middle_bb_alt)) > return false; > } > > @@ -1241,7 +1374,7 @@ match_simplify_replacement (basic_block cond_bb, > basic_block middle_bb, > semantics (that's not IEEE max semantics). */ > if (!HONOR_NANS (type) && !HONOR_SIGNED_ZEROS (type)) > return false; > - if (stmt_to_move || stmt_to_move_alt) > + if (nstmts || nstmts_alt) > return false; > tree_code cmp = gimple_cond_code (stmt); > if (cmp != LT_EXPR && cmp != LE_EXPR > @@ -1287,10 +1420,16 @@ match_simplify_replacement (basic_block cond_bb, > basic_block middle_bb, > gsi_insert_seq_before (&gsi, seq, GSI_CONTINUE_LINKING); > } > > - /* If there was a statement to move, move it to right before > - the original conditional. */ > - move_stmt (stmt_to_move, &gsi, exprs_maybe_dce); > - move_stmt (stmt_to_move_alt, &gsi, exprs_maybe_dce); > + /* Move the statements of the middle basic blocks that the replacement > + needs before it and remove the other ones, so that > + replace_phi_edge_with_variable removes the empty blocks and resets > + their debug binds. */ > + if (nstmts) > + move_needed_stmts (middle_bb, e0, phi, result, needed, &gsi, > + exprs_maybe_dce); > + if (nstmts_alt) > + move_needed_stmts (middle_bb_alt, e1, phi, result, needed, &gsi, > + exprs_maybe_dce); > > replace_phi_edge_with_variable (cond_bb, e1, phi, result, > exprs_maybe_dce); > > diff --git a/gcc/tree-ssa-reassoc.cc b/gcc/tree-ssa-reassoc.cc > index dec64883891..6494ea5371c 100644 > --- a/gcc/tree-ssa-reassoc.cc > +++ b/gcc/tree-ssa-reassoc.cc > @@ -704,21 +704,6 @@ is_reassociable_op (gimple *stmt, enum tree_code > code, class loop *loop) > } > > > -/* Return true if STMT is a nop-conversion. */ > - > -static bool > -gimple_nop_conversion_p (gimple *stmt) > -{ > - if (gassign *ass = dyn_cast <gassign *> (stmt)) > - { > - if (CONVERT_EXPR_CODE_P (gimple_assign_rhs_code (ass)) > - && tree_nop_conversion_p (TREE_TYPE (gimple_assign_lhs (ass)), > - TREE_TYPE (gimple_assign_rhs1 (ass)))) > - return true; > - } > - return false; > -} > - > /* Given NAME, if NAME is defined by a unary operation OPCODE, return the > operand of the negate operation. Otherwise, return NULL. */ > > -- > 2.54.0 (Apple Git-157) > >
