Re: [PATCH v2] match.pd: Fold umin(a, 1) | umin(b, 1) into umin(a | b, 1) [PR126646]
Andrea Pinski <[email protected]>
| Newsgroups | gmane.comp.gcc.patches |
|---|---|
| Message-ID | <CALvbMcAr_3Vtdi+mGKosQWRnvZ314-Rjm5tn5v+NsObB96pcYw@mail.gmail.com> |
On Thu, Aug 13, 2026 at 10:28 PM <[email protected]> wrote: > > From: Reshma Roy <[email protected]> > > gcc/ChangeLog: > > PR tree-optimization/126646 > * match.pd: Fold umin(a, 1) | umin(b, 1) into umin(a | b, 1). > > gcc/testsuite/ChangeLog: > > * gcc.dg/tree-ssa/pr126646-1.c: New test. > * gcc.dg/tree-ssa/pr126646-2.c: New test. Ok, see below about being ok about the testcase. > > --- > Hi, > The patch is updated as per the comments and added a new test case for > the same. My comments are added inline to the email. > > Bootstrapped and tested on x86_64-linux > > > -----Original Message----- > > From: Andrea Pinski <[email protected]> > > Sent: Wednesday, August 12, 2026 9:59 AM > > To: Roy, Reshma <[email protected]> > > Cc: [email protected]; Kumar, Venkataramanan > > <[email protected]> > > Subject: Re: [PATCH] match.pd: Fold umin(a, 1) | umin(b, 1) into umin(a | b, 1) > > [PR126646] > > > > [You don't often get email from [email protected]. Learn why this > > is important at https://aka.ms/LearnAboutSenderIdentification ] > > > > Caution: This message originated from an External Source. Use proper caution > > when opening attachments, clicking links, or responding. > > > > > > On Tue, Aug 11, 2026 at 8:50 PM <[email protected]> wrote: > > > > > > From: Reshma Roy <[email protected]> > > > > > > gcc/ChangeLog: > > > > > > PR tree-optimization/126646 > > > * match.pd: Fold umin(a, 1) | umin(b, 1) into umin(a | b, 1). > > > > > > gcc/testsuite/ChangeLog: > > > > > > * gcc.dg/tree-ssa/pr126646-1.c: New test. > > > --- > > > > > > Hi, > > > This patch fixes the missed optimization opportunity reported in > > > https://gcc.gnu.org/bugzilla/show_bug.cgi?id=126646. > > > For the following snippet of code, gcc generates 2 MIN_EXPR. > > > unsigned f(unsigned a, unsigned b) > > > { > > > unsigned t = 1; > > > a = a < t ? a : t; > > > b = b < t ? b : t; > > > return a | b; > > > } > > > But this can be optimized with just one MIN_EXPR since > > > umin(a,1) | umin(b,1) can be simplified to umin(a|b, 1). > > > > > > Bootstrapped and tested on x86_64-linux. > > > > > > Thanks, > > > Reshma > > > > > > gcc/match.pd | 5 +++++ > > > gcc/testsuite/gcc.dg/tree-ssa/pr126646-1.c | 15 +++++++++++++++ > > > 2 files changed, 20 insertions(+) > > > create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr126646-1.c > > > > > > diff --git a/gcc/match.pd b/gcc/match.pd index > > > 50e73177022..7347b83ec4f 100644 > > > --- a/gcc/match.pd > > > +++ b/gcc/match.pd > > > @@ -4860,6 +4860,11 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT) > > > (bit_not (minmax:cs (bit_not @0) @1)) > > > (maxmin @0 (bit_not @1)))) > > > > > > +/* umin (a, 1) | umin (b, 1) -> umin (a | b, 1). */ (simplify > > > + (bit_ior (min @0 integer_onep@2) (min @1 @2)) > > > + (if (TYPE_UNSIGNED (type)) > > > > Instead of TYPE_UNSIGNED here; I think it might be a good idea to use > > tree_expr_nonnegative_p on both @0 and @1.. > > tree_expr_nonnegative_p does return true for TYPE_UNSIGNED already but it also > > returns true when the argument is known to be zero or positive. > Agreed, updated to use tree_expr_nonnegative_p (@0) && > tree_expr_nonnegative_p (@1). This is strictly more general as it covers > unsigned types (where tree_expr_nonnegative_p returns true by definition) and > also signed types when the operands are non-negative. > > > > This allows for say: > > ``` > > int f(int a, int b) > > { > > int c = 0; > > if (a >= 0 && b >= 0) > > c = (a > 1 ? 1 : a) | (b > 1 ? 1 : b); > > return c; > > } > > ``` > Understood the test case scenario. But the above test case doesn't apply the > new guard in practice because by the time match.pd sees the MIN_EXPRs, the > condition a >= 0 && b >= 0 has already been folded into (a | b) >= 0. The > dominating condition is now on the combined OR, so tree_expr_nonnegative_p > on a and b individually can't prove non-negativity. Hence I have added a > different test case (pr126646-2.c) that uses __builtin_abs, where non-negativity > is implied in the expression itself. Hope that is fine. Let me know your > thoughts. Yes, the different testcase using abs is fine. I forgot the ranger is NOT always enabled for forwprop so it is not always used for tree_expr_nonnegative_p; instead tree_expr_nonnegative_p will use the global range. That is something which I have been thinking about changing but I always get side tracked. > > > Which then should optimize to just: > > _10 = a_6(D) | b_7(D); > > c_8 = MIN_EXPR <_10, 1>; > > _11 = MAX_EXPR <c_8, 0>; > > > > > > Thanks, > > Andrea > > > > > + (min (bit_ior @0 @1) @2))) > > > /* MIN (X, Y) == X -> X <= Y */ > > > /* MIN (X, Y) < X -> X > Y */ > > > /* MIN (X, Y) >= X -> X <= Y */ > > > diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr126646-1.c > > > b/gcc/testsuite/gcc.dg/tree-ssa/pr126646-1.c > > > new file mode 100644 > > > index 00000000000..447a1739b5f > > > --- /dev/null > > > +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr126646-1.c > > > @@ -0,0 +1,15 @@ > > > +/* { dg-do compile } */ > > > +/* { dg-options "-O2 -fdump-tree-optimized" } */ > > > + > > > +/* The test case should produce only one min expr. */ > > > +/* umin(a,1) | umin(b,1) -> umin(a|b, 1). */ > > > + > > > +unsigned min_or (unsigned a, unsigned b) { > > > + unsigned t = 1; > > > + a = a < t ? a : t; > > > + b = b < t ? b : t; > > > + return a | b; > > > +} > > > + > > > +/* { dg-final { scan-tree-dump-times "MIN_EXPR" 1 "optimized" } } */ > > > -- > > > 2.34.1 > > > > > Thanks, > Reshma > > > gcc/match.pd | 5 +++++ > gcc/testsuite/gcc.dg/tree-ssa/pr126646-1.c | 15 +++++++++++++++ > gcc/testsuite/gcc.dg/tree-ssa/pr126646-2.c | 13 +++++++++++++ > 3 files changed, 33 insertions(+) > create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr126646-1.c > create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr126646-2.c > > diff --git a/gcc/match.pd b/gcc/match.pd > index 5fc89426880..be2bc0dd8df 100644 > --- a/gcc/match.pd > +++ b/gcc/match.pd > @@ -4887,6 +4887,11 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT) > (bit_not (minmax:cs (bit_not @0) @1)) > (maxmin @0 (bit_not @1)))) > > +/* umin (a, 1) | umin (b, 1) -> umin (a | b, 1). */ > + (simplify > + (bit_ior (min @0 integer_onep@2) (min @1 @2)) > + (if (tree_expr_nonnegative_p (@0) && tree_expr_nonnegative_p (@1)) > + (min (bit_ior @0 @1) @2))) > /* MIN (X, Y) == X -> X <= Y */ > /* MIN (X, Y) < X -> X > Y */ > /* MIN (X, Y) >= X -> X <= Y */ > diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr126646-1.c b/gcc/testsuite/gcc.dg/tree-ssa/pr126646-1.c > new file mode 100644 > index 00000000000..447a1739b5f > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr126646-1.c > @@ -0,0 +1,15 @@ > +/* { dg-do compile } */ > +/* { dg-options "-O2 -fdump-tree-optimized" } */ > + > +/* The test case should produce only one min expr. */ > +/* umin(a,1) | umin(b,1) -> umin(a|b, 1). */ > + > +unsigned min_or (unsigned a, unsigned b) > +{ > + unsigned t = 1; > + a = a < t ? a : t; > + b = b < t ? b : t; > + return a | b; > +} > + > +/* { dg-final { scan-tree-dump-times "MIN_EXPR" 1 "optimized" } } */ > diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr126646-2.c b/gcc/testsuite/gcc.dg/tree-ssa/pr126646-2.c > new file mode 100644 > index 00000000000..3b47eee9656 > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr126646-2.c > @@ -0,0 +1,13 @@ > +/* { dg-do compile } */ > +/* { dg-options "-O2 -fdump-tree-optimized" } */ > + > +int f (int a, int b) > +{ > + int x = __builtin_abs (a); > + int y = __builtin_abs (b); > + int p = x < 1 ? x : 1; > + int q = y < 1 ? y : 1; > + return p | q; > +} > + > +/* { dg-final { scan-tree-dump-times "MIN_EXPR" 1 "optimized" } } */ > -- > 2.34.1 >