Add a pattern to handle cases where we have an OP that is
unconditionally being applied in the result of a gcond. In this case we
can apply OP to both legs of the conditional. E.g:
t = b ? 10 : 20;
t = t + 20;
becomes just:
t = b ? 30 : 40
A variant pattern was also added to handle the case where the gcond
result is used as the second operand. This was needed because most of
the ops we're handling aren't commutative.
PR 122608
gcc/ChangeLog:
* match.pd (`(c ? a : b) op d -> c ? (a op d) : (b op d)`): New
pattern.
(`d op (c ? a : b) -> c ? (d op a) : (d op b)`): Likewise
gcc/testsuite/ChangeLog:
* gcc.target/i386/pr110701.c: the pattern added is now folding
an XOR into the ifcond and the assembler isn't emitting an
'andl' anymore. The test was turned into a runtime test
instead.
* gcc.dg/torture/pr122608.c: New test.
Signed-off-by: Daniel Barboza <[email protected]>
---
Changes from v1:
- changed pr110710.c to be a runtime test
- pattern now handles pointer_diff, lrotate, rrotate and mult_highpart
- pattern was moved to be right after a similar pattern that handles
simple_comparisons
- a variant pattern was added: d op (c ? a : b) -> c ? (d op a) : (d op b)
- v1 link: https://gcc.gnu.org/pipermail/gcc-patches/2025-December/702665.html
gcc/match.pd | 17 +++
gcc/testsuite/gcc.dg/torture/pr122608.c | 178 +++++++++++++++++++++++
gcc/testsuite/gcc.target/i386/pr110701.c | 13 +-
3 files changed, 205 insertions(+), 3 deletions(-)
create mode 100644 gcc/testsuite/gcc.dg/torture/pr122608.c
diff --git a/gcc/match.pd b/gcc/match.pd
index 45d96844673..9577224305f 100644
--- a/gcc/match.pd
+++ b/gcc/match.pd
@@ -8603,6 +8603,23 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
(if (!FLOAT_TYPE_P (TREE_TYPE (@3))
|| !operation_could_trap_p (cmp, true, false, @3))
(cond @0 (cmp! @1 @3) (cmp! @2 @3)))))
+
+/* Similar to above:
+ (c ? a : b) op d -> c ? (a op d) : (b op d)
+ But with non-vector binary ops, most of them non-commutative. */
+(for op (plus minus mult bit_and bit_ior bit_xor
+ lshift rshift rdiv trunc_div ceil_div floor_div round_div exact_div
+ trunc_mod ceil_mod floor_mod round_mod min max pointer_diff
+ lrotate rrotate mult_highpart)
+ (simplify
+ (op (cond @0 @1 @2) @3)
+ (cond @0 (op! @1 @3) (op! @2 @3)))
+
+ /* Support the variant
+ d op (c ? a : b) -> c ? (d op a) : (d op b) */
+ (simplify
+ (op @3 (cond @0 @1 @2))
+ (cond @0 (op! @3 @1) (op! @3 @2))))
#endif
/* Transform comparisons of the form (X & Y) CMP 0 to X CMP2 Z
diff --git a/gcc/testsuite/gcc.dg/torture/pr122608.c
b/gcc/testsuite/gcc.dg/torture/pr122608.c
new file mode 100644
index 00000000000..8129a129769
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/torture/pr122608.c
@@ -0,0 +1,178 @@
+/* { dg-do compile } */
+/* { dg-options "-fgimple -fdump-tree-optimized" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-fno-fat-lto-objects" } { "" } } */
+
+#define F(OP,NAME) \
+ __GIMPLE int NAME##_test (int a) \
+ { _Bool b; \
+ int t; \
+ b = a > 0; \
+ t = b ? 20 : 40; \
+ t = t OP 11; \
+ return t; } \
+ __GIMPLE int NAME##_test2 (int a) \
+ { _Bool b; \
+ int t; \
+ b = a > 0; \
+ t = b ? 2 : 4; \
+ t = 11 OP t; \
+ return t; }
+
+F (+, plus)
+F (-, minus)
+F (*, mult)
+F (|, bit_ior)
+F (^, bit_xor)
+F (/, div)
+F (%, mod)
+F (<<, lshift)
+
+#define F2(OP,NAME) \
+__GIMPLE int NAME##_test (int a) \
+ { _Bool b; \
+ int t; \
+ b = a > 0; \
+ t = b ? 20 : 40; \
+ t = t OP 2; \
+ return t; } \
+__GIMPLE int NAME##_test2 (int a) \
+ { _Bool b; \
+ int t; \
+ b = a > 0; \
+ t = b ? 2 : 3; \
+ t = 11 OP 2; \
+ return t; }
+F2 (__ROTATE_RIGHT, rotateright)
+F2 (__ROTATE_LEFT, rotateleft)
+
+__GIMPLE int test_and (int a)
+{
+ _Bool b;
+ int t;
+ b = a > 0;
+ t = b ? 1 : 3;
+ t = t & 3;
+ return t;
+}
+
+__GIMPLE int test_and2 (int a)
+{
+ _Bool b;
+ int t;
+ b = a > 0;
+ t = b ? 1 : 3;
+ t = 3 & t;
+ return t;
+}
+
+__GIMPLE int test_rshift (int a)
+{
+ _Bool b;
+ int t;
+ b = a > 0;
+ t = b ? 2 : 8;
+ t = t >> 1;
+ return t;
+}
+
+__GIMPLE int test_rshift2 (int a)
+{
+ _Bool b;
+ int t;
+ b = a > 0;
+ t = b ? 1 : 2;
+ t = 8 >> t;
+ return t;
+}
+
+static int min (int a, int b)
+{
+ return a < b ? a : b;
+}
+
+static int max (int a, int b)
+{
+ return a > b ? a : b;
+}
+
+__GIMPLE int min_test (int a)
+{
+ _Bool b;
+ int t;
+ b = a > 0;
+ t = b ? 2 : 4;
+ t = min (t, 3);
+ return t;
+}
+
+__GIMPLE int max_test (int a)
+{
+ _Bool b;
+ int t;
+ b = a > 0;
+ t = b ? 2 : 4;
+ t = max (t, 3);
+ return t;
+}
+
+char *ptr[12];
+typedef __PTRDIFF_TYPE__ ptrdiff_t;
+
+__GIMPLE ptrdiff_t pointer_diff_test (int a)
+{
+ _Bool b;
+ ptrdiff_t t;
+ char *t1;
+ b = a > 0;
+ t1 = b ? _Literal(char *) & ptr[0] : _Literal(char *) & ptr[1];
+ t = t1 - _Literal(char *) & ptr[0];
+ return t;
+}
+
+__GIMPLE ptrdiff_t pointer_diff_test2 (int a)
+{
+ _Bool b;
+ ptrdiff_t t;
+ char *t1;
+ b = a > 0;
+ t1 = b ? _Literal(char *) & ptr[0] : _Literal(char *) & ptr[1];
+ t = _Literal(char *) & ptr[1] - t1;
+ return t;
+}
+
+__GIMPLE int mulhighpart_test (int a)
+{
+ _Bool b;
+ int t;
+ b = a > 0;
+ t = b ? 30 : 40;
+ t = t __MULT_HIGHPART 0x7fffffff;
+ return t;
+}
+
+__GIMPLE int mulhighpart_test2 (int a)
+{
+ _Bool b;
+ int t;
+ b = a > 0;
+ t = b ? 0x5fffffff : 0x7fffffff;
+ t = 40 __MULT_HIGHPART t;
+ return t;
+}
+
+/* { dg-final { scan-tree-dump-times " \\+ " 0 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " \\- " 0 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " \\* " 0 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " \\| " 0 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " \\^ " 0 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " & " 0 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " / " 0 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " % " 0 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " >> " 0 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " r>> " 0 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "MIN_EXPR" 0 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "MAX_EXPR" 0 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " h\\* " 0 "optimized" } } */
+
+/* Note: pointer_diff_tests adds a lshift each when they succeed */
+/* { dg-final { scan-tree-dump-times " << " 2 "optimized" } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr110701.c
b/gcc/testsuite/gcc.target/i386/pr110701.c
index 3f2cea5c3df..ae74b57afa7 100644
--- a/gcc/testsuite/gcc.target/i386/pr110701.c
+++ b/gcc/testsuite/gcc.target/i386/pr110701.c
@@ -1,12 +1,19 @@
-/* { dg-do compile } */
+/* { dg-do run } */
/* { dg-options "-O2" } */
+#include <stdio.h>
+
int a;
long b;
int *c = &a;
short d(short e, short f) { return e * f; }
void foo() {
*c = d(340, b >= 0) ^ 3;
+ printf("%d\n", a);
+}
+
+int main()
+{
+ foo();
}
-/* { dg-final { scan-assembler "andl\[ \\t]\\\$340," } } */
-/* { dg-final { scan-assembler-not "andw\[ \\t]\\\$340," } } */
+/* { dg-output "343" } */
--
2.43.0