aboutsummaryrefslogtreecommitdiff
path: root/backend/lib/CodeGen
diff options
context:
space:
mode:
authorRichard-Qin-X <richard.qin.001@gmail.com>2026-08-01 14:50:04 +0800
committerRichard-Qin-X <richard.qin.001@gmail.com>2026-08-01 14:50:04 +0800
commitcfdbcc469f9691bd9dce3b0b86982a63d86ca6b7 (patch)
tree19f4ad856c3e4900a0501ab2cad41981b49569b5 /backend/lib/CodeGen
parenta69d14b8ebc028dddc689658b08d42d3e83f5e38 (diff)
downloadrcc-project-cfdbcc469f9691bd9dce3b0b86982a63d86ca6b7.tar.gz
rcc-project-cfdbcc469f9691bd9dce3b0b86982a63d86ca6b7.tar.xz
[CodeGen] Add target state and compilation status
Diffstat (limited to 'backend/lib/CodeGen')
-rw-r--r--backend/lib/CodeGen/CMakeLists.txt8
-rw-r--r--backend/lib/CodeGen/MachineCompilationStatus.cpp195
-rw-r--r--backend/lib/CodeGen/MachineFunction.cpp11
-rw-r--r--backend/lib/CodeGen/MachineFunctionCloner.cpp97
-rw-r--r--backend/lib/CodeGen/MachineVerifier.cpp22
-rw-r--r--backend/lib/CodeGen/TargetFunctionState.cpp82
6 files changed, 415 insertions, 0 deletions
diff --git a/backend/lib/CodeGen/CMakeLists.txt b/backend/lib/CodeGen/CMakeLists.txt
index 3dd17a4..68ebf04 100644
--- a/backend/lib/CodeGen/CMakeLists.txt
+++ b/backend/lib/CodeGen/CMakeLists.txt
@@ -4,6 +4,7 @@ set(
RCCBackendCodeGenSources
InstructionDescriptor.cpp
LowLevelType.cpp
+ MachineCompilationStatus.cpp
MachineBasicBlock.cpp
MachineCFGUpdater.cpp
MachineConstantPool.cpp
@@ -22,11 +23,13 @@ set(
Register.cpp
RegisterMask.cpp
TargetConstant.cpp
+ TargetFunctionState.cpp
)
set(
RCCBackendCodeGenHeaders
"${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/InstructionDescriptor.h"
"${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/LowLevelType.h"
+ "${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/MachineCompilationStatus.h"
"${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/MachineBasicBlock.h"
"${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/MachineCFGUpdater.h"
"${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/MachineConstantPool.h"
@@ -46,6 +49,7 @@ set(
"${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/RegisterMask.h"
"${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/StableID.h"
"${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/TargetConstant.h"
+ "${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/TargetFunctionState.h"
)
add_library(RCCBackendCodeGen ${RCCBackendCodeGenSources})
@@ -79,6 +83,7 @@ install(
rcc_backend_register_format_files(
"${CMAKE_CURRENT_SOURCE_DIR}/InstructionDescriptor.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/LowLevelType.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/MachineCompilationStatus.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/MachineBasicBlock.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/MachineCFGUpdater.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/MachineConstantPool.cpp"
@@ -97,11 +102,13 @@ rcc_backend_register_format_files(
"${CMAKE_CURRENT_SOURCE_DIR}/Register.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/RegisterMask.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/TargetConstant.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/TargetFunctionState.cpp"
${RCCBackendCodeGenHeaders}
)
rcc_backend_register_tidy_files(
"${CMAKE_CURRENT_SOURCE_DIR}/InstructionDescriptor.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/LowLevelType.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/MachineCompilationStatus.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/MachineBasicBlock.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/MachineCFGUpdater.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/MachineConstantPool.cpp"
@@ -120,4 +127,5 @@ rcc_backend_register_tidy_files(
"${CMAKE_CURRENT_SOURCE_DIR}/Register.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/RegisterMask.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/TargetConstant.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/TargetFunctionState.cpp"
)
diff --git a/backend/lib/CodeGen/MachineCompilationStatus.cpp b/backend/lib/CodeGen/MachineCompilationStatus.cpp
new file mode 100644
index 0000000..600deac
--- /dev/null
+++ b/backend/lib/CodeGen/MachineCompilationStatus.cpp
@@ -0,0 +1,195 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "rcc/CodeGen/MachineCompilationStatus.h"
+
+#include "rcc/CodeGen/MachineFunctionCloner.h"
+
+#include <array>
+#include <cstddef>
+#include <optional>
+#include <string>
+#include <string_view>
+#include <utility>
+
+namespace rcc {
+namespace {
+
+constexpr std::array StateNames{
+ std::string_view{"Success"},
+ std::string_view{"Unsupported"},
+ std::string_view{"MIRParseFailed"},
+ std::string_view{"LegalizationFailed"},
+ std::string_view{"SelectionFailed"},
+ std::string_view{"RegisterAllocationFailed"},
+ std::string_view{"CallLoweringFailed"},
+ std::string_view{"FrameLayoutFailed"},
+ std::string_view{"FinalizationFailed"},
+ std::string_view{"AssemblyLoweringFailed"},
+ std::string_view{"InternalError"},
+};
+
+static_assert(StateNames.size() == static_cast<std::size_t>(MachineCompilationState::Count));
+
+[[nodiscard]] bool
+isKnownDiagnosticSeverity(MachineCompilationDiagnosticSeverity Severity) noexcept {
+ return static_cast<std::size_t>(Severity) <
+ static_cast<std::size_t>(MachineCompilationDiagnosticSeverity::Count);
+}
+
+[[nodiscard]] bool isKnownErrorCode(ErrorCode Code) noexcept {
+ switch (Code) {
+ case ErrorCode::InvalidArgument:
+ case ErrorCode::ParseError:
+ case ErrorCode::VerificationError:
+ case ErrorCode::Unsupported:
+ case ErrorCode::IoError:
+ case ErrorCode::InternalError:
+ return true;
+ }
+ return false;
+}
+
+[[nodiscard]] bool isKnownPipelineStage(PipelineStage Stage) noexcept {
+ switch (Stage) {
+ case PipelineStage::MachineIRInput:
+ case PipelineStage::Legalized:
+ case PipelineStage::RegBankSelected:
+ case PipelineStage::Selected:
+ case PipelineStage::SSAEliminated:
+ case PipelineStage::TiedOpsRewritten:
+ case PipelineStage::RegisterAllocated:
+ case PipelineStage::FrameFinalized:
+ case PipelineStage::PostRAFinalized:
+ case PipelineStage::Emission:
+ return true;
+ }
+ return false;
+}
+
+[[nodiscard]] Expected<void> verifyOptionalText(const std::optional<std::string> &Text,
+ std::string_view FieldName) {
+ if (Text && Text->empty()) {
+ return makeUnexpected(ErrorCode::VerificationError, "machine compilation failure " +
+ std::string(FieldName) +
+ " cannot be empty");
+ }
+ return {};
+}
+
+} // namespace
+
+bool isKnownMachineCompilationState(MachineCompilationState State) noexcept {
+ return static_cast<std::size_t>(State) < StateNames.size();
+}
+
+std::string_view getMachineCompilationStateName(MachineCompilationState State) noexcept {
+ const auto Index = static_cast<std::size_t>(State);
+ if (Index >= StateNames.size())
+ return "Unknown";
+ return StateNames[Index];
+}
+
+Expected<void> MachineCompilationStatus::fail(MachineCompilationState NewState,
+ MachineCompilationFailure NewFailure) {
+ if (!succeeded()) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "machine compilation status already contains a failure");
+ }
+ if (!isKnownMachineCompilationState(NewState) || NewState == MachineCompilationState::Success) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "machine compilation failure requires a known failure state");
+ }
+
+ MachineCompilationStatus Candidate;
+ Candidate.State = NewState;
+ Candidate.Failure = std::move(NewFailure);
+ auto Verification = Candidate.verify();
+ if (!Verification)
+ return std::unexpected(Verification.error());
+
+ *this = std::move(Candidate);
+ return {};
+}
+
+Expected<void> MachineCompilationStatus::verify() const {
+ if (!isKnownMachineCompilationState(State)) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "machine compilation status has an unknown state");
+ }
+ if (State == MachineCompilationState::Success) {
+ if (Failure) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "successful machine compilation status contains failure details");
+ }
+ return {};
+ }
+ if (!Failure) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "failed machine compilation status lacks failure details");
+ }
+
+ if (!isKnownDiagnosticSeverity(Failure->Diagnostic.Severity)) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "machine compilation diagnostic has an unknown severity");
+ }
+ if (!isKnownErrorCode(Failure->Diagnostic.Code)) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "machine compilation diagnostic has an unknown error code");
+ }
+ if (Failure->Diagnostic.Message.empty()) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "machine compilation diagnostic message cannot be empty");
+ }
+ if (Failure->Diagnostic.Location && !Failure->Diagnostic.Location->isValid()) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "machine compilation diagnostic has an invalid source location");
+ }
+ if (Failure->FailureInstruction && !Failure->FailureInstruction->isValid()) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "machine compilation failure has an invalid instruction StableID");
+ }
+ if (Failure->FailureStage && !isKnownPipelineStage(*Failure->FailureStage)) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "machine compilation failure has an unknown pipeline stage");
+ }
+
+ for (const auto &[Text, Name] :
+ {std::pair{&Failure->FailurePass, std::string_view{"pass"}},
+ std::pair{&Failure->FailureRule, std::string_view{"rule or pattern"}},
+ std::pair{&Failure->Target, std::string_view{"target"}},
+ std::pair{&Failure->Subtarget, std::string_view{"subtarget"}}}) {
+ auto TextVerification = verifyOptionalText(*Text, Name);
+ if (!TextVerification)
+ return TextVerification;
+ }
+ return {};
+}
+
+Expected<MachineCompilationStatus>
+MachineCompilationStatus::clone(const MachineFunctionCloneContext &CloneContext) const {
+ auto Verification = verify();
+ if (!Verification)
+ return std::unexpected(Verification.error());
+ if (succeeded())
+ return MachineCompilationStatus{};
+ if (!Failure) {
+ return makeUnexpected(ErrorCode::InternalError,
+ "verified failed compilation status has no failure details");
+ }
+
+ MachineCompilationFailure ClonedFailure = *Failure;
+ if (ClonedFailure.FailureInstruction) {
+ auto Mapped = CloneContext.mapInstructionStableID(*ClonedFailure.FailureInstruction);
+ if (!Mapped)
+ return std::unexpected(Mapped.error());
+ ClonedFailure.FailureInstruction = *Mapped;
+ }
+
+ MachineCompilationStatus Result;
+ auto FailureResult = Result.fail(State, std::move(ClonedFailure));
+ if (!FailureResult)
+ return std::unexpected(FailureResult.error());
+ return Result;
+}
+
+} // namespace rcc
diff --git a/backend/lib/CodeGen/MachineFunction.cpp b/backend/lib/CodeGen/MachineFunction.cpp
index 64b6440..294158b 100644
--- a/backend/lib/CodeGen/MachineFunction.cpp
+++ b/backend/lib/CodeGen/MachineFunction.cpp
@@ -75,6 +75,17 @@ MachineFunction::~MachineFunction() {
}
}
+Expected<void> MachineFunction::setTargetState(TargetFunctionState State) {
+ if (TargetState.hasValue()) {
+ return makeUnexpected(ErrorCode::InvalidArgument, "machine function already has target state");
+ }
+ auto Verification = State.verify();
+ if (!Verification)
+ return std::unexpected(Verification.error());
+ TargetState = std::move(State);
+ return {};
+}
+
Expected<MachineBasicBlock *> MachineFunction::createBasicBlock() {
auto ID = BlockIDGenerator.allocate();
if (!ID)
diff --git a/backend/lib/CodeGen/MachineFunctionCloner.cpp b/backend/lib/CodeGen/MachineFunctionCloner.cpp
index 5703161..a8fe15e 100644
--- a/backend/lib/CodeGen/MachineFunctionCloner.cpp
+++ b/backend/lib/CodeGen/MachineFunctionCloner.cpp
@@ -5,6 +5,7 @@
#include "rcc/CodeGen/InstructionDescriptor.h"
#include "rcc/CodeGen/MachineBasicBlock.h"
#include "rcc/CodeGen/MachineCFGUpdater.h"
+#include "rcc/CodeGen/MachineCompilationStatus.h"
#include "rcc/CodeGen/MachineConstantPool.h"
#include "rcc/CodeGen/MachineFrameInfo.h"
#include "rcc/CodeGen/MachineFunction.h"
@@ -14,6 +15,7 @@
#include "rcc/CodeGen/MachineOperand.h"
#include "rcc/CodeGen/MachineRegisterInfo.h"
#include "rcc/CodeGen/MachineVerifier.h"
+#include "rcc/CodeGen/TargetFunctionState.h"
#include <cstddef>
#include <cstdint>
@@ -463,6 +465,86 @@ cloneMemOperand(const MachineMemOperand &SourceOperand, CloneState &State) {
} // namespace
+Expected<MachineBasicBlock *>
+MachineFunctionCloneContext::mapBlock(const MachineBasicBlock &SourceBlock) const {
+ const auto Position = Blocks.find(&SourceBlock);
+ if (Position == Blocks.end()) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "target function state references an unmapped basic block");
+ }
+ return Position->second;
+}
+
+Expected<MachineInstr *>
+MachineFunctionCloneContext::mapInstruction(const MachineInstr &SourceInstruction) const {
+ const auto Position = Instructions.find(&SourceInstruction);
+ if (Position == Instructions.end()) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "target function state references an unmapped instruction");
+ }
+ return Position->second;
+}
+
+Expected<MachineBlockStableID>
+MachineFunctionCloneContext::mapBlockStableID(MachineBlockStableID SourceID) const {
+ const auto Position = Result.BlockStableIDs.find(SourceID);
+ if (Position == Result.BlockStableIDs.end()) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "target function state references an unmapped block StableID");
+ }
+ return Position->second;
+}
+
+Expected<MachineInstrStableID>
+MachineFunctionCloneContext::mapInstructionStableID(MachineInstrStableID SourceID) const {
+ const auto Position = Result.InstructionStableIDs.find(SourceID);
+ if (Position == Result.InstructionStableIDs.end()) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "function state references an unmapped instruction StableID");
+ }
+ return Position->second;
+}
+
+Expected<Register> MachineFunctionCloneContext::mapRegister(Register SourceRegister) const {
+ if (!SourceRegister.isVirtual())
+ return SourceRegister;
+ const auto Position = Result.VirtualRegisters.find(SourceRegister);
+ if (Position == Result.VirtualRegisters.end()) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "target function state references an unmapped virtual register");
+ }
+ return Position->second;
+}
+
+Expected<FrameIndex> MachineFunctionCloneContext::mapFrameIndex(FrameIndex SourceIndex) const {
+ const auto Position = Result.FrameIndices.find(SourceIndex);
+ if (Position == Result.FrameIndices.end()) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "target function state references an unmapped frame index");
+ }
+ return Position->second;
+}
+
+Expected<ConstantPoolIndex>
+MachineFunctionCloneContext::mapConstantPoolIndex(ConstantPoolIndex SourceIndex) const {
+ const auto Position = Result.ConstantPoolIndices.find(SourceIndex);
+ if (Position == Result.ConstantPoolIndices.end()) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "target function state references an unmapped constant pool entry");
+ }
+ return Position->second;
+}
+
+Expected<JumpTableIndex>
+MachineFunctionCloneContext::mapJumpTableIndex(JumpTableIndex SourceIndex) const {
+ const auto Position = Result.JumpTableIndices.find(SourceIndex);
+ if (Position == Result.JumpTableIndices.end()) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "target function state references an unmapped jump table");
+ }
+ return Position->second;
+}
+
MachineFunctionCloneResult::MachineFunctionCloneResult() = default;
MachineFunctionCloneResult::MachineFunctionCloneResult(MachineFunctionCloneResult &&) noexcept =
default;
@@ -517,6 +599,21 @@ Expected<MachineFunctionCloneResult> cloneMachineFunction(const MachineFunction
if (!CFGResult)
return std::unexpected(CFGResult.error());
+ const MachineFunctionCloneContext CloneContext(Source, *Result.ClonedFunction, Result,
+ State.Blocks, State.Instructions);
+ if (Source.targetState().hasValue()) {
+ auto TargetState = Source.targetState().clone(CloneContext);
+ if (!TargetState)
+ return std::unexpected(TargetState.error());
+ auto InstallResult = Result.ClonedFunction->setTargetState(std::move(*TargetState));
+ if (!InstallResult)
+ return std::unexpected(InstallResult.error());
+ }
+
+ auto CompilationStatus = Source.compilationStatus().clone(CloneContext);
+ if (!CompilationStatus)
+ return std::unexpected(CompilationStatus.error());
+ Result.ClonedFunction->compilationStatus() = std::move(*CompilationStatus);
Result.ClonedFunction->properties() = Source.properties();
auto DestinationVerification = MachineVerifier::verify(*Result.ClonedFunction, Instructions);
if (!DestinationVerification)
diff --git a/backend/lib/CodeGen/MachineVerifier.cpp b/backend/lib/CodeGen/MachineVerifier.cpp
index 50c1001..0c05a07 100644
--- a/backend/lib/CodeGen/MachineVerifier.cpp
+++ b/backend/lib/CodeGen/MachineVerifier.cpp
@@ -34,6 +34,11 @@ public:
if (Function.name().empty() || !Function.stableID().isValid())
return fail("function identity is invalid");
+ if (Function.targetState().hasValue()) {
+ auto TargetStateResult = Function.targetState().verify();
+ if (!TargetStateResult)
+ return failWith("target function state", TargetStateResult.error());
+ }
auto CatalogResult = catalogArenaObjects();
if (!CatalogResult)
return CatalogResult;
@@ -46,6 +51,9 @@ public:
auto LayoutResult = verifyBlockLayout();
if (!LayoutResult)
return LayoutResult;
+ auto CompilationStatusResult = verifyCompilationStatus();
+ if (!CompilationStatusResult)
+ return CompilationStatusResult;
auto JumpTableResult = Function.jumpTableInfo().verify();
if (!JumpTableResult)
return failWith("jump table info", JumpTableResult.error());
@@ -319,6 +327,20 @@ private:
return {};
}
+ [[nodiscard]] Expected<void> verifyCompilationStatus() const {
+ const MachineCompilationStatus &Status = Function.compilationStatus();
+ auto Verification = Status.verify();
+ if (!Verification)
+ return failWith("compilation status", Verification.error());
+
+ const MachineCompilationFailure *Failure = Status.failure();
+ if (Failure != nullptr && Failure->FailureInstruction &&
+ !LinkedInstructionIDs.contains(*Failure->FailureInstruction)) {
+ return fail("compilation failure references a detached or unknown instruction");
+ }
+ return {};
+ }
+
[[nodiscard]] Expected<void> verifyLiveInstruction(const MachineInstr &Instruction) {
auto LocalResult = verifyInstructionLocalState(Instruction);
if (!LocalResult)
diff --git a/backend/lib/CodeGen/TargetFunctionState.cpp b/backend/lib/CodeGen/TargetFunctionState.cpp
new file mode 100644
index 0000000..56ff363
--- /dev/null
+++ b/backend/lib/CodeGen/TargetFunctionState.cpp
@@ -0,0 +1,82 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "rcc/CodeGen/TargetFunctionState.h"
+
+#include <cstdint>
+#include <memory>
+#include <optional>
+#include <string>
+#include <utility>
+
+namespace rcc {
+
+TargetFunctionState::TargetFunctionState(TargetFunctionState &&) noexcept = default;
+TargetFunctionState &TargetFunctionState::operator=(TargetFunctionState &&) noexcept = default;
+TargetFunctionState::~TargetFunctionState() = default;
+
+std::optional<TargetFunctionStateTypeID> TargetFunctionState::typeID() const noexcept {
+ if (Storage == nullptr)
+ return std::nullopt;
+ return Storage->typeID();
+}
+
+std::optional<std::uint32_t> TargetFunctionState::schemaVersion() const noexcept {
+ if (Storage == nullptr)
+ return std::nullopt;
+ return Storage->schemaVersion();
+}
+
+Expected<void> TargetFunctionState::verify() const {
+ if (Storage == nullptr)
+ return makeUnexpected(ErrorCode::VerificationError, "target function state is empty");
+ if (Storage->typeID().value() == 0 || Storage->schemaVersion() == 0) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "target function state identity or schema version is invalid");
+ }
+ return Storage->verify();
+}
+
+Expected<TargetFunctionState>
+TargetFunctionState::clone(const MachineFunctionCloneContext &CloneContext) const {
+ auto Verification = verify();
+ if (!Verification)
+ return std::unexpected(Verification.error());
+ auto ClonedStorage = Storage->cloneStorage(CloneContext);
+ if (!ClonedStorage)
+ return std::unexpected(ClonedStorage.error());
+ TargetFunctionState Result(std::move(*ClonedStorage));
+ auto CloneVerification = Result.verify();
+ if (!CloneVerification)
+ return std::unexpected(CloneVerification.error());
+ return Result;
+}
+
+Expected<std::string> TargetFunctionState::print() const {
+ auto Verification = verify();
+ if (!Verification)
+ return std::unexpected(Verification.error());
+ return Storage->print();
+}
+
+Expected<EncodedTargetFunctionState> TargetFunctionState::encode() const {
+ auto Verification = verify();
+ if (!Verification)
+ return std::unexpected(Verification.error());
+ auto Payload = Storage->encodePayload();
+ if (!Payload)
+ return std::unexpected(Payload.error());
+ return EncodedTargetFunctionState{Storage->typeID(), Storage->schemaVersion(),
+ std::move(*Payload)};
+}
+
+bool TargetFunctionState::equals(const TargetFunctionState &Other) const noexcept {
+ if (Storage == nullptr || Other.Storage == nullptr)
+ return Storage == nullptr && Other.Storage == nullptr;
+ if (Storage->typeID() != Other.Storage->typeID() ||
+ Storage->localTypeToken() != Other.Storage->localTypeToken()) {
+ return false;
+ }
+ return Storage->equals(*Other.Storage);
+}
+
+} // namespace rcc