aboutsummaryrefslogtreecommitdiff
path: root/backend/lib/CodeGen
diff options
context:
space:
mode:
authorRichard-Qin-X <richard.qin.001@gmail.com>2026-08-01 14:19:20 +0800
committerRichard-Qin-X <richard.qin.001@gmail.com>2026-08-01 14:19:20 +0800
commita69d14b8ebc028dddc689658b08d42d3e83f5e38 (patch)
tree5bdbb9dbe88de07eeba5caba97a2e54e6b05ad38 /backend/lib/CodeGen
parentc9634fa9f529afbdf622a9cd96741bd09d14ef86 (diff)
downloadrcc-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.txt12
-rw-r--r--backend/lib/CodeGen/MachineConstantPool.cpp329
-rw-r--r--backend/lib/CodeGen/MachineFunction.cpp3
-rw-r--r--backend/lib/CodeGen/MachineFunctionCloner.cpp103
-rw-r--r--backend/lib/CodeGen/MachineIRBuilder.cpp38
-rw-r--r--backend/lib/CodeGen/MachineJumpTableInfo.cpp142
-rw-r--r--backend/lib/CodeGen/MachineVerifier.cpp41
-rw-r--r--backend/lib/CodeGen/TargetConstant.cpp146
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