https://gcc.gnu.org/g:59c8c4bbac46570ac36782172ed28569affe42c4

commit r17-4549-g59c8c4bbac46570ac36782172ed28569affe42c4
Author: Naveen <[email protected]>
Date:   Mon Sep 21 20:56:16 2026 -0700

    tree-optimization: fold abs(x) comparisons with x [PR112626]
    
    The patterns account for both undefined-overflow and wrapping-overflow
    semantics. Comparisons whose result is independent of the value of
    `x` are folded to constants while the remaining cases are reduced to
    sign tests. With wrapping overflow, the `INT_MIN` case is handled
    explicitly using unsigned comparisons.
    
    Handle comparisons between unsigned conversions of `abs (x)` and `x`.
    These transformations are applied only when the conversions have
    matching unsigned types and the same precision. Sign-test forms are
    restricted to undefined-overflow semantics; wrapping-overflow variants
    are left for future work.
    
    gcc ChangeLog:
            PR tree-optimization/112626
            * match.pd: Fold comparisons between abs (x) and x including
            unsigned conversions of both operands.
    
    gcc/testsuite/ChangeLog:
            PR tree-optimization/112626
            * gcc.dg/tree-ssa/pr112626.c: New test.
            * gcc.dg/tree-ssa/pr112626-wrap.c: New test.
    
    Signed-off-by: Naveen <[email protected]>

Diff:
---
 gcc/match.pd                                  | 72 +++++++++++++++++++++++++++
 gcc/testsuite/gcc.dg/tree-ssa/pr112626-wrap.c | 33 ++++++++++++
 gcc/testsuite/gcc.dg/tree-ssa/pr112626.c      | 37 ++++++++++++++
 3 files changed, 142 insertions(+)

diff --git a/gcc/match.pd b/gcc/match.pd
index 268bf3f11e52..b6a9d4e79be2 100644
--- a/gcc/match.pd
+++ b/gcc/match.pd
@@ -8164,6 +8164,78 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
        wide_int_to_tree (itype, wi::mul (i, wi::to_wide (@2)));
      })))))
 
+/* abs (X) >= X -> true.  */
+/* abs (X) < X -> false.  */
+(for cmp (ge lt)
+ (simplify
+  (cmp:c (abs @0) @0)
+  (if (INTEGRAL_TYPE_P (TREE_TYPE (@0))
+       && !TYPE_UNSIGNED (TREE_TYPE (@0))
+       && !TYPE_OVERFLOW_TRAPS (TREE_TYPE (@0))
+       && !TYPE_OVERFLOW_SANITIZED (TREE_TYPE (@0)))
+   { constant_boolean_node (cmp == GE_EXPR, type); })))
+
+/* abs (X) >  X -> X < 0.  */
+/* abs (X) != X -> X < 0.  */
+/* abs (X) <= X -> X >= 0.  */
+/* abs (X) == X -> X >= 0.  */
+/* When signed overflow wraps, abs (INT_MIN) == INT_MIN.  */
+(for cmp (gt ne le eq)
+     out (lt lt ge ge)
+ (simplify
+  (cmp:c (abs @0) @0)
+  (if (INTEGRAL_TYPE_P (TREE_TYPE (@0))
+       && !TYPE_UNSIGNED (TREE_TYPE (@0))
+       && !TYPE_OVERFLOW_TRAPS (TREE_TYPE (@0))
+       && !TYPE_OVERFLOW_SANITIZED (TREE_TYPE (@0)))
+   (switch
+    (if (TYPE_OVERFLOW_UNDEFINED (TREE_TYPE (@0)))
+     (out @0 { build_zero_cst (TREE_TYPE (@0)); }))
+    (if (TYPE_OVERFLOW_WRAPS (TREE_TYPE (@0)))
+     (with
+      {
+        tree utype = unsigned_type_for (TREE_TYPE (@0));
+        wide_int min = wi::min_value (TREE_TYPE (@0));
+      }
+      (if (cmp == LE_EXPR || cmp == EQ_EXPR)
+       (lt (convert:utype @0) { wide_int_to_tree (utype, min + 1); })
+       (gt (convert:utype @0) { wide_int_to_tree (utype, min); }))))))))
+
+/* (unsigned) abs (X) <= (unsigned) X -> true.  */
+/* (unsigned) abs (X) > (unsigned) X -> false.  */
+(for cmp (le gt)
+ (simplify
+  (cmp:c (convert@1 (abs @0)) (convert@2 @0))
+  (if (INTEGRAL_TYPE_P (TREE_TYPE (@0))
+       && !TYPE_UNSIGNED (TREE_TYPE (@0))
+       && INTEGRAL_TYPE_P (TREE_TYPE (@1))
+       && TYPE_UNSIGNED (TREE_TYPE (@1))
+       && types_match (TREE_TYPE (@1), TREE_TYPE (@2))
+       && element_precision (TREE_TYPE (@0)) == element_precision (TREE_TYPE 
(@1))
+       && !TYPE_OVERFLOW_TRAPS (TREE_TYPE (@0))
+       && !TYPE_OVERFLOW_SANITIZED (TREE_TYPE (@0)))
+   { constant_boolean_node (cmp == LE_EXPR, type); })))
+
+/* (unsigned) abs (X) <  (unsigned) X -> X < 0.  */
+/* (unsigned) abs (X) != (unsigned) X -> X < 0.  */
+/* (unsigned) abs (X) >= (unsigned) X -> X >= 0.  */
+/* (unsigned) abs (X) == (unsigned) X -> X >= 0.  */
+/* TODO: Add wrapping-overflow variants for these four cases.  */
+(for cmp (lt ne ge eq)
+     out (lt lt ge ge)
+ (simplify
+  (cmp:c (convert@1 (abs @0)) (convert@2 @0))
+  (if (INTEGRAL_TYPE_P (TREE_TYPE (@0))
+       && !TYPE_UNSIGNED (TREE_TYPE (@0))
+       && INTEGRAL_TYPE_P (TREE_TYPE (@1))
+       && TYPE_UNSIGNED (TREE_TYPE (@1))
+       && types_match (TREE_TYPE (@1), TREE_TYPE (@2))
+       && element_precision (TREE_TYPE (@0)) == element_precision (TREE_TYPE 
(@1))
+       && !TYPE_OVERFLOW_TRAPS (TREE_TYPE (@0))
+       && !TYPE_OVERFLOW_SANITIZED (TREE_TYPE (@0))
+       && TYPE_OVERFLOW_UNDEFINED (TREE_TYPE (@0)))
+   (out @0 { build_zero_cst (TREE_TYPE (@0)); }))))
+
 /* Simplify comparison of something with itself.  For IEEE
    floating-point, we can only do some of these simplifications.  */
 (for cmp (eq ge le)
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr112626-wrap.c 
b/gcc/testsuite/gcc.dg/tree-ssa/pr112626-wrap.c
new file mode 100644
index 000000000000..bdc0d17aa8a0
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/pr112626-wrap.c
@@ -0,0 +1,33 @@
+/* PR tree-optimization/112626 - wrapping (-fwrapv) variant.  */
+/* { dg-do compile } */
+/* { dg-options "-O2 -fwrapv -fdump-tree-optimized" } */
+
+#define FUNC(NAME, OP) \
+int abs_##NAME (int a) { return __builtin_abs (a) OP a; }
+#define FUNC_SWAPPED(NAME, OP) \
+int a_##NAME##_abs (int a) { return a OP __builtin_abs (a); }
+#define FUNC_U(NAME, OP) \
+int abs_##NAME##_u (int a) { return ((unsigned) __builtin_abs (a)) OP 
(unsigned) a; }
+
+FUNC (le, <=)
+FUNC (lt, <)
+FUNC (ge, >=)
+FUNC (gt, >)
+FUNC (eq, ==)
+FUNC (ne, !=)
+
+FUNC_SWAPPED (le, <=)
+FUNC_SWAPPED (lt, <)
+FUNC_SWAPPED (ge, >=)
+FUNC_SWAPPED (gt, >)
+FUNC_SWAPPED (eq, ==)
+FUNC_SWAPPED (ne, !=)
+
+FUNC_U (le, <=)
+FUNC_U (gt, >)
+
+/* { dg-final { scan-tree-dump-times "return 1;" 3 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "return 0;" 3 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " > 2147483648" 4 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " <= 2147483648" 4 "optimized" } } */
+/* { dg-final { scan-tree-dump-not "ABS_EXPR" "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr112626.c 
b/gcc/testsuite/gcc.dg/tree-ssa/pr112626.c
new file mode 100644
index 000000000000..428d491eff20
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/pr112626.c
@@ -0,0 +1,37 @@
+/* PR tree-optimization/112626 */
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+#define FUNC(NAME, OP) \
+int abs_##NAME (int a) { return __builtin_abs (a) OP a; }
+#define FUNC_SWAPPED(NAME, OP) \
+int a_##NAME##_abs (int a) { return a OP __builtin_abs (a); }
+#define FUNC_U(NAME, OP) \
+int abs_##NAME##_u (int a) { return ((unsigned) __builtin_abs (a)) OP 
(unsigned) a; }
+
+FUNC (le, <=)
+FUNC (lt, <)
+FUNC (ge, >=)
+FUNC (gt, >)
+FUNC (eq, ==)
+FUNC (ne, !=)
+
+FUNC_SWAPPED (le, <=)
+FUNC_SWAPPED (lt, <)
+FUNC_SWAPPED (ge, >=)
+FUNC_SWAPPED (gt, >)
+FUNC_SWAPPED (eq, ==)
+FUNC_SWAPPED (ne, !=)
+
+FUNC_U (le, <=)
+FUNC_U (lt, <)
+FUNC_U (ge, >=)
+FUNC_U (gt, >)
+FUNC_U (eq, ==)
+FUNC_U (ne, !=)
+
+/* { dg-final { scan-tree-dump-times "return 1;" 3 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "return 0;" 3 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " < 0" 6 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " >= 0" 6 "optimized" } } */
+/* { dg-final { scan-tree-dump-not "ABS_EXPR" "optimized" } } */

Reply via email to