Recognize the absolute-difference idiom whose selector compares the
operands directly (a > b ? a - b : b - a), optionally widened, as
+/- abs (A - B).

The same-width case is not folded under -fsanitize=signed-integer-overflow
so the rewrite does not move the overflow the sanitizer reports.

The transformation A </<= B ? A - B : B - A -> -abs (A - B) is only for
widened operation. For same precision operands it can introduce new
undefined behaviour for case a=0 and b=INT_MIN. (b-a) is defined for
given values. But -abs(a-b) overflows.

Transforming to abs(b-a) does not solve it either as for a=INT_MIN and
b=0, (b-a) overflows.

        PR tree-optimization/50856

gcc/ChangeLog:

        * match.pd (A CMP B ? A - B : B - A -> +/- abs (A - B)): New
        patterns.

gcc/testsuite/ChangeLog:

        * gcc.dg/tree-ssa/pr50856.c: New test.
        * g++.target/aarch64/pr50856.C: New test.

Signed-off-by: Eikansh Gupta <[email protected]>
---
 gcc/match.pd                               | 28 +++++++++++++
 gcc/testsuite/g++.target/aarch64/pr50856.C | 26 ++++++++++++
 gcc/testsuite/gcc.dg/tree-ssa/pr50856.c    | 46 ++++++++++++++++++++++
 3 files changed, 100 insertions(+)
 create mode 100644 gcc/testsuite/g++.target/aarch64/pr50856.C
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr50856.c

diff --git a/gcc/match.pd b/gcc/match.pd
index 3912f9f7d54..cc739b4361c 100644
--- a/gcc/match.pd
+++ b/gcc/match.pd
@@ -8055,6 +8055,34 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
       (convert (negate (absu:utype @0))))
       (negate (abs @0)))))
  )
+ /* A CMP B ? A - B : B - A -> +/- abs (A - B), with the operands
+    optionally widened first.  The </<= forms require widening: for
+    same-precision operands, -abs (A - B) can introduce undefined overflow.
+    For example, with A == 0 and B == INT_MIN, B - A is defined but
+    -abs (A - B) is not.  */
+ (for cmp (gt ge lt le)
+  (simplify
+   (cnd (cmp @0 @1)
+       (minus@4 (convert?@2 @0) (convert?@3 @1))
+       (minus @3 @2))
+   (if (ANY_INTEGRAL_TYPE_P (type)
+       && !TYPE_UNSIGNED (type)
+       /* A truncation can change the ordering used by the selector.  */
+       && (element_precision (@0) < element_precision (type)
+           || ((cmp == GT_EXPR || cmp == GE_EXPR)
+               && types_match (TREE_TYPE (@0), type)
+               && TYPE_OVERFLOW_UNDEFINED (type)
+               /* Hoisting A - B out of the selector could move the
+                  overflow the sanitizer reports.  */
+               && !sanitize_flags_p (SANITIZE_SI_OVERFLOW)))
+       && (!VECTOR_TYPE_P (type)
+           || (target_supports_op_p (type, ABS_EXPR, optab_vector)
+               && ((cmp == GT_EXPR || cmp == GE_EXPR)
+                   || target_supports_op_p (type, NEGATE_EXPR,
+                                    optab_vector)))))
+    (if (cmp == LT_EXPR || cmp == LE_EXPR)
+     (negate (abs @4))
+     (abs @4)))))
 )
 
 /* X >=/> 0 ? Y + X : Y - X
diff --git a/gcc/testsuite/g++.target/aarch64/pr50856.C 
b/gcc/testsuite/g++.target/aarch64/pr50856.C
new file mode 100644
index 00000000000..f10f16f3834
--- /dev/null
+++ b/gcc/testsuite/g++.target/aarch64/pr50856.C
@@ -0,0 +1,26 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+/* vector a CMP b ? a - b : b - a -> abs(a - b).  Folds when the
+   target has a vector abs.  */
+
+typedef short v8hi __attribute__ ((vector_size (16)));
+typedef int v4si __attribute__ ((vector_size (16)));
+
+v8hi abd_h (v8hi a, v8hi b)
+{
+  return a > b ? a - b : b - a;
+}
+
+v4si abd_s (v4si a, v4si b)
+{
+  return a >= b ? a - b : b - a;
+}
+
+/* Same-width -abs must not fold.  */
+v8hi nabd_h (v8hi a, v8hi b)
+{
+  return a < b ? a - b : b - a;
+}
+
+/* { dg-final { scan-tree-dump-times "ABS_EXPR" 2 "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr50856.c 
b/gcc/testsuite/gcc.dg/tree-ssa/pr50856.c
new file mode 100644
index 00000000000..8f12ef3a2ac
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/pr50856.c
@@ -0,0 +1,46 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+/* a CMP b ? a - b : b - a -> +/- abs (a - b).  */
+
+int gt_w (unsigned char a, unsigned char b)
+{
+  int x = a, y = b;
+  return a > b ? x - y : y - x;
+}
+
+int ge_w (unsigned char a, unsigned char b)
+{
+  int x = a, y = b;
+  return a >= b ? x - y : y - x;
+}
+
+int lt_w (unsigned char a, unsigned char b)
+{
+  int x = a, y = b;
+  return a < b ? x - y : y - x;
+}
+
+int le_w (unsigned char a, unsigned char b)
+{
+  int x = a, y = b;
+  return a <= b ? x - y : y - x;
+}
+
+int gt_i (int a, int b)
+{
+  return a > b ? a - b : b - a;
+}
+
+/* Same-width -abs must not fold.  */
+int lt_i (int a, int b)
+{
+  return a < b ? a - b : b - a;
+}
+
+int trunc_i (long a, long b)
+{
+  return a > b ? (int) a - (int) b : (int) b - (int) a;
+}
+
+/* { dg-final { scan-tree-dump-times "ABS_EXPR" 5 "optimized" } } */
-- 
2.34.1

Reply via email to