diff options
Diffstat (limited to 'backend/include')
183 files changed, 20587 insertions, 0 deletions
diff --git a/backend/include/rcc/ADT/APFloat.h b/backend/include/rcc/ADT/APFloat.h new file mode 100644 index 0000000..9b56a19 --- /dev/null +++ b/backend/include/rcc/ADT/APFloat.h @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_ADT_APFLOAT_H +#define RCC_ADT_APFLOAT_H + +#include "rcc/Support/Error.h" + +#include <array> +#include <cstdint> +#include <optional> +#include <span> +#include <string> +#include <string_view> + +namespace rcc { + +enum class FloatSemantics : std::uint8_t { + IEEEHalf, + BFloat, + IEEESingle, + IEEEDouble, + IEEEQuad, + PPCDoubleDouble, + Float8E5M2, + Float8E5M2FNUZ, + Float8E4M3, + Float8E4M3FN, + Float8E4M3FNUZ, + Float8E4M3B11FNUZ, + Float8E3M4, + FloatTF32, + Float8E8M0FNU, + Float6E3M2FN, + Float6E2M3FN, + Float4E2M1FN, + X87DoubleExtended, + Count, +}; + +[[nodiscard]] std::optional<std::uint32_t> +getFloatSemanticsBitWidth(FloatSemantics Semantics) noexcept; +[[nodiscard]] std::string_view getFloatSemanticsSpelling(FloatSemantics Semantics) noexcept; + +class APFloat final { +public: + [[nodiscard]] static Expected<APFloat> create(FloatSemantics Semantics, + std::span<const std::uint64_t> Words); + [[nodiscard]] static Expected<APFloat> parse(FloatSemantics Semantics, std::string_view Text); + + [[nodiscard]] FloatSemantics semantics() const noexcept { + return Semantics; + } + + [[nodiscard]] std::uint32_t bitWidth() const noexcept { + return getFloatSemanticsBitWidth(Semantics).value_or(0); + } + + [[nodiscard]] std::span<const std::uint64_t> words() const noexcept { + return std::span(Words).first(WordCount); + } + + [[nodiscard]] std::string toString() const; + + friend bool operator==(const APFloat &Left, const APFloat &Right) = default; + +private: + APFloat(FloatSemantics Semantics, std::array<std::uint64_t, 2> Words, + std::uint8_t WordCount) noexcept + : Semantics(Semantics), Words(Words), WordCount(WordCount) {} + + FloatSemantics Semantics; + std::array<std::uint64_t, 2> Words; + std::uint8_t WordCount; +}; + +} // namespace rcc + +#endif // RCC_ADT_APFLOAT_H diff --git a/backend/include/rcc/ADT/APInt.h b/backend/include/rcc/ADT/APInt.h new file mode 100644 index 0000000..dc48d16 --- /dev/null +++ b/backend/include/rcc/ADT/APInt.h @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_ADT_APINT_H +#define RCC_ADT_APINT_H + +#include "rcc/Support/Error.h" + +#include <cstddef> +#include <cstdint> +#include <span> +#include <string> +#include <string_view> +#include <utility> +#include <vector> + +namespace rcc { + +class APInt final { +public: + static constexpr std::uint32_t MaxBitWidth = std::uint32_t{1} << 23U; + + APInt() = default; + explicit APInt(std::uint64_t Value); + + [[nodiscard]] static Expected<APInt> create(std::uint32_t BitWidth, + std::span<const std::uint64_t> Words); + [[nodiscard]] static Expected<APInt> fromUnsigned(std::uint32_t BitWidth, std::uint64_t Value); + [[nodiscard]] static Expected<APInt> fromSigned(std::uint32_t BitWidth, std::int64_t Value); + [[nodiscard]] static Expected<APInt> getLowBitsSet(std::uint32_t BitWidth, + std::uint32_t SetBitCount); + [[nodiscard]] static Expected<APInt> parseDecimal(std::uint32_t BitWidth, std::string_view Text); + [[nodiscard]] static Expected<APInt> parseLLVMHex(std::uint32_t BitWidth, std::string_view Text); + + [[nodiscard]] std::uint32_t bitWidth() const noexcept { + return BitWidth; + } + + [[nodiscard]] std::span<const std::uint64_t> words() const noexcept { + return Words; + } + + [[nodiscard]] bool isZero() const noexcept; + [[nodiscard]] bool isNegative() const noexcept; + [[nodiscard]] std::size_t activeBitWidth() const noexcept; + [[nodiscard]] std::size_t countrZero() const noexcept; + [[nodiscard]] std::size_t countlOne() const noexcept; + [[nodiscard]] bool isAllOnes() const noexcept; + [[nodiscard]] bool testBit(std::size_t Bit) const noexcept; + [[nodiscard]] std::uint64_t word(std::size_t Index) const noexcept; + [[nodiscard]] Expected<APInt> bitwiseAnd(const APInt &Other) const; + [[nodiscard]] Expected<APInt> bitwiseOr(const APInt &Other) const; + [[nodiscard]] Expected<APInt> bitwiseXor(const APInt &Other) const; + [[nodiscard]] Expected<APInt> bitwiseNot() const; + [[nodiscard]] Expected<APInt> trunc(std::uint32_t NewBitWidth) const; + [[nodiscard]] Expected<APInt> zext(std::uint32_t NewBitWidth) const; + [[nodiscard]] Expected<APInt> sext(std::uint32_t NewBitWidth) const; + [[nodiscard]] Expected<APInt> logicalShiftRight(std::size_t Amount) const; + [[nodiscard]] int compare(const APInt &Other) const noexcept; + [[nodiscard]] int compareToShifted(const APInt &Other, std::size_t OtherShift) const noexcept; + + void multiplySmall(std::uint32_t Multiplier); + void addSmall(std::uint32_t Value); + void setBit(std::size_t Bit); + void clearBit(std::size_t Bit) noexcept; + void shiftRight(std::size_t Amount) noexcept; + [[nodiscard]] APInt shiftedLeft(std::size_t Amount) const; + void subtract(const APInt &Other) noexcept; + + [[nodiscard]] std::string toUnsignedDecimal() const; + [[nodiscard]] std::string toSignedDecimal() const; + [[nodiscard]] Expected<void> verify() const; + + friend bool operator==(const APInt &Left, const APInt &Right) = default; + +private: + APInt(std::uint32_t BitWidth, std::vector<std::uint64_t> Words) + : BitWidth(BitWidth), Words(std::move(Words)) {} + + void normalizeMagnitude() noexcept; + + std::uint32_t BitWidth = 0; + std::vector<std::uint64_t> Words; +}; + +} // namespace rcc + +#endif // RCC_ADT_APINT_H diff --git a/backend/include/rcc/AsmParser/LLParser.h b/backend/include/rcc/AsmParser/LLParser.h new file mode 100644 index 0000000..e15a006 --- /dev/null +++ b/backend/include/rcc/AsmParser/LLParser.h @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_ASMPARSER_LLPARSER_H +#define RCC_ASMPARSER_LLPARSER_H + +#include "rcc/IR/Constants.h" +#include "rcc/Support/Error.h" + +#include <cstddef> +#include <span> +#include <string_view> + +namespace rcc { + +struct LLNamedTypeDefinition final { + std::string_view Name; + std::string_view Definition; + bool NumericName = false; +}; + +struct LLParserOptions final { + std::span<const LLNamedTypeDefinition> NamedTypes; + std::size_t MaximumDepth = 1024; + std::size_t MaximumNodes = std::size_t{1} << 24U; + std::size_t MaximumTokenLength = std::size_t{1} << 24U; +}; + +[[nodiscard]] Expected<GenericConstantData> parseLLVMConstant(std::string_view Text, + LLParserOptions Options = {}); + +[[nodiscard]] Expected<GenericConstantType> parseLLVMType(std::string_view Text, + LLParserOptions Options = {}); + +} // namespace rcc + +#endif // RCC_ASMPARSER_LLPARSER_H diff --git a/backend/include/rcc/CodeGen/AsmPrinter.h b/backend/include/rcc/CodeGen/AsmPrinter.h new file mode 100644 index 0000000..4145093 --- /dev/null +++ b/backend/include/rcc/CodeGen/AsmPrinter.h @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_ASMPRINTER_H +#define RCC_CODEGEN_ASMPRINTER_H + +#include "rcc/IR/DataLayout.h" +#include "rcc/Support/Error.h" + +#include <cstddef> +#include <cstdint> +#include <utility> + +namespace rcc { + +class CompilationUnit; +class GlobalInitializer; +class MachineFunction; +class MachineJumpTableEntry; +class MachineMCContext; +struct GenericConstantData; +class TargetConstant; +class TargetDescription; +class TargetLoweringObjectFile; +class TargetMCInstLowering; + +namespace mc { +class MCStreamer; +} // namespace mc + +struct AsmPrinterOptions final { + bool EmitFunctionDirectives = true; + bool EmitCFI = true; + bool EmitBlockLabels = true; + bool EmitInstructionSymbols = true; + bool VerifyInput = true; +}; + +class AsmPrinter { +public: + AsmPrinter(const TargetDescription &Target, const TargetMCInstLowering &Lowering, + const TargetLoweringObjectFile &ObjectFile, DataLayout Layout, + MachineMCContext &Context, mc::MCStreamer &Streamer, + AsmPrinterOptions Options = {}) noexcept + : Target(&Target), Lowering(&Lowering), ObjectFile(&ObjectFile), Layout(std::move(Layout)), + Context(&Context), Streamer(&Streamer), Options(Options) {} + virtual ~AsmPrinter() = default; + + [[nodiscard]] Expected<void> emitCompilationUnit(const CompilationUnit &Unit); + [[nodiscard]] Expected<void> emitFunction(const MachineFunction &Function); + +protected: + [[nodiscard]] virtual Expected<void> emitStartOfAsmFile(); + [[nodiscard]] virtual Expected<void> emitEndOfAsmFile(); + [[nodiscard]] virtual Expected<void> emitTargetConstant(const TargetConstant &Constant); + [[nodiscard]] virtual Expected<void> emitTargetJumpTable(const MachineFunction &Function, + const MachineJumpTableEntry &Entry); + + [[nodiscard]] MachineMCContext &machineContext() const noexcept { + return *Context; + } + + [[nodiscard]] mc::MCStreamer &streamer() const noexcept { + return *Streamer; + } + +private: + [[nodiscard]] Expected<void> verifyFunction(const MachineFunction &Function) const; + [[nodiscard]] Expected<void> emitGlobalObjects(const CompilationUnit &Unit); + [[nodiscard]] Expected<void> emitFunctionImpl(const MachineFunction &Function); + [[nodiscard]] Expected<void> emitFunctionBody(const MachineFunction &Function); + [[nodiscard]] Expected<void> emitConstantPool(const MachineFunction &Function); + [[nodiscard]] Expected<void> emitJumpTables(const MachineFunction &Function); + [[nodiscard]] Expected<void> emitGenericConstant(const GenericConstantData &Constant, + std::uint64_t AllocationSize, + std::size_t Depth = 0); + [[nodiscard]] Expected<void> emitGlobalInitializer(const GlobalInitializer &Initializer, + std::size_t Depth = 0); + + const TargetDescription *Target; + const TargetMCInstLowering *Lowering; + const TargetLoweringObjectFile *ObjectFile; + DataLayout Layout; + MachineMCContext *Context; + mc::MCStreamer *Streamer; + AsmPrinterOptions Options; + bool EmissionStarted = false; + bool EmissionFinished = false; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_ASMPRINTER_H diff --git a/backend/include/rcc/CodeGen/CFIInstrInserter.h b/backend/include/rcc/CodeGen/CFIInstrInserter.h new file mode 100644 index 0000000..c6504fc --- /dev/null +++ b/backend/include/rcc/CodeGen/CFIInstrInserter.h @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_CFIINSTRINSERTER_H +#define RCC_CODEGEN_CFIINSTRINSERTER_H + +#include "rcc/CodeGen/MachinePassManager.h" + +namespace rcc { + +class TargetFrameLowering; + +class CFIInstrInserterPass final : public MachineFunctionPass { +public: + explicit CFIInstrInserterPass(const TargetFrameLowering &FrameLowering) noexcept + : FrameLowering(&FrameLowering) {} + + [[nodiscard]] const MachinePassContract &contract() const noexcept override; + [[nodiscard]] Expected<MachinePassResult> run(MachineFunction &Function, + const TargetDescription &Target, + MachineFunctionAnalysisManager &Analyses) override; + +private: + const TargetFrameLowering *FrameLowering = nullptr; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_CFIINSTRINSERTER_H diff --git a/backend/include/rcc/CodeGen/CSEConfigBase.h b/backend/include/rcc/CodeGen/CSEConfigBase.h new file mode 100644 index 0000000..68aa26e --- /dev/null +++ b/backend/include/rcc/CodeGen/CSEConfigBase.h @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_CSECONFIGBASE_H +#define RCC_CODEGEN_CSECONFIGBASE_H + +#include "rcc/CodeGen/MachineIRTypes.h" + +namespace rcc { + +class CSEConfigBase { +public: + virtual ~CSEConfigBase() = default; + + [[nodiscard]] virtual bool shouldCSEOpcode(Opcode) const noexcept { + return false; + } +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_CSECONFIGBASE_H diff --git a/backend/include/rcc/CodeGen/CommandFlags.h b/backend/include/rcc/CodeGen/CommandFlags.h new file mode 100644 index 0000000..aa7ed0d --- /dev/null +++ b/backend/include/rcc/CodeGen/CommandFlags.h @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_COMMANDFLAGS_H +#define RCC_CODEGEN_COMMANDFLAGS_H + +#include "rcc/Support/Error.h" + +#include <cstddef> +#include <cstdint> +#include <optional> +#include <span> +#include <string> +#include <string_view> +#include <vector> + +namespace rcc { + +enum class RelocationModel : std::uint8_t { + Static, + PositionIndependent, +}; + +enum class CodeModel : std::uint8_t { + Small, + Medium, + Large, +}; + +enum class OptimizationLevel : std::uint8_t { + O0, + O1, + O2, + O3, + Os, + Oz, +}; + +enum class CodeGenFileType : std::uint8_t { + AssemblyFile, + ObjectFile, + Null, +}; + +enum class GlobalISelAbortMode : std::uint8_t { + Disable, + Enable, + DisableWithDiagnostic, +}; + +struct CodeGenCommandFlags final { + std::optional<std::string> Triple; + std::optional<std::string> CPU; + std::optional<std::string> TuneCPU; + std::optional<std::string> ABI; + std::vector<std::string> FeatureOverrides; + std::optional<RelocationModel> Relocation; + std::optional<CodeModel> Code; + std::optional<OptimizationLevel> Optimization; + CodeGenFileType FileType = CodeGenFileType::AssemblyFile; + bool FileTypeWasSpecified = false; +}; + +[[nodiscard]] std::string_view spelling(RelocationModel Value) noexcept; +[[nodiscard]] std::string_view spelling(CodeModel Value) noexcept; +[[nodiscard]] std::string_view spelling(OptimizationLevel Value) noexcept; +[[nodiscard]] std::string_view spelling(CodeGenFileType Value) noexcept; + +[[nodiscard]] Expected<RelocationModel> parseRelocationModel(std::string_view Value); +[[nodiscard]] Expected<CodeModel> parseCodeModel(std::string_view Value); +[[nodiscard]] Expected<OptimizationLevel> parseOptimizationLevel(std::string_view Value); +[[nodiscard]] Expected<CodeGenFileType> parseCodeGenFileType(std::string_view Value); +[[nodiscard]] Expected<GlobalISelAbortMode> parseGlobalISelAbortMode(std::string_view Value); +[[nodiscard]] Expected<bool> parseCodeGenCommandFlag(std::span<const std::string_view> Arguments, + std::size_t &Index, + CodeGenCommandFlags &Flags); + +} // namespace rcc + +#endif // RCC_CODEGEN_COMMANDFLAGS_H diff --git a/backend/include/rcc/CodeGen/CompilationUnit.h b/backend/include/rcc/CodeGen/CompilationUnit.h new file mode 100644 index 0000000..14fd86f --- /dev/null +++ b/backend/include/rcc/CodeGen/CompilationUnit.h @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_COMPILATIONUNIT_H +#define RCC_CODEGEN_COMPILATIONUNIT_H + +#include "rcc/CodeGen/GlobalObject.h" +#include "rcc/CodeGen/StableID.h" +#include "rcc/Support/Error.h" + +#include <cstddef> +#include <functional> +#include <memory> +#include <string> +#include <string_view> +#include <unordered_map> +#include <vector> + +namespace rcc { + +class MachineFunction; + +class CompilationUnit final { +public: + CompilationUnit(); + CompilationUnit(const CompilationUnit &) = delete; + CompilationUnit &operator=(const CompilationUnit &) = delete; + CompilationUnit(CompilationUnit &&) noexcept; + CompilationUnit &operator=(CompilationUnit &&) noexcept; + ~CompilationUnit(); + + [[nodiscard]] Expected<MachineFunction *> createMachineFunction(std::string Name); + [[nodiscard]] Expected<GlobalObjectDesc *> addGlobalObject(GlobalObjectDesc Object); + + [[nodiscard]] std::size_t functionCount() const noexcept { + return Functions.size(); + } + + [[nodiscard]] MachineFunction *functionAt(std::size_t Index) noexcept; + [[nodiscard]] const MachineFunction *functionAt(std::size_t Index) const noexcept; + [[nodiscard]] MachineFunction *function(std::string_view Name) noexcept; + [[nodiscard]] const MachineFunction *function(std::string_view Name) const noexcept; + [[nodiscard]] MachineFunction *function(MachineFunctionStableID ID) noexcept; + [[nodiscard]] const MachineFunction *function(MachineFunctionStableID ID) const noexcept; + + [[nodiscard]] std::size_t globalObjectCount() const noexcept { + return GlobalObjects.size(); + } + + [[nodiscard]] GlobalObjectDesc *globalObjectAt(std::size_t Index) noexcept; + [[nodiscard]] const GlobalObjectDesc *globalObjectAt(std::size_t Index) const noexcept; + [[nodiscard]] GlobalObjectDesc *globalObject(std::string_view Name) noexcept; + [[nodiscard]] const GlobalObjectDesc *globalObject(std::string_view Name) const noexcept; + + [[nodiscard]] Expected<void> verify() const; + +private: + struct StringHash final { + using is_transparent = void; // NOLINT(readability-identifier-naming) + + [[nodiscard]] std::size_t operator()(std::string_view Value) const noexcept { + return std::hash<std::string_view>{}(Value); + } + }; + + std::vector<std::unique_ptr<MachineFunction>> Functions; + std::unordered_map<std::string, std::size_t, StringHash, std::equal_to<>> FunctionNameToIndex; + std::vector<std::unique_ptr<GlobalObjectDesc>> GlobalObjects; + std::unordered_map<std::string, std::size_t, StringHash, std::equal_to<>> GlobalNameToIndex; + StableIDGenerator<MachineFunctionStableID> FunctionIDGenerator; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_COMPILATIONUNIT_H diff --git a/backend/include/rcc/CodeGen/FinalizeISel.h b/backend/include/rcc/CodeGen/FinalizeISel.h new file mode 100644 index 0000000..c3121ec --- /dev/null +++ b/backend/include/rcc/CodeGen/FinalizeISel.h @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_FINALIZEISEL_H +#define RCC_CODEGEN_FINALIZEISEL_H + +#include "rcc/CodeGen/MachinePassManager.h" + +namespace rcc { + +class MachineBasicBlock; +class MachineInstr; + +class TargetFinalizeISelInfo { +public: + virtual ~TargetFinalizeISelInfo() = default; + + [[nodiscard]] virtual Expected<MachineBasicBlock *> + emitInstrWithCustomInserter(MachineInstr &Instruction, MachineBasicBlock &Block, + MachineFunction &Function, const TargetDescription &Target) const = 0; +}; + +class FinalizeISelPass final : public MachineFunctionPass { +public: + explicit FinalizeISelPass(const TargetFinalizeISelInfo &Info) noexcept : Info(&Info) {} + + [[nodiscard]] const MachinePassContract &contract() const noexcept override; + [[nodiscard]] Expected<MachinePassResult> run(MachineFunction &Function, + const TargetDescription &Target, + MachineFunctionAnalysisManager &Analyses) override; + +private: + const TargetFinalizeISelInfo *Info; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_FINALIZEISEL_H diff --git a/backend/include/rcc/CodeGen/FrameIndexElimination.h b/backend/include/rcc/CodeGen/FrameIndexElimination.h new file mode 100644 index 0000000..df67479 --- /dev/null +++ b/backend/include/rcc/CodeGen/FrameIndexElimination.h @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_FRAMEINDEXELIMINATION_H +#define RCC_CODEGEN_FRAMEINDEXELIMINATION_H + +#include "rcc/Support/Error.h" + +namespace rcc { + +class MachineFunction; +class RegisterScavenger; +class TargetDescription; +class TargetFrameLowering; + +[[nodiscard]] Expected<bool> eliminateFrameIndices(MachineFunction &Function, + const TargetDescription &Target, + const TargetFrameLowering &FrameLowering, + RegisterScavenger &Scavenger); + +} // namespace rcc + +#endif // RCC_CODEGEN_FRAMEINDEXELIMINATION_H diff --git a/backend/include/rcc/CodeGen/GenericOpcode.def b/backend/include/rcc/CodeGen/GenericOpcode.def new file mode 100644 index 0000000..8caaf7b --- /dev/null +++ b/backend/include/rcc/CodeGen/GenericOpcode.def @@ -0,0 +1,336 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +// This file is intentionally included multiple times. Define +// RCC_CORE_OPCODE and RCC_GENERIC_OPCODE before including it. + +RCC_CORE_OPCODE(PHI) +RCC_CORE_OPCODE(INLINEASM) +RCC_CORE_OPCODE(INLINEASM_BR) +RCC_CORE_OPCODE(CFI_INSTRUCTION) +RCC_CORE_OPCODE(EH_LABEL) +RCC_CORE_OPCODE(GC_LABEL) +RCC_CORE_OPCODE(ANNOTATION_LABEL) +RCC_CORE_OPCODE(KILL) +RCC_CORE_OPCODE(EXTRACT_SUBREG) +RCC_CORE_OPCODE(INSERT_SUBREG) +RCC_CORE_OPCODE(IMPLICIT_DEF) +RCC_CORE_OPCODE(INIT_UNDEF) +RCC_CORE_OPCODE(SUBREG_TO_REG) +RCC_CORE_OPCODE(COPY_TO_REGCLASS) +RCC_CORE_OPCODE(DBG_VALUE) +RCC_CORE_OPCODE(DBG_VALUE_LIST) +RCC_CORE_OPCODE(DBG_INSTR_REF) +RCC_CORE_OPCODE(DBG_PHI) +RCC_CORE_OPCODE(DBG_LABEL) +RCC_CORE_OPCODE(REG_SEQUENCE) +RCC_CORE_OPCODE(COPY) +RCC_CORE_OPCODE(COPY_LANEMASK) +RCC_CORE_OPCODE(BUNDLE) +RCC_CORE_OPCODE(LIFETIME_START) +RCC_CORE_OPCODE(LIFETIME_END) +RCC_CORE_OPCODE(PSEUDO_PROBE) +RCC_CORE_OPCODE(ARITH_FENCE) +RCC_CORE_OPCODE(STACKMAP) +RCC_CORE_OPCODE(FENTRY_CALL) +RCC_CORE_OPCODE(PATCHPOINT) +RCC_CORE_OPCODE(LOAD_STACK_GUARD) +RCC_CORE_OPCODE(PREALLOCATED_SETUP) +RCC_CORE_OPCODE(PREALLOCATED_ARG) +RCC_CORE_OPCODE(STATEPOINT) +RCC_CORE_OPCODE(LOCAL_ESCAPE) +RCC_CORE_OPCODE(FAULTING_OP) +RCC_CORE_OPCODE(PATCHABLE_OP) +RCC_CORE_OPCODE(PATCHABLE_FUNCTION_ENTER) +RCC_CORE_OPCODE(PATCHABLE_RET) +RCC_CORE_OPCODE(PATCHABLE_FUNCTION_EXIT) +RCC_CORE_OPCODE(PATCHABLE_TAIL_CALL) +RCC_CORE_OPCODE(PATCHABLE_EVENT_CALL) +RCC_CORE_OPCODE(PATCHABLE_TYPED_EVENT_CALL) +RCC_CORE_OPCODE(ICALL_BRANCH_FUNNEL) +RCC_CORE_OPCODE(FAKE_USE) +RCC_CORE_OPCODE(MEMBARRIER) +RCC_CORE_OPCODE(JUMP_TABLE_DEBUG_INFO) +RCC_CORE_OPCODE(RELOC_NONE) +RCC_CORE_OPCODE(CONVERGENCECTRL_ENTRY) +RCC_CORE_OPCODE(CONVERGENCECTRL_ANCHOR) +RCC_CORE_OPCODE(CONVERGENCECTRL_LOOP) +RCC_CORE_OPCODE(CONVERGENCECTRL_GLUE) + +RCC_GENERIC_OPCODE(G_ASSERT_SEXT) +RCC_GENERIC_OPCODE(G_ASSERT_ZEXT) +RCC_GENERIC_OPCODE(G_ASSERT_ALIGN) +RCC_GENERIC_OPCODE(G_ADD) +RCC_GENERIC_OPCODE(G_SUB) +RCC_GENERIC_OPCODE(G_MUL) +RCC_GENERIC_OPCODE(G_SDIV) +RCC_GENERIC_OPCODE(G_UDIV) +RCC_GENERIC_OPCODE(G_SREM) +RCC_GENERIC_OPCODE(G_UREM) +RCC_GENERIC_OPCODE(G_SDIVREM) +RCC_GENERIC_OPCODE(G_UDIVREM) +RCC_GENERIC_OPCODE(G_AND) +RCC_GENERIC_OPCODE(G_OR) +RCC_GENERIC_OPCODE(G_XOR) +RCC_GENERIC_OPCODE(G_ABDS) +RCC_GENERIC_OPCODE(G_ABDU) +RCC_GENERIC_OPCODE(G_UAVGFLOOR) +RCC_GENERIC_OPCODE(G_UAVGCEIL) +RCC_GENERIC_OPCODE(G_SAVGFLOOR) +RCC_GENERIC_OPCODE(G_SAVGCEIL) +RCC_GENERIC_OPCODE(G_IMPLICIT_DEF) +RCC_GENERIC_OPCODE(G_PHI) +RCC_GENERIC_OPCODE(G_FRAME_INDEX) +RCC_GENERIC_OPCODE(G_GLOBAL_VALUE) +RCC_GENERIC_OPCODE(G_PTRAUTH_GLOBAL_VALUE) +RCC_GENERIC_OPCODE(G_CONSTANT_POOL) +RCC_GENERIC_OPCODE(G_EXTRACT) +RCC_GENERIC_OPCODE(G_UNMERGE_VALUES) +RCC_GENERIC_OPCODE(G_INSERT) +RCC_GENERIC_OPCODE(G_MERGE_VALUES) +RCC_GENERIC_OPCODE(G_BUILD_VECTOR) +RCC_GENERIC_OPCODE(G_BUILD_VECTOR_TRUNC) +RCC_GENERIC_OPCODE(G_CONCAT_VECTORS) +RCC_GENERIC_OPCODE(G_PTRTOINT) +RCC_GENERIC_OPCODE(G_INTTOPTR) +RCC_GENERIC_OPCODE(G_BITCAST) +RCC_GENERIC_OPCODE(G_FREEZE) +RCC_GENERIC_OPCODE(G_CONSTANT_FOLD_BARRIER) +RCC_GENERIC_OPCODE(G_INTRINSIC_FPTRUNC_ROUND) +RCC_GENERIC_OPCODE(G_INTRINSIC_TRUNC) +RCC_GENERIC_OPCODE(G_INTRINSIC_ROUND) +RCC_GENERIC_OPCODE(G_INTRINSIC_LRINT) +RCC_GENERIC_OPCODE(G_INTRINSIC_LLRINT) +RCC_GENERIC_OPCODE(G_INTRINSIC_ROUNDEVEN) +RCC_GENERIC_OPCODE(G_READCYCLECOUNTER) +RCC_GENERIC_OPCODE(G_READSTEADYCOUNTER) +RCC_GENERIC_OPCODE(G_LOAD) +RCC_GENERIC_OPCODE(G_SEXTLOAD) +RCC_GENERIC_OPCODE(G_ZEXTLOAD) +RCC_GENERIC_OPCODE(G_FPEXTLOAD) +RCC_GENERIC_OPCODE(G_INDEXED_LOAD) +RCC_GENERIC_OPCODE(G_INDEXED_SEXTLOAD) +RCC_GENERIC_OPCODE(G_INDEXED_ZEXTLOAD) +RCC_GENERIC_OPCODE(G_STORE) +RCC_GENERIC_OPCODE(G_FPTRUNCSTORE) +RCC_GENERIC_OPCODE(G_INDEXED_STORE) +RCC_GENERIC_OPCODE(G_ATOMIC_CMPXCHG_WITH_SUCCESS) +RCC_GENERIC_OPCODE(G_ATOMIC_CMPXCHG) +RCC_GENERIC_OPCODE(G_ATOMICRMW_XCHG) +RCC_GENERIC_OPCODE(G_ATOMICRMW_ADD) +RCC_GENERIC_OPCODE(G_ATOMICRMW_SUB) +RCC_GENERIC_OPCODE(G_ATOMICRMW_AND) +RCC_GENERIC_OPCODE(G_ATOMICRMW_NAND) +RCC_GENERIC_OPCODE(G_ATOMICRMW_OR) +RCC_GENERIC_OPCODE(G_ATOMICRMW_XOR) +RCC_GENERIC_OPCODE(G_ATOMICRMW_MAX) +RCC_GENERIC_OPCODE(G_ATOMICRMW_MIN) +RCC_GENERIC_OPCODE(G_ATOMICRMW_UMAX) +RCC_GENERIC_OPCODE(G_ATOMICRMW_UMIN) +RCC_GENERIC_OPCODE(G_ATOMICRMW_FADD) +RCC_GENERIC_OPCODE(G_ATOMICRMW_FSUB) +RCC_GENERIC_OPCODE(G_ATOMICRMW_FMAX) +RCC_GENERIC_OPCODE(G_ATOMICRMW_FMIN) +RCC_GENERIC_OPCODE(G_ATOMICRMW_FMAXIMUM) +RCC_GENERIC_OPCODE(G_ATOMICRMW_FMINIMUM) +RCC_GENERIC_OPCODE(G_ATOMICRMW_FMAXIMUMNUM) +RCC_GENERIC_OPCODE(G_ATOMICRMW_FMINIMUMNUM) +RCC_GENERIC_OPCODE(G_ATOMICRMW_UINC_WRAP) +RCC_GENERIC_OPCODE(G_ATOMICRMW_UDEC_WRAP) +RCC_GENERIC_OPCODE(G_ATOMICRMW_USUB_COND) +RCC_GENERIC_OPCODE(G_ATOMICRMW_USUB_SAT) +RCC_GENERIC_OPCODE(G_FENCE) +RCC_GENERIC_OPCODE(G_PREFETCH) +RCC_GENERIC_OPCODE(G_BRCOND) +RCC_GENERIC_OPCODE(G_BRINDIRECT) +RCC_GENERIC_OPCODE(G_INVOKE_REGION_START) +RCC_GENERIC_OPCODE(G_INTRINSIC) +RCC_GENERIC_OPCODE(G_INTRINSIC_W_SIDE_EFFECTS) +RCC_GENERIC_OPCODE(G_INTRINSIC_CONVERGENT) +RCC_GENERIC_OPCODE(G_INTRINSIC_CONVERGENT_W_SIDE_EFFECTS) +RCC_GENERIC_OPCODE(G_ANYEXT) +RCC_GENERIC_OPCODE(G_TRUNC) +RCC_GENERIC_OPCODE(G_TRUNC_SSAT_S) +RCC_GENERIC_OPCODE(G_TRUNC_SSAT_U) +RCC_GENERIC_OPCODE(G_TRUNC_USAT_U) +RCC_GENERIC_OPCODE(G_CONSTANT) +RCC_GENERIC_OPCODE(G_FCONSTANT) +RCC_GENERIC_OPCODE(G_VASTART) +RCC_GENERIC_OPCODE(G_VAARG) +RCC_GENERIC_OPCODE(G_SEXT) +RCC_GENERIC_OPCODE(G_SEXT_INREG) +RCC_GENERIC_OPCODE(G_ZEXT) +RCC_GENERIC_OPCODE(G_SHL) +RCC_GENERIC_OPCODE(G_LSHR) +RCC_GENERIC_OPCODE(G_ASHR) +RCC_GENERIC_OPCODE(G_FSHL) +RCC_GENERIC_OPCODE(G_FSHR) +RCC_GENERIC_OPCODE(G_ROTR) +RCC_GENERIC_OPCODE(G_ROTL) +RCC_GENERIC_OPCODE(G_ICMP) +RCC_GENERIC_OPCODE(G_FCMP) +RCC_GENERIC_OPCODE(G_SCMP) +RCC_GENERIC_OPCODE(G_UCMP) +RCC_GENERIC_OPCODE(G_SELECT) +RCC_GENERIC_OPCODE(G_UADDO) +RCC_GENERIC_OPCODE(G_UADDE) +RCC_GENERIC_OPCODE(G_USUBO) +RCC_GENERIC_OPCODE(G_USUBE) +RCC_GENERIC_OPCODE(G_SADDO) +RCC_GENERIC_OPCODE(G_SADDE) +RCC_GENERIC_OPCODE(G_SSUBO) +RCC_GENERIC_OPCODE(G_SSUBE) +RCC_GENERIC_OPCODE(G_UMULO) +RCC_GENERIC_OPCODE(G_SMULO) +RCC_GENERIC_OPCODE(G_UMULH) +RCC_GENERIC_OPCODE(G_SMULH) +RCC_GENERIC_OPCODE(G_UADDSAT) +RCC_GENERIC_OPCODE(G_SADDSAT) +RCC_GENERIC_OPCODE(G_USUBSAT) +RCC_GENERIC_OPCODE(G_SSUBSAT) +RCC_GENERIC_OPCODE(G_USHLSAT) +RCC_GENERIC_OPCODE(G_SSHLSAT) +RCC_GENERIC_OPCODE(G_SMULFIX) +RCC_GENERIC_OPCODE(G_UMULFIX) +RCC_GENERIC_OPCODE(G_SMULFIXSAT) +RCC_GENERIC_OPCODE(G_UMULFIXSAT) +RCC_GENERIC_OPCODE(G_SDIVFIX) +RCC_GENERIC_OPCODE(G_UDIVFIX) +RCC_GENERIC_OPCODE(G_SDIVFIXSAT) +RCC_GENERIC_OPCODE(G_UDIVFIXSAT) +RCC_GENERIC_OPCODE(G_FADD) +RCC_GENERIC_OPCODE(G_FSUB) +RCC_GENERIC_OPCODE(G_FMUL) +RCC_GENERIC_OPCODE(G_FMA) +RCC_GENERIC_OPCODE(G_FMAD) +RCC_GENERIC_OPCODE(G_FDIV) +RCC_GENERIC_OPCODE(G_FREM) +RCC_GENERIC_OPCODE(G_FMODF) +RCC_GENERIC_OPCODE(G_FPOW) +RCC_GENERIC_OPCODE(G_FPOWI) +RCC_GENERIC_OPCODE(G_FEXP) +RCC_GENERIC_OPCODE(G_FEXP2) +RCC_GENERIC_OPCODE(G_FEXP10) +RCC_GENERIC_OPCODE(G_FLOG) +RCC_GENERIC_OPCODE(G_FLOG2) +RCC_GENERIC_OPCODE(G_FLOG10) +RCC_GENERIC_OPCODE(G_FLDEXP) +RCC_GENERIC_OPCODE(G_FFREXP) +RCC_GENERIC_OPCODE(G_FNEG) +RCC_GENERIC_OPCODE(G_FPEXT) +RCC_GENERIC_OPCODE(G_FPTRUNC) +RCC_GENERIC_OPCODE(G_FPTOSI) +RCC_GENERIC_OPCODE(G_FPTOUI) +RCC_GENERIC_OPCODE(G_SITOFP) +RCC_GENERIC_OPCODE(G_UITOFP) +RCC_GENERIC_OPCODE(G_FPTOSI_SAT) +RCC_GENERIC_OPCODE(G_FPTOUI_SAT) +RCC_GENERIC_OPCODE(G_FABS) +RCC_GENERIC_OPCODE(G_FCOPYSIGN) +RCC_GENERIC_OPCODE(G_IS_FPCLASS) +RCC_GENERIC_OPCODE(G_FCANONICALIZE) +RCC_GENERIC_OPCODE(G_FMINNUM) +RCC_GENERIC_OPCODE(G_FMAXNUM) +RCC_GENERIC_OPCODE(G_FMINNUM_IEEE) +RCC_GENERIC_OPCODE(G_FMAXNUM_IEEE) +RCC_GENERIC_OPCODE(G_FMINIMUM) +RCC_GENERIC_OPCODE(G_FMAXIMUM) +RCC_GENERIC_OPCODE(G_FMINIMUMNUM) +RCC_GENERIC_OPCODE(G_FMAXIMUMNUM) +RCC_GENERIC_OPCODE(G_GET_FPENV) +RCC_GENERIC_OPCODE(G_SET_FPENV) +RCC_GENERIC_OPCODE(G_RESET_FPENV) +RCC_GENERIC_OPCODE(G_GET_FPMODE) +RCC_GENERIC_OPCODE(G_SET_FPMODE) +RCC_GENERIC_OPCODE(G_RESET_FPMODE) +RCC_GENERIC_OPCODE(G_GET_ROUNDING) +RCC_GENERIC_OPCODE(G_SET_ROUNDING) +RCC_GENERIC_OPCODE(G_PTR_ADD) +RCC_GENERIC_OPCODE(G_PTRMASK) +RCC_GENERIC_OPCODE(G_SMIN) +RCC_GENERIC_OPCODE(G_SMAX) +RCC_GENERIC_OPCODE(G_UMIN) +RCC_GENERIC_OPCODE(G_UMAX) +RCC_GENERIC_OPCODE(G_ABS) +RCC_GENERIC_OPCODE(G_LROUND) +RCC_GENERIC_OPCODE(G_LLROUND) +RCC_GENERIC_OPCODE(G_BR) +RCC_GENERIC_OPCODE(G_BRJT) +RCC_GENERIC_OPCODE(G_VSCALE) +RCC_GENERIC_OPCODE(G_INSERT_SUBVECTOR) +RCC_GENERIC_OPCODE(G_EXTRACT_SUBVECTOR) +RCC_GENERIC_OPCODE(G_INSERT_VECTOR_ELT) +RCC_GENERIC_OPCODE(G_EXTRACT_VECTOR_ELT) +RCC_GENERIC_OPCODE(G_SHUFFLE_VECTOR) +RCC_GENERIC_OPCODE(G_SPLAT_VECTOR) +RCC_GENERIC_OPCODE(G_STEP_VECTOR) +RCC_GENERIC_OPCODE(G_VECTOR_COMPRESS) +RCC_GENERIC_OPCODE(G_CTTZ) +RCC_GENERIC_OPCODE(G_CTTZ_ZERO_POISON) +RCC_GENERIC_OPCODE(G_CTLZ) +RCC_GENERIC_OPCODE(G_CTLZ_ZERO_POISON) +RCC_GENERIC_OPCODE(G_CTLS) +RCC_GENERIC_OPCODE(G_CTPOP) +RCC_GENERIC_OPCODE(G_BSWAP) +RCC_GENERIC_OPCODE(G_BITREVERSE) +RCC_GENERIC_OPCODE(G_CLMUL) +RCC_GENERIC_OPCODE(G_FCEIL) +RCC_GENERIC_OPCODE(G_FCOS) +RCC_GENERIC_OPCODE(G_FSIN) +RCC_GENERIC_OPCODE(G_FSINCOS) +RCC_GENERIC_OPCODE(G_FTAN) +RCC_GENERIC_OPCODE(G_FACOS) +RCC_GENERIC_OPCODE(G_FASIN) +RCC_GENERIC_OPCODE(G_FATAN) +RCC_GENERIC_OPCODE(G_FATAN2) +RCC_GENERIC_OPCODE(G_FCOSH) +RCC_GENERIC_OPCODE(G_FSINH) +RCC_GENERIC_OPCODE(G_FTANH) +RCC_GENERIC_OPCODE(G_FSQRT) +RCC_GENERIC_OPCODE(G_FFLOOR) +RCC_GENERIC_OPCODE(G_FRINT) +RCC_GENERIC_OPCODE(G_FNEARBYINT) +RCC_GENERIC_OPCODE(G_ADDRSPACE_CAST) +RCC_GENERIC_OPCODE(G_BLOCK_ADDR) +RCC_GENERIC_OPCODE(G_JUMP_TABLE) +RCC_GENERIC_OPCODE(G_DYN_STACKALLOC) +RCC_GENERIC_OPCODE(G_STACKSAVE) +RCC_GENERIC_OPCODE(G_STACKRESTORE) +RCC_GENERIC_OPCODE(G_STRICT_FADD) +RCC_GENERIC_OPCODE(G_STRICT_FSUB) +RCC_GENERIC_OPCODE(G_STRICT_FMUL) +RCC_GENERIC_OPCODE(G_STRICT_FDIV) +RCC_GENERIC_OPCODE(G_STRICT_FREM) +RCC_GENERIC_OPCODE(G_STRICT_FMA) +RCC_GENERIC_OPCODE(G_STRICT_FSQRT) +RCC_GENERIC_OPCODE(G_STRICT_FLDEXP) +RCC_GENERIC_OPCODE(G_STRICT_FCMP) +RCC_GENERIC_OPCODE(G_STRICT_FCMPS) +RCC_GENERIC_OPCODE(G_READ_REGISTER) +RCC_GENERIC_OPCODE(G_WRITE_REGISTER) +RCC_GENERIC_OPCODE(G_MEMCPY) +RCC_GENERIC_OPCODE(G_MEMCPY_INLINE) +RCC_GENERIC_OPCODE(G_MEMMOVE) +RCC_GENERIC_OPCODE(G_MEMSET) +RCC_GENERIC_OPCODE(G_BZERO) +RCC_GENERIC_OPCODE(G_TRAP) +RCC_GENERIC_OPCODE(G_DEBUGTRAP) +RCC_GENERIC_OPCODE(G_UBSANTRAP) +RCC_GENERIC_OPCODE(G_VECREDUCE_SEQ_FADD) +RCC_GENERIC_OPCODE(G_VECREDUCE_SEQ_FMUL) +RCC_GENERIC_OPCODE(G_VECREDUCE_FADD) +RCC_GENERIC_OPCODE(G_VECREDUCE_FMUL) +RCC_GENERIC_OPCODE(G_VECREDUCE_FMAX) +RCC_GENERIC_OPCODE(G_VECREDUCE_FMIN) +RCC_GENERIC_OPCODE(G_VECREDUCE_FMAXIMUM) +RCC_GENERIC_OPCODE(G_VECREDUCE_FMINIMUM) +RCC_GENERIC_OPCODE(G_VECREDUCE_ADD) +RCC_GENERIC_OPCODE(G_VECREDUCE_MUL) +RCC_GENERIC_OPCODE(G_VECREDUCE_AND) +RCC_GENERIC_OPCODE(G_VECREDUCE_OR) +RCC_GENERIC_OPCODE(G_VECREDUCE_XOR) +RCC_GENERIC_OPCODE(G_VECREDUCE_SMAX) +RCC_GENERIC_OPCODE(G_VECREDUCE_SMIN) +RCC_GENERIC_OPCODE(G_VECREDUCE_UMAX) +RCC_GENERIC_OPCODE(G_VECREDUCE_UMIN) +RCC_GENERIC_OPCODE(G_SBFX) +RCC_GENERIC_OPCODE(G_UBFX) diff --git a/backend/include/rcc/CodeGen/GenericOpcodes.h b/backend/include/rcc/CodeGen/GenericOpcodes.h new file mode 100644 index 0000000..5a46e21 --- /dev/null +++ b/backend/include/rcc/CodeGen/GenericOpcodes.h @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_GENERICOPCODES_H +#define RCC_CODEGEN_GENERICOPCODES_H + +#include "rcc/CodeGen/InstructionDescriptor.h" + +#include <cstdint> +#include <span> +#include <string_view> +#include <vector> + +namespace rcc { + +// Target-independent values are build-local. Target tables start at 1024 and +// are appended to this set when a target description is constructed. +enum class GenericOpcode : std::uint32_t { + Invalid, +#define RCC_CORE_OPCODE(Name) Name, +#define RCC_GENERIC_OPCODE(Name) +#include "rcc/CodeGen/GenericOpcode.def" +#undef RCC_GENERIC_OPCODE +#undef RCC_CORE_OPCODE + CORE_PSEUDO_END, + +#define RCC_CORE_OPCODE(Name) +#define RCC_GENERIC_OPCODE(Name) Name, +#include "rcc/CodeGen/GenericOpcode.def" +#undef RCC_GENERIC_OPCODE +#undef RCC_CORE_OPCODE + G_GENERIC_END, +}; + +[[nodiscard]] constexpr Opcode opcode(GenericOpcode Generic) noexcept { + return Opcode::fromValue(static_cast<std::uint32_t>(Generic)); +} + +[[nodiscard]] constexpr bool isGenericOpcode(Opcode Value) noexcept { + return Value.value() >= static_cast<std::uint32_t>(GenericOpcode::G_ASSERT_SEXT) && + Value.value() < static_cast<std::uint32_t>(GenericOpcode::G_GENERIC_END); +} + +[[nodiscard]] constexpr bool isCorePseudoOpcode(Opcode Value) noexcept { + return Value.value() >= static_cast<std::uint32_t>(GenericOpcode::PHI) && + Value.value() < static_cast<std::uint32_t>(GenericOpcode::CORE_PSEUDO_END); +} + +inline constexpr std::size_t CorePseudoOpcodeCount = + static_cast<std::size_t>(GenericOpcode::CORE_PSEUDO_END) - + static_cast<std::size_t>(GenericOpcode::PHI); + +inline constexpr std::size_t GenericOpcodeCount = + static_cast<std::size_t>(GenericOpcode::G_GENERIC_END) - + static_cast<std::size_t>(GenericOpcode::G_ASSERT_SEXT); + +[[nodiscard]] std::string_view getGenericOpcodeName(GenericOpcode OpcodeValue) noexcept; + +[[nodiscard]] std::span<const InstructionDescriptor> genericInstructionDescriptors() noexcept; + +[[nodiscard]] std::span<const ImmediatePredicate> genericImmediatePredicates() noexcept; + +[[nodiscard]] Expected<void> +appendGenericInstructionInfo(std::vector<InstructionDescriptor> &Descriptors, + std::vector<ImmediatePredicate> &Predicates); + +} // namespace rcc + +#endif // RCC_CODEGEN_GENERICOPCODES_H diff --git a/backend/include/rcc/CodeGen/GlobalISel/CSEInfo.h b/backend/include/rcc/CodeGen/GlobalISel/CSEInfo.h new file mode 100644 index 0000000..27bc2a3 --- /dev/null +++ b/backend/include/rcc/CodeGen/GlobalISel/CSEInfo.h @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_GLOBALISEL_CSEINFO_H +#define RCC_CODEGEN_GLOBALISEL_CSEINFO_H + +#include "rcc/ADT/APInt.h" +#include "rcc/CodeGen/CSEConfigBase.h" +#include "rcc/CodeGen/CommandFlags.h" +#include "rcc/CodeGen/GlobalISel/GISelChangeObserver.h" +#include "rcc/CodeGen/GlobalISel/GISelWorkList.h" +#include "rcc/CodeGen/LowLevelType.h" +#include "rcc/CodeGen/MachineIRTypes.h" +#include "rcc/CodeGen/Register.h" +#include "rcc/Support/Error.h" + +#include <cstddef> +#include <cstdint> +#include <memory> +#include <span> +#include <unordered_map> +#include <vector> + +namespace rcc { + +class MachineBasicBlock; +class MachineFunction; +class MachineInstr; +class MachineIRBuilderResult; + +class CSEConfigFull final : public CSEConfigBase { +public: + [[nodiscard]] bool shouldCSEOpcode(Opcode InstructionOpcode) const noexcept override; +}; + +class CSEConfigConstantOnly final : public CSEConfigBase { +public: + [[nodiscard]] bool shouldCSEOpcode(Opcode InstructionOpcode) const noexcept override; +}; + +[[nodiscard]] std::unique_ptr<CSEConfigBase> getStandardCSEConfigForOpt(OptimizationLevel Level); + +class GISelCSEInfo final : public GISelChangeObserver { +public: + GISelCSEInfo() = default; + GISelCSEInfo(const GISelCSEInfo &) = delete; + GISelCSEInfo &operator=(const GISelCSEInfo &) = delete; + ~GISelCSEInfo() override = default; + + void setCSEConfig(std::unique_ptr<CSEConfigBase> Config) noexcept; + [[nodiscard]] bool shouldCSE(Opcode InstructionOpcode) const noexcept; + [[nodiscard]] Expected<void> analyze(MachineFunction &Function); + [[nodiscard]] Expected<void> verify(); + void releaseMemory() noexcept; + + void erasingInstr(MachineInstr &Instruction) override; + void createdInstr(MachineInstr &Instruction) override; + void changingInstr(MachineInstr &Instruction) override; + void changedInstr(MachineInstr &Instruction) override; + +private: + struct CSEKey final { + const MachineBasicBlock *Block = nullptr; + Opcode InstructionOpcode; + std::vector<std::uint64_t> Profile; + + friend bool operator==(const CSEKey &, const CSEKey &) = default; + }; + + struct CSEKeyHash final { + [[nodiscard]] std::size_t operator()(const CSEKey &Key) const noexcept; + }; + + [[nodiscard]] Expected<void> appendRegisterProfile(std::vector<std::uint64_t> &Profile, + Register Reg, bool IsDefinition) const; + [[nodiscard]] Expected<void> appendResultProfile(std::vector<std::uint64_t> &Profile, + const MachineIRBuilderResult &Result) const; + [[nodiscard]] Expected<CSEKey> profileInstruction(const MachineInstr &Instruction) const; + [[nodiscard]] Expected<CSEKey> profileBuilderInstruction( + Opcode InstructionOpcode, std::span<const MachineIRBuilderResult> Destinations, + std::span<const Register> Sources, std::span<const std::int64_t> Immediates, + MachineInstrFlags Flags, const MachineBasicBlock &Block) const; + [[nodiscard]] Expected<CSEKey> profileConstant(const MachineIRBuilderResult &Destination, + const APInt &Value, + const MachineBasicBlock &Block) const; + + void recordNewInstruction(MachineInstr &Instruction); + [[nodiscard]] Expected<void> handleRecordedInstruction(MachineInstr &Instruction); + [[nodiscard]] Expected<void> handleRecordedInstructions(); + void handleRemoveInstruction(MachineInstr &Instruction) noexcept; + [[nodiscard]] Expected<void> insertInstruction(MachineInstr &Instruction); + + [[nodiscard]] Expected<MachineInstr *> getMachineInstrIfExists( + Opcode InstructionOpcode, std::span<const MachineIRBuilderResult> Destinations, + std::span<const Register> Sources, std::span<const std::int64_t> Immediates, + MachineInstrFlags Flags, MachineBasicBlock &Block); + [[nodiscard]] Expected<MachineInstr *> + getConstantIfExists(const MachineIRBuilderResult &Destination, const APInt &Value, + MachineBasicBlock &Block); + void countOpcodeHit(Opcode InstructionOpcode) noexcept; + + MachineFunction *Function = nullptr; + std::unique_ptr<CSEConfigBase> Config; + std::unordered_map<CSEKey, MachineInstr *, CSEKeyHash> CSEMap; + std::unordered_map<const MachineInstr *, CSEKey> InstructionMapping; + GISelWorkList<8> TemporaryInstructions; + std::unordered_map<std::uint32_t, std::size_t> OpcodeHitTable; + bool HandlingRecordedInstructions = false; + + friend class CSEMIRBuilder; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_GLOBALISEL_CSEINFO_H diff --git a/backend/include/rcc/CodeGen/GlobalISel/CSEMIRBuilder.h b/backend/include/rcc/CodeGen/GlobalISel/CSEMIRBuilder.h new file mode 100644 index 0000000..1adf474 --- /dev/null +++ b/backend/include/rcc/CodeGen/GlobalISel/CSEMIRBuilder.h @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_GLOBALISEL_CSEMIRBUILDER_H +#define RCC_CODEGEN_GLOBALISEL_CSEMIRBUILDER_H + +#include "rcc/CodeGen/MachineIRBuilder.h" + +#include <cstdint> +#include <span> + +namespace rcc { + +class CSEMIRBuilder final : public MachineIRBuilder { +public: + using MachineIRBuilder::buildConstant; + using MachineIRBuilder::buildInstr; + using MachineIRBuilder::MachineIRBuilder; + + [[nodiscard]] Expected<MachineInstr *> + buildInstr(Opcode InstructionOpcode, std::span<const MachineIRBuilderResult> Destinations, + std::span<const Register> Sources) const override; + [[nodiscard]] Expected<MachineInstr *> buildConstant(MachineIRBuilderResult Destination, + APInt Value) const override; + [[nodiscard]] Expected<MachineInstr *> buildSExtInReg(MachineIRBuilderResult Destination, + Register Source, + std::uint32_t Width) const override; + [[nodiscard]] Expected<MachineInstr *> buildExtract(MachineIRBuilderResult Destination, + Register Source, + std::uint64_t Offset) const override; + +private: + [[nodiscard]] bool canPerformCSE(Opcode InstructionOpcode) const noexcept; + [[nodiscard]] bool dominatesInsertionPoint(const MachineInstr &Instruction) const noexcept; + [[nodiscard]] Expected<void> makeInstructionDominate(MachineInstr &Instruction) const; + [[nodiscard]] Expected<MachineInstr *> + generateCopyIfRequired(const MachineIRBuilderResult &Destination, + MachineInstr &Instruction) const; + [[nodiscard]] Expected<MachineInstr *> memoize(MachineInstr &Instruction) const; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_GLOBALISEL_CSEMIRBUILDER_H diff --git a/backend/include/rcc/CodeGen/GlobalISel/Combiner.h b/backend/include/rcc/CodeGen/GlobalISel/Combiner.h new file mode 100644 index 0000000..9ef7ed7 --- /dev/null +++ b/backend/include/rcc/CodeGen/GlobalISel/Combiner.h @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_GLOBALISEL_COMBINER_H +#define RCC_CODEGEN_GLOBALISEL_COMBINER_H + +#include "rcc/CodeGen/GlobalISel/GIMatchTableExecutor.h" +#include "rcc/CodeGen/MachinePassManager.h" + +#include <cstddef> +#include <string> +#include <utility> + +namespace rcc { + +class GlobalISelCombinerPass final : public MachineFunctionPass { +public: + GlobalISelCombinerPass(const GIMatchTable &Table, const GIMatchTableExecutor &Executor, + std::string PassName, std::size_t MaximumRewrites = 4096, + std::size_t MaximumIterations = 8) noexcept + : Table(&Table), Executor(&Executor), PassName(std::move(PassName)), + MaximumRewrites(MaximumRewrites), MaximumIterations(MaximumIterations) { + Contract.Name = this->PassName; + Contract.Requires = {MachineFunctionProperty::IsSSA}; + Contract.VerificationStage = PipelineStage::MachineIRInput; + Contract.ThreadSafety = PassThreadSafety::ThreadSafe; + Contract.DeclaredChanges = {MachinePassChange::Instructions, MachinePassChange::Operands, + MachinePassChange::Registers}; + } + + [[nodiscard]] const MachinePassContract &contract() const noexcept override; + [[nodiscard]] Expected<MachinePassResult> run(MachineFunction &Function, + const TargetDescription &Target, + MachineFunctionAnalysisManager &Analyses) override; + +private: + [[nodiscard]] Expected<MachinePassResult> + runImplementation(MachineFunction &Function, const TargetDescription &Target, + MachineFunctionAnalysisManager &Analyses) const; + + const GIMatchTable *Table; + const GIMatchTableExecutor *Executor; + std::string PassName; + std::size_t MaximumRewrites; + std::size_t MaximumIterations; + MachinePassContract Contract; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_GLOBALISEL_COMBINER_H diff --git a/backend/include/rcc/CodeGen/GlobalISel/GIMatchTableExecutor.h b/backend/include/rcc/CodeGen/GlobalISel/GIMatchTableExecutor.h new file mode 100644 index 0000000..f191db2 --- /dev/null +++ b/backend/include/rcc/CodeGen/GlobalISel/GIMatchTableExecutor.h @@ -0,0 +1,149 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_GLOBALISEL_GIMATCHTABLEEXECUTOR_H +#define RCC_CODEGEN_GLOBALISEL_GIMATCHTABLEEXECUTOR_H + +#include "rcc/CodeGen/MachineIRTypes.h" +#include "rcc/CodeGen/MachineOperand.h" +#include "rcc/Support/Error.h" + +#include <cstdint> +#include <optional> +#include <span> +#include <string_view> +#include <vector> + +namespace rcc { + +class MachineFunction; +class MachineInstr; +class TargetDescription; + +enum class GIMatchTypeKind : std::uint8_t { + Any, + Scalar, + Pointer, +}; + +enum class GIMatchImmediateTransform : std::uint8_t { + Identity, + Negate, + PlusOne, +}; + +enum class GIMatchTableOpcode : std::uint8_t { + CheckOpcode, + CheckOperandType, + CheckInstructionFlag, + ClearInstructionFlags, + CheckOperandBank, + CheckOperandClass, + CheckIntegerPredicate, + CheckFloatingPredicate, + CheckMemorySize, + CheckTargetPredicate, + CheckOperandImmediateRanges, + CheckOperandDefConstantRanges, + CheckOperandDefOpcode, + CheckOperandDefIntegerPredicate, + CheckOperandDefOperandConstantRanges, + CaptureDefOperand, + CreateTemporary, + CreateTemporaryType, + MutateOpcode, + BuildInstruction, + RenderOperand, + RenderComplexOperand, + RenderCapturedOperand, + RenderCapturedTypeBits, + RenderPhysicalRegister, + RenderTemporary, + RenderCapturedImmediate, + RenderImmediate, + ReplaceRootWithCapturedOperand, + EraseOperandDefinition, + Complete, +}; + +struct GIMatchTableInstruction final { + GIMatchTableOpcode Opcode = GIMatchTableOpcode::Complete; + std::uint16_t OperandIndex = 0; + std::uint32_t Value = 0; + std::uint32_t Auxiliary = 0; + std::int64_t Immediate = 0; + std::string_view Symbol; +}; + +struct GIMatchComplexInstruction final { + Opcode ResultOpcode; + LowLevelType ResultType; + std::vector<MachineOperand> Operands; +}; + +struct GIMatchComplexOperand final { + std::optional<MachineOperand> Operand; + std::optional<GIMatchComplexInstruction> Instruction; +}; + +struct GIMatchTableConstantRange final { + std::int64_t Minimum = 0; + std::int64_t Maximum = 0; +}; + +struct GIMatchTableRule final { + std::string_view Name; + std::uint32_t Priority = 0; + std::uint32_t FirstInstruction = 0; + std::uint32_t InstructionCount = 0; +}; + +enum class GIMatchTableKind : std::uint8_t { + Selection, + Combiner, +}; + +struct GIMatchTable final { + std::span<const GIMatchTableInstruction> Instructions; + std::span<const GIMatchTableRule> Rules; + std::span<const GIMatchTableConstantRange> ConstantRanges; + GIMatchTableKind Kind = GIMatchTableKind::Selection; +}; + +class GIMatchTableExecutor { +public: + virtual ~GIMatchTableExecutor() = default; + + [[nodiscard]] Expected<bool> match(const GIMatchTable &Table, const MachineInstr &Instruction, + const MachineFunction &Function, + const TargetDescription &Target) const; + + // A false result means that no table rule matched. The executor completes all + // predicates before applying the selected rule to MachineIR. + [[nodiscard]] Expected<bool> select(const GIMatchTable &Table, MachineInstr &Instruction, + MachineFunction &Function, + const TargetDescription &Target) const; + + // Match and apply one generated GlobalISel combiner rule. + [[nodiscard]] Expected<bool> combine(const GIMatchTable &Table, MachineInstr &Instruction, + MachineFunction &Function, + const TargetDescription &Target) const; + +protected: + [[nodiscard]] virtual Expected<bool> + matchesTargetPredicate(std::string_view Name, const MachineInstr &Instruction, + const MachineFunction &Function, const TargetDescription &Target) const; + + [[nodiscard]] virtual Expected<std::optional<GIMatchComplexOperand>> + renderComplexOperand(std::string_view Name, const MachineInstr &Instruction, + const MachineFunction &Function, const TargetDescription &Target, + std::uint32_t OperandIndex) const; + + [[nodiscard]] virtual Expected<void> + finalizeGeneratedInstruction(MachineInstr &Output, MachineFunction &Function, + const TargetDescription &Target, MachineInstrFlags InputFlags, + bool InputMayRaiseFPException) const; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_GLOBALISEL_GIMATCHTABLEEXECUTOR_H diff --git a/backend/include/rcc/CodeGen/GlobalISel/GISelChangeObserver.h b/backend/include/rcc/CodeGen/GlobalISel/GISelChangeObserver.h new file mode 100644 index 0000000..cc0c5a0 --- /dev/null +++ b/backend/include/rcc/CodeGen/GlobalISel/GISelChangeObserver.h @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_GLOBALISEL_GISELCHANGEOBSERVER_H +#define RCC_CODEGEN_GLOBALISEL_GISELCHANGEOBSERVER_H + +#include "rcc/CodeGen/MachineFunction.h" + +#include <span> +#include <vector> + +namespace rcc { + +class MachineInstr; + +class GISelChangeObserver { +public: + virtual ~GISelChangeObserver() = default; + + virtual void erasingInstr(MachineInstr &Instruction) = 0; + virtual void createdInstr(MachineInstr &Instruction) = 0; + virtual void changingInstr(MachineInstr &Instruction) = 0; + virtual void changedInstr(MachineInstr &Instruction) = 0; +}; + +class GISelObserverWrapper final : public MachineFunction::Delegate, public GISelChangeObserver { +public: + GISelObserverWrapper() = default; + explicit GISelObserverWrapper(GISelChangeObserver &Observer) { + Observers.push_back(&Observer); + } + explicit GISelObserverWrapper(std::span<GISelChangeObserver *const> InitialObservers) + : Observers(InitialObservers.begin(), InitialObservers.end()) {} + + void addObserver(GISelChangeObserver &Observer); + void removeObserver(GISelChangeObserver &Observer); + void clearObservers() noexcept; + + void erasingInstr(MachineInstr &Instruction) override; + void createdInstr(MachineInstr &Instruction) override; + void changingInstr(MachineInstr &Instruction) override; + void changedInstr(MachineInstr &Instruction) override; + + void handleInsertion(MachineInstr &Instruction) override; + void handleRemoval(MachineInstr &Instruction) override; + +private: + std::vector<GISelChangeObserver *> Observers; +}; + +class MachineFunctionDelegateInstaller final { +public: + MachineFunctionDelegateInstaller(MachineFunction &Function, MachineFunction::Delegate &Delegate); + MachineFunctionDelegateInstaller(const MachineFunctionDelegateInstaller &) = delete; + MachineFunctionDelegateInstaller &operator=(const MachineFunctionDelegateInstaller &) = delete; + ~MachineFunctionDelegateInstaller(); + +private: + MachineFunction *Function; + MachineFunction::Delegate *Delegate; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_GLOBALISEL_GISELCHANGEOBSERVER_H diff --git a/backend/include/rcc/CodeGen/GlobalISel/GISelValueTracking.h b/backend/include/rcc/CodeGen/GlobalISel/GISelValueTracking.h new file mode 100644 index 0000000..e51f8c1 --- /dev/null +++ b/backend/include/rcc/CodeGen/GlobalISel/GISelValueTracking.h @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_GLOBALISEL_GISELVALUETRACKING_H +#define RCC_CODEGEN_GLOBALISEL_GISELVALUETRACKING_H + +#include "rcc/ADT/APInt.h" +#include "rcc/CodeGen/GlobalISel/GISelChangeObserver.h" +#include "rcc/CodeGen/Register.h" +#include "rcc/Support/Error.h" + +#include <cstdint> + +namespace rcc { + +class MachineFunction; +class MachineRegisterInfo; +class TargetDescription; + +class GISelValueTracking final : public GISelChangeObserver { +public: + GISelValueTracking(const MachineFunction &Function, const TargetDescription &Target, + unsigned MaxDepth = 6) noexcept; + + [[nodiscard]] const MachineFunction &getMachineFunction() const noexcept { + return Function; + } + + [[nodiscard]] Expected<APInt> getKnownZeroes(Register Reg) const; + [[nodiscard]] Expected<std::uint32_t> computeNumSignBits(Register Reg, unsigned Depth = 0) const; + + void erasingInstr(MachineInstr &) override {} + void createdInstr(MachineInstr &) override {} + void changingInstr(MachineInstr &) override {} + void changedInstr(MachineInstr &) override {} + +private: + struct KnownBits final { + APInt Zero; + APInt One; + }; + + [[nodiscard]] Expected<KnownBits> makeUnknown(std::uint32_t BitWidth) const; + [[nodiscard]] Expected<KnownBits> getKnownBits(Register Reg, unsigned Depth = 0) const; + + const MachineFunction &Function; + const MachineRegisterInfo &MRI; + const TargetDescription &Target; + unsigned MaxDepth; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_GLOBALISEL_GISELVALUETRACKING_H diff --git a/backend/include/rcc/CodeGen/GlobalISel/GISelWorkList.h b/backend/include/rcc/CodeGen/GlobalISel/GISelWorkList.h new file mode 100644 index 0000000..faeafab --- /dev/null +++ b/backend/include/rcc/CodeGen/GlobalISel/GISelWorkList.h @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_GLOBALISEL_GISELWORKLIST_H +#define RCC_CODEGEN_GLOBALISEL_GISELWORKLIST_H + +#include <cstddef> +#include <unordered_map> +#include <vector> + +namespace rcc { + +class MachineInstr; + +template <std::size_t InlineCapacity> class GISelWorkList final { +public: + GISelWorkList() { + Worklist.reserve(InlineCapacity); + WorklistMap.reserve(InlineCapacity); + } + + [[nodiscard]] bool empty() const noexcept { + return WorklistMap.empty(); + } + + [[nodiscard]] std::size_t size() const noexcept { + return WorklistMap.size(); + } + + void deferredInsert(MachineInstr *Instruction) { + Worklist.push_back(Instruction); + } + + void finalize() { + WorklistMap.reserve(Worklist.size()); + for (std::size_t Index = 0; Index < Worklist.size(); ++Index) + WorklistMap.emplace(Worklist[Index], Index); + } + + void insert(MachineInstr *Instruction) { + const auto [Position, Inserted] = WorklistMap.emplace(Instruction, Worklist.size()); + if (Inserted) + Worklist.push_back(Instruction); + } + + void remove(const MachineInstr *Instruction) { + const auto Position = WorklistMap.find(Instruction); + if (Position == WorklistMap.end()) + return; + Worklist[Position->second] = nullptr; + WorklistMap.erase(Position); + } + + void clear() { + Worklist.clear(); + WorklistMap.clear(); + } + + [[nodiscard]] MachineInstr *popBack() { + MachineInstr *Instruction = nullptr; + do { + Instruction = Worklist.back(); + Worklist.pop_back(); + } while (Instruction == nullptr); + WorklistMap.erase(Instruction); + return Instruction; + } + +private: + std::vector<MachineInstr *> Worklist; + std::unordered_map<const MachineInstr *, std::size_t> WorklistMap; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_GLOBALISEL_GISELWORKLIST_H diff --git a/backend/include/rcc/CodeGen/GlobalISel/GenericMachineInstrs.h b/backend/include/rcc/CodeGen/GlobalISel/GenericMachineInstrs.h new file mode 100644 index 0000000..ea3532c --- /dev/null +++ b/backend/include/rcc/CodeGen/GlobalISel/GenericMachineInstrs.h @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_GLOBALISEL_GENERICMACHINEINSTRS_H +#define RCC_CODEGEN_GLOBALISEL_GENERICMACHINEINSTRS_H + +#include "rcc/CodeGen/GenericOpcodes.h" +#include "rcc/CodeGen/MachineInstr.h" +#include "rcc/CodeGen/MachineOperand.h" +#include "rcc/CodeGen/Register.h" + +#include <cassert> +#include <cstddef> + +namespace rcc { + +class GenericMachineInstr : public MachineInstr { +public: + GenericMachineInstr() = delete; + + [[nodiscard]] Register getReg(std::size_t Index) const { + const MachineOperand *Operand = operandAt(Index); + assert(Operand != nullptr && Operand->isRegister()); + const Register *Reg = Operand->getIf<Register>(); + assert(Reg != nullptr); + return *Reg; + } + + [[nodiscard]] static bool classof(const MachineInstr *Instruction) noexcept { + return Instruction != nullptr && isGenericOpcode(Instruction->opcode()); + } +}; + +class GUnmerge : public GenericMachineInstr { +public: + [[nodiscard]] std::size_t getNumDefs() const noexcept { + return operandCount() - 1; + } + + [[nodiscard]] Register getSourceReg() const { + return getReg(getNumDefs()); + } + + [[nodiscard]] static bool classof(const MachineInstr *Instruction) noexcept { + return Instruction != nullptr && + Instruction->opcode() == rcc::opcode(GenericOpcode::G_UNMERGE_VALUES); + } +}; + +class GMergeLikeInstr : public GenericMachineInstr { +public: + [[nodiscard]] std::size_t getNumSources() const noexcept { + return operandCount() - 1; + } + + [[nodiscard]] Register getSourceReg(std::size_t Index) const { + return getReg(Index + 1); + } + + [[nodiscard]] static bool classof(const MachineInstr *Instruction) noexcept { + if (Instruction == nullptr) + return false; + switch (static_cast<GenericOpcode>(Instruction->opcode().value())) { + case GenericOpcode::G_MERGE_VALUES: + case GenericOpcode::G_CONCAT_VECTORS: + case GenericOpcode::G_BUILD_VECTOR: + return true; + default: + return false; + } + } +}; + +class GMerge : public GMergeLikeInstr { +public: + [[nodiscard]] static bool classof(const MachineInstr *Instruction) noexcept { + return Instruction != nullptr && + Instruction->opcode() == rcc::opcode(GenericOpcode::G_MERGE_VALUES); + } +}; + +class GConcatVectors : public GMergeLikeInstr { +public: + [[nodiscard]] static bool classof(const MachineInstr *Instruction) noexcept { + return Instruction != nullptr && + Instruction->opcode() == rcc::opcode(GenericOpcode::G_CONCAT_VECTORS); + } +}; + +class GBuildVector : public GMergeLikeInstr { +public: + [[nodiscard]] static bool classof(const MachineInstr *Instruction) noexcept { + return Instruction != nullptr && + Instruction->opcode() == rcc::opcode(GenericOpcode::G_BUILD_VECTOR); + } +}; + +template <typename InstructionT> +[[nodiscard]] InstructionT &castGenericMachineInstr(MachineInstr &Instruction) { + assert(InstructionT::classof(&Instruction)); + return static_cast<InstructionT &>(Instruction); +} + +template <typename InstructionT> +[[nodiscard]] const InstructionT &castGenericMachineInstr(const MachineInstr &Instruction) { + assert(InstructionT::classof(&Instruction)); + return static_cast<const InstructionT &>(Instruction); +} + +} // namespace rcc + +#endif // RCC_CODEGEN_GLOBALISEL_GENERICMACHINEINSTRS_H diff --git a/backend/include/rcc/CodeGen/GlobalISel/InstructionSelect.h b/backend/include/rcc/CodeGen/GlobalISel/InstructionSelect.h new file mode 100644 index 0000000..33b1659 --- /dev/null +++ b/backend/include/rcc/CodeGen/GlobalISel/InstructionSelect.h @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_GLOBALISEL_INSTRUCTIONSELECT_H +#define RCC_CODEGEN_GLOBALISEL_INSTRUCTIONSELECT_H + +#include "rcc/CodeGen/GlobalISel/InstructionSelector.h" +#include "rcc/CodeGen/MachinePassManager.h" + +namespace rcc { + +class InstructionSelect final : public MachineFunctionPass { +public: + explicit InstructionSelect(const InstructionSelector *Info) : Info(Info) {} + explicit InstructionSelect(const InstructionSelector &Info) : Info(&Info) {} + + [[nodiscard]] const MachinePassContract &contract() const noexcept override; + [[nodiscard]] Expected<MachinePassResult> run(MachineFunction &Function, + const TargetDescription &Target, + MachineFunctionAnalysisManager &Analyses) override; + +private: + [[nodiscard]] Expected<MachinePassResult> + runImplementation(MachineFunction &Function, const TargetDescription &Target, + MachineFunctionAnalysisManager &Analyses); + + const InstructionSelector *Info; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_GLOBALISEL_INSTRUCTIONSELECT_H diff --git a/backend/include/rcc/CodeGen/GlobalISel/InstructionSelector.h b/backend/include/rcc/CodeGen/GlobalISel/InstructionSelector.h new file mode 100644 index 0000000..8e5b56d --- /dev/null +++ b/backend/include/rcc/CodeGen/GlobalISel/InstructionSelector.h @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_GLOBALISEL_INSTRUCTIONSELECTOR_H +#define RCC_CODEGEN_GLOBALISEL_INSTRUCTIONSELECTOR_H + +#include "rcc/CodeGen/GlobalISel/GIMatchTableExecutor.h" +#include "rcc/Support/Error.h" + +namespace rcc { + +class MachineFunction; +class MachineInstr; +class TargetDescription; + +class InstructionSelector : public GIMatchTableExecutor { +public: + ~InstructionSelector() override = default; + + [[nodiscard]] virtual Expected<void> select(MachineInstr &Instruction, MachineFunction &Function, + const TargetDescription &Target) const = 0; + + [[nodiscard]] virtual Expected<void> + constrainSelectedInstruction(MachineInstr &, MachineFunction &, const TargetDescription &) const { + return {}; + } + + [[nodiscard]] Expected<void> propagateSelectionFlags(MachineInstr &Output, + MachineInstrFlags InputFlags, + bool InputMayRaiseFPException, + const TargetDescription &Target) const; + + [[nodiscard]] Expected<void> propagateSelectionFlags(MachineInstr &Output, + const MachineInstr &Input, + const TargetDescription &Target) const; + +protected: + [[nodiscard]] Expected<void> + finalizeGeneratedInstruction(MachineInstr &Output, MachineFunction &Function, + const TargetDescription &Target, MachineInstrFlags InputFlags, + bool InputMayRaiseFPException) const override; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_GLOBALISEL_INSTRUCTIONSELECTOR_H diff --git a/backend/include/rcc/CodeGen/GlobalISel/LegalizationArtifactCombiner.h b/backend/include/rcc/CodeGen/GlobalISel/LegalizationArtifactCombiner.h new file mode 100644 index 0000000..12225cf --- /dev/null +++ b/backend/include/rcc/CodeGen/GlobalISel/LegalizationArtifactCombiner.h @@ -0,0 +1,1730 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_GLOBALISEL_LEGALIZATIONARTIFACTCOMBINER_H +#define RCC_CODEGEN_GLOBALISEL_LEGALIZATIONARTIFACTCOMBINER_H + +#include "rcc/CodeGen/GenericOpcodes.h" +#include "rcc/CodeGen/GlobalISel/GISelChangeObserver.h" +#include "rcc/CodeGen/GlobalISel/GISelValueTracking.h" +#include "rcc/CodeGen/GlobalISel/GenericMachineInstrs.h" +#include "rcc/CodeGen/GlobalISel/LegalizerInfo.h" +#include "rcc/CodeGen/MachineBasicBlock.h" +#include "rcc/CodeGen/MachineIRBuilder.h" +#include "rcc/CodeGen/MachineInstr.h" +#include "rcc/CodeGen/MachineOperand.h" +#include "rcc/CodeGen/MachineRegisterInfo.h" +#include "rcc/Support/Error.h" + +#include <algorithm> +#include <cstddef> +#include <cstdint> +#include <optional> +#include <ranges> +#include <span> +#include <unordered_set> +#include <utility> +#include <vector> + +namespace rcc { + +class LegalizationArtifactCombiner final { + MachineIRBuilder &Builder; + MachineRegisterInfo &MRI; + const LegalizerInfo &LI; + GISelValueTracking *VT; + + [[nodiscard]] static bool isArtifactCast(Opcode InstructionOpcode) noexcept { + switch (static_cast<GenericOpcode>(InstructionOpcode.value())) { + case GenericOpcode::G_TRUNC: + case GenericOpcode::G_SEXT: + case GenericOpcode::G_ZEXT: + case GenericOpcode::G_ANYEXT: + return true; + default: + return false; + } + } + +public: + LegalizationArtifactCombiner(MachineIRBuilder &Builder, MachineRegisterInfo &MRI, + const LegalizerInfo &Info, + GISelValueTracking *ValueTracking = nullptr) + : Builder(Builder), MRI(MRI), LI(Info), VT(ValueTracking) {} + + [[nodiscard]] Expected<bool> tryCombineInstruction(MachineInstr &Instruction, + std::vector<MachineInstr *> &DeadInstructions, + GISelObserverWrapper &Observer) { + if (!DeadInstructions.empty()) { + auto Deleted = deleteMarkedDeadInsts(DeadInstructions, Observer); + if (!Deleted) + return std::unexpected(Deleted.error()); + } + + std::vector<Register> UpdatedDefs; + Expected<bool> Changed = false; + switch (static_cast<GenericOpcode>(Instruction.opcode().value())) { + default: + return false; + case GenericOpcode::G_ANYEXT: + Changed = tryCombineAnyExt(Instruction, DeadInstructions, UpdatedDefs, Observer); + break; + case GenericOpcode::G_ZEXT: + Changed = tryCombineZExt(Instruction, DeadInstructions, UpdatedDefs, Observer); + break; + case GenericOpcode::G_SEXT: + Changed = tryCombineSExt(Instruction, DeadInstructions, UpdatedDefs, Observer); + break; + case GenericOpcode::G_UNMERGE_VALUES: + Changed = tryCombineUnmergeValues(Instruction, DeadInstructions, UpdatedDefs, Observer); + break; + case GenericOpcode::G_MERGE_VALUES: + case GenericOpcode::G_BUILD_VECTOR: + case GenericOpcode::G_CONCAT_VECTORS: { + const auto Destination = Instruction.operandAt(0)->registerValue(); + if (Destination) { + for (MachineOperand &Use : MRI.useOperands(*Destination)) { + MachineInstr *User = Use.parent(); + if (User == nullptr) + continue; + if (User->opcode() == opcode(GenericOpcode::G_UNMERGE_VALUES) || + User->opcode() == opcode(GenericOpcode::G_TRUNC)) { + UpdatedDefs.push_back(*Destination); + break; + } + } + } + Changed = tryCombineMergeLike(Instruction, DeadInstructions, UpdatedDefs, Observer); + break; + } + case GenericOpcode::G_EXTRACT: + Changed = tryCombineExtract(Instruction, DeadInstructions, UpdatedDefs); + break; + case GenericOpcode::G_TRUNC: + Changed = tryCombineTrunc(Instruction, DeadInstructions, UpdatedDefs, Observer); + if (Changed && !*Changed) { + const Register *Destination = Instruction.operandAt(0)->getIf<Register>(); + assert(Destination != nullptr); + UpdatedDefs.push_back(*Destination); + } + break; + } + if (!Changed) + return std::unexpected(Changed.error()); + + while (!UpdatedDefs.empty()) { + const Register NewDef = UpdatedDefs.back(); + UpdatedDefs.pop_back(); + std::vector<MachineInstr *> Uses; + for (MachineOperand &Use : MRI.useOperands(NewDef)) { + if (Use.parent() != nullptr) + Uses.push_back(Use.parent()); + } + for (MachineInstr *Use : Uses) { + switch (static_cast<GenericOpcode>(Use->opcode().value())) { + case GenericOpcode::G_ANYEXT: + case GenericOpcode::G_ZEXT: + case GenericOpcode::G_SEXT: + case GenericOpcode::G_UNMERGE_VALUES: + case GenericOpcode::G_EXTRACT: + case GenericOpcode::G_TRUNC: + case GenericOpcode::G_BUILD_VECTOR: + Observer.changedInstr(*Use); + break; + case GenericOpcode::G_ASSERT_SEXT: + case GenericOpcode::G_ASSERT_ZEXT: + case GenericOpcode::G_ASSERT_ALIGN: + if (const auto Copy = Use->operandAt(0)->registerValue(); Copy && Copy->isVirtual()) + UpdatedDefs.push_back(*Copy); + break; + default: + if (Use->opcode() == opcode(GenericOpcode::COPY)) { + if (const auto Copy = Use->operandAt(0)->registerValue(); Copy && Copy->isVirtual()) + UpdatedDefs.push_back(*Copy); + } + break; + } + } + } + return *Changed; + } + +private: + class ArtifactValueFinder final { + public: + explicit ArtifactValueFinder(LegalizationArtifactCombiner &Combiner) : Combiner(Combiner) {} + + [[nodiscard]] Expected<Register> findValueFromDef(Register DefReg, std::uint64_t StartBit, + std::uint64_t Size, + LowLevelType DestinationType); + [[nodiscard]] Expected<bool> tryCombineUnmergeDefs(GUnmerge &Instruction, + GISelChangeObserver &Observer, + std::vector<Register> &UpdatedDefs); + [[nodiscard]] Expected<GUnmerge *> findUnmergeThatDefinesReg(Register Reg, std::uint64_t Size, + std::size_t &DefOperandIndex); + [[nodiscard]] Expected<bool> + isSequenceFromUnmerge(GMergeLikeInstr &Instruction, std::size_t MergeStartIndex, + GUnmerge *Unmerge, std::size_t UnmergeStartIndex, + std::size_t ElementCount, std::uint64_t ElementSize, bool AllowUndef); + [[nodiscard]] Expected<bool> tryCombineMergeLike(GMergeLikeInstr &Instruction, + std::vector<MachineInstr *> &DeadInstructions, + std::vector<Register> &UpdatedDefs, + GISelChangeObserver &Observer); + + private: + [[nodiscard]] Expected<Register> findValueFromDefImpl(Register DefReg, std::uint64_t StartBit, + std::uint64_t Size, + LowLevelType DestinationType); + [[nodiscard]] Expected<Register> findValueFromConcat(GConcatVectors &Instruction, + std::uint64_t StartBit, std::uint64_t Size, + LowLevelType DestinationType); + [[nodiscard]] Expected<Register> + findValueFromBuildVector(GBuildVector &Instruction, std::uint64_t StartBit, std::uint64_t Size); + [[nodiscard]] Expected<Register> findValueFromInsert(MachineInstr &Instruction, + std::uint64_t StartBit, std::uint64_t Size, + LowLevelType DestinationType); + [[nodiscard]] Expected<Register> findValueFromExtension(MachineInstr &Instruction, + std::uint64_t StartBit, + std::uint64_t Size, + LowLevelType DestinationType); + [[nodiscard]] Expected<Register> findValueFromTrunc(MachineInstr &Instruction, + std::uint64_t StartBit, std::uint64_t Size, + LowLevelType DestinationType); + + LegalizationArtifactCombiner &Combiner; + Register CurrentBest; + std::unordered_set<Register> Visited; + }; + + [[nodiscard]] Expected<bool> tryCombineAnyExt(MachineInstr &Instruction, + std::vector<MachineInstr *> &DeadInstructions, + std::vector<Register> &UpdatedDefs, + GISelObserverWrapper &Observer); + [[nodiscard]] Expected<bool> tryCombineZExt(MachineInstr &Instruction, + std::vector<MachineInstr *> &DeadInstructions, + std::vector<Register> &UpdatedDefs, + GISelObserverWrapper &Observer); + [[nodiscard]] Expected<bool> tryCombineSExt(MachineInstr &Instruction, + std::vector<MachineInstr *> &DeadInstructions, + std::vector<Register> &UpdatedDefs, + GISelObserverWrapper &Observer); + [[nodiscard]] Expected<bool> tryCombineTrunc(MachineInstr &Instruction, + std::vector<MachineInstr *> &DeadInstructions, + std::vector<Register> &UpdatedDefs, + GISelObserverWrapper &Observer); + [[nodiscard]] Expected<bool> + tryCombineUnmergeValues(MachineInstr &Instruction, std::vector<MachineInstr *> &DeadInstructions, + std::vector<Register> &UpdatedDefs, GISelObserverWrapper &Observer); + [[nodiscard]] Expected<bool> tryCombineExtract(MachineInstr &Instruction, + std::vector<MachineInstr *> &DeadInstructions, + std::vector<Register> &UpdatedDefs); + [[nodiscard]] Expected<bool> tryCombineMergeLike(MachineInstr &Instruction, + std::vector<MachineInstr *> &DeadInstructions, + std::vector<Register> &UpdatedDefs, + GISelObserverWrapper &Observer); + [[nodiscard]] Expected<bool> tryFoldImplicitDef(MachineInstr &Instruction, + std::vector<MachineInstr *> &DeadInstructions, + std::vector<Register> &UpdatedDefs, + GISelObserverWrapper &Observer); + + [[nodiscard]] Expected<MachineInstr *> buildUnary(GenericOpcode InstructionOpcode, + Register Destination, Register Source); + [[nodiscard]] Expected<MachineInstr *> buildMergeLike(Register Destination, + const std::vector<Register> &Sources); + [[nodiscard]] Expected<void> buildConstant(Register Destination, APInt Value); + [[nodiscard]] Expected<Register> buildConstant(LowLevelType Type, APInt Value); + [[nodiscard]] Expected<bool> isConstantUnsupported(LowLevelType Type) const; + [[nodiscard]] static std::optional<std::int64_t> immediateOperand(const MachineInstr &Instruction, + std::size_t Index); + [[nodiscard]] static std::size_t getDefIndex(const MachineInstr &Instruction, Register SearchDef); + [[nodiscard]] Expected<std::pair<MachineInstr *, Register>> + definitionIgnoringCopies(Register Reg) const; + [[nodiscard]] Expected<std::vector<Register>> buildOrFindUnmerge(LowLevelType DestinationType, + Register Source); + [[nodiscard]] Expected<LowLevelType> divideType(LowLevelType Type, std::size_t Parts) const; + [[nodiscard]] Expected<bool> tryFoldUnmergeCast(MachineInstr &Instruction, MachineInstr &Cast, + std::vector<MachineInstr *> &DeadInstructions, + std::vector<Register> &UpdatedDefs); + [[nodiscard]] static bool canFoldMergeOpcode(Opcode MergeOpcode, + std::optional<Opcode> ConvertOpcode, + LowLevelType OperandType, + LowLevelType DestinationType); + + [[nodiscard]] Expected<void> replaceRegOrBuildCopy(Register Destination, Register Source, + std::vector<Register> &UpdatedDefs, + GISelChangeObserver &Observer) { + auto DestinationType = MRI.getType(Destination); + if (!DestinationType) + return std::unexpected(DestinationType.error()); + auto SourceType = MRI.getType(Source); + if (!SourceType) + return std::unexpected(SourceType.error()); + if (*DestinationType != *SourceType) { + auto Copy = Builder.buildCopy(Destination, Source); + if (!Copy) + return std::unexpected(Copy.error()); + UpdatedDefs.push_back(Destination); + return {}; + } + + std::vector<MachineInstr *> Uses; + for (MachineOperand &Use : MRI.useOperands(Destination)) { + if (Use.parent() == nullptr) + continue; + Uses.push_back(Use.parent()); + Observer.changingInstr(*Use.parent()); + } + auto Replaced = MRI.replaceRegWith(Destination, Source); + if (!Replaced) + return std::unexpected(Replaced.error()); + UpdatedDefs.push_back(Source); + for (MachineInstr *Use : Uses) + Observer.changedInstr(*Use); + return {}; + } + + [[nodiscard]] Register lookThroughCopyInstrs(Register Reg) const { + while (MachineInstr *Definition = MRI.getVRegDef(Reg)) { + if (Definition->opcode() != opcode(GenericOpcode::COPY) || Definition->operandCount() < 2) + break; + const auto Source = Definition->operandAt(1)->registerValue(); + if (!Source || !Source->isVirtual()) + break; + Reg = *Source; + } + return Reg; + } + + [[nodiscard]] static std::optional<Register> registerOperand(const MachineInstr &Instruction, + std::size_t Index) { + const MachineOperand *Operand = Instruction.operandAt(Index); + return Operand == nullptr ? std::nullopt : Operand->registerValue(); + } + + [[nodiscard]] static bool hasOpcode(const MachineInstr *Instruction, + GenericOpcode ExpectedOpcode) noexcept { + return Instruction != nullptr && Instruction->opcode() == opcode(ExpectedOpcode); + } + + void markDefDead(MachineInstr &Instruction, MachineInstr &Definition, + std::vector<MachineInstr *> &DeadInstructions, std::size_t DefIndex = 0) { + MachineInstr *Previous = &Instruction; + while (Previous != &Definition) { + const auto Source = artifactSourceRegister(*Previous); + if (!Source) + break; + MachineInstr *SourceDefinition = MRI.getVRegDef(*Source); + if (SourceDefinition == nullptr || !MRI.hasOneUse(*Source)) + break; + if (SourceDefinition != &Definition) + DeadInstructions.push_back(SourceDefinition); + Previous = SourceDefinition; + } + if (Previous != &Definition) + return; + + std::size_t CurrentDef = 0; + for (const MachineOperand &Operand : Definition.operands()) { + if (!Operand.isDef()) + continue; + const auto Reg = Operand.registerValue(); + if (!Reg) + return; + if (CurrentDef == DefIndex) { + if (!MRI.hasOneUse(*Reg)) + return; + } else if (!MRI.useEmpty(*Reg)) { + return; + } + ++CurrentDef; + } + DeadInstructions.push_back(&Definition); + } + + void markInstAndDefDead(MachineInstr &Instruction, MachineInstr &Definition, + std::vector<MachineInstr *> &DeadInstructions, std::size_t DefIndex = 0) { + DeadInstructions.push_back(&Instruction); + markDefDead(Instruction, Definition, DeadInstructions, DefIndex); + } + + [[nodiscard]] Expected<void> deleteMarkedDeadInsts(std::vector<MachineInstr *> &DeadInstructions, + GISelObserverWrapper &) { + for (MachineInstr *Dead : DeadInstructions) { + if (Dead == nullptr || Dead->parent() == nullptr) + continue; + auto Removed = Dead->eraseFromParent(); + if (!Removed) + return std::unexpected(Removed.error()); + } + DeadInstructions.clear(); + return {}; + } + + [[nodiscard]] static std::optional<Register> + artifactSourceRegister(const MachineInstr &Instruction) { + switch (static_cast<GenericOpcode>(Instruction.opcode().value())) { + case GenericOpcode::G_TRUNC: + case GenericOpcode::G_ZEXT: + case GenericOpcode::G_ANYEXT: + case GenericOpcode::G_SEXT: + case GenericOpcode::G_EXTRACT: + case GenericOpcode::G_ASSERT_SEXT: + case GenericOpcode::G_ASSERT_ZEXT: + case GenericOpcode::G_ASSERT_ALIGN: + return registerOperand(Instruction, 1); + case GenericOpcode::G_UNMERGE_VALUES: + return registerOperand(Instruction, Instruction.operandCount() - 1); + default: + if (Instruction.opcode() == opcode(GenericOpcode::COPY)) + return registerOperand(Instruction, 1); + return std::nullopt; + } + } + + [[nodiscard]] Expected<bool> isInstUnsupported(const LegalizationQuery &Query) const { + return LI.isUnsupported(Query); + } + + [[nodiscard]] Expected<bool> isInstLegal(const LegalizationQuery &Query) const { + return LI.isLegal(Query); + } +}; + +inline Expected<MachineInstr *> +LegalizationArtifactCombiner::buildUnary(GenericOpcode InstructionOpcode, Register Destination, + Register Source) { + auto Pending = Builder.buildInstr(opcode(InstructionOpcode)); + if (!Pending) + return std::unexpected(Pending.error()); + if (auto Added = Pending->addDef(Destination); !Added) + return std::unexpected(Added.error()); + if (auto Added = Pending->addUse(Source); !Added) + return std::unexpected(Added.error()); + return Pending->build(); +} + +inline Expected<MachineInstr *> +LegalizationArtifactCombiner::buildMergeLike(Register Destination, + const std::vector<Register> &Sources) { + if (Sources.empty()) + return makeUnexpected(ErrorCode::InvalidArgument, + "merge-like instruction requires source registers"); + auto DestinationType = MRI.getType(Destination); + if (!DestinationType) + return std::unexpected(DestinationType.error()); + auto SourceType = MRI.getType(Sources.front()); + if (!SourceType) + return std::unexpected(SourceType.error()); + + GenericOpcode InstructionOpcode = GenericOpcode::G_MERGE_VALUES; + if (DestinationType->isVector()) + InstructionOpcode = + SourceType->isVector() ? GenericOpcode::G_CONCAT_VECTORS : GenericOpcode::G_BUILD_VECTOR; + auto Pending = Builder.buildInstr(opcode(InstructionOpcode)); + if (!Pending) + return std::unexpected(Pending.error()); + if (auto Added = Pending->addDef(Destination); !Added) + return std::unexpected(Added.error()); + for (Register Source : Sources) { + if (auto Added = Pending->addUse(Source); !Added) + return std::unexpected(Added.error()); + } + return Pending->build(); +} + +inline Expected<void> LegalizationArtifactCombiner::buildConstant(Register Destination, + APInt Value) { + auto Built = Builder.buildConstant(Destination, std::move(Value)); + if (!Built) + return std::unexpected(Built.error()); + return {}; +} + +inline Expected<Register> LegalizationArtifactCombiner::buildConstant(LowLevelType Type, + APInt Value) { + auto Built = Builder.buildConstant(Type, std::move(Value)); + if (!Built) + return std::unexpected(Built.error()); + const MachineOperand *Definition = (*Built)->operandAt(0); + const Register *Destination = Definition == nullptr ? nullptr : Definition->getIf<Register>(); + if (Destination == nullptr || !Definition->isDef()) { + return makeUnexpected(ErrorCode::InternalError, + "constant builder returned an instruction without a register definition"); + } + return *Destination; +} + +inline Expected<bool> LegalizationArtifactCombiner::isConstantUnsupported(LowLevelType Type) const { + if (!Type.isVector()) + return isInstUnsupported(LegalizationQuery(opcode(GenericOpcode::G_CONSTANT), {Type})); + const auto ElementType = Type.scalarElementType(); + if (!ElementType) + return true; + auto ConstantUnsupported = + isInstUnsupported(LegalizationQuery(opcode(GenericOpcode::G_CONSTANT), {*ElementType})); + if (!ConstantUnsupported || *ConstantUnsupported) + return ConstantUnsupported; + return isInstUnsupported( + LegalizationQuery(opcode(GenericOpcode::G_BUILD_VECTOR), {Type, *ElementType})); +} + +inline std::optional<std::int64_t> +LegalizationArtifactCombiner::immediateOperand(const MachineInstr &Instruction, std::size_t Index) { + const MachineOperand *Operand = Instruction.operandAt(Index); + if (Operand == nullptr) + return std::nullopt; + if (const auto *Value = Operand->getIf<std::int64_t>()) + return *Value; + return std::nullopt; +} + +inline std::size_t LegalizationArtifactCombiner::getDefIndex(const MachineInstr &Instruction, + Register SearchDef) { + std::size_t Index = 0; + for (const MachineOperand &Operand : Instruction.operands()) { + if (!Operand.isDef()) + continue; + if (Operand.registerValue() == SearchDef) + return Index; + ++Index; + } + return Index; +} + +inline Expected<std::pair<MachineInstr *, Register>> +LegalizationArtifactCombiner::definitionIgnoringCopies(Register Reg) const { + std::unordered_set<Register> Visited; + while (Reg.isVirtual() && Visited.insert(Reg).second) { + MachineInstr *Definition = MRI.getVRegDef(Reg); + if (Definition == nullptr) + return std::pair<MachineInstr *, Register>{nullptr, Reg}; + if (Definition->opcode() != opcode(GenericOpcode::COPY)) + return std::pair{Definition, Reg}; + const auto Source = registerOperand(*Definition, 1); + if (!Source) + return std::pair{Definition, Reg}; + Reg = *Source; + } + return std::pair<MachineInstr *, Register>{nullptr, Reg}; +} + +inline Expected<LowLevelType> LegalizationArtifactCombiner::divideType(LowLevelType Type, + std::size_t Parts) const { + if (Parts == 0 || Type.minimumBitWidth() % Parts != 0) + return makeUnexpected(ErrorCode::InvalidArgument, + "LowLevelType cannot be divided into equal parts"); + if (!Type.isVector()) + return LowLevelType::makeCategoryNeutralScalar(Type.scalarBitWidth() / Parts); + if (Type.minimumElementCount() % Parts != 0) + return makeUnexpected(ErrorCode::InvalidArgument, + "vector element count cannot be divided into equal parts"); + const auto ElementType = Type.scalarElementType(); + if (!ElementType) + return makeUnexpected(ErrorCode::InvalidArgument, "vector has no scalar element type"); + const std::uint32_t Elements = Type.minimumElementCount() / static_cast<std::uint32_t>(Parts); + if (Elements == 1) + return *ElementType; + return Type.isScalableVector() ? LowLevelType::makeScalableVector(Elements, *ElementType) + : LowLevelType::makeFixedVector(Elements, *ElementType); +} + +inline Expected<std::vector<Register>> +LegalizationArtifactCombiner::buildOrFindUnmerge(LowLevelType DestinationType, Register Source) { + auto SourceType = MRI.getType(Source); + if (!SourceType) + return std::unexpected(SourceType.error()); + if (DestinationType.minimumBitWidth() == 0 || + SourceType->minimumBitWidth() % DestinationType.minimumBitWidth() != 0) + return makeUnexpected(ErrorCode::InvalidArgument, + "unmerge destination does not tile the source type"); + const std::size_t Count = + static_cast<std::size_t>(SourceType->minimumBitWidth() / DestinationType.minimumBitWidth()); + + for (MachineOperand &Use : MRI.useOperands(Source)) { + MachineInstr *Candidate = Use.parent(); + if (!GUnmerge::classof(Candidate) || Candidate->operandCount() != Count + 1) + continue; + std::vector<Register> Existing; + Existing.reserve(Count); + bool Matches = true; + for (std::size_t Index = 0; Index < Count; ++Index) { + const auto Reg = registerOperand(*Candidate, Index); + if (!Reg) { + Matches = false; + break; + } + auto Type = MRI.getType(*Reg); + if (!Type || *Type != DestinationType) { + Matches = false; + break; + } + Existing.push_back(*Reg); + } + if (Matches) + return Existing; + } + + std::vector<Register> Destinations; + Destinations.reserve(Count); + for (std::size_t Index = 0; Index < Count; ++Index) { + auto Destination = MRI.createVirtualRegister(DestinationType); + if (!Destination) + return std::unexpected(Destination.error()); + Destinations.push_back(*Destination); + } + auto Built = Builder.buildUnmergeValues(Destinations, Source); + if (!Built) + return std::unexpected(Built.error()); + return Destinations; +} + +inline bool LegalizationArtifactCombiner::canFoldMergeOpcode(Opcode MergeOpcode, + std::optional<Opcode> ConvertOpcode, + LowLevelType OperandType, + LowLevelType DestinationType) { + const auto Generic = static_cast<GenericOpcode>(MergeOpcode.value()); + if (Generic == GenericOpcode::G_BUILD_VECTOR || Generic == GenericOpcode::G_MERGE_VALUES) { + if (!ConvertOpcode) + return true; + const auto ElementType = OperandType.scalarElementType(); + return !DestinationType.isVector() && OperandType.isVector() && ElementType && + DestinationType == *ElementType; + } + if (Generic != GenericOpcode::G_CONCAT_VECTORS) + return false; + if (!ConvertOpcode) + return true; + if (!DestinationType.isVector()) + return false; + const auto ElementType = OperandType.scalarElementType(); + if (!ElementType) + return false; + if (*ConvertOpcode == opcode(GenericOpcode::G_TRUNC)) + return DestinationType.minimumBitWidth() <= ElementType->minimumBitWidth(); + return DestinationType.minimumBitWidth() >= ElementType->minimumBitWidth(); +} + +inline Expected<bool> LegalizationArtifactCombiner::tryCombineAnyExt( + MachineInstr &Instruction, std::vector<MachineInstr *> &DeadInstructions, + std::vector<Register> &UpdatedDefs, GISelObserverWrapper &Observer) { + const auto Destination = registerOperand(Instruction, 0); + const auto OriginalSource = registerOperand(Instruction, 1); + if (!Destination || !OriginalSource) + return makeUnexpected(ErrorCode::VerificationError, "G_ANYEXT has invalid register operands"); + const Register Source = lookThroughCopyInstrs(*OriginalSource); + MachineInstr *SourceDefinition = MRI.getVRegDef(Source); + if (SourceDefinition == nullptr) + return false; + if (auto Point = Builder.setInsertPointBefore(Instruction); !Point) + return std::unexpected(Point.error()); + + if (hasOpcode(SourceDefinition, GenericOpcode::G_TRUNC)) { + const auto TruncSource = registerOperand(*SourceDefinition, 1); + if (!TruncSource) + return false; + auto DestinationType = MRI.getType(*Destination); + if (!DestinationType) + return std::unexpected(DestinationType.error()); + auto TruncSourceType = MRI.getType(*TruncSource); + if (!TruncSourceType) + return std::unexpected(TruncSourceType.error()); + if (*DestinationType == *TruncSourceType) { + auto Replaced = replaceRegOrBuildCopy(*Destination, *TruncSource, UpdatedDefs, Observer); + if (!Replaced) + return std::unexpected(Replaced.error()); + } else { + auto Built = Builder.buildAnyExtOrTrunc(*Destination, *TruncSource); + if (!Built) + return std::unexpected(Built.error()); + UpdatedDefs.push_back(*Destination); + } + markInstAndDefDead(Instruction, *SourceDefinition, DeadInstructions); + return true; + } + + if (hasOpcode(SourceDefinition, GenericOpcode::G_ANYEXT) || + hasOpcode(SourceDefinition, GenericOpcode::G_SEXT) || + hasOpcode(SourceDefinition, GenericOpcode::G_ZEXT)) { + const auto ExtensionSource = registerOperand(*SourceDefinition, 1); + if (!ExtensionSource) + return false; + auto Built = buildUnary(static_cast<GenericOpcode>(SourceDefinition->opcode().value()), + *Destination, *ExtensionSource); + if (!Built) + return std::unexpected(Built.error()); + UpdatedDefs.push_back(*Destination); + markInstAndDefDead(Instruction, *SourceDefinition, DeadInstructions); + return true; + } + + if (hasOpcode(SourceDefinition, GenericOpcode::G_CONSTANT)) { + auto DestinationType = MRI.getType(*Destination); + if (!DestinationType) + return std::unexpected(DestinationType.error()); + auto Legal = + isInstLegal(LegalizationQuery(opcode(GenericOpcode::G_CONSTANT), {*DestinationType})); + if (!Legal) + return std::unexpected(Legal.error()); + const auto *Immediate = SourceDefinition->operandAt(1)->getIf<IntegerImmediateOperand>(); + if (*Legal && Immediate != nullptr) { + auto Extended = Immediate->Value.sext(DestinationType->scalarBitWidth()); + if (!Extended) + return std::unexpected(Extended.error()); + auto Built = buildConstant(*Destination, std::move(*Extended)); + if (!Built) + return std::unexpected(Built.error()); + UpdatedDefs.push_back(*Destination); + markInstAndDefDead(Instruction, *SourceDefinition, DeadInstructions); + return true; + } + } + return tryFoldImplicitDef(Instruction, DeadInstructions, UpdatedDefs, Observer); +} + +inline Expected<bool> LegalizationArtifactCombiner::tryCombineZExt( + MachineInstr &Instruction, std::vector<MachineInstr *> &DeadInstructions, + std::vector<Register> &UpdatedDefs, GISelObserverWrapper &Observer) { + const auto Destination = registerOperand(Instruction, 0); + const auto OriginalSource = registerOperand(Instruction, 1); + if (!Destination || !OriginalSource) + return makeUnexpected(ErrorCode::VerificationError, "G_ZEXT has invalid register operands"); + const Register Source = lookThroughCopyInstrs(*OriginalSource); + MachineInstr *SourceDefinition = MRI.getVRegDef(Source); + if (SourceDefinition == nullptr) + return false; + if (auto Point = Builder.setInsertPointBefore(Instruction); !Point) + return std::unexpected(Point.error()); + + if (hasOpcode(SourceDefinition, GenericOpcode::G_TRUNC) || + hasOpcode(SourceDefinition, GenericOpcode::G_SEXT)) { + const auto NarrowSource = registerOperand(*SourceDefinition, 1); + if (!NarrowSource) + return false; + auto DestinationType = MRI.getType(*Destination); + if (!DestinationType) + return std::unexpected(DestinationType.error()); + auto SourceType = MRI.getType(Source); + if (!SourceType) + return std::unexpected(SourceType.error()); + auto AndUnsupported = + isInstUnsupported(LegalizationQuery(opcode(GenericOpcode::G_AND), {*DestinationType})); + if (!AndUnsupported) + return std::unexpected(AndUnsupported.error()); + auto ConstantUnsupported = isConstantUnsupported(*DestinationType); + if (!ConstantUnsupported) + return std::unexpected(ConstantUnsupported.error()); + if (*AndUnsupported || *ConstantUnsupported) + return false; + + Register WideSource = *NarrowSource; + auto WideSourceType = MRI.getType(WideSource); + if (!WideSourceType) + return std::unexpected(WideSourceType.error()); + if (*WideSourceType != *DestinationType) { + auto Wide = MRI.createVirtualRegister(*DestinationType); + if (!Wide) + return std::unexpected(Wide.error()); + auto Built = hasOpcode(SourceDefinition, GenericOpcode::G_SEXT) + ? Builder.buildSExtOrTrunc(*Wide, WideSource) + : Builder.buildAnyExtOrTrunc(*Wide, WideSource); + if (!Built) + return std::unexpected(Built.error()); + WideSource = *Wide; + } + auto Mask = + APInt::getLowBitsSet(DestinationType->scalarBitWidth(), SourceType->scalarBitWidth()); + if (!Mask) + return std::unexpected(Mask.error()); + bool ElideAnd = false; + if (VT != nullptr) { + auto KnownZeroes = VT->getKnownZeroes(WideSource); + if (!KnownZeroes) + return std::unexpected(KnownZeroes.error()); + auto CoveredBits = KnownZeroes->bitwiseOr(*Mask); + if (!CoveredBits) + return std::unexpected(CoveredBits.error()); + ElideAnd = CoveredBits->isAllOnes(); + } + if (ElideAnd) { + auto Replaced = replaceRegOrBuildCopy(*Destination, WideSource, UpdatedDefs, Observer); + if (!Replaced) + return std::unexpected(Replaced.error()); + } else { + auto MaskRegister = buildConstant(*DestinationType, std::move(*Mask)); + if (!MaskRegister) + return std::unexpected(MaskRegister.error()); + auto Built = Builder.buildAnd(*Destination, WideSource, *MaskRegister); + if (!Built) + return std::unexpected(Built.error()); + } + UpdatedDefs.push_back(*Destination); + markInstAndDefDead(Instruction, *SourceDefinition, DeadInstructions); + return true; + } + + if (hasOpcode(SourceDefinition, GenericOpcode::G_ZEXT)) { + const auto ExtensionSource = registerOperand(*SourceDefinition, 1); + if (!ExtensionSource) + return false; + Observer.changingInstr(Instruction); + auto Updated = Instruction.operandAt(1)->setRegister(*ExtensionSource); + if (!Updated) + return std::unexpected(Updated.error()); + Observer.changedInstr(Instruction); + UpdatedDefs.push_back(*Destination); + markDefDead(Instruction, *SourceDefinition, DeadInstructions); + return true; + } + + if (hasOpcode(SourceDefinition, GenericOpcode::G_CONSTANT)) { + auto DestinationType = MRI.getType(*Destination); + if (!DestinationType) + return std::unexpected(DestinationType.error()); + auto Legal = + isInstLegal(LegalizationQuery(opcode(GenericOpcode::G_CONSTANT), {*DestinationType})); + if (!Legal) + return std::unexpected(Legal.error()); + const auto *Immediate = SourceDefinition->operandAt(1)->getIf<IntegerImmediateOperand>(); + if (*Legal && Immediate != nullptr) { + auto Extended = Immediate->Value.zext(DestinationType->scalarBitWidth()); + if (!Extended) + return std::unexpected(Extended.error()); + auto Built = buildConstant(*Destination, std::move(*Extended)); + if (!Built) + return std::unexpected(Built.error()); + UpdatedDefs.push_back(*Destination); + markInstAndDefDead(Instruction, *SourceDefinition, DeadInstructions); + return true; + } + } + return tryFoldImplicitDef(Instruction, DeadInstructions, UpdatedDefs, Observer); +} + +inline Expected<bool> LegalizationArtifactCombiner::tryCombineSExt( + MachineInstr &Instruction, std::vector<MachineInstr *> &DeadInstructions, + std::vector<Register> &UpdatedDefs, GISelObserverWrapper &Observer) { + const auto Destination = registerOperand(Instruction, 0); + const auto OriginalSource = registerOperand(Instruction, 1); + if (!Destination || !OriginalSource) + return makeUnexpected(ErrorCode::VerificationError, "G_SEXT has invalid register operands"); + const Register Source = lookThroughCopyInstrs(*OriginalSource); + MachineInstr *SourceDefinition = MRI.getVRegDef(Source); + if (SourceDefinition == nullptr) + return false; + if (auto Point = Builder.setInsertPointBefore(Instruction); !Point) + return std::unexpected(Point.error()); + + if (hasOpcode(SourceDefinition, GenericOpcode::G_TRUNC)) { + const auto TruncSource = registerOperand(*SourceDefinition, 1); + if (!TruncSource) + return false; + auto DestinationType = MRI.getType(*Destination); + if (!DestinationType) + return std::unexpected(DestinationType.error()); + auto SourceType = MRI.getType(Source); + if (!SourceType) + return std::unexpected(SourceType.error()); + auto Unsupported = isInstUnsupported( + LegalizationQuery(opcode(GenericOpcode::G_SEXT_INREG), {*DestinationType})); + if (!Unsupported) + return std::unexpected(Unsupported.error()); + if (*Unsupported) + return false; + Register WideSource = *TruncSource; + auto WideSourceType = MRI.getType(WideSource); + if (!WideSourceType) + return std::unexpected(WideSourceType.error()); + if (*WideSourceType != *DestinationType) { + auto Wide = MRI.createVirtualRegister(*DestinationType); + if (!Wide) + return std::unexpected(Wide.error()); + auto Built = Builder.buildAnyExtOrTrunc(*Wide, WideSource); + if (!Built) + return std::unexpected(Built.error()); + WideSource = *Wide; + } + bool ElideSExtInReg = false; + if (VT != nullptr) { + auto SignBits = VT->computeNumSignBits(WideSource); + if (!SignBits) + return std::unexpected(SignBits.error()); + ElideSExtInReg = *SignBits > DestinationType->scalarBitWidth() - SourceType->scalarBitWidth(); + } + if (ElideSExtInReg) { + auto Replaced = replaceRegOrBuildCopy(*Destination, WideSource, UpdatedDefs, Observer); + if (!Replaced) + return std::unexpected(Replaced.error()); + } else { + auto Built = Builder.buildSExtInReg(*Destination, WideSource, SourceType->scalarBitWidth()); + if (!Built) + return std::unexpected(Built.error()); + } + UpdatedDefs.push_back(*Destination); + markInstAndDefDead(Instruction, *SourceDefinition, DeadInstructions); + return true; + } + + if (hasOpcode(SourceDefinition, GenericOpcode::G_ZEXT) || + hasOpcode(SourceDefinition, GenericOpcode::G_SEXT)) { + const auto ExtensionSource = registerOperand(*SourceDefinition, 1); + if (!ExtensionSource) + return false; + auto Built = buildUnary(static_cast<GenericOpcode>(SourceDefinition->opcode().value()), + *Destination, *ExtensionSource); + if (!Built) + return std::unexpected(Built.error()); + UpdatedDefs.push_back(*Destination); + markInstAndDefDead(Instruction, *SourceDefinition, DeadInstructions); + return true; + } + + if (hasOpcode(SourceDefinition, GenericOpcode::G_CONSTANT)) { + auto DestinationType = MRI.getType(*Destination); + if (!DestinationType) + return std::unexpected(DestinationType.error()); + auto Legal = + isInstLegal(LegalizationQuery(opcode(GenericOpcode::G_CONSTANT), {*DestinationType})); + if (!Legal) + return std::unexpected(Legal.error()); + const auto *Immediate = SourceDefinition->operandAt(1)->getIf<IntegerImmediateOperand>(); + if (*Legal && Immediate != nullptr) { + auto Extended = Immediate->Value.sext(DestinationType->scalarBitWidth()); + if (!Extended) + return std::unexpected(Extended.error()); + auto Built = buildConstant(*Destination, std::move(*Extended)); + if (!Built) + return std::unexpected(Built.error()); + UpdatedDefs.push_back(*Destination); + markInstAndDefDead(Instruction, *SourceDefinition, DeadInstructions); + return true; + } + } + return tryFoldImplicitDef(Instruction, DeadInstructions, UpdatedDefs, Observer); +} + +inline Expected<bool> LegalizationArtifactCombiner::tryFoldImplicitDef( + MachineInstr &Instruction, std::vector<MachineInstr *> &DeadInstructions, + std::vector<Register> &UpdatedDefs, GISelObserverWrapper &) { + const auto Destination = registerOperand(Instruction, 0); + const auto Source = registerOperand(Instruction, 1); + if (!Destination || !Source) + return false; + MachineInstr *Definition = MRI.getVRegDef(lookThroughCopyInstrs(*Source)); + if (!hasOpcode(Definition, GenericOpcode::G_IMPLICIT_DEF)) + return false; + auto DestinationType = MRI.getType(*Destination); + if (!DestinationType) + return std::unexpected(DestinationType.error()); + if (auto Point = Builder.setInsertPointBefore(Instruction); !Point) + return std::unexpected(Point.error()); + if (Instruction.opcode() == opcode(GenericOpcode::G_ANYEXT)) { + auto Legal = + isInstLegal(LegalizationQuery(opcode(GenericOpcode::G_IMPLICIT_DEF), {*DestinationType})); + if (!Legal) + return std::unexpected(Legal.error()); + if (!*Legal) + return false; + auto Built = Builder.buildImplicitDef(*Destination); + if (!Built) + return std::unexpected(Built.error()); + } else { + auto Unsupported = isConstantUnsupported(*DestinationType); + if (!Unsupported) + return std::unexpected(Unsupported.error()); + if (*Unsupported) + return false; + auto Built = Builder.buildConstant(*Destination, 0); + if (!Built) + return std::unexpected(Built.error()); + } + UpdatedDefs.push_back(*Destination); + markInstAndDefDead(Instruction, *Definition, DeadInstructions); + return true; +} + +inline Expected<bool> LegalizationArtifactCombiner::tryCombineTrunc( + MachineInstr &Instruction, std::vector<MachineInstr *> &DeadInstructions, + std::vector<Register> &UpdatedDefs, GISelObserverWrapper &Observer) { + const auto Destination = registerOperand(Instruction, 0); + const auto OriginalSource = registerOperand(Instruction, 1); + if (!Destination || !OriginalSource) + return makeUnexpected(ErrorCode::VerificationError, "G_TRUNC has invalid register operands"); + const Register Source = lookThroughCopyInstrs(*OriginalSource); + MachineInstr *SourceDefinition = MRI.getVRegDef(Source); + if (SourceDefinition == nullptr) + return false; + auto DestinationType = MRI.getType(*Destination); + if (!DestinationType) + return std::unexpected(DestinationType.error()); + if (auto Point = Builder.setInsertPointBefore(Instruction); !Point) + return std::unexpected(Point.error()); + + if (hasOpcode(SourceDefinition, GenericOpcode::G_CONSTANT)) { + auto Legal = + isInstLegal(LegalizationQuery(opcode(GenericOpcode::G_CONSTANT), {*DestinationType})); + if (!Legal) + return std::unexpected(Legal.error()); + const auto *Immediate = SourceDefinition->operandAt(1)->getIf<IntegerImmediateOperand>(); + if (*Legal && Immediate != nullptr) { + auto Truncated = Immediate->Value.trunc(DestinationType->scalarBitWidth()); + if (!Truncated) + return std::unexpected(Truncated.error()); + auto Built = buildConstant(*Destination, std::move(*Truncated)); + if (!Built) + return std::unexpected(Built.error()); + UpdatedDefs.push_back(*Destination); + markInstAndDefDead(Instruction, *SourceDefinition, DeadInstructions); + return true; + } + } + + if (GMerge::classof(SourceDefinition)) { + auto &Merge = castGenericMachineInstr<GMerge>(*SourceDefinition); + if (Merge.getNumSources() == 0) + return false; + const Register FirstSource = Merge.getSourceReg(0); + auto FirstSourceType = MRI.getType(FirstSource); + if (!FirstSourceType) + return std::unexpected(FirstSourceType.error()); + if (!DestinationType->isCategoryNeutralScalar() || !FirstSourceType->isCategoryNeutralScalar()) + return false; + const std::uint64_t DestinationSize = DestinationType->minimumBitWidth(); + const std::uint64_t SourceSize = FirstSourceType->minimumBitWidth(); + if (DestinationSize < SourceSize) { + auto Unsupported = isInstUnsupported( + LegalizationQuery(opcode(GenericOpcode::G_TRUNC), {*DestinationType, *FirstSourceType})); + if (!Unsupported) + return std::unexpected(Unsupported.error()); + if (*Unsupported) + return false; + auto Built = Builder.buildTrunc(*Destination, FirstSource); + if (!Built) + return std::unexpected(Built.error()); + UpdatedDefs.push_back(*Destination); + } else if (DestinationSize == SourceSize) { + auto Replaced = replaceRegOrBuildCopy(*Destination, FirstSource, UpdatedDefs, Observer); + if (!Replaced) + return std::unexpected(Replaced.error()); + } else if (DestinationSize % SourceSize == 0) { + const std::size_t SourceCount = static_cast<std::size_t>(DestinationSize / SourceSize); + if (SourceCount >= Merge.getNumSources()) + return false; + auto Unsupported = isInstUnsupported(LegalizationQuery(opcode(GenericOpcode::G_MERGE_VALUES), + {*DestinationType, *FirstSourceType})); + if (!Unsupported) + return std::unexpected(Unsupported.error()); + if (*Unsupported) + return false; + std::vector<Register> Sources; + Sources.reserve(SourceCount); + for (std::size_t Index = 0; Index < SourceCount; ++Index) + Sources.push_back(Merge.getSourceReg(Index)); + auto Built = Builder.buildMergeValues(*Destination, Sources); + if (!Built) + return std::unexpected(Built.error()); + UpdatedDefs.push_back(*Destination); + } else { + return false; + } + markInstAndDefDead(Instruction, *SourceDefinition, DeadInstructions); + return true; + } + + if (hasOpcode(SourceDefinition, GenericOpcode::G_TRUNC)) { + const auto TruncSource = registerOperand(*SourceDefinition, 1); + if (!TruncSource) + return false; + auto Built = Builder.buildTrunc(*Destination, *TruncSource); + if (!Built) + return std::unexpected(Built.error()); + UpdatedDefs.push_back(*Destination); + markInstAndDefDead(Instruction, *SourceDefinition, DeadInstructions); + return true; + } + + ArtifactValueFinder Finder(*this); + auto Found = Finder.findValueFromDef(*Destination, 0, DestinationType->minimumBitWidth(), + *DestinationType); + if (!Found) + return std::unexpected(Found.error()); + if (!Found->isNoRegister()) { + auto FoundType = MRI.getType(*Found); + if (!FoundType) + return std::unexpected(FoundType.error()); + if (*FoundType == *DestinationType) { + auto Replaced = replaceRegOrBuildCopy(*Destination, *Found, UpdatedDefs, Observer); + if (!Replaced) + return std::unexpected(Replaced.error()); + markInstAndDefDead(Instruction, *SourceDefinition, DeadInstructions); + return true; + } + } + return false; +} + +inline Expected<bool> LegalizationArtifactCombiner::ArtifactValueFinder::tryCombineMergeLike( + GMergeLikeInstr &Instruction, std::vector<MachineInstr *> &DeadInstructions, + std::vector<Register> &UpdatedDefs, GISelChangeObserver &Observer) { + if (Instruction.getNumSources() == 0) + return false; + const Register FirstElement = Instruction.getSourceReg(0); + auto ElementType = Combiner.MRI.getType(FirstElement); + if (!ElementType) + return std::unexpected(ElementType.error()); + const std::uint64_t ElementSize = ElementType->minimumBitWidth(); + std::size_t FirstUnmergeIndex = 0; + auto UnmergeResult = findUnmergeThatDefinesReg(FirstElement, ElementSize, FirstUnmergeIndex); + if (!UnmergeResult) + return std::unexpected(UnmergeResult.error()); + GUnmerge *Unmerge = *UnmergeResult; + if (Unmerge == nullptr) + return false; + + const Register Destination = Instruction.getReg(0); + const Register UnmergeSource = Unmerge->getSourceReg(); + auto DestinationType = Combiner.MRI.getType(Destination); + if (!DestinationType) + return std::unexpected(DestinationType.error()); + auto UnmergeSourceType = Combiner.MRI.getType(UnmergeSource); + if (!UnmergeSourceType) + return std::unexpected(UnmergeSourceType.error()); + const std::size_t SourceCount = Instruction.getNumSources(); + + if (*DestinationType == *UnmergeSourceType && FirstUnmergeIndex == 0) { + auto Sequence = isSequenceFromUnmerge(Instruction, 0, Unmerge, 0, SourceCount, ElementSize, + DestinationType->isVector()); + if (!Sequence) + return std::unexpected(Sequence.error()); + if (!*Sequence) + return false; + auto Replaced = + Combiner.replaceRegOrBuildCopy(Destination, UnmergeSource, UpdatedDefs, Observer); + if (!Replaced) + return std::unexpected(Replaced.error()); + DeadInstructions.push_back(&Instruction); + return true; + } + + const bool MatchingDomains = + (!DestinationType->isVector() && !UnmergeSourceType->isVector()) || + (DestinationType->isVector() && UnmergeSourceType->isVector() && + DestinationType->scalarElementType() == UnmergeSourceType->scalarElementType()); + if (MatchingDomains && FirstUnmergeIndex % SourceCount == 0 && + UnmergeSourceType->minimumBitWidth() % DestinationType->minimumBitWidth() == 0) { + auto Sequence = isSequenceFromUnmerge(Instruction, 0, Unmerge, FirstUnmergeIndex, SourceCount, + ElementSize, false); + if (!Sequence) + return std::unexpected(Sequence.error()); + if (*Sequence) { + auto NewUnmerge = Combiner.buildOrFindUnmerge(*DestinationType, UnmergeSource); + if (!NewUnmerge) + return std::unexpected(NewUnmerge.error()); + const std::size_t DestinationIndex = static_cast<std::size_t>( + (FirstUnmergeIndex * ElementSize) / DestinationType->minimumBitWidth()); + if (DestinationIndex >= NewUnmerge->size()) + return false; + auto Replaced = Combiner.replaceRegOrBuildCopy(Destination, (*NewUnmerge)[DestinationIndex], + UpdatedDefs, Observer); + if (!Replaced) + return std::unexpected(Replaced.error()); + DeadInstructions.push_back(&Instruction); + return true; + } + } + + if (DestinationType->isVector() == UnmergeSourceType->isVector() && + DestinationType->minimumBitWidth() % UnmergeSourceType->minimumBitWidth() == 0) { + const std::size_t UnmergeElementCount = Unmerge->getNumDefs(); + if (UnmergeElementCount == 0 || SourceCount % UnmergeElementCount != 0) + return false; + std::vector<Register> MergeSources; + for (std::size_t Start = 0; Start < SourceCount; Start += UnmergeElementCount) { + std::size_t SourceDefIndex = 0; + auto SourceUnmerge = + findUnmergeThatDefinesReg(Instruction.getSourceReg(Start), ElementSize, SourceDefIndex); + if (!SourceUnmerge) + return std::unexpected(SourceUnmerge.error()); + if (*SourceUnmerge == nullptr || (*SourceUnmerge)->getNumDefs() != UnmergeElementCount || + SourceDefIndex != 0) + return false; + auto Sequence = isSequenceFromUnmerge(Instruction, Start, *SourceUnmerge, 0, + UnmergeElementCount, ElementSize, false); + if (!Sequence) + return std::unexpected(Sequence.error()); + if (!*Sequence) + return false; + MergeSources.push_back((*SourceUnmerge)->getSourceReg()); + } + auto Built = Combiner.buildMergeLike(Destination, MergeSources); + if (!Built) + return std::unexpected(Built.error()); + UpdatedDefs.push_back(Destination); + DeadInstructions.push_back(&Instruction); + return true; + } + return false; +} + +inline Expected<bool> LegalizationArtifactCombiner::tryCombineMergeLike( + MachineInstr &Instruction, std::vector<MachineInstr *> &DeadInstructions, + std::vector<Register> &UpdatedDefs, GISelObserverWrapper &Observer) { + if (!GMergeLikeInstr::classof(&Instruction)) + return false; + ArtifactValueFinder Finder(*this); + return Finder.tryCombineMergeLike(castGenericMachineInstr<GMergeLikeInstr>(Instruction), + DeadInstructions, UpdatedDefs, Observer); +} + +inline Expected<bool> +LegalizationArtifactCombiner::tryFoldUnmergeCast(MachineInstr &Instruction, MachineInstr &Cast, + std::vector<MachineInstr *> &DeadInstructions, + std::vector<Register> &UpdatedDefs) { + if (!isArtifactCast(Cast.opcode()) || Cast.opcode() != opcode(GenericOpcode::G_TRUNC)) + return false; + const auto CastSource = registerOperand(Cast, 1); + if (!CastSource) + return false; + auto CastSourceType = MRI.getType(*CastSource); + if (!CastSourceType) + return std::unexpected(CastSourceType.error()); + const std::size_t OriginalDefCount = Instruction.operandCount() - 1; + if (OriginalDefCount == 0) + return false; + const auto FirstDestination = registerOperand(Instruction, 0); + if (!FirstDestination) + return false; + auto DestinationType = MRI.getType(*FirstDestination); + if (!DestinationType) + return std::unexpected(DestinationType.error()); + if (DestinationType->isVector() || !CastSourceType->isCategoryNeutralScalar() || + CastSourceType->minimumBitWidth() % DestinationType->minimumBitWidth() != 0) + return false; + auto Unsupported = isInstUnsupported(LegalizationQuery(opcode(GenericOpcode::G_UNMERGE_VALUES), + {*DestinationType, *CastSourceType})); + if (!Unsupported) + return std::unexpected(Unsupported.error()); + if (*Unsupported) + return false; + const std::size_t NewDefCount = static_cast<std::size_t>(CastSourceType->minimumBitWidth() / + DestinationType->minimumBitWidth()); + std::vector<Register> Destinations; + Destinations.reserve(NewDefCount); + for (std::size_t Index = 0; Index < NewDefCount; ++Index) { + if (Index < OriginalDefCount) { + const auto Destination = registerOperand(Instruction, Index); + if (!Destination) + return false; + Destinations.push_back(*Destination); + } else { + auto Destination = MRI.createVirtualRegister(*DestinationType); + if (!Destination) + return std::unexpected(Destination.error()); + Destinations.push_back(*Destination); + } + } + if (auto Point = Builder.setInsertPointBefore(Instruction); !Point) + return std::unexpected(Point.error()); + auto Built = Builder.buildUnmergeValues(Destinations, *CastSource); + if (!Built) + return std::unexpected(Built.error()); + UpdatedDefs.insert(UpdatedDefs.end(), Destinations.begin(), + Destinations.begin() + static_cast<std::ptrdiff_t>(OriginalDefCount)); + markInstAndDefDead(Instruction, Cast, DeadInstructions); + return true; +} + +inline Expected<bool> LegalizationArtifactCombiner::tryCombineUnmergeValues( + MachineInstr &Instruction, std::vector<MachineInstr *> &DeadInstructions, + std::vector<Register> &UpdatedDefs, GISelObserverWrapper &Observer) { + if (!GUnmerge::classof(&Instruction)) + return false; + auto &Unmerge = castGenericMachineInstr<GUnmerge>(Instruction); + if (Unmerge.getNumDefs() == 0) + return false; + const Register Source = Unmerge.getSourceReg(); + auto Definition = definitionIgnoringCopies(Source); + if (!Definition) + return std::unexpected(Definition.error()); + MachineInstr *SourceDefinition = Definition->first; + if (SourceDefinition == nullptr) + return false; + const std::size_t SourceDefIndex = getDefIndex(*SourceDefinition, Definition->second); + auto OperandType = MRI.getType(Source); + if (!OperandType) + return std::unexpected(OperandType.error()); + auto DestinationType = MRI.getType(Unmerge.getReg(0)); + if (!DestinationType) + return std::unexpected(DestinationType.error()); + if (auto Point = Builder.setInsertPointBefore(Instruction); !Point) + return std::unexpected(Point.error()); + + ArtifactValueFinder Finder(*this); + auto AllCombined = Finder.tryCombineUnmergeDefs(Unmerge, Observer, UpdatedDefs); + if (!AllCombined) + return std::unexpected(AllCombined.error()); + if (*AllCombined) { + markInstAndDefDead(Instruction, *SourceDefinition, DeadInstructions, SourceDefIndex); + return true; + } + + if (GUnmerge::classof(SourceDefinition)) { + auto &SourceUnmerge = castGenericMachineInstr<GUnmerge>(*SourceDefinition); + const Register OriginalSource = SourceUnmerge.getSourceReg(); + auto OriginalSourceType = MRI.getType(OriginalSource); + if (!OriginalSourceType) + return std::unexpected(OriginalSourceType.error()); + auto NewUnmerge = buildOrFindUnmerge(*DestinationType, OriginalSource); + if (!NewUnmerge) + return std::unexpected(NewUnmerge.error()); + const std::size_t BaseIndex = SourceDefIndex * Unmerge.getNumDefs(); + if (BaseIndex + Unmerge.getNumDefs() > NewUnmerge->size()) + return false; + for (std::size_t Index = 0; Index < Unmerge.getNumDefs(); ++Index) { + auto Replaced = replaceRegOrBuildCopy(Unmerge.getReg(Index), (*NewUnmerge)[BaseIndex + Index], + UpdatedDefs, Observer); + if (!Replaced) + return std::unexpected(Replaced.error()); + } + markInstAndDefDead(Instruction, *SourceDefinition, DeadInstructions, SourceDefIndex); + return true; + } + + MachineInstr *Merge = SourceDefinition; + std::optional<Opcode> ConvertOpcode; + if (isArtifactCast(SourceDefinition->opcode())) { + ConvertOpcode = SourceDefinition->opcode(); + const auto CastSource = registerOperand(*SourceDefinition, 1); + if (!CastSource) + return false; + auto CastDefinition = definitionIgnoringCopies(*CastSource); + if (!CastDefinition) + return std::unexpected(CastDefinition.error()); + Merge = CastDefinition->first; + } + if (Merge == nullptr || + !canFoldMergeOpcode(Merge->opcode(), ConvertOpcode, *OperandType, *DestinationType)) + return tryFoldUnmergeCast(Instruction, *SourceDefinition, DeadInstructions, UpdatedDefs); + + const std::size_t MergeSourceCount = Merge->operandCount() - 1; + const std::size_t DefCount = Unmerge.getNumDefs(); + if (MergeSourceCount < DefCount) { + if (DefCount % MergeSourceCount != 0) + return false; + const std::size_t NewDefCount = DefCount / MergeSourceCount; + for (std::size_t SourceIndex = 0; SourceIndex < MergeSourceCount; ++SourceIndex) { + std::vector<Register> Destinations; + Destinations.reserve(NewDefCount); + for (std::size_t Index = 0; Index < NewDefCount; ++Index) + Destinations.push_back(Unmerge.getReg(SourceIndex * NewDefCount + Index)); + const auto SplitSourceOperand = registerOperand(*Merge, SourceIndex + 1); + if (!SplitSourceOperand) + return makeUnexpected(ErrorCode::VerificationError, + "merge source is not a register operand"); + Register SplitSource = SplitSourceOperand.value(); + if (ConvertOpcode) { + auto ConvertedType = divideType(*OperandType, MergeSourceCount); + if (!ConvertedType) + return std::unexpected(ConvertedType.error()); + auto Converted = MRI.createVirtualRegister(*ConvertedType); + if (!Converted) + return std::unexpected(Converted.error()); + auto Built = + buildUnary(static_cast<GenericOpcode>(ConvertOpcode->value()), *Converted, SplitSource); + if (!Built) + return std::unexpected(Built.error()); + SplitSource = *Converted; + } + auto Built = Builder.buildUnmergeValues(Destinations, SplitSource); + if (!Built) + return std::unexpected(Built.error()); + UpdatedDefs.insert(UpdatedDefs.end(), Destinations.begin(), Destinations.end()); + } + } else if (MergeSourceCount > DefCount) { + if (ConvertOpcode || MergeSourceCount % DefCount != 0) + return false; + const std::size_t SourcesPerDef = MergeSourceCount / DefCount; + for (std::size_t DefIndex = 0; DefIndex < DefCount; ++DefIndex) { + std::vector<Register> Sources; + Sources.reserve(SourcesPerDef); + for (std::size_t Index = 0; Index < SourcesPerDef; ++Index) { + const auto SourceReg = registerOperand(*Merge, 1 + DefIndex * SourcesPerDef + Index); + if (!SourceReg) + return false; + Sources.push_back(*SourceReg); + } + auto Built = buildMergeLike(Unmerge.getReg(DefIndex), Sources); + if (!Built) + return std::unexpected(Built.error()); + UpdatedDefs.push_back(Unmerge.getReg(DefIndex)); + } + } else { + const auto FirstMergeSource = registerOperand(*Merge, 1); + if (!FirstMergeSource) + return false; + auto MergeSourceType = MRI.getType(*FirstMergeSource); + if (!MergeSourceType) + return std::unexpected(MergeSourceType.error()); + if (!ConvertOpcode && *DestinationType != *MergeSourceType) { + if (DestinationType->isPointer()) + ConvertOpcode = opcode(GenericOpcode::G_INTTOPTR); + else if (MergeSourceType->isPointer()) + ConvertOpcode = opcode(GenericOpcode::G_PTRTOINT); + else + ConvertOpcode = opcode(GenericOpcode::G_BITCAST); + } + for (std::size_t Index = 0; Index < DefCount; ++Index) { + const auto MergeSource = registerOperand(*Merge, Index + 1); + if (!MergeSource) + return false; + if (ConvertOpcode) { + if (!MRI.useEmpty(Unmerge.getReg(Index))) { + auto Built = buildUnary(static_cast<GenericOpcode>(ConvertOpcode->value()), + Unmerge.getReg(Index), *MergeSource); + if (!Built) + return std::unexpected(Built.error()); + UpdatedDefs.push_back(Unmerge.getReg(Index)); + } + } else { + auto Replaced = + replaceRegOrBuildCopy(Unmerge.getReg(Index), *MergeSource, UpdatedDefs, Observer); + if (!Replaced) + return std::unexpected(Replaced.error()); + } + } + } + markInstAndDefDead(Instruction, *Merge, DeadInstructions); + return true; +} + +inline Expected<bool> +LegalizationArtifactCombiner::tryCombineExtract(MachineInstr &Instruction, + std::vector<MachineInstr *> &DeadInstructions, + std::vector<Register> &UpdatedDefs) { + const auto Destination = registerOperand(Instruction, 0); + const auto OriginalSource = registerOperand(Instruction, 1); + const auto OffsetValue = immediateOperand(Instruction, 2); + if (!Destination || !OriginalSource || !OffsetValue || *OffsetValue < 0) + return false; + const Register Source = lookThroughCopyInstrs(*OriginalSource); + MachineInstr *Merge = MRI.getVRegDef(Source); + if (Merge == nullptr) + return false; + if (auto Point = Builder.setInsertPointBefore(Instruction); !Point) + return std::unexpected(Point.error()); + if (hasOpcode(Merge, GenericOpcode::G_IMPLICIT_DEF)) { + auto Built = Builder.buildImplicitDef(*Destination); + if (!Built) + return std::unexpected(Built.error()); + UpdatedDefs.push_back(*Destination); + markInstAndDefDead(Instruction, *Merge, DeadInstructions); + return true; + } + if (!GMergeLikeInstr::classof(Merge)) + return false; + auto DestinationType = MRI.getType(*Destination); + if (!DestinationType) + return std::unexpected(DestinationType.error()); + auto SourceType = MRI.getType(Source); + if (!SourceType) + return std::unexpected(SourceType.error()); + const std::size_t SourceCount = Merge->operandCount() - 1; + if (SourceCount == 0) + return false; + const std::uint64_t MergeSourceSize = SourceType->minimumBitWidth() / SourceCount; + const std::uint64_t Offset = static_cast<std::uint64_t>(*OffsetValue); + const std::size_t FirstSource = static_cast<std::size_t>(Offset / MergeSourceSize); + const std::size_t LastSource = + static_cast<std::size_t>((Offset + DestinationType->minimumBitWidth() - 1) / MergeSourceSize); + if (FirstSource != LastSource || FirstSource >= SourceCount) + return false; + const auto MergeSource = registerOperand(*Merge, FirstSource + 1); + if (!MergeSource) + return false; + auto Built = + Builder.buildExtract(*Destination, *MergeSource, Offset - FirstSource * MergeSourceSize); + if (!Built) + return std::unexpected(Built.error()); + UpdatedDefs.push_back(*Destination); + markInstAndDefDead(Instruction, *Merge, DeadInstructions); + return true; +} + +inline Expected<Register> LegalizationArtifactCombiner::ArtifactValueFinder::findValueFromDef( + Register DefReg, std::uint64_t StartBit, std::uint64_t Size, LowLevelType DestinationType) { + CurrentBest = {}; + Visited.clear(); + auto Found = findValueFromDefImpl(DefReg, StartBit, Size, DestinationType); + if (!Found) + return std::unexpected(Found.error()); + return *Found == DefReg ? Register{} : *Found; +} + +inline Expected<Register> LegalizationArtifactCombiner::ArtifactValueFinder::findValueFromConcat( + GConcatVectors &Instruction, std::uint64_t StartBit, std::uint64_t Size, + LowLevelType DestinationType) { + if (Instruction.getNumSources() == 0) + return CurrentBest; + const Register FirstSource = Instruction.getSourceReg(0); + auto FirstType = Combiner.MRI.getType(FirstSource); + if (!FirstType) + return std::unexpected(FirstType.error()); + const std::uint64_t SourceSize = FirstType->minimumBitWidth(); + if (SourceSize == 0) + return CurrentBest; + const std::size_t SourceIndex = static_cast<std::size_t>(StartBit / SourceSize); + const std::uint64_t SourceOffset = StartBit % SourceSize; + if (SourceIndex >= Instruction.getNumSources() || SourceOffset + Size > SourceSize) + return CurrentBest; + const Register Source = Instruction.getSourceReg(SourceIndex); + if (SourceOffset == 0 && Size == SourceSize) + CurrentBest = Source; + return findValueFromDefImpl(Source, SourceOffset, Size, DestinationType); +} + +inline Expected<Register> +LegalizationArtifactCombiner::ArtifactValueFinder::findValueFromBuildVector( + GBuildVector &Instruction, std::uint64_t StartBit, std::uint64_t Size) { + if (Instruction.getNumSources() == 0) + return CurrentBest; + const Register FirstSource = Instruction.getSourceReg(0); + auto FirstType = Combiner.MRI.getType(FirstSource); + if (!FirstType) + return std::unexpected(FirstType.error()); + const std::uint64_t SourceSize = FirstType->minimumBitWidth(); + if (SourceSize == 0 || StartBit % SourceSize != 0 || Size < SourceSize || Size % SourceSize != 0) + return CurrentBest; + const std::size_t FirstIndex = static_cast<std::size_t>(StartBit / SourceSize); + const std::size_t SourceCount = static_cast<std::size_t>(Size / SourceSize); + if (FirstIndex + SourceCount > Instruction.getNumSources()) + return CurrentBest; + if (SourceCount == 1) + return Instruction.getSourceReg(FirstIndex); + if (FirstIndex == 0 && SourceCount == Instruction.getNumSources()) + return Instruction.getReg(0); + + auto VectorType = LowLevelType::makeFixedVector(SourceCount, *FirstType); + if (!VectorType) + return std::unexpected(VectorType.error()); + auto Legal = Combiner.isInstLegal( + LegalizationQuery(opcode(GenericOpcode::G_BUILD_VECTOR), {*VectorType, *FirstType})); + if (!Legal) + return std::unexpected(Legal.error()); + if (!*Legal) + return CurrentBest; + + std::vector<Register> Sources; + Sources.reserve(SourceCount); + for (std::size_t Index = FirstIndex; Index < FirstIndex + SourceCount; ++Index) + Sources.push_back(Instruction.getSourceReg(Index)); + auto Destination = Combiner.MRI.createVirtualRegister(*VectorType); + if (!Destination) + return std::unexpected(Destination.error()); + auto Built = Combiner.buildMergeLike(*Destination, Sources); + if (!Built) + return std::unexpected(Built.error()); + return *Destination; +} + +inline Expected<Register> LegalizationArtifactCombiner::ArtifactValueFinder::findValueFromInsert( + MachineInstr &Instruction, std::uint64_t StartBit, std::uint64_t Size, + LowLevelType DestinationType) { + const auto Container = registerOperand(Instruction, 1); + const auto Inserted = registerOperand(Instruction, 2); + const auto InsertOffset = immediateOperand(Instruction, 3); + if (!Container || !Inserted || !InsertOffset || *InsertOffset < 0) + return CurrentBest; + auto InsertedType = Combiner.MRI.getType(*Inserted); + if (!InsertedType) + return std::unexpected(InsertedType.error()); + const std::uint64_t Offset = static_cast<std::uint64_t>(*InsertOffset); + const std::uint64_t InsertedEnd = Offset + InsertedType->minimumBitWidth(); + const std::uint64_t End = StartBit + Size; + if (End <= Offset || InsertedEnd <= StartBit) + return findValueFromDefImpl(*Container, StartBit, Size, DestinationType); + if (Offset <= StartBit && End <= InsertedEnd) { + const std::uint64_t NewStart = StartBit - Offset; + if (NewStart == 0 && Size == InsertedType->minimumBitWidth()) + CurrentBest = *Inserted; + return findValueFromDefImpl(*Inserted, NewStart, Size, DestinationType); + } + return Register{}; +} + +inline Expected<Register> LegalizationArtifactCombiner::ArtifactValueFinder::findValueFromExtension( + MachineInstr &Instruction, std::uint64_t StartBit, std::uint64_t Size, + LowLevelType DestinationType) { + const auto Source = registerOperand(Instruction, 1); + if (!Source) + return CurrentBest; + auto SourceType = Combiner.MRI.getType(*Source); + if (!SourceType) + return std::unexpected(SourceType.error()); + if (!SourceType->isCategoryNeutralScalar() || StartBit + Size > SourceType->minimumBitWidth()) + return CurrentBest; + if (StartBit == 0 && SourceType->minimumBitWidth() == Size) + CurrentBest = *Source; + return findValueFromDefImpl(*Source, StartBit, Size, DestinationType); +} + +inline Expected<Register> LegalizationArtifactCombiner::ArtifactValueFinder::findValueFromTrunc( + MachineInstr &Instruction, std::uint64_t StartBit, std::uint64_t Size, + LowLevelType DestinationType) { + const auto Source = registerOperand(Instruction, 1); + if (!Source) + return CurrentBest; + auto SourceType = Combiner.MRI.getType(*Source); + if (!SourceType) + return std::unexpected(SourceType.error()); + if (!SourceType->isCategoryNeutralScalar()) + return CurrentBest; + return findValueFromDefImpl(*Source, StartBit, Size, DestinationType); +} + +inline Expected<Register> LegalizationArtifactCombiner::ArtifactValueFinder::findValueFromDefImpl( + Register DefReg, std::uint64_t StartBit, std::uint64_t Size, LowLevelType DestinationType) { + if (!DefReg.isVirtual() || !Visited.insert(DefReg).second) + return CurrentBest; + auto Definition = Combiner.definitionIgnoringCopies(DefReg); + if (!Definition) + return std::unexpected(Definition.error()); + MachineInstr *Instruction = Definition->first; + DefReg = Definition->second; + if (Instruction == nullptr) + return CurrentBest; + + switch (static_cast<GenericOpcode>(Instruction->opcode().value())) { + case GenericOpcode::G_CONCAT_VECTORS: + return findValueFromConcat(castGenericMachineInstr<GConcatVectors>(*Instruction), StartBit, + Size, DestinationType); + case GenericOpcode::G_UNMERGE_VALUES: { + auto DefType = Combiner.MRI.getType(DefReg); + if (!DefType) + return std::unexpected(DefType.error()); + const std::size_t DefIndex = getDefIndex(*Instruction, DefReg); + const auto Source = registerOperand(*Instruction, Instruction->operandCount() - 1); + if (!Source) + return CurrentBest; + auto Found = findValueFromDefImpl(*Source, StartBit + DefIndex * DefType->minimumBitWidth(), + Size, DestinationType); + if (!Found) + return std::unexpected(Found.error()); + if (!Found->isNoRegister()) + return *Found; + if (StartBit == 0 && Size == DefType->minimumBitWidth()) + return DefReg; + return CurrentBest; + } + case GenericOpcode::G_BUILD_VECTOR: + return findValueFromBuildVector(castGenericMachineInstr<GBuildVector>(*Instruction), StartBit, + Size); + case GenericOpcode::G_INSERT: + return findValueFromInsert(*Instruction, StartBit, Size, DestinationType); + case GenericOpcode::G_TRUNC: + return findValueFromTrunc(*Instruction, StartBit, Size, DestinationType); + case GenericOpcode::G_SEXT: + case GenericOpcode::G_ZEXT: + case GenericOpcode::G_ANYEXT: + return findValueFromExtension(*Instruction, StartBit, Size, DestinationType); + case GenericOpcode::G_IMPLICIT_DEF: { + auto Type = Combiner.MRI.getType(DefReg); + if (!Type) + return std::unexpected(Type.error()); + if (*Type == DestinationType) + return DefReg; + auto Undef = Combiner.MRI.createVirtualRegister(DestinationType); + if (!Undef) + return std::unexpected(Undef.error()); + auto Built = Combiner.Builder.buildImplicitDef(*Undef); + if (!Built) + return std::unexpected(Built.error()); + return *Undef; + } + default: + return CurrentBest; + } +} + +inline Expected<bool> LegalizationArtifactCombiner::ArtifactValueFinder::tryCombineUnmergeDefs( + GUnmerge &Instruction, GISelChangeObserver &Observer, std::vector<Register> &UpdatedDefs) { + if (Instruction.getNumDefs() == 0) + return false; + auto DestinationType = Combiner.MRI.getType(Instruction.getReg(0)); + if (!DestinationType) + return std::unexpected(DestinationType.error()); + bool AllDead = true; + for (std::size_t Index = 0; Index < Instruction.getNumDefs(); ++Index) { + const Register Def = Instruction.getReg(Index); + if (Combiner.MRI.useEmpty(Def)) + continue; + auto Found = findValueFromDef(Def, 0, DestinationType->minimumBitWidth(), *DestinationType); + if (!Found) + return std::unexpected(Found.error()); + if (Found->isNoRegister()) { + AllDead = false; + continue; + } + auto FoundType = Combiner.MRI.getType(*Found); + if (!FoundType) + return std::unexpected(FoundType.error()); + if (*FoundType != *DestinationType) { + AllDead = false; + continue; + } + auto Replaced = Combiner.replaceRegOrBuildCopy(Def, *Found, UpdatedDefs, Observer); + if (!Replaced) + return std::unexpected(Replaced.error()); + Observer.changingInstr(Instruction); + auto Restored = Instruction.operandAt(Index)->setRegister(Def); + if (!Restored) + return std::unexpected(Restored.error()); + Observer.changedInstr(Instruction); + } + return AllDead; +} + +inline Expected<GUnmerge *> +LegalizationArtifactCombiner::ArtifactValueFinder::findUnmergeThatDefinesReg( + Register Reg, std::uint64_t Size, std::size_t &DefOperandIndex) { + auto Type = Combiner.MRI.getType(Reg); + if (!Type) + return std::unexpected(Type.error()); + CurrentBest = {}; + Visited.clear(); + auto Found = findValueFromDefImpl(Reg, 0, Size, *Type); + if (!Found) + return std::unexpected(Found.error()); + if (Found->isNoRegister()) + return nullptr; + auto Definition = Combiner.definitionIgnoringCopies(*Found); + if (!Definition) + return std::unexpected(Definition.error()); + if (!GUnmerge::classof(Definition->first)) + return nullptr; + DefOperandIndex = getDefIndex(*Definition->first, Definition->second); + return &castGenericMachineInstr<GUnmerge>(*Definition->first); +} + +inline Expected<bool> LegalizationArtifactCombiner::ArtifactValueFinder::isSequenceFromUnmerge( + GMergeLikeInstr &Instruction, std::size_t MergeStartIndex, GUnmerge *Unmerge, + std::size_t UnmergeStartIndex, std::size_t ElementCount, std::uint64_t ElementSize, + bool AllowUndef) { + for (std::size_t Index = 0; Index < ElementCount; ++Index) { + const Register Source = Instruction.getSourceReg(MergeStartIndex + Index); + std::size_t SourceDefIndex = 0; + auto SourceUnmerge = findUnmergeThatDefinesReg(Source, ElementSize, SourceDefIndex); + if (!SourceUnmerge) + return std::unexpected(SourceUnmerge.error()); + if (*SourceUnmerge == Unmerge) { + if (SourceDefIndex != UnmergeStartIndex + Index) + return false; + continue; + } + MachineInstr *Definition = Combiner.MRI.getVRegDef(Source); + if (!AllowUndef || !hasOpcode(Definition, GenericOpcode::G_IMPLICIT_DEF)) + return false; + } + return true; +} + +} // namespace rcc + +#endif // RCC_CODEGEN_GLOBALISEL_LEGALIZATIONARTIFACTCOMBINER_H diff --git a/backend/include/rcc/CodeGen/GlobalISel/Legalizer.h b/backend/include/rcc/CodeGen/GlobalISel/Legalizer.h new file mode 100644 index 0000000..8f2f3f7 --- /dev/null +++ b/backend/include/rcc/CodeGen/GlobalISel/Legalizer.h @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_GLOBALISEL_LEGALIZER_H +#define RCC_CODEGEN_GLOBALISEL_LEGALIZER_H + +#include "rcc/CodeGen/CommandFlags.h" +#include "rcc/CodeGen/GlobalISel/LegalizerInfo.h" +#include "rcc/CodeGen/MachinePassManager.h" +#include "rcc/CodeGen/TargetCallLowering.h" + +#include <memory> + +namespace rcc { + +class Legalizer final : public MachineFunctionPass { +public: + Legalizer(const LegalizerInfo *Info, OptimizationLevel Optimization, + LibcallLoweringInfo Libcalls = {}, + GlobalISelAbortMode AbortMode = GlobalISelAbortMode::Enable) + : Info(Info), Optimization(Optimization), Libcalls(Libcalls), AbortMode(AbortMode) {} + Legalizer(const LegalizerInfo &Info, OptimizationLevel Optimization, + LibcallLoweringInfo Libcalls = {}, + GlobalISelAbortMode AbortMode = GlobalISelAbortMode::Enable) + : Info(&Info), Optimization(Optimization), Libcalls(Libcalls), AbortMode(AbortMode) {} + Legalizer(std::unique_ptr<LegalizerInfo> Info, OptimizationLevel Optimization, + LibcallLoweringInfo Libcalls = {}, + GlobalISelAbortMode AbortMode = GlobalISelAbortMode::Enable) + : OwnedInfo(std::move(Info)), Info(OwnedInfo.get()), Optimization(Optimization), + Libcalls(Libcalls), AbortMode(AbortMode) {} + + [[nodiscard]] const MachinePassContract &contract() const noexcept override; + [[nodiscard]] Expected<MachinePassResult> run(MachineFunction &Function, + const TargetDescription &Target, + MachineFunctionAnalysisManager &Analyses) override; + +private: + [[nodiscard]] Expected<MachinePassResult> + runImplementation(MachineFunction &Function, const TargetDescription &Target, + MachineFunctionAnalysisManager &Analyses); + + std::unique_ptr<LegalizerInfo> OwnedInfo; + const LegalizerInfo *Info = nullptr; + OptimizationLevel Optimization; + LibcallLoweringInfo Libcalls; + GlobalISelAbortMode AbortMode; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_GLOBALISEL_LEGALIZER_H diff --git a/backend/include/rcc/CodeGen/GlobalISel/LegalizerHelper.h b/backend/include/rcc/CodeGen/GlobalISel/LegalizerHelper.h new file mode 100644 index 0000000..fa4e5e2 --- /dev/null +++ b/backend/include/rcc/CodeGen/GlobalISel/LegalizerHelper.h @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_GLOBALISEL_LEGALIZERHELPER_H +#define RCC_CODEGEN_GLOBALISEL_LEGALIZERHELPER_H + +#include "rcc/CodeGen/GenericOpcodes.h" +#include "rcc/CodeGen/GlobalISel/LegalizerInfo.h" +#include "rcc/CodeGen/MachineIRBuilder.h" +#include "rcc/Support/Error.h" + +#include <vector> + +namespace rcc { + +class MachineFunction; +class MachineInstr; +class GISelChangeObserver; +struct LibcallLoweringInfo; + +class LegalizerHelper final { +public: + MachineIRBuilder &MIRBuilder; + GISelChangeObserver &Observer; + + LegalizerHelper(MachineFunction &Function, const LegalizerInfo &Info, + GISelChangeObserver &Observer, MachineIRBuilder &MIRBuilder, + const LibcallLoweringInfo *Libcalls = nullptr); + + [[nodiscard]] Expected<void> legalize(MachineInstr &Instruction, + const LegalizationDecision &Decision); + [[nodiscard]] Expected<void> lower(MachineInstr &Instruction, + const LegalizationDecision &Decision); + [[nodiscard]] Expected<void> lowerAbsToAddXor(MachineInstr &Instruction); + [[nodiscard]] Expected<void> lowerAbsDiffToSelect(MachineInstr &Instruction); + [[nodiscard]] Expected<void> lowerAbsDiffToMinMax(MachineInstr &Instruction); + [[nodiscard]] Expected<void> lowerMinMax(MachineInstr &Instruction); + [[nodiscard]] Expected<void> lowerThreewayCompare(MachineInstr &Instruction); + [[nodiscard]] Expected<void> lowerDivRem(MachineInstr &Instruction); + [[nodiscard]] Expected<void> lowerHighMultiply(MachineInstr &Instruction); + [[nodiscard]] Expected<void> lowerBitCount(MachineInstr &Instruction); + [[nodiscard]] Expected<void> lowerBswap(MachineInstr &Instruction); + [[nodiscard]] Expected<void> lowerFunnelShift(MachineInstr &Instruction); + [[nodiscard]] Expected<void> lowerRotate(MachineInstr &Instruction); + [[nodiscard]] Expected<void> lowerDynStackAlloc(MachineInstr &Instruction); + [[nodiscard]] Expected<void> lowerStackSave(MachineInstr &Instruction); + [[nodiscard]] Expected<void> lowerStackRestore(MachineInstr &Instruction); + [[nodiscard]] Expected<void> lowerMemcpyInline(MachineInstr &Instruction); + [[nodiscard]] Expected<void> lowerVAArg(MachineInstr &Instruction); + [[nodiscard]] Expected<void> libcall(MachineInstr &Instruction); + [[nodiscard]] Expected<void> widenScalar(MachineInstr &Instruction, std::size_t TypeIndex, + LowLevelType WideType); + [[nodiscard]] Expected<void> widenScalarSrc(MachineInstr &Instruction, LowLevelType WideType, + std::size_t OperandIndex, + GenericOpcode ExtensionOpcode); + [[nodiscard]] Expected<void> narrowScalarSrc(MachineInstr &Instruction, LowLevelType NarrowType, + std::size_t OperandIndex); + [[nodiscard]] Expected<void> narrowScalarDst(MachineInstr &Instruction, LowLevelType NarrowType, + std::size_t OperandIndex, + GenericOpcode ExtensionOpcode); + [[nodiscard]] Expected<void> widenScalarDst(MachineInstr &Instruction, LowLevelType WideType, + std::size_t OperandIndex = 0, + GenericOpcode TruncOpcode = GenericOpcode::G_TRUNC); + + [[nodiscard]] Expected<void> narrowScalar(MachineInstr &Instruction, std::size_t TypeIndex, + LowLevelType NarrowType); + +private: + [[nodiscard]] Expected<void> widenScalarMergeValues(MachineInstr &Instruction, + std::size_t TypeIndex, LowLevelType WideType); + [[nodiscard]] Expected<void> + widenScalarUnmergeValues(MachineInstr &Instruction, std::size_t TypeIndex, LowLevelType WideType); + [[nodiscard]] Expected<void> insertParts(Register Destination, LowLevelType ResultType, + LowLevelType PartType, + const std::vector<Register> &PartRegisters, + LowLevelType LeftoverType = {}, + const std::vector<Register> &LeftoverRegisters = {}); + [[nodiscard]] Expected<void> extractGCDType(std::vector<Register> &Parts, LowLevelType GCDType, + Register Source); + [[nodiscard]] Expected<LowLevelType> extractGCDType(std::vector<Register> &Parts, + LowLevelType DestinationType, + LowLevelType NarrowType, Register Source); + [[nodiscard]] Expected<LowLevelType> + buildLCMMergePieces(LowLevelType DestinationType, LowLevelType NarrowType, LowLevelType GCDType, + std::vector<Register> &Registers, + GenericOpcode PadStrategy = GenericOpcode::G_ANYEXT); + [[nodiscard]] Expected<void> + buildWidenedRemergeToDst(Register Destination, LowLevelType LCMType, + const std::vector<Register> &RemergeRegisters); + [[nodiscard]] Expected<void> multiplyRegisters(std::vector<Register> &Destinations, + const std::vector<Register> &FirstSources, + const std::vector<Register> &SecondSources, + LowLevelType NarrowType); + [[nodiscard]] Expected<void> narrowScalarAddSub(MachineInstr &Instruction, std::size_t TypeIndex, + LowLevelType NarrowType); + [[nodiscard]] Expected<void> narrowScalarMul(MachineInstr &Instruction, std::size_t TypeIndex, + LowLevelType NarrowType); + [[nodiscard]] Expected<void> + narrowScalarLoadStore(MachineInstr &Instruction, std::size_t TypeIndex, LowLevelType NarrowType); + [[nodiscard]] Expected<void> reduceLoadStoreWidth(MachineInstr &Instruction, + std::size_t TypeIndex, LowLevelType NarrowType); + [[nodiscard]] Expected<void> lowerLoad(MachineInstr &Instruction); + [[nodiscard]] Expected<void> lowerStore(MachineInstr &Instruction); + [[nodiscard]] Expected<void> narrowScalarBasic(MachineInstr &Instruction, std::size_t TypeIndex, + LowLevelType NarrowType); + [[nodiscard]] Expected<void> narrowScalarExt(MachineInstr &Instruction, std::size_t TypeIndex, + LowLevelType NarrowType); + [[nodiscard]] Expected<void> narrowScalarSelect(MachineInstr &Instruction, std::size_t TypeIndex, + LowLevelType NarrowType); + [[nodiscard]] Expected<void> narrowScalarShift(MachineInstr &Instruction, std::size_t TypeIndex, + LowLevelType NarrowType); + [[nodiscard]] Expected<void> narrowScalarShiftByConstant(MachineInstr &Instruction, + const APInt &Amount, + LowLevelType HalfType, + LowLevelType AmountType); + [[nodiscard]] Expected<void> narrowScalarShiftMultiway(MachineInstr &Instruction, + LowLevelType TargetType); + [[nodiscard]] Expected<void> narrowScalarShiftByConstantMultiway(MachineInstr &Instruction, + const APInt &Amount, + LowLevelType TargetType, + LowLevelType AmountType); + [[nodiscard]] Expected<void> fewerElements(MachineInstr &Instruction, std::size_t TypeIndex, + LowLevelType NarrowType); + + struct ShiftParameters final { + Register WordShift; + Register BitShift; + Register InverseBitShift; + Register Zero; + Register SignBit; + }; + + [[nodiscard]] Expected<Register> + buildConstantShiftPart(GenericOpcode Generic, std::size_t PartIndex, std::size_t PartCount, + const std::vector<Register> &SourceParts, + const ShiftParameters &Parameters, LowLevelType TargetType); + [[nodiscard]] Expected<Register> + buildVariableShiftPart(GenericOpcode Generic, Register MainOperand, Register ShiftAmount, + LowLevelType TargetType, Register CarryOperand = {}); + [[nodiscard]] Expected<void> widenScalarAddSubOverflow(MachineInstr &Instruction, + std::size_t TypeIndex, + LowLevelType WideType); + [[nodiscard]] Expected<void> widenScalarMulo(MachineInstr &Instruction, std::size_t TypeIndex, + LowLevelType WideType); + [[nodiscard]] Expected<void> lowerSignedAddSubOverflow(MachineInstr &Instruction); + [[nodiscard]] Expected<void> lowerSaturatingAddSubToMinMax(MachineInstr &Instruction); + [[nodiscard]] Expected<void> lowerSaturatingAddSubToOverflow(MachineInstr &Instruction); + [[nodiscard]] Expected<void> lowerSaturatingShiftLeft(MachineInstr &Instruction); + [[nodiscard]] Expected<void> lowerMulo(MachineInstr &Instruction); + + MachineFunction &Function; + MachineRegisterInfo &MRI; + const LegalizerInfo &Info; + const LibcallLoweringInfo *Libcalls = nullptr; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_GLOBALISEL_LEGALIZERHELPER_H diff --git a/backend/include/rcc/CodeGen/GlobalISel/LegalizerInfo.h b/backend/include/rcc/CodeGen/GlobalISel/LegalizerInfo.h new file mode 100644 index 0000000..78e6a32 --- /dev/null +++ b/backend/include/rcc/CodeGen/GlobalISel/LegalizerInfo.h @@ -0,0 +1,213 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_GLOBALISEL_LEGALIZERINFO_H +#define RCC_CODEGEN_GLOBALISEL_LEGALIZERINFO_H + +#include "rcc/CodeGen/LowLevelType.h" +#include "rcc/CodeGen/MachineIRTypes.h" +#include "rcc/CodeGen/MachineMemOperand.h" +#include "rcc/Support/Error.h" + +#include <cstddef> +#include <cstdint> +#include <functional> +#include <initializer_list> +#include <optional> +#include <tuple> +#include <unordered_map> +#include <utility> +#include <vector> + +namespace rcc { + +class MachineFunction; +class MachineInstr; +class TargetDescription; +class LegalizerHelper; +enum class GenericOpcode : std::uint32_t; + +enum class LegalizationAction : std::uint8_t { + Legal, + Unsupported, + Custom, + Lower, + Libcall, + WidenScalar, + NarrowScalar, + MoreElements, + FewerElements, + Bitcast, + ChangeElementType, + ChangeAddressSpace, +}; + +struct LegalizationMemoryDesc final { + std::optional<LowLevelType> Type; + MemorySize Size; + std::uint64_t AlignmentInBits = 0; + std::uint32_t AddressSpace = 0; + AtomicOperation Operation = AtomicOperation::None; + AtomicOrdering SuccessOrdering = AtomicOrdering::NotAtomic; + AtomicOrdering FailureOrdering = AtomicOrdering::NotAtomic; + SyncScopeID SyncScope; +}; + +struct LegalizeMemoryRule final { + LowLevelType ValueType; + LowLevelType AddressType; + LowLevelType MemoryType; + std::uint64_t MinimumAlignmentInBits = 0; +}; + +struct LegalizationQuery final { + Opcode InstructionOpcode; + std::vector<LowLevelType> Types; + std::vector<std::vector<std::size_t>> TypeOperands; + std::vector<LegalizationMemoryDesc> MemoryOperands; + + LegalizationQuery(Opcode InstructionOpcode, std::vector<LowLevelType> Types, + std::vector<LegalizationMemoryDesc> MemoryOperands = {}) + : InstructionOpcode(InstructionOpcode), Types(std::move(Types)), + MemoryOperands(std::move(MemoryOperands)) {} +}; + +struct LegalizationDecision final { + LegalizationAction Action = LegalizationAction::Unsupported; + std::size_t TypeIndex = 0; + LowLevelType NewType; +}; + +using LegalityPredicate = std::function<bool(const LegalizationQuery &)>; +using LegalizeMutation = + std::function<std::pair<std::size_t, LowLevelType>(const LegalizationQuery &)>; + +class LegalizeRule final { +public: + LegalizeRule(LegalityPredicate Predicate, LegalizationAction Action, + LegalizeMutation Mutation = {}); + + [[nodiscard]] bool matches(const LegalizationQuery &Query) const; + [[nodiscard]] LegalizationDecision apply(const LegalizationQuery &Query) const; + +private: + LegalityPredicate Predicate; + LegalizationAction Action; + LegalizeMutation Mutation; +}; + +class LegalizeRuleSet final { +public: + LegalizeRuleSet &actionIf(LegalizationAction Action, LegalityPredicate Predicate, + LegalizeMutation Mutation = {}); + LegalizeRuleSet &actionFor(LegalizationAction Action, std::initializer_list<LowLevelType> Types); + LegalizeRuleSet &actionFor(LegalizationAction Action, + std::initializer_list<std::pair<LowLevelType, LowLevelType>> Types); + LegalizeRuleSet & + actionFor(LegalizationAction Action, + std::initializer_list<std::tuple<LowLevelType, LowLevelType, LowLevelType>> Types); + LegalizeRuleSet &legalIf(LegalityPredicate Predicate); + LegalizeRuleSet &customIf(LegalityPredicate Predicate); + LegalizeRuleSet &lowerIf(LegalityPredicate Predicate); + LegalizeRuleSet &libcallIf(LegalityPredicate Predicate); + LegalizeRuleSet &unsupportedIf(LegalityPredicate Predicate); + LegalizeRuleSet &widenScalarIf(LegalityPredicate Predicate, LegalizeMutation Mutation); + LegalizeRuleSet &narrowScalarIf(LegalityPredicate Predicate, LegalizeMutation Mutation); + LegalizeRuleSet &moreElementsIf(LegalityPredicate Predicate, LegalizeMutation Mutation); + LegalizeRuleSet &fewerElementsIf(LegalityPredicate Predicate, LegalizeMutation Mutation); + LegalizeRuleSet &bitcastIf(LegalityPredicate Predicate, LegalizeMutation Mutation); + + LegalizeRuleSet &legalFor(std::initializer_list<LowLevelType> Types); + LegalizeRuleSet &legalFor(bool Enabled, std::initializer_list<LowLevelType> Types); + LegalizeRuleSet &legalFor(std::initializer_list<std::pair<LowLevelType, LowLevelType>> Types); + LegalizeRuleSet &legalFor(bool Enabled, + std::initializer_list<std::pair<LowLevelType, LowLevelType>> Types); + LegalizeRuleSet & + legalFor(std::initializer_list<std::tuple<LowLevelType, LowLevelType, LowLevelType>> Types); + LegalizeRuleSet &customFor(std::initializer_list<LowLevelType> Types); + LegalizeRuleSet &customFor(bool Enabled, std::initializer_list<LowLevelType> Types); + LegalizeRuleSet &customFor(std::initializer_list<std::pair<LowLevelType, LowLevelType>> Types); + LegalizeRuleSet &customFor(bool Enabled, + std::initializer_list<std::pair<LowLevelType, LowLevelType>> Types); + LegalizeRuleSet &lowerFor(std::initializer_list<LowLevelType> Types); + LegalizeRuleSet &lowerFor(std::initializer_list<std::pair<LowLevelType, LowLevelType>> Types); + LegalizeRuleSet &libcallFor(std::initializer_list<LowLevelType> Types); + LegalizeRuleSet &libcallFor(bool Enabled, std::initializer_list<LowLevelType> Types); + LegalizeRuleSet &libcallFor(std::initializer_list<std::pair<LowLevelType, LowLevelType>> Types); + LegalizeRuleSet &libcallFor(bool Enabled, + std::initializer_list<std::pair<LowLevelType, LowLevelType>> Types); + LegalizeRuleSet &legalForTypesWithMemDesc(std::initializer_list<LegalizeMemoryRule> Rules); + + LegalizeRuleSet &custom(); + LegalizeRuleSet &widenScalarToNextPow2(std::size_t TypeIndex, std::uint32_t MinimumBitWidth = 0); + LegalizeRuleSet &scalarize(std::size_t TypeIndex); + LegalizeRuleSet &minScalar(std::size_t TypeIndex, LowLevelType MinimumType); + LegalizeRuleSet &minScalar(bool Enabled, std::size_t TypeIndex, LowLevelType MinimumType); + LegalizeRuleSet &maxScalar(std::size_t TypeIndex, LowLevelType MaximumType); + LegalizeRuleSet &maxScalar(bool Enabled, std::size_t TypeIndex, LowLevelType MaximumType); + LegalizeRuleSet &clampScalar(std::size_t TypeIndex, LowLevelType MinimumType, + LowLevelType MaximumType); + LegalizeRuleSet &scalarSameSizeAs(std::size_t TypeIndex, std::size_t ReferenceTypeIndex); + LegalizeRuleSet &lowerIfMemSizeNotByteSizePow2(std::size_t MemoryIndex = 0); + LegalizeRuleSet &alwaysLegal(); + LegalizeRuleSet &lower(); + LegalizeRuleSet &libcall(); + + [[nodiscard]] std::optional<LegalizationDecision> apply(const LegalizationQuery &Query) const; + +private: + std::vector<LegalizeRule> Rules; +}; + +class LegalizerInfo { +public: + virtual ~LegalizerInfo() = default; + + [[nodiscard]] static Expected<LegalizationQuery> buildQuery(const MachineInstr &Instruction, + const MachineFunction &Function, + const TargetDescription &Target); + + [[nodiscard]] Expected<LegalizationDecision> getDecision(const MachineInstr &Instruction, + const MachineFunction &Function, + const TargetDescription &Target) const; + + [[nodiscard]] Expected<LegalizationDecision> getDecision(const LegalizationQuery &Query) const; + + [[nodiscard]] Expected<bool> isLegal(const LegalizationQuery &Query) const; + [[nodiscard]] Expected<bool> isUnsupported(const LegalizationQuery &Query) const; + + [[nodiscard]] virtual Expected<void> + legalizeCustom(LegalizerHelper &Helper, MachineInstr &Instruction, + const LegalizationDecision &Decision) const = 0; + + // Target intrinsic lowering runs before the ordinary legality action. + [[nodiscard]] virtual Expected<bool> legalizeIntrinsic(LegalizerHelper &, MachineInstr &) const { + return true; + } + + [[nodiscard]] virtual GenericOpcode getExtOpcodeForWideningConstant(LowLevelType SmallType) const; + + [[nodiscard]] virtual bool isSExtCheaperThanZExt(LowLevelType, LowLevelType) const { + return false; + } + + // This hook performs read-only target checks before a lowering mutation is committed. + [[nodiscard]] virtual Expected<void> validateLowering(const MachineInstr &, + const MachineFunction &, + const TargetDescription &, + const LegalizationDecision &) const { + return {}; + } + +protected: + [[nodiscard]] LegalizeRuleSet &getActionDefinitionsBuilder(Opcode InstructionOpcode); + [[nodiscard]] LegalizeRuleSet & + getActionDefinitionsBuilder(std::initializer_list<Opcode> InstructionOpcodes); + +private: + std::unordered_map<Opcode, LegalizeRuleSet> RuleSets; + std::unordered_map<Opcode, Opcode> RuleAliases; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_GLOBALISEL_LEGALIZERINFO_H diff --git a/backend/include/rcc/CodeGen/GlobalISel/RegBankSelect.h b/backend/include/rcc/CodeGen/GlobalISel/RegBankSelect.h new file mode 100644 index 0000000..a7fc6bf --- /dev/null +++ b/backend/include/rcc/CodeGen/GlobalISel/RegBankSelect.h @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_GLOBALISEL_REGBANKSELECT_H +#define RCC_CODEGEN_GLOBALISEL_REGBANKSELECT_H + +#include "rcc/CodeGen/MachinePassManager.h" +#include "rcc/CodeGen/RegisterBankInfo.h" + +namespace rcc { + +class RegBankSelect final : public MachineFunctionPass { +public: + explicit RegBankSelect(const RegisterBankInfo *Info) : Info(Info) {} + explicit RegBankSelect(const RegisterBankInfo &Info) : Info(&Info) {} + + [[nodiscard]] const MachinePassContract &contract() const noexcept override; + [[nodiscard]] Expected<MachinePassResult> run(MachineFunction &Function, + const TargetDescription &Target, + MachineFunctionAnalysisManager &Analyses) override; + +private: + const RegisterBankInfo *Info; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_GLOBALISEL_REGBANKSELECT_H diff --git a/backend/include/rcc/CodeGen/GlobalISel/Utils.h b/backend/include/rcc/CodeGen/GlobalISel/Utils.h new file mode 100644 index 0000000..f252454 --- /dev/null +++ b/backend/include/rcc/CodeGen/GlobalISel/Utils.h @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_GLOBALISEL_UTILS_H +#define RCC_CODEGEN_GLOBALISEL_UTILS_H + +#include "rcc/ADT/APInt.h" +#include "rcc/CodeGen/LowLevelType.h" +#include "rcc/CodeGen/Register.h" +#include "rcc/Support/Error.h" + +#include <cstddef> +#include <optional> +#include <span> +#include <vector> + +namespace rcc { + +class MachineIRBuilder; +class MachineInstr; +class MachineRegisterInfo; +class TargetDescription; + +struct ValueAndVReg final { + APInt Value; + Register VReg; +}; + +[[nodiscard]] std::optional<ValueAndVReg> +getIConstantVRegValWithLookThrough(Register VReg, const MachineRegisterInfo &MRI, + bool LookThroughInstrs = true); + +/// Check whether an instruction only defines dead virtual registers and has no +/// observable side effects. +[[nodiscard]] bool isTriviallyDead(const MachineInstr &Instruction, const MachineRegisterInfo &MRI, + const TargetDescription &Target) noexcept; + +[[nodiscard]] Expected<void> eraseInstrs(std::span<MachineInstr *const> Instructions, + MachineRegisterInfo &MRI, const TargetDescription &Target); + +[[nodiscard]] Expected<void> eraseInstr(MachineInstr &Instruction, MachineRegisterInfo &MRI, + const TargetDescription &Target); + +[[nodiscard]] Expected<void> extractParts(Register Reg, LowLevelType Type, int PartCount, + std::vector<Register> &Parts, + MachineIRBuilder &MIRBuilder, MachineRegisterInfo &MRI); + +[[nodiscard]] Expected<bool> extractParts(Register Reg, LowLevelType RegisterType, + LowLevelType MainType, LowLevelType &LeftoverType, + std::vector<Register> &Parts, + std::vector<Register> &LeftoverParts, + MachineIRBuilder &MIRBuilder, MachineRegisterInfo &MRI); + +[[nodiscard]] Expected<LowLevelType> getLCMType(LowLevelType OriginalType, LowLevelType TargetType); +[[nodiscard]] Expected<LowLevelType> getGCDType(LowLevelType OriginalType, LowLevelType TargetType); + +} // namespace rcc + +#endif // RCC_CODEGEN_GLOBALISEL_UTILS_H diff --git a/backend/include/rcc/CodeGen/GlobalObject.h b/backend/include/rcc/CodeGen/GlobalObject.h new file mode 100644 index 0000000..b67421f --- /dev/null +++ b/backend/include/rcc/CodeGen/GlobalObject.h @@ -0,0 +1,205 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_GLOBALOBJECT_H +#define RCC_CODEGEN_GLOBALOBJECT_H + +#include "rcc/CodeGen/MachineIRTypes.h" +#include "rcc/CodeGen/TargetConstant.h" +#include "rcc/IR/Constants.h" +#include "rcc/Support/Error.h" + +#include <cstddef> +#include <cstdint> +#include <memory> +#include <optional> +#include <string> +#include <utility> +#include <variant> +#include <vector> + +namespace rcc { + +class GlobalInitializer; +using GlobalInitializerRef = std::shared_ptr<const GlobalInitializer>; + +struct GlobalZeroFill final { + std::uint64_t Size = 0; +}; + +struct GlobalRepeatedInitializer final { + std::uint64_t Count = 0; + GlobalInitializerRef Value; +}; + +struct GlobalAggregateInitializer final { + std::vector<GlobalInitializerRef> Elements; +}; + +struct GlobalSymbolAddress final { + std::string Symbol; + std::int64_t Addend = 0; + std::uint8_t Size = 8; + std::uint32_t TargetFlags = 0; +}; + +struct GlobalSymbolDifference final { + std::string Minuend; + std::string Subtrahend; + std::int64_t Addend = 0; + std::uint8_t Size = 8; +}; + +struct GlobalAlignmentPadding final { + std::uint64_t Size = 0; + std::uint8_t Fill = 0; +}; + +struct GlobalUndefinedStorage final { + std::uint64_t Size = 0; +}; + +class GlobalInitializer final { +public: + enum class Kind : std::uint8_t { + Invalid, + Integer, + Floating, + Bytes, + ZeroFill, + Repeated, + Aggregate, + SymbolAddress, + SymbolDifference, + Target, + AlignmentPadding, + UndefinedStorage, + }; + + GlobalInitializer() = default; + GlobalInitializer(const GlobalInitializer &) = delete; + GlobalInitializer &operator=(const GlobalInitializer &) = delete; + GlobalInitializer(GlobalInitializer &&) noexcept = default; + GlobalInitializer &operator=(GlobalInitializer &&) noexcept = default; + ~GlobalInitializer() = default; + + [[nodiscard]] static GlobalInitializer integer(APInt Value); + [[nodiscard]] static GlobalInitializer floating(APFloat Value); + [[nodiscard]] static Expected<GlobalInitializer> bytes(std::vector<std::byte> Value); + [[nodiscard]] static Expected<GlobalInitializer> zeroFill(std::uint64_t Size); + [[nodiscard]] static Expected<GlobalInitializer> repeated(std::uint64_t Count, + GlobalInitializer Value); + [[nodiscard]] static Expected<GlobalInitializer> + aggregate(std::vector<GlobalInitializer> Elements); + [[nodiscard]] static Expected<GlobalInitializer> symbolAddress(GlobalSymbolAddress Value); + [[nodiscard]] static Expected<GlobalInitializer> symbolDifference(GlobalSymbolDifference Value); + [[nodiscard]] static Expected<GlobalInitializer> target(TargetConstant Value); + [[nodiscard]] static Expected<GlobalInitializer> alignmentPadding(std::uint64_t Size, + std::uint8_t Fill = 0); + [[nodiscard]] static Expected<GlobalInitializer> undefinedStorage(std::uint64_t Size); + + [[nodiscard]] Kind kind() const noexcept; + + template <typename ValueT> [[nodiscard]] const ValueT *getIf() const noexcept { + return std::get_if<ValueT>(&Storage); + } + + [[nodiscard]] Expected<void> verify(std::size_t MaximumDepth = 1024) const; + [[nodiscard]] Expected<std::uint64_t> storageSize(std::size_t MaximumDepth = 1024) const; + +private: + using StorageT = + std::variant<std::monostate, APInt, APFloat, std::vector<std::byte>, GlobalZeroFill, + GlobalRepeatedInitializer, GlobalAggregateInitializer, GlobalSymbolAddress, + GlobalSymbolDifference, TargetConstant, GlobalAlignmentPadding, + GlobalUndefinedStorage>; + + explicit GlobalInitializer(StorageT Storage) : Storage(std::move(Storage)) {} + + StorageT Storage; +}; + +enum class GlobalLinkage : std::uint8_t { + External, + AvailableExternally, + LinkOnceAny, + LinkOnceODR, + WeakAny, + WeakODR, + Appending, + Internal, + Private, + ExternalWeak, + Common, + Count, +}; + +enum class GlobalVisibility : std::uint8_t { + Default, + Hidden, + Protected, + Count, +}; + +enum class GlobalTLSModel : std::uint8_t { + None, + GeneralDynamic, + LocalDynamic, + InitialExec, + LocalExec, + Count, +}; + +enum class GlobalUnnamedAddress : std::uint8_t { + None, + Local, + Global, + Count, +}; + +struct GlobalValueProperties final { + bool DSOLocal = false; + bool ExternalWeak = false; + bool ThreadLocal = false; + + friend constexpr bool operator==(const GlobalValueProperties &Left, + const GlobalValueProperties &Right) noexcept = default; +}; + +struct GlobalObjectDesc final { + std::string Symbol; + GenericConstantType ObjectType; + GlobalLinkage Linkage = GlobalLinkage::External; + GlobalVisibility Visibility = GlobalVisibility::Default; + bool DSOLocal = false; + std::optional<std::string> Section; + Alignment Align; + std::uint64_t Size = 0; + std::uint32_t AddressSpace = 0; + GlobalTLSModel TLSModel = GlobalTLSModel::None; + bool ExternallyInitialized = false; + std::optional<std::string> COMDAT; + bool IsConstant = false; + GlobalUnnamedAddress UnnamedAddress = GlobalUnnamedAddress::None; + std::optional<GlobalInitializer> Initializer; + std::vector<std::string> DebugExpressions; + std::optional<OriginID> StableOrigin; + std::uint64_t TargetFlags = 0; + std::vector<std::pair<std::string, std::string>> TargetAttributes; + + GlobalObjectDesc() = default; + GlobalObjectDesc(const GlobalObjectDesc &) = delete; + GlobalObjectDesc &operator=(const GlobalObjectDesc &) = delete; + GlobalObjectDesc(GlobalObjectDesc &&) noexcept = default; + GlobalObjectDesc &operator=(GlobalObjectDesc &&) noexcept = default; + ~GlobalObjectDesc() = default; + + [[nodiscard]] bool isDeclaration() const noexcept { + return !Initializer.has_value() && Linkage != GlobalLinkage::Common; + } + + [[nodiscard]] Expected<void> verify() const; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_GLOBALOBJECT_H diff --git a/backend/include/rcc/CodeGen/InlineAsm.h b/backend/include/rcc/CodeGen/InlineAsm.h new file mode 100644 index 0000000..15c0de6 --- /dev/null +++ b/backend/include/rcc/CodeGen/InlineAsm.h @@ -0,0 +1,310 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_INLINEASM_H +#define RCC_CODEGEN_INLINEASM_H + +#include <array> +#include <cstddef> +#include <cstdint> +#include <optional> +#include <string_view> +#include <vector> + +namespace rcc { + +inline constexpr std::size_t InlineAsmStringOperandIndex = 0; +inline constexpr std::size_t InlineAsmExtraInfoOperandIndex = 1; +inline constexpr std::size_t InlineAsmFirstConstraintOperandIndex = 2; + +enum class InlineAsmExtraInfoFlag : std::uint32_t { + HasSideEffects = 1U, + AlignStack = 2U, + IntelDialect = 4U, + MayLoad = 8U, + MayStore = 16U, + Convergent = 32U, + MayUnwind = 64U, +}; + +class InlineAsmExtraInfo final { +public: + explicit constexpr InlineAsmExtraInfo(std::uint32_t RawValue = 0) noexcept : Value(RawValue) {} + + [[nodiscard]] constexpr std::uint32_t rawValue() const noexcept { + return Value; + } + + [[nodiscard]] constexpr bool has(InlineAsmExtraInfoFlag Flag) const noexcept { + return (Value & static_cast<std::uint32_t>(Flag)) != 0; + } + + constexpr void set(InlineAsmExtraInfoFlag Flag) noexcept { + Value |= static_cast<std::uint32_t>(Flag); + } + + [[nodiscard]] static constexpr std::optional<std::uint32_t> + namedFlagBits(std::string_view Name) noexcept { + if (Name == "sideeffect") + return static_cast<std::uint32_t>(InlineAsmExtraInfoFlag::HasSideEffects); + if (Name == "mayload") + return static_cast<std::uint32_t>(InlineAsmExtraInfoFlag::MayLoad); + if (Name == "maystore") + return static_cast<std::uint32_t>(InlineAsmExtraInfoFlag::MayStore); + if (Name == "isconvergent") + return static_cast<std::uint32_t>(InlineAsmExtraInfoFlag::Convergent); + if (Name == "alignstack") + return static_cast<std::uint32_t>(InlineAsmExtraInfoFlag::AlignStack); + if (Name == "unwind") + return static_cast<std::uint32_t>(InlineAsmExtraInfoFlag::MayUnwind); + if (Name == "attdialect") + return 0; + if (Name == "inteldialect") + return static_cast<std::uint32_t>(InlineAsmExtraInfoFlag::IntelDialect); + return std::nullopt; + } + + [[nodiscard]] std::vector<std::string_view> names() const { + std::vector<std::string_view> Result; + Result.reserve(7); + if (has(InlineAsmExtraInfoFlag::HasSideEffects)) + Result.push_back("sideeffect"); + if (has(InlineAsmExtraInfoFlag::MayLoad)) + Result.push_back("mayload"); + if (has(InlineAsmExtraInfoFlag::MayStore)) + Result.push_back("maystore"); + if (has(InlineAsmExtraInfoFlag::Convergent)) + Result.push_back("isconvergent"); + if (has(InlineAsmExtraInfoFlag::AlignStack)) + Result.push_back("alignstack"); + if (has(InlineAsmExtraInfoFlag::MayUnwind)) + Result.push_back("unwind"); + Result.push_back(has(InlineAsmExtraInfoFlag::IntelDialect) ? "inteldialect" : "attdialect"); + return Result; + } + + friend constexpr bool operator==(InlineAsmExtraInfo Left, + InlineAsmExtraInfo Right) noexcept = default; + +private: + std::uint32_t Value = 0; +}; + +enum class InlineAsmOperandKind : std::uint8_t { + RegisterUse = 1, + RegisterDef = 2, + RegisterDefEarlyClobber = 3, + Clobber = 4, + Immediate = 5, + Memory = 6, + Function = 7, +}; + +enum class InlineAsmMemoryConstraint : std::uint8_t { + Unknown = 0, + ES, + I, + K, + M, + O, + V, + A, + Q, + R, + S, + T, + UM, + UN, + UQ, + US, + UT, + UV, + UY, + X, + Z, + ZB, + ZC, + ZY, + P, + ZQ, + ZR, + ZS, + ZT, +}; + +struct InlineAsmMemoryConstraintName final { + std::string_view Name; + InlineAsmMemoryConstraint Constraint = InlineAsmMemoryConstraint::Unknown; +}; + +inline constexpr std::array InlineAsmMemoryConstraintNames{ + InlineAsmMemoryConstraintName{"es", InlineAsmMemoryConstraint::ES}, + InlineAsmMemoryConstraintName{"i", InlineAsmMemoryConstraint::I}, + InlineAsmMemoryConstraintName{"k", InlineAsmMemoryConstraint::K}, + InlineAsmMemoryConstraintName{"m", InlineAsmMemoryConstraint::M}, + InlineAsmMemoryConstraintName{"o", InlineAsmMemoryConstraint::O}, + InlineAsmMemoryConstraintName{"v", InlineAsmMemoryConstraint::V}, + InlineAsmMemoryConstraintName{"A", InlineAsmMemoryConstraint::A}, + InlineAsmMemoryConstraintName{"Q", InlineAsmMemoryConstraint::Q}, + InlineAsmMemoryConstraintName{"R", InlineAsmMemoryConstraint::R}, + InlineAsmMemoryConstraintName{"S", InlineAsmMemoryConstraint::S}, + InlineAsmMemoryConstraintName{"T", InlineAsmMemoryConstraint::T}, + InlineAsmMemoryConstraintName{"Um", InlineAsmMemoryConstraint::UM}, + InlineAsmMemoryConstraintName{"Un", InlineAsmMemoryConstraint::UN}, + InlineAsmMemoryConstraintName{"Uq", InlineAsmMemoryConstraint::UQ}, + InlineAsmMemoryConstraintName{"Us", InlineAsmMemoryConstraint::US}, + InlineAsmMemoryConstraintName{"Ut", InlineAsmMemoryConstraint::UT}, + InlineAsmMemoryConstraintName{"Uv", InlineAsmMemoryConstraint::UV}, + InlineAsmMemoryConstraintName{"Uy", InlineAsmMemoryConstraint::UY}, + InlineAsmMemoryConstraintName{"X", InlineAsmMemoryConstraint::X}, + InlineAsmMemoryConstraintName{"Z", InlineAsmMemoryConstraint::Z}, + InlineAsmMemoryConstraintName{"ZB", InlineAsmMemoryConstraint::ZB}, + InlineAsmMemoryConstraintName{"ZC", InlineAsmMemoryConstraint::ZC}, + InlineAsmMemoryConstraintName{"Zy", InlineAsmMemoryConstraint::ZY}, + InlineAsmMemoryConstraintName{"p", InlineAsmMemoryConstraint::P}, + InlineAsmMemoryConstraintName{"ZQ", InlineAsmMemoryConstraint::ZQ}, + InlineAsmMemoryConstraintName{"ZR", InlineAsmMemoryConstraint::ZR}, + InlineAsmMemoryConstraintName{"ZS", InlineAsmMemoryConstraint::ZS}, + InlineAsmMemoryConstraintName{"ZT", InlineAsmMemoryConstraint::ZT}, +}; + +class InlineAsmOperandFlag final { +public: + constexpr InlineAsmOperandFlag(InlineAsmOperandKind Kind, std::uint32_t OperandCount = 1) noexcept + : Value(static_cast<std::uint32_t>(Kind) | + ((OperandCount & OperandCountMask) << OperandCountShift)) {} + + explicit constexpr InlineAsmOperandFlag(std::uint32_t RawValue) noexcept : Value(RawValue) {} + + [[nodiscard]] constexpr std::uint32_t rawValue() const noexcept { + return Value; + } + + [[nodiscard]] constexpr InlineAsmOperandKind kind() const noexcept { + return static_cast<InlineAsmOperandKind>(Value & KindMask); + } + + [[nodiscard]] constexpr std::uint32_t operandCount() const noexcept { + return (Value >> OperandCountShift) & OperandCountMask; + } + + [[nodiscard]] constexpr bool isMatched() const noexcept { + return (Value & MatchedMask) != 0; + } + + [[nodiscard]] constexpr std::optional<std::uint32_t> matchingOperand() const noexcept { + return isMatched() ? std::optional((Value >> PayloadShift) & MatchedOperandMask) : std::nullopt; + } + + [[nodiscard]] constexpr std::optional<std::uint32_t> registerClass() const noexcept { + if (isMatched()) + return std::nullopt; + const std::uint32_t Encoded = (Value >> PayloadShift) & RegisterClassMask; + return Encoded == 0 ? std::nullopt : std::optional(Encoded); + } + + [[nodiscard]] constexpr InlineAsmMemoryConstraint memoryConstraint() const noexcept { + if (isMatched()) + return InlineAsmMemoryConstraint::Unknown; + return static_cast<InlineAsmMemoryConstraint>((Value >> PayloadShift) & MatchedOperandMask); + } + + [[nodiscard]] constexpr bool setMatchingOperand(std::uint32_t OperandNumber) noexcept { + if (OperandNumber > MatchedOperandMask) + return false; + Value &= ~(MatchedMask | (MatchedOperandMask << PayloadShift)); + Value |= MatchedMask | (OperandNumber << PayloadShift); + return true; + } + + [[nodiscard]] constexpr bool setRegisterClass(std::uint32_t RegisterClass) noexcept { + if (RegisterClass == 0 || RegisterClass > RegisterClassMask || isMatched()) + return false; + Value &= ~(RegisterClassMask << PayloadShift); + Value |= RegisterClass << PayloadShift; + return true; + } + + [[nodiscard]] constexpr bool setMemoryConstraint(InlineAsmMemoryConstraint Constraint) noexcept { + const std::uint32_t Encoded = static_cast<std::uint32_t>(Constraint); + if (Encoded == 0 || Encoded > MatchedOperandMask || isMatched()) + return false; + Value &= ~(MatchedOperandMask << PayloadShift); + Value |= Encoded << PayloadShift; + return true; + } + + [[nodiscard]] static constexpr std::optional<InlineAsmOperandKind> + kindForName(std::string_view Name) noexcept { + if (Name == "regdef") + return InlineAsmOperandKind::RegisterDef; + if (Name == "reguse") + return InlineAsmOperandKind::RegisterUse; + if (Name == "regdef-ec") + return InlineAsmOperandKind::RegisterDefEarlyClobber; + if (Name == "clobber") + return InlineAsmOperandKind::Clobber; + if (Name == "imm") + return InlineAsmOperandKind::Immediate; + if (Name == "mem") + return InlineAsmOperandKind::Memory; + return std::nullopt; + } + + [[nodiscard]] static constexpr std::string_view kindName(InlineAsmOperandKind Kind) noexcept { + switch (Kind) { + case InlineAsmOperandKind::RegisterUse: + return "reguse"; + case InlineAsmOperandKind::RegisterDef: + return "regdef"; + case InlineAsmOperandKind::RegisterDefEarlyClobber: + return "regdef-ec"; + case InlineAsmOperandKind::Clobber: + return "clobber"; + case InlineAsmOperandKind::Immediate: + return "imm"; + case InlineAsmOperandKind::Memory: + case InlineAsmOperandKind::Function: + return "mem"; + } + return {}; + } + + [[nodiscard]] static constexpr std::optional<InlineAsmMemoryConstraint> + memoryConstraintForName(std::string_view Name) noexcept { + for (const InlineAsmMemoryConstraintName &Entry : InlineAsmMemoryConstraintNames) { + if (Entry.Name == Name) + return Entry.Constraint; + } + return std::nullopt; + } + + [[nodiscard]] static constexpr std::optional<std::string_view> + memoryConstraintName(InlineAsmMemoryConstraint Constraint) noexcept { + for (const InlineAsmMemoryConstraintName &Entry : InlineAsmMemoryConstraintNames) { + if (Entry.Constraint == Constraint) + return Entry.Name; + } + return std::nullopt; + } + + [[nodiscard]] constexpr bool isRegisterKind() const noexcept { + return kind() == InlineAsmOperandKind::RegisterUse || + kind() == InlineAsmOperandKind::RegisterDef || + kind() == InlineAsmOperandKind::RegisterDefEarlyClobber; + } + +private: + static constexpr std::uint32_t KindMask = 0x7U; + static constexpr std::uint32_t OperandCountShift = 3U; + static constexpr std::uint32_t OperandCountMask = 0x1FFFU; + static constexpr std::uint32_t PayloadShift = 16U; + static constexpr std::uint32_t MatchedOperandMask = 0x7FFFU; + static constexpr std::uint32_t RegisterClassMask = 0x3FFFU; + static constexpr std::uint32_t MatchedMask = 0x80000000U; + + std::uint32_t Value = 0; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_INLINEASM_H diff --git a/backend/include/rcc/CodeGen/InstructionDescriptor.h b/backend/include/rcc/CodeGen/InstructionDescriptor.h new file mode 100644 index 0000000..ca127d4 --- /dev/null +++ b/backend/include/rcc/CodeGen/InstructionDescriptor.h @@ -0,0 +1,350 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_INSTRUCTIONDESCRIPTOR_H +#define RCC_CODEGEN_INSTRUCTIONDESCRIPTOR_H + +#include "rcc/CodeGen/LowLevelType.h" +#include "rcc/CodeGen/MachineIRTypes.h" +#include "rcc/CodeGen/MachineOperand.h" +#include "rcc/CodeGen/Register.h" +#include "rcc/Support/Error.h" + +#include <cstddef> +#include <cstdint> +#include <functional> +#include <initializer_list> +#include <optional> +#include <span> +#include <string> +#include <string_view> +#include <unordered_map> +#include <utility> +#include <vector> + +namespace rcc { + +class MachineInstr; +class MachineMemOperand; +class MachineRegisterInfo; + +using ExplicitOperandVerifier = Expected<void> (*)(Opcode, std::span<const MachineOperand>, + const MachineRegisterInfo &); +using InstructionSemanticVerifier = Expected<void> (*)(const MachineInstr &, + const MachineRegisterInfo &); + +class OperandKindSet final { +public: + static_assert(static_cast<std::uint32_t>(MachineOperand::Kind::Count) <= 32); + + constexpr OperandKindSet() noexcept = default; + + OperandKindSet(std::initializer_list<MachineOperand::Kind> 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<std::uint32_t>(Kind); + if (Index >= static_cast<std::uint32_t>(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<std::uint32_t>(Kind); + if (Index < static_cast<std::uint32_t>(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; +}; + +class LowLevelTypeKindSet final { +public: + static_assert(static_cast<std::uint32_t>(LowLevelType::Kind::VectorPointer) < 16); + + constexpr LowLevelTypeKindSet() noexcept = default; + + LowLevelTypeKindSet(std::initializer_list<LowLevelType::Kind> Kinds) noexcept; + + [[nodiscard]] constexpr bool empty() const noexcept { + return Mask == 0; + } + + [[nodiscard]] constexpr bool contains(LowLevelType::Kind Kind) const noexcept { + const auto Index = static_cast<std::uint32_t>(Kind); + return Index < 16 && (Mask & (std::uint16_t{1} << Index)) != 0; + } + + constexpr void add(LowLevelType::Kind Kind) noexcept { + const auto Index = static_cast<std::uint32_t>(Kind); + if (Index < 16) + Mask |= std::uint16_t{1} << Index; + } + + [[nodiscard]] constexpr std::uint16_t rawValue() const noexcept { + return Mask; + } + + friend constexpr bool operator==(LowLevelTypeKindSet Left, + LowLevelTypeKindSet Right) noexcept = default; + +private: + std::uint16_t Mask = 0; +}; + +enum class RegisterOperandRole : std::uint8_t { + None, + Def, + Use, + DefUse, + Any, +}; + +enum class MemoryOperandRole : std::uint8_t { + None, + Address, + Value, + Size, + Ordering, +}; + +struct OperandTypeConstraint final { + LowLevelTypeKindSet Kinds; + std::optional<std::uint16_t> EqualityGroup; +}; + +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, + MayRaiseFPException, + Phi, + FakeUse, + Generic, + TerminalEmission, + UsesCustomInserter, + Count, +}; + +class InstructionFlags final { +public: + static_assert(static_cast<std::uint32_t>(InstructionFlag::Count) <= 32); + + constexpr InstructionFlags() noexcept = default; + + InstructionFlags(std::initializer_list<InstructionFlag> Flags) noexcept; + + [[nodiscard]] constexpr bool has(InstructionFlag Flag) const noexcept { + const auto Index = static_cast<std::uint32_t>(Flag); + if (Index >= static_cast<std::uint32_t>(InstructionFlag::Count)) + return false; + return (Mask & (std::uint32_t{1} << Index)) != 0; + } + + constexpr void set(InstructionFlag Flag) noexcept { + const auto Index = static_cast<std::uint32_t>(Flag); + if (Index < static_cast<std::uint32_t>(InstructionFlag::Count)) + Mask |= std::uint32_t{1} << Index; + } + + constexpr void clear(InstructionFlag Flag) noexcept { + const auto Index = static_cast<std::uint32_t>(Flag); + if (Index < static_cast<std::uint32_t>(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, + PreRAExpansion, + RegisterAllocator, + PrologueEpilogueInsertion, + PostRAExpansion, + Finalization, + AsmPrinter, + TargetLateExpansion, +}; + +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<RegisterClassID> RegClass; + std::optional<RegisterBankID> RegBank; + std::optional<ImmediatePredicateID> ImmediatePredicate; + MemoryOperandRole MemoryRole = MemoryOperandRole::None; + std::optional<std::size_t> DefaultTiedOperand; + bool EarlyClobber = false; + bool Optional = false; + bool VariadicGroup = false; + bool AllowSubReg = false; + bool AllowAnyTargetFlags = false; + std::optional<NamedOperandID> Name; + std::optional<TargetOperandTypeID> TargetOperandType; + std::uint32_t AllowedTargetFlags = 0; + std::optional<OperandTypeConstraint> TypeConstraint; +}; + +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<OperandDescriptor> Operands; + std::size_t NumExplicitDefs = 0; + bool Variadic = false; + std::size_t MinimumVariadicRepetitions = 0; + std::vector<ImplicitRegisterDescriptor> ImplicitDefs; + std::vector<ImplicitRegisterDescriptor> ImplicitUses; + bool AllowExtraImplicitOperands = false; + InstructionFlags Flags; + SchedulingClassID SchedulingClass; + EncodingCategoryID EncodingCategory; + std::uint32_t SizeInBytes = 0; + AssemblyFormatID AssemblyFormat; + bool RequiresMemoryOperands = false; + PseudoOwner Owner = PseudoOwner::None; + PipelineStage Deadline = PipelineStage::Emission; + ExplicitOperandVerifier VerifyExplicitOperands = nullptr; + InstructionSemanticVerifier VerifyInstructionSemantics = nullptr; +}; + +[[nodiscard]] Expected<std::vector<const OperandDescriptor *>> +resolveExplicitOperandDescriptors(const InstructionDescriptor &Descriptor, + std::size_t OperandCount); + +struct ImmediatePredicate final { + ImmediatePredicateID ID; + std::string Name; + std::function<bool(const MachineOperand &)> Predicate; +}; + +struct MachineOperandTie final { + std::size_t DefOperand; + std::size_t UseOperand; + + friend bool operator==(MachineOperandTie Left, MachineOperandTie Right) noexcept = default; +}; + +class InstructionSet final { +public: + [[nodiscard]] static Expected<InstructionSet> + create(std::vector<InstructionDescriptor> Descriptors, + std::vector<ImmediatePredicate> ImmediatePredicates = {}); + + [[nodiscard]] const InstructionDescriptor *descriptor(Opcode InstructionOpcode) const noexcept; + [[nodiscard]] const InstructionDescriptor *descriptor(std::string_view Name) const noexcept; + [[nodiscard]] std::span<const InstructionDescriptor> descriptors() const noexcept { + return Descriptors; + } + + [[nodiscard]] Expected<void> validateExplicitOperands(Opcode InstructionOpcode, + std::span<const MachineOperand> Operands, + const MachineRegisterInfo &RegInfo) const; + [[nodiscard]] Expected<void> validateOperandTies(Opcode InstructionOpcode, + std::span<const MachineOperand> ExplicitOperands, + std::span<const MachineOperandTie> Ties) const; + [[nodiscard]] Expected<void> + validateImplicitOperands(Opcode InstructionOpcode, + std::span<const MachineOperand *const> ImplicitOperands) const; + [[nodiscard]] Expected<void> + validateMemOperands(Opcode InstructionOpcode, + std::span<const MachineMemOperand *const> MemOperands) const; + [[nodiscard]] Expected<void> validateInstruction(const MachineInstr &Instruction, + const MachineRegisterInfo &RegInfo) const; + + [[nodiscard]] Expected<std::vector<MachineOperand>> + createDefaultImplicitOperands(const InstructionDescriptor &Descriptor) const; + +private: + InstructionSet(std::vector<InstructionDescriptor> Descriptors, + std::vector<ImmediatePredicate> ImmediatePredicates) + : Descriptors(std::move(Descriptors)), ImmediatePredicates(std::move(ImmediatePredicates)) {} + + [[nodiscard]] const ImmediatePredicate * + immediatePredicate(ImmediatePredicateID ID) const noexcept; + [[nodiscard]] Expected<void> validateExplicitOperands(const InstructionDescriptor &Descriptor, + std::span<const MachineOperand> Operands, + const MachineRegisterInfo &RegInfo) const; + [[nodiscard]] Expected<void> validateOperandTies(const InstructionDescriptor &Descriptor, + std::span<const MachineOperand> ExplicitOperands, + std::span<const MachineOperandTie> Ties) const; + [[nodiscard]] Expected<void> + validateImplicitOperands(const InstructionDescriptor &Descriptor, + std::span<const MachineOperand *const> ImplicitOperands) const; + [[nodiscard]] Expected<void> + validateMemOperands(const InstructionDescriptor &Descriptor, + std::span<const MachineMemOperand *const> MemOperands) const; + [[nodiscard]] Expected<void> validateOperand(const OperandDescriptor &Descriptor, + const MachineOperand &Operand, + const MachineRegisterInfo &RegInfo) const; + + std::vector<InstructionDescriptor> Descriptors; + std::vector<ImmediatePredicate> ImmediatePredicates; + std::unordered_map<Opcode, std::size_t> OpcodeToIndex; + std::unordered_map<std::string, std::size_t> NameToIndex; + std::unordered_map<ImmediatePredicateID, std::size_t> PredicateToIndex; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_INSTRUCTIONDESCRIPTOR_H diff --git a/backend/include/rcc/CodeGen/LiveInterval.h b/backend/include/rcc/CodeGen/LiveInterval.h new file mode 100644 index 0000000..28791f1 --- /dev/null +++ b/backend/include/rcc/CodeGen/LiveInterval.h @@ -0,0 +1,144 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_LIVEINTERVAL_H +#define RCC_CODEGEN_LIVEINTERVAL_H + +#include "rcc/CodeGen/MachineIRTypes.h" +#include "rcc/CodeGen/Register.h" +#include "rcc/CodeGen/SlotIndexes.h" +#include "rcc/CodeGen/StableID.h" +#include "rcc/Support/Error.h" + +#include <compare> +#include <cstdint> +#include <limits> +#include <optional> +#include <span> +#include <vector> + +namespace rcc { + +class LiveValueNumberID final { +public: + constexpr LiveValueNumberID() noexcept = default; + + [[nodiscard]] static constexpr LiveValueNumberID fromValue(std::uint32_t Value) noexcept { + return LiveValueNumberID(Value); + } + + [[nodiscard]] constexpr bool isValid() const noexcept { + return Value != 0; + } + + [[nodiscard]] constexpr std::uint32_t value() const noexcept { + return Value; + } + + friend constexpr bool operator==(LiveValueNumberID, LiveValueNumberID) noexcept = default; + friend constexpr auto operator<=>(LiveValueNumberID, LiveValueNumberID) noexcept = default; + +private: + explicit constexpr LiveValueNumberID(std::uint32_t Value) noexcept : Value(Value) {} + + std::uint32_t Value = 0; +}; + +enum class LiveValueState : std::uint8_t { + Normal, + Phi, + Unused, + Redefined, +}; + +struct LiveValueNumber final { + LiveValueNumberID ID; + SlotIndex Definition; + LiveValueState State = LiveValueState::Normal; + std::optional<MachineInstrStableID> Origin; + + friend bool operator==(const LiveValueNumber &, const LiveValueNumber &) = default; +}; + +struct LiveSegment final { + SlotIndex Start; + SlotIndex End; + LiveValueNumberID Value; + + [[nodiscard]] constexpr bool empty() const noexcept { + return !(Start < End); + } + + [[nodiscard]] constexpr bool contains(SlotIndex Position) const noexcept { + return !(Position < Start) && Position < End; + } + + [[nodiscard]] constexpr bool overlaps(const LiveSegment &Other) const noexcept { + return Start < Other.End && Other.Start < End; + } + + friend constexpr bool operator==(const LiveSegment &, const LiveSegment &) noexcept = default; +}; + +class LiveRange final { +public: + [[nodiscard]] std::span<const LiveSegment> segments() const noexcept { + return Segments; + } + [[nodiscard]] std::span<const LiveValueNumber> values() const noexcept { + return Values; + } + [[nodiscard]] bool empty() const noexcept { + return Segments.empty(); + } + + [[nodiscard]] const LiveSegment *segmentAt(SlotIndex Position) const noexcept; + [[nodiscard]] const LiveValueNumber *value(LiveValueNumberID ID) const noexcept; + [[nodiscard]] const LiveValueNumber *valueAt(SlotIndex Position) const noexcept; + [[nodiscard]] bool liveAt(SlotIndex Position) const noexcept; + [[nodiscard]] bool overlaps(const LiveRange &Other) const noexcept; + + [[nodiscard]] Expected<LiveValueNumberID> + createValue(SlotIndex Definition, LiveValueState State = LiveValueState::Normal, + std::optional<MachineInstrStableID> Origin = std::nullopt); + [[nodiscard]] Expected<void> addSegment(LiveSegment Segment); + [[nodiscard]] Expected<void> extend(LiveValueNumberID Value, SlotIndex Start, SlotIndex End); + [[nodiscard]] Expected<void> remove(SlotIndex Start, SlotIndex End); + [[nodiscard]] Expected<void> shrinkToUses(std::span<const SlotIndex> Uses); + [[nodiscard]] Expected<void> verify() const; + +private: + void mergeAdjacent(); + + std::vector<LiveSegment> Segments; + std::vector<LiveValueNumber> Values; +}; + +struct LiveSubRange final { + LaneMask Lanes; + LiveRange Range; +}; + +struct LiveInterval final { + Register Reg; + LiveRange MainRange; + std::vector<LiveSubRange> SubRanges; + std::uint64_t SpillWeight = 0; + bool Spillable = true; + std::vector<PhysicalRegister> AllocationHints; + std::optional<Register> SplitParent; + std::optional<Register> Origin; + + [[nodiscard]] bool overlaps(const LiveInterval &Other) const noexcept { + return MainRange.overlaps(Other.MainRange); + } +}; + +} // namespace rcc + +template <> struct std::hash<rcc::LiveValueNumberID> { + [[nodiscard]] std::size_t operator()(rcc::LiveValueNumberID ID) const noexcept { + return std::hash<std::uint32_t>{}(ID.value()); + } +}; + +#endif // RCC_CODEGEN_LIVEINTERVAL_H diff --git a/backend/include/rcc/CodeGen/LiveIntervals.h b/backend/include/rcc/CodeGen/LiveIntervals.h new file mode 100644 index 0000000..b5ba05a --- /dev/null +++ b/backend/include/rcc/CodeGen/LiveIntervals.h @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_LIVEINTERVALS_H +#define RCC_CODEGEN_LIVEINTERVALS_H + +#include "rcc/CodeGen/LiveInterval.h" +#include "rcc/CodeGen/MachinePassManager.h" +#include "rcc/CodeGen/RegisterMask.h" + +#include <cstdint> +#include <memory> +#include <optional> +#include <span> +#include <string_view> +#include <unordered_map> +#include <vector> + +namespace rcc { + +class MachineFunction; +class MachineInstr; +class TargetDescription; + +struct RegMaskPosition final { + SlotIndex Position; + MachineInstrStableID Instruction; + std::shared_ptr<const RegisterMask> Mask; +}; + +class LiveIntervalsAnalysis final { +public: + class Result final { + public: + [[nodiscard]] const SlotIndexesAnalysis::Result &slotIndexes() const noexcept { + return Indexes; + } + [[nodiscard]] SlotIndex slot(const MachineInstr &Instruction) const noexcept { + return Indexes.instructionIndex(Instruction); + } + [[nodiscard]] const LiveInterval *interval(Register Reg) const noexcept; + [[nodiscard]] LiveInterval *interval(Register Reg) noexcept; + [[nodiscard]] const LiveRange *regUnitRange(RegisterUnitID Unit) const noexcept; + [[nodiscard]] std::span<const RegMaskPosition> regMaskPositions() const noexcept { + return RegMasks; + } + [[nodiscard]] bool interferes(Register Left, Register Right) const noexcept; + [[nodiscard]] bool regMaskClobbers(const LiveInterval &Interval, + PhysicalRegister Candidate) const noexcept; + [[nodiscard]] Expected<void> shrinkToUses(Register Reg, std::span<const SlotIndex> Uses); + + [[nodiscard]] bool invalidate(const MachineFunction &, + const PreservedAnalyses &Preserved) const noexcept; + + private: + SlotIndexesAnalysis::Result Indexes; + std::unordered_map<Register, LiveInterval> Intervals; + std::unordered_map<RegisterUnitID, LiveRange> RegisterUnitRanges; + std::vector<RegMaskPosition> RegMasks; + + friend class LiveIntervalsAnalysis; + }; + + [[nodiscard]] static constexpr std::string_view name() noexcept { + return "LiveIntervals"; + } + + [[nodiscard]] static Expected<Result> run(MachineFunction &Function, + const TargetDescription &Target, + MachineFunctionAnalysisManager &Analyses); +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_LIVEINTERVALS_H diff --git a/backend/include/rcc/CodeGen/LivePhysRegs.h b/backend/include/rcc/CodeGen/LivePhysRegs.h new file mode 100644 index 0000000..3d4b7ef --- /dev/null +++ b/backend/include/rcc/CodeGen/LivePhysRegs.h @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_LIVEPHYSREGS_H +#define RCC_CODEGEN_LIVEPHYSREGS_H + +#include "rcc/CodeGen/LiveVariables.h" +#include "rcc/Support/Error.h" + +#include <span> + +namespace rcc { + +class MachineBasicBlock; +class TargetDescription; + +[[nodiscard]] Expected<bool> +repairPostRAMachineBlockLiveness(std::span<MachineBasicBlock *const> Blocks, + const TargetDescription &Target, + const LiveVariablesAnalysis::Result &Liveness); + +} // namespace rcc + +#endif // RCC_CODEGEN_LIVEPHYSREGS_H diff --git a/backend/include/rcc/CodeGen/LiveRangeEdit.h b/backend/include/rcc/CodeGen/LiveRangeEdit.h new file mode 100644 index 0000000..66696df --- /dev/null +++ b/backend/include/rcc/CodeGen/LiveRangeEdit.h @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_LIVERANGEEDIT_H +#define RCC_CODEGEN_LIVERANGEEDIT_H + +#include "rcc/CodeGen/VirtRegMap.h" + +#include <optional> + +namespace rcc { + +class TargetDescription; + +class LiveRangeEdit final { +public: + LiveRangeEdit(MachineFunction &Function, const TargetDescription &Target, VirtRegMap &Mapping) + : Function(&Function), Target(&Target), Mapping(&Mapping) {} + + [[nodiscard]] Expected<Register> createFrom(Register Parent, bool Spillable, + std::optional<Register> Origin = std::nullopt); + [[nodiscard]] Expected<FrameIndex> getOrCreateSpillSlot(Register VirtualReg); + [[nodiscard]] Expected<void> eraseVirtualRegister(Register VirtualReg); + +private: + MachineFunction *Function; + const TargetDescription *Target; + VirtRegMap *Mapping; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_LIVERANGEEDIT_H diff --git a/backend/include/rcc/CodeGen/LiveRegMatrix.h b/backend/include/rcc/CodeGen/LiveRegMatrix.h new file mode 100644 index 0000000..fd1bac4 --- /dev/null +++ b/backend/include/rcc/CodeGen/LiveRegMatrix.h @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_LIVEREGMATRIX_H +#define RCC_CODEGEN_LIVEREGMATRIX_H + +#include "rcc/CodeGen/LiveIntervals.h" +#include "rcc/CodeGen/TargetRegisterInfo.h" + +#include <cstdint> +#include <optional> +#include <span> +#include <unordered_map> +#include <utility> +#include <vector> + +namespace rcc { + +class MachineFunction; +class TargetDescription; + +enum class RegisterInterferenceKind : std::uint8_t { + Free, + VirtualRegister, + FixedRegister, + RegisterMask, + RegisterUnit, +}; + +struct RegisterInterference final { + RegisterInterferenceKind Kind = RegisterInterferenceKind::Free; + std::optional<Register> VirtualReg; + std::optional<PhysicalRegister> FixedReg; + std::optional<RegisterUnitID> Unit; + std::optional<MachineInstrStableID> Instruction; +}; + +class LiveRegMatrix final { +public: + LiveRegMatrix(const MachineFunction &Function, const TargetDescription &Target, + const LiveIntervalsAnalysis::Result &Intervals) noexcept + : Function(&Function), Target(&Target), Intervals(&Intervals) {} + + [[nodiscard]] Expected<void> assign(Register VirtualReg, PhysicalRegister PhysicalReg); + [[nodiscard]] Expected<void> unassign(Register VirtualReg); + [[nodiscard]] std::optional<PhysicalRegister> assignment(Register VirtualReg) const noexcept; + [[nodiscard]] RegisterInterference checkInterference(Register VirtualReg, + PhysicalRegister PhysicalReg) const noexcept; + [[nodiscard]] std::vector<RegisterInterference> + collectInterferences(Register VirtualReg, PhysicalRegister PhysicalReg) const; + + [[nodiscard]] std::span<const std::pair<Register, PhysicalRegister>> + assignments() const noexcept { + return OrderedAssignments; + } + +private: + [[nodiscard]] bool intervalsOverlapOnCandidates(const LiveInterval &Left, + PhysicalRegister LeftReg, + const LiveInterval &Right, + PhysicalRegister RightReg) const noexcept; + [[nodiscard]] bool intervalOverlapsUnit(const LiveInterval &Interval, RegisterUnitLane Unit, + const LiveRange &UnitRange) const noexcept; + + const MachineFunction *Function; + const TargetDescription *Target; + const LiveIntervalsAnalysis::Result *Intervals; + std::unordered_map<Register, PhysicalRegister> Assignments; + std::vector<std::pair<Register, PhysicalRegister>> OrderedAssignments; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_LIVEREGMATRIX_H diff --git a/backend/include/rcc/CodeGen/LiveRegUnits.h b/backend/include/rcc/CodeGen/LiveRegUnits.h new file mode 100644 index 0000000..2e18bc9 --- /dev/null +++ b/backend/include/rcc/CodeGen/LiveRegUnits.h @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_LIVEREGUNITS_H +#define RCC_CODEGEN_LIVEREGUNITS_H + +#include "rcc/CodeGen/MachineIRTypes.h" +#include "rcc/CodeGen/Register.h" +#include "rcc/Support/Error.h" + +#include <cstddef> +#include <cstdint> +#include <optional> +#include <span> +#include <unordered_map> +#include <vector> + +namespace rcc { + +class MachineFunction; +class MachineInstr; +class MachineOperand; +class RegisterMask; +class TargetDescription; +struct PhysicalRegisterDesc; + +class LiveRegUnits final { +public: + [[nodiscard]] LaneMask lanes(RegisterUnitID Unit) const noexcept; + [[nodiscard]] bool contains(RegisterUnitID Unit, LaneMask Lanes = LaneMask::all()) const noexcept; + [[nodiscard]] bool intersects(RegisterUnitID Unit, + LaneMask Lanes = LaneMask::all()) const noexcept; + [[nodiscard]] bool empty() const noexcept { + return Values.empty(); + } + + void add(RegisterUnitID Unit, LaneMask Lanes); + void remove(RegisterUnitID Unit, LaneMask Lanes); + void unite(const LiveRegUnits &Other); + void subtract(const LiveRegUnits &Other); + + [[nodiscard]] Expected<bool> available(PhysicalRegister Reg, + const TargetDescription &Target) const; + [[nodiscard]] static Expected<void> accumulateUsedDefed(const MachineInstr &Instruction, + LiveRegUnits &Modified, + LiveRegUnits &Used, + const TargetDescription &Target); + + [[nodiscard]] const std::unordered_map<RegisterUnitID, LaneMask> &entries() const noexcept { + return Values; + } + + friend bool operator==(const LiveRegUnits &, const LiveRegUnits &) = default; + +private: + std::unordered_map<RegisterUnitID, LaneMask> Values; +}; + +struct PhysicalUnitAccess final { + RegisterUnitID Unit; + LaneMask Lanes; +}; + +[[nodiscard]] LaneMask normalizeUnitLanes(LaneMask UnitViewLanes, + LaneMask RegisterLanes = LaneMask::all()) noexcept; +[[nodiscard]] LaneMask denormalizeUnitLanes(LaneMask UnitLanes, LaneMask UnitViewLanes) noexcept; +[[nodiscard]] LaneMask physicalRegisterLanes(const PhysicalRegisterDesc &Description) noexcept; +[[nodiscard]] LiveRegUnits physicalRegisterCoverage(const PhysicalRegisterDesc &Description, + LaneMask Requested = LaneMask::all()); +[[nodiscard]] bool isSubset(const LiveRegUnits &Subset, const LiveRegUnits &Superset) noexcept; +[[nodiscard]] std::size_t laneCount(const LiveRegUnits &Set) noexcept; + +[[nodiscard]] Expected<LaneMask> virtualRegisterLanes(Register Reg, const MachineFunction &Function, + const TargetDescription &Target); +[[nodiscard]] Expected<LaneMask> virtualOperandLanes(const MachineOperand &Operand, + const MachineFunction &Function, + const TargetDescription &Target); +[[nodiscard]] Expected<std::vector<PhysicalUnitAccess>> +physicalOperandUnits(const MachineOperand &Operand, const TargetDescription &Target); + +void addPhysicalRegister(LiveRegUnits &Set, const PhysicalRegisterDesc &Description, + LaneMask Requested = LaneMask::all()); +[[nodiscard]] bool isReservedPhysicalRegister(Register Reg, const MachineFunction &Function, + const TargetDescription &Target) noexcept; +[[nodiscard]] Expected<void> addRegisterMaskDefs(LiveRegUnits &Set, const RegisterMask &Mask, + const TargetDescription &Target); +[[nodiscard]] Expected<void> addRegisterMaskUses(LiveRegUnits &Set, const RegisterMask &Mask, + const TargetDescription &Target); + +} // namespace rcc + +#endif // RCC_CODEGEN_LIVEREGUNITS_H diff --git a/backend/include/rcc/CodeGen/LiveVariables.h b/backend/include/rcc/CodeGen/LiveVariables.h new file mode 100644 index 0000000..ee11619 --- /dev/null +++ b/backend/include/rcc/CodeGen/LiveVariables.h @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_LIVEVARIABLES_H +#define RCC_CODEGEN_LIVEVARIABLES_H + +#include "rcc/CodeGen/LiveRegUnits.h" +#include "rcc/CodeGen/MachinePassManager.h" +#include "rcc/CodeGen/Register.h" +#include "rcc/Support/Error.h" + +#include <cstddef> +#include <cstdint> +#include <string_view> +#include <unordered_map> + +namespace rcc { + +class MachineBasicBlock; +class MachineFunction; +class TargetDescription; + +class RegisterLaneSet final { +public: + [[nodiscard]] LaneMask lanes(Register Reg) const noexcept; + [[nodiscard]] bool contains(Register Reg, LaneMask Lanes = LaneMask::all()) const noexcept; + [[nodiscard]] bool intersects(Register Reg, LaneMask Lanes = LaneMask::all()) const noexcept; + [[nodiscard]] bool empty() const noexcept { + return Values.empty(); + } + + void add(Register Reg, LaneMask Lanes); + void remove(Register Reg, LaneMask Lanes); + void unite(const RegisterLaneSet &Other); + void subtract(const RegisterLaneSet &Other); + + [[nodiscard]] const std::unordered_map<Register, LaneMask> &entries() const noexcept { + return Values; + } + + friend bool operator==(const RegisterLaneSet &, const RegisterLaneSet &) = default; + +private: + std::unordered_map<Register, LaneMask> Values; +}; + +struct MachineBlockLiveness final { + RegisterLaneSet VirtualUses; + RegisterLaneSet VirtualDefs; + RegisterLaneSet VirtualLiveIn; + RegisterLaneSet VirtualLiveOut; + LiveRegUnits PhysicalUses; + LiveRegUnits PhysicalDefs; + LiveRegUnits PhysicalLiveIn; + LiveRegUnits PhysicalLiveOut; +}; + +class LiveVariablesAnalysis final { +public: + class Result final { + public: + [[nodiscard]] MachineBlockLiveness &getOrCreateBlock(const MachineBasicBlock &Block); + [[nodiscard]] const MachineBlockLiveness *block(const MachineBasicBlock &Block) const noexcept; + [[nodiscard]] bool isLiveIn(const MachineBasicBlock &Block, Register Reg, + LaneMask Lanes = LaneMask::all()) const noexcept; + [[nodiscard]] bool isLiveOut(const MachineBasicBlock &Block, Register Reg, + LaneMask Lanes = LaneMask::all()) const noexcept; + [[nodiscard]] bool isUnitLiveIn(const MachineBasicBlock &Block, RegisterUnitID Unit, + LaneMask Lanes = LaneMask::all()) const noexcept; + [[nodiscard]] bool isUnitLiveOut(const MachineBasicBlock &Block, RegisterUnitID Unit, + LaneMask Lanes = LaneMask::all()) const noexcept; + + [[nodiscard]] bool invalidate(const MachineFunction &, + const PreservedAnalyses &Preserved) const noexcept; + + private: + std::unordered_map<const MachineBasicBlock *, MachineBlockLiveness> Blocks; + + friend class LiveVariablesAnalysis; + }; + + [[nodiscard]] static constexpr std::string_view name() noexcept { + return "LiveVariables"; + } + + [[nodiscard]] static Expected<Result> run(MachineFunction &Function, + const TargetDescription &Target, + MachineFunctionAnalysisManager &Analyses); +}; + +class RebuildMachineLivenessPass final : public MachineFunctionPass { +public: + [[nodiscard]] const MachinePassContract &contract() const noexcept override; + [[nodiscard]] Expected<MachinePassResult> run(MachineFunction &Function, + const TargetDescription &Target, + MachineFunctionAnalysisManager &Analyses) override; +}; + +[[nodiscard]] Expected<void> verifyTrackedMachineLiveness(const MachineFunction &Function, + const TargetDescription &Target); +[[nodiscard]] Expected<bool> rebuildMachineLiveness(MachineFunction &Function, + const TargetDescription &Target); + +} // namespace rcc + +#endif // RCC_CODEGEN_LIVEVARIABLES_H diff --git a/backend/include/rcc/CodeGen/LowLevelType.h b/backend/include/rcc/CodeGen/LowLevelType.h new file mode 100644 index 0000000..1915ccd --- /dev/null +++ b/backend/include/rcc/CodeGen/LowLevelType.h @@ -0,0 +1,163 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_LOWLEVELTYPE_H +#define RCC_CODEGEN_LOWLEVELTYPE_H + +#include "rcc/Support/Error.h" + +#include <cstddef> +#include <cstdint> +#include <functional> +#include <optional> +#include <string> +#include <string_view> +#include <type_traits> + +namespace rcc { + +class LowLevelType final { +public: + enum class Kind : std::uint8_t { + Invalid, + Token, + CategoryNeutralScalar, + Pointer, + Vector, + VectorPointer, + }; + + static constexpr std::uint32_t MaxBitWidth = (std::uint32_t{1} << 16) - 1; + static constexpr std::uint32_t MaxElementCount = (std::uint32_t{1} << 16) - 1; + static constexpr std::uint32_t MaxAddressSpace = (std::uint32_t{1} << 24) - 1; + + constexpr LowLevelType() noexcept = default; + + [[nodiscard]] static constexpr LowLevelType token() noexcept { + return LowLevelType(encode(Kind::Token, 0, 0, 0, false)); + } + + [[nodiscard]] static Expected<LowLevelType> makeCategoryNeutralScalar(std::uint64_t BitWidth); + [[nodiscard]] static Expected<LowLevelType> makePointer(std::uint64_t AddressSpace, + std::uint64_t BitWidth); + [[nodiscard]] static Expected<LowLevelType> makeFixedVector(std::uint64_t ElementCount, + LowLevelType ElementType); + [[nodiscard]] static Expected<LowLevelType> makeScalableVector(std::uint64_t MinimumElementCount, + LowLevelType ElementType); + + [[nodiscard]] constexpr Kind kind() const noexcept { + return static_cast<Kind>((RawValue >> KindShift) & KindMask); + } + + [[nodiscard]] constexpr bool isValid() const noexcept { + return kind() != Kind::Invalid; + } + + [[nodiscard]] constexpr bool isToken() const noexcept { + return kind() == Kind::Token; + } + + [[nodiscard]] constexpr bool isCategoryNeutralScalar() const noexcept { + return kind() == Kind::CategoryNeutralScalar; + } + + [[nodiscard]] constexpr bool isPointer() const noexcept { + return kind() == Kind::Pointer; + } + + [[nodiscard]] constexpr bool isVectorValue() const noexcept { + return kind() == Kind::Vector; + } + + [[nodiscard]] constexpr bool isPointerVector() const noexcept { + return kind() == Kind::VectorPointer; + } + + [[nodiscard]] constexpr bool isPointerOrPointerVector() const noexcept { + return isPointer() || isPointerVector(); + } + + [[nodiscard]] constexpr bool isVector() const noexcept { + return kind() == Kind::Vector || kind() == Kind::VectorPointer; + } + + [[nodiscard]] constexpr bool isScalableVector() const noexcept { + return isVector() && ((RawValue >> ScalableShift) & std::uint64_t{1}) != 0; + } + + [[nodiscard]] constexpr bool isScalarValue() const noexcept { + return isCategoryNeutralScalar() || isPointer(); + } + + [[nodiscard]] constexpr std::uint32_t scalarBitWidth() const noexcept { + return static_cast<std::uint32_t>((RawValue >> BitWidthShift) & BitWidthMask); + } + + [[nodiscard]] constexpr std::uint32_t minimumElementCount() const noexcept { + if (!isVector()) + return 0; + return static_cast<std::uint32_t>((RawValue >> ElementCountShift) & ElementCountMask); + } + + [[nodiscard]] constexpr std::uint64_t minimumBitWidth() const noexcept { + return isVector() ? static_cast<std::uint64_t>(scalarBitWidth()) * minimumElementCount() + : scalarBitWidth(); + } + + [[nodiscard]] std::optional<std::uint32_t> addressSpace() const noexcept; + [[nodiscard]] std::optional<LowLevelType> scalarElementType() const noexcept; + + [[nodiscard]] constexpr std::uint64_t rawValue() const noexcept { + return RawValue; + } + + [[nodiscard]] std::string toString() const; + + friend constexpr bool operator==(LowLevelType Left, LowLevelType Right) noexcept = default; + +private: + static constexpr unsigned BitWidthShift = 0; + static constexpr unsigned ElementCountShift = 16; + static constexpr unsigned AuxiliaryShift = 32; + static constexpr unsigned ScalableShift = 56; + static constexpr unsigned KindShift = 60; + + static constexpr std::uint64_t BitWidthMask = (std::uint64_t{1} << 16) - 1; + static constexpr std::uint64_t ElementCountMask = (std::uint64_t{1} << 16) - 1; + static constexpr std::uint64_t AuxiliaryMask = (std::uint64_t{1} << 24) - 1; + static constexpr std::uint64_t KindMask = (std::uint64_t{1} << 4) - 1; + + explicit constexpr LowLevelType(std::uint64_t RawValue) noexcept : RawValue(RawValue) {} + + [[nodiscard]] static constexpr std::uint64_t encode(Kind TypeKind, std::uint32_t BitWidth, + std::uint32_t ElementCount, + std::uint32_t Auxiliary, + bool IsScalable) noexcept { + return (static_cast<std::uint64_t>(TypeKind) << KindShift) | + (static_cast<std::uint64_t>(IsScalable) << ScalableShift) | + (static_cast<std::uint64_t>(Auxiliary) << AuxiliaryShift) | + (static_cast<std::uint64_t>(ElementCount) << ElementCountShift) | + (static_cast<std::uint64_t>(BitWidth) << BitWidthShift); + } + + [[nodiscard]] static Expected<LowLevelType> makeVector(std::uint64_t ElementCount, + bool IsScalable, LowLevelType ElementType); + + [[nodiscard]] constexpr std::uint32_t auxiliaryValue() const noexcept { + return static_cast<std::uint32_t>((RawValue >> AuxiliaryShift) & AuxiliaryMask); + } + + std::uint64_t RawValue = 0; +}; + +static_assert(sizeof(LowLevelType) == sizeof(std::uint64_t)); +static_assert(std::is_trivially_copyable_v<LowLevelType>); + +} // namespace rcc + +template <> struct std::hash<rcc::LowLevelType> { + [[nodiscard]] std::size_t operator()(rcc::LowLevelType Type) const noexcept { + return std::hash<std::uint64_t>{}(Type.rawValue()); + } +}; + +#endif // RCC_CODEGEN_LOWLEVELTYPE_H diff --git a/backend/include/rcc/CodeGen/MIRParser/MIParser.h b/backend/include/rcc/CodeGen/MIRParser/MIParser.h new file mode 100644 index 0000000..4df2ea4 --- /dev/null +++ b/backend/include/rcc/CodeGen/MIRParser/MIParser.h @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_MIRPARSER_MIPARSER_H +#define RCC_CODEGEN_MIRPARSER_MIPARSER_H + +#include "rcc/CodeGen/GlobalObject.h" +#include "rcc/CodeGen/MachineIRTypes.h" +#include "rcc/CodeGen/MachineMetadata.h" +#include "rcc/CodeGen/Register.h" +#include "rcc/Support/Error.h" +#include "rcc/Support/TextDiagnostic.h" + +#include <cstddef> +#include <cstdint> +#include <functional> +#include <memory> +#include <optional> +#include <span> +#include <string> +#include <string_view> +#include <vector> + +namespace rcc { + +class MachineFunction; +class TargetDescription; + +namespace mir { + +struct MIVirtualRegisterSlot final { + std::uint32_t TextID = 0; + Register Reg; +}; + +struct MIFrameObjectSlot final { + std::uint32_t TextID = 0; + FrameIndex Index; + std::optional<std::string> Name; +}; + +struct MIConstantPoolSlot final { + std::uint32_t TextID = 0; + ConstantPoolIndex Index; +}; + +struct MIJumpTableSlot final { + std::uint32_t TextID = 0; + JumpTableIndex Index; +}; + +struct MIMetadataSlot final { + std::uint32_t TextID = 0; + MachineMetadataReference Reference; +}; + +struct MISyncScopeSlot final { + std::string Name; + SyncScopeID ID; +}; + +struct MIParserContext final { + std::vector<MIVirtualRegisterSlot> VirtualRegisters; + std::vector<MIFrameObjectSlot> StackObjects; + std::vector<MIFrameObjectSlot> FixedStackObjects; + std::vector<MIConstantPoolSlot> ConstantPoolItems; + std::vector<MIJumpTableSlot> JumpTables; + std::vector<MIMetadataSlot> Metadata; + std::vector<MISyncScopeSlot> SyncScopes; + std::function<bool(std::string_view)> HasIRBlock; + std::function<GlobalValueProperties(std::string_view)> GlobalValuePropertiesForName; + std::function<Expected<SyncScopeID>(std::string_view)> InternSyncScope; + std::function<Expected<std::uint32_t>(std::uint32_t)> PointerSizeInBits; +}; + +struct MIParserOptions final { + std::size_t MaximumTokens = std::size_t{1} << 24U; + std::size_t MaximumTokenLength = std::size_t{1} << 24U; + std::size_t MaximumBlocks = std::size_t{1} << 20U; + std::size_t MaximumInstructions = std::size_t{1} << 24U; + std::size_t MaximumOperandsPerInstruction = std::size_t{1} << 20U; + std::size_t MaximumMemoryOperandsPerInstruction = std::size_t{1} << 20U; + bool VerifyAfterParse = true; +}; + +class MIParser final { +public: + MIParser(MachineFunction &Function, const TargetDescription &Target, std::string SourceName, + std::string SourceText, MIParserContext Context = {}, MIParserOptions Options = {}); + MIParser(const MIParser &) = delete; + MIParser &operator=(const MIParser &) = delete; + MIParser(MIParser &&) noexcept; + MIParser &operator=(MIParser &&) noexcept; + ~MIParser(); + + [[nodiscard]] Expected<void> parse(); + + [[nodiscard]] const TextSource &source() const noexcept; + [[nodiscard]] std::span<const TextDiagnostic> diagnostics() const noexcept; + [[nodiscard]] bool failed() const noexcept; + +private: + class Impl; + std::unique_ptr<Impl> Implementation; +}; + +} // namespace mir +} // namespace rcc + +#endif // RCC_CODEGEN_MIRPARSER_MIPARSER_H diff --git a/backend/include/rcc/CodeGen/MIRParser/MIRParser.h b/backend/include/rcc/CodeGen/MIRParser/MIRParser.h new file mode 100644 index 0000000..798a747 --- /dev/null +++ b/backend/include/rcc/CodeGen/MIRParser/MIRParser.h @@ -0,0 +1,130 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_MIRPARSER_MIRPARSER_H +#define RCC_CODEGEN_MIRPARSER_MIRPARSER_H + +#include "rcc/CodeGen/CompilationUnit.h" +#include "rcc/CodeGen/MIRParser/MIParser.h" +#include "rcc/CodeGen/StableID.h" +#include "rcc/MIR/EmbeddedIR.h" +#include "rcc/Support/TextDiagnostic.h" +#include "rcc/Support/YAML.h" + +#include <cstdint> +#include <optional> +#include <span> +#include <string> +#include <utility> +#include <vector> + +namespace rcc { + +class TargetDescription; + +namespace mir { + +struct MIRFunctionDocumentInfo final { + MachineFunctionStableID Function; + std::optional<bool> NoPHIs; + std::optional<bool> IsSSA; + std::optional<bool> NoVRegs; + std::optional<bool> HasFakeUses; + + friend bool operator==(const MIRFunctionDocumentInfo &Left, + const MIRFunctionDocumentInfo &Right) = default; +}; + +class MIRDocument final { +public: + MIRDocument(); + explicit MIRDocument(CompilationUnit Unit); + MIRDocument(const MIRDocument &) = delete; + MIRDocument &operator=(const MIRDocument &) = delete; + MIRDocument(MIRDocument &&) noexcept; + MIRDocument &operator=(MIRDocument &&) noexcept; + ~MIRDocument(); + + [[nodiscard]] CompilationUnit &unit() noexcept { + return Unit; + } + + [[nodiscard]] const CompilationUnit &unit() const noexcept { + return Unit; + } + + [[nodiscard]] const EmbeddedIRReferenceIndex *embeddedIR() const noexcept { + return EmbeddedIR ? &*EmbeddedIR : nullptr; + } + + [[nodiscard]] std::span<const MIRFunctionDocumentInfo> functionDocumentInfo() const noexcept { + return FunctionInfo; + } + + [[nodiscard]] std::span<const MISyncScopeSlot> syncScopes() const noexcept { + return SyncScopes; + } + +private: + CompilationUnit Unit; + std::optional<EmbeddedIRReferenceIndex> EmbeddedIR; + std::vector<MIRFunctionDocumentInfo> FunctionInfo; + std::vector<MISyncScopeSlot> SyncScopes; + + friend class MIRDocumentParser; +}; + +struct MIRDocumentParserOptions final { + yaml::ParseOptions YAML; + MIParserOptions MachineInstructions; + std::string DataLayout; + bool VerifyMachineFunctions = true; +}; + +class MIRDocumentParseResult final { +public: + MIRDocumentParseResult(const MIRDocumentParseResult &) = delete; + MIRDocumentParseResult &operator=(const MIRDocumentParseResult &) = delete; + MIRDocumentParseResult(MIRDocumentParseResult &&) noexcept; + MIRDocumentParseResult &operator=(MIRDocumentParseResult &&) noexcept; + ~MIRDocumentParseResult(); + + [[nodiscard]] bool succeeded() const noexcept { + return Document.has_value() && Diagnostics.empty(); + } + + [[nodiscard]] MIRDocument *document() noexcept { + return Document ? &*Document : nullptr; + } + + [[nodiscard]] const MIRDocument *document() const noexcept { + return Document ? &*Document : nullptr; + } + + [[nodiscard]] const TextSource &source() const noexcept { + return Source; + } + + [[nodiscard]] std::span<const TextDiagnostic> diagnostics() const noexcept { + return Diagnostics; + } + +private: + MIRDocumentParseResult(std::string SourceName, std::string SourceText) + : Source(std::move(SourceName), std::move(SourceText)) {} + + TextSource Source; + std::optional<MIRDocument> Document; + std::vector<TextDiagnostic> Diagnostics; + + friend class MIRDocumentParser; +}; + +[[nodiscard]] MIRDocumentParseResult parseMIRDocument(const TargetDescription &Target, + std::string SourceName, + std::string SourceText, + MIRDocumentParserOptions Options = {}); + +} // namespace mir +} // namespace rcc + +#endif // RCC_CODEGEN_MIRPARSER_MIRPARSER_H diff --git a/backend/include/rcc/CodeGen/MIRPrinter.h b/backend/include/rcc/CodeGen/MIRPrinter.h new file mode 100644 index 0000000..ea58434 --- /dev/null +++ b/backend/include/rcc/CodeGen/MIRPrinter.h @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_MIRPRINTER_H +#define RCC_CODEGEN_MIRPRINTER_H + +#include "rcc/CodeGen/MIRParser/MIRParser.h" +#include "rcc/Support/Error.h" + +#include <string> +#include <vector> + +namespace rcc { + +class MachineFunction; +class MachineBasicBlock; +class MachineInstr; +class MachineOperand; +class TargetDescription; + +namespace mir { + +enum class MIPrintMode : unsigned char { + Complete, + Readable, +}; + +struct MIPrinterOptions final { + MIPrintMode Mode = MIPrintMode::Complete; +}; + +[[nodiscard]] Expected<void> guessSuccessors(const MachineBasicBlock &Block, + const TargetDescription &Target, + std::vector<MachineBasicBlock *> &Result, + bool &IsFallthrough); + +[[nodiscard]] Expected<std::string> printMachineFunctionBody(const MachineFunction &Function, + const TargetDescription &Target, + const MIParserContext &Context = {}, + MIPrinterOptions Options = {}); + +[[nodiscard]] Expected<MIParserContext> +createMachineFunctionPrintContext(const MachineFunction &Function); +[[nodiscard]] Expected<std::string> printMachineInstruction(const MachineInstr &Instruction, + const TargetDescription &Target, + const MIParserContext &Context, + MIPrinterOptions Options = {}); +[[nodiscard]] Expected<std::string> printMachineOperand(const MachineOperand &Operand, + const TargetDescription &Target, + const MIParserContext &Context, + MIPrinterOptions Options = {}); + +struct MIRDocumentPrinterOptions final { + MIPrintMode Mode = MIPrintMode::Complete; + bool EmitEmbeddedIR = true; + std::string DataLayout; +}; + +[[nodiscard]] Expected<std::string> printMIRDocument(const MIRDocument &Document, + const TargetDescription &Target, + MIRDocumentPrinterOptions Options = {}); + +} // namespace mir +} // namespace rcc + +#endif // RCC_CODEGEN_MIRPRINTER_H diff --git a/backend/include/rcc/CodeGen/MIRYamlMapping.h b/backend/include/rcc/CodeGen/MIRYamlMapping.h new file mode 100644 index 0000000..c25559b --- /dev/null +++ b/backend/include/rcc/CodeGen/MIRYamlMapping.h @@ -0,0 +1,144 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_MIRYAMLMAPPING_H +#define RCC_CODEGEN_MIRYAMLMAPPING_H + +#include "rcc/CodeGen/MIRParser/MIRParser.h" +#include "rcc/CodeGen/MachineDebugInfo.h" +#include "rcc/CodeGen/MachineFrameInfo.h" +#include "rcc/CodeGen/MachineJumpTableInfo.h" +#include "rcc/CodeGen/MachineMetadata.h" +#include "rcc/CodeGen/TargetMIRTextInfo.h" +#include "rcc/IR/DataLayout.h" +#include "rcc/Support/Error.h" + +#include <cstdint> +#include <optional> +#include <span> +#include <string> +#include <string_view> +#include <utility> +#include <vector> + +namespace rcc { + +class MachineBasicBlock; +class MachineFunction; +class MachineInstr; + +namespace mir { + +struct PendingJumpTable final { + JumpTableIndex Index; + std::uint32_t TextID = 0; + JumpTableEntryEncoding Encoding = JumpTableEntryEncoding::BlockAddress; + std::uint8_t EntrySize = 8; + std::vector<std::string> Blocks; +}; + +class MIRDocumentParser final { +public: + MIRDocumentParser(const TargetDescription &Target, std::string SourceName, std::string SourceText, + MIRDocumentParserOptions Options) + : Target(Target), Options(std::move(Options)), + Result(std::move(SourceName), std::move(SourceText)) {} + + [[nodiscard]] MIRDocumentParseResult run(); + +private: + [[nodiscard]] bool fail(const TextRange &Range, std::string Message, + std::string Category = "mir-yaml"); + [[nodiscard]] bool importError(const TextRange &Range, const Error &Failure, + std::string Category = "mir-yaml"); + [[nodiscard]] const yaml::Node *node(yaml::NodeID ID) const noexcept { + return Stream->node(ID); + } + [[nodiscard]] const yaml::Node *mappingValue(const yaml::Node &Mapping, + std::string_view Key) const noexcept; + [[nodiscard]] bool requireKind(const yaml::Node &Node, yaml::NodeKind Kind, + std::string_view Description); + [[nodiscard]] bool validateKeys(const yaml::Node &Mapping, std::span<const std::string_view> Keys, + std::string_view Description); + [[nodiscard]] bool scalar(const yaml::Node &Node, std::string_view &Value, + std::string_view Description); + [[nodiscard]] bool boolean(const yaml::Node &Node, bool &Value); + [[nodiscard]] bool unsignedValue(const yaml::Node &Node, std::uint64_t &Value, + std::string_view Description); + [[nodiscard]] bool signedValue(const yaml::Node &Node, std::int64_t &Value, + std::string_view Description); + [[nodiscard]] bool alignment(const yaml::Node &Node, Alignment &Value, bool AllowZero); + [[nodiscard]] bool physicalRegister(const yaml::Node &Node, PhysicalRegister &Reg); + [[nodiscard]] bool physicalRegister(std::string_view Text, const TextRange &Range, + PhysicalRegister &Reg); + [[nodiscard]] bool virtualRegister(std::string_view Text, const TextRange &Range, + MachineFunction &Function, MIParserContext &Context, + Register &Reg); + [[nodiscard]] Expected<MachineMetadataReference> + resolveMetadataReference(const MachineFunction &Function, std::uint32_t Slot) const; + [[nodiscard]] bool parseVariableDebugInfo(const yaml::Node &Entry, MachineFunction &Function, + MachineVariableDebugInfo &DebugInfo); + [[nodiscard]] bool parseFunction(const yaml::Node &Root, MIRDocument &Document); + [[nodiscard]] bool parseFunctionScalars(const yaml::Node &Root, MachineFunction &Function, + MIRFunctionDocumentInfo &DocumentInfo); + [[nodiscard]] bool recomputeProperties(const yaml::Node &Root, MachineFunction &Function, + const MIRFunctionDocumentInfo &DocumentInfo); + [[nodiscard]] bool parseRegisters(const yaml::Node &Root, MachineFunction &Function, + MIParserContext &Context); + [[nodiscard]] bool parseMetadata(const yaml::Node &Root, MachineFunction &Function, + MIParserContext &Context); + [[nodiscard]] bool parseStackObjects(const yaml::Node &Root, MachineFunction &Function, + MIParserContext &Context); + [[nodiscard]] bool parseConstants(const yaml::Node &Root, MachineFunction &Function, + MIParserContext &Context); + [[nodiscard]] bool parseFrameState(const yaml::Node &Root, MachineFunction &Function, + const MIParserContext &Context); + [[nodiscard]] bool reserveJumpTables(const yaml::Node &Root, MachineFunction &Function, + MIParserContext &Context, + std::vector<PendingJumpTable> &Pending); + [[nodiscard]] bool defineJumpTables(MachineFunction &Function, + std::span<const PendingJumpTable> Pending); + [[nodiscard]] bool parseBody(const yaml::Node &Root, MachineFunction &Function, + MIParserContext &Context); + [[nodiscard]] TextRange scalarDiagnosticRange(const yaml::Node &Scalar, + const TextRange &ScalarRange) const noexcept; + [[nodiscard]] bool parseFunctionRecords(const yaml::Node &Root, MachineFunction &Function); + [[nodiscard]] bool parseTargetState(const yaml::Node &Root, MachineFunction &Function); + [[nodiscard]] bool convertTargetNode(const yaml::Node &Source, MIRTargetNode &Destination); + [[nodiscard]] bool validateDocumentReferences(const MachineFunction &Function); + [[nodiscard]] std::string_view effectiveDataLayout() const noexcept; + [[nodiscard]] Expected<DataLayout> dataLayout() const; + + [[nodiscard]] MachineBasicBlock *blockAt(MachineFunction &Function, + std::uint32_t Number) const noexcept; + [[nodiscard]] MachineInstr *instructionAt(MachineFunction &Function, std::uint32_t Block, + std::uint32_t Offset) const noexcept; + [[nodiscard]] bool parseBlockReference(std::string_view Text, const TextRange &Range, + MachineFunction &Function, MachineBasicBlock *&Block); + [[nodiscard]] bool parseFrameReference(const yaml::Node &Node, const MIParserContext &Context, + FrameIndex &Index); + [[nodiscard]] bool parseSaveRestorePoints(const yaml::Node &Node, MachineFunction &Function, + std::vector<MachineFrameSaveRestorePoint> &Points); + + const TargetDescription &Target; + MIRDocumentParserOptions Options; + MIRDocumentParseResult Result; + const yaml::Stream *Stream = nullptr; + MIRDocument *ActiveDocument = nullptr; + MachineFunction *ActiveFunction = nullptr; +}; + +[[nodiscard]] Expected<void> verifyMIRDocumentReferences(const MIRDocument &Document, + const MachineFunction &Function, + bool UseEmbeddedIR); + +[[nodiscard]] std::optional<std::uint64_t> +parseDIExpressionOpcodeName(std::string_view Name) noexcept; +[[nodiscard]] std::optional<std::uint64_t> +parseDIExpressionAttributeEncodingName(std::string_view Name) noexcept; +[[nodiscard]] std::string invalidDIExpressionElementMessage(std::string_view Name); +[[nodiscard]] std::string formatDIExpression(std::span<const std::uint64_t> Elements); + +} // namespace mir +} // namespace rcc + +#endif // RCC_CODEGEN_MIRYAMLMAPPING_H diff --git a/backend/include/rcc/CodeGen/MachineAnalyses.h b/backend/include/rcc/CodeGen/MachineAnalyses.h new file mode 100644 index 0000000..cd1f97e --- /dev/null +++ b/backend/include/rcc/CodeGen/MachineAnalyses.h @@ -0,0 +1,248 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_MACHINEANALYSES_H +#define RCC_CODEGEN_MACHINEANALYSES_H + +#include "rcc/CodeGen/MachineBasicBlock.h" +#include "rcc/CodeGen/MachineIRTypes.h" +#include "rcc/CodeGen/MachinePassManager.h" +#include "rcc/CodeGen/StableID.h" +#include "rcc/Support/Error.h" + +#include <cstddef> +#include <cstdint> +#include <span> +#include <string_view> +#include <unordered_map> +#include <unordered_set> +#include <vector> + +namespace rcc { + +class MachineBasicBlock; +class MachineFunction; +class MachineInstr; +class MachineMemOperand; +class TargetDescription; + +class MachineDominatorTreeAnalysis final { +public: + class Result final { + public: + [[nodiscard]] bool isReachable(const MachineBasicBlock &Block) const noexcept; + [[nodiscard]] bool dominates(const MachineBasicBlock &Dominator, + const MachineBasicBlock &Block) const noexcept; + [[nodiscard]] bool dominates(const MachineInstr &Dominator, + const MachineInstr &Instruction) const noexcept; + [[nodiscard]] const MachineBasicBlock * + immediateDominator(const MachineBasicBlock &Block) const noexcept; + [[nodiscard]] const MachineBasicBlock * + nearestCommonDominator(const MachineBasicBlock &Left, + const MachineBasicBlock &Right) const noexcept; + [[nodiscard]] std::span<MachineBasicBlock *const> unreachableBlocks() const noexcept { + return UnreachableBlocks; + } + + [[nodiscard]] Expected<void> updateAfterCFGChange(MachineFunction &Function); + + private: + [[nodiscard]] Expected<void> build(MachineFunction &Function); + + std::vector<MachineBasicBlock *> Blocks; + std::unordered_map<const MachineBasicBlock *, std::size_t> BlockToIndex; + std::vector<std::vector<bool>> Dominators; + std::vector<const MachineBasicBlock *> ImmediateDominators; + std::vector<bool> Reachable; + std::vector<MachineBasicBlock *> UnreachableBlocks; + + friend class MachineDominatorTreeAnalysis; + }; + + [[nodiscard]] static constexpr std::string_view name() noexcept { + return "MachineDominatorTree"; + } + + [[nodiscard]] static Expected<Result> run(MachineFunction &Function, + const TargetDescription &Target, + MachineFunctionAnalysisManager &Analyses); +}; + +struct MachineLoop final { + MachineBasicBlock *Header = nullptr; + std::vector<MachineBasicBlock *> Blocks; + std::vector<MachineBasicBlock *> Latches; + std::size_t Parent = static_cast<std::size_t>(-1); + std::vector<std::size_t> Children; + std::uint32_t Depth = 1; +}; + +struct IrreducibleMachineRegion final { + std::vector<MachineBasicBlock *> Blocks; + std::vector<MachineBasicBlock *> Entries; +}; + +class MachineLoopAnalysis final { +public: + class Result final { + public: + [[nodiscard]] std::span<const MachineLoop> loops() const noexcept { + return Loops; + } + [[nodiscard]] std::span<const IrreducibleMachineRegion> irreducibleRegions() const noexcept { + return IrreducibleRegions; + } + [[nodiscard]] const MachineLoop *loopFor(const MachineBasicBlock &Block) const noexcept; + [[nodiscard]] std::uint32_t loopDepth(const MachineBasicBlock &Block) const noexcept; + [[nodiscard]] bool isIrreducible(const MachineBasicBlock &Block) const noexcept; + + private: + std::vector<MachineLoop> Loops; + std::vector<IrreducibleMachineRegion> IrreducibleRegions; + std::unordered_map<const MachineBasicBlock *, std::size_t> InnermostLoop; + std::unordered_set<const MachineBasicBlock *> IrreducibleBlocks; + + friend class MachineLoopAnalysis; + }; + + [[nodiscard]] static constexpr std::string_view name() noexcept { + return "MachineLoopInfo"; + } + + [[nodiscard]] static Expected<Result> run(MachineFunction &Function, + const TargetDescription &Target, + MachineFunctionAnalysisManager &Analyses); +}; + +class MachineBranchProbabilityAnalysis final { +public: + class Result final { + public: + [[nodiscard]] BranchProbability probability(const MachineBasicBlock &From, + const MachineBasicBlock &To) const noexcept; + [[nodiscard]] std::span<const MachineSuccessor> + successors(const MachineBasicBlock &Block) const noexcept; + + private: + std::unordered_map<const MachineBasicBlock *, std::vector<MachineSuccessor>> Probabilities; + + friend class MachineBranchProbabilityAnalysis; + }; + + [[nodiscard]] static constexpr std::string_view name() noexcept { + return "MachineBranchProbability"; + } + + [[nodiscard]] static Expected<Result> run(MachineFunction &Function, + const TargetDescription &Target, + MachineFunctionAnalysisManager &Analyses); +}; + +class MachineBlockFrequencyAnalysis final { +public: + static constexpr std::uint64_t EntryFrequency = std::uint64_t{1} << 32U; + + class Result final { + public: + [[nodiscard]] std::uint64_t frequency(const MachineBasicBlock &Block) const noexcept; + + private: + std::unordered_map<const MachineBasicBlock *, std::uint64_t> Frequencies; + + friend class MachineBlockFrequencyAnalysis; + }; + + [[nodiscard]] static constexpr std::string_view name() noexcept { + return "MachineBlockFrequency"; + } + + [[nodiscard]] static Expected<Result> run(MachineFunction &Function, + const TargetDescription &Target, + MachineFunctionAnalysisManager &Analyses); +}; + +enum class MachineAliasResult : std::uint8_t { + NoAlias, + MayAlias, + PartialAlias, + MustAlias, +}; + +class MachineAliasAnalysis final { +public: + class Result final { + public: + [[nodiscard]] MachineAliasResult alias(const MachineMemOperand &Left, + const MachineMemOperand &Right) const noexcept; + }; + + [[nodiscard]] static constexpr std::string_view name() noexcept { + return "MachineAliasAnalysis"; + } + + [[nodiscard]] static Expected<Result> run(MachineFunction &Function, + const TargetDescription &Target, + MachineFunctionAnalysisManager &Analyses); +}; + +enum class MachineMemoryDependencyKind : std::uint8_t { + Flow, + Anti, + Output, + Ordered, +}; + +struct MachineMemoryDependency final { + MachineInstrStableID Source; + MachineInstrStableID Destination; + MachineMemoryDependencyKind Kind = MachineMemoryDependencyKind::Ordered; +}; + +struct MachineMemoryDependenceRegion final { + const MachineBasicBlock *Block = nullptr; + std::vector<MachineInstrStableID> Instructions; +}; + +class MachineMemoryDependenceAnalysis final { +public: + class Result final { + public: + [[nodiscard]] std::span<const MachineMemoryDependenceRegion> regions() const noexcept { + return Regions; + } + [[nodiscard]] const MachineMemoryDependenceRegion * + regionFor(const MachineBasicBlock &Block) const noexcept; + [[nodiscard]] std::span<const MachineMemoryDependency> + dependencies(const MachineBasicBlock &Block) const noexcept; + [[nodiscard]] std::span<const MachineMemoryDependency> + dependencies(const MachineInstr &Instruction) const noexcept; + [[nodiscard]] std::span<const MachineMemoryDependency> allDependencies() const noexcept { + return Dependencies; + } + + private: + struct Range final { + std::size_t Begin = 0; + std::size_t Size = 0; + }; + + std::vector<MachineMemoryDependency> Dependencies; + std::vector<MachineMemoryDependenceRegion> Regions; + std::unordered_map<const MachineBasicBlock *, std::size_t> BlockToRegion; + std::unordered_map<const MachineBasicBlock *, Range> BlockRanges; + std::unordered_map<MachineInstrStableID, Range> DestinationRanges; + + friend class MachineMemoryDependenceAnalysis; + }; + + [[nodiscard]] static constexpr std::string_view name() noexcept { + return "MachineMemoryDependence"; + } + + [[nodiscard]] static Expected<Result> run(MachineFunction &Function, + const TargetDescription &Target, + MachineFunctionAnalysisManager &Analyses); +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_MACHINEANALYSES_H diff --git a/backend/include/rcc/CodeGen/MachineBasicBlock.h b/backend/include/rcc/CodeGen/MachineBasicBlock.h new file mode 100644 index 0000000..10cc438 --- /dev/null +++ b/backend/include/rcc/CodeGen/MachineBasicBlock.h @@ -0,0 +1,167 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_MACHINEBASICBLOCK_H +#define RCC_CODEGEN_MACHINEBASICBLOCK_H + +#include "rcc/CodeGen/MachineBasicBlockAttributes.h" +#include "rcc/CodeGen/MachineIRTypes.h" +#include "rcc/CodeGen/Register.h" +#include "rcc/CodeGen/StableID.h" +#include "rcc/Support/Error.h" + +#include <cstddef> +#include <cstdint> +#include <optional> +#include <span> +#include <vector> + +namespace rcc { + +class MachineBasicBlock; +class MachineCFGUpdater; +class MachineFunction; +class MachineInstr; + +struct MachineSuccessor final { + MachineBasicBlock *Block = nullptr; + BranchProbability Probability; + + friend bool operator==(const MachineSuccessor &Left, const MachineSuccessor &Right) = default; +}; + +struct MachineBasicBlockLiveIn final { + PhysicalRegister PhysReg; + LaneMask Lanes = LaneMask::all(); + + friend bool operator==(const MachineBasicBlockLiveIn &Left, + const MachineBasicBlockLiveIn &Right) = default; +}; + +class MachineBasicBlock final { +public: + MachineBasicBlock(const MachineBasicBlock &) = delete; + MachineBasicBlock &operator=(const MachineBasicBlock &) = delete; + MachineBasicBlock(MachineBasicBlock &&) = delete; + MachineBasicBlock &operator=(MachineBasicBlock &&) = delete; + ~MachineBasicBlock() = default; + + [[nodiscard]] MachineBlockStableID stableID() const noexcept { + return StableID; + } + + [[nodiscard]] MachineFunction &function() noexcept { + return *OwnerFunction; + } + + [[nodiscard]] const MachineFunction &function() const noexcept { + return *OwnerFunction; + } + + [[nodiscard]] bool isLinked() const noexcept { + return IsLinked; + } + + [[nodiscard]] MachineBasicBlock *previous() noexcept { + return PreviousBlock; + } + + [[nodiscard]] const MachineBasicBlock *previous() const noexcept { + return PreviousBlock; + } + + [[nodiscard]] MachineBasicBlock *next() noexcept { + return NextBlock; + } + + [[nodiscard]] const MachineBasicBlock *next() const noexcept { + return NextBlock; + } + + [[nodiscard]] MachineInstr *firstInstruction() noexcept { + return FirstInstruction; + } + + [[nodiscard]] const MachineInstr *firstInstruction() const noexcept { + return FirstInstruction; + } + + [[nodiscard]] MachineInstr *lastInstruction() noexcept { + return LastInstruction; + } + + [[nodiscard]] const MachineInstr *lastInstruction() const noexcept { + return LastInstruction; + } + + [[nodiscard]] std::size_t instructionCount() const noexcept; + [[nodiscard]] Expected<void> appendInstruction(MachineInstr &Instruction); + [[nodiscard]] Expected<void> insertInstructionBefore(MachineInstr &Position, + MachineInstr &Instruction); + [[nodiscard]] Expected<void> insertInstructionAfter(MachineInstr &Position, + MachineInstr &Instruction); + // Reorder linked instructions without reporting removal or insertion to the delegate. + [[nodiscard]] Expected<void> spliceInstructionBefore(MachineInstr &Position, + MachineInstr &Instruction); + [[nodiscard]] Expected<void> removeInstruction(MachineInstr &Instruction); + void clearInstructions() noexcept; + + [[nodiscard]] std::span<MachineBasicBlock *const> predecessors() const noexcept { + return Predecessors; + } + + [[nodiscard]] std::span<const MachineSuccessor> successors() const noexcept { + return Successors; + } + + [[nodiscard]] Expected<void> addLiveIn(PhysicalRegister PhysReg, + LaneMask Lanes = LaneMask::all()); + [[nodiscard]] Expected<void> removeLiveIn(PhysicalRegister PhysReg); + void clearLiveIns() noexcept { + LiveIns.clear(); + } + [[nodiscard]] std::span<const MachineBasicBlockLiveIn> liveIns() const noexcept { + return LiveIns; + } + + [[nodiscard]] std::optional<std::uint32_t> denseNumber() const noexcept { + return DenseNumber; + } + + [[nodiscard]] std::optional<std::uint32_t> layoutIndex() const noexcept { + return LayoutIndex; + } + + [[nodiscard]] MachineBasicBlockAttributes &attributes() noexcept { + return Attributes; + } + + [[nodiscard]] const MachineBasicBlockAttributes &attributes() const noexcept { + return Attributes; + } + +private: + MachineBasicBlock(MachineFunction &OwnerFunction, MachineBlockStableID StableID) + : OwnerFunction(&OwnerFunction), StableID(StableID) {} + + MachineFunction *OwnerFunction; + MachineBlockStableID StableID; + bool IsLinked = false; + MachineBasicBlock *PreviousBlock = nullptr; + MachineBasicBlock *NextBlock = nullptr; + MachineInstr *FirstInstruction = nullptr; + MachineInstr *LastInstruction = nullptr; + std::vector<MachineBasicBlock *> Predecessors; + std::vector<MachineSuccessor> Successors; + std::vector<MachineBasicBlockLiveIn> LiveIns; + MachineBasicBlockAttributes Attributes; + std::optional<std::uint32_t> DenseNumber; + std::optional<std::uint32_t> LayoutIndex; + + friend class MachineArena; + friend class MachineCFGUpdater; + friend class MachineFunction; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_MACHINEBASICBLOCK_H diff --git a/backend/include/rcc/CodeGen/MachineBasicBlockAttributes.h b/backend/include/rcc/CodeGen/MachineBasicBlockAttributes.h new file mode 100644 index 0000000..7b132d2 --- /dev/null +++ b/backend/include/rcc/CodeGen/MachineBasicBlockAttributes.h @@ -0,0 +1,208 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_MACHINEBASICBLOCKATTRIBUTES_H +#define RCC_CODEGEN_MACHINEBASICBLOCKATTRIBUTES_H + +#include "rcc/CodeGen/MachineIRTypes.h" +#include "rcc/Support/Error.h" + +#include <cstdint> +#include <optional> +#include <string> +#include <string_view> + +namespace rcc { + +enum class MachineBasicBlockAttribute : std::uint8_t { + MachineBlockAddressTaken, + IRBlockAddressTaken, + LandingPad, + InlineAsmBrIndirectTarget, + EHFuncletEntry, + EHScopeEntry, + Count, +}; + +[[nodiscard]] std::string_view +getMachineBasicBlockAttributeName(MachineBasicBlockAttribute Attribute) noexcept; + +enum class MachineBasicBlockSectionKind : std::uint8_t { + Numbered, + Exception, + Cold, +}; + +class MachineBasicBlockSectionID final { +public: + constexpr MachineBasicBlockSectionID() noexcept = default; + + [[nodiscard]] static constexpr MachineBasicBlockSectionID + numbered(std::uint32_t Number) noexcept { + return MachineBasicBlockSectionID(MachineBasicBlockSectionKind::Numbered, Number); + } + + [[nodiscard]] static constexpr MachineBasicBlockSectionID exception() noexcept { + return MachineBasicBlockSectionID(MachineBasicBlockSectionKind::Exception, 0); + } + + [[nodiscard]] static constexpr MachineBasicBlockSectionID cold() noexcept { + return MachineBasicBlockSectionID(MachineBasicBlockSectionKind::Cold, 0); + } + + [[nodiscard]] constexpr MachineBasicBlockSectionKind kind() const noexcept { + return Kind; + } + + [[nodiscard]] constexpr std::uint32_t number() const noexcept { + return Number; + } + + [[nodiscard]] constexpr bool isDefault() const noexcept { + return Kind == MachineBasicBlockSectionKind::Numbered && Number == 0; + } + + friend constexpr bool operator==(MachineBasicBlockSectionID Left, + MachineBasicBlockSectionID Right) noexcept = default; + +private: + constexpr MachineBasicBlockSectionID(MachineBasicBlockSectionKind Kind, + std::uint32_t Number) noexcept + : Kind(Kind), Number(Number) {} + + MachineBasicBlockSectionKind Kind = MachineBasicBlockSectionKind::Numbered; + std::uint32_t Number = 0; +}; + +struct MachineBasicBlockProfileID final { + std::uint32_t BaseID = 0; + std::uint32_t CloneID = 0; + + friend constexpr bool operator==(MachineBasicBlockProfileID Left, + MachineBasicBlockProfileID Right) noexcept = default; +}; + +class MachineBasicBlockAttributes final { +public: + MachineBasicBlockAttributes() = default; + + [[nodiscard]] static Expected<MachineBasicBlockAttributes> fromRawFlags(std::uint8_t RawFlags); + + [[nodiscard]] bool has(MachineBasicBlockAttribute Attribute) const noexcept { + if (!isValidAttribute(Attribute)) + return false; + return (Flags & flagFor(Attribute)) != 0; + } + + void set(MachineBasicBlockAttribute Attribute) noexcept { + if (isValidAttribute(Attribute)) + Flags |= flagFor(Attribute); + } + + void clear(MachineBasicBlockAttribute Attribute) noexcept { + if (isValidAttribute(Attribute)) + Flags &= static_cast<std::uint8_t>(~flagFor(Attribute)); + } + + [[nodiscard]] std::uint8_t rawFlags() const noexcept { + return Flags; + } + + [[nodiscard]] std::optional<std::string_view> name() const noexcept { + if (!Name) + return std::nullopt; + return *Name; + } + + [[nodiscard]] Expected<void> setName(std::string NewName); + + void clearName() noexcept { + Name.reset(); + } + + [[nodiscard]] std::optional<std::string_view> irBlockName() const noexcept { + if (!IRBlockName) + return std::nullopt; + return *IRBlockName; + } + + [[nodiscard]] Expected<void> setIRBlockName(std::string NewName); + + void clearIRBlockName() noexcept { + IRBlockName.reset(); + } + + [[nodiscard]] Alignment alignment() const noexcept { + return BlockAlignment; + } + + void setAlignment(Alignment NewAlignment) noexcept { + BlockAlignment = NewAlignment; + } + + void clearAlignment() noexcept { + BlockAlignment = Alignment{}; + } + + [[nodiscard]] MachineBasicBlockSectionID sectionID() const noexcept { + return SectionID; + } + + void setSectionID(MachineBasicBlockSectionID NewSectionID) noexcept { + SectionID = NewSectionID; + } + + [[nodiscard]] const std::optional<MachineBasicBlockProfileID> &profileID() const noexcept { + return ProfileID; + } + + void setProfileID(MachineBasicBlockProfileID NewProfileID) noexcept { + ProfileID = NewProfileID; + } + + void clearProfileID() noexcept { + ProfileID.reset(); + } + + [[nodiscard]] std::uint32_t callFrameSize() const noexcept { + return CallFrameSize; + } + + void setCallFrameSize(std::uint32_t Size) noexcept { + CallFrameSize = Size; + } + + [[nodiscard]] Expected<void> verify() const; + + friend bool operator==(const MachineBasicBlockAttributes &Left, + const MachineBasicBlockAttributes &Right) = default; + +private: + static constexpr std::uint8_t AttributeCount = + static_cast<std::uint8_t>(MachineBasicBlockAttribute::Count); + static_assert(AttributeCount < 8); + + static constexpr std::uint8_t ValidFlags = + static_cast<std::uint8_t>((std::uint8_t{1} << AttributeCount) - 1); + + [[nodiscard]] static constexpr bool + isValidAttribute(MachineBasicBlockAttribute Attribute) noexcept { + return static_cast<std::uint8_t>(Attribute) < AttributeCount; + } + + [[nodiscard]] static constexpr std::uint8_t + flagFor(MachineBasicBlockAttribute Attribute) noexcept { + return static_cast<std::uint8_t>(std::uint8_t{1} << static_cast<std::uint8_t>(Attribute)); + } + + std::uint8_t Flags = 0; + std::optional<std::string> Name; + std::optional<std::string> IRBlockName; + Alignment BlockAlignment; + MachineBasicBlockSectionID SectionID; + std::optional<MachineBasicBlockProfileID> ProfileID; + std::uint32_t CallFrameSize = 0; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_MACHINEBASICBLOCKATTRIBUTES_H diff --git a/backend/include/rcc/CodeGen/MachineBlockPlacement.h b/backend/include/rcc/CodeGen/MachineBlockPlacement.h new file mode 100644 index 0000000..557855a --- /dev/null +++ b/backend/include/rcc/CodeGen/MachineBlockPlacement.h @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_MACHINEBLOCKPLACEMENT_H +#define RCC_CODEGEN_MACHINEBLOCKPLACEMENT_H + +#include "rcc/CodeGen/MachinePassManager.h" + +namespace rcc { + +class MachineBlockPlacementPass final : public MachineFunctionPass { +public: + [[nodiscard]] const MachinePassContract &contract() const noexcept override; + [[nodiscard]] Expected<MachinePassResult> run(MachineFunction &Function, + const TargetDescription &Target, + MachineFunctionAnalysisManager &Analyses) override; + +private: + [[nodiscard]] Expected<MachinePassResult> + runImplementation(MachineFunction &Function, const TargetDescription &Target, + MachineFunctionAnalysisManager &Analyses) const; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_MACHINEBLOCKPLACEMENT_H diff --git a/backend/include/rcc/CodeGen/MachineBranchOptimization.h b/backend/include/rcc/CodeGen/MachineBranchOptimization.h new file mode 100644 index 0000000..9c2a739 --- /dev/null +++ b/backend/include/rcc/CodeGen/MachineBranchOptimization.h @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_MACHINEBRANCHOPTIMIZATION_H +#define RCC_CODEGEN_MACHINEBRANCHOPTIMIZATION_H + +#include "rcc/CodeGen/MachinePassManager.h" + +#include <cstddef> + +namespace rcc { + +class TargetInstrInfo; + +struct MachineBranchOptimizationOptions final { + std::size_t MaximumThreadedEdges = 10'000; + std::size_t MaximumMergedBlocks = 4'096; +}; + +class MachineBranchOptimizationPass final : public MachineFunctionPass { +public: + explicit MachineBranchOptimizationPass(const TargetInstrInfo &Info, + MachineBranchOptimizationOptions Options = {}) noexcept + : Info(&Info), Options(Options) {} + + [[nodiscard]] const MachinePassContract &contract() const noexcept override; + [[nodiscard]] Expected<MachinePassResult> run(MachineFunction &Function, + const TargetDescription &Target, + MachineFunctionAnalysisManager &Analyses) override; + +private: + [[nodiscard]] Expected<MachinePassResult> + runImplementation(MachineFunction &Function, const TargetDescription &Target) const; + + const TargetInstrInfo *Info; + MachineBranchOptimizationOptions Options; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_MACHINEBRANCHOPTIMIZATION_H diff --git a/backend/include/rcc/CodeGen/MachineBranchRelaxation.h b/backend/include/rcc/CodeGen/MachineBranchRelaxation.h new file mode 100644 index 0000000..64687eb --- /dev/null +++ b/backend/include/rcc/CodeGen/MachineBranchRelaxation.h @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_MACHINEBRANCHRELAXATION_H +#define RCC_CODEGEN_MACHINEBRANCHRELAXATION_H + +#include "rcc/CodeGen/MachinePassManager.h" + +#include <cstddef> + +namespace rcc { + +class TargetInstrInfo; + +struct MachineBranchRelaxationOptions final { + std::size_t ConvergenceWatchdog = 1000000; +}; + +class MachineBranchRelaxationPass final : public MachineFunctionPass { +public: + explicit MachineBranchRelaxationPass(const TargetInstrInfo &Info, + MachineBranchRelaxationOptions Options = {}) noexcept + : Info(&Info), Options(Options) {} + + [[nodiscard]] const MachinePassContract &contract() const noexcept override; + [[nodiscard]] Expected<MachinePassResult> run(MachineFunction &Function, + const TargetDescription &Target, + MachineFunctionAnalysisManager &Analyses) override; + +private: + [[nodiscard]] Expected<MachinePassResult> + runImplementation(MachineFunction &Function, const TargetDescription &Target, + MachineFunctionAnalysisManager &Analyses) const; + + const TargetInstrInfo *Info; + MachineBranchRelaxationOptions Options; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_MACHINEBRANCHRELAXATION_H diff --git a/backend/include/rcc/CodeGen/MachineCFGUpdater.h b/backend/include/rcc/CodeGen/MachineCFGUpdater.h new file mode 100644 index 0000000..70fb9aa --- /dev/null +++ b/backend/include/rcc/CodeGen/MachineCFGUpdater.h @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_MACHINECFGUPDATER_H +#define RCC_CODEGEN_MACHINECFGUPDATER_H + +#include "rcc/CodeGen/MachineIRTypes.h" +#include "rcc/Support/Error.h" + +namespace rcc { + +class MachineBasicBlock; +class MachineFunction; +class MachineInstr; + +class MachineCFGUpdater final { +public: + explicit MachineCFGUpdater(MachineFunction &Function) noexcept : Function(&Function) {} + + [[nodiscard]] Expected<void> + addSuccessor(MachineBasicBlock &From, MachineBasicBlock &To, + BranchProbability Probability = BranchProbability::zero()); + [[nodiscard]] Expected<void> removeSuccessor(MachineBasicBlock &From, MachineBasicBlock &To); + [[nodiscard]] Expected<void> replaceSuccessor(MachineBasicBlock &From, + MachineBasicBlock &OldTarget, + MachineBasicBlock &NewTarget); + [[nodiscard]] Expected<void> splitEdge(MachineBasicBlock &From, MachineBasicBlock &To, + MachineBasicBlock &EdgeBlock); + [[nodiscard]] Expected<void> updateBranchProbability(MachineBasicBlock &From, + MachineBasicBlock &To, + BranchProbability Probability); + [[nodiscard]] Expected<void> transferSuccessors(MachineBasicBlock &From, MachineBasicBlock &To); + [[nodiscard]] Expected<void> disconnectBlock(MachineBasicBlock &Block); + [[nodiscard]] Expected<MachineBasicBlock *> splitBlock(MachineBasicBlock &Block, + MachineInstr &SplitBefore); + [[nodiscard]] Expected<void> deleteBlock(MachineBasicBlock &Block); + +private: + [[nodiscard]] Expected<void> verifyOwnership(const MachineBasicBlock &Block) const; + + MachineFunction *Function; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_MACHINECFGUPDATER_H diff --git a/backend/include/rcc/CodeGen/MachineCFIInfo.h b/backend/include/rcc/CodeGen/MachineCFIInfo.h new file mode 100644 index 0000000..1819daf --- /dev/null +++ b/backend/include/rcc/CodeGen/MachineCFIInfo.h @@ -0,0 +1,269 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_MACHINECFIINFO_H +#define RCC_CODEGEN_MACHINECFIINFO_H + +#include "rcc/CodeGen/MachineIRTypes.h" +#include "rcc/Support/Error.h" + +#include <cstdint> +#include <span> +#include <string> +#include <utility> +#include <variant> +#include <vector> + +namespace rcc { + +class DwarfRegisterNumber final { +public: + constexpr DwarfRegisterNumber() noexcept = default; + + [[nodiscard]] static constexpr DwarfRegisterNumber fromValue(std::uint32_t Value) noexcept { + return DwarfRegisterNumber(Value); + } + + [[nodiscard]] constexpr std::uint32_t value() const noexcept { + return Value; + } + + friend constexpr bool operator==(DwarfRegisterNumber Left, + DwarfRegisterNumber Right) noexcept = default; + +private: + explicit constexpr DwarfRegisterNumber(std::uint32_t Value) noexcept : Value(Value) {} + + std::uint32_t Value = 0; +}; + +enum class MachineCFIOperation : std::uint8_t { + SameValue, + RememberState, + RestoreState, + Offset, + LLVMDefAddressSpaceCFA, + DefCFARegister, + DefCFAOffset, + DefCFA, + RelativeOffset, + AdjustCFAOffset, + Escape, + Restore, + Undefined, + Register, + WindowSave, + NegateReturnAddressState, + NegateReturnAddressStateWithPC, + GNUArgsSize, + Label, + ValueOffset, + LLVMRegisterPair, + LLVMVectorRegisters, + LLVMVectorOffset, + LLVMVectorRegisterMask, + Count, +}; + +struct MachineCFINoOperands final { + friend constexpr bool operator==(MachineCFINoOperands Left, + MachineCFINoOperands Right) noexcept = default; +}; + +struct MachineCFIRegisterRule final { + DwarfRegisterNumber Register; + + friend constexpr bool operator==(MachineCFIRegisterRule Left, + MachineCFIRegisterRule Right) noexcept = default; +}; + +struct MachineCFIOffsetRule final { + std::int64_t Offset = 0; + + friend constexpr bool operator==(MachineCFIOffsetRule Left, + MachineCFIOffsetRule Right) noexcept = default; +}; + +struct MachineCFIRegisterOffsetRule final { + DwarfRegisterNumber Register; + std::int64_t Offset = 0; + + friend constexpr bool operator==(MachineCFIRegisterOffsetRule Left, + MachineCFIRegisterOffsetRule Right) noexcept = default; +}; + +struct MachineCFIRegisterRegisterRule final { + DwarfRegisterNumber Register; + DwarfRegisterNumber SourceRegister; + + friend constexpr bool operator==(MachineCFIRegisterRegisterRule Left, + MachineCFIRegisterRegisterRule Right) noexcept = default; +}; + +struct MachineCFIAddressSpaceCFARule final { + DwarfRegisterNumber Register; + std::int64_t Offset = 0; + std::uint32_t AddressSpace = 0; + + friend constexpr bool operator==(MachineCFIAddressSpaceCFARule Left, + MachineCFIAddressSpaceCFARule Right) noexcept = default; +}; + +struct MachineCFIEscapeRule final { + std::vector<std::uint8_t> Bytes; + + friend bool operator==(const MachineCFIEscapeRule &Left, + const MachineCFIEscapeRule &Right) = default; +}; + +struct MachineCFILabelRule final { + std::string Symbol; + + friend bool operator==(const MachineCFILabelRule &Left, + const MachineCFILabelRule &Right) = default; +}; + +struct MachineCFIRegisterPairRule final { + DwarfRegisterNumber Register; + DwarfRegisterNumber FirstRegister; + std::uint32_t FirstSizeInBits = 0; + DwarfRegisterNumber SecondRegister; + std::uint32_t SecondSizeInBits = 0; + + friend constexpr bool operator==(MachineCFIRegisterPairRule Left, + MachineCFIRegisterPairRule Right) noexcept = default; +}; + +struct MachineCFIVectorRegisterLane final { + DwarfRegisterNumber Register; + std::uint32_t Lane = 0; + std::uint32_t SizeInBits = 0; + + friend constexpr bool operator==(MachineCFIVectorRegisterLane Left, + MachineCFIVectorRegisterLane Right) noexcept = default; +}; + +struct MachineCFIVectorRegistersRule final { + DwarfRegisterNumber Register; + std::vector<MachineCFIVectorRegisterLane> VectorRegisters; + + friend bool operator==(const MachineCFIVectorRegistersRule &Left, + const MachineCFIVectorRegistersRule &Right) = default; +}; + +struct MachineCFIVectorOffsetRule final { + DwarfRegisterNumber Register; + std::uint32_t RegisterSizeInBits = 0; + DwarfRegisterNumber MaskRegister; + std::uint32_t MaskRegisterSizeInBits = 0; + std::int64_t Offset = 0; + + friend constexpr bool operator==(MachineCFIVectorOffsetRule Left, + MachineCFIVectorOffsetRule Right) noexcept = default; +}; + +struct MachineCFIVectorRegisterMaskRule final { + DwarfRegisterNumber Register; + DwarfRegisterNumber SpillRegister; + std::uint32_t SpillRegisterLaneSizeInBits = 0; + DwarfRegisterNumber MaskRegister; + std::uint32_t MaskRegisterSizeInBits = 0; + + friend constexpr bool operator==(MachineCFIVectorRegisterMaskRule Left, + MachineCFIVectorRegisterMaskRule Right) noexcept = default; +}; + +class MachineCFIInstruction final { +public: + using Payload = + std::variant<MachineCFINoOperands, MachineCFIRegisterRule, MachineCFIOffsetRule, + MachineCFIRegisterOffsetRule, MachineCFIRegisterRegisterRule, + MachineCFIAddressSpaceCFARule, MachineCFIEscapeRule, MachineCFILabelRule, + MachineCFIRegisterPairRule, MachineCFIVectorRegistersRule, + MachineCFIVectorOffsetRule, MachineCFIVectorRegisterMaskRule>; + + [[nodiscard]] static MachineCFIInstruction sameValue(DwarfRegisterNumber Register); + [[nodiscard]] static MachineCFIInstruction rememberState(); + [[nodiscard]] static MachineCFIInstruction restoreState(); + [[nodiscard]] static MachineCFIInstruction offset(DwarfRegisterNumber Register, + std::int64_t Offset); + [[nodiscard]] static MachineCFIInstruction llvmDefAddressSpaceCFA(DwarfRegisterNumber Register, + std::int64_t Offset, + std::uint32_t AddressSpace); + [[nodiscard]] static MachineCFIInstruction defCFARegister(DwarfRegisterNumber Register); + [[nodiscard]] static MachineCFIInstruction defCFAOffset(std::int64_t Offset); + [[nodiscard]] static MachineCFIInstruction defCFA(DwarfRegisterNumber Register, + std::int64_t Offset); + [[nodiscard]] static MachineCFIInstruction relativeOffset(DwarfRegisterNumber Register, + std::int64_t Offset); + [[nodiscard]] static MachineCFIInstruction adjustCFAOffset(std::int64_t Offset); + [[nodiscard]] static Expected<MachineCFIInstruction> escape(std::vector<std::uint8_t> Bytes); + [[nodiscard]] static MachineCFIInstruction restore(DwarfRegisterNumber Register); + [[nodiscard]] static MachineCFIInstruction undefined(DwarfRegisterNumber Register); + [[nodiscard]] static MachineCFIInstruction registerRule(DwarfRegisterNumber Register, + DwarfRegisterNumber SourceRegister); + [[nodiscard]] static MachineCFIInstruction windowSave(); + [[nodiscard]] static MachineCFIInstruction negateReturnAddressState(); + [[nodiscard]] static MachineCFIInstruction negateReturnAddressStateWithPC(); + [[nodiscard]] static MachineCFIInstruction gnuArgsSize(std::int64_t Size); + [[nodiscard]] static Expected<MachineCFIInstruction> label(std::string Symbol); + [[nodiscard]] static MachineCFIInstruction valueOffset(DwarfRegisterNumber Register, + std::int64_t Offset); + [[nodiscard]] static MachineCFIInstruction llvmRegisterPair(MachineCFIRegisterPairRule Rule); + [[nodiscard]] static Expected<MachineCFIInstruction> + llvmVectorRegisters(MachineCFIVectorRegistersRule Rule); + [[nodiscard]] static MachineCFIInstruction llvmVectorOffset(MachineCFIVectorOffsetRule Rule); + [[nodiscard]] static MachineCFIInstruction + llvmVectorRegisterMask(MachineCFIVectorRegisterMaskRule Rule); + + [[nodiscard]] MachineCFIOperation operation() const noexcept { + return Operation; + } + + template <typename PayloadT> [[nodiscard]] const PayloadT *getIf() const noexcept { + return std::get_if<PayloadT>(&Data); + } + + [[nodiscard]] Expected<void> verify() const; + + friend bool operator==(const MachineCFIInstruction &Left, + const MachineCFIInstruction &Right) = default; + +private: + MachineCFIInstruction(MachineCFIOperation Operation, Payload Data) + : Operation(Operation), Data(std::move(Data)) {} + + MachineCFIOperation Operation; + Payload Data; +}; + +class MachineCFIInfo final { +public: + MachineCFIInfo() = default; + MachineCFIInfo(const MachineCFIInfo &) = delete; + MachineCFIInfo &operator=(const MachineCFIInfo &) = delete; + MachineCFIInfo(MachineCFIInfo &&) noexcept = default; + MachineCFIInfo &operator=(MachineCFIInfo &&) noexcept = default; + ~MachineCFIInfo() = default; + + [[nodiscard]] Expected<CFIInstructionIndex> addInstruction(MachineCFIInstruction Instruction); + + [[nodiscard]] const MachineCFIInstruction *instruction(CFIInstructionIndex Index) const noexcept; + + [[nodiscard]] std::span<const MachineCFIInstruction> instructions() const noexcept { + return Instructions; + } + + [[nodiscard]] Expected<void> verify() const; + [[nodiscard]] Expected<MachineCFIInfo> clone() const; + + friend bool operator==(const MachineCFIInfo &Left, const MachineCFIInfo &Right) { + return Left.Instructions == Right.Instructions; + } + +private: + std::vector<MachineCFIInstruction> Instructions; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_MACHINECFIINFO_H diff --git a/backend/include/rcc/CodeGen/MachineCSE.h b/backend/include/rcc/CodeGen/MachineCSE.h new file mode 100644 index 0000000..dc10b62 --- /dev/null +++ b/backend/include/rcc/CodeGen/MachineCSE.h @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_MACHINECSE_H +#define RCC_CODEGEN_MACHINECSE_H + +#include "rcc/CodeGen/MachinePassManager.h" + +#include <cstddef> + +namespace rcc { + +struct MachineCSEOptions final { + std::size_t MaximumComparisons = 100'000; +}; + +class MachineCSEPass final : public MachineFunctionPass { +public: + explicit MachineCSEPass(MachineCSEOptions Options = {}) : Options(Options) {} + + [[nodiscard]] const MachinePassContract &contract() const noexcept override; + [[nodiscard]] Expected<MachinePassResult> run(MachineFunction &Function, + const TargetDescription &Target, + MachineFunctionAnalysisManager &Analyses) override; + +private: + [[nodiscard]] Expected<MachinePassResult> + runImplementation(MachineFunction &Function, const TargetDescription &Target, + MachineFunctionAnalysisManager &Analyses); + + MachineCSEOptions Options; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_MACHINECSE_H diff --git a/backend/include/rcc/CodeGen/MachineCombiner.h b/backend/include/rcc/CodeGen/MachineCombiner.h new file mode 100644 index 0000000..eba7ab5 --- /dev/null +++ b/backend/include/rcc/CodeGen/MachineCombiner.h @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_MACHINECOMBINER_H +#define RCC_CODEGEN_MACHINECOMBINER_H + +#include "rcc/CodeGen/MachineCombinerPattern.h" +#include "rcc/CodeGen/MachinePassManager.h" + +#include <cstddef> + +namespace rcc { + +struct MachineCombinerOptions final { + std::size_t MaximumRewrites = 4096; + std::size_t MaximumIterations = 8; +}; + +class MachineCombinerPass final : public MachineFunctionPass { +public: + MachineCombinerPass(const TargetMachineCombinerInfo &Info, + MachineCombinerOptions Options = {}) noexcept + : Info(&Info), Options(Options) {} + + [[nodiscard]] const MachinePassContract &contract() const noexcept override; + [[nodiscard]] Expected<MachinePassResult> run(MachineFunction &Function, + const TargetDescription &Target, + MachineFunctionAnalysisManager &Analyses) override; + +private: + [[nodiscard]] Expected<MachinePassResult> + runImplementation(MachineFunction &Function, const TargetDescription &Target, + MachineFunctionAnalysisManager &Analyses) const; + + const TargetMachineCombinerInfo *Info; + MachineCombinerOptions Options; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_MACHINECOMBINER_H diff --git a/backend/include/rcc/CodeGen/MachineCombinerPattern.h b/backend/include/rcc/CodeGen/MachineCombinerPattern.h new file mode 100644 index 0000000..3d590f9 --- /dev/null +++ b/backend/include/rcc/CodeGen/MachineCombinerPattern.h @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_MACHINECOMBINERPATTERN_H +#define RCC_CODEGEN_MACHINECOMBINERPATTERN_H + +#include "rcc/CodeGen/MachineIRTypes.h" +#include "rcc/Support/Error.h" + +#include <cstddef> +#include <span> +#include <string> +#include <unordered_map> +#include <vector> + +namespace rcc { + +struct MachineCombinerPattern final { + std::string Name; + Opcode RootOpcode; + Opcode ProducerOpcode; + Opcode ReplacementOpcode; + std::size_t ProducerOperand = 0; + std::size_t AddendOperand = 0; +}; + +class TargetMachineCombinerInfo final { +public: + [[nodiscard]] static Expected<TargetMachineCombinerInfo> + create(std::vector<MachineCombinerPattern> Patterns); + + [[nodiscard]] std::span<const MachineCombinerPattern> patterns(Opcode RootOpcode) const noexcept; + +private: + explicit TargetMachineCombinerInfo(std::vector<MachineCombinerPattern> Patterns) + : Patterns(std::move(Patterns)) {} + + std::vector<MachineCombinerPattern> Patterns; + std::unordered_map<Opcode, std::vector<MachineCombinerPattern>> MatchTable; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_MACHINECOMBINERPATTERN_H diff --git a/backend/include/rcc/CodeGen/MachineCompilationStatus.h b/backend/include/rcc/CodeGen/MachineCompilationStatus.h new file mode 100644 index 0000000..9746a4d --- /dev/null +++ b/backend/include/rcc/CodeGen/MachineCompilationStatus.h @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_MACHINECOMPILATIONSTATUS_H +#define RCC_CODEGEN_MACHINECOMPILATIONSTATUS_H + +#include "rcc/CodeGen/InstructionDescriptor.h" +#include "rcc/CodeGen/MachineIRTypes.h" +#include "rcc/CodeGen/StableID.h" +#include "rcc/Support/Error.h" + +#include <cstdint> +#include <optional> +#include <string> +#include <string_view> + +namespace rcc { + +class MachineFunctionCloneContext; + +enum class MachineCompilationState : std::uint8_t { + Success, + Unsupported, + MIRParseFailed, + LegalizationFailed, + SelectionFailed, + RegisterAllocationFailed, + CallLoweringFailed, + FrameLayoutFailed, + FinalizationFailed, + AssemblyLoweringFailed, + InternalError, + SchedulingFailed, + Count, +}; + +[[nodiscard]] bool isKnownMachineCompilationState(MachineCompilationState State) noexcept; +[[nodiscard]] std::string_view +getMachineCompilationStateName(MachineCompilationState State) noexcept; + +enum class MachineCompilationDiagnosticSeverity : std::uint8_t { + Error, + Fatal, + Count, +}; + +struct MachineCompilationDiagnostic final { + MachineCompilationDiagnosticSeverity Severity = MachineCompilationDiagnosticSeverity::Error; + ErrorCode Code = ErrorCode::InternalError; + std::string Message; + std::optional<SourceLocation> Location; + + friend bool operator==(const MachineCompilationDiagnostic &Left, + const MachineCompilationDiagnostic &Right) = default; +}; + +struct MachineCompilationFailure final { + MachineCompilationDiagnostic Diagnostic; + std::optional<std::string> FailurePass; + std::optional<MachineInstrStableID> FailureInstruction; + std::optional<PipelineStage> FailureStage; + std::optional<std::string> FailureRule; + std::optional<std::string> Target; + std::optional<std::string> Subtarget; + + friend bool operator==(const MachineCompilationFailure &Left, + const MachineCompilationFailure &Right) = default; +}; + +class MachineCompilationStatus final { +public: + MachineCompilationStatus() = default; + MachineCompilationStatus(const MachineCompilationStatus &) = delete; + MachineCompilationStatus &operator=(const MachineCompilationStatus &) = delete; + MachineCompilationStatus(MachineCompilationStatus &&) noexcept = default; + MachineCompilationStatus &operator=(MachineCompilationStatus &&) noexcept = default; + ~MachineCompilationStatus() = default; + + [[nodiscard]] MachineCompilationState state() const noexcept { + return State; + } + + [[nodiscard]] bool succeeded() const noexcept { + return State == MachineCompilationState::Success; + } + + [[nodiscard]] const MachineCompilationFailure *failure() const noexcept { + if (!Failure) + return nullptr; + return &*Failure; + } + + [[nodiscard]] Expected<void> fail(MachineCompilationState NewState, + MachineCompilationFailure NewFailure); + [[nodiscard]] Expected<void> verify() const; + [[nodiscard]] Expected<MachineCompilationStatus> + clone(const MachineFunctionCloneContext &CloneContext) const; + + friend bool operator==(const MachineCompilationStatus &Left, + const MachineCompilationStatus &Right) = default; + +private: + MachineCompilationState State = MachineCompilationState::Success; + std::optional<MachineCompilationFailure> Failure; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_MACHINECOMPILATIONSTATUS_H diff --git a/backend/include/rcc/CodeGen/MachineConstantPool.h b/backend/include/rcc/CodeGen/MachineConstantPool.h new file mode 100644 index 0000000..104e559 --- /dev/null +++ b/backend/include/rcc/CodeGen/MachineConstantPool.h @@ -0,0 +1,176 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_MACHINECONSTANTPOOL_H +#define RCC_CODEGEN_MACHINECONSTANTPOOL_H + +#include "rcc/CodeGen/MachineIRTypes.h" +#include "rcc/CodeGen/MachineMemOperand.h" +#include "rcc/CodeGen/TargetConstant.h" +#include "rcc/IR/Constants.h" +#include "rcc/Support/Error.h" + +#include <cstddef> +#include <cstdint> +#include <optional> +#include <span> +#include <string> +#include <utility> +#include <variant> +#include <vector> + +namespace rcc { + +enum class ConstantPoolEntryKind : std::uint8_t { + Generic, + Target, + SymbolDifference, + BlockAddress, + TargetExpression, +}; + +enum class ConstantPoolMergeKind : std::uint8_t { + Never, + Exact, +}; + +enum class ConstantPoolRelocationMode : std::uint8_t { + None, + Static, + PositionIndependent, + Target, +}; + +enum class ConstantPoolSectionKind : std::uint8_t { + TargetDefault, + ReadOnly, + MergeableReadOnly, + Data, + TextAdjacent, + Target, + Count, +}; + +struct ConstantPoolPlacement final { + ConstantPoolSectionKind Section = ConstantPoolSectionKind::TargetDefault; + bool UniqueSection = false; + std::optional<std::uint64_t> MaximumDistance; + std::uint64_t TargetFlags = 0; + + friend bool operator==(const ConstantPoolPlacement &Left, + const ConstantPoolPlacement &Right) = default; +}; + +struct SymbolDifferenceConstant final { + std::string Minuend; + std::string Subtrahend; + std::int64_t Addend = 0; + + friend bool operator==(const SymbolDifferenceConstant &Left, + const SymbolDifferenceConstant &Right) = default; +}; + +struct BlockAddressConstant final { + std::string FunctionName; + std::string BlockName; + std::int64_t Addend = 0; + + friend bool operator==(const BlockAddressConstant &Left, + const BlockAddressConstant &Right) = default; +}; + +struct TargetExpressionConstant final { + TargetConstant Expression; +}; + +using MachineConstantPoolValue = + std::variant<GenericConstantData, TargetConstant, SymbolDifferenceConstant, + BlockAddressConstant, TargetExpressionConstant>; + +struct MachineConstantPoolEntryDesc final { + MachineConstantPoolEntryDesc() = default; + MachineConstantPoolEntryDesc(const MachineConstantPoolEntryDesc &) = delete; + MachineConstantPoolEntryDesc &operator=(const MachineConstantPoolEntryDesc &) = delete; + MachineConstantPoolEntryDesc(MachineConstantPoolEntryDesc &&) noexcept = default; + MachineConstantPoolEntryDesc &operator=(MachineConstantPoolEntryDesc &&) noexcept = default; + ~MachineConstantPoolEntryDesc() = default; + + MachineConstantPoolValue Value; + std::uint64_t Size = 0; + Alignment Align; + ConstantPoolMergeKind Merge = ConstantPoolMergeKind::Never; + ConstantPoolRelocationMode Relocation = ConstantPoolRelocationMode::None; + ConstantPoolPlacement Placement; + + [[nodiscard]] ConstantPoolEntryKind kind() const noexcept; + [[nodiscard]] Expected<void> verify() const; + [[nodiscard]] Expected<MachineConstantPoolEntryDesc> clone() const; + [[nodiscard]] bool semanticallyEquals(const MachineConstantPoolEntryDesc &Other) const noexcept; +}; + +class MachineConstantPoolEntry final { +public: + [[nodiscard]] ConstantPoolIndex index() const noexcept { + return Index; + } + + [[nodiscard]] bool isTombstone() const noexcept { + return std::holds_alternative<std::monostate>(Description); + } + + [[nodiscard]] const MachineConstantPoolEntryDesc *description() const noexcept { + return std::get_if<MachineConstantPoolEntryDesc>(&Description); + } + + [[nodiscard]] Expected<MachineConstantPoolEntryDesc> cloneDescription() const; + +private: + MachineConstantPoolEntry(ConstantPoolIndex Index, MachineConstantPoolEntryDesc Description) + : Index(Index), Description(std::move(Description)) {} + + ConstantPoolIndex Index; + std::variant<std::monostate, MachineConstantPoolEntryDesc> Description; + + friend class MachineConstantPool; +}; + +class MachineConstantPool final { +public: + MachineConstantPool() = default; + MachineConstantPool(const MachineConstantPool &) = delete; + MachineConstantPool &operator=(const MachineConstantPool &) = delete; + MachineConstantPool(MachineConstantPool &&) noexcept = default; + MachineConstantPool &operator=(MachineConstantPool &&) noexcept = default; + ~MachineConstantPool() = default; + + [[nodiscard]] Expected<ConstantPoolIndex> addEntry(MachineConstantPoolEntryDesc Description); + [[nodiscard]] Expected<void> updateEntry(ConstantPoolIndex Index, + MachineConstantPoolEntryDesc Description); + [[nodiscard]] Expected<void> eraseEntry(ConstantPoolIndex Index); + + [[nodiscard]] const MachineConstantPoolEntry *entry(ConstantPoolIndex Index) const noexcept; + [[nodiscard]] std::span<const MachineConstantPoolEntry> entries() const noexcept { + return Entries; + } + + [[nodiscard]] std::size_t liveEntryCount() const noexcept; + + [[nodiscard]] bool isFrozen() const noexcept { + return Frozen; + } + + [[nodiscard]] Expected<void> freeze(); + [[nodiscard]] Expected<void> verify() const; + +private: + [[nodiscard]] Expected<MachineConstantPoolEntry *> mutableEntry(ConstantPoolIndex Index); + [[nodiscard]] const MachineConstantPoolEntry * + findEquivalent(const MachineConstantPoolEntryDesc &Description, + std::optional<ConstantPoolIndex> Excluded = std::nullopt) const noexcept; + + std::vector<MachineConstantPoolEntry> Entries; + bool Frozen = false; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_MACHINECONSTANTPOOL_H diff --git a/backend/include/rcc/CodeGen/MachineCopyPropagation.h b/backend/include/rcc/CodeGen/MachineCopyPropagation.h new file mode 100644 index 0000000..61b562e --- /dev/null +++ b/backend/include/rcc/CodeGen/MachineCopyPropagation.h @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_MACHINECOPYPROPAGATION_H +#define RCC_CODEGEN_MACHINECOPYPROPAGATION_H + +#include "rcc/CodeGen/MachinePassManager.h" + +#include <cstddef> + +namespace rcc { + +class TargetInstrInfo; + +struct MachineCopyPropagationOptions final { + std::size_t MaximumPropagatedCopies = 10'000; + bool UseTargetCopyInstructions = false; +}; + +class MachineCopyPropagationPass final : public MachineFunctionPass { +public: + explicit MachineCopyPropagationPass(const TargetInstrInfo &Info, + MachineCopyPropagationOptions Options = {}) noexcept + : Info(&Info), Options(Options) {} + + [[nodiscard]] const MachinePassContract &contract() const noexcept override; + [[nodiscard]] Expected<MachinePassResult> run(MachineFunction &Function, + const TargetDescription &Target, + MachineFunctionAnalysisManager &Analyses) override; + +private: + [[nodiscard]] Expected<MachinePassResult> + runImplementation(MachineFunction &Function, const TargetDescription &Target, + MachineFunctionAnalysisManager &Analyses) const; + + const TargetInstrInfo *Info; + MachineCopyPropagationOptions Options; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_MACHINECOPYPROPAGATION_H diff --git a/backend/include/rcc/CodeGen/MachineDCE.h b/backend/include/rcc/CodeGen/MachineDCE.h new file mode 100644 index 0000000..8860b0f --- /dev/null +++ b/backend/include/rcc/CodeGen/MachineDCE.h @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_MACHINEDCE_H +#define RCC_CODEGEN_MACHINEDCE_H + +#include "rcc/CodeGen/MachinePassManager.h" + +namespace rcc { + +class MachineDCEPass final : public MachineFunctionPass { +public: + [[nodiscard]] const MachinePassContract &contract() const noexcept override; + [[nodiscard]] Expected<MachinePassResult> run(MachineFunction &Function, + const TargetDescription &Target, + MachineFunctionAnalysisManager &Analyses) override; + +private: + [[nodiscard]] Expected<MachinePassResult> runImplementation(MachineFunction &Function, + const TargetDescription &Target); +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_MACHINEDCE_H diff --git a/backend/include/rcc/CodeGen/MachineDebugInfo.h b/backend/include/rcc/CodeGen/MachineDebugInfo.h new file mode 100644 index 0000000..0d95e96 --- /dev/null +++ b/backend/include/rcc/CodeGen/MachineDebugInfo.h @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_MACHINEDEBUGINFO_H +#define RCC_CODEGEN_MACHINEDEBUGINFO_H + +#include "rcc/CodeGen/MachineMetadata.h" +#include "rcc/Support/Error.h" + +#include <cstdint> +#include <optional> +#include <span> +#include <utility> +#include <variant> +#include <vector> + +namespace rcc { + +class MachineDIExpression final { +public: + MachineDIExpression() = default; + explicit MachineDIExpression(std::vector<std::uint64_t> Elements) + : Elements(std::move(Elements)) {} + + [[nodiscard]] std::span<const std::uint64_t> elements() const noexcept { + return Elements; + } + + friend bool operator==(const MachineDIExpression &Left, + const MachineDIExpression &Right) = default; + +private: + std::vector<std::uint64_t> Elements; +}; + +class MachineMetadataValue final { +public: + MachineMetadataValue() = default; + + [[nodiscard]] static Expected<MachineMetadataValue> reference(MachineMetadataReference Reference); + [[nodiscard]] static MachineMetadataValue expression(MachineDIExpression Expression); + + [[nodiscard]] bool isValid() const noexcept { + return !std::holds_alternative<std::monostate>(Storage); + } + + [[nodiscard]] const MachineMetadataReference *reference() const noexcept { + return std::get_if<MachineMetadataReference>(&Storage); + } + + [[nodiscard]] const MachineDIExpression *expression() const noexcept { + return std::get_if<MachineDIExpression>(&Storage); + } + + [[nodiscard]] Expected<void> verify(const MachineMetadataInfo &Metadata) const; + + friend bool operator==(const MachineMetadataValue &Left, + const MachineMetadataValue &Right) = default; + +private: + explicit MachineMetadataValue(MachineMetadataReference Reference) : Storage(Reference) {} + explicit MachineMetadataValue(MachineDIExpression Expression) : Storage(std::move(Expression)) {} + + std::variant<std::monostate, MachineMetadataReference, MachineDIExpression> Storage; +}; + +struct MachineDILocationFrame final { + std::uint32_t Line = 0; + std::uint16_t Column = 0; + MachineMetadataReference Scope; + bool IsImplicitCode = false; + std::uint64_t AtomGroup = 0; + std::uint8_t AtomRank = 0; + + friend constexpr bool operator==(MachineDILocationFrame Left, + MachineDILocationFrame Right) noexcept = default; +}; + +class MachineDILocation final { +public: + static constexpr std::uint64_t MaxAtomGroup = (std::uint64_t{1} << 61) - 1; + + [[nodiscard]] static Expected<MachineDILocation> + create(std::vector<MachineDILocationFrame> InlineChain, + std::optional<MachineMetadataReference> TerminalInlinedAt = std::nullopt); + + [[nodiscard]] std::span<const MachineDILocationFrame> inlineChain() const noexcept { + return InlineChain; + } + + [[nodiscard]] std::optional<MachineMetadataReference> terminalInlinedAt() const noexcept { + return TerminalInlinedAt; + } + + [[nodiscard]] Expected<void> verify(const MachineMetadataInfo &Metadata) const; + + friend bool operator==(const MachineDILocation &Left, const MachineDILocation &Right) = default; + +private: + MachineDILocation(std::vector<MachineDILocationFrame> InlineChain, + std::optional<MachineMetadataReference> TerminalInlinedAt) + : InlineChain(std::move(InlineChain)), TerminalInlinedAt(TerminalInlinedAt) {} + + [[nodiscard]] Expected<void> verifyStructure() const; + + std::vector<MachineDILocationFrame> InlineChain; + std::optional<MachineMetadataReference> TerminalInlinedAt; +}; + +class MachineDebugLocation final { +public: + MachineDebugLocation() = default; + + [[nodiscard]] static Expected<MachineDebugLocation> reference(MachineMetadataReference Reference); + [[nodiscard]] static MachineDebugLocation inlineLocation(MachineDILocation Location); + + [[nodiscard]] bool isValid() const noexcept { + return !std::holds_alternative<std::monostate>(Storage); + } + + [[nodiscard]] const MachineMetadataReference *reference() const noexcept { + return std::get_if<MachineMetadataReference>(&Storage); + } + + [[nodiscard]] const MachineDILocation *inlineLocation() const noexcept { + return std::get_if<MachineDILocation>(&Storage); + } + + [[nodiscard]] Expected<void> verify(const MachineMetadataInfo &Metadata) const; + + friend bool operator==(const MachineDebugLocation &Left, + const MachineDebugLocation &Right) = default; + +private: + explicit MachineDebugLocation(MachineMetadataReference Reference) : Storage(Reference) {} + explicit MachineDebugLocation(MachineDILocation Location) : Storage(std::move(Location)) {} + + std::variant<std::monostate, MachineMetadataReference, MachineDILocation> Storage; +}; + +struct MachineVariableDebugInfo final { + std::optional<MachineMetadataReference> Variable; + std::optional<MachineMetadataValue> Expression; + std::optional<MachineDebugLocation> Location; + + [[nodiscard]] Expected<void> verifyStructure() const; + [[nodiscard]] Expected<void> verify(const MachineMetadataInfo &Metadata) const; + + friend bool operator==(const MachineVariableDebugInfo &Left, + const MachineVariableDebugInfo &Right) = default; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_MACHINEDEBUGINFO_H diff --git a/backend/include/rcc/CodeGen/MachineFinalization.h b/backend/include/rcc/CodeGen/MachineFinalization.h new file mode 100644 index 0000000..d12ee0b --- /dev/null +++ b/backend/include/rcc/CodeGen/MachineFinalization.h @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_MACHINEFINALIZATION_H +#define RCC_CODEGEN_MACHINEFINALIZATION_H + +#include "rcc/CodeGen/MachinePassManager.h" + +namespace rcc { + +class TargetInstrInfo; + +class FinalizeEmissionPseudosPass final : public MachineFunctionPass { +public: + [[nodiscard]] const MachinePassContract &contract() const noexcept override; + [[nodiscard]] Expected<MachinePassResult> run(MachineFunction &Function, + const TargetDescription &Target, + MachineFunctionAnalysisManager &Analyses) override; +}; + +class FinalEncodabilityVerifierPass final : public MachineFunctionPass { +public: + explicit FinalEncodabilityVerifierPass(const TargetInstrInfo &BranchInfo) noexcept + : BranchInfo(&BranchInfo) {} + + [[nodiscard]] const MachinePassContract &contract() const noexcept override; + [[nodiscard]] Expected<MachinePassResult> run(MachineFunction &Function, + const TargetDescription &Target, + MachineFunctionAnalysisManager &Analyses) override; + +private: + const TargetInstrInfo *BranchInfo; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_MACHINEFINALIZATION_H diff --git a/backend/include/rcc/CodeGen/MachineFrameInfo.h b/backend/include/rcc/CodeGen/MachineFrameInfo.h new file mode 100644 index 0000000..c884afb --- /dev/null +++ b/backend/include/rcc/CodeGen/MachineFrameInfo.h @@ -0,0 +1,265 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_MACHINEFRAMEINFO_H +#define RCC_CODEGEN_MACHINEFRAMEINFO_H + +#include "rcc/CodeGen/MachineDebugInfo.h" +#include "rcc/CodeGen/MachineIRTypes.h" +#include "rcc/CodeGen/MachineMemOperand.h" +#include "rcc/CodeGen/Register.h" +#include "rcc/CodeGen/StableID.h" +#include "rcc/Support/Error.h" + +#include <cstdint> +#include <optional> +#include <span> +#include <string> +#include <string_view> +#include <utility> +#include <variant> +#include <vector> + +namespace rcc { + +enum class StackObjectKind : std::uint8_t { + Default, + FixedABI, + FixedSpillSlot, + SpillSlot, + VariableSized, + DynamicAlloca, + CalleeSaved, + EmergencySpill, + Target, + Count, +}; + +class StackID final { +public: + constexpr StackID() noexcept = default; + + [[nodiscard]] static constexpr StackID defaultStack() noexcept { + return StackID(DefaultStackValue); + } + + [[nodiscard]] static constexpr StackID sgprSpillStack() noexcept { + return StackID(SGPRSpillStackValue); + } + + [[nodiscard]] static constexpr StackID scalableVectorStack() noexcept { + return StackID(ScalableVectorStackValue); + } + + [[nodiscard]] static constexpr StackID wasmLocalStack() noexcept { + return StackID(WasmLocalStackValue); + } + + [[nodiscard]] static constexpr StackID scalablePredicateVectorStack() noexcept { + return StackID(ScalablePredicateVectorStackValue); + } + + [[nodiscard]] static constexpr StackID noAllocStack() noexcept { + return StackID(NoAllocStackValue); + } + + [[nodiscard]] static Expected<StackID> fromValue(std::uint32_t Value); + + [[nodiscard]] constexpr std::uint32_t value() const noexcept { + return Value; + } + + [[nodiscard]] constexpr bool isSpecialStack() const noexcept { + return Value != DefaultStackValue; + } + + [[nodiscard]] constexpr bool isValid() const noexcept { + return Value <= ScalablePredicateVectorStackValue || Value == NoAllocStackValue; + } + + friend constexpr bool operator==(StackID Left, StackID Right) noexcept = default; + +private: + static constexpr std::uint32_t DefaultStackValue = 0; + static constexpr std::uint32_t SGPRSpillStackValue = 1; + static constexpr std::uint32_t ScalableVectorStackValue = 2; + static constexpr std::uint32_t WasmLocalStackValue = 3; + static constexpr std::uint32_t ScalablePredicateVectorStackValue = 4; + static constexpr std::uint32_t NoAllocStackValue = 255; + + explicit constexpr StackID(std::uint32_t Value) noexcept : Value(Value) {} + + std::uint32_t Value = DefaultStackValue; +}; + +[[nodiscard]] std::optional<StackID> parseStackIDName(std::string_view Name) noexcept; +[[nodiscard]] std::string_view getStackIDName(StackID ID) noexcept; + +struct StackObjectLifetimeRange final { + MachineInstrStableID Begin; + MachineInstrStableID End; + + friend constexpr bool operator==(StackObjectLifetimeRange Left, + StackObjectLifetimeRange Right) noexcept = default; +}; + +struct StackObjectDesc final { + StackObjectKind Kind = StackObjectKind::Default; + std::optional<std::string> AllocaName; + std::optional<std::uint64_t> Size; + Alignment Align; + std::optional<std::int64_t> Offset; + StackID Stack; + bool IsDead = false; + bool IsImmutable = false; + bool IsAliased = false; + bool AddressTaken = false; + std::optional<PhysicalRegister> CalleeSavedRegister; + bool CalleeSavedRestored = true; + std::optional<std::int64_t> LocalOffset; + std::vector<StackObjectLifetimeRange> Lifetime; + std::optional<MachineVariableDebugInfo> DebugInfo; + std::uint64_t TargetFlags = 0; + + friend bool operator==(const StackObjectDesc &Left, const StackObjectDesc &Right) = default; +}; + +class StackObject final { +public: + [[nodiscard]] FrameIndex index() const noexcept { + return Index; + } + + [[nodiscard]] bool isTombstone() const noexcept { + return std::holds_alternative<std::monostate>(Description); + } + + [[nodiscard]] const StackObjectDesc *description() const noexcept { + return std::get_if<StackObjectDesc>(&Description); + } + + [[nodiscard]] bool isSpill() const noexcept; + [[nodiscard]] bool isFixed() const noexcept; + [[nodiscard]] bool isVariableSized() const noexcept; + +private: + StackObject(FrameIndex Index, StackObjectDesc Description) + : Index(Index), Description(std::move(Description)) {} + + FrameIndex Index; + std::variant<std::monostate, StackObjectDesc> Description; + + friend class MachineFrameInfo; +}; + +enum class MachineFramePointerPolicy : std::uint8_t { + None, + NonLeaf, + All, + Reserved, + NonLeafNoReserve, + Count, +}; + +[[nodiscard]] std::optional<MachineFramePointerPolicy> +parseMachineFramePointerPolicy(std::string_view Name) noexcept; +[[nodiscard]] std::string_view +getMachineFramePointerPolicyName(MachineFramePointerPolicy Policy) noexcept; + +struct MachineFrameSaveRestorePoint final { + MachineBlockStableID Block; + std::vector<PhysicalRegister> Registers; + + friend bool operator==(const MachineFrameSaveRestorePoint &Left, + const MachineFrameSaveRestorePoint &Right) = default; +}; + +struct MachineFrameState final { + bool IsFrameAddressTaken = false; + bool IsReturnAddressTaken = false; + bool HasStackMap = false; + bool HasPatchPoint = false; + std::uint64_t StackSize = 0; + std::int32_t OffsetAdjustment = 0; + std::optional<Alignment> MaxAlignment; + bool AdjustsStack = false; + bool HasCalls = false; + MachineFramePointerPolicy FramePointerPolicy = MachineFramePointerPolicy::None; + std::optional<FrameIndex> StackProtector; + std::optional<FrameIndex> FunctionContext; + std::optional<std::uint64_t> MaxCallFrameSize; + std::uint32_t CVBytesOfCalleeSavedRegisters = 0; + bool HasOpaqueSPAdjustment = false; + bool HasVAStart = false; + bool HasMustTailInVarArgFunc = false; + bool HasTailCall = false; + bool IsCalleeSavedInfoValid = false; + std::uint32_t LocalFrameSize = 0; + std::vector<MachineFrameSaveRestorePoint> SavePoints; + std::vector<MachineFrameSaveRestorePoint> RestorePoints; + + friend bool operator==(const MachineFrameState &Left, const MachineFrameState &Right) = default; +}; + +class MachineFrameInfo final { +public: + MachineFrameInfo() = default; + MachineFrameInfo(const MachineFrameInfo &) = delete; + MachineFrameInfo &operator=(const MachineFrameInfo &) = delete; + MachineFrameInfo(MachineFrameInfo &&) noexcept = default; + MachineFrameInfo &operator=(MachineFrameInfo &&) noexcept = default; + ~MachineFrameInfo() = default; + + [[nodiscard]] Expected<FrameIndex> createObject(StackObjectDesc Description); + [[nodiscard]] Expected<void> updateObject(FrameIndex Index, StackObjectDesc Description); + [[nodiscard]] Expected<void> setObjectOffset(FrameIndex Index, std::int64_t Offset); + [[nodiscard]] Expected<void> markObjectDead(FrameIndex Index); + [[nodiscard]] Expected<void> eraseObject(FrameIndex Index); + + [[nodiscard]] const StackObject *object(FrameIndex Index) const noexcept; + [[nodiscard]] std::span<const StackObject> objects() const noexcept { + return Objects; + } + + [[nodiscard]] std::size_t liveObjectCount() const noexcept; + + [[nodiscard]] bool isFrozen() const noexcept { + return Frozen; + } + + [[nodiscard]] const MachineFrameState &state() const noexcept { + return State; + } + + [[nodiscard]] std::optional<std::uint64_t> staticStackSize() const noexcept { + if (!Frozen) + return std::nullopt; + return State.StackSize; + } + + [[nodiscard]] Alignment stackAlignment() const noexcept { + return State.MaxAlignment.value_or(Alignment{}); + } + + [[nodiscard]] std::optional<std::uint64_t> maxCallFrameSize() const noexcept { + return State.MaxCallFrameSize; + } + + [[nodiscard]] Expected<void> setState(MachineFrameState NewState); + [[nodiscard]] Expected<void> setMaxCallFrameSize(std::uint64_t Size); + [[nodiscard]] Expected<void> clearMaxCallFrameSize(); + [[nodiscard]] Expected<void> freeze(std::uint64_t Size, Alignment Align); + [[nodiscard]] Expected<void> verify() const; + +private: + [[nodiscard]] static Expected<void> verifyDescription(const StackObjectDesc &Description); + [[nodiscard]] static Expected<void> verifyStateStructure(const MachineFrameState &State); + [[nodiscard]] Expected<StackObject *> mutableObject(FrameIndex Index); + + std::vector<StackObject> Objects; + MachineFrameState State; + bool Frozen = false; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_MACHINEFRAMEINFO_H diff --git a/backend/include/rcc/CodeGen/MachineFunction.h b/backend/include/rcc/CodeGen/MachineFunction.h new file mode 100644 index 0000000..501f334 --- /dev/null +++ b/backend/include/rcc/CodeGen/MachineFunction.h @@ -0,0 +1,251 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_MACHINEFUNCTION_H +#define RCC_CODEGEN_MACHINEFUNCTION_H + +#include "rcc/CodeGen/MachineCFIInfo.h" +#include "rcc/CodeGen/MachineCompilationStatus.h" +#include "rcc/CodeGen/MachineConstantPool.h" +#include "rcc/CodeGen/MachineFrameInfo.h" +#include "rcc/CodeGen/MachineFunctionAttributes.h" +#include "rcc/CodeGen/MachineFunctionProperties.h" +#include "rcc/CodeGen/MachineFunctionRecords.h" +#include "rcc/CodeGen/MachineJumpTableInfo.h" +#include "rcc/CodeGen/MachineMetadata.h" +#include "rcc/CodeGen/MachineRegisterInfo.h" +#include "rcc/CodeGen/StableID.h" +#include "rcc/CodeGen/TargetFunctionState.h" +#include "rcc/Support/Error.h" + +#include <cstddef> +#include <cstdint> +#include <memory> +#include <span> +#include <string> +#include <string_view> +#include <vector> + +namespace rcc { + +class MachineArena; +class MachineBasicBlock; +class MachineCFGUpdater; +class MachineInstr; +class MachineMemOperand; +struct MachineMemOperandDesc; +class Opcode; + +class MachineFunction final { +public: + class Delegate { + public: + virtual ~Delegate() = default; + virtual void handleInsertion(MachineInstr &Instruction) = 0; + virtual void handleRemoval(MachineInstr &Instruction) = 0; + }; + + [[nodiscard]] static Expected<std::unique_ptr<MachineFunction>> + create(std::string Name, MachineFunctionStableID StableID); + + MachineFunction(const MachineFunction &) = delete; + MachineFunction &operator=(const MachineFunction &) = delete; + MachineFunction(MachineFunction &&) = delete; + MachineFunction &operator=(MachineFunction &&) = delete; + ~MachineFunction(); + + [[nodiscard]] std::string_view name() const noexcept { + return Name; + } + + [[nodiscard]] MachineFunctionStableID stableID() const noexcept { + return StableID; + } + + [[nodiscard]] MachineRegisterInfo ®isterInfo() noexcept { + return RegInfo; + } + + [[nodiscard]] const MachineRegisterInfo ®isterInfo() const noexcept { + return RegInfo; + } + + [[nodiscard]] MachineFrameInfo &frameInfo() noexcept { + return FrameInfo; + } + + [[nodiscard]] const MachineFrameInfo &frameInfo() const noexcept { + return FrameInfo; + } + + [[nodiscard]] MachineConstantPool &constantPool() noexcept { + return ConstantPool; + } + + [[nodiscard]] const MachineConstantPool &constantPool() const noexcept { + return ConstantPool; + } + + [[nodiscard]] MachineJumpTableInfo &jumpTableInfo() noexcept { + return JumpTables; + } + + [[nodiscard]] const MachineJumpTableInfo &jumpTableInfo() const noexcept { + return JumpTables; + } + + [[nodiscard]] MachineFunctionProperties &properties() noexcept { + return Properties; + } + + [[nodiscard]] const MachineFunctionProperties &properties() const noexcept { + return Properties; + } + + [[nodiscard]] MachineFunctionAttributes &attributes() noexcept { + return Attributes; + } + + [[nodiscard]] const MachineFunctionAttributes &attributes() const noexcept { + return Attributes; + } + + [[nodiscard]] MachineFunctionRecords &records() noexcept { + return Records; + } + + [[nodiscard]] const MachineFunctionRecords &records() const noexcept { + return Records; + } + + [[nodiscard]] MachineMetadataInfo &metadata() noexcept { + return Metadata; + } + + [[nodiscard]] const MachineMetadataInfo &metadata() const noexcept { + return Metadata; + } + + [[nodiscard]] MachineCFIInfo &cfiInfo() noexcept { + return CFIInfo; + } + + [[nodiscard]] const MachineCFIInfo &cfiInfo() const noexcept { + return CFIInfo; + } + + [[nodiscard]] const TargetFunctionState &targetState() const noexcept { + return TargetState; + } + + [[nodiscard]] Expected<void> setTargetState(TargetFunctionState State); + + template <TargetFunctionStateValue ValueT> [[nodiscard]] ValueT *getTargetInfo() noexcept { + return TargetState.getIf<ValueT>(); + } + + template <TargetFunctionStateValue ValueT> + [[nodiscard]] const ValueT *getTargetInfo() const noexcept { + return TargetState.getIf<ValueT>(); + } + + [[nodiscard]] MachineCompilationStatus &compilationStatus() noexcept { + return CompilationStatus; + } + + [[nodiscard]] const MachineCompilationStatus &compilationStatus() const noexcept { + return CompilationStatus; + } + + [[nodiscard]] Expected<MachineBasicBlock *> createBasicBlock(); + [[nodiscard]] Expected<MachineInstr *> createInstruction(Opcode InstructionOpcode); + [[nodiscard]] Expected<const MachineMemOperand *> + createMemOperand(MachineMemOperandDesc Description); + + void setDelegate(Delegate &NewDelegate) noexcept { + TheDelegate = &NewDelegate; + } + void resetDelegate(Delegate &CurrentDelegate) noexcept { + if (TheDelegate == &CurrentDelegate) + TheDelegate = nullptr; + } + + [[nodiscard]] Expected<void> appendBlock(MachineBasicBlock &Block); + [[nodiscard]] Expected<void> insertBlockBefore(MachineBasicBlock &Position, + MachineBasicBlock &Block); + [[nodiscard]] Expected<void> moveBlockBefore(MachineBasicBlock &Position, + MachineBasicBlock &Block); + [[nodiscard]] Expected<void> moveBlockAfter(MachineBasicBlock &Position, + MachineBasicBlock &Block); + [[nodiscard]] Expected<void> removeBlock(MachineBasicBlock &Block); + + [[nodiscard]] MachineBasicBlock *firstBlock() noexcept { + return FirstBlock; + } + + [[nodiscard]] const MachineBasicBlock *firstBlock() const noexcept { + return FirstBlock; + } + + [[nodiscard]] MachineBasicBlock *lastBlock() noexcept { + return LastBlock; + } + + [[nodiscard]] const MachineBasicBlock *lastBlock() const noexcept { + return LastBlock; + } + + [[nodiscard]] std::size_t blockCount() const noexcept; + [[nodiscard]] std::span<MachineBasicBlock *const> allBlocks() const noexcept { + return AllBlocks; + } + + [[nodiscard]] std::span<MachineInstr *const> allInstructions() const noexcept { + return AllInstructions; + } + + [[nodiscard]] std::span<const MachineMemOperand *const> allMemOperands() const noexcept { + return AllMemOperands; + } + + [[nodiscard]] bool ownsMemOperand(const MachineMemOperand &MemOperand) const noexcept; + + [[nodiscard]] MachineCFGUpdater cfgUpdater() noexcept; + void renumberBlocks() noexcept; + +private: + MachineFunction(std::string Name, MachineFunctionStableID StableID); + [[nodiscard]] Expected<std::uint32_t> allocateDebugInstrNumber(); + void observeDebugInstrNumber(std::uint32_t Number) noexcept; + + std::string Name; + MachineFunctionStableID StableID; + MachineRegisterInfo RegInfo; + MachineFrameInfo FrameInfo; + MachineConstantPool ConstantPool; + MachineJumpTableInfo JumpTables; + MachineFunctionProperties Properties; + MachineFunctionAttributes Attributes; + MachineFunctionRecords Records; + MachineMetadataInfo Metadata; + MachineCFIInfo CFIInfo; + TargetFunctionState TargetState; + MachineCompilationStatus CompilationStatus; + MachineBasicBlock *FirstBlock = nullptr; + MachineBasicBlock *LastBlock = nullptr; + std::vector<MachineBasicBlock *> AllBlocks; + std::vector<MachineInstr *> AllInstructions; + std::vector<const MachineMemOperand *> AllMemOperands; + StableIDGenerator<MachineBlockStableID> BlockIDGenerator; + StableIDGenerator<MachineInstrStableID> InstrIDGenerator; + std::uint32_t DebugInstrNumberingCount = 0; + std::unique_ptr<MachineArena> Arena; + Delegate *TheDelegate = nullptr; + + friend class MachineBasicBlock; + friend class MachineCFGUpdater; + friend class MachineInstr; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_MACHINEFUNCTION_H diff --git a/backend/include/rcc/CodeGen/MachineFunctionAnalysisManager.h b/backend/include/rcc/CodeGen/MachineFunctionAnalysisManager.h new file mode 100644 index 0000000..9685e45 --- /dev/null +++ b/backend/include/rcc/CodeGen/MachineFunctionAnalysisManager.h @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_MACHINEFUNCTIONANALYSISMANAGER_H +#define RCC_CODEGEN_MACHINEFUNCTIONANALYSISMANAGER_H + +#include "rcc/CodeGen/StableID.h" +#include "rcc/IR/PassManager.h" + +#include <cstdint> +#include <initializer_list> + +namespace rcc { + +class CompilationUnit; +class MachineFunction; +class MachinePassInstrumentation; + +enum class MachinePassChange : std::uint32_t { + None = 0, + Instructions = 1U << 0U, + Operands = 1U << 1U, + Registers = 1U << 2U, + CFG = 1U << 3U, + BlockLayout = 1U << 4U, + Frame = 1U << 5U, + ConstantPool = 1U << 6U, + JumpTable = 1U << 7U, + TargetState = 1U << 8U, +}; + +class MachinePassChanges final { +public: + constexpr MachinePassChanges() noexcept = default; + constexpr MachinePassChanges(std::initializer_list<MachinePassChange> Changes) noexcept { + for (const MachinePassChange Change : Changes) + add(Change); + } + + [[nodiscard]] static constexpr MachinePassChanges all() noexcept { + return fromRaw((1U << 9U) - 1U); + } + + [[nodiscard]] static constexpr MachinePassChanges fromRaw(std::uint32_t Bits) noexcept { + MachinePassChanges Result; + Result.Bits = Bits & rawMask(); + return Result; + } + + constexpr void add(MachinePassChange Change) noexcept { + Bits |= static_cast<std::uint32_t>(Change); + } + + [[nodiscard]] constexpr bool has(MachinePassChange Change) const noexcept { + return (Bits & static_cast<std::uint32_t>(Change)) != 0; + } + + [[nodiscard]] constexpr bool contains(MachinePassChanges Other) const noexcept { + return (Bits & Other.Bits) == Other.Bits; + } + + [[nodiscard]] constexpr bool intersects(MachinePassChanges Other) const noexcept { + return (Bits & Other.Bits) != 0; + } + + [[nodiscard]] constexpr bool empty() const noexcept { + return Bits == 0; + } + + [[nodiscard]] constexpr std::uint32_t rawValue() const noexcept { + return Bits; + } + + friend constexpr bool operator==(MachinePassChanges Left, + MachinePassChanges Right) noexcept = default; + +private: + [[nodiscard]] static constexpr std::uint32_t rawMask() noexcept { + return (1U << 9U) - 1U; + } + + std::uint32_t Bits = 0; +}; + +template <> +struct AnalysisManagerInstrumentation<MachineFunction, MachinePassInstrumentation> final { + static void beforeAnalysis(MachinePassInstrumentation *Instrumentation, std::string_view Name, + const MachineFunction &Function); + static void afterAnalysis(MachinePassInstrumentation *Instrumentation, std::string_view Name, + const MachineFunction &Function, bool CacheHit, + std::uint64_t Nanoseconds); + static void analysisFailure(MachinePassInstrumentation *Instrumentation, std::string_view Name, + const MachineFunction &Function, const Error &Failure, + std::uint64_t Nanoseconds); + [[nodiscard]] static Expected<bool> requestAction(MachinePassInstrumentation *Instrumentation, + std::string_view Pass, std::string_view Rule, + const MachineFunction &Function, + MachineInstrStableID Instruction, + std::uint64_t Cost); + [[nodiscard]] static Expected<bool> requestAction(MachinePassInstrumentation *Instrumentation, + std::string_view Pass, std::string_view Rule, + const MachineFunction &Function, std::nullptr_t, + std::uint64_t Cost); +}; + +template <> +struct AnalysisManagerInstrumentation<CompilationUnit, MachinePassInstrumentation> final { + static void beforeAnalysis(MachinePassInstrumentation *Instrumentation, std::string_view Name, + const CompilationUnit &Unit); + static void afterAnalysis(MachinePassInstrumentation *Instrumentation, std::string_view Name, + const CompilationUnit &Unit, bool CacheHit, std::uint64_t Nanoseconds); + static void analysisFailure(MachinePassInstrumentation *Instrumentation, std::string_view Name, + const CompilationUnit &Unit, const Error &Failure, + std::uint64_t Nanoseconds); +}; + +using MachineAnalysisRegistration = AnalysisRegistration<MachinePassChanges>; +using CompilationUnitAnalysisRegistration = AnalysisRegistration<MachinePassChanges>; + +using MachineFunctionAnalysisManager = + AnalysisManager<MachineFunction, MachineAnalysisRegistration, MachinePassChanges, + MachinePassInstrumentation>; +using CompilationUnitAnalysisManager = + AnalysisManager<CompilationUnit, CompilationUnitAnalysisRegistration, MachinePassChanges, + MachinePassInstrumentation>; + +[[nodiscard]] Expected<void> +registerStage8MachineAnalyses(MachineFunctionAnalysisManager &Analyses); +[[nodiscard]] Expected<void> +registerStage11MachineAnalyses(MachineFunctionAnalysisManager &Analyses); + +} // namespace rcc + +#endif // RCC_CODEGEN_MACHINEFUNCTIONANALYSISMANAGER_H diff --git a/backend/include/rcc/CodeGen/MachineFunctionAttributes.h b/backend/include/rcc/CodeGen/MachineFunctionAttributes.h new file mode 100644 index 0000000..fd1d8d8 --- /dev/null +++ b/backend/include/rcc/CodeGen/MachineFunctionAttributes.h @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_MACHINEFUNCTIONATTRIBUTES_H +#define RCC_CODEGEN_MACHINEFUNCTIONATTRIBUTES_H + +#include "rcc/CodeGen/MachineIRTypes.h" +#include "rcc/Support/Error.h" + +#include <cstdint> +#include <optional> +#include <string_view> + +namespace rcc { + +enum class MachineFunctionAttribute : std::uint8_t { + ExposesReturnsTwice, + HasWinCFI, + HasFakeUses, + CallsEHReturn, + CallsUnwindInit, + HasEHContTarget, + HasEHScopes, + HasEHFunclets, + IsOutlined, + DebugInstrRef, + TracksDebugUserValues, + MinSize, + Count, +}; + +[[nodiscard]] std::string_view +getMachineFunctionAttributeName(MachineFunctionAttribute Attribute) noexcept; + +class MachineFunctionAttributes final { +public: + constexpr MachineFunctionAttributes() noexcept = default; + + [[nodiscard]] static Expected<MachineFunctionAttributes> fromRawFlags(std::uint16_t RawFlags); + + [[nodiscard]] constexpr bool has(MachineFunctionAttribute Attribute) const noexcept { + if (!isValidAttribute(Attribute)) + return false; + return (Flags & flagFor(Attribute)) != 0; + } + + constexpr void set(MachineFunctionAttribute Attribute) noexcept { + if (isValidAttribute(Attribute)) + Flags |= flagFor(Attribute); + } + + constexpr void clear(MachineFunctionAttribute Attribute) noexcept { + if (isValidAttribute(Attribute)) + Flags &= static_cast<std::uint16_t>(~flagFor(Attribute)); + } + + [[nodiscard]] constexpr std::uint16_t rawFlags() const noexcept { + return Flags; + } + + [[nodiscard]] std::optional<Alignment> alignment() const noexcept { + return FunctionAlignment; + } + + void setAlignment(Alignment NewAlignment) noexcept { + FunctionAlignment = NewAlignment; + } + + void clearAlignment() noexcept { + FunctionAlignment.reset(); + } + + [[nodiscard]] Expected<void> verify() const; + + friend bool operator==(const MachineFunctionAttributes &Left, + const MachineFunctionAttributes &Right) = default; + +private: + static constexpr std::uint16_t AttributeCount = + static_cast<std::uint16_t>(MachineFunctionAttribute::Count); + static_assert(AttributeCount < 16); + + static constexpr std::uint16_t ValidFlags = + static_cast<std::uint16_t>((std::uint16_t{1} << AttributeCount) - 1); + + [[nodiscard]] static constexpr bool + isValidAttribute(MachineFunctionAttribute Attribute) noexcept { + return static_cast<std::uint16_t>(Attribute) < AttributeCount; + } + + [[nodiscard]] static constexpr std::uint16_t + flagFor(MachineFunctionAttribute Attribute) noexcept { + return static_cast<std::uint16_t>(std::uint16_t{1} << static_cast<std::uint16_t>(Attribute)); + } + + std::uint16_t Flags = 0; + std::optional<Alignment> FunctionAlignment; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_MACHINEFUNCTIONATTRIBUTES_H diff --git a/backend/include/rcc/CodeGen/MachineFunctionCloner.h b/backend/include/rcc/CodeGen/MachineFunctionCloner.h new file mode 100644 index 0000000..09877d2 --- /dev/null +++ b/backend/include/rcc/CodeGen/MachineFunctionCloner.h @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_MACHINEFUNCTIONCLONER_H +#define RCC_CODEGEN_MACHINEFUNCTIONCLONER_H + +#include "rcc/CodeGen/MachineIRTypes.h" +#include "rcc/CodeGen/Register.h" +#include "rcc/CodeGen/StableID.h" +#include "rcc/Support/Error.h" + +#include <memory> +#include <optional> +#include <string> +#include <unordered_map> + +namespace rcc { + +class MachineBasicBlock; +class MachineFunction; +class MachineInstr; +class TargetDescription; + +struct MachineFunctionCloneOptions final { + MachineFunctionStableID NewStableID; + std::optional<std::string> NewName; +}; + +struct MachineFunctionCloneResult final { + MachineFunctionCloneResult(); + MachineFunctionCloneResult(const MachineFunctionCloneResult &) = delete; + MachineFunctionCloneResult &operator=(const MachineFunctionCloneResult &) = delete; + MachineFunctionCloneResult(MachineFunctionCloneResult &&) noexcept; + MachineFunctionCloneResult &operator=(MachineFunctionCloneResult &&) noexcept; + ~MachineFunctionCloneResult(); + + std::unique_ptr<MachineFunction> ClonedFunction; + std::unordered_map<MachineBlockStableID, MachineBlockStableID> BlockStableIDs; + std::unordered_map<MachineInstrStableID, MachineInstrStableID> InstructionStableIDs; + std::unordered_map<Register, Register> VirtualRegisters; + std::unordered_map<FrameIndex, FrameIndex> FrameIndices; + std::unordered_map<ConstantPoolIndex, ConstantPoolIndex> ConstantPoolIndices; + std::unordered_map<JumpTableIndex, JumpTableIndex> JumpTableIndices; +}; + +class MachineFunctionCloneContext final { +public: + [[nodiscard]] const MachineFunction &sourceFunction() const noexcept { + return Source; + } + + [[nodiscard]] MachineFunction &destinationFunction() const noexcept { + return Destination; + } + + [[nodiscard]] Expected<MachineBasicBlock *> mapBlock(const MachineBasicBlock &SourceBlock) const; + [[nodiscard]] Expected<MachineInstr *> + mapInstruction(const MachineInstr &SourceInstruction) const; + [[nodiscard]] Expected<MachineBlockStableID> + mapBlockStableID(MachineBlockStableID SourceID) const; + [[nodiscard]] Expected<MachineInstrStableID> + mapInstructionStableID(MachineInstrStableID SourceID) const; + [[nodiscard]] Expected<Register> mapRegister(Register SourceRegister) const; + [[nodiscard]] Expected<FrameIndex> mapFrameIndex(FrameIndex SourceIndex) const; + [[nodiscard]] Expected<ConstantPoolIndex> + mapConstantPoolIndex(ConstantPoolIndex SourceIndex) const; + [[nodiscard]] Expected<JumpTableIndex> mapJumpTableIndex(JumpTableIndex SourceIndex) const; + +private: + MachineFunctionCloneContext( + const MachineFunction &Source, MachineFunction &Destination, + const MachineFunctionCloneResult &Result, + const std::unordered_map<const MachineBasicBlock *, MachineBasicBlock *> &Blocks, + const std::unordered_map<const MachineInstr *, MachineInstr *> &Instructions) noexcept + : Source(Source), Destination(Destination), Result(Result), Blocks(Blocks), + Instructions(Instructions) {} + + const MachineFunction &Source; + MachineFunction &Destination; + const MachineFunctionCloneResult &Result; + const std::unordered_map<const MachineBasicBlock *, MachineBasicBlock *> &Blocks; + const std::unordered_map<const MachineInstr *, MachineInstr *> &Instructions; + + friend Expected<MachineFunctionCloneResult> + cloneMachineFunction(const MachineFunction &Source, const TargetDescription &Target, + MachineFunctionCloneOptions Options); +}; + +[[nodiscard]] Expected<MachineFunctionCloneResult> +cloneMachineFunction(const MachineFunction &Source, const TargetDescription &Target, + MachineFunctionCloneOptions Options); + +} // namespace rcc + +#endif // RCC_CODEGEN_MACHINEFUNCTIONCLONER_H diff --git a/backend/include/rcc/CodeGen/MachineFunctionProperties.h b/backend/include/rcc/CodeGen/MachineFunctionProperties.h new file mode 100644 index 0000000..95b247c --- /dev/null +++ b/backend/include/rcc/CodeGen/MachineFunctionProperties.h @@ -0,0 +1,134 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_MACHINEFUNCTIONPROPERTIES_H +#define RCC_CODEGEN_MACHINEFUNCTIONPROPERTIES_H + +#include "rcc/Support/Error.h" + +#include <bit> +#include <cstddef> +#include <cstdint> +#include <initializer_list> +#include <string> +#include <string_view> +#include <type_traits> + +namespace rcc { + +enum class MachineFunctionProperty : std::uint8_t { + IsSSA, + NoPHIs, + TracksLiveness, + Legalized, + RegBankSelected, + Selected, + TiedOpsRewritten, + NoVRegs, + FrameFinalized, + NoFrameIndices, + NoExecutablePseudos, + // These two properties mirror LLVM MIR pipeline state. They are deliberately + // separate from RCC's MachineCompilationStatus, which records a concrete run. + FailedISel, + FailsVerification, + Count, +}; + +[[nodiscard]] std::string_view +getMachineFunctionPropertyName(MachineFunctionProperty Property) noexcept; + +class MachineFunctionProperties final { +public: + constexpr MachineFunctionProperties() noexcept = default; + + MachineFunctionProperties(std::initializer_list<MachineFunctionProperty> Properties) noexcept; + + [[nodiscard]] static Expected<MachineFunctionProperties> fromRaw(std::uint64_t RawValue); + + [[nodiscard]] constexpr bool empty() const noexcept { + return Mask == 0; + } + + [[nodiscard]] constexpr std::size_t count() const noexcept { + return static_cast<std::size_t>(std::popcount(Mask)); + } + + [[nodiscard]] constexpr bool has(MachineFunctionProperty Property) const noexcept { + if (!isValidPropertyValue(Property)) + return false; + return (Mask & maskFor(Property)) != 0; + } + + [[nodiscard]] constexpr bool containsAll(MachineFunctionProperties Other) const noexcept { + return (Mask & Other.Mask) == Other.Mask; + } + + [[nodiscard]] constexpr bool intersects(MachineFunctionProperties Other) const noexcept { + return (Mask & Other.Mask) != 0; + } + + [[nodiscard]] constexpr MachineFunctionProperties + missingFrom(MachineFunctionProperties Available) const noexcept { + return MachineFunctionProperties(Mask & ~Available.Mask); + } + + constexpr void set(MachineFunctionProperty Property) noexcept { + if (isValidPropertyValue(Property)) + Mask |= maskFor(Property); + } + + constexpr void clear(MachineFunctionProperty Property) noexcept { + if (isValidPropertyValue(Property)) + Mask &= ~maskFor(Property); + } + + [[nodiscard]] Expected<void> applyTransition(MachineFunctionProperties Required, + MachineFunctionProperties Sets, + MachineFunctionProperties Clears); + + [[nodiscard]] constexpr std::uint64_t rawValue() const noexcept { + return Mask; + } + + [[nodiscard]] std::string toString() const; + + friend constexpr bool operator==(MachineFunctionProperties Left, + MachineFunctionProperties Right) noexcept = default; + + friend constexpr MachineFunctionProperties operator|(MachineFunctionProperties Left, + MachineFunctionProperties Right) noexcept { + return MachineFunctionProperties(Left.Mask | Right.Mask); + } + + friend constexpr MachineFunctionProperties operator&(MachineFunctionProperties Left, + MachineFunctionProperties Right) noexcept { + return MachineFunctionProperties(Left.Mask & Right.Mask); + } + +private: + static constexpr std::uint64_t PropertyCount = + static_cast<std::uint64_t>(MachineFunctionProperty::Count); + static_assert(PropertyCount < 64); + + static constexpr std::uint64_t ValidMask = (std::uint64_t{1} << PropertyCount) - 1; + + explicit constexpr MachineFunctionProperties(std::uint64_t Mask) noexcept : Mask(Mask) {} + + [[nodiscard]] static constexpr bool + isValidPropertyValue(MachineFunctionProperty Property) noexcept { + return static_cast<std::uint64_t>(Property) < PropertyCount; + } + + [[nodiscard]] static constexpr std::uint64_t maskFor(MachineFunctionProperty Property) noexcept { + return std::uint64_t{1} << static_cast<std::uint64_t>(Property); + } + + std::uint64_t Mask = 0; +}; + +static_assert(sizeof(MachineFunctionProperties) == sizeof(std::uint64_t)); +static_assert(std::is_trivially_copyable_v<MachineFunctionProperties>); + +} // namespace rcc + +#endif // RCC_CODEGEN_MACHINEFUNCTIONPROPERTIES_H diff --git a/backend/include/rcc/CodeGen/MachineFunctionRecords.h b/backend/include/rcc/CodeGen/MachineFunctionRecords.h new file mode 100644 index 0000000..645d514 --- /dev/null +++ b/backend/include/rcc/CodeGen/MachineFunctionRecords.h @@ -0,0 +1,161 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_MACHINEFUNCTIONRECORDS_H +#define RCC_CODEGEN_MACHINEFUNCTIONRECORDS_H + +#include "rcc/CodeGen/MachineBasicBlockAttributes.h" +#include "rcc/CodeGen/MachineDebugInfo.h" +#include "rcc/CodeGen/Register.h" +#include "rcc/CodeGen/StableID.h" +#include "rcc/Support/Error.h" + +#include <cstdint> +#include <optional> +#include <span> +#include <string> +#include <vector> + +namespace rcc { + +class MachineFunctionCloneContext; + +inline constexpr std::uint32_t MachineDebugMemoryOperandNumber = 1'000'000; + +struct MachineDebugValueReference final { + std::uint32_t InstructionNumber = 0; + std::uint32_t OperandNumber = 0; + + friend constexpr bool operator==(MachineDebugValueReference Left, + MachineDebugValueReference Right) noexcept = default; +}; + +struct MachineDebugValueSubstitution final { + MachineDebugValueReference Source; + MachineDebugValueReference Destination; + SubRegIndex Subregister; + + friend constexpr bool operator==(MachineDebugValueSubstitution Left, + MachineDebugValueSubstitution Right) noexcept = default; +}; + +struct MachineEntryValueInfo final { + PhysicalRegister Reg; + MachineVariableDebugInfo DebugInfo; + + friend bool operator==(const MachineEntryValueInfo &Left, + const MachineEntryValueInfo &Right) = default; +}; + +struct MachineCallSiteArgument final { + PhysicalRegister Reg; + std::uint16_t ArgumentNumber = 0; + + friend constexpr bool operator==(MachineCallSiteArgument Left, + MachineCallSiteArgument Right) noexcept = default; +}; + +struct MachineCallSiteInfo final { + MachineInstrStableID CallInstruction; + std::vector<MachineCallSiteArgument> ForwardedArguments; + std::vector<std::uint64_t> CalleeTypeIDs; + + friend bool operator==(const MachineCallSiteInfo &Left, + const MachineCallSiteInfo &Right) = default; +}; + +struct MachineCalledGlobal final { + MachineInstrStableID CallInstruction; + std::string Callee; + std::uint32_t Flags = 0; + + friend bool operator==(const MachineCalledGlobal &Left, + const MachineCalledGlobal &Right) = default; +}; + +struct MachinePrefetchTarget final { + MachineBasicBlockProfileID Block; + std::uint32_t CallsiteIndex = 0; + + friend constexpr bool operator==(MachinePrefetchTarget Left, + MachinePrefetchTarget Right) noexcept = default; +}; + +class MachineFunctionRecords final { +public: + MachineFunctionRecords() = default; + MachineFunctionRecords(const MachineFunctionRecords &) = delete; + MachineFunctionRecords &operator=(const MachineFunctionRecords &) = delete; + MachineFunctionRecords(MachineFunctionRecords &&) noexcept = default; + MachineFunctionRecords &operator=(MachineFunctionRecords &&) noexcept = default; + ~MachineFunctionRecords() = default; + + [[nodiscard]] bool hasCalleeSavedRegisters() const noexcept { + return CalleeSavedRegisters.has_value(); + } + + [[nodiscard]] std::span<const PhysicalRegister> calleeSavedRegisters() const noexcept { + if (!CalleeSavedRegisters) + return {}; + return *CalleeSavedRegisters; + } + + [[nodiscard]] Expected<void> setCalleeSavedRegisters(std::vector<PhysicalRegister> Registers); + + void clearCalleeSavedRegisters() noexcept { + CalleeSavedRegisters.reset(); + } + + [[nodiscard]] std::span<const MachineCallSiteInfo> callSites() const noexcept { + return CallSites; + } + + [[nodiscard]] Expected<void> addCallSite(MachineCallSiteInfo CallSite); + + [[nodiscard]] std::span<const MachineCalledGlobal> calledGlobals() const noexcept { + return CalledGlobals; + } + + [[nodiscard]] Expected<void> addCalledGlobal(MachineCalledGlobal CalledGlobal); + + [[nodiscard]] Expected<void> replaceInstructionReference(MachineInstrStableID OldInstruction, + MachineInstrStableID NewInstruction); + + [[nodiscard]] std::span<const MachinePrefetchTarget> prefetchTargets() const noexcept { + return PrefetchTargets; + } + + [[nodiscard]] Expected<void> addPrefetchTarget(MachinePrefetchTarget Target); + + [[nodiscard]] std::span<const MachineDebugValueSubstitution> + debugValueSubstitutions() const noexcept { + return DebugValueSubstitutions; + } + + [[nodiscard]] Expected<void> + addDebugValueSubstitution(MachineDebugValueSubstitution Substitution); + + [[nodiscard]] std::span<const MachineEntryValueInfo> entryValues() const noexcept { + return EntryValues; + } + + [[nodiscard]] Expected<void> addEntryValue(MachineEntryValueInfo EntryValue); + + [[nodiscard]] Expected<void> verify() const; + [[nodiscard]] Expected<MachineFunctionRecords> + clone(const MachineFunctionCloneContext &CloneContext) const; + + friend bool operator==(const MachineFunctionRecords &Left, + const MachineFunctionRecords &Right) = default; + +private: + std::optional<std::vector<PhysicalRegister>> CalleeSavedRegisters; + std::vector<MachineCallSiteInfo> CallSites; + std::vector<MachineCalledGlobal> CalledGlobals; + std::vector<MachinePrefetchTarget> PrefetchTargets; + std::vector<MachineDebugValueSubstitution> DebugValueSubstitutions; + std::vector<MachineEntryValueInfo> EntryValues; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_MACHINEFUNCTIONRECORDS_H diff --git a/backend/include/rcc/CodeGen/MachineIRBuilder.h b/backend/include/rcc/CodeGen/MachineIRBuilder.h new file mode 100644 index 0000000..cd4dbe4 --- /dev/null +++ b/backend/include/rcc/CodeGen/MachineIRBuilder.h @@ -0,0 +1,317 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_MACHINEIRBUILDER_H +#define RCC_CODEGEN_MACHINEIRBUILDER_H + +#include "rcc/CodeGen/GenericOpcodes.h" +#include "rcc/CodeGen/MachineIRTypes.h" +#include "rcc/CodeGen/MachineOperand.h" +#include "rcc/CodeGen/Register.h" +#include "rcc/CodeGen/TargetDescription.h" +#include "rcc/Support/Error.h" + +#include <cstddef> +#include <cstdint> +#include <initializer_list> +#include <memory> +#include <optional> +#include <span> +#include <string> +#include <utility> +#include <vector> + +namespace rcc { + +class MachineBasicBlock; +class MachineFunction; +class MachineInstr; +class MachineMemOperand; +class MachineRegisterInfo; +class RegisterMask; +class GISelCSEInfo; + +class MachineInstrBuilder final { +public: + MachineInstrBuilder(const MachineInstrBuilder &) = delete; + MachineInstrBuilder &operator=(const MachineInstrBuilder &) = delete; + MachineInstrBuilder(MachineInstrBuilder &&Other) noexcept; + MachineInstrBuilder &operator=(MachineInstrBuilder &&Other) noexcept; + ~MachineInstrBuilder() = default; + + [[nodiscard]] Expected<void> addOperand(MachineOperand &&Operand); + [[nodiscard]] Expected<void> addImplicitOperand(MachineOperand &&Operand); + + [[nodiscard]] Expected<void> addDef(Register Reg, RegisterOperandFlags AdditionalFlags = {}, + SubRegIndex SubReg = {}, std::uint32_t TargetFlags = 0); + [[nodiscard]] Expected<void> addUse(Register Reg, RegisterOperandFlags AdditionalFlags = {}, + SubRegIndex SubReg = {}, std::uint32_t TargetFlags = 0); + [[nodiscard]] Expected<void> addDefUse(Register Reg, RegisterOperandFlags AdditionalFlags = {}, + SubRegIndex SubReg = {}, std::uint32_t TargetFlags = 0); + [[nodiscard]] Expected<void> addImplicitDef(Register Reg, + RegisterOperandFlags AdditionalFlags = {}, + SubRegIndex SubReg = {}, + std::uint32_t TargetFlags = 0); + [[nodiscard]] Expected<void> addImplicitUse(Register Reg, + RegisterOperandFlags AdditionalFlags = {}, + SubRegIndex SubReg = {}, + std::uint32_t TargetFlags = 0); + + [[nodiscard]] Expected<void> addImmediate(std::int64_t Value, std::uint32_t TargetFlags = 0); + [[nodiscard]] Expected<void> addIntegerImmediate(APInt Value, std::uint32_t TargetFlags = 0); + [[nodiscard]] Expected<void> addFloatingImmediate(APFloat Bits, std::uint32_t TargetFlags = 0); + [[nodiscard]] Expected<void> addBasicBlock(MachineBasicBlock &Block, + std::uint32_t TargetFlags = 0); + [[nodiscard]] Expected<void> addFrameIndex(FrameIndex Index, std::int64_t Offset = 0, + std::uint32_t TargetFlags = 0); + [[nodiscard]] Expected<void> addConstantPool(ConstantPoolIndex Index, std::int64_t Offset = 0, + std::uint32_t TargetFlags = 0); + [[nodiscard]] Expected<void> addJumpTable(JumpTableIndex Index, std::uint32_t TargetFlags = 0); + [[nodiscard]] Expected<void> addGlobalSymbol(std::string Name, std::int64_t Offset = 0, + std::uint32_t TargetFlags = 0); + [[nodiscard]] Expected<void> addExternalSymbol(std::string Name, std::int64_t Offset = 0, + std::uint32_t TargetFlags = 0); + [[nodiscard]] Expected<void> addBlockAddress(std::string FunctionName, std::string BlockName, + std::int64_t Offset = 0, + std::uint32_t TargetFlags = 0); + [[nodiscard]] Expected<void> addRegisterMask(std::shared_ptr<const RegisterMask> Mask, + std::uint32_t TargetFlags = 0); + [[nodiscard]] Expected<void> addLiveOutRegisterMask(std::shared_ptr<const RegisterMask> Mask, + std::uint32_t TargetFlags = 0); + [[nodiscard]] Expected<void> addMetadata(MachineMetadataValue Value, + std::uint32_t TargetFlags = 0); + [[nodiscard]] Expected<void> addTargetIndex(TargetIndex Index, std::int64_t Offset = 0, + std::uint32_t TargetFlags = 0); + [[nodiscard]] Expected<void> addMCSymbol(std::string Name, std::int64_t Offset = 0, + std::uint32_t TargetFlags = 0); + [[nodiscard]] Expected<void> addCFIInstruction(CFIInstructionIndex Index, + std::uint32_t TargetFlags = 0); + [[nodiscard]] Expected<void> addIntrinsic(IntrinsicID ID, std::uint32_t TargetFlags = 0); + [[nodiscard]] Expected<void> addIntegerPredicate(IntegerPredicate Predicate, + std::uint32_t TargetFlags = 0); + [[nodiscard]] Expected<void> addFloatingPredicate(FloatingPredicate Predicate, + std::uint32_t TargetFlags = 0); + [[nodiscard]] Expected<void> addShuffleMask(std::vector<std::int32_t> Elements, + std::uint32_t TargetFlags = 0); + [[nodiscard]] Expected<void> addDebugInstrRef(std::uint64_t InstructionNumber, + std::uint64_t OperandNumber, + std::uint32_t TargetFlags = 0); + [[nodiscard]] Expected<void> addLaneMask(LaneMask Mask, std::uint32_t TargetFlags = 0); + + [[nodiscard]] Expected<void> tieOperands(std::size_t DefOperand, std::size_t UseOperand); + [[nodiscard]] Expected<void> addMemOperand(const MachineMemOperand &MemOperand); + + [[nodiscard]] Expected<void> setFlags(MachineInstrFlags NewFlags); + + [[nodiscard]] Expected<void> setSourceLocation(SourceLocation NewLocation); + [[nodiscard]] Expected<void> clearSourceLocation(); + [[nodiscard]] Expected<void> setDebugLocation(MachineDebugLocation NewLocation); + [[nodiscard]] Expected<void> clearDebugLocation(); + + [[nodiscard]] Expected<void> setOrigin(OriginID NewOrigin); + [[nodiscard]] Expected<void> clearOrigin(); + + [[nodiscard]] Expected<MachineInstr *> build(); + +private: + MachineInstrBuilder(MachineFunction &Function, const TargetDescription &Target, + const InstructionDescriptor &Descriptor, MachineBasicBlock &InsertBlock, + MachineInstr *InsertBefore) noexcept; + + [[nodiscard]] Expected<void> ensurePending() const; + [[nodiscard]] Expected<void> validateInsertionPoint() const; + [[nodiscard]] Expected<void> validateFrameReference(FrameIndex Index) const; + [[nodiscard]] Expected<void> validateConstantPoolReference(ConstantPoolIndex Index) const; + [[nodiscard]] Expected<void> validateJumpTableReference(JumpTableIndex Index) const; + [[nodiscard]] Expected<void> validateCFIReference(CFIInstructionIndex Index) const; + [[nodiscard]] Expected<void> validateOperandOwnership(const MachineOperand &Operand) const; + [[nodiscard]] std::vector<MachineOperandTie> collectOperandTies() const; + [[nodiscard]] Expected<void> appendRegister(Register Reg, RegisterOperandFlags Flags, + SubRegIndex SubReg, std::uint32_t TargetFlags, + bool IsImplicit); + + MachineFunction *Function; + const TargetDescription *Target; + const InstructionDescriptor *Descriptor; + MachineBasicBlock *InsertBlock; + MachineInstr *InsertBefore; + std::vector<MachineOperand> ExplicitOperands; + std::vector<MachineOperand> ExtraImplicitOperands; + std::vector<MachineOperandTie> RequestedTies; + std::vector<const MachineMemOperand *> MemOperands; + MachineInstrFlags Flags; + std::optional<SourceLocation> Location; + std::optional<MachineDebugLocation> DebugLocation; + std::optional<OriginID> Origin; + bool IsBuilt = false; + + friend class MachineIRBuilder; +}; + +class MachineIRBuilderResult final { +public: + enum class Kind : std::uint8_t { + Register, + Type, + }; + + MachineIRBuilderResult(Register Reg) noexcept : Reg(Reg), ResultKind(Kind::Register) {} + MachineIRBuilderResult(LowLevelType Type) noexcept : Type(Type), ResultKind(Kind::Type) {} + + [[nodiscard]] Kind kind() const noexcept { + return ResultKind; + } + + [[nodiscard]] Register reg() const noexcept; + [[nodiscard]] LowLevelType type() const noexcept; + [[nodiscard]] Expected<LowLevelType> type(const MachineRegisterInfo &MRI) const; + [[nodiscard]] Expected<void> addDef(MachineRegisterInfo &MRI, MachineInstrBuilder &Builder) const; + +private: + Register Reg; + LowLevelType Type; + Kind ResultKind; +}; + +class MachineIRBuilder { +public: + MachineIRBuilder(MachineFunction &Function, const TargetDescription &Target) noexcept + : Function(&Function), Target(&Target) {} + MachineIRBuilder(const MachineIRBuilder &) = delete; + MachineIRBuilder &operator=(const MachineIRBuilder &) = delete; + MachineIRBuilder(MachineIRBuilder &&) = delete; + MachineIRBuilder &operator=(MachineIRBuilder &&) = delete; + virtual ~MachineIRBuilder() = default; + + [[nodiscard]] MachineFunction &function() noexcept { + return *Function; + } + + [[nodiscard]] MachineFunction &function() const noexcept { + return *Function; + } + + [[nodiscard]] const TargetDescription &target() const noexcept { + return *Target; + } + + [[nodiscard]] MachineBasicBlock *insertionBlock() const noexcept { + return InsertBlock; + } + + [[nodiscard]] MachineInstr *insertionInstruction() const noexcept { + return InsertBefore; + } + + [[nodiscard]] GISelCSEInfo *getCSEInfo() noexcept { + return CSEInfo; + } + + [[nodiscard]] GISelCSEInfo *getCSEInfo() const noexcept { + return CSEInfo; + } + + void setCSEInfo(GISelCSEInfo *Info) noexcept { + CSEInfo = Info; + } + + [[nodiscard]] Expected<void> verifyInsertPoint() const; + + [[nodiscard]] Expected<void> setInsertPoint(MachineBasicBlock &Block); + [[nodiscard]] Expected<void> setInsertPointBefore(MachineInstr &Instruction); + [[nodiscard]] Expected<void> setInsertPointAfter(MachineInstr &Instruction); + void clearInsertPoint() noexcept { + InsertBlock = nullptr; + InsertBefore = nullptr; + } + + [[nodiscard]] Expected<MachineInstrBuilder> buildInstr(Opcode InstructionOpcode) const; + [[nodiscard]] Expected<MachineInstr *> + buildInstr(Opcode InstructionOpcode, std::initializer_list<MachineIRBuilderResult> Destinations, + std::initializer_list<Register> Sources) const; + [[nodiscard]] virtual Expected<MachineInstr *> + buildInstr(Opcode InstructionOpcode, std::span<const MachineIRBuilderResult> Destinations, + std::span<const Register> Sources) const; + [[nodiscard]] Expected<MachineInstr *> buildTrunc(MachineIRBuilderResult Destination, + Register Source) const; + [[nodiscard]] Expected<MachineInstr *> buildCopy(MachineIRBuilderResult Destination, + Register Source) const; + [[nodiscard]] Expected<MachineInstr *> buildImplicitDef(MachineIRBuilderResult Destination) const; + [[nodiscard]] Expected<MachineInstr *> buildAnyExt(MachineIRBuilderResult Destination, + Register Source) const; + [[nodiscard]] Expected<MachineInstr *> buildZExt(MachineIRBuilderResult Destination, + Register Source) const; + [[nodiscard]] Expected<MachineInstr *> buildSExt(MachineIRBuilderResult Destination, + Register Source) const; + [[nodiscard]] GenericOpcode getBoolExtOp(bool IsVector, bool IsFloatingPoint) const noexcept; + [[nodiscard]] Expected<MachineInstr *> buildUndef(MachineIRBuilderResult Destination) const; + [[nodiscard]] Expected<MachineInstr *> buildAnyExtOrTrunc(MachineIRBuilderResult Destination, + Register Source) const; + [[nodiscard]] Expected<MachineInstr *> buildZExtOrTrunc(MachineIRBuilderResult Destination, + Register Source) const; + [[nodiscard]] Expected<MachineInstr *> buildSExtOrTrunc(MachineIRBuilderResult Destination, + Register Source) const; + [[nodiscard]] virtual Expected<MachineInstr *> buildConstant(MachineIRBuilderResult Destination, + APInt Value) const; + [[nodiscard]] Expected<MachineInstr *> buildConstant(MachineIRBuilderResult Destination, + std::int64_t Value) const; + [[nodiscard]] virtual Expected<MachineInstr *> + buildSExtInReg(MachineIRBuilderResult Destination, Register Source, std::uint32_t Width) const; + [[nodiscard]] Expected<MachineInstr *> buildAnd(MachineIRBuilderResult Destination, Register Left, + Register Right) const; + [[nodiscard]] Expected<MachineInstr *> buildOr(MachineIRBuilderResult Destination, Register Left, + Register Right) const; + [[nodiscard]] Expected<MachineInstr *> buildXor(MachineIRBuilderResult Destination, Register Left, + Register Right) const; + [[nodiscard]] Expected<MachineInstr *> buildAdd(MachineIRBuilderResult Destination, Register Left, + Register Right) const; + [[nodiscard]] Expected<MachineInstr *> buildSub(MachineIRBuilderResult Destination, Register Left, + Register Right) const; + [[nodiscard]] Expected<MachineInstr *> buildShl(MachineIRBuilderResult Destination, Register Left, + Register Right) const; + [[nodiscard]] Expected<MachineInstr *> buildLShr(MachineIRBuilderResult Destination, + Register Left, Register Right) const; + [[nodiscard]] Expected<MachineInstr *> buildAShr(MachineIRBuilderResult Destination, + Register Left, Register Right) const; + [[nodiscard]] Expected<MachineInstr *> buildPtrToInt(MachineIRBuilderResult Destination, + Register Source) const; + [[nodiscard]] Expected<MachineInstr *> buildIntToPtr(MachineIRBuilderResult Destination, + Register Source) const; + [[nodiscard]] Expected<MachineInstr *> buildICmp(IntegerPredicate Predicate, + MachineIRBuilderResult Destination, + Register Left, Register Right) const; + [[nodiscard]] Expected<MachineInstr *> + buildMergeLikeInstr(MachineIRBuilderResult Destination, + const std::vector<Register> &Sources) const; + [[nodiscard]] Expected<MachineInstr *> + buildMergeValues(MachineIRBuilderResult Destination, const std::vector<Register> &Sources) const; + [[nodiscard]] Expected<MachineInstr *> + buildUnmergeValues(const std::vector<Register> &Destinations, Register Source) const; + [[nodiscard]] virtual Expected<MachineInstr *> + buildExtract(MachineIRBuilderResult Destination, Register Source, std::uint64_t Offset) const; + [[nodiscard]] Expected<MachineInstr *> buildInsert(MachineIRBuilderResult Destination, + Register Source, Register Inserted, + std::uint64_t Offset) const; + +private: + [[nodiscard]] Expected<MachineInstr *> + buildUnary(Opcode InstructionOpcode, MachineIRBuilderResult Destination, Register Source) const; + [[nodiscard]] Expected<MachineInstr *> buildBinary(Opcode InstructionOpcode, + MachineIRBuilderResult Destination, + Register Left, Register Right) const; + [[nodiscard]] Expected<MachineInstr *> buildExtOrTrunc(Opcode ExtensionOpcode, + MachineIRBuilderResult Destination, + Register Source) const; + [[nodiscard]] Expected<LowLevelType> registerType(Register Reg) const; + [[nodiscard]] Expected<void> validateBlock(const MachineBasicBlock &Block) const; + [[nodiscard]] Expected<void> validateInstruction(const MachineInstr &Instruction) const; + + MachineFunction *Function; + const TargetDescription *Target; + MachineBasicBlock *InsertBlock = nullptr; + MachineInstr *InsertBefore = nullptr; + GISelCSEInfo *CSEInfo = nullptr; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_MACHINEIRBUILDER_H diff --git a/backend/include/rcc/CodeGen/MachineIRTypes.h b/backend/include/rcc/CodeGen/MachineIRTypes.h new file mode 100644 index 0000000..abd547e --- /dev/null +++ b/backend/include/rcc/CodeGen/MachineIRTypes.h @@ -0,0 +1,354 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_MACHINEIRTYPES_H +#define RCC_CODEGEN_MACHINEIRTYPES_H + +#include "rcc/Support/Error.h" + +#include <bit> +#include <compare> +#include <cstddef> +#include <cstdint> +#include <functional> +#include <initializer_list> +#include <span> +#include <string> +#include <type_traits> + +namespace rcc { + +template <typename TagT, typename ValueT = std::uint32_t> class MachineIndex final { +public: + constexpr MachineIndex() noexcept = default; + + [[nodiscard]] static constexpr MachineIndex fromValue(ValueT Value) noexcept { + return MachineIndex(Value); + } + + [[nodiscard]] constexpr ValueT value() const noexcept { + return Value; + } + + friend constexpr bool operator==(MachineIndex Left, MachineIndex Right) noexcept = default; + friend constexpr auto operator<=>(MachineIndex Left, MachineIndex Right) noexcept = default; + +private: + explicit constexpr MachineIndex(ValueT Value) noexcept : Value(Value) {} + + ValueT Value = 0; +}; + +namespace detail { + +struct FrameIndexTag; +struct ConstantPoolIndexTag; +struct JumpTableIndexTag; +struct TargetIndexTag; +struct CFIInstructionIndexTag; +struct IntrinsicIDTag; +struct SubRegIndexTag; +struct RegisterUnitIDTag; +struct RegisterBankIDTag; +struct RegisterClassIDTag; +struct SchedulingClassIDTag; +struct ProcessorResourceIDTag; +struct RegisterPressureSetIDTag; +struct ImmediatePredicateIDTag; +struct NamedOperandIDTag; +struct TargetOperandTypeIDTag; +struct EncodingCategoryIDTag; +struct AssemblyFormatIDTag; +struct SyncScopeIDTag; +struct MemoryAliasSourceIDTag; +struct OriginIDTag; +struct TargetConstantTypeIDTag; +struct TargetFunctionStateTypeIDTag; +struct TargetRelocationKindIDTag; +struct CallingConventionIDTag; + +} // namespace detail + +using FrameIndex = MachineIndex<detail::FrameIndexTag>; +using ConstantPoolIndex = MachineIndex<detail::ConstantPoolIndexTag>; +using JumpTableIndex = MachineIndex<detail::JumpTableIndexTag>; +using TargetIndex = MachineIndex<detail::TargetIndexTag>; +using CFIInstructionIndex = MachineIndex<detail::CFIInstructionIndexTag>; +using IntrinsicID = MachineIndex<detail::IntrinsicIDTag>; + +enum class GenericIntrinsic : std::uint32_t { + VACopy = 1, +}; + +[[nodiscard]] constexpr IntrinsicID intrinsicID(GenericIntrinsic Intrinsic) noexcept { + return IntrinsicID::fromValue(static_cast<std::uint32_t>(Intrinsic)); +} + +using SubRegIndex = MachineIndex<detail::SubRegIndexTag>; +using RegisterUnitID = MachineIndex<detail::RegisterUnitIDTag>; +using RegisterBankID = MachineIndex<detail::RegisterBankIDTag>; +using RegisterClassID = MachineIndex<detail::RegisterClassIDTag>; +using SchedulingClassID = MachineIndex<detail::SchedulingClassIDTag>; +using ProcessorResourceID = MachineIndex<detail::ProcessorResourceIDTag>; +using RegisterPressureSetID = MachineIndex<detail::RegisterPressureSetIDTag>; +using ImmediatePredicateID = MachineIndex<detail::ImmediatePredicateIDTag>; +using NamedOperandID = MachineIndex<detail::NamedOperandIDTag>; +using TargetOperandTypeID = MachineIndex<detail::TargetOperandTypeIDTag>; +using EncodingCategoryID = MachineIndex<detail::EncodingCategoryIDTag>; +using AssemblyFormatID = MachineIndex<detail::AssemblyFormatIDTag>; +using SyncScopeID = MachineIndex<detail::SyncScopeIDTag>; +using MemoryAliasSourceID = MachineIndex<detail::MemoryAliasSourceIDTag, std::uint64_t>; +using OriginID = MachineIndex<detail::OriginIDTag, std::uint64_t>; +using TargetConstantTypeID = MachineIndex<detail::TargetConstantTypeIDTag, std::uint64_t>; +using TargetFunctionStateTypeID = MachineIndex<detail::TargetFunctionStateTypeIDTag, std::uint64_t>; +using TargetRelocationKindID = MachineIndex<detail::TargetRelocationKindIDTag>; +using CallingConventionID = MachineIndex<detail::CallingConventionIDTag>; + +class Alignment final { +public: + constexpr Alignment() noexcept = default; + + [[nodiscard]] static Expected<Alignment> create(std::uint64_t Bytes); + + [[nodiscard]] constexpr std::uint64_t bytes() const noexcept { + return Bytes; + } + + friend constexpr bool operator==(Alignment Left, Alignment Right) noexcept = default; + +private: + explicit constexpr Alignment(std::uint64_t Bytes) noexcept : Bytes(Bytes) {} + + std::uint64_t Bytes = 1; +}; + +class Opcode final { +public: + constexpr Opcode() noexcept = default; + + [[nodiscard]] static constexpr Opcode fromValue(std::uint32_t Value) noexcept { + return Opcode(Value); + } + + [[nodiscard]] constexpr bool isValid() const noexcept { + return Value != 0; + } + + [[nodiscard]] constexpr std::uint32_t value() const noexcept { + return Value; + } + + friend constexpr bool operator==(Opcode Left, Opcode Right) noexcept = default; + +private: + explicit constexpr Opcode(std::uint32_t Value) noexcept : Value(Value) {} + + std::uint32_t Value = 0; +}; + +class LaneMask final { +public: + constexpr LaneMask() noexcept = default; + + [[nodiscard]] static constexpr LaneMask fromRaw(std::uint64_t RawValue) noexcept { + return LaneMask(RawValue); + } + + [[nodiscard]] static constexpr LaneMask all() noexcept { + return LaneMask(~std::uint64_t{0}); + } + + [[nodiscard]] constexpr bool empty() const noexcept { + return RawValue == 0; + } + + [[nodiscard]] constexpr bool intersects(LaneMask Other) const noexcept { + return (RawValue & Other.RawValue) != 0; + } + + [[nodiscard]] constexpr bool contains(LaneMask Other) const noexcept { + return (RawValue & Other.RawValue) == Other.RawValue; + } + + [[nodiscard]] constexpr std::uint64_t rawValue() const noexcept { + return RawValue; + } + + [[nodiscard]] constexpr LaneMask operator~() const noexcept { + return LaneMask(~RawValue); + } + + constexpr LaneMask &operator|=(LaneMask Other) noexcept { + RawValue |= Other.RawValue; + return *this; + } + + constexpr LaneMask &operator&=(LaneMask Other) noexcept { + RawValue &= Other.RawValue; + return *this; + } + + friend constexpr bool operator==(LaneMask Left, LaneMask Right) noexcept = default; + + friend constexpr LaneMask operator|(LaneMask Left, LaneMask Right) noexcept { + return LaneMask(Left.RawValue | Right.RawValue); + } + + friend constexpr LaneMask operator&(LaneMask Left, LaneMask Right) noexcept { + return LaneMask(Left.RawValue & Right.RawValue); + } + +private: + explicit constexpr LaneMask(std::uint64_t RawValue) noexcept : RawValue(RawValue) {} + + std::uint64_t RawValue = 0; +}; + +class BranchProbability final { +public: + constexpr BranchProbability() noexcept = default; + + [[nodiscard]] static constexpr BranchProbability zero() noexcept { + return BranchProbability(0); + } + + [[nodiscard]] static constexpr BranchProbability one() noexcept { + return BranchProbability(MaxRawValue); + } + + [[nodiscard]] static constexpr BranchProbability fromRaw(std::uint32_t RawValue) noexcept { + return BranchProbability(RawValue); + } + + [[nodiscard]] static Expected<BranchProbability> fromFraction(std::uint32_t Numerator, + std::uint32_t Denominator); + + static void normalizeProbabilities(std::span<BranchProbability> Probabilities) noexcept; + + [[nodiscard]] constexpr std::uint32_t rawValue() const noexcept { + return RawValue; + } + + [[nodiscard]] constexpr BranchProbability saturatingAdd(BranchProbability Other) const noexcept { + if (MaxRawValue - RawValue < Other.RawValue) + return one(); + return BranchProbability(RawValue + Other.RawValue); + } + + [[nodiscard]] constexpr BranchProbability complement() const noexcept { + return BranchProbability(MaxRawValue - RawValue); + } + + friend constexpr bool operator==(BranchProbability Left, + BranchProbability Right) noexcept = default; + friend constexpr auto operator<=>(BranchProbability Left, + BranchProbability Right) noexcept = default; + +private: + static constexpr std::uint32_t MaxRawValue = std::uint32_t{1} << 31U; + + explicit constexpr BranchProbability(std::uint32_t RawValue) noexcept : RawValue(RawValue) {} + + std::uint32_t RawValue = 0; +}; + +enum class MachineInstrFlag : std::uint8_t { + NoSignedWrap, + NoUnsignedWrap, + Exact, + NoFPExcept, + FastMathAllowReassoc, + FastMathNoNaNs, + FastMathNoInfs, + FastMathNoSignedZeros, + FastMathAllowReciprocal, + FastMathAllowContract, + FastMathApproxFunc, + FrameSetup, + FrameDestroy, + BundledWithPred, + BundledWithSucc, + NoMerge, + Unpredictable, + NoConvergent, + NonNeg, + Disjoint, + NoUSWrap, + SameSign, + InBounds, + Count, +}; + +class MachineInstrFlags final { +public: + constexpr MachineInstrFlags() noexcept = default; + + MachineInstrFlags(std::initializer_list<MachineInstrFlag> Flags) noexcept; + + [[nodiscard]] constexpr bool has(MachineInstrFlag Flag) const noexcept { + const auto Index = static_cast<std::uint32_t>(Flag); + if (Index >= static_cast<std::uint32_t>(MachineInstrFlag::Count)) + return false; + return (Mask & (std::uint32_t{1} << Index)) != 0; + } + + constexpr void set(MachineInstrFlag Flag) noexcept { + const auto Index = static_cast<std::uint32_t>(Flag); + if (Index < static_cast<std::uint32_t>(MachineInstrFlag::Count)) + Mask |= std::uint32_t{1} << Index; + } + + constexpr void clear(MachineInstrFlag Flag) noexcept { + const auto Index = static_cast<std::uint32_t>(Flag); + if (Index < static_cast<std::uint32_t>(MachineInstrFlag::Count)) + Mask &= ~(std::uint32_t{1} << Index); + } + + [[nodiscard]] constexpr std::uint32_t rawValue() const noexcept { + return Mask; + } + + friend constexpr bool operator==(MachineInstrFlags Left, + MachineInstrFlags Right) noexcept = default; + +private: + std::uint32_t Mask = 0; +}; + +struct SourceLocation final { + std::string File; + std::uint32_t Line = 0; + std::uint32_t Column = 0; + std::uint32_t Discriminator = 0; + + [[nodiscard]] bool isValid() const noexcept { + return !File.empty() && Line != 0; + } + + friend bool operator==(const SourceLocation &Left, const SourceLocation &Right) = default; +}; + +static_assert(sizeof(Opcode) == sizeof(std::uint32_t)); +static_assert(sizeof(Alignment) == sizeof(std::uint64_t)); +static_assert(sizeof(FrameIndex) == sizeof(std::uint32_t)); +static_assert(sizeof(CallingConventionID) == sizeof(std::uint32_t)); +static_assert(sizeof(MemoryAliasSourceID) == sizeof(std::uint64_t)); +static_assert(std::is_trivially_copyable_v<Opcode>); +static_assert(std::is_trivially_copyable_v<FrameIndex>); +static_assert(std::is_trivially_copyable_v<LaneMask>); +static_assert(std::is_trivially_copyable_v<BranchProbability>); + +} // namespace rcc + +template <typename TagT, typename ValueT> struct std::hash<rcc::MachineIndex<TagT, ValueT>> { + [[nodiscard]] std::size_t operator()(rcc::MachineIndex<TagT, ValueT> Index) const noexcept { + return std::hash<ValueT>{}(Index.value()); + } +}; + +template <> struct std::hash<rcc::Opcode> { + [[nodiscard]] std::size_t operator()(rcc::Opcode Value) const noexcept { + return std::hash<std::uint32_t>{}(Value.value()); + } +}; + +#endif // RCC_CODEGEN_MACHINEIRTYPES_H diff --git a/backend/include/rcc/CodeGen/MachineInstr.h b/backend/include/rcc/CodeGen/MachineInstr.h new file mode 100644 index 0000000..441f80d --- /dev/null +++ b/backend/include/rcc/CodeGen/MachineInstr.h @@ -0,0 +1,253 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_MACHINEINSTR_H +#define RCC_CODEGEN_MACHINEINSTR_H + +#include "rcc/CodeGen/MachineIRTypes.h" +#include "rcc/CodeGen/MachineOperand.h" +#include "rcc/CodeGen/StableID.h" +#include "rcc/Support/Error.h" + +#include <cstddef> +#include <cstdint> +#include <functional> +#include <optional> +#include <span> +#include <string> +#include <string_view> +#include <utility> +#include <vector> + +namespace rcc { + +class MachineBasicBlock; +class MachineFunction; +class MachineMemOperand; +class MachineRegisterInfo; + +class MachineInstr { +public: + MachineInstr(const MachineInstr &) = delete; + MachineInstr &operator=(const MachineInstr &) = delete; + MachineInstr(MachineInstr &&) = delete; + MachineInstr &operator=(MachineInstr &&) = delete; + ~MachineInstr(); + + [[nodiscard]] Opcode opcode() const noexcept { + return InstructionOpcode; + } + + [[nodiscard]] Expected<void> setOpcode(Opcode NewOpcode); + + [[nodiscard]] MachineInstrStableID stableID() const noexcept { + return StableID; + } + + [[nodiscard]] MachineFunction &function() noexcept { + return *OwnerFunction; + } + + [[nodiscard]] const MachineFunction &function() const noexcept { + return *OwnerFunction; + } + + [[nodiscard]] MachineBasicBlock *parent() noexcept { + return ParentBlock; + } + + [[nodiscard]] const MachineBasicBlock *parent() const noexcept { + return ParentBlock; + } + + [[nodiscard]] MachineInstr *previous() noexcept { + return PreviousInstruction; + } + + [[nodiscard]] const MachineInstr *previous() const noexcept { + return PreviousInstruction; + } + + [[nodiscard]] MachineInstr *next() noexcept { + return NextInstruction; + } + + [[nodiscard]] const MachineInstr *next() const noexcept { + return NextInstruction; + } + + [[nodiscard]] bool isLinked() const noexcept { + return ParentBlock != nullptr; + } + + [[nodiscard]] Expected<void> eraseFromParent(); + + [[nodiscard]] bool isDebugInstr() const noexcept; + + [[nodiscard]] std::size_t operandCount() const noexcept { + return Operands.size(); + } + + [[nodiscard]] MachineOperand *operandAt(std::size_t Index) noexcept; + [[nodiscard]] const MachineOperand *operandAt(std::size_t Index) const noexcept; + [[nodiscard]] std::span<const MachineOperand> operands() const noexcept { + return Operands; + } + + void reserveOperands(std::size_t Capacity); + [[nodiscard]] Expected<void> addOperand(MachineOperand &&Operand); + [[nodiscard]] Expected<void> insertOperand(std::size_t Index, MachineOperand &&Operand); + [[nodiscard]] Expected<void> eraseOperand(std::size_t Index); + + [[nodiscard]] Expected<void> tieOperands(std::size_t DefIndex, std::size_t UseIndex); + [[nodiscard]] Expected<void> untieOperand(std::size_t Index); + + [[nodiscard]] Expected<void> addMemOperand(const MachineMemOperand &MemOperand); + [[nodiscard]] Expected<void> replaceMemOperand(std::size_t Index, + const MachineMemOperand &MemOperand); + [[nodiscard]] Expected<void> eraseMemOperand(std::size_t Index); + [[nodiscard]] std::span<const MachineMemOperand *const> memOperands() const noexcept { + return MemOperands; + } + + [[nodiscard]] MachineInstrFlags flags() const noexcept { + return Flags; + } + + void setFlags(MachineInstrFlags NewFlags) noexcept { + Flags = NewFlags; + } + + [[nodiscard]] const std::optional<SourceLocation> &sourceLocation() const noexcept { + return Location; + } + + void setSourceLocation(SourceLocation NewLocation) { + Location = std::move(NewLocation); + } + + void clearSourceLocation() noexcept { + Location.reset(); + } + + [[nodiscard]] const std::optional<MachineDebugLocation> &debugLocation() const noexcept { + return DebugLocation; + } + + [[nodiscard]] Expected<void> setDebugLocation(MachineDebugLocation NewLocation); + + void clearDebugLocation() noexcept { + DebugLocation.reset(); + } + + [[nodiscard]] std::optional<OriginID> origin() const noexcept { + return Origin; + } + + void setOrigin(OriginID NewOrigin) noexcept { + Origin = NewOrigin; + } + + void clearOrigin() noexcept { + Origin.reset(); + } + + [[nodiscard]] std::uint32_t debugInstrNumber() const noexcept { + return DebugInstrNumber; + } + + [[nodiscard]] Expected<void> setDebugInstrNumber(std::uint64_t Number); + [[nodiscard]] Expected<std::uint32_t> assignDebugInstrNumber(); + + void clearDebugInstrNumber() noexcept { + DebugInstrNumber = 0; + } + + [[nodiscard]] std::optional<std::string_view> preInstrSymbol() const noexcept; + [[nodiscard]] Expected<void> setPreInstrSymbol(std::string Symbol); + void clearPreInstrSymbol() noexcept { + PreInstrSymbol.reset(); + } + + [[nodiscard]] std::optional<std::string_view> postInstrSymbol() const noexcept; + [[nodiscard]] Expected<void> setPostInstrSymbol(std::string Symbol); + void clearPostInstrSymbol() noexcept { + PostInstrSymbol.reset(); + } + + [[nodiscard]] const std::optional<MachineMetadataReference> &heapAllocMarker() const noexcept { + return HeapAllocMarker; + } + [[nodiscard]] Expected<void> setHeapAllocMarker(MachineMetadataReference Marker); + void clearHeapAllocMarker() noexcept { + HeapAllocMarker.reset(); + } + + [[nodiscard]] const std::optional<MachineMetadataReference> &pcSections() const noexcept { + return PCSections; + } + [[nodiscard]] Expected<void> setPCSections(MachineMetadataReference Sections); + void clearPCSections() noexcept { + PCSections.reset(); + } + + [[nodiscard]] const std::optional<MachineMetadataReference> &mmra() const noexcept { + return MMRA; + } + [[nodiscard]] Expected<void> setMMRA(MachineMetadataReference Metadata); + void clearMMRA() noexcept { + MMRA.reset(); + } + + [[nodiscard]] std::optional<std::uint32_t> cfiType() const noexcept { + return CFIType; + } + [[nodiscard]] Expected<void> setCFIType(std::uint64_t Type); + void clearCFIType() noexcept { + CFIType.reset(); + } + + [[nodiscard]] std::optional<std::string_view> deactivationSymbol() const noexcept; + [[nodiscard]] Expected<void> setDeactivationSymbol(std::string Symbol); + void clearDeactivationSymbol() noexcept { + DeactivationSymbol.reset(); + } + +private: + MachineInstr(MachineFunction &OwnerFunction, Opcode InstructionOpcode, + MachineInstrStableID StableID); + + [[nodiscard]] MachineRegisterInfo *linkedRegisterInfo() noexcept; + [[nodiscard]] Expected<void> attachToBlock(MachineBasicBlock &Block); + void detachFromBlock() noexcept; + void adjustTiesForInsertion(std::size_t Index) noexcept; + void adjustTiesForErasure(std::size_t Index) noexcept; + + MachineFunction *OwnerFunction; + Opcode InstructionOpcode; + MachineInstrStableID StableID; + MachineBasicBlock *ParentBlock = nullptr; + MachineInstr *PreviousInstruction = nullptr; + MachineInstr *NextInstruction = nullptr; + std::vector<MachineOperand> Operands; + std::vector<const MachineMemOperand *> MemOperands; + MachineInstrFlags Flags; + std::optional<SourceLocation> Location; + std::optional<MachineDebugLocation> DebugLocation; + std::optional<OriginID> Origin; + std::uint32_t DebugInstrNumber = 0; + std::optional<std::string> PreInstrSymbol; + std::optional<std::string> PostInstrSymbol; + std::optional<MachineMetadataReference> HeapAllocMarker; + std::optional<MachineMetadataReference> PCSections; + std::optional<MachineMetadataReference> MMRA; + std::optional<std::uint32_t> CFIType; + std::optional<std::string> DeactivationSymbol; + + friend class MachineArena; + friend class MachineBasicBlock; + friend class MachineFunction; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_MACHINEINSTR_H diff --git a/backend/include/rcc/CodeGen/MachineJumpTableInfo.h b/backend/include/rcc/CodeGen/MachineJumpTableInfo.h new file mode 100644 index 0000000..b22ea50 --- /dev/null +++ b/backend/include/rcc/CodeGen/MachineJumpTableInfo.h @@ -0,0 +1,130 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_MACHINEJUMPTABLEINFO_H +#define RCC_CODEGEN_MACHINEJUMPTABLEINFO_H + +#include "rcc/CodeGen/MachineIRTypes.h" +#include "rcc/CodeGen/MachineMemOperand.h" +#include "rcc/Support/Error.h" + +#include <cstddef> +#include <cstdint> +#include <optional> +#include <span> +#include <utility> +#include <variant> +#include <vector> + +namespace rcc { + +class MachineBasicBlock; +class MachineFunction; + +enum class JumpTableEntryEncoding : std::uint8_t { + BlockAddress, + GPRelative64BlockAddress, + GPRelative32BlockAddress, + LabelDifference32, + LabelDifference64, + Inline, + Custom32, + Target, + Count, +}; + +struct JumpTablePlacement final { + bool InTextSection = true; + bool UniqueSection = false; + std::optional<std::uint64_t> MaximumDistance; + std::uint64_t TargetFlags = 0; + + friend bool operator==(const JumpTablePlacement &Left, const JumpTablePlacement &Right) = default; +}; + +struct MachineJumpTableDesc final { + JumpTableEntryEncoding Encoding = JumpTableEntryEncoding::BlockAddress; + std::uint8_t EntrySize = 8; + Alignment Align; + std::vector<MachineBasicBlock *> Targets; + JumpTablePlacement Placement; + std::uint32_t TargetEncoding = 0; + + friend bool operator==(const MachineJumpTableDesc &Left, + const MachineJumpTableDesc &Right) = default; +}; + +class MachineJumpTableEntry final { +public: + [[nodiscard]] JumpTableIndex index() const noexcept { + return Index; + } + + [[nodiscard]] bool isTombstone() const noexcept { + return std::holds_alternative<std::monostate>(Description); + } + + [[nodiscard]] bool isPending() const noexcept { + return std::holds_alternative<PendingDescription>(Description); + } + + [[nodiscard]] const MachineJumpTableDesc *description() const noexcept { + return std::get_if<MachineJumpTableDesc>(&Description); + } + +private: + struct PendingDescription final {}; + + MachineJumpTableEntry(JumpTableIndex Index, MachineJumpTableDesc Description) + : Index(Index), Description(std::move(Description)) {} + explicit MachineJumpTableEntry(JumpTableIndex Index) + : Index(Index), Description(PendingDescription{}) {} + + JumpTableIndex Index; + std::variant<std::monostate, PendingDescription, MachineJumpTableDesc> Description; + + friend class MachineJumpTableInfo; +}; + +class MachineJumpTableInfo final { +public: + explicit MachineJumpTableInfo(MachineFunction &Owner) noexcept : Owner(&Owner) {} + + MachineJumpTableInfo(const MachineJumpTableInfo &) = delete; + MachineJumpTableInfo &operator=(const MachineJumpTableInfo &) = delete; + MachineJumpTableInfo(MachineJumpTableInfo &&) = delete; + MachineJumpTableInfo &operator=(MachineJumpTableInfo &&) = delete; + ~MachineJumpTableInfo() = default; + + [[nodiscard]] Expected<JumpTableIndex> createTable(MachineJumpTableDesc Description); + [[nodiscard]] Expected<JumpTableIndex> reserveTable(); + [[nodiscard]] Expected<void> defineReservedTable(JumpTableIndex Index, + MachineJumpTableDesc Description); + [[nodiscard]] Expected<void> updateTable(JumpTableIndex Index, MachineJumpTableDesc Description); + [[nodiscard]] Expected<void> eraseTable(JumpTableIndex Index); + + [[nodiscard]] const MachineJumpTableEntry *table(JumpTableIndex Index) const noexcept; + [[nodiscard]] std::span<const MachineJumpTableEntry> tables() const noexcept { + return Tables; + } + + [[nodiscard]] std::size_t liveTableCount() const noexcept; + + [[nodiscard]] bool isFrozen() const noexcept { + return Frozen; + } + + [[nodiscard]] Expected<void> freeze(); + [[nodiscard]] Expected<void> verify() const; + +private: + [[nodiscard]] Expected<void> verifyDescription(const MachineJumpTableDesc &Description) const; + [[nodiscard]] Expected<MachineJumpTableEntry *> mutableTable(JumpTableIndex Index); + + MachineFunction *Owner; + std::vector<MachineJumpTableEntry> Tables; + bool Frozen = false; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_MACHINEJUMPTABLEINFO_H diff --git a/backend/include/rcc/CodeGen/MachineLICM.h b/backend/include/rcc/CodeGen/MachineLICM.h new file mode 100644 index 0000000..5efaf20 --- /dev/null +++ b/backend/include/rcc/CodeGen/MachineLICM.h @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_MACHINELICM_H +#define RCC_CODEGEN_MACHINELICM_H + +#include "rcc/CodeGen/MachinePassManager.h" + +#include <cstddef> +#include <cstdint> + +namespace rcc { + +enum class MachineCodeMotionStage : std::uint8_t { + SelectedSSA, + PostRegisterAllocation, +}; + +struct MachineLICMOptions final { + std::size_t MaximumHoistedInstructions = 10'000; + std::size_t MaximumHoistedDefinitionsPerLoop = 256; +}; + +class MachineLICMPass final : public MachineFunctionPass { +public: + explicit MachineLICMPass(MachineLICMOptions Options = {}) : Options(Options) {} + MachineLICMPass(MachineCodeMotionStage Stage, MachineLICMOptions Options = {}) + : Stage(Stage), Options(Options) {} + + [[nodiscard]] const MachinePassContract &contract() const noexcept override; + [[nodiscard]] Expected<MachinePassResult> run(MachineFunction &Function, + const TargetDescription &Target, + MachineFunctionAnalysisManager &Analyses) override; + +private: + [[nodiscard]] Expected<MachinePassResult> + runImplementation(MachineFunction &Function, const TargetDescription &Target, + MachineFunctionAnalysisManager &Analyses); + + MachineCodeMotionStage Stage = MachineCodeMotionStage::SelectedSSA; + MachineLICMOptions Options; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_MACHINELICM_H diff --git a/backend/include/rcc/CodeGen/MachineMCContext.h b/backend/include/rcc/CodeGen/MachineMCContext.h new file mode 100644 index 0000000..2464dc5 --- /dev/null +++ b/backend/include/rcc/CodeGen/MachineMCContext.h @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_MACHINEMCCONTEXT_H +#define RCC_CODEGEN_MACHINEMCCONTEXT_H + +#include "rcc/CodeGen/MachineIRTypes.h" +#include "rcc/MC/MCContext.h" +#include "rcc/MC/MCSymbol.h" +#include "rcc/Support/Error.h" + +#include <string> +#include <string_view> +#include <unordered_map> + +namespace rcc { + +class CompilationUnit; +struct GlobalObjectDesc; +class MachineBasicBlock; +class MachineFunction; + +class MachineMCContext final { +public: + explicit MachineMCContext(mc::MCContext &Context) noexcept : Context(&Context) {} + + [[nodiscard]] Expected<void> initialize(const CompilationUnit &Unit); + [[nodiscard]] Expected<void> initializeFunction(const MachineFunction &Function); + + [[nodiscard]] mc::MCSymbolRef functionSymbol(const MachineFunction &Function) const noexcept; + [[nodiscard]] mc::MCSymbolRef globalSymbol(const GlobalObjectDesc &Object) const noexcept; + [[nodiscard]] mc::MCSymbolRef blockSymbol(const MachineBasicBlock &Block) const noexcept; + [[nodiscard]] mc::MCSymbolRef constantPoolSymbol(const MachineFunction &Function, + ConstantPoolIndex Index) const noexcept; + [[nodiscard]] mc::MCSymbolRef jumpTableSymbol(const MachineFunction &Function, + JumpTableIndex Index) const noexcept; + + [[nodiscard]] Expected<mc::MCSymbolRef> symbol(std::string Name, + mc::MCSymbolKind Kind = mc::MCSymbolKind::Generic); + [[nodiscard]] Expected<mc::MCSymbolRef> blockAddressSymbol(std::string_view FunctionName, + std::string_view BlockName) const; + + [[nodiscard]] mc::MCContext &context() const noexcept { + return *Context; + } + +private: + struct FunctionIndexKey final { + const MachineFunction *Function = nullptr; + std::uint32_t Index = 0; + + friend bool operator==(FunctionIndexKey Left, FunctionIndexKey Right) noexcept = default; + }; + + struct FunctionIndexHash final { + [[nodiscard]] std::size_t operator()(FunctionIndexKey Key) const noexcept; + }; + + mc::MCContext *Context; + std::unordered_map<const MachineFunction *, mc::MCSymbolRef> FunctionSymbols; + std::unordered_map<const GlobalObjectDesc *, mc::MCSymbolRef> GlobalSymbols; + std::unordered_map<const MachineBasicBlock *, mc::MCSymbolRef> BlockSymbols; + std::unordered_map<FunctionIndexKey, mc::MCSymbolRef, FunctionIndexHash> ConstantPoolSymbols; + std::unordered_map<FunctionIndexKey, mc::MCSymbolRef, FunctionIndexHash> JumpTableSymbols; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_MACHINEMCCONTEXT_H diff --git a/backend/include/rcc/CodeGen/MachineMemOperand.h b/backend/include/rcc/CodeGen/MachineMemOperand.h new file mode 100644 index 0000000..0ddd1b5 --- /dev/null +++ b/backend/include/rcc/CodeGen/MachineMemOperand.h @@ -0,0 +1,262 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_MACHINEMEMOPERAND_H +#define RCC_CODEGEN_MACHINEMEMOPERAND_H + +#include "rcc/CodeGen/LowLevelType.h" +#include "rcc/CodeGen/MachineIRTypes.h" +#include "rcc/CodeGen/MachineMetadata.h" +#include "rcc/IR/Constants.h" + +#include <cstdint> +#include <initializer_list> +#include <optional> +#include <string> +#include <string_view> +#include <utility> +#include <variant> + +namespace rcc { + +enum class MemoryAccessFlag : std::uint8_t { + Load, + Store, + Volatile, + Invariant, + Dereferenceable, + NonTemporal, + Count, +}; + +class MemoryAccessFlags final { +public: + constexpr MemoryAccessFlags() noexcept = default; + + MemoryAccessFlags(std::initializer_list<MemoryAccessFlag> Flags) noexcept; + + [[nodiscard]] constexpr bool has(MemoryAccessFlag Flag) const noexcept { + const auto Index = static_cast<std::uint8_t>(Flag); + if (Index >= static_cast<std::uint8_t>(MemoryAccessFlag::Count)) + return false; + return (Mask & (std::uint8_t{1} << Index)) != 0; + } + + constexpr void set(MemoryAccessFlag Flag) noexcept { + const auto Index = static_cast<std::uint8_t>(Flag); + if (Index < static_cast<std::uint8_t>(MemoryAccessFlag::Count)) + Mask |= std::uint8_t{1} << Index; + } + + constexpr void clear(MemoryAccessFlag Flag) noexcept { + const auto Index = static_cast<std::uint8_t>(Flag); + if (Index < static_cast<std::uint8_t>(MemoryAccessFlag::Count)) + Mask &= static_cast<std::uint8_t>(~(std::uint8_t{1} << Index)); + } + + [[nodiscard]] constexpr std::uint8_t rawValue() const noexcept { + return Mask; + } + + friend constexpr bool operator==(MemoryAccessFlags Left, + MemoryAccessFlags Right) noexcept = default; + +private: + std::uint8_t Mask = 0; +}; + +enum class AtomicOperation : std::uint8_t { + None, + Load, + Store, + ReadModifyWrite, + CompareExchange, +}; + +enum class AtomicOrdering : std::uint8_t { + NotAtomic, + Unordered, + Monotonic, + Acquire, + Release, + AcquireRelease, + SequentiallyConsistent, +}; + +class MemorySize final { +public: + enum class Kind : std::uint8_t { + Unknown, + Fixed, + Scalable, + }; + + constexpr MemorySize() noexcept = default; + + [[nodiscard]] static Expected<MemorySize> fixed(std::uint64_t Bytes); + [[nodiscard]] static Expected<MemorySize> scalable(std::uint64_t MinimumBytes); + + [[nodiscard]] constexpr Kind kind() const noexcept { + return SizeKind; + } + + [[nodiscard]] constexpr bool isKnown() const noexcept { + return SizeKind != Kind::Unknown; + } + + [[nodiscard]] constexpr bool isScalable() const noexcept { + return SizeKind == Kind::Scalable; + } + + [[nodiscard]] constexpr std::uint64_t minimumBytes() const noexcept { + return MinimumBytes; + } + + friend constexpr bool operator==(MemorySize Left, MemorySize Right) noexcept = default; + +private: + constexpr MemorySize(Kind SizeKind, std::uint64_t MinimumBytes) noexcept + : SizeKind(SizeKind), MinimumBytes(MinimumBytes) {} + + Kind SizeKind = Kind::Unknown; + std::uint64_t MinimumBytes = 0; +}; + +struct MemoryOffsetRange final { + std::int64_t Lower = 0; + std::int64_t Upper = 0; + + [[nodiscard]] static Expected<MemoryOffsetRange> create(std::int64_t Lower, std::int64_t Upper); + + friend constexpr bool operator==(MemoryOffsetRange Left, + MemoryOffsetRange Right) noexcept = default; +}; + +class MemoryBase final { +public: + enum class Kind : std::uint8_t { + Unknown, + FrameIndex, + ConstantPool, + JumpTable, + GlobalSymbol, + ExternalSymbol, + IRValue, + IRConstant, + Stack, + GOT, + ConstantPoolPseudo, + JumpTablePseudo, + CallEntryGlobal, + CallEntryExternal, + Custom, + }; + + MemoryBase() = default; + + [[nodiscard]] static MemoryBase frameIndex(FrameIndex Index); + [[nodiscard]] static MemoryBase constantPool(ConstantPoolIndex Index); + [[nodiscard]] static MemoryBase jumpTable(JumpTableIndex Index); + [[nodiscard]] static MemoryBase globalSymbol(std::string Name); + [[nodiscard]] static MemoryBase externalSymbol(std::string Name); + [[nodiscard]] static MemoryBase irValue(std::string Name); + [[nodiscard]] static Expected<MemoryBase> irConstant(GenericConstantData Constant); + [[nodiscard]] static MemoryBase stack(); + [[nodiscard]] static MemoryBase got(); + [[nodiscard]] static MemoryBase constantPoolPseudo(); + [[nodiscard]] static MemoryBase jumpTablePseudo(); + [[nodiscard]] static MemoryBase callEntryGlobal(std::string Name); + [[nodiscard]] static MemoryBase callEntryExternal(std::string Name); + [[nodiscard]] static MemoryBase custom(std::string Name); + + [[nodiscard]] Kind kind() const noexcept { + return BaseKind; + } + + [[nodiscard]] std::optional<FrameIndex> frameIndex() const noexcept; + [[nodiscard]] std::optional<ConstantPoolIndex> constantPoolIndex() const noexcept; + [[nodiscard]] std::optional<JumpTableIndex> jumpTableIndex() const noexcept; + [[nodiscard]] std::optional<std::string_view> symbolName() const noexcept; + [[nodiscard]] const GenericConstantData *constantValue() const noexcept; + + friend bool operator==(const MemoryBase &Left, const MemoryBase &Right) = default; + +private: + using Storage = std::variant<std::monostate, FrameIndex, ConstantPoolIndex, JumpTableIndex, + std::string, GenericConstantData>; + + MemoryBase(Kind BaseKind, Storage Value) : BaseKind(BaseKind), Value(std::move(Value)) {} + + Kind BaseKind = Kind::Unknown; + Storage Value; +}; + +struct MachineMemOperandDesc final { + MemoryAccessFlags Accesses; + AtomicOperation AtomicKind = AtomicOperation::None; + AtomicOrdering SuccessOrdering = AtomicOrdering::NotAtomic; + AtomicOrdering FailureOrdering = AtomicOrdering::NotAtomic; + SyncScopeID SyncScope; + MemorySize Size; + std::optional<LowLevelType> MemoryType; + Alignment Align; + std::optional<Alignment> BaseAlign; + std::uint32_t AddressSpace = 0; + MemoryBase Base; + std::int64_t Offset = 0; + std::optional<MemoryOffsetRange> OffsetRange; + std::optional<MemoryAliasSourceID> AliasSource; + std::optional<MachineMetadataReference> TBAA; + std::optional<MachineMetadataReference> AliasScope; + std::optional<MachineMetadataReference> NoAlias; + std::optional<MachineMetadataReference> NoAliasAddressSpace; + std::optional<MachineMetadataReference> RangeMetadata; + std::uint16_t TargetFlags = 0; + + friend bool operator==(const MachineMemOperandDesc &Left, + const MachineMemOperandDesc &Right) = default; +}; + +class MachineMemOperand final { +public: + MachineMemOperand(const MachineMemOperand &) = default; + MachineMemOperand &operator=(const MachineMemOperand &) = default; + MachineMemOperand(MachineMemOperand &&) noexcept = default; + MachineMemOperand &operator=(MachineMemOperand &&) noexcept = default; + ~MachineMemOperand() = default; + + [[nodiscard]] static Expected<MachineMemOperand> create(MachineMemOperandDesc Description); + + [[nodiscard]] const MachineMemOperandDesc &description() const noexcept { + return Description; + } + + [[nodiscard]] bool isLoad() const noexcept { + return Description.Accesses.has(MemoryAccessFlag::Load); + } + + [[nodiscard]] bool isStore() const noexcept { + return Description.Accesses.has(MemoryAccessFlag::Store); + } + + [[nodiscard]] bool isAtomic() const noexcept { + return Description.AtomicKind != AtomicOperation::None; + } + + [[nodiscard]] bool isVolatile() const noexcept { + return Description.Accesses.has(MemoryAccessFlag::Volatile); + } + + friend bool operator==(const MachineMemOperand &Left, const MachineMemOperand &Right) = default; + +private: + explicit MachineMemOperand(MachineMemOperandDesc Description) + : Description(std::move(Description)) {} + + MachineMemOperandDesc Description; +}; + +[[nodiscard]] std::string_view getAtomicOrderingName(AtomicOrdering Ordering) noexcept; + +} // namespace rcc + +#endif // RCC_CODEGEN_MACHINEMEMOPERAND_H diff --git a/backend/include/rcc/CodeGen/MachineMemoryFolding.h b/backend/include/rcc/CodeGen/MachineMemoryFolding.h new file mode 100644 index 0000000..9f415d7 --- /dev/null +++ b/backend/include/rcc/CodeGen/MachineMemoryFolding.h @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_MACHINEMEMORYFOLDING_H +#define RCC_CODEGEN_MACHINEMEMORYFOLDING_H + +#include "rcc/CodeGen/MachinePassManager.h" + +#include <cstddef> + +namespace rcc { + +class TargetMemoryFoldingInfo; + +struct MachineMemoryFoldingOptions final { + std::size_t MaximumFolds = 4'096; +}; + +class MachineMemoryFoldingPass final : public MachineFunctionPass { +public: + explicit MachineMemoryFoldingPass(const TargetMemoryFoldingInfo &Info, + MachineMemoryFoldingOptions Options = {}) noexcept + : Info(&Info), Options(Options) {} + + [[nodiscard]] const MachinePassContract &contract() const noexcept override; + [[nodiscard]] Expected<MachinePassResult> run(MachineFunction &Function, + const TargetDescription &Target, + MachineFunctionAnalysisManager &Analyses) override; + +private: + [[nodiscard]] Expected<MachinePassResult> + runImplementation(MachineFunction &Function, const TargetDescription &Target) const; + + const TargetMemoryFoldingInfo *Info; + MachineMemoryFoldingOptions Options; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_MACHINEMEMORYFOLDING_H diff --git a/backend/include/rcc/CodeGen/MachineMetadata.h b/backend/include/rcc/CodeGen/MachineMetadata.h new file mode 100644 index 0000000..df56b76 --- /dev/null +++ b/backend/include/rcc/CodeGen/MachineMetadata.h @@ -0,0 +1,144 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_MACHINEMETADATA_H +#define RCC_CODEGEN_MACHINEMETADATA_H + +#include "rcc/Support/Error.h" + +#include <cstddef> +#include <cstdint> +#include <span> +#include <string> +#include <unordered_map> +#include <utility> +#include <variant> +#include <vector> + +namespace rcc { + +enum class MachineMetadataScope : std::uint8_t { + EmbeddedIR, + MachineFunction, +}; + +class MachineMetadataReference final { +public: + constexpr MachineMetadataReference() noexcept = default; + + [[nodiscard]] static constexpr MachineMetadataReference embeddedIR(std::uint32_t Slot) noexcept { + return MachineMetadataReference(MachineMetadataScope::EmbeddedIR, Slot); + } + + [[nodiscard]] static constexpr MachineMetadataReference + machineFunction(std::uint32_t Slot) noexcept { + return MachineMetadataReference(MachineMetadataScope::MachineFunction, Slot); + } + + [[nodiscard]] constexpr bool isValid() const noexcept { + return Valid; + } + + [[nodiscard]] constexpr MachineMetadataScope scope() const noexcept { + return Scope; + } + + [[nodiscard]] constexpr std::uint32_t slot() const noexcept { + return Slot; + } + + friend constexpr bool operator==(MachineMetadataReference Left, + MachineMetadataReference Right) noexcept = default; + +private: + constexpr MachineMetadataReference(MachineMetadataScope Scope, std::uint32_t Slot) noexcept + : Scope(Scope), Slot(Slot), Valid(true) {} + + MachineMetadataScope Scope = MachineMetadataScope::EmbeddedIR; + std::uint32_t Slot = 0; + bool Valid = false; +}; + +class MachineMetadataElement final { +public: + MachineMetadataElement() = default; + + struct NullValue final { + friend constexpr bool operator==(NullValue Left, NullValue Right) noexcept = default; + }; + + [[nodiscard]] static MachineMetadataElement null(); + [[nodiscard]] static Expected<MachineMetadataElement> + reference(MachineMetadataReference Reference); + [[nodiscard]] static MachineMetadataElement string(std::string Value); + + [[nodiscard]] bool isValid() const noexcept { + return !std::holds_alternative<std::monostate>(Storage); + } + + [[nodiscard]] const MachineMetadataReference *reference() const noexcept { + return std::get_if<MachineMetadataReference>(&Storage); + } + + [[nodiscard]] const std::string *string() const noexcept { + return std::get_if<std::string>(&Storage); + } + + [[nodiscard]] bool isNull() const noexcept { + return std::holds_alternative<NullValue>(Storage); + } + + friend bool operator==(const MachineMetadataElement &Left, + const MachineMetadataElement &Right) = default; + +private: + explicit MachineMetadataElement(NullValue Value) : Storage(Value) {} + explicit MachineMetadataElement(MachineMetadataReference Reference) : Storage(Reference) {} + explicit MachineMetadataElement(std::string Value) : Storage(std::move(Value)) {} + + std::variant<std::monostate, NullValue, MachineMetadataReference, std::string> Storage; +}; + +struct MachineMetadataNode final { + std::uint32_t Slot = 0; + bool IsDistinct = false; + std::vector<MachineMetadataElement> Elements; + + friend bool operator==(const MachineMetadataNode &Left, + const MachineMetadataNode &Right) = default; +}; + +class MachineMetadataInfo final { +public: + MachineMetadataInfo() = default; + MachineMetadataInfo(const MachineMetadataInfo &) = delete; + MachineMetadataInfo &operator=(const MachineMetadataInfo &) = delete; + MachineMetadataInfo(MachineMetadataInfo &&) noexcept = default; + MachineMetadataInfo &operator=(MachineMetadataInfo &&) noexcept = default; + ~MachineMetadataInfo() = default; + + [[nodiscard]] Expected<void> addNode(MachineMetadataNode Node); + + [[nodiscard]] const MachineMetadataNode *node(std::uint32_t Slot) const noexcept; + + [[nodiscard]] std::span<const MachineMetadataNode> nodes() const noexcept { + return Nodes; + } + + [[nodiscard]] Expected<void> verify() const; + [[nodiscard]] Expected<MachineMetadataInfo> clone() const; + + friend bool operator==(const MachineMetadataInfo &Left, const MachineMetadataInfo &Right) { + return Left.Nodes == Right.Nodes; + } + +private: + std::vector<MachineMetadataNode> Nodes; + std::unordered_map<std::uint32_t, std::size_t> SlotToIndex; +}; + +[[nodiscard]] Expected<void> verifyMachineMetadataReference(MachineMetadataReference Reference, + const MachineMetadataInfo &Metadata); + +} // namespace rcc + +#endif // RCC_CODEGEN_MACHINEMETADATA_H diff --git a/backend/include/rcc/CodeGen/MachineOperand.h b/backend/include/rcc/CodeGen/MachineOperand.h new file mode 100644 index 0000000..fecb4e0 --- /dev/null +++ b/backend/include/rcc/CodeGen/MachineOperand.h @@ -0,0 +1,463 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_MACHINEOPERAND_H +#define RCC_CODEGEN_MACHINEOPERAND_H + +#include "rcc/ADT/APFloat.h" +#include "rcc/ADT/APInt.h" +#include "rcc/CodeGen/GlobalObject.h" +#include "rcc/CodeGen/LowLevelType.h" +#include "rcc/CodeGen/MachineDebugInfo.h" +#include "rcc/CodeGen/MachineIRTypes.h" +#include "rcc/CodeGen/Register.h" +#include "rcc/CodeGen/RegisterMask.h" +#include "rcc/Support/Error.h" + +#include <cstddef> +#include <cstdint> +#include <initializer_list> +#include <memory> +#include <optional> +#include <span> +#include <string> +#include <string_view> +#include <utility> +#include <variant> +#include <vector> + +namespace rcc { + +class MachineBasicBlock; +class MachineInstr; +class MachineRegisterInfo; +class RegOperandIterator; + +enum class RegisterOperandFlag : std::uint8_t { + Define, + Use, + Implicit, + Kill, + Dead, + Undef, + EarlyClobber, + InternalRead, + Renamable, + Debug, + Count, +}; + +class RegisterOperandFlags final { +public: + constexpr RegisterOperandFlags() noexcept = default; + + RegisterOperandFlags(std::initializer_list<RegisterOperandFlag> Flags) noexcept; + + [[nodiscard]] constexpr bool has(RegisterOperandFlag Flag) const noexcept { + const auto Index = static_cast<std::uint16_t>(Flag); + if (Index >= static_cast<std::uint16_t>(RegisterOperandFlag::Count)) + return false; + return (Mask & (std::uint16_t{1} << Index)) != 0; + } + + constexpr void set(RegisterOperandFlag Flag) noexcept { + const auto Index = static_cast<std::uint16_t>(Flag); + if (Index < static_cast<std::uint16_t>(RegisterOperandFlag::Count)) + Mask |= std::uint16_t{1} << Index; + } + + constexpr void clear(RegisterOperandFlag Flag) noexcept { + const auto Index = static_cast<std::uint16_t>(Flag); + if (Index < static_cast<std::uint16_t>(RegisterOperandFlag::Count)) + Mask &= static_cast<std::uint16_t>(~(std::uint16_t{1} << Index)); + } + + [[nodiscard]] constexpr std::uint16_t rawValue() const noexcept { + return Mask; + } + + friend constexpr bool operator==(RegisterOperandFlags Left, + RegisterOperandFlags Right) noexcept = default; + +private: + std::uint16_t Mask = 0; +}; + +struct FloatingImmediateOperand final { + APFloat Bits; + + friend bool operator==(const FloatingImmediateOperand &Left, + const FloatingImmediateOperand &Right) = default; +}; + +struct IntegerImmediateOperand final { + APInt Value; + + friend bool operator==(const IntegerImmediateOperand &Left, + const IntegerImmediateOperand &Right) = default; +}; + +struct BasicBlockOperand final { + MachineBasicBlock *Block = nullptr; + + friend bool operator==(BasicBlockOperand Left, BasicBlockOperand Right) noexcept = default; +}; + +struct FrameIndexOperand final { + FrameIndex Index; + std::int64_t Offset = 0; + + friend constexpr bool operator==(FrameIndexOperand Left, + FrameIndexOperand Right) noexcept = default; +}; + +struct ConstantPoolOperand final { + ConstantPoolIndex Index; + std::int64_t Offset = 0; + + friend constexpr bool operator==(ConstantPoolOperand Left, + ConstantPoolOperand Right) noexcept = default; +}; + +struct JumpTableOperand final { + JumpTableIndex Index; + + friend constexpr bool operator==(JumpTableOperand Left, + JumpTableOperand Right) noexcept = default; +}; + +struct GlobalSymbolOperand final { + std::string Name; + std::int64_t Offset = 0; + GlobalValueProperties Properties; + + friend bool operator==(const GlobalSymbolOperand &Left, + const GlobalSymbolOperand &Right) = default; +}; + +struct ExternalSymbolOperand final { + std::string Name; + std::int64_t Offset = 0; + + friend bool operator==(const ExternalSymbolOperand &Left, + const ExternalSymbolOperand &Right) = default; +}; + +struct BlockAddressOperand final { + std::string FunctionName; + std::string BlockName; + std::int64_t Offset = 0; + + friend bool operator==(const BlockAddressOperand &Left, + const BlockAddressOperand &Right) = default; +}; + +struct RegisterMaskOperand final { + std::shared_ptr<const RegisterMask> Mask; + + friend bool operator==(const RegisterMaskOperand &Left, + const RegisterMaskOperand &Right) = default; +}; + +struct LiveOutRegisterMaskOperand final { + std::shared_ptr<const RegisterMask> Mask; + + friend bool operator==(const LiveOutRegisterMaskOperand &Left, + const LiveOutRegisterMaskOperand &Right) = default; +}; + +struct MetadataOperand final { + MachineMetadataValue Value; + + friend bool operator==(const MetadataOperand &Left, const MetadataOperand &Right) = default; +}; + +struct TargetIndexOperand final { + TargetIndex Index; + std::int64_t Offset = 0; + + friend constexpr bool operator==(TargetIndexOperand Left, + TargetIndexOperand Right) noexcept = default; +}; + +struct MCSymbolOperand final { + std::string Name; + std::int64_t Offset = 0; + + friend bool operator==(const MCSymbolOperand &Left, const MCSymbolOperand &Right) = default; +}; + +struct CFIIndexOperand final { + CFIInstructionIndex Index; + + friend constexpr bool operator==(CFIIndexOperand Left, CFIIndexOperand Right) noexcept = default; +}; + +struct IntrinsicOperand final { + IntrinsicID ID; + + friend constexpr bool operator==(IntrinsicOperand Left, + IntrinsicOperand Right) noexcept = default; +}; + +enum class IntegerPredicate : std::uint8_t { + Equal, + NotEqual, + SignedGreaterThan, + SignedGreaterEqual, + SignedLessThan, + SignedLessEqual, + UnsignedGreaterThan, + UnsignedGreaterEqual, + UnsignedLessThan, + UnsignedLessEqual, + Count, +}; + +enum class FloatingPredicate : std::uint8_t { + AlwaysFalse, + OrderedEqual, + OrderedGreaterThan, + OrderedGreaterEqual, + OrderedLessThan, + OrderedLessEqual, + OrderedNotEqual, + Ordered, + Unordered, + UnorderedEqual, + UnorderedGreaterThan, + UnorderedGreaterEqual, + UnorderedLessThan, + UnorderedLessEqual, + UnorderedNotEqual, + AlwaysTrue, + Count, +}; + +class PredicateOperand final { +public: + explicit PredicateOperand(IntegerPredicate Predicate) : Value(Predicate) {} + explicit PredicateOperand(FloatingPredicate Predicate) : Value(Predicate) {} + + [[nodiscard]] bool isInteger() const noexcept { + return std::holds_alternative<IntegerPredicate>(Value); + } + + [[nodiscard]] const IntegerPredicate *integerPredicate() const noexcept { + return std::get_if<IntegerPredicate>(&Value); + } + + [[nodiscard]] const FloatingPredicate *floatingPredicate() const noexcept { + return std::get_if<FloatingPredicate>(&Value); + } + + [[nodiscard]] Expected<void> verify() const; + + friend bool operator==(const PredicateOperand &Left, const PredicateOperand &Right) = default; + +private: + std::variant<IntegerPredicate, FloatingPredicate> Value; +}; + +struct ShuffleMaskOperand final { + static constexpr std::int32_t Undef = -1; + + std::vector<std::int32_t> Elements; + + friend bool operator==(const ShuffleMaskOperand &Left, const ShuffleMaskOperand &Right) = default; +}; + +struct DebugInstrRefOperand final { + std::uint32_t InstructionNumber = 0; + std::uint32_t OperandNumber = 0; + + friend constexpr bool operator==(DebugInstrRefOperand Left, + DebugInstrRefOperand Right) noexcept = default; +}; + +struct LaneMaskOperand final { + LaneMask Mask; + + friend constexpr bool operator==(LaneMaskOperand Left, LaneMaskOperand Right) noexcept = default; +}; + +class MachineOperand final { +public: + enum class Kind : std::uint8_t { + Register, + Immediate, + IntegerImmediate, + FloatingImmediate, + BasicBlock, + FrameIndex, + ConstantPoolIndex, + JumpTableIndex, + GlobalSymbol, + ExternalSymbol, + BlockAddress, + RegisterMask, + LiveOutRegisterMask, + Metadata, + TargetIndex, + MCSymbol, + CFIIndex, + Intrinsic, + Predicate, + ShuffleMask, + DebugInstrRef, + LaneMask, + Count, + }; + + MachineOperand(const MachineOperand &) = delete; + MachineOperand &operator=(const MachineOperand &) = delete; + MachineOperand(MachineOperand &&Other) noexcept; + MachineOperand &operator=(MachineOperand &&Other) noexcept; + ~MachineOperand(); + + [[nodiscard]] static Expected<MachineOperand> registerOperand(Register Reg, + RegisterOperandFlags Flags, + SubRegIndex SubReg = {}, + std::uint32_t TargetFlags = 0); + [[nodiscard]] static MachineOperand immediate(std::int64_t Value, std::uint32_t TargetFlags = 0); + [[nodiscard]] static MachineOperand integerImmediate(APInt Value, std::uint32_t TargetFlags = 0); + [[nodiscard]] static MachineOperand floatingImmediate(APFloat Bits, + std::uint32_t TargetFlags = 0); + [[nodiscard]] static MachineOperand basicBlock(MachineBasicBlock &Block, + std::uint32_t TargetFlags = 0); + [[nodiscard]] static MachineOperand frameIndex(FrameIndex Index, std::int64_t Offset = 0, + std::uint32_t TargetFlags = 0); + [[nodiscard]] static MachineOperand constantPool(ConstantPoolIndex Index, std::int64_t Offset = 0, + std::uint32_t TargetFlags = 0); + [[nodiscard]] static MachineOperand jumpTable(JumpTableIndex Index, + std::uint32_t TargetFlags = 0); + [[nodiscard]] static Expected<MachineOperand> globalSymbol(std::string Name, + std::int64_t Offset = 0, + std::uint32_t TargetFlags = 0, + GlobalValueProperties Properties = {}); + [[nodiscard]] static Expected<MachineOperand> + externalSymbol(std::string Name, std::int64_t Offset = 0, std::uint32_t TargetFlags = 0); + [[nodiscard]] static Expected<MachineOperand> blockAddress(std::string FunctionName, + std::string BlockName, + std::int64_t Offset = 0, + std::uint32_t TargetFlags = 0); + [[nodiscard]] static Expected<MachineOperand> + registerMask(std::shared_ptr<const RegisterMask> Mask, std::uint32_t TargetFlags = 0); + [[nodiscard]] static Expected<MachineOperand> + liveOutRegisterMask(std::shared_ptr<const RegisterMask> Mask, std::uint32_t TargetFlags = 0); + [[nodiscard]] static Expected<MachineOperand> metadata(MachineMetadataValue Value, + std::uint32_t TargetFlags = 0); + [[nodiscard]] static MachineOperand targetIndex(TargetIndex Index, std::int64_t Offset = 0, + std::uint32_t TargetFlags = 0); + [[nodiscard]] static Expected<MachineOperand> mcSymbol(std::string Name, std::int64_t Offset = 0, + std::uint32_t TargetFlags = 0); + [[nodiscard]] static MachineOperand cfiIndex(CFIInstructionIndex Index, + std::uint32_t TargetFlags = 0); + [[nodiscard]] static Expected<MachineOperand> intrinsic(IntrinsicID ID, + std::uint32_t TargetFlags = 0); + [[nodiscard]] static Expected<MachineOperand> integerPredicate(IntegerPredicate Predicate, + std::uint32_t TargetFlags = 0); + [[nodiscard]] static Expected<MachineOperand> floatingPredicate(FloatingPredicate Predicate, + std::uint32_t TargetFlags = 0); + [[nodiscard]] static Expected<MachineOperand> shuffleMask(std::vector<std::int32_t> Elements, + std::uint32_t TargetFlags = 0); + [[nodiscard]] static Expected<MachineOperand> debugInstrRef(std::uint64_t InstructionNumber, + std::uint64_t OperandNumber, + std::uint32_t TargetFlags = 0); + [[nodiscard]] static MachineOperand laneMask(LaneMask Mask, std::uint32_t TargetFlags = 0); + + [[nodiscard]] Kind kind() const noexcept { + return OperandKind; + } + + [[nodiscard]] bool isRegister() const noexcept { + return OperandKind == Kind::Register; + } + + [[nodiscard]] std::optional<Register> registerValue() const noexcept; + [[nodiscard]] RegisterOperandFlags registerFlags() const noexcept { + return RegFlags; + } + [[nodiscard]] SubRegIndex subReg() const noexcept { + return RegSubReg; + } + [[nodiscard]] bool isDef() const noexcept; + [[nodiscard]] bool isUse() const noexcept; + [[nodiscard]] bool isDebugUse() const noexcept; + [[nodiscard]] bool isImplicit() const noexcept; + + [[nodiscard]] Expected<void> setRegister(Register Reg); + [[nodiscard]] Expected<void> changeToImmediate(std::int64_t Value); + [[nodiscard]] Expected<void> setImmediate(std::int64_t Value); + [[nodiscard]] Expected<void> setIntegerImmediate(APInt Value); + [[nodiscard]] Expected<void> setRegisterFlags(RegisterOperandFlags Flags); + [[nodiscard]] Expected<void> setSubReg(SubRegIndex SubReg); + [[nodiscard]] Expected<void> setBasicBlock(MachineBasicBlock &Block); + [[nodiscard]] Expected<void> setFrameIndex(FrameIndex Index); + + [[nodiscard]] std::optional<std::size_t> tiedOperandIndex() const noexcept { + return TiedOperandIndex; + } + + [[nodiscard]] std::uint32_t targetFlags() const noexcept { + return TargetFlags; + } + + void setTargetFlags(std::uint32_t NewTargetFlags) noexcept { + TargetFlags = NewTargetFlags; + } + + [[nodiscard]] MachineInstr *parent() noexcept { + return Parent; + } + + [[nodiscard]] const MachineInstr *parent() const noexcept { + return Parent; + } + + template <typename PayloadT> [[nodiscard]] const PayloadT *getIf() const noexcept { + return std::get_if<PayloadT>(&Storage); + } + + [[nodiscard]] MachineOperand cloneDetached() const; + +private: + using OperandStorage = + std::variant<Register, std::int64_t, IntegerImmediateOperand, FloatingImmediateOperand, + BasicBlockOperand, FrameIndexOperand, ConstantPoolOperand, JumpTableOperand, + GlobalSymbolOperand, ExternalSymbolOperand, BlockAddressOperand, + RegisterMaskOperand, LiveOutRegisterMaskOperand, MetadataOperand, + TargetIndexOperand, MCSymbolOperand, CFIIndexOperand, IntrinsicOperand, + PredicateOperand, ShuffleMaskOperand, DebugInstrRefOperand, LaneMaskOperand>; + + MachineOperand(Kind OperandKind, OperandStorage Storage, std::uint32_t TargetFlags = 0) + : OperandKind(OperandKind), Storage(std::move(Storage)), TargetFlags(TargetFlags) {} + + [[nodiscard]] static Expected<void> verifyRegisterFlags(RegisterOperandFlags Flags); + void attachTo(MachineInstr &Instruction, MachineRegisterInfo *RegInfo); + void detachFromInstruction() noexcept; + void linkToRegInfo(MachineRegisterInfo &RegInfo); + void unlinkFromRegInfo() noexcept; + void takeRegLinkFrom(MachineOperand &Other) noexcept; + void setTiedOperandIndex(std::optional<std::size_t> Index) noexcept { + TiedOperandIndex = Index; + } + + Kind OperandKind; + OperandStorage Storage; + RegisterOperandFlags RegFlags; + SubRegIndex RegSubReg; + std::optional<std::size_t> TiedOperandIndex; + std::uint32_t TargetFlags = 0; + MachineInstr *Parent = nullptr; + MachineRegisterInfo *LinkedRegInfo = nullptr; + MachineOperand **PreviousRegLink = nullptr; + MachineOperand *NextRegOperand = nullptr; + + friend class MachineInstr; + friend class MachineRegisterInfo; + friend class RegOperandIterator; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_MACHINEOPERAND_H diff --git a/backend/include/rcc/CodeGen/MachineOptimizationPipeline.h b/backend/include/rcc/CodeGen/MachineOptimizationPipeline.h new file mode 100644 index 0000000..c47fc48 --- /dev/null +++ b/backend/include/rcc/CodeGen/MachineOptimizationPipeline.h @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_MACHINEOPTIMIZATIONPIPELINE_H +#define RCC_CODEGEN_MACHINEOPTIMIZATIONPIPELINE_H + +#include "rcc/CodeGen/MachinePassManager.h" +#include "rcc/CodeGen/MachineScheduler.h" +#include "rcc/CodeGen/TargetFrameLowering.h" + +#include <memory> + +namespace rcc { + +class TargetInstrInfo; +class TargetFrameLowering; +class TargetMachineCombinerInfo; +class TargetMemoryFoldingInfo; +class TargetPseudoExpansionInfo; +class TargetSchedulingModel; + +struct MachineOptimizationPipelineTargetInfo final { + const TargetInstrInfo &InstrInfo; + const TargetMachineCombinerInfo &Combiner; + const TargetMemoryFoldingInfo &MemoryFolding; + const TargetFrameLowering &FrameLowering; + const TargetPseudoExpansionInfo &PseudoExpansion; + const TargetSchedulingModel &SchedulingModel; + MachineSchedulerOptions SchedulerOptions; + FrameLoweringOptions FrameOptions; +}; + +struct MachineOptimizationPipelineOptions final { + bool UseTargetCopyInstructions = false; +}; + +[[nodiscard]] Expected<void> +registerMachineOptimizationAnalyses(MachineFunctionAnalysisManager &Analyses); + +[[nodiscard]] Expected<void> +addSelectedSSAOptimizationPipeline(MachineFunctionPassManager &Pipeline, + const MachineOptimizationPipelineTargetInfo &TargetInfo); + +[[nodiscard]] Expected<void> addPreRegisterAllocationOptimizationPipeline( + MachineFunctionPassManager &Pipeline, const MachineOptimizationPipelineTargetInfo &TargetInfo); + +[[nodiscard]] Expected<void> addPostRegisterAllocationPipeline( + MachineFunctionPassManager &Pipeline, const MachineOptimizationPipelineTargetInfo &TargetInfo, + MachineOptimizationPipelineOptions Options = {}, + MachineFunctionPassManager::PassList PostRAPseudoExpansionTargetPasses = {}); + +} // namespace rcc + +#endif // RCC_CODEGEN_MACHINEOPTIMIZATIONPIPELINE_H diff --git a/backend/include/rcc/CodeGen/MachineOutliner.h b/backend/include/rcc/CodeGen/MachineOutliner.h new file mode 100644 index 0000000..a12ddaf --- /dev/null +++ b/backend/include/rcc/CodeGen/MachineOutliner.h @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_MACHINEOUTLINER_H +#define RCC_CODEGEN_MACHINEOUTLINER_H + +#include "rcc/CodeGen/MachinePassManager.h" +#include "rcc/CodeGen/Register.h" +#include "rcc/Support/Error.h" + +#include <cstdint> +#include <memory> +#include <string> +#include <unordered_map> +#include <vector> + +namespace rcc { + +class MachineBasicBlock; +class MachineFunction; +class MachineInstr; +class TargetDescription; + +enum class MachineOutlinerInstrType : std::uint8_t { + Legal, + LegalTerminator, + Illegal, + Invisible, +}; + +struct MachineOutlinerFunctionProperties final { + bool LinkOnceODR = false; + bool HasSection = false; + bool HasComdat = false; + bool HasSectionPrefix = false; +}; + +using MachineOutlinerFunctionPropertyMap = + std::unordered_map<std::string, MachineOutlinerFunctionProperties>; + +struct MachineOutlinerOptions final { + MachineOutlinerFunctionPropertyMap FunctionProperties; +}; + +struct MachineOutlinerCandidate final { + MachineFunction *Function = nullptr; + MachineBasicBlock *Block = nullptr; + std::vector<MachineInstr *> Instructions; + std::vector<PhysicalRegister> LiveIns; + std::vector<PhysicalRegister> DefinedRegisters; + std::uint32_t CallConstructionID = 0; + std::uint32_t CallOverhead = 0; + PhysicalRegister SaveRegister; + + [[nodiscard]] MachineInstr &front() const noexcept; + [[nodiscard]] MachineInstr &back() const noexcept; + [[nodiscard]] bool containsRegister(PhysicalRegister Reg, + const TargetDescription &Target) const noexcept; + [[nodiscard]] bool isRegisterLiveAfter(PhysicalRegister Reg, + const TargetDescription &Target) const noexcept; + [[nodiscard]] bool isRegisterAvailable(PhysicalRegister Reg, + const TargetDescription &Target) const noexcept; +}; + +class TargetMachineOutlinerInfo { +public: + virtual ~TargetMachineOutlinerInfo(); + + [[nodiscard]] virtual bool isFunctionSafeToOutlineFrom( + const MachineFunction &Function, + const MachineOutlinerFunctionProperties &Properties) const noexcept = 0; + [[nodiscard]] virtual MachineOutlinerInstrType + getOutliningType(const MachineInstr &Instruction, + const MachineOutlinerFunctionProperties &Properties, + const TargetDescription &Target) const noexcept = 0; + [[nodiscard]] virtual Expected<bool> analyzeCandidate(MachineOutlinerCandidate &Candidate, + const TargetDescription &Target) const = 0; + [[nodiscard]] virtual Expected<std::uint32_t> + instructionSize(const MachineInstr &Instruction, const MachineFunction &Function, + const TargetDescription &Target) const = 0; + [[nodiscard]] virtual std::uint32_t + frameOverhead(const MachineOutlinerCandidate &Candidate) const noexcept = 0; + [[nodiscard]] virtual Expected<void> + insertOutlinedCall(MachineOutlinerCandidate &Candidate, std::string OutlinedFunctionName, + const TargetDescription &Target) const = 0; + [[nodiscard]] virtual Expected<void> + buildOutlinedFrame(MachineFunction &Function, MachineBasicBlock &Block, + const MachineOutlinerCandidate &Candidate, + const TargetDescription &Target) const = 0; +}; + +[[nodiscard]] std::unique_ptr<CompilationUnitPass> +createMachineOutlinerPass(std::unique_ptr<TargetMachineOutlinerInfo> TargetInfo, + MachineOutlinerOptions Options = {}); + +} // namespace rcc + +#endif // RCC_CODEGEN_MACHINEOUTLINER_H diff --git a/backend/include/rcc/CodeGen/MachinePassInstrumentation.h b/backend/include/rcc/CodeGen/MachinePassInstrumentation.h new file mode 100644 index 0000000..9cc5098 --- /dev/null +++ b/backend/include/rcc/CodeGen/MachinePassInstrumentation.h @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_MACHINEPASSINSTRUMENTATION_H +#define RCC_CODEGEN_MACHINEPASSINSTRUMENTATION_H + +#include "rcc/CodeGen/MachineFunctionAnalysisManager.h" +#include "rcc/IR/PassInstrumentation.h" + +#include <cstdint> +#include <functional> +#include <mutex> +#include <optional> +#include <string> +#include <string_view> +#include <unordered_map> +#include <vector> + +namespace rcc { + +class CompilationUnit; +class MachineFunction; +struct MachinePassResult; +struct CompilationUnitPassResult; + +struct InstrumentationStatistic final { + std::string Name; + bool IsAnalysis = false; + std::uint64_t Invocations = 0; + std::uint64_t CacheHits = 0; + std::uint64_t Changes = 0; + std::uint64_t Failures = 0; + std::uint64_t TotalNanoseconds = 0; +}; + +struct MachineActionID final { + std::string Pass; + std::string Rule; + MachineFunctionStableID Function; + MachineInstrStableID Instruction; + + [[nodiscard]] std::string toString() const; + + friend bool operator==(const MachineActionID &, const MachineActionID &) = default; +}; + +struct MachineActionEvent final { + MachineActionID ID; + std::uint64_t Cost = 0; + bool Accepted = false; + + friend bool operator==(const MachineActionEvent &, const MachineActionEvent &) = default; +}; + +class MachinePassInstrumentation final { + using FunctionCallbackRegistry = PassInstrumentationCallbacks<MachineFunction, MachinePassResult>; + using UnitCallbackRegistry = + PassInstrumentationCallbacks<CompilationUnit, CompilationUnitPassResult>; + +public: + using BeforeCallback = FunctionCallbackRegistry::BeforeCallback; + using AfterCallback = FunctionCallbackRegistry::AfterCallback; + using FailureCallback = FunctionCallbackRegistry::FailureCallback; + using BeforeAnalysisCallback = FunctionCallbackRegistry::BeforeAnalysisCallback; + using AfterAnalysisCallback = FunctionCallbackRegistry::AfterAnalysisCallback; + using AnalysisFailureCallback = FunctionCallbackRegistry::AnalysisFailureCallback; + using IRPrintCallback = + std::function<void(InstrumentationPhase, std::string_view, const MachineFunction &)>; + using ChangeCallback = + std::function<void(std::string_view, const MachineFunction &, MachinePassChanges, bool)>; + using ActionFilter = std::function<bool(const MachineActionID &, std::uint64_t)>; + + using BeforeUnitCallback = UnitCallbackRegistry::BeforeCallback; + using AfterUnitCallback = UnitCallbackRegistry::AfterCallback; + using UnitFailureCallback = UnitCallbackRegistry::FailureCallback; + using BeforeUnitAnalysisCallback = UnitCallbackRegistry::BeforeAnalysisCallback; + using AfterUnitAnalysisCallback = UnitCallbackRegistry::AfterAnalysisCallback; + using UnitAnalysisFailureCallback = UnitCallbackRegistry::AnalysisFailureCallback; + + void addBeforeCallback(BeforeCallback Callback); + void addAfterCallback(AfterCallback Callback); + void addFailureCallback(FailureCallback Callback); + void addBeforeAnalysisCallback(BeforeAnalysisCallback Callback); + void addAfterAnalysisCallback(AfterAnalysisCallback Callback); + void addAnalysisFailureCallback(AnalysisFailureCallback Callback); + void addIRPrintCallback(IRPrintCallback Callback); + void addChangeCallback(ChangeCallback Callback); + void setActionFilter(ActionFilter Filter); + void setPerFunctionFuel(std::optional<std::uint64_t> Fuel); + + void addBeforeUnitCallback(BeforeUnitCallback Callback); + void addAfterUnitCallback(AfterUnitCallback Callback); + void addUnitFailureCallback(UnitFailureCallback Callback); + void addBeforeUnitAnalysisCallback(BeforeUnitAnalysisCallback Callback); + void addAfterUnitAnalysisCallback(AfterUnitAnalysisCallback Callback); + void addUnitAnalysisFailureCallback(UnitAnalysisFailureCallback Callback); + + [[nodiscard]] std::vector<InstrumentationStatistic> statistics() const; + [[nodiscard]] std::vector<MachineActionEvent> actionEvents() const; + [[nodiscard]] bool requiresSerialFunctionExecution() const; + + [[nodiscard]] Expected<bool> requestAction(std::string_view Pass, std::string_view Rule, + const MachineFunction &Function, + MachineInstrStableID Instruction, std::uint64_t Cost); + [[nodiscard]] Expected<bool> requestAction(std::string_view Pass, std::string_view Rule, + const MachineFunction &Function, std::nullptr_t, + std::uint64_t Cost); + +private: + void before(std::string_view Name, const MachineFunction &Function) const; + void after(std::string_view Name, const MachineFunction &Function, + const MachinePassResult &Result, std::uint64_t Nanoseconds) const; + void failure(std::string_view Name, const MachineFunction &Function, const Error &ErrorValue, + std::uint64_t Nanoseconds = 0) const; + void beforeAnalysis(std::string_view Name, const MachineFunction &Function) const; + void afterAnalysis(std::string_view Name, const MachineFunction &Function, bool CacheHit, + std::uint64_t Nanoseconds) const; + void analysisFailure(std::string_view Name, const MachineFunction &Function, + const Error &ErrorValue, std::uint64_t Nanoseconds) const; + void change(std::string_view Name, const MachineFunction &Function, MachinePassChanges Changes, + bool FingerprintChanged) const; + + void beforeUnit(std::string_view Name, const CompilationUnit &Unit) const; + void afterUnit(std::string_view Name, const CompilationUnit &Unit, + const CompilationUnitPassResult &Result, std::uint64_t Nanoseconds) const; + void unitFailure(std::string_view Name, const CompilationUnit &Unit, const Error &ErrorValue, + std::uint64_t Nanoseconds = 0) const; + void beforeAnalysis(std::string_view Name, const CompilationUnit &Unit) const; + void afterAnalysis(std::string_view Name, const CompilationUnit &Unit, bool CacheHit, + std::uint64_t Nanoseconds) const; + void analysisFailure(std::string_view Name, const CompilationUnit &Unit, const Error &ErrorValue, + std::uint64_t Nanoseconds) const; + + [[nodiscard]] InstrumentationStatistic &statistic(std::string_view Name, bool IsAnalysis) const; + + FunctionCallbackRegistry FunctionCallbacks; + UnitCallbackRegistry UnitCallbacks; + mutable std::mutex Mutex; + mutable std::mutex ActionMutex; + std::vector<IRPrintCallback> IRPrint; + std::vector<ChangeCallback> Change; + ActionFilter Filter; + std::optional<std::uint64_t> PerFunctionFuel; + std::unordered_map<std::uint64_t, std::uint64_t> ConsumedFuel; + std::vector<MachineActionEvent> ActionEvents; + mutable std::unordered_map<std::string, InstrumentationStatistic> Statistics; + + template <typename, typename> friend struct AnalysisManagerInstrumentation; + template <typename, typename, typename> friend struct PassManagerCallbacks; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_MACHINEPASSINSTRUMENTATION_H diff --git a/backend/include/rcc/CodeGen/MachinePassManager.h b/backend/include/rcc/CodeGen/MachinePassManager.h new file mode 100644 index 0000000..af7a5d3 --- /dev/null +++ b/backend/include/rcc/CodeGen/MachinePassManager.h @@ -0,0 +1,134 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_MACHINEPASSMANAGER_H +#define RCC_CODEGEN_MACHINEPASSMANAGER_H + +#include "rcc/CodeGen/InstructionDescriptor.h" +#include "rcc/CodeGen/MachineFunctionAnalysisManager.h" +#include "rcc/CodeGen/MachineFunctionProperties.h" +#include "rcc/CodeGen/MachinePassInstrumentation.h" + +#include <cstddef> +#include <memory> +#include <optional> +#include <string> +#include <vector> + +namespace rcc { + +class CompilationUnit; +class MachineFunction; +class TargetDescription; + +struct MachinePassRegistrationMetadata { + std::vector<PassParameter> Parameters; + PassThreadSafety ThreadSafety = PassThreadSafety::NotThreadSafe; + MachinePassChanges DeclaredChanges = MachinePassChanges::all(); +}; + +struct MachinePassContract final : MachinePassRegistrationMetadata { + std::string Name; + MachineFunctionProperties Requires; + MachineFunctionProperties Sets; + MachineFunctionProperties Clears; + std::vector<AnalysisID> RequiredAnalyses; + std::optional<PipelineStage> VerificationStage; +}; + +struct MachinePassResult final { + bool Changed = false; + MachinePassChanges Changes; + PreservedAnalyses Preserved = PreservedAnalyses::none(); +}; + +struct CompilationUnitPassContract final { + std::string Name; + std::vector<AnalysisID> RequiredAnalyses; + std::vector<PassParameter> Parameters; + MachinePassChanges DeclaredChanges = MachinePassChanges::all(); +}; + +struct CompilationUnitPassResult final { + bool Changed = false; + MachinePassChanges Changes; + PreservedAnalyses PreservedUnitAnalyses = PreservedAnalyses::none(); + PreservedAnalyses PreservedFunctionAnalyses = PreservedAnalyses::none(); +}; + +class MachineFunctionPass { +public: + MachineFunctionPass() = default; + MachineFunctionPass(const MachineFunctionPass &) = delete; + MachineFunctionPass &operator=(const MachineFunctionPass &) = delete; + virtual ~MachineFunctionPass() = default; + + [[nodiscard]] virtual const MachinePassContract &contract() const noexcept = 0; + [[nodiscard]] virtual Expected<MachinePassResult> + run(MachineFunction &Function, const TargetDescription &Target, + MachineFunctionAnalysisManager &Analyses) = 0; +}; + +class CompilationUnitPass { +public: + CompilationUnitPass() = default; + CompilationUnitPass(const CompilationUnitPass &) = delete; + CompilationUnitPass &operator=(const CompilationUnitPass &) = delete; + virtual ~CompilationUnitPass() = default; + + [[nodiscard]] virtual const CompilationUnitPassContract &contract() const noexcept = 0; + [[nodiscard]] virtual Expected<CompilationUnitPassResult> + run(CompilationUnit &Unit, const TargetDescription &Target, + CompilationUnitAnalysisManager &UnitAnalyses, + MachineFunctionAnalysisManager &FunctionAnalyses) = 0; +}; + +struct MachinePassManagerOptions final { + bool VerifyBefore = true; + bool VerifyEach = true; + bool DetectUnreportedChanges = false; +}; + +template <> +struct PassManagerCallbacks<MachineFunction, MachineFunctionPass, MachinePassManagerOptions> final { + using PassList = std::vector<std::unique_ptr<MachineFunctionPass>>; + + [[nodiscard]] static Expected<void> validate(const MachineFunctionPass &Pass); + [[nodiscard]] static Expected<void> + run(const PassList &Passes, const MachinePassManagerOptions &Options, MachineFunction &Function, + const TargetDescription &Target, MachineFunctionAnalysisManager &Analyses, + MachinePassInstrumentation *Instrumentation = nullptr); + [[nodiscard]] static Expected<void> + runFunctions(const PassList &Passes, const MachinePassManagerOptions &Options, + CompilationUnit &Unit, const TargetDescription &Target, + const MachineFunctionAnalysisManager &AnalysisPrototype, std::size_t ThreadCount, + MachinePassInstrumentation *Instrumentation = nullptr); +}; + +using MachineFunctionPassManager = + PassManager<MachineFunction, MachineFunctionPass, MachinePassManagerOptions>; + +struct CompilationUnitPassManagerOptions final { + bool VerifyBefore = true; + bool VerifyEach = true; +}; + +template <> +struct PassManagerCallbacks<CompilationUnit, CompilationUnitPass, CompilationUnitPassManagerOptions> + final { + using PassList = std::vector<std::unique_ptr<CompilationUnitPass>>; + + [[nodiscard]] static Expected<void> validate(const CompilationUnitPass &Pass); + [[nodiscard]] static Expected<void> run(const PassList &Passes, + const CompilationUnitPassManagerOptions &Options, + CompilationUnit &Unit, const TargetDescription &Target, + CompilationUnitAnalysisManager &UnitAnalyses, + MachineFunctionAnalysisManager &FunctionAnalyses, + MachinePassInstrumentation *Instrumentation = nullptr); +}; + +using CompilationUnitPassManager = + PassManager<CompilationUnit, CompilationUnitPass, CompilationUnitPassManagerOptions>; + +} // namespace rcc + +#endif // RCC_CODEGEN_MACHINEPASSMANAGER_H diff --git a/backend/include/rcc/CodeGen/MachinePeepholeOptimizer.h b/backend/include/rcc/CodeGen/MachinePeepholeOptimizer.h new file mode 100644 index 0000000..9505625 --- /dev/null +++ b/backend/include/rcc/CodeGen/MachinePeepholeOptimizer.h @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_MACHINEPEEPHOLEOPTIMIZER_H +#define RCC_CODEGEN_MACHINEPEEPHOLEOPTIMIZER_H + +#include "rcc/CodeGen/MachinePassManager.h" + +namespace rcc { + +class TargetInstrInfo; + +class MachinePeepholeOptimizerPass final : public MachineFunctionPass { +public: + explicit MachinePeepholeOptimizerPass(const TargetInstrInfo &Info) noexcept : Info(&Info) {} + + [[nodiscard]] const MachinePassContract &contract() const noexcept override; + [[nodiscard]] Expected<MachinePassResult> run(MachineFunction &Function, + const TargetDescription &Target, + MachineFunctionAnalysisManager &Analyses) override; + +private: + [[nodiscard]] Expected<MachinePassResult> + runImplementation(MachineFunction &Function, const TargetDescription &Target) const; + + const TargetInstrInfo *Info; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_MACHINEPEEPHOLEOPTIMIZER_H diff --git a/backend/include/rcc/CodeGen/MachinePseudoExpansion.h b/backend/include/rcc/CodeGen/MachinePseudoExpansion.h new file mode 100644 index 0000000..3c72307 --- /dev/null +++ b/backend/include/rcc/CodeGen/MachinePseudoExpansion.h @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_MACHINEPSEUDOEXPANSION_H +#define RCC_CODEGEN_MACHINEPSEUDOEXPANSION_H + +#include "rcc/CodeGen/MachinePassManager.h" +#include "rcc/CodeGen/TargetPseudoExpansion.h" + +namespace rcc { + +class TargetInstrInfo; +class TargetPseudoExpansionInfo; + +class PreRATargetPseudoExpansionPass final : public MachineFunctionPass { +public: + explicit PreRATargetPseudoExpansionPass(const TargetPseudoExpansionInfo &Info) noexcept + : Info(&Info) {} + + [[nodiscard]] const MachinePassContract &contract() const noexcept override; + [[nodiscard]] Expected<MachinePassResult> run(MachineFunction &Function, + const TargetDescription &Target, + MachineFunctionAnalysisManager &Analyses) override; + +private: + const TargetPseudoExpansionInfo *Info; +}; + +class PreparePostRAPseudosPass final : public MachineFunctionPass { +public: + explicit PreparePostRAPseudosPass(const TargetPseudoExpansionInfo &Info) noexcept : Info(&Info) {} + + [[nodiscard]] const MachinePassContract &contract() const noexcept override; + [[nodiscard]] Expected<MachinePassResult> run(MachineFunction &Function, + const TargetDescription &Target, + MachineFunctionAnalysisManager &Analyses) override; + +private: + const TargetPseudoExpansionInfo *Info; +}; + +class ExpandPostRAPseudosPass final : public MachineFunctionPass { +public: + explicit ExpandPostRAPseudosPass(const TargetPseudoExpansionInfo &Info) noexcept : Info(&Info) {} + + [[nodiscard]] const MachinePassContract &contract() const noexcept override; + [[nodiscard]] Expected<MachinePassResult> run(MachineFunction &Function, + const TargetDescription &Target, + MachineFunctionAnalysisManager &Analyses) override; + +private: + const TargetPseudoExpansionInfo *Info; +}; + +class TargetLatePseudoExpansionPass final : public MachineFunctionPass { +public: + TargetLatePseudoExpansionPass(const TargetPseudoExpansionInfo &Info, + const TargetInstrInfo &BranchInfo) noexcept + : Info(&Info), BranchInfo(&BranchInfo) {} + + [[nodiscard]] const MachinePassContract &contract() const noexcept override; + [[nodiscard]] Expected<MachinePassResult> run(MachineFunction &Function, + const TargetDescription &Target, + MachineFunctionAnalysisManager &Analyses) override; + +private: + const TargetPseudoExpansionInfo *Info; + const TargetInstrInfo *BranchInfo; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_MACHINEPSEUDOEXPANSION_H diff --git a/backend/include/rcc/CodeGen/MachineRegisterInfo.h b/backend/include/rcc/CodeGen/MachineRegisterInfo.h new file mode 100644 index 0000000..d0353b3 --- /dev/null +++ b/backend/include/rcc/CodeGen/MachineRegisterInfo.h @@ -0,0 +1,184 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_MACHINEREGISTERINFO_H +#define RCC_CODEGEN_MACHINEREGISTERINFO_H + +#include "rcc/CodeGen/LowLevelType.h" +#include "rcc/CodeGen/MachineIRTypes.h" +#include "rcc/CodeGen/Register.h" +#include "rcc/Support/Error.h" + +#include <cstddef> +#include <functional> +#include <iterator> +#include <memory> +#include <optional> +#include <span> +#include <string> +#include <unordered_map> +#include <vector> + +namespace rcc { + +class MachineOperand; +class MachineInstr; + +class RegOperandIterator final { +public: + using difference_type = std::ptrdiff_t; // NOLINT(readability-identifier-naming) + using value_type = MachineOperand; // NOLINT(readability-identifier-naming) + using pointer = MachineOperand *; // NOLINT(readability-identifier-naming) + using reference = MachineOperand &; // NOLINT(readability-identifier-naming) + using iterator_category = // NOLINT(readability-identifier-naming) + std::forward_iterator_tag; + + RegOperandIterator() = default; + + [[nodiscard]] reference operator*() const noexcept; + [[nodiscard]] pointer operator->() const noexcept; + RegOperandIterator &operator++() noexcept; + RegOperandIterator operator++(int) noexcept; + + friend bool operator==(const RegOperandIterator &Left, const RegOperandIterator &Right) noexcept { + return Left.Current == Right.Current; + } + +private: + RegOperandIterator(MachineOperand *Current, bool RequireUse, bool RequireDef, + bool SkipDebug) noexcept; + + void skipFiltered() noexcept; + + MachineOperand *Current = nullptr; + bool RequireUse = false; + bool RequireDef = false; + bool SkipDebug = false; + + friend class RegOperandRange; + friend class MachineRegisterInfo; +}; + +class RegOperandRange final { +public: + [[nodiscard]] RegOperandIterator begin() const noexcept { + return RegOperandIterator(Head, RequireUse, RequireDef, SkipDebug); + } + + [[nodiscard]] RegOperandIterator end() const noexcept { + return {}; + } + +private: + RegOperandRange(MachineOperand *Head, bool RequireUse, bool RequireDef, bool SkipDebug) noexcept + : Head(Head), RequireUse(RequireUse), RequireDef(RequireDef), SkipDebug(SkipDebug) {} + + MachineOperand *Head = nullptr; + bool RequireUse = false; + bool RequireDef = false; + bool SkipDebug = false; + + friend class MachineRegisterInfo; +}; + +struct VirtualRegisterInfo final { + std::optional<std::string> Name; + std::optional<LowLevelType> Type; + std::optional<RegisterBankID> Bank; + std::optional<RegisterClassID> RegClass; + std::vector<PhysicalRegister> AllocationHints; + std::uint8_t TargetFlags = 0; + bool IsTombstone = false; +}; + +struct PhysicalRegisterLiveIn final { + PhysicalRegister PhysReg; + std::optional<Register> InitialVirtualRegister; + + friend bool operator==(const PhysicalRegisterLiveIn &Left, + const PhysicalRegisterLiveIn &Right) = default; +}; + +class MachineRegisterInfo final { +public: + MachineRegisterInfo() = default; + MachineRegisterInfo(const MachineRegisterInfo &) = delete; + MachineRegisterInfo &operator=(const MachineRegisterInfo &) = delete; + MachineRegisterInfo(MachineRegisterInfo &&) = delete; + MachineRegisterInfo &operator=(MachineRegisterInfo &&) = delete; + ~MachineRegisterInfo(); + + [[nodiscard]] Expected<Register> createVirtualRegister(); + [[nodiscard]] Expected<Register> createVirtualRegister(LowLevelType Type); + [[nodiscard]] Expected<Register> cloneVirtualRegister(Register Reg); + [[nodiscard]] Expected<void> eraseVirtualRegister(Register Reg); + + [[nodiscard]] bool isKnownVirtualRegister(Register Reg) const noexcept; + [[nodiscard]] std::span<const VirtualRegisterInfo> virtualRegisters() const noexcept { + return VirtualRegisters; + } + [[nodiscard]] Expected<std::reference_wrapper<VirtualRegisterInfo>> + virtualRegisterInfo(Register Reg); + [[nodiscard]] Expected<std::reference_wrapper<const VirtualRegisterInfo>> + virtualRegisterInfo(Register Reg) const; + + [[nodiscard]] Expected<void> setType(Register Reg, LowLevelType Type); + [[nodiscard]] Expected<void> clearType(Register Reg); + [[nodiscard]] Expected<void> setRegisterBank(Register Reg, RegisterBankID Bank); + [[nodiscard]] Expected<void> clearRegisterBank(Register Reg); + [[nodiscard]] Expected<void> setRegisterClass(Register Reg, RegisterClassID RegClass); + [[nodiscard]] Expected<void> clearRegisterClass(Register Reg); + [[nodiscard]] Expected<void> addAllocationHint(Register Reg, PhysicalRegister Hint); + [[nodiscard]] Expected<void> setTargetRegisterFlags(Register Reg, std::uint8_t Flags); + [[nodiscard]] Expected<void> clearTargetRegisterFlags(Register Reg); + void clearAllVirtualRegisterTypes() noexcept; + + [[nodiscard]] Expected<void> + addLiveIn(PhysicalRegister PhysReg, + std::optional<Register> InitialVirtualRegister = std::nullopt); + [[nodiscard]] std::span<const PhysicalRegisterLiveIn> liveIns() const noexcept { + return LiveIns; + } + void clearLiveInVirtualRegisters() noexcept; + + [[nodiscard]] Expected<void> reservePhysicalRegister(PhysicalRegister PhysReg); + [[nodiscard]] bool isReserved(PhysicalRegister PhysReg) const noexcept; + [[nodiscard]] std::span<const PhysicalRegister> reservedRegisters() const noexcept { + return ReservedRegisters; + } + + [[nodiscard]] RegOperandRange regOperands(Register Reg) noexcept; + [[nodiscard]] RegOperandRange regOperands(Register Reg) const noexcept; + [[nodiscard]] RegOperandRange regNoDebugOperands(Register Reg) noexcept; + [[nodiscard]] RegOperandRange useOperands(Register Reg, bool IncludeDebug = true) noexcept; + [[nodiscard]] RegOperandRange defOperands(Register Reg) noexcept; + + [[nodiscard]] bool useEmpty(Register Reg, bool IncludeDebug = true) const noexcept; + [[nodiscard]] bool hasOneUse(Register Reg, bool IncludeDebug = true) const noexcept; + [[nodiscard]] const MachineOperand *getUniqueVRegDef(Register Reg) const noexcept; + [[nodiscard]] MachineInstr *getVRegDef(Register Reg) noexcept; + [[nodiscard]] const MachineInstr *getVRegDef(Register Reg) const noexcept; + [[nodiscard]] Expected<LowLevelType> getType(Register Reg) const; + [[nodiscard]] Expected<std::size_t> replaceRegWith(Register OldReg, Register NewReg); + + [[nodiscard]] Expected<void> verifyDefUseChains() const; + [[nodiscard]] bool isLinkedOperand(const MachineOperand &Operand) const noexcept; + +private: + struct RegOperandListHead final { + MachineOperand *Head = nullptr; + }; + + [[nodiscard]] MachineOperand *headFor(Register Reg) const noexcept; + void linkOperand(MachineOperand &Operand); + + std::vector<VirtualRegisterInfo> VirtualRegisters; + std::vector<PhysicalRegisterLiveIn> LiveIns; + std::vector<PhysicalRegister> ReservedRegisters; + std::unordered_map<Register, std::unique_ptr<RegOperandListHead>> DefUseHeads; + + friend class MachineOperand; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_MACHINEREGISTERINFO_H diff --git a/backend/include/rcc/CodeGen/MachineScheduler.h b/backend/include/rcc/CodeGen/MachineScheduler.h new file mode 100644 index 0000000..614ef86 --- /dev/null +++ b/backend/include/rcc/CodeGen/MachineScheduler.h @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_MACHINESCHEDULER_H +#define RCC_CODEGEN_MACHINESCHEDULER_H + +#include "rcc/CodeGen/MachinePassManager.h" + +#include <cstddef> + +namespace rcc { + +class TargetSchedulingModel; + +struct MachineSchedulerOptions final { + std::size_t MaximumRegionSize = 512; + bool EnableBidirectionalScheduling = true; + bool EmitDebugOutput = false; +}; + +class MachineSchedulerPass final : public MachineFunctionPass { +public: + explicit MachineSchedulerPass(const TargetSchedulingModel &Model, + MachineSchedulerOptions Options = {}) noexcept + : Model(&Model), Options(Options) {} + + [[nodiscard]] const MachinePassContract &contract() const noexcept override; + [[nodiscard]] Expected<MachinePassResult> run(MachineFunction &Function, + const TargetDescription &Target, + MachineFunctionAnalysisManager &Analyses) override; + +private: + const TargetSchedulingModel *Model; + MachineSchedulerOptions Options; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_MACHINESCHEDULER_H diff --git a/backend/include/rcc/CodeGen/MachineShrinkWrapping.h b/backend/include/rcc/CodeGen/MachineShrinkWrapping.h new file mode 100644 index 0000000..14edeb7 --- /dev/null +++ b/backend/include/rcc/CodeGen/MachineShrinkWrapping.h @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_MACHINESHRINKWRAPPING_H +#define RCC_CODEGEN_MACHINESHRINKWRAPPING_H + +#include "rcc/CodeGen/MachinePassManager.h" + +#include <cstddef> + +namespace rcc { + +class TargetFrameLowering; + +struct MachineShrinkWrappingOptions final { + std::size_t MaximumAnalyzedBlocks = 10'000; +}; + +class MachineShrinkWrappingPass final : public MachineFunctionPass { +public: + explicit MachineShrinkWrappingPass(const TargetFrameLowering &Info, + MachineShrinkWrappingOptions Options = {}) noexcept + : Info(&Info), Options(Options) {} + + [[nodiscard]] const MachinePassContract &contract() const noexcept override; + [[nodiscard]] Expected<MachinePassResult> run(MachineFunction &Function, + const TargetDescription &Target, + MachineFunctionAnalysisManager &Analyses) override; + +private: + const TargetFrameLowering *Info; + MachineShrinkWrappingOptions Options; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_MACHINESHRINKWRAPPING_H diff --git a/backend/include/rcc/CodeGen/MachineSink.h b/backend/include/rcc/CodeGen/MachineSink.h new file mode 100644 index 0000000..5b47f9b --- /dev/null +++ b/backend/include/rcc/CodeGen/MachineSink.h @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_MACHINESINK_H +#define RCC_CODEGEN_MACHINESINK_H + +#include "rcc/CodeGen/MachineLICM.h" + +#include <cstddef> + +namespace rcc { + +struct MachineSinkOptions final { + std::size_t MaximumSunkInstructions = 10'000; +}; + +class MachineSinkPass final : public MachineFunctionPass { +public: + explicit MachineSinkPass(MachineSinkOptions Options = {}) : Options(Options) {} + MachineSinkPass(MachineCodeMotionStage Stage, MachineSinkOptions Options = {}) + : Stage(Stage), Options(Options) {} + + [[nodiscard]] const MachinePassContract &contract() const noexcept override; + [[nodiscard]] Expected<MachinePassResult> run(MachineFunction &Function, + const TargetDescription &Target, + MachineFunctionAnalysisManager &Analyses) override; + +private: + [[nodiscard]] Expected<MachinePassResult> + runImplementation(MachineFunction &Function, const TargetDescription &Target, + MachineFunctionAnalysisManager &Analyses); + + MachineCodeMotionStage Stage = MachineCodeMotionStage::SelectedSSA; + MachineSinkOptions Options; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_MACHINESINK_H diff --git a/backend/include/rcc/CodeGen/MachineStackSlotColoring.h b/backend/include/rcc/CodeGen/MachineStackSlotColoring.h new file mode 100644 index 0000000..9aae710 --- /dev/null +++ b/backend/include/rcc/CodeGen/MachineStackSlotColoring.h @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_MACHINESTACKSLOTCOLORING_H +#define RCC_CODEGEN_MACHINESTACKSLOTCOLORING_H + +#include "rcc/CodeGen/MachinePassManager.h" + +#include <cstddef> + +namespace rcc { + +struct MachineStackSlotColoringOptions final { + std::size_t MaximumColoredSlots = 4'096; +}; + +class MachineStackSlotColoringPass final : public MachineFunctionPass { +public: + explicit MachineStackSlotColoringPass(MachineStackSlotColoringOptions Options = {}) noexcept + : Options(Options) {} + + [[nodiscard]] const MachinePassContract &contract() const noexcept override; + [[nodiscard]] Expected<MachinePassResult> run(MachineFunction &Function, + const TargetDescription &Target, + MachineFunctionAnalysisManager &Analyses) override; + +private: + [[nodiscard]] Expected<MachinePassResult> runImplementation(MachineFunction &Function) const; + + MachineStackSlotColoringOptions Options; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_MACHINESTACKSLOTCOLORING_H diff --git a/backend/include/rcc/CodeGen/MachineStripDebug.h b/backend/include/rcc/CodeGen/MachineStripDebug.h new file mode 100644 index 0000000..eb89155 --- /dev/null +++ b/backend/include/rcc/CodeGen/MachineStripDebug.h @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_MACHINESTRIPDEBUG_H +#define RCC_CODEGEN_MACHINESTRIPDEBUG_H + +#include "rcc/Support/Error.h" + +namespace rcc { + +class MachineFunction; + +[[nodiscard]] Expected<bool> stripDebugMachineFunction(MachineFunction &Function); + +} // namespace rcc + +#endif // RCC_CODEGEN_MACHINESTRIPDEBUG_H diff --git a/backend/include/rcc/CodeGen/MachineTailDuplication.h b/backend/include/rcc/CodeGen/MachineTailDuplication.h new file mode 100644 index 0000000..f52f638 --- /dev/null +++ b/backend/include/rcc/CodeGen/MachineTailDuplication.h @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_MACHINETAILDUPLICATION_H +#define RCC_CODEGEN_MACHINETAILDUPLICATION_H + +#include "rcc/CodeGen/MachinePassManager.h" + +#include <cstddef> + +namespace rcc { + +class TargetInstrInfo; + +struct MachineTailDuplicationOptions final { + std::size_t MaximumTailInstructions = 8; + std::size_t MaximumDuplicatedInstructions = 4'096; +}; + +class MachineTailDuplicationPass final : public MachineFunctionPass { +public: + explicit MachineTailDuplicationPass(const TargetInstrInfo &Info, + MachineTailDuplicationOptions Options = {}) noexcept + : Info(&Info), Options(Options) {} + + [[nodiscard]] const MachinePassContract &contract() const noexcept override; + [[nodiscard]] Expected<MachinePassResult> run(MachineFunction &Function, + const TargetDescription &Target, + MachineFunctionAnalysisManager &Analyses) override; + +private: + [[nodiscard]] Expected<MachinePassResult> + runImplementation(MachineFunction &Function, const TargetDescription &Target) const; + + const TargetInstrInfo *Info; + MachineTailDuplicationOptions Options; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_MACHINETAILDUPLICATION_H diff --git a/backend/include/rcc/CodeGen/MachineVerifier.h b/backend/include/rcc/CodeGen/MachineVerifier.h new file mode 100644 index 0000000..efe430e --- /dev/null +++ b/backend/include/rcc/CodeGen/MachineVerifier.h @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_MACHINEVERIFIER_H +#define RCC_CODEGEN_MACHINEVERIFIER_H + +#include "rcc/CodeGen/TargetDescription.h" +#include "rcc/Support/Error.h" + +#include <cstdint> +#include <optional> + +namespace rcc { + +class MachineFunction; + +enum class MachineVerifierMode : std::uint8_t { + Generic, + PreLegalizer, +}; + +struct MachineVerifierOptions final { + MachineVerifierMode Mode = MachineVerifierMode::Generic; + std::optional<PipelineStage> Stage; + bool VerifyProperties = true; +}; + +class MachineVerifier final { +public: + [[nodiscard]] static Expected<void> verify(const MachineFunction &Function, + const TargetDescription &Target, + MachineVerifierOptions Options = {}); +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_MACHINEVERIFIER_H diff --git a/backend/include/rcc/CodeGen/MacroFusion.h b/backend/include/rcc/CodeGen/MacroFusion.h new file mode 100644 index 0000000..c3d811d --- /dev/null +++ b/backend/include/rcc/CodeGen/MacroFusion.h @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_MACROFUSION_H +#define RCC_CODEGEN_MACROFUSION_H + +#include "rcc/CodeGen/MachineIRTypes.h" +#include "rcc/Support/Error.h" + +#include <cstdint> +#include <limits> +#include <span> +#include <string_view> + +namespace rcc { + +class MachineInstr; +class ScheduleDAG; +class TargetDescription; + +enum class MacroFusionInstruction : std::uint8_t { + First, + Second, +}; + +struct MacroFusionOpcodePair final { + Opcode First; + Opcode Second; +}; + +struct MacroFusionImmediatePredicate final { + MacroFusionInstruction Instruction = MacroFusionInstruction::First; + std::uint32_t OperandIndex = 0; + std::int64_t Minimum = 0; + std::int64_t Maximum = 0; +}; + +struct MacroFusionRegisterInequality final { + MacroFusionInstruction Instruction = MacroFusionInstruction::First; + std::uint32_t LeftOperand = 0; + std::uint32_t RightOperand = 0; +}; + +struct MacroFusionImmediateRelation final { + std::uint32_t FirstOperand = 0; + std::uint32_t SecondOperand = 0; +}; + +struct MacroFusionRule final { + static constexpr std::uint32_t NoOperand = std::numeric_limits<std::uint32_t>::max(); + + std::string_view Name; + std::string_view Feature; + std::span<const Opcode> FirstOpcodes; + std::span<const Opcode> SecondOpcodes; + std::span<const MacroFusionOpcodePair> OpcodePairs; + std::span<const MacroFusionImmediatePredicate> ImmediatePredicates; + std::span<const MacroFusionRegisterInequality> RegisterInequalities; + std::span<const MacroFusionImmediateRelation> ImmediateRelations; + std::uint32_t FirstDefinitionOperand = 0; + std::uint32_t SecondUseOperand = 1; + std::uint32_t SecondDefinitionOperand = 0; + std::uint32_t CommutableSecondUseOperand = NoOperand; + bool RequireSingleNonDebugUse = false; + bool RequireSecondDestinationTie = false; +}; + +[[nodiscard]] bool matchesMacroFusion(const MacroFusionRule &Rule, const MachineInstr *First, + const MachineInstr &Second) noexcept; + +[[nodiscard]] Expected<void> applyMacroFusion(ScheduleDAG &DAG, + std::span<const MacroFusionRule> Rules, + const TargetDescription &Target, + bool EmitDebugOutput); + +} // namespace rcc + +#endif // RCC_CODEGEN_MACROFUSION_H diff --git a/backend/include/rcc/CodeGen/PBQP/Graph.h b/backend/include/rcc/CodeGen/PBQP/Graph.h new file mode 100644 index 0000000..9173bfd --- /dev/null +++ b/backend/include/rcc/CodeGen/PBQP/Graph.h @@ -0,0 +1,194 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_PBQP_GRAPH_H +#define RCC_CODEGEN_PBQP_GRAPH_H + +#include "rcc/CodeGen/PBQP/Math.h" +#include "rcc/Support/Error.h" + +#include <compare> +#include <cstddef> +#include <cstdint> +#include <functional> +#include <limits> +#include <optional> +#include <span> +#include <utility> +#include <vector> + +namespace rcc::pbqp { + +class NodeID final { +public: + constexpr NodeID() noexcept = default; + [[nodiscard]] static constexpr NodeID fromValue(std::uint32_t Value) noexcept { + return NodeID(Value); + } + [[nodiscard]] constexpr bool isValid() const noexcept { + return Value != InvalidValue; + } + [[nodiscard]] constexpr std::uint32_t value() const noexcept { + return Value; + } + friend constexpr bool operator==(NodeID, NodeID) noexcept = default; + friend constexpr auto operator<=>(NodeID, NodeID) noexcept = default; + +private: + static constexpr std::uint32_t InvalidValue = std::numeric_limits<std::uint32_t>::max(); + explicit constexpr NodeID(std::uint32_t Value) noexcept : Value(Value) {} + std::uint32_t Value = InvalidValue; +}; + +class EdgeID final { +public: + constexpr EdgeID() noexcept = default; + [[nodiscard]] static constexpr EdgeID fromValue(std::uint32_t Value) noexcept { + return EdgeID(Value); + } + [[nodiscard]] constexpr bool isValid() const noexcept { + return Value != InvalidValue; + } + [[nodiscard]] constexpr std::uint32_t value() const noexcept { + return Value; + } + friend constexpr bool operator==(EdgeID, EdgeID) noexcept = default; + friend constexpr auto operator<=>(EdgeID, EdgeID) noexcept = default; + +private: + static constexpr std::uint32_t InvalidValue = std::numeric_limits<std::uint32_t>::max(); + explicit constexpr EdgeID(std::uint32_t Value) noexcept : Value(Value) {} + std::uint32_t Value = InvalidValue; +}; + +struct Node final { + NodeID ID; + std::uint64_t StableKey = 0; + CostVector Costs; + std::vector<EdgeID> AdjacentEdges; +}; + +struct Edge final { + EdgeID ID; + NodeID First; + NodeID Second; + std::uint64_t StableKey = 0; + CostMatrix Costs; +}; + +class Graph final { +public: + [[nodiscard]] Expected<NodeID> addNode(CostVector Costs, std::uint64_t StableKey = 0) { + if (Costs.empty()) + return makeUnexpected(ErrorCode::InvalidArgument, "PBQP node requires at least one option"); + if (Nodes.size() >= std::numeric_limits<std::uint32_t>::max()) + return makeUnexpected(ErrorCode::Unsupported, "PBQP node ID space is exhausted"); + const NodeID ID = NodeID::fromValue(static_cast<std::uint32_t>(Nodes.size())); + Nodes.push_back(Node{ID, StableKey, std::move(Costs), {}}); + return ID; + } + + [[nodiscard]] Expected<EdgeID> addEdge(NodeID First, NodeID Second, CostMatrix Costs, + std::uint64_t StableKey = 0) { + const Node *FirstNode = node(First); + const Node *SecondNode = node(Second); + if (FirstNode == nullptr || SecondNode == nullptr || First == Second) { + return makeUnexpected(ErrorCode::InvalidArgument, + "PBQP edge requires two distinct known nodes"); + } + if (edgeBetween(First, Second)) + return makeUnexpected(ErrorCode::InvalidArgument, "PBQP graph already contains this edge"); + + if (Second < First) { + std::swap(First, Second); + Costs = Costs.transpose(); + std::swap(FirstNode, SecondNode); + } + if (Costs.rows() != FirstNode->Costs.size() || Costs.columns() != SecondNode->Costs.size()) { + return makeUnexpected(ErrorCode::InvalidArgument, + "PBQP edge matrix dimensions do not match its nodes"); + } + if (Edges.size() >= std::numeric_limits<std::uint32_t>::max()) + return makeUnexpected(ErrorCode::Unsupported, "PBQP edge ID space is exhausted"); + const EdgeID ID = EdgeID::fromValue(static_cast<std::uint32_t>(Edges.size())); + Edges.push_back(Edge{ID, First, Second, StableKey, std::move(Costs)}); + Nodes[First.value()].AdjacentEdges.push_back(ID); + Nodes[Second.value()].AdjacentEdges.push_back(ID); + return ID; + } + + [[nodiscard]] const Node *node(NodeID ID) const noexcept { + return ID.isValid() && ID.value() < Nodes.size() ? &Nodes[ID.value()] : nullptr; + } + [[nodiscard]] const Edge *edge(EdgeID ID) const noexcept { + return ID.isValid() && ID.value() < Edges.size() ? &Edges[ID.value()] : nullptr; + } + + [[nodiscard]] const Edge *edgeBetween(NodeID Left, NodeID Right) const noexcept { + const Node *LeftNode = node(Left); + if (LeftNode == nullptr || node(Right) == nullptr) + return nullptr; + for (const EdgeID ID : LeftNode->AdjacentEdges) { + const Edge &Candidate = Edges[ID.value()]; + if ((Candidate.First == Left && Candidate.Second == Right) || + (Candidate.First == Right && Candidate.Second == Left)) + return &Candidate; + } + return nullptr; + } + + [[nodiscard]] std::span<const Node> nodes() const noexcept { + return Nodes; + } + [[nodiscard]] std::span<const Edge> edges() const noexcept { + return Edges; + } + [[nodiscard]] std::size_t nodeCount() const noexcept { + return Nodes.size(); + } + + [[nodiscard]] Expected<void> verify() const { + for (std::size_t Index = 0; Index < Nodes.size(); ++Index) { + const Node &Value = Nodes[Index]; + if (Value.ID.value() != Index || Value.Costs.empty()) + return makeUnexpected(ErrorCode::VerificationError, "PBQP graph contains an invalid node"); + for (const EdgeID ID : Value.AdjacentEdges) { + const Edge *Adjacent = edge(ID); + if (Adjacent == nullptr || (Adjacent->First != Value.ID && Adjacent->Second != Value.ID)) { + return makeUnexpected(ErrorCode::VerificationError, + "PBQP node has an invalid adjacency entry"); + } + } + } + for (std::size_t Index = 0; Index < Edges.size(); ++Index) { + const Edge &Value = Edges[Index]; + const Node *First = node(Value.First); + const Node *Second = node(Value.Second); + if (Value.ID.value() != Index || First == nullptr || Second == nullptr || + !(Value.First < Value.Second) || Value.Costs.rows() != First->Costs.size() || + Value.Costs.columns() != Second->Costs.size()) { + return makeUnexpected(ErrorCode::VerificationError, "PBQP graph contains an invalid edge"); + } + } + return {}; + } + +private: + std::vector<Node> Nodes; + std::vector<Edge> Edges; +}; + +} // namespace rcc::pbqp + +template <> struct std::hash<rcc::pbqp::NodeID> { + [[nodiscard]] std::size_t operator()(rcc::pbqp::NodeID ID) const noexcept { + return std::hash<std::uint32_t>{}(ID.value()); + } +}; + +template <> struct std::hash<rcc::pbqp::EdgeID> { + [[nodiscard]] std::size_t operator()(rcc::pbqp::EdgeID ID) const noexcept { + return std::hash<std::uint32_t>{}(ID.value()); + } +}; + +#endif // RCC_CODEGEN_PBQP_GRAPH_H diff --git a/backend/include/rcc/CodeGen/PBQP/Math.h b/backend/include/rcc/CodeGen/PBQP/Math.h new file mode 100644 index 0000000..90fc751 --- /dev/null +++ b/backend/include/rcc/CodeGen/PBQP/Math.h @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_PBQP_MATH_H +#define RCC_CODEGEN_PBQP_MATH_H + +#include <algorithm> +#include <cstddef> +#include <cstdint> +#include <limits> +#include <span> +#include <utility> +#include <vector> + +namespace rcc::pbqp { + +using Cost = std::int64_t; + +inline constexpr Cost InfiniteCost = std::numeric_limits<Cost>::max() / 4; +inline constexpr Cost NegativeInfiniteCost = -InfiniteCost; + +[[nodiscard]] constexpr bool isInfinite(Cost Value) noexcept { + return Value >= InfiniteCost; +} + +[[nodiscard]] constexpr Cost addCosts(Cost Left, Cost Right) noexcept { + if (isInfinite(Left) || isInfinite(Right)) + return InfiniteCost; + if (Left <= NegativeInfiniteCost || Right <= NegativeInfiniteCost) + return NegativeInfiniteCost; + if (Right > 0 && Left > InfiniteCost - Right) + return InfiniteCost; + if (Right < 0 && Left < NegativeInfiniteCost - Right) + return NegativeInfiniteCost; + return Left + Right; +} + +class CostVector final { +public: + explicit CostVector(std::size_t Size, Cost Initial = 0) : Values(Size, Initial) {} + explicit CostVector(std::vector<Cost> Values) : Values(std::move(Values)) {} + + [[nodiscard]] std::size_t size() const noexcept { + return Values.size(); + } + [[nodiscard]] bool empty() const noexcept { + return Values.empty(); + } + [[nodiscard]] Cost &operator[](std::size_t Index) noexcept { + return Values[Index]; + } + [[nodiscard]] Cost operator[](std::size_t Index) const noexcept { + return Values[Index]; + } + [[nodiscard]] std::span<const Cost> values() const noexcept { + return Values; + } + + [[nodiscard]] std::size_t minimumIndex() const noexcept { + return static_cast<std::size_t>(std::ranges::min_element(Values) - Values.begin()); + } + + void add(const CostVector &Other) noexcept { + for (std::size_t Index = 0; Index < Values.size(); ++Index) + Values[Index] = addCosts(Values[Index], Other.Values[Index]); + } + +private: + std::vector<Cost> Values; +}; + +class CostMatrix final { +public: + CostMatrix(std::size_t Rows, std::size_t Columns, Cost Initial = 0) + : RowCount(Rows), ColumnCount(Columns), Values(Rows * Columns, Initial) {} + + [[nodiscard]] std::size_t rows() const noexcept { + return RowCount; + } + [[nodiscard]] std::size_t columns() const noexcept { + return ColumnCount; + } + [[nodiscard]] bool empty() const noexcept { + return RowCount == 0 || ColumnCount == 0; + } + + [[nodiscard]] Cost &at(std::size_t Row, std::size_t Column) noexcept { + return Values[Row * ColumnCount + Column]; + } + [[nodiscard]] Cost at(std::size_t Row, std::size_t Column) const noexcept { + return Values[Row * ColumnCount + Column]; + } + + [[nodiscard]] CostMatrix transpose() const { + CostMatrix Result(ColumnCount, RowCount); + for (std::size_t Row = 0; Row < RowCount; ++Row) + for (std::size_t Column = 0; Column < ColumnCount; ++Column) + Result.at(Column, Row) = at(Row, Column); + return Result; + } + + void add(const CostMatrix &Other) noexcept { + for (std::size_t Index = 0; Index < Values.size(); ++Index) + Values[Index] = addCosts(Values[Index], Other.Values[Index]); + } + +private: + std::size_t RowCount; + std::size_t ColumnCount; + std::vector<Cost> Values; +}; + +} // namespace rcc::pbqp + +#endif // RCC_CODEGEN_PBQP_MATH_H diff --git a/backend/include/rcc/CodeGen/PBQP/ReductionRules.h b/backend/include/rcc/CodeGen/PBQP/ReductionRules.h new file mode 100644 index 0000000..1b28e33 --- /dev/null +++ b/backend/include/rcc/CodeGen/PBQP/ReductionRules.h @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_PBQP_REDUCTIONRULES_H +#define RCC_CODEGEN_PBQP_REDUCTIONRULES_H + +#include "rcc/CodeGen/PBQP/Graph.h" + +#include <cstdint> + +namespace rcc::pbqp { + +enum class ReductionKind : std::uint8_t { + DegreeZero, + DegreeOne, + DegreeTwo, + Heuristic, +}; + +struct ReductionRecord final { + ReductionKind Kind = ReductionKind::DegreeZero; + NodeID Node; + NodeID FirstNeighbor; + NodeID SecondNeighbor; + std::size_t FixedSelection = 0; + CostVector NodeCosts{1}; + CostMatrix FirstCosts{1, 1}; + CostMatrix SecondCosts{1, 1}; +}; + +} // namespace rcc::pbqp + +#endif // RCC_CODEGEN_PBQP_REDUCTIONRULES_H diff --git a/backend/include/rcc/CodeGen/PBQP/Solution.h b/backend/include/rcc/CodeGen/PBQP/Solution.h new file mode 100644 index 0000000..512eb57 --- /dev/null +++ b/backend/include/rcc/CodeGen/PBQP/Solution.h @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_PBQP_SOLUTION_H +#define RCC_CODEGEN_PBQP_SOLUTION_H + +#include "rcc/CodeGen/PBQP/Graph.h" +#include "rcc/Support/Error.h" + +#include <cstddef> +#include <limits> +#include <optional> +#include <span> +#include <vector> + +namespace rcc::pbqp { + +class Solution final { +public: + explicit Solution(std::size_t NodeCount) + : Selections(NodeCount, std::numeric_limits<std::size_t>::max()) {} + + [[nodiscard]] Expected<void> setSelection(NodeID ID, std::size_t Selection) { + if (!ID.isValid() || ID.value() >= Selections.size()) + return makeUnexpected(ErrorCode::InvalidArgument, "PBQP solution received an unknown node"); + Selections[ID.value()] = Selection; + return {}; + } + + [[nodiscard]] std::optional<std::size_t> selection(NodeID ID) const noexcept { + if (!ID.isValid() || ID.value() >= Selections.size() || + Selections[ID.value()] == std::numeric_limits<std::size_t>::max()) + return std::nullopt; + return Selections[ID.value()]; + } + + [[nodiscard]] std::span<const std::size_t> selections() const noexcept { + return Selections; + } + [[nodiscard]] Cost totalCost() const noexcept { + return TotalCost; + } + void setTotalCost(Cost Value) noexcept { + TotalCost = Value; + } + + [[nodiscard]] Expected<void> verify(const Graph &Problem) const { + if (Selections.size() != Problem.nodeCount()) + return makeUnexpected(ErrorCode::VerificationError, "PBQP solution has the wrong node count"); + Cost Total = 0; + for (const Node &NodeValue : Problem.nodes()) { + const auto Selection = selection(NodeValue.ID); + if (!Selection) + return makeUnexpected(ErrorCode::VerificationError, + "PBQP solution omits a valid node selection"); + const std::size_t SelectedOption = Selection.value(); + if (SelectedOption >= NodeValue.Costs.size()) + return makeUnexpected(ErrorCode::VerificationError, + "PBQP solution selects an invalid node option"); + Total = addCosts(Total, NodeValue.Costs[SelectedOption]); + } + for (const Edge &EdgeValue : Problem.edges()) { + const auto FirstSelection = selection(EdgeValue.First); + const auto SecondSelection = selection(EdgeValue.Second); + if (!FirstSelection || !SecondSelection) { + return makeUnexpected(ErrorCode::VerificationError, + "PBQP solution omits an edge endpoint selection"); + } + Total = addCosts(Total, EdgeValue.Costs.at(FirstSelection.value(), SecondSelection.value())); + } + if (isInfinite(Total)) + return makeUnexpected(ErrorCode::RegisterAllocationFailed, + "PBQP problem has no finite solution"); + if (Total != TotalCost) + return makeUnexpected(ErrorCode::VerificationError, "PBQP solution cost is inconsistent"); + return {}; + } + +private: + std::vector<std::size_t> Selections; + Cost TotalCost = 0; +}; + +} // namespace rcc::pbqp + +#endif // RCC_CODEGEN_PBQP_SOLUTION_H diff --git a/backend/include/rcc/CodeGen/PBQP/Solver.h b/backend/include/rcc/CodeGen/PBQP/Solver.h new file mode 100644 index 0000000..f010fd1 --- /dev/null +++ b/backend/include/rcc/CodeGen/PBQP/Solver.h @@ -0,0 +1,381 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_PBQP_SOLVER_H +#define RCC_CODEGEN_PBQP_SOLVER_H + +#include "rcc/CodeGen/PBQP/ReductionRules.h" +#include "rcc/CodeGen/PBQP/Solution.h" + +#include <algorithm> +#include <cstddef> +#include <cstdint> +#include <functional> +#include <map> +#include <optional> +#include <set> +#include <tuple> +#include <utility> +#include <vector> + +namespace rcc::pbqp { + +enum class SolverActionKind : std::uint8_t { + Reduction, + Choice, +}; + +struct SolverAction final { + SolverActionKind Kind = SolverActionKind::Reduction; + ReductionKind Reduction = ReductionKind::DegreeZero; + NodeID Node; + std::size_t Value = 0; +}; + +class DeterministicSolver final { +public: + using ActionCallback = std::function<Expected<bool>(const SolverAction &)>; + + [[nodiscard]] Expected<Solution> solve(const Graph &Problem, + const ActionCallback &Action = {}) const { + auto Verified = Problem.verify(); + if (!Verified) + return std::unexpected(Verified.error()); + + WorkingProblem Work(Problem); + std::vector<ReductionRecord> Reductions; + Reductions.reserve(Problem.nodeCount()); + while (Work.activeNodeCount() != 0) { + const NodeID Selected = Work.selectReductionNode(); + const std::size_t Degree = Work.degree(Selected); + const ReductionKind Kind = Degree == 0 ? ReductionKind::DegreeZero + : Degree == 1 ? ReductionKind::DegreeOne + : Degree == 2 ? ReductionKind::DegreeTwo + : ReductionKind::Heuristic; + if (Action) { + auto Accepted = Action({SolverActionKind::Reduction, Kind, Selected, Degree}); + if (!Accepted) + return std::unexpected(Accepted.error()); + if (!*Accepted) { + return makeUnexpected(ErrorCode::RegisterAllocationFailed, + "PBQP reduction action was rejected"); + } + } + Expected<ReductionRecord> Reduced = + Kind == ReductionKind::DegreeZero ? Work.reduceDegreeZero(Selected) + : Kind == ReductionKind::DegreeOne ? Work.reduceDegreeOne(Selected) + : Kind == ReductionKind::DegreeTwo ? Work.reduceDegreeTwo(Selected) + : Work.reduceHeuristically(Selected); + if (!Reduced) + return std::unexpected(Reduced.error()); + Reductions.push_back(std::move(*Reduced)); + } + + Solution Result(Problem.nodeCount()); + for (auto Position = Reductions.rbegin(); Position != Reductions.rend(); ++Position) { + const ReductionRecord &Record = *Position; + std::size_t Choice = Record.FixedSelection; + if (Record.Kind != ReductionKind::Heuristic) { + Cost Best = InfiniteCost; + for (std::size_t Option = 0; Option < Record.NodeCosts.size(); ++Option) { + Cost Candidate = Record.NodeCosts[Option]; + if (Record.Kind == ReductionKind::DegreeOne || Record.Kind == ReductionKind::DegreeTwo) { + const auto Neighbor = Result.selection(Record.FirstNeighbor); + if (!Neighbor) + return makeUnexpected(ErrorCode::InternalError, + "PBQP back-propagation encountered an unsolved neighbor"); + Candidate = addCosts(Candidate, Record.FirstCosts.at(Option, *Neighbor)); + } + if (Record.Kind == ReductionKind::DegreeTwo) { + const auto Neighbor = Result.selection(Record.SecondNeighbor); + if (!Neighbor) + return makeUnexpected(ErrorCode::InternalError, + "PBQP back-propagation encountered an unsolved neighbor"); + Candidate = addCosts(Candidate, Record.SecondCosts.at(Option, *Neighbor)); + } + if (Candidate < Best) { + Best = Candidate; + Choice = Option; + } + } + if (isInfinite(Best)) + return makeUnexpected(ErrorCode::RegisterAllocationFailed, + "PBQP reductions produced no finite option"); + } + if (Action) { + auto Accepted = Action({SolverActionKind::Choice, Record.Kind, Record.Node, Choice}); + if (!Accepted) + return std::unexpected(Accepted.error()); + if (!*Accepted) { + return makeUnexpected(ErrorCode::RegisterAllocationFailed, + "PBQP choice action was rejected"); + } + } + auto Set = Result.setSelection(Record.Node, Choice); + if (!Set) + return std::unexpected(Set.error()); + } + + Cost Total = 0; + for (const Node &NodeValue : Problem.nodes()) { + const auto Selection = Result.selection(NodeValue.ID); + if (!Selection) { + return makeUnexpected(ErrorCode::InternalError, + "PBQP solver did not select a reduced node option"); + } + Total = addCosts(Total, NodeValue.Costs[Selection.value()]); + } + for (const Edge &EdgeValue : Problem.edges()) { + const auto FirstSelection = Result.selection(EdgeValue.First); + const auto SecondSelection = Result.selection(EdgeValue.Second); + if (!FirstSelection || !SecondSelection) { + return makeUnexpected(ErrorCode::InternalError, + "PBQP solver did not select an edge endpoint option"); + } + Total = addCosts(Total, EdgeValue.Costs.at(FirstSelection.value(), SecondSelection.value())); + } + if (isInfinite(Total)) + return makeUnexpected(ErrorCode::RegisterAllocationFailed, + "PBQP problem has no finite solution"); + Result.setTotalCost(Total); + auto ResultVerification = Result.verify(Problem); + if (!ResultVerification) + return std::unexpected(ResultVerification.error()); + return Result; + } + +private: + struct WorkingEdge final { + NodeID First; + NodeID Second; + CostMatrix Costs; + }; + + class WorkingProblem final { + public: + explicit WorkingProblem(const Graph &Problem) { + Costs.reserve(Problem.nodeCount()); + StableKeys.reserve(Problem.nodeCount()); + Active.assign(Problem.nodeCount(), true); + for (const Node &NodeValue : Problem.nodes()) { + Costs.push_back(NodeValue.Costs); + StableKeys.push_back(NodeValue.StableKey); + } + for (const Edge &EdgeValue : Problem.edges()) + Edges.emplace(key(EdgeValue.First, EdgeValue.Second), + WorkingEdge{EdgeValue.First, EdgeValue.Second, EdgeValue.Costs}); + ActiveCount = Problem.nodeCount(); + } + + [[nodiscard]] std::size_t activeNodeCount() const noexcept { + return ActiveCount; + } + + [[nodiscard]] std::size_t degree(NodeID ID) const noexcept { + std::size_t Result = 0; + for (const auto &[EdgeKey, EdgeValue] : Edges) { + (void)EdgeKey; + if (Active[EdgeValue.First.value()] && Active[EdgeValue.Second.value()] && + (EdgeValue.First == ID || EdgeValue.Second == ID)) + ++Result; + } + return Result; + } + + [[nodiscard]] NodeID selectReductionNode() const noexcept { + std::optional<std::tuple<bool, std::size_t, std::uint64_t, std::uint32_t>> Best; + NodeID Result; + for (std::size_t Index = 0; Index < Active.size(); ++Index) { + if (!Active[Index]) + continue; + const NodeID ID = NodeID::fromValue(static_cast<std::uint32_t>(Index)); + const std::size_t Degree = degree(ID); + const auto Rank = std::tuple{Degree > 2, Degree, StableKeys[Index], ID.value()}; + if (!Best || Rank < *Best) { + Best = Rank; + Result = ID; + } + } + return Result; + } + + [[nodiscard]] Expected<ReductionRecord> reduceDegreeZero(NodeID ID) { + ReductionRecord Record; + Record.Kind = ReductionKind::DegreeZero; + Record.Node = ID; + Record.NodeCosts = Costs[ID.value()]; + deactivate(ID); + return Record; + } + + [[nodiscard]] Expected<ReductionRecord> reduceDegreeOne(NodeID ID) { + const std::vector<WorkingEdge> Incident = incidentEdges(ID); + if (Incident.size() != 1) + return makeUnexpected(ErrorCode::InternalError, + "PBQP degree-one reduction is inconsistent"); + const WorkingEdge &EdgeValue = Incident.front(); + const NodeID Neighbor = other(EdgeValue, ID); + const CostMatrix Oriented = orientFrom(EdgeValue, ID); + + ReductionRecord Record; + Record.Kind = ReductionKind::DegreeOne; + Record.Node = ID; + Record.FirstNeighbor = Neighbor; + Record.NodeCosts = Costs[ID.value()]; + Record.FirstCosts = Oriented; + + for (std::size_t NeighborOption = 0; NeighborOption < Costs[Neighbor.value()].size(); + ++NeighborOption) { + Cost Minimum = InfiniteCost; + for (std::size_t Option = 0; Option < Costs[ID.value()].size(); ++Option) { + Minimum = std::min( + Minimum, addCosts(Costs[ID.value()][Option], Oriented.at(Option, NeighborOption))); + } + Costs[Neighbor.value()][NeighborOption] = + addCosts(Costs[Neighbor.value()][NeighborOption], Minimum); + } + removeIncidentEdges(ID); + deactivate(ID); + return Record; + } + + [[nodiscard]] Expected<ReductionRecord> reduceDegreeTwo(NodeID ID) { + const std::vector<WorkingEdge> Incident = incidentEdges(ID); + if (Incident.size() != 2) + return makeUnexpected(ErrorCode::InternalError, + "PBQP degree-two reduction is inconsistent"); + NodeID First = other(Incident[0], ID); + NodeID Second = other(Incident[1], ID); + CostMatrix FirstCosts = orientFrom(Incident[0], ID); + CostMatrix SecondCosts = orientFrom(Incident[1], ID); + if (Second < First) { + std::swap(First, Second); + std::swap(FirstCosts, SecondCosts); + } + + ReductionRecord Record; + Record.Kind = ReductionKind::DegreeTwo; + Record.Node = ID; + Record.FirstNeighbor = First; + Record.SecondNeighbor = Second; + Record.NodeCosts = Costs[ID.value()]; + Record.FirstCosts = FirstCosts; + Record.SecondCosts = SecondCosts; + + CostMatrix Delta(Costs[First.value()].size(), Costs[Second.value()].size(), InfiniteCost); + for (std::size_t FirstOption = 0; FirstOption < Delta.rows(); ++FirstOption) { + for (std::size_t SecondOption = 0; SecondOption < Delta.columns(); ++SecondOption) { + for (std::size_t Option = 0; Option < Costs[ID.value()].size(); ++Option) { + const Cost Candidate = + addCosts(Costs[ID.value()][Option], addCosts(FirstCosts.at(Option, FirstOption), + SecondCosts.at(Option, SecondOption))); + Delta.at(FirstOption, SecondOption) = + std::min(Delta.at(FirstOption, SecondOption), Candidate); + } + } + } + removeIncidentEdges(ID); + const auto Position = Edges.find(key(First, Second)); + if (Position == Edges.end()) + Edges.emplace(key(First, Second), WorkingEdge{First, Second, std::move(Delta)}); + else + Position->second.Costs.add(Delta); + deactivate(ID); + return Record; + } + + [[nodiscard]] Expected<ReductionRecord> reduceHeuristically(NodeID ID) { + const std::vector<WorkingEdge> Incident = incidentEdges(ID); + Cost Best = InfiniteCost; + std::size_t Choice = 0; + for (std::size_t Option = 0; Option < Costs[ID.value()].size(); ++Option) { + Cost Candidate = Costs[ID.value()][Option]; + for (const WorkingEdge &EdgeValue : Incident) { + const NodeID Neighbor = other(EdgeValue, ID); + const CostMatrix Oriented = orientFrom(EdgeValue, ID); + Cost NeighborMinimum = InfiniteCost; + for (std::size_t NeighborOption = 0; NeighborOption < Costs[Neighbor.value()].size(); + ++NeighborOption) { + NeighborMinimum = std::min(NeighborMinimum, Oriented.at(Option, NeighborOption)); + } + Candidate = addCosts(Candidate, NeighborMinimum); + } + if (Candidate < Best) { + Best = Candidate; + Choice = Option; + } + } + if (isInfinite(Best)) + return makeUnexpected(ErrorCode::RegisterAllocationFailed, + "PBQP heuristic reduction has no finite option"); + + ReductionRecord Record; + Record.Kind = ReductionKind::Heuristic; + Record.Node = ID; + Record.FixedSelection = Choice; + Record.NodeCosts = Costs[ID.value()]; + for (const WorkingEdge &EdgeValue : Incident) { + const NodeID Neighbor = other(EdgeValue, ID); + const CostMatrix Oriented = orientFrom(EdgeValue, ID); + for (std::size_t Option = 0; Option < Costs[Neighbor.value()].size(); ++Option) { + Costs[Neighbor.value()][Option] = + addCosts(Costs[Neighbor.value()][Option], Oriented.at(Choice, Option)); + } + } + removeIncidentEdges(ID); + deactivate(ID); + return Record; + } + + private: + using EdgeKey = std::pair<std::uint32_t, std::uint32_t>; + + [[nodiscard]] static EdgeKey key(NodeID First, NodeID Second) noexcept { + if (Second < First) + std::swap(First, Second); + return {First.value(), Second.value()}; + } + + [[nodiscard]] static NodeID other(const WorkingEdge &EdgeValue, NodeID ID) noexcept { + return EdgeValue.First == ID ? EdgeValue.Second : EdgeValue.First; + } + + [[nodiscard]] static CostMatrix orientFrom(const WorkingEdge &EdgeValue, NodeID ID) { + return EdgeValue.First == ID ? EdgeValue.Costs : EdgeValue.Costs.transpose(); + } + + [[nodiscard]] std::vector<WorkingEdge> incidentEdges(NodeID ID) const { + std::vector<WorkingEdge> Result; + for (const auto &[EdgeKeyValue, EdgeValue] : Edges) { + (void)EdgeKeyValue; + if (Active[EdgeValue.First.value()] && Active[EdgeValue.Second.value()] && + (EdgeValue.First == ID || EdgeValue.Second == ID)) + Result.push_back(EdgeValue); + } + return Result; + } + + void removeIncidentEdges(NodeID ID) { + for (auto Position = Edges.begin(); Position != Edges.end();) { + if (Position->second.First == ID || Position->second.Second == ID) + Position = Edges.erase(Position); + else + ++Position; + } + } + + void deactivate(NodeID ID) noexcept { + Active[ID.value()] = false; + --ActiveCount; + } + + std::vector<CostVector> Costs; + std::vector<std::uint64_t> StableKeys; + std::vector<bool> Active; + std::size_t ActiveCount = 0; + std::map<EdgeKey, WorkingEdge> Edges; + }; +}; + +} // namespace rcc::pbqp + +#endif // RCC_CODEGEN_PBQP_SOLVER_H diff --git a/backend/include/rcc/CodeGen/PHIElimination.h b/backend/include/rcc/CodeGen/PHIElimination.h new file mode 100644 index 0000000..244ce04 --- /dev/null +++ b/backend/include/rcc/CodeGen/PHIElimination.h @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_PHIELIMINATION_H +#define RCC_CODEGEN_PHIELIMINATION_H + +#include "rcc/CodeGen/MachinePassManager.h" + +namespace rcc { + +class TargetInstrInfo; + +class PHIEliminationPass final : public MachineFunctionPass { +public: + explicit PHIEliminationPass(const TargetInstrInfo &Info) noexcept : Info(Info) {} + + [[nodiscard]] const MachinePassContract &contract() const noexcept override; + [[nodiscard]] Expected<MachinePassResult> run(MachineFunction &Function, + const TargetDescription &Target, + MachineFunctionAnalysisManager &Analyses) override; + +private: + [[nodiscard]] Expected<MachinePassResult> + runImplementation(MachineFunction &Function, const TargetDescription &Target, + MachineFunctionAnalysisManager &Analyses); + + const TargetInstrInfo &Info; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_PHIELIMINATION_H diff --git a/backend/include/rcc/CodeGen/PostRAScheduler.h b/backend/include/rcc/CodeGen/PostRAScheduler.h new file mode 100644 index 0000000..3254d1e --- /dev/null +++ b/backend/include/rcc/CodeGen/PostRAScheduler.h @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_POSTRASCHEDULER_H +#define RCC_CODEGEN_POSTRASCHEDULER_H + +#include "rcc/CodeGen/MachinePassManager.h" +#include "rcc/CodeGen/MachineScheduler.h" + +namespace rcc { + +class TargetSchedulingModel; + +class PostRASchedulerPass final : public MachineFunctionPass { +public: + explicit PostRASchedulerPass(const TargetSchedulingModel &Model, + MachineSchedulerOptions Options = {}) noexcept + : Model(&Model), Options(Options) {} + + [[nodiscard]] const MachinePassContract &contract() const noexcept override; + [[nodiscard]] Expected<MachinePassResult> run(MachineFunction &Function, + const TargetDescription &Target, + MachineFunctionAnalysisManager &Analyses) override; + +private: + const TargetSchedulingModel *Model; + MachineSchedulerOptions Options; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_POSTRASCHEDULER_H diff --git a/backend/include/rcc/CodeGen/PrologueEpilogueInsertion.h b/backend/include/rcc/CodeGen/PrologueEpilogueInsertion.h new file mode 100644 index 0000000..dfade65 --- /dev/null +++ b/backend/include/rcc/CodeGen/PrologueEpilogueInsertion.h @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_PROLOGUEEPILOGUEINSERTION_H +#define RCC_CODEGEN_PROLOGUEEPILOGUEINSERTION_H + +#include "rcc/CodeGen/MachinePassManager.h" +#include "rcc/CodeGen/TargetFrameLowering.h" + +namespace rcc { + +class PrologueEpilogueInsertionPass final : public MachineFunctionPass { +public: + explicit PrologueEpilogueInsertionPass(const TargetFrameLowering &Info, + FrameLoweringOptions Options = {}) noexcept + : Info(&Info), Options(Options) {} + + [[nodiscard]] const MachinePassContract &contract() const noexcept override; + [[nodiscard]] Expected<MachinePassResult> run(MachineFunction &Function, + const TargetDescription &Target, + MachineFunctionAnalysisManager &Analyses) override; + +private: + [[nodiscard]] Expected<MachinePassResult> + runImplementation(MachineFunction &Function, const TargetDescription &Target, + FrameLoweringOptions RunOptions) const; + + const TargetFrameLowering *Info; + FrameLoweringOptions Options; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_PROLOGUEEPILOGUEINSERTION_H diff --git a/backend/include/rcc/CodeGen/ReachingDefAnalysis.h b/backend/include/rcc/CodeGen/ReachingDefAnalysis.h new file mode 100644 index 0000000..64eb462 --- /dev/null +++ b/backend/include/rcc/CodeGen/ReachingDefAnalysis.h @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_REACHINGDEFANALYSIS_H +#define RCC_CODEGEN_REACHINGDEFANALYSIS_H + +#include "rcc/CodeGen/MachineFunctionAnalysisManager.h" +#include "rcc/Support/Error.h" + +#include <cstddef> +#include <span> +#include <string_view> +#include <unordered_map> +#include <vector> + +namespace rcc { + +class MachineFunction; +class MachineInstr; +class MachineOperand; +class TargetDescription; + +class ReachingDefAnalysis final { +public: + using DefinitionSet = std::vector<const MachineInstr *>; + + class Result final { + public: + [[nodiscard]] std::span<const MachineInstr *const> + reachingDefinitions(const MachineOperand &Operand) const noexcept; + [[nodiscard]] std::size_t instructionNumber(const MachineInstr &Instruction) const noexcept; + [[nodiscard]] bool invalidate(const MachineFunction &, + const PreservedAnalyses &Preserved) const noexcept; + + private: + std::unordered_map<const MachineOperand *, DefinitionSet> OperandDefinitions; + std::unordered_map<const MachineInstr *, std::size_t> InstructionNumbers; + + friend class ReachingDefAnalysis; + }; + + [[nodiscard]] static constexpr std::string_view name() noexcept { + return "ReachingDefAnalysis"; + } + + [[nodiscard]] static Expected<Result> run(MachineFunction &Function, + const TargetDescription &Target, + MachineFunctionAnalysisManager &Analyses); +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_REACHINGDEFANALYSIS_H diff --git a/backend/include/rcc/CodeGen/RegAllocCommon.h b/backend/include/rcc/CodeGen/RegAllocCommon.h new file mode 100644 index 0000000..5994d64 --- /dev/null +++ b/backend/include/rcc/CodeGen/RegAllocCommon.h @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_REGALLOCCOMMON_H +#define RCC_CODEGEN_REGALLOCCOMMON_H + +#include "rcc/CodeGen/MachinePassManager.h" + +#include <memory> + +namespace rcc { + +class TargetRegisterAllocationInfo; + +struct RegisterAllocationOptions final { + bool EnableRematerialization = true; + bool EnableCopyCoalescing = true; +}; + +[[nodiscard]] std::unique_ptr<MachineFunctionPass> +createBasicAllocatorPass(const TargetRegisterAllocationInfo &Info, + RegisterAllocationOptions Options = {}); +[[nodiscard]] std::unique_ptr<MachineFunctionPass> +createGreedyAllocatorPass(const TargetRegisterAllocationInfo &Info, + RegisterAllocationOptions Options = {}); + +} // namespace rcc + +#endif // RCC_CODEGEN_REGALLOCCOMMON_H diff --git a/backend/include/rcc/CodeGen/RegAllocFast.h b/backend/include/rcc/CodeGen/RegAllocFast.h new file mode 100644 index 0000000..6ee2881 --- /dev/null +++ b/backend/include/rcc/CodeGen/RegAllocFast.h @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_REGALLOCFAST_H +#define RCC_CODEGEN_REGALLOCFAST_H + +#include "rcc/CodeGen/MachinePassManager.h" +#include "rcc/CodeGen/TargetRegisterAllocationInfo.h" + +namespace rcc { + +class FastAllocatorPass final : public MachineFunctionPass { +public: + explicit FastAllocatorPass(const TargetRegisterAllocationInfo &Info) noexcept : Info(&Info) {} + + [[nodiscard]] const MachinePassContract &contract() const noexcept override; + [[nodiscard]] Expected<MachinePassResult> run(MachineFunction &Function, + const TargetDescription &Target, + MachineFunctionAnalysisManager &Analyses) override; + +private: + const TargetRegisterAllocationInfo *Info; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_REGALLOCFAST_H diff --git a/backend/include/rcc/CodeGen/RegAllocPBQP.h b/backend/include/rcc/CodeGen/RegAllocPBQP.h new file mode 100644 index 0000000..0ae0088 --- /dev/null +++ b/backend/include/rcc/CodeGen/RegAllocPBQP.h @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_REGALLOCPBQP_H +#define RCC_CODEGEN_REGALLOCPBQP_H + +#include "rcc/CodeGen/RegAllocCommon.h" +#include "rcc/CodeGen/TargetRegisterAllocationInfo.h" + +namespace rcc { + +class PBQPAllocatorPass final : public MachineFunctionPass { +public: + explicit PBQPAllocatorPass(const TargetRegisterAllocationInfo &Info, + RegisterAllocationOptions Options = {}) noexcept + : Info(&Info), Options(Options) {} + + [[nodiscard]] const MachinePassContract &contract() const noexcept override; + [[nodiscard]] Expected<MachinePassResult> run(MachineFunction &Function, + const TargetDescription &Target, + MachineFunctionAnalysisManager &Analyses) override; + +private: + const TargetRegisterAllocationInfo *Info; + RegisterAllocationOptions Options; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_REGALLOCPBQP_H diff --git a/backend/include/rcc/CodeGen/Register.h b/backend/include/rcc/CodeGen/Register.h new file mode 100644 index 0000000..7d98a21 --- /dev/null +++ b/backend/include/rcc/CodeGen/Register.h @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_REGISTER_H +#define RCC_CODEGEN_REGISTER_H + +#include "rcc/Support/Error.h" + +#include <cstddef> +#include <cstdint> +#include <functional> +#include <optional> +#include <string> +#include <type_traits> + +namespace rcc { + +class PhysicalRegister final { +public: + constexpr PhysicalRegister() noexcept = default; + + [[nodiscard]] static Expected<PhysicalRegister> create(std::uint64_t Number); + + [[nodiscard]] constexpr bool isValid() const noexcept { + return Number != 0; + } + + [[nodiscard]] constexpr std::uint32_t number() const noexcept { + return Number; + } + + friend constexpr bool operator==(PhysicalRegister Left, + PhysicalRegister Right) noexcept = default; + +private: + explicit constexpr PhysicalRegister(std::uint32_t Number) noexcept : Number(Number) {} + + std::uint32_t Number = 0; + + friend class Register; +}; + +class Register final { +public: + constexpr Register() noexcept = default; + explicit constexpr Register(std::uint32_t OpaqueValue) noexcept : OpaqueValue(OpaqueValue) {} + + [[nodiscard]] static Expected<Register> makePhysical(std::uint64_t Number); + [[nodiscard]] static Expected<Register> makeVirtual(std::uint64_t Index); + + [[nodiscard]] static constexpr Register fromPhysical(PhysicalRegister PhysReg) noexcept { + return Register(PhysReg.Number); + } + + [[nodiscard]] constexpr bool isNoRegister() const noexcept { + return OpaqueValue == 0; + } + + [[nodiscard]] constexpr bool isPhysical() const noexcept { + return OpaqueValue != 0 && (OpaqueValue & VirtualBit) == 0; + } + + [[nodiscard]] constexpr bool isVirtual() const noexcept { + return (OpaqueValue & VirtualBit) != 0; + } + + [[nodiscard]] std::optional<PhysicalRegister> physicalRegister() const noexcept; + [[nodiscard]] std::optional<std::uint32_t> virtualIndex() const noexcept; + + [[nodiscard]] constexpr std::uint32_t opaqueValue() const noexcept { + return OpaqueValue; + } + + [[nodiscard]] std::string toString() const; + + friend constexpr bool operator==(Register Left, Register Right) noexcept = default; + +private: + static constexpr std::uint32_t VirtualBit = std::uint32_t{1} << 31; + static constexpr std::uint32_t IndexMask = VirtualBit - 1; + + std::uint32_t OpaqueValue = 0; +}; + +static_assert(sizeof(PhysicalRegister) == sizeof(std::uint32_t)); +static_assert(sizeof(Register) == sizeof(std::uint32_t)); +static_assert(std::is_trivially_copyable_v<PhysicalRegister>); +static_assert(std::is_trivially_copyable_v<Register>); + +} // namespace rcc + +template <> struct std::hash<rcc::PhysicalRegister> { + [[nodiscard]] std::size_t operator()(rcc::PhysicalRegister PhysReg) const noexcept { + return std::hash<std::uint32_t>{}(PhysReg.number()); + } +}; + +template <> struct std::hash<rcc::Register> { + [[nodiscard]] std::size_t operator()(rcc::Register Reg) const noexcept { + return std::hash<std::uint32_t>{}(Reg.opaqueValue()); + } +}; + +#endif // RCC_CODEGEN_REGISTER_H diff --git a/backend/include/rcc/CodeGen/RegisterBankInfo.h b/backend/include/rcc/CodeGen/RegisterBankInfo.h new file mode 100644 index 0000000..bc918cc --- /dev/null +++ b/backend/include/rcc/CodeGen/RegisterBankInfo.h @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_REGISTERBANKINFO_H +#define RCC_CODEGEN_REGISTERBANKINFO_H + +#include "rcc/CodeGen/MachineIRTypes.h" +#include "rcc/CodeGen/Register.h" +#include "rcc/Support/Error.h" + +#include <optional> +#include <vector> + +namespace rcc { + +class MachineFunction; +class MachineInstr; +class TargetDescription; +struct VirtualRegisterInfo; + +struct RegisterBankSelection final { + RegisterBankID Bank; + bool IsConstrained = false; +}; + +using RegisterBankInstructionMapping = std::vector<std::optional<RegisterBankID>>; + +class RegisterBankInfo { +public: + virtual ~RegisterBankInfo() = default; + + [[nodiscard]] virtual Expected<RegisterBankSelection> + selectBank(const MachineFunction &Function, Register Reg, const VirtualRegisterInfo &Info, + const MachineInstr *Context, const TargetDescription &Target) const = 0; + + [[nodiscard]] virtual Expected<RegisterBankInstructionMapping> + getInstructionMapping(const MachineInstr &, const MachineFunction &, + const TargetDescription &) const { + return RegisterBankInstructionMapping{}; + } + + // This hook rejects invalid mappings before register-bank state is published. + [[nodiscard]] virtual Expected<void> validateInstruction(const MachineInstr &, + const MachineFunction &, + const TargetDescription &) const { + return {}; + } +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_REGISTERBANKINFO_H diff --git a/backend/include/rcc/CodeGen/RegisterMask.h b/backend/include/rcc/CodeGen/RegisterMask.h new file mode 100644 index 0000000..d5a5b71 --- /dev/null +++ b/backend/include/rcc/CodeGen/RegisterMask.h @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_REGISTERMASK_H +#define RCC_CODEGEN_REGISTERMASK_H + +#include "rcc/CodeGen/Register.h" + +#include <cstddef> +#include <cstdint> +#include <span> +#include <utility> +#include <vector> + +namespace rcc { + +class RegisterMask final { +public: + explicit RegisterMask(std::vector<std::uint64_t> PreservedWords) + : PreservedWords(std::move(PreservedWords)) {} + + [[nodiscard]] bool preserves(PhysicalRegister PhysReg) const noexcept; + + [[nodiscard]] bool clobbers(PhysicalRegister PhysReg) const noexcept { + return !preserves(PhysReg); + } + + [[nodiscard]] std::span<const std::uint64_t> words() const noexcept { + return PreservedWords; + } + + friend bool operator==(const RegisterMask &Left, const RegisterMask &Right) = default; + +private: + std::vector<std::uint64_t> PreservedWords; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_REGISTERMASK_H diff --git a/backend/include/rcc/CodeGen/RegisterPressure.h b/backend/include/rcc/CodeGen/RegisterPressure.h new file mode 100644 index 0000000..107bc29 --- /dev/null +++ b/backend/include/rcc/CodeGen/RegisterPressure.h @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_REGISTERPRESSURE_H +#define RCC_CODEGEN_REGISTERPRESSURE_H + +#include "rcc/CodeGen/MachineIRTypes.h" +#include "rcc/CodeGen/ScheduleDAG.h" +#include "rcc/Support/Error.h" + +#include <cstddef> +#include <cstdint> +#include <span> +#include <unordered_map> +#include <vector> + +namespace rcc { + +class MachineFunction; +class TargetDescription; +class TargetSchedulingModel; +struct MachineBlockLiveness; + +namespace detail { +struct RegisterPressureBuilder; +} // namespace detail + +struct RegisterPressureValue final { + RegisterPressureSetID Set; + std::uint32_t Current = 0; + std::uint32_t Maximum = 0; + std::uint32_t Limit = 0; + + friend constexpr bool operator==(RegisterPressureValue, RegisterPressureValue) noexcept = default; +}; + +struct RegisterPressureCost final { + std::uint64_t CurrentPressure = 0; + std::uint64_t CurrentExcess = 0; + std::uint64_t PeakExcess = 0; + std::uint64_t PeakIncrease = 0; + + friend constexpr bool operator==(RegisterPressureCost, RegisterPressureCost) noexcept = default; +}; + +enum class RegisterPressureDirection : std::uint8_t { + TopDown, + BottomUp, +}; + +class RegisterPressureTracker final { +public: + [[nodiscard]] static Expected<RegisterPressureTracker> + create(const ScheduleDAG &DAG, const MachineFunction &Function, const TargetDescription &Target, + const TargetSchedulingModel &Model, ScheduleDAGMode Mode, + const MachineBlockLiveness &Liveness, + RegisterPressureDirection Direction = RegisterPressureDirection::TopDown); + + [[nodiscard]] std::span<const RegisterPressureValue> pressure() const noexcept { + return Pressure; + } + + [[nodiscard]] bool wasScheduled(std::size_t UnitID) const noexcept { + return UnitID < Scheduled.size() && Scheduled[UnitID]; + } + + [[nodiscard]] Expected<RegisterPressureCost> evaluate(std::size_t UnitID) const; + [[nodiscard]] Expected<void> schedule(std::size_t UnitID); + [[nodiscard]] RegisterPressureCost cost() const noexcept; + +private: + using EntityID = std::size_t; + + struct Entity final { + std::vector<RegisterPressureSetID> Sets; + std::size_t RemainingUses = 0; + bool HasDefinition = false; + bool DefinitionPending = false; + bool LiveOut = false; + bool Live = false; + }; + + struct Event final { + std::vector<EntityID> Uses; + std::vector<EntityID> Definitions; + }; + + struct UnitEffects final { + std::vector<Event> Events; + }; + + [[nodiscard]] Expected<void> applyEvent(const Event &Value); + [[nodiscard]] Expected<void> makeLive(EntityID Entity); + [[nodiscard]] Expected<void> makeDead(EntityID Entity); + + std::vector<RegisterPressureValue> Pressure; + std::unordered_map<RegisterPressureSetID, std::size_t> PressureIndexes; + std::vector<Entity> Entities; + std::vector<UnitEffects> Effects; + std::vector<bool> Scheduled; + RegisterPressureDirection Direction = RegisterPressureDirection::TopDown; + + friend struct detail::RegisterPressureBuilder; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_REGISTERPRESSURE_H diff --git a/backend/include/rcc/CodeGen/RegisterScavenger.h b/backend/include/rcc/CodeGen/RegisterScavenger.h new file mode 100644 index 0000000..6351b98 --- /dev/null +++ b/backend/include/rcc/CodeGen/RegisterScavenger.h @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_REGISTERSCAVENGER_H +#define RCC_CODEGEN_REGISTERSCAVENGER_H + +#include "rcc/CodeGen/Register.h" +#include "rcc/Support/Error.h" + +#include <span> + +namespace rcc { + +class MachineFunction; +class MachineInstr; +class TargetDescription; + +struct ScavengedRegister final { + PhysicalRegister Reg; + bool RequiresSpill = false; +}; + +class RegisterScavenger final { +public: + RegisterScavenger(const MachineFunction &Function, const TargetDescription &Target) noexcept + : Function(&Function), Target(&Target) {} + + [[nodiscard]] Expected<ScavengedRegister> + scavenge(const MachineInstr &Instruction, std::span<const PhysicalRegister> Candidates) const; + +private: + [[nodiscard]] Expected<bool> isLiveBefore(const MachineInstr &Instruction, + PhysicalRegister Reg) const; + [[nodiscard]] bool instructionUses(const MachineInstr &Instruction, + PhysicalRegister Reg) const noexcept; + + const MachineFunction *Function; + const TargetDescription *Target; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_REGISTERSCAVENGER_H diff --git a/backend/include/rcc/CodeGen/ScheduleDAG.h b/backend/include/rcc/CodeGen/ScheduleDAG.h new file mode 100644 index 0000000..7634202 --- /dev/null +++ b/backend/include/rcc/CodeGen/ScheduleDAG.h @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_SCHEDULEDAG_H +#define RCC_CODEGEN_SCHEDULEDAG_H + +#include "rcc/CodeGen/StableID.h" +#include "rcc/CodeGen/TargetSchedulingModel.h" +#include "rcc/Support/Error.h" + +#include <cstddef> +#include <cstdint> +#include <optional> +#include <span> +#include <vector> + +namespace rcc { + +class MachineBasicBlock; +class MachineFunction; +class MachineInstr; +class TargetDescription; + +enum class ScheduleDAGMode : std::uint8_t { + PreRegisterAllocation, + PostRegisterAllocation, +}; + +enum class ScheduleDependencyKind : std::uint8_t { + Data, + Anti, + Output, + Order, + Memory, + Barrier, + Artificial, + Cluster, + RegisterUnit, + RegisterMask, +}; + +struct ScheduleDependency final { + std::size_t Source = 0; + std::size_t Destination = 0; + ScheduleDependencyKind Kind = ScheduleDependencyKind::Artificial; + std::uint32_t Latency = 0; + + friend constexpr bool operator==(const ScheduleDependency &, + const ScheduleDependency &) noexcept = default; +}; + +struct ScheduleUnit final { + std::size_t ID = 0; + std::vector<MachineInstr *> Instructions; + std::vector<ScheduleDependency> Predecessors; + std::vector<ScheduleDependency> Successors; + std::vector<SchedulingResourceUse> Resources; + std::uint32_t MicroOps = 0; + std::uint32_t Latency = 0; + std::uint64_t Depth = 0; + std::uint64_t Height = 0; + MachineInstrStableID StableID; + std::optional<std::size_t> ClusterPredecessor; + std::optional<std::size_t> ClusterSuccessor; +}; + +class ScheduleDAG final { +public: + [[nodiscard]] static Expected<ScheduleDAG> + build(MachineBasicBlock &Block, std::span<MachineInstr *const> Instructions, + const MachineFunction &Function, const TargetDescription &Target, + const TargetSchedulingModel &Model, ScheduleDAGMode Mode, bool EmitDebugOutput = false); + + [[nodiscard]] MachineBasicBlock &block() const noexcept { + return *Block; + } + + [[nodiscard]] std::span<const ScheduleUnit> units() const noexcept { + return Units; + } + + [[nodiscard]] std::span<ScheduleUnit> units() noexcept { + return Units; + } + + [[nodiscard]] const ScheduleUnit *unit(std::size_t ID) const noexcept { + return ID < Units.size() ? &Units[ID] : nullptr; + } + + [[nodiscard]] Expected<void> addArtificialDependency(std::size_t Source, std::size_t Destination, + std::uint32_t Latency); + [[nodiscard]] Expected<bool> fuseInstructionPair(std::size_t First, std::size_t Second); + +private: + explicit ScheduleDAG(MachineBasicBlock &Block) : Block(&Block) {} + + [[nodiscard]] Expected<void> buildUnits(std::span<MachineInstr *const> Instructions, + const TargetDescription &Target, + const TargetSchedulingModel &Model); + [[nodiscard]] Expected<void> buildRegisterDependencies(const MachineFunction &Function, + const TargetDescription &Target, + const TargetSchedulingModel &Model, + ScheduleDAGMode Mode); + [[nodiscard]] Expected<void> buildMemoryDependencies(const TargetDescription &Target); + [[nodiscard]] Expected<void> buildOrderDependencies(const TargetDescription &Target); + [[nodiscard]] bool hasPath(std::size_t Source, std::size_t Destination) const; + [[nodiscard]] bool hasDirectDependency(std::size_t Source, std::size_t Destination) const; + void addDependency(std::size_t Source, std::size_t Destination, ScheduleDependencyKind Kind, + std::uint32_t Latency); + [[nodiscard]] Expected<void> computeCriticalPaths(); + + MachineBasicBlock *Block; + std::vector<ScheduleUnit> Units; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_SCHEDULEDAG_H diff --git a/backend/include/rcc/CodeGen/ScheduleHazardRecognizer.h b/backend/include/rcc/CodeGen/ScheduleHazardRecognizer.h new file mode 100644 index 0000000..947db5e --- /dev/null +++ b/backend/include/rcc/CodeGen/ScheduleHazardRecognizer.h @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_SCHEDULEHAZARDRECOGNIZER_H +#define RCC_CODEGEN_SCHEDULEHAZARDRECOGNIZER_H + +#include "rcc/CodeGen/TargetSchedulingModel.h" +#include "rcc/Support/Error.h" + +#include <cstddef> +#include <cstdint> +#include <memory> +#include <unordered_map> +#include <vector> + +namespace rcc { + +struct ScheduleUnit; + +class ScheduleHazardRecognizer final { +public: + [[nodiscard]] static Expected<ScheduleHazardRecognizer> + create(const TargetSchedulingModel &Model, + ScheduleDirection Direction = ScheduleDirection::TopDown); + + ScheduleHazardRecognizer(const ScheduleHazardRecognizer &Other); + ScheduleHazardRecognizer &operator=(const ScheduleHazardRecognizer &Other); + ScheduleHazardRecognizer(ScheduleHazardRecognizer &&) noexcept = default; + ScheduleHazardRecognizer &operator=(ScheduleHazardRecognizer &&) noexcept = default; + ~ScheduleHazardRecognizer() = default; + + [[nodiscard]] std::uint64_t currentCycle() const noexcept { + return CurrentCycle; + } + + [[nodiscard]] Expected<bool> canIssue(const ScheduleUnit &Unit, std::uint64_t Cycle) const; + [[nodiscard]] Expected<std::uint64_t> earliestIssueCycle(const ScheduleUnit &Unit, + std::uint64_t Earliest) const; + [[nodiscard]] Expected<void> issue(const ScheduleUnit &Unit, std::uint64_t Cycle); + +private: + struct ResourceState final { + ProcessorResourceID ID; + std::vector<std::uint64_t> AvailableAt; + }; + + ScheduleHazardRecognizer(const TargetSchedulingModel &Model, ScheduleDirection Direction) noexcept + : Model(&Model), Direction(Direction) {} + + [[nodiscard]] Expected<void> reset(); + [[nodiscard]] Expected<void> moveToCycle(std::uint64_t Cycle); + [[nodiscard]] Expected<void> issueImplementation(const ScheduleUnit &Unit, std::uint64_t Cycle); + [[nodiscard]] Expected<void> reserveResource(ProcessorResourceID Resource, std::uint32_t Cycles, + std::uint64_t Cycle); + + const TargetSchedulingModel *Model; + ScheduleDirection Direction = ScheduleDirection::TopDown; + std::unique_ptr<TargetScheduleHazardRecognizer> TargetHazards; + std::vector<ResourceState> Resources; + std::unordered_map<ProcessorResourceID, std::size_t> ResourceIndexes; + std::uint64_t CurrentCycle = 0; + std::uint64_t DispatchAvailableAt = 0; + std::uint32_t IssuedMicroOps = 0; + std::uint32_t DispatchedUnits = 0; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_SCHEDULEHAZARDRECOGNIZER_H diff --git a/backend/include/rcc/CodeGen/SlotIndexes.h b/backend/include/rcc/CodeGen/SlotIndexes.h new file mode 100644 index 0000000..423cc64 --- /dev/null +++ b/backend/include/rcc/CodeGen/SlotIndexes.h @@ -0,0 +1,131 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_SLOTINDEXES_H +#define RCC_CODEGEN_SLOTINDEXES_H + +#include "rcc/CodeGen/MachineIRTypes.h" +#include "rcc/CodeGen/MachinePassManager.h" +#include "rcc/CodeGen/StableID.h" +#include "rcc/Support/Error.h" + +#include <compare> +#include <cstddef> +#include <cstdint> +#include <string_view> +#include <unordered_map> +#include <vector> + +namespace rcc { + +class MachineBasicBlock; +class MachineFunction; +class MachineInstr; +class TargetDescription; + +enum class SlotIndexKind : std::uint8_t { + Block, + EarlyClobber, + Register, + Dead, +}; + +class SlotIndex final { +public: + constexpr SlotIndex() noexcept = default; + + [[nodiscard]] static constexpr SlotIndex fromValue(std::uint64_t Value) noexcept { + return SlotIndex(Value); + } + + [[nodiscard]] constexpr bool isValid() const noexcept { + return Value != 0; + } + + [[nodiscard]] constexpr std::uint64_t value() const noexcept { + return Value; + } + + [[nodiscard]] constexpr SlotIndexKind kind() const noexcept { + return static_cast<SlotIndexKind>(Value & KindMask); + } + + [[nodiscard]] constexpr SlotIndex baseIndex() const noexcept { + return SlotIndex(Value & ~KindMask); + } + + [[nodiscard]] constexpr SlotIndex withKind(SlotIndexKind Kind) const noexcept { + return isValid() ? SlotIndex((Value & ~KindMask) | static_cast<std::uint64_t>(Kind)) + : SlotIndex{}; + } + + [[nodiscard]] constexpr SlotIndex earlyClobberSlot() const noexcept { + return withKind(SlotIndexKind::EarlyClobber); + } + + [[nodiscard]] constexpr SlotIndex registerSlot() const noexcept { + return withKind(SlotIndexKind::Register); + } + + [[nodiscard]] constexpr SlotIndex deadSlot() const noexcept { + return withKind(SlotIndexKind::Dead); + } + + friend constexpr bool operator==(SlotIndex, SlotIndex) noexcept = default; + friend constexpr auto operator<=>(SlotIndex, SlotIndex) noexcept = default; + +private: + static constexpr std::uint64_t KindMask = 3; + + explicit constexpr SlotIndex(std::uint64_t Value) noexcept : Value(Value) {} + + std::uint64_t Value = 0; +}; + +class SlotIndexesAnalysis final { +public: + class Result final { + public: + [[nodiscard]] SlotIndex instructionIndex(const MachineInstr &Instruction) const noexcept; + [[nodiscard]] SlotIndex slot(const MachineInstr &Instruction) const noexcept { + return instructionIndex(Instruction); + } + [[nodiscard]] SlotIndex blockStart(const MachineBasicBlock &Block) const noexcept; + [[nodiscard]] SlotIndex blockEnd(const MachineBasicBlock &Block) const noexcept; + [[nodiscard]] const MachineInstr *instructionFromIndex(SlotIndex Index) const noexcept; + [[nodiscard]] const MachineBasicBlock *blockFromIndex(SlotIndex Index) const noexcept; + + [[nodiscard]] Expected<void> insertInstruction(const MachineInstr &Instruction); + [[nodiscard]] Expected<void> eraseInstruction(const MachineInstr &Instruction); + [[nodiscard]] Expected<void> renumber(MachineFunction &Function); + + [[nodiscard]] bool invalidate(const MachineFunction &, + const PreservedAnalyses &Preserved) const noexcept; + + private: + std::unordered_map<MachineInstrStableID, SlotIndex> InstructionSlots; + std::unordered_map<std::uint64_t, const MachineInstr *> SlotToInstruction; + std::unordered_map<const MachineBasicBlock *, SlotIndex> BlockStarts; + std::unordered_map<const MachineBasicBlock *, SlotIndex> BlockEnds; + std::vector<const MachineBasicBlock *> BlocksByStart; + + friend class SlotIndexesAnalysis; + }; + + [[nodiscard]] static constexpr std::string_view name() noexcept { + return "SlotIndexes"; + } + + [[nodiscard]] static Expected<Result> run(MachineFunction &Function, + const TargetDescription &Target, + MachineFunctionAnalysisManager &Analyses); +}; + +} // namespace rcc + +template <> struct std::hash<rcc::SlotIndex> { + [[nodiscard]] std::size_t operator()(rcc::SlotIndex Index) const noexcept { + return std::hash<std::uint64_t>{}(Index.value()); + } +}; + +#endif // RCC_CODEGEN_SLOTINDEXES_H diff --git a/backend/include/rcc/CodeGen/Spiller.h b/backend/include/rcc/CodeGen/Spiller.h new file mode 100644 index 0000000..b44b8f2 --- /dev/null +++ b/backend/include/rcc/CodeGen/Spiller.h @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_SPILLER_H +#define RCC_CODEGEN_SPILLER_H + +#include "rcc/CodeGen/LiveRangeEdit.h" +#include "rcc/CodeGen/MachineFunctionAnalysisManager.h" +#include "rcc/CodeGen/TargetRegisterAllocationInfo.h" + +#include <optional> +#include <vector> + +namespace rcc { + +class Spiller final { +public: + Spiller(MachineFunction &Function, const TargetDescription &Target, + const TargetRegisterAllocationInfo &TargetInfo, VirtRegMap &Mapping, + bool EnableRematerialization = true, + MachineFunctionAnalysisManager *ActionAnalyses = nullptr) + : Function(&Function), Target(&Target), TargetInfo(&TargetInfo), Mapping(&Mapping), + Edit(Function, Target, Mapping), EnableRematerialization(EnableRematerialization), + ActionAnalyses(ActionAnalyses) {} + + [[nodiscard]] Expected<std::vector<Register>> spill(Register VirtualReg); + +private: + [[nodiscard]] Expected<std::vector<Register>> spillAtInstructions(Register VirtualReg); + [[nodiscard]] Expected<void> spillLiveIn(Register VirtualReg, RegisterClassID Class, + FrameIndex StackSlot); + [[nodiscard]] Expected<std::optional<std::vector<Register>>> + tryRematerialize(Register VirtualReg); + + MachineFunction *Function; + const TargetDescription *Target; + const TargetRegisterAllocationInfo *TargetInfo; + VirtRegMap *Mapping; + LiveRangeEdit Edit; + bool EnableRematerialization; + MachineFunctionAnalysisManager *ActionAnalyses; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_SPILLER_H diff --git a/backend/include/rcc/CodeGen/StableID.h b/backend/include/rcc/CodeGen/StableID.h new file mode 100644 index 0000000..89a4266 --- /dev/null +++ b/backend/include/rcc/CodeGen/StableID.h @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_STABLEID_H +#define RCC_CODEGEN_STABLEID_H + +#include "rcc/Support/Error.h" + +#include <cstddef> +#include <cstdint> +#include <functional> +#include <limits> +#include <type_traits> + +namespace rcc { + +template <typename TagT> class StableID final { +public: + constexpr StableID() noexcept = default; + + [[nodiscard]] static constexpr StableID fromValue(std::uint64_t Value) noexcept { + return StableID(Value); + } + + [[nodiscard]] constexpr bool isValid() const noexcept { + return Value != 0; + } + + [[nodiscard]] constexpr std::uint64_t value() const noexcept { + return Value; + } + + friend constexpr bool operator==(StableID Left, StableID Right) noexcept = default; + +private: + explicit constexpr StableID(std::uint64_t Value) noexcept : Value(Value) {} + + std::uint64_t Value = 0; +}; + +namespace detail { + +struct MachineFunctionStableIDTag; +struct MachineBlockStableIDTag; +struct MachineInstrStableIDTag; + +} // namespace detail + +using MachineFunctionStableID = StableID<detail::MachineFunctionStableIDTag>; +using MachineBlockStableID = StableID<detail::MachineBlockStableIDTag>; +using MachineInstrStableID = StableID<detail::MachineInstrStableIDTag>; + +template <typename StableIDT> class StableIDGenerator final { +public: + StableIDGenerator() = default; + StableIDGenerator(const StableIDGenerator &) = delete; + StableIDGenerator &operator=(const StableIDGenerator &) = delete; + StableIDGenerator(StableIDGenerator &&) noexcept = default; + StableIDGenerator &operator=(StableIDGenerator &&) noexcept = default; + + [[nodiscard]] Expected<StableIDT> allocate() { + if (NextValue == 0) { + return makeUnexpected(ErrorCode::InternalError, "stable ID space is exhausted"); + } + + const StableIDT Result = StableIDT::fromValue(NextValue); + if (NextValue == std::numeric_limits<std::uint64_t>::max()) + NextValue = 0; + else + ++NextValue; + return Result; + } + +private: + std::uint64_t NextValue = 1; +}; + +static_assert(sizeof(MachineFunctionStableID) == sizeof(std::uint64_t)); +static_assert(sizeof(MachineBlockStableID) == sizeof(std::uint64_t)); +static_assert(sizeof(MachineInstrStableID) == sizeof(std::uint64_t)); +static_assert(std::is_trivially_copyable_v<MachineFunctionStableID>); +static_assert(std::is_trivially_copyable_v<MachineBlockStableID>); +static_assert(std::is_trivially_copyable_v<MachineInstrStableID>); + +} // namespace rcc + +template <typename TagT> struct std::hash<rcc::StableID<TagT>> { + [[nodiscard]] std::size_t operator()(rcc::StableID<TagT> ID) const noexcept { + return std::hash<std::uint64_t>{}(ID.value()); + } +}; + +#endif // RCC_CODEGEN_STABLEID_H diff --git a/backend/include/rcc/CodeGen/TargetCallLowering.h b/backend/include/rcc/CodeGen/TargetCallLowering.h new file mode 100644 index 0000000..576ec39 --- /dev/null +++ b/backend/include/rcc/CodeGen/TargetCallLowering.h @@ -0,0 +1,412 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_TARGETCALLLOWERING_H +#define RCC_CODEGEN_TARGETCALLLOWERING_H + +#include "rcc/CodeGen/LowLevelType.h" +#include "rcc/CodeGen/MachineIRTypes.h" +#include "rcc/CodeGen/Register.h" +#include "rcc/CodeGen/RegisterMask.h" +#include "rcc/Support/Error.h" + +#include <cstddef> +#include <cstdint> +#include <initializer_list> +#include <memory> +#include <optional> +#include <span> +#include <string> +#include <string_view> +#include <utility> +#include <variant> +#include <vector> + +namespace rcc { + +class MachineIRBuilder; + +enum class ABIExtensionKind : std::uint8_t { + None, + AnyExtend, + SignExtend, + ZeroExtend, + Count, +}; + +enum class ABIValueCategory : std::uint8_t { + Invalid, + Integer, + FloatingPoint, + Pointer, + Count, +}; + +enum class ABIValueFlag : std::uint8_t { + ByVal, + StructReturn, + Indirect, + InRegister, + Count, +}; + +class ABIValueFlags final { +public: + constexpr ABIValueFlags() noexcept = default; + + ABIValueFlags(std::initializer_list<ABIValueFlag> Flags) noexcept; + + [[nodiscard]] constexpr bool has(ABIValueFlag Flag) const noexcept { + const auto Index = static_cast<std::uint32_t>(Flag); + return Index < static_cast<std::uint32_t>(ABIValueFlag::Count) && + (Bits & (std::uint8_t{1} << Index)) != 0; + } + + constexpr void set(ABIValueFlag Flag) noexcept { + const auto Index = static_cast<std::uint32_t>(Flag); + if (Index < static_cast<std::uint32_t>(ABIValueFlag::Count)) + Bits |= std::uint8_t{1} << Index; + } + + [[nodiscard]] constexpr std::uint8_t rawValue() const noexcept { + return Bits; + } + + friend constexpr bool operator==(ABIValueFlags Left, ABIValueFlags Right) noexcept = default; + +private: + std::uint8_t Bits = 0; +}; + +struct ABIValue final { + Register Value; + LowLevelType Type; + ABIValueCategory Category = ABIValueCategory::Invalid; + std::uint32_t OriginalValue = 0; + std::uint16_t FragmentIndex = 0; + std::uint16_t FragmentCount = 1; + std::uint32_t FragmentOffsetBytes = 0; + Alignment OriginalAlignment; + ABIExtensionKind Extension = ABIExtensionKind::None; + ABIValueFlags Flags; + std::uint64_t ByValSizeBytes = 0; + Alignment ByValAlignment; + + friend bool operator==(const ABIValue &Left, const ABIValue &Right) = default; +}; + +enum class MachineCallTargetKind : std::uint8_t { + Invalid, + GlobalSymbol, + ExternalSymbol, + IndirectRegister, +}; + +class MachineCallTarget final { +public: + constexpr MachineCallTarget() noexcept = default; + + [[nodiscard]] static Expected<MachineCallTarget> globalSymbol(std::string Name, + std::uint32_t TargetFlags = 0); + [[nodiscard]] static Expected<MachineCallTarget> externalSymbol(std::string Name, + std::uint32_t TargetFlags = 0); + [[nodiscard]] static Expected<MachineCallTarget> indirect(Register Callee, + std::uint32_t TargetFlags = 0); + + [[nodiscard]] MachineCallTargetKind kind() const noexcept { + return Kind; + } + + [[nodiscard]] std::string_view symbolName() const noexcept; + [[nodiscard]] Register indirectRegister() const noexcept; + [[nodiscard]] std::uint32_t targetFlags() const noexcept { + return TargetFlags; + } + + [[nodiscard]] bool isValid() const noexcept { + return Kind != MachineCallTargetKind::Invalid; + } + + friend bool operator==(const MachineCallTarget &Left, const MachineCallTarget &Right) = default; + +private: + using Storage = std::variant<std::monostate, std::string, Register>; + + MachineCallTarget(MachineCallTargetKind Kind, Storage Value, std::uint32_t TargetFlags) noexcept + : Kind(Kind), Value(std::move(Value)), TargetFlags(TargetFlags) {} + + MachineCallTargetKind Kind = MachineCallTargetKind::Invalid; + Storage Value; + std::uint32_t TargetFlags = 0; +}; + +enum class TailCallKind : std::uint8_t { + NotTail, + Tail, + MustTail, +}; + +struct TailCallContext final { + CallingConventionID CallerConvention; + std::vector<ABIValue> CallerReturns; + std::uint64_t ReusableIncomingArgumentBytes = 0; + std::size_t CallerFixedArgumentCount = 0; + bool CallerIsVariadic = false; + bool CallerHasDynamicStackAllocation = false; + bool CallerUsesStructReturn = false; +}; + +struct FormalArgumentRequest final { + CallingConventionID Convention; + std::vector<ABIValue> Arguments; + std::size_t FixedArgumentCount = 0; + bool IsVariadic = false; +}; + +struct CallLoweringRequest final { + CallingConventionID Convention; + MachineCallTarget Callee; + std::vector<ABIValue> Arguments; + std::vector<ABIValue> Results; + std::size_t FixedArgumentCount = 0; + bool IsVariadic = false; + TailCallKind TailKind = TailCallKind::NotTail; + std::optional<TailCallContext> TailContext; +}; + +struct ReturnLoweringRequest final { + CallingConventionID Convention; + std::vector<ABIValue> Values; + bool FunctionIsVariadic = false; +}; + +enum class CCLocationKind : std::uint8_t { + Register, + Stack, + Indirect, + Custom, +}; + +enum class CCAssignmentFlag : std::uint8_t { + Variadic, + ByVal, + StructReturn, + Indirect, + InRegister, + Bitcast, + Count, +}; + +class CCAssignmentFlags final { +public: + constexpr CCAssignmentFlags() noexcept = default; + + CCAssignmentFlags(std::initializer_list<CCAssignmentFlag> Flags) noexcept; + + [[nodiscard]] constexpr bool has(CCAssignmentFlag Flag) const noexcept { + const auto Index = static_cast<std::uint32_t>(Flag); + return Index < static_cast<std::uint32_t>(CCAssignmentFlag::Count) && + (Bits & (std::uint8_t{1} << Index)) != 0; + } + + constexpr void set(CCAssignmentFlag Flag) noexcept { + const auto Index = static_cast<std::uint32_t>(Flag); + if (Index < static_cast<std::uint32_t>(CCAssignmentFlag::Count)) + Bits |= std::uint8_t{1} << Index; + } + + [[nodiscard]] constexpr std::uint8_t rawValue() const noexcept { + return Bits; + } + + friend constexpr bool operator==(CCAssignmentFlags Left, + CCAssignmentFlags Right) noexcept = default; + +private: + std::uint8_t Bits = 0; +}; + +struct CCAssignment final { + std::size_t ValueIndex = 0; + Register OriginalValue; + std::uint32_t OriginalValueIndex = 0; + std::uint16_t FragmentIndex = 0; + std::uint16_t FragmentCount = 1; + LowLevelType ValueType; + LowLevelType LocationType; + CCLocationKind LocationKind = CCLocationKind::Register; + std::optional<PhysicalRegister> LocationRegister; + std::optional<std::uint64_t> StackOffsetBytes; + std::uint64_t LocationSizeBytes = 0; + std::uint64_t StackSlotSizeBytes = 0; + Alignment LocationAlignment; + ABIExtensionKind Extension = ABIExtensionKind::None; + CCAssignmentFlags CustomFlags; + + friend bool operator==(const CCAssignment &Left, const CCAssignment &Right) = default; +}; + +struct StackArgumentArea final { + std::uint64_t SizeBytes = 0; + std::uint64_t ShadowSpaceBytes = 0; + Alignment Align; + + friend bool operator==(StackArgumentArea Left, StackArgumentArea Right) noexcept = default; +}; + +struct CallFrameRequirements final { + std::uint64_t SizeBytes = 0; + Alignment Align; + bool EmitSetup = false; + bool EmitDestroy = false; + bool ReusesIncomingArea = false; + + friend bool operator==(CallFrameRequirements Left, + CallFrameRequirements Right) noexcept = default; +}; + +struct VarArgRegisterSave final { + PhysicalRegister Reg; + std::uint64_t OffsetBytes = 0; + + friend bool operator==(VarArgRegisterSave Left, VarArgRegisterSave Right) noexcept = default; +}; + +struct VarArgLoweringState final { + bool IsVariadic = false; + std::size_t FixedArgumentCount = 0; + std::size_t FirstUnallocatedIntegerRegister = 0; + std::uint64_t OverflowAreaOffsetBytes = 0; + std::int64_t RegisterSaveAreaOffsetBytes = 0; + std::uint64_t RegisterSaveAreaSizeBytes = 0; + std::uint64_t RegisterSavePaddingBytes = 0; + std::vector<VarArgRegisterSave> RegistersToSave; + + friend bool operator==(const VarArgLoweringState &Left, + const VarArgLoweringState &Right) = default; +}; + +struct ByValCopyAssignment final { + std::size_t ArgumentAssignmentIndex = 0; + Register Source; + LowLevelType PointerType; + std::uint64_t StackOffsetBytes = 0; + std::uint64_t SizeBytes = 0; + Alignment Align; + + friend bool operator==(ByValCopyAssignment Left, ByValCopyAssignment Right) noexcept = default; +}; + +enum class CallLoweringPlanKind : std::uint8_t { + FormalArguments, + Call, + Return, +}; + +struct CallLoweringPlanDesc final { + CallLoweringPlanKind Kind = CallLoweringPlanKind::FormalArguments; + std::string Target; + CallingConventionID Convention; + std::vector<CCAssignment> ArgumentAssignments; + std::vector<CCAssignment> ReturnAssignments; + StackArgumentArea StackArguments; + CallFrameRequirements CallFrame; + VarArgLoweringState VarArgs; + std::vector<ByValCopyAssignment> ByValCopies; + std::shared_ptr<const RegisterMask> PreservedMask; + std::optional<MachineCallTarget> Callee; + TailCallKind TailDecision = TailCallKind::NotTail; + std::string TailRejectionReason; +}; + +class CallLoweringPlan final { +public: + [[nodiscard]] static Expected<CallLoweringPlan> create(CallLoweringPlanDesc Description); + + [[nodiscard]] CallLoweringPlanKind kind() const noexcept { + return Description.Kind; + } + + [[nodiscard]] std::string_view target() const noexcept { + return Description.Target; + } + + [[nodiscard]] CallingConventionID convention() const noexcept { + return Description.Convention; + } + + [[nodiscard]] std::span<const CCAssignment> argumentAssignments() const noexcept { + return Description.ArgumentAssignments; + } + + [[nodiscard]] std::span<const CCAssignment> returnAssignments() const noexcept { + return Description.ReturnAssignments; + } + + [[nodiscard]] const StackArgumentArea &stackArguments() const noexcept { + return Description.StackArguments; + } + + [[nodiscard]] const CallFrameRequirements &callFrame() const noexcept { + return Description.CallFrame; + } + + [[nodiscard]] const VarArgLoweringState &varArgs() const noexcept { + return Description.VarArgs; + } + + [[nodiscard]] std::span<const ByValCopyAssignment> byValCopies() const noexcept { + return Description.ByValCopies; + } + + [[nodiscard]] const std::shared_ptr<const RegisterMask> &preservedMask() const noexcept { + return Description.PreservedMask; + } + + [[nodiscard]] const MachineCallTarget *callee() const noexcept { + return Description.Callee ? &*Description.Callee : nullptr; + } + + [[nodiscard]] TailCallKind tailDecision() const noexcept { + return Description.TailDecision; + } + + [[nodiscard]] std::string_view tailRejectionReason() const noexcept { + return Description.TailRejectionReason; + } + +private: + explicit CallLoweringPlan(CallLoweringPlanDesc Description) + : Description(std::move(Description)) {} + + CallLoweringPlanDesc Description; +}; + +class TargetCallLowering { +public: + virtual ~TargetCallLowering() = default; + + [[nodiscard]] Expected<void> lowerFormalArguments(MachineIRBuilder &Builder, + const FormalArgumentRequest &Request) const; + [[nodiscard]] Expected<void> lowerCall(MachineIRBuilder &Builder, + const CallLoweringRequest &Request) const; + [[nodiscard]] Expected<void> lowerReturn(MachineIRBuilder &Builder, + const ReturnLoweringRequest &Request) const; + + [[nodiscard]] virtual Expected<CallLoweringPlan> + planFormalArguments(const FormalArgumentRequest &Request) const = 0; + [[nodiscard]] virtual Expected<CallLoweringPlan> + planCall(const CallLoweringRequest &Request) const = 0; + [[nodiscard]] virtual Expected<CallLoweringPlan> + planReturn(const ReturnLoweringRequest &Request) const = 0; + [[nodiscard]] virtual Expected<void> emitPlan(MachineIRBuilder &Builder, + const CallLoweringPlan &Plan) const = 0; +}; + +struct LibcallLoweringInfo final { + const TargetCallLowering *CallLowering = nullptr; + CallingConventionID Convention; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_TARGETCALLLOWERING_H diff --git a/backend/include/rcc/CodeGen/TargetConstant.h b/backend/include/rcc/CodeGen/TargetConstant.h new file mode 100644 index 0000000..37f352e --- /dev/null +++ b/backend/include/rcc/CodeGen/TargetConstant.h @@ -0,0 +1,259 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_TARGETCONSTANT_H +#define RCC_CODEGEN_TARGETCONSTANT_H + +#include "rcc/CodeGen/MachineIRTypes.h" +#include "rcc/CodeGen/MachineMemOperand.h" +#include "rcc/Support/Error.h" + +#include <concepts> +#include <cstddef> +#include <cstdint> +#include <memory> +#include <optional> +#include <span> +#include <string> +#include <string_view> +#include <utility> +#include <vector> + +namespace rcc { + +struct TargetConstantFixup final { + std::uint64_t Offset = 0; + TargetRelocationKindID Kind; + std::string Symbol; + std::int64_t Addend = 0; + + friend bool operator==(const TargetConstantFixup &Left, + const TargetConstantFixup &Right) = default; +}; + +struct TargetConstantEmission final { + std::vector<std::byte> Bytes; + std::vector<TargetConstantFixup> Fixups; + + [[nodiscard]] Expected<void> verify() const; + + friend bool operator==(const TargetConstantEmission &Left, + const TargetConstantEmission &Right) = default; +}; + +struct EncodedTargetConstant final { + TargetConstantTypeID TypeID; + std::uint32_t Version = 0; + std::vector<std::byte> Payload; + + friend bool operator==(const EncodedTargetConstant &Left, + const EncodedTargetConstant &Right) = default; +}; + +template <typename ValueT> +concept TargetConstantValue = + std::move_constructible<ValueT> && + requires(const ValueT &Value, const ValueT &Other, std::uint32_t Version, + std::span<const std::byte> Payload, std::string_view Text) { + { ValueT::stableTypeID() } -> std::same_as<TargetConstantTypeID>; + { ValueT::schemaVersion() } -> std::same_as<std::uint32_t>; + { Value.verify() } -> std::same_as<Expected<void>>; + { Value.clone() } -> std::same_as<Expected<ValueT>>; + { Value.stableHash() } -> std::same_as<std::uint64_t>; + { Value.print() } -> std::same_as<std::string>; + { Value.encode() } -> std::same_as<Expected<std::vector<std::byte>>>; + { Value.emit() } -> std::same_as<Expected<TargetConstantEmission>>; + { Value.requiredAlignment() } -> std::same_as<Alignment>; + { Value.relocationKind() } -> std::same_as<std::optional<TargetRelocationKindID>>; + { Value == Other } -> std::convertible_to<bool>; + { ValueT::parse(Text) } -> std::same_as<Expected<ValueT>>; + { ValueT::decode(Version, Payload) } -> std::same_as<Expected<ValueT>>; + }; + +class TargetConstant final { +public: + TargetConstant() = default; + TargetConstant(const TargetConstant &) = delete; + TargetConstant &operator=(const TargetConstant &) = delete; + TargetConstant(TargetConstant &&) noexcept; + TargetConstant &operator=(TargetConstant &&) noexcept; + ~TargetConstant(); + + template <TargetConstantValue ValueT> + [[nodiscard]] static Expected<TargetConstant> create(ValueT Value) { + if (ValueT::stableTypeID().value() == 0) + return makeUnexpected(ErrorCode::InvalidArgument, "target constant TypeID must be nonzero"); + if (ValueT::schemaVersion() == 0) { + return makeUnexpected(ErrorCode::InvalidArgument, + "target constant schema version must be nonzero"); + } + auto Verification = Value.verify(); + if (!Verification) + return std::unexpected(Verification.error()); + + std::unique_ptr<Concept> NewStorage = std::make_unique<Model<ValueT>>(std::move(Value)); + return TargetConstant(std::move(NewStorage)); + } + + template <TargetConstantValue ValueT> + [[nodiscard]] static Expected<TargetConstant> parse(std::string_view Text) { + auto Value = ValueT::parse(Text); + if (!Value) + return std::unexpected(Value.error()); + return create<ValueT>(std::move(*Value)); + } + + template <TargetConstantValue ValueT> + [[nodiscard]] static Expected<TargetConstant> decode(const EncodedTargetConstant &Encoded) { + if (Encoded.TypeID != ValueT::stableTypeID()) { + return makeUnexpected(ErrorCode::ParseError, + "encoded target constant TypeID does not match its target schema"); + } + if (Encoded.Version == 0) + return makeUnexpected(ErrorCode::ParseError, "target constant version must be nonzero"); + auto Value = ValueT::decode(Encoded.Version, Encoded.Payload); + if (!Value) + return std::unexpected(Value.error()); + return create<ValueT>(std::move(*Value)); + } + + [[nodiscard]] bool hasValue() const noexcept { + return Storage != nullptr; + } + + [[nodiscard]] std::optional<TargetConstantTypeID> typeID() const noexcept; + [[nodiscard]] std::optional<std::uint32_t> schemaVersion() const noexcept; + [[nodiscard]] Expected<void> verify() const; + [[nodiscard]] Expected<TargetConstant> clone() const; + [[nodiscard]] Expected<std::uint64_t> stableHash() const; + [[nodiscard]] Expected<std::string> print() const; + [[nodiscard]] Expected<EncodedTargetConstant> encode() const; + [[nodiscard]] Expected<TargetConstantEmission> emit() const; + [[nodiscard]] Expected<Alignment> requiredAlignment() const; + [[nodiscard]] Expected<std::optional<TargetRelocationKindID>> relocationKind() const; + + [[nodiscard]] bool equals(const TargetConstant &Other) const noexcept; + + template <TargetConstantValue ValueT> [[nodiscard]] ValueT *getIf() noexcept { + if (!hasType<ValueT>()) + return nullptr; + return &static_cast<Model<ValueT> *>(Storage.get())->value(); + } + + template <TargetConstantValue ValueT> [[nodiscard]] const ValueT *getIf() const noexcept { + if (!hasType<ValueT>()) + return nullptr; + return &static_cast<const Model<ValueT> *>(Storage.get())->value(); + } + +private: + class Concept { + public: + Concept() = default; + Concept(const Concept &) = delete; + Concept &operator=(const Concept &) = delete; + Concept(Concept &&) = delete; + Concept &operator=(Concept &&) = delete; + virtual ~Concept() = default; + + [[nodiscard]] virtual TargetConstantTypeID typeID() const noexcept = 0; + [[nodiscard]] virtual std::uint32_t schemaVersion() const noexcept = 0; + [[nodiscard]] virtual const void *localTypeToken() const noexcept = 0; + [[nodiscard]] virtual Expected<void> verify() const = 0; + [[nodiscard]] virtual Expected<std::unique_ptr<Concept>> cloneStorage() const = 0; + [[nodiscard]] virtual std::uint64_t stableHash() const noexcept = 0; + [[nodiscard]] virtual std::string print() const = 0; + [[nodiscard]] virtual Expected<std::vector<std::byte>> encodePayload() const = 0; + [[nodiscard]] virtual Expected<TargetConstantEmission> emit() const = 0; + [[nodiscard]] virtual Alignment requiredAlignment() const noexcept = 0; + [[nodiscard]] virtual std::optional<TargetRelocationKindID> relocationKind() const noexcept = 0; + [[nodiscard]] virtual bool equals(const Concept &Other) const noexcept = 0; + }; + + template <typename ValueT> [[nodiscard]] static const void *typeToken() noexcept { + static const std::byte Token{}; + return &Token; + } + + template <TargetConstantValue ValueT> class Model final : public Concept { + public: + explicit Model(ValueT Value) : Value(std::move(Value)) {} + + [[nodiscard]] TargetConstantTypeID typeID() const noexcept override { + return ValueT::stableTypeID(); + } + + [[nodiscard]] std::uint32_t schemaVersion() const noexcept override { + return ValueT::schemaVersion(); + } + + [[nodiscard]] const void *localTypeToken() const noexcept override { + return TargetConstant::typeToken<ValueT>(); + } + + [[nodiscard]] Expected<void> verify() const override { + return Value.verify(); + } + + [[nodiscard]] Expected<std::unique_ptr<Concept>> cloneStorage() const override { + auto Cloned = Value.clone(); + if (!Cloned) + return std::unexpected(Cloned.error()); + std::unique_ptr<Concept> Result = std::make_unique<Model<ValueT>>(std::move(*Cloned)); + return Result; + } + + [[nodiscard]] std::uint64_t stableHash() const noexcept override { + return Value.stableHash(); + } + + [[nodiscard]] std::string print() const override { + return Value.print(); + } + + [[nodiscard]] Expected<std::vector<std::byte>> encodePayload() const override { + return Value.encode(); + } + + [[nodiscard]] Expected<TargetConstantEmission> emit() const override { + return Value.emit(); + } + + [[nodiscard]] Alignment requiredAlignment() const noexcept override { + return Value.requiredAlignment(); + } + + [[nodiscard]] std::optional<TargetRelocationKindID> relocationKind() const noexcept override { + return Value.relocationKind(); + } + + [[nodiscard]] bool equals(const Concept &Other) const noexcept override { + if (Other.localTypeToken() != TargetConstant::typeToken<ValueT>()) + return false; + return Value == static_cast<const Model<ValueT> &>(Other).Value; + } + + [[nodiscard]] ValueT &value() noexcept { + return Value; + } + + [[nodiscard]] const ValueT &value() const noexcept { + return Value; + } + + private: + ValueT Value; + }; + + explicit TargetConstant(std::unique_ptr<Concept> Storage) : Storage(std::move(Storage)) {} + + template <TargetConstantValue ValueT> [[nodiscard]] bool hasType() const noexcept { + return Storage != nullptr && Storage->typeID() == ValueT::stableTypeID() && + Storage->localTypeToken() == typeToken<ValueT>(); + } + + std::unique_ptr<Concept> Storage; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_TARGETCONSTANT_H diff --git a/backend/include/rcc/CodeGen/TargetDescription.h b/backend/include/rcc/CodeGen/TargetDescription.h new file mode 100644 index 0000000..6504ba8 --- /dev/null +++ b/backend/include/rcc/CodeGen/TargetDescription.h @@ -0,0 +1,151 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_TARGETDESCRIPTION_H +#define RCC_CODEGEN_TARGETDESCRIPTION_H + +#include "rcc/CodeGen/InstructionDescriptor.h" +#include "rcc/CodeGen/TargetMIRTextInfo.h" +#include "rcc/CodeGen/TargetRegisterInfo.h" +#include "rcc/Support/Error.h" + +#include <cstdint> +#include <optional> +#include <span> +#include <utility> +#include <vector> + +namespace rcc { + +class MachineBasicBlock; +class MachineCFIInfo; +class MachineFrameInfo; +class MachineInstr; +class MachineMemOperand; +class MachineRegisterInfo; +struct VirtualRegisterInfo; + +enum class BooleanContent : std::uint8_t { + Undefined, + ZeroOrOne, + ZeroOrNegativeOne, +}; + +struct TargetBooleanContentInfo final { + BooleanContent ScalarInteger = BooleanContent::Undefined; + BooleanContent ScalarFloatingPoint = BooleanContent::Undefined; + BooleanContent Vector = BooleanContent::Undefined; +}; + +enum class StackGrowthDirection : std::uint8_t { + Down, + Up, +}; + +struct TargetStackInfo final { + PhysicalRegister StackPointerRegisterToSaveRestore; + StackGrowthDirection GrowthDirection = StackGrowthDirection::Down; +}; + +class TargetDescription final { +public: + TargetDescription(const TargetDescription &) = delete; + TargetDescription &operator=(const TargetDescription &) = delete; + TargetDescription(TargetDescription &&) noexcept = default; + TargetDescription &operator=(TargetDescription &&) noexcept = default; + ~TargetDescription() = default; + + [[nodiscard]] static Expected<TargetDescription> create(InstructionSet Instructions, + TargetRegisterInfo Registers, + TargetMIRTextInfoDesc MIRText = {}, + TargetBooleanContentInfo BooleanInfo = {}, + TargetStackInfo StackInfo = {}); + + [[nodiscard]] const InstructionSet &instructions() const noexcept { + return Instructions; + } + + [[nodiscard]] const TargetRegisterInfo ®isters() const noexcept { + return Registers; + } + + [[nodiscard]] const TargetMIRTextInfo &mirText() const noexcept { + return MIRText; + } + + [[nodiscard]] BooleanContent getBooleanContents(bool IsVector, + bool IsFloatingPoint) const noexcept { + if (IsVector) + return BooleanInfo.Vector; + return IsFloatingPoint ? BooleanInfo.ScalarFloatingPoint : BooleanInfo.ScalarInteger; + } + + [[nodiscard]] PhysicalRegister stackPointerRegisterToSaveRestore() const noexcept { + return StackInfo.StackPointerRegisterToSaveRestore; + } + + [[nodiscard]] StackGrowthDirection stackGrowthDirection() const noexcept { + return StackInfo.GrowthDirection; + } + + [[nodiscard]] const InstructionDescriptor * + instructionDescriptor(Opcode InstructionOpcode) const noexcept { + return Instructions.descriptor(InstructionOpcode); + } + + [[nodiscard]] Expected<void> validateExplicitOperands(Opcode InstructionOpcode, + std::span<const MachineOperand> Operands, + const MachineRegisterInfo &RegInfo) const; + [[nodiscard]] Expected<void> validateOperandTies(Opcode InstructionOpcode, + std::span<const MachineOperand> ExplicitOperands, + std::span<const MachineOperandTie> Ties, + const MachineRegisterInfo &RegInfo) const; + [[nodiscard]] Expected<void> + validateImplicitOperands(Opcode InstructionOpcode, + std::span<const MachineOperand *const> ImplicitOperands, + const MachineRegisterInfo &RegInfo) const; + [[nodiscard]] Expected<void> + validateEarlyClobber(std::span<const MachineOperand> ExplicitOperands, + std::span<const MachineOperand *const> ImplicitOperands, + std::span<const MachineOperandTie> Ties) const; + [[nodiscard]] Expected<void> + validateMemOperands(Opcode InstructionOpcode, + std::span<const MachineMemOperand *const> MemOperands) const; + [[nodiscard]] Expected<void> validateInstruction(const MachineInstr &Instruction, + const MachineRegisterInfo &RegInfo) const; + [[nodiscard]] Expected<void> validateTieCompatibility(const MachineOperand &Def, + const MachineOperand &Use, + const MachineRegisterInfo &RegInfo) const; + [[nodiscard]] Expected<void> validateCFIInfo(const MachineCFIInfo &Info) const; + [[nodiscard]] Expected<void> validateFrameInfo(const MachineFrameInfo &Info) const; + [[nodiscard]] Expected<void> validateRegisterInfo(const MachineRegisterInfo &RegInfo) const; + [[nodiscard]] Expected<void> validateBlockLiveIns(const MachineBasicBlock &Block) const; + + [[nodiscard]] Expected<std::vector<MachineOperand>> + createDefaultImplicitOperands(const InstructionDescriptor &Descriptor) const { + return Instructions.createDefaultImplicitOperands(Descriptor); + } + +private: + TargetDescription(InstructionSet Instructions, TargetRegisterInfo Registers, + TargetMIRTextInfo MIRText, TargetBooleanContentInfo BooleanInfo, + TargetStackInfo StackInfo) + : Instructions(std::move(Instructions)), Registers(std::move(Registers)), + MIRText(std::move(MIRText)), BooleanInfo(BooleanInfo), StackInfo(StackInfo) {} + + [[nodiscard]] Expected<void> validateTargetOperand(const OperandDescriptor *Descriptor, + const MachineOperand &Operand, + const MachineRegisterInfo &RegInfo) const; + [[nodiscard]] Expected<void> validateVirtualRegister(Register Reg, + const VirtualRegisterInfo &Info) const; + [[nodiscard]] std::optional<RegisterClassID> + effectiveRegisterClass(const MachineOperand &Operand, const MachineRegisterInfo &RegInfo) const; + InstructionSet Instructions; + TargetRegisterInfo Registers; + TargetMIRTextInfo MIRText; + TargetBooleanContentInfo BooleanInfo; + TargetStackInfo StackInfo; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_TARGETDESCRIPTION_H diff --git a/backend/include/rcc/CodeGen/TargetFrameLowering.h b/backend/include/rcc/CodeGen/TargetFrameLowering.h new file mode 100644 index 0000000..e3f0f37 --- /dev/null +++ b/backend/include/rcc/CodeGen/TargetFrameLowering.h @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_TARGETFRAMELOWERING_H +#define RCC_CODEGEN_TARGETFRAMELOWERING_H + +#include "rcc/CodeGen/MachineIRTypes.h" +#include "rcc/CodeGen/Register.h" +#include "rcc/Support/Error.h" + +#include <cstddef> +#include <cstdint> + +namespace rcc { + +class MachineBasicBlock; +class MachineFunction; +class MachineInstr; +class RegisterScavenger; +class TargetDescription; + +struct FrameIndexReference final { + PhysicalRegister BaseRegister; + std::int64_t Offset = 0; +}; + +struct FrameLoweringOptions final { + bool EmitDebugOutput = false; +}; + +class TargetFrameLowering { +public: + virtual ~TargetFrameLowering() = default; + + [[nodiscard]] virtual PhysicalRegister + initialCFARegister(const MachineFunction &Function, + const TargetDescription &Target) const noexcept = 0; + [[nodiscard]] virtual std::int64_t initialCFAOffset(const MachineFunction &) const noexcept { + return 0; + } + + [[nodiscard]] virtual Expected<void> prepareFrame(MachineFunction &Function, + const TargetDescription &Target, + FrameLoweringOptions Options = {}) const = 0; + [[nodiscard]] virtual Expected<void> + insertCalleeSavedSpillsAndRestores(MachineFunction &Function, + const TargetDescription &Target) const = 0; + [[nodiscard]] virtual Expected<void> + processFunctionBeforeFrameFinalized(MachineFunction &, const TargetDescription &) const { + return {}; + } + [[nodiscard]] virtual Expected<void> finalizeFrame(MachineFunction &Function, + const TargetDescription &Target) const = 0; + + [[nodiscard]] virtual bool isCalleeSavedRegister(PhysicalRegister, const MachineFunction &, + const TargetDescription &) const noexcept { + return false; + } + + [[nodiscard]] virtual bool isCallFramePseudo(const MachineInstr &Instruction) const noexcept = 0; + [[nodiscard]] virtual Expected<void> + eliminateCallFramePseudo(MachineInstr &Instruction, MachineFunction &Function, + const TargetDescription &Target, RegisterScavenger &Scavenger) const = 0; + + [[nodiscard]] virtual Expected<void> emitPrologue(MachineFunction &Function, + const TargetDescription &Target, + RegisterScavenger &Scavenger) const = 0; + [[nodiscard]] virtual bool + isEpilogueTerminator(const MachineInstr &Instruction, + const TargetDescription &Target) const noexcept = 0; + [[nodiscard]] virtual Expected<void> + emitEpilogue(MachineBasicBlock &Block, MachineInstr &Terminator, MachineFunction &Function, + const TargetDescription &Target, RegisterScavenger &Scavenger) const = 0; + [[nodiscard]] virtual Expected<void> + processFunctionBeforeFrameIndexElimination(MachineFunction &, const TargetDescription &) const { + return {}; + } + + [[nodiscard]] virtual Expected<FrameIndexReference> + getFrameIndexReference(const MachineFunction &Function, const MachineInstr &Instruction, + FrameIndex Index, std::int64_t OperandOffset) const = 0; + [[nodiscard]] virtual Expected<void> eliminateFrameIndex(MachineInstr &Instruction, + std::size_t OperandIndex, + MachineFunction &Function, + const TargetDescription &Target, + RegisterScavenger &Scavenger) const = 0; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_TARGETFRAMELOWERING_H diff --git a/backend/include/rcc/CodeGen/TargetFunctionState.h b/backend/include/rcc/CodeGen/TargetFunctionState.h new file mode 100644 index 0000000..b11c91b --- /dev/null +++ b/backend/include/rcc/CodeGen/TargetFunctionState.h @@ -0,0 +1,210 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_TARGETFUNCTIONSTATE_H +#define RCC_CODEGEN_TARGETFUNCTIONSTATE_H + +#include "rcc/CodeGen/MachineIRTypes.h" +#include "rcc/Support/Error.h" + +#include <concepts> +#include <cstddef> +#include <cstdint> +#include <memory> +#include <optional> +#include <span> +#include <string> +#include <utility> +#include <vector> + +namespace rcc { + +class MachineFunctionCloneContext; + +struct EncodedTargetFunctionState final { + TargetFunctionStateTypeID TypeID; + std::uint32_t Version = 0; + std::vector<std::byte> Payload; + + friend bool operator==(const EncodedTargetFunctionState &Left, + const EncodedTargetFunctionState &Right) = default; +}; + +template <typename ValueT> +concept TargetFunctionStateValue = + std::move_constructible<ValueT> && + requires(const ValueT &Value, const ValueT &Other, + const MachineFunctionCloneContext &CloneContext, std::uint32_t Version, + std::span<const std::byte> Payload) { + { ValueT::stableTypeID() } -> std::same_as<TargetFunctionStateTypeID>; + { ValueT::schemaVersion() } -> std::same_as<std::uint32_t>; + { Value.verify() } -> std::same_as<Expected<void>>; + { Value.clone(CloneContext) } -> std::same_as<Expected<ValueT>>; + { Value.print() } -> std::same_as<std::string>; + { Value.encode() } -> std::same_as<Expected<std::vector<std::byte>>>; + { Value == Other } -> std::convertible_to<bool>; + { ValueT::decode(Version, Payload) } -> std::same_as<Expected<ValueT>>; + }; + +class TargetFunctionState final { +public: + TargetFunctionState() = default; + TargetFunctionState(const TargetFunctionState &) = delete; + TargetFunctionState &operator=(const TargetFunctionState &) = delete; + TargetFunctionState(TargetFunctionState &&) noexcept; + TargetFunctionState &operator=(TargetFunctionState &&) noexcept; + ~TargetFunctionState(); + + template <TargetFunctionStateValue ValueT> + [[nodiscard]] static Expected<TargetFunctionState> create(ValueT Value) { + if (ValueT::stableTypeID().value() == 0) { + return makeUnexpected(ErrorCode::InvalidArgument, + "target function state TypeID must be nonzero"); + } + if (ValueT::schemaVersion() == 0) { + return makeUnexpected(ErrorCode::InvalidArgument, + "target function state schema version must be nonzero"); + } + auto Verification = Value.verify(); + if (!Verification) + return std::unexpected(Verification.error()); + + std::unique_ptr<Concept> NewStorage = std::make_unique<Model<ValueT>>(std::move(Value)); + return TargetFunctionState(std::move(NewStorage)); + } + + template <TargetFunctionStateValue ValueT> + [[nodiscard]] static Expected<TargetFunctionState> + decode(const EncodedTargetFunctionState &Encoded) { + if (Encoded.TypeID != ValueT::stableTypeID()) { + return makeUnexpected( + ErrorCode::ParseError, + "encoded target function state TypeID does not match its target schema"); + } + if (Encoded.Version == 0) { + return makeUnexpected(ErrorCode::ParseError, "target function state version must be nonzero"); + } + auto Value = ValueT::decode(Encoded.Version, Encoded.Payload); + if (!Value) + return std::unexpected(Value.error()); + return create<ValueT>(std::move(*Value)); + } + + [[nodiscard]] bool hasValue() const noexcept { + return Storage != nullptr; + } + + [[nodiscard]] std::optional<TargetFunctionStateTypeID> typeID() const noexcept; + [[nodiscard]] std::optional<std::uint32_t> schemaVersion() const noexcept; + [[nodiscard]] Expected<void> verify() const; + [[nodiscard]] Expected<TargetFunctionState> + clone(const MachineFunctionCloneContext &CloneContext) const; + [[nodiscard]] Expected<std::string> print() const; + [[nodiscard]] Expected<EncodedTargetFunctionState> encode() const; + [[nodiscard]] bool equals(const TargetFunctionState &Other) const noexcept; + + template <TargetFunctionStateValue ValueT> [[nodiscard]] ValueT *getIf() noexcept { + if (!hasType<ValueT>()) + return nullptr; + return &static_cast<Model<ValueT> *>(Storage.get())->value(); + } + + template <TargetFunctionStateValue ValueT> [[nodiscard]] const ValueT *getIf() const noexcept { + if (!hasType<ValueT>()) + return nullptr; + return &static_cast<const Model<ValueT> *>(Storage.get())->value(); + } + +private: + class Concept { + public: + Concept() = default; + Concept(const Concept &) = delete; + Concept &operator=(const Concept &) = delete; + Concept(Concept &&) = delete; + Concept &operator=(Concept &&) = delete; + virtual ~Concept() = default; + + [[nodiscard]] virtual TargetFunctionStateTypeID typeID() const noexcept = 0; + [[nodiscard]] virtual std::uint32_t schemaVersion() const noexcept = 0; + [[nodiscard]] virtual const void *localTypeToken() const noexcept = 0; + [[nodiscard]] virtual Expected<void> verify() const = 0; + [[nodiscard]] virtual Expected<std::unique_ptr<Concept>> + cloneStorage(const MachineFunctionCloneContext &CloneContext) const = 0; + [[nodiscard]] virtual std::string print() const = 0; + [[nodiscard]] virtual Expected<std::vector<std::byte>> encodePayload() const = 0; + [[nodiscard]] virtual bool equals(const Concept &Other) const noexcept = 0; + }; + + template <typename ValueT> [[nodiscard]] static const void *typeToken() noexcept { + static const std::byte Token{}; + return &Token; + } + + template <TargetFunctionStateValue ValueT> class Model final : public Concept { + public: + explicit Model(ValueT Value) : Value(std::move(Value)) {} + + [[nodiscard]] TargetFunctionStateTypeID typeID() const noexcept override { + return ValueT::stableTypeID(); + } + + [[nodiscard]] std::uint32_t schemaVersion() const noexcept override { + return ValueT::schemaVersion(); + } + + [[nodiscard]] const void *localTypeToken() const noexcept override { + return TargetFunctionState::typeToken<ValueT>(); + } + + [[nodiscard]] Expected<void> verify() const override { + return Value.verify(); + } + + [[nodiscard]] Expected<std::unique_ptr<Concept>> + cloneStorage(const MachineFunctionCloneContext &CloneContext) const override { + auto Cloned = Value.clone(CloneContext); + if (!Cloned) + return std::unexpected(Cloned.error()); + std::unique_ptr<Concept> Result = std::make_unique<Model<ValueT>>(std::move(*Cloned)); + return Result; + } + + [[nodiscard]] std::string print() const override { + return Value.print(); + } + + [[nodiscard]] Expected<std::vector<std::byte>> encodePayload() const override { + return Value.encode(); + } + + [[nodiscard]] bool equals(const Concept &Other) const noexcept override { + if (Other.localTypeToken() != TargetFunctionState::typeToken<ValueT>()) + return false; + return Value == static_cast<const Model<ValueT> &>(Other).Value; + } + + [[nodiscard]] ValueT &value() noexcept { + return Value; + } + + [[nodiscard]] const ValueT &value() const noexcept { + return Value; + } + + private: + ValueT Value; + }; + + explicit TargetFunctionState(std::unique_ptr<Concept> Storage) : Storage(std::move(Storage)) {} + + template <TargetFunctionStateValue ValueT> [[nodiscard]] bool hasType() const noexcept { + return Storage != nullptr && Storage->typeID() == ValueT::stableTypeID() && + Storage->localTypeToken() == typeToken<ValueT>(); + } + + std::unique_ptr<Concept> Storage; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_TARGETFUNCTIONSTATE_H diff --git a/backend/include/rcc/CodeGen/TargetInstrInfo.h b/backend/include/rcc/CodeGen/TargetInstrInfo.h new file mode 100644 index 0000000..aada102 --- /dev/null +++ b/backend/include/rcc/CodeGen/TargetInstrInfo.h @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_TARGETINSTRINFO_H +#define RCC_CODEGEN_TARGETINSTRINFO_H + +#include "rcc/CodeGen/MachineIRTypes.h" +#include "rcc/CodeGen/Register.h" +#include "rcc/Support/Error.h" + +#include <cstddef> +#include <cstdint> +#include <optional> + +namespace rcc { + +class MachineBasicBlock; +class MachineFunction; +class MachineInstr; +class MachineOperand; +class TargetDescription; + +struct MachineCopyOperands final { + MachineOperand *Destination = nullptr; + MachineOperand *Source = nullptr; +}; + +struct MachineBranchAnalysis final { + MachineInstr *Conditional = nullptr; + MachineInstr *Unconditional = nullptr; + MachineBasicBlock *TrueTarget = nullptr; + MachineBasicBlock *FalseTarget = nullptr; + bool HasUnanalyzableTerminator = false; +}; + +class TargetInstrInfo { +public: + virtual ~TargetInstrInfo(); + + [[nodiscard]] virtual Expected<MachineBranchAnalysis> + analyzeBranch(MachineBasicBlock &Block, MachineFunction &Function, + const TargetDescription &Target) const = 0; + [[nodiscard]] virtual Expected<void> + reverseBranchCondition(MachineInstr &Branch, MachineBasicBlock &NewTarget, + MachineFunction &Function, const TargetDescription &Target) const = 0; + [[nodiscard]] virtual Expected<void> removeBranch(MachineInstr &Branch, MachineFunction &Function, + const TargetDescription &Target) const = 0; + [[nodiscard]] virtual Expected<bool> + rewriteBranchTarget(MachineBasicBlock &Block, MachineBasicBlock &OldTarget, + MachineBasicBlock &NewTarget, MachineFunction &Function, + const TargetDescription &Target) const = 0; + [[nodiscard]] virtual Expected<MachineInstr *> + insertBranch(MachineBasicBlock &Block, MachineBasicBlock &TargetBlock, MachineFunction &Function, + const TargetDescription &Target) const = 0; + + [[nodiscard]] virtual Expected<bool> isFallthroughEdge(const MachineBasicBlock &From, + const MachineBasicBlock &To, + const MachineFunction &Function, + const TargetDescription &Target) const = 0; + + [[nodiscard]] virtual Expected<void> validateCopy(Register Destination, + SubRegIndex DestinationSubReg, Register Source, + SubRegIndex SourceSubReg, + const MachineFunction &Function, + const TargetDescription &Target) const = 0; + + [[nodiscard]] virtual std::optional<MachineCopyOperands> + getCopyOperands(MachineInstr &) const noexcept { + return std::nullopt; + } + + [[nodiscard]] virtual Expected<bool> simplifyInstruction(MachineInstr &, MachineFunction &, + const TargetDescription &) const { + return false; + } + + [[nodiscard]] virtual Expected<bool> optimizeCondBranch(MachineInstr &, MachineFunction &, + const TargetDescription &) const { + return false; + } + + [[nodiscard]] virtual Expected<bool> tryConvertToThreeAddress(MachineInstr &, std::size_t, + std::size_t, MachineFunction &, + const TargetDescription &) const { + return false; + } + + [[nodiscard]] virtual Expected<std::uint32_t> + instructionSize(const MachineInstr &Instruction, const MachineFunction &Function, + const TargetDescription &Target) const = 0; + [[nodiscard]] virtual Expected<MachineBasicBlock *> + branchTarget(MachineInstr &Instruction, MachineFunction &Function, + const TargetDescription &Target) const = 0; + [[nodiscard]] virtual Expected<std::optional<bool>> + evaluateBranchCondition(const MachineInstr &, const MachineFunction &, + const TargetDescription &) const { + return std::optional<bool>{}; + } + [[nodiscard]] virtual bool isBranchOffsetInRange(const MachineInstr &Instruction, + std::int64_t Offset) const noexcept = 0; + [[nodiscard]] virtual Expected<bool> relaxBranch(MachineInstr &Instruction, std::int64_t Offset, + MachineFunction &Function, + const TargetDescription &Target) const = 0; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_TARGETINSTRINFO_H diff --git a/backend/include/rcc/CodeGen/TargetLoweringObjectFile.h b/backend/include/rcc/CodeGen/TargetLoweringObjectFile.h new file mode 100644 index 0000000..f415e4f --- /dev/null +++ b/backend/include/rcc/CodeGen/TargetLoweringObjectFile.h @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_TARGETLOWERINGOBJECTFILE_H +#define RCC_CODEGEN_TARGETLOWERINGOBJECTFILE_H + +#include "rcc/MC/MCSection.h" +#include "rcc/Support/Error.h" + +namespace rcc { + +struct GlobalObjectDesc; +class MachineConstantPoolEntry; +class MachineFunction; +class MachineJumpTableEntry; +class MachineMCContext; + +class TargetLoweringObjectFile { +public: + virtual ~TargetLoweringObjectFile() = default; + + [[nodiscard]] virtual Expected<mc::MCSectionDesc> + textSection(const MachineFunction &Function) const = 0; + [[nodiscard]] virtual Expected<mc::MCSectionDesc> + globalSection(const GlobalObjectDesc &Object, MachineMCContext &Context) const = 0; + [[nodiscard]] virtual Expected<mc::MCSectionDesc> + constantPoolSection(const MachineFunction &Function, + const MachineConstantPoolEntry &Entry) const = 0; + [[nodiscard]] virtual Expected<mc::MCSectionDesc> + jumpTableSection(const MachineFunction &Function, const MachineJumpTableEntry &Entry) const = 0; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_TARGETLOWERINGOBJECTFILE_H diff --git a/backend/include/rcc/CodeGen/TargetMCInstLowering.h b/backend/include/rcc/CodeGen/TargetMCInstLowering.h new file mode 100644 index 0000000..eacb48e --- /dev/null +++ b/backend/include/rcc/CodeGen/TargetMCInstLowering.h @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_TARGETMCINSTLOWERING_H +#define RCC_CODEGEN_TARGETMCINSTLOWERING_H + +#include "rcc/MC/MCExpr.h" +#include "rcc/MC/MCInst.h" +#include "rcc/MC/MCInstPrinter.h" +#include "rcc/MC/MCStreamer.h" +#include "rcc/Support/Error.h" + +namespace rcc { + +class MachineInstr; +class MachineMCContext; + +class TargetMCInstLowering { +public: + virtual ~TargetMCInstLowering() = default; + + [[nodiscard]] virtual const mc::MCInstPrinter &instructionPrinter() const noexcept = 0; + + [[nodiscard]] virtual Expected<mc::MCInst> lowerInstruction(const MachineInstr &Instruction, + MachineMCContext &Context) const = 0; + + [[nodiscard]] virtual Expected<mc::MCExprRef> + lowerSymbolExpression(mc::MCExprRef Expression, std::uint32_t TargetFlags) const = 0; + + [[nodiscard]] virtual Expected<void> emitTerminalInstruction(const MachineInstr &Instruction, + MachineMCContext &Context, + mc::MCStreamer &Streamer) const; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_TARGETMCINSTLOWERING_H diff --git a/backend/include/rcc/CodeGen/TargetMIRTextInfo.h b/backend/include/rcc/CodeGen/TargetMIRTextInfo.h new file mode 100644 index 0000000..3ed6022 --- /dev/null +++ b/backend/include/rcc/CodeGen/TargetMIRTextInfo.h @@ -0,0 +1,266 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_TARGETMIRTEXTINFO_H +#define RCC_CODEGEN_TARGETMIRTEXTINFO_H + +#include "rcc/CodeGen/MachineFrameInfo.h" +#include "rcc/CodeGen/MachineIRTypes.h" +#include "rcc/CodeGen/Register.h" +#include "rcc/CodeGen/RegisterMask.h" +#include "rcc/CodeGen/TargetConstant.h" +#include "rcc/CodeGen/TargetFunctionState.h" +#include "rcc/Support/Error.h" + +#include <cstdint> +#include <memory> +#include <optional> +#include <span> +#include <string> +#include <string_view> +#include <unordered_map> +#include <unordered_set> +#include <utility> +#include <vector> + +namespace rcc { + +class TargetRegisterInfo; + +enum class MIRTargetNodeKind : std::uint8_t { + Null, + Scalar, + Sequence, + Mapping, + Count, +}; + +struct MIRTargetMappingEntry; + +struct MIRTargetNode final { + MIRTargetNode(); + MIRTargetNode(const MIRTargetNode &); + MIRTargetNode &operator=(const MIRTargetNode &); + MIRTargetNode(MIRTargetNode &&) noexcept; + MIRTargetNode &operator=(MIRTargetNode &&) noexcept; + ~MIRTargetNode(); + + MIRTargetNodeKind Kind = MIRTargetNodeKind::Null; + std::string Scalar; + std::vector<MIRTargetNode> Sequence; + std::vector<MIRTargetMappingEntry> Mapping; + + [[nodiscard]] static MIRTargetNode null(); + [[nodiscard]] static MIRTargetNode scalar(std::string Value); + [[nodiscard]] static MIRTargetNode sequence(std::vector<MIRTargetNode> Values); + [[nodiscard]] static MIRTargetNode mapping(std::vector<MIRTargetMappingEntry> Entries); + + [[nodiscard]] const MIRTargetNode *mappingValue(std::string_view Key) const noexcept; + [[nodiscard]] Expected<void> verify() const; + + friend bool operator==(const MIRTargetNode &Left, const MIRTargetNode &Right); +}; + +struct MIRTargetMappingEntry final { + std::string Key; + MIRTargetNode Value; + + friend bool operator==(const MIRTargetMappingEntry &Left, + const MIRTargetMappingEntry &Right) = default; +}; + +using MIRTargetFunctionStateParser = Expected<TargetFunctionState> (*)(const MIRTargetNode &Node); +using MIRTargetFunctionStatePrinter = Expected<MIRTargetNode> (*)(const TargetFunctionState &State); +using MIRTargetConstantParser = Expected<TargetConstant> (*)(std::string_view Text); +using MIRTargetConstantPrinter = Expected<std::string> (*)(const TargetConstant &Constant); + +struct MIRRegisterMaskDesc final { + std::string Name; + std::shared_ptr<const RegisterMask> Mask; +}; + +struct MIRTargetIndexDesc final { + std::string Name; + TargetIndex Index; +}; + +struct MIROperandFlagDesc final { + std::string Name; + std::uint32_t Value = 0; +}; + +struct MIRMachineMemOperandFlagDesc final { + std::string Name; + std::uint16_t Value = 0; +}; + +struct MIRVirtualRegisterFlagDesc final { + std::string Name; + std::uint8_t Value = 0; +}; + +struct MIRIntrinsicDesc final { + std::string Name; + IntrinsicID ID; +}; + +struct MIRCFIRegisterMappingDesc final { + PhysicalRegister Register; + std::uint32_t DwarfRegister = 0; + bool IsCanonical = true; +}; + +struct TargetMIRTextInfoDesc final { + std::vector<MIRRegisterMaskDesc> RegisterMasks; + std::vector<MIRTargetIndexDesc> TargetIndices; + std::uint32_t DirectOperandFlagMask = 0; + std::vector<MIROperandFlagDesc> DirectOperandFlags; + std::vector<MIROperandFlagDesc> BitmaskOperandFlags; + std::vector<MIRMachineMemOperandFlagDesc> MachineMemOperandFlags; + std::vector<MIRVirtualRegisterFlagDesc> VirtualRegisterFlags; + std::vector<MIRIntrinsicDesc> Intrinsics; + std::vector<MIRCFIRegisterMappingDesc> CFIRegisters; + std::vector<StackID> SupportedStackIDs{ + StackID::defaultStack(), + StackID::noAllocStack(), + }; + std::optional<TargetFunctionStateTypeID> FunctionStateType; + MIRTargetFunctionStateParser ParseFunctionState = nullptr; + MIRTargetFunctionStatePrinter PrintFunctionState = nullptr; + MIRTargetConstantParser ParseTargetConstant = nullptr; + MIRTargetConstantPrinter PrintTargetConstant = nullptr; +}; + +class TargetMIRTextInfo final { +public: + TargetMIRTextInfo(const TargetMIRTextInfo &) = delete; + TargetMIRTextInfo &operator=(const TargetMIRTextInfo &) = delete; + TargetMIRTextInfo(TargetMIRTextInfo &&) noexcept = default; + TargetMIRTextInfo &operator=(TargetMIRTextInfo &&) noexcept = default; + ~TargetMIRTextInfo() = default; + + [[nodiscard]] static Expected<TargetMIRTextInfo> create(TargetMIRTextInfoDesc Description, + const TargetRegisterInfo &Registers); + + [[nodiscard]] std::span<const MIRRegisterMaskDesc> registerMasks() const noexcept { + return Description.RegisterMasks; + } + + [[nodiscard]] std::span<const MIRTargetIndexDesc> targetIndices() const noexcept { + return Description.TargetIndices; + } + + [[nodiscard]] std::span<const MIROperandFlagDesc> directOperandFlags() const noexcept { + return Description.DirectOperandFlags; + } + + [[nodiscard]] std::span<const MIROperandFlagDesc> bitmaskOperandFlags() const noexcept { + return Description.BitmaskOperandFlags; + } + + [[nodiscard]] std::span<const MIRMachineMemOperandFlagDesc> + machineMemOperandFlags() const noexcept { + return Description.MachineMemOperandFlags; + } + + [[nodiscard]] std::span<const MIRVirtualRegisterFlagDesc> virtualRegisterFlags() const noexcept { + return Description.VirtualRegisterFlags; + } + + [[nodiscard]] std::span<const MIRIntrinsicDesc> intrinsics() const noexcept { + return Description.Intrinsics; + } + + [[nodiscard]] std::shared_ptr<const RegisterMask> + registerMask(std::string_view Name) const noexcept; + [[nodiscard]] std::optional<std::string_view> + registerMaskName(const RegisterMask &Mask) const noexcept; + + [[nodiscard]] std::optional<TargetIndex> targetIndex(std::string_view Name) const noexcept; + [[nodiscard]] std::optional<std::string_view> targetIndexName(TargetIndex Index) const noexcept; + + [[nodiscard]] std::optional<std::uint32_t> + directOperandFlag(std::string_view Name) const noexcept; + [[nodiscard]] std::optional<std::uint32_t> + bitmaskOperandFlag(std::string_view Name) const noexcept; + [[nodiscard]] Expected<std::vector<std::string_view>> operandFlagNames(std::uint32_t Flags) const; + [[nodiscard]] Expected<void> validateOperandFlags(std::uint32_t Flags) const; + + [[nodiscard]] std::optional<std::uint16_t> + machineMemOperandFlag(std::string_view Name) const noexcept; + [[nodiscard]] Expected<std::vector<std::string_view>> + machineMemOperandFlagNames(std::uint16_t Flags) const; + [[nodiscard]] Expected<void> validateMachineMemOperandFlags(std::uint16_t Flags) const; + + [[nodiscard]] std::optional<std::uint8_t> + virtualRegisterFlag(std::string_view Name) const noexcept; + [[nodiscard]] Expected<std::vector<std::string_view>> + virtualRegisterFlagNames(std::uint8_t Flags) const; + [[nodiscard]] Expected<void> validateVirtualRegisterFlags(std::uint8_t Flags) const; + + [[nodiscard]] std::optional<IntrinsicID> intrinsic(std::string_view Name) const noexcept; + [[nodiscard]] std::optional<std::string_view> intrinsicName(IntrinsicID ID) const noexcept; + + [[nodiscard]] std::optional<std::uint32_t> + dwarfRegister(PhysicalRegister Register) const noexcept; + [[nodiscard]] std::optional<PhysicalRegister> + physicalRegister(std::uint32_t DwarfRegister) const noexcept; + + [[nodiscard]] std::span<const StackID> supportedStackIDs() const noexcept { + return Description.SupportedStackIDs; + } + + [[nodiscard]] bool supportsStackID(StackID ID) const noexcept { + return SupportedStackIDs.contains(ID.value()); + } + + [[nodiscard]] std::uint32_t directOperandFlagMask() const noexcept { + return Description.DirectOperandFlagMask; + } + + [[nodiscard]] std::uint32_t serializableOperandFlagMask() const noexcept { + return SerializableOperandFlagMask; + } + + [[nodiscard]] std::uint16_t serializableMachineMemOperandFlagMask() const noexcept { + return SerializableMachineMemOperandFlagMask; + } + + [[nodiscard]] std::uint8_t serializableVirtualRegisterFlagMask() const noexcept { + return SerializableVirtualRegisterFlagMask; + } + + [[nodiscard]] std::optional<TargetFunctionStateTypeID> functionStateType() const noexcept { + return Description.FunctionStateType; + } + + [[nodiscard]] Expected<TargetFunctionState> parseFunctionState(const MIRTargetNode &Node) const; + [[nodiscard]] Expected<MIRTargetNode> printFunctionState(const TargetFunctionState &State) const; + [[nodiscard]] Expected<TargetConstant> parseTargetConstant(std::string_view Text) const; + [[nodiscard]] Expected<std::string> printTargetConstant(const TargetConstant &Constant) const; + +private: + explicit TargetMIRTextInfo(TargetMIRTextInfoDesc Description) + : Description(std::move(Description)) {} + + TargetMIRTextInfoDesc Description; + std::unordered_map<std::string, std::size_t> RegisterMaskNameToIndex; + std::unordered_map<std::string, std::size_t> TargetIndexNameToIndex; + std::unordered_map<TargetIndex, std::size_t> TargetIndexToIndex; + std::unordered_map<std::string, std::size_t> DirectOperandFlagNameToIndex; + std::unordered_map<std::uint32_t, std::size_t> DirectOperandFlagToIndex; + std::unordered_map<std::string, std::size_t> BitmaskOperandFlagNameToIndex; + std::unordered_map<std::string, std::size_t> MachineMemOperandFlagNameToIndex; + std::unordered_map<std::string, std::size_t> VirtualRegisterFlagNameToIndex; + std::unordered_map<std::string, std::size_t> IntrinsicNameToIndex; + std::unordered_map<IntrinsicID, std::size_t> IntrinsicToIndex; + std::unordered_map<PhysicalRegister, std::uint32_t> RegisterToDwarf; + std::unordered_map<std::uint32_t, PhysicalRegister> DwarfToRegister; + std::unordered_set<std::uint32_t> SupportedStackIDs; + std::uint32_t SerializableOperandFlagMask = 0; + std::uint16_t SerializableMachineMemOperandFlagMask = 0; + std::uint8_t SerializableVirtualRegisterFlagMask = 0; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_TARGETMIRTEXTINFO_H diff --git a/backend/include/rcc/CodeGen/TargetMemoryFolding.h b/backend/include/rcc/CodeGen/TargetMemoryFolding.h new file mode 100644 index 0000000..e40fbc0 --- /dev/null +++ b/backend/include/rcc/CodeGen/TargetMemoryFolding.h @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_TARGETMEMORYFOLDING_H +#define RCC_CODEGEN_TARGETMEMORYFOLDING_H + +#include "rcc/CodeGen/MachineMemOperand.h" +#include "rcc/Support/Error.h" + +#include <cstddef> +#include <optional> + +namespace rcc { + +class MachineFunction; +class MachineInstr; +class TargetDescription; + +class TargetMemoryFoldingInfo { +public: + virtual ~TargetMemoryFoldingInfo() = default; + + [[nodiscard]] virtual Expected<bool> + canFoldMemoryOperand(const MachineInstr &Consumer, std::size_t OperandIndex, + const MachineInstr &MemoryInstruction, const MachineFunction &Function, + const TargetDescription &Target) const = 0; + + [[nodiscard]] virtual Expected<MachineInstr *> + foldMemoryOperand(MachineInstr &Consumer, std::size_t OperandIndex, + MachineInstr &MemoryInstruction, MachineFunction &Function, + const TargetDescription &Target) const = 0; + + [[nodiscard]] virtual Expected<bool> + unfoldMemoryOperand(MachineInstr &Instruction, MachineFunction &Function, + const TargetDescription &Target) const = 0; + + [[nodiscard]] virtual Expected<std::optional<MemorySize>> + getFoldedMemorySize(const MachineInstr &Instruction, const MachineFunction &Function, + const TargetDescription &Target) const = 0; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_TARGETMEMORYFOLDING_H diff --git a/backend/include/rcc/CodeGen/TargetPseudoExpansion.h b/backend/include/rcc/CodeGen/TargetPseudoExpansion.h new file mode 100644 index 0000000..039b221 --- /dev/null +++ b/backend/include/rcc/CodeGen/TargetPseudoExpansion.h @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_TARGETPSEUDOEXPANSION_H +#define RCC_CODEGEN_TARGETPSEUDOEXPANSION_H + +#include "rcc/Support/Error.h" + +#include <cstdint> + +namespace rcc { + +class MachineFunction; +class MachineInstr; +class TargetDescription; + +enum class MachinePseudoExpansionPhase : std::uint8_t { + PreRA, + PostRA, + TargetLate, +}; + +class TargetPseudoExpansionInfo { +public: + virtual ~TargetPseudoExpansionInfo() = default; + + [[nodiscard]] virtual Expected<bool> preparePostRAPseudo(MachineInstr &, MachineFunction &, + const TargetDescription &) const { + return false; + } + + [[nodiscard]] virtual Expected<bool> expandPseudo(MachineInstr &, MachinePseudoExpansionPhase, + MachineFunction &, + const TargetDescription &) const { + return false; + } +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_TARGETPSEUDOEXPANSION_H diff --git a/backend/include/rcc/CodeGen/TargetRegisterAllocationInfo.h b/backend/include/rcc/CodeGen/TargetRegisterAllocationInfo.h new file mode 100644 index 0000000..3fd0e55 --- /dev/null +++ b/backend/include/rcc/CodeGen/TargetRegisterAllocationInfo.h @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_TARGETREGISTERALLOCATIONINFO_H +#define RCC_CODEGEN_TARGETREGISTERALLOCATIONINFO_H + +#include "rcc/CodeGen/LiveInterval.h" +#include "rcc/CodeGen/MachineFrameInfo.h" +#include "rcc/CodeGen/TargetRegisterInfo.h" + +#include <cstdint> + +namespace rcc { + +class MachineIRBuilder; +class MachineInstr; +class TargetDescription; + +class TargetRegisterAllocationInfo { +public: + virtual ~TargetRegisterAllocationInfo() = default; + + [[nodiscard]] virtual Expected<void> + validateSpillReload(RegisterClassID Class, const TargetDescription &Target) const = 0; + [[nodiscard]] virtual Expected<MachineInstr *> + emitReload(MachineIRBuilder &Builder, Register Destination, RegisterClassID Class, + FrameIndex StackSlot, LaneMask Lanes) const = 0; + [[nodiscard]] virtual Expected<MachineInstr *> emitSpill(MachineIRBuilder &Builder, + Register Source, RegisterClassID Class, + FrameIndex StackSlot, + LaneMask Lanes) const = 0; + + [[nodiscard]] virtual Expected<bool> isRematerializable(const MachineInstr &, Register, + const MachineFunction &, + const TargetDescription &) const { + return false; + } + [[nodiscard]] virtual Expected<MachineInstr *> rematerialize(MachineIRBuilder &, + const MachineInstr &, Register, + Register, + const TargetDescription &) const { + return makeUnexpected(ErrorCode::InternalError, + "target accepted rematerialization without implementing it"); + } + + [[nodiscard]] virtual std::uint64_t physicalRegisterCost(const MachineFunction &, Register, + PhysicalRegister, RegisterClassID, + bool) const noexcept { + return 0; + } + [[nodiscard]] virtual std::uint64_t spillCost(const MachineFunction &, const LiveInterval &, + RegisterClassID) const noexcept { + return 1; + } + + [[nodiscard]] virtual bool isReservedForFunction(const MachineFunction &, PhysicalRegister, + const TargetDescription &) const noexcept { + return false; + } +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_TARGETREGISTERALLOCATIONINFO_H diff --git a/backend/include/rcc/CodeGen/TargetRegisterInfo.h b/backend/include/rcc/CodeGen/TargetRegisterInfo.h new file mode 100644 index 0000000..ed33263 --- /dev/null +++ b/backend/include/rcc/CodeGen/TargetRegisterInfo.h @@ -0,0 +1,215 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_TARGETREGISTERINFO_H +#define RCC_CODEGEN_TARGETREGISTERINFO_H + +#include "rcc/CodeGen/MachineIRTypes.h" +#include "rcc/CodeGen/Register.h" +#include "rcc/CodeGen/RegisterMask.h" +#include "rcc/Support/Error.h" + +#include <cstddef> +#include <cstdint> +#include <optional> +#include <span> +#include <string> +#include <string_view> +#include <unordered_map> +#include <unordered_set> +#include <utility> +#include <vector> + +namespace rcc { + +struct RegisterUnitDesc final { + RegisterUnitID ID; + std::string Name; +}; + +struct RegisterUnitLane final { + RegisterUnitID Unit; + LaneMask Lanes; +}; + +struct PhysicalRegisterDesc final { + PhysicalRegister Reg; + std::string Name; + std::uint32_t Encoding = 0; + std::vector<RegisterUnitLane> Units; + bool IsConstant = false; + bool IsArtificial = false; + bool IsReserved = false; + std::vector<std::string> AlternativeNames; +}; + +struct SubRegIndexDesc final { + SubRegIndex ID; + std::string Name; + LaneMask Lanes; + std::uint32_t SizeInBits = 0; + std::uint32_t OffsetInBits = 0; +}; + +struct PhysicalSubRegisterDesc final { + PhysicalRegister SuperReg; + SubRegIndex Index; + PhysicalRegister SubReg; +}; + +struct SubRegIndexComposition final { + SubRegIndex First; + SubRegIndex Second; + SubRegIndex Result; +}; + +struct RegisterClassDesc final { + RegisterClassID ID; + std::string Name; + std::vector<PhysicalRegister> Registers; + std::vector<PhysicalRegister> AllocationOrder; + std::uint32_t SizeInBits = 0; + std::uint32_t SpillSizeInBits = 0; + std::uint32_t SpillAlignmentInBits = 0; + LaneMask Lanes; + std::vector<SubRegIndex> SupportedSubRegs; + std::vector<RegisterClassID> SuperClasses; + RegisterBankID DefaultBank; + bool Allocatable = true; + std::uint32_t CopyCost = 1; + bool CoveredBySubRegs = false; +}; + +struct SubRegClassMapping final { + RegisterClassID SourceClass; + SubRegIndex Index; + RegisterClassID ResultClass; +}; + +struct RegisterBankDesc final { + RegisterBankID ID; + std::string Name; + std::uint32_t MaximumSizeInBits = 0; + std::vector<RegisterClassID> RegisterClasses; +}; + +struct TargetRegisterInfoDesc final { + std::vector<RegisterUnitDesc> RegisterUnits; + std::vector<PhysicalRegisterDesc> PhysicalRegisters; + std::vector<SubRegIndexDesc> SubRegIndices; + std::vector<PhysicalSubRegisterDesc> PhysicalSubRegisters; + std::vector<SubRegIndexComposition> SubRegCompositions; + std::vector<RegisterClassDesc> RegisterClasses; + std::vector<SubRegClassMapping> SubRegClassMappings; + std::vector<RegisterBankDesc> RegisterBanks; +}; + +class TargetRegisterInfo final { +public: + [[nodiscard]] static Expected<TargetRegisterInfo> create(TargetRegisterInfoDesc Description); + + [[nodiscard]] std::span<const RegisterUnitDesc> registerUnits() const noexcept { + return Description.RegisterUnits; + } + + [[nodiscard]] std::span<const PhysicalRegisterDesc> physicalRegisters() const noexcept { + return Description.PhysicalRegisters; + } + + [[nodiscard]] std::span<const SubRegIndexDesc> subRegIndices() const noexcept { + return Description.SubRegIndices; + } + + [[nodiscard]] std::span<const RegisterClassDesc> registerClasses() const noexcept { + return Description.RegisterClasses; + } + + [[nodiscard]] std::span<const RegisterBankDesc> registerBanks() const noexcept { + return Description.RegisterBanks; + } + + [[nodiscard]] const RegisterUnitDesc *registerUnit(RegisterUnitID ID) const noexcept; + [[nodiscard]] const PhysicalRegisterDesc *physicalRegister(PhysicalRegister Reg) const noexcept; + [[nodiscard]] const PhysicalRegisterDesc *physicalRegister(std::string_view Name) const noexcept; + [[nodiscard]] const PhysicalRegisterDesc * + physicalRegisterByAlternativeName(std::string_view Name) const noexcept; + [[nodiscard]] const SubRegIndexDesc *subRegIndex(SubRegIndex Index) const noexcept; + [[nodiscard]] const SubRegIndexDesc *subRegIndex(std::string_view Name) const noexcept; + [[nodiscard]] const RegisterClassDesc *registerClass(RegisterClassID ID) const noexcept; + [[nodiscard]] const RegisterClassDesc *registerClass(std::string_view Name) const noexcept; + [[nodiscard]] const RegisterBankDesc *registerBank(RegisterBankID ID) const noexcept; + [[nodiscard]] const RegisterBankDesc *registerBank(std::string_view Name) const noexcept; + + [[nodiscard]] std::optional<std::string_view> + physicalRegisterMIRName(PhysicalRegister Reg) const noexcept; + [[nodiscard]] std::optional<std::string_view> + registerClassMIRName(RegisterClassID ID) const noexcept; + [[nodiscard]] std::optional<std::string_view> + registerBankMIRName(RegisterBankID ID) const noexcept; + + [[nodiscard]] bool isKnownPhysicalRegister(PhysicalRegister Reg) const noexcept { + return physicalRegister(Reg) != nullptr; + } + + [[nodiscard]] bool isReservedRegister(PhysicalRegister Reg) const noexcept; + + [[nodiscard]] LaneMask registerLaneMask(PhysicalRegister Reg) const noexcept; + [[nodiscard]] bool isValidLaneMask(PhysicalRegister Reg, LaneMask Lanes) const noexcept; + [[nodiscard]] bool regsOverlap(PhysicalRegister Left, PhysicalRegister Right) const noexcept; + [[nodiscard]] std::span<const PhysicalRegister> aliases(PhysicalRegister Reg) const noexcept; + + [[nodiscard]] std::optional<PhysicalRegister> subRegister(PhysicalRegister SuperReg, + SubRegIndex Index) const noexcept; + [[nodiscard]] bool isSubRegister(PhysicalRegister SuperReg, + PhysicalRegister SubReg) const noexcept; + [[nodiscard]] std::span<const PhysicalRegister> + superRegisters(PhysicalRegister Reg) const noexcept; + [[nodiscard]] std::optional<SubRegIndex> composeSubRegIndices(SubRegIndex First, + SubRegIndex Second) const noexcept; + + [[nodiscard]] bool registerClassContains(RegisterClassID Class, + PhysicalRegister Reg) const noexcept; + [[nodiscard]] bool isSubclass(RegisterClassID Candidate, RegisterClassID Required) const noexcept; + [[nodiscard]] bool registerClassesOverlap(RegisterClassID Left, + RegisterClassID Right) const noexcept; + [[nodiscard]] std::optional<RegisterClassID> subRegClass(RegisterClassID Source, + SubRegIndex Index) const noexcept; + + [[nodiscard]] bool bankCoversClass(RegisterBankID Bank, RegisterClassID Class) const noexcept; + [[nodiscard]] bool bankContainsRegister(RegisterBankID Bank, PhysicalRegister Reg) const noexcept; + [[nodiscard]] bool registerBanksOverlap(RegisterBankID Left, RegisterBankID Right) const noexcept; + + [[nodiscard]] Expected<void> validateRegisterMask(const RegisterMask &Mask) const; + +private: + explicit TargetRegisterInfo(TargetRegisterInfoDesc Description) + : Description(std::move(Description)) {} + + [[nodiscard]] static std::uint64_t pairKey(std::uint32_t First, std::uint32_t Second) noexcept { + return (static_cast<std::uint64_t>(First) << 32U) | Second; + } + + TargetRegisterInfoDesc Description; + std::unordered_map<RegisterUnitID, std::size_t> UnitToIndex; + std::unordered_map<PhysicalRegister, std::size_t> RegisterToIndex; + std::unordered_map<std::string, std::size_t> RegisterNameToIndex; + std::unordered_map<std::string, std::size_t> AlternativeRegisterNameToIndex; + std::unordered_map<SubRegIndex, std::size_t> SubRegIndexToIndex; + std::unordered_map<std::string, std::size_t> SubRegNameToIndex; + std::unordered_map<RegisterClassID, std::size_t> ClassToIndex; + std::unordered_map<std::string, std::size_t> ClassNameToIndex; + std::unordered_map<RegisterBankID, std::size_t> BankToIndex; + std::unordered_map<std::string, std::size_t> BankNameToIndex; + std::unordered_map<std::uint64_t, PhysicalRegister> SubRegisterMap; + std::unordered_map<std::uint64_t, SubRegIndex> CompositionMap; + std::unordered_map<std::uint64_t, RegisterClassID> SubRegClassMap; + std::unordered_set<std::uint64_t> SubclassRelations; + std::vector<std::vector<PhysicalRegister>> Aliases; + std::vector<std::vector<PhysicalRegister>> SuperRegisters; + std::vector<std::string> RegisterMIRNames; + std::vector<std::string> ClassMIRNames; + std::vector<std::string> BankMIRNames; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_TARGETREGISTERINFO_H diff --git a/backend/include/rcc/CodeGen/TargetSchedulingModel.h b/backend/include/rcc/CodeGen/TargetSchedulingModel.h new file mode 100644 index 0000000..5eb7116 --- /dev/null +++ b/backend/include/rcc/CodeGen/TargetSchedulingModel.h @@ -0,0 +1,179 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_TARGETSCHEDULINGMODEL_H +#define RCC_CODEGEN_TARGETSCHEDULINGMODEL_H + +#include "rcc/CodeGen/MachineIRTypes.h" +#include "rcc/Support/Error.h" + +#include <cstdint> +#include <memory> +#include <optional> +#include <span> +#include <string> +#include <string_view> +#include <unordered_map> +#include <utility> +#include <vector> + +namespace rcc { + +class MachineBasicBlock; +class MachineFunction; +class MachineInstr; +class ScheduleDAG; +class TargetDescription; +struct ScheduleUnit; + +enum class ScheduleDAGMode : std::uint8_t; + +enum class ScheduleDirection : std::uint8_t { + TopDown, + BottomUp, +}; + +enum class ScheduleHazardType : std::uint8_t { + NoHazard, + Hazard, + NoopHazard, +}; + +struct ProcessorResourceDesc final { + ProcessorResourceID ID; + std::string Name; + std::uint32_t Units = 1; + std::optional<ProcessorResourceID> Parent; +}; + +struct SchedulingResourceUse final { + ProcessorResourceID Resource; + std::uint32_t Cycles = 1; + + friend constexpr bool operator==(SchedulingResourceUse, SchedulingResourceUse) noexcept = default; +}; + +struct SchedulingWriteLatency final { + std::uint32_t OperandIndex = 0; + std::uint32_t Cycles = 0; + + friend constexpr bool operator==(SchedulingWriteLatency, + SchedulingWriteLatency) noexcept = default; +}; + +struct SchedulingReadAdvance final { + std::uint32_t OperandIndex = 0; + std::optional<SchedulingClassID> ProducerClass; + std::uint32_t Cycles = 0; + + friend constexpr bool operator==(const SchedulingReadAdvance &, + const SchedulingReadAdvance &) noexcept = default; +}; + +struct SchedulingClassModelDesc final { + SchedulingClassID ID; + std::string Name; + std::uint32_t MicroOps = 1; + std::uint32_t Latency = 1; + std::vector<SchedulingResourceUse> Resources; + std::vector<SchedulingWriteLatency> WriteLatencies; + std::vector<SchedulingReadAdvance> ReadAdvances; +}; + +struct RegisterPressureSetDesc final { + RegisterPressureSetID ID; + std::string Name; + std::vector<RegisterClassID> RegisterClasses; + std::uint32_t Limit = 1; +}; + +class TargetScheduleHazardRecognizer { +public: + virtual ~TargetScheduleHazardRecognizer() = default; + + [[nodiscard]] virtual std::unique_ptr<TargetScheduleHazardRecognizer> clone() const = 0; + [[nodiscard]] virtual Expected<ScheduleHazardType> getHazardType(const ScheduleUnit &Unit) const; + [[nodiscard]] virtual Expected<void> emitInstruction(const ScheduleUnit &Unit); + [[nodiscard]] virtual Expected<void> advanceCycle(); + [[nodiscard]] virtual Expected<void> recedeCycle(); + [[nodiscard]] virtual Expected<void> reset(); +}; + +class TargetSchedulingHooks { +public: + virtual ~TargetSchedulingHooks() = default; + + [[nodiscard]] virtual bool isSchedulingBoundary(const MachineInstr &Instruction, + const TargetDescription &Target) const noexcept; + [[nodiscard]] virtual Expected<void> mutateScheduleDAG(ScheduleDAG &DAG, + const MachineFunction &Function, + const TargetDescription &Target, + ScheduleDAGMode Mode, + bool EmitDebugOutput) const; + [[nodiscard]] virtual std::int64_t + candidatePreference(const ScheduleUnit &Unit, ScheduleDirection Direction, + const MachineBasicBlock &Block, std::uint64_t BlockFrequency) const noexcept; + [[nodiscard]] virtual std::unique_ptr<TargetScheduleHazardRecognizer> + createHazardRecognizer(ScheduleDirection Direction) const; +}; + +struct TargetSchedulingModelDesc final { + std::uint32_t IssueWidth = 1; + std::uint32_t DispatchLimit = 1; + std::vector<ProcessorResourceDesc> ProcessorResources; + std::vector<SchedulingClassModelDesc> SchedulingClasses; + std::vector<RegisterPressureSetDesc> PressureSets; + const TargetSchedulingHooks *Hooks = nullptr; +}; + +class TargetSchedulingModel final { +public: + [[nodiscard]] static Expected<TargetSchedulingModel> + create(TargetSchedulingModelDesc Description); + + [[nodiscard]] std::uint32_t issueWidth() const noexcept { + return Description.IssueWidth; + } + + [[nodiscard]] std::uint32_t dispatchLimit() const noexcept { + return Description.DispatchLimit; + } + + [[nodiscard]] std::span<const ProcessorResourceDesc> processorResources() const noexcept { + return Description.ProcessorResources; + } + + [[nodiscard]] std::span<const SchedulingClassModelDesc> schedulingClasses() const noexcept { + return Description.SchedulingClasses; + } + + [[nodiscard]] std::span<const RegisterPressureSetDesc> pressureSets() const noexcept { + return Description.PressureSets; + } + + [[nodiscard]] const ProcessorResourceDesc * + processorResource(ProcessorResourceID ID) const noexcept; + [[nodiscard]] const SchedulingClassModelDesc * + schedulingClass(SchedulingClassID ID) const noexcept; + [[nodiscard]] const RegisterPressureSetDesc *pressureSet(RegisterPressureSetID ID) const noexcept; + [[nodiscard]] std::span<const RegisterPressureSetID> + pressureSetsForClass(RegisterClassID Class) const noexcept; + [[nodiscard]] Expected<std::uint32_t> operandLatency(SchedulingClassID ProducerClass, + std::size_t DefinitionOperand, + SchedulingClassID ConsumerClass, + std::size_t UseOperand) const; + [[nodiscard]] const TargetSchedulingHooks &hooks() const noexcept; + +private: + explicit TargetSchedulingModel(TargetSchedulingModelDesc Description) + : Description(std::move(Description)) {} + + TargetSchedulingModelDesc Description; + std::unordered_map<ProcessorResourceID, std::size_t> ResourceToIndex; + std::unordered_map<SchedulingClassID, std::size_t> ClassToIndex; + std::unordered_map<RegisterPressureSetID, std::size_t> PressureSetToIndex; + std::unordered_map<RegisterClassID, std::vector<RegisterPressureSetID>> ClassToPressureSets; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_TARGETSCHEDULINGMODEL_H diff --git a/backend/include/rcc/CodeGen/TwoAddressInstructionPass.h b/backend/include/rcc/CodeGen/TwoAddressInstructionPass.h new file mode 100644 index 0000000..3b3bdd1 --- /dev/null +++ b/backend/include/rcc/CodeGen/TwoAddressInstructionPass.h @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_TWOADDRESSINSTRUCTIONPASS_H +#define RCC_CODEGEN_TWOADDRESSINSTRUCTIONPASS_H + +#include "rcc/CodeGen/MachinePassManager.h" + +namespace rcc { + +class TargetInstrInfo; + +class TwoAddressInstructionPass final : public MachineFunctionPass { +public: + explicit TwoAddressInstructionPass(const TargetInstrInfo &Info) noexcept : Info(Info) {} + + [[nodiscard]] const MachinePassContract &contract() const noexcept override; + [[nodiscard]] Expected<MachinePassResult> run(MachineFunction &Function, + const TargetDescription &Target, + MachineFunctionAnalysisManager &Analyses) override; + +private: + [[nodiscard]] Expected<MachinePassResult> + runImplementation(MachineFunction &Function, const TargetDescription &Target, + MachineFunctionAnalysisManager &Analyses); + + const TargetInstrInfo &Info; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_TWOADDRESSINSTRUCTIONPASS_H diff --git a/backend/include/rcc/CodeGen/VirtRegMap.h b/backend/include/rcc/CodeGen/VirtRegMap.h new file mode 100644 index 0000000..eda71c2 --- /dev/null +++ b/backend/include/rcc/CodeGen/VirtRegMap.h @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_CODEGEN_VIRTREGMAP_H +#define RCC_CODEGEN_VIRTREGMAP_H + +#include "rcc/CodeGen/MachineFrameInfo.h" +#include "rcc/CodeGen/MachinePassManager.h" +#include "rcc/CodeGen/TargetRegisterInfo.h" + +#include <cstdint> +#include <optional> +#include <string_view> +#include <unordered_map> +#include <vector> + +namespace rcc { + +class MachineFunction; +class TargetDescription; + +enum class VirtRegAllocationState : std::uint8_t { + Unassigned, + Assigned, + Spilled, + Split, +}; + +struct VirtRegSpillMetadata final { + std::optional<FrameIndex> StackSlot; + LaneMask Lanes = LaneMask::all(); + std::uint64_t SpillCount = 0; + std::uint64_t ReloadCount = 0; + std::uint64_t RematerializationCount = 0; +}; + +struct VirtRegMapEntry final { + Register VirtualReg; + VirtRegAllocationState State = VirtRegAllocationState::Unassigned; + std::optional<PhysicalRegister> PhysicalReg; + std::optional<Register> SplitParent; + std::optional<Register> Origin; + bool Spillable = true; + VirtRegSpillMetadata Spill; +}; + +class VirtRegMap final { +public: + explicit VirtRegMap(MachineFunction &Function) noexcept : Function(&Function) {} + + [[nodiscard]] MachineFunction &function() const noexcept { + return *Function; + } + [[nodiscard]] Expected<void> add(Register VirtualReg, bool Spillable = true, + std::optional<Register> SplitParent = std::nullopt, + std::optional<Register> Origin = std::nullopt); + [[nodiscard]] Expected<void> assign(Register VirtualReg, PhysicalRegister PhysicalReg); + [[nodiscard]] Expected<void> assignStackSlot(Register VirtualReg, FrameIndex StackSlot, + LaneMask Lanes = LaneMask::all()); + [[nodiscard]] Expected<void> markSplit(Register VirtualReg, Register Parent, Register Origin); + [[nodiscard]] Expected<void> setSpillable(Register VirtualReg, bool Spillable); + [[nodiscard]] VirtRegMapEntry *entry(Register VirtualReg) noexcept; + [[nodiscard]] const VirtRegMapEntry *entry(Register VirtualReg) const noexcept; + [[nodiscard]] std::optional<PhysicalRegister> + physicalRegister(Register VirtualReg) const noexcept; + [[nodiscard]] std::optional<FrameIndex> stackSlot(Register VirtualReg) const noexcept; + [[nodiscard]] bool isSpillable(Register VirtualReg) const noexcept; + [[nodiscard]] std::vector<const VirtRegMapEntry *> orderedEntries() const; + [[nodiscard]] Expected<void> verify() const; + +private: + MachineFunction *Function; + std::unordered_map<Register, VirtRegMapEntry> Entries; +}; + +[[nodiscard]] Expected<bool> rewriteVirtualRegisters(MachineFunction &Function, + const TargetDescription &Target, + const VirtRegMap &Mapping); + +class VirtRegMapAnalysis final { +public: + using Result = VirtRegMap; + + [[nodiscard]] static constexpr std::string_view name() noexcept { + return "VirtRegMap"; + } + [[nodiscard]] static Expected<Result> run(MachineFunction &Function, + const TargetDescription &Target, + MachineFunctionAnalysisManager &Analyses); +}; + +class VirtualRegisterRewritePass final : public MachineFunctionPass { +public: + [[nodiscard]] const MachinePassContract &contract() const noexcept override; + [[nodiscard]] Expected<MachinePassResult> run(MachineFunction &Function, + const TargetDescription &Target, + MachineFunctionAnalysisManager &Analyses) override; +}; + +} // namespace rcc + +#endif // RCC_CODEGEN_VIRTREGMAP_H diff --git a/backend/include/rcc/Config/Targets.def b/backend/include/rcc/Config/Targets.def new file mode 100644 index 0000000..e6cdd96 --- /dev/null +++ b/backend/include/rcc/Config/Targets.def @@ -0,0 +1,7 @@ +#ifndef RCC_TARGET +#define RCC_TARGET(TargetName) +#endif + +RCC_TARGET(RISCV) + +#undef RCC_TARGET diff --git a/backend/include/rcc/IR/Constants.h b/backend/include/rcc/IR/Constants.h new file mode 100644 index 0000000..9f894f2 --- /dev/null +++ b/backend/include/rcc/IR/Constants.h @@ -0,0 +1,177 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_IR_CONSTANTS_H +#define RCC_IR_CONSTANTS_H + +#include "rcc/ADT/APFloat.h" +#include "rcc/ADT/APInt.h" +#include "rcc/Support/Error.h" + +#include <cstddef> +#include <cstdint> +#include <optional> +#include <string> +#include <vector> + +namespace rcc { + +enum class GenericConstantTypeKind : std::uint8_t { + Invalid, + Void, + Label, + Token, + Metadata, + Integer, + Byte, + Floating, + Pointer, + Array, + Vector, + Struct, + Function, + Named, +}; + +struct GenericConstantType final { + GenericConstantTypeKind Kind = GenericConstantTypeKind::Invalid; + std::uint32_t BitWidth = 0; + FloatSemantics Semantics = FloatSemantics::IEEEHalf; + std::uint32_t AddressSpace = 0; + std::uint64_t ElementCount = 0; + bool Scalable = false; + bool Packed = false; + bool VarArg = false; + bool NumericName = false; + std::string Name; + std::vector<GenericConstantType> Elements; + + [[nodiscard]] static Expected<GenericConstantType> integer(std::uint32_t BitWidth); + [[nodiscard]] static GenericConstantType primitive(GenericConstantTypeKind Kind) noexcept; + [[nodiscard]] static GenericConstantType byte() noexcept; + [[nodiscard]] static Expected<GenericConstantType> floating(FloatSemantics Semantics); + [[nodiscard]] static GenericConstantType pointer(std::uint32_t AddressSpace = 0) noexcept; + [[nodiscard]] static Expected<GenericConstantType> array(std::uint64_t ElementCount, + GenericConstantType Element); + [[nodiscard]] static Expected<GenericConstantType> + vector(std::uint64_t ElementCount, GenericConstantType Element, bool Scalable = false); + [[nodiscard]] static Expected<GenericConstantType> + structure(std::vector<GenericConstantType> Elements, bool Packed = false); + [[nodiscard]] static Expected<GenericConstantType> + function(GenericConstantType Return, std::vector<GenericConstantType> Parameters, + bool VarArg = false); + [[nodiscard]] static Expected<GenericConstantType> named(std::string Name, + bool NumericName = false); + + [[nodiscard]] const GenericConstantType &resolved() const noexcept; + [[nodiscard]] Expected<void> verify() const; + + friend bool operator==(const GenericConstantType &Left, + const GenericConstantType &Right) = default; +}; + +enum class GenericConstantKind : std::uint8_t { + Invalid, + Integer, + Floating, + NullPointer, + ZeroInitializer, + Undef, + Poison, + Aggregate, + ByteString, + GlobalAddress, + BlockAddress, + DSOLocalEquivalent, + NoCFI, + PointerAuth, + Expression, +}; + +enum class GenericConstantExpressionOpcode : std::uint8_t { + Invalid, + Trunc, + BitCast, + AddressSpaceCast, + IntToPtr, + PtrToAddr, + PtrToInt, + Add, + Sub, + Xor, + GetElementPtr, + ShuffleVector, + InsertElement, + ExtractElement, + Splat, +}; + +enum class GenericConstantExpressionFlag : std::uint8_t { + NoUnsignedWrap, + NoSignedWrap, + InBounds, + NoUnsignedSignedWrap, + GEPNoUnsignedWrap, + Count, +}; + +class GenericConstantExpressionFlags final { +public: + constexpr GenericConstantExpressionFlags() noexcept = default; + + [[nodiscard]] constexpr bool has(GenericConstantExpressionFlag Flag) const noexcept { + const auto Index = static_cast<std::uint8_t>(Flag); + if (Index >= static_cast<std::uint8_t>(GenericConstantExpressionFlag::Count)) + return false; + return (Mask & (std::uint8_t{1} << Index)) != 0; + } + + constexpr void set(GenericConstantExpressionFlag Flag) noexcept { + const auto Index = static_cast<std::uint8_t>(Flag); + if (Index < static_cast<std::uint8_t>(GenericConstantExpressionFlag::Count)) + Mask |= std::uint8_t{1} << Index; + } + + [[nodiscard]] constexpr std::uint8_t rawValue() const noexcept { + return Mask; + } + + friend constexpr bool operator==(GenericConstantExpressionFlags Left, + GenericConstantExpressionFlags Right) noexcept = default; + +private: + std::uint8_t Mask = 0; +}; + +struct GenericConstantData final { + GenericConstantType Type; + GenericConstantKind Kind = GenericConstantKind::Invalid; + std::optional<APInt> IntegerValue; + std::optional<APFloat> FloatingValue; + std::vector<std::byte> ByteValue; + std::vector<GenericConstantData> Elements; + std::string Symbol; + std::string Block; + bool NumericSymbol = false; + bool NumericBlock = false; + GenericConstantExpressionOpcode ExpressionOpcode = GenericConstantExpressionOpcode::Invalid; + GenericConstantExpressionFlags ExpressionFlags; + std::optional<GenericConstantType> SourceElementType; + std::optional<APInt> InRangeStart; + std::optional<APInt> InRangeEnd; + + [[nodiscard]] static Expected<GenericConstantData> integer(APInt Value); + [[nodiscard]] static Expected<GenericConstantData> floating(APFloat Value); + [[nodiscard]] static Expected<GenericConstantData> byteString(GenericConstantType Type, + std::vector<std::byte> Bytes); + + [[nodiscard]] Expected<std::string> toLLVMString() const; + [[nodiscard]] Expected<void> verify() const; + [[nodiscard]] bool referencesSymbol() const noexcept; + + friend bool operator==(const GenericConstantData &Left, + const GenericConstantData &Right) = default; +}; + +} // namespace rcc + +#endif // RCC_IR_CONSTANTS_H diff --git a/backend/include/rcc/IR/DataLayout.h b/backend/include/rcc/IR/DataLayout.h new file mode 100644 index 0000000..0ca3f07 --- /dev/null +++ b/backend/include/rcc/IR/DataLayout.h @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_IR_DATALAYOUT_H +#define RCC_IR_DATALAYOUT_H + +#include "rcc/IR/Constants.h" +#include "rcc/Support/Error.h" + +#include <cstddef> +#include <cstdint> +#include <string> +#include <string_view> +#include <vector> + +namespace rcc { + +class DataLayout final { +public: + [[nodiscard]] static Expected<DataLayout> parse(std::string_view LayoutString); + + [[nodiscard]] Expected<std::uint64_t> getTypeAllocSize(const GenericConstantType &Type) const; + [[nodiscard]] Expected<std::uint64_t> getABITypeAlign(const GenericConstantType &Type) const; + [[nodiscard]] Expected<std::uint64_t> getPrefTypeAlign(const GenericConstantType &Type) const; + [[nodiscard]] Expected<std::uint32_t> getPointerSizeInBits(std::uint32_t AddressSpace = 0) const; + +private: + struct AlignmentSpec final { + std::uint64_t ABI = 1; + std::uint64_t Preferred = 1; + }; + + struct PrimitiveSpec final { + char Kind = '\0'; + std::uint32_t BitWidth = 0; + AlignmentSpec Alignment; + }; + + struct PointerSpec final { + std::uint32_t AddressSpace = 0; + std::uint32_t SizeInBits = 0; + AlignmentSpec Alignment; + std::string AddressSpaceName; + }; + + struct TypeLayout final { + std::uint64_t StoreSize = 0; + std::uint64_t AllocationSize = 0; + std::uint64_t ABIAlignment = 1; + std::uint64_t PreferredAlignment = 1; + }; + + [[nodiscard]] static Expected<std::uint64_t> parseUnsignedField(std::string_view Text, + std::string_view Description); + [[nodiscard]] static Expected<std::uint64_t> alignmentBytes(std::string_view Text, bool AllowZero, + std::string_view Description); + [[nodiscard]] static std::vector<std::string_view> split(std::string_view Text, char Separator); + + void setPrimitiveSpec(PrimitiveSpec Entry); + void setPointerSpec(PointerSpec Entry); + [[nodiscard]] AlignmentSpec naturalAlignment(std::uint64_t StoreSize) const noexcept; + [[nodiscard]] AlignmentSpec scalarAlignment(char Kind, std::uint32_t BitWidth, + std::uint64_t StoreSize) const noexcept; + [[nodiscard]] AlignmentSpec integerAlignment(std::uint32_t BitWidth, + std::uint64_t StoreSize) const noexcept; + [[nodiscard]] AlignmentSpec floatingAlignment(std::uint32_t BitWidth, + std::uint64_t StoreSize) const noexcept; + [[nodiscard]] AlignmentSpec vectorAlignment(std::uint32_t BitWidth, + std::uint64_t StoreSize) const noexcept; + [[nodiscard]] Expected<PointerSpec> getPointerSpec(std::uint32_t AddressSpace) const; + [[nodiscard]] Expected<TypeLayout> getTypeLayout(const GenericConstantType &Type, + std::vector<std::string> &NamedTypeStack, + std::size_t Depth) const; + + bool Explicit = false; + std::vector<PrimitiveSpec> PrimitiveSpecs; + std::vector<PointerSpec> PointerSpecs; + AlignmentSpec AggregateAlignment{1, 8}; +}; + +} // namespace rcc + +#endif // RCC_IR_DATALAYOUT_H diff --git a/backend/include/rcc/IR/PassInstrumentation.h b/backend/include/rcc/IR/PassInstrumentation.h new file mode 100644 index 0000000..b0a673a --- /dev/null +++ b/backend/include/rcc/IR/PassInstrumentation.h @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_IR_PASSINSTRUMENTATION_H +#define RCC_IR_PASSINSTRUMENTATION_H + +#include "rcc/Support/Error.h" + +#include <cstdint> +#include <functional> +#include <mutex> +#include <string_view> +#include <utility> +#include <vector> + +namespace rcc { + +enum class InstrumentationPhase : std::uint8_t { + Before, + After, +}; + +template <typename IRUnitT, typename PassResultT> class PassInstrumentationCallbacks final { +public: + using BeforeCallback = std::function<void(std::string_view, const IRUnitT &)>; + using AfterCallback = std::function<void(std::string_view, const IRUnitT &, const PassResultT &)>; + using FailureCallback = std::function<void(std::string_view, const IRUnitT &, const Error &)>; + using BeforeAnalysisCallback = std::function<void(std::string_view, const IRUnitT &)>; + using AfterAnalysisCallback = + std::function<void(std::string_view, const IRUnitT &, bool, std::uint64_t)>; + using AnalysisFailureCallback = + std::function<void(std::string_view, const IRUnitT &, const Error &)>; + + void addBeforeCallback(BeforeCallback Callback) { + addCallback(Before, std::move(Callback)); + } + + void addAfterCallback(AfterCallback Callback) { + addCallback(After, std::move(Callback)); + } + + void addFailureCallback(FailureCallback Callback) { + addCallback(Failure, std::move(Callback)); + } + + void addBeforeAnalysisCallback(BeforeAnalysisCallback Callback) { + addCallback(BeforeAnalysis, std::move(Callback)); + } + + void addAfterAnalysisCallback(AfterAnalysisCallback Callback) { + addCallback(AfterAnalysis, std::move(Callback)); + } + + void addAnalysisFailureCallback(AnalysisFailureCallback Callback) { + addCallback(AnalysisFailure, std::move(Callback)); + } + + void runBefore(std::string_view Name, const IRUnitT &Unit) const { + for (const BeforeCallback &Callback : snapshot(Before)) + Callback(Name, Unit); + } + + void runAfter(std::string_view Name, const IRUnitT &Unit, const PassResultT &Result) const { + for (const AfterCallback &Callback : snapshot(After)) + Callback(Name, Unit, Result); + } + + void runFailure(std::string_view Name, const IRUnitT &Unit, const Error &FailureValue) const { + for (const FailureCallback &Callback : snapshot(Failure)) + Callback(Name, Unit, FailureValue); + } + + void runBeforeAnalysis(std::string_view Name, const IRUnitT &Unit) const { + for (const BeforeAnalysisCallback &Callback : snapshot(BeforeAnalysis)) + Callback(Name, Unit); + } + + void runAfterAnalysis(std::string_view Name, const IRUnitT &Unit, bool CacheHit, + std::uint64_t Nanoseconds) const { + for (const AfterAnalysisCallback &Callback : snapshot(AfterAnalysis)) + Callback(Name, Unit, CacheHit, Nanoseconds); + } + + void runAnalysisFailure(std::string_view Name, const IRUnitT &Unit, + const Error &FailureValue) const { + for (const AnalysisFailureCallback &Callback : snapshot(AnalysisFailure)) + Callback(Name, Unit, FailureValue); + } + + [[nodiscard]] bool empty() const { + std::scoped_lock Lock(Mutex); + return Before.empty() && After.empty() && Failure.empty() && BeforeAnalysis.empty() && + AfterAnalysis.empty() && AnalysisFailure.empty(); + } + +private: + template <typename CallbackT> + void addCallback(std::vector<CallbackT> &Callbacks, CallbackT Callback) { + if (!Callback) + return; + std::scoped_lock Lock(Mutex); + Callbacks.push_back(std::move(Callback)); + } + + template <typename CallbackT> + [[nodiscard]] std::vector<CallbackT> snapshot(const std::vector<CallbackT> &Callbacks) const { + std::scoped_lock Lock(Mutex); + return Callbacks; + } + + mutable std::mutex Mutex; + std::vector<BeforeCallback> Before; + std::vector<AfterCallback> After; + std::vector<FailureCallback> Failure; + std::vector<BeforeAnalysisCallback> BeforeAnalysis; + std::vector<AfterAnalysisCallback> AfterAnalysis; + std::vector<AnalysisFailureCallback> AnalysisFailure; +}; + +} // namespace rcc + +#endif // RCC_IR_PASSINSTRUMENTATION_H diff --git a/backend/include/rcc/IR/PassManager.h b/backend/include/rcc/IR/PassManager.h new file mode 100644 index 0000000..568cf97 --- /dev/null +++ b/backend/include/rcc/IR/PassManager.h @@ -0,0 +1,532 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_IR_PASSMANAGER_H +#define RCC_IR_PASSMANAGER_H + +#include "rcc/Support/Error.h" + +#include <algorithm> +#include <chrono> +#include <concepts> +#include <cstddef> +#include <cstdint> +#include <functional> +#include <memory> +#include <optional> +#include <ranges> +#include <set> +#include <span> +#include <string> +#include <string_view> +#include <unordered_map> +#include <unordered_set> +#include <utility> +#include <vector> + +namespace rcc { + +class TargetDescription; + +using AnalysisID = const void *; + +class PreservedAnalyses final { +public: + [[nodiscard]] static PreservedAnalyses all() noexcept { + PreservedAnalyses Result; + Result.PreserveAll = true; + return Result; + } + + [[nodiscard]] static PreservedAnalyses none() noexcept { + return {}; + } + + void preserve(AnalysisID ID) { + if (ID != nullptr) + Preserved.insert(ID); + } + + template <typename AnalysisT> void preserve() { + preserve(analysisID<AnalysisT>()); + } + + [[nodiscard]] bool preserves(AnalysisID ID) const noexcept { + return PreserveAll || (ID != nullptr && Preserved.contains(ID)); + } + + [[nodiscard]] bool preservesAll() const noexcept { + return PreserveAll; + } + + template <typename AnalysisT> [[nodiscard]] static AnalysisID analysisID() noexcept { + static const std::uint8_t Token = 0; + return &Token; + } + +private: + bool PreserveAll = false; + std::unordered_set<AnalysisID> Preserved; +}; + +enum class PassThreadSafety : std::uint8_t { + NotThreadSafe, + ThreadSafe, +}; + +struct PassParameter final { + std::string Name; + std::string Description; + std::optional<std::string> DefaultValue; + bool Required = false; +}; + +template <typename IRUnitT, typename InstrumentationT> struct AnalysisManagerInstrumentation { + static void beforeAnalysis(InstrumentationT *, std::string_view, const IRUnitT &) {} + static void afterAnalysis(InstrumentationT *, std::string_view, const IRUnitT &, bool, + std::uint64_t) {} + static void analysisFailure(InstrumentationT *, std::string_view, const IRUnitT &, const Error &, + std::uint64_t) {} + + template <typename InstructionT> + [[nodiscard]] static Expected<bool> requestAction(InstrumentationT *, std::string_view, + std::string_view, const IRUnitT &, InstructionT, + std::uint64_t) { + return true; + } +}; + +template <typename ChangeT> struct AnalysisRegistration final { + AnalysisID ID = nullptr; + std::string Name; + std::vector<AnalysisID> Dependencies; + ChangeT InvalidatedBy = ChangeT::all(); + PassThreadSafety ThreadSafety = PassThreadSafety::NotThreadSafe; +}; + +template <typename IRUnitT, typename PassT, typename OptionsT> struct PassManagerCallbacks; + +template <typename IRUnitT, typename PassT, typename OptionsT> class PassManager { +public: + using PassList = std::vector<std::unique_ptr<PassT>>; + using Callbacks = PassManagerCallbacks<IRUnitT, PassT, OptionsT>; + + explicit PassManager(OptionsT Options = {}) : Options(Options) {} + PassManager(const PassManager &) = delete; + PassManager &operator=(const PassManager &) = delete; + PassManager(PassManager &&) = default; + PassManager &operator=(PassManager &&) = default; + ~PassManager() = default; + + [[nodiscard]] Expected<void> addPass(std::unique_ptr<PassT> Pass) { + if (!Pass) + return makeUnexpected(ErrorCode::InvalidArgument, "pass manager cannot add a null pass"); + auto Validation = Callbacks::validate(*Pass); + if (!Validation) + return std::unexpected(Validation.error()); + Passes.push_back(std::move(Pass)); + return {}; + } + + template <typename... Args> + [[nodiscard]] Expected<void> run(IRUnitT &Unit, Args &&...Arguments) const { + return Callbacks::run(Passes, Options, Unit, std::forward<Args>(Arguments)...); + } + + template <typename... Args> + [[nodiscard]] Expected<void> runFunctions(Args &&...Arguments) const + requires requires(const PassList &InputPasses, const OptionsT &InputOptions, + Args &&...InputArguments) { + Callbacks::runFunctions(InputPasses, InputOptions, std::forward<Args>(InputArguments)...); + } + { + return Callbacks::runFunctions(Passes, Options, std::forward<Args>(Arguments)...); + } + + [[nodiscard]] std::size_t size() const noexcept { + return Passes.size(); + } + + [[nodiscard]] PassList takePasses() && noexcept { + return std::move(Passes); + } + +private: + PassList Passes; + OptionsT Options; +}; + +template <typename IRUnitT, typename RegistrationT, typename ChangeT, typename InstrumentationT> +class AnalysisManager { +public: + using Self = AnalysisManager<IRUnitT, RegistrationT, ChangeT, InstrumentationT>; + + AnalysisManager() = default; + AnalysisManager(const AnalysisManager &) = delete; + AnalysisManager &operator=(const AnalysisManager &) = delete; + AnalysisManager(AnalysisManager &&) = delete; + AnalysisManager &operator=(AnalysisManager &&) = delete; + ~AnalysisManager() = default; + + template <typename AnalysisT> [[nodiscard]] static AnalysisID id() noexcept { + return PreservedAnalyses::analysisID<AnalysisT>(); + } + + template <typename AnalysisT> + [[nodiscard]] Expected<void> registerAnalysis(RegistrationT Registration = {}) { + using ResultT = typename AnalysisT::Result; + if (Registration.ID == nullptr) + Registration.ID = id<AnalysisT>(); + if (Registration.Name.empty()) { + if constexpr (requires { + { AnalysisT::name() } -> std::convertible_to<std::string_view>; + }) + Registration.Name = std::string(AnalysisT::name()); + else + Registration.Name = "unnamed-analysis"; + } + + ErasedRunner Runner = [](IRUnitT &Unit, const TargetDescription &Target, + Self &Manager) -> Expected<std::shared_ptr<void>> { + auto Computed = AnalysisT::run(Unit, Target, Manager); + if (!Computed) + return std::unexpected(Computed.error()); + return std::shared_ptr<void>(new ResultT(std::move(*Computed)), + [](void *Value) { delete static_cast<ResultT *>(Value); }); + }; + ErasedInvalidator Invalidator = [](void *Value, const IRUnitT &Unit, + const PreservedAnalyses &Preserved) -> bool { + if constexpr (requires(ResultT &Result, const IRUnitT &InputUnit, + const PreservedAnalyses &Analyses) { + { Result.invalidate(InputUnit, Analyses) } -> std::convertible_to<bool>; + }) + return static_cast<ResultT *>(Value)->invalidate(Unit, Preserved); + return true; + }; + return registerAnalysisImpl(std::move(Registration), std::move(Runner), std::move(Invalidator)); + } + + template <typename AnalysisT> + [[nodiscard]] Expected<typename AnalysisT::Result *> getResult(IRUnitT &Unit, + const TargetDescription &Target) { + const AnalysisID Key = id<AnalysisT>(); + if (!isRegistered(Key)) { + auto Registered = registerAnalysis<AnalysisT>(); + if (!Registered) + return std::unexpected(Registered.error()); + } + auto Result = getResultImpl(Key, Unit, Target); + if (!Result) + return std::unexpected(Result.error()); + return static_cast<typename AnalysisT::Result *>(*Result); + } + + [[nodiscard]] Expected<void *> getResultByID(AnalysisID ID, IRUnitT &Unit, + const TargetDescription &Target) { + return getResultImpl(ID, Unit, Target); + } + + void invalidate(IRUnitT &Unit, const PreservedAnalyses &Preserved, ChangeT Changes) noexcept { + const auto UnitPosition = Cache.find(&Unit); + if (UnitPosition == Cache.end() || Preserved.preservesAll() || Changes.empty()) + return; + + std::set<AnalysisID> Invalid; + for (auto &[ID, Entry] : UnitPosition->second) { + if (Preserved.preserves(ID)) + continue; + const auto Registration = Registry.find(ID); + if (Registration == Registry.end() || + Registration->second.Info.InvalidatedBy.intersects(Changes)) { + const bool ShouldInvalidate = + Registration == Registry.end() || !Entry.Value || !Registration->second.Invalidator || + Registration->second.Invalidator(Entry.Value.get(), Unit, Preserved); + if (ShouldInvalidate) + Invalid.insert(ID); + } + } + + bool Added = true; + while (Added) { + Added = false; + for (const auto &[ID, Entry] : UnitPosition->second) { + (void)Entry; + if (Invalid.contains(ID) || Preserved.preserves(ID)) + continue; + const auto Registration = Registry.find(ID); + if (Registration != Registry.end() && + std::ranges::any_of( + Registration->second.Info.Dependencies, + [&Invalid](AnalysisID Dependency) { return Invalid.contains(Dependency); })) + Added = Invalid.insert(ID).second || Added; + } + } + + for (const AnalysisID ID : Invalid) + UnitPosition->second.erase(ID); + if (UnitPosition->second.empty()) + Cache.erase(UnitPosition); + } + + void invalidate(IRUnitT &Unit, const PreservedAnalyses &Preserved) noexcept { + invalidate(Unit, Preserved, ChangeT::all()); + } + + [[nodiscard]] bool hasCached(const IRUnitT &Unit, AnalysisID ID) const noexcept { + const auto UnitPosition = Cache.find(const_cast<IRUnitT *>(&Unit)); + if (UnitPosition == Cache.end()) + return false; + const auto AnalysisPosition = UnitPosition->second.find(ID); + return AnalysisPosition != UnitPosition->second.end() && AnalysisPosition->second.Value; + } + + [[nodiscard]] std::size_t cachedResultCount(const IRUnitT &Unit) const noexcept { + const auto Position = Cache.find(const_cast<IRUnitT *>(&Unit)); + return Position == Cache.end() ? 0 : Position->second.size(); + } + + [[nodiscard]] bool isRegistered(AnalysisID ID) const noexcept { + return ID != nullptr && Registry.contains(ID); + } + + void clear(IRUnitT &Unit) noexcept { + Cache.erase(&Unit); + ActiveComputations.erase(&Unit); + } + + void clearAll() noexcept { + Cache.clear(); + ActiveComputations.clear(); + } + + [[nodiscard]] Expected<std::unique_ptr<Self>> forkEmpty() const { + if (!ActiveComputations.empty()) + return makeUnexpected(ErrorCode::InvalidArgument, + "cannot fork an analysis manager during analysis computation"); + auto Result = std::make_unique<Self>(); + Result->Registry = Registry; + return Result; + } + + [[nodiscard]] Expected<void> replaceRegistrations(Self &&Source) { + if (!Cache.empty() || !ActiveComputations.empty() || !Source.Cache.empty() || + !Source.ActiveComputations.empty()) + return makeUnexpected(ErrorCode::InvalidArgument, + "analysis registrations can only be replaced on empty managers"); + Registry = std::move(Source.Registry); + return {}; + } + + [[nodiscard]] Expected<void> + validateParallelRequirements(std::span<const AnalysisID> RequiredAnalyses) const { + std::vector<AnalysisID> Worklist(RequiredAnalyses.begin(), RequiredAnalyses.end()); + std::unordered_set<AnalysisID> Visited; + for (std::size_t Index = 0; Index < Worklist.size(); ++Index) { + const AnalysisID ID = Worklist[Index]; + if (ID == nullptr || !Visited.insert(ID).second) + continue; + const auto Position = Registry.find(ID); + if (Position == Registry.end()) + return makeUnexpected(ErrorCode::InvalidArgument, + "parallel pass requires an unregistered analysis"); + if (Position->second.Info.ThreadSafety != PassThreadSafety::ThreadSafe) + return makeUnexpected(ErrorCode::InvalidArgument, "parallel pipeline requires analysis '" + + Position->second.Info.Name + + "' to be marked thread-safe"); + Worklist.insert(Worklist.end(), Position->second.Info.Dependencies.begin(), + Position->second.Info.Dependencies.end()); + } + return {}; + } + + void setInstrumentation(InstrumentationT *NewInstrumentation) noexcept { + Instrumentation = NewInstrumentation; + } + + void setActivePass(std::string Name) { + ActivePass = std::move(Name); + } + + template <typename InstructionT> + [[nodiscard]] Expected<bool> requestAction(std::string_view Rule, const IRUnitT &Unit, + InstructionT Instruction, std::uint64_t Cost = 1) { + if (Rule.empty() || Cost == 0) + return makeUnexpected(ErrorCode::InvalidArgument, + "pass action requires a name and nonzero cost"); + if (Instrumentation == nullptr) + return true; + return AnalysisManagerInstrumentation<IRUnitT, InstrumentationT>::requestAction( + Instrumentation, ActivePass.empty() ? "unscoped" : ActivePass, Rule, Unit, Instruction, + Cost); + } + + [[nodiscard]] Expected<bool> requestAction(std::string_view Rule, const IRUnitT &Unit, + std::uint64_t Cost = 1) { + if (Rule.empty() || Cost == 0) + return makeUnexpected(ErrorCode::InvalidArgument, + "pass action requires a name and nonzero cost"); + if (Instrumentation == nullptr) + return true; + return AnalysisManagerInstrumentation<IRUnitT, InstrumentationT>::requestAction( + Instrumentation, ActivePass.empty() ? "unscoped" : ActivePass, Rule, Unit, nullptr, Cost); + } + + [[nodiscard]] const std::string &activePass() const noexcept { + return ActivePass; + } + +private: + using ErasedRunner = + std::function<Expected<std::shared_ptr<void>>(IRUnitT &, const TargetDescription &, Self &)>; + using ErasedInvalidator = std::function<bool(void *, const IRUnitT &, const PreservedAnalyses &)>; + + struct RegisteredAnalysis final { + RegistrationT Info; + ErasedRunner Runner; + ErasedInvalidator Invalidator; + }; + + struct CacheEntry final { + std::shared_ptr<void> Value; + AnalysisID ID = nullptr; + }; + + [[nodiscard]] static bool hasDuplicateAnalysisIDs(std::span<const AnalysisID> IDs) { + std::unordered_set<AnalysisID> Unique; + for (const AnalysisID ID : IDs) { + if (ID == nullptr || !Unique.insert(ID).second) + return true; + } + return false; + } + + [[nodiscard]] Expected<void> registerAnalysisImpl(RegistrationT Registration, ErasedRunner Runner, + ErasedInvalidator Invalidator) { + if (Registration.ID == nullptr || Registration.Name.empty() || !Runner || !Invalidator) + return makeUnexpected(ErrorCode::InvalidArgument, "analysis registration is incomplete"); + if (hasDuplicateAnalysisIDs(Registration.Dependencies)) + return makeUnexpected(ErrorCode::InvalidArgument, + "analysis dependencies must be non-null and unique"); + if (std::ranges::find(Registration.Dependencies, Registration.ID) != + Registration.Dependencies.end()) + return makeUnexpected(ErrorCode::InvalidArgument, + "analysis cannot directly depend on itself"); + if (Registry.contains(Registration.ID)) + return makeUnexpected(ErrorCode::InvalidArgument, "analysis ID is already registered"); + if (std::ranges::any_of(Registry, [&Registration](const auto &Entry) { + return Entry.second.Info.Name == Registration.Name; + })) + return makeUnexpected(ErrorCode::InvalidArgument, "analysis name is already registered"); + const AnalysisID ID = Registration.ID; + Registry.emplace( + ID, RegisteredAnalysis{std::move(Registration), std::move(Runner), std::move(Invalidator)}); + return {}; + } + + [[nodiscard]] Expected<void *> getResultImpl(AnalysisID ID, IRUnitT &Unit, + const TargetDescription &Target) { + if (ID == nullptr) + return makeUnexpected(ErrorCode::InvalidArgument, "analysis ID cannot be null"); + auto &UnitCache = Cache[&Unit]; + if (const auto Cached = UnitCache.find(ID); Cached != UnitCache.end() && Cached->second.Value) { + const auto Registration = Registry.find(ID); + const std::string_view Name = Registration == Registry.end() + ? std::string_view{"unknown-analysis"} + : std::string_view{Registration->second.Info.Name}; + if (Instrumentation != nullptr) + AnalysisManagerInstrumentation<IRUnitT, InstrumentationT>::afterAnalysis( + Instrumentation, Name, Unit, true, 0); + return Cached->second.Value.get(); + } + + const auto RegistrationPosition = Registry.find(ID); + if (RegistrationPosition == Registry.end()) + return makeUnexpected(ErrorCode::InvalidArgument, "analysis ID is not registered"); + const RegisteredAnalysis &Registration = RegistrationPosition->second; + auto &Stack = ActiveComputations[&Unit]; + if (std::ranges::find(Stack, ID) != Stack.end()) { + Error Failure(ErrorCode::InvalidArgument, + "analysis dependency cycle reaches '" + Registration.Info.Name + "'"); + if (Instrumentation != nullptr) + AnalysisManagerInstrumentation<IRUnitT, InstrumentationT>::analysisFailure( + Instrumentation, Registration.Info.Name, Unit, Failure, 0); + return std::unexpected(std::move(Failure)); + } + + if (Instrumentation != nullptr) + AnalysisManagerInstrumentation<IRUnitT, InstrumentationT>::beforeAnalysis( + Instrumentation, Registration.Info.Name, Unit); + const auto Start = std::chrono::steady_clock::now(); + Stack.push_back(ID); + for (const AnalysisID Dependency : Registration.Info.Dependencies) { + if (!Registry.contains(Dependency)) { + Stack.pop_back(); + if (Stack.empty()) + ActiveComputations.erase(&Unit); + return makeUnexpected(ErrorCode::InvalidArgument, + "analysis depends on an unregistered analysis"); + } + auto DependencyResult = getResultImpl(Dependency, Unit, Target); + if (!DependencyResult) { + Stack.pop_back(); + if (Stack.empty()) + ActiveComputations.erase(&Unit); + Error Failure = DependencyResult.error(); + std::string Message = "analysis '" + Registration.Info.Name + "' failed: "; + Message.append(Failure.message()); + Failure = Error(Failure.code(), std::move(Message)); + if (Instrumentation != nullptr) + AnalysisManagerInstrumentation<IRUnitT, InstrumentationT>::analysisFailure( + Instrumentation, Registration.Info.Name, Unit, Failure, elapsedNanoseconds(Start)); + return std::unexpected(std::move(Failure)); + } + } + + auto Computed = Registration.Runner(Unit, Target, *this); + Stack.pop_back(); + if (Stack.empty()) + ActiveComputations.erase(&Unit); + const std::uint64_t Duration = elapsedNanoseconds(Start); + if (!Computed) { + Error Failure = Computed.error(); + std::string Message = "analysis '" + Registration.Info.Name + "' failed: "; + Message.append(Failure.message()); + Failure = Error(Failure.code(), std::move(Message)); + if (Instrumentation != nullptr) + AnalysisManagerInstrumentation<IRUnitT, InstrumentationT>::analysisFailure( + Instrumentation, Registration.Info.Name, Unit, Failure, Duration); + return std::unexpected(std::move(Failure)); + } + if (!*Computed) { + Error Failure(ErrorCode::InternalError, + "analysis '" + Registration.Info.Name + "' returned a null result"); + if (Instrumentation != nullptr) + AnalysisManagerInstrumentation<IRUnitT, InstrumentationT>::analysisFailure( + Instrumentation, Registration.Info.Name, Unit, Failure, Duration); + return std::unexpected(std::move(Failure)); + } + + void *Result = Computed->get(); + UnitCache.emplace(ID, CacheEntry{std::move(*Computed), ID}); + if (Instrumentation != nullptr) + AnalysisManagerInstrumentation<IRUnitT, InstrumentationT>::afterAnalysis( + Instrumentation, Registration.Info.Name, Unit, false, Duration); + return Result; + } + + [[nodiscard]] static std::uint64_t + elapsedNanoseconds(std::chrono::steady_clock::time_point Start) noexcept { + const auto Elapsed = std::chrono::duration_cast<std::chrono::nanoseconds>( + std::chrono::steady_clock::now() - Start); + return Elapsed.count() <= 0 ? 0 : static_cast<std::uint64_t>(Elapsed.count()); + } + + std::unordered_map<AnalysisID, RegisteredAnalysis> Registry; + std::unordered_map<IRUnitT *, std::unordered_map<AnalysisID, CacheEntry>> Cache; + std::unordered_map<IRUnitT *, std::vector<AnalysisID>> ActiveComputations; + InstrumentationT *Instrumentation = nullptr; + std::string ActivePass; +}; + +} // namespace rcc + +#endif // RCC_IR_PASSMANAGER_H diff --git a/backend/include/rcc/MC/ELFObjectWriter.h b/backend/include/rcc/MC/ELFObjectWriter.h new file mode 100644 index 0000000..2cd8879 --- /dev/null +++ b/backend/include/rcc/MC/ELFObjectWriter.h @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_MC_ELFOBJECTWRITER_H +#define RCC_MC_ELFOBJECTWRITER_H + +#include "rcc/MC/MCObjectWriter.h" + +#include <cstdint> +#include <string> +#include <utility> +#include <vector> + +namespace rcc::mc { + +struct ELFExtraSection final { + std::string Name; + std::uint32_t Type = 1; + std::uint64_t Flags = 0; + std::uint64_t Alignment = 1; + std::uint64_t EntrySize = 0; + std::vector<std::uint8_t> Contents; +}; + +struct ELFObjectTargetInfo final { + std::uint16_t Machine = 0; + std::uint8_t OSABI = 0; + std::uint8_t ABIVersion = 0; + std::uint32_t Flags = 0; + std::vector<ELFExtraSection> ExtraSections; +}; + +class ELF64LEObjectWriter final : public MCObjectWriter { +public: + explicit ELF64LEObjectWriter(ELFObjectTargetInfo Target) : Target(std::move(Target)) {} + + [[nodiscard]] Expected<std::vector<std::uint8_t>> + writeObject(const MCAssembledObject &Object) const override; + +private: + ELFObjectTargetInfo Target; +}; + +} // namespace rcc::mc + +#endif // RCC_MC_ELFOBJECTWRITER_H diff --git a/backend/include/rcc/MC/MCAsmBackend.h b/backend/include/rcc/MC/MCAsmBackend.h new file mode 100644 index 0000000..76e6214 --- /dev/null +++ b/backend/include/rcc/MC/MCAsmBackend.h @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_MC_MCASMBACKEND_H +#define RCC_MC_MCASMBACKEND_H + +#include "rcc/MC/MCFixup.h" +#include "rcc/MC/MCInst.h" +#include "rcc/Support/Error.h" + +#include <cstdint> +#include <span> +#include <vector> + +namespace rcc::mc { + +struct MCFixupKindInfo final { + std::uint8_t StorageSize = 0; + bool PCRelative = false; +}; + +struct MCFixupRelocation final { + std::uint32_t Type = 0; + MCSymbolRef Symbol; + std::int64_t Addend = 0; +}; + +class MCAsmBackend { +public: + virtual ~MCAsmBackend() = default; + + [[nodiscard]] virtual Expected<MCFixupKindInfo> fixupKindInfo(MCFixupKind Kind) const = 0; + [[nodiscard]] virtual Expected<void> applyFixup(MCFixupKind Kind, std::span<std::uint8_t> Data, + std::uint64_t Offset, + std::int64_t Value) const = 0; + [[nodiscard]] virtual Expected<std::uint32_t> relocationType(const MCFixup &Fixup, + const MCValue &Value) const = 0; + [[nodiscard]] virtual Expected<std::vector<MCFixupRelocation>> + relocationsForFixup(const MCFixup &Fixup, const MCValue &Value) const; + + [[nodiscard]] virtual bool mayNeedRelaxation(const MCInst &) const noexcept { + return false; + } + [[nodiscard]] virtual Expected<bool> fixupNeedsRelaxation(const MCFixup &, std::int64_t) const { + return false; + } + [[nodiscard]] virtual Expected<MCInst> relaxInstruction(const MCInst &) const; + + [[nodiscard]] virtual std::uint32_t objectFlags() const noexcept { + return 0; + } +}; + +} // namespace rcc::mc + +#endif // RCC_MC_MCASMBACKEND_H diff --git a/backend/include/rcc/MC/MCAssembler.h b/backend/include/rcc/MC/MCAssembler.h new file mode 100644 index 0000000..7a75498 --- /dev/null +++ b/backend/include/rcc/MC/MCAssembler.h @@ -0,0 +1,131 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_MC_MCASSEMBLER_H +#define RCC_MC_MCASSEMBLER_H + +#include "rcc/MC/MCAsmBackend.h" +#include "rcc/MC/MCCodeEmitter.h" +#include "rcc/MC/MCDwarfFrameEmitter.h" +#include "rcc/MC/MCObjectWriter.h" +#include "rcc/MC/MCStreamer.h" +#include "rcc/Support/Error.h" + +#include <cstddef> +#include <cstdint> +#include <memory> +#include <optional> +#include <span> +#include <unordered_map> +#include <variant> +#include <vector> + +namespace rcc::mc { + +class MCAssembler final { +public: + MCAssembler(const MCCodeEmitter &Emitter, const MCAsmBackend &Backend) noexcept + : Emitter(&Emitter), Backend(&Backend) {} + + [[nodiscard]] Expected<void> switchSection(MCSectionRef Section); + [[nodiscard]] Expected<void> emitLabel(MCSymbolRef Symbol); + [[nodiscard]] Expected<void> emitInstruction(const MCInst &Instruction); + [[nodiscard]] Expected<void> emitValue(MCExprRef Value, std::uint8_t Size); + [[nodiscard]] Expected<void> emitBytes(std::span<const std::uint8_t> Bytes); + [[nodiscard]] Expected<void> emitZeroFill(std::uint64_t Size); + [[nodiscard]] Expected<void> emitAlignment(std::uint64_t Alignment, std::uint8_t Fill, + std::uint64_t MaximumBytes); + [[nodiscard]] Expected<void> emitSymbolAttribute(MCSymbolRef Symbol, MCSymbolAttribute Attribute); + [[nodiscard]] Expected<void> emitSymbolSize(MCSymbolRef Symbol, MCExprRef Size); + [[nodiscard]] Expected<void> emitCommonSymbol(MCSymbolRef Symbol, std::uint64_t Size, + std::uint64_t Alignment); + [[nodiscard]] Expected<void> emitCFI(const MCCFIInstruction &Instruction); + [[nodiscard]] Expected<void> configureDwarfFrame(MCSectionRef Section, MCDwarfFrameInfo Info); + + [[nodiscard]] Expected<MCAssembledObject> assemble(); + +private: + struct InstructionFragment final { + MCInst Instruction; + }; + struct DataFragment final { + std::vector<std::uint8_t> Bytes; + std::vector<MCFixup> Fixups; + }; + struct AlignmentFragment final { + std::uint64_t Alignment = 1; + std::uint8_t Fill = 0; + std::uint64_t MaximumBytes = 0; + }; + struct ZeroFillFragment final { + std::uint64_t Size = 0; + }; + using FragmentPayload = + std::variant<InstructionFragment, DataFragment, AlignmentFragment, ZeroFillFragment>; + + struct Fragment final { + FragmentPayload Payload; + std::uint64_t Offset = 0; + std::uint64_t Size = 0; + std::vector<std::uint8_t> EncodedBytes; + std::vector<MCFixup> Fixups; + }; + + struct SectionData final { + MCSectionRef Section; + std::vector<Fragment> Fragments; + std::uint64_t AddressSize = 0; + std::uint64_t Alignment = 1; + }; + + struct SymbolData final { + MCSymbolRef Symbol; + MCObjectSymbolBinding Binding = MCObjectSymbolBinding::Local; + bool BindingWasSet = false; + MCObjectSymbolType Type = MCObjectSymbolType::None; + MCObjectSymbolVisibility Visibility = MCObjectSymbolVisibility::Default; + std::optional<std::size_t> SectionIndex; + std::size_t FragmentIndex = 0; + MCExprRef Size; + std::uint64_t CommonSize = 0; + std::uint64_t CommonAlignment = 0; + }; + + struct CFIEvent final { + MCCFIInstruction Instruction; + std::size_t FragmentIndex = 0; + }; + + struct CFIProcedure final { + MCSymbolRef Function; + std::size_t SectionIndex = 0; + std::size_t BeginFragmentIndex = 0; + std::size_t EndFragmentIndex = 0; + std::vector<CFIEvent> Events; + bool IsClosed = false; + }; + + [[nodiscard]] Expected<std::size_t> currentSectionIndex() const; + [[nodiscard]] SymbolData &getOrCreateSymbol(MCSymbolRef Symbol); + [[nodiscard]] Expected<void> encodeFragments(); + [[nodiscard]] Expected<bool> layoutAndRelax(); + [[nodiscard]] Expected<MCAssembledObject> finalizeObject(); + [[nodiscard]] Expected<void> appendDwarfFrame(MCAssembledObject &Object) const; + [[nodiscard]] std::optional<std::uint64_t> symbolOffset(const SymbolData &Symbol) const; + + const MCCodeEmitter *Emitter; + const MCAsmBackend *Backend; + std::vector<SectionData> Sections; + std::unordered_map<const MCSection *, std::size_t> SectionIndices; + std::vector<SymbolData> Symbols; + std::unordered_map<const MCSymbol *, std::size_t> SymbolIndices; + std::optional<std::size_t> CurrentSection; + std::vector<CFIProcedure> CFIProcedures; + std::optional<std::size_t> ActiveCFIProcedure; + MCSectionRef EHFrameSection; + std::optional<MCDwarfFrameInfo> DwarfFrameInfo; + bool WasAssembled = false; +}; + +} // namespace rcc::mc + +#endif // RCC_MC_MCASSEMBLER_H diff --git a/backend/include/rcc/MC/MCCFI.h b/backend/include/rcc/MC/MCCFI.h new file mode 100644 index 0000000..e6ac396 --- /dev/null +++ b/backend/include/rcc/MC/MCCFI.h @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_MC_MCCFI_H +#define RCC_MC_MCCFI_H + +#include "rcc/MC/MCSymbol.h" +#include "rcc/Support/Error.h" + +#include <cstdint> +#include <vector> + +namespace rcc::mc { + +enum class MCCFIOperation : std::uint8_t { + StartProc, + EndProc, + Sections, + Personality, + LSDA, + SignalFrame, + ReturnColumn, + SameValue, + RememberState, + RestoreState, + Offset, + DefCFARegister, + DefCFAOffset, + DefCFA, + RelativeOffset, + AdjustCFAOffset, + Escape, + Restore, + Undefined, + Register, + WindowSave, + NegateReturnAddressState, + NegateReturnAddressStateWithPC, + GNUArgsSize, + Label, + ValueOffset, +}; + +struct MCCFIInstruction final { + MCCFIOperation Operation = MCCFIOperation::StartProc; + std::vector<std::int64_t> Operands; + std::vector<std::uint8_t> Bytes; + MCSymbolRef Symbol; + + [[nodiscard]] Expected<void> verify() const; +}; + +} // namespace rcc::mc + +#endif // RCC_MC_MCCFI_H diff --git a/backend/include/rcc/MC/MCCodeEmitter.h b/backend/include/rcc/MC/MCCodeEmitter.h new file mode 100644 index 0000000..7307a83 --- /dev/null +++ b/backend/include/rcc/MC/MCCodeEmitter.h @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_MC_MCCODEEMITTER_H +#define RCC_MC_MCCODEEMITTER_H + +#include "rcc/MC/MCFixup.h" +#include "rcc/MC/MCInst.h" +#include "rcc/Support/Error.h" + +#include <cstdint> +#include <vector> + +namespace rcc::mc { + +struct MCEncodedInstruction final { + std::vector<std::uint8_t> Bytes; + std::vector<MCFixup> Fixups; +}; + +class MCCodeEmitter { +public: + virtual ~MCCodeEmitter() = default; + + [[nodiscard]] virtual Expected<MCEncodedInstruction> + encodeInstruction(const MCInst &Instruction) const = 0; +}; + +} // namespace rcc::mc + +#endif // RCC_MC_MCCODEEMITTER_H diff --git a/backend/include/rcc/MC/MCContext.h b/backend/include/rcc/MC/MCContext.h new file mode 100644 index 0000000..e9b2d90 --- /dev/null +++ b/backend/include/rcc/MC/MCContext.h @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_MC_MCCONTEXT_H +#define RCC_MC_MCCONTEXT_H + +#include "rcc/MC/MCSection.h" +#include "rcc/MC/MCSymbol.h" +#include "rcc/Support/Error.h" + +#include <cstdint> +#include <functional> +#include <string> +#include <string_view> +#include <unordered_map> + +namespace rcc::mc { + +class MCContext final { +public: + [[nodiscard]] Expected<MCSymbolRef> getOrCreateSymbol(std::string Name, MCSymbolKind Kind); + [[nodiscard]] Expected<MCSymbolRef> createTemporarySymbol(std::string_view Prefix); + [[nodiscard]] MCSymbolRef symbol(std::string_view Name) const noexcept; + + [[nodiscard]] Expected<MCSectionRef> getOrCreateSection(MCSectionDesc Description); + [[nodiscard]] MCSectionRef section(std::string_view Name) const noexcept; + +private: + struct StringHash final { + using is_transparent = void; // NOLINT(readability-identifier-naming) + + [[nodiscard]] std::size_t operator()(std::string_view Value) const noexcept { + return std::hash<std::string_view>{}(Value); + } + }; + + std::unordered_map<std::string, MCSymbolRef, StringHash, std::equal_to<>> Symbols; + std::unordered_map<std::string, MCSectionRef, StringHash, std::equal_to<>> Sections; + std::uint64_t NextTemporarySymbol = 0; +}; + +} // namespace rcc::mc + +#endif // RCC_MC_MCCONTEXT_H diff --git a/backend/include/rcc/MC/MCDwarfFrameEmitter.h b/backend/include/rcc/MC/MCDwarfFrameEmitter.h new file mode 100644 index 0000000..f338f36 --- /dev/null +++ b/backend/include/rcc/MC/MCDwarfFrameEmitter.h @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_MC_MCDWARFFRAMEEMITTER_H +#define RCC_MC_MCDWARFFRAMEEMITTER_H + +#include "rcc/MC/MCAsmBackend.h" +#include "rcc/MC/MCCFI.h" +#include "rcc/MC/MCObjectWriter.h" +#include "rcc/Support/Error.h" + +#include <cstdint> +#include <span> +#include <vector> + +namespace rcc::mc { + +struct MCDwarfFrameInfo final { + std::uint64_t CodeAlignment = 1; + std::int64_t DataAlignment = -8; + std::uint32_t ReturnAddressRegister = 0; + std::uint32_t InitialCFARegister = 0; + std::uint64_t InitialCFAOffset = 0; + + [[nodiscard]] Expected<void> verify() const; +}; + +struct MCDwarfFrameInstruction final { + MCCFIInstruction Instruction; + std::uint64_t Offset = 0; +}; + +struct MCDwarfFrameProcedure final { + MCSymbolRef Function; + std::uint64_t AddressRange = 0; + std::vector<MCDwarfFrameInstruction> Instructions; +}; + +[[nodiscard]] Expected<MCObjectSection> +emitDwarfEHFrame(MCSectionRef Section, std::span<const MCDwarfFrameProcedure> Procedures, + const MCDwarfFrameInfo &Info, const MCAsmBackend &Backend); + +} // namespace rcc::mc + +#endif // RCC_MC_MCDWARFFRAMEEMITTER_H diff --git a/backend/include/rcc/MC/MCExpr.h b/backend/include/rcc/MC/MCExpr.h new file mode 100644 index 0000000..15ff6fd --- /dev/null +++ b/backend/include/rcc/MC/MCExpr.h @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_MC_MCEXPR_H +#define RCC_MC_MCEXPR_H + +#include "rcc/MC/MCSymbol.h" +#include "rcc/Support/Error.h" + +#include <cstddef> +#include <cstdint> +#include <memory> +#include <optional> +#include <variant> + +namespace rcc::mc { + +class MCExpr; +using MCExprRef = std::shared_ptr<const MCExpr>; + +enum class MCUnaryOperator : std::uint8_t { + Plus, + Minus, + BitwiseNot, + LogicalNot, +}; + +enum class MCBinaryOperator : std::uint8_t { + Add, + Subtract, + Multiply, + Divide, + Modulo, + ShiftLeft, + ShiftRight, + BitwiseAnd, + BitwiseOr, + BitwiseXor, + LogicalAnd, + LogicalOr, + Equal, + NotEqual, + Less, + LessEqual, + Greater, + GreaterEqual, +}; + +struct MCSymbolRefExpr final { + MCSymbolRef Symbol; +}; + +struct MCUnaryExpr final { + MCUnaryOperator Operator = MCUnaryOperator::Plus; + MCExprRef Operand; +}; + +struct MCBinaryExpr final { + MCBinaryOperator Operator = MCBinaryOperator::Add; + MCExprRef Left; + MCExprRef Right; +}; + +struct MCTargetExpr final { + std::uint32_t Kind = 0; + MCExprRef Operand; +}; + +class MCExpr final { +public: + enum class Kind : std::uint8_t { + Constant, + SymbolRef, + Unary, + Binary, + Target, + }; + + [[nodiscard]] static MCExprRef constant(std::int64_t Value); + [[nodiscard]] static Expected<MCExprRef> symbol(MCSymbolRef Symbol); + [[nodiscard]] static Expected<MCExprRef> unary(MCUnaryOperator Operator, MCExprRef Operand); + [[nodiscard]] static Expected<MCExprRef> binary(MCBinaryOperator Operator, MCExprRef Left, + MCExprRef Right); + [[nodiscard]] static Expected<MCExprRef> target(std::uint32_t Kind, MCExprRef Operand); + + [[nodiscard]] Kind kind() const noexcept { + return ExpressionKind; + } + + template <typename ValueT> [[nodiscard]] const ValueT *getIf() const noexcept { + return std::get_if<ValueT>(&Storage); + } + + [[nodiscard]] Expected<void> verify(std::size_t MaximumDepth = 256) const; + [[nodiscard]] std::optional<std::int64_t> evaluateAbsolute() const noexcept; + +private: + using StorageT = + std::variant<std::int64_t, MCSymbolRefExpr, MCUnaryExpr, MCBinaryExpr, MCTargetExpr>; + + MCExpr(Kind ExpressionKind, StorageT Storage) + : ExpressionKind(ExpressionKind), Storage(std::move(Storage)) {} + + Kind ExpressionKind; + StorageT Storage; +}; + +} // namespace rcc::mc + +#endif // RCC_MC_MCEXPR_H diff --git a/backend/include/rcc/MC/MCFixup.h b/backend/include/rcc/MC/MCFixup.h new file mode 100644 index 0000000..3329fab --- /dev/null +++ b/backend/include/rcc/MC/MCFixup.h @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_MC_MCFIXUP_H +#define RCC_MC_MCFIXUP_H + +#include "rcc/MC/MCExpr.h" +#include "rcc/Support/Error.h" + +#include <cstdint> + +namespace rcc::mc { + +using MCFixupKind = std::uint16_t; + +namespace fixup_kind { + +inline constexpr MCFixupKind None = 0; +inline constexpr MCFixupKind Data1 = 1; +inline constexpr MCFixupKind Data2 = 2; +inline constexpr MCFixupKind Data4 = 3; +inline constexpr MCFixupKind Data8 = 4; +inline constexpr MCFixupKind PCRelative4 = 5; +inline constexpr MCFixupKind FirstTarget = 256; + +} // namespace fixup_kind + +struct MCFixup final { + std::uint64_t Offset = 0; + MCExprRef Value; + MCFixupKind Kind = fixup_kind::None; + bool PCRelative = false; + bool LinkerRelaxable = false; + + [[nodiscard]] Expected<void> verify() const; +}; + +struct MCValue final { + MCSymbolRef AddSymbol; + MCSymbolRef SubtractSymbol; + std::int64_t Addend = 0; + std::uint32_t TargetKind = 0; + + [[nodiscard]] bool isAbsolute() const noexcept { + return !AddSymbol && !SubtractSymbol; + } +}; + +[[nodiscard]] Expected<MCValue> evaluateAsRelocatable(const MCExpr &Expression); + +} // namespace rcc::mc + +#endif // RCC_MC_MCFIXUP_H diff --git a/backend/include/rcc/MC/MCInst.h b/backend/include/rcc/MC/MCInst.h new file mode 100644 index 0000000..c6da93b --- /dev/null +++ b/backend/include/rcc/MC/MCInst.h @@ -0,0 +1,174 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_MC_MCINST_H +#define RCC_MC_MCINST_H + +#include "rcc/MC/MCExpr.h" + +#include <cstddef> +#include <cstdint> +#include <span> +#include <utility> +#include <variant> +#include <vector> + +namespace rcc::mc { + +class MCRegister final { +public: + constexpr MCRegister() noexcept = default; + + [[nodiscard]] static constexpr MCRegister fromValue(std::uint32_t Value) noexcept { + return MCRegister(Value); + } + + [[nodiscard]] constexpr std::uint32_t value() const noexcept { + return Value; + } + + [[nodiscard]] constexpr bool isValid() const noexcept { + return Value != 0; + } + + friend constexpr bool operator==(MCRegister Left, MCRegister Right) noexcept = default; + +private: + explicit constexpr MCRegister(std::uint32_t Value) noexcept : Value(Value) {} + + std::uint32_t Value = 0; +}; + +class MCOpcode final { +public: + constexpr MCOpcode() noexcept = default; + + [[nodiscard]] static constexpr MCOpcode fromValue(std::uint32_t Value) noexcept { + return MCOpcode(Value); + } + + [[nodiscard]] constexpr std::uint32_t value() const noexcept { + return Value; + } + + [[nodiscard]] constexpr bool isValid() const noexcept { + return Value != 0; + } + + friend constexpr bool operator==(MCOpcode Left, MCOpcode Right) noexcept = default; + +private: + explicit constexpr MCOpcode(std::uint32_t Value) noexcept : Value(Value) {} + + std::uint32_t Value = 0; +}; + +class MCOperand final { +public: + enum class Kind : std::uint8_t { + Register, + Immediate, + Float32Bits, + Float64Bits, + Expression, + }; + + [[nodiscard]] static MCOperand registerOperand(MCRegister Register) noexcept { + return MCOperand(Kind::Register, Register); + } + + [[nodiscard]] static MCOperand immediate(std::int64_t Value) noexcept { + return MCOperand(Kind::Immediate, Value); + } + + [[nodiscard]] static MCOperand float32Bits(std::uint32_t Value) noexcept { + return MCOperand(Kind::Float32Bits, Value); + } + + [[nodiscard]] static MCOperand float64Bits(std::uint64_t Value) noexcept { + return MCOperand(Kind::Float64Bits, Value); + } + + [[nodiscard]] static MCOperand expression(MCExprRef Value) { + return MCOperand(Kind::Expression, std::move(Value)); + } + + [[nodiscard]] Kind kind() const noexcept { + return OperandKind; + } + + [[nodiscard]] const MCRegister *registerValue() const noexcept { + return std::get_if<MCRegister>(&Storage); + } + + [[nodiscard]] const std::int64_t *immediateValue() const noexcept { + return std::get_if<std::int64_t>(&Storage); + } + + [[nodiscard]] const std::uint32_t *float32BitsValue() const noexcept { + return std::get_if<std::uint32_t>(&Storage); + } + + [[nodiscard]] const std::uint64_t *float64BitsValue() const noexcept { + return std::get_if<std::uint64_t>(&Storage); + } + + [[nodiscard]] const MCExprRef *expressionValue() const noexcept { + return std::get_if<MCExprRef>(&Storage); + } + +private: + using StorageT = std::variant<MCRegister, std::int64_t, std::uint32_t, std::uint64_t, MCExprRef>; + + MCOperand(Kind OperandKind, StorageT Storage) + : OperandKind(OperandKind), Storage(std::move(Storage)) {} + + Kind OperandKind; + StorageT Storage; +}; + +class MCInst final { +public: + explicit MCInst(MCOpcode InstructionOpcode) : InstructionOpcode(InstructionOpcode) {} + + [[nodiscard]] MCOpcode opcode() const noexcept { + return InstructionOpcode; + } + + void setFlags(std::uint32_t NewFlags) noexcept { + Flags = NewFlags; + } + + [[nodiscard]] std::uint32_t flags() const noexcept { + return Flags; + } + + void setSourceIdentity(std::uint64_t Identity) noexcept { + SourceIdentity = Identity; + } + + [[nodiscard]] std::uint64_t sourceIdentity() const noexcept { + return SourceIdentity; + } + + void reserveOperands(std::size_t Count) { + Operands.reserve(Count); + } + + void addOperand(MCOperand Operand) { + Operands.push_back(std::move(Operand)); + } + + [[nodiscard]] std::span<const MCOperand> operands() const noexcept { + return Operands; + } + +private: + MCOpcode InstructionOpcode; + std::uint32_t Flags = 0; + std::uint64_t SourceIdentity = 0; + std::vector<MCOperand> Operands; +}; + +} // namespace rcc::mc + +#endif // RCC_MC_MCINST_H diff --git a/backend/include/rcc/MC/MCInstPrinter.h b/backend/include/rcc/MC/MCInstPrinter.h new file mode 100644 index 0000000..6024ced --- /dev/null +++ b/backend/include/rcc/MC/MCInstPrinter.h @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_MC_MCINSTPRINTER_H +#define RCC_MC_MCINSTPRINTER_H + +#include "rcc/MC/MCInst.h" +#include "rcc/Support/Error.h" + +#include <cstdint> +#include <string> +#include <string_view> + +namespace rcc::mc { + +class MCInstPrinter { +public: + virtual ~MCInstPrinter() = default; + + [[nodiscard]] virtual Expected<std::string> printInstruction(const MCInst &Instruction) const = 0; + + [[nodiscard]] virtual Expected<std::string> + printTargetExpression(std::uint32_t Kind, std::string_view Operand) const = 0; +}; + +[[nodiscard]] Expected<std::string> printMCExpression(const MCExpr &Expression, + const MCInstPrinter &Printer); +[[nodiscard]] Expected<std::string> printMCSymbolName(std::string_view Name); + +} // namespace rcc::mc + +#endif // RCC_MC_MCINSTPRINTER_H diff --git a/backend/include/rcc/MC/MCObjectStreamer.h b/backend/include/rcc/MC/MCObjectStreamer.h new file mode 100644 index 0000000..4a6b56c --- /dev/null +++ b/backend/include/rcc/MC/MCObjectStreamer.h @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_MC_MCOBJECTSTREAMER_H +#define RCC_MC_MCOBJECTSTREAMER_H + +#include "rcc/MC/MCAssembler.h" +#include "rcc/MC/MCObjectWriter.h" +#include "rcc/MC/MCStreamer.h" + +#include <cstdint> +#include <span> +#include <string_view> +#include <vector> + +namespace rcc::mc { + +class ELFObjectStreamer final : public MCStreamer { +public: + ELFObjectStreamer(const MCCodeEmitter &Emitter, const MCAsmBackend &Backend, + const MCObjectWriter &Writer) noexcept + : Assembler(Emitter, Backend), Writer(&Writer) {} + + [[nodiscard]] Expected<void> switchSection(MCSectionRef Section) override; + [[nodiscard]] Expected<void> emitLabel(MCSymbolRef Symbol) override; + [[nodiscard]] Expected<void> emitInstruction(const MCInst &Instruction) override; + [[nodiscard]] Expected<void> emitValue(MCExprRef Value, std::uint8_t Size) override; + [[nodiscard]] Expected<void> emitBytes(std::span<const std::uint8_t> Bytes) override; + [[nodiscard]] Expected<void> emitZeroFill(std::uint64_t Size) override; + [[nodiscard]] Expected<void> emitAlignment(std::uint64_t Alignment, std::uint8_t Fill = 0, + std::uint64_t MaximumBytes = 0) override; + [[nodiscard]] Expected<void> emitSymbolAttribute(MCSymbolRef Symbol, + MCSymbolAttribute Attribute) override; + [[nodiscard]] Expected<void> emitSymbolSize(MCSymbolRef Symbol, MCExprRef Size) override; + [[nodiscard]] Expected<void> emitCommonSymbol(MCSymbolRef Symbol, std::uint64_t Size, + std::uint64_t Alignment) override; + [[nodiscard]] Expected<void> emitDirective(std::string_view Name, + std::string_view Arguments = {}) override; + [[nodiscard]] Expected<void> emitCFI(const MCCFIInstruction &Instruction) override; + [[nodiscard]] Expected<void> emitInlineAssembly(std::string_view Text) override; + [[nodiscard]] Expected<void> emitComment(std::string_view Text) override; + [[nodiscard]] Expected<void> finish() override; + + [[nodiscard]] Expected<void> configureDwarfFrame(MCSectionRef Section, MCDwarfFrameInfo Info); + + [[nodiscard]] Expected<std::vector<std::uint8_t>> takeOutput(); + +private: + [[nodiscard]] Expected<void> ensureOpen() const; + + MCAssembler Assembler; + const MCObjectWriter *Writer; + std::vector<std::uint8_t> Output; + bool IsFinished = false; + bool OutputWasTaken = false; +}; + +} // namespace rcc::mc + +#endif // RCC_MC_MCOBJECTSTREAMER_H diff --git a/backend/include/rcc/MC/MCObjectWriter.h b/backend/include/rcc/MC/MCObjectWriter.h new file mode 100644 index 0000000..16c6a50 --- /dev/null +++ b/backend/include/rcc/MC/MCObjectWriter.h @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_MC_MCOBJECTWRITER_H +#define RCC_MC_MCOBJECTWRITER_H + +#include "rcc/MC/MCSection.h" +#include "rcc/MC/MCSymbol.h" +#include "rcc/Support/Error.h" + +#include <cstddef> +#include <cstdint> +#include <optional> +#include <vector> + +namespace rcc::mc { + +enum class MCObjectSymbolBinding : std::uint8_t { + Local, + Global, + Weak, +}; + +enum class MCObjectSymbolType : std::uint8_t { + None, + Function, + Object, + TLSObject, + Section, +}; + +enum class MCObjectSymbolVisibility : std::uint8_t { + Default, + Hidden, + Protected, +}; + +struct MCObjectRelocation final { + std::uint64_t Offset = 0; + std::uint32_t Type = 0; + MCSymbolRef Symbol; + std::int64_t Addend = 0; +}; + +struct MCObjectSection final { + MCSectionRef Section; + std::vector<std::uint8_t> Contents; + std::uint64_t AddressSize = 0; + std::uint64_t Alignment = 1; + std::vector<MCObjectRelocation> Relocations; +}; + +struct MCObjectSymbol final { + MCSymbolRef Symbol; + MCObjectSymbolBinding Binding = MCObjectSymbolBinding::Local; + MCObjectSymbolType Type = MCObjectSymbolType::None; + MCObjectSymbolVisibility Visibility = MCObjectSymbolVisibility::Default; + std::optional<std::size_t> SectionIndex; + std::uint64_t Value = 0; + std::uint64_t Size = 0; + std::uint64_t CommonAlignment = 0; +}; + +struct MCAssembledObject final { + std::vector<MCObjectSection> Sections; + std::vector<MCObjectSymbol> Symbols; + std::uint32_t Flags = 0; +}; + +class MCObjectWriter { +public: + virtual ~MCObjectWriter() = default; + + [[nodiscard]] virtual Expected<std::vector<std::uint8_t>> + writeObject(const MCAssembledObject &Object) const = 0; +}; + +} // namespace rcc::mc + +#endif // RCC_MC_MCOBJECTWRITER_H diff --git a/backend/include/rcc/MC/MCSection.h b/backend/include/rcc/MC/MCSection.h new file mode 100644 index 0000000..4f76427 --- /dev/null +++ b/backend/include/rcc/MC/MCSection.h @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_MC_MCSECTION_H +#define RCC_MC_MCSECTION_H + +#include "rcc/MC/MCSymbol.h" +#include "rcc/Support/Error.h" + +#include <cstdint> +#include <initializer_list> +#include <memory> +#include <optional> +#include <string> +#include <string_view> + +namespace rcc::mc { + +enum class MCSectionKind : std::uint8_t { + Text, + ReadOnly, + Data, + BSS, + TLSData, + TLSBSS, + EHFrame, + Custom, +}; + +enum class MCSectionFlag : std::uint8_t { + Alloc, + Executable, + Writable, + Merge, + Strings, + TLS, + Group, + Retain, + Count, +}; + +class MCSectionFlags final { +public: + constexpr MCSectionFlags() noexcept = default; + + constexpr MCSectionFlags(std::initializer_list<MCSectionFlag> Flags) noexcept { + for (const MCSectionFlag Flag : Flags) + set(Flag); + } + + constexpr void set(MCSectionFlag Flag) noexcept { + const auto Index = static_cast<std::uint8_t>(Flag); + if (Index < static_cast<std::uint8_t>(MCSectionFlag::Count)) + Mask |= std::uint16_t{1} << Index; + } + + [[nodiscard]] constexpr bool has(MCSectionFlag Flag) const noexcept { + const auto Index = static_cast<std::uint8_t>(Flag); + return Index < static_cast<std::uint8_t>(MCSectionFlag::Count) && + (Mask & (std::uint16_t{1} << Index)) != 0; + } + + [[nodiscard]] constexpr std::uint16_t rawValue() const noexcept { + return Mask; + } + + friend constexpr bool operator==(MCSectionFlags Left, MCSectionFlags Right) noexcept = default; + +private: + std::uint16_t Mask = 0; +}; + +struct MCSectionDesc final { + std::string Name; + MCSectionKind Kind = MCSectionKind::Custom; + MCSectionFlags Flags; + std::uint64_t Alignment = 1; + std::uint64_t EntrySize = 0; + MCSymbolRef Group; + std::optional<std::uint64_t> UniqueID; + + [[nodiscard]] Expected<void> verify() const; + + friend bool operator==(const MCSectionDesc &Left, const MCSectionDesc &Right) = default; +}; + +class MCSection final { +public: + [[nodiscard]] std::string_view name() const noexcept { + return Description.Name; + } + + [[nodiscard]] const MCSectionDesc &description() const noexcept { + return Description; + } + +private: + explicit MCSection(MCSectionDesc Description) : Description(std::move(Description)) {} + + MCSectionDesc Description; + + friend class MCContext; +}; + +using MCSectionRef = std::shared_ptr<const MCSection>; + +} // namespace rcc::mc + +#endif // RCC_MC_MCSECTION_H diff --git a/backend/include/rcc/MC/MCStreamer.h b/backend/include/rcc/MC/MCStreamer.h new file mode 100644 index 0000000..ea37178 --- /dev/null +++ b/backend/include/rcc/MC/MCStreamer.h @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_MC_MCSTREAMER_H +#define RCC_MC_MCSTREAMER_H + +#include "rcc/MC/MCCFI.h" +#include "rcc/MC/MCInst.h" +#include "rcc/MC/MCInstPrinter.h" +#include "rcc/MC/MCSection.h" +#include "rcc/MC/MCSymbol.h" +#include "rcc/Support/Error.h" + +#include <cstdint> +#include <span> +#include <string> +#include <string_view> +#include <unordered_set> + +namespace rcc::mc { + +enum class MCSymbolAttribute : std::uint8_t { + Global, + Weak, + Local, + Hidden, + Protected, + Function, + Object, + TLSObject, +}; + +class MCStreamer { +public: + virtual ~MCStreamer() = default; + + [[nodiscard]] virtual Expected<void> switchSection(MCSectionRef Section) = 0; + [[nodiscard]] virtual Expected<void> emitLabel(MCSymbolRef Symbol) = 0; + [[nodiscard]] virtual Expected<void> emitInstruction(const MCInst &Instruction) = 0; + [[nodiscard]] virtual Expected<void> emitValue(MCExprRef Value, std::uint8_t Size) = 0; + [[nodiscard]] virtual Expected<void> emitBytes(std::span<const std::uint8_t> Bytes) = 0; + [[nodiscard]] virtual Expected<void> emitZeroFill(std::uint64_t Size) = 0; + [[nodiscard]] virtual Expected<void> emitAlignment(std::uint64_t Alignment, std::uint8_t Fill = 0, + std::uint64_t MaximumBytes = 0) = 0; + [[nodiscard]] virtual Expected<void> emitSymbolAttribute(MCSymbolRef Symbol, + MCSymbolAttribute Attribute) = 0; + [[nodiscard]] virtual Expected<void> emitSymbolSize(MCSymbolRef Symbol, MCExprRef Size) = 0; + [[nodiscard]] virtual Expected<void> emitCommonSymbol(MCSymbolRef Symbol, std::uint64_t Size, + std::uint64_t Alignment) = 0; + [[nodiscard]] virtual Expected<void> emitDirective(std::string_view Name, + std::string_view Arguments = {}) = 0; + [[nodiscard]] virtual Expected<void> emitCFI(const MCCFIInstruction &Instruction) = 0; + [[nodiscard]] virtual Expected<void> emitInlineAssembly(std::string_view Text) = 0; + [[nodiscard]] virtual Expected<void> emitComment(std::string_view Text) = 0; + [[nodiscard]] virtual Expected<void> finish() = 0; +}; + +class AsmTextStreamer final : public MCStreamer { +public: + explicit AsmTextStreamer(const MCInstPrinter &Printer) noexcept : Printer(&Printer) {} + + [[nodiscard]] Expected<void> switchSection(MCSectionRef Section) override; + [[nodiscard]] Expected<void> emitLabel(MCSymbolRef Symbol) override; + [[nodiscard]] Expected<void> emitInstruction(const MCInst &Instruction) override; + [[nodiscard]] Expected<void> emitValue(MCExprRef Value, std::uint8_t Size) override; + [[nodiscard]] Expected<void> emitBytes(std::span<const std::uint8_t> Bytes) override; + [[nodiscard]] Expected<void> emitZeroFill(std::uint64_t Size) override; + [[nodiscard]] Expected<void> emitAlignment(std::uint64_t Alignment, std::uint8_t Fill = 0, + std::uint64_t MaximumBytes = 0) override; + [[nodiscard]] Expected<void> emitSymbolAttribute(MCSymbolRef Symbol, + MCSymbolAttribute Attribute) override; + [[nodiscard]] Expected<void> emitSymbolSize(MCSymbolRef Symbol, MCExprRef Size) override; + [[nodiscard]] Expected<void> emitCommonSymbol(MCSymbolRef Symbol, std::uint64_t Size, + std::uint64_t Alignment) override; + [[nodiscard]] Expected<void> emitDirective(std::string_view Name, + std::string_view Arguments = {}) override; + [[nodiscard]] Expected<void> emitCFI(const MCCFIInstruction &Instruction) override; + [[nodiscard]] Expected<void> emitInlineAssembly(std::string_view Text) override; + [[nodiscard]] Expected<void> emitComment(std::string_view Text) override; + [[nodiscard]] Expected<void> finish() override; + + [[nodiscard]] Expected<std::string> takeOutput(); + +private: + [[nodiscard]] Expected<void> appendLine(std::string_view Prefix, std::string_view Text); + [[nodiscard]] Expected<void> requireActiveSection() const; + + const MCInstPrinter *Printer; + MCSectionRef CurrentSection; + std::unordered_set<std::string> DefinedSymbols; + std::string Output; + bool IsFinished = false; + bool OutputWasTaken = false; +}; + +} // namespace rcc::mc + +#endif // RCC_MC_MCSTREAMER_H diff --git a/backend/include/rcc/MC/MCSymbol.h b/backend/include/rcc/MC/MCSymbol.h new file mode 100644 index 0000000..1b7b878 --- /dev/null +++ b/backend/include/rcc/MC/MCSymbol.h @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_MC_MCSYMBOL_H +#define RCC_MC_MCSYMBOL_H + +#include <cstdint> +#include <memory> +#include <string> +#include <string_view> + +namespace rcc::mc { + +class MCContext; + +enum class MCSymbolKind : std::uint8_t { + Generic, + Function, + Object, + BasicBlock, + ConstantPool, + JumpTable, + LiteralIsland, + Section, + Temporary, +}; + +class MCSymbol final { +public: + [[nodiscard]] std::string_view name() const noexcept { + return Name; + } + + [[nodiscard]] MCSymbolKind kind() const noexcept { + return Kind; + } + + [[nodiscard]] bool isTemporary() const noexcept { + return Kind == MCSymbolKind::Temporary || Kind == MCSymbolKind::BasicBlock || + Kind == MCSymbolKind::ConstantPool || Kind == MCSymbolKind::JumpTable || + Kind == MCSymbolKind::LiteralIsland; + } + +private: + MCSymbol(std::string Name, MCSymbolKind Kind) : Name(std::move(Name)), Kind(Kind) {} + + std::string Name; + MCSymbolKind Kind; + + friend class MCContext; +}; + +using MCSymbolRef = std::shared_ptr<const MCSymbol>; + +} // namespace rcc::mc + +#endif // RCC_MC_MCSYMBOL_H diff --git a/backend/include/rcc/MC/TargetRegistry.h b/backend/include/rcc/MC/TargetRegistry.h new file mode 100644 index 0000000..3e1de7c --- /dev/null +++ b/backend/include/rcc/MC/TargetRegistry.h @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_MC_TARGETREGISTRY_H +#define RCC_MC_TARGETREGISTRY_H + +#include "rcc/Support/Error.h" + +#include <memory> +#include <span> +#include <string> +#include <string_view> + +namespace rcc { + +class TargetMachine; +struct TargetMachineOptions; + +class Target final { +public: + using MatchQualityFn = unsigned (*)(std::string_view Triple); + using NormalizeTripleFn = std::string (*)(std::string_view Triple); + using DefaultOptionsFn = TargetMachineOptions (*)(); + using DefaultABIFn = std::string (*)(std::span<const std::string> Features); + using TargetMachineCtorFn = Expected<std::unique_ptr<TargetMachine>> (*)( + const Target &RegisteredTarget, TargetMachineOptions Options); + + constexpr Target(std::string_view Name, std::string_view Description, bool IsDefault, + MatchQualityFn MatchQuality, NormalizeTripleFn NormalizeTriple, + DefaultOptionsFn DefaultOptions, DefaultABIFn DefaultABI, + std::span<const std::string_view> BaselineFeatures, + TargetMachineCtorFn TargetMachineCtor) noexcept + : Name(Name), Description(Description), IsDefault(IsDefault), MatchQuality(MatchQuality), + NormalizeTriple(NormalizeTriple), DefaultOptions(DefaultOptions), DefaultABI(DefaultABI), + BaselineFeatures(BaselineFeatures), TargetMachineCtor(TargetMachineCtor) {} + + [[nodiscard]] std::string_view name() const noexcept { + return Name; + } + + [[nodiscard]] std::string_view description() const noexcept { + return Description; + } + + [[nodiscard]] bool isDefault() const noexcept { + return IsDefault; + } + + [[nodiscard]] unsigned matchQuality(std::string_view Triple) const noexcept { + return MatchQuality(Triple); + } + + [[nodiscard]] std::string normalizeTriple(std::string_view Triple) const { + return NormalizeTriple(Triple); + } + + [[nodiscard]] TargetMachineOptions defaultMachineOptions() const; + [[nodiscard]] std::string defaultABI(std::span<const std::string> Features) const { + return DefaultABI(Features); + } + + [[nodiscard]] std::span<const std::string_view> baselineFeatures() const noexcept { + return BaselineFeatures; + } + + [[nodiscard]] Expected<std::unique_ptr<TargetMachine>> + createTargetMachine(TargetMachineOptions Options) const; + +private: + std::string_view Name; + std::string_view Description; + bool IsDefault = false; + MatchQualityFn MatchQuality = nullptr; + NormalizeTripleFn NormalizeTriple = nullptr; + DefaultOptionsFn DefaultOptions = nullptr; + DefaultABIFn DefaultABI = nullptr; + std::span<const std::string_view> BaselineFeatures; + TargetMachineCtorFn TargetMachineCtor = nullptr; +}; + +class TargetRegistry final { +public: + static void registerTarget(const Target &RegisteredTarget); + [[nodiscard]] static Expected<const Target *> lookupTarget(std::string_view Triple); + [[nodiscard]] static Expected<const Target *> defaultTarget(); +}; + +} // namespace rcc + +#endif // RCC_MC_TARGETREGISTRY_H diff --git a/backend/include/rcc/MIR/EmbeddedIR.h b/backend/include/rcc/MIR/EmbeddedIR.h new file mode 100644 index 0000000..17b19cb --- /dev/null +++ b/backend/include/rcc/MIR/EmbeddedIR.h @@ -0,0 +1,248 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_MIR_EMBEDDEDIR_H +#define RCC_MIR_EMBEDDEDIR_H + +#include "rcc/CodeGen/GlobalObject.h" +#include "rcc/Support/TextDiagnostic.h" + +#include <cstddef> +#include <cstdint> +#include <functional> +#include <optional> +#include <span> +#include <string> +#include <string_view> +#include <unordered_map> +#include <utility> +#include <vector> + +namespace rcc::mir { + +class EmbeddedIRIndexer; + +struct EmbeddedIRReference final { + std::string Name; + TextRange Range; +}; + +struct EmbeddedIRMetadataDefinition final { + std::string Name; + TextRange Range; + std::string DynamicType; +}; + +struct EmbeddedIRTypeDefinition final { + std::string Name; + TextRange Range; + std::string Definition; + TextRange DefinitionRange; + bool NumericName = false; +}; + +class EmbeddedIRFunction final { +public: + [[nodiscard]] std::string_view name() const noexcept { + return Name; + } + + [[nodiscard]] const TextRange &nameRange() const noexcept { + return NameRange; + } + + [[nodiscard]] bool isDefinition() const noexcept { + return IsDefinition; + } + + [[nodiscard]] bool isUnnamed() const noexcept { + return IsUnnamed; + } + + [[nodiscard]] bool hasLinkOnceODRLinkage() const noexcept { + return HasLinkOnceODRLinkage; + } + + [[nodiscard]] bool hasSection() const noexcept { + return HasSection; + } + + [[nodiscard]] bool hasComdat() const noexcept { + return HasComdat; + } + + [[nodiscard]] bool hasSectionPrefix() const noexcept { + return HasSectionPrefix; + } + + [[nodiscard]] bool hasMinSize() const noexcept { + return HasMinSize; + } + + [[nodiscard]] std::span<const EmbeddedIRReference> blocks() const noexcept { + return Blocks; + } + + [[nodiscard]] std::span<const EmbeddedIRReference> allocas() const noexcept { + return Allocas; + } + + [[nodiscard]] std::span<const EmbeddedIRReference> values() const noexcept { + return Values; + } + + [[nodiscard]] const EmbeddedIRReference *block(std::string_view QueryName) const noexcept; + [[nodiscard]] const EmbeddedIRReference *alloca(std::string_view QueryName) const noexcept; + [[nodiscard]] const EmbeddedIRReference *value(std::string_view QueryName) const noexcept; + +private: + struct StringHash final { + using is_transparent = void; // NOLINT(readability-identifier-naming) + + [[nodiscard]] std::size_t operator()(std::string_view Value) const noexcept { + return std::hash<std::string_view>{}(Value); + } + }; + + std::string Name; + TextRange NameRange; + bool IsDefinition = false; + bool IsUnnamed = false; + bool HasLinkOnceODRLinkage = false; + bool HasSection = false; + bool HasComdat = false; + bool HasSectionPrefix = false; + bool HasMinSize = false; + std::optional<std::string> AttributeGroup; + std::uint64_t NextUnnamedSlot = 0; + std::vector<EmbeddedIRReference> Blocks; + std::vector<EmbeddedIRReference> Allocas; + std::vector<EmbeddedIRReference> Values; + std::vector<EmbeddedIRReference> LocalReferences; + std::unordered_map<std::string, std::size_t, StringHash, std::equal_to<>> BlockNameToIndex; + std::unordered_map<std::string, std::size_t, StringHash, std::equal_to<>> AllocaNameToIndex; + std::unordered_map<std::string, std::size_t, StringHash, std::equal_to<>> ValueNameToIndex; + + friend class EmbeddedIRIndexer; +}; + +class EmbeddedIRReferenceIndex final { +public: + EmbeddedIRReferenceIndex(const EmbeddedIRReferenceIndex &) = delete; + EmbeddedIRReferenceIndex &operator=(const EmbeddedIRReferenceIndex &) = delete; + EmbeddedIRReferenceIndex(EmbeddedIRReferenceIndex &&) noexcept = default; + EmbeddedIRReferenceIndex &operator=(EmbeddedIRReferenceIndex &&) noexcept = default; + ~EmbeddedIRReferenceIndex() = default; + + [[nodiscard]] const TextSource &source() const noexcept { + return Source; + } + + [[nodiscard]] std::optional<std::string_view> targetTriple() const noexcept { + if (!TargetTriple) + return std::nullopt; + return *TargetTriple; + } + + [[nodiscard]] std::optional<std::string_view> dataLayout() const noexcept { + if (!DataLayout) + return std::nullopt; + return *DataLayout; + } + + [[nodiscard]] std::span<const EmbeddedIRFunction> functions() const noexcept { + return Functions; + } + + [[nodiscard]] std::span<const EmbeddedIRReference> globalObjects() const noexcept { + return GlobalObjects; + } + + [[nodiscard]] std::span<const EmbeddedIRMetadataDefinition> metadataDefinitions() const noexcept { + return MetadataDefinitions; + } + + [[nodiscard]] std::span<const EmbeddedIRTypeDefinition> typeDefinitions() const noexcept { + return TypeDefinitions; + } + + [[nodiscard]] const EmbeddedIRFunction *function(std::string_view Name) const noexcept; + [[nodiscard]] const EmbeddedIRFunction *unnamedFunction(std::size_t Ordinal) const noexcept; + [[nodiscard]] const EmbeddedIRReference *globalObject(std::string_view Name) const noexcept; + [[nodiscard]] std::optional<GlobalValueProperties> + globalObjectProperties(std::string_view Name) const noexcept; + [[nodiscard]] const EmbeddedIRMetadataDefinition *metadata(std::string_view Name) const noexcept; + [[nodiscard]] const EmbeddedIRTypeDefinition * + typeDefinition(std::string_view Name) const noexcept; + [[nodiscard]] bool hasGlobalValue(std::string_view Name) const noexcept { + return function(Name) != nullptr || globalObject(Name) != nullptr; + } + +private: + struct StringHash final { + using is_transparent = void; // NOLINT(readability-identifier-naming) + + [[nodiscard]] std::size_t operator()(std::string_view Value) const noexcept { + return std::hash<std::string_view>{}(Value); + } + }; + + EmbeddedIRReferenceIndex(std::string SourceName, std::string SourceText) + : Source(std::move(SourceName), std::move(SourceText)) {} + + TextSource Source; + std::optional<std::string> TargetTriple; + std::optional<TextRange> TargetTripleRange; + std::optional<std::string> DataLayout; + std::optional<TextRange> DataLayoutRange; + std::vector<EmbeddedIRFunction> Functions; + std::vector<EmbeddedIRReference> GlobalObjects; + std::unordered_map<std::string, GlobalValueProperties, StringHash, std::equal_to<>> + GlobalProperties; + std::vector<EmbeddedIRMetadataDefinition> MetadataDefinitions; + std::vector<EmbeddedIRTypeDefinition> TypeDefinitions; + std::unordered_map<std::string, std::size_t, StringHash, std::equal_to<>> FunctionNameToIndex; + std::unordered_map<std::string, std::size_t, StringHash, std::equal_to<>> GlobalNameToIndex; + std::unordered_map<std::string, std::size_t, StringHash, std::equal_to<>> MetadataNameToIndex; + std::unordered_map<std::string, std::size_t, StringHash, std::equal_to<>> TypeNameToIndex; + + friend class EmbeddedIRIndexer; +}; + +class EmbeddedIRIndexResult final { +public: + EmbeddedIRIndexResult(const EmbeddedIRIndexResult &) = delete; + EmbeddedIRIndexResult &operator=(const EmbeddedIRIndexResult &) = delete; + EmbeddedIRIndexResult(EmbeddedIRIndexResult &&) noexcept = default; + EmbeddedIRIndexResult &operator=(EmbeddedIRIndexResult &&) noexcept = default; + ~EmbeddedIRIndexResult() = default; + + [[nodiscard]] bool succeeded() const noexcept { + return Index.has_value() && Diagnostics.empty(); + } + + [[nodiscard]] const EmbeddedIRReferenceIndex *index() const noexcept { + return Index.has_value() ? Index.operator->() : nullptr; + } + + [[nodiscard]] std::span<const TextDiagnostic> diagnostics() const noexcept { + return Diagnostics; + } + + [[nodiscard]] std::optional<EmbeddedIRReferenceIndex> takeIndex() && { + return std::move(Index); + } + +private: + EmbeddedIRIndexResult() = default; + + std::optional<EmbeddedIRReferenceIndex> Index; + std::vector<TextDiagnostic> Diagnostics; + + friend class EmbeddedIRIndexer; +}; + +[[nodiscard]] EmbeddedIRIndexResult indexEmbeddedIR(std::string SourceName, std::string SourceText); + +} // namespace rcc::mir + +#endif // RCC_MIR_EMBEDDEDIR_H diff --git a/backend/include/rcc/MIR/ReachingDefPrinter.h b/backend/include/rcc/MIR/ReachingDefPrinter.h new file mode 100644 index 0000000..8b4d1e0 --- /dev/null +++ b/backend/include/rcc/MIR/ReachingDefPrinter.h @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_MIR_REACHINGDEFPRINTER_H +#define RCC_MIR_REACHINGDEFPRINTER_H + +#include "rcc/CodeGen/MachinePassManager.h" + +#include <string_view> + +namespace rcc::mir { + +class ReachingDefPrinterPass final : public MachineFunctionPass { +public: + [[nodiscard]] static constexpr std::string_view name() noexcept { + return "print<reaching-def>"; + } + + [[nodiscard]] const MachinePassContract &contract() const noexcept override; + [[nodiscard]] Expected<MachinePassResult> run(MachineFunction &Function, + const TargetDescription &Target, + MachineFunctionAnalysisManager &Analyses) override; +}; + +} // namespace rcc::mir + +#endif // RCC_MIR_REACHINGDEFPRINTER_H diff --git a/backend/include/rcc/Support/Error.h b/backend/include/rcc/Support/Error.h new file mode 100644 index 0000000..b631635 --- /dev/null +++ b/backend/include/rcc/Support/Error.h @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_SUPPORT_ERROR_H +#define RCC_SUPPORT_ERROR_H + +#include <cstdint> +#include <expected> +#include <string> +#include <string_view> +#include <utility> + +namespace rcc { + +enum class ErrorCode : std::uint8_t { + InvalidArgument, + ParseError, + VerificationError, + Unsupported, + RegisterAllocationFailed, + IoError, + InternalError, + FrameLayoutFailed, + SchedulingFailed, + BranchRelaxationFailed, + PseudoExpansionFailed, + FinalEncodabilityFailed, + CFGOptimizationFailed, + AssemblyLoweringFailed, + InstructionEncodingFailed, + FixupApplicationFailed, + RelocationFailed, + ObjectWritingFailed, + InvalidInvocation, + ManifestError, + CacheMiss, + ReproducerError, + ReductionFailed, + FuzzingFailure, +}; + +[[nodiscard]] std::string_view getErrorCodeName(ErrorCode Code) noexcept; + +class Error final { +public: + Error(ErrorCode Code, std::string Message); + + [[nodiscard]] ErrorCode code() const noexcept { + return Code; + } + [[nodiscard]] std::string_view message() const noexcept { + return Message; + } + +private: + ErrorCode Code; + std::string Message; +}; + +[[nodiscard]] Error withErrorContext(const Error &Failure, std::string Context); + +template <typename ValueT> using Expected = std::expected<ValueT, Error>; + +[[nodiscard]] inline std::unexpected<Error> makeUnexpected(ErrorCode Code, std::string Message) { + return std::unexpected<Error>(Error(Code, std::move(Message))); +} + +} // namespace rcc + +#endif // RCC_SUPPORT_ERROR_H diff --git a/backend/include/rcc/Support/SHA256.h b/backend/include/rcc/Support/SHA256.h new file mode 100644 index 0000000..ff7b50b --- /dev/null +++ b/backend/include/rcc/Support/SHA256.h @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_SUPPORT_SHA256_H +#define RCC_SUPPORT_SHA256_H + +#include <array> +#include <cstddef> +#include <cstdint> +#include <span> +#include <string> +#include <string_view> + +namespace rcc { + +using SHA256Digest = std::array<std::uint8_t, 32>; + +[[nodiscard]] SHA256Digest sha256(std::span<const std::byte> Input); +[[nodiscard]] SHA256Digest sha256(std::string_view Input); +[[nodiscard]] std::string sha256Hex(std::span<const std::byte> Input); +[[nodiscard]] std::string sha256Hex(std::string_view Input); + +} // namespace rcc + +#endif // RCC_SUPPORT_SHA256_H diff --git a/backend/include/rcc/Support/TargetSelect.h b/backend/include/rcc/Support/TargetSelect.h new file mode 100644 index 0000000..f56090b --- /dev/null +++ b/backend/include/rcc/Support/TargetSelect.h @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_SUPPORT_TARGETSELECT_H +#define RCC_SUPPORT_TARGETSELECT_H + +namespace rcc { + +#define RCC_TARGET(TargetName) void initialize##TargetName##Target(); +#include "rcc/Config/Targets.def" + +inline void initializeAllTargets() { +#define RCC_TARGET(TargetName) initialize##TargetName##Target(); +#include "rcc/Config/Targets.def" +} + +} // namespace rcc + +#endif // RCC_SUPPORT_TARGETSELECT_H diff --git a/backend/include/rcc/Support/TextDiagnostic.h b/backend/include/rcc/Support/TextDiagnostic.h new file mode 100644 index 0000000..4c21486 --- /dev/null +++ b/backend/include/rcc/Support/TextDiagnostic.h @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_SUPPORT_TEXTDIAGNOSTIC_H +#define RCC_SUPPORT_TEXTDIAGNOSTIC_H + +#include <cstddef> +#include <span> +#include <string> +#include <string_view> +#include <vector> + +namespace rcc { + +struct TextPosition final { + std::size_t Offset = 0; + std::size_t Line = 1; + std::size_t Column = 1; + + friend constexpr bool operator==(TextPosition Left, TextPosition Right) noexcept = default; +}; + +struct TextRange final { + TextPosition Begin; + TextPosition End; + + [[nodiscard]] constexpr bool isOrdered() const noexcept { + return Begin.Offset <= End.Offset; + } + + friend constexpr bool operator==(TextRange Left, TextRange Right) noexcept = default; +}; + +enum class DiagnosticSeverity : unsigned char { + Note, + Warning, + Error, +}; + +[[nodiscard]] std::string_view getDiagnosticSeverityName(DiagnosticSeverity Severity) noexcept; + +struct TextDiagnostic final { + DiagnosticSeverity Severity = DiagnosticSeverity::Error; + std::string Category; + std::string Message; + std::string SourceName; + TextRange Range; +}; + +class TextSource final { +public: + TextSource(std::string Name, std::string Text); + + [[nodiscard]] std::string_view name() const noexcept { + return Name; + } + + [[nodiscard]] std::string_view text() const noexcept { + return Text; + } + + [[nodiscard]] std::size_t lineCount() const noexcept { + return LineOffsets.size(); + } + + [[nodiscard]] TextPosition positionAt(std::size_t Offset) const noexcept; + [[nodiscard]] TextRange range(std::size_t Begin, std::size_t End) const noexcept; + [[nodiscard]] std::string_view line(std::size_t OneBasedLine) const noexcept; + [[nodiscard]] std::span<const std::size_t> lineOffsets() const noexcept { + return LineOffsets; + } + +private: + std::string Name; + std::string Text; + std::vector<std::size_t> LineOffsets; +}; + +[[nodiscard]] std::string formatTextDiagnostic(const TextDiagnostic &Diagnostic, + const TextSource &Source); + +} // namespace rcc + +#endif // RCC_SUPPORT_TEXTDIAGNOSTIC_H diff --git a/backend/include/rcc/Support/YAML.h b/backend/include/rcc/Support/YAML.h new file mode 100644 index 0000000..2e403a9 --- /dev/null +++ b/backend/include/rcc/Support/YAML.h @@ -0,0 +1,197 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_SUPPORT_YAML_H +#define RCC_SUPPORT_YAML_H + +#include "rcc/Support/TextDiagnostic.h" + +#include <cstddef> +#include <cstdint> +#include <limits> +#include <optional> +#include <span> +#include <string> +#include <string_view> +#include <utility> +#include <vector> + +namespace rcc::yaml { + +class Parser; + +class NodeID final { +public: + constexpr NodeID() noexcept = default; + + [[nodiscard]] constexpr bool isValid() const noexcept { + return Value != InvalidValue; + } + + [[nodiscard]] constexpr std::uint32_t rawValue() const noexcept { + return Value; + } + + friend constexpr bool operator==(NodeID Left, NodeID Right) noexcept = default; + +private: + static constexpr std::uint32_t InvalidValue = std::numeric_limits<std::uint32_t>::max(); + + explicit constexpr NodeID(std::uint32_t Value) noexcept : Value(Value) {} + + std::uint32_t Value = InvalidValue; + + friend class Parser; + friend class Stream; +}; + +enum class NodeKind : std::uint8_t { + Null, + Scalar, + Sequence, + Mapping, +}; + +enum class ScalarStyle : std::uint8_t { + Plain, + SingleQuoted, + DoubleQuoted, + Literal, + Folded, +}; + +enum class CollectionStyle : std::uint8_t { + Block, + Flow, +}; + +struct MappingEntry final { + std::string Key; + TextRange KeyRange; + NodeID Value; +}; + +class Node final { +public: + [[nodiscard]] NodeKind kind() const noexcept { + return Kind; + } + + [[nodiscard]] const TextRange &range() const noexcept { + return Range; + } + + [[nodiscard]] ScalarStyle scalarStyle() const noexcept { + return ScalarStyleValue; + } + + [[nodiscard]] CollectionStyle collectionStyle() const noexcept { + return CollectionStyleValue; + } + + [[nodiscard]] std::string_view scalar() const noexcept { + return Scalar; + } + + [[nodiscard]] std::span<const NodeID> sequence() const noexcept { + return Sequence; + } + + [[nodiscard]] std::span<const MappingEntry> mapping() const noexcept { + return Mapping; + } + + [[nodiscard]] std::optional<NodeID> mappingValue(std::string_view Key) const noexcept; + +private: + explicit Node(NodeKind Kind, TextRange Range) : Kind(Kind), Range(Range) {} + + NodeKind Kind; + TextRange Range; + ScalarStyle ScalarStyleValue = ScalarStyle::Plain; + CollectionStyle CollectionStyleValue = CollectionStyle::Block; + std::string Scalar; + std::vector<NodeID> Sequence; + std::vector<MappingEntry> Mapping; + + friend class Parser; +}; + +struct Document final { + NodeID Root; + TextRange Range; + bool HasExplicitStart = false; + bool HasExplicitEnd = false; +}; + +class Stream final { +public: + Stream(const Stream &) = delete; + Stream &operator=(const Stream &) = delete; + Stream(Stream &&) noexcept = default; + Stream &operator=(Stream &&) noexcept = default; + ~Stream() = default; + + [[nodiscard]] const TextSource &source() const noexcept { + return Source; + } + + [[nodiscard]] std::span<const Document> documents() const noexcept { + return Documents; + } + + [[nodiscard]] const Node *node(NodeID ID) const noexcept; + [[nodiscard]] std::size_t nodeCount() const noexcept { + return Nodes.size(); + } + +private: + Stream(std::string SourceName, std::string SourceText) + : Source(std::move(SourceName), std::move(SourceText)) {} + + TextSource Source; + std::vector<Node> Nodes; + std::vector<Document> Documents; + + friend class Parser; +}; + +struct ParseOptions final { + std::size_t MaximumDepth = 128; + std::size_t MaximumNodes = 1U << 20U; +}; + +class ParseResult final { +public: + ParseResult(const ParseResult &) = delete; + ParseResult &operator=(const ParseResult &) = delete; + ParseResult(ParseResult &&) noexcept = default; + ParseResult &operator=(ParseResult &&) noexcept = default; + ~ParseResult() = default; + + [[nodiscard]] bool succeeded() const noexcept { + return ParsedStream.has_value() && Diagnostics.empty(); + } + + [[nodiscard]] const Stream *stream() const noexcept { + return ParsedStream.has_value() ? ParsedStream.operator->() : nullptr; + } + + [[nodiscard]] std::span<const TextDiagnostic> diagnostics() const noexcept { + return Diagnostics; + } + +private: + ParseResult() = default; + + std::optional<Stream> ParsedStream; + std::vector<TextDiagnostic> Diagnostics; + + friend class Parser; +}; + +[[nodiscard]] ParseResult parse(std::string SourceName, std::string SourceText, + ParseOptions Options = {}); + +} // namespace rcc::yaml + +#endif // RCC_SUPPORT_YAML_H diff --git a/backend/include/rcc/TableGen/Parser.h b/backend/include/rcc/TableGen/Parser.h new file mode 100644 index 0000000..ff10aa7 --- /dev/null +++ b/backend/include/rcc/TableGen/Parser.h @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_TABLEGEN_PARSER_H +#define RCC_TABLEGEN_PARSER_H + +#include "rcc/TableGen/Record.h" + +#include <optional> +#include <string> +#include <vector> + +namespace rcc::targetgen { + +struct DescriptionParseResult final { + std::optional<DescriptionAST> Records; + std::vector<TextDiagnostic> Diagnostics; +}; + +[[nodiscard]] DescriptionParseResult parseDescription(std::string SourceName, + std::string SourceText); + +} // namespace rcc::targetgen + +#endif // RCC_TABLEGEN_PARSER_H diff --git a/backend/include/rcc/TableGen/Record.h b/backend/include/rcc/TableGen/Record.h new file mode 100644 index 0000000..525d63f --- /dev/null +++ b/backend/include/rcc/TableGen/Record.h @@ -0,0 +1,470 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_TABLEGEN_RECORD_H +#define RCC_TABLEGEN_RECORD_H + +#include "rcc/Support/TextDiagnostic.h" + +#include <cstdint> +#include <optional> +#include <ostream> +#include <string> +#include <string_view> +#include <vector> + +namespace rcc::targetgen { + +struct SourceTarget final { + std::string Namespace; + std::string Name; + std::string Triple; + std::string CPU; + std::string ABI; + std::string DataLayout; + std::vector<std::string> Features; + TextRange Range; +}; + +struct SourceRegisterUnit final { + std::uint32_t ID = 0; + std::string Symbol; + std::string Name; + TextRange Range; +}; + +struct SourceRegisterUnitLane final { + std::string UnitSymbol; + std::uint64_t Lanes = 0; + TextRange Range; +}; + +struct SourceRegister final { + std::uint32_t ID = 0; + std::string Symbol; + std::string Name; + std::uint32_t Encoding = 0; + std::uint32_t DwarfRegister = 0; + std::vector<SourceRegisterUnitLane> Units; + bool IsConstant = false; + bool IsArtificial = false; + bool IsReserved = false; + std::vector<std::string> AlternativeNames; + TextRange Range; +}; + +struct SourceRegisterBank final { + std::uint32_t ID = 0; + std::string Symbol; + std::string Name; + std::uint32_t MaximumSizeInBits = 0; + TextRange Range; +}; + +struct SourceRegisterClass final { + std::uint32_t ID = 0; + std::string Symbol; + std::string Name; + std::string BankSymbol; + std::uint32_t SizeInBits = 0; + std::uint32_t SpillSizeInBits = 0; + std::uint32_t SpillAlignmentInBits = 0; + std::uint64_t Lanes = 0; + std::vector<std::string> RegisterSymbols; + std::vector<std::string> AllocationOrderSymbols; + std::vector<std::string> SuperClassSymbols; + bool Allocatable = true; + std::uint32_t CopyCost = 1; + TextRange Range; +}; + +struct SourceRegisterMask final { + std::string Name; + std::vector<std::string> PreservedRegisterSymbols; + TextRange Range; +}; + +struct SourceMIROperandFlag final { + std::string Name; + std::uint32_t Value = 0; + TextRange Range; +}; + +struct SourceMIROperandFlags final { + std::uint32_t DirectMask = 0; + std::vector<SourceMIROperandFlag> Direct; + std::vector<SourceMIROperandFlag> Bitmask; + TextRange Range; +}; + +struct SourceCallingConvention final { + std::uint32_t ID = 0; + std::string Symbol; + std::string Name; + std::string IntegerRegisterClassSymbol; + std::string FloatingRegisterClassSymbol; + std::vector<std::string> IntegerArgumentRegisterSymbols; + std::vector<std::string> FloatingArgumentRegisterSymbols; + std::vector<std::string> IntegerReturnRegisterSymbols; + std::vector<std::string> FloatingReturnRegisterSymbols; + std::string PreservedMaskName; + std::uint32_t StackAlignmentBytes = 0; + std::uint32_t StackArgumentAlignmentBytes = 0; + std::uint32_t ShadowSpaceBytes = 0; + bool SupportsVarArgs = false; + bool SupportsTailCalls = false; + TextRange Range; +}; + +struct SourceNamedOperand final { + std::uint32_t ID = 0; + std::string Symbol; + std::string Name; + TextRange Range; +}; + +struct SourceImmediatePredicate final { + std::uint32_t ID = 0; + std::string Symbol; + std::string Name; + std::string Kind; + std::uint32_t Bits = 0; + std::uint64_t Alignment = 1; + bool NonZero = false; + std::vector<std::int64_t> AllowedValues; + TextRange Range; +}; + +struct SourceSchedulingClass final { + std::uint32_t ID = 0; + std::string Symbol; + std::string Name; + std::uint32_t MicroOps = 0; + std::uint32_t Latency = 0; + TextRange Range; +}; + +struct SourceMacroFusionOpcodePair final { + std::string FirstOpcodeSymbol; + std::string SecondOpcodeSymbol; + TextRange Range; +}; + +struct SourceMacroFusionImmediatePredicate final { + std::string Instruction; + std::uint32_t OperandIndex = 0; + std::int64_t Minimum = 0; + std::int64_t Maximum = 0; + TextRange Range; +}; + +struct SourceMacroFusionRegisterInequality final { + std::string Instruction; + std::uint32_t LeftOperand = 0; + std::uint32_t RightOperand = 0; + TextRange Range; +}; + +struct SourceMacroFusionImmediateRelation final { + std::string Kind; + std::uint32_t FirstOperand = 0; + std::uint32_t SecondOperand = 0; + TextRange Range; +}; + +struct SourceMacroFusionRule final { + std::string Symbol; + std::string Feature; + std::vector<std::string> FirstOpcodeSymbols; + std::vector<std::string> SecondOpcodeSymbols; + std::vector<SourceMacroFusionOpcodePair> OpcodePairs; + std::vector<SourceMacroFusionImmediatePredicate> ImmediatePredicates; + std::vector<SourceMacroFusionRegisterInequality> RegisterInequalities; + std::vector<SourceMacroFusionImmediateRelation> ImmediateRelations; + std::uint32_t FirstDefinitionOperand = 0; + std::uint32_t SecondUseOperand = 0; + std::uint32_t SecondDefinitionOperand = 0; + std::optional<std::uint32_t> CommutableSecondUseOperand; + bool RequireSingleNonDebugUse = false; + bool RequireSecondDestinationTie = false; + TextRange Range; +}; + +struct SourceMCPseudoOperand final { + std::optional<std::uint32_t> SourceOperandIndex; + std::string RegisterSymbol; + std::optional<std::int64_t> Immediate; + TextRange Range; +}; + +struct SourceMCPseudoExpansion final { + std::string PseudoOpcodeSymbol; + std::string ResultOpcodeSymbol; + std::vector<SourceMCPseudoOperand> Operands; + TextRange Range; +}; + +struct SourceEncodingCategory final { + std::uint32_t ID = 0; + std::string Symbol; + std::string Name; + std::uint32_t SizeInBytes = 0; + TextRange Range; +}; + +struct SourceAssemblyFormat final { + std::uint32_t ID = 0; + std::string Symbol; + std::string Name; + TextRange Range; +}; + +struct SourceOperand final { + std::vector<std::string> Kinds; + std::string RegisterRole = "none"; + std::optional<std::string> RegisterClassSymbol; + std::optional<std::string> RegisterBankSymbol; + std::optional<std::string> ImmediatePredicateSymbol; + std::string MemoryRole = "none"; + std::optional<std::uint32_t> DefaultTiedOperand; + bool EarlyClobber = false; + bool Optional = false; + bool VariadicGroup = false; + bool AllowSubReg = false; + std::optional<std::string> NameSymbol; + std::optional<std::uint16_t> TypeIndex; + std::uint32_t AllowedTargetFlags = 0; + TextRange Range; +}; + +struct SourceImplicitRegister final { + std::string RegisterSymbol; + bool EarlyClobber = false; + bool Renamable = false; + TextRange Range; +}; + +struct SourceInstruction final { + std::uint32_t ID = 0; + std::string Symbol; + std::string Name; + std::string AssemblyMnemonic; + std::optional<std::uint32_t> BinaryEncoding; + std::vector<SourceOperand> Operands; + std::uint32_t NumExplicitDefs = 0; + bool Variadic = false; + std::vector<SourceImplicitRegister> ImplicitDefs; + std::vector<SourceImplicitRegister> ImplicitUses; + bool AllowExtraImplicitOperands = false; + std::vector<std::string> Flags; + std::string SchedulingClassSymbol; + std::string EncodingCategorySymbol; + std::uint32_t SizeInBytes = 0; + std::string AssemblyFormatSymbol; + bool RequiresMemoryOperands = false; + std::string Owner = "none"; + std::string Deadline = "emission"; + TextRange Range; +}; + +struct SourceSelectionTypePredicate final { + std::string Kind; + std::uint32_t Bits = 0; + TextRange Range; +}; + +struct SourceSelectionOperandTypePredicate final { + std::uint32_t OperandIndex = 0; + SourceSelectionTypePredicate Type; + TextRange Range; +}; + +struct SourceSelectionOperandBankPredicate final { + std::uint32_t OperandIndex = 0; + std::string BankSymbol; + TextRange Range; +}; + +struct SourceSelectionOperandClassPredicate final { + std::uint32_t OperandIndex = 0; + std::string ClassSymbol; + TextRange Range; +}; + +struct SourceSelectionIntegerPredicate final { + std::uint32_t OperandIndex = 0; + std::string Predicate; + TextRange Range; +}; + +struct SourceSelectionFloatingPredicate final { + std::uint32_t OperandIndex = 0; + std::string Predicate; + TextRange Range; +}; + +struct SourceSelectionTargetPredicate final { + std::string Symbol; + TextRange Range; +}; + +struct SourceSelectionMemoryPredicate final { + std::uint32_t MemoryOperandIndex = 0; + std::uint32_t Bytes = 0; + TextRange Range; +}; + +struct SourceSelectionConstantRange final { + std::int64_t Minimum = 0; + std::int64_t Maximum = 0; + TextRange SourceRange; +}; + +struct SourceSelectionOperandImmediatePredicate final { + std::uint32_t OperandIndex = 0; + std::vector<SourceSelectionConstantRange> Ranges; + TextRange Range; +}; + +struct SourceSelectionOperandDefConstantPredicate final { + std::uint32_t OperandIndex = 0; + std::vector<SourceSelectionConstantRange> Ranges; + TextRange Range; +}; + +struct SourceSelectionDefUseCapture final { + std::string Symbol; + std::uint32_t OperandIndex = 0; + TextRange Range; +}; + +struct SourceSelectionDefUsePredicate final { + std::uint32_t RootOperandIndex = 0; + std::string OpcodeSymbol; + std::optional<SourceSelectionIntegerPredicate> IntegerPredicate; + std::vector<SourceSelectionDefUseCapture> Captures; + std::vector<SourceSelectionOperandDefConstantPredicate> OperandDefConstants; + TextRange Range; +}; + +struct SourceSelectionTemporary final { + std::string Symbol; + std::optional<std::uint32_t> TypeFromOperand; + std::optional<SourceSelectionTypePredicate> Type; + TextRange Range; +}; + +struct SourceSelectionOperandRenderer final { + std::optional<std::uint32_t> OperandIndex; + std::optional<std::uint32_t> ComplexOperandIndex; + std::string PhysicalRegisterSymbol; + std::string ComplexRendererSymbol; + std::string TemporarySymbol; + std::string CapturedOperandSymbol; + std::optional<std::uint32_t> CapturedImmediateOperand; + std::optional<std::int64_t> Immediate; + std::string ImmediateTransform; + std::vector<std::string> Flags; + TextRange Range; +}; + +struct SourceSelectionAction final { + std::string ResultOpcodeSymbol; + bool ReplacesRoot = false; + std::vector<SourceSelectionOperandRenderer> OperandRenderers; + TextRange Range; +}; + +struct SourceSelectionRule final { + std::string Symbol; + std::uint32_t Priority = 0; + std::string MatchOpcodeSymbol; + std::optional<SourceSelectionTypePredicate> ResultType; + std::optional<SourceSelectionIntegerPredicate> IntegerPredicate; + std::optional<SourceSelectionFloatingPredicate> FloatingPredicate; + std::optional<SourceSelectionMemoryPredicate> MemoryPredicate; + std::vector<SourceSelectionTargetPredicate> TargetPredicates; + std::vector<SourceSelectionOperandTypePredicate> OperandTypes; + std::vector<SourceSelectionOperandBankPredicate> OperandBanks; + std::vector<SourceSelectionOperandClassPredicate> OperandClasses; + std::vector<SourceSelectionOperandImmediatePredicate> OperandImmediates; + std::vector<SourceSelectionOperandDefConstantPredicate> OperandDefConstants; + std::vector<SourceSelectionDefUsePredicate> DefUsePredicates; + std::vector<SourceSelectionTemporary> Temporaries; + std::vector<SourceSelectionAction> Actions; + std::vector<std::uint32_t> EraseOperandDefinitions; + TextRange Range; +}; + +struct SourceCombinerOperandRenderer final { + enum class Kind : std::uint8_t { + RootOperand, + CapturedOperand, + CapturedTypeBits, + Immediate, + }; + + Kind SourceKind = Kind::RootOperand; + std::uint32_t OperandIndex = 0; + std::string CaptureSymbol; + std::int64_t Immediate = 0; + TextRange Range; +}; + +enum class SourceCombinerActionKind : std::uint8_t { + ReplaceRootWithCapturedOperand, + MutateRoot, +}; + +struct SourceCombinerAction final { + SourceCombinerActionKind Kind = SourceCombinerActionKind::ReplaceRootWithCapturedOperand; + std::string CapturedOperandSymbol; + std::string ResultOpcodeSymbol; + std::vector<SourceCombinerOperandRenderer> OperandRenderers; + std::vector<std::string> ClearInstructionFlags; + TextRange Range; +}; + +struct SourceCombinerRule final { + std::string Symbol; + std::uint32_t Priority = 0; + std::string MatchOpcodeSymbol; + std::optional<SourceSelectionTypePredicate> ResultType; + std::vector<SourceSelectionOperandTypePredicate> OperandTypes; + std::vector<SourceSelectionTargetPredicate> TargetPredicates; + std::vector<std::string> InstructionFlags; + std::vector<SourceSelectionDefUsePredicate> DefUsePredicates; + SourceCombinerAction Action; + std::vector<std::uint32_t> EraseOperandDefinitions; + TextRange Range; +}; + +struct DescriptionAST final { + SourceTarget Target; + std::vector<SourceRegisterUnit> RegisterUnits; + std::vector<SourceRegister> Registers; + std::vector<SourceRegisterBank> RegisterBanks; + std::vector<SourceRegisterClass> RegisterClasses; + std::vector<SourceRegisterMask> RegisterMasks; + SourceMIROperandFlags MIROperandFlags; + std::vector<SourceCallingConvention> CallingConventions; + std::vector<SourceNamedOperand> NamedOperands; + std::vector<SourceImmediatePredicate> ImmediatePredicates; + std::vector<SourceSchedulingClass> SchedulingClasses; + std::vector<SourceEncodingCategory> EncodingCategories; + std::vector<SourceAssemblyFormat> AssemblyFormats; + std::vector<SourceInstruction> Instructions; + std::vector<SourceSelectionRule> SelectionRules; + std::vector<SourceCombinerRule> CombinerRules; + std::vector<SourceMacroFusionRule> MacroFusionRules; + std::vector<SourceMCPseudoExpansion> MCPseudoExpansions; +}; + +[[nodiscard]] std::string lowerASCII(std::string_view Value); +[[nodiscard]] bool isIdentifier(std::string_view Value) noexcept; +[[nodiscard]] bool isAssemblyMnemonic(std::string_view Value) noexcept; +[[nodiscard]] std::string upperIdentifier(std::string_view Value); +void appendQuoted(std::ostream &Output, std::string_view Value); + +} // namespace rcc::targetgen + +#endif // RCC_TABLEGEN_RECORD_H diff --git a/backend/include/rcc/Target/RISCV/RISCVAsmBackend.h b/backend/include/rcc/Target/RISCV/RISCVAsmBackend.h new file mode 100644 index 0000000..11dcb77 --- /dev/null +++ b/backend/include/rcc/Target/RISCV/RISCVAsmBackend.h @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_TARGET_RISCV_RISCVASMBACKEND_H +#define RCC_TARGET_RISCV_RISCVASMBACKEND_H + +#include "rcc/MC/MCAsmBackend.h" + +namespace rcc::riscv { + +class RISCVAsmBackend final : public mc::MCAsmBackend { +public: + [[nodiscard]] Expected<mc::MCFixupKindInfo> fixupKindInfo(mc::MCFixupKind Kind) const override; + [[nodiscard]] Expected<void> applyFixup(mc::MCFixupKind Kind, std::span<std::uint8_t> Data, + std::uint64_t Offset, std::int64_t Value) const override; + [[nodiscard]] Expected<std::uint32_t> relocationType(const mc::MCFixup &Fixup, + const mc::MCValue &Value) const override; + [[nodiscard]] Expected<std::vector<mc::MCFixupRelocation>> + relocationsForFixup(const mc::MCFixup &Fixup, const mc::MCValue &Value) const override; + + [[nodiscard]] bool mayNeedRelaxation(const mc::MCInst &Instruction) const noexcept override; + [[nodiscard]] Expected<bool> fixupNeedsRelaxation(const mc::MCFixup &Fixup, + std::int64_t Value) const override; + [[nodiscard]] Expected<mc::MCInst> relaxInstruction(const mc::MCInst &Instruction) const override; + + [[nodiscard]] std::uint32_t objectFlags() const noexcept override; +}; + +} // namespace rcc::riscv + +#endif // RCC_TARGET_RISCV_RISCVASMBACKEND_H diff --git a/backend/include/rcc/Target/RISCV/RISCVAssemblyPrinter.h b/backend/include/rcc/Target/RISCV/RISCVAssemblyPrinter.h new file mode 100644 index 0000000..5c8dca5 --- /dev/null +++ b/backend/include/rcc/Target/RISCV/RISCVAssemblyPrinter.h @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_TARGET_RISCV_RISCVASSEMBLYPRINTER_H +#define RCC_TARGET_RISCV_RISCVASSEMBLYPRINTER_H + +#include "rcc/CodeGen/AsmPrinter.h" +#include "rcc/CodeGen/TargetDescription.h" +#include "rcc/Support/Error.h" + +#include <string> + +namespace rcc { +class CompilationUnit; +class MachineFunction; +} // namespace rcc + +namespace rcc::riscv { + +struct AssemblyEmissionOptions final { + bool EmitFunctionDirectives = true; + bool EmitCFI = true; + bool EmitBlockLabels = true; + bool VerifyInput = true; +}; + +[[nodiscard]] Expected<std::string> emitAssembly(const CompilationUnit &Unit, + const TargetDescription &Target, + AssemblyEmissionOptions Options = {}); +[[nodiscard]] Expected<std::string> emitAssembly(const MachineFunction &Function, + const TargetDescription &Target, + AssemblyEmissionOptions Options = {}); + +} // namespace rcc::riscv + +#endif // RCC_TARGET_RISCV_RISCVASSEMBLYPRINTER_H diff --git a/backend/include/rcc/Target/RISCV/RISCVFixupKinds.h b/backend/include/rcc/Target/RISCV/RISCVFixupKinds.h new file mode 100644 index 0000000..e030bfc --- /dev/null +++ b/backend/include/rcc/Target/RISCV/RISCVFixupKinds.h @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_TARGET_RISCV_RISCVFIXUPKINDS_H +#define RCC_TARGET_RISCV_RISCVFIXUPKINDS_H + +#include "rcc/MC/MCFixup.h" + +#include <cstdint> + +namespace rcc::riscv { + +enum class RISCVFixupKind : mc::MCFixupKind { + High20 = mc::fixup_kind::FirstTarget, + Low12I, + Low12S, + PCRelativeHigh20, + GOTPCRelativeHigh20, + PCRelativeLow12I, + PCRelativeLow12S, + JAL, + Branch, + RVCJump, + RVCBranch, + CallPLT, +}; + +[[nodiscard]] constexpr mc::MCFixupKind fixupKind(RISCVFixupKind Kind) noexcept { + return static_cast<mc::MCFixupKind>(Kind); +} + +} // namespace rcc::riscv + +#endif // RCC_TARGET_RISCV_RISCVFIXUPKINDS_H diff --git a/backend/include/rcc/Target/RISCV/RISCVFunctionInfo.h b/backend/include/rcc/Target/RISCV/RISCVFunctionInfo.h new file mode 100644 index 0000000..6a05096 --- /dev/null +++ b/backend/include/rcc/Target/RISCV/RISCVFunctionInfo.h @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_TARGET_RISCV_RISCVFUNCTIONINFO_H +#define RCC_TARGET_RISCV_RISCVFUNCTIONINFO_H + +#include "rcc/CodeGen/TargetFunctionState.h" + +#include <cstddef> +#include <cstdint> +#include <span> +#include <string> +#include <vector> + +namespace rcc { + +struct TargetMIRTextInfoDesc; + +namespace riscv { + +class RISCVFunctionInfo final { +public: + RISCVFunctionInfo(std::int32_t VarArgsFrameIndex = 0, std::int32_t VarArgsSaveSize = 0) noexcept + : VarArgsFrameIndex(VarArgsFrameIndex), VarArgsSaveSize(VarArgsSaveSize) {} + + [[nodiscard]] static constexpr TargetFunctionStateTypeID stableTypeID() noexcept { + return TargetFunctionStateTypeID::fromValue(0x5243435256460001ULL); + } + + [[nodiscard]] static constexpr std::uint32_t schemaVersion() noexcept { + return 1; + } + + [[nodiscard]] Expected<void> verify() const; + [[nodiscard]] Expected<RISCVFunctionInfo> clone(const MachineFunctionCloneContext &Context) const; + [[nodiscard]] std::string print() const; + [[nodiscard]] Expected<std::vector<std::byte>> encode() const; + [[nodiscard]] static Expected<RISCVFunctionInfo> decode(std::uint32_t Version, + std::span<const std::byte> Payload); + + [[nodiscard]] std::int32_t varArgsFrameIndex() const noexcept { + return VarArgsFrameIndex; + } + + [[nodiscard]] std::int32_t varArgsSaveSize() const noexcept { + return VarArgsSaveSize; + } + + friend bool operator==(const RISCVFunctionInfo &Left, const RISCVFunctionInfo &Right) = default; + +private: + std::int32_t VarArgsFrameIndex = 0; + std::int32_t VarArgsSaveSize = 0; +}; + +void configureFunctionInfoMIRSchema(TargetMIRTextInfoDesc &Description); + +} // namespace riscv +} // namespace rcc + +#endif // RCC_TARGET_RISCV_RISCVFUNCTIONINFO_H diff --git a/backend/include/rcc/Target/RISCV/RISCVInstPrinter.h b/backend/include/rcc/Target/RISCV/RISCVInstPrinter.h new file mode 100644 index 0000000..7e74ab2 --- /dev/null +++ b/backend/include/rcc/Target/RISCV/RISCVInstPrinter.h @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_TARGET_RISCV_RISCVINSTPRINTER_H +#define RCC_TARGET_RISCV_RISCVINSTPRINTER_H + +#include "rcc/CodeGen/TargetDescription.h" +#include "rcc/MC/MCInstPrinter.h" + +#include <cstdint> +#include <string> +#include <string_view> + +namespace rcc::riscv { + +enum class RISCVMCExprKind : std::uint32_t { + CallPLT = 1, + PCRelativeHigh = 2, + PCRelativeLow = 3, + AbsoluteHigh = 4, + AbsoluteLow = 5, + GOTPCRelativeHigh = 6, +}; + +class RISCVInstPrinter final : public mc::MCInstPrinter { +public: + explicit RISCVInstPrinter(const TargetDescription &Target) noexcept : Target(&Target) {} + + [[nodiscard]] Expected<std::string> + printInstruction(const mc::MCInst &Instruction) const override; + [[nodiscard]] Expected<std::string> + printTargetExpression(std::uint32_t Kind, std::string_view Operand) const override; + +private: + const TargetDescription *Target; +}; + +} // namespace rcc::riscv + +#endif // RCC_TARGET_RISCV_RISCVINSTPRINTER_H diff --git a/backend/include/rcc/Target/RISCV/RISCVMCCodeEmitter.h b/backend/include/rcc/Target/RISCV/RISCVMCCodeEmitter.h new file mode 100644 index 0000000..700cdc2 --- /dev/null +++ b/backend/include/rcc/Target/RISCV/RISCVMCCodeEmitter.h @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_TARGET_RISCV_RISCVMCCODEEMITTER_H +#define RCC_TARGET_RISCV_RISCVMCCODEEMITTER_H + +#include "rcc/CodeGen/TargetDescription.h" +#include "rcc/MC/MCCodeEmitter.h" + +namespace rcc::riscv { + +class RISCVMCCodeEmitter final : public mc::MCCodeEmitter { +public: + explicit RISCVMCCodeEmitter(const TargetDescription &Target) noexcept : Target(&Target) {} + + [[nodiscard]] Expected<mc::MCEncodedInstruction> + encodeInstruction(const mc::MCInst &Instruction) const override; + +private: + const TargetDescription *Target; +}; + +} // namespace rcc::riscv + +#endif // RCC_TARGET_RISCV_RISCVMCCODEEMITTER_H diff --git a/backend/include/rcc/Target/RISCV/RISCVMCTargetLowering.h b/backend/include/rcc/Target/RISCV/RISCVMCTargetLowering.h new file mode 100644 index 0000000..3713bb5 --- /dev/null +++ b/backend/include/rcc/Target/RISCV/RISCVMCTargetLowering.h @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_TARGET_RISCV_RISCVMCTARGETLOWERING_H +#define RCC_TARGET_RISCV_RISCVMCTARGETLOWERING_H + +#include "rcc/CodeGen/TargetMCInstLowering.h" +#include "rcc/Target/RISCV/RISCVInstPrinter.h" + +namespace rcc::riscv { + +class RISCVMCTargetLowering final : public TargetMCInstLowering { +public: + explicit RISCVMCTargetLowering(const TargetDescription &Target) noexcept + : Target(&Target), Printer(Target) {} + + [[nodiscard]] const mc::MCInstPrinter &instructionPrinter() const noexcept override { + return Printer; + } + + [[nodiscard]] Expected<mc::MCInst> lowerInstruction(const MachineInstr &Instruction, + MachineMCContext &Context) const override; + [[nodiscard]] Expected<mc::MCExprRef> + lowerSymbolExpression(mc::MCExprRef Expression, std::uint32_t TargetFlags) const override; + [[nodiscard]] Expected<void> emitTerminalInstruction(const MachineInstr &Instruction, + MachineMCContext &Context, + mc::MCStreamer &Streamer) const override; + +private: + const TargetDescription *Target; + RISCVInstPrinter Printer; +}; + +} // namespace rcc::riscv + +#endif // RCC_TARGET_RISCV_RISCVMCTARGETLOWERING_H diff --git a/backend/include/rcc/Target/RISCV/RISCVObjectFile.h b/backend/include/rcc/Target/RISCV/RISCVObjectFile.h new file mode 100644 index 0000000..fd25012 --- /dev/null +++ b/backend/include/rcc/Target/RISCV/RISCVObjectFile.h @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_TARGET_RISCV_RISCVOBJECTFILE_H +#define RCC_TARGET_RISCV_RISCVOBJECTFILE_H + +#include "rcc/CodeGen/TargetDescription.h" +#include "rcc/Support/Error.h" + +#include <cstdint> +#include <vector> + +namespace rcc { +class CompilationUnit; +class MachineFunction; +} // namespace rcc + +namespace rcc::riscv { + +struct ObjectEmissionOptions final { + bool EmitFunctionDirectives = true; + bool EmitCFI = true; + bool EmitBlockLabels = true; + bool VerifyInput = true; +}; + +[[nodiscard]] Expected<std::vector<std::uint8_t>> emitObject(const CompilationUnit &Unit, + const TargetDescription &Target, + ObjectEmissionOptions Options = {}); +[[nodiscard]] Expected<std::vector<std::uint8_t>> emitObject(const MachineFunction &Function, + const TargetDescription &Target, + ObjectEmissionOptions Options = {}); + +} // namespace rcc::riscv + +#endif // RCC_TARGET_RISCV_RISCVOBJECTFILE_H diff --git a/backend/include/rcc/Target/RISCV/RISCVPipeline.h b/backend/include/rcc/Target/RISCV/RISCVPipeline.h new file mode 100644 index 0000000..f556ded --- /dev/null +++ b/backend/include/rcc/Target/RISCV/RISCVPipeline.h @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_TARGET_RISCV_RISCVPIPELINE_H +#define RCC_TARGET_RISCV_RISCVPIPELINE_H + +#include "rcc/CodeGen/MachinePassManager.h" +#include "rcc/Support/Error.h" +#include "rcc/Target/RISCV/RISCVSubtarget.h" +#include "rcc/Target/TargetMachine.h" + +#include <memory> +#include <string> +#include <string_view> +#include <vector> + +namespace rcc { + +class MachineFunction; +class TargetDescription; +class TargetMachineOutlinerInfo; + +namespace riscv { + +[[nodiscard]] Expected<void> addCanonicalPipeline(MachineFunctionPassManager &Pipeline, + const MachineFunction &Function, + const RISCVSubtarget &Subtarget, + TargetPipelineOptions Options = {}); +[[nodiscard]] Expected<std::unique_ptr<MachineFunctionPass>> +createCanonicalPipelinePass(std::string_view Name, const RISCVSubtarget &Subtarget, + TargetPipelineOptions Options = {}); +[[nodiscard]] Expected<std::vector<std::string>> +canonicalPipelinePassNames(const MachineFunction &Function, const RISCVSubtarget &Subtarget); +[[nodiscard]] Expected<std::unique_ptr<TargetMachineOutlinerInfo>> +createMachineOutlinerInfo(bool FunctionSections, bool EnableRegisterSave); +} // namespace riscv +} // namespace rcc + +#endif // RCC_TARGET_RISCV_RISCVPIPELINE_H diff --git a/backend/include/rcc/Target/RISCV/RISCVSubtarget.h b/backend/include/rcc/Target/RISCV/RISCVSubtarget.h new file mode 100644 index 0000000..a5ba01a --- /dev/null +++ b/backend/include/rcc/Target/RISCV/RISCVSubtarget.h @@ -0,0 +1,156 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_TARGET_RISCV_RISCVSUBTARGET_H +#define RCC_TARGET_RISCV_RISCVSUBTARGET_H + +#include "rcc/CodeGen/CommandFlags.h" +#include "rcc/Support/Error.h" + +#include <cstdint> +#include <memory> +#include <span> +#include <string> +#include <string_view> + +namespace rcc { + +class InstructionSelector; +class LegalizerInfo; +class TargetCallLowering; +class TargetFrameLowering; +class TargetSchedulingHooks; +class TargetSchedulingModel; + +namespace riscv { + +class RISCVRegisterBankInfo; + +enum class RISCVExtension : std::uint8_t { + I, + M, + A, + F, + D, + C, + Zba, + Zbb, + Zicsr, + Zifencei, + Zacas, + Zabha, + Zcmp, + Count, +}; + +enum class RISCVSubtargetOption : std::uint8_t { + DisablePostMISchedLoadClustering, + DisablePostMISchedStoreClustering, + FusionAddLoad, + FusionAddMemory, + FusionAddImmediateLoad, + FusionAUIPCAddImmediate, + FusionAUIPCLoad, + FusionBitfieldExtract, + FusionLoadDoubleAdd, + FusionLogicImmediateRegister, + FusionLogicRegisterImmediate, + FusionLogicRegisterRegister, + FusionLUIAddImmediate, + FusionLUILoad, + FusionMultiplyAdd, + FusionShiftBitExtract, + FusionShiftedAddLoad, + FusionShiftedZeroExtendWord, + FusionZeroExtendHalf, + FusionZeroExtendWord, + PreferWordInstructions, + ReserveX6, + ReserveX7, + SaveRestore, + TaggedGlobals, + UnalignedScalarMemory, + UsePostRAScheduler, + Count, +}; + +enum class RISCVABI : std::uint8_t { + LP64, + LP64D, +}; + +[[nodiscard]] Expected<RISCVABI> parseRISCVABI(std::string_view Name); + +class RISCVSubtargetFeatures final { +public: + [[nodiscard]] static Expected<RISCVSubtargetFeatures> + fromFeatureList(std::span<const std::string> Features); + [[nodiscard]] static RISCVSubtargetFeatures rv64gc() noexcept; + + [[nodiscard]] bool has(RISCVExtension Extension) const noexcept; + [[nodiscard]] bool has(RISCVSubtargetOption Option) const noexcept; + [[nodiscard]] bool hasFeature(std::string_view Feature) const noexcept; + + friend bool operator==(const RISCVSubtargetFeatures &Left, + const RISCVSubtargetFeatures &Right) = default; + +private: + explicit constexpr RISCVSubtargetFeatures(std::uint16_t Enabled, + std::uint32_t Options = 0) noexcept + : Enabled(Enabled), Options(Options) {} + + std::uint16_t Enabled = 0; + std::uint32_t Options = 0; +}; + +class RISCVSubtarget final { +public: + explicit RISCVSubtarget(RISCVSubtargetFeatures Features, + RelocationModel Relocation = RelocationModel::Static, + CodeModel Code = CodeModel::Small, RISCVABI ABI = RISCVABI::LP64D); + RISCVSubtarget(const RISCVSubtarget &) = delete; + RISCVSubtarget &operator=(const RISCVSubtarget &) = delete; + RISCVSubtarget(RISCVSubtarget &&) = delete; + RISCVSubtarget &operator=(RISCVSubtarget &&) = delete; + ~RISCVSubtarget(); + + [[nodiscard]] const RISCVSubtargetFeatures &features() const noexcept { + return Features; + } + + [[nodiscard]] RelocationModel relocationModel() const noexcept { + return Relocation; + } + + [[nodiscard]] CodeModel codeModel() const noexcept { + return Code; + } + + [[nodiscard]] RISCVABI targetABI() const noexcept { + return ABI; + } + + [[nodiscard]] const TargetCallLowering *getCallLowering() const; + [[nodiscard]] InstructionSelector *getInstructionSelector() const; + [[nodiscard]] const LegalizerInfo *getLegalizerInfo() const; + [[nodiscard]] const RISCVRegisterBankInfo *getRegBankInfo() const; + [[nodiscard]] const TargetFrameLowering &frameLowering() const noexcept; + [[nodiscard]] const TargetSchedulingModel &schedulingModel() const noexcept; + +private: + RISCVSubtargetFeatures Features; + RelocationModel Relocation; + CodeModel Code; + RISCVABI ABI; + std::unique_ptr<TargetFrameLowering> FrameLoweringInfo; + std::unique_ptr<TargetCallLowering> CallLoweringInfo; + std::unique_ptr<InstructionSelector> InstSelector; + std::unique_ptr<LegalizerInfo> Legalizer; + std::unique_ptr<RISCVRegisterBankInfo> RegBankInfo; + std::unique_ptr<TargetSchedulingHooks> SchedulingHooks; + std::unique_ptr<TargetSchedulingModel> SchedulingModel; +}; + +} // namespace riscv +} // namespace rcc + +#endif // RCC_TARGET_RISCV_RISCVSUBTARGET_H diff --git a/backend/include/rcc/Target/RISCV/RISCVTarget.h b/backend/include/rcc/Target/RISCV/RISCVTarget.h new file mode 100644 index 0000000..6d6b806 --- /dev/null +++ b/backend/include/rcc/Target/RISCV/RISCVTarget.h @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_TARGET_RISCV_RISCVTARGET_H +#define RCC_TARGET_RISCV_RISCVTARGET_H + +#include "rcc/CodeGen/TargetDescription.h" +#include "rcc/Support/Error.h" +#include "rcc/Target/RISCV/RISCVInstructions.h" +#include "rcc/Target/RISCV/RISCVRegisters.h" + +#include <cstdint> +#include <memory> +#include <span> +#include <string_view> + +namespace rcc::riscv { + +struct SchedulingClassInfo final { + SchedulingClassID ID; + std::string_view Name; + std::uint32_t MicroOps = 0; + std::uint32_t Latency = 0; +}; + +struct EncodingCategoryInfo final { + EncodingCategoryID ID; + std::string_view Name; + std::uint32_t SizeInBytes = 0; +}; + +struct AssemblyFormatInfo final { + AssemblyFormatID ID; + std::string_view Name; +}; + +struct AssemblyInstructionInfo final { + Opcode InstructionOpcode; + std::string_view Mnemonic; + AssemblyFormatID Format; + std::uint32_t Encoding = 0; +}; + +struct CallingConventionInfo final { + CallingConvention ID; + std::string_view Name; + RegisterClassID IntegerRegisterClass; + RegisterClassID FloatingRegisterClass; + std::span<const PhysicalRegister> IntegerArgumentRegisters; + std::span<const PhysicalRegister> FloatingArgumentRegisters; + std::span<const PhysicalRegister> IntegerReturnRegisters; + std::span<const PhysicalRegister> FloatingReturnRegisters; + std::string_view PreservedMaskName; + std::uint32_t StackAlignmentBytes = 0; + std::uint32_t StackArgumentAlignmentBytes = 0; + std::uint32_t ShadowSpaceBytes = 0; + bool SupportsVarArgs = false; + bool SupportsTailCalls = false; +}; + +[[nodiscard]] Expected<PhysicalRegister> physicalRegister(PhysicalRegisterID ID); +[[nodiscard]] Expected<TargetRegisterInfo> createRegisterInfo(); +[[nodiscard]] Expected<TargetMIRTextInfo> createMIRTextInfo(); +[[nodiscard]] Expected<TargetDescription> createTargetDescription(); +[[nodiscard]] std::shared_ptr<const RegisterMask> +createCallPreservedMask(CallingConvention Convention); +[[nodiscard]] std::span<const SchedulingClassInfo> schedulingClasses() noexcept; +[[nodiscard]] std::span<const EncodingCategoryInfo> encodingCategories() noexcept; +[[nodiscard]] std::span<const AssemblyFormatInfo> assemblyFormats() noexcept; +[[nodiscard]] std::span<const AssemblyInstructionInfo> assemblyInstructions() noexcept; +[[nodiscard]] const AssemblyInstructionInfo *assemblyInstruction(Opcode InstructionOpcode) noexcept; +[[nodiscard]] std::span<const CallingConventionInfo> callingConventions() noexcept; +[[nodiscard]] const CallingConventionInfo *callingConvention(CallingConvention ID) noexcept; + +} // namespace rcc::riscv + +#endif // RCC_TARGET_RISCV_RISCVTARGET_H diff --git a/backend/include/rcc/Target/RISCV/RISCVTargetConstant.h b/backend/include/rcc/Target/RISCV/RISCVTargetConstant.h new file mode 100644 index 0000000..2244117 --- /dev/null +++ b/backend/include/rcc/Target/RISCV/RISCVTargetConstant.h @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_TARGET_RISCV_RISCVTARGETCONSTANT_H +#define RCC_TARGET_RISCV_RISCVTARGETCONSTANT_H + +#include "rcc/CodeGen/TargetConstant.h" + +#include <cstddef> +#include <cstdint> +#include <optional> +#include <span> +#include <string> +#include <string_view> +#include <utility> +#include <vector> + +namespace rcc::riscv { + +enum class RISCVSymbolConstantKind : std::uint8_t { + GlobalValue, + ExternalSymbol, +}; + +class RISCVSymbolConstant final { +public: + RISCVSymbolConstant(RISCVSymbolConstantKind Kind, std::string Symbol, std::int64_t Addend = 0) + : ConstantKind(Kind), SymbolName(std::move(Symbol)), SymbolAddend(Addend) {} + + [[nodiscard]] static constexpr TargetConstantTypeID stableTypeID() noexcept { + return TargetConstantTypeID::fromValue(0x5243435256435001ULL); + } + + [[nodiscard]] static constexpr std::uint32_t schemaVersion() noexcept { + return 1; + } + + [[nodiscard]] Expected<void> verify() const; + [[nodiscard]] Expected<RISCVSymbolConstant> clone() const; + [[nodiscard]] std::uint64_t stableHash() const noexcept; + [[nodiscard]] std::string print() const; + [[nodiscard]] Expected<std::vector<std::byte>> encode() const; + [[nodiscard]] Expected<TargetConstantEmission> emit() const; + [[nodiscard]] Alignment requiredAlignment() const noexcept; + [[nodiscard]] std::optional<TargetRelocationKindID> relocationKind() const noexcept; + + [[nodiscard]] static Expected<RISCVSymbolConstant> parse(std::string_view Text); + [[nodiscard]] static Expected<RISCVSymbolConstant> decode(std::uint32_t Version, + std::span<const std::byte> Payload); + + [[nodiscard]] RISCVSymbolConstantKind kind() const noexcept { + return ConstantKind; + } + + [[nodiscard]] std::string_view symbol() const noexcept { + return SymbolName; + } + + [[nodiscard]] std::int64_t addend() const noexcept { + return SymbolAddend; + } + + friend bool operator==(const RISCVSymbolConstant &Left, + const RISCVSymbolConstant &Right) = default; + +private: + RISCVSymbolConstantKind ConstantKind; + std::string SymbolName; + std::int64_t SymbolAddend = 0; +}; + +} // namespace rcc::riscv + +#endif // RCC_TARGET_RISCV_RISCVTARGETCONSTANT_H diff --git a/backend/include/rcc/Target/RISCV/RISCVTargetRelocationKinds.h b/backend/include/rcc/Target/RISCV/RISCVTargetRelocationKinds.h new file mode 100644 index 0000000..08bc73f --- /dev/null +++ b/backend/include/rcc/Target/RISCV/RISCVTargetRelocationKinds.h @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_TARGET_RISCV_RISCVTARGETRELOCATIONKINDS_H +#define RCC_TARGET_RISCV_RISCVTARGETRELOCATIONKINDS_H + +#include "rcc/CodeGen/MachineIRTypes.h" + +#include <cstdint> + +namespace rcc::riscv { + +enum class RISCVTargetRelocationKind : std::uint32_t { + Absolute32 = 1, + Absolute64, +}; + +[[nodiscard]] constexpr TargetRelocationKindID +targetRelocationKind(RISCVTargetRelocationKind Kind) noexcept { + return TargetRelocationKindID::fromValue(static_cast<std::uint32_t>(Kind)); +} + +} // namespace rcc::riscv + +#endif // RCC_TARGET_RISCV_RISCVTARGETRELOCATIONKINDS_H diff --git a/backend/include/rcc/Target/TargetMachine.h b/backend/include/rcc/Target/TargetMachine.h new file mode 100644 index 0000000..e45b8ca --- /dev/null +++ b/backend/include/rcc/Target/TargetMachine.h @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_TARGET_TARGETMACHINE_H +#define RCC_TARGET_TARGETMACHINE_H + +#include "rcc/CodeGen/MachineOutliner.h" +#include "rcc/CodeGen/MachinePassManager.h" +#include "rcc/CodeGen/TargetDescription.h" +#include "rcc/Support/Error.h" +#include "rcc/Target/TargetOptions.h" + +#include <cstdint> +#include <memory> +#include <string> +#include <string_view> +#include <vector> + +namespace rcc { + +class CompilationUnit; +class MachineFunction; +class Target; + +class TargetMachine { +public: + TargetMachine(const TargetMachine &) = delete; + TargetMachine &operator=(const TargetMachine &) = delete; + TargetMachine(TargetMachine &&) = delete; + TargetMachine &operator=(TargetMachine &&) = delete; + virtual ~TargetMachine(); + + [[nodiscard]] const Target &target() const noexcept { + return RegisteredTarget; + } + + [[nodiscard]] const TargetDescription &targetDescription() const noexcept { + return Description; + } + + [[nodiscard]] const TargetMachineOptions &options() const noexcept { + return Options; + } + + [[nodiscard]] virtual Expected<void> + addCodeGenPasses(MachineFunctionPassManager &Pipeline, const MachineFunction &Function, + TargetPipelineOptions PipelineOptions) const = 0; + [[nodiscard]] virtual Expected<std::unique_ptr<MachineFunctionPass>> + createMachinePass(std::string_view Name, TargetPipelineOptions PipelineOptions) const = 0; + [[nodiscard]] virtual Expected<std::vector<std::string>> + codeGenPassNames(const MachineFunction &Function) const = 0; + [[nodiscard]] virtual Expected<std::unique_ptr<TargetMachineOutlinerInfo>> + createOutlinerInfo() const = 0; + [[nodiscard]] virtual Expected<std::string> emitAssembly(const CompilationUnit &Unit) const = 0; + [[nodiscard]] virtual Expected<std::vector<std::uint8_t>> + emitObject(const CompilationUnit &Unit) const = 0; + +protected: + TargetMachine(const Target &RegisteredTarget, TargetMachineOptions Options, + TargetDescription Description); + +private: + const Target &RegisteredTarget; + TargetMachineOptions Options; + TargetDescription Description; +}; + +} // namespace rcc + +#endif // RCC_TARGET_TARGETMACHINE_H diff --git a/backend/include/rcc/Target/TargetOptions.h b/backend/include/rcc/Target/TargetOptions.h new file mode 100644 index 0000000..1db183c --- /dev/null +++ b/backend/include/rcc/Target/TargetOptions.h @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_TARGET_TARGETOPTIONS_H +#define RCC_TARGET_TARGETOPTIONS_H + +#include "rcc/CodeGen/CommandFlags.h" + +#include <string> +#include <vector> + +namespace rcc { + +struct TargetOption final { + std::string Name; + std::string Value; + + friend bool operator==(const TargetOption &, const TargetOption &) = default; +}; + +struct TargetMachineOptions final { + std::string Triple; + std::string CPU; + std::string TuneCPU; + std::vector<std::string> Features; + std::string ABI; + std::string DataLayout; + RelocationModel Relocation = RelocationModel::Static; + CodeModel Code = CodeModel::Small; + OptimizationLevel Optimization = OptimizationLevel::O2; + std::vector<TargetOption> Options; + std::string ABILoweringVersion; + std::string AssemblerFlavor; + std::string AssemblerVersion; + std::vector<TargetOption> AssemblerOptions; + + friend bool operator==(const TargetMachineOptions &, const TargetMachineOptions &) = default; +}; + +struct TargetPipelineOptions final { + OptimizationLevel Optimization = OptimizationLevel::O2; + bool UseTargetCopyInstructions = false; + GlobalISelAbortMode GlobalISelAbort = GlobalISelAbortMode::Enable; + bool DebugMachineScheduler = false; + bool DebugPrologueEpilogue = false; +}; + +} // namespace rcc + +#endif // RCC_TARGET_TARGETOPTIONS_H diff --git a/backend/include/rcc/Tooling/ArtifactStore.h b/backend/include/rcc/Tooling/ArtifactStore.h new file mode 100644 index 0000000..e4bca4c --- /dev/null +++ b/backend/include/rcc/Tooling/ArtifactStore.h @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_TOOLING_ARTIFACTSTORE_H +#define RCC_TOOLING_ARTIFACTSTORE_H + +#include "rcc/CodeGen/MIRParser/MIRParser.h" +#include "rcc/Support/Error.h" +#include "rcc/Tooling/BackendManifest.h" + +#include <cstdint> +#include <filesystem> +#include <optional> +#include <span> +#include <string> +#include <vector> + +namespace rcc { + +class TargetDescription; + +namespace tooling { + +struct CanonicalMIR final { + mir::MIRDocument Document; + std::string Text; +}; + +struct CacheEntry final { + BackendManifest Manifest; + std::string InputMIR; + std::vector<std::uint8_t> Artifact; + std::optional<CanonicalMIR> ParsedArtifact; +}; + +struct ReproducerPackage final { + BackendManifest Manifest; + CanonicalMIR Input; +}; + +[[nodiscard]] bool isMIRArtifact(ArtifactKind Artifact) noexcept; +[[nodiscard]] bool isCacheableArtifact(ArtifactKind Artifact) noexcept; + +[[nodiscard]] Expected<CanonicalMIR> parseAndCanonicalizeMIR(std::string SourceName, + std::string SourceText, + const TargetDescription &Target, + const BackendInvocation &Invocation); + +[[nodiscard]] Expected<BackendManifest> +createBackendManifest(ManifestKind Kind, ArtifactKind Artifact, const BackendInvocation &Invocation, + const TargetDescription &Target, std::string_view CanonicalInputMIR, + std::optional<std::span<const std::uint8_t>> ArtifactBytes = std::nullopt, + std::vector<StructuredDiagnostic> Diagnostics = {}); + +[[nodiscard]] Expected<void> storeCacheEntry(const std::filesystem::path &CacheRoot, + const BackendManifest &Manifest, + std::string_view CanonicalInputMIR, + std::span<const std::uint8_t> Artifact); +[[nodiscard]] Expected<CacheEntry> loadCacheEntry(const std::filesystem::path &CacheRoot, + ArtifactKind Artifact, + const BackendInvocation &Invocation, + const TargetDescription &Target, + std::string_view CanonicalInputMIR); + +[[nodiscard]] Expected<void> writeReproducer(const std::filesystem::path &PackagePath, + const BackendManifest &Manifest, + std::string_view CanonicalInputMIR); +[[nodiscard]] Expected<void> +updateReproducerDiagnostics(const std::filesystem::path &PackagePath, + std::vector<StructuredDiagnostic> Diagnostics); +[[nodiscard]] Expected<ReproducerPackage> loadReproducer(const std::filesystem::path &PackagePath, + const TargetDescription &Target); + +} // namespace tooling +} // namespace rcc + +#endif // RCC_TOOLING_ARTIFACTSTORE_H diff --git a/backend/include/rcc/Tooling/BackendDriver.h b/backend/include/rcc/Tooling/BackendDriver.h new file mode 100644 index 0000000..2003983 --- /dev/null +++ b/backend/include/rcc/Tooling/BackendDriver.h @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_TOOLING_BACKENDDRIVER_H +#define RCC_TOOLING_BACKENDDRIVER_H + +#include "rcc/CodeGen/MachinePassManager.h" +#include "rcc/Support/Error.h" +#include "rcc/Tooling/ArtifactStore.h" +#include "rcc/Tooling/PipelineDriver.h" + +#include <cstdint> +#include <span> +#include <string_view> +#include <vector> + +namespace rcc::tooling { + +struct BackendRunResult final { + ArtifactKind Kind = ArtifactKind::FinalMIR; + std::vector<std::uint8_t> Output; + PipelineRunResult Pipeline; + std::vector<MachineActionEvent> Actions; + + [[nodiscard]] std::string_view text() const noexcept; +}; + +[[nodiscard]] Expected<BackendRunResult> +runBackend(CanonicalMIR Input, const BackendInvocation &Invocation, ArtifactKind Output, + PipelineControls Controls = {}, MachinePassInstrumentation *Instrumentation = nullptr); + +} // namespace rcc::tooling + +#endif // RCC_TOOLING_BACKENDDRIVER_H diff --git a/backend/include/rcc/Tooling/BackendInvocation.h b/backend/include/rcc/Tooling/BackendInvocation.h new file mode 100644 index 0000000..c50148a --- /dev/null +++ b/backend/include/rcc/Tooling/BackendInvocation.h @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_TOOLING_BACKENDINVOCATION_H +#define RCC_TOOLING_BACKENDINVOCATION_H + +#include "rcc/CodeGen/CommandFlags.h" +#include "rcc/Support/Error.h" +#include "rcc/Target/TargetOptions.h" + +#include <cstdint> +#include <memory> +#include <optional> +#include <span> +#include <string> +#include <string_view> +#include <vector> + +namespace rcc { + +class TargetDescription; +class Target; +class TargetMachine; + +namespace tooling { + +inline constexpr std::uint32_t BackendInvocationSchemaVersion = 1; +inline constexpr std::string_view LLVMReferenceRevision = + "2b40c303e79f64c5c7642032554eda026594d604"; +inline constexpr std::string_view GenericOpcodeReferenceVersion = "rcc-generic-opcode-v1"; +inline constexpr std::string_view CompilerBuildFingerprint = "rcc-backend-0.1.0-stage17"; + +enum class LayoutOwnership : std::uint8_t { + NativeObject, + CompilerFinalLayout, + ExternalRelaxation, +}; + +using BackendOption = TargetOption; + +struct DeterminismOptions final { + std::uint64_t RandomSeed = 0; + std::uint32_t ThreadCount = 1; + bool StableScheduling = true; + + friend bool operator==(const DeterminismOptions &, const DeterminismOptions &) = default; +}; + +struct BackendInvocation final { + std::uint32_t SchemaVersion = BackendInvocationSchemaVersion; + std::string Triple; + std::string CPU; + std::string TuneCPU; + std::vector<std::string> Features; + std::string ABI; + std::string DataLayout; + rcc::RelocationModel Relocation = rcc::RelocationModel::Static; + rcc::CodeModel Code = rcc::CodeModel::Small; + std::vector<BackendOption> TargetOptions; + LayoutOwnership Layout = LayoutOwnership::NativeObject; + std::string AssemblerFlavor; + std::string AssemblerVersion; + std::vector<BackendOption> AssemblerOptions; + rcc::OptimizationLevel Optimization = rcc::OptimizationLevel::O2; + std::vector<std::string> PassPipeline{"default"}; + DeterminismOptions Determinism; + std::optional<std::uint64_t> ActionFuel; + std::vector<std::string> EnabledActions; + std::string MIRReferenceRevision{LLVMReferenceRevision}; + std::string GenericOpcodeVersion{GenericOpcodeReferenceVersion}; + std::string TargetABILoweringVersion; + std::string BuildFingerprint{CompilerBuildFingerprint}; + + [[nodiscard]] Expected<void> canonicalizeAndVerify(); + [[nodiscard]] std::string canonicalText() const; + + friend bool operator==(const BackendInvocation &, const BackendInvocation &) = default; +}; + +[[nodiscard]] Expected<BackendInvocation> defaultBackendInvocation(const Target &Target); +[[nodiscard]] Expected<std::unique_ptr<TargetMachine>> +createTargetMachine(const BackendInvocation &Invocation); +[[nodiscard]] Expected<std::string> +computeTargetDescriptionFingerprint(const TargetDescription &Target); +[[nodiscard]] Expected<std::string> computeCacheKey(std::string_view CanonicalMIR, + const BackendInvocation &Invocation, + std::string_view TargetFingerprint); + +[[nodiscard]] std::string_view spelling(LayoutOwnership Value) noexcept; +[[nodiscard]] Expected<LayoutOwnership> parseLayoutOwnership(std::string_view Value); + +} // namespace tooling +} // namespace rcc + +#endif // RCC_TOOLING_BACKENDINVOCATION_H diff --git a/backend/include/rcc/Tooling/BackendManifest.h b/backend/include/rcc/Tooling/BackendManifest.h new file mode 100644 index 0000000..d5d5733 --- /dev/null +++ b/backend/include/rcc/Tooling/BackendManifest.h @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_TOOLING_BACKENDMANIFEST_H +#define RCC_TOOLING_BACKENDMANIFEST_H + +#include "rcc/Tooling/BackendInvocation.h" + +#include <cstdint> +#include <optional> +#include <string> +#include <string_view> +#include <vector> + +namespace rcc::tooling { + +inline constexpr std::uint32_t BackendManifestSchemaVersion = 1; + +enum class ManifestKind : std::uint8_t { + Cache, + Reproducer, +}; + +enum class ArtifactKind : std::uint8_t { + PreLegalizerMIR, + LegalizedMIR, + RegisterBankMIR, + SelectedMIR, + PostSSAMIR, + PostRegisterAllocationMIR, + FinalMIR, + Assembly, + RelocatableObject, +}; + +struct StructuredDiagnostic final { + std::string Category; + std::string Stage; + std::string Pass; + std::string Function; + std::optional<std::uint64_t> Block; + std::optional<std::uint64_t> Instruction; + std::optional<std::uint32_t> Operand; + std::string SourceLocation; + std::string TargetConstraint; + std::string Rule; + std::string Message; + + friend bool operator==(const StructuredDiagnostic &, const StructuredDiagnostic &) = default; +}; + +struct BackendManifest final { + std::uint32_t SchemaVersion = BackendManifestSchemaVersion; + ManifestKind Kind = ManifestKind::Reproducer; + ArtifactKind Artifact = ArtifactKind::FinalMIR; + BackendInvocation Invocation; + std::string InputMIRHash; + std::string TargetFingerprint; + std::string CacheKey; + std::optional<std::string> ArtifactHash; + std::vector<StructuredDiagnostic> Diagnostics; + + [[nodiscard]] Expected<void> verify() const; + + friend bool operator==(const BackendManifest &, const BackendManifest &) = default; +}; + +[[nodiscard]] Expected<std::string> printBackendManifest(const BackendManifest &Manifest); +[[nodiscard]] Expected<BackendManifest> parseBackendManifest(std::string SourceName, + std::string SourceText); +[[nodiscard]] std::string_view spelling(ManifestKind Value) noexcept; +[[nodiscard]] std::string_view spelling(ArtifactKind Value) noexcept; + +} // namespace rcc::tooling + +#endif // RCC_TOOLING_BACKENDMANIFEST_H diff --git a/backend/include/rcc/Tooling/MIRFuzzer.h b/backend/include/rcc/Tooling/MIRFuzzer.h new file mode 100644 index 0000000..7fccb6d --- /dev/null +++ b/backend/include/rcc/Tooling/MIRFuzzer.h @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_TOOLING_MIRFUZZER_H +#define RCC_TOOLING_MIRFUZZER_H + +#include "rcc/Support/Error.h" +#include "rcc/Tooling/BackendInvocation.h" + +#include <cstddef> +#include <cstdint> +#include <string> +#include <string_view> +#include <vector> + +namespace rcc { + +class TargetDescription; + +namespace tooling { + +enum class MIRFuzzLayer : std::uint8_t { + Parser, + BuilderStructural, + PropertyStage, + Mutation, + Pipeline, + RestrictedSemantics, +}; + +struct MIRFuzzOptions final { + std::uint64_t Seed = 0; + std::size_t Iterations = 100; + std::size_t MaximumInputSize = 1U << 20U; + std::vector<MIRFuzzLayer> Layers; +}; + +struct MIRFuzzLayerResult final { + MIRFuzzLayer Layer = MIRFuzzLayer::Parser; + std::size_t Executed = 0; + std::size_t Accepted = 0; + std::size_t Rejected = 0; + + friend bool operator==(const MIRFuzzLayerResult &, const MIRFuzzLayerResult &) = default; +}; + +struct MIRFuzzResult final { + std::uint64_t Seed = 0; + std::vector<MIRFuzzLayerResult> Layers; + + [[nodiscard]] std::size_t executed() const noexcept; + [[nodiscard]] std::size_t accepted() const noexcept; + [[nodiscard]] std::size_t rejected() const noexcept; +}; + +[[nodiscard]] Expected<MIRFuzzResult> fuzzMIR(std::string SourceName, std::string SourceText, + BackendInvocation Invocation, + const TargetDescription &Target, + MIRFuzzOptions Options = {}); + +[[nodiscard]] std::string_view spelling(MIRFuzzLayer Layer) noexcept; +[[nodiscard]] Expected<MIRFuzzLayer> parseMIRFuzzLayer(std::string_view Value); + +} // namespace tooling +} // namespace rcc + +#endif // RCC_TOOLING_MIRFUZZER_H diff --git a/backend/include/rcc/Tooling/MIRReducer.h b/backend/include/rcc/Tooling/MIRReducer.h new file mode 100644 index 0000000..35ef9c0 --- /dev/null +++ b/backend/include/rcc/Tooling/MIRReducer.h @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_TOOLING_MIRREDUCER_H +#define RCC_TOOLING_MIRREDUCER_H + +#include "rcc/Support/Error.h" +#include "rcc/Tooling/BackendInvocation.h" + +#include <cstddef> +#include <cstdint> +#include <functional> +#include <string> +#include <string_view> +#include <vector> + +namespace rcc { + +class TargetDescription; + +namespace tooling { + +enum class ReductionMode : std::uint8_t { + PreserveValidity, + PreserveFailure, +}; + +enum class ReductionGranularity : std::uint8_t { + CompilationUnit, + MachineFunction, + BasicBlock, + CFGEdge, + Instruction, + Operand, + RegisterSubgraph, + FrameObject, + ConstantPool, + JumpTable, + TargetFeature, + PassPipeline, +}; + +struct MIRReducerOptions final { + ReductionMode Mode = ReductionMode::PreserveFailure; + std::size_t MaximumAttempts = 100'000; + std::vector<ReductionGranularity> Granularities; +}; + +using MIRInterestingnessTest = + std::function<Expected<bool>(std::string_view, const BackendInvocation &)>; + +// PreserveFailure requires and evaluates the interestingness test. PreserveValidity accepts every +// smaller candidate that reparses, canonicalizes, and verifies; its interestingness test is +// ignored. + +struct MIRReductionStep final { + ReductionGranularity Granularity = ReductionGranularity::Instruction; + std::string Description; + std::size_t PreviousSize = 0; + std::size_t ReducedSize = 0; +}; + +struct MIRReductionResult final { + std::string MIR; + BackendInvocation Invocation; + std::vector<MIRReductionStep> Steps; + std::size_t Attempts = 0; + bool Exhausted = false; +}; + +[[nodiscard]] Expected<MIRReductionResult> reduceMIR(std::string SourceName, std::string SourceText, + BackendInvocation Invocation, + const TargetDescription &Target, + const MIRInterestingnessTest &Interesting, + MIRReducerOptions Options = {}); + +[[nodiscard]] std::string_view spelling(ReductionGranularity Granularity) noexcept; + +} // namespace tooling +} // namespace rcc + +#endif // RCC_TOOLING_MIRREDUCER_H diff --git a/backend/include/rcc/Tooling/PipelineDriver.h b/backend/include/rcc/Tooling/PipelineDriver.h new file mode 100644 index 0000000..ce88e64 --- /dev/null +++ b/backend/include/rcc/Tooling/PipelineDriver.h @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef RCC_TOOLING_PIPELINEDRIVER_H +#define RCC_TOOLING_PIPELINEDRIVER_H + +#include "rcc/CodeGen/MIRParser/MIRParser.h" +#include "rcc/CodeGen/MachinePassManager.h" +#include "rcc/Target/TargetMachine.h" +#include "rcc/Tooling/BackendInvocation.h" + +#include <cstdint> +#include <optional> +#include <string> +#include <string_view> +#include <vector> + +namespace rcc { + +namespace tooling { + +enum class BackendStage : std::uint8_t { + PreLegalizerMIR, + LegalizedMIR, + RegisterBankMIR, + SelectedMIR, + PostSSAMIR, + PostRegisterAllocationMIR, + FinalMIR, +}; + +enum class SnapshotPhase : std::uint8_t { + Stage, + BeforePass, + AfterPass, +}; + +struct PipelineControls final { + std::optional<std::string> StartBefore; + std::optional<std::string> StartAfter; + std::optional<std::string> StopBefore; + std::optional<std::string> StopAfter; + std::vector<std::string> PrintBefore; + std::vector<std::string> PrintAfter; + std::optional<BackendStage> StopAtStage; + bool CaptureStageSnapshots = false; + bool VerifyEach = true; + bool DetectUnreportedChanges = true; + bool SimplifyMIR = false; + bool DebugifyAndStripAllSafe = false; +}; + +struct PipelineSnapshot final { + SnapshotPhase Phase = SnapshotPhase::Stage; + BackendStage Stage = BackendStage::PreLegalizerMIR; + std::string Function; + std::string Pass; + std::uint32_t Occurrence = 0; + std::string MIR; +}; + +struct ExecutedPass final { + std::string Function; + std::string Pass; + std::uint32_t Occurrence = 0; + + friend bool operator==(const ExecutedPass &, const ExecutedPass &) = default; +}; + +struct PipelineRunResult final { + std::vector<ExecutedPass> ExecutedPasses; + std::vector<PipelineSnapshot> Snapshots; +}; + +[[nodiscard]] Expected<PipelineRunResult> +runTargetPipeline(mir::MIRDocument &Document, const TargetMachine &TargetMachine, + const BackendInvocation &Invocation, PipelineControls Controls = {}, + MachinePassInstrumentation *Instrumentation = nullptr); +[[nodiscard]] BackendStage stageForFunction(const MachineFunction &Function) noexcept; +[[nodiscard]] bool isEarlierStage(BackendStage Left, BackendStage Right) noexcept; +[[nodiscard]] std::string_view spelling(BackendStage Stage) noexcept; +[[nodiscard]] Expected<BackendStage> parseBackendStage(std::string_view Value); + +} // namespace tooling +} // namespace rcc + +#endif // RCC_TOOLING_PIPELINEDRIVER_H |
