> On 13 Oct 2026, at 10:26, Andrew Pinski <[email protected]> wrote:
> 
> 
> 
> 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. 
> 

Ok, I can drop that part.

> 
> - 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. 
> 

The reason for that was the diamond case. If one arm keeps statements that the 
result does not use, replace_phi_edge_with_variable cannot delete both blocks. 
The other arm then stays as an unconditional block, and its debug binds apply 
on both paths. Do you prefer to leave those statements to DCE, or should the 
arms be emptied some other way? Or did you mean something else by the wrong 
direction?

Thanks for the quick reply.
Kyrill

> - 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)
> 

Reply via email to