[llvm] [RISCV] Use QC.INSBI instead of ORI when possible (PR #147349)

Sudharsan Veeravalli via llvm-commits llvm-commits at lists.llvm.org
Mon Jul 7 21:59:39 PDT 2025


https://github.com/svs-quic updated https://github.com/llvm/llvm-project/pull/147349

>From c554cee293c61abf3648decfd20192fbd16f485a Mon Sep 17 00:00:00 2001
From: Sudharsan Veeravalli <quic_svs at quicinc.com>
Date: Mon, 7 Jul 2025 22:23:55 +0530
Subject: [PATCH 1/3] [RISCV] Use QC.INSBI instead of ORI when possible

When the immediate to the ORI is a ShiftedMask_32 that does not fit in 12-bits
we can use the QC.INSBI instruction instead. We do not do this for cases where the
ORI can be replaced with a BSETI since these can be compressesd when the Xqcibm
extension (which QC.INSBI is a part of) is enabled.
---
 llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp   | 37 +++++++-
 .../test/CodeGen/RISCV/xqcibm-cto-clo-brev.ll |  3 +-
 llvm/test/CodeGen/RISCV/xqcibm-insert.ll      | 88 +++++++++++++++++++
 3 files changed, 125 insertions(+), 3 deletions(-)
 create mode 100644 llvm/test/CodeGen/RISCV/xqcibm-insert.ll

diff --git a/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp b/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp
index 6298c7d5e9ef5..195e264582673 100644
--- a/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp
@@ -1298,7 +1298,42 @@ void RISCVDAGToDAGISel::Select(SDNode *Node) {
     ReplaceNode(Node, SRAI);
     return;
   }
-  case ISD::OR:
+  case ISD::OR: {
+    if (Subtarget->hasVendorXqcibm()) {
+      auto *N1C = dyn_cast<ConstantSDNode>(Node->getOperand(1));
+      if (!N1C)
+        break;
+
+      int32_t C1 = N1C->getSExtValue();
+      // If C1 is a shifted mask (but can't be formed as an ORI),
+      // use a bitfield insert of -1.
+      // Transform (or x, C1)
+      //        -> (qc.insbi x, width, shift)
+      if (isShiftedMask_32(C1) && !isInt<12>(C1)) {
+        const unsigned Leading = llvm::countl_zero((uint32_t)C1);
+        const unsigned Trailing = llvm::countr_zero((uint32_t)C1);
+
+        // If Zbs is enabled and it is a single bit set we can use BSETI which
+        // can be compressed to C_BSETI when Xqcibm in enabled.
+        if ((Leading + Trailing == 31) && Subtarget->hasStdExtZbs())
+          break;
+
+        const unsigned Width = 32 - Leading - Trailing;
+        SmallVector<SDValue, 4> Ops = {
+            CurDAG->getSignedTargetConstant(-1, DL, VT),
+            CurDAG->getTargetConstant(Width, DL, VT),
+            CurDAG->getTargetConstant(Trailing, DL, VT)};
+        SDNode *BitIns = CurDAG->getMachineNode(RISCV::QC_INSBI, DL, VT, Ops);
+        ReplaceNode(Node, BitIns);
+        return;
+      }
+    }
+
+    if (tryShrinkShlLogicImm(Node))
+      return;
+
+    break;
+  }
   case ISD::XOR:
     if (tryShrinkShlLogicImm(Node))
       return;
diff --git a/llvm/test/CodeGen/RISCV/xqcibm-cto-clo-brev.ll b/llvm/test/CodeGen/RISCV/xqcibm-cto-clo-brev.ll
index 691c5bec7fb51..f227fa9aa423d 100644
--- a/llvm/test/CodeGen/RISCV/xqcibm-cto-clo-brev.ll
+++ b/llvm/test/CodeGen/RISCV/xqcibm-cto-clo-brev.ll
@@ -105,8 +105,7 @@ define i16 @test_cttz_i16(i16 %a) nounwind {
 ;
 ; RV32ZBBXQCIBM-LABEL: test_cttz_i16:
 ; RV32ZBBXQCIBM:       # %bb.0:
-; RV32ZBBXQCIBM-NEXT:    lui a1, 16
-; RV32ZBBXQCIBM-NEXT:    orn a0, a1, a0
+; RV32ZBBXQCIBM-NEXT:    qc.insbi a0, -1, 1, 16
 ; RV32ZBBXQCIBM-NEXT:    ctz a0, a0
 ; RV32ZBBXQCIBM-NEXT:    ret
   %1 = xor i16 %a, -1
diff --git a/llvm/test/CodeGen/RISCV/xqcibm-insert.ll b/llvm/test/CodeGen/RISCV/xqcibm-insert.ll
new file mode 100644
index 0000000000000..6b7f9ae856625
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/xqcibm-insert.ll
@@ -0,0 +1,88 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5
+; RUN: llc -mtriple=riscv32 -verify-machineinstrs < %s \
+; RUN:   | FileCheck %s -check-prefixes=RV32I
+; RUN: llc -mtriple=riscv32 -mattr=+experimental-xqcibm -verify-machineinstrs < %s \
+; RUN:   | FileCheck %s -check-prefixes=RV32IXQCIBM
+; RUN: llc -mtriple=riscv32 -mattr=+experimental-xqcibm,+zbs -verify-machineinstrs < %s \
+; RUN:   | FileCheck %s -check-prefixes=RV32IXQCIBMZBS
+
+
+define i32 @test_ori(i32 %a) nounwind {
+; RV32I-LABEL: test_ori:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    ori a0, a0, 511
+; RV32I-NEXT:    ret
+;
+; RV32IXQCIBM-LABEL: test_ori:
+; RV32IXQCIBM:       # %bb.0:
+; RV32IXQCIBM-NEXT:    ori a0, a0, 511
+; RV32IXQCIBM-NEXT:    ret
+;
+; RV32IXQCIBMZBS-LABEL: test_ori:
+; RV32IXQCIBMZBS:       # %bb.0:
+; RV32IXQCIBMZBS-NEXT:    ori a0, a0, 511
+; RV32IXQCIBMZBS-NEXT:    ret
+  %or = or i32 %a, 511
+  ret i32 %or
+}
+
+define i32 @test_insbi_mask(i32 %a) nounwind {
+; RV32I-LABEL: test_insbi_mask:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    lui a1, 16
+; RV32I-NEXT:    addi a1, a1, -1
+; RV32I-NEXT:    or a0, a0, a1
+; RV32I-NEXT:    ret
+;
+; RV32IXQCIBM-LABEL: test_insbi_mask:
+; RV32IXQCIBM:       # %bb.0:
+; RV32IXQCIBM-NEXT:    qc.insbi a0, -1, 16, 0
+; RV32IXQCIBM-NEXT:    ret
+;
+; RV32IXQCIBMZBS-LABEL: test_insbi_mask:
+; RV32IXQCIBMZBS:       # %bb.0:
+; RV32IXQCIBMZBS-NEXT:    qc.insbi a0, -1, 16, 0
+; RV32IXQCIBMZBS-NEXT:    ret
+  %or = or i32 %a, 65535
+  ret i32 %or
+}
+
+define i32 @test_insbi_shifted_mask(i32 %a) nounwind {
+; RV32I-LABEL: test_insbi_shifted_mask:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    lui a1, 15
+; RV32I-NEXT:    or a0, a0, a1
+; RV32I-NEXT:    ret
+;
+; RV32IXQCIBM-LABEL: test_insbi_shifted_mask:
+; RV32IXQCIBM:       # %bb.0:
+; RV32IXQCIBM-NEXT:    qc.insbi a0, -1, 4, 12
+; RV32IXQCIBM-NEXT:    ret
+;
+; RV32IXQCIBMZBS-LABEL: test_insbi_shifted_mask:
+; RV32IXQCIBMZBS:       # %bb.0:
+; RV32IXQCIBMZBS-NEXT:    qc.insbi a0, -1, 4, 12
+; RV32IXQCIBMZBS-NEXT:    ret
+  %or = or i32 %a, 61440
+  ret i32 %or
+}
+
+define i32 @test_single_bit_set(i32 %a) nounwind {
+; RV32I-LABEL: test_single_bit_set:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    lui a1, 1
+; RV32I-NEXT:    or a0, a0, a1
+; RV32I-NEXT:    ret
+;
+; RV32IXQCIBM-LABEL: test_single_bit_set:
+; RV32IXQCIBM:       # %bb.0:
+; RV32IXQCIBM-NEXT:    qc.insbi a0, -1, 1, 12
+; RV32IXQCIBM-NEXT:    ret
+;
+; RV32IXQCIBMZBS-LABEL: test_single_bit_set:
+; RV32IXQCIBMZBS:       # %bb.0:
+; RV32IXQCIBMZBS-NEXT:    bseti a0, a0, 12
+; RV32IXQCIBMZBS-NEXT:    ret
+  %or = or i32 %a, 4096
+  ret i32 %or
+}

>From e24a29150b4cf8231ea4987bc963229eb7f6a4d2 Mon Sep 17 00:00:00 2001
From: Sudharsan Veeravalli <quic_svs at quicinc.com>
Date: Tue, 8 Jul 2025 08:10:20 +0530
Subject: [PATCH 2/3] Move code into separate function

---
 llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp | 68 ++++++++++++---------
 llvm/lib/Target/RISCV/RISCVISelDAGToDAG.h   |  1 +
 2 files changed, 40 insertions(+), 29 deletions(-)

diff --git a/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp b/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp
index 195e264582673..a96ecc0c7c430 100644
--- a/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp
@@ -681,6 +681,43 @@ bool RISCVDAGToDAGISel::trySignedBitfieldExtract(SDNode *Node) {
   return false;
 }
 
+bool RISCVDAGToDAGISel::trySignedBitfieldInsertInMask(SDNode *Node) {
+  // Supported only in Xqcibm for now.
+  if (!Subtarget->hasVendorXqcibm())
+    return false;
+
+  auto *N1C = dyn_cast<ConstantSDNode>(Node->getOperand(1));
+  if (!N1C)
+    return false;
+
+  int32_t C1 = N1C->getSExtValue();
+  if (!(isShiftedMask_32(C1) && !isInt<12>(C1)))
+    return false;
+
+  // If C1 is a shifted mask (but can't be formed as an ORI),
+  // use a bitfield insert of -1.
+  // Transform (or x, C1)
+  //        -> (qc.insbi x, width, shift)
+  const unsigned Leading = llvm::countl_zero((uint32_t)C1);
+  const unsigned Trailing = llvm::countr_zero((uint32_t)C1);
+  const unsigned Width = 32 - Leading - Trailing;
+
+  // If Zbs is enabled and it is a single bit set we can use BSETI which
+  // can be compressed to C_BSETI when Xqcibm in enabled.
+  if (Width == 1 && Subtarget->hasStdExtZbs())
+    return false;
+
+  SDLoc DL(Node);
+  MVT VT = Node->getSimpleValueType(0);
+
+  SmallVector<SDValue, 4> Ops = {CurDAG->getSignedTargetConstant(-1, DL, VT),
+                                 CurDAG->getTargetConstant(Width, DL, VT),
+                                 CurDAG->getTargetConstant(Trailing, DL, VT)};
+  SDNode *BitIns = CurDAG->getMachineNode(RISCV::QC_INSBI, DL, VT, Ops);
+  ReplaceNode(Node, BitIns);
+  return true;
+}
+
 bool RISCVDAGToDAGISel::trySignedBitfieldInsertInSign(SDNode *Node) {
   // Only supported with XAndesPerf at the moment.
   if (!Subtarget->hasVendorXAndesPerf())
@@ -1299,35 +1336,8 @@ void RISCVDAGToDAGISel::Select(SDNode *Node) {
     return;
   }
   case ISD::OR: {
-    if (Subtarget->hasVendorXqcibm()) {
-      auto *N1C = dyn_cast<ConstantSDNode>(Node->getOperand(1));
-      if (!N1C)
-        break;
-
-      int32_t C1 = N1C->getSExtValue();
-      // If C1 is a shifted mask (but can't be formed as an ORI),
-      // use a bitfield insert of -1.
-      // Transform (or x, C1)
-      //        -> (qc.insbi x, width, shift)
-      if (isShiftedMask_32(C1) && !isInt<12>(C1)) {
-        const unsigned Leading = llvm::countl_zero((uint32_t)C1);
-        const unsigned Trailing = llvm::countr_zero((uint32_t)C1);
-
-        // If Zbs is enabled and it is a single bit set we can use BSETI which
-        // can be compressed to C_BSETI when Xqcibm in enabled.
-        if ((Leading + Trailing == 31) && Subtarget->hasStdExtZbs())
-          break;
-
-        const unsigned Width = 32 - Leading - Trailing;
-        SmallVector<SDValue, 4> Ops = {
-            CurDAG->getSignedTargetConstant(-1, DL, VT),
-            CurDAG->getTargetConstant(Width, DL, VT),
-            CurDAG->getTargetConstant(Trailing, DL, VT)};
-        SDNode *BitIns = CurDAG->getMachineNode(RISCV::QC_INSBI, DL, VT, Ops);
-        ReplaceNode(Node, BitIns);
-        return;
-      }
-    }
+    if (trySignedBitfieldInsertInMask(Node))
+      return;
 
     if (tryShrinkShlLogicImm(Node))
       return;
diff --git a/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.h b/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.h
index 66d878f037446..29ee3ae31606e 100644
--- a/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.h
+++ b/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.h
@@ -79,6 +79,7 @@ class RISCVDAGToDAGISel : public SelectionDAGISel {
   bool tryShrinkShlLogicImm(SDNode *Node);
   bool trySignedBitfieldExtract(SDNode *Node);
   bool trySignedBitfieldInsertInSign(SDNode *Node);
+  bool trySignedBitfieldInsertInMask(SDNode *Node);
   bool tryUnsignedBitfieldExtract(SDNode *Node, const SDLoc &DL, MVT VT,
                                   SDValue X, unsigned Msb, unsigned Lsb);
   bool tryUnsignedBitfieldInsertInZero(SDNode *Node, const SDLoc &DL, MVT VT,

>From f10ce1962595a67938685f881e89eac961334554 Mon Sep 17 00:00:00 2001
From: Sudharsan Veeravalli <quic_svs at quicinc.com>
Date: Tue, 8 Jul 2025 10:29:01 +0530
Subject: [PATCH 3/3] Simplify code

---
 llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp b/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp
index a96ecc0c7c430..1a6f65a789a28 100644
--- a/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp
@@ -691,7 +691,7 @@ bool RISCVDAGToDAGISel::trySignedBitfieldInsertInMask(SDNode *Node) {
     return false;
 
   int32_t C1 = N1C->getSExtValue();
-  if (!(isShiftedMask_32(C1) && !isInt<12>(C1)))
+  if (!isShiftedMask_32(C1) || isInt<12>(C1))
     return false;
 
   // If C1 is a shifted mask (but can't be formed as an ORI),
@@ -710,9 +710,9 @@ bool RISCVDAGToDAGISel::trySignedBitfieldInsertInMask(SDNode *Node) {
   SDLoc DL(Node);
   MVT VT = Node->getSimpleValueType(0);
 
-  SmallVector<SDValue, 4> Ops = {CurDAG->getSignedTargetConstant(-1, DL, VT),
-                                 CurDAG->getTargetConstant(Width, DL, VT),
-                                 CurDAG->getTargetConstant(Trailing, DL, VT)};
+  SDValue Ops[] = {CurDAG->getSignedTargetConstant(-1, DL, VT),
+                   CurDAG->getTargetConstant(Width, DL, VT),
+                   CurDAG->getTargetConstant(Trailing, DL, VT)};
   SDNode *BitIns = CurDAG->getMachineNode(RISCV::QC_INSBI, DL, VT, Ops);
   ReplaceNode(Node, BitIns);
   return true;



More information about the llvm-commits mailing list