diff options
Diffstat (limited to 'backend')
| -rw-r--r-- | backend/include/rcc/CodeGen/MachineFunction.h | 10 | ||||
| -rw-r--r-- | backend/include/rcc/CodeGen/MachineMetadata.h | 131 | ||||
| -rw-r--r-- | backend/lib/CodeGen/CMakeLists.txt | 4 | ||||
| -rw-r--r-- | backend/lib/CodeGen/MachineFunctionCloner.cpp | 4 | ||||
| -rw-r--r-- | backend/lib/CodeGen/MachineMetadata.cpp | 114 | ||||
| -rw-r--r-- | backend/lib/CodeGen/MachineVerifier.cpp | 3 | ||||
| -rw-r--r-- | backend/unittests/CodeGen/CMakeLists.txt | 15 | ||||
| -rw-r--r-- | backend/unittests/CodeGen/MachineMetadataTest.cpp | 211 |
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(); +} |
