https://gcc.gnu.org/g:885ad73e630ca23d1810d55fd480f6ff519d42bf

commit r17-4511-g885ad73e630ca23d1810d55fd480f6ff519d42bf
Author: Artemiy Volkov <[email protected]>
Date:   Tue Aug 11 15:04:58 2026 +0000

    match.pd: fold extract-bitfield-and-shift expressions
    
    This patch adds a simplification rule for expressions of the form
    ((X >> C1) & C2) << C3, which extract some bits from X at position C1,
    perform an "and" with a mask which is normally just 2^N - 1, then shift
    the result left by C3.  The transformation is limited to cases where X
    is unsigned, has its precision equal to its width and where C1 and C3
    are smaller than the precision of X (the last condition could probably
    be just assumed but I wasn't sure so decided to play it safe).
    
    When all of the above conditions hold, the expression is folded into
    either: (a) (X >> (C1 - C3)) & (C2 << C3) when C1 >= C3, or (b)
    (X << (C3 - C1)) & (C2 << C3) when C1 < C3.  Additional care is required
    to preserve the leading zeros formed by the X >> C1 operation in the
    original expression; to handle this, we clear the leading bits of the
    mask operand as a preliminary step.
    
    The corner case where C1 is one less the precision of X is handled
    elsewhere (and is folded to just (X >> C1) << C3 as long as the LSB of
    C2 isn't 0.)
    
    On aarch64, this results in:
    
            lsr     x0, x0, 16
            and     w0, w0, 130816
    
    being emitted instead of:
    
            lsr     x1, x0, 32
            lsr     x0, x0, 24
            ubfiz   w1, w1, 16, 1
            ubfiz   w0, w0, 8, 8
            orr     w0, w1, w0
    
    for the expression "((x >> 32) & 1) << 16) | (x >> 24) & 0xff) << 8)".
    
    A couple of new testcases added, with some focus on the case where X is
    shifted too far to the right as described above.
    
    Survives bootstrap and regtest on aarch64-linux-gnu and x86_64-linux-gnu.
    
    gcc/ChangeLog:
    
            * match.pd: New rule to fold ((X >> C1) & C2) << C3.
    
    gcc/testsuite/ChangeLog:
    
            * gcc.dg/tree-ssa/match-bit-extract-shift.c: New test.

Diff:
---
 gcc/match.pd                                       | 22 ++++++++
 .../gcc.dg/tree-ssa/match-bit-extract-shift.c      | 61 ++++++++++++++++++++++
 2 files changed, 83 insertions(+)

diff --git a/gcc/match.pd b/gcc/match.pd
index 88533dcf77a8..268bf3f11e52 100644
--- a/gcc/match.pd
+++ b/gcc/match.pd
@@ -1527,6 +1527,28 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
     { tree utype = unsigned_type_for (type); }
     (convert (rshift (lshift (convert:utype @0) @2) @3))))))
 
+/* Fold (((X >> C1) & C2) << C3) into (X >>/<< |C1 - C3|) & (C2' << C3),
+   where C2' is obtained from C2 by clearing all but the rightmost C1 bits.  */
+(simplify
+ (lshift (bit_and (rshift @0 INTEGER_CST@1) INTEGER_CST@2) INTEGER_CST@3)
+  (if (TYPE_UNSIGNED (TREE_TYPE (@0))
+       && type_has_mode_precision_p (TREE_TYPE (@0)))
+   (with { int prec = TYPE_PRECISION (TREE_TYPE (@0)); }
+    (if (wi::ltu_p (wi::to_wide (@1), prec)
+        && wi::ltu_p (wi::to_wide (@3), prec))
+     (with
+      {
+       /* Clear the first PREC - @1 bits of the mask, then shift it.  */
+       wide_int mask_lz = wi::mask (prec - tree_to_uhwi (@1), false, prec);
+       tree mask = fold_build2 (BIT_AND_EXPR, TREE_TYPE (@0),
+                       @2, wide_int_to_tree (TREE_TYPE (@0), mask_lz));
+       tree mask_shifted = fold_build2 (LSHIFT_EXPR, TREE_TYPE (@0),
+                                        mask, @3);
+      }
+      (if (wi::leu_p (wi::to_wide (@1), wi::to_wide (@3)))
+       (bit_and (lshift @0 (minus @3 @1)) { mask_shifted; })
+       (bit_and (rshift @0 (minus @1 @3)) { mask_shifted; })))))))
+
 /* Fold ((type)(a<0)) << SIGNBITOFA into ((type)a) & signbit. */
 (simplify
  (lshift (convert (lt @0 integer_zerop@1)) INTEGER_CST@2)
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/match-bit-extract-shift.c 
b/gcc/testsuite/gcc.dg/tree-ssa/match-bit-extract-shift.c
new file mode 100644
index 000000000000..292ce5a41c90
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/match-bit-extract-shift.c
@@ -0,0 +1,61 @@
+/* { dg-require-effective-target stdint_types } */
+/* { dg-do compile } */
+/* { dg-options "-O1 -fdump-tree-forwprop1" } */
+
+#include <stdint.h>
+
+uint32_t f (uint64_t x)
+{
+  return (((x >> 32) & 0x1) << 16)
+         | (((x >> 24) & 0xff) << 8);
+}
+
+uint32_t f2 (uint64_t x)
+{
+  return (((x >> 3) & 0x3) << 16);
+}
+
+uint32_t f3 (uint64_t x)
+{
+  return (((x >> 61) & 0xE) << 16);
+}
+
+uint32_t f4 (uint64_t x)
+{
+  return (((x >> 62) & 0xE) << 16);
+}
+
+uint32_t f5 (uint64_t x)
+{
+  return (((x >> 63) & 0xF) << 17);
+}
+
+uint64_t f6 (uint64_t x)
+{
+  return (((x >> 32) & 0xFFFFFF) << 47);
+}
+
+/* { dg-final { scan-tree-dump-not "<< 16" forwprop1 } } */
+/* { dg-final { scan-tree-dump-not ">> 32" forwprop1 } } */
+/* { dg-final { scan-tree-dump-not ">> 24" forwprop1 } } */
+/* { dg-final { scan-tree-dump-not ">> 3" forwprop1 } } */
+/* { dg-final { scan-tree-dump-not ">> 61" forwprop1 } } */
+/* { dg-final { scan-tree-dump-not ">> 62" forwprop1 } } */
+/* { dg-final { scan-tree-dump-not "<< 47" forwprop1 } } */
+/* { dg-final { scan-tree-dump-not "& 65536" forwprop1 } } */
+/* { dg-final { scan-tree-dump-not "& 65535" forwprop1 } } */
+/* { dg-final { scan-tree-dump-not "& 917504" forwprop1 } } */
+
+/* { dg-final { scan-tree-dump-times ">> 16" 1 forwprop1 } } */
+/* { dg-final { scan-tree-dump-times ">> 45" 1 forwprop1 } } */
+/* { dg-final { scan-tree-dump-times ">> 46" 1 forwprop1 } } */
+/* { dg-final { scan-tree-dump-times "<< 13" 1 forwprop1 } } */
+/* { dg-final { scan-tree-dump-times ">> 63" 1 forwprop1 } } */
+/* { dg-final { scan-tree-dump-times "<< 17" 1 forwprop1 } } */
+/* { dg-final { scan-tree-dump-times "<< 15" 1 forwprop1 } } */
+/* { dg-final { scan-tree-dump-times "& 130816" 1 forwprop1 } } */
+/* { dg-final { scan-tree-dump-times "& 196608" 1 forwprop1 } } */
+/* { dg-final { scan-tree-dump-times "& 393216" 1 forwprop1 } } */
+/* { dg-final { scan-tree-dump-times "& 131072" 1 forwprop1 } } */
+/* { dg-final { scan-tree-dump-times "& 18446603336221196288" 1 forwprop1 } } 
*/
+

Reply via email to