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

Reply via email to