aboutsummaryrefslogtreecommitdiff
path: root/backend/unittests/CodeGen
diff options
context:
space:
mode:
Diffstat (limited to 'backend/unittests/CodeGen')
-rw-r--r--backend/unittests/CodeGen/CMakeLists.txt12
-rw-r--r--backend/unittests/CodeGen/MachineFrameInfoTest.cpp156
-rw-r--r--backend/unittests/CodeGen/MachineFunctionClonerTest.cpp78
-rw-r--r--backend/unittests/CodeGen/MachineIRBuilderTest.cpp10
-rw-r--r--backend/unittests/CodeGen/MachineVerifierTest.cpp53
5 files changed, 294 insertions, 15 deletions
diff --git a/backend/unittests/CodeGen/CMakeLists.txt b/backend/unittests/CodeGen/CMakeLists.txt
index 650cfa9..18cda7c 100644
--- a/backend/unittests/CodeGen/CMakeLists.txt
+++ b/backend/unittests/CodeGen/CMakeLists.txt
@@ -4,6 +4,7 @@ set(
RCCBackendCodeGenTestSources
CoreTypesTest.cpp
InstructionDescriptorTest.cpp
+ MachineFrameInfoTest.cpp
MachineFunctionClonerTest.cpp
MachineIRBuilderTest.cpp
MachineIRCoreTest.cpp
@@ -32,6 +33,15 @@ add_test(
COMMAND RCCBackendCodeGenInstructionDescriptorTests
)
+add_executable(RCCBackendCodeGenMachineFrameInfoTests MachineFrameInfoTest.cpp)
+rcc_backend_configure_target(RCCBackendCodeGenMachineFrameInfoTests)
+target_link_libraries(RCCBackendCodeGenMachineFrameInfoTests PRIVATE RCCBackend::CodeGen)
+
+add_test(
+ NAME RCCBackend.CodeGen.MachineFrameInfo
+ COMMAND RCCBackendCodeGenMachineFrameInfoTests
+)
+
add_executable(
RCCBackendCodeGenMachineFunctionClonerTests
MachineFunctionClonerTest.cpp
@@ -92,6 +102,7 @@ add_test(
rcc_backend_register_format_files(
"${CMAKE_CURRENT_SOURCE_DIR}/CoreTypesTest.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/InstructionDescriptorTest.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/MachineFrameInfoTest.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/MachineFunctionClonerTest.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/MachineIRBuilderTest.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/MachineIRCoreTest.cpp"
@@ -101,6 +112,7 @@ rcc_backend_register_format_files(
rcc_backend_register_tidy_files(
"${CMAKE_CURRENT_SOURCE_DIR}/CoreTypesTest.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/InstructionDescriptorTest.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/MachineFrameInfoTest.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/MachineFunctionClonerTest.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/MachineIRBuilderTest.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/MachineIRCoreTest.cpp"
diff --git a/backend/unittests/CodeGen/MachineFrameInfoTest.cpp b/backend/unittests/CodeGen/MachineFrameInfoTest.cpp
new file mode 100644
index 0000000..7368815
--- /dev/null
+++ b/backend/unittests/CodeGen/MachineFrameInfoTest.cpp
@@ -0,0 +1,156 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "rcc/CodeGen/MachineFrameInfo.h"
+
+#include <cstdint>
+#include <iostream>
+#include <string_view>
+
+namespace {
+
+class TestContext final {
+public:
+ void expect(bool Condition, std::string_view Message) {
+ if (Condition)
+ return;
+ std::cerr << Message << '\n';
+ ++FailureCount;
+ }
+
+ [[nodiscard]] int result() const noexcept {
+ return FailureCount == 0 ? 0 : 1;
+ }
+
+private:
+ int FailureCount = 0;
+};
+
+[[nodiscard]] rcc::StackObjectDesc fixedObject(std::uint64_t Size, std::uint64_t Align) {
+ rcc::StackObjectDesc Description;
+ Description.Size = Size;
+ Description.Align = *rcc::Alignment::create(Align);
+ return Description;
+}
+
+void testObjectLifecycle(TestContext &Context) {
+ rcc::MachineFrameInfo Frame;
+
+ auto LocalDescription = fixedObject(8, 8);
+ LocalDescription.IsAliased = true;
+ LocalDescription.AddressTaken = true;
+ LocalDescription.Lifetime.push_back(
+ {rcc::MachineInstrStableID::fromValue(3), rcc::MachineInstrStableID::fromValue(7)});
+ auto Local = Frame.createObject(LocalDescription);
+
+ auto FixedDescription = fixedObject(16, 16);
+ FixedDescription.Kind = rcc::StackObjectKind::FixedABI;
+ FixedDescription.Offset = -32;
+ auto Fixed = Frame.createObject(FixedDescription);
+
+ auto SpillDescription = fixedObject(8, 8);
+ SpillDescription.Kind = rcc::StackObjectKind::SpillSlot;
+ auto Spill = Frame.createObject(SpillDescription);
+
+ rcc::StackObjectDesc VariableDescription;
+ VariableDescription.Kind = rcc::StackObjectKind::VariableSized;
+ VariableDescription.Align = *rcc::Alignment::create(32);
+ auto Variable = Frame.createObject(VariableDescription);
+
+ Context.expect(Local && Fixed && Spill && Variable, "valid stack object kinds must be created");
+ if (!Local || !Fixed || !Spill || !Variable)
+ return;
+
+ Context.expect(Local->value() == 0 && Fixed->value() == 1 && Spill->value() == 2 &&
+ Variable->value() == 3 && Frame.liveObjectCount() == 4,
+ "frame indices must be monotonic table identities");
+ Context.expect(Frame.object(*Fixed)->isFixed() && Frame.object(*Spill)->isSpill() &&
+ Frame.object(*Variable)->isVariableSized(),
+ "stack object semantic categories must be derived exactly");
+
+ Context.expect(Frame.setObjectOffset(*Local, -8).has_value() &&
+ Frame.setObjectOffset(*Spill, -16).has_value() &&
+ Frame.markObjectDead(*Spill).has_value(),
+ "live stack objects must support controlled pre-freeze updates");
+ Context.expect(Frame.object(*Spill)->description()->IsDead,
+ "dead stack object state must remain explicit");
+
+ Context.expect(Frame.eraseObject(*Fixed).has_value() && Frame.object(*Fixed) == nullptr &&
+ Frame.liveObjectCount() == 3,
+ "erasing a stack object must leave an inaccessible tombstone");
+ auto Replacement = Frame.createObject(fixedObject(4, 4));
+ Context.expect(Replacement && Replacement->value() == 4,
+ "erased frame indices must never be reused");
+ if (!Replacement)
+ return;
+ Context.expect(Frame.setObjectOffset(*Replacement, -24).has_value(),
+ "replacement stack object must accept a final offset");
+
+ Context.expect(Frame.setMaxCallFrameSize(24).has_value() &&
+ Frame.freeze(64, *rcc::Alignment::create(16)).has_value() &&
+ Frame.isFrozen() && Frame.staticStackSize() == 64 &&
+ Frame.stackAlignment().bytes() == 16 && Frame.maxCallFrameSize() == 24 &&
+ Frame.verify().has_value(),
+ "complete frame layout must freeze atomically");
+
+ Context.expect(!Frame.createObject(fixedObject(8, 8)) && !Frame.setObjectOffset(*Local, -40) &&
+ !Frame.eraseObject(*Local) && !Frame.setMaxCallFrameSize(32) &&
+ !Frame.freeze(64, *rcc::Alignment::create(16)),
+ "frozen frames must reject every semantic mutation");
+}
+
+void testInvalidDescriptions(TestContext &Context) {
+ rcc::MachineFrameInfo Frame;
+
+ rcc::StackObjectDesc MissingSize;
+ Context.expect(!Frame.createObject(MissingSize),
+ "fixed-size stack object must reject a missing size");
+
+ auto ZeroSize = fixedObject(0, 1);
+ Context.expect(!Frame.createObject(ZeroSize), "fixed-size stack object must reject zero size");
+
+ rcc::StackObjectDesc InvalidVariable;
+ InvalidVariable.Kind = rcc::StackObjectKind::DynamicAlloca;
+ InvalidVariable.Size = 8;
+ Context.expect(!Frame.createObject(InvalidVariable),
+ "variable-sized stack object must reject a fixed size");
+
+ auto MissingFixedOffset = fixedObject(8, 8);
+ MissingFixedOffset.Kind = rcc::StackObjectKind::FixedABI;
+ Context.expect(!Frame.createObject(MissingFixedOffset),
+ "fixed ABI stack object must require an offset");
+
+ auto InvalidTarget = fixedObject(8, 8);
+ InvalidTarget.Kind = rcc::StackObjectKind::Target;
+ Context.expect(!Frame.createObject(InvalidTarget),
+ "target stack object must carry target identity");
+
+ auto InvalidLifetime = fixedObject(8, 8);
+ InvalidLifetime.Lifetime.push_back(
+ {rcc::MachineInstrStableID::fromValue(4), rcc::MachineInstrStableID::fromValue(4)});
+ Context.expect(!Frame.createObject(InvalidLifetime),
+ "stack object lifetime must reject equal boundaries");
+
+ Context.expect(!rcc::StackID::targetStack(4), "target stack IDs must not overlap built-in IDs");
+ auto TargetStack = rcc::StackID::targetStack(256);
+ auto Target = fixedObject(8, 8);
+ Target.Kind = rcc::StackObjectKind::Target;
+ if (TargetStack)
+ Target.Stack = *TargetStack;
+ auto TargetIndex = Frame.createObject(Target);
+ Context.expect(TargetStack && TargetIndex,
+ "target stack identity must admit target stack objects");
+
+ auto Unresolved = Frame.createObject(fixedObject(8, 8));
+ Context.expect(Unresolved && !Frame.freeze(16, *rcc::Alignment::create(8)) && !Frame.isFrozen() &&
+ !Frame.staticStackSize() && Frame.verify().has_value(),
+ "failed frame freeze must not mutate frame state");
+}
+
+} // namespace
+
+int main() {
+ TestContext Context;
+ testObjectLifecycle(Context);
+ testInvalidDescriptions(Context);
+ return Context.result();
+}
diff --git a/backend/unittests/CodeGen/MachineFunctionClonerTest.cpp b/backend/unittests/CodeGen/MachineFunctionClonerTest.cpp
index 90e847b..3b2fce9 100644
--- a/backend/unittests/CodeGen/MachineFunctionClonerTest.cpp
+++ b/backend/unittests/CodeGen/MachineFunctionClonerTest.cpp
@@ -6,6 +6,7 @@
#include "rcc/CodeGen/LowLevelType.h"
#include "rcc/CodeGen/MachineBasicBlock.h"
#include "rcc/CodeGen/MachineCFGUpdater.h"
+#include "rcc/CodeGen/MachineFrameInfo.h"
#include "rcc/CodeGen/MachineFunction.h"
#include "rcc/CodeGen/MachineFunctionProperties.h"
#include "rcc/CodeGen/MachineIRBuilder.h"
@@ -137,12 +138,12 @@ createFunction(TestContext &Context, std::string Name, std::uint64_t ID) {
return *Block;
}
-[[nodiscard]] rcc::MachineMemOperandDesc loadDescription() {
+[[nodiscard]] rcc::MachineMemOperandDesc loadDescription(rcc::FrameIndex Index) {
rcc::MachineMemOperandDesc Description;
Description.Accesses = {rcc::MemoryAccessFlag::Load, rcc::MemoryAccessFlag::Dereferenceable};
Description.Size = *rcc::MemorySize::fixed(8);
Description.Align = *rcc::Alignment::create(8);
- Description.Base = rcc::MemoryBase::frameIndex(rcc::FrameIndex::fromValue(4));
+ Description.Base = rcc::MemoryBase::frameIndex(Index);
Description.Offset = -8;
return Description;
}
@@ -162,6 +163,7 @@ struct SourceFixture final {
rcc::MachineBasicBlock *Exit = nullptr;
rcc::Register Value;
rcc::Register CopyValue;
+ rcc::FrameIndex StackSlot;
const rcc::MachineMemOperand *LoadMMO = nullptr;
};
@@ -177,6 +179,25 @@ struct SourceFixture final {
Context.expect(Tombstone && Function.registerInfo().eraseVirtualRegister(*Tombstone).has_value(),
"source vreg history must contain a tombstone");
+ rcc::StackObjectDesc HistoricalFrame;
+ HistoricalFrame.Size = 4;
+ HistoricalFrame.Align = *rcc::Alignment::create(4);
+ auto HistoricalFrameIndex = Function.frameInfo().createObject(HistoricalFrame);
+ Context.expect(HistoricalFrameIndex &&
+ Function.frameInfo().eraseObject(*HistoricalFrameIndex).has_value(),
+ "source frame history must contain a tombstone");
+
+ rcc::StackObjectDesc StackSlot;
+ StackSlot.Kind = rcc::StackObjectKind::SpillSlot;
+ StackSlot.Size = 8;
+ StackSlot.Align = *rcc::Alignment::create(8);
+ StackSlot.Offset = -16;
+ auto StackSlotIndex = Function.frameInfo().createObject(StackSlot);
+ Context.expect(StackSlotIndex.has_value(), "source live stack object must be created");
+ if (!StackSlotIndex)
+ return false;
+ Fixture.StackSlot = *StackSlotIndex;
+
Fixture.Entry = createBlock(Context, Function);
Fixture.Exit = createBlock(Context, Function);
if (Fixture.Entry == nullptr || Fixture.Exit == nullptr)
@@ -236,8 +257,7 @@ struct SourceFixture final {
"source tied copy must build");
auto Frame = Builder.buildInstr(rcc::Opcode::fromValue(FrameOpcode));
- Context.expect(Frame && Frame->addFrameIndex(rcc::FrameIndex::fromValue(4), -8).has_value() &&
- Frame->build(),
+ Context.expect(Frame && Frame->addFrameIndex(Fixture.StackSlot, -8).has_value() && Frame->build(),
"source frame-index instruction must build");
auto Address = Builder.buildInstr(rcc::Opcode::fromValue(BlockAddressOpcode));
@@ -246,7 +266,7 @@ struct SourceFixture final {
Address->build(),
"source self block-address instruction must build");
- auto MMO = Function.createMemOperand(loadDescription());
+ auto MMO = Function.createMemOperand(loadDescription(Fixture.StackSlot));
Context.expect(MMO.has_value(), "source MMO must be created");
if (!MMO)
return false;
@@ -255,6 +275,20 @@ struct SourceFixture final {
Context.expect(Load && Load->addMemOperand(**MMO).has_value() && Load->build(),
"source load with MMO must build");
+ rcc::MachineInstr *Definition = findInstruction(*Fixture.Entry, DefOpcode);
+ rcc::MachineInstr *LoadInstruction = findInstruction(*Fixture.Exit, LoadOpcode);
+ const rcc::StackObject *Object = Function.frameInfo().object(Fixture.StackSlot);
+ if (Definition == nullptr || LoadInstruction == nullptr || Object == nullptr)
+ return false;
+ rcc::StackObjectDesc UpdatedStackSlot = *Object->description();
+ UpdatedStackSlot.Lifetime.push_back({Definition->stableID(), LoadInstruction->stableID()});
+ Context.expect(Function.frameInfo()
+ .updateObject(Fixture.StackSlot, std::move(UpdatedStackSlot))
+ .has_value() &&
+ Function.frameInfo().setMaxCallFrameSize(16).has_value() &&
+ Function.frameInfo().freeze(32, *rcc::Alignment::create(16)).has_value(),
+ "source frame metadata must be finalized");
+
Function.properties().set(rcc::MachineFunctionProperty::IsSSA);
Function.properties().set(rcc::MachineFunctionProperty::TracksLiveness);
Function.properties().set(rcc::MachineFunctionProperty::NoPHIs);
@@ -289,7 +323,9 @@ void testDeepClone(TestContext &Context) {
"source and destination must independently preserve valid properties");
Context.expect(Source->allBlocks().size() == 3 && Destination.allBlocks().size() == 2 &&
Source->allInstructions().size() == 7 &&
- Destination.allInstructions().size() == 6,
+ Destination.allInstructions().size() == 6 &&
+ Source->frameInfo().objects().size() == 2 &&
+ Destination.frameInfo().objects().size() == 1,
"clone must omit detached arena history");
Context.expect(Clone->BlockStableIDs.size() == 2 && Clone->InstructionStableIDs.size() == 6 &&
Clone->VirtualRegisters.size() == 2,
@@ -375,21 +411,39 @@ void testDeepClone(TestContext &Context) {
DestinationCopy->operandAt(0)->tiedOperandIndex() == 1 &&
DestinationCopy->operandAt(1)->tiedOperandIndex() == 0,
"clone must remap tied registers and rebuild ties");
- Context.expect(DestinationFrame->operandAt(0)->getIf<rcc::FrameIndexOperand>()->Index ==
- rcc::FrameIndex::fromValue(4) &&
- Clone->FrameIndices.contains(rcc::FrameIndex::fromValue(4)),
- "clone must preserve and map frame indices");
+ const auto FramePosition = Clone->FrameIndices.find(Fixture.StackSlot);
+ Context.expect(FramePosition != Clone->FrameIndices.end() &&
+ FramePosition->second == rcc::FrameIndex::fromValue(0) &&
+ DestinationFrame->operandAt(0)->getIf<rcc::FrameIndexOperand>()->Index ==
+ FramePosition->second,
+ "clone must compact and remap frame indices");
Context.expect(
DestinationAddress->operandAt(0)->getIf<rcc::BlockAddressOperand>()->FunctionName ==
"cloned_function",
"clone must rewrite self block-address function names");
+ rcc::MachineMemOperandDesc ExpectedMMO = Fixture.LoadMMO->description();
+ if (FramePosition != Clone->FrameIndices.end())
+ ExpectedMMO.Base = rcc::MemoryBase::frameIndex(FramePosition->second);
Context.expect(DestinationLoad->memOperands().size() == 1 &&
DestinationLoad->memOperands()[0] != Fixture.LoadMMO &&
- *DestinationLoad->memOperands()[0] == *Fixture.LoadMMO &&
+ DestinationLoad->memOperands()[0]->description() == ExpectedMMO &&
Destination.ownsMemOperand(*DestinationLoad->memOperands()[0]) &&
!Source->ownsMemOperand(*DestinationLoad->memOperands()[0]),
- "clone must deep-copy MMO ownership while preserving semantics");
+ "clone must deep-copy MMO ownership while remapping frame semantics");
+
+ const rcc::StackObject *DestinationStackSlot =
+ FramePosition == Clone->FrameIndices.end()
+ ? nullptr
+ : Destination.frameInfo().object(FramePosition->second);
+ Context.expect(
+ DestinationStackSlot != nullptr && Destination.frameInfo().isFrozen() &&
+ Destination.frameInfo().staticStackSize() == 32 &&
+ Destination.frameInfo().maxCallFrameSize() == 16 &&
+ DestinationStackSlot->description()->Lifetime.size() == 1 &&
+ DestinationStackSlot->description()->Lifetime[0].Begin == DestinationDef->stableID() &&
+ DestinationStackSlot->description()->Lifetime[0].End == DestinationLoad->stableID(),
+ "clone must preserve frozen frame layout and remap instruction lifetimes");
rcc::MachineInstr *SourceDef = findInstruction(*Fixture.Entry, DefOpcode);
const auto InstructionID = SourceDef == nullptr
diff --git a/backend/unittests/CodeGen/MachineIRBuilderTest.cpp b/backend/unittests/CodeGen/MachineIRBuilderTest.cpp
index bda30b6..ba5e3db 100644
--- a/backend/unittests/CodeGen/MachineIRBuilderTest.cpp
+++ b/backend/unittests/CodeGen/MachineIRBuilderTest.cpp
@@ -356,6 +356,16 @@ void testImplicitAndMemoryValidation(TestContext &Context) {
Function->allInstructions().size() == BeforeMissingMemory,
"missing required MMO must fail before instruction allocation");
+ rcc::StackObjectDesc StackSlot;
+ StackSlot.Size = 8;
+ StackSlot.Align = *rcc::Alignment::create(8);
+ auto FrameIndex = Function->frameInfo().createObject(StackSlot);
+ auto ForeignFrameIndex = OtherFunction->frameInfo().createObject(StackSlot);
+ Context.expect(FrameIndex && ForeignFrameIndex,
+ "memory validation stack objects must be created");
+ if (!FrameIndex || !ForeignFrameIndex)
+ return;
+
auto LoadMemory = Function->createMemOperand(memoryDescription(rcc::MemoryAccessFlag::Load));
auto StoreMemory = Function->createMemOperand(memoryDescription(rcc::MemoryAccessFlag::Store));
auto ForeignMemory =
diff --git a/backend/unittests/CodeGen/MachineVerifierTest.cpp b/backend/unittests/CodeGen/MachineVerifierTest.cpp
index b8b985e..82ae68b 100644
--- a/backend/unittests/CodeGen/MachineVerifierTest.cpp
+++ b/backend/unittests/CodeGen/MachineVerifierTest.cpp
@@ -346,12 +346,17 @@ void testPropertyFailures(TestContext &Context) {
auto Frame = createFunction(Context, "frame_index", 9);
rcc::MachineBasicBlock *FrameBlock = Frame ? createBlock(Context, *Frame) : nullptr;
if (Frame && FrameBlock != nullptr) {
+ rcc::StackObjectDesc StackSlot;
+ StackSlot.Size = 8;
+ StackSlot.Align = *rcc::Alignment::create(8);
+ auto FrameIndex = Frame->frameInfo().createObject(StackSlot);
rcc::MachineIRBuilder Builder(*Frame, *InstructionSet);
- Context.expect(Builder.setInsertPoint(*FrameBlock).has_value(),
+ Context.expect(FrameIndex && Builder.setInsertPoint(*FrameBlock).has_value(),
"frame-index negative test setup must succeed");
+ if (!FrameIndex)
+ return;
auto Pending = Builder.buildInstr(rcc::Opcode::fromValue(FrameOpcode));
- Context.expect(Pending && Pending->addFrameIndex(rcc::FrameIndex::fromValue(0)).has_value() &&
- Pending->build(),
+ Context.expect(Pending && Pending->addFrameIndex(*FrameIndex).has_value() && Pending->build(),
"frame-index negative test instruction must build");
Frame->properties().set(rcc::MachineFunctionProperty::NoFrameIndices);
Context.expect(!rcc::MachineVerifier::verify(*Frame, *InstructionSet),
@@ -447,6 +452,47 @@ void testSelectedStateAndPseudoDeadline(TestContext &Context) {
}
}
+void testFrameStateFailures(TestContext &Context) {
+ auto InstructionSet = createInstructionSet();
+ auto Function = createFunction(Context, "frame_references", 14);
+ rcc::MachineBasicBlock *Block = Function ? createBlock(Context, *Function) : nullptr;
+ if (!InstructionSet || !Function || Block == nullptr)
+ return;
+
+ rcc::MachineIRBuilder Builder(*Function, *InstructionSet);
+ Context.expect(Builder.setInsertPoint(*Block).has_value(),
+ "frame reference insertion point must be valid");
+ auto Unknown = Builder.buildInstr(rcc::Opcode::fromValue(FrameOpcode));
+ Context.expect(Unknown && Unknown->addFrameIndex(rcc::FrameIndex::fromValue(99)).has_value() &&
+ !Unknown->build() && Function->allInstructions().empty(),
+ "builder must reject an unknown frame index before instruction allocation");
+
+ rcc::StackObjectDesc StackSlot;
+ StackSlot.Size = 8;
+ StackSlot.Align = *rcc::Alignment::create(8);
+ auto FrameIndex = Function->frameInfo().createObject(StackSlot);
+ if (!FrameIndex)
+ return;
+ auto Live = Builder.buildInstr(rcc::Opcode::fromValue(FrameOpcode));
+ Context.expect(Live && Live->addFrameIndex(*FrameIndex).has_value() && Live->build(),
+ "known live frame index must build");
+ Context.expect(Function->frameInfo().markObjectDead(*FrameIndex).has_value() &&
+ !rcc::MachineVerifier::verify(*Function, *InstructionSet),
+ "verifier must reject an instruction that references a dead stack object");
+
+ auto Lifetime = createFunction(Context, "frame_lifetime", 15);
+ if (!Lifetime)
+ return;
+ rcc::StackObjectDesc LifetimeSlot;
+ LifetimeSlot.Size = 8;
+ LifetimeSlot.Align = *rcc::Alignment::create(8);
+ LifetimeSlot.Lifetime.push_back(
+ {rcc::MachineInstrStableID::fromValue(1), rcc::MachineInstrStableID::fromValue(2)});
+ Context.expect(Lifetime->frameInfo().createObject(LifetimeSlot).has_value() &&
+ !rcc::MachineVerifier::verify(*Lifetime, *InstructionSet),
+ "verifier must reject stack lifetimes that reference unknown instructions");
+}
+
} // namespace
int main() {
@@ -455,5 +501,6 @@ int main() {
testReachableStructuralFailures(Context);
testPropertyFailures(Context);
testSelectedStateAndPseudoDeadline(Context);
+ testFrameStateFailures(Context);
return Context.result();
}