[llvm] [RISCV] Generate Xqcilsm multi-word load/store instructions for three or more words (PR #174789)

Sudharsan Veeravalli via llvm-commits llvm-commits at lists.llvm.org
Wed Jan 7 08:24:41 PST 2026


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

>From b5238c514f4c5fe9c33464c0c46d9330731ed6a8 Mon Sep 17 00:00:00 2001
From: Sudharsan Veeravalli <quic_svs at quicinc.com>
Date: Wed, 7 Jan 2026 20:43:19 +0530
Subject: [PATCH 1/2] Generate Xqcilsm instructions for 3 or more loads/stores

---
 .../Target/RISCV/RISCVLoadStoreOptimizer.cpp  | 200 ++++++++++++++++++
 .../RISCV/xqcilsm-lwmi-swmi-multiple.mir      | 159 ++++++++++++++
 llvm/test/CodeGen/RISCV/xqcilsm-memset.ll     |  12 +-
 3 files changed, 363 insertions(+), 8 deletions(-)
 create mode 100644 llvm/test/CodeGen/RISCV/xqcilsm-lwmi-swmi-multiple.mir

diff --git a/llvm/lib/Target/RISCV/RISCVLoadStoreOptimizer.cpp b/llvm/lib/Target/RISCV/RISCVLoadStoreOptimizer.cpp
index 63b50913ce74b..4358e8b8a30bd 100644
--- a/llvm/lib/Target/RISCV/RISCVLoadStoreOptimizer.cpp
+++ b/llvm/lib/Target/RISCV/RISCVLoadStoreOptimizer.cpp
@@ -26,6 +26,7 @@
 
 #include "RISCV.h"
 #include "RISCVTargetMachine.h"
+#include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/Statistic.h"
 #include "llvm/Analysis/AliasAnalysis.h"
 #include "llvm/CodeGen/Passes.h"
@@ -73,6 +74,7 @@ struct RISCVLoadStoreOpt : public MachineFunctionPass {
   bool tryConvertToXqcilsmLdStPair(MachineFunction *MF,
                                    MachineBasicBlock::iterator First,
                                    MachineBasicBlock::iterator Second);
+  bool tryConvertToXqcilsmMultiLdSt(MachineBasicBlock::iterator &First);
   bool tryConvertToMIPSLdStPair(MachineFunction *MF,
                                 MachineBasicBlock::iterator First,
                                 MachineBasicBlock::iterator Second);
@@ -163,6 +165,13 @@ bool RISCVLoadStoreOpt::tryToPairLdStInst(MachineBasicBlock::iterator &MBBI) {
   if (!TII->isLdStSafeToPair(MI, TRI))
     return false;
 
+  // If Xqcilsm is available, first try to form a multi-instruction group (>2).
+  const RISCVSubtarget &STI = MI.getMF()->getSubtarget<RISCVSubtarget>();
+  if (!STI.is64Bit() && STI.hasVendorXqcilsm()) {
+    if (tryConvertToXqcilsmMultiLdSt(MBBI))
+      return true;
+  }
+
   // Look ahead for a pairable instruction.
   MachineBasicBlock::iterator E = MI.getParent()->end();
   bool MergeForward;
@@ -174,6 +183,197 @@ bool RISCVLoadStoreOpt::tryToPairLdStInst(MachineBasicBlock::iterator &MBBI) {
   return false;
 }
 
+// Convert set of 3 or more LW/SW instructions to QC_LWMI/QC_SWMI/QC_SETWMI.
+// For now this only handles consecutive loads and stores traversing the basic
+// block top-down.
+// TODO: Traverse the basic block bottom-up as well.
+bool RISCVLoadStoreOpt::tryConvertToXqcilsmMultiLdSt(
+    MachineBasicBlock::iterator &FirstIt) {
+  MachineInstr &FirstMI = *FirstIt;
+  MachineFunction *MF = FirstMI.getMF();
+  const RISCVSubtarget &STI = MF->getSubtarget<RISCVSubtarget>();
+
+  if (STI.is64Bit() || !STI.hasVendorXqcilsm())
+    return false;
+
+  unsigned Opc = FirstMI.getOpcode();
+  if (Opc != RISCV::LW && Opc != RISCV::SW)
+    return false;
+
+  // Require simple reg+imm addressing.
+  const MachineOperand &BaseOp = FirstMI.getOperand(1);
+  const MachineOperand &OffOp = FirstMI.getOperand(2);
+  if (!BaseOp.isReg() || !OffOp.isImm())
+    return false;
+
+  Register Base = BaseOp.getReg();
+  int64_t BaseOff = OffOp.getImm();
+
+  const MachineMemOperand *MMO = *FirstMI.memoperands_begin();
+  Align MMOAlign = MMO->getAlign();
+  if (MMOAlign < Align(4))
+    return false;
+
+  if (!isShiftedUInt<5, 2>(BaseOff))
+    return false;
+
+  Register StartReg = FirstMI.getOperand(0).getReg();
+  bool IsLoad = (Opc == RISCV::LW);
+
+  // Load rd cannot be x0 and must not clobber the base register.
+  if (IsLoad) {
+    if (StartReg == RISCV::X0)
+      return false;
+    if (StartReg == Base)
+      return false;
+  }
+
+  // Collect a set of consecutive matching instructions.
+  SmallVector<MachineInstr *, 8> Group;
+  Group.push_back(&FirstMI);
+
+  MachineBasicBlock::iterator E = FirstIt->getParent()->end();
+  MachineBasicBlock::iterator It = next_nodbg(FirstIt, E);
+  int64_t ExpectedOff = BaseOff + 4;
+  unsigned Index = 1;
+  enum class StoreMode { Unknown, Setwmi, Swmi };
+  StoreMode SMode = StoreMode::Unknown;
+
+  while (It != E) {
+    MachineInstr &MI = *It;
+
+    if (!TII->isPairableLdStInstOpc(MI.getOpcode()))
+      break;
+    if (MI.getOpcode() != Opc)
+      break;
+    if (!TII->isLdStSafeToPair(MI, TRI))
+      break;
+
+    const MachineOperand &BaseMIOp = MI.getOperand(1);
+    const MachineOperand &OffsetMIOp = MI.getOperand(2);
+    if (!BaseMIOp.isReg() || !OffsetMIOp.isImm())
+      break;
+    if (BaseMIOp.getReg() != Base)
+      break;
+    int64_t Off = OffsetMIOp.getImm();
+    if (Off != ExpectedOff)
+      break;
+
+    Register Reg = MI.getOperand(0).getReg();
+    if (IsLoad) {
+      // For loads, require consecutive destination registers.
+      if (Reg != StartReg + Index)
+        break;
+      if (Reg == Base)
+        break;
+    } else {
+      // For stores, decide mode based on the second instruction and then
+      // enforce the same for the rest.
+      if (SMode == StoreMode::Unknown) {
+        if (Reg == StartReg)
+          SMode = StoreMode::Setwmi;
+        else if (Reg == StartReg + 1)
+          SMode = StoreMode::Swmi;
+        else
+          break;
+      } else if (SMode == StoreMode::Setwmi) {
+        if (Reg != StartReg)
+          break;
+      } else {
+        if (Reg != StartReg + Index)
+          break;
+      }
+    }
+
+    // Passed checks, extend the group.
+    Group.push_back(&MI);
+    ++Index;
+    ExpectedOff += 4;
+    It = next_nodbg(It, E);
+  }
+
+  // We only handle more than 2 here. Pairs are handled in
+  // tryConvertToXqcilsmLdStPair.
+  unsigned Len = Group.size();
+  if (Len <= 2)
+    return false;
+  if (!isUInt<5>(Len))
+    return false;
+
+  unsigned NewOpc;
+  unsigned StartRegState;
+  bool AddImplicitRegs = false;
+
+  if (IsLoad) {
+    NewOpc = RISCV::QC_LWMI;
+    StartRegState = static_cast<unsigned>(RegState::Define);
+    AddImplicitRegs = true;
+  } else {
+    if (SMode == StoreMode::Setwmi) {
+      NewOpc = RISCV::QC_SETWMI;
+      // Kill if any of the individual stores killed the reg.
+      bool StartKill = false;
+      for (MachineInstr *MI : Group)
+        StartKill |= MI->getOperand(0).isKill();
+      StartRegState = getKillRegState(StartKill);
+      AddImplicitRegs = false;
+    } else {
+      // SWMI requires consecutive source regs and rd != x0.
+      if (StartReg == RISCV::X0)
+        return false;
+      NewOpc = RISCV::QC_SWMI;
+      StartRegState = getKillRegState(Group.front()->getOperand(0).isKill());
+      AddImplicitRegs = true;
+    }
+  }
+
+  // Aggregate kill on base.
+  bool BaseKill = false;
+  for (MachineInstr *MI : Group)
+    BaseKill |= MI->getOperand(1).isKill();
+
+  // Build the new instruction.
+  DebugLoc DL = FirstMI.getDebugLoc();
+  if (!DL)
+    DL = Group.back()->getDebugLoc();
+  MachineInstrBuilder MIB = BuildMI(*MF, DL, TII->get(NewOpc));
+  MIB.addReg(StartReg, StartRegState)
+      .addReg(Base, getKillRegState(BaseKill))
+      .addImm(Len)
+      .addImm(BaseOff);
+
+  // Merge memory references from all merged instructions.
+  SmallVector<const MachineInstr *, 8> ConstGroup;
+  ConstGroup.reserve(Group.size());
+  for (MachineInstr *MI : Group)
+    ConstGroup.push_back(MI);
+  MIB.cloneMergedMemRefs(ConstGroup);
+
+  if (AddImplicitRegs) {
+    // Add implicit operands for the additional registers.
+    for (unsigned i = 1; i < Len; ++i) {
+      Register R = StartReg + i;
+      unsigned State = 0;
+      if (IsLoad)
+        State = static_cast<unsigned>(RegState::ImplicitDefine);
+      else
+        State = RegState::Implicit |
+                getKillRegState(Group[i]->getOperand(0).isKill());
+      MIB.addReg(R, State);
+    }
+  }
+
+  // Insert before the first instruction and remove all in the group.
+  MachineBasicBlock *MBB = FirstIt->getParent();
+  MachineBasicBlock::iterator NewIt = MBB->insert(FirstIt, MIB);
+  for (MachineInstr *MI : Group)
+    MI->removeFromParent();
+
+  // Advance the cursor to the next non-debug instruction after the group.
+  FirstIt = next_nodbg(NewIt, MBB->end());
+  return true;
+}
+
 bool RISCVLoadStoreOpt::tryConvertToXqcilsmLdStPair(
     MachineFunction *MF, MachineBasicBlock::iterator First,
     MachineBasicBlock::iterator Second) {
diff --git a/llvm/test/CodeGen/RISCV/xqcilsm-lwmi-swmi-multiple.mir b/llvm/test/CodeGen/RISCV/xqcilsm-lwmi-swmi-multiple.mir
new file mode 100644
index 0000000000000..0bda7914836f1
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/xqcilsm-lwmi-swmi-multiple.mir
@@ -0,0 +1,159 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
+# RUN: llc -mtriple=riscv32 -mattr=+xqcilsm -run-pass=riscv-load-store-opt %s -o - | FileCheck %s
+
+--- |
+
+  define void @lwmi() { ret void }
+  define void @lwmi_x0() { ret void }
+  define void @lwmi_nonconsecutive() { ret void }
+  define void @lwmi_misaligned() { ret void }
+  define void @swmi() { ret void }
+  define void @swmi_x0() { ret void }
+  define void @swmi_nonconsecutive() { ret void }
+  define void @setwmi() { ret void }
+...
+---
+name:            lwmi
+body:             |
+  bb.0:
+    liveins: $x10
+    ; CHECK-LABEL: name: lwmi
+    ; CHECK: liveins: $x10
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: $x28 = QC_LWMI $x10, 4, 0, implicit-def $x29, implicit-def $x30, implicit-def $x31 :: (load (s32))
+    ; CHECK-NEXT: $x10 = ADD $x28, $x29
+    ; CHECK-NEXT: $x10 = ADD $x10, $x30
+    ; CHECK-NEXT: $x10 = ADD $x10, $x31
+    ; CHECK-NEXT: PseudoRET
+    $x28 = LW $x10, 0 :: (load (s32))
+    $x29 = LW $x10, 4 :: (load (s32))
+    $x30 = LW $x10, 8 :: (load (s32))
+    $x31 = LW $x10, 12 :: (load (s32))
+    $x10 = ADD $x28, $x29
+    $x10 = ADD $x10, $x30
+    $x10 = ADD $x10, $x31
+    PseudoRET
+...
+---
+name:            lwmi_x0
+body:             |
+  bb.0:
+    liveins: $x10
+    ; CHECK-LABEL: name: lwmi_x0
+    ; CHECK: liveins: $x10
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: $x0 = LW $x10, 0 :: (load (s32))
+    ; CHECK-NEXT: $x1 = QC_LWMI killed $x10, 3, 4, implicit-def $x2, implicit-def $x3 :: (load (s32))
+    ; CHECK-NEXT: PseudoRET
+    $x0 = LW $x10, 0 :: (load (s32))
+    $x1 = LW $x10, 4 :: (load (s32))
+    $x2 = LW $x10, 8 :: (load (s32))
+    $x3 = LW killed $x10, 12 :: (load (s32))
+    PseudoRET
+...
+---
+name:            lwmi_nonconsecutive
+body:             |
+  bb.0:
+    liveins: $x10
+    ; CHECK-LABEL: name: lwmi_nonconsecutive
+    ; CHECK: liveins: $x10
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: $x28 = LW $x10, 0 :: (load (s32))
+    ; CHECK-NEXT: $x30 = LW $x10, 4 :: (load (s32))
+    ; CHECK-NEXT: $x29 = LW $x10, 8 :: (load (s32))
+    ; CHECK-NEXT: $x31 = LW $x10, 12 :: (load (s32))
+    ; CHECK-NEXT: PseudoRET
+    $x28 = LW $x10, 0 :: (load (s32))
+    $x29 = LW $x10, 8 :: (load (s32))
+    $x30 = LW $x10, 4 :: (load (s32))
+    $x31 = LW $x10, 12 :: (load (s32))
+    PseudoRET
+...
+---
+name:            lwmi_misaligned
+body:             |
+  bb.0:
+    liveins: $x10
+    ; CHECK-LABEL: name: lwmi_misaligned
+    ; CHECK: liveins: $x10
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: $x28 = LW $x10, 2 :: (load (s32))
+    ; CHECK-NEXT: $x29 = LW $x10, 6 :: (load (s32))
+    ; CHECK-NEXT: $x30 = LW $x10, 10 :: (load (s32))
+    ; CHECK-NEXT: PseudoRET
+    $x28 = LW $x10, 2 :: (load (s32))
+    $x29 = LW $x10, 6 :: (load (s32))
+    $x30 = LW $x10, 10 :: (load (s32))
+    PseudoRET
+...
+---
+name:            swmi
+body:             |
+  bb.0:
+    liveins: $x10
+    ; CHECK-LABEL: name: swmi
+    ; CHECK: liveins: $x10
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: QC_SWMI killed $x28, $x10, 4, 0, implicit killed $x29, implicit $x30, implicit killed $x31 :: (store (s32))
+    ; CHECK-NEXT: PseudoRET
+    SW killed $x28, $x10, 0 :: (store (s32))
+    SW killed $x29, $x10, 4 :: (store (s32))
+    SW $x30, $x10, 8 :: (store (s32))
+    SW killed $x31, $x10, 12 :: (store (s32))
+    PseudoRET
+...
+---
+name:            swmi_x0
+body:             |
+  bb.0:
+    liveins: $x10
+    ; CHECK-LABEL: name: swmi_x0
+    ; CHECK: liveins: $x10
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: SW $x0, $x10, 0 :: (store (s32))
+    ; CHECK-NEXT: QC_SWMI $x1, $x10, 3, 4, implicit $x2, implicit $x3 :: (store (s32))
+    ; CHECK-NEXT: PseudoRET
+    SW $x0, $x10, 0 :: (store (s32))
+    SW $x1, $x10, 4 :: (store (s32))
+    SW $x2, $x10, 8 :: (store (s32))
+    SW $x3, $x10, 12 :: (store (s32))
+    PseudoRET
+...
+---
+name:            swmi_nonconsecutive
+body:             |
+  bb.0:
+    liveins: $x10
+    ; CHECK-LABEL: name: swmi_nonconsecutive
+    ; CHECK: liveins: $x10
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: QC_SWMI $x28, $x10, 2, 0, implicit killed $x29 :: (store (s32))
+    ; CHECK-NEXT: $x11 = ADDI $x28, 1
+    ; CHECK-NEXT: QC_SWMI $x30, $x10, 2, 8, implicit killed $x31 :: (store (s32))
+    ; CHECK-NEXT: $x12 = ADDI $x30, 1
+    ; CHECK-NEXT: PseudoRET
+    SW $x28, $x10, 0 :: (store (s32))
+    $x11 = ADDI $x28, 1
+    SW killed $x29, $x10, 4 :: (store (s32))
+    SW $x30, $x10, 8 :: (store (s32))
+    $x12 = ADDI $x30, 1
+    SW killed $x31, $x10, 12 :: (store (s32))
+    PseudoRET
+...
+---
+name:            setwmi
+body:             |
+  bb.0:
+    liveins: $x10
+    ; CHECK-LABEL: name: setwmi
+    ; CHECK: liveins: $x10
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: QC_SETWMI $x0, $x10, 4, 0 :: (store (s32))
+    ; CHECK-NEXT: PseudoRET
+    SW $x0, $x10, 0 :: (store (s32))
+    SW $x0, $x10, 4 :: (store (s32))
+    SW $x0, $x10, 8 :: (store (s32))
+    SW $x0, $x10, 12 :: (store (s32))
+    PseudoRET
+...
diff --git a/llvm/test/CodeGen/RISCV/xqcilsm-memset.ll b/llvm/test/CodeGen/RISCV/xqcilsm-memset.ll
index 399d6066c3366..e380122cf6efe 100644
--- a/llvm/test/CodeGen/RISCV/xqcilsm-memset.ll
+++ b/llvm/test/CodeGen/RISCV/xqcilsm-memset.ll
@@ -539,8 +539,7 @@ 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:    qc.setwmi a1, 2, 0(a0)
-; RV32IXQCILSM-NEXT:    qc.setwmi a1, 2, 8(a0)
+; RV32IXQCILSM-NEXT:    qc.setwmi a1, 4, 0(a0)
 ; RV32IXQCILSM-NEXT:    ret
 entry:
   tail call void @llvm.memset.p0.i32(ptr align 4 @arr1, i8 -1, i32 17, i1 false)
@@ -654,8 +653,7 @@ define void @test8() nounwind {
 ; RV32IXQCILSM:       # %bb.0: # %entry
 ; RV32IXQCILSM-NEXT:    lui a0, %hi(arr1)
 ; RV32IXQCILSM-NEXT:    addi a0, a0, %lo(arr1)
-; RV32IXQCILSM-NEXT:    qc.setwmi zero, 2, 0(a0)
-; RV32IXQCILSM-NEXT:    qc.setwmi zero, 2, 8(a0)
+; RV32IXQCILSM-NEXT:    qc.setwmi zero, 4, 0(a0)
 ; RV32IXQCILSM-NEXT:    ret
 entry:
   tail call void @llvm.memset.p0.i32(ptr align 4 @arr1, i8 0, i32 16, i1 false)
@@ -681,10 +679,8 @@ define void @test9() nounwind {
 ; RV32IXQCILSM:       # %bb.0: # %entry
 ; RV32IXQCILSM-NEXT:    lui a0, %hi(arr1)
 ; RV32IXQCILSM-NEXT:    addi a0, a0, %lo(arr1)
-; 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:    qc.setwmi zero, 4, 16(a0)
+; RV32IXQCILSM-NEXT:    qc.setwmi zero, 4, 0(a0)
 ; RV32IXQCILSM-NEXT:    ret
 entry:
   tail call void @llvm.memset.p0.i32(ptr align 4 @arr1, i8 0, i32 32, i1 false)

>From cd3c4e162a5410f168b99f90813925866dcbd162 Mon Sep 17 00:00:00 2001
From: Sudharsan Veeravalli <quic_svs at quicinc.com>
Date: Wed, 7 Jan 2026 21:53:09 +0530
Subject: [PATCH 2/2] Split out alignment check.

---
 .../Target/RISCV/RISCVLoadStoreOptimizer.cpp  | 28 ++++---
 .../RISCV/xqcilsm-lwmi-swmi-multiple.mir      | 82 ++++++++++++-------
 2 files changed, 66 insertions(+), 44 deletions(-)

diff --git a/llvm/lib/Target/RISCV/RISCVLoadStoreOptimizer.cpp b/llvm/lib/Target/RISCV/RISCVLoadStoreOptimizer.cpp
index 4358e8b8a30bd..a57d6ecf1f617 100644
--- a/llvm/lib/Target/RISCV/RISCVLoadStoreOptimizer.cpp
+++ b/llvm/lib/Target/RISCV/RISCVLoadStoreOptimizer.cpp
@@ -183,6 +183,14 @@ bool RISCVLoadStoreOpt::tryToPairLdStInst(MachineBasicBlock::iterator &MBBI) {
   return false;
 }
 
+static bool isMemOpAligned(MachineInstr &MI, Align RequiredAlignment) {
+  const MachineMemOperand *MMO = *MI.memoperands_begin();
+  Align MMOAlign = MMO->getAlign();
+  if (MMOAlign < RequiredAlignment)
+    return false;
+  return true;
+}
+
 // Convert set of 3 or more LW/SW instructions to QC_LWMI/QC_SWMI/QC_SETWMI.
 // For now this only handles consecutive loads and stores traversing the basic
 // block top-down.
@@ -200,6 +208,9 @@ bool RISCVLoadStoreOpt::tryConvertToXqcilsmMultiLdSt(
   if (Opc != RISCV::LW && Opc != RISCV::SW)
     return false;
 
+  if (!isMemOpAligned(FirstMI, Align(4)))
+    return false;
+
   // Require simple reg+imm addressing.
   const MachineOperand &BaseOp = FirstMI.getOperand(1);
   const MachineOperand &OffOp = FirstMI.getOperand(2);
@@ -209,11 +220,6 @@ bool RISCVLoadStoreOpt::tryConvertToXqcilsmMultiLdSt(
   Register Base = BaseOp.getReg();
   int64_t BaseOff = OffOp.getImm();
 
-  const MachineMemOperand *MMO = *FirstMI.memoperands_begin();
-  Align MMOAlign = MMO->getAlign();
-  if (MMOAlign < Align(4))
-    return false;
-
   if (!isShiftedUInt<5, 2>(BaseOff))
     return false;
 
@@ -248,6 +254,8 @@ bool RISCVLoadStoreOpt::tryConvertToXqcilsmMultiLdSt(
       break;
     if (!TII->isLdStSafeToPair(MI, TRI))
       break;
+    if (!isMemOpAligned(MI, Align(4)))
+      break;
 
     const MachineOperand &BaseMIOp = MI.getOperand(1);
     const MachineOperand &OffsetMIOp = MI.getOperand(2);
@@ -397,10 +405,7 @@ bool RISCVLoadStoreOpt::tryConvertToXqcilsmLdStPair(
   if (Base1 != Base2)
     return false;
 
-  const MachineMemOperand *MMO = *First->memoperands_begin();
-  Align MMOAlign = MMO->getAlign();
-
-  if (MMOAlign < Align(4))
+  if (!isMemOpAligned(*First, Align(4)) || !isMemOpAligned(*Second, Align(4)))
     return false;
 
   auto &FirstOp0 = First->getOperand(0);
@@ -503,10 +508,7 @@ bool RISCVLoadStoreOpt::tryConvertToMIPSLdStPair(
     break;
   }
 
-  const MachineMemOperand *MMO = *First->memoperands_begin();
-  Align MMOAlign = MMO->getAlign();
-
-  if (MMOAlign < RequiredAlignment)
+  if (!isMemOpAligned(*First, RequiredAlignment))
     return false;
 
   int64_t Offset = First->getOperand(2).getImm();
diff --git a/llvm/test/CodeGen/RISCV/xqcilsm-lwmi-swmi-multiple.mir b/llvm/test/CodeGen/RISCV/xqcilsm-lwmi-swmi-multiple.mir
index 0bda7914836f1..20b7e306cbdcf 100644
--- a/llvm/test/CodeGen/RISCV/xqcilsm-lwmi-swmi-multiple.mir
+++ b/llvm/test/CodeGen/RISCV/xqcilsm-lwmi-swmi-multiple.mir
@@ -10,6 +10,7 @@
   define void @swmi() { ret void }
   define void @swmi_x0() { ret void }
   define void @swmi_nonconsecutive() { ret void }
+  define void @swmi_misaligned() { ret void}
   define void @setwmi() { ret void }
 ...
 ---
@@ -25,10 +26,10 @@ body:             |
     ; CHECK-NEXT: $x10 = ADD $x10, $x30
     ; CHECK-NEXT: $x10 = ADD $x10, $x31
     ; CHECK-NEXT: PseudoRET
-    $x28 = LW $x10, 0 :: (load (s32))
-    $x29 = LW $x10, 4 :: (load (s32))
-    $x30 = LW $x10, 8 :: (load (s32))
-    $x31 = LW $x10, 12 :: (load (s32))
+    $x28 = LW $x10, 0 :: (load (s32), align 4)
+    $x29 = LW $x10, 4 :: (load (s32), align 4)
+    $x30 = LW $x10, 8 :: (load (s32), align 4)
+    $x31 = LW $x10, 12 :: (load (s32), align 4)
     $x10 = ADD $x28, $x29
     $x10 = ADD $x10, $x30
     $x10 = ADD $x10, $x31
@@ -45,10 +46,10 @@ body:             |
     ; CHECK-NEXT: $x0 = LW $x10, 0 :: (load (s32))
     ; CHECK-NEXT: $x1 = QC_LWMI killed $x10, 3, 4, implicit-def $x2, implicit-def $x3 :: (load (s32))
     ; CHECK-NEXT: PseudoRET
-    $x0 = LW $x10, 0 :: (load (s32))
-    $x1 = LW $x10, 4 :: (load (s32))
-    $x2 = LW $x10, 8 :: (load (s32))
-    $x3 = LW killed $x10, 12 :: (load (s32))
+    $x0 = LW $x10, 0 :: (load (s32), align 4)
+    $x1 = LW $x10, 4 :: (load (s32), align 4)
+    $x2 = LW $x10, 8 :: (load (s32), align 4)
+    $x3 = LW killed $x10, 12 :: (load (s32), align 4)
     PseudoRET
 ...
 ---
@@ -64,10 +65,10 @@ body:             |
     ; CHECK-NEXT: $x29 = LW $x10, 8 :: (load (s32))
     ; CHECK-NEXT: $x31 = LW $x10, 12 :: (load (s32))
     ; CHECK-NEXT: PseudoRET
-    $x28 = LW $x10, 0 :: (load (s32))
-    $x29 = LW $x10, 8 :: (load (s32))
-    $x30 = LW $x10, 4 :: (load (s32))
-    $x31 = LW $x10, 12 :: (load (s32))
+    $x28 = LW $x10, 0 :: (load (s32), align 4)
+    $x29 = LW $x10, 8 :: (load (s32), align 4)
+    $x30 = LW $x10, 4 :: (load (s32), align 4)
+    $x31 = LW $x10, 12 :: (load (s32), align 4)
     PseudoRET
 ...
 ---
@@ -82,9 +83,9 @@ body:             |
     ; CHECK-NEXT: $x29 = LW $x10, 6 :: (load (s32))
     ; CHECK-NEXT: $x30 = LW $x10, 10 :: (load (s32))
     ; CHECK-NEXT: PseudoRET
-    $x28 = LW $x10, 2 :: (load (s32))
-    $x29 = LW $x10, 6 :: (load (s32))
-    $x30 = LW $x10, 10 :: (load (s32))
+    $x28 = LW $x10, 2 :: (load (s32), align 4)
+    $x29 = LW $x10, 6 :: (load (s32), align 4)
+    $x30 = LW $x10, 10 :: (load (s32), align 4)
     PseudoRET
 ...
 ---
@@ -97,10 +98,10 @@ body:             |
     ; CHECK-NEXT: {{  $}}
     ; CHECK-NEXT: QC_SWMI killed $x28, $x10, 4, 0, implicit killed $x29, implicit $x30, implicit killed $x31 :: (store (s32))
     ; CHECK-NEXT: PseudoRET
-    SW killed $x28, $x10, 0 :: (store (s32))
-    SW killed $x29, $x10, 4 :: (store (s32))
-    SW $x30, $x10, 8 :: (store (s32))
-    SW killed $x31, $x10, 12 :: (store (s32))
+    SW killed $x28, $x10, 0 :: (store (s32), align 4)
+    SW killed $x29, $x10, 4 :: (store (s32), align 4)
+    SW $x30, $x10, 8 :: (store (s32), align 4)
+    SW killed $x31, $x10, 12 :: (store (s32), align 4)
     PseudoRET
 ...
 ---
@@ -114,10 +115,10 @@ body:             |
     ; CHECK-NEXT: SW $x0, $x10, 0 :: (store (s32))
     ; CHECK-NEXT: QC_SWMI $x1, $x10, 3, 4, implicit $x2, implicit $x3 :: (store (s32))
     ; CHECK-NEXT: PseudoRET
-    SW $x0, $x10, 0 :: (store (s32))
-    SW $x1, $x10, 4 :: (store (s32))
-    SW $x2, $x10, 8 :: (store (s32))
-    SW $x3, $x10, 12 :: (store (s32))
+    SW $x0, $x10, 0 :: (store (s32), align 4)
+    SW $x1, $x10, 4 :: (store (s32), align 4)
+    SW $x2, $x10, 8 :: (store (s32), align 4)
+    SW $x3, $x10, 12 :: (store (s32), align 4)
     PseudoRET
 ...
 ---
@@ -133,12 +134,31 @@ body:             |
     ; CHECK-NEXT: QC_SWMI $x30, $x10, 2, 8, implicit killed $x31 :: (store (s32))
     ; CHECK-NEXT: $x12 = ADDI $x30, 1
     ; CHECK-NEXT: PseudoRET
-    SW $x28, $x10, 0 :: (store (s32))
+    SW $x28, $x10, 0 :: (store (s32), align 4)
     $x11 = ADDI $x28, 1
-    SW killed $x29, $x10, 4 :: (store (s32))
-    SW $x30, $x10, 8 :: (store (s32))
+    SW killed $x29, $x10, 4 :: (store (s32), align 4)
+    SW $x30, $x10, 8 :: (store (s32), align 4)
     $x12 = ADDI $x30, 1
-    SW killed $x31, $x10, 12 :: (store (s32))
+    SW killed $x31, $x10, 12 :: (store (s32), align 4)
+    PseudoRET
+...
+---
+name:            swmi_misaligned
+body:             |
+  bb.0:
+    liveins: $x10
+    ; CHECK-LABEL: name: swmi_misaligned
+    ; CHECK: liveins: $x10
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: SW $x0, $x10, 0 :: (store (s32))
+    ; CHECK-NEXT: SW $x1, $x10, 4 :: (store (s32))
+    ; CHECK-NEXT: SW $x2, $x10, 8 :: (store (s32), align 2)
+    ; CHECK-NEXT: SW $x3, $x10, 12 :: (store (s32))
+    ; CHECK-NEXT: PseudoRET
+    SW $x0, $x10, 0 :: (store (s32), align 4)
+    SW $x1, $x10, 4 :: (store (s32), align 4)
+    SW $x2, $x10, 8 :: (store (s32), align 2)
+    SW $x3, $x10, 12 :: (store (s32), align 4)
     PseudoRET
 ...
 ---
@@ -151,9 +171,9 @@ body:             |
     ; CHECK-NEXT: {{  $}}
     ; CHECK-NEXT: QC_SETWMI $x0, $x10, 4, 0 :: (store (s32))
     ; CHECK-NEXT: PseudoRET
-    SW $x0, $x10, 0 :: (store (s32))
-    SW $x0, $x10, 4 :: (store (s32))
-    SW $x0, $x10, 8 :: (store (s32))
-    SW $x0, $x10, 12 :: (store (s32))
+    SW $x0, $x10, 0 :: (store (s32), align 4)
+    SW $x0, $x10, 4 :: (store (s32), align 4)
+    SW $x0, $x10, 8 :: (store (s32), align 4)
+    SW $x0, $x10, 12 :: (store (s32), align 4)
     PseudoRET
 ...



More information about the llvm-commits mailing list