[llvm] [MIR] Save internal VirtRegMap state in MIR (PR #197361)
Quentin Colombet via llvm-commits
llvm-commits at lists.llvm.org
Fri Jun 12 07:09:35 PDT 2026
https://github.com/qcolombet updated https://github.com/llvm/llvm-project/pull/197361
>From f751df5ee0183492d358241fa3701b9f741f1c56 Mon Sep 17 00:00:00 2001
From: Quentin Colombet <quentin.colombet at gmail.com>
Date: Fri, 24 Apr 2026 15:37:47 -0700
Subject: [PATCH] [MIR] Save internal VirtRegMap state
Adds two optional fields to the per-vreg YAML record so MIR tests can
express VirtRegMap state that previously had no representation:
registers:
- { id: 1, class: vgpr_32, split-from: '%0', assigned-phys: '$vgpr5' }
Testing passes that consume sibling-register information (e.g.
InlineSpiller) requires constructing a VirtRegMap with split
relationships from a MIR test, which implies triggering live-range
splitting at minimum and make reproducers unnecessarily complicated.
So this change introduces a mechanism to serialize/deserialize the state
of the VirtRegMap pass.
Mechanism:
- For serialization:
- MIRPrinter emits the new fields only when the VirtRegMap is available.
- For deserialization:
- MIRParser stashes parsed entries in the MachineRegisterInfo object
- VirtRegMap::init() drains the stash via assignVirt2Phys and
setIsSplitFromReg, then clears it.
Validation at parse time:
- 'assigned-phys' must be a physical register.
- 'split-from' must reference a different vreg than 'id'.
- 'split-from' physregs / named-vreg references already rejected
by parseStandaloneVirtualRegister.
I'm not super happy about stashing the VRM info in the MRI, but that's
a small price to pay.
---
llvm/include/llvm/CodeGen/MIRPrinter.h | 11 ++++-
llvm/include/llvm/CodeGen/MIRYamlMapping.h | 17 +++++++-
llvm/include/llvm/CodeGen/MachineFunction.h | 7 +---
.../llvm/CodeGen/MachineRegisterInfo.h | 31 ++++++++++++++
llvm/lib/CodeGen/MIRParser/MIRParser.cpp | 40 +++++++++++++++++++
llvm/lib/CodeGen/MIRPrinter.cpp | 33 ++++++++++-----
llvm/lib/CodeGen/MIRPrintingPass.cpp | 11 ++++-
llvm/lib/CodeGen/MachineFunction.cpp | 16 ++++++++
llvm/lib/CodeGen/VirtRegMap.cpp | 13 ++++++
.../MIR/AMDGPU/virtregmap-stash-bad-phys.mir | 18 +++++++++
.../MIR/AMDGPU/virtregmap-stash-bad-split.mir | 16 ++++++++
.../AMDGPU/virtregmap-stash-self-split.mir | 17 ++++++++
.../CodeGen/MIR/AMDGPU/virtregmap-stash.mir | 32 +++++++++++++++
13 files changed, 242 insertions(+), 20 deletions(-)
create mode 100644 llvm/test/CodeGen/MIR/AMDGPU/virtregmap-stash-bad-phys.mir
create mode 100644 llvm/test/CodeGen/MIR/AMDGPU/virtregmap-stash-bad-split.mir
create mode 100644 llvm/test/CodeGen/MIR/AMDGPU/virtregmap-stash-self-split.mir
create mode 100644 llvm/test/CodeGen/MIR/AMDGPU/virtregmap-stash.mir
diff --git a/llvm/include/llvm/CodeGen/MIRPrinter.h b/llvm/include/llvm/CodeGen/MIRPrinter.h
index 829d16823d721..82d0ab1edbb4a 100644
--- a/llvm/include/llvm/CodeGen/MIRPrinter.h
+++ b/llvm/include/llvm/CodeGen/MIRPrinter.h
@@ -24,6 +24,7 @@ class MachineBasicBlock;
class MachineFunction;
class MachineModuleInfo;
class Module;
+class VirtRegMap;
template <typename T> class SmallVectorImpl;
class PrintMIRPreparePass : public RequiredPassInfoMixin<PrintMIRPreparePass> {
@@ -47,12 +48,18 @@ class PrintMIRPass : public RequiredPassInfoMixin<PrintMIRPass> {
LLVM_ABI void printMIR(raw_ostream &OS, const Module &M);
/// Print MIR using Legacy Pass Manager (uses MachineModuleInfo).
+/// If \p VRM is non-null, the printer will serialize VirtRegMap state
+/// (split-from, assigned-phys).
LLVM_ABI void printMIR(raw_ostream &OS, const MachineModuleInfo &MMI,
- const MachineFunction &MF);
+ const MachineFunction &MF,
+ const VirtRegMap *VRM = nullptr);
/// Print MIR using New Pass Manager (uses FunctionAnalysisManager).
+/// If \p VRM is non-null, the printer will serialize VirtRegMap state
+/// (split-from, assigned-phys).
LLVM_ABI void printMIR(raw_ostream &OS, FunctionAnalysisManager &FAM,
- const MachineFunction &MF);
+ const MachineFunction &MF,
+ const VirtRegMap *VRM = nullptr);
/// Determine a possible list of successors of a basic block based on the
/// basic block machine operand being used inside the block. This should give
diff --git a/llvm/include/llvm/CodeGen/MIRYamlMapping.h b/llvm/include/llvm/CodeGen/MIRYamlMapping.h
index cd0bcb656d34c..76f5e38a045ff 100644
--- a/llvm/include/llvm/CodeGen/MIRYamlMapping.h
+++ b/llvm/include/llvm/CodeGen/MIRYamlMapping.h
@@ -203,12 +203,19 @@ struct VirtualRegisterDefinition {
StringValue Class;
StringValue PreferredRegister;
std::vector<FlowStringValue> RegisterFlags;
+ // VirtRegMap state.
+ // SplitFrom: id-form virtual register only (e.g. '%0'); physregs and named
+ // vregs are rejected by the parser.
+ // AssignedPhys: physical register only (e.g. '$r5'); virtregs are rejected.
+ StringValue SplitFrom;
+ StringValue AssignedPhys;
// TODO: Serialize the target specific register hints.
bool operator==(const VirtualRegisterDefinition &Other) const {
return ID == Other.ID && Class == Other.Class &&
- PreferredRegister == Other.PreferredRegister;
+ PreferredRegister == Other.PreferredRegister &&
+ SplitFrom == Other.SplitFrom && AssignedPhys == Other.AssignedPhys;
}
};
@@ -220,6 +227,14 @@ template <> struct MappingTraits<VirtualRegisterDefinition> {
StringValue()); // Don't print out when it's empty.
YamlIO.mapOptional("flags", Reg.RegisterFlags,
std::vector<FlowStringValue>());
+ // MIRPrinter sets WriteDefaultValues=true unless -simplify-mir is passed,
+ // so a plain mapOptional with an empty default would still emit the keys
+ // and change every existing test's output.
+ // Skip the call on output when empty to keep them off entirely.
+ if (!YamlIO.outputting() || !Reg.SplitFrom.Value.empty())
+ YamlIO.mapOptional("split-from", Reg.SplitFrom, StringValue());
+ if (!YamlIO.outputting() || !Reg.AssignedPhys.Value.empty())
+ YamlIO.mapOptional("assigned-phys", Reg.AssignedPhys, StringValue());
}
static const bool flow = true;
diff --git a/llvm/include/llvm/CodeGen/MachineFunction.h b/llvm/include/llvm/CodeGen/MachineFunction.h
index bd796fee2226f..b3c814e1c3590 100644
--- a/llvm/include/llvm/CodeGen/MachineFunction.h
+++ b/llvm/include/llvm/CodeGen/MachineFunction.h
@@ -901,12 +901,7 @@ class LLVM_ABI MachineFunction {
MachineFunctionInfo *cloneInfoFrom(
const MachineFunction &OrigMF,
- const DenseMap<MachineBasicBlock *, MachineBasicBlock *> &Src2DstMBB) {
- assert(!MFInfo && "new function already has MachineFunctionInfo");
- if (!OrigMF.MFInfo)
- return nullptr;
- return OrigMF.MFInfo->clone(Allocator, *this, Src2DstMBB);
- }
+ const DenseMap<MachineBasicBlock *, MachineBasicBlock *> &Src2DstMBB);
/// Returns the denormal handling type for the default rounding mode of the
/// function.
diff --git a/llvm/include/llvm/CodeGen/MachineRegisterInfo.h b/llvm/include/llvm/CodeGen/MachineRegisterInfo.h
index 2c29c8bbd0b31..166c701cf5e9b 100644
--- a/llvm/include/llvm/CodeGen/MachineRegisterInfo.h
+++ b/llvm/include/llvm/CodeGen/MachineRegisterInfo.h
@@ -65,6 +65,14 @@ class MachineRegisterInfo {
}
};
+ // VirtRegMap state parsed from MIR and waiting to be consumed by
+ // VirtRegMap::init().
+ struct PendingVirtRegMapEntry {
+ Register VReg;
+ Register SplitFrom; // NoReg if absent.
+ MCRegister AssignedPhys; // NoReg if absent.
+ };
+
private:
MachineFunction *MF;
SmallPtrSet<Delegate *, 1> TheDelegates;
@@ -107,6 +115,10 @@ class MachineRegisterInfo {
VirtReg2IndexFunctor>
RegAllocHints;
+ /// Hold the register properties that are used to populate the VirtRegMap
+ /// pass when deserializing from .mir files.
+ SmallVector<PendingVirtRegMapEntry, 0> PendingVirtRegMapEntries;
+
/// PhysRegUseDefLists - This is an array of the head of the use/def list for
/// physical registers.
std::unique_ptr<MachineOperand *[]> PhysRegUseDefLists;
@@ -807,6 +819,25 @@ class MachineRegisterInfo {
/// clearVirtRegs - Remove all virtual registers (after physreg assignment).
LLVM_ABI void clearVirtRegs();
+ void addPendingVirtRegMapEntry(PendingVirtRegMapEntry Entry) {
+ assert(Entry.VReg.isVirtual());
+ assert(!Entry.SplitFrom.isValid() || Entry.SplitFrom.isVirtual());
+ assert(!Entry.AssignedPhys.isValid() || Entry.AssignedPhys.isPhysical());
+ PendingVirtRegMapEntries.push_back(Entry);
+ }
+
+ ArrayRef<PendingVirtRegMapEntry> getPendingVirtRegMapEntries() const {
+ return PendingVirtRegMapEntries;
+ }
+
+ void clearPendingVirtRegMapEntries() { PendingVirtRegMapEntries.clear(); }
+
+ void copyPendingVirtRegMapEntriesFrom(const MachineRegisterInfo &Other) {
+ assert(getNumVirtRegs() == Other.getNumVirtRegs() &&
+ "expected MachineFunction clone to preserve virtual registers");
+ PendingVirtRegMapEntries = Other.PendingVirtRegMapEntries;
+ }
+
/// setRegAllocationHint - Specify a register allocation hint for the
/// specified virtual register. This is typically used by target, and in case
/// of an earlier hint it will be overwritten.
diff --git a/llvm/lib/CodeGen/MIRParser/MIRParser.cpp b/llvm/lib/CodeGen/MIRParser/MIRParser.cpp
index 00967e1de43cc..6f1e7594f34da 100644
--- a/llvm/lib/CodeGen/MIRParser/MIRParser.cpp
+++ b/llvm/lib/CodeGen/MIRParser/MIRParser.cpp
@@ -804,6 +804,46 @@ bool MIRParserImpl::parseRegisterInfo(PerFunctionMIParsingState &PFS,
RegInfo.setCalleeSavedRegs(CalleeSavedRegisters);
}
+ // Stash any VirtRegMap state on MRI.
+ // VirtRegMap::init() will use that information to get pre-populated
+ // on the first analysis run.
+ for (const auto &VReg : YamlMF.VirtualRegisters) {
+ if (VReg.SplitFrom.Value.empty() && VReg.AssignedPhys.Value.empty())
+ continue;
+
+ auto It = PFS.VRegInfos.find(VReg.ID.Value);
+ if (It == PFS.VRegInfos.end())
+ continue;
+ Register ChildReg = It->second->VReg;
+
+ MachineRegisterInfo::PendingVirtRegMapEntry Pending;
+ Pending.VReg = ChildReg;
+
+ if (!VReg.SplitFrom.Value.empty()) {
+ VRegInfo *Parent = nullptr;
+ if (parseVirtualRegisterReference(PFS, Parent, VReg.SplitFrom.Value,
+ Error))
+ return error(Error, VReg.SplitFrom.SourceRange);
+ if (Parent->VReg == ChildReg)
+ return error(VReg.SplitFrom.SourceRange.Start,
+ Twine("'split-from' references the same vreg as 'id' (%") +
+ Twine(VReg.ID.Value) + ")");
+ Pending.SplitFrom = Parent->VReg;
+ }
+ if (!VReg.AssignedPhys.Value.empty()) {
+ Register Phys;
+ if (parseRegisterReference(PFS, Phys, VReg.AssignedPhys.Value, Error))
+ return error(Error, VReg.AssignedPhys.SourceRange);
+ if (!Phys.isPhysical())
+ return error(
+ VReg.AssignedPhys.SourceRange.Start,
+ Twine("'assigned-phys' must be a physical register, got '") +
+ VReg.AssignedPhys.Value + "'");
+ Pending.AssignedPhys = Phys.asMCReg();
+ }
+ RegInfo.addPendingVirtRegMapEntry(Pending);
+ }
+
return false;
}
diff --git a/llvm/lib/CodeGen/MIRPrinter.cpp b/llvm/lib/CodeGen/MIRPrinter.cpp
index 679304c43af02..b65de01898fd5 100644
--- a/llvm/lib/CodeGen/MIRPrinter.cpp
+++ b/llvm/lib/CodeGen/MIRPrinter.cpp
@@ -37,6 +37,7 @@
#include "llvm/CodeGen/TargetInstrInfo.h"
#include "llvm/CodeGen/TargetRegisterInfo.h"
#include "llvm/CodeGen/TargetSubtargetInfo.h"
+#include "llvm/CodeGen/VirtRegMap.h"
#include "llvm/CodeGenTypes/LowLevelType.h"
#include "llvm/IR/DebugInfoMetadata.h"
#include "llvm/IR/DebugLoc.h"
@@ -143,7 +144,7 @@ static void printMBB(raw_ostream &OS, MFPrintState &State,
const MachineBasicBlock &MBB);
static void convertMRI(yaml::MachineFunction &YamlMF, const MachineFunction &MF,
const MachineRegisterInfo &RegInfo,
- const TargetRegisterInfo *TRI);
+ const TargetRegisterInfo *TRI, const VirtRegMap *VRM);
static void convertMCP(yaml::MachineFunction &MF,
const MachineConstantPool &ConstantPool);
static void convertMJTI(ModuleSlotTracker &MST, yaml::MachineJumpTable &YamlJTI,
@@ -174,8 +175,8 @@ static void convertCalledGlobals(yaml::MachineFunction &YMF,
static void convertPrefetchTargets(yaml::MachineFunction &YMF,
const MachineFunction &MF);
-static void printMF(raw_ostream &OS, MFGetterFnT Fn,
- const MachineFunction &MF) {
+static void printMF(raw_ostream &OS, MFGetterFnT Fn, const MachineFunction &MF,
+ const VirtRegMap *VRM) {
MFPrintState State(std::move(Fn), MF);
State.RegisterMaskIds = initRegisterMaskIds(MF);
@@ -206,7 +207,8 @@ static void printMF(raw_ostream &OS, MFGetterFnT Fn,
YamlMF.IsSSA = Props.hasIsSSA();
YamlMF.NoVRegs = Props.hasNoVRegs();
- convertMRI(YamlMF, MF, MF.getRegInfo(), MF.getSubtarget().getRegisterInfo());
+ convertMRI(YamlMF, MF, MF.getRegInfo(), MF.getSubtarget().getRegisterInfo(),
+ VRM);
MachineModuleSlotTracker &MST = State.MST;
MST.incorporateFunction(MF.getFunction());
convertMFI(MST, YamlMF.FrameInfo, MF.getFrameInfo(),
@@ -306,7 +308,7 @@ static void printRegFlags(Register Reg,
static void convertMRI(yaml::MachineFunction &YamlMF, const MachineFunction &MF,
const MachineRegisterInfo &RegInfo,
- const TargetRegisterInfo *TRI) {
+ const TargetRegisterInfo *TRI, const VirtRegMap *VRM) {
YamlMF.TracksRegLiveness = RegInfo.tracksLiveness();
// Print the virtual register definitions.
@@ -321,6 +323,17 @@ static void convertMRI(yaml::MachineFunction &YamlMF, const MachineFunction &MF,
if (PreferredReg)
printRegMIR(PreferredReg, VReg.PreferredRegister, TRI);
printRegFlags(Reg, VReg.RegisterFlags, MF, TRI);
+ if (VRM) {
+ Register Orig = VRM->getPreSplitReg(Reg);
+ if (Orig && Orig != Reg) {
+ raw_string_ostream OS(VReg.SplitFrom.Value);
+ OS << printReg(Orig, TRI);
+ }
+ if (VRM->hasPhys(Reg)) {
+ raw_string_ostream OS(VReg.AssignedPhys.Value);
+ OS << printReg(VRM->getPhys(Reg), TRI);
+ }
+ }
YamlMF.VirtualRegisters.push_back(std::move(VReg));
}
@@ -1070,12 +1083,14 @@ void llvm::printMIR(raw_ostream &OS, const Module &M) {
}
void llvm::printMIR(raw_ostream &OS, const MachineModuleInfo &MMI,
- const MachineFunction &MF) {
- printMF(OS, [&](const Function &F) { return MMI.getMachineFunction(F); }, MF);
+ const MachineFunction &MF, const VirtRegMap *VRM) {
+ printMF(
+ OS, [&](const Function &F) { return MMI.getMachineFunction(F); }, MF,
+ VRM);
}
void llvm::printMIR(raw_ostream &OS, FunctionAnalysisManager &FAM,
- const MachineFunction &MF) {
+ const MachineFunction &MF, const VirtRegMap *VRM) {
printMF(
OS,
[&](const Function &F) {
@@ -1083,5 +1098,5 @@ void llvm::printMIR(raw_ostream &OS, FunctionAnalysisManager &FAM,
const_cast<Function &>(F))
.getMF();
},
- MF);
+ MF, VRM);
}
diff --git a/llvm/lib/CodeGen/MIRPrintingPass.cpp b/llvm/lib/CodeGen/MIRPrintingPass.cpp
index a6fdaa6ab9c50..f5e455a520151 100644
--- a/llvm/lib/CodeGen/MIRPrintingPass.cpp
+++ b/llvm/lib/CodeGen/MIRPrintingPass.cpp
@@ -15,6 +15,7 @@
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/CodeGen/MachineModuleInfo.h"
#include "llvm/CodeGen/Passes.h"
+#include "llvm/CodeGen/VirtRegMap.h"
#include "llvm/IR/Function.h"
#include "llvm/InitializePasses.h"
@@ -30,7 +31,8 @@ PreservedAnalyses PrintMIRPass::run(MachineFunction &MF,
auto &FAM = MFAM.getResult<FunctionAnalysisManagerMachineFunctionProxy>(MF)
.getManager();
- printMIR(OS, FAM, MF);
+ const VirtRegMap *VRM = MFAM.getCachedResult<VirtRegMapAnalysis>(MF);
+ printMIR(OS, FAM, MF, VRM);
return PreservedAnalyses::all();
}
@@ -50,6 +52,7 @@ struct MIRPrintingPass : public MachineFunctionPass {
void getAnalysisUsage(AnalysisUsage &AU) const override {
AU.setPreservesAll();
+ AU.addUsedIfAvailable<VirtRegMapWrapperLegacy>();
MachineFunctionPass::getAnalysisUsage(AU);
}
@@ -60,7 +63,11 @@ struct MIRPrintingPass : public MachineFunctionPass {
MachineModuleInfo *MMI =
&getAnalysis<MachineModuleInfoWrapperPass>().getMMI();
- printMIR(StrOS, *MMI, MF);
+ const VirtRegMap *VRM = nullptr;
+ if (auto *W = getAnalysisIfAvailable<VirtRegMapWrapperLegacy>())
+ VRM = &W->getVRM();
+
+ printMIR(StrOS, *MMI, MF, VRM);
MachineFunctions.append(Str);
return false;
}
diff --git a/llvm/lib/CodeGen/MachineFunction.cpp b/llvm/lib/CodeGen/MachineFunction.cpp
index 49d8a19e5d126..2922922535333 100644
--- a/llvm/lib/CodeGen/MachineFunction.cpp
+++ b/llvm/lib/CodeGen/MachineFunction.cpp
@@ -264,6 +264,22 @@ void MachineFunction::initTargetMachineFunctionInfo(
MFInfo = Target.createMachineFunctionInfo(Allocator, F, &STI);
}
+MachineFunctionInfo *MachineFunction::cloneInfoFrom(
+ const MachineFunction &OrigMF,
+ const DenseMap<MachineBasicBlock *, MachineBasicBlock *> &Src2DstMBB) {
+ assert(!MFInfo && "new function already has MachineFunctionInfo");
+ if (!OrigMF.MFInfo)
+ return nullptr;
+
+ MachineFunctionInfo *ClonedInfo =
+ OrigMF.MFInfo->clone(Allocator, *this, Src2DstMBB);
+ if (!ClonedInfo)
+ return nullptr;
+
+ RegInfo->copyPendingVirtRegMapEntriesFrom(OrigMF.getRegInfo());
+ return ClonedInfo;
+}
+
MachineFunction::~MachineFunction() {
clear();
}
diff --git a/llvm/lib/CodeGen/VirtRegMap.cpp b/llvm/lib/CodeGen/VirtRegMap.cpp
index 5a10a31050186..73e910027119e 100644
--- a/llvm/lib/CodeGen/VirtRegMap.cpp
+++ b/llvm/lib/CodeGen/VirtRegMap.cpp
@@ -74,6 +74,19 @@ void VirtRegMap::init(MachineFunction &mf) {
Virt2ShapeMap.clear();
grow();
+
+ // Drain any VirtRegMap state stashed by MIRParser on MRI. Must run after
+ // grow() so that Virt2*Map have capacity for every vreg referenced by the
+ // stash.
+ for (const auto &P : MRI->getPendingVirtRegMapEntries()) {
+ if (P.AssignedPhys.isValid())
+ assignVirt2Phys(P.VReg, P.AssignedPhys);
+ // Guard against self-reference; the parser also rejects this, but a
+ // belt-and-braces check keeps Virt2SplitMap meaningful.
+ if (P.SplitFrom.isValid() && P.SplitFrom != P.VReg)
+ setIsSplitFromReg(P.VReg, P.SplitFrom);
+ }
+ MRI->clearPendingVirtRegMapEntries();
}
void VirtRegMap::grow() {
diff --git a/llvm/test/CodeGen/MIR/AMDGPU/virtregmap-stash-bad-phys.mir b/llvm/test/CodeGen/MIR/AMDGPU/virtregmap-stash-bad-phys.mir
new file mode 100644
index 0000000000000..9dddfbf4fb6ca
--- /dev/null
+++ b/llvm/test/CodeGen/MIR/AMDGPU/virtregmap-stash-bad-phys.mir
@@ -0,0 +1,18 @@
+# RUN: not llc -mtriple=amdgcn -mcpu=gfx1100 -run-pass=virtregmap \
+# RUN: -filetype=null %s 2>&1 | FileCheck %s
+#
+# 'assigned-phys' must be a physical register; passing a vreg should error.
+
+# CHECK: 'assigned-phys' must be a physical register, got '%0'
+---
+name: bad_phys
+tracksRegLiveness: true
+registers:
+ - { id: 0, class: vgpr_32 }
+ - { id: 1, class: vgpr_32, assigned-phys: '%0' }
+body: |
+ bb.0:
+ %0:vgpr_32 = V_MOV_B32_e32 0, implicit $exec
+ %1:vgpr_32 = V_MOV_B32_e32 1, implicit $exec
+ S_ENDPGM 0, implicit %0, implicit %1
+...
diff --git a/llvm/test/CodeGen/MIR/AMDGPU/virtregmap-stash-bad-split.mir b/llvm/test/CodeGen/MIR/AMDGPU/virtregmap-stash-bad-split.mir
new file mode 100644
index 0000000000000..494677f23f7d1
--- /dev/null
+++ b/llvm/test/CodeGen/MIR/AMDGPU/virtregmap-stash-bad-split.mir
@@ -0,0 +1,16 @@
+# RUN: not llc -mtriple=amdgcn -mcpu=gfx1100 -run-pass=virtregmap \
+# RUN: -filetype=null %s 2>&1 | FileCheck %s
+#
+# 'split-from' must reference a virtual register; physregs are rejected.
+
+# CHECK: expected a virtual register
+---
+name: bad_split
+tracksRegLiveness: true
+registers:
+ - { id: 0, class: vgpr_32, split-from: '$vgpr5' }
+body: |
+ bb.0:
+ %0:vgpr_32 = V_MOV_B32_e32 0, implicit $exec
+ S_ENDPGM 0, implicit %0
+...
diff --git a/llvm/test/CodeGen/MIR/AMDGPU/virtregmap-stash-self-split.mir b/llvm/test/CodeGen/MIR/AMDGPU/virtregmap-stash-self-split.mir
new file mode 100644
index 0000000000000..a14c37e01b2e7
--- /dev/null
+++ b/llvm/test/CodeGen/MIR/AMDGPU/virtregmap-stash-self-split.mir
@@ -0,0 +1,17 @@
+# RUN: not llc -mtriple=amdgcn -mcpu=gfx1100 -run-pass=virtregmap \
+# RUN: -filetype=null %s 2>&1 | FileCheck %s
+#
+# 'split-from' must reference a different vreg than 'id'.
+# I.e., disallow self-references
+
+# CHECK: 'split-from' references the same vreg as 'id' (%0)
+---
+name: self_split
+tracksRegLiveness: true
+registers:
+ - { id: 0, class: vgpr_32, split-from: '%0' }
+body: |
+ bb.0:
+ %0:vgpr_32 = V_MOV_B32_e32 0, implicit $exec
+ S_ENDPGM 0, implicit %0
+...
diff --git a/llvm/test/CodeGen/MIR/AMDGPU/virtregmap-stash.mir b/llvm/test/CodeGen/MIR/AMDGPU/virtregmap-stash.mir
new file mode 100644
index 0000000000000..a9272c92775a6
--- /dev/null
+++ b/llvm/test/CodeGen/MIR/AMDGPU/virtregmap-stash.mir
@@ -0,0 +1,32 @@
+# RUN: llc -mtriple=amdgcn -mcpu=gfx1100 -run-pass=virtregmap \
+# RUN: -o - %s | FileCheck %s
+#
+# Verify that VirtRegMap state (split-from, assigned-phys) round-trips through
+# MIR.
+
+# CHECK-LABEL: name: test_vrm_roundtrip
+# CHECK: registers:
+# CHECK: id: 0,
+# CHECK-NOT: split-from:
+# CHECK: assigned-phys: '$vgpr5'
+# CHECK: id: 1,
+# CHECK: split-from: '%0'
+# CHECK: assigned-phys: '$vgpr6'
+# CHECK: id: 2,
+# CHECK: split-from: '%0'
+# CHECK: assigned-phys: '$vgpr7'
+---
+name: test_vrm_roundtrip
+tracksRegLiveness: true
+registers:
+ - { id: 0, class: vgpr_32, assigned-phys: '$vgpr5' }
+ - { id: 1, class: vgpr_32, assigned-phys: '$vgpr6', split-from: '%0' }
+ - { id: 2, class: vgpr_32, assigned-phys: '$vgpr7', split-from: '%0' }
+body: |
+ bb.0:
+ liveins: $vgpr0
+ %0:vgpr_32 = COPY $vgpr0
+ %1:vgpr_32 = V_ADD_U32_e32 1, %0:vgpr_32, implicit $exec
+ %2:vgpr_32 = V_ADD_U32_e32 2, %0:vgpr_32, implicit $exec
+ S_ENDPGM 0, implicit %0, implicit %1, implicit %2
+...
More information about the llvm-commits
mailing list