aboutsummaryrefslogtreecommitdiff
path: root/backend/unittests/CodeGen
diff options
context:
space:
mode:
authorRichard-Qin-X <richard.qin.001@gmail.com>2026-08-02 03:37:46 +0800
committerRichard-Qin-X <richard.qin.001@gmail.com>2026-08-02 03:37:46 +0800
commita8a9d7d926202450645deae872d4d9dc11d63215 (patch)
tree16a3640a93e3f567ff31045969c02a42e1f467e0 /backend/unittests/CodeGen
parent49d70380a6a95e4039f6b5e5ccce09671d5b4796 (diff)
downloadrcc-project-a8a9d7d926202450645deae872d4d9dc11d63215.tar.gz
rcc-project-a8a9d7d926202450645deae872d4d9dc11d63215.tar.xz
[CodeGen] Model machine debug metadata
Replace marker-based metadata operands with scoped references and inline DIExpression values. Add structured DILocation chains, instruction debug attachments, stack variable records, and entry values with unified verification, builder support, and deterministic cloning.
Diffstat (limited to 'backend/unittests/CodeGen')
-rw-r--r--backend/unittests/CodeGen/MachineDebugInfoTest.cpp221
-rw-r--r--backend/unittests/CodeGen/MachineFrameInfoTest.cpp19
-rw-r--r--backend/unittests/CodeGen/MachineFunctionRecordsTest.cpp79
-rw-r--r--backend/unittests/CodeGen/MachineIRBuilderTest.cpp24
-rw-r--r--backend/unittests/CodeGen/MachineIRCoreTest.cpp16
5 files changed, 335 insertions, 24 deletions
diff --git a/backend/unittests/CodeGen/MachineDebugInfoTest.cpp b/backend/unittests/CodeGen/MachineDebugInfoTest.cpp
index 6c5eea7..80a83e0 100644
--- a/backend/unittests/CodeGen/MachineDebugInfoTest.cpp
+++ b/backend/unittests/CodeGen/MachineDebugInfoTest.cpp
@@ -1,8 +1,11 @@
// SPDX-License-Identifier: GPL-3.0-or-later
+#include "rcc/CodeGen/MachineDebugInfo.h"
+
#include "Target/TestTarget.h"
#include "rcc/CodeGen/InstructionDescriptor.h"
#include "rcc/CodeGen/MachineBasicBlock.h"
+#include "rcc/CodeGen/MachineFrameInfo.h"
#include "rcc/CodeGen/MachineFunction.h"
#include "rcc/CodeGen/MachineFunctionAttributes.h"
#include "rcc/CodeGen/MachineFunctionCloner.h"
@@ -15,6 +18,7 @@
#include <iostream>
#include <limits>
#include <memory>
+#include <optional>
#include <string>
#include <string_view>
#include <utility>
@@ -47,6 +51,10 @@ private:
Value.InstructionOpcode = rcc::Opcode::fromValue(ValueOpcode);
Value.Name = "TEST_VALUE";
Value.Flags = {rcc::InstructionFlag::ProducesCode};
+ rcc::OperandDescriptor OptionalMetadata;
+ OptionalMetadata.Kinds = {rcc::MachineOperand::Kind::Metadata};
+ OptionalMetadata.Optional = true;
+ Value.Operands.push_back(OptionalMetadata);
rcc::InstructionDescriptor Meta;
Meta.InstructionOpcode = rcc::Opcode::fromValue(MetaOpcode);
@@ -90,6 +98,217 @@ createFunction(TestContext &Context, std::uint64_t StableID, std::string Name) {
return Instruction ? *Instruction : nullptr;
}
+void testMetadataValuesAndLocations(TestContext &Context) {
+ rcc::MachineMetadataInfo Metadata;
+ Context.expect(Metadata.addNode({.Slot = 0, .IsDistinct = false, .Elements = {}}).has_value(),
+ "function-local debug metadata setup must succeed");
+
+ const rcc::MachineDIExpression Expression(
+ std::vector<std::uint64_t>{0x1005, 0, 0x22, 0x1000, 0, 64});
+ Context.expect(Expression.elements().size() == 6 && Expression.elements()[0] == 0x1005,
+ "inline DIExpression elements must preserve their exact unsigned encoding");
+ auto ExpressionValue = rcc::MachineMetadataValue::expression(Expression);
+ Context.expect(ExpressionValue.verify(Metadata).has_value() &&
+ ExpressionValue.expression() != nullptr &&
+ *ExpressionValue.expression() == Expression,
+ "inline DIExpression metadata values must remain structured");
+ Context.expect(!rcc::MachineMetadataValue{}.verify(Metadata),
+ "an unrepresented metadata value must be rejected");
+
+ Context.expect(!rcc::MachineDILocation::create({}),
+ "an inline DILocation must contain a root frame");
+ Context.expect(!rcc::MachineDILocation::create({{}}),
+ "an inline DILocation frame must contain a valid scope");
+ Context.expect(!rcc::MachineDILocation::create({
+ {
+ .Line = 1,
+ .Column = 0,
+ .Scope = rcc::MachineMetadataReference::embeddedIR(1),
+ .IsImplicitCode = false,
+ .AtomGroup = 0,
+ .AtomRank = 8,
+ },
+ }),
+ "an inline DILocation atom rank must fit the fixed three-bit field");
+ Context.expect(!rcc::MachineDILocation::create({
+ {
+ .Line = 1,
+ .Column = 0,
+ .Scope = rcc::MachineMetadataReference::embeddedIR(1),
+ .IsImplicitCode = false,
+ .AtomGroup = rcc::MachineDILocation::MaxAtomGroup + 1,
+ .AtomRank = 0,
+ },
+ }),
+ "an inline DILocation atom group must fit the fixed 61-bit field");
+
+ auto InlineLocation = rcc::MachineDILocation::create(
+ {
+ {
+ .Line = 7,
+ .Column = 65'535,
+ .Scope = rcc::MachineMetadataReference::embeddedIR(3),
+ .IsImplicitCode = true,
+ .AtomGroup = rcc::MachineDILocation::MaxAtomGroup,
+ .AtomRank = 7,
+ },
+ {
+ .Line = 9,
+ .Column = 4,
+ .Scope = rcc::MachineMetadataReference::embeddedIR(0),
+ .IsImplicitCode = false,
+ .AtomGroup = 0,
+ .AtomRank = 0,
+ },
+ },
+ rcc::MachineMetadataReference::embeddedIR(4));
+ Context.expect(InlineLocation.has_value() && InlineLocation->verify(Metadata).has_value() &&
+ InlineLocation->inlineChain().size() == 2 &&
+ InlineLocation->terminalInlinedAt() ==
+ rcc::MachineMetadataReference::embeddedIR(4),
+ "inline DILocation chains must preserve nested and terminal references");
+ if (!InlineLocation)
+ return;
+
+ auto DebugLocation = rcc::MachineDebugLocation::inlineLocation(*InlineLocation);
+ Context.expect(DebugLocation.verify(Metadata).has_value() &&
+ DebugLocation.inlineLocation() != nullptr,
+ "a structured inline DILocation must form a debug-location attachment");
+ rcc::MachineVariableDebugInfo VariableInfo{
+ .Variable = rcc::MachineMetadataReference::embeddedIR(5),
+ .Expression = ExpressionValue,
+ .Location = DebugLocation,
+ };
+ Context.expect(VariableInfo.verify(Metadata).has_value() &&
+ !rcc::MachineVariableDebugInfo{}.verify(Metadata),
+ "variable debug records must carry at least one verified metadata component");
+
+ auto Undefined =
+ rcc::MachineDebugLocation::reference(rcc::MachineMetadataReference::machineFunction(99));
+ Context.expect(!Undefined, "typed debug locations must not accept function-local tuple metadata");
+}
+
+void testInstructionMetadataAndClone(TestContext &Context) {
+ auto Target = createTarget();
+ auto Function = createFunction(Context, 20, "debug-metadata");
+ if (!Target || Function == nullptr)
+ return;
+ Context.expect(
+ Function->metadata().addNode({.Slot = 0, .IsDistinct = false, .Elements = {}}).has_value(),
+ "instruction metadata graph setup must succeed");
+ rcc::MachineBasicBlock *Block = appendBlock(Context, *Function);
+ if (Block == nullptr)
+ return;
+ rcc::MachineInstr *Instruction = appendInstruction(Context, *Function, *Block, ValueOpcode);
+ if (Instruction == nullptr)
+ return;
+
+ auto MetadataValue =
+ rcc::MachineMetadataValue::reference(rcc::MachineMetadataReference::machineFunction(0));
+ Context.expect(MetadataValue.has_value(), "instruction metadata reference must be valid");
+ if (!MetadataValue)
+ return;
+ auto Operand = rcc::MachineOperand::metadata(std::move(*MetadataValue));
+ auto InlineLocation = rcc::MachineDILocation::create({
+ {
+ .Line = 12,
+ .Column = 8,
+ .Scope = rcc::MachineMetadataReference::embeddedIR(7),
+ .IsImplicitCode = false,
+ .AtomGroup = 0,
+ .AtomRank = 0,
+ },
+ });
+ Context.expect(Operand.has_value() && InlineLocation.has_value(),
+ "instruction metadata values must be constructible");
+ if (!Operand || !InlineLocation)
+ return;
+ const rcc::MachineDebugLocation DebugLocation =
+ rcc::MachineDebugLocation::inlineLocation(*InlineLocation);
+ Context.expect(Instruction->addOperand(std::move(*Operand)).has_value() &&
+ Instruction->setDebugLocation(DebugLocation).has_value(),
+ "instruction metadata operand and debug location must attach independently");
+ Instruction->setSourceLocation({"debug-input.mir", 3, 5, 0});
+ const rcc::MachineVariableDebugInfo StackDebugInfo{
+ .Variable = rcc::MachineMetadataReference::embeddedIR(8),
+ .Expression = rcc::MachineMetadataValue::expression(
+ rcc::MachineDIExpression(std::vector<std::uint64_t>{0x1000, 0, 64})),
+ .Location = DebugLocation,
+ };
+ rcc::StackObjectDesc StackDescription;
+ StackDescription.Size = 8;
+ StackDescription.Align = *rcc::Alignment::create(8);
+ StackDescription.DebugInfo = StackDebugInfo;
+ auto StackIndex = Function->frameInfo().createObject(std::move(StackDescription));
+ Context.expect(StackIndex.has_value(),
+ "structured variable debug information must attach to a stack object");
+ if (!StackIndex)
+ return;
+ Context.expect(rcc::MachineVerifier::verify(*Function, *Target).has_value(),
+ "well-formed instruction metadata must pass unified verification");
+
+ auto Clone =
+ rcc::cloneMachineFunction(*Function, *Target,
+ {
+ .NewStableID = rcc::MachineFunctionStableID::fromValue(21),
+ .NewName = std::string("debug-metadata.clone"),
+ });
+ Context.expect(Clone.has_value(), "instruction debug metadata must support deep cloning");
+ if (!Clone)
+ return;
+ const rcc::MachineInstr *ClonedInstruction =
+ Clone->ClonedFunction->firstBlock()->firstInstruction();
+ const auto ClonedStackIndex = Clone->FrameIndices.find(*StackIndex);
+ const rcc::StackObject *ClonedStack =
+ ClonedStackIndex == Clone->FrameIndices.end()
+ ? nullptr
+ : Clone->ClonedFunction->frameInfo().object(ClonedStackIndex->second);
+ Context.expect(
+ ClonedInstruction->debugLocation() == DebugLocation &&
+ ClonedInstruction->sourceLocation() == rcc::SourceLocation{"debug-input.mir", 3, 5, 0} &&
+ ClonedInstruction->operandAt(0)->getIf<rcc::MetadataOperand>()->Value.reference() !=
+ nullptr &&
+ ClonedStack != nullptr && ClonedStack->description() != nullptr &&
+ ClonedStack->description()->DebugInfo == StackDebugInfo,
+ "clone must preserve distinct parser source and semantic debug information");
+
+ auto InvalidFunction = createFunction(Context, 22, "invalid-debug-metadata");
+ if (InvalidFunction == nullptr)
+ return;
+ rcc::MachineBasicBlock *InvalidBlock = appendBlock(Context, *InvalidFunction);
+ if (InvalidBlock == nullptr)
+ return;
+ rcc::MachineInstr *InvalidInstruction =
+ appendInstruction(Context, *InvalidFunction, *InvalidBlock, ValueOpcode);
+ if (InvalidInstruction == nullptr)
+ return;
+ auto InvalidValue =
+ rcc::MachineMetadataValue::reference(rcc::MachineMetadataReference::machineFunction(9));
+ Context.expect(InvalidValue.has_value(), "undefined scoped references remain representable");
+ if (!InvalidValue)
+ return;
+ auto InvalidOperand = rcc::MachineOperand::metadata(std::move(*InvalidValue));
+ Context.expect(InvalidOperand.has_value() &&
+ InvalidInstruction->addOperand(std::move(*InvalidOperand)).has_value() &&
+ !rcc::MachineVerifier::verify(*InvalidFunction, *Target),
+ "undefined instruction metadata references must fail unified verification");
+
+ auto InvalidFrameFunction = createFunction(Context, 23, "invalid-frame-debug-metadata");
+ if (InvalidFrameFunction == nullptr)
+ return;
+ rcc::StackObjectDesc InvalidStackDescription;
+ InvalidStackDescription.Size = 8;
+ InvalidStackDescription.Align = *rcc::Alignment::create(8);
+ InvalidStackDescription.DebugInfo = rcc::MachineVariableDebugInfo{
+ .Variable = rcc::MachineMetadataReference::machineFunction(9),
+ .Expression = std::nullopt,
+ .Location = std::nullopt,
+ };
+ Context.expect(
+ !InvalidFrameFunction->frameInfo().createObject(std::move(InvalidStackDescription)),
+ "typed stack debug information must reject function-local tuple metadata");
+}
+
void testInstructionNumbering(TestContext &Context) {
auto Target = createTarget();
auto Function = createFunction(Context, 1, "debug-numbering");
@@ -261,6 +480,8 @@ void testVerifierFailures(TestContext &Context) {
int main() {
TestContext Context;
+ testMetadataValuesAndLocations(Context);
+ testInstructionMetadataAndClone(Context);
testInstructionNumbering(Context);
testSubstitutionsAndClone(Context);
testVerifierFailures(Context);
diff --git a/backend/unittests/CodeGen/MachineFrameInfoTest.cpp b/backend/unittests/CodeGen/MachineFrameInfoTest.cpp
index 7368815..c9289c7 100644
--- a/backend/unittests/CodeGen/MachineFrameInfoTest.cpp
+++ b/backend/unittests/CodeGen/MachineFrameInfoTest.cpp
@@ -2,8 +2,11 @@
#include "rcc/CodeGen/MachineFrameInfo.h"
+#include "rcc/CodeGen/MachineDebugInfo.h"
+
#include <cstdint>
#include <iostream>
+#include <optional>
#include <string_view>
namespace {
@@ -130,6 +133,22 @@ void testInvalidDescriptions(TestContext &Context) {
Context.expect(!Frame.createObject(InvalidLifetime),
"stack object lifetime must reject equal boundaries");
+ auto InvalidDebugInfo = fixedObject(8, 8);
+ InvalidDebugInfo.DebugInfo.emplace();
+ Context.expect(!Frame.createObject(InvalidDebugInfo),
+ "stack object debug information must not be structurally empty");
+
+ auto DebugObject = fixedObject(8, 8);
+ DebugObject.DebugInfo = rcc::MachineVariableDebugInfo{
+ .Variable = rcc::MachineMetadataReference::embeddedIR(1),
+ .Expression = rcc::MachineMetadataValue::expression(rcc::MachineDIExpression{}),
+ .Location = std::nullopt,
+ };
+ auto DebugIndex = Frame.createObject(DebugObject);
+ Context.expect(DebugIndex.has_value() &&
+ Frame.object(*DebugIndex)->description()->DebugInfo == DebugObject.DebugInfo,
+ "stack object debug information must remain structured");
+
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);
diff --git a/backend/unittests/CodeGen/MachineFunctionRecordsTest.cpp b/backend/unittests/CodeGen/MachineFunctionRecordsTest.cpp
index 4f41b39..8ca6bf3 100644
--- a/backend/unittests/CodeGen/MachineFunctionRecordsTest.cpp
+++ b/backend/unittests/CodeGen/MachineFunctionRecordsTest.cpp
@@ -5,6 +5,7 @@
#include "Target/TestTarget.h"
#include "rcc/CodeGen/InstructionDescriptor.h"
#include "rcc/CodeGen/MachineBasicBlock.h"
+#include "rcc/CodeGen/MachineDebugInfo.h"
#include "rcc/CodeGen/MachineFunction.h"
#include "rcc/CodeGen/MachineFunctionCloner.h"
#include "rcc/CodeGen/MachineInstr.h"
@@ -14,6 +15,7 @@
#include <cstdint>
#include <iostream>
#include <memory>
+#include <optional>
#include <string>
#include <string_view>
#include <utility>
@@ -120,6 +122,8 @@ void testValueValidation(TestContext &Context) {
.Flags = 0,
}),
"a called-global record must require a nonempty callee");
+ Context.expect(!Records.addEntryValue({}),
+ "an entry-value record must require a physical register and debug metadata");
const rcc::MachinePrefetchTarget Target{
.Block = {.BaseID = 7, .CloneID = 3},
@@ -135,6 +139,9 @@ void testVerifierAndClone(TestContext &Context) {
auto Function = createFunction(Context, 1, "records");
if (!Target || Function == nullptr)
return;
+ Context.expect(
+ Function->metadata().addNode({.Slot = 0, .IsDistinct = false, .Elements = {}}).has_value(),
+ "function record metadata setup must succeed");
rcc::MachineBasicBlock *Block = appendBlock(Context, *Function);
if (Block == nullptr)
@@ -175,6 +182,22 @@ void testVerifierAndClone(TestContext &Context) {
})
.has_value(),
"structured prefetch target information must be stored");
+ auto EntryLocation =
+ rcc::MachineDebugLocation::reference(rcc::MachineMetadataReference::embeddedIR(12));
+ Context.expect(EntryLocation.has_value(), "entry-value debug location must be representable");
+ if (!EntryLocation)
+ return;
+ const rcc::MachineEntryValueInfo EntryValue{
+ .Reg = G0,
+ .DebugInfo =
+ {
+ .Variable = rcc::MachineMetadataReference::embeddedIR(10),
+ .Expression = rcc::MachineMetadataValue::expression(rcc::MachineDIExpression{}),
+ .Location = *EntryLocation,
+ },
+ };
+ Context.expect(Function->records().addEntryValue(EntryValue).has_value(),
+ "structured entry-value debug information must be stored");
Context.expect(rcc::MachineVerifier::verify(*Function, *Target).has_value(),
"well-formed function records must pass unified verification");
@@ -190,24 +213,25 @@ void testVerifierAndClone(TestContext &Context) {
const auto InstructionID = Clone->InstructionStableIDs.find(Call->stableID());
const rcc::MachineFunctionRecords &ClonedRecords = Clone->ClonedFunction->records();
- Context.expect(InstructionID != Clone->InstructionStableIDs.end() &&
- ClonedRecords.hasCalleeSavedRegisters() &&
- ClonedRecords.calleeSavedRegisters().size() == 2 &&
- ClonedRecords.callSites().size() == 1 &&
- ClonedRecords.callSites()[0].CallInstruction == InstructionID->second &&
- ClonedRecords.callSites()[0].ForwardedArguments.size() == 2 &&
- ClonedRecords.callSites()[0].ForwardedArguments[1].ArgumentNumber == 0 &&
- ClonedRecords.callSites()[0].CalleeTypeIDs ==
- std::vector<std::uint64_t>({0x11, 0x22}) &&
- ClonedRecords.calledGlobals().size() == 1 &&
- ClonedRecords.calledGlobals()[0].CallInstruction == InstructionID->second &&
- ClonedRecords.calledGlobals()[0].Callee == "callee" &&
- ClonedRecords.calledGlobals()[0].Flags == 5 &&
- ClonedRecords.prefetchTargets().size() == 1 &&
- ClonedRecords.prefetchTargets()[0].Block ==
- rcc::MachineBasicBlockProfileID{.BaseID = 19, .CloneID = 2} &&
- ClonedRecords.prefetchTargets()[0].CallsiteIndex == 8,
- "deep clone must preserve records and remap instruction StableIDs");
+ Context.expect(
+ InstructionID != Clone->InstructionStableIDs.end() &&
+ ClonedRecords.hasCalleeSavedRegisters() &&
+ ClonedRecords.calleeSavedRegisters().size() == 2 &&
+ ClonedRecords.callSites().size() == 1 &&
+ ClonedRecords.callSites()[0].CallInstruction == InstructionID->second &&
+ ClonedRecords.callSites()[0].ForwardedArguments.size() == 2 &&
+ ClonedRecords.callSites()[0].ForwardedArguments[1].ArgumentNumber == 0 &&
+ ClonedRecords.callSites()[0].CalleeTypeIDs == std::vector<std::uint64_t>({0x11, 0x22}) &&
+ ClonedRecords.calledGlobals().size() == 1 &&
+ ClonedRecords.calledGlobals()[0].CallInstruction == InstructionID->second &&
+ ClonedRecords.calledGlobals()[0].Callee == "callee" &&
+ ClonedRecords.calledGlobals()[0].Flags == 5 &&
+ ClonedRecords.prefetchTargets().size() == 1 &&
+ ClonedRecords.prefetchTargets()[0].Block ==
+ rcc::MachineBasicBlockProfileID{.BaseID = 19, .CloneID = 2} &&
+ ClonedRecords.prefetchTargets()[0].CallsiteIndex == 8 &&
+ ClonedRecords.entryValues().size() == 1 && ClonedRecords.entryValues()[0] == EntryValue,
+ "deep clone must preserve records and remap instruction StableIDs");
}
void testCrossReferenceFailures(TestContext &Context) {
@@ -306,6 +330,25 @@ void testCrossReferenceFailures(TestContext &Context) {
Context.expect(!rcc::MachineVerifier::verify(*Function, *Target),
"linked block bb_id values must be unique");
}
+
+ {
+ auto Function = createFunction(Context, 13, "entry-value-register");
+ if (Function == nullptr)
+ return;
+ Context.expect(Function->records()
+ .addEntryValue({
+ .Reg = phys(999),
+ .DebugInfo =
+ {
+ .Variable = rcc::MachineMetadataReference::embeddedIR(1),
+ .Expression = std::nullopt,
+ .Location = std::nullopt,
+ },
+ })
+ .has_value() &&
+ !rcc::MachineVerifier::verify(*Function, *Target),
+ "entry-value records must reject target-unknown physical registers");
+ }
}
} // namespace
diff --git a/backend/unittests/CodeGen/MachineIRBuilderTest.cpp b/backend/unittests/CodeGen/MachineIRBuilderTest.cpp
index 15f6cd2..9f5cf5e 100644
--- a/backend/unittests/CodeGen/MachineIRBuilderTest.cpp
+++ b/backend/unittests/CodeGen/MachineIRBuilderTest.cpp
@@ -7,6 +7,7 @@
#include "rcc/CodeGen/LowLevelType.h"
#include "rcc/CodeGen/MachineBasicBlock.h"
#include "rcc/CodeGen/MachineConstantPool.h"
+#include "rcc/CodeGen/MachineDebugInfo.h"
#include "rcc/CodeGen/MachineFunction.h"
#include "rcc/CodeGen/MachineInstr.h"
#include "rcc/CodeGen/MachineJumpTableInfo.h"
@@ -208,6 +209,7 @@ void testTransactionalBuild(TestContext &Context) {
if (!Incomplete)
return;
Context.expect(!Incomplete->tieOperands(0, 1) && !Incomplete->setSourceLocation({"", 0, 0, 0}) &&
+ !Incomplete->setDebugLocation({}) &&
!Incomplete->setOrigin(rcc::OriginID::fromValue(0)),
"invalid pending metadata and tie indices must be rejected");
Context.expect(!Incomplete->build(), "descriptor validation must reject a missing operand");
@@ -225,10 +227,27 @@ void testTransactionalBuild(TestContext &Context) {
"an explicit tie matching the descriptor default must be idempotent");
Context.expect(Complete->setFlags({rcc::MachineInstrFlag::FrameSetup}).has_value(),
"valid instruction flags must be accepted");
+ auto InlineDebugLocation = rcc::MachineDILocation::create({
+ {
+ .Line = 11,
+ .Column = 2,
+ .Scope = rcc::MachineMetadataReference::embeddedIR(3),
+ .IsImplicitCode = false,
+ .AtomGroup = 0,
+ .AtomRank = 0,
+ },
+ });
+ Context.expect(InlineDebugLocation.has_value(),
+ "builder debug location setup must be structurally valid");
+ if (!InlineDebugLocation)
+ return;
+ const rcc::MachineDebugLocation DebugLocation =
+ rcc::MachineDebugLocation::inlineLocation(*InlineDebugLocation);
Context.expect(
Complete->setSourceLocation(rcc::SourceLocation{"builder.mir", 7, 3, 1}).has_value() &&
+ Complete->setDebugLocation(DebugLocation).has_value() &&
Complete->setOrigin(rcc::OriginID::fromValue(19)).has_value(),
- "valid source and origin metadata must be accepted");
+ "valid parser source, debug location, and origin metadata must be accepted");
auto Built = Complete->build();
Context.expect(Built.has_value(), "valid pending instruction must build");
if (!Built)
@@ -244,13 +263,14 @@ void testTransactionalBuild(TestContext &Context) {
"descriptor-default tie must be materialized bidirectionally");
Context.expect(Instruction->flags().has(rcc::MachineInstrFlag::FrameSetup) &&
Instruction->sourceLocation() == rcc::SourceLocation{"builder.mir", 7, 3, 1} &&
+ Instruction->debugLocation() == DebugLocation &&
Instruction->origin() == rcc::OriginID::fromValue(19),
"builder metadata must be preserved on the published instruction");
Context.expect(Target->validateInstruction(*Instruction, Function->registerInfo()).has_value(),
"builder output must satisfy the shared instruction descriptor");
Context.expect(!Complete->build(), "a successful pending builder must be single-use");
Context.expect(!Complete->setFlags({}) && !Complete->clearSourceLocation() &&
- !Complete->clearOrigin(),
+ !Complete->clearDebugLocation() && !Complete->clearOrigin(),
"all pending-builder mutators must reject use after publication");
Context.expect(Function->registerInfo().verifyDefUseChains().has_value(),
"published builder operands must enter exact def-use chains");
diff --git a/backend/unittests/CodeGen/MachineIRCoreTest.cpp b/backend/unittests/CodeGen/MachineIRCoreTest.cpp
index 1083422..028afc5 100644
--- a/backend/unittests/CodeGen/MachineIRCoreTest.cpp
+++ b/backend/unittests/CodeGen/MachineIRCoreTest.cpp
@@ -169,10 +169,18 @@ void testOperandKinds(TestContext &Context) {
Context.expect(!rcc::MachineOperand::registerMask(nullptr),
"null register mask operand must be rejected");
- auto Metadata = rcc::MachineOperand::metadata("source-marker");
- Context.expect(Metadata && Metadata->kind() == rcc::MachineOperand::Kind::Metadata,
- "metadata operand must preserve its kind");
- Context.expect(!rcc::MachineOperand::metadata(""), "empty metadata marker must be rejected");
+ auto MetadataReference =
+ rcc::MachineMetadataValue::reference(rcc::MachineMetadataReference::embeddedIR(0));
+ Context.expect(MetadataReference.has_value(),
+ "a valid scoped metadata reference must form an operand value");
+ if (!MetadataReference)
+ return;
+ auto Metadata = rcc::MachineOperand::metadata(std::move(*MetadataReference));
+ Context.expect(Metadata && Metadata->kind() == rcc::MachineOperand::Kind::Metadata &&
+ Metadata->getIf<rcc::MetadataOperand>()->Value.reference() != nullptr,
+ "metadata operand must preserve its scoped semantic value");
+ Context.expect(!rcc::MachineOperand::metadata({}),
+ "an unrepresented metadata operand value must be rejected");
auto Target = rcc::MachineOperand::targetIndex(rcc::TargetIndex::fromValue(8), 9);
Context.expect(Target.getIf<rcc::TargetIndexOperand>()->Offset == 9,