From 36990bb0d14c4840d0250f000559d7a2a569fbc9 Mon Sep 17 00:00:00 2001 From: Naveen Date: Wed, 5 Aug 2026 00:52:53 -0700 Subject: [PATCH] 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 --- 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(+) create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr56223-2.c create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr56223-3.c create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr56223-4.c create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr56223.c diff --git a/gcc/match.pd b/gcc/match.pd index d5160d87e30..2545d62b4ab 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 00000000000..f03105683c4 --- /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 00000000000..595a7df1b2f --- /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 00000000000..f17f84219d9 --- /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 00000000000..9a7c94bfb75 --- /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" } } */ -- 2.47.3