[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