[llvm] [RISCV][GlobalISel] Support i64 load/store for RV32+Zilsd (PR #221148)

Kane Wang via llvm-commits llvm-commits at lists.llvm.org
Wed Sep 23 00:45:00 PDT 2026


https://github.com/ReVe1uv updated https://github.com/llvm/llvm-project/pull/221148

>From 14fb7bc74bf1dfb5534404e0bbf20a70b9c99346 Mon Sep 17 00:00:00 2001
From: Kane Wang <wangqiang1 at kylinos.cn>
Date: Fri, 4 Sep 2026 14:14:22 +0800
Subject: [PATCH] [RISCV][GlobalISel] Support i64 load/store for RV32+Zilsd

Extend the legalizer, register bank info and instruction selector to handle
i64 load/store via LD_RV32/SD_RV32, along with the s64 G_MERGE_VALUES and
G_UNMERGE_VALUES needed to move values in and out of a GPRPair.

An s64 value on RV32 is either an f64 in FPR64 (needs D) or an i64 in a
GPRPair (needs Zilsd). LLT cannot tell the two apart, and guessing from the
uses is not reliable: an f64 whose only use is a G_SELECT looks like an
integer, because G_SELECT is not itself floating point constrained, while
G_SELECT always maps an RV32 s64 to FPR64, so the two would disagree about
the bank and crash. Only use a GPRPair when the value cannot be an f64, which
today means when D is absent. With extended LLTs the type answers this
directly, so prefer that when they are enabled; until RISC-V is migrated,
-mattr=+d,+zilsd keeps its existing behaviour.

Tests cover +zilsd, +unaligned-scalar-mem, +zilsd-word-align and +d,+zilsd,
the last with regressions for a select of two i64 values, a select of two
doubles fed by double loads, and an i64 that is bitcast to double.

Assisted-by: Claude
---
 .../RISCV/GISel/RISCVInstructionSelector.cpp  |  72 +-
 .../Target/RISCV/GISel/RISCVLegalizerInfo.cpp |   9 +-
 .../RISCV/GISel/RISCVRegisterBankInfo.cpp     |  61 +-
 .../zilsd-load-store-rv32.mir                 |  70 ++
 .../legalizer/legalize-load-rv32-zilsd.mir    | 171 ++++
 .../legalizer/legalize-store-rv32-zilsd.mir   | 181 ++++
 .../regbankselect/zilsd-load-store-rv32.mir   |  76 ++
 llvm/test/CodeGen/RISCV/GlobalISel/zilsd.ll   | 779 ++++++++++++++++++
 8 files changed, 1399 insertions(+), 20 deletions(-)
 create mode 100644 llvm/test/CodeGen/RISCV/GlobalISel/instruction-select/zilsd-load-store-rv32.mir
 create mode 100644 llvm/test/CodeGen/RISCV/GlobalISel/legalizer/legalize-load-rv32-zilsd.mir
 create mode 100644 llvm/test/CodeGen/RISCV/GlobalISel/legalizer/legalize-store-rv32-zilsd.mir
 create mode 100644 llvm/test/CodeGen/RISCV/GlobalISel/regbankselect/zilsd-load-store-rv32.mir
 create mode 100644 llvm/test/CodeGen/RISCV/GlobalISel/zilsd.ll

diff --git a/llvm/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp b/llvm/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp
index c8e8d8f247b630..4b1bac9ad9ea8a 100644
--- a/llvm/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp
+++ b/llvm/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp
@@ -93,6 +93,7 @@ class RISCVInstructionSelector : public InstructionSelector {
   void emitFence(AtomicOrdering FenceOrdering, SyncScope::ID FenceSSID,
                  MachineInstr &MI) const;
   bool selectUnmergeValues(MachineInstr &MI) const;
+  bool selectMergeValues(MachineInstr &MI) const;
   void addVectorLoadStoreOperands(MachineInstr &I,
                                   SmallVectorImpl<Register> &SrcOps,
                                   unsigned &CurOp, bool IsMasked,
@@ -1492,6 +1493,8 @@ bool RISCVInstructionSelector::select(MachineInstr &MI) {
     return selectImplicitDef(MI);
   case TargetOpcode::G_UNMERGE_VALUES:
     return selectUnmergeValues(MI);
+  case TargetOpcode::G_MERGE_VALUES:
+    return selectMergeValues(MI);
   case TargetOpcode::G_LOAD:
   case TargetOpcode::G_STORE: {
     GLoadStore &LdSt = cast<GLoadStore>(MI);
@@ -1524,9 +1527,17 @@ bool RISCVInstructionSelector::select(MachineInstr &MI) {
       return true;
     }
 
-    const unsigned NewOpc = selectRegImmLoadStoreOp(MI.getOpcode(), MemSize);
-    if (NewOpc == MI.getOpcode())
-      return false;
+    // RV32+Zilsd uses the pair load/store pseudo (LD_RV32/SD_RV32) for 64-bit
+    // accesses; selectRegImmLoadStoreOp would otherwise return LD/SD, which
+    // are RV64-only.
+    unsigned NewOpc;
+    if (!STI.is64Bit() && STI.hasStdExtZilsd() && MemSize == 64) {
+      NewOpc = isa<GStore>(MI) ? RISCV::SD_RV32 : RISCV::LD_RV32;
+    } else {
+      NewOpc = selectRegImmLoadStoreOp(MI.getOpcode(), MemSize);
+      if (NewOpc == MI.getOpcode())
+        return false;
+    }
 
     // Check if we can fold anything into the addressing mode.
     auto AddrModeFns = selectAddrRegImm(MI.getOperand(1));
@@ -1565,15 +1576,36 @@ bool RISCVInstructionSelector::select(MachineInstr &MI) {
 bool RISCVInstructionSelector::selectUnmergeValues(MachineInstr &MI) const {
   assert(MI.getOpcode() == TargetOpcode::G_UNMERGE_VALUES);
 
-  if (!Subtarget->hasStdExtZfa())
-    return false;
-
-  // Split F64 Src into two s32 parts
+  // Split Src into two s32 parts.
   if (MI.getNumOperands() != 3)
     return false;
   Register Src = MI.getOperand(2).getReg();
   Register Lo = MI.getOperand(0).getReg();
   Register Hi = MI.getOperand(1).getReg();
+
+  // RV32+Zilsd: split a GPRPair (i64) into its two s32 GPR halves via
+  // subregister extracts.
+  if (!Subtarget->is64Bit() && Subtarget->hasStdExtZilsd() &&
+      MRI->getType(Src) == LLT::scalar(64) && isRegInGprb(Src) &&
+      isRegInGprb(Lo) && isRegInGprb(Hi)) {
+    if (!RBI.constrainGenericRegister(Src, RISCV::GPRPairRegClass, *MRI))
+      return false;
+    auto ExtractHalf = [&](Register Dst, unsigned SubIdx) {
+      RBI.constrainGenericRegister(Dst, RISCV::GPRRegClass, *MRI);
+      MachineInstr &C = *BuildMI(*MI.getParent(), MI, MI.getDebugLoc(),
+                                 TII.get(TargetOpcode::COPY), Dst)
+                             .addReg(Src, RegState::NoFlags, SubIdx);
+      constrainSelectedInstRegOperands(C, TII, TRI, RBI);
+    };
+    ExtractHalf(Lo, RISCV::sub_gpr_even);
+    ExtractHalf(Hi, RISCV::sub_gpr_odd);
+    MI.eraseFromParent();
+    return true;
+  }
+
+  if (!Subtarget->hasStdExtZfa())
+    return false;
+
   if (!isRegInFprb(Src) || !isRegInGprb(Lo) || !isRegInGprb(Hi))
     return false;
 
@@ -1591,6 +1623,32 @@ bool RISCVInstructionSelector::selectUnmergeValues(MachineInstr &MI) const {
   return true;
 }
 
+bool RISCVInstructionSelector::selectMergeValues(MachineInstr &MI) const {
+  assert(MI.getOpcode() == TargetOpcode::G_MERGE_VALUES);
+
+  if (MI.getNumOperands() != 3)
+    return false;
+  Register Dst = MI.getOperand(0).getReg();
+  Register Lo = MI.getOperand(1).getReg();
+  Register Hi = MI.getOperand(2).getReg();
+
+  // RV32+Zilsd: build a GPRPair (i64) from two s32 GPR halves.
+  if (!Subtarget->is64Bit() && Subtarget->hasStdExtZilsd() &&
+      MRI->getType(Dst) == LLT::scalar(64) && isRegInGprb(Dst) &&
+      isRegInGprb(Lo) && isRegInGprb(Hi)) {
+    MachineInstr &Seq = *BuildMI(*MI.getParent(), MI, MI.getDebugLoc(),
+                                 TII.get(TargetOpcode::REG_SEQUENCE), Dst)
+                             .addReg(Lo)
+                             .addImm(RISCV::sub_gpr_even)
+                             .addReg(Hi)
+                             .addImm(RISCV::sub_gpr_odd);
+    MI.eraseFromParent();
+    constrainSelectedInstRegOperands(Seq, TII, TRI, RBI);
+    return true;
+  }
+  return false;
+}
+
 bool RISCVInstructionSelector::replacePtrWithInt(MachineInstr &MI,
                                                  unsigned OpIdx) {
   MachineOperand &Op = MI.getOperand(OpIdx);
diff --git a/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp b/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp
index 451d21df4d95ca..357dfd60b89793 100644
--- a/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp
+++ b/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp
@@ -198,7 +198,7 @@ RISCVLegalizerInfo::RISCVLegalizerInfo(const RISCVSubtarget &ST)
     auto &MergeUnmergeActions = getActionDefinitionsBuilder(Op);
     unsigned BigTyIdx = Op == G_MERGE_VALUES ? 0 : 1;
     unsigned LitTyIdx = Op == G_MERGE_VALUES ? 1 : 0;
-    if (XLen == 32 && ST.hasStdExtD()) {
+    if (XLen == 32 && (ST.hasStdExtD() || ST.hasStdExtZilsd())) {
       MergeUnmergeActions.legalIf(
           all(typeIs(BigTyIdx, s64), typeIs(LitTyIdx, s32)));
     }
@@ -370,7 +370,12 @@ RISCVLegalizerInfo::RISCVLegalizerInfo(const RISCVSubtarget &ST)
          {s64, p0, s64, getScalarMemAlign(64)}});
     ExtLoadActions.legalForTypesWithMemDesc(
         {{s64, p0, s32, getScalarMemAlign(32)}});
-  } else if (ST.hasStdExtD()) {
+  } else if (ST.hasStdExtD() || ST.hasStdExtZilsd()) {
+    // RV32: s64 is legal for f64 (D/Zdinx) or i64 (Zilsd).
+    // FIXME: Under -mzilsd-word-align SDAG allows 4-byte alignment for i64
+    // (getZilsdAlign()); we require 8-byte and fall back to two s32
+    // accesses. LLT cannot distinguish i64 from f64, so a single rule
+    // applies to both.
     LoadActions.legalForTypesWithMemDesc(
         {{s64, p0, s64, getScalarMemAlign(64)}});
     StoreActions.legalForTypesWithMemDesc(
diff --git a/llvm/lib/Target/RISCV/GISel/RISCVRegisterBankInfo.cpp b/llvm/lib/Target/RISCV/GISel/RISCVRegisterBankInfo.cpp
index d6a9df281ceb18..4f84f74c704073 100644
--- a/llvm/lib/Target/RISCV/GISel/RISCVRegisterBankInfo.cpp
+++ b/llvm/lib/Target/RISCV/GISel/RISCVRegisterBankInfo.cpp
@@ -203,6 +203,23 @@ bool RISCVRegisterBankInfo::anyUseOnlyUseFP(
       [&](const MachineInstr &UseMI) { return onlyUsesFP(UseMI, MRI, TRI); });
 }
 
+/// Decide whether an s64 value on RV32 belongs in a GPRPair (an i64, needs
+/// Zilsd) rather than in FPR64 (an f64, needs D).
+///
+/// With extended LLTs the type answers this directly. Without them every
+/// 64-bit scalar is an ANY_SCALAR LLT, so i64 and f64 are indistinguishable --
+/// guessing from the uses gets it wrong, e.g. an f64 whose only use is a
+/// G_SELECT looks like an integer because G_SELECT is not itself floating
+/// point constrained. So fall back to using a GPRPair only where the value
+/// cannot be an f64 at all.
+static bool useGPRPairForS64(const RISCVSubtarget &ST, LLT Ty) {
+  if (!ST.hasStdExtZilsd())
+    return false;
+  if (LLT::getUseExtended())
+    return Ty.isInteger();
+  return !ST.hasStdExtD();
+}
+
 static const RegisterBankInfo::ValueMapping *getVRBValueMapping(unsigned Size) {
   unsigned Idx;
 
@@ -345,9 +362,15 @@ RISCVRegisterBankInfo::getInstrMapping(const MachineInstr &MI) const {
     if (cast<GLoad>(MI).isAtomic())
       break;
 
-    // Use FPR64 for s64 loads on rv32.
+    // RV32 s64 loads: f64 (D) uses FPR64; i64 (Zilsd without D) uses a
+    // GPRPair.
     if (GPRSize == 32 && Size.getFixedValue() == 64) {
-      assert(MF.getSubtarget<RISCVSubtarget>().hasStdExtD());
+      const RISCVSubtarget &ST = MF.getSubtarget<RISCVSubtarget>();
+      if (useGPRPairForS64(ST, Ty)) {
+        OpdsMapping[0] = &RISCV::ValueMappings[RISCV::GPRB64Idx];
+        break;
+      }
+      assert(ST.hasStdExtD() && "s64 load without D or Zilsd");
       OpdsMapping[0] = getFPValueMapping(Size);
       break;
     }
@@ -383,9 +406,15 @@ RISCVRegisterBankInfo::getInstrMapping(const MachineInstr &MI) const {
     if (cast<GStore>(MI).isAtomic())
       break;
 
-    // Use FPR64 for s64 stores on rv32.
+    // RV32 s64 stores: f64 (D) uses FPR64; i64 (Zilsd without D) uses a
+    // GPRPair.
     if (GPRSize == 32 && Size.getFixedValue() == 64) {
-      assert(MF.getSubtarget<RISCVSubtarget>().hasStdExtD());
+      const RISCVSubtarget &ST = MF.getSubtarget<RISCVSubtarget>();
+      if (useGPRPairForS64(ST, Ty)) {
+        OpdsMapping[0] = &RISCV::ValueMappings[RISCV::GPRB64Idx];
+        break;
+      }
+      assert(ST.hasStdExtD() && "s64 store without D or Zilsd");
       OpdsMapping[0] = getFPValueMapping(Ty.getSizeInBits());
       break;
     }
@@ -414,7 +443,9 @@ RISCVRegisterBankInfo::getInstrMapping(const MachineInstr &MI) const {
     // everything has to be on GPR.
     unsigned NumFP = 0;
 
-    // Use FPR64 for s64 select on rv32.
+    // Use FPR64 for s64 select on rv32. An s64 select is only legal here when
+    // D is present (see the G_SELECT rules), so this cannot be a Zilsd i64,
+    // which would need a GPRPair instead.
     if (GPRSize == 32 && Ty.getSizeInBits() == 64) {
       NumFP = 3;
     } else {
@@ -493,24 +524,32 @@ RISCVRegisterBankInfo::getInstrMapping(const MachineInstr &MI) const {
     break;
   }
   case TargetOpcode::G_MERGE_VALUES: {
-    // Use FPR64 for s64 merge on rv32.
+    // RV32 s64: f64 (D) uses FPR64; i64 (Zilsd without D) uses a GPRPair.
     LLT Ty = MRI.getType(MI.getOperand(0).getReg());
     if (GPRSize == 32 && Ty.getSizeInBits() == 64) {
-      assert(MF.getSubtarget<RISCVSubtarget>().hasStdExtD());
-      OpdsMapping[0] = getFPValueMapping(Ty.getSizeInBits());
+      const RISCVSubtarget &ST = MF.getSubtarget<RISCVSubtarget>();
+      OpdsMapping[0] = useGPRPairForS64(ST, Ty)
+                           ? &RISCV::ValueMappings[RISCV::GPRB64Idx]
+                           : getFPValueMapping(Ty.getSizeInBits());
+      assert((useGPRPairForS64(ST, Ty) || ST.hasStdExtD()) &&
+             "s64 merge without D or Zilsd");
       OpdsMapping[1] = GPRValueMapping;
       OpdsMapping[2] = GPRValueMapping;
     }
     break;
   }
   case TargetOpcode::G_UNMERGE_VALUES: {
-    // Use FPR64 for s64 unmerge on rv32.
+    // RV32 s64: f64 (D) uses FPR64; i64 (Zilsd without D) uses a GPRPair.
     LLT Ty = MRI.getType(MI.getOperand(2).getReg());
     if (GPRSize == 32 && Ty.getSizeInBits() == 64) {
-      assert(MF.getSubtarget<RISCVSubtarget>().hasStdExtD());
+      const RISCVSubtarget &ST = MF.getSubtarget<RISCVSubtarget>();
+      OpdsMapping[2] = useGPRPairForS64(ST, Ty)
+                           ? &RISCV::ValueMappings[RISCV::GPRB64Idx]
+                           : getFPValueMapping(Ty.getSizeInBits());
+      assert((useGPRPairForS64(ST, Ty) || ST.hasStdExtD()) &&
+             "s64 unmerge without D or Zilsd");
       OpdsMapping[0] = GPRValueMapping;
       OpdsMapping[1] = GPRValueMapping;
-      OpdsMapping[2] = getFPValueMapping(Ty.getSizeInBits());
     }
     break;
   }
diff --git a/llvm/test/CodeGen/RISCV/GlobalISel/instruction-select/zilsd-load-store-rv32.mir b/llvm/test/CodeGen/RISCV/GlobalISel/instruction-select/zilsd-load-store-rv32.mir
new file mode 100644
index 00000000000000..33972fb0cf9087
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/GlobalISel/instruction-select/zilsd-load-store-rv32.mir
@@ -0,0 +1,70 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
+# RUN: llc -mtriple=riscv32 -mattr=+zilsd -run-pass=instruction-select %s -o - | FileCheck %s
+
+---
+name:            load_i64_zilsd
+alignment:       4
+legalized:       true
+regBankSelected: true
+selected:        false
+tracksRegLiveness: true
+frameInfo:
+  maxAlignment:    1
+machineFunctionInfo:
+  varArgsFrameIndex: 0
+  varArgsSaveSize: 0
+body:             |
+  bb.1:
+    liveins: $x10
+
+    ; CHECK-LABEL: name: load_i64_zilsd
+    ; CHECK: liveins: $x10
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[COPY:%[0-9]+]]:gpr = COPY $x10
+    ; CHECK-NEXT: [[LD_RV32_:%[0-9]+]]:gprpair = LD_RV32 [[COPY]], 0 :: (load (s64))
+    ; CHECK-NEXT: [[COPY1:%[0-9]+]]:gpr = COPY [[LD_RV32_]].sub_gpr_even
+    ; CHECK-NEXT: [[COPY2:%[0-9]+]]:gpr = COPY [[LD_RV32_]].sub_gpr_odd
+    ; CHECK-NEXT: $x10 = COPY [[COPY1]]
+    ; CHECK-NEXT: $x11 = COPY [[COPY2]]
+    ; CHECK-NEXT: PseudoRET implicit $x10, implicit $x11
+    %0:gprb(p0) = COPY $x10
+    %1:gprb(s64) = G_LOAD %0(p0) :: (load (s64), align 8)
+    %2:gprb(s32), %3:gprb(s32) = G_UNMERGE_VALUES %1(s64)
+    $x10 = COPY %2(s32)
+    $x11 = COPY %3(s32)
+    PseudoRET implicit $x10, implicit $x11
+
+...
+---
+name:            store_i64_zilsd
+alignment:       4
+legalized:       true
+regBankSelected: true
+selected:        false
+tracksRegLiveness: true
+frameInfo:
+  maxAlignment:    1
+machineFunctionInfo:
+  varArgsFrameIndex: 0
+  varArgsSaveSize: 0
+body:             |
+  bb.1:
+    liveins: $x10, $x11, $x12
+
+    ; CHECK-LABEL: name: store_i64_zilsd
+    ; CHECK: liveins: $x10, $x11, $x12
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[COPY:%[0-9]+]]:gpr = COPY $x10
+    ; CHECK-NEXT: [[COPY1:%[0-9]+]]:gpr = COPY $x11
+    ; CHECK-NEXT: [[COPY2:%[0-9]+]]:gpr = COPY $x12
+    ; CHECK-NEXT: [[REG_SEQUENCE:%[0-9]+]]:gprpair = REG_SEQUENCE [[COPY1]], %subreg.sub_gpr_even, [[COPY2]], %subreg.sub_gpr_odd
+    ; CHECK-NEXT: SD_RV32 [[REG_SEQUENCE]], [[COPY]], 0 :: (store (s64))
+    ; CHECK-NEXT: PseudoRET
+    %0:gprb(p0) = COPY $x10
+    %1:gprb(s32) = COPY $x11
+    %2:gprb(s32) = COPY $x12
+    %3:gprb(s64) = G_MERGE_VALUES %1(s32), %2(s32)
+    G_STORE %3(s64), %0(p0) :: (store (s64), align 8)
+    PseudoRET
+
+...
diff --git a/llvm/test/CodeGen/RISCV/GlobalISel/legalizer/legalize-load-rv32-zilsd.mir b/llvm/test/CodeGen/RISCV/GlobalISel/legalizer/legalize-load-rv32-zilsd.mir
new file mode 100644
index 00000000000000..661f8c545f5504
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/GlobalISel/legalizer/legalize-load-rv32-zilsd.mir
@@ -0,0 +1,171 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
+# RUN: llc -mtriple=riscv32 -mattr=+zilsd -run-pass=legalizer %s -o - | FileCheck %s
+# RUN: llc -mtriple=riscv32 -mattr=+zilsd,+unaligned-scalar-mem -run-pass=legalizer %s -o - | FileCheck %s --check-prefix=UNALIGNED
+
+---
+name:            load_i64_zilsd
+alignment:       4
+tracksRegLiveness: true
+registers:
+  - { id: 0, class: _ }
+  - { id: 1, class: _ }
+  - { id: 2, class: _ }
+  - { id: 3, class: _ }
+frameInfo:
+  maxAlignment:    1
+machineFunctionInfo:
+  varArgsFrameIndex: 0
+  varArgsSaveSize: 0
+body:             |
+  bb.1:
+    liveins: $x10
+
+    ; CHECK-LABEL: name: load_i64_zilsd
+    ; CHECK: liveins: $x10
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[COPY:%[0-9]+]]:_(p0) = COPY $x10
+    ; CHECK-NEXT: [[LOAD:%[0-9]+]]:_(s64) = G_LOAD [[COPY]](p0) :: (load (s64))
+    ; CHECK-NEXT: [[UV:%[0-9]+]]:_(s32), [[UV1:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[LOAD]](s64)
+    ; CHECK-NEXT: $x10 = COPY [[UV]](s32)
+    ; CHECK-NEXT: $x11 = COPY [[UV1]](s32)
+    ; CHECK-NEXT: PseudoRET implicit $x10, implicit $x11
+    ;
+    ; UNALIGNED-LABEL: name: load_i64_zilsd
+    ; UNALIGNED: liveins: $x10
+    ; UNALIGNED-NEXT: {{  $}}
+    ; UNALIGNED-NEXT: [[COPY:%[0-9]+]]:_(p0) = COPY $x10
+    ; UNALIGNED-NEXT: [[LOAD:%[0-9]+]]:_(s64) = G_LOAD [[COPY]](p0) :: (load (s64))
+    ; UNALIGNED-NEXT: [[UV:%[0-9]+]]:_(s32), [[UV1:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[LOAD]](s64)
+    ; UNALIGNED-NEXT: $x10 = COPY [[UV]](s32)
+    ; UNALIGNED-NEXT: $x11 = COPY [[UV1]](s32)
+    ; UNALIGNED-NEXT: PseudoRET implicit $x10, implicit $x11
+    %0:_(p0) = COPY $x10
+    %1:_(s64) = G_LOAD %0(p0) :: (load (s64), align 8)
+    %2:_(s32), %3:_(s32) = G_UNMERGE_VALUES %1(s64)
+    $x10 = COPY %2(s32)
+    $x11 = COPY %3(s32)
+    PseudoRET implicit $x10, implicit $x11
+
+...
+---
+name:            load_i64_zilsd_unaligned
+alignment:       4
+tracksRegLiveness: true
+registers:
+  - { id: 0, class: _ }
+  - { id: 1, class: _ }
+  - { id: 2, class: _ }
+  - { id: 3, class: _ }
+frameInfo:
+  maxAlignment:    1
+machineFunctionInfo:
+  varArgsFrameIndex: 0
+  varArgsSaveSize: 0
+body:             |
+  bb.1:
+    liveins: $x10
+
+    ; CHECK-LABEL: name: load_i64_zilsd_unaligned
+    ; CHECK: liveins: $x10
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[COPY:%[0-9]+]]:_(p0) = COPY $x10
+    ; CHECK-NEXT: [[ZEXTLOAD:%[0-9]+]]:_(s32) = G_ZEXTLOAD [[COPY]](p0) :: (load (s8))
+    ; CHECK-NEXT: [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 1
+    ; CHECK-NEXT: [[PTR_ADD:%[0-9]+]]:_(p0) = nuw inbounds G_PTR_ADD [[COPY]], [[C]](s32)
+    ; CHECK-NEXT: [[ZEXTLOAD1:%[0-9]+]]:_(s32) = G_ZEXTLOAD [[PTR_ADD]](p0) :: (load (s8) from unknown-address + 1)
+    ; CHECK-NEXT: [[C1:%[0-9]+]]:_(s32) = G_CONSTANT i32 8
+    ; CHECK-NEXT: [[SHL:%[0-9]+]]:_(s32) = G_SHL [[ZEXTLOAD1]], [[C1]](s32)
+    ; CHECK-NEXT: [[OR:%[0-9]+]]:_(s32) = G_OR [[SHL]], [[ZEXTLOAD]]
+    ; CHECK-NEXT: [[C2:%[0-9]+]]:_(s32) = G_CONSTANT i32 2
+    ; CHECK-NEXT: [[PTR_ADD1:%[0-9]+]]:_(p0) = nuw inbounds G_PTR_ADD [[COPY]], [[C2]](s32)
+    ; CHECK-NEXT: [[ZEXTLOAD2:%[0-9]+]]:_(s32) = G_ZEXTLOAD [[PTR_ADD1]](p0) :: (load (s8) from unknown-address + 2)
+    ; CHECK-NEXT: [[PTR_ADD2:%[0-9]+]]:_(p0) = nuw inbounds G_PTR_ADD [[PTR_ADD1]], [[C]](s32)
+    ; CHECK-NEXT: [[LOAD:%[0-9]+]]:_(s32) = G_LOAD [[PTR_ADD2]](p0) :: (load (s8) from unknown-address + 3)
+    ; CHECK-NEXT: [[SHL1:%[0-9]+]]:_(s32) = G_SHL [[LOAD]], [[C1]](s32)
+    ; CHECK-NEXT: [[OR1:%[0-9]+]]:_(s32) = G_OR [[SHL1]], [[ZEXTLOAD2]]
+    ; CHECK-NEXT: [[C3:%[0-9]+]]:_(s32) = G_CONSTANT i32 16
+    ; CHECK-NEXT: [[SHL2:%[0-9]+]]:_(s32) = G_SHL [[OR1]], [[C3]](s32)
+    ; CHECK-NEXT: [[OR2:%[0-9]+]]:_(s32) = G_OR [[SHL2]], [[OR]]
+    ; CHECK-NEXT: [[C4:%[0-9]+]]:_(s32) = G_CONSTANT i32 4
+    ; CHECK-NEXT: [[PTR_ADD3:%[0-9]+]]:_(p0) = nuw inbounds G_PTR_ADD [[COPY]], [[C4]](s32)
+    ; CHECK-NEXT: [[ZEXTLOAD3:%[0-9]+]]:_(s32) = G_ZEXTLOAD [[PTR_ADD3]](p0) :: (load (s8) from unknown-address + 4)
+    ; CHECK-NEXT: [[PTR_ADD4:%[0-9]+]]:_(p0) = nuw inbounds G_PTR_ADD [[PTR_ADD3]], [[C]](s32)
+    ; CHECK-NEXT: [[ZEXTLOAD4:%[0-9]+]]:_(s32) = G_ZEXTLOAD [[PTR_ADD4]](p0) :: (load (s8) from unknown-address + 5)
+    ; CHECK-NEXT: [[SHL3:%[0-9]+]]:_(s32) = G_SHL [[ZEXTLOAD4]], [[C1]](s32)
+    ; CHECK-NEXT: [[OR3:%[0-9]+]]:_(s32) = G_OR [[SHL3]], [[ZEXTLOAD3]]
+    ; CHECK-NEXT: [[PTR_ADD5:%[0-9]+]]:_(p0) = nuw inbounds G_PTR_ADD [[PTR_ADD3]], [[C2]](s32)
+    ; CHECK-NEXT: [[ZEXTLOAD5:%[0-9]+]]:_(s32) = G_ZEXTLOAD [[PTR_ADD5]](p0) :: (load (s8) from unknown-address + 6)
+    ; CHECK-NEXT: [[PTR_ADD6:%[0-9]+]]:_(p0) = nuw inbounds G_PTR_ADD [[PTR_ADD5]], [[C]](s32)
+    ; CHECK-NEXT: [[LOAD1:%[0-9]+]]:_(s32) = G_LOAD [[PTR_ADD6]](p0) :: (load (s8) from unknown-address + 7)
+    ; CHECK-NEXT: [[SHL4:%[0-9]+]]:_(s32) = G_SHL [[LOAD1]], [[C1]](s32)
+    ; CHECK-NEXT: [[OR4:%[0-9]+]]:_(s32) = G_OR [[SHL4]], [[ZEXTLOAD5]]
+    ; CHECK-NEXT: [[SHL5:%[0-9]+]]:_(s32) = G_SHL [[OR4]], [[C3]](s32)
+    ; CHECK-NEXT: [[OR5:%[0-9]+]]:_(s32) = G_OR [[SHL5]], [[OR3]]
+    ; CHECK-NEXT: $x10 = COPY [[OR2]](s32)
+    ; CHECK-NEXT: $x11 = COPY [[OR5]](s32)
+    ; CHECK-NEXT: PseudoRET implicit $x10, implicit $x11
+    ;
+    ; UNALIGNED-LABEL: name: load_i64_zilsd_unaligned
+    ; UNALIGNED: liveins: $x10
+    ; UNALIGNED-NEXT: {{  $}}
+    ; UNALIGNED-NEXT: [[COPY:%[0-9]+]]:_(p0) = COPY $x10
+    ; UNALIGNED-NEXT: [[LOAD:%[0-9]+]]:_(s64) = G_LOAD [[COPY]](p0) :: (load (s64), align 1)
+    ; UNALIGNED-NEXT: [[UV:%[0-9]+]]:_(s32), [[UV1:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[LOAD]](s64)
+    ; UNALIGNED-NEXT: $x10 = COPY [[UV]](s32)
+    ; UNALIGNED-NEXT: $x11 = COPY [[UV1]](s32)
+    ; UNALIGNED-NEXT: PseudoRET implicit $x10, implicit $x11
+    %0:_(p0) = COPY $x10
+    %1:_(s64) = G_LOAD %0(p0) :: (load (s64), align 1)
+    %2:_(s32), %3:_(s32) = G_UNMERGE_VALUES %1(s64)
+    $x10 = COPY %2(s32)
+    $x11 = COPY %3(s32)
+    PseudoRET implicit $x10, implicit $x11
+
+...
+---
+name:            load_i64_zilsd_word_align
+alignment:       4
+tracksRegLiveness: true
+registers:
+  - { id: 0, class: _ }
+  - { id: 1, class: _ }
+  - { id: 2, class: _ }
+  - { id: 3, class: _ }
+frameInfo:
+  maxAlignment:    1
+machineFunctionInfo:
+  varArgsFrameIndex: 0
+  varArgsSaveSize: 0
+body:             |
+  bb.1:
+    liveins: $x10
+
+    ; CHECK-LABEL: name: load_i64_zilsd_word_align
+    ; CHECK: liveins: $x10
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[COPY:%[0-9]+]]:_(p0) = COPY $x10
+    ; CHECK-NEXT: [[LOAD:%[0-9]+]]:_(s32) = G_LOAD [[COPY]](p0) :: (load (s32))
+    ; CHECK-NEXT: [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 4
+    ; CHECK-NEXT: [[PTR_ADD:%[0-9]+]]:_(p0) = nuw inbounds G_PTR_ADD [[COPY]], [[C]](s32)
+    ; CHECK-NEXT: [[LOAD1:%[0-9]+]]:_(s32) = G_LOAD [[PTR_ADD]](p0) :: (load (s32) from unknown-address + 4)
+    ; CHECK-NEXT: $x10 = COPY [[LOAD]](s32)
+    ; CHECK-NEXT: $x11 = COPY [[LOAD1]](s32)
+    ; CHECK-NEXT: PseudoRET implicit $x10, implicit $x11
+    ;
+    ; UNALIGNED-LABEL: name: load_i64_zilsd_word_align
+    ; UNALIGNED: liveins: $x10
+    ; UNALIGNED-NEXT: {{  $}}
+    ; UNALIGNED-NEXT: [[COPY:%[0-9]+]]:_(p0) = COPY $x10
+    ; UNALIGNED-NEXT: [[LOAD:%[0-9]+]]:_(s64) = G_LOAD [[COPY]](p0) :: (load (s64), align 4)
+    ; UNALIGNED-NEXT: [[UV:%[0-9]+]]:_(s32), [[UV1:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[LOAD]](s64)
+    ; UNALIGNED-NEXT: $x10 = COPY [[UV]](s32)
+    ; UNALIGNED-NEXT: $x11 = COPY [[UV1]](s32)
+    ; UNALIGNED-NEXT: PseudoRET implicit $x10, implicit $x11
+    %0:_(p0) = COPY $x10
+    %1:_(s64) = G_LOAD %0(p0) :: (load (s64), align 4)
+    %2:_(s32), %3:_(s32) = G_UNMERGE_VALUES %1(s64)
+    $x10 = COPY %2(s32)
+    $x11 = COPY %3(s32)
+    PseudoRET implicit $x10, implicit $x11
+
+...
diff --git a/llvm/test/CodeGen/RISCV/GlobalISel/legalizer/legalize-store-rv32-zilsd.mir b/llvm/test/CodeGen/RISCV/GlobalISel/legalizer/legalize-store-rv32-zilsd.mir
new file mode 100644
index 00000000000000..c746c35e320744
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/GlobalISel/legalizer/legalize-store-rv32-zilsd.mir
@@ -0,0 +1,181 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
+# RUN: llc -mtriple=riscv32 -mattr=+zilsd -run-pass=legalizer %s -o - | FileCheck %s
+# RUN: llc -mtriple=riscv32 -mattr=+zilsd,+unaligned-scalar-mem -run-pass=legalizer %s -o - | FileCheck %s --check-prefix=UNALIGNED
+
+---
+name:            store_i64_zilsd
+alignment:       4
+tracksRegLiveness: true
+registers:
+  - { id: 0, class: _ }
+  - { id: 1, class: _ }
+  - { id: 2, class: _ }
+  - { id: 3, class: _ }
+frameInfo:
+  maxAlignment:    1
+machineFunctionInfo:
+  varArgsFrameIndex: 0
+  varArgsSaveSize: 0
+body:             |
+  bb.1:
+    liveins: $x10, $x11, $x12
+
+    ; CHECK-LABEL: name: store_i64_zilsd
+    ; CHECK: liveins: $x10, $x11, $x12
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[COPY:%[0-9]+]]:_(p0) = COPY $x10
+    ; CHECK-NEXT: [[COPY1:%[0-9]+]]:_(s32) = COPY $x11
+    ; CHECK-NEXT: [[COPY2:%[0-9]+]]:_(s32) = COPY $x12
+    ; CHECK-NEXT: [[MV:%[0-9]+]]:_(s64) = G_MERGE_VALUES [[COPY1]](s32), [[COPY2]](s32)
+    ; CHECK-NEXT: G_STORE [[MV]](s64), [[COPY]](p0) :: (store (s64))
+    ; CHECK-NEXT: PseudoRET
+    ;
+    ; UNALIGNED-LABEL: name: store_i64_zilsd
+    ; UNALIGNED: liveins: $x10, $x11, $x12
+    ; UNALIGNED-NEXT: {{  $}}
+    ; UNALIGNED-NEXT: [[COPY:%[0-9]+]]:_(p0) = COPY $x10
+    ; UNALIGNED-NEXT: [[COPY1:%[0-9]+]]:_(s32) = COPY $x11
+    ; UNALIGNED-NEXT: [[COPY2:%[0-9]+]]:_(s32) = COPY $x12
+    ; UNALIGNED-NEXT: [[MV:%[0-9]+]]:_(s64) = G_MERGE_VALUES [[COPY1]](s32), [[COPY2]](s32)
+    ; UNALIGNED-NEXT: G_STORE [[MV]](s64), [[COPY]](p0) :: (store (s64))
+    ; UNALIGNED-NEXT: PseudoRET
+    %0:_(p0) = COPY $x10
+    %1:_(s32) = COPY $x11
+    %2:_(s32) = COPY $x12
+    %3:_(s64) = G_MERGE_VALUES %1(s32), %2(s32)
+    G_STORE %3(s64), %0(p0) :: (store (s64), align 8)
+    PseudoRET
+
+...
+---
+name:            store_i64_zilsd_unaligned
+alignment:       4
+tracksRegLiveness: true
+registers:
+  - { id: 0, class: _ }
+  - { id: 1, class: _ }
+  - { id: 2, class: _ }
+  - { id: 3, class: _ }
+frameInfo:
+  maxAlignment:    1
+machineFunctionInfo:
+  varArgsFrameIndex: 0
+  varArgsSaveSize: 0
+body:             |
+  bb.1:
+    liveins: $x10, $x11, $x12
+
+    ; CHECK-LABEL: name: store_i64_zilsd_unaligned
+    ; CHECK: liveins: $x10, $x11, $x12
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[COPY:%[0-9]+]]:_(p0) = COPY $x10
+    ; CHECK-NEXT: [[COPY1:%[0-9]+]]:_(s32) = COPY $x11
+    ; CHECK-NEXT: [[COPY2:%[0-9]+]]:_(s32) = COPY $x12
+    ; CHECK-NEXT: [[COPY3:%[0-9]+]]:_(s32) = COPY [[COPY1]](s32)
+    ; CHECK-NEXT: [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 16
+    ; CHECK-NEXT: [[LSHR:%[0-9]+]]:_(s32) = G_LSHR [[COPY3]], [[C]](s32)
+    ; CHECK-NEXT: [[C1:%[0-9]+]]:_(s32) = G_CONSTANT i32 2
+    ; CHECK-NEXT: [[PTR_ADD:%[0-9]+]]:_(p0) = nuw inbounds G_PTR_ADD [[COPY]], [[C1]](s32)
+    ; CHECK-NEXT: [[TRUNC:%[0-9]+]]:_(s16) = G_TRUNC [[COPY3]](s32)
+    ; CHECK-NEXT: [[C2:%[0-9]+]]:_(s32) = G_CONSTANT i32 8
+    ; CHECK-NEXT: [[C3:%[0-9]+]]:_(s32) = G_CONSTANT i32 65535
+    ; CHECK-NEXT: [[AND:%[0-9]+]]:_(s32) = G_AND [[COPY3]], [[C3]]
+    ; CHECK-NEXT: [[LSHR1:%[0-9]+]]:_(s32) = G_LSHR [[AND]], [[C2]](s32)
+    ; CHECK-NEXT: [[TRUNC1:%[0-9]+]]:_(s16) = G_TRUNC [[LSHR1]](s32)
+    ; CHECK-NEXT: [[C4:%[0-9]+]]:_(s32) = G_CONSTANT i32 1
+    ; CHECK-NEXT: [[PTR_ADD1:%[0-9]+]]:_(p0) = nuw inbounds G_PTR_ADD [[COPY]], [[C4]](s32)
+    ; CHECK-NEXT: G_STORE [[TRUNC]](s16), [[COPY]](p0) :: (store (s8))
+    ; CHECK-NEXT: G_STORE [[TRUNC1]](s16), [[PTR_ADD1]](p0) :: (store (s8) into unknown-address + 1)
+    ; CHECK-NEXT: [[TRUNC2:%[0-9]+]]:_(s16) = G_TRUNC [[LSHR]](s32)
+    ; CHECK-NEXT: [[C5:%[0-9]+]]:_(s32) = G_CONSTANT i32 8
+    ; CHECK-NEXT: [[LSHR2:%[0-9]+]]:_(s32) = G_LSHR [[LSHR]], [[C5]](s32)
+    ; CHECK-NEXT: [[TRUNC3:%[0-9]+]]:_(s16) = G_TRUNC [[LSHR2]](s32)
+    ; CHECK-NEXT: [[PTR_ADD2:%[0-9]+]]:_(p0) = nuw inbounds G_PTR_ADD [[PTR_ADD]], [[C4]](s32)
+    ; CHECK-NEXT: G_STORE [[TRUNC2]](s16), [[PTR_ADD]](p0) :: (store (s8) into unknown-address + 2)
+    ; CHECK-NEXT: G_STORE [[TRUNC3]](s16), [[PTR_ADD2]](p0) :: (store (s8) into unknown-address + 3)
+    ; CHECK-NEXT: [[C6:%[0-9]+]]:_(s32) = G_CONSTANT i32 4
+    ; CHECK-NEXT: [[PTR_ADD3:%[0-9]+]]:_(p0) = nuw inbounds G_PTR_ADD [[COPY]], [[C6]](s32)
+    ; CHECK-NEXT: [[COPY4:%[0-9]+]]:_(s32) = COPY [[COPY2]](s32)
+    ; CHECK-NEXT: [[LSHR3:%[0-9]+]]:_(s32) = G_LSHR [[COPY4]], [[C]](s32)
+    ; CHECK-NEXT: [[PTR_ADD4:%[0-9]+]]:_(p0) = nuw inbounds G_PTR_ADD [[PTR_ADD3]], [[C1]](s32)
+    ; CHECK-NEXT: [[TRUNC4:%[0-9]+]]:_(s16) = G_TRUNC [[COPY4]](s32)
+    ; CHECK-NEXT: [[C7:%[0-9]+]]:_(s32) = G_CONSTANT i32 8
+    ; CHECK-NEXT: [[AND1:%[0-9]+]]:_(s32) = G_AND [[COPY4]], [[C3]]
+    ; CHECK-NEXT: [[LSHR4:%[0-9]+]]:_(s32) = G_LSHR [[AND1]], [[C7]](s32)
+    ; CHECK-NEXT: [[TRUNC5:%[0-9]+]]:_(s16) = G_TRUNC [[LSHR4]](s32)
+    ; CHECK-NEXT: [[PTR_ADD5:%[0-9]+]]:_(p0) = nuw inbounds G_PTR_ADD [[PTR_ADD3]], [[C4]](s32)
+    ; CHECK-NEXT: G_STORE [[TRUNC4]](s16), [[PTR_ADD3]](p0) :: (store (s8) into unknown-address + 4)
+    ; CHECK-NEXT: G_STORE [[TRUNC5]](s16), [[PTR_ADD5]](p0) :: (store (s8) into unknown-address + 5)
+    ; CHECK-NEXT: [[TRUNC6:%[0-9]+]]:_(s16) = G_TRUNC [[LSHR3]](s32)
+    ; CHECK-NEXT: [[C8:%[0-9]+]]:_(s32) = G_CONSTANT i32 8
+    ; CHECK-NEXT: [[LSHR5:%[0-9]+]]:_(s32) = G_LSHR [[LSHR3]], [[C8]](s32)
+    ; CHECK-NEXT: [[TRUNC7:%[0-9]+]]:_(s16) = G_TRUNC [[LSHR5]](s32)
+    ; CHECK-NEXT: [[PTR_ADD6:%[0-9]+]]:_(p0) = nuw inbounds G_PTR_ADD [[PTR_ADD4]], [[C4]](s32)
+    ; CHECK-NEXT: G_STORE [[TRUNC6]](s16), [[PTR_ADD4]](p0) :: (store (s8) into unknown-address + 6)
+    ; CHECK-NEXT: G_STORE [[TRUNC7]](s16), [[PTR_ADD6]](p0) :: (store (s8) into unknown-address + 7)
+    ; CHECK-NEXT: PseudoRET
+    ;
+    ; UNALIGNED-LABEL: name: store_i64_zilsd_unaligned
+    ; UNALIGNED: liveins: $x10, $x11, $x12
+    ; UNALIGNED-NEXT: {{  $}}
+    ; UNALIGNED-NEXT: [[COPY:%[0-9]+]]:_(p0) = COPY $x10
+    ; UNALIGNED-NEXT: [[COPY1:%[0-9]+]]:_(s32) = COPY $x11
+    ; UNALIGNED-NEXT: [[COPY2:%[0-9]+]]:_(s32) = COPY $x12
+    ; UNALIGNED-NEXT: [[MV:%[0-9]+]]:_(s64) = G_MERGE_VALUES [[COPY1]](s32), [[COPY2]](s32)
+    ; UNALIGNED-NEXT: G_STORE [[MV]](s64), [[COPY]](p0) :: (store (s64), align 1)
+    ; UNALIGNED-NEXT: PseudoRET
+    %0:_(p0) = COPY $x10
+    %1:_(s32) = COPY $x11
+    %2:_(s32) = COPY $x12
+    %3:_(s64) = G_MERGE_VALUES %1(s32), %2(s32)
+    G_STORE %3(s64), %0(p0) :: (store (s64), align 1)
+    PseudoRET
+
+...
+---
+name:            store_i64_zilsd_word_align
+alignment:       4
+tracksRegLiveness: true
+registers:
+  - { id: 0, class: _ }
+  - { id: 1, class: _ }
+  - { id: 2, class: _ }
+  - { id: 3, class: _ }
+frameInfo:
+  maxAlignment:    1
+machineFunctionInfo:
+  varArgsFrameIndex: 0
+  varArgsSaveSize: 0
+body:             |
+  bb.1:
+    liveins: $x10, $x11, $x12
+
+    ; CHECK-LABEL: name: store_i64_zilsd_word_align
+    ; CHECK: liveins: $x10, $x11, $x12
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[COPY:%[0-9]+]]:_(p0) = COPY $x10
+    ; CHECK-NEXT: [[COPY1:%[0-9]+]]:_(s32) = COPY $x11
+    ; CHECK-NEXT: [[COPY2:%[0-9]+]]:_(s32) = COPY $x12
+    ; CHECK-NEXT: G_STORE [[COPY1]](s32), [[COPY]](p0) :: (store (s32))
+    ; CHECK-NEXT: [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 4
+    ; CHECK-NEXT: [[PTR_ADD:%[0-9]+]]:_(p0) = nuw inbounds G_PTR_ADD [[COPY]], [[C]](s32)
+    ; CHECK-NEXT: G_STORE [[COPY2]](s32), [[PTR_ADD]](p0) :: (store (s32) into unknown-address + 4)
+    ; CHECK-NEXT: PseudoRET
+    ;
+    ; UNALIGNED-LABEL: name: store_i64_zilsd_word_align
+    ; UNALIGNED: liveins: $x10, $x11, $x12
+    ; UNALIGNED-NEXT: {{  $}}
+    ; UNALIGNED-NEXT: [[COPY:%[0-9]+]]:_(p0) = COPY $x10
+    ; UNALIGNED-NEXT: [[COPY1:%[0-9]+]]:_(s32) = COPY $x11
+    ; UNALIGNED-NEXT: [[COPY2:%[0-9]+]]:_(s32) = COPY $x12
+    ; UNALIGNED-NEXT: [[MV:%[0-9]+]]:_(s64) = G_MERGE_VALUES [[COPY1]](s32), [[COPY2]](s32)
+    ; UNALIGNED-NEXT: G_STORE [[MV]](s64), [[COPY]](p0) :: (store (s64), align 4)
+    ; UNALIGNED-NEXT: PseudoRET
+    %0:_(p0) = COPY $x10
+    %1:_(s32) = COPY $x11
+    %2:_(s32) = COPY $x12
+    %3:_(s64) = G_MERGE_VALUES %1(s32), %2(s32)
+    G_STORE %3(s64), %0(p0) :: (store (s64), align 4)
+    PseudoRET
+
+...
diff --git a/llvm/test/CodeGen/RISCV/GlobalISel/regbankselect/zilsd-load-store-rv32.mir b/llvm/test/CodeGen/RISCV/GlobalISel/regbankselect/zilsd-load-store-rv32.mir
new file mode 100644
index 00000000000000..6520edd5d549ce
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/GlobalISel/regbankselect/zilsd-load-store-rv32.mir
@@ -0,0 +1,76 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
+# RUN: llc -mtriple=riscv32 -mattr=+zilsd -run-pass=reg-bank-select \
+# RUN:   -disable-gisel-legality-check -simplify-mir %s -o - | FileCheck %s
+
+---
+name:            load_i64_zilsd
+alignment:       4
+legalized:       true
+tracksRegLiveness: true
+registers:
+  - { id: 0, class: _ }
+  - { id: 1, class: _ }
+  - { id: 2, class: _ }
+  - { id: 3, class: _ }
+frameInfo:
+  maxAlignment:    1
+machineFunctionInfo:
+  varArgsFrameIndex: 0
+  varArgsSaveSize: 0
+body:             |
+  bb.1:
+    liveins: $x10
+
+    ; CHECK-LABEL: name: load_i64_zilsd
+    ; CHECK: liveins: $x10
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[COPY:%[0-9]+]]:gprb(p0) = COPY $x10
+    ; CHECK-NEXT: [[LOAD:%[0-9]+]]:gprb(s64) = G_LOAD [[COPY]](p0) :: (load (s64))
+    ; CHECK-NEXT: [[UV:%[0-9]+]]:gprb(s32), [[UV1:%[0-9]+]]:gprb(s32) = G_UNMERGE_VALUES [[LOAD]](s64)
+    ; CHECK-NEXT: $x10 = COPY [[UV]](s32)
+    ; CHECK-NEXT: $x11 = COPY [[UV1]](s32)
+    ; CHECK-NEXT: PseudoRET implicit $x10, implicit $x11
+    %0:_(p0) = COPY $x10
+    %1:_(s64) = G_LOAD %0(p0) :: (load (s64), align 8)
+    %2:_(s32), %3:_(s32) = G_UNMERGE_VALUES %1(s64)
+    $x10 = COPY %2(s32)
+    $x11 = COPY %3(s32)
+    PseudoRET implicit $x10, implicit $x11
+
+...
+---
+name:            store_i64_zilsd
+alignment:       4
+legalized:       true
+tracksRegLiveness: true
+registers:
+  - { id: 0, class: _ }
+  - { id: 1, class: _ }
+  - { id: 2, class: _ }
+  - { id: 3, class: _ }
+frameInfo:
+  maxAlignment:    1
+machineFunctionInfo:
+  varArgsFrameIndex: 0
+  varArgsSaveSize: 0
+body:             |
+  bb.1:
+    liveins: $x10, $x11, $x12
+
+    ; CHECK-LABEL: name: store_i64_zilsd
+    ; CHECK: liveins: $x10, $x11, $x12
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[COPY:%[0-9]+]]:gprb(p0) = COPY $x10
+    ; CHECK-NEXT: [[COPY1:%[0-9]+]]:gprb(s32) = COPY $x11
+    ; CHECK-NEXT: [[COPY2:%[0-9]+]]:gprb(s32) = COPY $x12
+    ; CHECK-NEXT: [[MV:%[0-9]+]]:gprb(s64) = G_MERGE_VALUES [[COPY1]](s32), [[COPY2]](s32)
+    ; CHECK-NEXT: G_STORE [[MV]](s64), [[COPY]](p0) :: (store (s64))
+    ; CHECK-NEXT: PseudoRET
+    %0:_(p0) = COPY $x10
+    %1:_(s32) = COPY $x11
+    %2:_(s32) = COPY $x12
+    %3:_(s64) = G_MERGE_VALUES %1(s32), %2(s32)
+    G_STORE %3(s64), %0(p0) :: (store (s64), align 8)
+    PseudoRET
+
+...
diff --git a/llvm/test/CodeGen/RISCV/GlobalISel/zilsd.ll b/llvm/test/CodeGen/RISCV/GlobalISel/zilsd.ll
new file mode 100644
index 00000000000000..e8dfe89685a826
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/GlobalISel/zilsd.ll
@@ -0,0 +1,779 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5
+; RUN: llc -mtriple=riscv32 -mattr=+zilsd -global-isel -global-isel-abort=1 < %s \
+; RUN:   | FileCheck -check-prefixes=CHECK,SLOW %s
+; RUN: llc -mtriple=riscv32 -mattr=+zilsd,+unaligned-scalar-mem -global-isel -global-isel-abort=1 < %s \
+; RUN:   | FileCheck -check-prefixes=CHECK,FAST %s
+; RUN: llc -mtriple=riscv32 -mattr=+zilsd,+zilsd-word-align -global-isel -global-isel-abort=1 < %s \
+; RUN:   | FileCheck -check-prefixes=CHECK,4BYTEALIGN %s
+; RUN: llc -mtriple=riscv32 -mattr=+d,+zilsd -target-abi=ilp32d -global-isel \
+; RUN:   -global-isel-abort=1 < %s | FileCheck -check-prefix=WITHD %s
+
+define i64 @load(ptr %a) nounwind {
+; CHECK-LABEL: load:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    mv a2, a0
+; CHECK-NEXT:    ld a0, 80(a0)
+; CHECK-NEXT:    ld zero, 0(a2)
+; CHECK-NEXT:    ret
+;
+; WITHD-LABEL: load:
+; WITHD:       # %bb.0:
+; WITHD-NEXT:    addi sp, sp, -16
+; WITHD-NEXT:    fld fa5, 80(a0)
+; WITHD-NEXT:    fld fa4, 0(a0)
+; WITHD-NEXT:    fsd fa5, 8(sp)
+; WITHD-NEXT:    ld a0, 8(sp)
+; WITHD-NEXT:    addi sp, sp, 16
+; WITHD-NEXT:    ret
+  %1 = getelementptr i64, ptr %a, i32 10
+  %2 = load i64, ptr %1
+  %3 = load volatile i64, ptr %a
+  ret i64 %2
+}
+
+define i64 @load_align4(ptr %a) nounwind {
+; SLOW-LABEL: load_align4:
+; SLOW:       # %bb.0:
+; SLOW-NEXT:    lw a2, 80(a0)
+; SLOW-NEXT:    lw a1, 84(a0)
+; SLOW-NEXT:    mv a0, a2
+; SLOW-NEXT:    ret
+;
+; FAST-LABEL: load_align4:
+; FAST:       # %bb.0:
+; FAST-NEXT:    ld a0, 80(a0)
+; FAST-NEXT:    ret
+;
+; 4BYTEALIGN-LABEL: load_align4:
+; 4BYTEALIGN:       # %bb.0:
+; 4BYTEALIGN-NEXT:    ld a0, 80(a0)
+; 4BYTEALIGN-NEXT:    ret
+;
+; WITHD-LABEL: load_align4:
+; WITHD:       # %bb.0:
+; WITHD-NEXT:    lw a2, 80(a0)
+; WITHD-NEXT:    lw a1, 84(a0)
+; WITHD-NEXT:    mv a0, a2
+; WITHD-NEXT:    ret
+  %1 = getelementptr i64, ptr %a, i32 10
+  %2 = load i64, ptr %1, align 4
+  ret i64 %2
+}
+
+define void @store(ptr %a, i64 %b) nounwind {
+; CHECK-LABEL: store:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    mv a4, a1
+; CHECK-NEXT:    mv a5, a2
+; CHECK-NEXT:    sd a4, 0(a0)
+; CHECK-NEXT:    sd a4, 88(a0)
+; CHECK-NEXT:    ret
+;
+; WITHD-LABEL: store:
+; WITHD:       # %bb.0:
+; WITHD-NEXT:    addi sp, sp, -16
+; WITHD-NEXT:    sw a1, 8(sp)
+; WITHD-NEXT:    sw a2, 12(sp)
+; WITHD-NEXT:    fld fa5, 8(sp)
+; WITHD-NEXT:    fsd fa5, 0(a0)
+; WITHD-NEXT:    fsd fa5, 88(a0)
+; WITHD-NEXT:    addi sp, sp, 16
+; WITHD-NEXT:    ret
+  store i64 %b, ptr %a
+  %1 = getelementptr i64, ptr %a, i32 11
+  store i64 %b, ptr %1
+  ret void
+}
+
+define void @store_align4(ptr %a, i64 %b) nounwind {
+; SLOW-LABEL: store_align4:
+; SLOW:       # %bb.0:
+; SLOW-NEXT:    sw a1, 88(a0)
+; SLOW-NEXT:    sw a2, 92(a0)
+; SLOW-NEXT:    ret
+;
+; FAST-LABEL: store_align4:
+; FAST:       # %bb.0:
+; FAST-NEXT:    mv a4, a1
+; FAST-NEXT:    mv a5, a2
+; FAST-NEXT:    sd a4, 88(a0)
+; FAST-NEXT:    ret
+;
+; 4BYTEALIGN-LABEL: store_align4:
+; 4BYTEALIGN:       # %bb.0:
+; 4BYTEALIGN-NEXT:    sw a1, 88(a0)
+; 4BYTEALIGN-NEXT:    sw a2, 92(a0)
+; 4BYTEALIGN-NEXT:    ret
+;
+; WITHD-LABEL: store_align4:
+; WITHD:       # %bb.0:
+; WITHD-NEXT:    sw a1, 88(a0)
+; WITHD-NEXT:    sw a2, 92(a0)
+; WITHD-NEXT:    ret
+  %1 = getelementptr i64, ptr %a, i32 11
+  store i64 %b, ptr %1, align 4
+  ret void
+}
+
+define i64 @load_unaligned(ptr %p) {
+; SLOW-LABEL: load_unaligned:
+; SLOW:       # %bb.0:
+; SLOW-NEXT:    lbu a1, 1(a0)
+; SLOW-NEXT:    lbu a2, 3(a0)
+; SLOW-NEXT:    lbu a3, 0(a0)
+; SLOW-NEXT:    lbu a4, 2(a0)
+; SLOW-NEXT:    lbu a5, 5(a0)
+; SLOW-NEXT:    lbu a6, 7(a0)
+; SLOW-NEXT:    lbu a7, 4(a0)
+; SLOW-NEXT:    lbu a0, 6(a0)
+; SLOW-NEXT:    slli a1, a1, 8
+; SLOW-NEXT:    slli a2, a2, 8
+; SLOW-NEXT:    or a1, a1, a3
+; SLOW-NEXT:    or a2, a2, a4
+; SLOW-NEXT:    slli a5, a5, 8
+; SLOW-NEXT:    slli a6, a6, 8
+; SLOW-NEXT:    or a3, a5, a7
+; SLOW-NEXT:    or a0, a6, a0
+; SLOW-NEXT:    slli a2, a2, 16
+; SLOW-NEXT:    slli a4, a0, 16
+; SLOW-NEXT:    or a0, a2, a1
+; SLOW-NEXT:    or a1, a4, a3
+; SLOW-NEXT:    ret
+;
+; FAST-LABEL: load_unaligned:
+; FAST:       # %bb.0:
+; FAST-NEXT:    ld a0, 0(a0)
+; FAST-NEXT:    ret
+;
+; 4BYTEALIGN-LABEL: load_unaligned:
+; 4BYTEALIGN:       # %bb.0:
+; 4BYTEALIGN-NEXT:    lbu a1, 1(a0)
+; 4BYTEALIGN-NEXT:    lbu a2, 3(a0)
+; 4BYTEALIGN-NEXT:    lbu a3, 0(a0)
+; 4BYTEALIGN-NEXT:    lbu a4, 2(a0)
+; 4BYTEALIGN-NEXT:    lbu a5, 5(a0)
+; 4BYTEALIGN-NEXT:    lbu a6, 7(a0)
+; 4BYTEALIGN-NEXT:    lbu a7, 4(a0)
+; 4BYTEALIGN-NEXT:    lbu a0, 6(a0)
+; 4BYTEALIGN-NEXT:    slli a1, a1, 8
+; 4BYTEALIGN-NEXT:    slli a2, a2, 8
+; 4BYTEALIGN-NEXT:    or a1, a1, a3
+; 4BYTEALIGN-NEXT:    or a2, a2, a4
+; 4BYTEALIGN-NEXT:    slli a5, a5, 8
+; 4BYTEALIGN-NEXT:    slli a6, a6, 8
+; 4BYTEALIGN-NEXT:    or a3, a5, a7
+; 4BYTEALIGN-NEXT:    or a0, a6, a0
+; 4BYTEALIGN-NEXT:    slli a2, a2, 16
+; 4BYTEALIGN-NEXT:    slli a4, a0, 16
+; 4BYTEALIGN-NEXT:    or a0, a2, a1
+; 4BYTEALIGN-NEXT:    or a1, a4, a3
+; 4BYTEALIGN-NEXT:    ret
+;
+; WITHD-LABEL: load_unaligned:
+; WITHD:       # %bb.0:
+; WITHD-NEXT:    lbu a1, 1(a0)
+; WITHD-NEXT:    lbu a2, 3(a0)
+; WITHD-NEXT:    lbu a3, 0(a0)
+; WITHD-NEXT:    lbu a4, 2(a0)
+; WITHD-NEXT:    lbu a5, 5(a0)
+; WITHD-NEXT:    lbu a6, 7(a0)
+; WITHD-NEXT:    lbu a7, 4(a0)
+; WITHD-NEXT:    lbu a0, 6(a0)
+; WITHD-NEXT:    slli a1, a1, 8
+; WITHD-NEXT:    slli a2, a2, 8
+; WITHD-NEXT:    or a1, a1, a3
+; WITHD-NEXT:    or a2, a2, a4
+; WITHD-NEXT:    slli a5, a5, 8
+; WITHD-NEXT:    slli a6, a6, 8
+; WITHD-NEXT:    or a3, a5, a7
+; WITHD-NEXT:    or a0, a6, a0
+; WITHD-NEXT:    slli a2, a2, 16
+; WITHD-NEXT:    slli a4, a0, 16
+; WITHD-NEXT:    or a0, a2, a1
+; WITHD-NEXT:    or a1, a4, a3
+; WITHD-NEXT:    ret
+  %res = load i64, ptr %p, align 1
+  ret i64 %res
+}
+
+define void @store_unaligned(ptr %p, i64 %v) {
+; SLOW-LABEL: store_unaligned:
+; SLOW:       # %bb.0:
+; SLOW-NEXT:    lui a3, 16
+; SLOW-NEXT:    addi a3, a3, -1
+; SLOW-NEXT:    srli a4, a1, 16
+; SLOW-NEXT:    and a5, a1, a3
+; SLOW-NEXT:    srli a5, a5, 8
+; SLOW-NEXT:    srli a6, a1, 24
+; SLOW-NEXT:    sb a1, 0(a0)
+; SLOW-NEXT:    sb a5, 1(a0)
+; SLOW-NEXT:    sb a4, 2(a0)
+; SLOW-NEXT:    sb a6, 3(a0)
+; SLOW-NEXT:    srli a1, a2, 16
+; SLOW-NEXT:    and a3, a2, a3
+; SLOW-NEXT:    srli a3, a3, 8
+; SLOW-NEXT:    srli a4, a2, 24
+; SLOW-NEXT:    sb a2, 4(a0)
+; SLOW-NEXT:    sb a3, 5(a0)
+; SLOW-NEXT:    sb a1, 6(a0)
+; SLOW-NEXT:    sb a4, 7(a0)
+; SLOW-NEXT:    ret
+;
+; FAST-LABEL: store_unaligned:
+; FAST:       # %bb.0:
+; FAST-NEXT:    mv a4, a1
+; FAST-NEXT:    mv a5, a2
+; FAST-NEXT:    sd a4, 0(a0)
+; FAST-NEXT:    ret
+;
+; 4BYTEALIGN-LABEL: store_unaligned:
+; 4BYTEALIGN:       # %bb.0:
+; 4BYTEALIGN-NEXT:    lui a3, 16
+; 4BYTEALIGN-NEXT:    addi a3, a3, -1
+; 4BYTEALIGN-NEXT:    srli a4, a1, 16
+; 4BYTEALIGN-NEXT:    and a5, a1, a3
+; 4BYTEALIGN-NEXT:    srli a5, a5, 8
+; 4BYTEALIGN-NEXT:    srli a6, a1, 24
+; 4BYTEALIGN-NEXT:    sb a1, 0(a0)
+; 4BYTEALIGN-NEXT:    sb a5, 1(a0)
+; 4BYTEALIGN-NEXT:    sb a4, 2(a0)
+; 4BYTEALIGN-NEXT:    sb a6, 3(a0)
+; 4BYTEALIGN-NEXT:    srli a1, a2, 16
+; 4BYTEALIGN-NEXT:    and a3, a2, a3
+; 4BYTEALIGN-NEXT:    srli a3, a3, 8
+; 4BYTEALIGN-NEXT:    srli a4, a2, 24
+; 4BYTEALIGN-NEXT:    sb a2, 4(a0)
+; 4BYTEALIGN-NEXT:    sb a3, 5(a0)
+; 4BYTEALIGN-NEXT:    sb a1, 6(a0)
+; 4BYTEALIGN-NEXT:    sb a4, 7(a0)
+; 4BYTEALIGN-NEXT:    ret
+;
+; WITHD-LABEL: store_unaligned:
+; WITHD:       # %bb.0:
+; WITHD-NEXT:    lui a3, 16
+; WITHD-NEXT:    addi a3, a3, -1
+; WITHD-NEXT:    srli a4, a1, 16
+; WITHD-NEXT:    and a5, a1, a3
+; WITHD-NEXT:    srli a5, a5, 8
+; WITHD-NEXT:    srli a6, a1, 24
+; WITHD-NEXT:    sb a1, 0(a0)
+; WITHD-NEXT:    sb a5, 1(a0)
+; WITHD-NEXT:    sb a4, 2(a0)
+; WITHD-NEXT:    sb a6, 3(a0)
+; WITHD-NEXT:    srli a1, a2, 16
+; WITHD-NEXT:    and a3, a2, a3
+; WITHD-NEXT:    srli a3, a3, 8
+; WITHD-NEXT:    srli a4, a2, 24
+; WITHD-NEXT:    sb a2, 4(a0)
+; WITHD-NEXT:    sb a3, 5(a0)
+; WITHD-NEXT:    sb a1, 6(a0)
+; WITHD-NEXT:    sb a4, 7(a0)
+; WITHD-NEXT:    ret
+  store i64 %v, ptr %p, align 1
+  ret void
+}
+
+ at g = dso_local global i64 0, align 8
+
+define i64 @load_g() nounwind {
+; CHECK-LABEL: load_g:
+; CHECK:       # %bb.0: # %entry
+; CHECK-NEXT:    lui a0, %hi(g)
+; CHECK-NEXT:    ld a0, %lo(g)(a0)
+; CHECK-NEXT:    ret
+;
+; WITHD-LABEL: load_g:
+; WITHD:       # %bb.0: # %entry
+; WITHD-NEXT:    addi sp, sp, -16
+; WITHD-NEXT:    lui a0, %hi(g)
+; WITHD-NEXT:    fld fa5, %lo(g)(a0)
+; WITHD-NEXT:    fsd fa5, 8(sp)
+; WITHD-NEXT:    ld a0, 8(sp)
+; WITHD-NEXT:    addi sp, sp, 16
+; WITHD-NEXT:    ret
+entry:
+  %0 = load i64, ptr @g
+  ret i64 %0
+}
+
+define void @store_g() nounwind {
+; CHECK-LABEL: store_g:
+; CHECK:       # %bb.0: # %entyr
+; CHECK-NEXT:    li a0, 0
+; CHECK-NEXT:    lui a2, %hi(g)
+; CHECK-NEXT:    li a1, 0
+; CHECK-NEXT:    sd a0, %lo(g)(a2)
+; CHECK-NEXT:    ret
+;
+; WITHD-LABEL: store_g:
+; WITHD:       # %bb.0: # %entyr
+; WITHD-NEXT:    addi sp, sp, -16
+; WITHD-NEXT:    sw zero, 8(sp)
+; WITHD-NEXT:    sw zero, 12(sp)
+; WITHD-NEXT:    fld fa5, 8(sp)
+; WITHD-NEXT:    lui a0, %hi(g)
+; WITHD-NEXT:    fsd fa5, %lo(g)(a0)
+; WITHD-NEXT:    addi sp, sp, 16
+; WITHD-NEXT:    ret
+entyr:
+  store i64 0, ptr @g
+  ret void
+}
+
+define void @large_offset(ptr nocapture %p, i64 %d) nounwind {
+; CHECK-LABEL: large_offset:
+; CHECK:       # %bb.0: # %entry
+; CHECK-NEXT:    lui a1, 4
+; CHECK-NEXT:    add a0, a0, a1
+; CHECK-NEXT:    ld a2, -384(a0)
+; CHECK-NEXT:    addi a2, a2, 1
+; CHECK-NEXT:    seqz a1, a2
+; CHECK-NEXT:    add a3, a3, a1
+; CHECK-NEXT:    sd a2, -384(a0)
+; CHECK-NEXT:    ret
+;
+; WITHD-LABEL: large_offset:
+; WITHD:       # %bb.0: # %entry
+; WITHD-NEXT:    addi sp, sp, -16
+; WITHD-NEXT:    lui a1, 4
+; WITHD-NEXT:    add a0, a0, a1
+; WITHD-NEXT:    fld fa5, -384(a0)
+; WITHD-NEXT:    fsd fa5, 8(sp)
+; WITHD-NEXT:    ld a2, 8(sp)
+; WITHD-NEXT:    addi a2, a2, 1
+; WITHD-NEXT:    seqz a1, a2
+; WITHD-NEXT:    add a3, a3, a1
+; WITHD-NEXT:    sd a2, 8(sp)
+; WITHD-NEXT:    fld fa5, 8(sp)
+; WITHD-NEXT:    fsd fa5, -384(a0)
+; WITHD-NEXT:    addi sp, sp, 16
+; WITHD-NEXT:    ret
+entry:
+  %add.ptr = getelementptr inbounds i64, ptr %p, i64 2000
+  %a = load i64, ptr %add.ptr, align 8
+  %b = add i64 %a, 1
+  store i64 %b, ptr %add.ptr, align 8
+  ret void
+}
+
+define void @fold_frame_idx(i32 %val1, i32 %val2, ptr nocapture %p) nounwind {
+; SLOW-LABEL: fold_frame_idx:
+; SLOW:       # %bb.0: # %entry
+; SLOW-NEXT:    addi sp, sp, -400
+; SLOW-NEXT:    sw a0, 40(sp)
+; SLOW-NEXT:    sw a1, 44(sp)
+; SLOW-NEXT:    addi sp, sp, 400
+; SLOW-NEXT:    ret
+;
+; FAST-LABEL: fold_frame_idx:
+; FAST:       # %bb.0: # %entry
+; FAST-NEXT:    addi sp, sp, -400
+; FAST-NEXT:    sd a0, 40(sp)
+; FAST-NEXT:    addi sp, sp, 400
+; FAST-NEXT:    ret
+;
+; 4BYTEALIGN-LABEL: fold_frame_idx:
+; 4BYTEALIGN:       # %bb.0: # %entry
+; 4BYTEALIGN-NEXT:    addi sp, sp, -400
+; 4BYTEALIGN-NEXT:    sd a0, 40(sp)
+; 4BYTEALIGN-NEXT:    addi sp, sp, 400
+; 4BYTEALIGN-NEXT:    ret
+;
+; WITHD-LABEL: fold_frame_idx:
+; WITHD:       # %bb.0: # %entry
+; WITHD-NEXT:    addi sp, sp, -400
+; WITHD-NEXT:    sw a0, 40(sp)
+; WITHD-NEXT:    sw a1, 44(sp)
+; WITHD-NEXT:    addi sp, sp, 400
+; WITHD-NEXT:    ret
+entry:
+  %local = alloca [100 x i32]
+  %local.ptr = getelementptr inbounds i32, ptr %local, i64 10
+  %local1.ptr = getelementptr inbounds i32, ptr %local, i64 11
+  store i32 %val1, ptr %local.ptr, align 4
+  store i32 %val2, ptr %local1.ptr, align 4
+  ret void
+}
+
+define void @dont_fold_frame_idx(ptr nocapture %p, i32 %val1, i32 %val2) nounwind {
+; CHECK-LABEL: dont_fold_frame_idx:
+; CHECK:       # %bb.0: # %entry
+; CHECK-NEXT:    addi sp, sp, -400
+; CHECK-NEXT:    sw a1, 40(sp)
+; CHECK-NEXT:    sw a2, 44(sp)
+; CHECK-NEXT:    addi sp, sp, 400
+; CHECK-NEXT:    ret
+;
+; WITHD-LABEL: dont_fold_frame_idx:
+; WITHD:       # %bb.0: # %entry
+; WITHD-NEXT:    addi sp, sp, -400
+; WITHD-NEXT:    sw a1, 40(sp)
+; WITHD-NEXT:    sw a2, 44(sp)
+; WITHD-NEXT:    addi sp, sp, 400
+; WITHD-NEXT:    ret
+entry:
+  %local = alloca [100 x i32]
+  %local.ptr = getelementptr inbounds i32, ptr %local, i64 10
+  %local1.ptr = getelementptr inbounds i32, ptr %local, i64 11
+  store i32 %val1, ptr %local.ptr, align 4
+  store i32 %val2, ptr %local1.ptr, align 4
+  ret void
+}
+
+define void @basic_store_zero_combine(ptr %0, i32 %1, i32 %2) {
+; CHECK-LABEL: basic_store_zero_combine:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    sw zero, 0(a0)
+; CHECK-NEXT:    sw zero, 4(a0)
+; CHECK-NEXT:    ret
+;
+; WITHD-LABEL: basic_store_zero_combine:
+; WITHD:       # %bb.0:
+; WITHD-NEXT:    sw zero, 0(a0)
+; WITHD-NEXT:    sw zero, 4(a0)
+; WITHD-NEXT:    ret
+  store i32 0, ptr %0, align 4
+  %4 = getelementptr inbounds i32, ptr %0, i32 1
+  store i32 0, ptr %4, align 4
+  ret void
+}
+
+declare void @llvm.memset.p0.i32(ptr, i8, i32, i1 immarg)
+
+define void @no_coalesce() #0 {
+; SLOW-LABEL: no_coalesce:
+; SLOW:       # %bb.0: # %entry
+; SLOW-NEXT:    addi sp, sp, -16
+; SLOW-NEXT:    .cfi_def_cfa_offset 16
+; SLOW-NEXT:    sw ra, 12(sp) # 4-byte Folded Spill
+; SLOW-NEXT:    sd s0, 0(sp) # 8-byte Folded Spill
+; SLOW-NEXT:    .cfi_offset ra, -4
+; SLOW-NEXT:    .cfi_offset s0, -16
+; SLOW-NEXT:    .cfi_offset s1, -12
+; SLOW-NEXT:    .cfi_remember_state
+; SLOW-NEXT:    li s0, 1
+; SLOW-NEXT:    li s1, 0
+; SLOW-NEXT:    j .LBB12_2
+; SLOW-NEXT:  # %bb.1: # %crash
+; SLOW-NEXT:    sd s0, 0(zero)
+; SLOW-NEXT:    li a2, 12
+; SLOW-NEXT:    li a0, 0
+; SLOW-NEXT:    li a1, 0
+; SLOW-NEXT:    call memset
+; SLOW-NEXT:  .LBB12_2: # %loop
+; SLOW-NEXT:    # =>This Inner Loop Header: Depth=1
+; SLOW-NEXT:    .cfi_restore_state
+; SLOW-NEXT:  # %bb.3: # %loop
+; SLOW-NEXT:    # in Loop: Header=BB12_2 Depth=1
+; SLOW-NEXT:    j .LBB12_2
+; SLOW-NEXT:  # %bb.4: # %exit
+; SLOW-NEXT:    lw ra, 12(sp) # 4-byte Folded Reload
+; SLOW-NEXT:    ld s0, 0(sp) # 8-byte Folded Reload
+; SLOW-NEXT:    .cfi_restore ra
+; SLOW-NEXT:    .cfi_restore s0
+; SLOW-NEXT:    .cfi_restore s1
+; SLOW-NEXT:    addi sp, sp, 16
+; SLOW-NEXT:    .cfi_def_cfa_offset 0
+; SLOW-NEXT:    ret
+;
+; FAST-LABEL: no_coalesce:
+; FAST:       # %bb.0: # %entry
+; FAST-NEXT:    addi sp, sp, -16
+; FAST-NEXT:    .cfi_def_cfa_offset 16
+; FAST-NEXT:    sw ra, 12(sp) # 4-byte Folded Spill
+; FAST-NEXT:    sd s0, 4(sp) # 8-byte Folded Spill
+; FAST-NEXT:    .cfi_offset ra, -4
+; FAST-NEXT:    .cfi_offset s0, -12
+; FAST-NEXT:    .cfi_offset s1, -8
+; FAST-NEXT:    .cfi_remember_state
+; FAST-NEXT:    li s0, 1
+; FAST-NEXT:    li s1, 0
+; FAST-NEXT:    j .LBB12_2
+; FAST-NEXT:  # %bb.1: # %crash
+; FAST-NEXT:    sd s0, 0(zero)
+; FAST-NEXT:    li a2, 12
+; FAST-NEXT:    li a0, 0
+; FAST-NEXT:    li a1, 0
+; FAST-NEXT:    call memset
+; FAST-NEXT:  .LBB12_2: # %loop
+; FAST-NEXT:    # =>This Inner Loop Header: Depth=1
+; FAST-NEXT:    .cfi_restore_state
+; FAST-NEXT:  # %bb.3: # %loop
+; FAST-NEXT:    # in Loop: Header=BB12_2 Depth=1
+; FAST-NEXT:    j .LBB12_2
+; FAST-NEXT:  # %bb.4: # %exit
+; FAST-NEXT:    lw ra, 12(sp) # 4-byte Folded Reload
+; FAST-NEXT:    ld s0, 4(sp) # 8-byte Folded Reload
+; FAST-NEXT:    .cfi_restore ra
+; FAST-NEXT:    .cfi_restore s0
+; FAST-NEXT:    .cfi_restore s1
+; FAST-NEXT:    addi sp, sp, 16
+; FAST-NEXT:    .cfi_def_cfa_offset 0
+; FAST-NEXT:    ret
+;
+; 4BYTEALIGN-LABEL: no_coalesce:
+; 4BYTEALIGN:       # %bb.0: # %entry
+; 4BYTEALIGN-NEXT:    addi sp, sp, -16
+; 4BYTEALIGN-NEXT:    .cfi_def_cfa_offset 16
+; 4BYTEALIGN-NEXT:    sw ra, 12(sp) # 4-byte Folded Spill
+; 4BYTEALIGN-NEXT:    sd s0, 4(sp) # 8-byte Folded Spill
+; 4BYTEALIGN-NEXT:    .cfi_offset ra, -4
+; 4BYTEALIGN-NEXT:    .cfi_offset s0, -12
+; 4BYTEALIGN-NEXT:    .cfi_offset s1, -8
+; 4BYTEALIGN-NEXT:    .cfi_remember_state
+; 4BYTEALIGN-NEXT:    li s0, 1
+; 4BYTEALIGN-NEXT:    li s1, 0
+; 4BYTEALIGN-NEXT:    j .LBB12_2
+; 4BYTEALIGN-NEXT:  # %bb.1: # %crash
+; 4BYTEALIGN-NEXT:    sd s0, 0(zero)
+; 4BYTEALIGN-NEXT:    li a2, 12
+; 4BYTEALIGN-NEXT:    li a0, 0
+; 4BYTEALIGN-NEXT:    li a1, 0
+; 4BYTEALIGN-NEXT:    call memset
+; 4BYTEALIGN-NEXT:  .LBB12_2: # %loop
+; 4BYTEALIGN-NEXT:    # =>This Inner Loop Header: Depth=1
+; 4BYTEALIGN-NEXT:    .cfi_restore_state
+; 4BYTEALIGN-NEXT:  # %bb.3: # %loop
+; 4BYTEALIGN-NEXT:    # in Loop: Header=BB12_2 Depth=1
+; 4BYTEALIGN-NEXT:    j .LBB12_2
+; 4BYTEALIGN-NEXT:  # %bb.4: # %exit
+; 4BYTEALIGN-NEXT:    lw ra, 12(sp) # 4-byte Folded Reload
+; 4BYTEALIGN-NEXT:    ld s0, 4(sp) # 8-byte Folded Reload
+; 4BYTEALIGN-NEXT:    .cfi_restore ra
+; 4BYTEALIGN-NEXT:    .cfi_restore s0
+; 4BYTEALIGN-NEXT:    .cfi_restore s1
+; 4BYTEALIGN-NEXT:    addi sp, sp, 16
+; 4BYTEALIGN-NEXT:    .cfi_def_cfa_offset 0
+; 4BYTEALIGN-NEXT:    ret
+;
+; WITHD-LABEL: no_coalesce:
+; WITHD:       # %bb.0: # %entry
+; WITHD-NEXT:    addi sp, sp, -32
+; WITHD-NEXT:    .cfi_def_cfa_offset 32
+; WITHD-NEXT:    sw ra, 28(sp) # 4-byte Folded Spill
+; WITHD-NEXT:    fsd fs0, 16(sp) # 8-byte Folded Spill
+; WITHD-NEXT:    .cfi_offset ra, -4
+; WITHD-NEXT:    .cfi_offset fs0, -16
+; WITHD-NEXT:    .cfi_remember_state
+; WITHD-NEXT:    li a0, 1
+; WITHD-NEXT:    sw a0, 8(sp)
+; WITHD-NEXT:    sw zero, 12(sp)
+; WITHD-NEXT:    fld fs0, 8(sp)
+; WITHD-NEXT:    j .LBB12_2
+; WITHD-NEXT:  # %bb.1: # %crash
+; WITHD-NEXT:    fsd fs0, 0(zero)
+; WITHD-NEXT:    li a2, 12
+; WITHD-NEXT:    li a0, 0
+; WITHD-NEXT:    li a1, 0
+; WITHD-NEXT:    call memset
+; WITHD-NEXT:  .LBB12_2: # %loop
+; WITHD-NEXT:    # =>This Inner Loop Header: Depth=1
+; WITHD-NEXT:    .cfi_restore_state
+; WITHD-NEXT:  # %bb.3: # %loop
+; WITHD-NEXT:    # in Loop: Header=BB12_2 Depth=1
+; WITHD-NEXT:    j .LBB12_2
+; WITHD-NEXT:  # %bb.4: # %exit
+; WITHD-NEXT:    lw ra, 28(sp) # 4-byte Folded Reload
+; WITHD-NEXT:    fld fs0, 16(sp) # 8-byte Folded Reload
+; WITHD-NEXT:    .cfi_restore ra
+; WITHD-NEXT:    .cfi_restore fs0
+; WITHD-NEXT:    addi sp, sp, 32
+; WITHD-NEXT:    .cfi_def_cfa_offset 0
+; WITHD-NEXT:    ret
+entry:
+  br label %loop
+
+loop:
+  switch i8 0, label %loop [
+    i8 1, label %crash
+    i8 3, label %exit
+  ]
+
+crash:
+  store i64 1, ptr null, align 8
+  call void @llvm.memset.p0.i32(ptr null, i8 0, i32 12, i1 false)
+  br label %loop
+
+exit:
+  ret void
+}
+
+;; With D present an s64 could be either an f64 (FPR64) or an i64 (GPRPair), and
+;; LLT cannot tell them apart, so these must not crash. See useGPRPairForS64().
+define i64 @select_i64(i1 %c, i64 %a, i64 %b) nounwind {
+; CHECK-LABEL: select_i64:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    mv a5, a0
+; CHECK-NEXT:    mv a0, a1
+; CHECK-NEXT:    mv a1, a2
+; CHECK-NEXT:    andi a5, a5, 1
+; CHECK-NEXT:    bnez a5, .LBB13_2
+; CHECK-NEXT:  # %bb.1:
+; CHECK-NEXT:    mv a0, a3
+; CHECK-NEXT:    mv a1, a4
+; CHECK-NEXT:  .LBB13_2:
+; CHECK-NEXT:    ret
+;
+; WITHD-LABEL: select_i64:
+; WITHD:       # %bb.0:
+; WITHD-NEXT:    addi sp, sp, -16
+; WITHD-NEXT:    mv a7, a2
+; WITHD-NEXT:    sw a1, 8(sp)
+; WITHD-NEXT:    sw a7, 12(sp)
+; WITHD-NEXT:    fld fa5, 8(sp)
+; WITHD-NEXT:    andi a0, a0, 1
+; WITHD-NEXT:    sw a3, 8(sp)
+; WITHD-NEXT:    sw a4, 12(sp)
+; WITHD-NEXT:    bnez a0, .LBB13_2
+; WITHD-NEXT:  # %bb.1:
+; WITHD-NEXT:    fld fa5, 8(sp)
+; WITHD-NEXT:  .LBB13_2:
+; WITHD-NEXT:    fsd fa5, 8(sp)
+; WITHD-NEXT:    ld a0, 8(sp)
+; WITHD-NEXT:    addi sp, sp, 16
+; WITHD-NEXT:    ret
+  %r = select i1 %c, i64 %a, i64 %b
+  ret i64 %r
+}
+
+define double @select_f64(i1 %c, double %a, double %b) nounwind {
+; CHECK-LABEL: select_f64:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    mv a5, a0
+; CHECK-NEXT:    mv a0, a1
+; CHECK-NEXT:    mv a1, a2
+; CHECK-NEXT:    andi a5, a5, 1
+; CHECK-NEXT:    bnez a5, .LBB14_2
+; CHECK-NEXT:  # %bb.1:
+; CHECK-NEXT:    mv a0, a3
+; CHECK-NEXT:    mv a1, a4
+; CHECK-NEXT:  .LBB14_2:
+; CHECK-NEXT:    ret
+;
+; WITHD-LABEL: select_f64:
+; WITHD:       # %bb.0:
+; WITHD-NEXT:    andi a0, a0, 1
+; WITHD-NEXT:    bnez a0, .LBB14_2
+; WITHD-NEXT:  # %bb.1:
+; WITHD-NEXT:    fmv.d fa0, fa1
+; WITHD-NEXT:  .LBB14_2:
+; WITHD-NEXT:    ret
+  %r = select i1 %c, double %a, double %b
+  ret double %r
+}
+
+define double @load_select_f64(ptr %p, ptr %q, i1 %c) nounwind {
+; CHECK-LABEL: load_select_f64:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    andi a2, a2, 1
+; CHECK-NEXT:    bnez a2, .LBB15_2
+; CHECK-NEXT:  # %bb.1:
+; CHECK-NEXT:    ld a0, 0(a1)
+; CHECK-NEXT:    ret
+; CHECK-NEXT:  .LBB15_2:
+; CHECK-NEXT:    ld a0, 0(a0)
+; CHECK-NEXT:    ret
+;
+; WITHD-LABEL: load_select_f64:
+; WITHD:       # %bb.0:
+; WITHD-NEXT:    andi a2, a2, 1
+; WITHD-NEXT:    bnez a2, .LBB15_2
+; WITHD-NEXT:  # %bb.1:
+; WITHD-NEXT:    fld fa0, 0(a1)
+; WITHD-NEXT:    ret
+; WITHD-NEXT:  .LBB15_2:
+; WITHD-NEXT:    fld fa0, 0(a0)
+; WITHD-NEXT:    ret
+  %a = load double, ptr %p, align 8
+  %b = load double, ptr %q, align 8
+  %r = select i1 %c, double %a, double %b
+  ret double %r
+}
+
+define i64 @mixed_i64_f64(ptr %p) nounwind {
+; SLOW-LABEL: mixed_i64_f64:
+; SLOW:       # %bb.0:
+; SLOW-NEXT:    addi sp, sp, -16
+; SLOW-NEXT:    sw ra, 12(sp) # 4-byte Folded Spill
+; SLOW-NEXT:    sd s0, 0(sp) # 8-byte Folded Spill
+; SLOW-NEXT:    lui a1, %hi(.LCPI16_0)
+; SLOW-NEXT:    ld a2, %lo(.LCPI16_0)(a1)
+; SLOW-NEXT:    ld s0, 0(a0)
+; SLOW-NEXT:    mv a0, s0
+; SLOW-NEXT:    mv a1, s1
+; SLOW-NEXT:    call __adddf3
+; SLOW-NEXT:    add a0, a0, s0
+; SLOW-NEXT:    sltu a2, a0, s0
+; SLOW-NEXT:    add a1, a1, s1
+; SLOW-NEXT:    add a1, a1, a2
+; SLOW-NEXT:    lw ra, 12(sp) # 4-byte Folded Reload
+; SLOW-NEXT:    ld s0, 0(sp) # 8-byte Folded Reload
+; SLOW-NEXT:    addi sp, sp, 16
+; SLOW-NEXT:    ret
+;
+; FAST-LABEL: mixed_i64_f64:
+; FAST:       # %bb.0:
+; FAST-NEXT:    addi sp, sp, -16
+; FAST-NEXT:    sw ra, 12(sp) # 4-byte Folded Spill
+; FAST-NEXT:    sd s0, 4(sp) # 8-byte Folded Spill
+; FAST-NEXT:    lui a1, %hi(.LCPI16_0)
+; FAST-NEXT:    ld a2, %lo(.LCPI16_0)(a1)
+; FAST-NEXT:    ld s0, 0(a0)
+; FAST-NEXT:    mv a0, s0
+; FAST-NEXT:    mv a1, s1
+; FAST-NEXT:    call __adddf3
+; FAST-NEXT:    add a0, a0, s0
+; FAST-NEXT:    sltu a2, a0, s0
+; FAST-NEXT:    add a1, a1, s1
+; FAST-NEXT:    add a1, a1, a2
+; FAST-NEXT:    lw ra, 12(sp) # 4-byte Folded Reload
+; FAST-NEXT:    ld s0, 4(sp) # 8-byte Folded Reload
+; FAST-NEXT:    addi sp, sp, 16
+; FAST-NEXT:    ret
+;
+; 4BYTEALIGN-LABEL: mixed_i64_f64:
+; 4BYTEALIGN:       # %bb.0:
+; 4BYTEALIGN-NEXT:    addi sp, sp, -16
+; 4BYTEALIGN-NEXT:    sw ra, 12(sp) # 4-byte Folded Spill
+; 4BYTEALIGN-NEXT:    sd s0, 4(sp) # 8-byte Folded Spill
+; 4BYTEALIGN-NEXT:    lui a1, %hi(.LCPI16_0)
+; 4BYTEALIGN-NEXT:    ld a2, %lo(.LCPI16_0)(a1)
+; 4BYTEALIGN-NEXT:    ld s0, 0(a0)
+; 4BYTEALIGN-NEXT:    mv a0, s0
+; 4BYTEALIGN-NEXT:    mv a1, s1
+; 4BYTEALIGN-NEXT:    call __adddf3
+; 4BYTEALIGN-NEXT:    add a0, a0, s0
+; 4BYTEALIGN-NEXT:    sltu a2, a0, s0
+; 4BYTEALIGN-NEXT:    add a1, a1, s1
+; 4BYTEALIGN-NEXT:    add a1, a1, a2
+; 4BYTEALIGN-NEXT:    lw ra, 12(sp) # 4-byte Folded Reload
+; 4BYTEALIGN-NEXT:    ld s0, 4(sp) # 8-byte Folded Reload
+; 4BYTEALIGN-NEXT:    addi sp, sp, 16
+; 4BYTEALIGN-NEXT:    ret
+;
+; WITHD-LABEL: mixed_i64_f64:
+; WITHD:       # %bb.0:
+; WITHD-NEXT:    addi sp, sp, -16
+; WITHD-NEXT:    lui a1, 261888
+; WITHD-NEXT:    sw zero, 8(sp)
+; WITHD-NEXT:    sw a1, 12(sp)
+; WITHD-NEXT:    fld fa5, 8(sp)
+; WITHD-NEXT:    fld fa4, 0(a0)
+; WITHD-NEXT:    fadd.d fa5, fa4, fa5
+; WITHD-NEXT:    fsd fa5, 8(sp)
+; WITHD-NEXT:    ld a0, 8(sp)
+; WITHD-NEXT:    fsd fa4, 8(sp)
+; WITHD-NEXT:    ld a2, 8(sp)
+; WITHD-NEXT:    add a0, a0, a2
+; WITHD-NEXT:    sltu a2, a0, a2
+; WITHD-NEXT:    add a1, a1, a3
+; WITHD-NEXT:    add a1, a1, a2
+; WITHD-NEXT:    addi sp, sp, 16
+; WITHD-NEXT:    ret
+  %v = load i64, ptr %p, align 8
+  %d = bitcast i64 %v to double
+  %e = fadd double %d, 1.0
+  %r = bitcast double %e to i64
+  %s = add i64 %r, %v
+  ret i64 %s
+}



More information about the llvm-commits mailing list