We currently have an issue in my previous RTL when dest is a 64-bit value.
The upper 32 bits are lost during the sext instruction.
https://godbolt.org/z/q31obx3b6
Example:
dest = 0xFFFFFFFF00000001
a = 29
In the initial implementation, we get:
0xFFFFFFFF00000001 | 0x0000000020000000
which results in:
0xFFFFFFFF20000001
However, with the bset + sext sequence, the behavior is incorrect:
after best : 0xFFFFFFFF20000001
after sext : 0x0000000020000001
So the sign extension is performed incorrectly, causing the upper 32 bits
to be dropped and leading to an incorrect final result. I would like to change
the pattern so that it becomes something like bset + sext + or, since it is
required for this case. The issue is that both long and int cases currently
share the same RTL in combine pass when our pattern match, so at the moment
I don’t have a clear way to distinguish between them. As a result, GCC would
also
generate bset + sext + or for the int case, which should be just bset + sext.
2026-05-06 Milan Tripkovic <[email protected]>
gcc/ChangeLog:
* config/riscv/bitmanip.md (bset_sextw_or): new pattern for bset
gcc/testsuite/ChangeLog:
* gcc.target/riscv/pr123884-c.c: New test for new pattern
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 | 26 +++++++++++++++++++++
gcc/testsuite/gcc.target/riscv/pr123884-c.c | 12 ++++++++++
2 files changed, 38 insertions(+)
create mode 100644 gcc/testsuite/gcc.target/riscv/pr123884-c.c
diff --git a/gcc/config/riscv/bitmanip.md b/gcc/config/riscv/bitmanip.md
index 45881994f..14e98693a 100644
--- a/gcc/config/riscv/bitmanip.md
+++ b/gcc/config/riscv/bitmanip.md
@@ -740,6 +740,32 @@
{ operands[2] = GEN_INT (INTVAL (operands[2]) & 0x1f); }
[(set_attr "type" "bitmanip")])
+;;Pattern for bset with sign_extend
+(define_insn_and_split "bset_sextw_or"
+ [(set (match_operand:DI 0 "register_operand" "=r")
+ (ior:DI
+ (sign_extend:DI
+ (ashift:SI
+ (const_int 1)
+ (subreg:QI
+ (match_operand:DI 1 "register_operand" "r") 0)))
+ (match_operand:DI 2 "register_operand" "r")))]
+ "TARGET_64BIT && TARGET_ZBS"
+ "#"
+ "&& !reload_completed"
+ [
+ (set (match_dup 3)
+ (ashift:DI (const_int 1)
+ (subreg:QI (match_dup 1) 0)))
+ (set (match_dup 3)
+ (sign_extend:DI (subreg:SI (match_dup 3) 0)))
+ (set (match_dup 0)
+ (ior:DI (match_dup 2) (match_dup 3)))
+ ]
+ {
+ operands[3] = gen_reg_rtx (DImode);
+ }
+)
;; Similarly two patterns for AND generating bclr to
;; manipulate a bit in a register
(define_insn_and_split ""
diff --git a/gcc/testsuite/gcc.target/riscv/pr123884-c.c
b/gcc/testsuite/gcc.target/riscv/pr123884-c.c
new file mode 100644
index 000000000..6a941a9a3
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/pr123884-c.c
@@ -0,0 +1,12 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64gc_zbs -mabi=lp64d -O2" } */
+
+int foo2(int dst, int a)
+{
+ dst |= (1 << a);
+ return dst;
+}
+
+/* { dg-final { scan-assembler-times "bset" 1 } } */
+/* { dg-final { scan-assembler-times "sext.w" 1 } } */
+/* { dg-final { scan-assembler-times "or" 1 } } */
\ No newline at end of file
--
2.34.1