From fd8770b01a7a3ae85ba85fb43b21803c88593ad7 Mon Sep 17 00:00:00 2001 From: Richard-Qin-X Date: Sat, 1 Aug 2026 12:05:36 +0800 Subject: [CodeGen] Add machine instruction descriptors Introduce an immutable instruction registry with exact operand, implicit register, tie, pseudo lifecycle, and memory constraints. Validate descriptor tables before publication and use the same rules to verify MachineInstr instances. --- .../include/rcc/CodeGen/InstructionDescriptor.h | 264 +++++++++++++++++++++ 1 file changed, 264 insertions(+) create mode 100644 backend/include/rcc/CodeGen/InstructionDescriptor.h (limited to 'backend/include/rcc/CodeGen/InstructionDescriptor.h') diff --git a/backend/include/rcc/CodeGen/InstructionDescriptor.h b/backend/include/rcc/CodeGen/InstructionDescriptor.h new file mode 100644 index 0000000..c6049b9 --- /dev/null +++ b/backend/include/rcc/CodeGen/InstructionDescriptor.h @@ -0,0 +1,264 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_INSTRUCTIONDESCRIPTOR_H +#define RCC_CODEGEN_INSTRUCTIONDESCRIPTOR_H + +#include "rcc/CodeGen/MachineIRTypes.h" +#include "rcc/CodeGen/MachineOperand.h" +#include "rcc/CodeGen/Register.h" +#include "rcc/Support/Error.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace rcc { + +class MachineInstr; +class MachineRegisterInfo; + +class OperandKindSet final { +public: + static_assert(static_cast(MachineOperand::Kind::Count) <= 32); + + constexpr OperandKindSet() noexcept = default; + + OperandKindSet(std::initializer_list Kinds) noexcept; + + [[nodiscard]] constexpr bool empty() const noexcept { + return Mask == 0; + } + + [[nodiscard]] constexpr bool contains(MachineOperand::Kind Kind) const noexcept { + const auto Index = static_cast(Kind); + if (Index >= static_cast(MachineOperand::Kind::Count)) + return false; + return (Mask & (std::uint32_t{1} << Index)) != 0; + } + + constexpr void add(MachineOperand::Kind Kind) noexcept { + const auto Index = static_cast(Kind); + if (Index < static_cast(MachineOperand::Kind::Count)) + Mask |= std::uint32_t{1} << Index; + } + + [[nodiscard]] constexpr std::uint32_t rawValue() const noexcept { + return Mask; + } + + friend constexpr bool operator==(OperandKindSet Left, OperandKindSet Right) noexcept = default; + +private: + std::uint32_t Mask = 0; +}; + +enum class RegisterOperandRole : std::uint8_t { + None, + Def, + Use, + DefUse, +}; + +enum class MemoryOperandRole : std::uint8_t { + None, + Address, + Value, + Size, + Ordering, +}; + +enum class InstructionFlag : std::uint8_t { + Terminator, + Branch, + IndirectBranch, + Call, + Return, + Barrier, + MayLoad, + MayStore, + HasSideEffects, + Pseudo, + Meta, + AllowedPreLegalizer, + ProducesCode, + Commutable, + Rematerializable, + Predicable, + NotDuplicable, + Convergent, + UnmodeledSideEffects, + Phi, + Generic, + TerminalEmission, + Count, +}; + +class InstructionFlags final { +public: + static_assert(static_cast(InstructionFlag::Count) <= 32); + + constexpr InstructionFlags() noexcept = default; + + InstructionFlags(std::initializer_list Flags) noexcept; + + [[nodiscard]] constexpr bool has(InstructionFlag Flag) const noexcept { + const auto Index = static_cast(Flag); + if (Index >= static_cast(InstructionFlag::Count)) + return false; + return (Mask & (std::uint32_t{1} << Index)) != 0; + } + + constexpr void set(InstructionFlag Flag) noexcept { + const auto Index = static_cast(Flag); + if (Index < static_cast(InstructionFlag::Count)) + Mask |= std::uint32_t{1} << Index; + } + + constexpr void clear(InstructionFlag Flag) noexcept { + const auto Index = static_cast(Flag); + if (Index < static_cast(InstructionFlag::Count)) + Mask &= ~(std::uint32_t{1} << Index); + } + + [[nodiscard]] constexpr std::uint32_t rawValue() const noexcept { + return Mask; + } + + friend constexpr bool operator==(InstructionFlags Left, + InstructionFlags Right) noexcept = default; + +private: + std::uint32_t Mask = 0; +}; + +enum class PseudoOwner : std::uint8_t { + None, + Legalizer, + RegisterBankSelect, + InstructionSelect, + PhiElimination, + TwoAddressRewrite, + RegisterAllocator, + PrePEIExpansion, + PrologueEpilogueInsertion, + PostRAExpansion, + Finalization, + AsmPrinter, +}; + +enum class PipelineStage : std::uint8_t { + MachineIRInput, + Legalized, + RegBankSelected, + Selected, + SSAEliminated, + TiedOpsRewritten, + RegisterAllocated, + FrameFinalized, + PostRAFinalized, + Emission, +}; + +struct OperandDescriptor final { + OperandKindSet Kinds; + RegisterOperandRole RegisterRole = RegisterOperandRole::None; + std::optional RegClass; + std::optional RegBank; + std::optional ImmediatePredicate; + MemoryOperandRole MemoryRole = MemoryOperandRole::None; + std::optional DefaultTiedOperand; + bool EarlyClobber = false; + bool Optional = false; + bool VariadicGroup = false; + bool AllowSubReg = false; + std::optional Name; + std::optional TargetOperandType; + std::uint32_t AllowedTargetFlags = 0; +}; + +struct ImplicitRegisterDescriptor final { + PhysicalRegister Reg; + bool EarlyClobber = false; + bool Renamable = false; + + friend bool operator==(const ImplicitRegisterDescriptor &Left, + const ImplicitRegisterDescriptor &Right) = default; +}; + +struct InstructionDescriptor final { + Opcode InstructionOpcode; + std::string Name; + std::vector Operands; + std::size_t NumExplicitDefs = 0; + bool Variadic = false; + std::vector ImplicitDefs; + std::vector ImplicitUses; + bool AllowExtraImplicitOperands = false; + InstructionFlags Flags; + SchedulingClassID SchedulingClass; + EncodingCategoryID EncodingCategory; + AssemblyFormatID AssemblyFormat; + bool RequiresMemoryOperands = false; + PseudoOwner Owner = PseudoOwner::None; + PipelineStage Deadline = PipelineStage::Emission; +}; + +struct ImmediatePredicate final { + ImmediatePredicateID ID; + std::string Name; + std::function Predicate; +}; + +class InstructionSet final { +public: + [[nodiscard]] static Expected + create(std::vector Descriptors, + std::vector ImmediatePredicates = {}); + + [[nodiscard]] const InstructionDescriptor *descriptor(Opcode InstructionOpcode) const noexcept; + [[nodiscard]] const InstructionDescriptor *descriptor(std::string_view Name) const noexcept; + [[nodiscard]] std::span descriptors() const noexcept { + return Descriptors; + } + + [[nodiscard]] Expected validateExplicitOperands(Opcode InstructionOpcode, + std::span Operands, + const MachineRegisterInfo &RegInfo) const; + [[nodiscard]] Expected validateInstruction(const MachineInstr &Instruction, + const MachineRegisterInfo &RegInfo) const; + + [[nodiscard]] Expected> + createDefaultImplicitOperands(const InstructionDescriptor &Descriptor) const; + +private: + InstructionSet(std::vector Descriptors, + std::vector ImmediatePredicates) + : Descriptors(std::move(Descriptors)), ImmediatePredicates(std::move(ImmediatePredicates)) {} + + [[nodiscard]] const ImmediatePredicate * + immediatePredicate(ImmediatePredicateID ID) const noexcept; + [[nodiscard]] Expected validateExplicitOperands(const InstructionDescriptor &Descriptor, + std::span Operands, + const MachineRegisterInfo &RegInfo) const; + [[nodiscard]] Expected validateOperand(const OperandDescriptor &Descriptor, + const MachineOperand &Operand, + const MachineRegisterInfo &RegInfo) const; + + std::vector Descriptors; + std::vector ImmediatePredicates; + std::unordered_map OpcodeToIndex; + std::unordered_map NameToIndex; + std::unordered_map PredicateToIndex; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_INSTRUCTIONDESCRIPTOR_H -- cgit v1.2.3