On Wed, Aug 19, 2026 at 3:25 AM Kyrylo Tkachov <[email protected]> wrote:
>
>
>
> > On 19 Aug 2026, at 07:54, Andrea Pinski <[email protected]> 
> > wrote:
> >
> > On Mon, Aug 17, 2026 at 5:59 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
> >>
> >> aarch64 -O2 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 the canonical addition of minus one and a direct subtraction so the 
> >> rule
> >> also handles vector expressions.
> >>
> >> The signed form is not valid when the source 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.
> >>
> >> Bootstrapped and tested on aarch64-none-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-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                                  | 23 ++++++++++++++----
> >> .../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 | 24 +++++++++++++++++++
> >> 6 files changed, 97 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
> >>
> >> diff --git a/gcc/match.pd b/gcc/match.pd
> >> index 02684d8a302..4ead7bc316c 100644
> >> --- a/gcc/match.pd
> >> +++ b/gcc/match.pd
> >> @@ -1558,11 +1558,24 @@ 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 sub (plus minus)
> >> + (simplify
> >> +  (bit_and:c @0
> >> +   (sub:s (lshift:s integer_onep @1) uniform_integer_cst_p@2))
> >> +  (with { tree cst = uniform_integer_cst_p (@2);
> >> +         tree etype = VECTOR_TYPE_P (type) ? TREE_TYPE (type) : type; }
> >> +   (if ((sub == PLUS_EXPR ? integer_minus_onep (cst) : integer_onep (cst))
> >> +       && INTEGRAL_TYPE_P (etype)
> >> +       && (TYPE_UNSIGNED (etype)
> >> +           || (!TYPE_OVERFLOW_TRAPS (etype)
> >> +               && !TYPE_OVERFLOW_SANITIZED (etype)
> >> +               && (GIMPLE || !sanitize_flags_p (SANITIZE_SHIFT_BASE)))))
> >> +    (with { tree utype = unsigned_type_for (type); }
> >> +     (bit_and @0 (convert
> >> +                  (bit_not (lshift { build_all_ones_cst (utype); } 
> >> @1)))))))))
> >
> >
> > It seems like we should canonicalize `a - {1,1,1,1}` into `a +
> > {-1,-1,-1,-1}` and not need the above mess of checking for `a + -1`/`a
> > - 1`.
> > Can you add that? That is `a - VECTOR_CST`  transform into `a +
> > (-VECTOR_CST)`. Test on both x86_64 and aarch64 to see if there any
> > fall out as there might be.
>
> Something like the attached? It passes clean on aarch64 and x86_64.

Remove the check for:
if (!TYPE_UNSIGNED (etype))
Since that is not needed after this check: `TYPE_OVERFLOW_WRAPS
(type)` which already does the unsigned check.

Can you submit the negate_expr_p separate from the `x & ((1U << b) -
1)` pattern modification though?

Also:
-(simplify
-  (bit_and:c @0 (plus:s (lshift:s integer_onep @1) integer_minus_onep))
+(simplify
+ (bit_and:c @0
+  (plus:s (lshift:s integer_onep @1) integer_minus_onep))

Please don't reformat unless you really need to.

> Thanks,
> Kyrill
>
> >
> >>
> >> (for bitop (bit_and bit_ior)
> >>      cmp (eq ne)
> >> 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..51ba0b35334
> >> --- /dev/null
> >> +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr71636-signed-vector-1.c
> >> @@ -0,0 +1,24 @@
> >> +/* { 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);
> >> +}
> >> +
> >> +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 "= ~" 2 "optimized" } } */
> >> --
> >> 2.50.1 (Apple Git-155)
> >>
>

Reply via email to