[PATCH] match: drop a mask that the following right shift discards

<[email protected]> Tue, 4 Aug 2026 12:47:28 +0200
Newsgroups gmane.comp.gcc.patches
Message-ID <[email protected]>
From: Kyrylo Tkachov <[email protected]>

(X & (-1 << Y)) >> Y clears the low Y bits and then shifts them out, so
the mask is redundant.  This drops three operations, and it turns up in
code that aligns a value down before scaling it.

  unsigned f (unsigned x, int y) { return (x & (~0u << y)) >> y; }

aarch64 -O2:

  before                          after
    mov   w2, -1                    lsr   w0, w0, w1
    lsl   w2, w2, w1
    and   w0, w2, w0
    lsr   w0, w0, w1

Bootstrapped and tested on aarch64-none-linux-gnu.
Ok for trunk?
Thanks,
Kyrill

gcc/ChangeLog:

	* match.pd ((X & (-1 << Y)) >> Y): New simplification.

gcc/testsuite/ChangeLog:

	* gcc.dg/tree-ssa/shift-mask-1.c: New test.

Signed-off-by: Kyrylo Tkachov <[email protected]>
---
 gcc/match.pd                                 |  5 +++++
 gcc/testsuite/gcc.dg/tree-ssa/shift-mask-1.c | 13 +++++++++++++
 2 files changed, 18 insertions(+)
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/shift-mask-1.c

diff --git a/gcc/match.pd b/gcc/match.pd
index a97158bf062..ac35442f8ba 100644
--- a/gcc/match.pd
+++ b/gcc/match.pd
@@ -5261,6 +5261,11 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
 			- TYPE_PRECISION (TREE_TYPE (@2)))))
   (bit_and (convert @0) (lshift { build_minus_one_cst (type); } @1))))
 
+/* (X & (-1 << Y)) >> Y -> X >> Y.  The mask only clears bits that the
+   shift discards.  */
+(simplify
+ (rshift (bit_and:c @0 (lshift integer_all_onesp @1)) @1)
+ (rshift @0 @1))
 /* (X op Y) >> C -> X >> C when every set bit of Y lies below bit C.
    Neither an inclusive nor an exclusive or can carry into the bits the
    shift keeps, so Y contributes nothing to the result.  */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/shift-mask-1.c b/gcc/testsuite/gcc.dg/tree-ssa/shift-mask-1.c
new file mode 100644
index 00000000000..16d129d1f29
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/shift-mask-1.c
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+/* Masking off the bits that the following right shift discards is a no-op.  */
+
+unsigned int f1 (unsigned int x, int y) { return (x & (~0u << y)) >> y; }
+int f2 (int x, int y) { return (x & (~0 << y)) >> y; }
+unsigned long f3 (unsigned long x, int y) { return (x & (~0ul << y)) >> y; }
+unsigned int f4 (unsigned int x, int y) { return ((~0u << y) & x) >> y; }
+
+/* { dg-final { scan-tree-dump-not " & " "optimized" } } */
+/* { dg-final { scan-tree-dump-not " << " "optimized" } } */
+/* { dg-final { scan-tree-dump-times " >> " 4 "optimized" } } */
-- 
2.50.1 (Apple Git-155)