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.

---
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.

> 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

Reply via email to