1 //===- SPIRVTargetMachine.cpp - Define TargetMachine for SPIR-V -*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // Implements the info about SPIR-V target spec.
10 //
11 //===----------------------------------------------------------------------===//
12
13 #include "SPIRVTargetMachine.h"
14 #include "SPIRV.h"
15 #include "SPIRVGlobalRegistry.h"
16 #include "SPIRVLegalizerInfo.h"
17 #include "SPIRVStructurizerWrapper.h"
18 #include "SPIRVTargetObjectFile.h"
19 #include "SPIRVTargetTransformInfo.h"
20 #include "TargetInfo/SPIRVTargetInfo.h"
21 #include "llvm/CodeGen/GlobalISel/IRTranslator.h"
22 #include "llvm/CodeGen/GlobalISel/InstructionSelect.h"
23 #include "llvm/CodeGen/GlobalISel/Legalizer.h"
24 #include "llvm/CodeGen/GlobalISel/RegBankSelect.h"
25 #include "llvm/CodeGen/Passes.h"
26 #include "llvm/CodeGen/TargetPassConfig.h"
27 #include "llvm/InitializePasses.h"
28 #include "llvm/MC/TargetRegistry.h"
29 #include "llvm/Pass.h"
30 #include "llvm/Passes/PassBuilder.h"
31 #include "llvm/Support/Compiler.h"
32 #include "llvm/Target/TargetOptions.h"
33 #include "llvm/Transforms/Scalar.h"
34 #include "llvm/Transforms/Utils.h"
35 #include <optional>
36
37 using namespace llvm;
38
LLVMInitializeSPIRVTarget()39 extern "C" LLVM_ABI LLVM_EXTERNAL_VISIBILITY void LLVMInitializeSPIRVTarget() {
40 // Register the target.
41 RegisterTargetMachine<SPIRVTargetMachine> X(getTheSPIRV32Target());
42 RegisterTargetMachine<SPIRVTargetMachine> Y(getTheSPIRV64Target());
43 RegisterTargetMachine<SPIRVTargetMachine> Z(getTheSPIRVLogicalTarget());
44
45 PassRegistry &PR = *PassRegistry::getPassRegistry();
46 initializeGlobalISel(PR);
47 initializeSPIRVModuleAnalysisPass(PR);
48 initializeSPIRVAsmPrinterPass(PR);
49 initializeSPIRVConvergenceRegionAnalysisWrapperPassPass(PR);
50 initializeSPIRVStructurizerPass(PR);
51 initializeSPIRVPreLegalizerCombinerPass(PR);
52 initializeSPIRVLegalizePointerCastPass(PR);
53 initializeSPIRVRegularizerPass(PR);
54 initializeSPIRVPreLegalizerPass(PR);
55 initializeSPIRVPostLegalizerPass(PR);
56 initializeSPIRVMergeRegionExitTargetsPass(PR);
57 initializeSPIRVEmitIntrinsicsPass(PR);
58 initializeSPIRVEmitNonSemanticDIPass(PR);
59 initializeSPIRVPrepareFunctionsPass(PR);
60 initializeSPIRVStripConvergentIntrinsicsPass(PR);
61 }
62
computeDataLayout(const Triple & TT)63 static std::string computeDataLayout(const Triple &TT) {
64 const auto Arch = TT.getArch();
65 // TODO: this probably needs to be revisited:
66 // Logical SPIR-V has no pointer size, so any fixed pointer size would be
67 // wrong. The choice to default to 32 or 64 is just motivated by another
68 // memory model used for graphics: PhysicalStorageBuffer64. But it shouldn't
69 // mean anything.
70 if (Arch == Triple::spirv32)
71 return "e-p:32:32-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-"
72 "v256:256-v512:512-v1024:1024-n8:16:32:64-G1";
73 if (Arch == Triple::spirv)
74 return "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-"
75 "v512:512-v1024:1024-n8:16:32:64-G10";
76 if (TT.getVendor() == Triple::VendorType::AMD &&
77 TT.getOS() == Triple::OSType::AMDHSA)
78 return "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-"
79 "v512:512-v1024:1024-n32:64-S32-G1-P4-A0";
80 return "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-"
81 "v512:512-v1024:1024-n8:16:32:64-G1";
82 }
83
getEffectiveRelocModel(std::optional<Reloc::Model> RM)84 static Reloc::Model getEffectiveRelocModel(std::optional<Reloc::Model> RM) {
85 if (!RM)
86 return Reloc::PIC_;
87 return *RM;
88 }
89
90 // Pin SPIRVTargetObjectFile's vtables to this file.
~SPIRVTargetObjectFile()91 SPIRVTargetObjectFile::~SPIRVTargetObjectFile() {}
92
SPIRVTargetMachine(const Target & T,const Triple & TT,StringRef CPU,StringRef FS,const TargetOptions & Options,std::optional<Reloc::Model> RM,std::optional<CodeModel::Model> CM,CodeGenOptLevel OL,bool JIT)93 SPIRVTargetMachine::SPIRVTargetMachine(const Target &T, const Triple &TT,
94 StringRef CPU, StringRef FS,
95 const TargetOptions &Options,
96 std::optional<Reloc::Model> RM,
97 std::optional<CodeModel::Model> CM,
98 CodeGenOptLevel OL, bool JIT)
99 : CodeGenTargetMachineImpl(T, computeDataLayout(TT), TT, CPU, FS, Options,
100 getEffectiveRelocModel(RM),
101 getEffectiveCodeModel(CM, CodeModel::Small), OL),
102 TLOF(std::make_unique<SPIRVTargetObjectFile>()),
103 Subtarget(TT, CPU.str(), FS.str(), *this) {
104 initAsmInfo();
105 setGlobalISel(true);
106 setFastISel(false);
107 setO0WantsFastISel(false);
108 setRequiresStructuredCFG(false);
109 }
110
registerPassBuilderCallbacks(PassBuilder & PB)111 void SPIRVTargetMachine::registerPassBuilderCallbacks(PassBuilder &PB) {
112 #define GET_PASS_REGISTRY "SPIRVPassRegistry.def"
113 #include "llvm/Passes/TargetPassRegistry.inc"
114 }
115
116 namespace {
117 // SPIR-V Code Generator Pass Configuration Options.
118 class SPIRVPassConfig : public TargetPassConfig {
119 public:
SPIRVPassConfig(SPIRVTargetMachine & TM,PassManagerBase & PM)120 SPIRVPassConfig(SPIRVTargetMachine &TM, PassManagerBase &PM)
121 : TargetPassConfig(TM, PM), TM(TM) {}
122
getSPIRVTargetMachine() const123 SPIRVTargetMachine &getSPIRVTargetMachine() const {
124 return getTM<SPIRVTargetMachine>();
125 }
126 void addMachineSSAOptimization() override;
127 void addIRPasses() override;
128 void addISelPrepare() override;
129
130 bool addIRTranslator() override;
131 void addPreLegalizeMachineIR() override;
132 bool addLegalizeMachineIR() override;
133 bool addRegBankSelect() override;
134 bool addGlobalInstructionSelect() override;
135
136 FunctionPass *createTargetRegisterAllocator(bool) override;
addFastRegAlloc()137 void addFastRegAlloc() override {}
addOptimizedRegAlloc()138 void addOptimizedRegAlloc() override {}
139
140 void addPostRegAlloc() override;
141 void addPreEmitPass() override;
142
143 private:
144 const SPIRVTargetMachine &TM;
145 };
146 } // namespace
147
148 // We do not use physical registers, and maintain virtual registers throughout
149 // the entire pipeline, so return nullptr to disable register allocation.
createTargetRegisterAllocator(bool)150 FunctionPass *SPIRVPassConfig::createTargetRegisterAllocator(bool) {
151 return nullptr;
152 }
153
154 // A place to disable passes that may break CFG.
addMachineSSAOptimization()155 void SPIRVPassConfig::addMachineSSAOptimization() {
156 TargetPassConfig::addMachineSSAOptimization();
157 }
158
159 // Disable passes that break from assuming no virtual registers exist.
addPostRegAlloc()160 void SPIRVPassConfig::addPostRegAlloc() {
161 // Do not work with vregs instead of physical regs.
162 disablePass(&MachineCopyPropagationID);
163 disablePass(&PostRAMachineSinkingID);
164 disablePass(&PostRASchedulerID);
165 disablePass(&FuncletLayoutID);
166 disablePass(&StackMapLivenessID);
167 disablePass(&PatchableFunctionID);
168 disablePass(&ShrinkWrapID);
169 disablePass(&LiveDebugValuesID);
170 disablePass(&MachineLateInstrsCleanupID);
171 disablePass(&RemoveLoadsIntoFakeUsesID);
172
173 // Do not work with OpPhi.
174 disablePass(&BranchFolderPassID);
175 disablePass(&MachineBlockPlacementID);
176
177 TargetPassConfig::addPostRegAlloc();
178 }
179
180 TargetTransformInfo
getTargetTransformInfo(const Function & F) const181 SPIRVTargetMachine::getTargetTransformInfo(const Function &F) const {
182 return TargetTransformInfo(std::make_unique<SPIRVTTIImpl>(this, F));
183 }
184
createPassConfig(PassManagerBase & PM)185 TargetPassConfig *SPIRVTargetMachine::createPassConfig(PassManagerBase &PM) {
186 return new SPIRVPassConfig(*this, PM);
187 }
188
addIRPasses()189 void SPIRVPassConfig::addIRPasses() {
190 TargetPassConfig::addIRPasses();
191
192 if (TM.getSubtargetImpl()->isShader()) {
193 // Vulkan does not allow address space casts. This pass is run to remove
194 // address space casts that can be removed.
195 // If an address space cast is not removed while targeting Vulkan, lowering
196 // will fail during MIR lowering.
197 addPass(createInferAddressSpacesPass());
198
199 // 1. Simplify loop for subsequent transformations. After this steps, loops
200 // have the following properties:
201 // - loops have a single entry edge (pre-header to loop header).
202 // - all loop exits are dominated by the loop pre-header.
203 // - loops have a single back-edge.
204 addPass(createLoopSimplifyPass());
205
206 // 2. Removes registers whose lifetime spans across basic blocks. Also
207 // removes phi nodes. This will greatly simplify the next steps.
208 addPass(createRegToMemWrapperPass());
209
210 // 3. Merge the convergence region exit nodes into one. After this step,
211 // regions are single-entry, single-exit. This will help determine the
212 // correct merge block.
213 addPass(createSPIRVMergeRegionExitTargetsPass());
214
215 // 4. Structurize.
216 addPass(createSPIRVStructurizerPass());
217
218 // 5. Reduce the amount of variables required by pushing some operations
219 // back to virtual registers.
220 addPass(createPromoteMemoryToRegisterPass());
221 }
222
223 addPass(createSPIRVRegularizerPass());
224 addPass(createSPIRVPrepareFunctionsPass(TM));
225 addPass(createSPIRVStripConvergenceIntrinsicsPass());
226 }
227
addISelPrepare()228 void SPIRVPassConfig::addISelPrepare() {
229 addPass(createSPIRVEmitIntrinsicsPass(&getTM<SPIRVTargetMachine>()));
230 if (TM.getSubtargetImpl()->isLogicalSPIRV())
231 addPass(createSPIRVLegalizePointerCastPass(&getTM<SPIRVTargetMachine>()));
232 TargetPassConfig::addISelPrepare();
233 }
234
addIRTranslator()235 bool SPIRVPassConfig::addIRTranslator() {
236 addPass(new IRTranslator(getOptLevel()));
237 return false;
238 }
239
addPreLegalizeMachineIR()240 void SPIRVPassConfig::addPreLegalizeMachineIR() {
241 addPass(createSPIRVPreLegalizerCombiner());
242 addPass(createSPIRVPreLegalizerPass());
243 }
244
245 // Use the default legalizer.
addLegalizeMachineIR()246 bool SPIRVPassConfig::addLegalizeMachineIR() {
247 addPass(new Legalizer());
248 addPass(createSPIRVPostLegalizerPass());
249 return false;
250 }
251
252 // Do not add the RegBankSelect pass, as we only ever need virtual registers.
addRegBankSelect()253 bool SPIRVPassConfig::addRegBankSelect() {
254 disablePass(&RegBankSelect::ID);
255 return false;
256 }
257
258 static cl::opt<bool> SPVEnableNonSemanticDI(
259 "spv-emit-nonsemantic-debug-info",
260 cl::desc("Emit SPIR-V NonSemantic.Shader.DebugInfo.100 instructions"),
261 cl::Optional, cl::init(false));
262
addPreEmitPass()263 void SPIRVPassConfig::addPreEmitPass() {
264 if (SPVEnableNonSemanticDI) {
265 addPass(createSPIRVEmitNonSemanticDIPass(&getTM<SPIRVTargetMachine>()));
266 }
267 }
268
269 namespace {
270 // A custom subclass of InstructionSelect, which is mostly the same except from
271 // not requiring RegBankSelect to occur previously.
272 class SPIRVInstructionSelect : public InstructionSelect {
273 // We don't use register banks, so unset the requirement for them
getRequiredProperties() const274 MachineFunctionProperties getRequiredProperties() const override {
275 return InstructionSelect::getRequiredProperties().resetRegBankSelected();
276 }
277 };
278 } // namespace
279
280 // Add the custom SPIRVInstructionSelect from above.
addGlobalInstructionSelect()281 bool SPIRVPassConfig::addGlobalInstructionSelect() {
282 addPass(new SPIRVInstructionSelect());
283 return false;
284 }
285