This commit expands on the work by Jeff and Milan regarding generating
more Zbs instructions for RISC-V (PR123884).
For the testcase:
void foo(unsigned char *data, unsigned int lo_bit) {
unsigned int mask = ((1UL << 1) - 1) << lo_bit;
*data = (*data & ~mask) | ((1 << lo_bit) & mask)
}
Without their patch the following assembly would be produced:
foo(unsigned char*, unsigned int):
lbu a4,0(a0)
li a5,1
sllw a5,a5,a1
xor a5,a4,a5
bset a1,x0,a1
and a5,a5,a1
xor a4,a4,a5
sb a4,0(a0)
ret
With this patch it would be transformed into:
foo(unsigned char*, unsigned int):
lbu a5,0(a0)
li a4,1
sllw a4,a4,a1
bclr a5,a5,a1
or a5,a5,a4
sb a5,0(a0)
ret
Thus, in the combine pass arises a new pattern to try:
Trying 17, 18 -> 22:
17: r155:SI=0x1
18: r156:DI=sign_extend(r155:SI<<r143:DI#0)
REG_DEAD r155:SI
REG_DEAD r143:DI
REG_EQUAL sign_extend(0x1<<r143:DI#0)
22: r159:DI=r152:DI|r156:DI
REG_DEAD r156:DI
REG_DEAD r152:DI
Failed to match this instruction:
(set (reg:DI 159)
(ior:DI (sign_extend:DI (ashift:SI (const_int 1 [0x1])
(subreg:QI (reg/v:DI 143 [ lo_bitD.2469 ]) 0)))
(reg:DI 152)))
Splitting with gen_split_161 (bitmanip.md:796) <- pattern added in PR123884
Successfully matched this instruction:
(set (reg:DI 159)
(ior:DI (ashift:DI (const_int 1 [0x1])
(subreg:QI (reg/v:DI 143 [ lo_bitD.2469 ]) 0))
(reg:DI 152)))
Successfully matched this instruction:
(set (reg:DI 159)
(sign_extend:DI (subreg:SI (reg:DI 159) 0)))
And ultimately generates the following assembly:
foo:
lbu a5,0(a0)
bset a5,a5,a1
sb a5,0(a0)
ret
The match.pd pattern is inspired by Jeff's notes in the bugreport.
Not all testcases added by this patch simplify completely.
*For rv32 the smaller type to ULL produces poor assembly still
*For rv64 the UI to UL/ULL are still not clean.
Regression tested on x86, rv64 and rv32.
2026-07-07 Dusan Stojkovic <[email protected]>
PR target/123883
gcc/ChangeLog:
* match.pd: New pattern.
gcc/testsuite/ChangeLog:
* gcc.target/riscv/pr123883.c: New test.
Co-authored-by: Jeff Law <[email protected]>
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/match.pd | 22 ++++++++++
gcc/testsuite/gcc.target/riscv/pr123883.c | 49 +++++++++++++++++++++++
2 files changed, 71 insertions(+)
create mode 100644 gcc/testsuite/gcc.target/riscv/pr123883.c
diff --git a/gcc/match.pd b/gcc/match.pd
index a7cec25dbad..b0c13e72d97 100644
--- a/gcc/match.pd
+++ b/gcc/match.pd
@@ -12335,6 +12335,28 @@ and,
&& @0 == @3)
(bit_xor (rrotate @0 @4) @2)))
+/* (T)(1 << x) & (T)(1 << x) -> (T)(1 << x),
+ * but keep the shift in the wider type to avoid introducing
+ * undefined behaviour. */
+(simplify
+ (bit_and:c
+ (convert? (lshift@2 integer_onep@1 @0))
+ (convert? (lshift@3 integer_onep@4 @0)))
+ (with
+ {
+ tree ltype0 = TREE_TYPE (@2);
+ tree ltype1 = TREE_TYPE (@3);
+ tree largertype = ltype0;
+ tree largertypeone = build_one_cst (largertype);
+ }
+ (if (INTEGRAL_TYPE_P (type)
+ && INTEGRAL_TYPE_P (ltype0)
+ && INTEGRAL_TYPE_P (ltype1)
+ && element_precision (type) <= element_precision (ltype0)
+ && element_precision (type) <= element_precision (ltype1))
+ (convert:type
+ (lshift:largertype { largertypeone; } @0)))))
+
/* Optimize extraction from a uniform vector to a representative element as
long as the requested element is within range. */
(simplify (IFN_VEC_EXTRACT (vec_duplicate @0) INTEGER_CST@1)
diff --git a/gcc/testsuite/gcc.target/riscv/pr123883.c
b/gcc/testsuite/gcc.target/riscv/pr123883.c
new file mode 100644
index 00000000000..e4f86da0650
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/pr123883.c
@@ -0,0 +1,49 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64gcb -mabi=lp64d" { target rv64 } } */
+/* { dg-options "-march=rv32gcb -mabi=ilp32" { target rv32 } } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Og" "-Os" "-Oz" } } */
+
+#define TEST_STORE_NARROW_SHIFT(TYPE, WTYPE, NAME) \
+void \
+NAME (TYPE *data, unsigned int lo_bit) \
+{ \
+ WTYPE mask = (((WTYPE) 1UL << 1) - 1) << lo_bit; \
+ *data = (*data & ~mask) | ((1U << lo_bit) & mask); \
+}
+
+#define TEST_RET_NARROW_SHIFT(TYPE, WTYPE, NAME) \
+TYPE \
+NAME (TYPE data, unsigned int lo_bit) \
+{ \
+ WTYPE mask = (((WTYPE) 1 << 1) - 1) << lo_bit; \
+ return (data & ~mask) | ((1U << lo_bit) & mask); \
+}
+
+TEST_STORE_NARROW_SHIFT(unsigned char, unsigned long, store_uc_ul_narrow)
+TEST_STORE_NARROW_SHIFT(unsigned short, unsigned long, store_us_ul_narrow)
+TEST_STORE_NARROW_SHIFT(unsigned int, unsigned long, store_ui_ul_narrow)
+
+TEST_RET_NARROW_SHIFT(unsigned char, unsigned long, ret_uc_ul_narrow)
+TEST_RET_NARROW_SHIFT(unsigned short, unsigned long, ret_us_ul_narrow)
+TEST_RET_NARROW_SHIFT(unsigned int, unsigned long, ret_ui_ul_narrow)
+
+TEST_STORE_NARROW_SHIFT(unsigned char, unsigned long long,
store_uc_ull_narrow)
+TEST_STORE_NARROW_SHIFT(unsigned short, unsigned long long,
store_us_ull_narrow)
+TEST_STORE_NARROW_SHIFT(unsigned int, unsigned long long,
store_ui_ull_narrow)
+
+TEST_RET_NARROW_SHIFT(unsigned char, unsigned long long, ret_uc_ull_narrow)
+TEST_RET_NARROW_SHIFT(unsigned short, unsigned long long, ret_us_ull_narrow)
+TEST_RET_NARROW_SHIFT(unsigned int, unsigned long long, ret_ui_ull_narrow)
+
+
+/* { dg-final { scan-assembler-times "bset" 12 { target rv64 } } } */
+/* Current remaining missed SI-mode cases. */
+/* { dg-final { scan-assembler-times "sllw" 2 { target rv64 } } } */
+/* { dg-final { scan-assembler-times "binv" 2 { target rv64 } } } */
+/* { dg-final { scan-assembler-times "sext.w" 4 { target rv64 } } } */
+
+/* Current remaining ULL wide-mask cases on RV32. */
+/* { dg-final { scan-assembler-times "bset" 14 { target rv32 } } } */
+/* { dg-final { scan-assembler-times "binv" 2 { target rv32 } } } */
+/* { dg-final { scan-assembler-times "srai" 4 { target rv32 } } } */
+
--
2.43.0