aboutsummaryrefslogtreecommitdiff
path: root/backend/include
diff options
context:
space:
mode:
Diffstat (limited to 'backend/include')
-rw-r--r--backend/include/rcc/ADT/APFloat.h78
-rw-r--r--backend/include/rcc/ADT/APInt.h87
-rw-r--r--backend/include/rcc/AsmParser/LLParser.h36
-rw-r--r--backend/include/rcc/CodeGen/AsmPrinter.h92
-rw-r--r--backend/include/rcc/CodeGen/CFIInstrInserter.h28
-rw-r--r--backend/include/rcc/CodeGen/CSEConfigBase.h21
-rw-r--r--backend/include/rcc/CodeGen/CommandFlags.h79
-rw-r--r--backend/include/rcc/CodeGen/CompilationUnit.h74
-rw-r--r--backend/include/rcc/CodeGen/FinalizeISel.h37
-rw-r--r--backend/include/rcc/CodeGen/FrameIndexElimination.h22
-rw-r--r--backend/include/rcc/CodeGen/GenericOpcode.def336
-rw-r--r--backend/include/rcc/CodeGen/GenericOpcodes.h68
-rw-r--r--backend/include/rcc/CodeGen/GlobalISel/CSEInfo.h114
-rw-r--r--backend/include/rcc/CodeGen/GlobalISel/CSEMIRBuilder.h43
-rw-r--r--backend/include/rcc/CodeGen/GlobalISel/Combiner.h50
-rw-r--r--backend/include/rcc/CodeGen/GlobalISel/GIMatchTableExecutor.h149
-rw-r--r--backend/include/rcc/CodeGen/GlobalISel/GISelChangeObserver.h64
-rw-r--r--backend/include/rcc/CodeGen/GlobalISel/GISelValueTracking.h53
-rw-r--r--backend/include/rcc/CodeGen/GlobalISel/GISelWorkList.h75
-rw-r--r--backend/include/rcc/CodeGen/GlobalISel/GenericMachineInstrs.h111
-rw-r--r--backend/include/rcc/CodeGen/GlobalISel/InstructionSelect.h31
-rw-r--r--backend/include/rcc/CodeGen/GlobalISel/InstructionSelector.h45
-rw-r--r--backend/include/rcc/CodeGen/GlobalISel/LegalizationArtifactCombiner.h1730
-rw-r--r--backend/include/rcc/CodeGen/GlobalISel/Legalizer.h50
-rw-r--r--backend/include/rcc/CodeGen/GlobalISel/LegalizerHelper.h158
-rw-r--r--backend/include/rcc/CodeGen/GlobalISel/LegalizerInfo.h213
-rw-r--r--backend/include/rcc/CodeGen/GlobalISel/RegBankSelect.h27
-rw-r--r--backend/include/rcc/CodeGen/GlobalISel/Utils.h58
-rw-r--r--backend/include/rcc/CodeGen/GlobalObject.h205
-rw-r--r--backend/include/rcc/CodeGen/InlineAsm.h310
-rw-r--r--backend/include/rcc/CodeGen/InstructionDescriptor.h350
-rw-r--r--backend/include/rcc/CodeGen/LiveInterval.h144
-rw-r--r--backend/include/rcc/CodeGen/LiveIntervals.h74
-rw-r--r--backend/include/rcc/CodeGen/LivePhysRegs.h23
-rw-r--r--backend/include/rcc/CodeGen/LiveRangeEdit.h32
-rw-r--r--backend/include/rcc/CodeGen/LiveRegMatrix.h73
-rw-r--r--backend/include/rcc/CodeGen/LiveRegUnits.h91
-rw-r--r--backend/include/rcc/CodeGen/LiveVariables.h105
-rw-r--r--backend/include/rcc/CodeGen/LowLevelType.h163
-rw-r--r--backend/include/rcc/CodeGen/MIRParser/MIParser.h109
-rw-r--r--backend/include/rcc/CodeGen/MIRParser/MIRParser.h130
-rw-r--r--backend/include/rcc/CodeGen/MIRPrinter.h65
-rw-r--r--backend/include/rcc/CodeGen/MIRYamlMapping.h144
-rw-r--r--backend/include/rcc/CodeGen/MachineAnalyses.h248
-rw-r--r--backend/include/rcc/CodeGen/MachineBasicBlock.h167
-rw-r--r--backend/include/rcc/CodeGen/MachineBasicBlockAttributes.h208
-rw-r--r--backend/include/rcc/CodeGen/MachineBlockPlacement.h25
-rw-r--r--backend/include/rcc/CodeGen/MachineBranchOptimization.h40
-rw-r--r--backend/include/rcc/CodeGen/MachineBranchRelaxation.h40
-rw-r--r--backend/include/rcc/CodeGen/MachineCFGUpdater.h45
-rw-r--r--backend/include/rcc/CodeGen/MachineCFIInfo.h269
-rw-r--r--backend/include/rcc/CodeGen/MachineCSE.h35
-rw-r--r--backend/include/rcc/CodeGen/MachineCombiner.h40
-rw-r--r--backend/include/rcc/CodeGen/MachineCombinerPattern.h43
-rw-r--r--backend/include/rcc/CodeGen/MachineCompilationStatus.h108
-rw-r--r--backend/include/rcc/CodeGen/MachineConstantPool.h176
-rw-r--r--backend/include/rcc/CodeGen/MachineCopyPropagation.h41
-rw-r--r--backend/include/rcc/CodeGen/MachineDCE.h24
-rw-r--r--backend/include/rcc/CodeGen/MachineDebugInfo.h154
-rw-r--r--backend/include/rcc/CodeGen/MachineFinalization.h36
-rw-r--r--backend/include/rcc/CodeGen/MachineFrameInfo.h265
-rw-r--r--backend/include/rcc/CodeGen/MachineFunction.h251
-rw-r--r--backend/include/rcc/CodeGen/MachineFunctionAnalysisManager.h133
-rw-r--r--backend/include/rcc/CodeGen/MachineFunctionAttributes.h101
-rw-r--r--backend/include/rcc/CodeGen/MachineFunctionCloner.h94
-rw-r--r--backend/include/rcc/CodeGen/MachineFunctionProperties.h134
-rw-r--r--backend/include/rcc/CodeGen/MachineFunctionRecords.h161
-rw-r--r--backend/include/rcc/CodeGen/MachineIRBuilder.h317
-rw-r--r--backend/include/rcc/CodeGen/MachineIRTypes.h354
-rw-r--r--backend/include/rcc/CodeGen/MachineInstr.h253
-rw-r--r--backend/include/rcc/CodeGen/MachineJumpTableInfo.h130
-rw-r--r--backend/include/rcc/CodeGen/MachineLICM.h45
-rw-r--r--backend/include/rcc/CodeGen/MachineMCContext.h68
-rw-r--r--backend/include/rcc/CodeGen/MachineMemOperand.h262
-rw-r--r--backend/include/rcc/CodeGen/MachineMemoryFolding.h39
-rw-r--r--backend/include/rcc/CodeGen/MachineMetadata.h144
-rw-r--r--backend/include/rcc/CodeGen/MachineOperand.h463
-rw-r--r--backend/include/rcc/CodeGen/MachineOptimizationPipeline.h53
-rw-r--r--backend/include/rcc/CodeGen/MachineOutliner.h97
-rw-r--r--backend/include/rcc/CodeGen/MachinePassInstrumentation.h153
-rw-r--r--backend/include/rcc/CodeGen/MachinePassManager.h134
-rw-r--r--backend/include/rcc/CodeGen/MachinePeepholeOptimizer.h30
-rw-r--r--backend/include/rcc/CodeGen/MachinePseudoExpansion.h72
-rw-r--r--backend/include/rcc/CodeGen/MachineRegisterInfo.h184
-rw-r--r--backend/include/rcc/CodeGen/MachineScheduler.h38
-rw-r--r--backend/include/rcc/CodeGen/MachineShrinkWrapping.h36
-rw-r--r--backend/include/rcc/CodeGen/MachineSink.h38
-rw-r--r--backend/include/rcc/CodeGen/MachineStackSlotColoring.h34
-rw-r--r--backend/include/rcc/CodeGen/MachineStripDebug.h16
-rw-r--r--backend/include/rcc/CodeGen/MachineTailDuplication.h40
-rw-r--r--backend/include/rcc/CodeGen/MachineVerifier.h36
-rw-r--r--backend/include/rcc/CodeGen/MacroFusion.h77
-rw-r--r--backend/include/rcc/CodeGen/PBQP/Graph.h194
-rw-r--r--backend/include/rcc/CodeGen/PBQP/Math.h114
-rw-r--r--backend/include/rcc/CodeGen/PBQP/ReductionRules.h32
-rw-r--r--backend/include/rcc/CodeGen/PBQP/Solution.h85
-rw-r--r--backend/include/rcc/CodeGen/PBQP/Solver.h381
-rw-r--r--backend/include/rcc/CodeGen/PHIElimination.h31
-rw-r--r--backend/include/rcc/CodeGen/PostRAScheduler.h31
-rw-r--r--backend/include/rcc/CodeGen/PrologueEpilogueInsertion.h33
-rw-r--r--backend/include/rcc/CodeGen/ReachingDefAnalysis.h52
-rw-r--r--backend/include/rcc/CodeGen/RegAllocCommon.h28
-rw-r--r--backend/include/rcc/CodeGen/RegAllocFast.h26
-rw-r--r--backend/include/rcc/CodeGen/RegAllocPBQP.h29
-rw-r--r--backend/include/rcc/CodeGen/Register.h103
-rw-r--r--backend/include/rcc/CodeGen/RegisterBankInfo.h51
-rw-r--r--backend/include/rcc/CodeGen/RegisterMask.h39
-rw-r--r--backend/include/rcc/CodeGen/RegisterPressure.h107
-rw-r--r--backend/include/rcc/CodeGen/RegisterScavenger.h42
-rw-r--r--backend/include/rcc/CodeGen/ScheduleDAG.h117
-rw-r--r--backend/include/rcc/CodeGen/ScheduleHazardRecognizer.h68
-rw-r--r--backend/include/rcc/CodeGen/SlotIndexes.h131
-rw-r--r--backend/include/rcc/CodeGen/Spiller.h45
-rw-r--r--backend/include/rcc/CodeGen/StableID.h92
-rw-r--r--backend/include/rcc/CodeGen/TargetCallLowering.h412
-rw-r--r--backend/include/rcc/CodeGen/TargetConstant.h259
-rw-r--r--backend/include/rcc/CodeGen/TargetDescription.h151
-rw-r--r--backend/include/rcc/CodeGen/TargetFrameLowering.h90
-rw-r--r--backend/include/rcc/CodeGen/TargetFunctionState.h210
-rw-r--r--backend/include/rcc/CodeGen/TargetInstrInfo.h107
-rw-r--r--backend/include/rcc/CodeGen/TargetLoweringObjectFile.h34
-rw-r--r--backend/include/rcc/CodeGen/TargetMCInstLowering.h36
-rw-r--r--backend/include/rcc/CodeGen/TargetMIRTextInfo.h266
-rw-r--r--backend/include/rcc/CodeGen/TargetMemoryFolding.h43
-rw-r--r--backend/include/rcc/CodeGen/TargetPseudoExpansion.h40
-rw-r--r--backend/include/rcc/CodeGen/TargetRegisterAllocationInfo.h63
-rw-r--r--backend/include/rcc/CodeGen/TargetRegisterInfo.h215
-rw-r--r--backend/include/rcc/CodeGen/TargetSchedulingModel.h179
-rw-r--r--backend/include/rcc/CodeGen/TwoAddressInstructionPass.h31
-rw-r--r--backend/include/rcc/CodeGen/VirtRegMap.h101
-rw-r--r--backend/include/rcc/Config/Targets.def7
-rw-r--r--backend/include/rcc/IR/Constants.h177
-rw-r--r--backend/include/rcc/IR/DataLayout.h82
-rw-r--r--backend/include/rcc/IR/PassInstrumentation.h121
-rw-r--r--backend/include/rcc/IR/PassManager.h532
-rw-r--r--backend/include/rcc/MC/ELFObjectWriter.h45
-rw-r--r--backend/include/rcc/MC/MCAsmBackend.h55
-rw-r--r--backend/include/rcc/MC/MCAssembler.h131
-rw-r--r--backend/include/rcc/MC/MCCFI.h54
-rw-r--r--backend/include/rcc/MC/MCCodeEmitter.h30
-rw-r--r--backend/include/rcc/MC/MCContext.h43
-rw-r--r--backend/include/rcc/MC/MCDwarfFrameEmitter.h44
-rw-r--r--backend/include/rcc/MC/MCExpr.h109
-rw-r--r--backend/include/rcc/MC/MCFixup.h52
-rw-r--r--backend/include/rcc/MC/MCInst.h174
-rw-r--r--backend/include/rcc/MC/MCInstPrinter.h31
-rw-r--r--backend/include/rcc/MC/MCObjectStreamer.h59
-rw-r--r--backend/include/rcc/MC/MCObjectWriter.h79
-rw-r--r--backend/include/rcc/MC/MCSection.h108
-rw-r--r--backend/include/rcc/MC/MCStreamer.h97
-rw-r--r--backend/include/rcc/MC/MCSymbol.h56
-rw-r--r--backend/include/rcc/MC/TargetRegistry.h89
-rw-r--r--backend/include/rcc/MIR/EmbeddedIR.h248
-rw-r--r--backend/include/rcc/MIR/ReachingDefPrinter.h26
-rw-r--r--backend/include/rcc/Support/Error.h69
-rw-r--r--backend/include/rcc/Support/SHA256.h24
-rw-r--r--backend/include/rcc/Support/TargetSelect.h18
-rw-r--r--backend/include/rcc/Support/TextDiagnostic.h83
-rw-r--r--backend/include/rcc/Support/YAML.h197
-rw-r--r--backend/include/rcc/TableGen/Parser.h24
-rw-r--r--backend/include/rcc/TableGen/Record.h470
-rw-r--r--backend/include/rcc/Target/RISCV/RISCVAsmBackend.h30
-rw-r--r--backend/include/rcc/Target/RISCV/RISCVAssemblyPrinter.h35
-rw-r--r--backend/include/rcc/Target/RISCV/RISCVFixupKinds.h33
-rw-r--r--backend/include/rcc/Target/RISCV/RISCVFunctionInfo.h60
-rw-r--r--backend/include/rcc/Target/RISCV/RISCVInstPrinter.h39
-rw-r--r--backend/include/rcc/Target/RISCV/RISCVMCCodeEmitter.h24
-rw-r--r--backend/include/rcc/Target/RISCV/RISCVMCTargetLowering.h35
-rw-r--r--backend/include/rcc/Target/RISCV/RISCVObjectFile.h35
-rw-r--r--backend/include/rcc/Target/RISCV/RISCVPipeline.h38
-rw-r--r--backend/include/rcc/Target/RISCV/RISCVSubtarget.h156
-rw-r--r--backend/include/rcc/Target/RISCV/RISCVTarget.h76
-rw-r--r--backend/include/rcc/Target/RISCV/RISCVTargetConstant.h73
-rw-r--r--backend/include/rcc/Target/RISCV/RISCVTargetRelocationKinds.h24
-rw-r--r--backend/include/rcc/Target/TargetMachine.h69
-rw-r--r--backend/include/rcc/Target/TargetOptions.h49
-rw-r--r--backend/include/rcc/Tooling/ArtifactStore.h76
-rw-r--r--backend/include/rcc/Tooling/BackendDriver.h33
-rw-r--r--backend/include/rcc/Tooling/BackendInvocation.h94
-rw-r--r--backend/include/rcc/Tooling/BackendManifest.h75
-rw-r--r--backend/include/rcc/Tooling/MIRFuzzer.h66
-rw-r--r--backend/include/rcc/Tooling/MIRReducer.h81
-rw-r--r--backend/include/rcc/Tooling/PipelineDriver.h86
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 &registerInfo() noexcept {
+ return RegInfo;
+ }
+
+ [[nodiscard]] const MachineRegisterInfo &registerInfo() 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 &registers() 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