aboutsummaryrefslogtreecommitdiff
path: root/backend/lib
diff options
context:
space:
mode:
authorRichard-Qin-X <richard.qin.001@gmail.com>2026-08-01 13:09:30 +0800
committerRichard-Qin-X <richard.qin.001@gmail.com>2026-08-01 13:09:30 +0800
commit8553df97bbc6154dae9d0ad861857e71a0efa420 (patch)
tree754fa62b6181e96ed60f1f918c4ee44229d4e0d7 /backend/lib
parentb4d1ddb99e1305f6ea6217e7570dda2a94b16e51 (diff)
downloadrcc-project-8553df97bbc6154dae9d0ad861857e71a0efa420.tar.gz
rcc-project-8553df97bbc6154dae9d0ad861857e71a0efa420.tar.xz
[CodeGen] Add transactional MachineIR builder
Validate insertion cursors, operand ownership, descriptor constraints, ties, implicit registers, and memory operands before allocating or publishing an instruction. Share instruction legality checks with the verifier path and preserve deterministic insertion order.
Diffstat (limited to 'backend/lib')
-rw-r--r--backend/lib/CodeGen/CMakeLists.txt4
-rw-r--r--backend/lib/CodeGen/InstructionDescriptor.cpp223
-rw-r--r--backend/lib/CodeGen/MachineIRBuilder.cpp537
-rw-r--r--backend/lib/CodeGen/MachineInstr.cpp8
4 files changed, 702 insertions, 70 deletions
diff --git a/backend/lib/CodeGen/CMakeLists.txt b/backend/lib/CodeGen/CMakeLists.txt
index d700d54..deaacb1 100644
--- a/backend/lib/CodeGen/CMakeLists.txt
+++ b/backend/lib/CodeGen/CMakeLists.txt
@@ -8,6 +8,7 @@ set(
MachineCFGUpdater.cpp
MachineFunction.cpp
MachineIRTypes.cpp
+ MachineIRBuilder.cpp
MachineInstr.cpp
MachineMemOperand.cpp
MachineOperand.cpp
@@ -24,6 +25,7 @@ set(
"${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/MachineCFGUpdater.h"
"${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/MachineFunction.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/MachineMemOperand.h"
"${RCC_BACKEND_SOURCE_DIR}/include/rcc/CodeGen/MachineOperand.h"
@@ -69,6 +71,7 @@ rcc_backend_register_format_files(
"${CMAKE_CURRENT_SOURCE_DIR}/MachineCFGUpdater.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/MachineFunction.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/MachineIRTypes.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/MachineIRBuilder.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/MachineInstr.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/MachineMemOperand.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/MachineOperand.cpp"
@@ -85,6 +88,7 @@ rcc_backend_register_tidy_files(
"${CMAKE_CURRENT_SOURCE_DIR}/MachineCFGUpdater.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/MachineFunction.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/MachineIRTypes.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/MachineIRBuilder.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/MachineInstr.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/MachineMemOperand.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/MachineOperand.cpp"
diff --git a/backend/lib/CodeGen/InstructionDescriptor.cpp b/backend/lib/CodeGen/InstructionDescriptor.cpp
index e5efad7..9f4a178 100644
--- a/backend/lib/CodeGen/InstructionDescriptor.cpp
+++ b/backend/lib/CodeGen/InstructionDescriptor.cpp
@@ -35,6 +35,14 @@ namespace {
(Left == RegisterOperandRole::Use && Right == RegisterOperandRole::Def);
}
+[[nodiscard]] bool isPureDef(const MachineOperand &Operand) noexcept {
+ return Operand.isRegister() && Operand.isDef() && !Operand.isUse();
+}
+
+[[nodiscard]] bool isPureUse(const MachineOperand &Operand) noexcept {
+ return Operand.isRegister() && Operand.isUse() && !Operand.isDef();
+}
+
[[nodiscard]] Expected<void> validateDescriptorStructure(const InstructionDescriptor &Descriptor) {
if (!Descriptor.InstructionOpcode.isValid())
return makeUnexpected(ErrorCode::InvalidArgument, "instruction descriptor opcode is invalid");
@@ -320,6 +328,133 @@ Expected<void> InstructionSet::validateExplicitOperands(Opcode InstructionOpcode
return validateExplicitOperands(*Descriptor, Operands, RegInfo);
}
+Expected<void> InstructionSet::validateOperandTies(const InstructionDescriptor &Descriptor,
+ std::span<const MachineOperand> ExplicitOperands,
+ std::span<const MachineOperandTie> Ties) const {
+ std::vector<std::optional<std::size_t>> Partners(ExplicitOperands.size());
+ for (const MachineOperandTie Tie : Ties) {
+ if (Tie.DefOperand >= ExplicitOperands.size() || Tie.UseOperand >= ExplicitOperands.size() ||
+ Tie.DefOperand == Tie.UseOperand || !isPureDef(ExplicitOperands[Tie.DefOperand]) ||
+ !isPureUse(ExplicitOperands[Tie.UseOperand])) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "instruction tie must connect an in-range pure register def and use");
+ }
+ if (Partners[Tie.DefOperand] || Partners[Tie.UseOperand]) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "instruction operand participates in duplicate or conflicting ties");
+ }
+ Partners[Tie.DefOperand] = Tie.UseOperand;
+ Partners[Tie.UseOperand] = Tie.DefOperand;
+ }
+
+ for (std::size_t Index = 0; Index < Descriptor.Operands.size(); ++Index) {
+ const OperandDescriptor &Operand = Descriptor.Operands[Index];
+ if (Operand.DefaultTiedOperand &&
+ (Index >= Partners.size() || Partners[Index] != Operand.DefaultTiedOperand)) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "required descriptor tie is not materialized");
+ }
+ }
+ return {};
+}
+
+Expected<void> InstructionSet::validateOperandTies(Opcode InstructionOpcode,
+ std::span<const MachineOperand> ExplicitOperands,
+ std::span<const MachineOperandTie> Ties) const {
+ const InstructionDescriptor *Descriptor = descriptor(InstructionOpcode);
+ if (Descriptor == nullptr) {
+ return makeUnexpected(ErrorCode::VerificationError, "instruction opcode is not registered");
+ }
+ return validateOperandTies(*Descriptor, ExplicitOperands, Ties);
+}
+
+Expected<void> InstructionSet::validateImplicitOperands(
+ const InstructionDescriptor &Descriptor,
+ std::span<const MachineOperand *const> ImplicitOperands) const {
+ const std::size_t DefaultCount = Descriptor.ImplicitDefs.size() + Descriptor.ImplicitUses.size();
+ if (ImplicitOperands.size() < DefaultCount ||
+ (!Descriptor.AllowExtraImplicitOperands && ImplicitOperands.size() != DefaultCount)) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "instruction implicit operand count does not match its descriptor");
+ }
+
+ std::size_t Index = 0;
+ auto ValidateDefault = [&](const ImplicitRegisterDescriptor &Implicit,
+ bool IsDef) -> Expected<void> {
+ const MachineOperand *Operand = ImplicitOperands[Index++];
+ const Register *Reg = Operand == nullptr ? nullptr : Operand->getIf<Register>();
+ if (Reg == nullptr || Reg->physicalRegister() != Implicit.Reg || Operand->isDef() != IsDef ||
+ Operand->isUse() == IsDef || !Operand->isImplicit() ||
+ Operand->registerFlags().has(RegisterOperandFlag::EarlyClobber) != Implicit.EarlyClobber ||
+ Operand->registerFlags().has(RegisterOperandFlag::Renamable) != Implicit.Renamable) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "default implicit register operand does not match its descriptor");
+ }
+ return {};
+ };
+
+ for (const ImplicitRegisterDescriptor &Implicit : Descriptor.ImplicitDefs) {
+ auto Validation = ValidateDefault(Implicit, true);
+ if (!Validation)
+ return std::unexpected(Validation.error());
+ }
+ for (const ImplicitRegisterDescriptor &Implicit : Descriptor.ImplicitUses) {
+ auto Validation = ValidateDefault(Implicit, false);
+ if (!Validation)
+ return std::unexpected(Validation.error());
+ }
+ for (; Index < ImplicitOperands.size(); ++Index) {
+ const MachineOperand *Operand = ImplicitOperands[Index];
+ if (Operand == nullptr || !Operand->isRegister() || !Operand->isImplicit()) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "dynamic implicit operand must be an implicit register");
+ }
+ }
+ return {};
+}
+
+Expected<void> InstructionSet::validateImplicitOperands(
+ Opcode InstructionOpcode, std::span<const MachineOperand *const> ImplicitOperands) const {
+ const InstructionDescriptor *Descriptor = descriptor(InstructionOpcode);
+ if (Descriptor == nullptr) {
+ return makeUnexpected(ErrorCode::VerificationError, "instruction opcode is not registered");
+ }
+ return validateImplicitOperands(*Descriptor, ImplicitOperands);
+}
+
+Expected<void>
+InstructionSet::validateMemOperands(const InstructionDescriptor &Descriptor,
+ std::span<const MachineMemOperand *const> MemOperands) const {
+ const bool MayLoad = Descriptor.Flags.has(InstructionFlag::MayLoad);
+ const bool MayStore = Descriptor.Flags.has(InstructionFlag::MayStore);
+ if (Descriptor.RequiresMemoryOperands && MemOperands.empty()) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "memory instruction is missing MachineMemOperand");
+ }
+ if (!MayLoad && !MayStore && !MemOperands.empty()) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "non-memory instruction carries MachineMemOperand");
+ }
+ for (const MachineMemOperand *MemOperand : MemOperands) {
+ if (MemOperand == nullptr || (MemOperand->isLoad() && !MayLoad) ||
+ (MemOperand->isStore() && !MayStore)) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "MachineMemOperand access conflicts with instruction descriptor");
+ }
+ }
+ return {};
+}
+
+Expected<void>
+InstructionSet::validateMemOperands(Opcode InstructionOpcode,
+ std::span<const MachineMemOperand *const> MemOperands) const {
+ const InstructionDescriptor *Descriptor = descriptor(InstructionOpcode);
+ if (Descriptor == nullptr) {
+ return makeUnexpected(ErrorCode::VerificationError, "instruction opcode is not registered");
+ }
+ return validateMemOperands(*Descriptor, MemOperands);
+}
+
Expected<void> InstructionSet::validateOperand(const OperandDescriptor &Descriptor,
const MachineOperand &Operand,
const MachineRegisterInfo &RegInfo) const {
@@ -402,86 +537,42 @@ Expected<void> InstructionSet::validateInstruction(const MachineInstr &Instructi
if (!ExplicitValidation)
return std::unexpected(ExplicitValidation.error());
+ std::vector<MachineOperandTie> Ties;
for (std::size_t Index = 0; Index < ExplicitCount; ++Index) {
const MachineOperand &Operand = AllOperands[Index];
- const OperandDescriptor &OperandDesc = descriptorForOperand(*Descriptor, Index);
- if (OperandDesc.DefaultTiedOperand &&
- Operand.tiedOperandIndex() != OperandDesc.DefaultTiedOperand) {
- return makeUnexpected(ErrorCode::VerificationError,
- "required descriptor tie is not materialized");
- }
if (!Operand.tiedOperandIndex())
continue;
const std::size_t OtherIndex = *Operand.tiedOperandIndex();
- if (OtherIndex >= ExplicitCount || AllOperands[OtherIndex].tiedOperandIndex() != Index ||
- !Operand.isRegister() || !AllOperands[OtherIndex].isRegister() ||
- Operand.isDef() == AllOperands[OtherIndex].isDef() ||
- Operand.isUse() == AllOperands[OtherIndex].isUse()) {
+ if (OtherIndex >= ExplicitCount || AllOperands[OtherIndex].tiedOperandIndex() != Index) {
return makeUnexpected(ErrorCode::VerificationError,
- "instruction operand tie is invalid or not reciprocal");
+ "instruction operand tie is out of range or not reciprocal");
}
+ if (Index > OtherIndex)
+ continue;
+ if (isPureUse(Operand) && isPureDef(AllOperands[OtherIndex]))
+ Ties.push_back(MachineOperandTie{OtherIndex, Index});
+ else
+ Ties.push_back(MachineOperandTie{Index, OtherIndex});
}
- const std::size_t DefaultImplicitCount =
- Descriptor->ImplicitDefs.size() + Descriptor->ImplicitUses.size();
- const std::size_t ActualImplicitCount = AllOperands.size() - ExplicitCount;
- if (ActualImplicitCount < DefaultImplicitCount ||
- (!Descriptor->AllowExtraImplicitOperands && ActualImplicitCount != DefaultImplicitCount)) {
- return makeUnexpected(ErrorCode::VerificationError,
- "instruction implicit operand count does not match its descriptor");
- }
-
- std::size_t ImplicitIndex = ExplicitCount;
- auto ValidateImplicit = [&](const ImplicitRegisterDescriptor &Implicit,
- bool IsDef) -> Expected<void> {
- const MachineOperand &Operand = AllOperands[ImplicitIndex++];
- const auto Reg = Operand.registerValue();
- if (!Reg || Reg->physicalRegister() != Implicit.Reg || Operand.isDef() != IsDef ||
- Operand.isUse() == IsDef || !Operand.isImplicit() ||
- Operand.registerFlags().has(RegisterOperandFlag::EarlyClobber) != Implicit.EarlyClobber ||
- Operand.registerFlags().has(RegisterOperandFlag::Renamable) != Implicit.Renamable) {
- return makeUnexpected(ErrorCode::VerificationError,
- "default implicit register operand does not match its descriptor");
- }
- return {};
- };
-
- for (const ImplicitRegisterDescriptor &Implicit : Descriptor->ImplicitDefs) {
- auto Validation = ValidateImplicit(Implicit, true);
- if (!Validation)
- return std::unexpected(Validation.error());
- }
- for (const ImplicitRegisterDescriptor &Implicit : Descriptor->ImplicitUses) {
- auto Validation = ValidateImplicit(Implicit, false);
- if (!Validation)
- return std::unexpected(Validation.error());
- }
- for (; ImplicitIndex < AllOperands.size(); ++ImplicitIndex) {
- if (!AllOperands[ImplicitIndex].isRegister() || !AllOperands[ImplicitIndex].isImplicit()) {
- return makeUnexpected(ErrorCode::VerificationError,
- "dynamic implicit operand must be an implicit register");
- }
- }
+ auto TieValidation = validateOperandTies(*Descriptor, AllOperands.first(ExplicitCount), Ties);
+ if (!TieValidation)
+ return std::unexpected(TieValidation.error());
- const bool MayLoad = Descriptor->Flags.has(InstructionFlag::MayLoad);
- const bool MayStore = Descriptor->Flags.has(InstructionFlag::MayStore);
- if (Descriptor->RequiresMemoryOperands && Instruction.memOperands().empty()) {
- return makeUnexpected(ErrorCode::VerificationError,
- "memory instruction is missing MachineMemOperand");
- }
- for (const MachineMemOperand *MemOperand : Instruction.memOperands()) {
- if (MemOperand == nullptr || (MemOperand->isLoad() && !MayLoad) ||
- (MemOperand->isStore() && !MayStore)) {
+ std::vector<const MachineOperand *> ImplicitOperands;
+ ImplicitOperands.reserve(AllOperands.size() - ExplicitCount);
+ for (std::size_t Index = ExplicitCount; Index < AllOperands.size(); ++Index) {
+ if (AllOperands[Index].tiedOperandIndex()) {
return makeUnexpected(ErrorCode::VerificationError,
- "MachineMemOperand access conflicts with instruction descriptor");
+ "implicit register operand cannot participate in a tie");
}
- }
- if (!MayLoad && !MayStore && !Instruction.memOperands().empty()) {
- return makeUnexpected(ErrorCode::VerificationError,
- "non-memory instruction carries MachineMemOperand");
+ ImplicitOperands.push_back(&AllOperands[Index]);
}
- return {};
+ auto ImplicitValidation = validateImplicitOperands(*Descriptor, ImplicitOperands);
+ if (!ImplicitValidation)
+ return std::unexpected(ImplicitValidation.error());
+ return validateMemOperands(*Descriptor, Instruction.memOperands());
}
Expected<std::vector<MachineOperand>>
diff --git a/backend/lib/CodeGen/MachineIRBuilder.cpp b/backend/lib/CodeGen/MachineIRBuilder.cpp
new file mode 100644
index 0000000..8f80ad6
--- /dev/null
+++ b/backend/lib/CodeGen/MachineIRBuilder.cpp
@@ -0,0 +1,537 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "rcc/CodeGen/MachineIRBuilder.h"
+
+#include "rcc/CodeGen/MachineBasicBlock.h"
+#include "rcc/CodeGen/MachineFunction.h"
+#include "rcc/CodeGen/MachineInstr.h"
+#include "rcc/CodeGen/MachineMemOperand.h"
+#include "rcc/CodeGen/MachineRegisterInfo.h"
+
+#include <algorithm>
+#include <cstddef>
+#include <cstdint>
+#include <memory>
+#include <optional>
+#include <string>
+#include <utility>
+#include <vector>
+
+namespace rcc {
+
+MachineInstrBuilder::MachineInstrBuilder(MachineFunction &Function,
+ const InstructionSet &Instructions,
+ const InstructionDescriptor &Descriptor,
+ MachineBasicBlock &InsertBlock,
+ MachineInstr *InsertBefore) noexcept
+ : Function(&Function), Instructions(&Instructions), Descriptor(&Descriptor),
+ InsertBlock(&InsertBlock), InsertBefore(InsertBefore) {}
+
+MachineInstrBuilder::MachineInstrBuilder(MachineInstrBuilder &&Other) noexcept
+ : Function(Other.Function), Instructions(Other.Instructions), Descriptor(Other.Descriptor),
+ InsertBlock(Other.InsertBlock), InsertBefore(Other.InsertBefore),
+ ExplicitOperands(std::move(Other.ExplicitOperands)),
+ ExtraImplicitOperands(std::move(Other.ExtraImplicitOperands)),
+ RequestedTies(std::move(Other.RequestedTies)), MemOperands(std::move(Other.MemOperands)),
+ Flags(Other.Flags), Location(std::move(Other.Location)), Origin(Other.Origin),
+ IsBuilt(Other.IsBuilt) {
+ Other.Function = nullptr;
+ Other.Instructions = nullptr;
+ Other.Descriptor = nullptr;
+ Other.InsertBlock = nullptr;
+ Other.InsertBefore = nullptr;
+ Other.IsBuilt = true;
+}
+
+MachineInstrBuilder &MachineInstrBuilder::operator=(MachineInstrBuilder &&Other) noexcept {
+ if (this == &Other)
+ return *this;
+
+ Function = Other.Function;
+ Instructions = Other.Instructions;
+ Descriptor = Other.Descriptor;
+ InsertBlock = Other.InsertBlock;
+ InsertBefore = Other.InsertBefore;
+ ExplicitOperands = std::move(Other.ExplicitOperands);
+ ExtraImplicitOperands = std::move(Other.ExtraImplicitOperands);
+ RequestedTies = std::move(Other.RequestedTies);
+ MemOperands = std::move(Other.MemOperands);
+ Flags = Other.Flags;
+ Location = std::move(Other.Location);
+ Origin = Other.Origin;
+ IsBuilt = Other.IsBuilt;
+
+ Other.Function = nullptr;
+ Other.Instructions = nullptr;
+ Other.Descriptor = nullptr;
+ Other.InsertBlock = nullptr;
+ Other.InsertBefore = nullptr;
+ Other.IsBuilt = true;
+ return *this;
+}
+
+Expected<void> MachineInstrBuilder::ensurePending() const {
+ if (IsBuilt) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "machine instruction builder was already consumed");
+ }
+ return {};
+}
+
+Expected<void> MachineInstrBuilder::addOperand(MachineOperand &&Operand) {
+ auto Pending = ensurePending();
+ if (!Pending)
+ return std::unexpected(Pending.error());
+ if (Operand.parent() != nullptr) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "builder operand already belongs to an instruction");
+ }
+ if (Operand.isImplicit()) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "implicit register requires addImplicitOperand");
+ }
+ ExplicitOperands.push_back(std::move(Operand));
+ return {};
+}
+
+Expected<void> MachineInstrBuilder::addImplicitOperand(MachineOperand &&Operand) {
+ auto Pending = ensurePending();
+ if (!Pending)
+ return std::unexpected(Pending.error());
+ if (Operand.parent() != nullptr) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "builder operand already belongs to an instruction");
+ }
+ if (!Operand.isRegister() || !Operand.isImplicit()) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "dynamic implicit operand must be an implicit register");
+ }
+ ExtraImplicitOperands.push_back(std::move(Operand));
+ return {};
+}
+
+Expected<void> MachineInstrBuilder::appendRegister(Register Reg, RegisterOperandFlags RegisterFlags,
+ SubRegIndex SubReg, std::uint32_t TargetFlags,
+ bool IsImplicit) {
+ auto Operand = MachineOperand::registerOperand(Reg, RegisterFlags, SubReg, TargetFlags);
+ if (!Operand)
+ return std::unexpected(Operand.error());
+ if (IsImplicit)
+ return addImplicitOperand(std::move(*Operand));
+ return addOperand(std::move(*Operand));
+}
+
+Expected<void> MachineInstrBuilder::addDef(Register Reg, RegisterOperandFlags AdditionalFlags,
+ SubRegIndex SubReg, std::uint32_t TargetFlags) {
+ AdditionalFlags.set(RegisterOperandFlag::Define);
+ return appendRegister(Reg, AdditionalFlags, SubReg, TargetFlags, false);
+}
+
+Expected<void> MachineInstrBuilder::addUse(Register Reg, RegisterOperandFlags AdditionalFlags,
+ SubRegIndex SubReg, std::uint32_t TargetFlags) {
+ AdditionalFlags.set(RegisterOperandFlag::Use);
+ return appendRegister(Reg, AdditionalFlags, SubReg, TargetFlags, false);
+}
+
+Expected<void> MachineInstrBuilder::addDefUse(Register Reg, RegisterOperandFlags AdditionalFlags,
+ SubRegIndex SubReg, std::uint32_t TargetFlags) {
+ AdditionalFlags.set(RegisterOperandFlag::Define);
+ AdditionalFlags.set(RegisterOperandFlag::Use);
+ AdditionalFlags.set(RegisterOperandFlag::InternalRead);
+ return appendRegister(Reg, AdditionalFlags, SubReg, TargetFlags, false);
+}
+
+Expected<void> MachineInstrBuilder::addImplicitDef(Register Reg,
+ RegisterOperandFlags AdditionalFlags,
+ SubRegIndex SubReg, std::uint32_t TargetFlags) {
+ AdditionalFlags.set(RegisterOperandFlag::Define);
+ AdditionalFlags.set(RegisterOperandFlag::Implicit);
+ return appendRegister(Reg, AdditionalFlags, SubReg, TargetFlags, true);
+}
+
+Expected<void> MachineInstrBuilder::addImplicitUse(Register Reg,
+ RegisterOperandFlags AdditionalFlags,
+ SubRegIndex SubReg, std::uint32_t TargetFlags) {
+ AdditionalFlags.set(RegisterOperandFlag::Use);
+ AdditionalFlags.set(RegisterOperandFlag::Implicit);
+ return appendRegister(Reg, AdditionalFlags, SubReg, TargetFlags, true);
+}
+
+Expected<void> MachineInstrBuilder::addImmediate(std::int64_t Value, std::uint32_t TargetFlags) {
+ return addOperand(MachineOperand::immediate(Value, TargetFlags));
+}
+
+Expected<void> MachineInstrBuilder::addFloatingImmediate(FloatingPointBits Bits,
+ std::uint32_t TargetFlags) {
+ return addOperand(MachineOperand::floatingImmediate(Bits, TargetFlags));
+}
+
+Expected<void> MachineInstrBuilder::addBasicBlock(MachineBasicBlock &Block,
+ std::uint32_t TargetFlags) {
+ return addOperand(MachineOperand::basicBlock(Block, TargetFlags));
+}
+
+Expected<void> MachineInstrBuilder::addFrameIndex(FrameIndex Index, std::int64_t Offset,
+ std::uint32_t TargetFlags) {
+ return addOperand(MachineOperand::frameIndex(Index, Offset, TargetFlags));
+}
+
+Expected<void> MachineInstrBuilder::addConstantPool(ConstantPoolIndex Index, std::int64_t Offset,
+ std::uint32_t TargetFlags) {
+ return addOperand(MachineOperand::constantPool(Index, Offset, TargetFlags));
+}
+
+Expected<void> MachineInstrBuilder::addJumpTable(JumpTableIndex Index, std::uint32_t TargetFlags) {
+ return addOperand(MachineOperand::jumpTable(Index, TargetFlags));
+}
+
+Expected<void> MachineInstrBuilder::addGlobalSymbol(std::string Name, std::int64_t Offset,
+ std::uint32_t TargetFlags) {
+ auto Operand = MachineOperand::globalSymbol(std::move(Name), Offset, TargetFlags);
+ if (!Operand)
+ return std::unexpected(Operand.error());
+ return addOperand(std::move(*Operand));
+}
+
+Expected<void> MachineInstrBuilder::addExternalSymbol(std::string Name, std::int64_t Offset,
+ std::uint32_t TargetFlags) {
+ auto Operand = MachineOperand::externalSymbol(std::move(Name), Offset, TargetFlags);
+ if (!Operand)
+ return std::unexpected(Operand.error());
+ return addOperand(std::move(*Operand));
+}
+
+Expected<void> MachineInstrBuilder::addBlockAddress(std::string FunctionName, std::string BlockName,
+ std::int64_t Offset,
+ std::uint32_t TargetFlags) {
+ auto Operand = MachineOperand::blockAddress(std::move(FunctionName), std::move(BlockName), Offset,
+ TargetFlags);
+ if (!Operand)
+ return std::unexpected(Operand.error());
+ return addOperand(std::move(*Operand));
+}
+
+Expected<void> MachineInstrBuilder::addRegisterMask(std::shared_ptr<const RegisterMask> Mask,
+ std::uint32_t TargetFlags) {
+ auto Operand = MachineOperand::registerMask(std::move(Mask), TargetFlags);
+ if (!Operand)
+ return std::unexpected(Operand.error());
+ return addOperand(std::move(*Operand));
+}
+
+Expected<void> MachineInstrBuilder::addMetadata(std::string Marker, std::uint32_t TargetFlags) {
+ auto Operand = MachineOperand::metadata(std::move(Marker), TargetFlags);
+ if (!Operand)
+ return std::unexpected(Operand.error());
+ return addOperand(std::move(*Operand));
+}
+
+Expected<void> MachineInstrBuilder::addTargetIndex(TargetIndex Index, std::int64_t Offset,
+ std::uint32_t TargetFlags) {
+ return addOperand(MachineOperand::targetIndex(Index, Offset, TargetFlags));
+}
+
+Expected<void> MachineInstrBuilder::addMCSymbol(std::string Name, std::int64_t Offset,
+ std::uint32_t TargetFlags) {
+ auto Operand = MachineOperand::mcSymbol(std::move(Name), Offset, TargetFlags);
+ if (!Operand)
+ return std::unexpected(Operand.error());
+ return addOperand(std::move(*Operand));
+}
+
+Expected<void> MachineInstrBuilder::tieOperands(std::size_t DefOperand, std::size_t UseOperand) {
+ auto Pending = ensurePending();
+ if (!Pending)
+ return std::unexpected(Pending.error());
+ if (DefOperand >= ExplicitOperands.size() || UseOperand >= ExplicitOperands.size() ||
+ DefOperand == UseOperand) {
+ return makeUnexpected(ErrorCode::InvalidArgument, "builder operand tie indices are invalid");
+ }
+
+ const MachineOperand &Def = ExplicitOperands[DefOperand];
+ const MachineOperand &Use = ExplicitOperands[UseOperand];
+ if (!Def.isRegister() || !Use.isRegister() || !Def.isDef() || Def.isUse() || !Use.isUse() ||
+ Use.isDef()) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "builder tie must connect a pure register def and use");
+ }
+
+ const MachineOperandTie Tie{DefOperand, UseOperand};
+ if (std::ranges::find(RequestedTies, Tie) == RequestedTies.end())
+ RequestedTies.push_back(Tie);
+ return {};
+}
+
+Expected<void> MachineInstrBuilder::addMemOperand(const MachineMemOperand &MemOperand) {
+ auto Pending = ensurePending();
+ if (!Pending)
+ return std::unexpected(Pending.error());
+ if (!Function->ownsMemOperand(MemOperand)) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "builder memory operand belongs to another function");
+ }
+ MemOperands.push_back(&MemOperand);
+ return {};
+}
+
+Expected<void> MachineInstrBuilder::setFlags(MachineInstrFlags NewFlags) {
+ auto Pending = ensurePending();
+ if (!Pending)
+ return std::unexpected(Pending.error());
+ Flags = NewFlags;
+ return {};
+}
+
+Expected<void> MachineInstrBuilder::setSourceLocation(SourceLocation NewLocation) {
+ auto Pending = ensurePending();
+ if (!Pending)
+ return std::unexpected(Pending.error());
+ if (!NewLocation.isValid()) {
+ return makeUnexpected(ErrorCode::InvalidArgument, "builder source location is incomplete");
+ }
+ Location = std::move(NewLocation);
+ return {};
+}
+
+Expected<void> MachineInstrBuilder::clearSourceLocation() {
+ auto Pending = ensurePending();
+ if (!Pending)
+ return std::unexpected(Pending.error());
+ Location.reset();
+ return {};
+}
+
+Expected<void> MachineInstrBuilder::setOrigin(OriginID NewOrigin) {
+ auto Pending = ensurePending();
+ if (!Pending)
+ return std::unexpected(Pending.error());
+ if (NewOrigin.value() == 0) {
+ return makeUnexpected(ErrorCode::InvalidArgument, "builder origin ID must be nonzero");
+ }
+ Origin = NewOrigin;
+ return {};
+}
+
+Expected<void> MachineInstrBuilder::clearOrigin() {
+ auto Pending = ensurePending();
+ if (!Pending)
+ return std::unexpected(Pending.error());
+ Origin.reset();
+ return {};
+}
+
+Expected<void> MachineInstrBuilder::validateInsertionPoint() const {
+ if (InsertBlock == nullptr || &InsertBlock->function() != Function || !InsertBlock->isLinked()) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "builder insertion block is not linked to its function");
+ }
+ if (InsertBefore != nullptr && InsertBefore->parent() != InsertBlock) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "builder insertion cursor is no longer in its block");
+ }
+ return {};
+}
+
+Expected<void> MachineInstrBuilder::validateOperandOwnership(const MachineOperand &Operand) const {
+ if (Operand.parent() != nullptr) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "builder operand already belongs to an instruction");
+ }
+ if (const auto *Block = Operand.getIf<BasicBlockOperand>();
+ Block != nullptr && (Block->Block == nullptr || &Block->Block->function() != Function ||
+ !Block->Block->isLinked())) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "builder basic block operand is not linked to its function");
+ }
+ if (const Register *Reg = Operand.getIf<Register>();
+ Reg != nullptr && Reg->isVirtual() &&
+ !Function->registerInfo().isKnownVirtualRegister(*Reg)) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "builder operand references an unknown virtual register");
+ }
+ return {};
+}
+
+std::vector<MachineOperandTie> MachineInstrBuilder::collectOperandTies() const {
+ std::vector<MachineOperandTie> Result;
+ for (std::size_t Index = 0; Index < Descriptor->Operands.size(); ++Index) {
+ const OperandDescriptor &Operand = Descriptor->Operands[Index];
+ if (!Operand.DefaultTiedOperand || Index > *Operand.DefaultTiedOperand)
+ continue;
+ const std::size_t Other = *Operand.DefaultTiedOperand;
+ const bool IndexIsDef = Operand.RegisterRole == RegisterOperandRole::Def;
+ Result.push_back(IndexIsDef ? MachineOperandTie{Index, Other}
+ : MachineOperandTie{Other, Index});
+ }
+ for (const MachineOperandTie Tie : RequestedTies) {
+ if (std::ranges::find(Result, Tie) == Result.end())
+ Result.push_back(Tie);
+ }
+ return Result;
+}
+
+Expected<MachineInstr *> MachineInstrBuilder::build() {
+ auto Pending = ensurePending();
+ if (!Pending)
+ return std::unexpected(Pending.error());
+
+ auto InsertionValidation = validateInsertionPoint();
+ if (!InsertionValidation)
+ return std::unexpected(InsertionValidation.error());
+
+ for (const MachineOperand &Operand : ExplicitOperands) {
+ auto Ownership = validateOperandOwnership(Operand);
+ if (!Ownership)
+ return std::unexpected(Ownership.error());
+ }
+ for (const MachineOperand &Operand : ExtraImplicitOperands) {
+ auto Ownership = validateOperandOwnership(Operand);
+ if (!Ownership)
+ return std::unexpected(Ownership.error());
+ }
+
+ auto ExplicitValidation = Instructions->validateExplicitOperands(
+ Descriptor->InstructionOpcode, ExplicitOperands, Function->registerInfo());
+ if (!ExplicitValidation)
+ return std::unexpected(ExplicitValidation.error());
+
+ auto DefaultImplicit = Instructions->createDefaultImplicitOperands(*Descriptor);
+ if (!DefaultImplicit)
+ return std::unexpected(DefaultImplicit.error());
+
+ std::vector<const MachineOperand *> ImplicitOperands;
+ ImplicitOperands.reserve(DefaultImplicit->size() + ExtraImplicitOperands.size());
+ for (const MachineOperand &Operand : *DefaultImplicit)
+ ImplicitOperands.push_back(&Operand);
+ for (const MachineOperand &Operand : ExtraImplicitOperands)
+ ImplicitOperands.push_back(&Operand);
+ auto ImplicitValidation =
+ Instructions->validateImplicitOperands(Descriptor->InstructionOpcode, ImplicitOperands);
+ if (!ImplicitValidation)
+ return std::unexpected(ImplicitValidation.error());
+
+ const std::vector<MachineOperandTie> Ties = collectOperandTies();
+ auto TieValidation =
+ Instructions->validateOperandTies(Descriptor->InstructionOpcode, ExplicitOperands, Ties);
+ if (!TieValidation)
+ return std::unexpected(TieValidation.error());
+
+ auto MemoryValidation =
+ Instructions->validateMemOperands(Descriptor->InstructionOpcode, MemOperands);
+ if (!MemoryValidation)
+ return std::unexpected(MemoryValidation.error());
+
+ auto Instruction = Function->createInstruction(Descriptor->InstructionOpcode);
+ if (!Instruction)
+ return std::unexpected(Instruction.error());
+ MachineInstr *Result = *Instruction;
+ Result->reserveOperands(ExplicitOperands.size() + DefaultImplicit->size() +
+ ExtraImplicitOperands.size());
+
+ for (MachineOperand &Operand : ExplicitOperands) {
+ auto AddResult = Result->addOperand(std::move(Operand));
+ if (!AddResult)
+ return std::unexpected(AddResult.error());
+ }
+ for (MachineOperand &Operand : *DefaultImplicit) {
+ auto AddResult = Result->addOperand(std::move(Operand));
+ if (!AddResult)
+ return std::unexpected(AddResult.error());
+ }
+ for (MachineOperand &Operand : ExtraImplicitOperands) {
+ auto AddResult = Result->addOperand(std::move(Operand));
+ if (!AddResult)
+ return std::unexpected(AddResult.error());
+ }
+ for (const MachineOperandTie Tie : Ties) {
+ auto TieResult = Result->tieOperands(Tie.DefOperand, Tie.UseOperand);
+ if (!TieResult)
+ return std::unexpected(TieResult.error());
+ }
+ for (const MachineMemOperand *MemOperand : MemOperands) {
+ auto AddResult = Result->addMemOperand(*MemOperand);
+ if (!AddResult)
+ return std::unexpected(AddResult.error());
+ }
+
+ Result->setFlags(Flags);
+ if (Location)
+ Result->setSourceLocation(std::move(*Location));
+ if (Origin)
+ Result->setOrigin(*Origin);
+
+ Expected<void> InsertResult;
+ if (InsertBefore == nullptr)
+ InsertResult = InsertBlock->appendInstruction(*Result);
+ else
+ InsertResult = InsertBlock->insertInstructionBefore(*InsertBefore, *Result);
+ if (!InsertResult)
+ return std::unexpected(InsertResult.error());
+
+ IsBuilt = true;
+ return Result;
+}
+
+Expected<void> MachineIRBuilder::validateBlock(const MachineBasicBlock &Block) const {
+ if (&Block.function() != Function || !Block.isLinked()) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "builder insertion block is not linked to its function");
+ }
+ return {};
+}
+
+Expected<void> MachineIRBuilder::validateInstruction(const MachineInstr &Instruction) const {
+ if (&Instruction.function() != Function || Instruction.parent() == nullptr) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "builder insertion instruction is not linked to its function");
+ }
+ return {};
+}
+
+Expected<void> MachineIRBuilder::setInsertPoint(MachineBasicBlock &Block) {
+ auto Validation = validateBlock(Block);
+ if (!Validation)
+ return std::unexpected(Validation.error());
+ InsertBlock = &Block;
+ InsertBefore = nullptr;
+ return {};
+}
+
+Expected<void> MachineIRBuilder::setInsertPointBefore(MachineInstr &Instruction) {
+ auto Validation = validateInstruction(Instruction);
+ if (!Validation)
+ return std::unexpected(Validation.error());
+ InsertBlock = Instruction.parent();
+ InsertBefore = &Instruction;
+ return {};
+}
+
+Expected<void> MachineIRBuilder::setInsertPointAfter(MachineInstr &Instruction) {
+ auto Validation = validateInstruction(Instruction);
+ if (!Validation)
+ return std::unexpected(Validation.error());
+ InsertBlock = Instruction.parent();
+ InsertBefore = Instruction.next();
+ return {};
+}
+
+Expected<MachineInstrBuilder> MachineIRBuilder::buildInstr(Opcode InstructionOpcode) const {
+ if (InsertBlock == nullptr) {
+ return makeUnexpected(ErrorCode::InvalidArgument, "machine IR builder has no insertion point");
+ }
+ const InstructionDescriptor *Descriptor = Instructions->descriptor(InstructionOpcode);
+ if (Descriptor == nullptr) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "machine IR builder opcode is not registered");
+ }
+ auto BlockValidation = validateBlock(*InsertBlock);
+ if (!BlockValidation)
+ return std::unexpected(BlockValidation.error());
+ if (InsertBefore != nullptr && InsertBefore->parent() != InsertBlock) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "machine IR builder insertion cursor is stale");
+ }
+ return MachineInstrBuilder(*Function, *Instructions, *Descriptor, *InsertBlock, InsertBefore);
+}
+
+} // namespace rcc
diff --git a/backend/lib/CodeGen/MachineInstr.cpp b/backend/lib/CodeGen/MachineInstr.cpp
index 3692258..b201533 100644
--- a/backend/lib/CodeGen/MachineInstr.cpp
+++ b/backend/lib/CodeGen/MachineInstr.cpp
@@ -46,11 +46,11 @@ void MachineInstr::reserveOperands(std::size_t Capacity) {
Operands.reserve(Capacity);
}
-Expected<void> MachineInstr::addOperand(MachineOperand Operand) {
+Expected<void> MachineInstr::addOperand(MachineOperand &&Operand) {
return insertOperand(Operands.size(), std::move(Operand));
}
-Expected<void> MachineInstr::insertOperand(std::size_t Index, MachineOperand Operand) {
+Expected<void> MachineInstr::insertOperand(std::size_t Index, MachineOperand &&Operand) {
if (Index > Operands.size())
return makeUnexpected(ErrorCode::InvalidArgument, "operand insertion index is out of range");
if (Operand.Parent != nullptr)
@@ -93,9 +93,9 @@ Expected<void> MachineInstr::tieOperands(std::size_t DefIndex, std::size_t UseIn
MachineOperand &DefOperand = Operands[DefIndex];
MachineOperand &UseOperand = Operands[UseIndex];
if (!DefOperand.isRegister() || !UseOperand.isRegister() || !DefOperand.isDef() ||
- !UseOperand.isUse()) {
+ DefOperand.isUse() || !UseOperand.isUse() || UseOperand.isDef()) {
return makeUnexpected(ErrorCode::InvalidArgument,
- "tie must connect a register definition and use");
+ "tie must connect a pure register definition and use");
}
if (DefOperand.TiedOperandIndex || UseOperand.TiedOperandIndex) {
return makeUnexpected(ErrorCode::InvalidArgument,