aboutsummaryrefslogtreecommitdiff
path: root/backend/lib/CodeGen/InstructionDescriptor.cpp
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/CodeGen/InstructionDescriptor.cpp
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/CodeGen/InstructionDescriptor.cpp')
-rw-r--r--backend/lib/CodeGen/InstructionDescriptor.cpp223
1 files changed, 157 insertions, 66 deletions
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>>