[gcc r17-2972] tree-optimization: Recognize add/sub absolute-value idiom [PR56223]

"Naveen H.S via Gcc-cvs" <[email protected]> Wed, 5 Aug 2026 07:54:05 +0000 (GMT)
Newsgroups gmane.comp.gcc.cvs
Message-ID <[email protected]>
https://gcc.gnu.org/g:36990bb0d14c4840d0250f000559d7a2a569fbc9

commit r17-2972-g36990bb0d14c4840d0250f000559d7a2a569fbc9
Author: Naveen <[email protected]>
Date:   Wed Aug 5 00:52:53 2026 -0700

    tree-optimization: Recognize add/sub absolute-value idiom [PR56223]
    
    Recognize conditional addition and subtraction patterns equivalent to
    Y + abs (X) and Y - abs (X).
    
    For signed integral types, perform the addition or subtraction in the
    corresponding unsigned type and convert the result back to the original
    type.  This avoids introducing signed overflow and preserves wrapping
    semantics including when X is TYPE_MIN_VALUE.
    
    Do not perform the transformation when signed overflow traps or is
    sanitized.
    
    gcc/ChangeLog:
            PR tree-optimization/56223
            * match.pd (X >=/> 0 ? Y + X : Y - X): New simplification.
            (X <=/< 0 ? Y - X : Y + X): Likewise.
    
    gcc/testsuite/ChangeLog:
            PR tree-optimization/56223
            * gcc.dg/tree-ssa/pr56223.c: New test.
            * gcc.dg/tree-ssa/pr56223-2.c: New test.
            * gcc.dg/tree-ssa/pr56223-3.c: New test.
            * gcc.dg/tree-ssa/pr56223-4.c: New test.
    
    Signed-off-by: Naveen <[email protected]>

Diff:
---
 gcc/match.pd                              | 48 +++++++++++++++++
 gcc/testsuite/gcc.dg/tree-ssa/pr56223-2.c | 63 ++++++++++++++++++++++
 gcc/testsuite/gcc.dg/tree-ssa/pr56223-3.c | 25 +++++++++
 gcc/testsuite/gcc.dg/tree-ssa/pr56223-4.c | 25 +++++++++
 gcc/testsuite/gcc.dg/tree-ssa/pr56223.c   | 86 +++++++++++++++++++++++++++++++
 5 files changed, 247 insertions(+)

diff --git a/gcc/match.pd b/gcc/match.pd
index d5160d87e304..2545d62b4ab4 100644
--- a/gcc/match.pd
+++ b/gcc/match.pd
@@ -7270,6 +7270,54 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
  )
 )
 
+/* X >=/> 0 ? Y + X : Y - X
+   X <=/< 0 ? Y - X : Y + X
+     same as Y + abs (X).
+
+   X >=/> 0 ? Y - X : Y + X
+   X <=/< 0 ? Y + X : Y - X
+     same as Y - abs (X).
+
+   Build the addition or subtraction in an unsigned type for integral
+   types so the replacement does not introduce signed overflow.  */
+
+#if GIMPLE
+(for cmp (ge gt)
+ (simplify
+  (cond (cmp @0 integer_zerop) (plus:c @0 @1) (minus @1 @0))
+  (if (!TYPE_UNSIGNED (type)
+       && !TYPE_OVERFLOW_TRAPS (type)
+       && !TYPE_OVERFLOW_SANITIZED (type))
+   (with { tree utype = unsigned_type_for (type); }
+    (convert (plus:utype (convert:utype @1)
+			 (absu:utype @0))))))
+ (simplify
+  (cond (cmp @0 integer_zerop) (minus @1 @0) (plus:c @0 @1))
+  (if (!TYPE_UNSIGNED (type)
+       && !TYPE_OVERFLOW_TRAPS (type)
+       && !TYPE_OVERFLOW_SANITIZED (type))
+   (with { tree utype = unsigned_type_for (type); }
+    (convert (minus:utype (convert:utype @1)
+			  (absu:utype @0)))))))
+(for cmp (le lt)
+ (simplify
+  (cond (cmp @0 integer_zerop) (minus @1 @0) (plus:c @0 @1))
+  (if (!TYPE_UNSIGNED (type)
+       && !TYPE_OVERFLOW_TRAPS (type)
+       && !TYPE_OVERFLOW_SANITIZED (type))
+   (with { tree utype = unsigned_type_for (type); }
+    (convert (plus:utype (convert:utype @1)
+			 (absu:utype @0))))))
+ (simplify
+  (cond (cmp @0 integer_zerop) (plus:c @0 @1) (minus @1 @0))
+  (if (!TYPE_UNSIGNED (type)
+       && !TYPE_OVERFLOW_TRAPS (type)
+       && !TYPE_OVERFLOW_SANITIZED (type))
+   (with { tree utype = unsigned_type_for (type); }
+    (convert (minus:utype (convert:utype @1)
+			  (absu:utype @0)))))))
+#endif
+
 /* -(type)!A -> (type)A - 1.  */
 (simplify
  (negate (convert?:s (logical_inverted_value:s @0)))
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr56223-2.c b/gcc/testsuite/gcc.dg/tree-ssa/pr56223-2.c
new file mode 100644
index 000000000000..f03105683c44
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/pr56223-2.c
@@ -0,0 +1,63 @@
+/* PR tree-optimization/56223 */
+/* Verify wrapping and INT_MIN behavior of the ABSU-based replacement.  */
+/* { dg-do run } */
+/* { dg-options "-O2 -fwrapv" } */
+
+#define INT_MAX __INT_MAX__
+#define INT_MIN (-INT_MAX - 1)
+
+__attribute__ ((noipa)) int
+add_abs (int s, int x)
+{
+  if (x >= 0)
+    s += x;
+  else
+    s -= x;
+  return s;
+}
+
+__attribute__ ((noipa)) int
+sub_abs (int s, int x)
+{
+  if (x >= 0)
+    s -= x;
+  else
+    s += x;
+  return s;
+}
+
+int
+main (void)
+{
+  if (add_abs (10, 4) != 14)
+    __builtin_abort ();
+
+  if (add_abs (10, -4) != 14)
+    __builtin_abort ();
+
+  if (add_abs (INT_MIN, INT_MIN) != 0)
+    __builtin_abort ();
+
+  if (add_abs (INT_MAX, 1) != INT_MIN)
+    __builtin_abort ();
+
+  if (add_abs (0, INT_MIN) != INT_MIN)
+    __builtin_abort ();
+
+  if (sub_abs (10, 4) != 6)
+    __builtin_abort ();
+
+  if (sub_abs (10, -4) != 6)
+    __builtin_abort ();
+
+  if (sub_abs (INT_MIN, INT_MIN) != 0)
+    __builtin_abort ();
+
+  if (sub_abs (INT_MIN, 1) != INT_MAX)
+    __builtin_abort ();
+
+  if (sub_abs (0, INT_MIN) != INT_MIN)
+    __builtin_abort ();
+
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr56223-3.c b/gcc/testsuite/gcc.dg/tree-ssa/pr56223-3.c
new file mode 100644
index 000000000000..595a7df1b2fd
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/pr56223-3.c
@@ -0,0 +1,25 @@
+/* PR tree-optimization/56223 */
+/* { dg-do compile } */
+/* { dg-options "-O2 -ftrapv -fdump-tree-phiopt2" } */
+
+int
+add_abs (int s, int x)
+{
+  if (x >= 0)
+    s += x;
+  else
+    s -= x;
+  return s;
+}
+
+int
+sub_abs (int s, int x)
+{
+  if (x >= 0)
+    s -= x;
+  else
+    s += x;
+  return s;
+}
+
+/* { dg-final { scan-tree-dump-not "ABSU_EXPR" "phiopt2" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr56223-4.c b/gcc/testsuite/gcc.dg/tree-ssa/pr56223-4.c
new file mode 100644
index 000000000000..f17f84219d92
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/pr56223-4.c
@@ -0,0 +1,25 @@
+/* PR tree-optimization/56223 */
+/* { dg-do compile } */
+/* { dg-options "-O2 -fsanitize=signed-integer-overflow -fdump-tree-phiopt2" } */
+
+int
+add_abs (int s, int x)
+{
+  if (x >= 0)
+    s += x;
+  else
+    s -= x;
+  return s;
+}
+
+int
+sub_abs (int s, int x)
+{
+  if (x >= 0)
+    s -= x;
+  else
+    s += x;
+  return s;
+}
+
+/* { dg-final { scan-tree-dump-not "ABSU_EXPR" "phiopt2" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr56223.c b/gcc/testsuite/gcc.dg/tree-ssa/pr56223.c
new file mode 100644
index 000000000000..9a7c94bfb75d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/pr56223.c
@@ -0,0 +1,86 @@
+/* PR tree-optimization/56223 */
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-phiopt2" } */
+
+int
+add_ge (int s, int x)
+{
+  if (x >= 0)
+    s += x;
+  else
+    s -= x;
+  return s;
+}
+
+int
+add_gt (int s, int x)
+{
+  if (x > 0)
+    s += x;
+  else
+    s -= x;
+  return s;
+}
+
+int
+add_le (int s, int x)
+{
+  if (x <= 0)
+    s -= x;
+  else
+    s += x;
+  return s;
+}
+
+int
+add_lt (int s, int x)
+{
+  if (x < 0)
+    s -= x;
+  else
+    s += x;
+  return s;
+}
+
+int
+sub_ge (int s, int x)
+{
+  if (x >= 0)
+    s -= x;
+  else
+    s += x;
+  return s;
+}
+
+int
+sub_gt (int s, int x)
+{
+  if (x > 0)
+    s -= x;
+  else
+    s += x;
+  return s;
+}
+
+int
+sub_le (int s, int x)
+{
+  if (x <= 0)
+    s += x;
+  else
+    s -= x;
+  return s;
+}
+
+int
+sub_lt (int s, int x)
+{
+  if (x < 0)
+    s += x;
+  else
+    s -= x;
+  return s;
+}
+
+/* { dg-final { scan-tree-dump-times "ABSU_EXPR" 8 "phiopt2" } } */
+/* { dg-final { scan-tree-dump-not "if " "phiopt2" } } */