diff options
| author | Richard-Qin-X <richard.qin.001@gmail.com> | 2026-08-01 14:19:20 +0800 |
|---|---|---|
| committer | Richard-Qin-X <richard.qin.001@gmail.com> | 2026-08-01 14:19:20 +0800 |
| commit | a69d14b8ebc028dddc689658b08d42d3e83f5e38 (patch) | |
| tree | 5bdbb9dbe88de07eeba5caba97a2e54e6b05ad38 /backend/lib/CodeGen | |
| parent | c9634fa9f529afbdf622a9cd96741bd09d14ef86 (diff) | |
| download | rcc-project-a69d14b8ebc028dddc689658b08d42d3e83f5e38.tar.gz rcc-project-a69d14b8ebc028dddc689658b08d42d3e83f5e38.tar.xz | |
[CodeGen] Add constant and jump table state
Add stable, tombstone-preserving MachineConstantPool and MachineJumpTableInfo ownership. Define the versioned TargetConstant contract and integrate all table references with builders, verification, and deterministic deep cloning.
Diffstat (limited to 'backend/lib/CodeGen')
| -rw-r--r-- | backend/lib/CodeGen/CMakeLists.txt | 12 | ||||
| -rw-r--r-- | backend/lib/CodeGen/MachineConstantPool.cpp | 329 | ||||
| -rw-r--r-- | backend/lib/CodeGen/MachineFunction.cpp | 3 | ||||
| -rw-r--r-- | backend/lib/CodeGen/MachineFunctionCloner.cpp | 103 | ||||
| -rw-r--r-- | backend/lib/CodeGen/MachineIRBuilder.cpp | 38 | ||||
| -rw-r--r-- | backend/lib/CodeGen/MachineJumpTableInfo.cpp | 142 | ||||
| -rw-r--r-- | backend/lib/CodeGen/MachineVerifier.cpp | 41 | ||||
| -rw-r--r-- | backend/lib/CodeGen/TargetConstant.cpp | 146 |
8 files changed, 798 insertions, 16 deletions
diff --git a/backend/lib/CodeGen/CMakeLists.txt b/backend/lib/CodeGen/CMakeLists.txt index 283727c..3dd17a4 100644 --- a/backend/lib/CodeGen/CMakeLists.txt +++ b/backend/lib/CodeGen/CMakeLists.txt @@ -6,12 +6,14 @@ set( LowLevelType.cpp MachineBasicBlock.cpp MachineCFGUpdater.cpp + MachineConstantPool.cpp MachineFrameInfo.cpp MachineFunction.cpp MachineFunctionCloner.cpp MachineIRTypes.cpp MachineIRBuilder.cpp MachineInstr.cpp + MachineJumpTableInfo.cpp MachineMemOperand.cpp MachineOperand.cpp MachineRegisterInfo.cpp @@ -19,6 +21,7 @@ set( MachineFunctionProperties.cpp Register.cpp RegisterMask.cpp + TargetConstant.cpp ) set( RCCBackendCodeGenHeaders @@ -26,12 +29,14 @@ set( "${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/LowLevelType.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" "${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/MachineFrameInfo.h" "${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/MachineFunction.h" "${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/MachineFunctionCloner.h" "${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/MachineIRTypes.h" "${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/MachineIRBuilder.h" "${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/MachineOperand.h" "${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/MachineRegisterInfo.h" @@ -40,6 +45,7 @@ set( "${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/Register.h" "${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" ) add_library(RCCBackendCodeGen ${RCCBackendCodeGenSources}) @@ -75,12 +81,14 @@ rcc_backend_register_format_files( "${CMAKE_CURRENT_SOURCE_DIR}/LowLevelType.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/MachineBasicBlock.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/MachineCFGUpdater.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/MachineConstantPool.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/MachineFrameInfo.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/MachineFunction.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/MachineFunctionCloner.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/MachineIRTypes.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/MachineIRBuilder.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/MachineInstr.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/MachineJumpTableInfo.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/MachineMemOperand.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/MachineOperand.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/MachineRegisterInfo.cpp" @@ -88,6 +96,7 @@ rcc_backend_register_format_files( "${CMAKE_CURRENT_SOURCE_DIR}/MachineFunctionProperties.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/Register.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/RegisterMask.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/TargetConstant.cpp" ${RCCBackendCodeGenHeaders} ) rcc_backend_register_tidy_files( @@ -95,12 +104,14 @@ rcc_backend_register_tidy_files( "${CMAKE_CURRENT_SOURCE_DIR}/LowLevelType.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/MachineBasicBlock.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/MachineCFGUpdater.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/MachineConstantPool.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/MachineFrameInfo.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/MachineFunction.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/MachineFunctionCloner.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/MachineIRTypes.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/MachineIRBuilder.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/MachineInstr.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/MachineJumpTableInfo.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/MachineMemOperand.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/MachineOperand.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/MachineRegisterInfo.cpp" @@ -108,4 +119,5 @@ rcc_backend_register_tidy_files( "${CMAKE_CURRENT_SOURCE_DIR}/MachineFunctionProperties.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/Register.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/RegisterMask.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/TargetConstant.cpp" ) diff --git a/backend/lib/CodeGen/MachineConstantPool.cpp b/backend/lib/CodeGen/MachineConstantPool.cpp new file mode 100644 index 0000000..43f2620 --- /dev/null +++ b/backend/lib/CodeGen/MachineConstantPool.cpp @@ -0,0 +1,329 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "rcc/CodeGen/MachineConstantPool.h" + +#include <cstddef> +#include <cstdint> +#include <limits> +#include <optional> +#include <utility> + +namespace rcc { +namespace { + +[[nodiscard]] const TargetConstant * +getTargetConstant(const MachineConstantPoolValue &Value) noexcept { + if (const auto *Target = std::get_if<TargetConstant>(&Value)) + return Target; + if (const auto *Expression = std::get_if<TargetExpressionConstant>(&Value)) + return &Expression->Expression; + return nullptr; +} + +[[nodiscard]] Expected<void> verifyTargetConstant(const MachineConstantPoolEntryDesc &Description, + const TargetConstant &Constant) { + auto Verification = Constant.verify(); + if (!Verification) + return std::unexpected(Verification.error()); + + auto RequiredAlignment = Constant.requiredAlignment(); + if (!RequiredAlignment) + return std::unexpected(RequiredAlignment.error()); + if (Description.Align.bytes() < RequiredAlignment->bytes()) { + return makeUnexpected(ErrorCode::VerificationError, + "constant pool alignment is weaker than target constant requirement"); + } + + auto Emission = Constant.emit(); + if (!Emission) + return std::unexpected(Emission.error()); + if (Emission->Bytes.size() != Description.Size) { + return makeUnexpected(ErrorCode::VerificationError, + "target constant emitted size does not match its pool entry size"); + } + + auto RelocationKind = Constant.relocationKind(); + if (!RelocationKind) + return std::unexpected(RelocationKind.error()); + const bool HasRelocation = RelocationKind->has_value() || !Emission->Fixups.empty(); + if (HasRelocation == (Description.Relocation == ConstantPoolRelocationMode::None)) { + return makeUnexpected(ErrorCode::VerificationError, + "target constant relocation contract disagrees with its pool entry"); + } + return {}; +} + +} // namespace + +ConstantPoolEntryKind MachineConstantPoolEntryDesc::kind() const noexcept { + switch (Value.index()) { + case 0: + return ConstantPoolEntryKind::Generic; + case 1: + return ConstantPoolEntryKind::Target; + case 2: + return ConstantPoolEntryKind::SymbolDifference; + case 3: + return ConstantPoolEntryKind::BlockAddress; + case 4: + return ConstantPoolEntryKind::TargetExpression; + default: + return ConstantPoolEntryKind::Generic; + } +} + +Expected<void> MachineConstantPoolEntryDesc::verify() const { + if (Size == 0) + return makeUnexpected(ErrorCode::VerificationError, "constant pool entry size must be nonzero"); + if (Placement.Section >= ConstantPoolSectionKind::Count) { + return makeUnexpected(ErrorCode::VerificationError, "constant pool section kind is invalid"); + } + if (Placement.MaximumDistance && *Placement.MaximumDistance == 0) { + return makeUnexpected(ErrorCode::VerificationError, + "constant pool maximum distance must be nonzero"); + } + if ((Placement.Section == ConstantPoolSectionKind::Target) != (Placement.TargetFlags != 0)) { + return makeUnexpected(ErrorCode::VerificationError, + "target constant pool placement requires exactly one target identity"); + } + if (Placement.Section == ConstantPoolSectionKind::MergeableReadOnly && + Merge != ConstantPoolMergeKind::Exact) { + return makeUnexpected(ErrorCode::VerificationError, + "mergeable constant section requires exact entry merging"); + } + + switch (kind()) { + case ConstantPoolEntryKind::Generic: { + const auto *Generic = std::get_if<GenericConstantData>(&Value); + if (Generic == nullptr || Generic->TypeIdentity.empty() || Generic->Bytes.size() != Size) { + return makeUnexpected(ErrorCode::VerificationError, + "generic constant pool entry has invalid type or byte size"); + } + if (Relocation != ConstantPoolRelocationMode::None) { + return makeUnexpected(ErrorCode::VerificationError, + "raw generic constant pool bytes cannot carry relocations"); + } + return {}; + } + case ConstantPoolEntryKind::Target: + case ConstantPoolEntryKind::TargetExpression: { + const TargetConstant *Target = getTargetConstant(Value); + if (Target == nullptr) + return makeUnexpected(ErrorCode::InternalError, "target constant variant is inconsistent"); + return verifyTargetConstant(*this, *Target); + } + case ConstantPoolEntryKind::SymbolDifference: { + const auto *Difference = std::get_if<SymbolDifferenceConstant>(&Value); + if (Difference == nullptr || Difference->Minuend.empty() || Difference->Subtrahend.empty()) { + return makeUnexpected(ErrorCode::VerificationError, + "symbol-difference constant requires two symbols"); + } + if (Relocation == ConstantPoolRelocationMode::None) { + return makeUnexpected(ErrorCode::VerificationError, + "symbol-difference constant requires a relocation mode"); + } + return {}; + } + case ConstantPoolEntryKind::BlockAddress: { + const auto *Address = std::get_if<BlockAddressConstant>(&Value); + if (Address == nullptr || Address->FunctionName.empty() || Address->BlockName.empty()) { + return makeUnexpected(ErrorCode::VerificationError, + "block-address constant requires a function and block name"); + } + if (Relocation == ConstantPoolRelocationMode::None) { + return makeUnexpected(ErrorCode::VerificationError, + "block-address constant requires a relocation mode"); + } + return {}; + } + } + return makeUnexpected(ErrorCode::InternalError, "constant pool entry kind is unhandled"); +} + +Expected<MachineConstantPoolEntryDesc> MachineConstantPoolEntryDesc::clone() const { + MachineConstantPoolEntryDesc Result; + Result.Size = Size; + Result.Align = Align; + Result.Merge = Merge; + Result.Relocation = Relocation; + Result.Placement = Placement; + + if (const auto *Generic = std::get_if<GenericConstantData>(&Value)) { + Result.Value = *Generic; + } else if (const auto *Target = std::get_if<TargetConstant>(&Value)) { + auto Cloned = Target->clone(); + if (!Cloned) + return std::unexpected(Cloned.error()); + Result.Value = std::move(*Cloned); + } else if (const auto *Difference = std::get_if<SymbolDifferenceConstant>(&Value)) { + Result.Value = *Difference; + } else if (const auto *Address = std::get_if<BlockAddressConstant>(&Value)) { + Result.Value = *Address; + } else if (const auto *Expression = std::get_if<TargetExpressionConstant>(&Value)) { + auto Cloned = Expression->Expression.clone(); + if (!Cloned) + return std::unexpected(Cloned.error()); + Result.Value = TargetExpressionConstant{std::move(*Cloned)}; + } else { + return makeUnexpected(ErrorCode::InternalError, "constant pool value is unhandled"); + } + return Result; +} + +bool MachineConstantPoolEntryDesc::semanticallyEquals( + const MachineConstantPoolEntryDesc &Other) const noexcept { + if (Size != Other.Size || Align != Other.Align || Merge != Other.Merge || + Relocation != Other.Relocation || Placement != Other.Placement || kind() != Other.kind()) { + return false; + } + + if (const auto *Generic = std::get_if<GenericConstantData>(&Value)) { + const auto *OtherGeneric = std::get_if<GenericConstantData>(&Other.Value); + return OtherGeneric != nullptr && *Generic == *OtherGeneric; + } + if (const auto *Target = std::get_if<TargetConstant>(&Value)) { + const auto *OtherTarget = std::get_if<TargetConstant>(&Other.Value); + return OtherTarget != nullptr && Target->equals(*OtherTarget); + } + if (const auto *Difference = std::get_if<SymbolDifferenceConstant>(&Value)) { + const auto *OtherDifference = std::get_if<SymbolDifferenceConstant>(&Other.Value); + return OtherDifference != nullptr && *Difference == *OtherDifference; + } + if (const auto *Address = std::get_if<BlockAddressConstant>(&Value)) { + const auto *OtherAddress = std::get_if<BlockAddressConstant>(&Other.Value); + return OtherAddress != nullptr && *Address == *OtherAddress; + } + if (const auto *Expression = std::get_if<TargetExpressionConstant>(&Value)) { + const auto *OtherExpression = std::get_if<TargetExpressionConstant>(&Other.Value); + return OtherExpression != nullptr && Expression->Expression.equals(OtherExpression->Expression); + } + return false; +} + +Expected<MachineConstantPoolEntryDesc> MachineConstantPoolEntry::cloneDescription() const { + const MachineConstantPoolEntryDesc *Desc = description(); + if (Desc == nullptr) { + return makeUnexpected(ErrorCode::InvalidArgument, + "constant pool tombstone has no description to clone"); + } + return Desc->clone(); +} + +const MachineConstantPoolEntry * +MachineConstantPool::findEquivalent(const MachineConstantPoolEntryDesc &Description, + std::optional<ConstantPoolIndex> Excluded) const noexcept { + for (const MachineConstantPoolEntry &Entry : Entries) { + if (Entry.isTombstone() || (Excluded && Entry.index() == *Excluded)) + continue; + const MachineConstantPoolEntryDesc *Existing = Entry.description(); + if (Existing != nullptr && Existing->semanticallyEquals(Description)) + return &Entry; + } + return nullptr; +} + +Expected<ConstantPoolIndex> +MachineConstantPool::addEntry(MachineConstantPoolEntryDesc Description) { + if (Frozen) + return makeUnexpected(ErrorCode::InvalidArgument, "frozen constant pool cannot add entries"); + auto Verification = Description.verify(); + if (!Verification) + return std::unexpected(Verification.error()); + if (Description.Merge == ConstantPoolMergeKind::Exact) { + if (const MachineConstantPoolEntry *Existing = findEquivalent(Description)) + return Existing->index(); + } + if (Entries.size() > std::numeric_limits<std::uint32_t>::max()) { + return makeUnexpected(ErrorCode::Unsupported, "constant pool index space is exhausted"); + } + + const ConstantPoolIndex Index = + ConstantPoolIndex::fromValue(static_cast<std::uint32_t>(Entries.size())); + Entries.push_back(MachineConstantPoolEntry(Index, std::move(Description))); + return Index; +} + +Expected<MachineConstantPoolEntry *> MachineConstantPool::mutableEntry(ConstantPoolIndex Index) { + if (Frozen) + return makeUnexpected(ErrorCode::InvalidArgument, "frozen constant pool cannot modify entries"); + if (Index.value() >= Entries.size()) + return makeUnexpected(ErrorCode::InvalidArgument, "constant pool index is out of range"); + MachineConstantPoolEntry &Entry = Entries[Index.value()]; + if (Entry.isTombstone()) + return makeUnexpected(ErrorCode::InvalidArgument, "constant pool index is a tombstone"); + return &Entry; +} + +Expected<void> MachineConstantPool::updateEntry(ConstantPoolIndex Index, + MachineConstantPoolEntryDesc Description) { + auto Verification = Description.verify(); + if (!Verification) + return std::unexpected(Verification.error()); + auto Entry = mutableEntry(Index); + if (!Entry) + return std::unexpected(Entry.error()); + if (Description.Merge == ConstantPoolMergeKind::Exact && + findEquivalent(Description, Index) != nullptr) { + return makeUnexpected(ErrorCode::InvalidArgument, + "constant pool update would create duplicate mergeable entries"); + } + (*Entry)->Description.emplace<MachineConstantPoolEntryDesc>(std::move(Description)); + return {}; +} + +Expected<void> MachineConstantPool::eraseEntry(ConstantPoolIndex Index) { + auto Entry = mutableEntry(Index); + if (!Entry) + return std::unexpected(Entry.error()); + (*Entry)->Description.emplace<std::monostate>(); + return {}; +} + +const MachineConstantPoolEntry *MachineConstantPool::entry(ConstantPoolIndex Index) const noexcept { + if (Index.value() >= Entries.size() || Entries[Index.value()].isTombstone()) + return nullptr; + return &Entries[Index.value()]; +} + +std::size_t MachineConstantPool::liveEntryCount() const noexcept { + std::size_t Count = 0; + for (const MachineConstantPoolEntry &Entry : Entries) { + if (!Entry.isTombstone()) + ++Count; + } + return Count; +} + +Expected<void> MachineConstantPool::freeze() { + if (Frozen) + return makeUnexpected(ErrorCode::InvalidArgument, "constant pool is already frozen"); + auto Verification = verify(); + if (!Verification) + return std::unexpected(Verification.error()); + Frozen = true; + return {}; +} + +Expected<void> MachineConstantPool::verify() const { + for (std::size_t Position = 0; Position < Entries.size(); ++Position) { + const MachineConstantPoolEntry &Entry = Entries[Position]; + if (Entry.index() != ConstantPoolIndex::fromValue(static_cast<std::uint32_t>(Position))) { + return makeUnexpected(ErrorCode::VerificationError, + "constant pool index does not match its stable table position"); + } + const MachineConstantPoolEntryDesc *Description = Entry.description(); + if (Description == nullptr) + continue; + auto Verification = Description->verify(); + if (!Verification) + return std::unexpected(Verification.error()); + if (Description->Merge == ConstantPoolMergeKind::Exact && + findEquivalent(*Description, Entry.index()) != nullptr) { + return makeUnexpected(ErrorCode::VerificationError, + "constant pool contains duplicate mergeable entries"); + } + } + return {}; +} + +} // namespace rcc diff --git a/backend/lib/CodeGen/MachineFunction.cpp b/backend/lib/CodeGen/MachineFunction.cpp index fc1508c..64b6440 100644 --- a/backend/lib/CodeGen/MachineFunction.cpp +++ b/backend/lib/CodeGen/MachineFunction.cpp @@ -64,7 +64,8 @@ MachineFunction::create(std::string Name, MachineFunctionStableID StableID) { } MachineFunction::MachineFunction(std::string Name, MachineFunctionStableID StableID) - : Name(std::move(Name)), StableID(StableID), Arena(std::make_unique<MachineArena>()) {} + : Name(std::move(Name)), StableID(StableID), JumpTables(*this), + Arena(std::make_unique<MachineArena>()) {} MachineFunction::~MachineFunction() { for (MachineBasicBlock *Block = FirstBlock; Block != nullptr; Block = Block->NextBlock) { diff --git a/backend/lib/CodeGen/MachineFunctionCloner.cpp b/backend/lib/CodeGen/MachineFunctionCloner.cpp index 46fed6c..5703161 100644 --- a/backend/lib/CodeGen/MachineFunctionCloner.cpp +++ b/backend/lib/CodeGen/MachineFunctionCloner.cpp @@ -5,9 +5,11 @@ #include "rcc/CodeGen/InstructionDescriptor.h" #include "rcc/CodeGen/MachineBasicBlock.h" #include "rcc/CodeGen/MachineCFGUpdater.h" +#include "rcc/CodeGen/MachineConstantPool.h" #include "rcc/CodeGen/MachineFrameInfo.h" #include "rcc/CodeGen/MachineFunction.h" #include "rcc/CodeGen/MachineInstr.h" +#include "rcc/CodeGen/MachineJumpTableInfo.h" #include "rcc/CodeGen/MachineMemOperand.h" #include "rcc/CodeGen/MachineOperand.h" #include "rcc/CodeGen/MachineRegisterInfo.h" @@ -100,16 +102,25 @@ struct CloneState final { return makeUnexpected(ErrorCode::VerificationError, "source constant-pool operand is invalid"); } - State.Result.ConstantPoolIndices.emplace(Constant->Index, Constant->Index); - return SourceOperand.cloneDetached(); + const auto Position = State.Result.ConstantPoolIndices.find(Constant->Index); + if (Position == State.Result.ConstantPoolIndices.end()) { + return makeUnexpected(ErrorCode::VerificationError, + "source operand references an unmapped constant pool entry"); + } + return MachineOperand::constantPool(Position->second, Constant->Offset, + SourceOperand.targetFlags()); } case MachineOperand::Kind::JumpTableIndex: { const auto *JumpTable = SourceOperand.getIf<JumpTableOperand>(); if (JumpTable == nullptr) { return makeUnexpected(ErrorCode::VerificationError, "source jump-table operand is invalid"); } - State.Result.JumpTableIndices.emplace(JumpTable->Index, JumpTable->Index); - return SourceOperand.cloneDetached(); + const auto Position = State.Result.JumpTableIndices.find(JumpTable->Index); + if (Position == State.Result.JumpTableIndices.end()) { + return makeUnexpected(ErrorCode::VerificationError, + "source operand references an unmapped jump table"); + } + return MachineOperand::jumpTable(Position->second, SourceOperand.targetFlags()); } case MachineOperand::Kind::Immediate: case MachineOperand::Kind::FloatingImmediate: @@ -126,14 +137,6 @@ struct CloneState final { return makeUnexpected(ErrorCode::InternalError, "source operand kind is not handled"); } -void recordMemoryIndexMappings(const MachineMemOperandDesc &Description, - MachineFunctionCloneResult &Result) { - if (const auto Constant = Description.Base.constantPoolIndex()) - Result.ConstantPoolIndices.emplace(*Constant, *Constant); - if (const auto JumpTable = Description.Base.jumpTableIndex()) - Result.JumpTableIndices.emplace(*JumpTable, *JumpTable); -} - [[nodiscard]] Expected<const MachineMemOperand *> cloneMemOperand(const MachineMemOperand &SourceOperand, CloneState &State) { const auto Existing = State.MemOperands.find(&SourceOperand); @@ -141,7 +144,6 @@ cloneMemOperand(const MachineMemOperand &SourceOperand, CloneState &State) { return Existing->second; MachineMemOperandDesc Description = SourceOperand.description(); - recordMemoryIndexMappings(Description, State.Result); if (const auto Frame = Description.Base.frameIndex()) { const auto Position = State.Result.FrameIndices.find(*Frame); if (Position == State.Result.FrameIndices.end()) { @@ -150,6 +152,22 @@ cloneMemOperand(const MachineMemOperand &SourceOperand, CloneState &State) { } Description.Base = MemoryBase::frameIndex(Position->second); } + if (const auto Constant = Description.Base.constantPoolIndex()) { + const auto Position = State.Result.ConstantPoolIndices.find(*Constant); + if (Position == State.Result.ConstantPoolIndices.end()) { + return makeUnexpected(ErrorCode::VerificationError, + "source MMO references an unmapped constant pool entry"); + } + Description.Base = MemoryBase::constantPool(Position->second); + } + if (const auto JumpTable = Description.Base.jumpTableIndex()) { + const auto Position = State.Result.JumpTableIndices.find(*JumpTable); + if (Position == State.Result.JumpTableIndices.end()) { + return makeUnexpected(ErrorCode::VerificationError, + "source MMO references an unmapped jump table"); + } + Description.Base = MemoryBase::jumpTable(Position->second); + } auto DestinationOperand = State.Destination.createMemOperand(std::move(Description)); if (!DestinationOperand) return std::unexpected(DestinationOperand.error()); @@ -235,6 +253,31 @@ cloneMemOperand(const MachineMemOperand &SourceOperand, CloneState &State) { return {}; } +[[nodiscard]] Expected<void> cloneConstantPool(CloneState &State) { + for (const MachineConstantPoolEntry &SourceEntry : State.Source.constantPool().entries()) { + if (SourceEntry.isTombstone()) + continue; + auto Description = SourceEntry.cloneDescription(); + if (!Description) + return std::unexpected(Description.error()); + if (auto *Address = std::get_if<BlockAddressConstant>(&Description->Value); + Address != nullptr && Address->FunctionName == State.Source.name()) { + Address->FunctionName = std::string(State.Destination.name()); + } + + auto DestinationIndex = State.Destination.constantPool().addEntry(std::move(*Description)); + if (!DestinationIndex) + return std::unexpected(DestinationIndex.error()); + State.Result.ConstantPoolIndices.emplace(SourceEntry.index(), *DestinationIndex); + } + if (State.Source.constantPool().isFrozen()) { + auto FreezeResult = State.Destination.constantPool().freeze(); + if (!FreezeResult) + return std::unexpected(FreezeResult.error()); + } + return {}; +} + [[nodiscard]] Expected<void> cloneBlocks(CloneState &State) { for (const MachineBasicBlock *SourceBlock = State.Source.firstBlock(); SourceBlock != nullptr; SourceBlock = SourceBlock->next()) { @@ -256,6 +299,34 @@ cloneMemOperand(const MachineMemOperand &SourceOperand, CloneState &State) { return {}; } +[[nodiscard]] Expected<void> cloneJumpTables(CloneState &State) { + for (const MachineJumpTableEntry &SourceEntry : State.Source.jumpTableInfo().tables()) { + const MachineJumpTableDesc *SourceDescription = SourceEntry.description(); + if (SourceDescription == nullptr) + continue; + MachineJumpTableDesc DestinationDescription = *SourceDescription; + for (MachineBasicBlock *&Target : DestinationDescription.Targets) { + const auto Position = State.Blocks.find(Target); + if (Position == State.Blocks.end()) { + return makeUnexpected(ErrorCode::VerificationError, + "source jump table references an unmapped block"); + } + Target = Position->second; + } + auto DestinationIndex = + State.Destination.jumpTableInfo().createTable(std::move(DestinationDescription)); + if (!DestinationIndex) + return std::unexpected(DestinationIndex.error()); + State.Result.JumpTableIndices.emplace(SourceEntry.index(), *DestinationIndex); + } + if (State.Source.jumpTableInfo().isFrozen()) { + auto FreezeResult = State.Destination.jumpTableInfo().freeze(); + if (!FreezeResult) + return std::unexpected(FreezeResult.error()); + } + return {}; +} + [[nodiscard]] Expected<void> cloneInstructions(CloneState &State) { for (const MachineBasicBlock *SourceBlock = State.Source.firstBlock(); SourceBlock != nullptr; SourceBlock = SourceBlock->next()) { @@ -427,9 +498,15 @@ Expected<MachineFunctionCloneResult> cloneMachineFunction(const MachineFunction auto FrameResult = cloneFrameObjects(State); if (!FrameResult) return std::unexpected(FrameResult.error()); + auto ConstantPoolResult = cloneConstantPool(State); + if (!ConstantPoolResult) + return std::unexpected(ConstantPoolResult.error()); auto BlockResult = cloneBlocks(State); if (!BlockResult) return std::unexpected(BlockResult.error()); + auto JumpTableResult = cloneJumpTables(State); + if (!JumpTableResult) + return std::unexpected(JumpTableResult.error()); auto InstructionResult = cloneInstructions(State); if (!InstructionResult) return std::unexpected(InstructionResult.error()); diff --git a/backend/lib/CodeGen/MachineIRBuilder.cpp b/backend/lib/CodeGen/MachineIRBuilder.cpp index 185b7df..74ca55b 100644 --- a/backend/lib/CodeGen/MachineIRBuilder.cpp +++ b/backend/lib/CodeGen/MachineIRBuilder.cpp @@ -342,6 +342,21 @@ Expected<void> MachineInstrBuilder::validateFrameReference(FrameIndex Index) con return {}; } +Expected<void> MachineInstrBuilder::validateConstantPoolReference(ConstantPoolIndex Index) const { + if (Function->constantPool().entry(Index) == nullptr) { + return makeUnexpected(ErrorCode::InvalidArgument, + "builder references an unknown constant pool entry"); + } + return {}; +} + +Expected<void> MachineInstrBuilder::validateJumpTableReference(JumpTableIndex Index) const { + if (Function->jumpTableInfo().table(Index) == nullptr) { + return makeUnexpected(ErrorCode::InvalidArgument, "builder references an unknown jump table"); + } + return {}; +} + Expected<void> MachineInstrBuilder::validateOperandOwnership(const MachineOperand &Operand) const { if (Operand.parent() != nullptr) { return makeUnexpected(ErrorCode::InvalidArgument, @@ -364,6 +379,16 @@ Expected<void> MachineInstrBuilder::validateOperandOwnership(const MachineOperan if (!FrameResult) return FrameResult; } + if (const auto *Constant = Operand.getIf<ConstantPoolOperand>(); Constant != nullptr) { + auto ConstantResult = validateConstantPoolReference(Constant->Index); + if (!ConstantResult) + return ConstantResult; + } + if (const auto *JumpTable = Operand.getIf<JumpTableOperand>(); JumpTable != nullptr) { + auto JumpTableResult = validateJumpTableReference(JumpTable->Index); + if (!JumpTableResult) + return JumpTableResult; + } return {}; } @@ -405,11 +430,22 @@ Expected<MachineInstr *> MachineInstrBuilder::build() { return std::unexpected(Ownership.error()); } for (const MachineMemOperand *MemOperand : MemOperands) { - if (const auto Frame = MemOperand->description().Base.frameIndex()) { + const MemoryBase &Base = MemOperand->description().Base; + if (const auto Frame = Base.frameIndex()) { auto FrameResult = validateFrameReference(*Frame); if (!FrameResult) return std::unexpected(FrameResult.error()); } + if (const auto Constant = Base.constantPoolIndex()) { + auto ConstantResult = validateConstantPoolReference(*Constant); + if (!ConstantResult) + return std::unexpected(ConstantResult.error()); + } + if (const auto JumpTable = Base.jumpTableIndex()) { + auto JumpTableResult = validateJumpTableReference(*JumpTable); + if (!JumpTableResult) + return std::unexpected(JumpTableResult.error()); + } } auto ExplicitValidation = Instructions->validateExplicitOperands( diff --git a/backend/lib/CodeGen/MachineJumpTableInfo.cpp b/backend/lib/CodeGen/MachineJumpTableInfo.cpp new file mode 100644 index 0000000..4ae0c15 --- /dev/null +++ b/backend/lib/CodeGen/MachineJumpTableInfo.cpp @@ -0,0 +1,142 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "rcc/CodeGen/MachineJumpTableInfo.h" + +#include "rcc/CodeGen/MachineBasicBlock.h" +#include "rcc/CodeGen/MachineFunction.h" + +#include <bit> +#include <cstddef> +#include <cstdint> +#include <limits> +#include <utility> + +namespace rcc { + +Expected<void> +MachineJumpTableInfo::verifyDescription(const MachineJumpTableDesc &Description) const { + if (Description.Encoding >= JumpTableEntryEncoding::Count) + return makeUnexpected(ErrorCode::VerificationError, "jump table encoding is invalid"); + if (Description.EntrySize == 0 || Description.EntrySize > 16 || + !std::has_single_bit(Description.EntrySize)) { + return makeUnexpected(ErrorCode::VerificationError, + "jump table entry size must be a power of two no larger than 16 bytes"); + } + if (Description.Targets.empty()) + return makeUnexpected(ErrorCode::VerificationError, "jump table must have at least one target"); + if (Description.Placement.MaximumDistance && *Description.Placement.MaximumDistance == 0) { + return makeUnexpected(ErrorCode::VerificationError, + "jump table maximum distance must be nonzero"); + } + if ((Description.Encoding == JumpTableEntryEncoding::Target) != + (Description.TargetEncoding != 0)) { + return makeUnexpected(ErrorCode::VerificationError, + "target jump table encoding and target encoding ID are inconsistent"); + } + if (Description.Encoding == JumpTableEntryEncoding::CompressedOffset && + Description.EntrySize > 4) { + return makeUnexpected(ErrorCode::VerificationError, + "compressed jump table entries cannot exceed four bytes"); + } + + for (const MachineBasicBlock *Target : Description.Targets) { + if (Target == nullptr || &Target->function() != Owner || !Target->isLinked()) { + return makeUnexpected(ErrorCode::VerificationError, + "jump table target must be linked to its owning function"); + } + } + return {}; +} + +Expected<JumpTableIndex> MachineJumpTableInfo::createTable(MachineJumpTableDesc Description) { + if (Frozen) + return makeUnexpected(ErrorCode::InvalidArgument, + "frozen jump table info cannot create tables"); + auto Verification = verifyDescription(Description); + if (!Verification) + return std::unexpected(Verification.error()); + if (Tables.size() > std::numeric_limits<std::uint32_t>::max()) + return makeUnexpected(ErrorCode::Unsupported, "jump table index space is exhausted"); + + const JumpTableIndex Index = JumpTableIndex::fromValue(static_cast<std::uint32_t>(Tables.size())); + Tables.push_back(MachineJumpTableEntry(Index, std::move(Description))); + return Index; +} + +Expected<MachineJumpTableEntry *> MachineJumpTableInfo::mutableTable(JumpTableIndex Index) { + if (Frozen) + return makeUnexpected(ErrorCode::InvalidArgument, + "frozen jump table info cannot modify tables"); + if (Index.value() >= Tables.size()) + return makeUnexpected(ErrorCode::InvalidArgument, "jump table index is out of range"); + MachineJumpTableEntry &Entry = Tables[Index.value()]; + if (Entry.isTombstone()) + return makeUnexpected(ErrorCode::InvalidArgument, "jump table index is a tombstone"); + return &Entry; +} + +Expected<void> MachineJumpTableInfo::updateTable(JumpTableIndex Index, + MachineJumpTableDesc Description) { + auto Verification = verifyDescription(Description); + if (!Verification) + return std::unexpected(Verification.error()); + auto Entry = mutableTable(Index); + if (!Entry) + return std::unexpected(Entry.error()); + (*Entry)->Description.emplace<MachineJumpTableDesc>(std::move(Description)); + return {}; +} + +Expected<void> MachineJumpTableInfo::eraseTable(JumpTableIndex Index) { + auto Entry = mutableTable(Index); + if (!Entry) + return std::unexpected(Entry.error()); + (*Entry)->Description.emplace<std::monostate>(); + return {}; +} + +const MachineJumpTableEntry *MachineJumpTableInfo::table(JumpTableIndex Index) const noexcept { + if (Index.value() >= Tables.size() || Tables[Index.value()].isTombstone()) + return nullptr; + return &Tables[Index.value()]; +} + +std::size_t MachineJumpTableInfo::liveTableCount() const noexcept { + std::size_t Count = 0; + for (const MachineJumpTableEntry &Entry : Tables) { + if (!Entry.isTombstone()) + ++Count; + } + return Count; +} + +Expected<void> MachineJumpTableInfo::freeze() { + if (Frozen) + return makeUnexpected(ErrorCode::InvalidArgument, "jump table info is already frozen"); + auto Verification = verify(); + if (!Verification) + return std::unexpected(Verification.error()); + Frozen = true; + return {}; +} + +Expected<void> MachineJumpTableInfo::verify() const { + if (Owner == nullptr) + return makeUnexpected(ErrorCode::VerificationError, "jump table info has no owner"); + for (std::size_t Position = 0; Position < Tables.size(); ++Position) { + const MachineJumpTableEntry &Entry = Tables[Position]; + if (Entry.index() != JumpTableIndex::fromValue(static_cast<std::uint32_t>(Position))) { + return makeUnexpected(ErrorCode::VerificationError, + "jump table index does not match its stable table position"); + } + const MachineJumpTableDesc *Description = Entry.description(); + if (Description == nullptr) + continue; + auto Verification = verifyDescription(*Description); + if (!Verification) + return std::unexpected(Verification.error()); + } + return {}; +} + +} // namespace rcc diff --git a/backend/lib/CodeGen/MachineVerifier.cpp b/backend/lib/CodeGen/MachineVerifier.cpp index 6331366..50c1001 100644 --- a/backend/lib/CodeGen/MachineVerifier.cpp +++ b/backend/lib/CodeGen/MachineVerifier.cpp @@ -40,9 +40,15 @@ public: auto FrameResult = Function.frameInfo().verify(); if (!FrameResult) return failWith("frame info", FrameResult.error()); + auto ConstantPoolResult = Function.constantPool().verify(); + if (!ConstantPoolResult) + return failWith("constant pool", ConstantPoolResult.error()); auto LayoutResult = verifyBlockLayout(); if (!LayoutResult) return LayoutResult; + auto JumpTableResult = Function.jumpTableInfo().verify(); + if (!JumpTableResult) + return failWith("jump table info", JumpTableResult.error()); auto FrameLifetimeResult = verifyFrameLifetimes(); if (!FrameLifetimeResult) return FrameLifetimeResult; @@ -286,6 +292,18 @@ private: return {}; } + [[nodiscard]] Expected<void> verifyConstantPoolIndex(ConstantPoolIndex Index) const { + if (Function.constantPool().entry(Index) == nullptr) + return fail("live instruction references an unknown constant pool entry"); + return {}; + } + + [[nodiscard]] Expected<void> verifyJumpTableIndex(JumpTableIndex Index) const { + if (Function.jumpTableInfo().table(Index) == nullptr) + return fail("live instruction references an unknown jump table"); + return {}; + } + [[nodiscard]] Expected<void> verifyFrameLifetimes() const { for (const StackObject &Object : Function.frameInfo().objects()) { const StackObjectDesc *Description = Object.description(); @@ -334,6 +352,16 @@ private: if (!FrameResult) return FrameResult; } + if (const auto *Constant = Operand.getIf<ConstantPoolOperand>(); Constant != nullptr) { + auto ConstantResult = verifyConstantPoolIndex(Constant->Index); + if (!ConstantResult) + return ConstantResult; + } + if (const auto *JumpTable = Operand.getIf<JumpTableOperand>(); JumpTable != nullptr) { + auto JumpTableResult = verifyJumpTableIndex(JumpTable->Index); + if (!JumpTableResult) + return JumpTableResult; + } if (const auto *Block = Operand.getIf<BasicBlockOperand>(); Block != nullptr && @@ -364,11 +392,22 @@ private: } } for (const MachineMemOperand *MemOperand : Instruction.memOperands()) { - if (const auto Frame = MemOperand->description().Base.frameIndex()) { + const MemoryBase &Base = MemOperand->description().Base; + if (const auto Frame = Base.frameIndex()) { auto FrameResult = verifyFrameIndex(*Frame); if (!FrameResult) return FrameResult; } + if (const auto Constant = Base.constantPoolIndex()) { + auto ConstantResult = verifyConstantPoolIndex(*Constant); + if (!ConstantResult) + return ConstantResult; + } + if (const auto JumpTable = Base.jumpTableIndex()) { + auto JumpTableResult = verifyJumpTableIndex(*JumpTable); + if (!JumpTableResult) + return JumpTableResult; + } } return {}; } diff --git a/backend/lib/CodeGen/TargetConstant.cpp b/backend/lib/CodeGen/TargetConstant.cpp new file mode 100644 index 0000000..8859325 --- /dev/null +++ b/backend/lib/CodeGen/TargetConstant.cpp @@ -0,0 +1,146 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "rcc/CodeGen/TargetConstant.h" + +#include <cstddef> +#include <cstdint> +#include <memory> +#include <optional> +#include <string> +#include <unordered_set> +#include <utility> + +namespace rcc { + +Expected<void> TargetConstantEmission::verify() const { + if (Bytes.empty()) + return makeUnexpected(ErrorCode::VerificationError, + "target constant emission must contain at least one byte"); + + std::unordered_set<std::uint64_t> FixupOffsets; + for (const TargetConstantFixup &Fixup : Fixups) { + if (Fixup.Offset >= Bytes.size()) { + return makeUnexpected(ErrorCode::VerificationError, + "target constant fixup offset is outside emitted bytes"); + } + if (Fixup.Kind.value() == 0) + return makeUnexpected(ErrorCode::VerificationError, + "target constant fixup relocation kind must be nonzero"); + if (Fixup.Symbol.empty()) + return makeUnexpected(ErrorCode::VerificationError, + "target constant fixup symbol cannot be empty"); + if (!FixupOffsets.insert(Fixup.Offset).second) { + return makeUnexpected(ErrorCode::VerificationError, + "target constant emission has duplicate fixup offsets"); + } + } + return {}; +} + +TargetConstant::TargetConstant(TargetConstant &&) noexcept = default; +TargetConstant &TargetConstant::operator=(TargetConstant &&) noexcept = default; +TargetConstant::~TargetConstant() = default; + +std::optional<TargetConstantTypeID> TargetConstant::typeID() const noexcept { + if (Storage == nullptr) + return std::nullopt; + return Storage->typeID(); +} + +std::optional<std::uint32_t> TargetConstant::schemaVersion() const noexcept { + if (Storage == nullptr) + return std::nullopt; + return Storage->schemaVersion(); +} + +Expected<void> TargetConstant::verify() const { + if (Storage == nullptr) + return makeUnexpected(ErrorCode::VerificationError, "target constant is empty"); + if (Storage->typeID().value() == 0 || Storage->schemaVersion() == 0) { + return makeUnexpected(ErrorCode::VerificationError, + "target constant identity or schema version is invalid"); + } + return Storage->verify(); +} + +Expected<TargetConstant> TargetConstant::clone() const { + auto Verification = verify(); + if (!Verification) + return std::unexpected(Verification.error()); + auto ClonedStorage = Storage->cloneStorage(); + if (!ClonedStorage) + return std::unexpected(ClonedStorage.error()); + TargetConstant Result(std::move(*ClonedStorage)); + auto CloneVerification = Result.verify(); + if (!CloneVerification) + return std::unexpected(CloneVerification.error()); + return Result; +} + +Expected<std::uint64_t> TargetConstant::stableHash() const { + auto Verification = verify(); + if (!Verification) + return std::unexpected(Verification.error()); + return Storage->stableHash(); +} + +Expected<std::string> TargetConstant::print() const { + auto Verification = verify(); + if (!Verification) + return std::unexpected(Verification.error()); + return Storage->print(); +} + +Expected<EncodedTargetConstant> TargetConstant::encode() const { + auto Verification = verify(); + if (!Verification) + return std::unexpected(Verification.error()); + auto Payload = Storage->encodePayload(); + if (!Payload) + return std::unexpected(Payload.error()); + return EncodedTargetConstant{Storage->typeID(), Storage->schemaVersion(), std::move(*Payload)}; +} + +Expected<TargetConstantEmission> TargetConstant::emit() const { + auto Verification = verify(); + if (!Verification) + return std::unexpected(Verification.error()); + auto Emission = Storage->emit(); + if (!Emission) + return std::unexpected(Emission.error()); + auto EmissionVerification = Emission->verify(); + if (!EmissionVerification) + return std::unexpected(EmissionVerification.error()); + return Emission; +} + +Expected<Alignment> TargetConstant::requiredAlignment() const { + auto Verification = verify(); + if (!Verification) + return std::unexpected(Verification.error()); + return Storage->requiredAlignment(); +} + +Expected<std::optional<TargetRelocationKindID>> TargetConstant::relocationKind() const { + auto Verification = verify(); + if (!Verification) + return std::unexpected(Verification.error()); + const std::optional<TargetRelocationKindID> Kind = Storage->relocationKind(); + if (Kind && Kind->value() == 0) { + return makeUnexpected(ErrorCode::VerificationError, + "target constant relocation kind must be nonzero"); + } + return Kind; +} + +bool TargetConstant::equals(const TargetConstant &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 |
