From: Naveen <[email protected]>
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.
Bootstrapped and regtested on x86_64 and aarch64. No new regressions.
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]>
---
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(+)
create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr112626-wrap.c
create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr112626.c
diff --git a/gcc/match.pd b/gcc/match.pd
index 268bf3f11e5..b6a9d4e79be 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 00000000000..bdc0d17aa8a
--- /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 00000000000..428d491eff2
--- /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" } } */
--
2.34.1