This patch contains 1 split and 1 insn_and_split pattern:
The define_split handles 3rd case (shafinish). On RV64
~(1 << n) for an int n is computed in SImode and sign-extended to
DImode. If the other operand has no bits set above bit 30,
the sign extension does not matter, and the AND
can be rewritten with (rotate (const_int -2) n), which matches bclr.
void shafinish(unsigned char *block, int blockcnt)
{
block[blockcnt] |= (0x01 << (blockcnt));
block[blockcnt] &= ~(0x01 << (blockcnt));
}
before:
add a0,a0,a1
lbu a4,0(a0)
li a5,1
sllw a5,a5,a1
andn a5,a4,a5
sb a5,0(a0)
after:
add a0,a0,a1
lbu a5,0(a0)
bclr t0,a5,a1
sb t0,0(a0)
The define_insn_and_split removes the redundant andi in the 1st
example (shabits). The shift count is (~x) & C, which
is at most C, so when C < log2 (M + 1) the bit always lies inside the
mask M and the AND is a no-op. The pattern splits into li/andn/bset.
There should be a 2nd patch that covers the GIMPLE side of the problem
for the 1st and 2nd example.
Regression tested on riscv64.
2026-10-21 Milan Tripkovic <[email protected]>
PR target/124020
gcc/ChangeLog:
*config/riscv/bitmanip.md (bset_andn_masked): new pattern
gcc/testsuite/ChangeLog:
*gcc.target/riscv/pr124020-1.c: New test.
*gcc.target/riscv/pr124020-2.c: New test.
CONFIDENTIALITY: The contents of this e-mail are confidential and intended only
for the above addressee(s). If you are not the intended recipient, or the
person responsible for delivering it to the intended recipient, copying or
delivering it to anyone else or using it in any unauthorized manner is
prohibited and may be unlawful. If you receive this e-mail by mistake, please
notify the sender and the systems administrator at [email protected]
immediately.
---
gcc/config/riscv/bitmanip.md | 38 +++++++++++++++++++++
gcc/testsuite/gcc.target/riscv/pr124020-1.c | 20 +++++++++++
gcc/testsuite/gcc.target/riscv/pr124020-2.c | 28 +++++++++++++++
3 files changed, 86 insertions(+)
create mode 100644 gcc/testsuite/gcc.target/riscv/pr124020-1.c
create mode 100644 gcc/testsuite/gcc.target/riscv/pr124020-2.c
diff --git a/gcc/config/riscv/bitmanip.md b/gcc/config/riscv/bitmanip.md
index 1027aa2dc06..0136ea7d8b3 100644
--- a/gcc/config/riscv/bitmanip.md
+++ b/gcc/config/riscv/bitmanip.md
@@ -740,6 +740,44 @@
{ operands[2] = GEN_INT (INTVAL (operands[2]) & 0x1f); }
[(set_attr "type" "bitmanip")])
+(define_split
+ [(set (match_operand:DI 0 "register_operand")
+ (and:DI (not:DI (sign_extend:DI
+ (ashift:SI (const_int 1)
+ (match_operand:QI 1 "register_operand"))))
+ (match_operand:DI 2 "register_operand")))]
+ "TARGET_64BIT && TARGET_ZBS
+ && (nonzero_bits (operands[2], DImode)
+ & ~HOST_WIDE_INT_C (0x7fffffff)) == 0"
+ [(set (match_dup 0)
+ (and:DI (rotate:DI (const_int -2) (match_dup 1))
+ (match_dup 2)))])
+
+(define_insn_and_split "*bset_andn_masked"
+ [(set (match_operand:DI 0 "register_operand" "=r")
+ (and:DI
+ (ashift:DI
+ (const_int 1)
+ (subreg:QI
+ (and:DI (not:DI (match_operand:DI 1 "register_operand" "r"))
+ (match_operand:DI 2 "const_int_operand" "n")) 0))
+ (match_operand:DI 3 "const_int_operand" "n")))
+ (clobber (match_scratch:DI 4 "=&r"))]
+ "TARGET_64BIT && TARGET_ZBS && (TARGET_ZBB || TARGET_ZBKB)
+ && exact_log2 (UINTVAL (operands[3]) + 1) > 0
+ && UINTVAL (operands[2])
+ < (unsigned HOST_WIDE_INT) exact_log2 (UINTVAL (operands[3]) + 1)"
+ "#"
+ "&& 1"
+ [(set (match_dup 4) (match_dup 2))
+ (set (match_dup 4) (and:DI (not:DI (match_dup 1)) (match_dup 4)))
+ (set (match_dup 0) (ashift:DI (const_int 1) (subreg:QI (match_dup 4) 0)))]
+{
+ if (GET_CODE (operands[4]) == SCRATCH)
+ operands[4] = gen_reg_rtx (DImode);
+}
+ [(set_attr "type" "bitmanip")])
+
;; Similarly two patterns for IOR/XOR generating bset/binv to
;; manipulate a bit in a register
(define_insn_and_split ""
diff --git a/gcc/testsuite/gcc.target/riscv/pr124020-1.c
b/gcc/testsuite/gcc.target/riscv/pr124020-1.c
new file mode 100644
index 00000000000..eec0d0562e2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/pr124020-1.c
@@ -0,0 +1,20 @@
+/* PR rtl-optimization/124020 */
+/* { dg-do compile { target rv64 } } */
+/* { dg-options "-O2 -march=rv64gc_zbb_zbs -mabi=lp64d" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Og" "-Os" "-Oz" } } */
+
+void
+shafinish (unsigned char *block, int blockcnt)
+{
+ block[blockcnt] |= (0x01 << (blockcnt));
+ block[blockcnt] &= ~(0x01 << (blockcnt));
+}
+
+void
+clr_short (unsigned short *p, int n)
+{
+ *p &= ~(1 << n);
+}
+
+/* { dg-final { scan-assembler-times {\mbclr\M} 2 } } */
+/* { dg-final { scan-assembler-not {\msllw\M} } } */
\ No newline at end of file
diff --git a/gcc/testsuite/gcc.target/riscv/pr124020-2.c
b/gcc/testsuite/gcc.target/riscv/pr124020-2.c
new file mode 100644
index 00000000000..06d367e0808
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/pr124020-2.c
@@ -0,0 +1,28 @@
+/* PR rtl-optimization/124020 */
+/* { dg-do compile { target rv64 } } */
+/* { dg-options "-O2 -march=rv64gc_zbb_zbs -mabi=lp64d -mtune=rocket" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Og" "-Os" "-Oz" } } */
+
+typedef struct SHA {
+ unsigned char block[128];
+ unsigned int blockcnt;
+} SHA;
+
+#define BITSET(s, pos) s[(pos) >> 3] & (char) (0x01 << (7 - (pos) % 8))
+#define SETBIT(s, pos) s[(pos) >> 3] |= (char) (0x01 << (7 - (pos) % 8))
+#define CLRBIT(s, pos) s[(pos) >> 3] &= (char) ~(0x01 << (7 - (pos) % 8))
+
+void
+shabits (char *bitstr, long bitcnt, SHA *s)
+{
+ unsigned long i;
+ for (i = 0UL; i < bitcnt; i++)
+ {
+ if (BITSET (bitstr, i))
+ SETBIT (s->block, s->blockcnt);
+ else
+ CLRBIT (s->block, s->blockcnt);
+ }
+}
+
+/* { dg-final { scan-assembler-not {\mandi\M[^\n]*0xff} } } */
\ No newline at end of file
--
2.34.1