aboutsummaryrefslogtreecommitdiff
path: root/backend/unittests/CodeGen/MachineFunctionClonerTest.cpp
diff options
context:
space:
mode:
authorRichard-Qin-X <richard.qin.001@gmail.com>2026-08-01 13:55:21 +0800
committerRichard-Qin-X <richard.qin.001@gmail.com>2026-08-01 13:55:21 +0800
commitc9634fa9f529afbdf622a9cd96741bd09d14ef86 (patch)
tree59fd91b741fe187ae25854ab0d762864976cf0d8 /backend/unittests/CodeGen/MachineFunctionClonerTest.cpp
parent786ccc115f851962f3a7d234846a50f4af8aad64 (diff)
downloadrcc-project-c9634fa9f529afbdf622a9cd96741bd09d14ef86.tar.gz
rcc-project-c9634fa9f529afbdf622a9cd96741bd09d14ef86.tar.xz
[CodeGen] Add MachineFrameInfo
Model stable frame indices, stack object kinds, tombstones, lifetimes, and frozen layouts. Validate owned frame references in the builder and verifier, and remap complete frame state during MachineFunction cloning.
Diffstat (limited to 'backend/unittests/CodeGen/MachineFunctionClonerTest.cpp')
-rw-r--r--backend/unittests/CodeGen/MachineFunctionClonerTest.cpp78
1 files changed, 66 insertions, 12 deletions
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