Re: [PATCH] match.pd: build signed low-bit masks in an unsigned type

Andrea Pinski <[email protected]>
Newsgroups gmane.comp.gcc.patches
Message-ID <CALvbMcDztfcz5A3=+w=XQNBixjVzpH795AZPkm7LXVfyc13MJA@mail.gmail.com>
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)
> >>
>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.