>From 3f062e535fb22ba95de195357364ec503cb28ceb Mon Sep 17 00:00:00 2001
From: Uttam Singh Bhadauriya <[email protected]>
Date: Wed, 21 Oct 2026 10:23:10 +0530
Subject: [PATCH v2] match.pd: Recognize X == MIN ? MIN : X - 1 as signed sat add
 [PR127985]

Commit ff133b4cd22 (PR126627) simplified (X & -X) < 0 into X == INT_MIN
and (X & -X) >= 0 into X != INT_MIN.  However, the signed saturating
addition pattern for IMM = -1 in match-sat-alu.pd looked for the
unsimplified (X & -X) < 0 condition.  As a result, a saturating decrement
is no longer recognized as .SAT_ADD, which causes regressions in
gcc.target/riscv/sat/sat_s_add_imm-*.c.

Because the old (X & -X) < 0 pattern can no longer match outside
-ftrapv, this patch replaces it with match patterns for the canonical
X == MIN ? MIN : X - 1 and X != MIN ? X - 1 : MIN forms.  This restores
.SAT_ADD discovery on RISC-V and also captures saturating decrements
written directly with MIN comparisons.

    PR tree-optimization/127985

gcc/ChangeLog:

    * match-sat-alu.pd (signed_integer_sat_add): Replace obsolete
    (X & -X) < 0 pattern with patterns for X == MIN ? MIN : X - 1
    and X != MIN ? X - 1 : MIN.

gcc/testsuite/ChangeLog:

    * gcc.dg/torture/pr127985.c: New test.
    * gcc.target/riscv/sat/pr127985.c: New test.

Signed-off-by: Uttam Singh Bhadauriya <[email protected]>
---
Hello,

Following the suggestions by Zhongyao, I have updated the patch to
recognize both
comparison polarities:
  X == MIN ? MIN : X - 1
  X != MIN ? X - 1 : MIN

Because commit ff133b4cd22 (PR126627) eagerly simplifies (X & -X) < 0 into
X == INT_MIN and (X & -X) >= 0 into X != INT_MIN before math-opts runs,
the old (X & -X) < 0 pattern in match-sat-alu.pd can no longer match under
normal optimization. This patch cleanly replaces it with the two canonical
forms above.

The patch includes:
- A target-independent dg-do run torture test covering MIN, MIN + 1, -1, 0, 10,
  and MAX across 8, 16, 32, and 64-bit signed types.
- A RISC-V scan test verifying 8 .SAT_ADD occurrences and branchless assembly.

Regtested on x86_64-pc-linux-gnu:
- dg-torture: pr127985.c passed execution across -O0, -O1, -O2, -O3, and -Os.



Requesting you to re-run the rv64gcv tests on this updated patch and
confirm if everything works fine.

Kind regards,
Uttam

 gcc/match-sat-alu.pd                          |  31 ++++-
 gcc/testsuite/gcc.dg/torture/pr127985.c       | 122 ++++++++++++++++++
 gcc/testsuite/gcc.target/riscv/sat/pr127985.c |  56 ++++++++
 3 files changed, 204 insertions(+), 5 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/torture/pr127985.c
 create mode 100644 gcc/testsuite/gcc.target/riscv/sat/pr127985.c

diff --git a/gcc/match-sat-alu.pd b/gcc/match-sat-alu.pd
index 236d6fb2e19..28bed437334 100644
--- a/gcc/match-sat-alu.pd
+++ b/gcc/match-sat-alu.pd
@@ -486,10 +486,8 @@ along with GCC; see the file COPYING3.  If not see

 (match (signed_integer_sat_add @0 @1)
   /* T SUM = (T)((UT)X + (UT)-1);
-     SAT_S_ADD = (X ^ -1) < 0 ? SUM : (X ^ SUM) >= 0 ? SUM
-                             : (x < 0) ? MIN : MAX  */
-  (convert (cond^ (lt (bit_and:c @0 (nop_convert (negate (nop_convert @0))))
-             integer_zerop)
+     SAT_S_ADD = X == MIN ? MIN : SUM.  */
+  (convert (cond^ (eq (nop_convert? @0) INTEGER_CST@3)
     INTEGER_CST@2
     (plus (nop_convert @0) integer_all_onesp@1)))
    (with
@@ -497,11 +495,34 @@ along with GCC; see the file COPYING3.  If not see
      unsigned precision = TYPE_PRECISION (type);
      wide_int c1 = wi::to_wide (@1);
      wide_int c2 = wi::to_wide (@2);
+     wide_int c3 = wi::to_wide (@3);
+     wide_int sum = wi::add (c1, c2);
+    }
+    /* The outer conversion must not change the type of X.  */
+    (if (types_match (type, @0)
+    && TYPE_PRECISION (TREE_TYPE (@3)) == precision
+    && wi::eq_p (sum, wi::max_value (precision, SIGNED))
+    && wi::eq_p (c3, wi::min_value (precision, SIGNED))))))
+
+(match (signed_integer_sat_add @0 @1)
+  /* T SUM = (T)((UT)X + (UT)-1);
+     SAT_S_ADD = X != MIN ? SUM : MIN.  */
+  (convert (cond^ (ne (nop_convert? @0) INTEGER_CST@3)
+    (plus (nop_convert @0) integer_all_onesp@1)
+    INTEGER_CST@2))
+   (with
+    {
+     unsigned precision = TYPE_PRECISION (type);
+     wide_int c1 = wi::to_wide (@1);
+     wide_int c2 = wi::to_wide (@2);
+     wide_int c3 = wi::to_wide (@3);
      wide_int sum = wi::add (c1, c2);
     }
     /* The outer conversion must not change the type of X.  */
     (if (types_match (type, @0)
-    && wi::eq_p (sum, wi::max_value (precision, SIGNED))))))
+    && TYPE_PRECISION (TREE_TYPE (@3)) == precision
+    && wi::eq_p (sum, wi::max_value (precision, SIGNED))
+    && wi::eq_p (c3, wi::min_value (precision, SIGNED))))))

 (match (signed_integer_sat_add @0 @1)
   /* T SUM = (T)((UT)X + (UT)IMM)
diff --git a/gcc/testsuite/gcc.dg/torture/pr127985.c
b/gcc/testsuite/gcc.dg/torture/pr127985.c
new file mode 100644
index 00000000000..0ebdc47b2b4
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/torture/pr127985.c
@@ -0,0 +1,122 @@
+/* PR tree-optimization/127985 */
+/* { dg-do run } */
+
+#include <stdint.h>
+
+__attribute__((noipa)) int8_t
+dec_i8 (int8_t x)
+{
+  return x == INT8_MIN ? INT8_MIN : x - 1;
+}
+
+__attribute__((noipa)) int16_t
+dec_i16 (int16_t x)
+{
+  return x == INT16_MIN ? INT16_MIN : x - 1;
+}
+
+__attribute__((noipa)) int32_t
+dec_i32 (int32_t x)
+{
+  return x == INT32_MIN ? INT32_MIN : x - 1;
+}
+
+__attribute__((noipa)) int64_t
+dec_i64 (int64_t x)
+{
+  return x == INT64_MIN ? INT64_MIN : x - 1;
+}
+
+__attribute__((noipa)) int8_t
+dec_ne_i8 (int8_t x)
+{
+  return x != INT8_MIN ? x - 1 : INT8_MIN;
+}
+
+__attribute__((noipa)) int16_t
+dec_ne_i16 (int16_t x)
+{
+  return x != INT16_MIN ? x - 1 : INT16_MIN;
+}
+
+__attribute__((noipa)) int32_t
+dec_ne_i32 (int32_t x)
+{
+  return x != INT32_MIN ? x - 1 : INT32_MIN;
+}
+
+__attribute__((noipa)) int64_t
+dec_ne_i64 (int64_t x)
+{
+  return x != INT64_MIN ? x - 1 : INT64_MIN;
+}
+
+int
+main (void)
+{
+  if (dec_i8 (INT8_MIN) != INT8_MIN
+      || dec_i8 (INT8_MIN + 1) != INT8_MIN
+      || dec_i8 (-1) != -2
+      || dec_i8 (0) != -1
+      || dec_i8 (10) != 9
+      || dec_i8 (INT8_MAX) != INT8_MAX - 1)
+    __builtin_abort ();
+
+  if (dec_i16 (INT16_MIN) != INT16_MIN
+      || dec_i16 (INT16_MIN + 1) != INT16_MIN
+      || dec_i16 (-1) != -2
+      || dec_i16 (0) != -1
+      || dec_i16 (10) != 9
+      || dec_i16 (INT16_MAX) != INT16_MAX - 1)
+    __builtin_abort ();
+
+  if (dec_i32 (INT32_MIN) != INT32_MIN
+      || dec_i32 (INT32_MIN + 1) != INT32_MIN
+      || dec_i32 (-1) != -2
+      || dec_i32 (0) != -1
+      || dec_i32 (10) != 9
+      || dec_i32 (INT32_MAX) != INT32_MAX - 1)
+    __builtin_abort ();
+
+  if (dec_i64 (INT64_MIN) != INT64_MIN
+      || dec_i64 (INT64_MIN + 1) != INT64_MIN
+      || dec_i64 (-1) != -2
+      || dec_i64 (0) != -1
+      || dec_i64 (10) != 9
+      || dec_i64 (INT64_MAX) != INT64_MAX - 1)
+    __builtin_abort ();
+
+  if (dec_ne_i8 (INT8_MIN) != INT8_MIN
+      || dec_ne_i8 (INT8_MIN + 1) != INT8_MIN
+      || dec_ne_i8 (-1) != -2
+      || dec_ne_i8 (0) != -1
+      || dec_ne_i8 (10) != 9
+      || dec_ne_i8 (INT8_MAX) != INT8_MAX - 1)
+    __builtin_abort ();
+
+  if (dec_ne_i16 (INT16_MIN) != INT16_MIN
+      || dec_ne_i16 (INT16_MIN + 1) != INT16_MIN
+      || dec_ne_i16 (-1) != -2
+      || dec_ne_i16 (0) != -1
+      || dec_ne_i16 (10) != 9
+      || dec_ne_i16 (INT16_MAX) != INT16_MAX - 1)
+    __builtin_abort ();
+
+  if (dec_ne_i32 (INT32_MIN) != INT32_MIN
+      || dec_ne_i32 (INT32_MIN + 1) != INT32_MIN
+      || dec_ne_i32 (-1) != -2
+      || dec_ne_i32 (0) != -1
+      || dec_ne_i32 (10) != 9
+      || dec_ne_i32 (INT32_MAX) != INT32_MAX - 1)
+    __builtin_abort ();
+
+  if (dec_ne_i64 (INT64_MIN) != INT64_MIN
+      || dec_ne_i64 (INT64_MIN + 1) != INT64_MIN
+      || dec_ne_i64 (-1) != -2
+      || dec_ne_i64 (0) != -1
+      || dec_ne_i64 (10) != 9
+      || dec_ne_i64 (INT64_MAX) != INT64_MAX - 1)
+    __builtin_abort ();
+
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/riscv/sat/pr127985.c
b/gcc/testsuite/gcc.target/riscv/sat/pr127985.c
new file mode 100644
index 00000000000..50c61def96b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/sat/pr127985.c
@@ -0,0 +1,56 @@
+/* PR tree-optimization/127985 */
+/* { dg-do compile } */
+/* { dg-options "-march=rv64gc -mabi=lp64d -O3 -fdump-tree-optimized" } */
+
+#include <stdint.h>
+
+int8_t
+dec_i8 (int8_t x)
+{
+  return x == INT8_MIN ? INT8_MIN : x - 1;
+}
+
+int16_t
+dec_i16 (int16_t x)
+{
+  return x == INT16_MIN ? INT16_MIN : x - 1;
+}
+
+int32_t
+dec_i32 (int32_t x)
+{
+  return x == INT32_MIN ? INT32_MIN : x - 1;
+}
+
+int64_t
+dec_i64 (int64_t x)
+{
+  return x == INT64_MIN ? INT64_MIN : x - 1;
+}
+
+int8_t
+dec_ne_i8 (int8_t x)
+{
+  return x != INT8_MIN ? x - 1 : INT8_MIN;
+}
+
+int16_t
+dec_ne_i16 (int16_t x)
+{
+  return x != INT16_MIN ? x - 1 : INT16_MIN;
+}
+
+int32_t
+dec_ne_i32 (int32_t x)
+{
+  return x != INT32_MIN ? x - 1 : INT32_MIN;
+}
+
+int64_t
+dec_ne_i64 (int64_t x)
+{
+  return x != INT64_MIN ? x - 1 : INT64_MIN;
+}
+
+/* { dg-final { scan-tree-dump-times ".SAT_ADD " 8 "optimized" } } */
+/* { dg-final { scan-assembler-not "\\.L\[0-9\]+" } } */
-- 
2.34.1

Reply via email to