[llvm] [RISCV] Use bseti 31 for (or X, -2147483648) when upper 32 bits aren't used. (PR #159678)

Craig Topper via llvm-commits llvm-commits at lists.llvm.org
Thu Sep 18 16:49:00 PDT 2025


https://github.com/topperc updated https://github.com/llvm/llvm-project/pull/159678

>From c7fa8d22ac59e76b229dacaa488aa2145248c43f Mon Sep 17 00:00:00 2001
From: Craig Topper <craig.topper at sifive.com>
Date: Thu, 18 Sep 2025 15:55:36 -0700
Subject: [PATCH 1/3] Pre-commit test

---
 llvm/test/CodeGen/RISCV/rv64zbs.ll | 38 +++++++++++++++++++++++++++++-
 1 file changed, 37 insertions(+), 1 deletion(-)

diff --git a/llvm/test/CodeGen/RISCV/rv64zbs.ll b/llvm/test/CodeGen/RISCV/rv64zbs.ll
index a8b06b2af5764..d58b8480412c0 100644
--- a/llvm/test/CodeGen/RISCV/rv64zbs.ll
+++ b/llvm/test/CodeGen/RISCV/rv64zbs.ll
@@ -547,10 +547,22 @@ define signext i32 @bclri_i32_31(i32 signext %a) nounwind {
 ; CHECK-NEXT:    slli a0, a0, 33
 ; CHECK-NEXT:    srli a0, a0, 33
 ; CHECK-NEXT:    ret
-  %and = and i32 %a, -2147483649
+  %and = and i32 %a, 2147483647
   ret i32 %and
 }
 
+define signext i32 @bclri_i32_31_allWUsers(i32 signext %a) nounwind {
+; CHECK-LABEL: bclri_i32_31_allWUsers:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    slli a0, a0, 33
+; CHECK-NEXT:    srli a0, a0, 33
+; CHECK-NEXT:    addiw a0, a0, 1
+; CHECK-NEXT:    ret
+  %and = and i32 %a, 2147483647
+  %add = add i32 %and, 1
+  ret i32 %add
+}
+
 define i64 @bclri_i64_10(i64 %a) nounwind {
 ; CHECK-LABEL: bclri_i64_10:
 ; CHECK:       # %bb.0:
@@ -724,6 +736,18 @@ define signext i32 @bseti_i32_31(i32 signext %a) nounwind {
   ret i32 %or
 }
 
+define signext i32 @bseti_i32_31_allWUsers(i32 signext %a) nounwind {
+; CHECK-LABEL: bseti_i32_31_allWUsers:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    lui a1, 524288
+; CHECK-NEXT:    or a0, a0, a1
+; CHECK-NEXT:    addiw a0, a0, 1
+; CHECK-NEXT:    ret
+  %or = or i32 %a, 2147483648
+  %add = add i32 %or, 1
+  ret i32 %add
+}
+
 define i64 @bseti_i64_10(i64 %a) nounwind {
 ; CHECK-LABEL: bseti_i64_10:
 ; CHECK:       # %bb.0:
@@ -862,6 +886,18 @@ define signext i32 @binvi_i32_31(i32 signext %a) nounwind {
   ret i32 %xor
 }
 
+define void @binvi_i32_31_allWUsers(i32 signext %a, ptr %p) nounwind {
+; CHECK-LABEL: binvi_i32_31_allWUsers:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    lui a2, 524288
+; CHECK-NEXT:    xor a0, a0, a2
+; CHECK-NEXT:    sw a0, 0(a1)
+; CHECK-NEXT:    ret
+  %xor = xor i32 %a, 2147483648
+  store i32 %xor, ptr %p
+  ret void
+}
+
 define i64 @binvi_i64_10(i64 %a) nounwind {
 ; CHECK-LABEL: binvi_i64_10:
 ; CHECK:       # %bb.0:

>From 078305192b4dd2ceeacdf0cdeeeee78466e8b4ad Mon Sep 17 00:00:00 2001
From: Craig Topper <craig.topper at sifive.com>
Date: Thu, 18 Sep 2025 15:57:36 -0700
Subject: [PATCH 2/3] [RISCV] Use bseti 31 for (or X, -2147483648) when upper
 32 bits aren't used.

If the original type was i32, type legalization will sign extend
the constant. This prevents it from having a single bit set or clear
so other patterns can't match. If the upper bits aren't used, we
can ignore the sign extension.

Similar for bclri and binvi.
---
 llvm/lib/Target/RISCV/RISCVInstrInfoZb.td | 13 ++++++
 llvm/test/CodeGen/RISCV/rv64zbs.ll        | 54 +++++++++++++++--------
 2 files changed, 49 insertions(+), 18 deletions(-)

diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfoZb.td b/llvm/lib/Target/RISCV/RISCVInstrInfoZb.td
index a2b4302e19edc..9b98588073212 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfoZb.td
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfoZb.td
@@ -574,6 +574,19 @@ def : Pat<(XLenVT (and GPR:$r, BCLRIANDIMask:$i)),
                  (BCLRITwoBitsMaskHigh BCLRIANDIMask:$i))>;
 } // Predicates = [HasStdExtZbs]
 
+let Predicates = [HasStdExtZbs, IsRV64] in {
+// If the original code was setting, clearing, or inverting bit 31 or an i32,
+// type legalization might have sign extended the constant so it doesn't have a
+// single 0 or 1 bit. If the upper 32 bits aren't used by later instructions we
+// can still use bseti/bclri.
+def : Pat<(binop_allwusers<and> GPR:$rs1, (XLenVT 2147483647)),
+          (BCLRI GPR:$rs1, 31)>;
+def : Pat<(binop_allwusers<or> GPR:$rs1, (XLenVT -2147483648)),
+          (BSETI GPR:$rs1, 31)>;
+def : Pat<(binop_allwusers<xor> GPR:$rs1, (XLenVT -2147483648)),
+          (BINVI GPR:$rs1, 31)>;
+}
+
 let Predicates = [HasStdExtZbb] in
 def : PatGpr<riscv_orc_b, ORC_B>;
 
diff --git a/llvm/test/CodeGen/RISCV/rv64zbs.ll b/llvm/test/CodeGen/RISCV/rv64zbs.ll
index d58b8480412c0..b4edcf6cc55cf 100644
--- a/llvm/test/CodeGen/RISCV/rv64zbs.ll
+++ b/llvm/test/CodeGen/RISCV/rv64zbs.ll
@@ -552,12 +552,18 @@ define signext i32 @bclri_i32_31(i32 signext %a) nounwind {
 }
 
 define signext i32 @bclri_i32_31_allWUsers(i32 signext %a) nounwind {
-; CHECK-LABEL: bclri_i32_31_allWUsers:
-; CHECK:       # %bb.0:
-; CHECK-NEXT:    slli a0, a0, 33
-; CHECK-NEXT:    srli a0, a0, 33
-; CHECK-NEXT:    addiw a0, a0, 1
-; CHECK-NEXT:    ret
+; RV64I-LABEL: bclri_i32_31_allWUsers:
+; RV64I:       # %bb.0:
+; RV64I-NEXT:    slli a0, a0, 33
+; RV64I-NEXT:    srli a0, a0, 33
+; RV64I-NEXT:    addiw a0, a0, 1
+; RV64I-NEXT:    ret
+;
+; RV64ZBS-LABEL: bclri_i32_31_allWUsers:
+; RV64ZBS:       # %bb.0:
+; RV64ZBS-NEXT:    bclri a0, a0, 31
+; RV64ZBS-NEXT:    addiw a0, a0, 1
+; RV64ZBS-NEXT:    ret
   %and = and i32 %a, 2147483647
   %add = add i32 %and, 1
   ret i32 %add
@@ -737,12 +743,18 @@ define signext i32 @bseti_i32_31(i32 signext %a) nounwind {
 }
 
 define signext i32 @bseti_i32_31_allWUsers(i32 signext %a) nounwind {
-; CHECK-LABEL: bseti_i32_31_allWUsers:
-; CHECK:       # %bb.0:
-; CHECK-NEXT:    lui a1, 524288
-; CHECK-NEXT:    or a0, a0, a1
-; CHECK-NEXT:    addiw a0, a0, 1
-; CHECK-NEXT:    ret
+; RV64I-LABEL: bseti_i32_31_allWUsers:
+; RV64I:       # %bb.0:
+; RV64I-NEXT:    lui a1, 524288
+; RV64I-NEXT:    or a0, a0, a1
+; RV64I-NEXT:    addiw a0, a0, 1
+; RV64I-NEXT:    ret
+;
+; RV64ZBS-LABEL: bseti_i32_31_allWUsers:
+; RV64ZBS:       # %bb.0:
+; RV64ZBS-NEXT:    bseti a0, a0, 31
+; RV64ZBS-NEXT:    addiw a0, a0, 1
+; RV64ZBS-NEXT:    ret
   %or = or i32 %a, 2147483648
   %add = add i32 %or, 1
   ret i32 %add
@@ -887,12 +899,18 @@ define signext i32 @binvi_i32_31(i32 signext %a) nounwind {
 }
 
 define void @binvi_i32_31_allWUsers(i32 signext %a, ptr %p) nounwind {
-; CHECK-LABEL: binvi_i32_31_allWUsers:
-; CHECK:       # %bb.0:
-; CHECK-NEXT:    lui a2, 524288
-; CHECK-NEXT:    xor a0, a0, a2
-; CHECK-NEXT:    sw a0, 0(a1)
-; CHECK-NEXT:    ret
+; RV64I-LABEL: binvi_i32_31_allWUsers:
+; RV64I:       # %bb.0:
+; RV64I-NEXT:    lui a2, 524288
+; RV64I-NEXT:    xor a0, a0, a2
+; RV64I-NEXT:    sw a0, 0(a1)
+; RV64I-NEXT:    ret
+;
+; RV64ZBS-LABEL: binvi_i32_31_allWUsers:
+; RV64ZBS:       # %bb.0:
+; RV64ZBS-NEXT:    binvi a0, a0, 31
+; RV64ZBS-NEXT:    sw a0, 0(a1)
+; RV64ZBS-NEXT:    ret
   %xor = xor i32 %a, 2147483648
   store i32 %xor, ptr %p
   ret void

>From 623a10bf08781f719b9f4fdf45b15fac27362bbe Mon Sep 17 00:00:00 2001
From: Craig Topper <craig.topper at sifive.com>
Date: Thu, 18 Sep 2025 16:48:52 -0700
Subject: [PATCH 3/3] Update llvm/lib/Target/RISCV/RISCVInstrInfoZb.td

Co-authored-by: Sam Elliott <sam at lenary.co.uk>
---
 llvm/lib/Target/RISCV/RISCVInstrInfoZb.td | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfoZb.td b/llvm/lib/Target/RISCV/RISCVInstrInfoZb.td
index 9b98588073212..ce21d831250b2 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfoZb.td
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfoZb.td
@@ -575,7 +575,7 @@ def : Pat<(XLenVT (and GPR:$r, BCLRIANDIMask:$i)),
 } // Predicates = [HasStdExtZbs]
 
 let Predicates = [HasStdExtZbs, IsRV64] in {
-// If the original code was setting, clearing, or inverting bit 31 or an i32,
+// If the original code was setting, clearing, or inverting bit 31 of an i32,
 // type legalization might have sign extended the constant so it doesn't have a
 // single 0 or 1 bit. If the upper 32 bits aren't used by later instructions we
 // can still use bseti/bclri.



More information about the llvm-commits mailing list