Hi all,

sending a v2 of [0], adding support for converts of subexpressions in the
extract-bitfield-and-shift folding pattern.  This is a complete rework so I
briefly considered making this a brand new patch instead.

Changes since v1:
- Expand commit message to better explain the transformation.
- Perform all operations in unsigned version of `type`, then convert back
  to signed if needed.
- Remove the `type_has_mode_precision_p` check.
- Replace the `TYPE_UNSIGNED_P` requirement for `type` and the type of @0
  with one on the type of @4.
- Add more tests involving signed types.

OK for trunk?

Thanks,
Artemiy

[0] https://gcc.gnu.org/pipermail/gcc-patches/2026-September/732547.html

---------- 8< ----------

This patch extends the extract-bitfield-and-shift pattern by allowing
(a) X to be a result of an integer conversion as long as the target type
is unsigned, (b) the result of the left-shift to be (sometimes as a result
of integer promotions) converted to another integer type, and (c) the
resulting type of the whole expression to be signed.

Firstly, we prepare the new mask C2' from the original mask C2 similar to
before, by clearing the first N bits, where N is the difference between
the precision of the converted-to type and C1.  For instance, for x of
type uint32_t in the expression

(uint16_t) ((((uint8_t) x >> 5) & 0xFFFF) << 10),

C2' = 0x7 is obtained from C2 = 0xFFFF by trimming down to
precision (uint8_t) - 5 = 3 bits.

This allows us to zero-extend the result of (X >> C1) to the unsigned
version of the resulting type of the expression and perform the rest of
the operations (AND and SHIFT) in that type.  In case the original result
type was signed (case (c) above), we convert back to that as the last
step.

Some new tests involving combinations of casts have been added as
match-bit-extract-shift-2.c.

Bootstrapped and regtested on aarch64-linux-gnu and x86_64-linux-gnu.

gcc/ChangeLog:

        * match.pd: Support conversions in ((X >> C1) & C2) << C3.

gcc/testsuite/ChangeLog:

        * gcc.dg/tree-ssa/match-bit-extract-shift-2.c: New test.
---
 gcc/match.pd                                  | 27 ++++---
 .../tree-ssa/match-bit-extract-shift-2.c      | 75 +++++++++++++++++++
 2 files changed, 91 insertions(+), 11 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/match-bit-extract-shift-2.c

diff --git a/gcc/match.pd b/gcc/match.pd
index 688357f0ecd..2cb72e04875 100644
--- a/gcc/match.pd
+++ b/gcc/match.pd
@@ -1530,24 +1530,29 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
 /* 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)); }
+ (lshift (bit_and (convert1? (rshift (convert2?@4 @0) INTEGER_CST@1))
+                  INTEGER_CST@2) INTEGER_CST@3)
+  (if (TYPE_UNSIGNED (TREE_TYPE (@4)))
+   (with
+    {
+      int prec = TYPE_PRECISION (TREE_TYPE (@4));
+      tree utype = unsigned_type_for (type);
+    }
     (if (wi::ltu_p (wi::to_wide (@1), prec)
-        && wi::ltu_p (wi::to_wide (@3), prec))
+        && wi::ltu_p (wi::to_wide (@3), TYPE_PRECISION (utype)))
      (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);
+       tree mask = fold_build2 (BIT_AND_EXPR, utype, @2,
+               wide_int_to_tree (TREE_TYPE (@2), mask_lz));
+       tree mask_shifted = fold_build2 (LSHIFT_EXPR, utype, mask, @3);
       }
       (if (wi::to_widest (@1) <= wi::to_widest (@3))
-       (bit_and (lshift @0 (minus @3 @1)) { mask_shifted; })
-       (bit_and (rshift @0 (minus @1 @3)) { mask_shifted; })))))))
+       (convert (bit_and (lshift (convert:utype @0) (minus @3 @1))
+                        { mask_shifted; }))
+       (convert (bit_and (rshift (convert:utype @0) (minus @1 @3))
+                        { mask_shifted; }))))))))
 
 /* Fold ((type)(a<0)) << SIGNBITOFA into ((type)a) & signbit. */
 (simplify
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/match-bit-extract-shift-2.c 
b/gcc/testsuite/gcc.dg/tree-ssa/match-bit-extract-shift-2.c
new file mode 100644
index 00000000000..c61856d916d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/match-bit-extract-shift-2.c
@@ -0,0 +1,75 @@
+/* { 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 >> 5UL) & 0x5) << 10ULL;
+}
+
+uint32_t f2 (uint64_t x)
+{
+  return (((uint16_t)x >> 5ULL) & 0x5) << 10UL;
+}
+
+uint32_t f3 (uint64_t x)
+{
+  return (uint16_t) ((((uint8_t) x >> 5ULL) & 0x5) << 10U);
+}
+
+uint32_t f4 (uint64_t x)
+{
+  return (uint8_t) ((((uint16_t)x >> 5U) & 0x5) << 10ULL);
+}
+
+uint32_t f5 (uint64_t x)
+{
+  return (uint16_t) ((((uint8_t) x >> 10) & 0x5) << 5ULL);
+}
+
+uint32_t f6 (uint64_t x)
+{
+  return (uint16_t) ((((uint8_t) x >> 5ULL) & -0x3) << 10U);
+}
+
+int32_t fs (uint64_t x)
+{
+  return (int32_t) (((x >> 5UL) & -0x5) << 1ULL);
+}
+
+int32_t f2s (uint64_t x)
+{
+  return (int32_t) ((((uint16_t)x >> 5ULL) & -0x5) << 1UL);
+}
+
+int32_t f3s (uint64_t x)
+{
+  return (int16_t) ((((uint8_t) x >> 5ULL) & -0x5) << 1U);
+}
+
+int32_t f4s (uint64_t x)
+{
+  return (int8_t) ((((uint16_t)x >> 5U) & -0x5) << 1ULL);
+}
+
+int32_t f5s (uint64_t x)
+{
+  return (int16_t) ((((uint8_t) x >> 5) & -0x5) << 1ULL);
+}
+
+int32_t f6s (uint64_t x)
+{
+  return (int16_t) ((((uint8_t) x >> 5ULL) & 0x3) << 1U);
+}
+
+/* { dg-final { scan-tree-dump-times "<< 5" 4 forwprop1 } } */
+/* { dg-final { scan-tree-dump-times "& 5120" 4 forwprop1 } } */
+/* { dg-final { scan-tree-dump-times "return 0" 2 forwprop1 } } */
+
+/* { dg-final { scan-tree-dump-times ">> 4" 6 forwprop1 } } */
+/* { dg-final { scan-tree-dump-times "& 4294967286" 1 forwprop1 } } */
+/* { dg-final { scan-tree-dump-times "& 4086" 1 forwprop1 } } */
+/* { dg-final { scan-tree-dump-times "& 6" 3 forwprop1 } } */
+/* { dg-final { scan-tree-dump-times "& 246" 1 forwprop1 } } */
-- 
2.43.0

Reply via email to