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