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
>

Reply via email to