[llvm] [RISCV] Generate QC_INSB/QC_INSBI instructions from OR of AND Imm (PR #154023)

Sudharsan Veeravalli via llvm-commits llvm-commits at lists.llvm.org
Mon Aug 18 00:23:27 PDT 2025


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

>From cac73dad7c18cc47fe1000d065bd6f2ff6e1b3a6 Mon Sep 17 00:00:00 2001
From: Sudharsan Veeravalli <quic_svs at quicinc.com>
Date: Sun, 17 Aug 2025 21:23:05 +0530
Subject: [PATCH 1/3] [RISCV] Generate QC_INSB/QC_INSBI instructions from OR of
 AND Imm

Generate QC_INSB/QC_INSBI from 'or (and X, MaskImm), OrImm' iff the
value being inserted only sets known zero bits
---
 llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp |  71 +++++++
 llvm/lib/Target/RISCV/RISCVISelDAGToDAG.h   |   1 +
 llvm/test/CodeGen/RISCV/xqcibm-insert.ll    | 205 ++++++++++++++++++++
 3 files changed, 277 insertions(+)

diff --git a/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp b/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp
index 9e1530a2d00f4..33648c0c11ebf 100644
--- a/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp
@@ -720,6 +720,74 @@ bool RISCVDAGToDAGISel::trySignedBitfieldInsertInMask(SDNode *Node) {
   return true;
 }
 
+// Generate a QC_INSB/QC_INSBI from 'or (and X, MaskImm), OrImm' iff the value
+// being inserted only sets known zero bits.
+bool RISCVDAGToDAGISel::tryBitfieldInsertOpFromOrAndImm(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;
+
+  SDValue And = Node->getOperand(0);
+
+  if (And.getOpcode() != ISD::AND)
+    return false;
+
+  auto *N2C = dyn_cast<ConstantSDNode>(And->getOperand(1));
+  if (!And.hasOneUse() || !N2C)
+    return false;
+
+  int32_t OrImm = N1C->getSExtValue();
+
+  // Compute the Known Zero for the AND as this allows us to catch more general
+  // cases than just looking for AND with imm.
+  KnownBits Known = CurDAG->computeKnownBits(And);
+
+  // Non-zero in the sense that they're not provably zero, which is the key
+  // point if we want to use this value.
+  uint64_t NotKnownZero = (~Known.Zero).getZExtValue();
+
+  // The KnownZero mask must be a shifted mask (e.g., 1110..011, 11100..00).
+  if (!isShiftedMask_32(Known.Zero.getZExtValue()))
+    return false;
+
+  // The bits being inserted must only set those bits that are known to be zero.
+  if ((OrImm & NotKnownZero) != 0) {
+    // FIXME:  It's okay if the OrImm sets NotKnownZero bits to 1, but we don't
+    // currently handle this case.
+    return false;
+  }
+
+  // QC_INSB(I) dst, src, #width, #shamt.
+  MVT VT = Node->getSimpleValueType(0);
+  unsigned BitWidth = VT.getSizeInBits();
+  const unsigned ShAmt = llvm::countr_one(NotKnownZero);
+  const unsigned Width = BitWidth - APInt(BitWidth, NotKnownZero).popcount();
+
+  SDLoc DL(Node);
+  SDValue ImmNode;
+  auto Opc = RISCV::QC_INSB;
+
+  int32_t LIImm = OrImm >> ShAmt;
+
+  if (isInt<5>(LIImm)) {
+    Opc = RISCV::QC_INSBI;
+    ImmNode = CurDAG->getSignedTargetConstant(LIImm, DL, MVT::i32);
+  } else {
+    ImmNode = selectImm(CurDAG, DL, MVT::i32, LIImm, *Subtarget);
+  }
+
+  SDValue Ops[] = {And.getOperand(0), ImmNode,
+                   CurDAG->getTargetConstant(Width, DL, VT),
+                   CurDAG->getTargetConstant(ShAmt, DL, VT)};
+  SDNode *BitIns = CurDAG->getMachineNode(Opc, DL, VT, Ops);
+  ReplaceNode(Node, BitIns);
+  return true;
+}
+
 bool RISCVDAGToDAGISel::trySignedBitfieldInsertInSign(SDNode *Node) {
   // Only supported with XAndesPerf at the moment.
   if (!Subtarget->hasVendorXAndesPerf())
@@ -1384,6 +1452,9 @@ void RISCVDAGToDAGISel::Select(SDNode *Node) {
     if (trySignedBitfieldInsertInMask(Node))
       return;
 
+    if (tryBitfieldInsertOpFromOrAndImm(Node))
+      return;
+
     if (tryShrinkShlLogicImm(Node))
       return;
 
diff --git a/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.h b/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.h
index 9d4cd0e6e3393..c329a4c6ec62e 100644
--- a/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.h
+++ b/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.h
@@ -76,6 +76,7 @@ class RISCVDAGToDAGISel : public SelectionDAGISel {
   bool trySignedBitfieldInsertInSign(SDNode *Node);
   bool trySignedBitfieldInsertInMask(SDNode *Node);
   bool tryBitfieldInsertOpFromXor(SDNode *Node);
+  bool tryBitfieldInsertOpFromOrAndImm(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,
diff --git a/llvm/test/CodeGen/RISCV/xqcibm-insert.ll b/llvm/test/CodeGen/RISCV/xqcibm-insert.ll
index 88054a691bad1..0039580bcd53f 100644
--- a/llvm/test/CodeGen/RISCV/xqcibm-insert.ll
+++ b/llvm/test/CodeGen/RISCV/xqcibm-insert.ll
@@ -139,3 +139,208 @@ define i32 @test_single_bit_set(i32 %a) nounwind {
   %or = or i32 %a, 4096
   ret i32 %or
 }
+
+
+; Tests for INSB(I) generation from OR and AND
+
+define i32 @test1(i32 %a) {
+; RV32I-LABEL: test1:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    andi a0, a0, -16
+; RV32I-NEXT:    addi a0, a0, 5
+; RV32I-NEXT:    ret
+;
+; RV32IXQCIBM-LABEL: test1:
+; RV32IXQCIBM:       # %bb.0:
+; RV32IXQCIBM-NEXT:    qc.insbi a0, 5, 4, 0
+; RV32IXQCIBM-NEXT:    ret
+;
+; RV32IXQCIBMZBS-LABEL: test1:
+; RV32IXQCIBMZBS:       # %bb.0:
+; RV32IXQCIBMZBS-NEXT:    qc.insbi a0, 5, 4, 0
+; RV32IXQCIBMZBS-NEXT:    ret
+  %1 = and i32 %a, -16 ; 0xfffffff0
+  %2 = or i32 %1, 5    ; 0x00000005
+  ret i32 %2
+}
+
+define i32 @test2(i32 %a) {
+; RV32I-LABEL: test2:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    lui a1, 1033216
+; RV32I-NEXT:    addi a1, a1, -1
+; RV32I-NEXT:    and a0, a0, a1
+; RV32I-NEXT:    lui a1, 10240
+; RV32I-NEXT:    or a0, a0, a1
+; RV32I-NEXT:    ret
+;
+; RV32IXQCIBM-LABEL: test2:
+; RV32IXQCIBM:       # %bb.0:
+; RV32IXQCIBM-NEXT:    qc.insbi a0, 10, 4, 22
+; RV32IXQCIBM-NEXT:    ret
+;
+; RV32IXQCIBMZBS-LABEL: test2:
+; RV32IXQCIBMZBS:       # %bb.0:
+; RV32IXQCIBMZBS-NEXT:    qc.insbi a0, 10, 4, 22
+; RV32IXQCIBMZBS-NEXT:    ret
+  %1 = and i32 %a, -62914561 ; 0xfc3fffff
+  %2 = or i32 %1, 41943040   ; 0x06400000
+  ret i32 %2
+}
+
+define i64 @test3(i64 %a) {
+; RV32I-LABEL: test3:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    andi a0, a0, -8
+; RV32I-NEXT:    addi a0, a0, 5
+; RV32I-NEXT:    ret
+;
+; RV32IXQCIBM-LABEL: test3:
+; RV32IXQCIBM:       # %bb.0:
+; RV32IXQCIBM-NEXT:    qc.insbi a0, 5, 3, 0
+; RV32IXQCIBM-NEXT:    ret
+;
+; RV32IXQCIBMZBS-LABEL: test3:
+; RV32IXQCIBMZBS:       # %bb.0:
+; RV32IXQCIBMZBS-NEXT:    qc.insbi a0, 5, 3, 0
+; RV32IXQCIBMZBS-NEXT:    ret
+  %1 = and i64 %a, -8 ; 0xfffffffffffffff8
+  %2 = or i64 %1, 5   ; 0x0000000000000005
+  ret i64 %2
+}
+
+define i64 @test4(i64 %a) {
+; RV32I-LABEL: test4:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    andi a0, a0, -255
+; RV32I-NEXT:    addi a0, a0, 18
+; RV32I-NEXT:    ret
+;
+; RV32IXQCIBM-LABEL: test4:
+; RV32IXQCIBM:       # %bb.0:
+; RV32IXQCIBM-NEXT:    qc.insbi a0, 9, 7, 1
+; RV32IXQCIBM-NEXT:    ret
+;
+; RV32IXQCIBMZBS-LABEL: test4:
+; RV32IXQCIBMZBS:       # %bb.0:
+; RV32IXQCIBMZBS-NEXT:    qc.insbi a0, 9, 7, 1
+; RV32IXQCIBMZBS-NEXT:    ret
+  %1 = and i64 %a, -255 ; 0xffffffffffffff01
+  %2 = or i64 %1,  18   ; 0x0000000000000012
+  ret i64 %2
+}
+
+define i32 @test5(i32 %a) {
+; RV32I-LABEL: test5:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    andi a0, a0, -16
+; RV32I-NEXT:    addi a0, a0, 6
+; RV32I-NEXT:    ret
+;
+; RV32IXQCIBM-LABEL: test5:
+; RV32IXQCIBM:       # %bb.0:
+; RV32IXQCIBM-NEXT:    qc.insbi a0, 6, 4, 0
+; RV32IXQCIBM-NEXT:    ret
+;
+; RV32IXQCIBMZBS-LABEL: test5:
+; RV32IXQCIBMZBS:       # %bb.0:
+; RV32IXQCIBMZBS-NEXT:    qc.insbi a0, 6, 4, 0
+; RV32IXQCIBMZBS-NEXT:    ret
+  %1 = and i32 %a, 4294967280 ; 0xfffffff0
+  %2 = or i32 %1, 6           ; 0x00000006
+  ret i32 %2
+}
+
+define i32 @test6(i32 %a) {
+; RV32I-LABEL: test6:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    lui a1, 1048320
+; RV32I-NEXT:    and a0, a0, a1
+; RV32I-NEXT:    lui a1, 182
+; RV32I-NEXT:    addi a1, a1, -1326
+; RV32I-NEXT:    or a0, a0, a1
+; RV32I-NEXT:    ret
+;
+; RV32IXQCIBM-LABEL: test6:
+; RV32IXQCIBM:       # %bb.0:
+; RV32IXQCIBM-NEXT:    lui a1, 182
+; RV32IXQCIBM-NEXT:    addi a1, a1, -1326
+; RV32IXQCIBM-NEXT:    qc.insb a0, a1, 20, 0
+; RV32IXQCIBM-NEXT:    ret
+;
+; RV32IXQCIBMZBS-LABEL: test6:
+; RV32IXQCIBMZBS:       # %bb.0:
+; RV32IXQCIBMZBS-NEXT:    lui a1, 182
+; RV32IXQCIBMZBS-NEXT:    addi a1, a1, -1326
+; RV32IXQCIBMZBS-NEXT:    qc.insb a0, a1, 20, 0
+; RV32IXQCIBMZBS-NEXT:    ret
+  %1 = and i32 %a, 4293918720 ; 0xfff00000
+  %2 = or i32 %1, 744146      ; 0x000b5ad2
+  ret i32 %2
+}
+
+define i32 @test7(i32 %a) {
+; RV32I-LABEL: test7:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    lui a1, 1048320
+; RV32I-NEXT:    addi a1, a1, 1
+; RV32I-NEXT:    and a0, a0, a1
+; RV32I-NEXT:    lui a1, 182
+; RV32I-NEXT:    addi a1, a1, -1326
+; RV32I-NEXT:    or a0, a0, a1
+; RV32I-NEXT:    ret
+;
+; RV32IXQCIBM-LABEL: test7:
+; RV32IXQCIBM:       # %bb.0:
+; RV32IXQCIBM-NEXT:    lui a1, 91
+; RV32IXQCIBM-NEXT:    addi a1, a1, -663
+; RV32IXQCIBM-NEXT:    qc.insb a0, a1, 19, 1
+; RV32IXQCIBM-NEXT:    ret
+;
+; RV32IXQCIBMZBS-LABEL: test7:
+; RV32IXQCIBMZBS:       # %bb.0:
+; RV32IXQCIBMZBS-NEXT:    lui a1, 91
+; RV32IXQCIBMZBS-NEXT:    addi a1, a1, -663
+; RV32IXQCIBMZBS-NEXT:    qc.insb a0, a1, 19, 1
+; RV32IXQCIBMZBS-NEXT:    ret
+  %1 = and i32 %a, 4293918721 ; 0xfff00001
+  %2 = or i32 %1, 744146      ; 0x000b5ad2
+  ret i32 %2
+}
+
+define i64 @test8(i64 %a) {
+; RV32I-LABEL: test8:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    lui a2, 1044480
+; RV32I-NEXT:    zext.b a0, a0
+; RV32I-NEXT:    and a1, a1, a2
+; RV32I-NEXT:    lui a2, 496944
+; RV32I-NEXT:    or a0, a0, a2
+; RV32I-NEXT:    lui a2, 9
+; RV32I-NEXT:    addi a2, a2, -170
+; RV32I-NEXT:    or a1, a1, a2
+; RV32I-NEXT:    ret
+;
+; RV32IXQCIBM-LABEL: test8:
+; RV32IXQCIBM:       # %bb.0:
+; RV32IXQCIBM-NEXT:    lui a2, 1941
+; RV32IXQCIBM-NEXT:    addi a2, a2, 768
+; RV32IXQCIBM-NEXT:    qc.insb a0, a2, 24, 8
+; RV32IXQCIBM-NEXT:    lui a2, 9
+; RV32IXQCIBM-NEXT:    addi a2, a2, -170
+; RV32IXQCIBM-NEXT:    qc.insb a1, a2, 24, 0
+; RV32IXQCIBM-NEXT:    ret
+;
+; RV32IXQCIBMZBS-LABEL: test8:
+; RV32IXQCIBMZBS:       # %bb.0:
+; RV32IXQCIBMZBS-NEXT:    lui a2, 1941
+; RV32IXQCIBMZBS-NEXT:    addi a2, a2, 768
+; RV32IXQCIBMZBS-NEXT:    qc.insb a0, a2, 24, 8
+; RV32IXQCIBMZBS-NEXT:    lui a2, 9
+; RV32IXQCIBMZBS-NEXT:    addi a2, a2, -170
+; RV32IXQCIBMZBS-NEXT:    qc.insb a1, a2, 24, 0
+; RV32IXQCIBMZBS-NEXT:    ret
+  %1 = and i64 %a, -72057594037927681 ; 0xff000000000000ff
+  %2 = or i64 %1, 157601565442048     ; 0x00008f5679530000
+  ret i64 %2
+}

>From 0013100ccfda6ab2e00f9afd7051a7735cdad7a3 Mon Sep 17 00:00:00 2001
From: Sudharsan Veeravalli <quic_svs at quicinc.com>
Date: Mon, 18 Aug 2025 10:38:07 +0530
Subject: [PATCH 2/3] Correct hex value in comment

---
 llvm/test/CodeGen/RISCV/xqcibm-insert.ll | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/test/CodeGen/RISCV/xqcibm-insert.ll b/llvm/test/CodeGen/RISCV/xqcibm-insert.ll
index 0039580bcd53f..2a954ae1eb297 100644
--- a/llvm/test/CodeGen/RISCV/xqcibm-insert.ll
+++ b/llvm/test/CodeGen/RISCV/xqcibm-insert.ll
@@ -184,7 +184,7 @@ define i32 @test2(i32 %a) {
 ; RV32IXQCIBMZBS-NEXT:    qc.insbi a0, 10, 4, 22
 ; RV32IXQCIBMZBS-NEXT:    ret
   %1 = and i32 %a, -62914561 ; 0xfc3fffff
-  %2 = or i32 %1, 41943040   ; 0x06400000
+  %2 = or i32 %1, 41943040   ; 0x02800000
   ret i32 %2
 }
 

>From d21e3b3fe7dd874e583e5e0a38dca9666161ade4 Mon Sep 17 00:00:00 2001
From: Sudharsan Veeravalli <quic_svs at quicinc.com>
Date: Mon, 18 Aug 2025 12:52:32 +0530
Subject: [PATCH 3/3] Use sd_match and APInt

---
 llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp | 32 ++++++++-------------
 1 file changed, 12 insertions(+), 20 deletions(-)

diff --git a/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp b/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp
index 33648c0c11ebf..e52de9fc1a169 100644
--- a/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp
@@ -727,31 +727,24 @@ bool RISCVDAGToDAGISel::tryBitfieldInsertOpFromOrAndImm(SDNode *Node) {
   if (!Subtarget->hasVendorXqcibm())
     return false;
 
-  auto *N1C = dyn_cast<ConstantSDNode>(Node->getOperand(1));
-  if (!N1C)
-    return false;
-
-  SDValue And = Node->getOperand(0);
-
-  if (And.getOpcode() != ISD::AND)
-    return false;
+  using namespace SDPatternMatch;
 
-  auto *N2C = dyn_cast<ConstantSDNode>(And->getOperand(1));
-  if (!And.hasOneUse() || !N2C)
+  SDValue And;
+  APInt MaskImm, OrImm;
+  if (!sd_match(Node, m_Or(m_OneUse(m_And(m_Value(And), m_ConstInt(MaskImm))),
+                           m_ConstInt(OrImm))))
     return false;
 
-  int32_t OrImm = N1C->getSExtValue();
-
   // Compute the Known Zero for the AND as this allows us to catch more general
   // cases than just looking for AND with imm.
-  KnownBits Known = CurDAG->computeKnownBits(And);
+  KnownBits Known = CurDAG->computeKnownBits(Node->getOperand(0));
 
   // Non-zero in the sense that they're not provably zero, which is the key
   // point if we want to use this value.
-  uint64_t NotKnownZero = (~Known.Zero).getZExtValue();
+  APInt NotKnownZero = ~Known.Zero;
 
   // The KnownZero mask must be a shifted mask (e.g., 1110..011, 11100..00).
-  if (!isShiftedMask_32(Known.Zero.getZExtValue()))
+  if (!Known.Zero.isShiftedMask())
     return false;
 
   // The bits being inserted must only set those bits that are known to be zero.
@@ -764,14 +757,14 @@ bool RISCVDAGToDAGISel::tryBitfieldInsertOpFromOrAndImm(SDNode *Node) {
   // QC_INSB(I) dst, src, #width, #shamt.
   MVT VT = Node->getSimpleValueType(0);
   unsigned BitWidth = VT.getSizeInBits();
-  const unsigned ShAmt = llvm::countr_one(NotKnownZero);
-  const unsigned Width = BitWidth - APInt(BitWidth, NotKnownZero).popcount();
+  const unsigned ShAmt = llvm::countr_one(NotKnownZero.getZExtValue());
+  const unsigned Width = BitWidth - NotKnownZero.popcount();
 
   SDLoc DL(Node);
   SDValue ImmNode;
   auto Opc = RISCV::QC_INSB;
 
-  int32_t LIImm = OrImm >> ShAmt;
+  int32_t LIImm = OrImm.getSExtValue() >> ShAmt;
 
   if (isInt<5>(LIImm)) {
     Opc = RISCV::QC_INSBI;
@@ -780,8 +773,7 @@ bool RISCVDAGToDAGISel::tryBitfieldInsertOpFromOrAndImm(SDNode *Node) {
     ImmNode = selectImm(CurDAG, DL, MVT::i32, LIImm, *Subtarget);
   }
 
-  SDValue Ops[] = {And.getOperand(0), ImmNode,
-                   CurDAG->getTargetConstant(Width, DL, VT),
+  SDValue Ops[] = {And, ImmNode, CurDAG->getTargetConstant(Width, DL, VT),
                    CurDAG->getTargetConstant(ShAmt, DL, VT)};
   SDNode *BitIns = CurDAG->getMachineNode(Opc, DL, VT, Ops);
   ReplaceNode(Node, BitIns);



More information about the llvm-commits mailing list