On Sat, Oct 10, 2026 at 7:00 AM Uttam Bhadauriya
<[email protected]> wrote:
>
> Hello !
>
> I am suggesting a patch for PR126627 / tree - optimization via this email.
>
> 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 ('a == INT_MIN' and 'a !=
> INT_MIN').
>
> It also explicitly guards the transformation with
> '!TYPE_OVERFLOW_TRAPS' since '-a' could otherwise trap for 'INT_MIN',
> which would incorrectly be removed if folded.
>
> This patch has been locally built and regression tested on
> 'x86_64-pc-linux-gnu' ( As per the target shown in make file ).
>
> OS : Windows 11, Version : 23H2, with WSL Ubuntu 22.04.05 LTS
>
> Hopefully this is as per the expectations of the maintainers, this
> would be my first contribution in the GCC toolchain so apologies for
> any mistakes.
>
> Kind regards
> Uttam Bhadauriya
> P.S.: Please check the patch details below.
>
> From 31ff0cbfe3481c58956c6609bb09c10444978737 Mon Sep 17 00:00:00 2001
> From: Uttam Singh Bhadauriya <[email protected]>
> Date: Sat, 10 Oct 2026 18:57:38 +0530
> Subject: [PATCH] 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 guards the transformation with !TYPE_OVERFLOW_TRAPS since -a could
> otherwise trap for INT_MIN, which would incorrectly be removed.
>
> gcc/ChangeLog:
>
> PR tree-optimization/126627
> * match.pd: Simplify (a & -a) < 0 to a == INT_MIN and
> (a & -a) >= 0 to a != INT_MIN.
>
> gcc/testsuite/ChangeLog:
>
> PR tree-optimization/126627
> * gcc.dg/tree-ssa/pr126627.c: New test.
>
> Signed-off-by: Uttam Singh Bhadauriya <[email protected]>
> ---
> gcc/match.pd | 18 +++++++++++++++++
> gcc/testsuite/gcc.dg/tree-ssa/pr126627.c | 25 ++++++++++++++++++++++++
> 2 files changed, 43 insertions(+)
> create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr126627.c
>
> diff --git a/gcc/match.pd b/gcc/match.pd
> index 0a4f7b718b4..5cc08f36e9b 100644
> --- a/gcc/match.pd
> +++ b/gcc/match.pd
> @@ -9269,6 +9269,24 @@ 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 */
> +(for cmp (lt ge)
> + eqcmp (eq ne)
> + (simplify
> + (cmp (convert?@0 (bit_and:c (negate @1) @1)) integer_zerop)
> + (if (INTEGRAL_TYPE_P (TREE_TYPE (@0))
> + && INTEGRAL_TYPE_P (TREE_TYPE (@1))
> + && TYPE_PRECISION (TREE_TYPE (@1)) > 1
> + && TYPE_PRECISION (TREE_TYPE (@0)) == TYPE_PRECISION (TREE_TYPE (@1))
Instead of convert, use nop_convert and then you can remove the
TYPE_PRECISION==TYPE_PRECISION check since that will be true there.
> + && !TYPE_UNSIGNED (TREE_TYPE (@0))
> + && !TYPE_OVERFLOW_TRAPS (TREE_TYPE (@1)))
> + (with {
> + unsigned int prec = TYPE_PRECISION (TREE_TYPE (@1));
> + wide_int min = wi::set_bit_in_zero (prec - 1, prec);
Instead of set_bit_in_zero use `wi::min_value (prec, SIGNED)`.
> + }
> + (eqcmp @1 { wide_int_to_tree (TREE_TYPE (@1), min); })))))
> +
> /* 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/tree-ssa/pr126627.c
> b/gcc/testsuite/gcc.dg/tree-ssa/pr126627.c
> new file mode 100644
> index 00000000000..07c91bd7862
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr126627.c
> @@ -0,0 +1,25 @@
> +/* PR tree-optimization/126627 */
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fdump-tree-optimized" } */
> +
> +int test1(int a) {
> + return (a & -a) < 0;
> +}
> +
> +int test2(int a) {
> + return (a & -a) >= 0;
> +}
> +
> +int test3(unsigned int a) {
> + return (int)(a & -a) < 0;
> +}
> +
> +int test4(unsigned int a) {
> + return (int)(a & -a) >= 0;
> +}
> +
> +/* { dg-final { scan-tree-dump-not " & " "optimized" } } */
> +/* { dg-final { scan-tree-dump-not " < 0" "optimized" } } */
> +/* { dg-final { scan-tree-dump-not " >= 0" "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " == -2147483648" 2 "optimized"
> { target { ilp32 || lp64 } } } } */
> +/* { dg-final { scan-tree-dump-times " == 2147483648" 2 "optimized" {
> target { ilp32 || lp64 } } } } */
Just use int32 as suggested by Jeff since that will also support LLP64
targets (aka windows).
But you should also add a testcase for short and char.
e.g.:
```
int test_us(unsigned short a) {
return ((short)(a & -a)) >= 0;
}
int test_ss(signed short a) {
return ((short)(a & -a)) >= 0;
}
```
I am less worried about long though.
You will notice with short, your match pattern does not match as there
are few extra converts so that `-a` would not introduce undefined
behavior (for short, it will promote to int) so you need to support
that case too.
Likewise this for int too:
```
int test_si(signed a) {
unsigned b = a;
b = -b;
signed t = b;
return (a & t) >= 0;
}
```
The simplified way of doing this is something like:
(match (negate_maybe_without_ud @0)
(negate @0))
(match (negate_maybe_without_ud @0)
(convert (negate (nop_convert @0)))
(if (types_match (TREE_TYPE (@0, type)))
And then use negate_maybe_without_ud instead of negate.
This should simplify the pattern without adding too many optional converts.
Thanks,
Andrea
> --
> 2.34.1