On Mon, Aug 24, 2026 at 9:01 AM <[email protected]> wrote:
>
> From: Kyrylo Tkachov <[email protected]>
>
> The PR71636 fold turns
>
>   x & ((1U << b) - 1)
>
> into
>
>   x & ~(~0U << b)
>
> but only when the mask type is unsigned.  Signed source and vector forms keep
> the longer expression.
>
>   int f (int x, int b)
>   {
>     return x & ((1 << b) - 1);
>   }
>
> aarch64 -O2 before:
>
>   f:
>           mov     w2, 1
>           lsl     w2, w2, w1
>           sub     w2, w2, #1
>           and     w0, w2, w0
>           ret
>
> After:
>
>   f:
>           mov     w2, -1
>           lsl     w2, w2, w1
>           bic     w0, w0, w2
>           ret
>
> Build a signed mask in the corresponding unsigned type and convert it back.
> This makes the all-ones shift defined and exposes the shorter form.  Accept
> both subtraction of one and addition of minus one.  Signed nonwrapping vectors
> can retain the subtraction form, while scalar and wrapping forms can use the
> addition form.

I don't think the unsigned dance is necessary.  left-shift of a negative value
isn't UB in GIMPLE, left-shifts are always "logical" as GENERIC documents.
UBSAN of shifts should happen in the frontends(?), so that should not be
of concern?

Thanks,
Richard.

>
> The signed form is not valid when the source addition or subtraction can trap
> or is instrumented for overflow.  It can also remove the signed shift-base
> check for the top-bit count.  Keep these cases.  After GIMPLE lowering, an
> explicit shift sanitizer check remains visible, so the fold is safe again.
>
> Vector types carry the lane signedness used by the unsigned and trapping
> checks.  The first patch makes TYPE_OVERFLOW_SANITIZED accept integral vector
> types.  Query the matched type directly.  Test both vector source spellings
> and keep both operations under the sanitizer.
>
> Bootstrapped and tested on aarch64-none-linux-gnu.
> Tested on x86_64-pc-linux-gnu.
>
> Ok for trunk?
> Thanks,
> Kyrill
>
> gcc/ChangeLog:
>
>         * match.pd (x & ((1 << b) - 1)): Handle signed scalar and vector
>         types.
>
> gcc/testsuite/ChangeLog:
>
>         * gcc.dg/tree-ssa/pr71636-signed-1.c: New test.
>         * gcc.dg/tree-ssa/pr71636-signed-vector-1.c: Likewise.
>         * gcc.dg/tree-ssa/pr71636-signed-vector-ubsan-1.c: Likewise.
>         * gcc.dg/tree-ssa/pr71636-signed-trap-1.c: Likewise.
>         * gcc.dg/tree-ssa/pr71636-signed-ubsan-1.c: Likewise.
>         * gcc.dg/tree-ssa/pr71636-signed-shift-ubsan-1.c: Likewise.
>
> Signed-off-by: Kyrylo Tkachov <[email protected]>
> ---
>  gcc/match.pd                                  | 22 ++++++++++---
>  .../gcc.dg/tree-ssa/pr71636-signed-1.c        | 24 ++++++++++++++
>  .../tree-ssa/pr71636-signed-shift-ubsan-1.c   | 11 +++++++
>  .../gcc.dg/tree-ssa/pr71636-signed-trap-1.c   | 10 ++++++
>  .../gcc.dg/tree-ssa/pr71636-signed-ubsan-1.c  | 10 ++++++
>  .../gcc.dg/tree-ssa/pr71636-signed-vector-1.c | 32 +++++++++++++++++++
>  .../tree-ssa/pr71636-signed-vector-ubsan-1.c  | 22 +++++++++++++
>  7 files changed, 126 insertions(+), 5 deletions(-)
>  create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr71636-signed-1.c
>  create mode 100644 
> gcc/testsuite/gcc.dg/tree-ssa/pr71636-signed-shift-ubsan-1.c
>  create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr71636-signed-trap-1.c
>  create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr71636-signed-ubsan-1.c
>  create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr71636-signed-vector-1.c
>  create mode 100644 
> gcc/testsuite/gcc.dg/tree-ssa/pr71636-signed-vector-ubsan-1.c
>
> diff --git a/gcc/match.pd b/gcc/match.pd
> index a969aa20417..cbdc3fcf9c3 100644
> --- a/gcc/match.pd
> +++ b/gcc/match.pd
> @@ -1575,11 +1575,23 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
>      (convert @0)
>      (convert @1)))))
>
> -/* PR71636: Transform x & ((1U << b) - 1) -> x & ~(~0U << b);  */
> -(simplify
> -  (bit_and:c @0 (plus:s (lshift:s integer_onep @1) integer_minus_onep))
> -  (if (TYPE_UNSIGNED (type))
> -    (bit_and @0 (bit_not (lshift { build_all_ones_cst (type); } @1)))))
> +/* PR71636: Transform x & ((1U << b) - 1) -> x & ~(~0U << b).  For signed
> +   types, build the mask in the corresponding unsigned type, where shifting
> +   all ones left is defined.  Preserve signed overflow and shift checks.  */
> +(for op (plus minus)
> + (simplify
> +  (bit_and:c @0
> +   (op:s (lshift:s integer_onep @1) uniform_integer_cst_p@2))
> +  (with { tree cst = uniform_integer_cst_p (@2); }
> +   (if ((op == PLUS_EXPR ? integer_minus_onep (cst) : integer_onep (cst))
> +       && (TYPE_UNSIGNED (type)
> +           || (!TYPE_OVERFLOW_TRAPS (type)
> +               && !TYPE_OVERFLOW_SANITIZED (type)
> +               && (GIMPLE || !sanitize_flags_p (SANITIZE_SHIFT_BASE)))))
> +    (with { tree utype = unsigned_type_for (type); }
> +     (bit_and @0
> +      (convert:type
> +       (bit_not (lshift { build_all_ones_cst (utype); } @1)))))))))
>
>  /* PR112533: Canonicalize boolean comparisons of masked pow2 bits into
>     xor-mask tests.
> diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr71636-signed-1.c 
> b/gcc/testsuite/gcc.dg/tree-ssa/pr71636-signed-1.c
> new file mode 100644
> index 00000000000..9db533fdf64
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr71636-signed-1.c
> @@ -0,0 +1,24 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fdump-tree-optimized" } */
> +
> +int
> +f_signed (int x, int b)
> +{
> +  return x & ((1 << b) - 1);
> +}
> +
> +unsigned int
> +f_unsigned (unsigned int x, int b)
> +{
> +  return x & ((1U << b) - 1U);
> +}
> +
> +long
> +f_long (long x, int b)
> +{
> +  return x & ((1L << b) - 1L);
> +}
> +
> +/* { dg-final { scan-tree-dump-not "1 <<" "optimized" } } */
> +/* { dg-final { scan-tree-dump-not " \\+ -1;" "optimized" } } */
> +/* { dg-final { scan-tree-dump-times "= ~" 3 "optimized" } } */
> diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr71636-signed-shift-ubsan-1.c 
> b/gcc/testsuite/gcc.dg/tree-ssa/pr71636-signed-shift-ubsan-1.c
> new file mode 100644
> index 00000000000..3abee74264c
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr71636-signed-shift-ubsan-1.c
> @@ -0,0 +1,11 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fsanitize=shift-base -fdump-tree-optimized" } */
> +
> +int
> +f (int x, int b)
> +{
> +  return x & ((1 << b) - 1);
> +}
> +
> +/* { dg-final { scan-tree-dump-times 
> "__builtin___ubsan_handle_shift_out_of_bounds" 1 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times "= ~" 1 "optimized" } } */
> diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr71636-signed-trap-1.c 
> b/gcc/testsuite/gcc.dg/tree-ssa/pr71636-signed-trap-1.c
> new file mode 100644
> index 00000000000..13a1a5b4cf2
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr71636-signed-trap-1.c
> @@ -0,0 +1,10 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -ftrapv -fdump-tree-optimized" } */
> +
> +int
> +f (int x, int b)
> +{
> +  return x & ((1 << b) - 1);
> +}
> +
> +/* { dg-final { scan-tree-dump-times " \\+ -1;" 1 "optimized" } } */
> diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr71636-signed-ubsan-1.c 
> b/gcc/testsuite/gcc.dg/tree-ssa/pr71636-signed-ubsan-1.c
> new file mode 100644
> index 00000000000..68c1b92f18b
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr71636-signed-ubsan-1.c
> @@ -0,0 +1,10 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fsanitize=signed-integer-overflow 
> -fdump-tree-optimized" } */
> +
> +int
> +f (int x, int b)
> +{
> +  return x & ((1 << b) - 1);
> +}
> +
> +/* { dg-final { scan-tree-dump-times "\\.UBSAN_CHECK_SUB" 1 "optimized" } } 
> */
> diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr71636-signed-vector-1.c 
> b/gcc/testsuite/gcc.dg/tree-ssa/pr71636-signed-vector-1.c
> new file mode 100644
> index 00000000000..27ea7b5a54b
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr71636-signed-vector-1.c
> @@ -0,0 +1,32 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fdump-tree-optimized" } */
> +/* { dg-require-effective-target vect_int } */
> +/* { dg-require-effective-target vect_var_shift } */
> +
> +typedef int v4si __attribute__ ((vector_size (16)));
> +typedef unsigned int v4ui __attribute__ ((vector_size (16)));
> +
> +v4si
> +f_signed (v4si x, v4si b)
> +{
> +  v4si one = { 1, 1, 1, 1 };
> +  return x & ((one << b) - one);
> +}
> +
> +v4si
> +f_signed_plus (v4si x, v4si b)
> +{
> +  v4si one = { 1, 1, 1, 1 };
> +  v4si minus_one = { -1, -1, -1, -1 };
> +  return x & ((one << b) + minus_one);
> +}
> +
> +v4ui
> +f_unsigned (v4ui x, v4ui b)
> +{
> +  v4ui one = { 1, 1, 1, 1 };
> +  return x & ((one << b) - one);
> +}
> +
> +/* { dg-final { scan-tree-dump-not "\\{ 1, 1, 1, 1 \\} <<" "optimized" } } */
> +/* { dg-final { scan-tree-dump-times "= ~" 3 "optimized" } } */
> diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr71636-signed-vector-ubsan-1.c 
> b/gcc/testsuite/gcc.dg/tree-ssa/pr71636-signed-vector-ubsan-1.c
> new file mode 100644
> index 00000000000..58bc022bde0
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr71636-signed-vector-ubsan-1.c
> @@ -0,0 +1,22 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -Wno-psabi -fsanitize=signed-integer-overflow 
> -fno-sanitize=shift-base -fdump-tree-ubsan" } */
> +
> +typedef int v4si __attribute__ ((vector_size (4 * sizeof (int))));
> +
> +v4si
> +f_add (v4si x, v4si b)
> +{
> +  v4si one = { 1, 1, 1, 1 };
> +  v4si minus_one = { -1, -1, -1, -1 };
> +  return x & ((one << b) + minus_one);
> +}
> +
> +v4si
> +f_sub (v4si x, v4si b)
> +{
> +  v4si one = { 1, 1, 1, 1 };
> +  return x & ((one << b) - one);
> +}
> +
> +/* { dg-final { scan-tree-dump-times "\\.UBSAN_CHECK_ADD" 1 "ubsan" } } */
> +/* { dg-final { scan-tree-dump-times "\\.UBSAN_CHECK_SUB" 1 "ubsan" } } */
> --
> 2.50.1 (Apple Git-155)
>

Reply via email to