On Mon, Dec 29, 2025 at 11:57 AM Daniel Barboza <[email protected]> wrote: > > 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.
This is mostly ok from my side except for the testcase change for pr110701.c. See below; dg-output is not used most of the time. > > 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" } */ We normally don't use dg-output in the GCC testsuite. There are a few exceptions; mostly in the libstdc++, fortran testsuites and for the sanitizers testcases. So the best approach would be: ``` __attribute__((noipa)) void foo() { *c = d(340, b >= 0) ^ 3; } int main() { foo(); if (a != 343) __builtin_abort(); } ``` Hopefully I did that correctly. But you should get the idea. Thanks, Andrew Pinski > -- > 2.43.0 >
