> On 24 Oct 2026, at 06:56, Andrea Pinski <[email protected]>
> wrote:
>
> This adds support to match_simplify_replacement to allowing of the
> middle-bbs to have more than 1 statement in a few cases. If we are
> replacing the both middle bbs with a fully independent sequence we
> can handle that. If we are replacing with a sequence that depends
> on the first statement of either (or both) blocks, this allows that.
> If the result is defined by part or fully of one of the middle bbs.
> Then make the whole bb unconditional.
>
> Note the middle bbs still need to have statements that can't trap nor have
> side effects. And each of the statements need to feed into the next
> instruction and including into the phi. And there is a limited number of
> stmts is allowed. Also have an allow list of expressions type to be used
> after the first few stmts.
>
> This allows removal of the manual matching done in
> cond_removal_in_builtin_zero_pattern as those have been
> moved to match patterns back in r14-362-gc53237cefbad0c
> and r14-414-g078339fc04f5de.
>
> Changes since v1:
> * v2: Add allow list to feeding_bb_info and add a limit to
> the number of stmts to consider. Adds 2 testcases based on 2 pre-existing
> match patterns to show what can be done.
> * v3: Use walk_gimple_op instead of gimple_op walk. Add check for some
> possible undefined code when becoming unconditional (shifts, rotates,
> clz/ctz); add testcases for that too. Fix allow expressions detection
> and calls to be better.
> * v4: Fix up shift undefined behavior slightly more.
>
> Note Sashiko LLM analysis is not clean because the cases it is complaining
> about will be rejected later on or won't show up (e.g. CLZ with a one bit
> argument is folded aways or a shift for one bit will also be simplified away).
> The code left behind in one of the middle bb is dead; the patch which was
> posted earlier today will fix up those dead stmts; the ICE it mentions also
> will never show up because simple_dce will not delete stmts that are in use
> still and the phi will have been changed to reference only the ones from the
> condition (and dominating bb) and the dominator is not updated until
> cfgcleanup even so messes up that analysis too.
>
> Bootstrapped and tested on x86_64-linux-gnu.
>
With this version, the PR126035 testcase itself (lo ? __builtin_ctz (lo) : 32 +
__builtin_ctz (hi)) is no longer combined. feeding_bb_info rejects the else arm
because of had_undefined_stmt, before the match. The replacement there is an
independent .CTZ (x), and the arm statements are not executed. Could the
undefined-count restriction apply only when the arm statements are moved before
the condition, that is when the result uses them? With that check removed, the
double-word clz/ctz patterns in my series match again.
Thanks,
Kyrill
> gcc/ChangeLog:
>
> * tree-ssa-phiopt.cc (gsi_next_nondebug_nonnop_non_predict): New function.
> (enum bbfeeding_info): New enum.
> (empty_bb_or_one_feeding_into_p): Rename to ...
> (feeding_bb_info): This and support few different other cases.
> (auto_flow_sensitive): Increase the vect base size to 4.
> (auto_flow_sensitive::auto_flow_sensitive(gimple*)): Split out into ...
> (auto_flow_sensitive::save_stmt): This.
> (auto_flow_sensitive::auto_flow_sensitive(basic_block)): New constructor.
> (match_simplify_replacement): Support More than one statement in
> the middle BB.
> * params.opt (-param=phiopt-max-feeding-stmts=): New option.
> * doc/params.texi (phiopt-max-feeding-stmts): Document.
>
> PR tree-optimization/127166
>
> gcc/testsuite/ChangeLog:
>
> * gcc.dg/tree-ssa/ssa-dom-thread-16.c: Disable phiopt.
> * gcc.dg/tree-ssa/clamp-1.c: New test.
> * gcc.dg/tree-ssa/umax-sub-1.c: New test.
> * gcc.dg/tree-ssa/and-ctz-1.c: New test.
> * gcc.dg/tree-ssa/and-shift-1.c: New test.
>
> Signed-off-by: Andrea Pinski <[email protected]>
> ---
> gcc/doc/params.texi | 5 +
> gcc/params.opt | 4 +
> gcc/testsuite/gcc.dg/tree-ssa/and-ctz-1.c | 21 +
> gcc/testsuite/gcc.dg/tree-ssa/and-shift-1.c | 14 +
> gcc/testsuite/gcc.dg/tree-ssa/clamp-1.c | 25 ++
> .../gcc.dg/tree-ssa/ssa-dom-thread-16.c | 2 +-
> gcc/testsuite/gcc.dg/tree-ssa/umax-sub-1.c | 14 +
> gcc/tree-ssa-phiopt.cc | 421 ++++++++++++++----
> 8 files changed, 415 insertions(+), 91 deletions(-)
> create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/and-ctz-1.c
> create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/and-shift-1.c
> create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/clamp-1.c
> create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/umax-sub-1.c
>
> diff --git a/gcc/doc/params.texi b/gcc/doc/params.texi
> index 090af64a7e7..b26513a7ea1 100644
> --- a/gcc/doc/params.texi
> +++ b/gcc/doc/params.texi
> @@ -57,6 +57,11 @@ 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-feeding-stmts
> +@item phiopt-max-feeding-stmts
> +When deciding if a basic block is too big to phiopt, this decides the maximum
> +number of statements that feed into one another is too many. This excludes
> converts.
> +
> @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/params.opt b/gcc/params.opt
> index 29a5c7018a8..f0702f97625 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-feeding-stmts=
> +Common Joined UInteger Var(param_phiopt_max_feeding_stmts) Init(4)
> Optimization IntegerRange(1, 100) Param
> +Maximum number of statements can considered for each basic block excluding
> converts.
> +
> -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/and-ctz-1.c
> b/gcc/testsuite/gcc.dg/tree-ssa/and-ctz-1.c
> new file mode 100644
> index 00000000000..4408addfe3a
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-ssa/and-ctz-1.c
> @@ -0,0 +1,21 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fdump-tree-phiopt2-details -fdump-tree-optimized" } */
> +
> +int f(int a, int b)
> +{
> + if (a == 0)
> + return 0;
> + return a & __builtin_ctz(b);
> +}
> +
> +int f1(int a, int b)
> +{
> + if (a == 0)
> + return 0;
> + return a & __builtin_clz(b);
> +}
> +
> +/* Here we can't make the call to ctz/clz unconditional without undefined
> behavior. */
> +
> +/* { dg-final { scan-tree-dump-not "converted to straightline code"
> "phiopt2" } } */
> +/* { dg-final { scan-tree-dump-times "if " 2 "optimized" } } */
> diff --git a/gcc/testsuite/gcc.dg/tree-ssa/and-shift-1.c
> b/gcc/testsuite/gcc.dg/tree-ssa/and-shift-1.c
> new file mode 100644
> index 00000000000..f0e96e0b0d6
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-ssa/and-shift-1.c
> @@ -0,0 +1,14 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fdump-tree-phiopt2-details -fdump-tree-optimized" } */
> +
> +int f(int a, int b, int c)
> +{
> + if (a == 0)
> + return 0;
> + return a & (b >> c);
> +}
> +
> +/* Here we can't make the shift unconditional without undefined behavior. */
> +
> +/* { dg-final { scan-tree-dump-not "converted to straightline code"
> "phiopt2" } } */
> +/* { dg-final { scan-tree-dump "if " "optimized" } } */
> diff --git a/gcc/testsuite/gcc.dg/tree-ssa/clamp-1.c
> b/gcc/testsuite/gcc.dg/tree-ssa/clamp-1.c
> new file mode 100644
> index 00000000000..43239772ca0
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-ssa/clamp-1.c
> @@ -0,0 +1,25 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fdump-tree-phiopt2-details -fdump-tree-optimized" } */
> +
> +/* PR tree-optimization/127166 */
> +
> +unsigned char clip(int x)
> +{
> + unsigned u;
> + unsigned char r;
> + u = x;
> + r = x;
> + if (u > 255)
> + {
> + int n = -x;
> + int s = n >> (sizeof(int)*8 - 1);
> + r = s;
> + }
> + return r;
> +}
> +
> +
> +/* { 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" } } */
> +/* { dg-final { scan-tree-dump-not "if " "optimized" } } */
> diff --git a/gcc/testsuite/gcc.dg/tree-ssa/ssa-dom-thread-16.c
> b/gcc/testsuite/gcc.dg/tree-ssa/ssa-dom-thread-16.c
> index e8555f2d963..51b686b1de8 100644
> --- a/gcc/testsuite/gcc.dg/tree-ssa/ssa-dom-thread-16.c
> +++ b/gcc/testsuite/gcc.dg/tree-ssa/ssa-dom-thread-16.c
> @@ -1,5 +1,5 @@
> /* { dg-do compile } */
> -/* { dg-options "-O2 -fdump-tree-dom2-details -w --param
> logical-op-non-short-circuit=1 -fdisable-tree-threadfull1" } */
> +/* { dg-options "-O2 -fdump-tree-dom2-details -w --param
> logical-op-non-short-circuit=1 -fdisable-tree-threadfull1 -fno-ssa-phiopt" }
> */
> unsigned char
> validate_subreg (unsigned int offset, unsigned int isize, unsigned int osize,
> int zz, int qq)
> {
> diff --git a/gcc/testsuite/gcc.dg/tree-ssa/umax-sub-1.c
> b/gcc/testsuite/gcc.dg/tree-ssa/umax-sub-1.c
> new file mode 100644
> index 00000000000..c07eb4154d6
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-ssa/umax-sub-1.c
> @@ -0,0 +1,14 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fdump-tree-phiopt2-details -fdump-tree-optimized" } */
> +
> +signed f1 (signed x)
> +{
> + if (x == 0) return 0;
> + unsigned y = x;
> + y -= 1;
> + x = y;
> + return x;
> +}
> +
> +/* { dg-final { scan-tree-dump "converted to straightline code" "phiopt2" }
> } */
> +/* { dg-final { scan-tree-dump-not "if " "optimized" } } */
> diff --git a/gcc/tree-ssa-phiopt.cc b/gcc/tree-ssa-phiopt.cc
> index 53256d1f787..08b40897a60 100644
> --- a/gcc/tree-ssa-phiopt.cc
> +++ b/gcc/tree-ssa-phiopt.cc
> @@ -56,6 +56,7 @@ along with GCC; see the file COPYING3. If not see
> #include "gimple-predict.h"
> #include "alias.h"
> #include "tree-ssa-threadedge.h"
> +#include "gimple-walk.h"
>
> /* Return the singleton PHI in the SEQ of PHIs for edges E0 and E1. */
>
> @@ -832,6 +833,19 @@ gimple_simplify_phiopt (bool early_p, tree type, gimple
> *comp_stmt,
> return NULL;
> }
>
> +/* Like gsi_next_nondebug but also skips over NOPs and predicats. */
> +static void
> +gsi_next_nondebug_nonnop_nonpredict (gimple_stmt_iterator *i)
> +{
> + do
> + {
> + gsi_next_nondebug (i);
> + }
> + while (!gsi_end_p (*i)
> + && (gimple_code (**i) == GIMPLE_PREDICT
> + || gimple_code (**i) == GIMPLE_NOP));
> +}
> +
> /* one_feeding_comparison_into_p returns true if BB has one comparison
> statement and it sets STMT to that statement. Note the comparison can
> be trapping too. */
> @@ -896,110 +910,191 @@ 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)
> +
> +enum bbfeeding_info
> +{
> + invalid_bb,
> + empty_bb,
> + one_stmt_bb,
> + feeding_bb
> +};
> +
> +/* This returns empty_bb if bb was empty basic block.
> + or returns one_stmt_bb if it has one cheap preparation
> + statement that feeds into the PHI statement and it sets
> + STMT to that statement. Returns feeding_bb if the
> + basic block is not empty but has a sequence of statements
> + feeding into the phi that has no side effects. */
> +static bbfeeding_info
> +feeding_bb_info (basic_block bb, gimple *phi, gimple *&stmt)
> {
> stmt = nullptr;
> gimple *stmt_to_move = nullptr;
> tree lhs;
>
> if (empty_block_p (bb))
> - return true;
> + return empty_bb;
>
> if (!single_pred_p (bb))
> - return false;
> + return invalid_bb;
>
> /* 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;
> + return invalid_bb;
>
> gimple_stmt_iterator gsi;
>
> gsi = gsi_start_nondebug_after_labels_bb (bb);
> + int stmts = 0;
> + int stmts_minus_convert = 0;
> + bool non_allowed_stmt = false;
> + bool had_undefined_stmt = false;
> + bool has_call = false;
> while (!gsi_end_p (gsi))
> {
> - gimple *s = gsi_stmt (gsi);
> - gsi_next_nondebug (&gsi);
> - /* Skip over Predict and nop statements. */
> + gimple *s = *gsi;
> + gsi_next_nondebug_nonnop_nonpredict (&gsi);
> + // Skip over predict and nop statements which could happen
> + // if they are at the beginging.
> 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;
> -
> - if (gimple_could_trap_p (stmt_to_move)
> - || gimple_has_side_effects (stmt_to_move))
> - return false;
> -
> - ssa_op_iter it;
> - tree use;
> - FOR_EACH_SSA_TREE_OPERAND (use, stmt_to_move, it, SSA_OP_USE)
> - if (ssa_name_maybe_undef_p (use))
> - return false;
> -
> - /* Allow assignments but allow some builtin/internal calls.
> - As const calls don't match any of the above, yet they could
> - still have some side-effects - they could contain
> - gimple_could_trap_p statements, like floating point
> - exceptions or integer division by zero. See PR70586.
> - FIXME: perhaps gimple_has_side_effects or gimple_could_trap_p
> - 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))
> - {
> - if (!is_gimple_call (stmt_to_move))
> - return false;
> - combined_fn cfn = gimple_call_combined_fn (stmt_to_move);
> - switch (cfn)
> + stmts++;
> + stmts_minus_convert++;
> + if (stmts_minus_convert > param_phiopt_max_feeding_stmts)
> + return invalid_bb;
> + if (gimple_vuse (s))
> + return invalid_bb;
> + if (gimple_could_trap_p (s)
> + || gimple_has_side_effects (s))
> + return invalid_bb;
> + ssa_op_iter it;
> + tree use;
> + FOR_EACH_SSA_TREE_OPERAND (use, s, it, SSA_OP_USE)
> + if (ssa_name_maybe_undef_p (use))
> + return invalid_bb;
> +
> + /* Allow some assignments but allow some builtin/internal calls.
> + As const calls don't match any of the above, yet they could
> + still have some side-effects - they could contain
> + gimple_could_trap_p statements, like floating point
> + exceptions or integer division by zero. See PR70586.
> + FIXME: perhaps gimple_has_side_effects or gimple_could_trap_p
> + 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 (s))
> {
> - default:
> - return false;
> - CASE_CFN_BSWAP:
> - CASE_CFN_BITREVERSE:
> - CASE_CFN_FFS:
> - CASE_CFN_PARITY:
> - CASE_CFN_POPCOUNT:
> - CASE_CFN_CLZ:
> - CASE_CFN_CTZ:
> - case CFN_BUILT_IN_CLRSB:
> - case CFN_BUILT_IN_CLRSBL:
> - case CFN_BUILT_IN_CLRSBLL:
> - lhs = gimple_call_lhs (stmt_to_move);
> - break;
> + if (!is_gimple_call (s))
> + return invalid_bb;
> + combined_fn cfn = gimple_call_combined_fn (s);
> + switch (cfn)
> + {
> + default:
> + return invalid_bb;
> + CASE_CFN_CLZ:
> + CASE_CFN_CTZ:
> + if (gimple_call_num_args (s) != 2)
> + had_undefined_stmt = true;
> + // FALLTHRU
> + case CFN_BUILT_IN_CLRSB:
> + case CFN_BUILT_IN_CLRSBL:
> + case CFN_BUILT_IN_CLRSBLL:
> + CASE_CFN_BSWAP:
> + CASE_CFN_BITREVERSE:
> + CASE_CFN_FFS:
> + CASE_CFN_PARITY:
> + CASE_CFN_POPCOUNT:
> + lhs = gimple_call_lhs (s);
> + // A call can after some stmts but not with a non allowed one
> + // or another call.
> + if (non_allowed_stmt || has_call)
> + return invalid_bb;
> + has_call = true;
> + break;
> + }
> }
> + else
> + {
> + lhs = gimple_assign_lhs (s);
> + // Only allow a few different for the feeding list.
> + switch (gimple_assign_rhs_code (s))
> + {
> + // Always allow a convert.
> + CASE_CONVERT:
> + stmts_minus_convert --;
> + break;
> + case PLUS_EXPR:
> + case MINUS_EXPR:
> + case ABS_EXPR:
> + case ABSU_EXPR:
> + case BIT_IOR_EXPR:
> + case BIT_XOR_EXPR:
> + case BIT_AND_EXPR:
> + case MIN_EXPR:
> + case MAX_EXPR:
> + case NEGATE_EXPR:
> + break;
> + // Shifts with a (positive) constant shifter are on the allow list.
> + // Shifters which are above the precision are not well defined, so
> + // that is undefined stmt.
> + case LSHIFT_EXPR:
> + case RSHIFT_EXPR:
> + if (TREE_CODE (gimple_assign_rhs2 (s)) == INTEGER_CST
> + && wi::ltu_p (wi::to_wide (gimple_assign_rhs2 (s)),
> + element_precision (TREE_TYPE (lhs))))
> + break;
> + // Don't allow non-const shifters as they will cause
> + // undefined behavior
> + had_undefined_stmt = true;
> + break;
> + case LROTATE_EXPR:
> + case RROTATE_EXPR:
> + had_undefined_stmt = true;
> + // FALLTHRU
> + default:
> + // Allow all kind of expressions after the first
> + // convert but not with other stmts before hand.
> + if (stmts_minus_convert > 1 && non_allowed_stmt)
> + return invalid_bb;
> + if (has_call)
> + return invalid_bb;
> + non_allowed_stmt = true;
> + break;
> + }
> + }
> +
> + gimple *use_stmt;
> + use_operand_p use_p;
> +
> + /* Allow only a statement which feeds into the next stmt
> + or the phi (when last statement). */
> + if (!lhs || TREE_CODE (lhs) != SSA_NAME
> + || !single_imm_use (lhs, &use_p, &use_stmt)
> + || use_stmt != (gsi_end_p (gsi) ? phi : *gsi ))
> + return invalid_bb;
> + if (!stmt_to_move)
> + stmt_to_move = s;
> }
> - else
> - lhs = gimple_assign_lhs (stmt_to_move);
>
> - gimple *use_stmt;
> - use_operand_p use_p;
> + // CLZ/CTZ is undefined at 0 (or a shift with a non constant)
> + // so if other stmts besides converts moving it will cause
> + // undefined behavior.
> + if (had_undefined_stmt && stmts_minus_convert != 1)
> + return invalid_bb;
>
> - /* 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;
> + // If there are any questionable stmts, make sure there are only
> + // that one (and possible converts).
> + if (stmts_minus_convert != 1 && non_allowed_stmt)
> + return invalid_bb;
>
> + /* The only statement here was a Predict or a nop statement
> + so return 1. */
> + if (stmts == 0)
> + return empty_bb;
> stmt = stmt_to_move;
> - return true;
> + return stmts > 1 ? feeding_bb : one_stmt_bb;
> }
>
> /* Move STMT to before GSI and insert its defining
> @@ -1031,14 +1126,17 @@ move_stmt (gimple *stmt, gimple_stmt_iterator *gsi,
> auto_bitmap &inserted_exprs)
> }
>
> /* RAII style class to temporarily remove flow sensitive
> - from ssa names defined by a gimple statement. */
> + from ssa names defined by a gimple statement or
> + a whole 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;
> + auto_vec<std::pair<tree, flow_sensitive_info_storage>, 4> stack;
> + void save_stmt (gimple *s);
> };
>
> /* Constructor for auto_flow_sensitive. Saves
> @@ -1050,6 +1148,15 @@ auto_flow_sensitive::auto_flow_sensitive (gimple *s)
> {
> if (!s)
> return;
> + save_stmt (s);
> +}
> +
> +/* Saves off the ssa names' flow sensitive information
> + that was defined by gimple statement S and
> + resets it to be non-flow based ones. */
> +void
> +auto_flow_sensitive::save_stmt (gimple *s)
> +{
> ssa_op_iter it;
> tree def;
> FOR_EACH_SSA_TREE_OPERAND (def, s, it, SSA_OP_DEF)
> @@ -1060,6 +1167,21 @@ auto_flow_sensitive::auto_flow_sensitive (gimple *s)
> }
> }
>
> +/* Constructor for auto_flow_sensitive. Saves
> + off the ssa names' flow sensitive information
> + that was defined in the BB and
> + resets it to be non-flow based ones. */
> +
> +auto_flow_sensitive::auto_flow_sensitive (basic_block bb)
> +{
> + if (!bb)
> + return;
> + gimple_stmt_iterator gsi;
> + gsi = gsi_start_nondebug_after_labels_bb (bb);
> + for (; !gsi_end_p (gsi); gsi_next_nondebug (&gsi))
> + save_stmt (*gsi);
> +}
> +
> /* Deconstructor, restores the flow sensitive information
> for the SSA names that had been saved off. */
>
> @@ -1118,14 +1240,20 @@ match_simplify_replacement (basic_block cond_bb,
> basic_block middle_bb,
>
> /* 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))
> + bbfeeding_info middle_bb_info = feeding_bb_info (middle_bb,
> + phi, stmt_to_move);
> + if (middle_bb_info == invalid_bb)
> return false;
>
> + bbfeeding_info middle_bb_alt_info = invalid_bb;
> if (threeway_p
> - && middle_bb != middle_bb_alt
> - && !empty_bb_or_one_feeding_into_p (middle_bb_alt, phi,
> - stmt_to_move_alt))
> - return false;
> + && middle_bb != middle_bb_alt)
> + {
> + middle_bb_alt_info = feeding_bb_info (middle_bb_alt, phi,
> + stmt_to_move_alt);
> + if (middle_bb_alt_info == invalid_bb)
> + return false;
> + }
>
> /* Do not make conditional undefs unconditional. */
> if ((TREE_CODE (arg0) == SSA_NAME
> @@ -1175,12 +1303,15 @@ 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);
> + basic_block other_bb = nullptr;
> + if (middle_bb_alt_info >= one_stmt_bb)
> + other_bb = middle_bb_alt;
> + auto_flow_sensitive s_alt(other_bb);
>
> result = gimple_simplify_phiopt (early_p, type, stmt,
> arg_true, arg_false,
> - &seq);
> + &seq);
> }
>
> /* For early phiopt, we don't want to lose user generated predictors
> @@ -1220,7 +1351,8 @@ 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 (middle_bb_info >= one_stmt_bb
> + || middle_bb_alt_info >= one_stmt_bb)
> return false;
> tree_code cmp = gimple_cond_code (stmt);
> if (cmp != LT_EXPR && cmp != LE_EXPR
> @@ -1242,11 +1374,120 @@ match_simplify_replacement (basic_block cond_bb,
> basic_block middle_bb,
> statistics_counter_event (cfun, "Non-IEEE FP MIN/MAX PHI replacement",
> 1);
> }
> - if (dump_file && (dump_flags & TDF_FOLDING))
> - fprintf (dump_file, "accepted the phiopt match-simplify.\n");
>
> auto_bitmap exprs_maybe_dce;
>
> + /* One of the middle BBs had more than one statements. */
> + if (middle_bb_info == feeding_bb
> + || middle_bb_alt_info == feeding_bb)
> + {
> + gimple_stmt_iterator gsi = gsi_start (seq);
> + bool ok = true;
> + walk_stmt_info wi = {};
> + struct op_info {
> + gimple *stmt_to_move;
> + gimple *stmt_to_move_alt;
> + basic_block middle_bb;
> + basic_block middle_bb_alt;
> + bbfeeding_info middle_bb_info;
> + bbfeeding_info middle_bb_alt_info;
> + } info = { stmt_to_move, stmt_to_move_alt,
> + middle_bb, middle_bb_alt,
> + middle_bb_info, middle_bb_alt_info };
> + wi.info = &info;
> + /* Check to see if any of the new sequence uses the definitions
> + from the middle bbs. */
> + for (gsi = gsi_start (seq); !gsi_end_p (gsi); gsi_next (&gsi))
> + {
> + tree use;
> + use = walk_gimple_op (*gsi,
> + [](tree *tp, int *, void *data) -> tree
> + {
> + walk_stmt_info *wi = (walk_stmt_info*)data;
> + op_info *info = (op_info *)wi->info;
> + tree use = *tp;
> + if (!use || TREE_CODE (use) != SSA_NAME)
> + return NULL_TREE;
> + gimple *us = SSA_NAME_DEF_STMT (use);
> + if (!us)
> + return NULL_TREE;
> + if (us == info->stmt_to_move
> + || us == info->stmt_to_move_alt)
> + return NULL_TREE;
> + basic_block use_bb = gimple_bb (us);
> + // If an operand is defined from the middle bb
> + // where there was more one statement, reject it.
> + if ((info->middle_bb_info == feeding_bb
> + && use_bb == info->middle_bb)
> + || (info->middle_bb_alt_info == feeding_bb
> + && use_bb == info->middle_bb_alt))
> + return use;
> + return NULL_TREE;
> + }, &wi);
> + if (use)
> + {
> + ok = false;
> + break;
> + }
> + }
> + if (!ok)
> + {
> + gimple_seq_discard (seq);
> + if (dump_file && (dump_flags & TDF_FOLDING))
> + fprintf (dump_file, "rejected the phiopt match-simplify for having more
> stmts.\n");
> + return false;
> + }
> + statistics_counter_event (cfun, "unconditionalized one middle bb
> phiopt",
> + 1);
> + // If the result is the one of the arguments or a partial
> + // defined in the middle bb, then make those statements unconditional.
> + if (TREE_CODE (result) == SSA_NAME
> + && (middle_bb_info == feeding_bb
> + || middle_bb_alt_info == feeding_bb))
> + {
> + gimple *us = SSA_NAME_DEF_STMT (result);
> + if (us && ((middle_bb_info == feeding_bb
> + && gimple_bb (us) == middle_bb)
> + || (middle_bb_alt_info == feeding_bb
> + && gimple_bb (us) == middle_bb_alt)))
> + {
> + gimple_stmt_iterator middle_gsi;
> + basic_block use_bb = gimple_bb (us);
> + middle_gsi = gsi_start_nondebug_after_labels_bb (use_bb);
> +
> + // Move the statements one at a time until reach the last
> + // statement or the statement that is referenced from the result.
> + while (!gsi_end_p (middle_gsi))
> + {
> + // Skip over nop or predicates
> + // Also don't move debug statements
> + if (gimple_code (*middle_gsi) == GIMPLE_NOP
> + || gimple_code (*middle_gsi) == GIMPLE_PREDICT)
> + {
> + gsi_next_nondebug_nonnop_nonpredict (&middle_gsi);
> + continue;
> + }
> + gsi = gsi_last_bb (cond_bb);
> + gimple *s = *middle_gsi;
> + move_stmt (s, &gsi, exprs_maybe_dce);
> +
> + // Reached the last statement that is refenced,
> + // all the rest would be DCEd anyways.
> + if (s == us)
> + break;
> + middle_gsi = gsi_start_nondebug_after_labels_bb (use_bb);
> + }
> + stmt_to_move = nullptr;
> + stmt_to_move_alt = nullptr;
> + statistics_counter_event (cfun, "unconditionalized partial middle bb
> phiopt",
> + 1);
> + }
> + }
> + }
> +
> + if (dump_file && (dump_flags & TDF_FOLDING))
> + fprintf (dump_file, "accepted the phiopt match-simplify.\n");
> +
> /* Mark the cond statements' lhs/rhs as maybe dce. */
> if (TREE_CODE (gimple_cond_lhs (stmt)) == SSA_NAME
> && !SSA_NAME_IS_DEFAULT_DEF (gimple_cond_lhs (stmt)))
> @@ -1272,7 +1513,7 @@ 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
> + /* If there was one 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);
> --
> 2.43.0
>