RISC-V has the ability to extract a single bit out of a register from a
fixed or variable position.
While looking at 502.gcc a little while ago I realize that we failed to
use bext inside bitmap_bit_p for its return value.
The core "problem" is that the RISC-V does not define
SHIFT_COUNT_TRUNCATED (for good reasons). As a result the target is
largely responsible for handling elimination of shift count/bit position
masking.
There's a follow-up patch I've been working on with an intern to improve
detection of bext in more cases. This one stands independently though
and is probably the most important of the missed cases.
Will push to the trunk assuming pre-commit testing is green. It's
already been through my tester as well as Ventana's internal testing.
jeff
commit 3530e7a8bbd3c55962b39c7f9d0d5712edead621
Author: Jeff Law <j...@ventanamicro.com>
Date: Sun Mar 16 20:30:56 2025 -0600
Fix missed bext discovered in 502.gcc
(cherry picked from commit bc1a3419fadf63a7a2aec427082cf38cd369b77a)
gcc
* config/riscv/bitmanip.md (*bext<mode>_mask_pos): New pattern
for extracting a single bit at masked bit position.
gcc/testsuite
* gcc.target/riscv/bext-ext-2.c: New test
diff --git a/gcc/config/riscv/bitmanip.md b/gcc/config/riscv/bitmanip.md
index 2fa121b1a70..9c5253376d0 100644
--- a/gcc/config/riscv/bitmanip.md
+++ b/gcc/config/riscv/bitmanip.md
@@ -1004,6 +1004,24 @@ (define_insn "*bext<mode>"
"bext\t%0,%1,%2"
[(set_attr "type" "bitmanip")])
+;; We do not define SHIFT_COUNT_TRUNCATED, so we have to have variants
+;; that mask/extend the count if we want to eliminate those ops
+;;
+;; We could (in theory) use GPR for the various modes, but I haven't
+;; seen those cases appear in practice. Without a testcase I've
+;; elected to keep the modes X which is easy to reason about.
+(define_insn "*bext<mode>_mask_pos"
+ [(set (match_operand:X 0 "register_operand" "=r")
+ (zero_extract:X (match_operand:X 1 "register_operand" "r")
+ (const_int 1)
+ (and:X
+ (match_operand:X 2 "register_operand" "r")
+ (match_operand 3 "const_int_operand"))))]
+ "(TARGET_ZBS
+ && INTVAL (operands[3]) + 1 == GET_MODE_BITSIZE (<MODE>mode))"
+ "bext\t%0,%1,%2"
+ [(set_attr "type" "bitmanip")])
+
;; This is a bext followed by a seqz. Normally this would be a 3->2 split
;; But the and-not pattern with a constant operand is a define_insn_and_split,
;; so this looks like a 2->2 split, which combine rejects. So implement it
diff --git a/gcc/testsuite/gcc.target/riscv/bext-ext-2.c
b/gcc/testsuite/gcc.target/riscv/bext-ext-2.c
new file mode 100644
index 00000000000..aa170d06d0e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/bext-ext-2.c
@@ -0,0 +1,74 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64gcb -mabi=lp64" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" } } */
+
+struct obstack;
+struct bitmap_head_def;
+typedef struct bitmap_head_def *bitmap;
+struct obstack
+{
+ long chunk_size;
+ struct _obstack_chunk *chunk;
+ char *object_base;
+ char *next_free;
+ char *chunk_limit;
+ long int temp;
+ int alignment_mask;
+
+
+
+ struct _obstack_chunk *(*chunkfun) (void *, long);
+ void (*freefun) (void *, struct _obstack_chunk *);
+ void *extra_arg;
+ unsigned use_extra_arg:1;
+ unsigned maybe_empty_object:1;
+
+
+
+ unsigned alloc_failed:1;
+
+
+};
+
+typedef unsigned long BITMAP_WORD;
+typedef struct bitmap_obstack {
+ struct bitmap_element_def *elements;
+ struct bitmap_head_def *heads;
+ struct obstack obstack;
+} bitmap_obstack;
+typedef struct bitmap_element_def {
+ struct bitmap_element_def *next;
+ struct bitmap_element_def *prev;
+ unsigned int indx;
+ BITMAP_WORD bits[((128 + (8
+ * 8 * 1u) - 1) / (8
+ * 8 * 1u))];
+} bitmap_element;
+bitmap_element *bitmap_find_bit (bitmap, unsigned int);
+
+
+int
+bitmap_bit_p (bitmap head, int bit)
+{
+ bitmap_element *ptr;
+ unsigned bit_num;
+ unsigned word_num;
+
+ ptr = bitmap_find_bit (head, bit);
+ if (ptr == 0)
+ return 0;
+
+ bit_num = bit % (8
+ * 8 * 1u);
+ word_num = bit / (8
+ * 8 * 1u) % ((128 + (8
+ * 8 * 1u) - 1) / (8
+ * 8 * 1u));
+
+ return (ptr->bits[word_num] >> bit_num) & 1;
+}
+
+/* { dg-final { scan-assembler-times "bext\t" 1 } } */
+/* { dg-final { scan-assembler-not "slr\t"} } */
+/* { dg-final { scan-assembler-not "andi\t"} } */
+