diff options
Diffstat (limited to 'backend/lib/CodeGen')
| -rw-r--r-- | backend/lib/CodeGen/CMakeLists.txt | 8 | ||||
| -rw-r--r-- | backend/lib/CodeGen/MachineCompilationStatus.cpp | 195 | ||||
| -rw-r--r-- | backend/lib/CodeGen/MachineFunction.cpp | 11 | ||||
| -rw-r--r-- | backend/lib/CodeGen/MachineFunctionCloner.cpp | 97 | ||||
| -rw-r--r-- | backend/lib/CodeGen/MachineVerifier.cpp | 22 | ||||
| -rw-r--r-- | backend/lib/CodeGen/TargetFunctionState.cpp | 82 |
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 |
