aboutsummaryrefslogtreecommitdiff
path: root/backend
diff options
context:
space:
mode:
Diffstat (limited to 'backend')
-rw-r--r--backend/include/rcc/CodeGen/MachineFunction.h10
-rw-r--r--backend/include/rcc/CodeGen/MachineMetadata.h131
-rw-r--r--backend/lib/CodeGen/CMakeLists.txt4
-rw-r--r--backend/lib/CodeGen/MachineFunctionCloner.cpp4
-rw-r--r--backend/lib/CodeGen/MachineMetadata.cpp114
-rw-r--r--backend/lib/CodeGen/MachineVerifier.cpp3
-rw-r--r--backend/unittests/CodeGen/CMakeLists.txt15
-rw-r--r--backend/unittests/CodeGen/MachineMetadataTest.cpp211
8 files changed, 492 insertions, 0 deletions
diff --git a/backend/include/rcc/CodeGen/MachineFunction.h b/backend/include/rcc/CodeGen/MachineFunction.h
index 8addd4e..7b325d4 100644
--- a/backend/include/rcc/CodeGen/MachineFunction.h
+++ b/backend/include/rcc/CodeGen/MachineFunction.h
@@ -10,6 +10,7 @@
#include "rcc/CodeGen/MachineFunctionProperties.h"
#include "rcc/CodeGen/MachineFunctionRecords.h"
#include "rcc/CodeGen/MachineJumpTableInfo.h"
+#include "rcc/CodeGen/MachineMetadata.h"
#include "rcc/CodeGen/MachineRegisterInfo.h"
#include "rcc/CodeGen/StableID.h"
#include "rcc/CodeGen/TargetFunctionState.h"
@@ -108,6 +109,14 @@ public:
return Records;
}
+ [[nodiscard]] MachineMetadataInfo &metadata() noexcept {
+ return Metadata;
+ }
+
+ [[nodiscard]] const MachineMetadataInfo &metadata() const noexcept {
+ return Metadata;
+ }
+
[[nodiscard]] const TargetFunctionState &targetState() const noexcept {
return TargetState;
}
@@ -189,6 +198,7 @@ private:
MachineFunctionProperties Properties;
MachineFunctionAttributes Attributes;
MachineFunctionRecords Records;
+ MachineMetadataInfo Metadata;
TargetFunctionState TargetState;
MachineCompilationStatus CompilationStatus;
MachineBasicBlock *FirstBlock = nullptr;
diff --git a/backend/include/rcc/CodeGen/MachineMetadata.h b/backend/include/rcc/CodeGen/MachineMetadata.h
new file mode 100644
index 0000000..577d5da
--- /dev/null
+++ b/backend/include/rcc/CodeGen/MachineMetadata.h
@@ -0,0 +1,131 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#ifndef RCC_CODEGEN_MACHINEMETADATA_H
+#define RCC_CODEGEN_MACHINEMETADATA_H
+
+#include "rcc/Support/Error.h"
+
+#include <cstddef>
+#include <cstdint>
+#include <span>
+#include <string>
+#include <unordered_map>
+#include <utility>
+#include <variant>
+#include <vector>
+
+namespace rcc {
+
+enum class MachineMetadataScope : std::uint8_t {
+ EmbeddedIR,
+ MachineFunction,
+};
+
+class MachineMetadataReference final {
+public:
+ constexpr MachineMetadataReference() noexcept = default;
+
+ [[nodiscard]] static constexpr MachineMetadataReference embeddedIR(std::uint32_t Slot) noexcept {
+ return MachineMetadataReference(MachineMetadataScope::EmbeddedIR, Slot);
+ }
+
+ [[nodiscard]] static constexpr MachineMetadataReference
+ machineFunction(std::uint32_t Slot) noexcept {
+ return MachineMetadataReference(MachineMetadataScope::MachineFunction, Slot);
+ }
+
+ [[nodiscard]] constexpr bool isValid() const noexcept {
+ return Valid;
+ }
+
+ [[nodiscard]] constexpr MachineMetadataScope scope() const noexcept {
+ return Scope;
+ }
+
+ [[nodiscard]] constexpr std::uint32_t slot() const noexcept {
+ return Slot;
+ }
+
+ friend constexpr bool operator==(MachineMetadataReference Left,
+ MachineMetadataReference Right) noexcept = default;
+
+private:
+ constexpr MachineMetadataReference(MachineMetadataScope Scope, std::uint32_t Slot) noexcept
+ : Scope(Scope), Slot(Slot), Valid(true) {}
+
+ MachineMetadataScope Scope = MachineMetadataScope::EmbeddedIR;
+ std::uint32_t Slot = 0;
+ bool Valid = false;
+};
+
+class MachineMetadataElement final {
+public:
+ MachineMetadataElement() = default;
+
+ [[nodiscard]] static Expected<MachineMetadataElement>
+ reference(MachineMetadataReference Reference);
+ [[nodiscard]] static MachineMetadataElement string(std::string Value);
+
+ [[nodiscard]] bool isValid() const noexcept {
+ return !std::holds_alternative<std::monostate>(Storage);
+ }
+
+ [[nodiscard]] const MachineMetadataReference *reference() const noexcept {
+ return std::get_if<MachineMetadataReference>(&Storage);
+ }
+
+ [[nodiscard]] const std::string *string() const noexcept {
+ return std::get_if<std::string>(&Storage);
+ }
+
+ friend bool operator==(const MachineMetadataElement &Left,
+ const MachineMetadataElement &Right) = default;
+
+private:
+ explicit MachineMetadataElement(MachineMetadataReference Reference) : Storage(Reference) {}
+ explicit MachineMetadataElement(std::string Value) : Storage(std::move(Value)) {}
+
+ std::variant<std::monostate, MachineMetadataReference, std::string> Storage;
+};
+
+struct MachineMetadataNode final {
+ std::uint32_t Slot = 0;
+ bool IsDistinct = false;
+ std::vector<MachineMetadataElement> Elements;
+
+ friend bool operator==(const MachineMetadataNode &Left,
+ const MachineMetadataNode &Right) = default;
+};
+
+class MachineMetadataInfo final {
+public:
+ MachineMetadataInfo() = default;
+ MachineMetadataInfo(const MachineMetadataInfo &) = delete;
+ MachineMetadataInfo &operator=(const MachineMetadataInfo &) = delete;
+ MachineMetadataInfo(MachineMetadataInfo &&) noexcept = default;
+ MachineMetadataInfo &operator=(MachineMetadataInfo &&) noexcept = default;
+ ~MachineMetadataInfo() = default;
+
+ [[nodiscard]] Expected<void> addNode(MachineMetadataNode Node);
+
+ [[nodiscard]] const MachineMetadataNode *node(std::uint32_t Slot) const noexcept;
+
+ [[nodiscard]] std::span<const MachineMetadataNode> nodes() const noexcept {
+ return Nodes;
+ }
+
+ [[nodiscard]] Expected<void> verify() const;
+ [[nodiscard]] Expected<MachineMetadataInfo> clone() const;
+
+ friend bool operator==(const MachineMetadataInfo &Left, const MachineMetadataInfo &Right) {
+ return Left.Nodes == Right.Nodes;
+ }
+
+private:
+ std::vector<MachineMetadataNode> Nodes;
+ std::unordered_map<std::uint32_t, std::size_t> SlotToIndex;
+};
+
+} // namespace rcc
+
+#endif // RCC_CODEGEN_MACHINEMETADATA_H
diff --git a/backend/lib/CodeGen/CMakeLists.txt b/backend/lib/CodeGen/CMakeLists.txt
index 3625473..ec95c3c 100644
--- a/backend/lib/CodeGen/CMakeLists.txt
+++ b/backend/lib/CodeGen/CMakeLists.txt
@@ -19,6 +19,7 @@ set(
MachineInstr.cpp
MachineJumpTableInfo.cpp
MachineMemOperand.cpp
+ MachineMetadata.cpp
MachineOperand.cpp
MachineRegisterInfo.cpp
MachineVerifier.cpp
@@ -50,6 +51,7 @@ set(
"${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/MachineInstr.h"
"${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/MachineJumpTableInfo.h"
"${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/MachineMemOperand.h"
+ "${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/MachineMetadata.h"
"${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/MachineOperand.h"
"${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/MachineRegisterInfo.h"
"${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/MachineVerifier.h"
@@ -110,6 +112,7 @@ rcc_backend_register_format_files(
"${CMAKE_CURRENT_SOURCE_DIR}/MachineInstr.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/MachineJumpTableInfo.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/MachineMemOperand.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/MachineMetadata.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/MachineOperand.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/MachineRegisterInfo.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/MachineVerifier.cpp"
@@ -141,6 +144,7 @@ rcc_backend_register_tidy_files(
"${CMAKE_CURRENT_SOURCE_DIR}/MachineInstr.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/MachineJumpTableInfo.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/MachineMemOperand.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/MachineMetadata.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/MachineOperand.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/MachineRegisterInfo.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/MachineVerifier.cpp"
diff --git a/backend/lib/CodeGen/MachineFunctionCloner.cpp b/backend/lib/CodeGen/MachineFunctionCloner.cpp
index 2f99341..e12e33e 100644
--- a/backend/lib/CodeGen/MachineFunctionCloner.cpp
+++ b/backend/lib/CodeGen/MachineFunctionCloner.cpp
@@ -626,6 +626,10 @@ Expected<MachineFunctionCloneResult> cloneMachineFunction(const MachineFunction
if (!Records)
return std::unexpected(Records.error());
Result.ClonedFunction->records() = std::move(*Records);
+ auto Metadata = Source.metadata().clone();
+ if (!Metadata)
+ return std::unexpected(Metadata.error());
+ Result.ClonedFunction->metadata() = std::move(*Metadata);
Result.ClonedFunction->properties() = Source.properties();
Result.ClonedFunction->attributes() = Source.attributes();
auto DestinationVerification = MachineVerifier::verify(*Result.ClonedFunction, Target);
diff --git a/backend/lib/CodeGen/MachineMetadata.cpp b/backend/lib/CodeGen/MachineMetadata.cpp
new file mode 100644
index 0000000..24a04e9
--- /dev/null
+++ b/backend/lib/CodeGen/MachineMetadata.cpp
@@ -0,0 +1,114 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "rcc/CodeGen/MachineMetadata.h"
+
+#include <cstddef>
+#include <string>
+#include <unordered_set>
+#include <utility>
+
+namespace rcc {
+namespace {
+
+[[nodiscard]] Expected<void> verifyNodeElements(const MachineMetadataNode &Node) {
+ for (const MachineMetadataElement &Element : Node.Elements) {
+ if (!Element.isValid()) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "machine metadata tuple contains an invalid element");
+ }
+ const MachineMetadataReference *Reference = Element.reference();
+ if (Reference != nullptr && !Reference->isValid()) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "machine metadata tuple contains an invalid reference");
+ }
+ }
+ return {};
+}
+
+} // namespace
+
+Expected<MachineMetadataElement>
+MachineMetadataElement::reference(MachineMetadataReference Reference) {
+ if (!Reference.isValid()) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "machine metadata element reference must be valid");
+ }
+ return MachineMetadataElement(Reference);
+}
+
+MachineMetadataElement MachineMetadataElement::string(std::string Value) {
+ return MachineMetadataElement(std::move(Value));
+}
+
+Expected<void> MachineMetadataInfo::addNode(MachineMetadataNode Node) {
+ auto Verification = verifyNodeElements(Node);
+ if (!Verification)
+ return std::unexpected(Verification.error());
+ if (SlotToIndex.contains(Node.Slot)) {
+ return makeUnexpected(ErrorCode::InvalidArgument, "machine metadata slot is already defined");
+ }
+
+ const std::size_t Index = Nodes.size();
+ SlotToIndex.emplace(Node.Slot, Index);
+ Nodes.push_back(std::move(Node));
+ return {};
+}
+
+const MachineMetadataNode *MachineMetadataInfo::node(std::uint32_t Slot) const noexcept {
+ const auto Position = SlotToIndex.find(Slot);
+ if (Position == SlotToIndex.end())
+ return nullptr;
+ return &Nodes[Position->second];
+}
+
+Expected<void> MachineMetadataInfo::verify() const {
+ if (SlotToIndex.size() != Nodes.size()) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "machine metadata slot index has an inconsistent size");
+ }
+
+ std::unordered_set<std::uint32_t> Slots;
+ for (std::size_t Index = 0; Index < Nodes.size(); ++Index) {
+ const MachineMetadataNode &Node = Nodes[Index];
+ if (!Slots.insert(Node.Slot).second) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "machine metadata contains duplicate slot definitions");
+ }
+ const auto Position = SlotToIndex.find(Node.Slot);
+ if (Position == SlotToIndex.end() || Position->second != Index) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "machine metadata slot index is inconsistent");
+ }
+ auto ElementVerification = verifyNodeElements(Node);
+ if (!ElementVerification)
+ return ElementVerification;
+ }
+
+ for (const MachineMetadataNode &Node : Nodes) {
+ for (const MachineMetadataElement &Element : Node.Elements) {
+ const MachineMetadataReference *Reference = Element.reference();
+ if (Reference != nullptr && Reference->scope() == MachineMetadataScope::MachineFunction &&
+ !Slots.contains(Reference->slot())) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "machine metadata tuple references an undefined function slot");
+ }
+ }
+ }
+ return {};
+}
+
+Expected<MachineMetadataInfo> MachineMetadataInfo::clone() const {
+ auto Verification = verify();
+ if (!Verification)
+ return std::unexpected(Verification.error());
+
+ MachineMetadataInfo Result;
+ for (const MachineMetadataNode &Node : Nodes) {
+ auto AddResult = Result.addNode(Node);
+ if (!AddResult)
+ return std::unexpected(AddResult.error());
+ }
+ return Result;
+}
+
+} // namespace rcc
diff --git a/backend/lib/CodeGen/MachineVerifier.cpp b/backend/lib/CodeGen/MachineVerifier.cpp
index 2fbbd38..2a29343 100644
--- a/backend/lib/CodeGen/MachineVerifier.cpp
+++ b/backend/lib/CodeGen/MachineVerifier.cpp
@@ -37,6 +37,9 @@ public:
auto AttributeResult = Function.attributes().verify();
if (!AttributeResult)
return failWith("function attributes", AttributeResult.error());
+ auto MetadataResult = Function.metadata().verify();
+ if (!MetadataResult)
+ return failWith("machine metadata", MetadataResult.error());
if (Function.targetState().hasValue()) {
auto TargetStateResult = Function.targetState().verify();
if (!TargetStateResult)
diff --git a/backend/unittests/CodeGen/CMakeLists.txt b/backend/unittests/CodeGen/CMakeLists.txt
index ed534bd..e7eed63 100644
--- a/backend/unittests/CodeGen/CMakeLists.txt
+++ b/backend/unittests/CodeGen/CMakeLists.txt
@@ -15,6 +15,7 @@ set(
MachineIRBuilderTest.cpp
MachineIRCoreTest.cpp
MachineJumpTableInfoTest.cpp
+ MachineMetadataTest.cpp
MachineVerifierTest.cpp
MachineVerifierTargetTest.cpp
MachineValueTypesTest.cpp
@@ -206,6 +207,18 @@ add_test(
COMMAND RCCBackendCodeGenMachineJumpTableInfoTests
)
+add_executable(RCCBackendCodeGenMachineMetadataTests MachineMetadataTest.cpp)
+rcc_backend_configure_target(RCCBackendCodeGenMachineMetadataTests)
+target_link_libraries(
+ RCCBackendCodeGenMachineMetadataTests
+ PRIVATE RCCBackend::CodeGen RCCBackend::TestTarget
+)
+
+add_test(
+ NAME RCCBackend.CodeGen.MachineMetadata
+ COMMAND RCCBackendCodeGenMachineMetadataTests
+)
+
add_executable(RCCBackendCodeGenTargetConstantTests TargetConstantTest.cpp)
rcc_backend_configure_target(RCCBackendCodeGenTargetConstantTests)
target_link_libraries(RCCBackendCodeGenTargetConstantTests PRIVATE RCCBackend::CodeGen)
@@ -253,6 +266,7 @@ rcc_backend_register_format_files(
"${CMAKE_CURRENT_SOURCE_DIR}/MachineIRBuilderTest.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/MachineIRCoreTest.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/MachineJumpTableInfoTest.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/MachineMetadataTest.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/MachineVerifierTest.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/MachineVerifierTargetTest.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/MachineValueTypesTest.cpp"
@@ -274,6 +288,7 @@ rcc_backend_register_tidy_files(
"${CMAKE_CURRENT_SOURCE_DIR}/MachineIRBuilderTest.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/MachineIRCoreTest.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/MachineJumpTableInfoTest.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/MachineMetadataTest.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/MachineVerifierTest.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/MachineVerifierTargetTest.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/MachineValueTypesTest.cpp"
diff --git a/backend/unittests/CodeGen/MachineMetadataTest.cpp b/backend/unittests/CodeGen/MachineMetadataTest.cpp
new file mode 100644
index 0000000..6669e2a
--- /dev/null
+++ b/backend/unittests/CodeGen/MachineMetadataTest.cpp
@@ -0,0 +1,211 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "rcc/CodeGen/MachineMetadata.h"
+
+#include "Target/TestTarget.h"
+#include "rcc/CodeGen/InstructionDescriptor.h"
+#include "rcc/CodeGen/MachineFunction.h"
+#include "rcc/CodeGen/MachineFunctionCloner.h"
+#include "rcc/CodeGen/MachineVerifier.h"
+#include "rcc/CodeGen/TargetDescription.h"
+
+#include <cstdint>
+#include <iostream>
+#include <memory>
+#include <string>
+#include <string_view>
+#include <utility>
+#include <vector>
+
+namespace {
+
+constexpr std::uint32_t NopOpcode = 1;
+
+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::Expected<rcc::TargetDescription> createTarget() {
+ rcc::InstructionDescriptor Nop;
+ Nop.InstructionOpcode = rcc::Opcode::fromValue(NopOpcode);
+ Nop.Name = "TEST_NOP";
+ Nop.Flags = {rcc::InstructionFlag::ProducesCode};
+
+ std::vector<rcc::InstructionDescriptor> Descriptors;
+ Descriptors.push_back(std::move(Nop));
+ auto Instructions = rcc::InstructionSet::create(std::move(Descriptors));
+ if (!Instructions)
+ return std::unexpected(Instructions.error());
+ return rcc::test::createTestTargetDescription(std::move(*Instructions));
+}
+
+[[nodiscard]] std::unique_ptr<rcc::MachineFunction>
+createFunction(TestContext &Context, std::uint64_t StableID, std::string Name) {
+ auto Function = rcc::MachineFunction::create(std::move(Name),
+ rcc::MachineFunctionStableID::fromValue(StableID));
+ Context.expect(Function.has_value(), "machine function creation must succeed");
+ if (!Function)
+ return nullptr;
+ return std::move(*Function);
+}
+
+[[nodiscard]] rcc::MachineMetadataElement
+referenceElement(TestContext &Context, rcc::MachineMetadataReference Reference) {
+ auto Element = rcc::MachineMetadataElement::reference(Reference);
+ Context.expect(Element.has_value(), "valid machine metadata references must form elements");
+ return Element ? std::move(*Element) : rcc::MachineMetadataElement{};
+}
+
+void testReferenceIdentity(TestContext &Context) {
+ const rcc::MachineMetadataReference Invalid;
+ const rcc::MachineMetadataReference EmbeddedZero = rcc::MachineMetadataReference::embeddedIR(0);
+ const rcc::MachineMetadataReference FunctionZero =
+ rcc::MachineMetadataReference::machineFunction(0);
+
+ Context.expect(!Invalid.isValid() && EmbeddedZero.isValid() && FunctionZero.isValid(),
+ "metadata slot zero must be valid in both explicit reference scopes");
+ Context.expect(EmbeddedZero != FunctionZero &&
+ EmbeddedZero.scope() == rcc::MachineMetadataScope::EmbeddedIR &&
+ FunctionZero.scope() == rcc::MachineMetadataScope::MachineFunction &&
+ EmbeddedZero.slot() == 0 && FunctionZero.slot() == 0,
+ "embedded and function-local metadata slots must have distinct identities");
+ Context.expect(!rcc::MachineMetadataElement::reference(Invalid),
+ "an invalid metadata reference must not form an element");
+}
+
+void testOrderedGraphValidation(TestContext &Context) {
+ rcc::MachineMetadataInfo Metadata;
+ rcc::MachineMetadataNode First{
+ .Slot = 0,
+ .IsDistinct = true,
+ .Elements =
+ {
+ referenceElement(Context, rcc::MachineMetadataReference::embeddedIR(0)),
+ rcc::MachineMetadataElement::string(""),
+ referenceElement(Context, rcc::MachineMetadataReference::machineFunction(1)),
+ },
+ };
+ Context.expect(Metadata.addNode(std::move(First)).has_value() && !Metadata.verify(),
+ "forward references must be accepted before graph closure is verified");
+
+ rcc::MachineMetadataNode Second{
+ .Slot = 1,
+ .IsDistinct = false,
+ .Elements =
+ {
+ referenceElement(Context, rcc::MachineMetadataReference::machineFunction(0)),
+ rcc::MachineMetadataElement::string("payload"),
+ },
+ };
+ Context.expect(Metadata.addNode(std::move(Second)).has_value() && Metadata.verify().has_value(),
+ "closed cyclic metadata tuple graphs must be valid");
+ Context.expect(Metadata.nodes().size() == 2 && Metadata.nodes()[0].Slot == 0 &&
+ Metadata.nodes()[0].IsDistinct && Metadata.nodes()[1].Slot == 1 &&
+ Metadata.node(0) == &Metadata.nodes()[0] &&
+ Metadata.node(1) == &Metadata.nodes()[1] && Metadata.node(2) == nullptr,
+ "metadata definitions and lookup must preserve deterministic source order");
+ Context.expect(Metadata.nodes()[0].Elements[1].string() != nullptr &&
+ Metadata.nodes()[0].Elements[1].string()->empty(),
+ "empty metadata strings must remain valid tuple elements");
+
+ const std::size_t OriginalSize = Metadata.nodes().size();
+ Context.expect(!Metadata.addNode({.Slot = 0, .IsDistinct = false, .Elements = {}}) &&
+ Metadata.nodes().size() == OriginalSize,
+ "duplicate metadata definitions must be rejected without changing the graph");
+
+ auto Clone = Metadata.clone();
+ Context.expect(Clone.has_value() && *Clone == Metadata &&
+ Clone->nodes().data() != Metadata.nodes().data(),
+ "metadata graphs must support independent deterministic deep cloning");
+
+ rcc::MachineMetadataInfo InvalidMetadata;
+ rcc::MachineMetadataNode InvalidNode;
+ InvalidNode.Elements.emplace_back();
+ Context.expect(!InvalidMetadata.addNode(std::move(InvalidNode)) &&
+ InvalidMetadata.nodes().empty(),
+ "invalid tuple elements must be rejected before publication");
+}
+
+void testFunctionVerifierAndClone(TestContext &Context) {
+ auto Target = createTarget();
+ auto Function = createFunction(Context, 1, "metadata");
+ if (!Target || Function == nullptr)
+ return;
+
+ Context.expect(
+ Function->metadata()
+ .addNode({
+ .Slot = 7,
+ .IsDistinct = false,
+ .Elements =
+ {
+ referenceElement(Context, rcc::MachineMetadataReference::embeddedIR(3)),
+ rcc::MachineMetadataElement::string("root"),
+ },
+ })
+ .has_value() &&
+ Function->metadata()
+ .addNode({
+ .Slot = 2,
+ .IsDistinct = true,
+ .Elements =
+ {
+ referenceElement(Context,
+ rcc::MachineMetadataReference::machineFunction(7)),
+ },
+ })
+ .has_value() &&
+ rcc::MachineVerifier::verify(*Function, *Target).has_value(),
+ "well-formed machine metadata must pass unified function verification");
+
+ auto Clone =
+ rcc::cloneMachineFunction(*Function, *Target,
+ {
+ .NewStableID = rcc::MachineFunctionStableID::fromValue(2),
+ .NewName = std::string("metadata.clone"),
+ });
+ Context.expect(Clone.has_value() && Clone->ClonedFunction->metadata() == Function->metadata() &&
+ Clone->ClonedFunction->metadata().nodes().data() !=
+ Function->metadata().nodes().data(),
+ "function cloning must preserve an independent metadata graph");
+
+ auto InvalidFunction = createFunction(Context, 3, "invalid-metadata");
+ if (InvalidFunction == nullptr)
+ return;
+ Context.expect(
+ InvalidFunction->metadata()
+ .addNode({
+ .Slot = 0,
+ .Elements =
+ {
+ referenceElement(Context,
+ rcc::MachineMetadataReference::machineFunction(99)),
+ },
+ })
+ .has_value() &&
+ !rcc::MachineVerifier::verify(*InvalidFunction, *Target),
+ "the unified verifier must reject undefined function-local metadata references");
+}
+
+} // namespace
+
+int main() {
+ TestContext Context;
+ testReferenceIdentity(Context);
+ testOrderedGraphValidation(Context);
+ testFunctionVerifierAndClone(Context);
+ return Context.result();
+}