https://gcc.gnu.org/g:c43fed44d9ac34ce88b261cbc0da65e5d3db7132

commit r17-1507-gc43fed44d9ac34ce88b261cbc0da65e5d3db7132
Author: Andrew Pinski <[email protected]>
Date:   Thu Jun 4 16:09:41 2026 -0700

    phiopt: Allow factoring out of more than just single operand operations
    
    This takes https://gcc.gnu.org/pipermail/gcc-patches/2026-June/719384.html
    and merge it into factor_out_conditional_operation. Also expands it to
    allow for more than just unary and binary operands.
    It handles as similar as what ifcvt does in factor_out_operators
    but rejects some cases due to those not being profitable.
    The cases which are not profitable:
    * pointer plus in early with constant operand 1 (except if equal).
    * division/mod with constant operand 1
    * Complex expr, it would cause to lose an unitialization warning
    (gcc.dg/uninit-17.c)
    
    some cases needed to be rejected for validity (copied from ifcvt):
    * BIT_FIELD_REF/BIT_INSERT_EXPR (non first operand)
    * VEC_PERM_EXPR with constant operand 2
    
    Notes on the testcase changes:
    The recip-*.c testcases need to be disable phiopt since it removes
    a division in some cases which causes the recip pass not to run.
    
    slsr-12.c and slsr-34.c need to be xfailed. SLSR pass is mostly
    in maintaince mode and is not getting improved.
    
    pr122629-1.c and vect-reduc-cond-2.c are now handled in phiopt
    rather than ifcvt.
    
    cinc_common_1.c is xfailed because of missing pattern in the aarch64
    backend, PR112304.
    
    fuse_cmp_csel.c needed to be updated since the add is now after the
    cmp/csel pair and ira puts the constants formation inbetween the cmp/csel.
    fuse_cmp_csel-1.c is new version where there is no constant formation.
    
    changes since v1:
    * v2: Fix some comments. Add CEIL_MOD_EXPR and ROUND_MOD_EXPR to
    is_divide_or_mod_p. Remove operand_equal from POINTER_PLUS case.
    xfail cinc_common_1.c. Fixed up fuse_cmp_csel.c testcase.
    * v3: Fix up cost model, was only calling is_factor_profitable on
    the different operands when it needs to be on all operands.
    * v4: Move is_divide_or_mod_p to tree.h with a rename to 
int_divide_or_mod_p.
    Move find_different_opnum to gimple-match-exports.cc/gimple-match.h.
    
            PR tree-optimization/125557
            PR tree-optimization/64700
            PR tree-optimization/29144
            PR tree-optimization/94274
    gcc/ChangeLog:
    
            * tree-if-conv.cc (find_different_opnum): Move to ...
            * gimple-match-exports.cc (find_different_opnum): Here.
            * gimple-match.h (find_different_opnum): New decl.
            * tree-ssa-phiopt.cc (is_factor_profitable): Take
            gimple_match_op instead of one operand.
            Rearrange the code to check the lifetime of the operands last.
            (factor_out_conditional_operation): Handle operands > 1,
            including operands communitive operands. Add early_p argument
            for costing. Update call of is_factor_profitable.
            (pass_phiopt::execute): Pass early_p to
            factor_out_conditional_operation.
            * tree.h (int_divide_or_mod_p): New function.
    
    gcc/testsuite/ChangeLog:
    
            * gcc.dg/tree-ssa/recip-3.c: Disable phiopt since it removes
            one division and recip pass needs 3.
            * gcc.dg/tree-ssa/recip-5.c: Likewise.
            * gcc.dg/tree-ssa/recip-6.c: Likewise.
            * gcc.dg/tree-ssa/recip-7.c: Likewise.
            * gcc.dg/tree-ssa/slsr-12.c: xfail.
            * gcc.dg/tree-ssa/slsr-34.c: Likewise.
            * gcc.dg/tree-ssa/pr122629-1.c: Update to scan phiopt1.
            * gcc.dg/vect/vect-reduc-cond-2.c: Likewise.
            * gcc.dg/tree-ssa/phi-factor-binary-1.c: New test.
            * gcc.dg/tree-ssa/phi-factor-binary-2.c: New test.
            * gcc.target/aarch64/phi-factor-binary-1.c: New test.
            * gcc.target/aarch64/cinc_common_1.c: xfail.
            * gcc.target/aarch64/fuse_cmp_csel.c: xfail.
            * gcc.target/aarch64/fuse_cmp_csel-1.c: New test.
    
    Co-authored-by: Kyrylo Tkachov <[email protected]>
    Signed-off-by: Andrew Pinski <[email protected]>

Diff:
---
 gcc/gimple-match-exports.cc                        |  79 +++++++++++
 gcc/gimple-match.h                                 |   3 +
 .../gcc.dg/tree-ssa/phi-factor-binary-1.c          |  20 +++
 .../gcc.dg/tree-ssa/phi-factor-binary-2.c          |  41 ++++++
 gcc/testsuite/gcc.dg/tree-ssa/pr122629-1.c         |   4 +-
 gcc/testsuite/gcc.dg/tree-ssa/recip-3.c            |   3 +
 gcc/testsuite/gcc.dg/tree-ssa/recip-5.c            |   2 +-
 gcc/testsuite/gcc.dg/tree-ssa/recip-6.c            |   2 +-
 gcc/testsuite/gcc.dg/tree-ssa/recip-7.c            |   2 +-
 gcc/testsuite/gcc.dg/tree-ssa/slsr-12.c            |   2 +-
 gcc/testsuite/gcc.dg/tree-ssa/slsr-34.c            |   2 +-
 gcc/testsuite/gcc.dg/vect/vect-reduc-cond-2.c      |   4 +-
 gcc/testsuite/gcc.target/aarch64/cinc_common_1.c   |   5 +-
 gcc/testsuite/gcc.target/aarch64/fuse_cmp_csel-1.c |  35 +++++
 gcc/testsuite/gcc.target/aarch64/fuse_cmp_csel.c   |  14 +-
 .../gcc.target/aarch64/phi-factor-binary-1.c       |  60 ++++++++
 gcc/tree-if-conv.cc                                |  79 -----------
 gcc/tree-ssa-phiopt.cc                             | 157 +++++++++++++++------
 gcc/tree.h                                         |  21 +++
 19 files changed, 398 insertions(+), 137 deletions(-)

diff --git a/gcc/gimple-match-exports.cc b/gcc/gimple-match-exports.cc
index 2cbdc9833817..74f2c5f19bcf 100644
--- a/gcc/gimple-match-exports.cc
+++ b/gcc/gimple-match-exports.cc
@@ -1413,3 +1413,82 @@ get_conditional_internal_fn (code_helper code, tree type)
   auto cfn = combined_fn (code);
   return get_conditional_internal_fn (associated_internal_fn (cfn, type));
 }
+
+/* Find the operand which is different between ARG0_OP and ARG1_OP.
+   Returns the operand num where the difference is.
+   Set NEWARG0 and NEWARG1 from the different argument.
+   Returns -1 if none is found.
+   If ARG0_OP/ARG1_OP is commutative also try swapping the
+   two commutative operands and return the operand number where
+   the difference happens in ARG0_OP. */
+
+int
+find_different_opnum (const gimple_match_op &arg0_op,
+                     const gimple_match_op &arg1_op,
+                     tree *new_arg0, tree *new_arg1)
+{
+  unsigned opnum = -1;
+  unsigned first;
+  first = first_commutative_argument (arg1_op.code, arg1_op.type);
+  for (unsigned i = 0; i < arg0_op.num_ops; i++)
+    {
+      if (!operand_equal_for_phi_arg_p (arg0_op.ops[i],
+                                       arg1_op.ops[i]))
+       {
+         /* Can handle only one non equal operand. */
+         if (opnum != -1u)
+           {
+             /* Though if opnum is right before i and opnum is equal
+                to the first communtative argument, handle communtative
+                specially. */
+             if (i == opnum + 1 && opnum == first)
+               goto commutative;
+             return -1;
+           }
+         opnum = i;
+       }
+  }
+  /* If all operands are equal only do this is there was single
+     operand.  */
+  if (opnum == -1u)
+    {
+      if (arg0_op.num_ops != 1)
+       return -1;
+      opnum = 0;
+    }
+  *new_arg0 = arg0_op.ops[opnum];
+  *new_arg1 = arg1_op.ops[opnum];
+  return opnum;
+
+/* Handle commutative operations. */
+commutative:
+  gcc_assert (first != -1u);
+
+  /* Check the rest of the arguments to make sure they are the same. */
+  for (unsigned i = first + 2; i < arg0_op.num_ops; i++)
+    if (!operand_equal_for_phi_arg_p (arg0_op.ops[i],
+                                     arg1_op.ops[i]))
+      return -1;
+
+  /* If the arg0[first+1] and arg1[first] are the same
+     then the one which is different is arg0[first] and arg1[first+1]
+     return first since this is based on arg0.  */
+  if (operand_equal_for_phi_arg_p (arg0_op.ops[first + 1],
+                                  arg1_op.ops[first]))
+    {
+       *new_arg0 = arg0_op.ops[first];
+       *new_arg1 = arg1_op.ops[first + 1];
+       return first;
+    }
+  /* If the arg0[first] and arg1[first+1] are the same
+     then the one which is different is arg0[first+1] and arg1[first]
+     return first+1 since this is based on arg0.  */
+  if (operand_equal_for_phi_arg_p (arg0_op.ops[first],
+                                  arg1_op.ops[first + 1]))
+    {
+       *new_arg0 = arg0_op.ops[first + 1];
+       *new_arg1 = arg1_op.ops[first];
+       return first + 1;
+    }
+  return -1;
+}
diff --git a/gcc/gimple-match.h b/gcc/gimple-match.h
index 83d4d7060538..8ff58e8cd7f2 100644
--- a/gcc/gimple-match.h
+++ b/gcc/gimple-match.h
@@ -426,5 +426,8 @@ bool directly_supported_p (code_helper, tree, tree,
 #endif
 
 internal_fn get_conditional_internal_fn (code_helper, tree);
+int find_different_opnum (const gimple_match_op &arg0_op,
+                         const gimple_match_op &arg1_op,
+                         tree *new_arg0, tree *new_arg1);
 
 #endif  /* GCC_GIMPLE_MATCH_H */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/phi-factor-binary-1.c 
b/gcc/testsuite/gcc.dg/tree-ssa/phi-factor-binary-1.c
new file mode 100644
index 000000000000..bfcae888125a
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/phi-factor-binary-1.c
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-phiopt1-details" } */
+
+/* PR tree-optimization/125557.  Both arms of the diamond apply the same binary
+   operation sharing one operand (* c), so phi-opt factors it out: the join
+   becomes t = PHI <a, b>; x = t * c -- one multiply at the merge instead of
+   one in each arm, and no extra PHI.  */
+
+int
+f (int cond, int a, int b, int c)
+{
+  int x;
+  if (cond)
+    x = a * c;
+  else
+    x = b * c;
+  return x;
+}
+
+/* { dg-final { scan-tree-dump " changed to factor operation out from 
COND_EXP" "phiopt1" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/phi-factor-binary-2.c 
b/gcc/testsuite/gcc.dg/tree-ssa/phi-factor-binary-2.c
new file mode 100644
index 000000000000..eb7669b664bb
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/phi-factor-binary-2.c
@@ -0,0 +1,41 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-phiopt1-details" } */
+
+/* tree-optimization/125557.  factor_out_binary_common_operand handles more 
than
+   one operation kind and, when several join PHIs share the differing operand,
+   factors each of them -- collapsing the arms to a single select.  */
+
+/* Two PHIs share the differing operand a/b: the multiply and the add are each
+   factored out, so both arms reduce to one select of a/b.  */
+void
+f2 (int cond, int a, int b, int c, int d, int *p, int *q)
+{
+  int x, y;
+  if (cond)
+    {
+      x = a * c;
+      y = a + d;
+    }
+  else
+    {
+      x = b * c;
+      y = b + d;
+    }
+  *p = x;
+  *q = y;
+}
+
+/* A shift is factored just like the arithmetic ops.  */
+unsigned long
+f3 (int cond, unsigned long a, unsigned long b, int s)
+{
+  unsigned long x;
+  if (cond)
+    x = a << s;
+  else
+    x = b << s;
+  return x;
+}
+
+/* f2 factors twice (the * and the +), f3 once (the <<).  */
+/* { dg-final { scan-tree-dump-times " changed to factor operation out from 
COND_EXP" 3 "phiopt1" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr122629-1.c 
b/gcc/testsuite/gcc.dg/tree-ssa/pr122629-1.c
index a80d4a1990b1..54f7bf11d482 100644
--- a/gcc/testsuite/gcc.dg/tree-ssa/pr122629-1.c
+++ b/gcc/testsuite/gcc.dg/tree-ssa/pr122629-1.c
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-O2 -fdump-tree-ifcvt-details" } */
+/* { dg-options "-O2 -fdump-tree-ifcvt-details -fdump-tree-phiopt-details" } */
 /* PR tree-optimization/122629 */
 
 typedef int ix4 __attribute__((vector_size(4*sizeof(int))));
@@ -33,4 +33,4 @@ int g(ix4 *a, int l, int *b, ix4 *c)
 }
 
 /* Make sure BIT_INSERT_EXPR/BIT_FIELD_REF is still factored out for the case 
if operand 0 is different. */
-/* { dg-final { scan-tree-dump-times "changed to factor operation out from" 2 
"ifcvt" } } */
+/* { dg-final { scan-tree-dump-times "changed to factor operation out from" 2 
"phiopt1" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/recip-3.c 
b/gcc/testsuite/gcc.dg/tree-ssa/recip-3.c
index 036f32a9c338..3c6df8d07267 100644
--- a/gcc/testsuite/gcc.dg/tree-ssa/recip-3.c
+++ b/gcc/testsuite/gcc.dg/tree-ssa/recip-3.c
@@ -5,6 +5,9 @@
    to optimize a sequence.  With a FP enabled ranger, we eliminate one of them
    earlier, causing the pass to skip this optimization.  */
 /* { dg-additional-options "-fno-thread-jumps -fno-tree-dominator-opts" } */
+/* PHI-OPT will factor out the `/d` from the `?:` expression which means there 
will
+   only be 2 `/d` left so disable that. */
+/* { dg-additional-options "-fno-ssa-phiopt" } */
 
 double F[5] = { 0.0, 0.0 }, e;
 
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/recip-5.c 
b/gcc/testsuite/gcc.dg/tree-ssa/recip-5.c
index 6ac0559bc7cc..f05cfe866cce 100644
--- a/gcc/testsuite/gcc.dg/tree-ssa/recip-5.c
+++ b/gcc/testsuite/gcc.dg/tree-ssa/recip-5.c
@@ -1,4 +1,4 @@
-/* { dg-options "-O1 -funsafe-math-optimizations -ftrapping-math 
-fdump-tree-recip -fdump-tree-optimized" } */
+/* { dg-options "-O1 -funsafe-math-optimizations -ftrapping-math 
-fdump-tree-recip -fdump-tree-optimized -fno-ssa-phiopt" } */
 /* { dg-do compile } */
 /* { dg-warning "'-fassociative-math' disabled" "" { target *-*-* } 0 } */
 
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/recip-6.c 
b/gcc/testsuite/gcc.dg/tree-ssa/recip-6.c
index b5d537a6ccdc..c172fb34fcf0 100644
--- a/gcc/testsuite/gcc.dg/tree-ssa/recip-6.c
+++ b/gcc/testsuite/gcc.dg/tree-ssa/recip-6.c
@@ -1,4 +1,4 @@
-/* { dg-options "-O1 -funsafe-math-optimizations -fno-trapping-math 
-fdump-tree-recip" } */
+/* { dg-options "-O1 -funsafe-math-optimizations -fno-trapping-math 
-fdump-tree-recip -fno-ssa-phiopt" } */
 /* { dg-do compile } */
 
 /* Test inserting in a block that does not contain a division.  */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/recip-7.c 
b/gcc/testsuite/gcc.dg/tree-ssa/recip-7.c
index 13fca0b0e09e..0e205d178913 100644
--- a/gcc/testsuite/gcc.dg/tree-ssa/recip-7.c
+++ b/gcc/testsuite/gcc.dg/tree-ssa/recip-7.c
@@ -1,4 +1,4 @@
-/* { dg-options "-O1 -funsafe-math-optimizations -fno-trapping-math 
-fdump-tree-recip" } */
+/* { dg-options "-O1 -funsafe-math-optimizations -fno-trapping-math 
-fdump-tree-recip -fno-ssa-phiopt" } */
 /* { dg-do compile } */
 
 /* Test inserting in a block that does not contain a division.  */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/slsr-12.c 
b/gcc/testsuite/gcc.dg/tree-ssa/slsr-12.c
index f92b70ffafbb..f50c2020157b 100644
--- a/gcc/testsuite/gcc.dg/tree-ssa/slsr-12.c
+++ b/gcc/testsuite/gcc.dg/tree-ssa/slsr-12.c
@@ -26,4 +26,4 @@ f (int s, int c)
   return x;
 }
 
-/* { dg-final { scan-tree-dump-times " \\* " 3 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " \\* " 3 "optimized" { xfail *-*-* } } } 
*/
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/slsr-34.c 
b/gcc/testsuite/gcc.dg/tree-ssa/slsr-34.c
index d7ede2efe8ce..31d59b84d497 100644
--- a/gcc/testsuite/gcc.dg/tree-ssa/slsr-34.c
+++ b/gcc/testsuite/gcc.dg/tree-ssa/slsr-34.c
@@ -38,5 +38,5 @@ f (int c, int i)
   return x;
 }
 
-/* { dg-final { scan-tree-dump-times " \\* " 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " \\* " 1 "optimized" { xfail *-*-* } } } 
*/
 /* { dg-final { scan-tree-dump-times "PHI" 2 "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/vect/vect-reduc-cond-2.c 
b/gcc/testsuite/gcc.dg/vect/vect-reduc-cond-2.c
index 126a50f3e56f..aa204d1ab67b 100644
--- a/gcc/testsuite/gcc.dg/vect/vect-reduc-cond-2.c
+++ b/gcc/testsuite/gcc.dg/vect/vect-reduc-cond-2.c
@@ -1,6 +1,6 @@
 /* { dg-require-effective-target vect_int } */
 /* { dg-require-effective-target vect_condition } */
-/* { dg-additional-options "-fdump-tree-ifcvt-details" } */
+/* { dg-additional-options "-fdump-tree-phiopt1-details" } */
 
 #include <stdarg.h>
 #include "tree-vect.h"
@@ -59,4 +59,4 @@ int main (void)
 }
 
 /* { dg-final { scan-tree-dump-times "vectorized 1 loops" 1 "vect" { xfail { 
vect_no_int_add } } } } */
-/* { dg-final { scan-tree-dump-times "changed to factor operation out from 
COND_EXPR" 2 "ifcvt" } } */
+/* { dg-final { scan-tree-dump-times "changed to factor operation out from 
COND_EXPR" 2 "phiopt1" } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/cinc_common_1.c 
b/gcc/testsuite/gcc.target/aarch64/cinc_common_1.c
index f93364f74ba0..030adf3e897a 100644
--- a/gcc/testsuite/gcc.target/aarch64/cinc_common_1.c
+++ b/gcc/testsuite/gcc.target/aarch64/cinc_common_1.c
@@ -27,8 +27,9 @@ bardi (long long x)
   return x > 100 ? x + 4 : x + 3;
 }
 
-/* { dg-final { scan-assembler-times "cs?inc\tw\[0-9\]*" 2 } } */
-/* { dg-final { scan-assembler-times "cs?inc\tx\[0-9\]*" 2 } } */
+/* xfailed due to PR112304. */
+/* { dg-final { scan-assembler-times "cs?inc\tw\[0-9\]*" 2 { xfail *-*-* } } } 
*/
+/* { dg-final { scan-assembler-times "cs?inc\tx\[0-9\]*" 2 { xfail *-*-* } } } 
*/
 
 int
 main (void)
diff --git a/gcc/testsuite/gcc.target/aarch64/fuse_cmp_csel-1.c 
b/gcc/testsuite/gcc.target/aarch64/fuse_cmp_csel-1.c
new file mode 100644
index 000000000000..9fcca5043ece
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/fuse_cmp_csel-1.c
@@ -0,0 +1,35 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mcpu=neoverse-v2" } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+/*
+** f1:
+**     ...
+**     cmp     w[0-9]+, w[0-9]+
+**     csel    w[0-9]+, w[0-9]+, w[0-9]+, gt
+**     add     w[0-9]+, w[0-9]+, w[0-9]+
+**     ret
+*/
+int f1 (int a, int b, int c, int d, int e)
+{
+  int cmp = a > b;
+  int add1 = c + d;
+  int add2 = c + e;
+  return cmp ? add1 : add2;
+}
+
+/*
+** f2:
+**     ...
+**     cmp     x[0-9]+, x[0-9]+
+**     csel    x[0-9]+, x[0-9]+, x[0-9]+, gt
+**     add     x[0-9]+, x[0-9]+, x[0-9]+
+**     ret
+*/
+long long f2 (long long a, long long b, long long c, long long d, long long e)
+{
+ long long cmp = a > b;
+  long long add1 = c + d;
+  long long add2 = c + e;
+  return cmp ? add1 : add2;
+}
diff --git a/gcc/testsuite/gcc.target/aarch64/fuse_cmp_csel.c 
b/gcc/testsuite/gcc.target/aarch64/fuse_cmp_csel.c
index 85f302bab983..be0fb8f935de 100644
--- a/gcc/testsuite/gcc.target/aarch64/fuse_cmp_csel.c
+++ b/gcc/testsuite/gcc.target/aarch64/fuse_cmp_csel.c
@@ -2,11 +2,16 @@
 /* { dg-options "-O2 -mcpu=neoverse-v2" } */
 /* { dg-final { check-function-bodies "**" "" } } */
 
+/* IRA moves constant moves between cmp and csel
+   and never recovers since they use the same register
+   #s. */
+
 /*
-** f1:
+** f1: { xfail *-*-* }
 **     ...
 **     cmp     w[0-9]+, w[0-9]+
-**     csel    w[0-9]+, w[0-9]+, w[0-9]+, le
+**     csel    w[0-9]+, w[0-9]+, w[0-9]+, gt
+**     add     w[0-9]+, w[0-9]+, w[0-9]+
 **     ret
 */
 int f1 (int a, int b, int c)
@@ -18,10 +23,11 @@ int f1 (int a, int b, int c)
 }
 
 /*
-** f2:
+** f2: { xfail *-*-* }
 **     ...
 **     cmp     x[0-9]+, x[0-9]+
-**     csel    x[0-9]+, x[0-9]+, x[0-9]+, le
+**     csel    x[0-9]+, x[0-9]+, x[0-9]+, gt
+**     add     x[0-9]+, x[0-9]+, x[0-9]+
 **     ret
 */
 long long f2 (long long a, long long b, long long c)
diff --git a/gcc/testsuite/gcc.target/aarch64/phi-factor-binary-1.c 
b/gcc/testsuite/gcc.target/aarch64/phi-factor-binary-1.c
new file mode 100644
index 000000000000..fd6b4f556c06
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/phi-factor-binary-1.c
@@ -0,0 +1,60 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+/* tree-optimization/125557: factor_out_binary_common_operand computes a binary
+   operation shared between a PHI's two arms once, behind a single select,
+   instead of once per arm -- and when several join PHIs share the differing
+   operand it collapses them to one select, which also removes the
+   data-dependent branch.  These are the asm wins of patch 1 on its own.  */
+
+/* f1: one multiply behind a select, not one per arm.  */
+int
+f1 (int cond, int a, int b, int c)
+{
+  int x;
+  if (cond)
+    x = a * c;
+  else
+    x = b * c;
+  return x;
+}
+
+/* f3: one shift, not one per arm (snappy's address-arithmetic shape).  */
+unsigned long
+f3 (int cond, unsigned long a, unsigned long b, int s)
+{
+  unsigned long x;
+  if (cond)
+    x = a << s;
+  else
+    x = b << s;
+  return x;
+}
+
+/* f2: two results share the differing operand (a/b): one select feeds both, so
+   only one multiply and one add remain and the diamond is branchless.  */
+void
+f2 (int cond, int a, int b, int c, int d, int *p, int *q)
+{
+  int x, y;
+  if (cond)
+    {
+      x = a * c;
+      y = a + d;
+    }
+  else
+    {
+      x = b * c;
+      y = b + d;
+    }
+  *p = x;
+  *q = y;
+}
+
+/* f1 and f2 each keep a single multiply (two without the factoring).  */
+/* { dg-final { scan-assembler-times {\tmul\t} 2 } } */
+/* f3 keeps a single shift (two without the factoring).  */
+/* { dg-final { scan-assembler-times {\tlsl\t} 1 } } */
+/* Each diamond becomes a branchless select; no data-dependent branch remains. 
 */
+/* { dg-final { scan-assembler-not {\tcbn?z\t} } } */
+/* { dg-final { scan-assembler-not {\tb\.[a-z]} } } */
diff --git a/gcc/tree-if-conv.cc b/gcc/tree-if-conv.cc
index b685e30bd0b2..5d8abfb52b53 100644
--- a/gcc/tree-if-conv.cc
+++ b/gcc/tree-if-conv.cc
@@ -2173,85 +2173,6 @@ gen_phi_arg_condition (gphi *phi, ifcvt_arg_entry_t &arg,
   return cond;
 }
 
-/* Find the operand which is different between ARG0_OP and ARG1_OP.
-   Returns the operand num where the difference is.
-   Set NEWARG0 and NEWARG1 from the different argument.
-   Returns -1 if none is found.
-   If ARG0_OP/ARG1_OP is commutative also try swapping the
-   two commutative operands and return the operand number where
-   the difference happens in ARG0_OP. */
-
-static int
-find_different_opnum (const gimple_match_op &arg0_op,
-                     const gimple_match_op &arg1_op,
-                     tree *new_arg0, tree *new_arg1)
-{
-  unsigned opnum = -1;
-  unsigned first;
-  first = first_commutative_argument (arg1_op.code, arg1_op.type);
-  for (unsigned i = 0; i < arg0_op.num_ops; i++)
-    {
-      if (!operand_equal_for_phi_arg_p (arg0_op.ops[i],
-                                       arg1_op.ops[i]))
-       {
-         /* Can handle only one non equal operand. */
-         if (opnum != -1u)
-           {
-             /* Though if opnum is right before i and opnum is equal
-                to the first communtative argument, handle communtative
-                specially. */
-             if (i == opnum + 1 && opnum == first)
-               goto commutative;
-             return -1;
-           }
-         opnum = i;
-       }
-  }
-  /* If all operands are equal only do this is there was single
-     operand.  */
-  if (opnum == -1u)
-    {
-      if (arg0_op.num_ops != 1)
-       return -1;
-      opnum = 0;
-    }
-  *new_arg0 = arg0_op.ops[opnum];
-  *new_arg1 = arg1_op.ops[opnum];
-  return opnum;
-
-/* Handle commutative operations. */
-commutative:
-  gcc_assert (first != -1u);
-
-  /* Check the rest of the arguments to make sure they are the same. */
-  for (unsigned i = first + 2; i < arg0_op.num_ops; i++)
-    if (!operand_equal_for_phi_arg_p (arg0_op.ops[i],
-                                     arg1_op.ops[i]))
-      return -1;
-
-  /* If the arg0[first+1] and arg1[first] are the same
-     then the one which is different is arg0[first] and arg1[first+1]
-     return first since this is based on arg0.  */
-  if (operand_equal_for_phi_arg_p (arg0_op.ops[first + 1],
-                                  arg1_op.ops[first]))
-    {
-       *new_arg0 = arg0_op.ops[first];
-       *new_arg1 = arg1_op.ops[first + 1];
-       return first;
-    }
-  /* If the arg0[first] and arg1[first+1] are the same
-     then the one which is different is arg0[first+1] and arg1[first]
-     return first+1 since this is based on arg0.  */
-  if (operand_equal_for_phi_arg_p (arg0_op.ops[first],
-                                  arg1_op.ops[first + 1]))
-    {
-       *new_arg0 = arg0_op.ops[first + 1];
-       *new_arg1 = arg1_op.ops[first];
-       return first + 1;
-    }
-  return -1;
-}
-
 /* Factors out an operation from *ARG0 and *ARG1 and
    create the new statement at GSI. *RES is the
    result of that new statement. Update *ARG0 and *ARG1
diff --git a/gcc/tree-ssa-phiopt.cc b/gcc/tree-ssa-phiopt.cc
index eec65a7b68d5..82e67dc15d76 100644
--- a/gcc/tree-ssa-phiopt.cc
+++ b/gcc/tree-ssa-phiopt.cc
@@ -221,26 +221,17 @@ replace_phi_edge_with_variable (basic_block cond_block,
              bb->index);
 }
 
-/* Returns true if the ARG used from DEF_STMT is profitable to move
-   to a PHI node of the basic block MERGE where the new statement
+/* Returns true if the operands of arg_op defined from DEF_STMT is profitable 
to move
+   to the usage into the basic block MERGE where the new statement
    will be located.  */
 static bool
-is_factor_profitable (gimple *def_stmt, basic_block merge, tree arg)
+is_factor_profitable (gimple *def_stmt, basic_block merge, const 
gimple_match_op &arg_op)
 {
   /* The defining statement should be conditional.  */
   if (dominated_by_p (CDI_DOMINATORS, merge,
                      gimple_bb (def_stmt)))
     return false;
 
-  /* If the arg is invariant, then there is
-     no extending of the live range. */
-  if (is_gimple_min_invariant (arg))
-    return true;
-
-  /* Otherwise, the arg needs to be a ssa name. */
-  if (TREE_CODE (arg) != SSA_NAME)
-    return false;
-
   /* We should not increase the live range of arg
      across too many statements or calls.  */
   gimple_stmt_iterator gsi = gsi_for_stmt (def_stmt);
@@ -257,23 +248,11 @@ is_factor_profitable (gimple *def_stmt, basic_block 
merge, tree arg)
   if (gsi_end_p (gsi))
     return true;
 
-  /* Check if the uses of arg is dominated by merge block, this is a quick and
-     rough estimate if arg is still alive at the merge bb.  */
-  /* FIXME: extend to a more complete live range detection.  */
-  use_operand_p use_p;
-  imm_use_iterator iter;
-  FOR_EACH_IMM_USE_FAST (use_p, iter, arg)
-    {
-      gimple *use_stmt = USE_STMT (use_p);
-      basic_block use_bb = gimple_bb (use_stmt);
-      if (dominated_by_p (CDI_DOMINATORS, merge, use_bb))
-       return true;
-    }
-
   /* If there are a few (non-call/asm) statements between
      the old defining statement and end of the bb, then
-     the live range of new arg does not increase enough.  */
+     the live range of operands will increase enough.  */
   int max_statements = param_phiopt_factor_max_stmts_live;
+  bool stmts_extending_ok = true;
 
   while (!gsi_end_p (gsi))
     {
@@ -288,11 +267,52 @@ is_factor_profitable (gimple *def_stmt, basic_block 
merge, tree arg)
        }
       /* Non-assigns will extend the live range too much.  */
       if (gcode != GIMPLE_ASSIGN)
-       return false;
+       {
+         stmts_extending_ok = false;
+         break;
+       }
       max_statements --;
       if (max_statements == 0)
-       return false;
+       {
+         stmts_extending_ok = false;
+         break;
+       }
       gsi_next_nondebug (&gsi);
+  }
+  if (stmts_extending_ok)
+    return true;
+
+  /* Loop over all of the operands to see if all are used after anyways.  */
+  for (unsigned i = 0; i < arg_op.num_ops; i++)
+    {
+      tree arg = arg_op.ops[i];
+      /* If the arg is invariant, then there is
+        no extending of the live range. */
+      if (is_gimple_min_invariant (arg))
+       continue;
+
+      /* Otherwise, the arg needs to be a ssa name. */
+      if (TREE_CODE (arg) != SSA_NAME)
+       return false;
+
+      /* Check if the uses of arg is dominated by merge block, this is a quick 
and
+        rough estimate if arg is still alive at the merge bb.  */
+      /* FIXME: extend to a more complete live range detection.  */
+      use_operand_p use_p;
+      imm_use_iterator iter;
+      bool usedafter = false;
+      FOR_EACH_IMM_USE_FAST (use_p, iter, arg)
+       {
+         gimple *use_stmt = USE_STMT (use_p);
+         basic_block use_bb = gimple_bb (use_stmt);
+         if (dominated_by_p (CDI_DOMINATORS, merge, use_bb))
+           {
+             usedafter = true;
+             break;
+           }
+       }
+      if (!usedafter)
+       return false;
     }
   return true;
 }
@@ -304,7 +324,8 @@ is_factor_profitable (gimple *def_stmt, basic_block merge, 
tree arg)
 
 static bool
 factor_out_conditional_operation (edge e0, edge e1, basic_block merge,
-                                 gphi *phi, gimple *cond_stmt)
+                                 gphi *phi, gimple *cond_stmt,
+                                 bool early_p)
 {
   gimple *arg0_def_stmt = NULL, *arg1_def_stmt = NULL;
   tree temp, result;
@@ -358,19 +379,14 @@ factor_out_conditional_operation (edge e0, edge e1, 
basic_block merge,
   if (arg0_op.operands_occurs_in_abnormal_phi ())
    return false;
 
-  /* Currently just support one operand expressions. */
-  if (arg0_op.num_ops != 1)
-    return false;
-
-  tree new_arg0 = arg0_op.ops[0];
+  tree new_arg0;
   tree new_arg1;
+  int opnum = -1;
 
   /* If arg0 have > 1 use, then this transformation actually increases
      the number of expressions evaluated at runtime.  */
   if (!has_single_use (arg0))
     return false;
-  if (!is_factor_profitable (arg0_def_stmt, merge, new_arg0))
-    return false;
   if (gimple_has_location (arg0_def_stmt))
     narg0_loc = gimple_location (arg0_def_stmt);
 
@@ -387,14 +403,67 @@ factor_out_conditional_operation (edge e0, edge e1, 
basic_block merge,
       if (arg1_op.operands_occurs_in_abnormal_phi ())
        return false;
 
+      /* For the complex expression, don't factor
+        out, that will confuse the uninitializing
+        warnings.  */
+      if (arg1_op.code == COMPLEX_EXPR)
+       return false;
+
       /* If arg1 have > 1 use, then this transformation actually increases
         the number of expressions evaluated at runtime.  */
       if (!has_single_use (arg1))
        return false;
 
-      new_arg1 = arg1_op.ops[0];
-      if (!is_factor_profitable (arg1_def_stmt, merge, new_arg1))
+      opnum = find_different_opnum (arg0_op, arg1_op, &new_arg0, &new_arg1);
+      if (opnum == -1)
+       return false;
+
+      /* Check to make sure extending the lifetimes of all operands is ok.  */
+      if (!is_factor_profitable (arg0_def_stmt, merge, arg0_op))
+       return false;
+      if (!is_factor_profitable (arg1_def_stmt, merge, arg1_op))
+       return false;
+
+      /* If this was a division and the operand is the divisor
+        and either divisor was a constant, don't factor out
+        the division; dividing by an explicit constant can be
+        expanded better than without an constant.
+        FIXME: maybe isel could undo this case.  */
+      if (int_divide_or_mod_p (arg1_op.code)
+         && opnum == 1
+         && (poly_int_tree_p (new_arg0)
+             || poly_int_tree_p (new_arg1)))
        return false;
+
+      /* For early phiopt, don't factor out constants for pointer plus.
+        BOS pass does not like that factoring.  */
+      if (early_p && arg1_op.code == POINTER_PLUS_EXPR
+         && opnum == 1
+         && TREE_CODE (new_arg0) != SSA_NAME
+         && TREE_CODE (new_arg1) != SSA_NAME)
+       return false;
+
+      /* BIT_FIELD_REF and BIT_INSERT_EXPR can't be factored out for non-0 
operands
+        as the other operands require constants. */
+      if ((arg1_op.code == BIT_FIELD_REF
+          || arg1_op.code == BIT_INSERT_EXPR)
+         && opnum != 0)
+       return false;
+
+      /* It is not profitability to factor out vec_perm with
+        constant masks (operand 2).  The target might not support it
+        and that might be invalid to do as such. Also with constants
+        masks, the number of elements of the mask type does not need
+        to match the number of elements of other operands and can be
+        arbitrary integral vector type so factoring that out can't work.
+        Note in the case where one mask is a constant and the other is not,
+        the check for compatible types will reject the case the
+        constant mask has the incompatible type.  */
+      if (arg1_op.code == VEC_PERM_EXPR && opnum == 2
+         && TREE_CODE (new_arg0) == VECTOR_CST
+         && TREE_CODE (new_arg1) == VECTOR_CST)
+       return false;
+
       if (gimple_has_location (arg1_def_stmt))
        narg1_loc = gimple_location (arg1_def_stmt);
 
@@ -409,17 +478,19 @@ factor_out_conditional_operation (edge e0, edge e1, 
basic_block merge,
            locus = narg0_loc;
        }
     }
+  else if (arg0_op.num_ops != 1)
+    return false;
   else
     {
+      new_arg0 = arg0_op.ops[0];
+      opnum = 0;
       /* For constants only handle if the phi was the only one. */
       if (single_non_singleton_phi_for_edges (phi_nodes (merge), e0, e1) == 
NULL)
        return false;
       /* TODO: handle more than just casts here. */
       if (!gimple_assign_cast_p (arg0_def_stmt))
        return false;
-
-      /* arg0_def_stmt should be conditional.  */
-      if (dominated_by_p (CDI_DOMINATORS, gimple_bb (phi), gimple_bb 
(arg0_def_stmt)))
+      if (!is_factor_profitable (arg0_def_stmt, merge, arg0_op))
        return false;
 
       /* If arg1 is an INTEGER_CST, fold it to new type if it fits, or else
@@ -509,7 +580,7 @@ factor_out_conditional_operation (edge e0, edge e1, 
basic_block merge,
   gimple_match_op new_op = arg0_op;
 
   /* Create the operation stmt if possible and insert it.  */
-  new_op.ops[0] = temp;
+  new_op.ops[opnum] = temp;
   gimple_seq seq = NULL;
   result = maybe_push_res_to_seq (&new_op, &seq, result);
 
@@ -4108,7 +4179,7 @@ pass_phiopt::execute (function *)
              gphi *phi = as_a <gphi *> (gsi_stmt (gsi));
 
              if (factor_out_conditional_operation (e1, e2, merge, phi,
-                 cond_stmt))
+                 cond_stmt, early_p))
                {
                  /* Start over if there was an operation that was factored out 
because the new phi might have another opportunity.  */
                  phis = phi_nodes (merge);
diff --git a/gcc/tree.h b/gcc/tree.h
index 415404cfbf4c..3e59bec1a946 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -5805,6 +5805,27 @@ trunc_or_exact_div_p (tree_code code)
   return code == TRUNC_DIV_EXPR || code == EXACT_DIV_EXPR;
 }
 
+/* Return true if CODE is an integer division or integer mod code. */
+inline bool
+int_divide_or_mod_p (const code_helper &code)
+{
+  switch (code.get_rep())
+    {
+    case TRUNC_DIV_EXPR:
+    case CEIL_DIV_EXPR:
+    case FLOOR_DIV_EXPR:
+    case ROUND_DIV_EXPR:
+    case EXACT_DIV_EXPR:
+    case TRUNC_MOD_EXPR:
+    case FLOOR_MOD_EXPR:
+    case CEIL_MOD_EXPR:
+    case ROUND_MOD_EXPR:
+      return true;
+    default:
+      return false;
+    }
+}
+
 /* Return nonzero if CODE is a tree code that represents a truth value.  */
 inline bool
 truth_value_p (enum tree_code code)

Reply via email to