aboutsummaryrefslogtreecommitdiff
path: root/backend/lib/CodeGen/InstructionDescriptor.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'backend/lib/CodeGen/InstructionDescriptor.cpp')
-rw-r--r--backend/lib/CodeGen/InstructionDescriptor.cpp769
1 files changed, 769 insertions, 0 deletions
diff --git a/backend/lib/CodeGen/InstructionDescriptor.cpp b/backend/lib/CodeGen/InstructionDescriptor.cpp
new file mode 100644
index 0000000..027f738
--- /dev/null
+++ b/backend/lib/CodeGen/InstructionDescriptor.cpp
@@ -0,0 +1,769 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "rcc/CodeGen/InstructionDescriptor.h"
+
+#include "rcc/CodeGen/MachineInstr.h"
+#include "rcc/CodeGen/MachineMemOperand.h"
+#include "rcc/CodeGen/MachineRegisterInfo.h"
+
+#include <algorithm>
+#include <cstddef>
+#include <cstdint>
+#include <string>
+#include <unordered_map>
+#include <unordered_set>
+#include <utility>
+#include <vector>
+
+namespace rcc {
+namespace {
+
+[[nodiscard]] bool isRegisterOnly(const OperandKindSet &Kinds) noexcept {
+ OperandKindSet RegisterKind{MachineOperand::Kind::Register};
+ return Kinds == RegisterKind;
+}
+
+[[nodiscard]] bool isDefRole(RegisterOperandRole Role) noexcept {
+ return Role == RegisterOperandRole::Def || Role == RegisterOperandRole::DefUse;
+}
+
+[[nodiscard]] bool isUseRole(RegisterOperandRole Role) noexcept {
+ return Role == RegisterOperandRole::Use || Role == RegisterOperandRole::DefUse;
+}
+
+[[nodiscard]] bool formsDefUsePair(RegisterOperandRole Left, RegisterOperandRole Right) noexcept {
+ return (Left == RegisterOperandRole::Def && Right == RegisterOperandRole::Use) ||
+ (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");
+ if (Descriptor.Name.empty())
+ return makeUnexpected(ErrorCode::InvalidArgument, "instruction descriptor name is empty");
+ if (Descriptor.NumExplicitDefs > Descriptor.Operands.size()) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "instruction descriptor explicit def count is out of range");
+ }
+
+ bool SawOptional = false;
+ std::optional<std::size_t> VariadicBegin;
+ std::optional<std::size_t> VariadicEnd;
+ for (std::size_t Index = 0; Index < Descriptor.Operands.size(); ++Index) {
+ const OperandDescriptor &Operand = Descriptor.Operands[Index];
+ if (Operand.Kinds.empty()) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "operand descriptor must accept at least one operand kind");
+ }
+
+ const bool IsRegister = Operand.Kinds.contains(MachineOperand::Kind::Register);
+ if (IsRegister != (Operand.RegisterRole != RegisterOperandRole::None)) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "register-capable operand descriptor must declare a register role");
+ }
+ if (isDefRole(Operand.RegisterRole) && !isRegisterOnly(Operand.Kinds)) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "register definition descriptor must accept only registers");
+ }
+ if (!IsRegister &&
+ (Operand.RegClass || Operand.RegBank || Operand.EarlyClobber || Operand.AllowSubReg)) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "non-register operand descriptor has register constraints");
+ }
+ if (Operand.TypeConstraint) {
+ if (!isRegisterOnly(Operand.Kinds) || Operand.TypeConstraint->Kinds.empty() ||
+ Operand.TypeConstraint->Kinds.contains(LowLevelType::Kind::Invalid)) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "typed operand descriptor requires a nonempty register type set");
+ }
+ }
+ if (Operand.EarlyClobber && !isDefRole(Operand.RegisterRole)) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "early-clobber operand descriptor must define a register");
+ }
+ if (Descriptor.Variadic && Operand.Optional) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "variadic instruction descriptor cannot contain optional operands");
+ }
+ if (Operand.Optional)
+ SawOptional = true;
+ else if (SawOptional && !Operand.VariadicGroup) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "optional operand descriptors must form a trailing suffix");
+ }
+
+ if (Operand.VariadicGroup && (Operand.Optional || Operand.DefaultTiedOperand)) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "variadic operand descriptor cannot be optional or have a default tie");
+ }
+ if (Operand.VariadicGroup) {
+ if (!Descriptor.Variadic) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "non-variadic instruction descriptor has a variadic group");
+ }
+ if (!VariadicBegin)
+ VariadicBegin = Index;
+ VariadicEnd = Index + 1;
+ } else if (VariadicEnd) {
+ const bool LaterGroup =
+ std::ranges::any_of(Descriptor.Operands.begin() + static_cast<std::ptrdiff_t>(Index),
+ Descriptor.Operands.end(), [](const OperandDescriptor &Candidate) {
+ return Candidate.VariadicGroup;
+ });
+ if (LaterGroup) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "variadic operand group descriptors must be contiguous");
+ }
+ }
+ }
+
+ if (Descriptor.Variadic && !VariadicBegin) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "variadic instruction descriptor requires a variadic group");
+ }
+ if (!Descriptor.Variadic && Descriptor.MinimumVariadicRepetitions != 0) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "non-variadic instruction has a minimum variadic repetition count");
+ }
+
+ bool SawUse = false;
+ std::size_t MinimumDefs = 0;
+ for (std::size_t Index = 0; Index < Descriptor.Operands.size(); ++Index) {
+ const OperandDescriptor &Operand = Descriptor.Operands[Index];
+ const std::size_t Repetitions =
+ Operand.VariadicGroup ? Descriptor.MinimumVariadicRepetitions : 1;
+ for (std::size_t Repeat = 0; Repeat < Repetitions; ++Repeat) {
+ if (isDefRole(Operand.RegisterRole)) {
+ if (SawUse) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "register definition appears after an instruction use");
+ }
+ ++MinimumDefs;
+ } else {
+ SawUse = true;
+ }
+ }
+ }
+ if (Descriptor.NumExplicitDefs != MinimumDefs) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "instruction descriptor explicit def count does not match its shape");
+ }
+
+ for (std::size_t Index = 0; Index < Descriptor.Operands.size(); ++Index) {
+ const OperandDescriptor &Operand = Descriptor.Operands[Index];
+ if (!Operand.DefaultTiedOperand)
+ continue;
+ const std::size_t OtherIndex = *Operand.DefaultTiedOperand;
+ if (OtherIndex >= Descriptor.Operands.size() || OtherIndex == Index ||
+ Descriptor.Operands[OtherIndex].DefaultTiedOperand != Index) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "default operand tie must be reciprocal and in range");
+ }
+ if (Operand.Optional || Descriptor.Operands[OtherIndex].Optional ||
+ !isRegisterOnly(Operand.Kinds) || !isRegisterOnly(Descriptor.Operands[OtherIndex].Kinds) ||
+ !formsDefUsePair(Operand.RegisterRole, Descriptor.Operands[OtherIndex].RegisterRole)) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "default operand tie must connect required register def and use");
+ }
+ }
+
+ std::unordered_set<NamedOperandID> OperandNames;
+ for (const OperandDescriptor &Operand : Descriptor.Operands) {
+ if (Operand.Name && !OperandNames.insert(*Operand.Name).second) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "instruction descriptor has duplicate named operands");
+ }
+ }
+
+ std::unordered_set<PhysicalRegister> ImplicitDefs;
+ for (const ImplicitRegisterDescriptor &Implicit : Descriptor.ImplicitDefs) {
+ if (!Implicit.Reg.isValid() || !ImplicitDefs.insert(Implicit.Reg).second) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "implicit definitions must name unique physical registers");
+ }
+ }
+ std::unordered_set<PhysicalRegister> ImplicitUses;
+ for (const ImplicitRegisterDescriptor &Implicit : Descriptor.ImplicitUses) {
+ if (!Implicit.Reg.isValid() || Implicit.EarlyClobber ||
+ !ImplicitUses.insert(Implicit.Reg).second) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "implicit uses must name unique physical registers and cannot "
+ "early-clobber");
+ }
+ }
+
+ const bool IsBranch = Descriptor.Flags.has(InstructionFlag::Branch);
+ const bool IsTerminator = Descriptor.Flags.has(InstructionFlag::Terminator);
+ if ((IsBranch || Descriptor.Flags.has(InstructionFlag::Return) ||
+ Descriptor.Flags.has(InstructionFlag::Barrier) ||
+ Descriptor.Flags.has(InstructionFlag::IndirectBranch)) &&
+ !IsTerminator) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "branch, return, and barrier descriptors must be terminators");
+ }
+ if (Descriptor.Flags.has(InstructionFlag::IndirectBranch) && !IsBranch) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "indirect branch descriptor must also be a branch");
+ }
+
+ const bool IsPseudo = Descriptor.Flags.has(InstructionFlag::Pseudo);
+ const bool IsMeta = Descriptor.Flags.has(InstructionFlag::Meta);
+ if (IsMeta && (!IsPseudo || Descriptor.Flags.has(InstructionFlag::ProducesCode))) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "meta instruction must be a non-emitting pseudo");
+ }
+ if (!IsPseudo && Descriptor.Owner != PseudoOwner::None) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "non-pseudo instruction cannot have a pseudo owner");
+ }
+ if (IsPseudo && !IsMeta && Descriptor.Owner == PseudoOwner::None) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "executable pseudo must declare its expansion owner");
+ }
+ if (Descriptor.Flags.has(InstructionFlag::TerminalEmission) &&
+ (!IsPseudo || Descriptor.Owner != PseudoOwner::AsmPrinter ||
+ !Descriptor.Flags.has(InstructionFlag::ProducesCode))) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "terminal emission pseudo must produce code and be owned by AsmPrinter");
+ }
+ if (Descriptor.RequiresMemoryOperands && !Descriptor.Flags.has(InstructionFlag::MayLoad) &&
+ !Descriptor.Flags.has(InstructionFlag::MayStore)) {
+ return makeUnexpected(ErrorCode::InvalidArgument, "MMO requirement needs mayLoad or mayStore");
+ }
+
+ return {};
+}
+
+struct OperandShape final {
+ std::size_t VariadicBegin = 0;
+ std::size_t VariadicEnd = 0;
+ std::size_t Repetitions = 0;
+};
+
+[[nodiscard]] Expected<OperandShape> resolveOperandShape(const InstructionDescriptor &Descriptor,
+ std::size_t OperandCount) {
+ if (!Descriptor.Variadic) {
+ std::size_t RequiredCount = Descriptor.Operands.size();
+ while (RequiredCount != 0 && Descriptor.Operands[RequiredCount - 1].Optional)
+ --RequiredCount;
+ if (OperandCount < RequiredCount || OperandCount > Descriptor.Operands.size()) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "instruction has an invalid explicit operand count");
+ }
+ return OperandShape{};
+ }
+
+ const auto Begin = std::ranges::find_if(
+ Descriptor.Operands, [](const OperandDescriptor &Operand) { return Operand.VariadicGroup; });
+ const auto End = std::ranges::find_if_not(
+ Begin, Descriptor.Operands.end(),
+ [](const OperandDescriptor &Operand) { return Operand.VariadicGroup; });
+ const std::size_t VariadicBegin =
+ static_cast<std::size_t>(std::distance(Descriptor.Operands.begin(), Begin));
+ const std::size_t VariadicEnd =
+ static_cast<std::size_t>(std::distance(Descriptor.Operands.begin(), End));
+ const std::size_t GroupSize = VariadicEnd - VariadicBegin;
+ const std::size_t FixedCount = Descriptor.Operands.size() - GroupSize;
+ if (GroupSize == 0 || OperandCount < FixedCount) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "instruction has an invalid variadic operand count");
+ }
+ const std::size_t VariableCount = OperandCount - FixedCount;
+ if (VariableCount % GroupSize != 0 ||
+ VariableCount / GroupSize < Descriptor.MinimumVariadicRepetitions) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "instruction has an incomplete or undersized variadic operand group");
+ }
+ return OperandShape{VariadicBegin, VariadicEnd, VariableCount / GroupSize};
+}
+
+[[nodiscard]] const OperandDescriptor &descriptorForOperand(const InstructionDescriptor &Descriptor,
+ const OperandShape &Shape,
+ std::size_t Index) {
+ if (!Descriptor.Variadic)
+ return Descriptor.Operands[Index];
+ if (Index < Shape.VariadicBegin)
+ return Descriptor.Operands[Index];
+ const std::size_t GroupSize = Shape.VariadicEnd - Shape.VariadicBegin;
+ const std::size_t VariableCount = Shape.Repetitions * GroupSize;
+ if (Index < Shape.VariadicBegin + VariableCount) {
+ return Descriptor.Operands[Shape.VariadicBegin + (Index - Shape.VariadicBegin) % GroupSize];
+ }
+ return Descriptor.Operands[Shape.VariadicEnd + (Index - Shape.VariadicBegin - VariableCount)];
+}
+
+} // namespace
+
+Expected<std::vector<const OperandDescriptor *>>
+resolveExplicitOperandDescriptors(const InstructionDescriptor &Descriptor,
+ std::size_t OperandCount) {
+ auto Shape = resolveOperandShape(Descriptor, OperandCount);
+ if (!Shape)
+ return std::unexpected(Shape.error());
+ std::vector<const OperandDescriptor *> Result;
+ Result.reserve(OperandCount);
+ for (std::size_t Index = 0; Index < OperandCount; ++Index)
+ Result.push_back(&descriptorForOperand(Descriptor, *Shape, Index));
+ return Result;
+}
+
+OperandKindSet::OperandKindSet(std::initializer_list<MachineOperand::Kind> Kinds) noexcept {
+ for (const MachineOperand::Kind Kind : Kinds)
+ add(Kind);
+}
+
+LowLevelTypeKindSet::LowLevelTypeKindSet(std::initializer_list<LowLevelType::Kind> Kinds) noexcept {
+ for (const LowLevelType::Kind Kind : Kinds)
+ add(Kind);
+}
+
+InstructionFlags::InstructionFlags(std::initializer_list<InstructionFlag> Flags) noexcept {
+ for (const InstructionFlag Flag : Flags)
+ set(Flag);
+}
+
+Expected<InstructionSet>
+InstructionSet::create(std::vector<InstructionDescriptor> Descriptors,
+ std::vector<ImmediatePredicate> ImmediatePredicates) {
+ if (Descriptors.empty()) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "instruction set requires at least one descriptor");
+ }
+
+ InstructionSet Result(std::move(Descriptors), std::move(ImmediatePredicates));
+
+ std::unordered_set<std::string> PredicateNames;
+ for (std::size_t Index = 0; Index < Result.ImmediatePredicates.size(); ++Index) {
+ const ImmediatePredicate &Predicate = Result.ImmediatePredicates[Index];
+ if (Predicate.Name.empty() || !Predicate.Predicate) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "immediate predicate requires a name and callable");
+ }
+ if (!Result.PredicateToIndex.emplace(Predicate.ID, Index).second) {
+ return makeUnexpected(ErrorCode::InvalidArgument, "duplicate immediate predicate ID");
+ }
+ if (!PredicateNames.insert(Predicate.Name).second) {
+ return makeUnexpected(ErrorCode::InvalidArgument, "duplicate immediate predicate name");
+ }
+ }
+
+ for (std::size_t Index = 0; Index < Result.Descriptors.size(); ++Index) {
+ const InstructionDescriptor &Descriptor = Result.Descriptors[Index];
+ const auto Validation = validateDescriptorStructure(Descriptor);
+ if (!Validation)
+ return std::unexpected(Validation.error());
+ if (!Result.OpcodeToIndex.emplace(Descriptor.InstructionOpcode, Index).second)
+ return makeUnexpected(ErrorCode::InvalidArgument, "duplicate instruction opcode");
+ if (!Result.NameToIndex.emplace(Descriptor.Name, Index).second)
+ return makeUnexpected(ErrorCode::InvalidArgument, "duplicate instruction name");
+
+ for (const OperandDescriptor &Operand : Descriptor.Operands) {
+ if (Operand.ImmediatePredicate &&
+ Result.immediatePredicate(*Operand.ImmediatePredicate) == nullptr) {
+ return makeUnexpected(ErrorCode::InvalidArgument,
+ "operand descriptor references an unknown immediate predicate");
+ }
+ }
+ }
+
+ return Result;
+}
+
+const InstructionDescriptor *InstructionSet::descriptor(Opcode InstructionOpcode) const noexcept {
+ const auto Position = OpcodeToIndex.find(InstructionOpcode);
+ if (Position == OpcodeToIndex.end())
+ return nullptr;
+ return &Descriptors[Position->second];
+}
+
+const InstructionDescriptor *InstructionSet::descriptor(std::string_view Name) const noexcept {
+ const auto Position = NameToIndex.find(std::string(Name));
+ if (Position == NameToIndex.end())
+ return nullptr;
+ return &Descriptors[Position->second];
+}
+
+Expected<void> InstructionSet::validateExplicitOperands(const InstructionDescriptor &Descriptor,
+ std::span<const MachineOperand> Operands,
+ const MachineRegisterInfo &RegInfo) const {
+ auto Shape = resolveOperandShape(Descriptor, Operands.size());
+ if (!Shape)
+ return std::unexpected(Shape.error());
+
+ bool SawUse = false;
+ std::unordered_map<std::uint16_t, LowLevelType> EqualityGroups;
+ for (std::size_t Index = 0; Index < Operands.size(); ++Index) {
+ const MachineOperand &Operand = Operands[Index];
+ if (Operand.isImplicit()) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "explicit operand sequence contains an implicit register");
+ }
+ const OperandDescriptor &OperandDesc = descriptorForOperand(Descriptor, *Shape, Index);
+ const auto Validation = validateOperand(OperandDesc, Operand, RegInfo);
+ if (!Validation) {
+ return makeUnexpected(Validation.error().code(),
+ "operand " + std::to_string(Index) + " of '" + Descriptor.Name +
+ "': " + std::string(Validation.error().message()));
+ }
+ if (Operand.isRegister()) {
+ if (isDefRole(OperandDesc.RegisterRole)) {
+ if (SawUse) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "instruction register definition appears after a use");
+ }
+ } else {
+ SawUse = true;
+ }
+ } else {
+ SawUse = true;
+ }
+
+ if (!OperandDesc.TypeConstraint)
+ continue;
+ const Register *Reg = Operand.getIf<Register>();
+ if (Reg == nullptr || !Reg->isVirtual()) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "typed instruction operand must use a virtual register");
+ }
+ auto Info = RegInfo.virtualRegisterInfo(*Reg);
+ if (!Info)
+ return std::unexpected(Info.error());
+ const std::optional<LowLevelType> &RegisterType = Info->get().Type;
+ if (!RegisterType) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "typed instruction operand virtual register has no LowLevelType");
+ }
+ const LowLevelType Type = RegisterType.value();
+ if (!OperandDesc.TypeConstraint->Kinds.contains(Type.kind())) {
+ return makeUnexpected(
+ ErrorCode::VerificationError,
+ "instruction operand LowLevelType kind is not permitted by its descriptor");
+ }
+ if (OperandDesc.TypeConstraint->EqualityGroup) {
+ const auto [Position, Inserted] =
+ EqualityGroups.emplace(*OperandDesc.TypeConstraint->EqualityGroup, Type);
+ if (!Inserted && Position->second != Type) {
+ return makeUnexpected(
+ ErrorCode::VerificationError,
+ "instruction operands in one LowLevelType group have different types");
+ }
+ }
+ }
+ if (Descriptor.VerifyExplicitOperands != nullptr)
+ return Descriptor.VerifyExplicitOperands(Descriptor.InstructionOpcode, Operands, RegInfo);
+ return {};
+}
+
+Expected<void> InstructionSet::validateExplicitOperands(Opcode InstructionOpcode,
+ std::span<const MachineOperand> Operands,
+ const MachineRegisterInfo &RegInfo) const {
+ const InstructionDescriptor *Descriptor = descriptor(InstructionOpcode);
+ if (Descriptor == nullptr) {
+ return makeUnexpected(ErrorCode::VerificationError, "instruction opcode is not registered");
+ }
+ 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 {
+ if (!Descriptor.Kinds.contains(Operand.kind())) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "instruction operand kind does not match its descriptor");
+ }
+ if (!Descriptor.AllowAnyTargetFlags &&
+ (Operand.targetFlags() & ~Descriptor.AllowedTargetFlags) != 0) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "instruction operand uses unregistered target flags");
+ }
+
+ if (Operand.isRegister()) {
+ const bool IsDef = Operand.isDef();
+ const bool IsUse = Operand.isUse();
+ const bool ExpectedDef = isDefRole(Descriptor.RegisterRole);
+ const bool ExpectedUse = isUseRole(Descriptor.RegisterRole);
+ if (Descriptor.RegisterRole == RegisterOperandRole::Any) {
+ if (!IsDef && !IsUse) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "unclassified register operand must define or use its register");
+ }
+ } else if (IsDef != ExpectedDef || IsUse != ExpectedUse) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "register operand def/use role does not match its descriptor");
+ }
+ if (Operand.registerFlags().has(RegisterOperandFlag::EarlyClobber) != Descriptor.EarlyClobber) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "register operand early-clobber does not match its descriptor");
+ }
+ if (!Descriptor.AllowSubReg && Operand.subReg().value() != 0) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "register operand has a disallowed subregister index");
+ }
+
+ const Register *Reg = Operand.getIf<Register>();
+ if (Reg == nullptr) {
+ return makeUnexpected(ErrorCode::InternalError, "register operand has inconsistent storage");
+ }
+ if (Reg->isVirtual()) {
+ auto Info = RegInfo.virtualRegisterInfo(*Reg);
+ if (!Info)
+ return std::unexpected(Info.error());
+ }
+ }
+
+ if (Descriptor.ImmediatePredicate && Operand.kind() == MachineOperand::Kind::Immediate) {
+ const ImmediatePredicate *Predicate = immediatePredicate(*Descriptor.ImmediatePredicate);
+ if (Predicate == nullptr || !Predicate->Predicate(Operand)) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "instruction operand fails its immediate predicate");
+ }
+ }
+
+ return {};
+}
+
+Expected<void> InstructionSet::validateInstruction(const MachineInstr &Instruction,
+ const MachineRegisterInfo &RegInfo) const {
+ const InstructionDescriptor *Descriptor = descriptor(Instruction.opcode());
+ if (Descriptor == nullptr)
+ return makeUnexpected(ErrorCode::VerificationError, "instruction opcode is not registered");
+
+ const auto AllOperands = Instruction.operands();
+ std::size_t ExplicitCount = 0;
+ while (ExplicitCount < AllOperands.size() && !AllOperands[ExplicitCount].isImplicit())
+ ++ExplicitCount;
+ for (std::size_t Index = ExplicitCount; Index < AllOperands.size(); ++Index) {
+ if (!AllOperands[Index].isImplicit()) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "explicit operand appears after implicit operands");
+ }
+ }
+
+ const auto ExplicitValidation =
+ validateExplicitOperands(*Descriptor, AllOperands.first(ExplicitCount), RegInfo);
+ 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];
+ if (!Operand.tiedOperandIndex())
+ continue;
+ const std::size_t OtherIndex = *Operand.tiedOperandIndex();
+ if (OtherIndex >= ExplicitCount || AllOperands[OtherIndex].tiedOperandIndex() != Index) {
+ return makeUnexpected(ErrorCode::VerificationError,
+ "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});
+ }
+
+ auto TieValidation = validateOperandTies(*Descriptor, AllOperands.first(ExplicitCount), Ties);
+ if (!TieValidation)
+ return std::unexpected(TieValidation.error());
+
+ 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,
+ "implicit register operand cannot participate in a tie");
+ }
+ ImplicitOperands.push_back(&AllOperands[Index]);
+ }
+
+ auto ImplicitValidation = validateImplicitOperands(*Descriptor, ImplicitOperands);
+ if (!ImplicitValidation)
+ return std::unexpected(ImplicitValidation.error());
+ auto MemoryValidation = validateMemOperands(*Descriptor, Instruction.memOperands());
+ if (!MemoryValidation)
+ return std::unexpected(MemoryValidation.error());
+ if (Descriptor->VerifyInstructionSemantics != nullptr)
+ return Descriptor->VerifyInstructionSemantics(Instruction, RegInfo);
+ return {};
+}
+
+Expected<std::vector<MachineOperand>>
+InstructionSet::createDefaultImplicitOperands(const InstructionDescriptor &Descriptor) const {
+ std::vector<MachineOperand> Result;
+ Result.reserve(Descriptor.ImplicitDefs.size() + Descriptor.ImplicitUses.size());
+
+ auto AppendImplicit = [&Result](const ImplicitRegisterDescriptor &Implicit,
+ bool IsDef) -> Expected<void> {
+ RegisterOperandFlags Flags{
+ IsDef ? RegisterOperandFlag::Define : RegisterOperandFlag::Use,
+ RegisterOperandFlag::Implicit,
+ };
+ if (Implicit.EarlyClobber)
+ Flags.set(RegisterOperandFlag::EarlyClobber);
+ if (Implicit.Renamable)
+ Flags.set(RegisterOperandFlag::Renamable);
+
+ auto Operand = MachineOperand::registerOperand(Register::fromPhysical(Implicit.Reg), Flags);
+ if (!Operand)
+ return std::unexpected(Operand.error());
+ Result.push_back(std::move(*Operand));
+ return {};
+ };
+
+ for (const ImplicitRegisterDescriptor &Implicit : Descriptor.ImplicitDefs) {
+ auto AppendResult = AppendImplicit(Implicit, true);
+ if (!AppendResult)
+ return std::unexpected(AppendResult.error());
+ }
+ for (const ImplicitRegisterDescriptor &Implicit : Descriptor.ImplicitUses) {
+ auto AppendResult = AppendImplicit(Implicit, false);
+ if (!AppendResult)
+ return std::unexpected(AppendResult.error());
+ }
+ return Result;
+}
+
+const ImmediatePredicate *
+InstructionSet::immediatePredicate(ImmediatePredicateID ID) const noexcept {
+ const auto Position = PredicateToIndex.find(ID);
+ if (Position == PredicateToIndex.end())
+ return nullptr;
+ return &ImmediatePredicates[Position->second];
+}
+
+} // namespace rcc