Fold redundant count masks in inverted bit extraction using define_split.

Across the baseline and Zbs-only builds, C/C++ selftests and RV32/RV64
QEMU checks passed: 48 correctness runs and 8 benchmark binaries.
Performance was measured in 16 gem5 Minor/O3 runs.

For the RV64 microkernel, gem5 instruction counts decreased 11.11%;
cycles decreased 9.99% on Minor and 14.27% on O3. K1 reductions were
11.12% in instructions and 14.31% in cycles, from 15 paired measurements.

Changes in v2:

Use define_split, as suggested by Jeff.

gcc/ChangeLog:

        * config/riscv/bitmanip.md: Add a splitter for masked inverted
        bit extraction.

gcc/testsuite/ChangeLog:

        * gcc.target/riscv/zbs-bext-3.c: New test.

---
diff --git a/gcc/config/riscv/bitmanip.md b/gcc/config/riscv/bitmanip.md
index 
d710d594def0fcc41a6109c9eadef7704d24486e..4d390a7fab0638579d111e029e3651f04d2c95c7
 100644
--- a/gcc/config/riscv/bitmanip.md
+++ b/gcc/config/riscv/bitmanip.md
@@ -1101,6 +1101,32 @@
   "operands[1] = gen_lowpart (word_mode, operands[1]);"
   [(set_attr "type" "bitmanip")])
 
+;; Split inverted bit extraction with a masked count into bext and seqz.
+(define_split
+  [(set (match_operand:X 0 "register_operand")
+       (and:X
+         (not:X
+           (lshiftrt:X
+             (match_operand:X 1 "register_operand")
+             (match_operator 4 "subreg_lowpart_operator"
+               [(and:GPR (match_operand:GPR 2 "register_operand")
+                         (match_operand 3 "<X:shiftm1>"))])))
+         (const_int 1)))]
+  "TARGET_ZBS"
+  [(set (match_dup 0)
+       (zero_extract:X (match_dup 1)
+                       (const_int 1)
+                       (match_dup 4)))
+   (set (match_dup 0) (eq:X (match_dup 0) (const_int 0)))]
+{
+  /* Keep the bit position in range in the split RTL.  The masked bext
+     patterns omit this mask when emitting the instruction.  */
+  operands[4] = gen_rtx_AND (<GPR:MODE>mode, operands[2],
+                           GEN_INT (GET_MODE_BITSIZE (<X:MODE>mode) - 1));
+  if (<GPR:MODE>mode != <X:MODE>mode)
+    operands[4] = gen_rtx_ZERO_EXTEND (<X:MODE>mode, operands[4]);
+})
+
 ;; The logical-and against 0x1 implicitly extends the result.   So we can treat
 ;; an SImode bext as-if it's DImode without any explicit extension.
 (define_insn "*bextdisi"
diff --git a/gcc/testsuite/gcc.target/riscv/zbs-bext-3.c 
b/gcc/testsuite/gcc.target/riscv/zbs-bext-3.c
new file mode 100644
index 
0000000000000000000000000000000000000000..cf4287c11b3a2a09df0f23fbc7a1b5d73eca365b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/zbs-bext-3.c
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64gc_zbs -mabi=lp64" { target { rv64 } } } */
+/* { dg-options "-march=rv32gc_zbs -mabi=ilp32" { target { rv32 } } } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-Og" } } */
+
+unsigned long
+foo (unsigned long x, unsigned int n)
+{
+  return (~(x >> (n & (__riscv_xlen - 1)))) & 1UL;
+}
+
+/* A narrower mask must be retained.  */
+unsigned long
+bar (unsigned long x, unsigned int n)
+{
+  return (~(x >> (n & 7))) & 1UL;
+}
+
+/* { dg-final { scan-assembler-times "bext\t" 2 } } */
+/* { dg-final { scan-assembler-times "seqz\t|xori\t" 2 } } */
+/* { dg-final { scan-assembler-times "andi\t" 1 } } */

Reply via email to