[llvm] [RISCV] Generate Xqcilsm QC_SETWMI store multiple instruction (PR #172438)

Sudharsan Veeravalli via llvm-commits llvm-commits at lists.llvm.org
Tue Dec 16 01:21:41 PST 2025


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

>From 8160943be0fbfb812285d375f5b322549f43686c Mon Sep 17 00:00:00 2001
From: Sudharsan Veeravalli <quic_svs at quicinc.com>
Date: Tue, 16 Dec 2025 13:42:57 +0530
Subject: [PATCH 1/2] Setwmi

---
 .../Target/RISCV/RISCVLoadStoreOptimizer.cpp  | 43 +++++++++------
 llvm/test/CodeGen/RISCV/xqcilsm-lwmi-swmi.mir | 53 +++++++++++++++++++
 llvm/test/CodeGen/RISCV/xqcilsm-memset.ll     | 36 +++++--------
 3 files changed, 93 insertions(+), 39 deletions(-)

diff --git a/llvm/lib/Target/RISCV/RISCVLoadStoreOptimizer.cpp b/llvm/lib/Target/RISCV/RISCVLoadStoreOptimizer.cpp
index 5eef1a998719f..6a0915126b055 100644
--- a/llvm/lib/Target/RISCV/RISCVLoadStoreOptimizer.cpp
+++ b/llvm/lib/Target/RISCV/RISCVLoadStoreOptimizer.cpp
@@ -214,31 +214,41 @@ bool RISCVLoadStoreOpt::tryConvertToXqcilsmLdStPair(
     std::swap(Off1, Off2);
   }
 
+  if (!isShiftedUInt<5, 2>(Off1) || (Off2 - Off1 != 4))
+    return false;
+
   Register StartReg = FirstOp0.getReg();
   Register NextReg = SecondOp0.getReg();
 
-  if (StartReg == RISCV::X0 || NextReg == RISCV::X0)
-    return false;
-
-  // If the base reg gets overwritten by one of the loads then bail out.
-  if (Opc == RISCV::LW && (StartReg == Base1 || NextReg == Base1))
+  // Only QC_SETWMI can have the StartReg as X0.
+  if (StartReg == RISCV::X0 && Opc != RISCV::SW)
     return false;
 
-  if (!isShiftedUInt<5, 2>(Off1) || (Off2 - Off1 != 4))
+  // If the base reg gets overwritten by one of the loads or the StartReg and
+  // NextReg are not consecutive then bail out.
+  if (Opc == RISCV::LW &&
+      ((StartReg == Base1 || NextReg == Base1) || NextReg != StartReg + 1))
     return false;
 
-  if (NextReg != StartReg + 1)
+  // For stores the regs need to be equal or consecutive.
+  if (NextReg != StartReg + 1 && NextReg != StartReg)
     return false;
 
-  unsigned XqciOpc = (Opc == RISCV::LW) ? RISCV::QC_LWMI : RISCV::QC_SWMI;
+  unsigned XqciOpc = (Opc == RISCV::LW)      ? RISCV::QC_LWMI
+                     : (NextReg == StartReg) ? RISCV::QC_SETWMI
+                                             : RISCV::QC_SWMI;
 
-  unsigned StartRegState = (Opc == RISCV::LW)
-                               ? static_cast<unsigned>(RegState::Define)
-                               : getKillRegState(FirstOp0.isKill());
+  unsigned StartRegState =
+      (XqciOpc == RISCV::QC_LWMI) ? static_cast<unsigned>(RegState::Define)
+      : (XqciOpc == RISCV::QC_SWMI)
+          ? getKillRegState(FirstOp0.isKill())
+          : getKillRegState(FirstOp0.isKill() || SecondOp0.isKill());
   unsigned NextRegState =
-      (Opc == RISCV::LW)
+      (XqciOpc == RISCV::QC_LWMI)
           ? static_cast<unsigned>(RegState::ImplicitDefine)
-          : (RegState::Implicit | getKillRegState(SecondOp0.isKill()));
+      : (XqciOpc == RISCV::QC_SWMI)
+          ? (RegState::Implicit | getKillRegState(SecondOp0.isKill()))
+          : 0;
 
   DebugLoc DL =
       First->getDebugLoc() ? First->getDebugLoc() : Second->getDebugLoc();
@@ -247,8 +257,11 @@ bool RISCVLoadStoreOpt::tryConvertToXqcilsmLdStPair(
       .addReg(Base1, getKillRegState(FirstOp1.isKill() || SecondOp1.isKill()))
       .addImm(2)
       .addImm(Off1)
-      .cloneMergedMemRefs({&*First, &*Second})
-      .addReg(NextReg, NextRegState);
+      .cloneMergedMemRefs({&*First, &*Second});
+
+  // Add the NextRegState only if it is not QC_SETWMI.
+  if (XqciOpc != RISCV::QC_SETWMI)
+    MIB.addReg(NextReg, NextRegState);
 
   First->getParent()->insert(First, MIB);
   First->removeFromParent();
diff --git a/llvm/test/CodeGen/RISCV/xqcilsm-lwmi-swmi.mir b/llvm/test/CodeGen/RISCV/xqcilsm-lwmi-swmi.mir
index 396f67326a7ca..71bd8ce2df965 100644
--- a/llvm/test/CodeGen/RISCV/xqcilsm-lwmi-swmi.mir
+++ b/llvm/test/CodeGen/RISCV/xqcilsm-lwmi-swmi.mir
@@ -20,6 +20,9 @@
   define void @pair_if_not_adjacent() nounwind { ret void }
   define void @pair_if_not_adjacent_use() nounwind { ret void }
   define void @no_pair_if_not_adjacent_use() nounwind { ret void }
+  define void @pair_two_sw_into_qc_setwmi() nounwind { ret void }
+  define void @pair_two_sw_into_qc_setwmi_reversed() nounwind { ret void }
+  define void @pair_if_not_adjacent_setwmi() nounwind { ret void }
 ---
 name: pair_two_lw_into_qc_lwmi
 tracksRegLiveness: false
@@ -313,3 +316,53 @@ body: |
     PseudoRET
 
 ...
+---
+name: pair_two_sw_into_qc_setwmi
+tracksRegLiveness: false
+body: |
+  bb.0:
+    liveins: $x10, $x1
+    ; CHECK-LABEL: name: pair_two_sw_into_qc_setwmi
+    ; CHECK: liveins: $x10, $x1
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: QC_SETWMI $x1, $x10, 2, 20 :: (store (s32))
+    ; CHECK-NEXT: PseudoRET
+    SW $x1, $x10, 20 :: (store (s32), align 4)
+    SW $x1, $x10, 24 :: (store (s32), align 4)
+    PseudoRET
+
+...
+---
+name: pair_two_sw_into_qc_setwmi_reversed
+tracksRegLiveness: false
+body: |
+  bb.0:
+    liveins: $x10, $x2
+    ; CHECK-LABEL: name: pair_two_sw_into_qc_setwmi_reversed
+    ; CHECK: liveins: $x10, $x2
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: QC_SETWMI $x2, $x10, 2, 20 :: (store (s32))
+    ; CHECK-NEXT: PseudoRET
+    SW $x2, $x10, 24 :: (store (s32), align 4)
+    SW $x2, $x10, 20 :: (store (s32), align 4)
+    PseudoRET
+
+...
+---
+name: pair_if_not_adjacent_setwmi
+tracksRegLiveness: false
+body: |
+  bb.0:
+    liveins: $x10, $x1
+    ; CHECK-LABEL: name: pair_if_not_adjacent_setwmi
+    ; CHECK: liveins: $x10, $x1
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: QC_SETWMI $x1, $x10, 2, 20 :: (store (s32))
+    ; CHECK-NEXT: $x2 = ADDI $x2, 10
+    ; CHECK-NEXT: PseudoRET
+    SW $x1, $x10, 20 :: (store (s32), align 4)
+    $x2 = ADDI $x2, 10
+    SW $x1, $x10, 24 :: (store (s32), align 4)
+    PseudoRET
+
+...
diff --git a/llvm/test/CodeGen/RISCV/xqcilsm-memset.ll b/llvm/test/CodeGen/RISCV/xqcilsm-memset.ll
index 2fad19a653f1f..e0546a41779f7 100644
--- a/llvm/test/CodeGen/RISCV/xqcilsm-memset.ll
+++ b/llvm/test/CodeGen/RISCV/xqcilsm-memset.ll
@@ -130,8 +130,7 @@ define void @test2d(ptr nocapture %p) nounwind {
 ; RV32IXQCILSM-NEXT:    lui a3, 678490
 ; RV32IXQCILSM-NEXT:    addi a2, a2, 1445
 ; RV32IXQCILSM-NEXT:    addi a3, a3, 1445
-; RV32IXQCILSM-NEXT:    sw a3, 0(a0)
-; RV32IXQCILSM-NEXT:    sw a3, 4(a0)
+; RV32IXQCILSM-NEXT:    qc.setwmi a3, 2, 0(a0)
 ; RV32IXQCILSM-NEXT:    sh a2, 8(a0)
 ; RV32IXQCILSM-NEXT:    sb a1, 10(a0)
 ; RV32IXQCILSM-NEXT:    ret
@@ -380,8 +379,7 @@ define i64 @test6b_ll() nounwind {
 ; RV32IXQCILSM-LABEL: test6b_ll:
 ; RV32IXQCILSM:       # %bb.0: # %entry
 ; RV32IXQCILSM-NEXT:    addi sp, sp, -16
-; RV32IXQCILSM-NEXT:    sw zero, 8(sp)
-; RV32IXQCILSM-NEXT:    sw zero, 12(sp)
+; RV32IXQCILSM-NEXT:    qc.setwmi zero, 2, 8(sp)
 ; RV32IXQCILSM-NEXT:    lw a0, 8(sp)
 ; RV32IXQCILSM-NEXT:    lw a1, 12(sp)
 ; RV32IXQCILSM-NEXT:    addi sp, sp, 16
@@ -478,8 +476,7 @@ define i64 @test6c_ll() nounwind {
 ; RV32IXQCILSM-LABEL: test6c_ll:
 ; RV32IXQCILSM:       # %bb.0: # %entry
 ; RV32IXQCILSM-NEXT:    addi sp, sp, -16
-; RV32IXQCILSM-NEXT:    sw zero, 8(sp)
-; RV32IXQCILSM-NEXT:    sw zero, 12(sp)
+; RV32IXQCILSM-NEXT:    qc.setwmi zero, 2, 8(sp)
 ; RV32IXQCILSM-NEXT:    li a0, 0
 ; RV32IXQCILSM-NEXT:    li a1, 0
 ; RV32IXQCILSM-NEXT:    addi sp, sp, 16
@@ -504,8 +501,7 @@ define void @test7() nounwind {
 ; RV32IXQCILSM:       # %bb.0:
 ; RV32IXQCILSM-NEXT:    lui a0, %hi(arr1)
 ; RV32IXQCILSM-NEXT:    addi a0, a0, %lo(arr1)
-; RV32IXQCILSM-NEXT:    sw zero, 0(a0)
-; RV32IXQCILSM-NEXT:    sw zero, 4(a0)
+; RV32IXQCILSM-NEXT:    qc.setwmi zero, 2, 0(a0)
 ; RV32IXQCILSM-NEXT:    ret
   tail call void @llvm.memset.p0.i32(ptr align 4 @arr1, i8 0, i32 8, i1 false)
   ret void
@@ -543,10 +539,8 @@ define void @test7a_unalign() nounwind {
 ; RV32IXQCILSM-NEXT:    addi a0, a0, %lo(arr1)
 ; RV32IXQCILSM-NEXT:    li a1, -1
 ; RV32IXQCILSM-NEXT:    sb a1, 16(a0)
-; RV32IXQCILSM-NEXT:    sw a1, 0(a0)
-; RV32IXQCILSM-NEXT:    sw a1, 4(a0)
-; RV32IXQCILSM-NEXT:    sw a1, 8(a0)
-; RV32IXQCILSM-NEXT:    sw a1, 12(a0)
+; RV32IXQCILSM-NEXT:    qc.setwmi a1, 2, 0(a0)
+; RV32IXQCILSM-NEXT:    qc.setwmi a1, 2, 8(a0)
 ; RV32IXQCILSM-NEXT:    ret
 entry:
   tail call void @llvm.memset.p0.i32(ptr align 4 @arr1, i8 -1, i32 17, i1 false)
@@ -660,10 +654,8 @@ define void @test8() nounwind {
 ; RV32IXQCILSM:       # %bb.0: # %entry
 ; RV32IXQCILSM-NEXT:    lui a0, %hi(arr1)
 ; RV32IXQCILSM-NEXT:    addi a0, a0, %lo(arr1)
-; RV32IXQCILSM-NEXT:    sw zero, 0(a0)
-; RV32IXQCILSM-NEXT:    sw zero, 4(a0)
-; RV32IXQCILSM-NEXT:    sw zero, 8(a0)
-; RV32IXQCILSM-NEXT:    sw zero, 12(a0)
+; RV32IXQCILSM-NEXT:    qc.setwmi zero, 2, 0(a0)
+; RV32IXQCILSM-NEXT:    qc.setwmi zero, 2, 8(a0)
 ; RV32IXQCILSM-NEXT:    ret
 entry:
   tail call void @llvm.memset.p0.i32(ptr align 4 @arr1, i8 0, i32 16, i1 false)
@@ -689,14 +681,10 @@ define void @test9() nounwind {
 ; RV32IXQCILSM:       # %bb.0: # %entry
 ; RV32IXQCILSM-NEXT:    lui a0, %hi(arr1)
 ; RV32IXQCILSM-NEXT:    addi a0, a0, %lo(arr1)
-; RV32IXQCILSM-NEXT:    sw zero, 16(a0)
-; RV32IXQCILSM-NEXT:    sw zero, 20(a0)
-; RV32IXQCILSM-NEXT:    sw zero, 24(a0)
-; RV32IXQCILSM-NEXT:    sw zero, 28(a0)
-; RV32IXQCILSM-NEXT:    sw zero, 0(a0)
-; RV32IXQCILSM-NEXT:    sw zero, 4(a0)
-; RV32IXQCILSM-NEXT:    sw zero, 8(a0)
-; RV32IXQCILSM-NEXT:    sw zero, 12(a0)
+; RV32IXQCILSM-NEXT:    qc.setwmi zero, 2, 16(a0)
+; RV32IXQCILSM-NEXT:    qc.setwmi zero, 2, 24(a0)
+; RV32IXQCILSM-NEXT:    qc.setwmi zero, 2, 0(a0)
+; RV32IXQCILSM-NEXT:    qc.setwmi zero, 2, 8(a0)
 ; RV32IXQCILSM-NEXT:    ret
 entry:
   tail call void @llvm.memset.p0.i32(ptr align 4 @arr1, i8 0, i32 32, i1 false)

>From bedc15e41b1d77077117a4ffffd4b619b2c02582 Mon Sep 17 00:00:00 2001
From: Sudharsan Veeravalli <quic_svs at quicinc.com>
Date: Tue, 16 Dec 2025 14:49:41 +0530
Subject: [PATCH 2/2] Address comments

---
 .../Target/RISCV/RISCVLoadStoreOptimizer.cpp  | 61 ++++++++++---------
 llvm/test/CodeGen/RISCV/xqcilsm-lwmi-swmi.mir |  4 +-
 2 files changed, 35 insertions(+), 30 deletions(-)

diff --git a/llvm/lib/Target/RISCV/RISCVLoadStoreOptimizer.cpp b/llvm/lib/Target/RISCV/RISCVLoadStoreOptimizer.cpp
index 6a0915126b055..ff5894e1a657d 100644
--- a/llvm/lib/Target/RISCV/RISCVLoadStoreOptimizer.cpp
+++ b/llvm/lib/Target/RISCV/RISCVLoadStoreOptimizer.cpp
@@ -220,35 +220,41 @@ bool RISCVLoadStoreOpt::tryConvertToXqcilsmLdStPair(
   Register StartReg = FirstOp0.getReg();
   Register NextReg = SecondOp0.getReg();
 
-  // Only QC_SETWMI can have the StartReg as X0.
-  if (StartReg == RISCV::X0 && Opc != RISCV::SW)
-    return false;
+  unsigned XqciOpc;
+  unsigned StartRegState;
+  unsigned NextRegState = 0;
+  bool AddNextReg = true;
 
-  // If the base reg gets overwritten by one of the loads or the StartReg and
-  // NextReg are not consecutive then bail out.
-  if (Opc == RISCV::LW &&
-      ((StartReg == Base1 || NextReg == Base1) || NextReg != StartReg + 1))
-    return false;
+  if (Opc == RISCV::LW) {
 
-  // For stores the regs need to be equal or consecutive.
-  if (NextReg != StartReg + 1 && NextReg != StartReg)
-    return false;
+    if (StartReg == RISCV::X0)
+      return false;
+
+    // If the base reg gets overwritten by one of the loads bail out.
+    if (StartReg == Base1 || NextReg == Base1)
+      return false;
+
+    // The registers need to be consecutive.
+    if (NextReg != StartReg + 1)
+      return false;
 
-  unsigned XqciOpc = (Opc == RISCV::LW)      ? RISCV::QC_LWMI
-                     : (NextReg == StartReg) ? RISCV::QC_SETWMI
-                                             : RISCV::QC_SWMI;
-
-  unsigned StartRegState =
-      (XqciOpc == RISCV::QC_LWMI) ? static_cast<unsigned>(RegState::Define)
-      : (XqciOpc == RISCV::QC_SWMI)
-          ? getKillRegState(FirstOp0.isKill())
-          : getKillRegState(FirstOp0.isKill() || SecondOp0.isKill());
-  unsigned NextRegState =
-      (XqciOpc == RISCV::QC_LWMI)
-          ? static_cast<unsigned>(RegState::ImplicitDefine)
-      : (XqciOpc == RISCV::QC_SWMI)
-          ? (RegState::Implicit | getKillRegState(SecondOp0.isKill()))
-          : 0;
+    XqciOpc = RISCV::QC_LWMI;
+    StartRegState = static_cast<unsigned>(RegState::Define);
+    NextRegState = static_cast<unsigned>(RegState::ImplicitDefine);
+  } else {
+    assert(Opc == RISCV::SW && "Expected a SW instruction");
+    if (StartReg == NextReg) {
+      XqciOpc = RISCV::QC_SETWMI;
+      StartRegState = getKillRegState(FirstOp0.isKill() || SecondOp0.isKill());
+      AddNextReg = false;
+    } else if (NextReg == StartReg + 1) {
+      XqciOpc = RISCV::QC_SWMI;
+      StartRegState = getKillRegState(FirstOp0.isKill());
+      NextRegState = RegState::Implicit | getKillRegState(SecondOp0.isKill());
+    } else {
+      return false;
+    }
+  }
 
   DebugLoc DL =
       First->getDebugLoc() ? First->getDebugLoc() : Second->getDebugLoc();
@@ -259,8 +265,7 @@ bool RISCVLoadStoreOpt::tryConvertToXqcilsmLdStPair(
       .addImm(Off1)
       .cloneMergedMemRefs({&*First, &*Second});
 
-  // Add the NextRegState only if it is not QC_SETWMI.
-  if (XqciOpc != RISCV::QC_SETWMI)
+  if (AddNextReg)
     MIB.addReg(NextReg, NextRegState);
 
   First->getParent()->insert(First, MIB);
diff --git a/llvm/test/CodeGen/RISCV/xqcilsm-lwmi-swmi.mir b/llvm/test/CodeGen/RISCV/xqcilsm-lwmi-swmi.mir
index 71bd8ce2df965..126c1fd442000 100644
--- a/llvm/test/CodeGen/RISCV/xqcilsm-lwmi-swmi.mir
+++ b/llvm/test/CodeGen/RISCV/xqcilsm-lwmi-swmi.mir
@@ -357,12 +357,12 @@ body: |
     ; CHECK-LABEL: name: pair_if_not_adjacent_setwmi
     ; CHECK: liveins: $x10, $x1
     ; CHECK-NEXT: {{  $}}
-    ; CHECK-NEXT: QC_SETWMI $x1, $x10, 2, 20 :: (store (s32))
+    ; CHECK-NEXT: QC_SETWMI killed $x1, $x10, 2, 20 :: (store (s32))
     ; CHECK-NEXT: $x2 = ADDI $x2, 10
     ; CHECK-NEXT: PseudoRET
     SW $x1, $x10, 20 :: (store (s32), align 4)
     $x2 = ADDI $x2, 10
-    SW $x1, $x10, 24 :: (store (s32), align 4)
+    SW killed $x1, $x10, 24 :: (store (s32), align 4)
     PseudoRET
 
 ...



More information about the llvm-commits mailing list