https://gcc.gnu.org/g:ff133b4cd2202fc7a5be5cd9dc935c0a75fb6b68
commit r17-5264-gff133b4cd2202fc7a5be5cd9dc935c0a75fb6b68 Author: Uttam Bhadauriya <[email protected]> Date: Fri Oct 16 09:44:17 2026 -0600 [PATCH v4] tree-optimization: Simplify (a & -a) < 0 to a == INT_MIN [PR126627] Hello, apologies for the formatting and rebase issue, I am sending v4 of the patch to address the same. From 987b64d5230398390f926a91612ea5c0a5dcb781 Mon Sep 17 00:00:00 2001 From: Uttam Singh Bhadauriya <[email protected]> Date: Fri, 16 Oct 2026 17:58:52 +0530 Subject: [PATCH v4] tree-optimization: Simplify (a & -a) < 0 to a == INT_MIN [PR126627] The expression (a & -a) < 0 on a signed integer (or an unsigned integer cast to a signed integer) is equivalent to checking if a is the minimum signed value for its type. The only value where the lowest set bit is the sign bit is INT_MIN. This patch introduces a simplification in match.pd to optimize this pattern into a direct equality check. It also handles the negate-in-unsigned form which avoids UB on negation, matching it using the new negate_maybe_without_ud predicate. It guards the transformation with !TYPE_OVERFLOW_TRAPS since -a could otherwise trap for INT_MIN, which would incorrectly be removed. --- v4: - Replaced scan counts and regexes in the testcases with exact counts. - Dropped precision > 1 requirement to also support 1-bit signed types. - Added '/* { dg-require-effective-target int32 } */' to both testcases. - Added INTEGRAL_TYPE_P guards to prevent matching pointer conversions. - Introduced negate_maybe_without_ud predicate to handle unsigned negation. - Guarded with !TYPE_OVERFLOW_TRAPS to prevent dropping traps under -ftrapv. - Fixed (short) cast in pr126627-2.c and changed -fdump-tree-all to -fdump-tree-optimized. - Formatted comments according to GNU style guidelines. Bootstrapped and regression tested on x86_64-pc-linux-gnu, Windows 11 Version 23H2 with WSL Ubuntu 22.04.05 LTS: - pr126627.c and pr126627-2.c: 12 passes in DejaGnu. - tree-ssa.exp: 9,194 passes, no regressions. - Verified behavior under -ftrapv (no folds) and -fwrapv (folded). PR tree-optimization/126627 gcc/ChangeLog: * match.pd: Simplify (a & -a) < 0 to a == INT_MIN and (a & -a) >= 0 to a != INT_MIN, using the negate_maybe_without_ud predicate to support both signed and unsigned negation. gcc/testsuite/ChangeLog: * gcc.dg/tree-ssa/pr126627.c: New test. * gcc.dg/tree-ssa/pr126627-2.c: New test. Signed-off-by: Uttam Singh Bhadauriya <[email protected]> Diff: --- gcc/match.pd | 20 ++++++++++++++++ gcc/testsuite/gcc.dg/pr127916.c | 38 ++++++++++++++++++++++++++++++ gcc/testsuite/gcc.dg/tree-ssa/pr126627-2.c | 15 ++++++++++++ gcc/testsuite/gcc.dg/tree-ssa/pr126627.c | 24 +++++++++++++++++++ 4 files changed, 97 insertions(+) diff --git a/gcc/match.pd b/gcc/match.pd index a28d307c819c..629114591a2b 100644 --- a/gcc/match.pd +++ b/gcc/match.pd @@ -9268,6 +9268,26 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT) && TYPE_PRECISION (TREE_TYPE (@0)) == TYPE_PRECISION (TREE_TYPE (@1))) (cmp @0 (convert @1))))) +/* (a & -a) < 0 -> a == INT_MIN + (a & -a) >= 0 -> a != INT_MIN. */ +(match (negate_maybe_without_ud @0) + (negate @0)) +(match (negate_maybe_without_ud @0) + (convert (negate (nop_convert @0))) + (if (types_match (type, @0)))) + +(for cmp (lt ge) + eqcmp (eq ne) + (simplify + (cmp (nop_convert?@0 (bit_and:c (negate_maybe_without_ud @1) @1)) + integer_zerop) + (if (INTEGRAL_TYPE_P (TREE_TYPE (@0)) + && INTEGRAL_TYPE_P (TREE_TYPE (@1)) + && !TYPE_UNSIGNED (TREE_TYPE (@0)) + && !TYPE_OVERFLOW_TRAPS (TREE_TYPE (@1))) + (eqcmp @1 { wide_int_to_tree (TREE_TYPE (@1), + wi::min_value (TYPE_PRECISION (TREE_TYPE (@1)), SIGNED)); })))) + /* Non-equality compare simplifications from fold_binary */ (for cmp (lt gt le ge) /* Comparisons with the highest or lowest possible integer of diff --git a/gcc/testsuite/gcc.dg/pr127916.c b/gcc/testsuite/gcc.dg/pr127916.c new file mode 100644 index 000000000000..e4229946408e --- /dev/null +++ b/gcc/testsuite/gcc.dg/pr127916.c @@ -0,0 +1,38 @@ +/* { dg-do run } */ + +#include <limits.h> + +__attribute__((noipa)) int +s_sat (int x) +{ + int sum = (int) ((unsigned) x + 1u); + if (((x ^ sum) & (x ^ 2)) < 0) + return -(int) (x < 0) ^ INT_MAX; + return sum; +} + +__attribute__((noipa)) signed char +another_sat (int a, int b) +{ + signed char r; + if (__builtin_add_overflow (a, b, &r)) + return a < 0 ? SCHAR_MIN : SCHAR_MAX; + return r; +} + +int main (void) +{ + if (s_sat (INT_MAX) != INT_MIN) + __builtin_abort (); + + if (s_sat (-1) != INT_MIN) + __builtin_abort (); + + if (another_sat (-1, 200) != -128) + __builtin_abort (); + + if (another_sat (100, 100) != 127) + __builtin_abort (); + + __builtin_exit (0); +} diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr126627-2.c b/gcc/testsuite/gcc.dg/tree-ssa/pr126627-2.c new file mode 100644 index 000000000000..ea68b8897be8 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr126627-2.c @@ -0,0 +1,15 @@ +/* PR tree-optimization/126627 */ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-optimized" } */ +/* { dg-require-effective-target int32 } */ + +int test_ss (short a) { return (short)(a & -a) < 0; } +int test_ss2 (short a) { return (short)(a & -a) >= 0; } +int test_us (unsigned short a) { return (short)(a & -a) < 0; } +int test_us2 (unsigned short a) { return (short)(a & -a) >= 0; } + +/* { dg-final { scan-tree-dump-not " & " "optimized" } } */ +/* { dg-final { scan-tree-dump-times " == -32768" 1 "optimized" } } */ +/* { dg-final { scan-tree-dump-times " != -32768" 1 "optimized" } } */ +/* { dg-final { scan-tree-dump-times " == 32768" 1 "optimized" } } */ +/* { dg-final { scan-tree-dump-times " != 32768" 1 "optimized" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr126627.c b/gcc/testsuite/gcc.dg/tree-ssa/pr126627.c new file mode 100644 index 000000000000..d11f5312baf2 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr126627.c @@ -0,0 +1,24 @@ +/* PR tree-optimization/126627 */ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-optimized" } */ +/* { dg-require-effective-target int32 } */ + +int test1 (int a) { return (a & -a) < 0; } +int test2 (int a) { return (a & -a) >= 0; } +int test3 (unsigned a) { return (int)(a & -a) < 0; } +int test4 (unsigned a) { return (int)(a & -a) >= 0; } + +/* Negation done in unsigned to avoid UB. */ +int test_si (signed a) +{ + unsigned b = a; + b = -b; + signed t = b; + return (a & t) >= 0; +} + +/* { dg-final { scan-tree-dump-not " & " "optimized" } } */ +/* { dg-final { scan-tree-dump-times " == -2147483648" 1 "optimized" } } */ +/* { dg-final { scan-tree-dump-times " != -2147483648" 2 "optimized" } } */ +/* { dg-final { scan-tree-dump-times " == 2147483648" 1 "optimized" } } */ +/* { dg-final { scan-tree-dump-times " != 2147483648" 1 "optimized" } } */
