[v2] match: Simplify sign tests for MIN and BIT_IOR [PR126087]
Checks
Commit Message
When `b` is known to be non-negative, the sign of both `MIN (a, b)`
and `a | b` depends only on the sign of `a`. Add patterns to match.pd
to optimize `MIN (a, b) cmp 0` and `(a | b) cmp 0` into `a cmp 0`,
where `cmp` is `<` or `>=`. Currently, `(a | b) cmp 0` is optimized
at the RTL level for some targets, but simplifying earlier is
preferable.
PR tree-optimization/126087
gcc/ChangeLog:
* match.pd: Simplify MIN and BIT_IOR compared to 0 when
one operand is non-negative.
gcc/testsuite/ChangeLog:
* gcc.dg/tree-ssa/pr126087.c: New test.
Signed-off-by: Neal Patalay <nealpatalay0@gmail.com>
---
If this patch is approved, could someone please commit it and update
PR126087 for me?
v2: Merged both patterns with a (for op (min bit_ior))
Bootstrapped and regression tested on x86_64-pc-linux-gnu with no
regressions.
gcc/match.pd | 11 ++++++++
gcc/testsuite/gcc.dg/tree-ssa/pr126087.c | 36 ++++++++++++++++++++++++
2 files changed, 47 insertions(+)
create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr126087.c
Comments
On Fri, 17 Jul 2026, Neal Patalay wrote:
> When `b` is known to be non-negative, the sign of both `MIN (a, b)`
> and `a | b` depends only on the sign of `a`. Add patterns to match.pd
> to optimize `MIN (a, b) cmp 0` and `(a | b) cmp 0` into `a cmp 0`,
> where `cmp` is `<` or `>=`. Currently, `(a | b) cmp 0` is optimized
> at the RTL level for some targets, but simplifying earlier is
> preferable.
>
> PR tree-optimization/126087
>
> gcc/ChangeLog:
>
> * match.pd: Simplify MIN and BIT_IOR compared to 0 when
> one operand is non-negative.
>
> gcc/testsuite/ChangeLog:
>
> * gcc.dg/tree-ssa/pr126087.c: New test.
>
> Signed-off-by: Neal Patalay <nealpatalay0@gmail.com>
> ---
> If this patch is approved, could someone please commit it and update
> PR126087 for me?
OK and will do.
Richard.
> v2: Merged both patterns with a (for op (min bit_ior))
>
> Bootstrapped and regression tested on x86_64-pc-linux-gnu with no
> regressions.
>
> gcc/match.pd | 11 ++++++++
> gcc/testsuite/gcc.dg/tree-ssa/pr126087.c | 36 ++++++++++++++++++++++++
> 2 files changed, 47 insertions(+)
> create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr126087.c
>
> diff --git a/gcc/match.pd b/gcc/match.pd
> index cb8c68bd915..a5870dae46b 100644
> --- a/gcc/match.pd
> +++ b/gcc/match.pd
> @@ -4840,6 +4840,17 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
> (cmp (bit_ior (convert:utype @0) (convert:utype @1))
> { build_zero_cst (utype); } ))))
>
> +/* Optimize MIN (a, b) >= 0 to a >= 0 if b is non-negative.
> + Optimize MIN (a, b) < 0 to a < 0 if b is non-negative.
> + Optimize (a | b) >= 0 to a >= 0 if b is non-negative.
> + Optimize (a | b) < 0 to a < 0 if b is non-negative. */
> +(for op (min bit_ior)
> + (for cmp (ge lt)
> + (simplify
> + (cmp (op:c @0 @1) integer_zerop@2)
> + (if (tree_expr_nonnegative_p (@1))
> + (cmp @0 @2)))))
> +
> /* Undo fancy ways of writing max/min or other ?: expressions, like
> a - ((a - b) & -(a < b)) and a - (a - b) * (a < b) into (a < b) ? b : a.
> People normally use ?: and that is what we actually try to optimize. */
> diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr126087.c b/gcc/testsuite/gcc.dg/tree-ssa/pr126087.c
> new file mode 100644
> index 00000000000..308a5096c42
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr126087.c
> @@ -0,0 +1,36 @@
> +/* PR tree-optimization/126087 */
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fdump-tree-optimized" } */
> +
> +void use();
> +
> +void f2(int a, unsigned short bb) {
> + int b = bb;
> + int m = a < b ? a : b;
> + if (m >= 0)
> + use();
> +}
> +
> +void f3(int a, unsigned short bb) {
> + int b = bb;
> + int m = a | b;
> + if (m >= 0)
> + use();
> +}
> +
> +void f4(int a, unsigned short bb) {
> + int b = bb;
> + int m = a < b ? a : b;
> + if (m < 0)
> + use();
> +}
> +
> +void f5(int a, unsigned short bb) {
> + int b = bb;
> + int m = a | b;
> + if (m < 0)
> + use();
> +}
> +
> +/* { dg-final { scan-tree-dump-not "MIN_EXPR" "optimized" } } */
> +/* { dg-final { scan-tree-dump-not " \\| " "optimized" } } */
>
@@ -4840,6 +4840,17 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
(cmp (bit_ior (convert:utype @0) (convert:utype @1))
{ build_zero_cst (utype); } ))))
+/* Optimize MIN (a, b) >= 0 to a >= 0 if b is non-negative.
+ Optimize MIN (a, b) < 0 to a < 0 if b is non-negative.
+ Optimize (a | b) >= 0 to a >= 0 if b is non-negative.
+ Optimize (a | b) < 0 to a < 0 if b is non-negative. */
+(for op (min bit_ior)
+ (for cmp (ge lt)
+ (simplify
+ (cmp (op:c @0 @1) integer_zerop@2)
+ (if (tree_expr_nonnegative_p (@1))
+ (cmp @0 @2)))))
+
/* Undo fancy ways of writing max/min or other ?: expressions, like
a - ((a - b) & -(a < b)) and a - (a - b) * (a < b) into (a < b) ? b : a.
People normally use ?: and that is what we actually try to optimize. */
new file mode 100644
@@ -0,0 +1,36 @@
+/* PR tree-optimization/126087 */
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+void use();
+
+void f2(int a, unsigned short bb) {
+ int b = bb;
+ int m = a < b ? a : b;
+ if (m >= 0)
+ use();
+}
+
+void f3(int a, unsigned short bb) {
+ int b = bb;
+ int m = a | b;
+ if (m >= 0)
+ use();
+}
+
+void f4(int a, unsigned short bb) {
+ int b = bb;
+ int m = a < b ? a : b;
+ if (m < 0)
+ use();
+}
+
+void f5(int a, unsigned short bb) {
+ int b = bb;
+ int m = a | b;
+ if (m < 0)
+ use();
+}
+
+/* { dg-final { scan-tree-dump-not "MIN_EXPR" "optimized" } } */
+/* { dg-final { scan-tree-dump-not " \\| " "optimized" } } */