Skip to content

Conversation

topperc
Copy link
Collaborator

@topperc topperc commented Sep 18, 2025

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.

…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.
@llvmbot
Copy link
Member

llvmbot commented Sep 18, 2025

@llvm/pr-subscribers-backend-risc-v

Author: Craig Topper (topperc)

Changes

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.


Full diff: https://github.com/llvm/llvm-project/pull/159678.diff

2 Files Affected:

  • (modified) llvm/lib/Target/RISCV/RISCVInstrInfoZb.td (+13)
  • (modified) llvm/test/CodeGen/RISCV/rv64zbs.ll (+55-1)
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 a8b06b2af5764..b4edcf6cc55cf 100644
--- a/llvm/test/CodeGen/RISCV/rv64zbs.ll
+++ b/llvm/test/CodeGen/RISCV/rv64zbs.ll
@@ -547,10 +547,28 @@ 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 {
+; 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
+}
+
 define i64 @bclri_i64_10(i64 %a) nounwind {
 ; CHECK-LABEL: bclri_i64_10:
 ; CHECK:       # %bb.0:
@@ -724,6 +742,24 @@ define signext i32 @bseti_i32_31(i32 signext %a) nounwind {
   ret i32 %or
 }
 
+define signext i32 @bseti_i32_31_allWUsers(i32 signext %a) nounwind {
+; 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
+}
+
 define i64 @bseti_i64_10(i64 %a) nounwind {
 ; CHECK-LABEL: bseti_i64_10:
 ; CHECK:       # %bb.0:
@@ -862,6 +898,24 @@ 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 {
+; 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
+}
+
 define i64 @binvi_i64_10(i64 %a) nounwind {
 ; CHECK-LABEL: binvi_i64_10:
 ; CHECK:       # %bb.0:

Copy link
Member

@lenary lenary left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Co-authored-by: Sam Elliott <sam@lenary.co.uk>
@topperc
Copy link
Collaborator Author

topperc commented Sep 19, 2025

As a quick sanity check to make sure this had real world value, this triggered about 18 times in 502.gcc_r from spec2017.

@topperc topperc merged commit 0c1ab02 into llvm:main Sep 19, 2025
8 checks passed
@topperc topperc deleted the pr/bit31 branch September 19, 2025 00:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants