The branch-specific Zbs pattern only recognizes bit indices
zero-extended from QImode.  Bit tests with an XLEN-wide index instead
mask the index with XLEN - 1, so combine emits a bset/and pair before
the branch.

BEXT applies the same XLEN - 1 mask to its index.  Match the masked
form only when the mask is exactly XLEN - 1, then split it into bext
and the branch.  With register allocation normalized, the key sequence
changes from:

  bset  t0,zero,a1
  and   a4,t0,t1
  bne   a4,zero,.L

to:

  bext  a4,t1,a1
  bne   a4,zero,.L

This removes one instruction from each affected branch.  On SPEC CPU
2017 Integer 541.leela_r, the measured dynamic instruction count drops
by about 0.5%.

gcc/ChangeLog:

        * config/riscv/bitmanip.md (*branch<X:mode>_bext_mask_pos):
        New pattern.

gcc/testsuite/ChangeLog:

        * g++.target/riscv/zbs-branch-bext.C: New test.
        * gcc.target/riscv/zbs-branch-bext.c: New test.

Signed-off-by: Jin Ma <[email protected]>
---
 gcc/config/riscv/bitmanip.md                  | 30 ++++++++++++
 .../g++.target/riscv/zbs-branch-bext.C        | 48 +++++++++++++++++++
 .../gcc.target/riscv/zbs-branch-bext.c        | 36 ++++++++++++++
 3 files changed, 114 insertions(+)
 create mode 100644 gcc/testsuite/g++.target/riscv/zbs-branch-bext.C
 create mode 100644 gcc/testsuite/gcc.target/riscv/zbs-branch-bext.c

diff --git a/gcc/config/riscv/bitmanip.md b/gcc/config/riscv/bitmanip.md
index be893f12d91..4048f3cc44f 100644
--- a/gcc/config/riscv/bitmanip.md
+++ b/gcc/config/riscv/bitmanip.md
@@ -1295,6 +1295,36 @@ (define_insn_and_split "*branch<X:mode>_bext"
    ""
   [(set_attr "type" "bitmanip")])
 
+;; BEXT masks the bit position with XLEN - 1.  Fold an explicit mask only
+;; when it has exactly the same semantics.
+(define_insn_and_split "*branch<X:mode>_bext_mask_pos"
+  [(set (pc)
+       (if_then_else
+         (match_operator 1 "equality_operator"
+          [(zero_extract:X (match_operand:X 2 "register_operand" "r")
+                           (const_int 1)
+                           (and:X
+                             (match_operand:X 3 "register_operand" "r")
+                             (match_operand 4 "const_int_operand")))
+           (const_int 0)])
+         (label_ref (match_operand 0 "" ""))
+         (pc)))
+   (clobber (match_scratch:X 5 "=&r"))]
+  "(TARGET_ZBS
+    && INTVAL (operands[4]) + 1 == GET_MODE_BITSIZE (<MODE>mode))"
+  "#"
+  "&& reload_completed"
+  [(set (match_dup 5) (zero_extract:X (match_dup 2)
+                                     (const_int 1)
+                                     (and:X (match_dup 3)
+                                            (match_dup 4))))
+   (set (pc) (if_then_else (match_op_dup 1
+                                       [(match_dup 5) (const_int 0)])
+                          (label_ref (match_dup 0))
+                          (pc)))]
+  ""
+  [(set_attr "type" "bitmanip")])
+
 ;; ZBKC or ZBC extension
 (define_insn "riscv_clmul_<mode>"
   [(set (match_operand:GPR 0 "register_operand" "=r")
diff --git a/gcc/testsuite/g++.target/riscv/zbs-branch-bext.C 
b/gcc/testsuite/g++.target/riscv/zbs-branch-bext.C
new file mode 100644
index 00000000000..2876faf070f
--- /dev/null
+++ b/gcc/testsuite/g++.target/riscv/zbs-branch-bext.C
@@ -0,0 +1,48 @@
+/* { dg-do compile { target { rv64 } } } */
+/* { dg-skip-if "requires hosted libstdc++ for bitset" { ! hostedlib } } */
+/* { dg-options "-march=rv64gc_zbs -mabi=lp64d -O2 -std=c++03" } */
+
+/* Model the std::bitset predicate used by 541.leela_r's
+   Playout::passthrough and its inlined branch in UCTNode::updateRAVE.  */
+
+#include <bitset>
+
+class FastBoard
+{
+public:
+  static const int MAXBOARDSIZE = 19;
+  static const int MAXSQ = ((MAXBOARDSIZE + 2) * (MAXBOARDSIZE + 2));
+  static const int PASS = -1;
+  static const int BLACK = 0;
+};
+
+class Playout
+{
+public:
+  typedef std::bitset<FastBoard::MAXSQ> bitboard_t;
+
+  bool
+  passthrough (int color, int vertex)
+  {
+    if (vertex == FastBoard::PASS)
+      return false;
+
+    return m_sq[color][vertex];
+  }
+
+private:
+  bitboard_t m_sq[2];
+};
+
+extern void sink ();
+
+void
+update_rave (Playout &playout, int move)
+{
+  bool bpass = playout.passthrough (FastBoard::BLACK, move);
+
+  if (bpass)
+    sink ();
+}
+
+/* { dg-final { scan-assembler-times {\mbext\t} 1 } } */
diff --git a/gcc/testsuite/gcc.target/riscv/zbs-branch-bext.c 
b/gcc/testsuite/gcc.target/riscv/zbs-branch-bext.c
new file mode 100644
index 00000000000..0c0cd982d0a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/zbs-branch-bext.c
@@ -0,0 +1,36 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64gc_zbs -mabi=lp64 -O2" { target rv64 } } */
+/* { dg-options "-march=rv32gc_zbs -mabi=ilp32 -O2" { target rv32 } } */
+
+/* Verify that masked bit positions in minimal inlined bitset tests are
+   folded into BEXT for both branch polarities.  */
+
+typedef __SIZE_TYPE__ size_t;
+
+extern void sink (void);
+
+#define BITS_PER_WORD (sizeof (unsigned long) * 8)
+
+static inline _Bool
+bitset_test (const unsigned long *words, size_t index)
+{
+  unsigned long word = words[index / BITS_PER_WORD];
+  unsigned long mask = 1UL << (index % BITS_PER_WORD);
+  return (word & mask) != 0;
+}
+
+void
+branch_on_bit_set (const unsigned long *words, size_t index)
+{
+  if (bitset_test (words, index))
+    sink ();
+}
+
+void
+branch_on_bit_clear (const unsigned long *words, size_t index)
+{
+  if (!bitset_test (words, index))
+    sink ();
+}
+
+/* { dg-final { scan-assembler-times {\mbext\t} 2 } } */
-- 
2.52.0

Reply via email to