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
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.
PR tree-optimization/125557
PR tree-optimization/64700
PR tree-optimization/29144
PR tree-optimization/94274
gcc/ChangeLog:
* tree-if-conv.cc: Include tree-ssa-phiopt.h
(find_different_opnum): Move to ...
* tree-ssa-phiopt.cc (find_different_opnum): Here.
(is_divide_or_mod_p): New function.
(factor_out_conditional_operation): Handle operands > 1,
including operands communitive operands. Add early_p argument
for costing.
(pass_phiopt::execute): Pass early_p to
factor_out_conditional_operation.
* tree-ssa-phiopt.h: New file.
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.
Co-authored-by: Kyrylo Tkachov <[email protected]>
Signed-off-by: Andrew Pinski <[email protected]>
---
.../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.target/aarch64/phi-factor-binary-1.c | 60 ++++++
gcc/tree-if-conv.cc | 80 +-------
gcc/tree-ssa-phiopt.cc | 172 ++++++++++++++++--
gcc/tree-ssa-phiopt.h | 24 +++
14 files changed, 319 insertions(+), 99 deletions(-)
create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/phi-factor-binary-1.c
create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/phi-factor-binary-2.c
create mode 100644 gcc/testsuite/gcc.target/aarch64/phi-factor-binary-1.c
create mode 100644 gcc/tree-ssa-phiopt.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 00000000000..bfcae888125
--- /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 00000000000..eb7669b664b
--- /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 a80d4a1990b..54f7bf11d48 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 036f32a9c33..3c6df8d0726 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 6ac0559bc7c..f05cfe866cc 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 b5d537a6ccd..c172fb34fcf 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 13fca0b0e09..0e205d17891 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 f92b70ffafb..f50c2020157 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 d7ede2efe8c..31d59b84d49 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 126a50f3e56..aa204d1ab67 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/phi-factor-binary-1.c
b/gcc/testsuite/gcc.target/aarch64/phi-factor-binary-1.c
new file mode 100644
index 00000000000..fd6b4f556c0
--- /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 b685e30bd0b..b8ae52100fb 100644
--- a/gcc/tree-if-conv.cc
+++ b/gcc/tree-if-conv.cc
@@ -124,6 +124,7 @@ along with GCC; see the file COPYING3. If not see
#include "tree-vectorizer.h"
#include "tree-eh.h"
#include "cgraph.h"
+#include "tree-ssa-phiopt.h"
/* For lang_hooks.types.type_for_mode. */
#include "langhooks.h"
@@ -2173,85 +2174,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 6a7e4c70722..f8881e61d2f 100644
--- a/gcc/tree-ssa-phiopt.cc
+++ b/gcc/tree-ssa-phiopt.cc
@@ -297,6 +297,104 @@ is_factor_profitable (gimple *def_stmt, basic_block
merge, tree arg)
return true;
}
+/* Return true if CODE is an integer division or integer mod code. */
+static bool
+is_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:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/* 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;
+}
+
/* PR66726: Factor operations out of COND_EXPR. If the arguments of the PHI
stmt are Unary operator, factor out the operation and perform the operation
to the result of PHI stmt. COND_STMT is the controlling predicate.
@@ -304,7 +402,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 +457,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,12 +481,62 @@ 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];
+ opnum = find_different_opnum (arg0_op, arg1_op, &new_arg0, &new_arg1);
+ if (opnum == -1)
+ 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 (is_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 except
if they are equal. */
+ if (early_p && arg1_op.code == POINTER_PLUS_EXPR
+ && opnum == 1
+ && TREE_CODE (new_arg0) != SSA_NAME
+ && TREE_CODE (new_arg1) != SSA_NAME
+ && !operand_equal_p (new_arg0, new_arg1))
+ 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 next 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 (!is_factor_profitable (arg0_def_stmt, merge, new_arg0))
+ return false;
if (!is_factor_profitable (arg1_def_stmt, merge, new_arg1))
return false;
if (gimple_has_location (arg1_def_stmt))
@@ -409,14 +553,20 @@ 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;
+ if (!is_factor_profitable (arg0_def_stmt, merge, new_arg0))
+ return false;
/* arg0_def_stmt should be conditional. */
if (dominated_by_p (CDI_DOMINATORS, gimple_bb (phi), gimple_bb
(arg0_def_stmt)))
@@ -509,7 +659,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);
@@ -4105,7 +4255,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-ssa-phiopt.h b/gcc/tree-ssa-phiopt.h
new file mode 100644
index 00000000000..bba3136965d
--- /dev/null
+++ b/gcc/tree-ssa-phiopt.h
@@ -0,0 +1,24 @@
+/* Copyright (C) 2026 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 3, or (at your option) any
+later version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
+
+#ifndef TREE_SSA_PHIOPT_H
+#define TREE_SSA_PHIOPT_H
+extern int find_different_opnum (const gimple_match_op &arg0_op,
+ const gimple_match_op &arg1_op,
+ tree *new_arg0, tree *new_arg1);
+#endif
--
2.43.0