[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