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 "Analysis/SPIRVConvergenceRegionAnalysis.h"
15#include "SPIRV.h"
16#include "SPIRVGlobalRegistry.h"
17#include "SPIRVLegalizerInfo.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/CodeGen.h"
32#include "llvm/Support/Compiler.h"
33#include "llvm/Target/TargetOptions.h"
34#include "llvm/Transforms/IPO/ExpandVariadics.h"
35#include "llvm/Transforms/Scalar.h"
36#include "llvm/Transforms/Utils.h"
37#include <optional>
38
39using namespace llvm;
40
41extern "C" LLVM_ABI LLVM_EXTERNAL_VISIBILITY void LLVMInitializeSPIRVTarget() {
42 // Register the target.
43 RegisterTargetMachine<SPIRVTargetMachine> X(getTheSPIRV32Target());
44 RegisterTargetMachine<SPIRVTargetMachine> Y(getTheSPIRV64Target());
45 RegisterTargetMachine<SPIRVTargetMachine> Z(getTheSPIRVLogicalTarget());
46
47 PassRegistry &PR = *PassRegistry::getPassRegistry();
48 initializeGlobalISel(PR);
49 initializeSPIRVModuleAnalysisPass(PR);
50 initializeSPIRVAsmPrinterPass(PR);
51 initializeSPIRVConvergenceRegionAnalysisWrapperPassPass(PR);
52 initializeSPIRVStructurizerPass(PR);
53 initializeSPIRVCBufferAccessLegacyPass(PR);
54 initializeSPIRVPushConstantAccessLegacyPass(PR);
55 initializeSPIRVPreLegalizerCombinerLegacyPass(PR);
56 initializeSPIRVLegalizePointerCastLegacyPass(PR);
57 initializeSPIRVLegalizeZeroSizeArraysLegacyPass(PR);
58 initializeSPIRVRegularizerLegacyPass(PR);
59 initializeSPIRVPreLegalizerLegacyPass(PR);
60 initializeSPIRVPostLegalizerLegacyPass(PR);
61 initializeSPIRVMergeRegionExitTargetsLegacyPass(PR);
62 initializeSPIRVEmitIntrinsicsLegacyPass(PR);
63 initializeSPIRVPrepareFunctionsLegacyPass(PR);
64 initializeSPIRVPrepareGlobalsLegacyPass(PR);
65 initializeSPIRVLegalizeImplicitBindingLegacyPass(PR);
66 initializeSPIRVCtorDtorLoweringLegacyPass(PR);
67 initializeSPIRVFinalizeShaderLinkageLegacyPass(PR);
68}
69
70static Reloc::Model getEffectiveRelocModel(std::optional<Reloc::Model> RM) {
71 if (!RM)
72 return Reloc::PIC_;
73 return *RM;
74}
75
76// Pin SPIRVTargetObjectFile's vtables to this file.
77SPIRVTargetObjectFile::~SPIRVTargetObjectFile() = default;
78
79SPIRVTargetMachine::SPIRVTargetMachine(const Target &T, const Triple &TT,
80 StringRef CPU, StringRef FS,
81 const TargetOptions &Options,
82 std::optional<Reloc::Model> RM,
83 std::optional<CodeModel::Model> CM,
84 CodeGenOptLevel OL, bool JIT)
85 : CodeGenTargetMachineImpl(T, TT.computeDataLayout(), TT, CPU, FS, Options,
86 getEffectiveRelocModel(RM),
87 getEffectiveCodeModel(CM, Default: CodeModel::Small), OL),
88 TLOF(std::make_unique<SPIRVTargetObjectFile>()),
89 Subtarget(TT, CPU.str(), FS.str(), *this) {
90 initAsmInfo();
91 setGlobalISel(true);
92 setFastISel(false);
93 setO0WantsFastISel(false);
94 setRequiresStructuredCFG(false);
95}
96
97namespace {
98// SPIR-V Code Generator Pass Configuration Options.
99class SPIRVPassConfig : public TargetPassConfig {
100public:
101 SPIRVPassConfig(SPIRVTargetMachine &TM, PassManagerBase &PM)
102 : TargetPassConfig(TM, PM), TM(TM) {}
103
104 SPIRVTargetMachine &getSPIRVTargetMachine() const {
105 return getTM<SPIRVTargetMachine>();
106 }
107 void addMachineSSAOptimization() override;
108 void addIRPasses() override;
109 void addISelPrepare() override;
110
111 bool addIRTranslator() override;
112 void addPreLegalizeMachineIR() override;
113 bool addLegalizeMachineIR() override;
114 bool addRegBankSelect() override;
115 bool addGlobalInstructionSelect() override;
116
117 FunctionPass *createTargetRegisterAllocator(bool) override;
118 void addFastRegAlloc() override {}
119 void addOptimizedRegAlloc() override {}
120
121 void addPostRegAlloc() override;
122
123private:
124 const SPIRVTargetMachine &TM;
125};
126} // namespace
127
128// We do not use physical registers, and maintain virtual registers throughout
129// the entire pipeline, so return nullptr to disable register allocation.
130FunctionPass *SPIRVPassConfig::createTargetRegisterAllocator(bool) {
131 return nullptr;
132}
133
134// A place to disable passes that may break CFG.
135void SPIRVPassConfig::addMachineSSAOptimization() {
136 TargetPassConfig::addMachineSSAOptimization();
137}
138
139// Disable passes that break from assuming no virtual registers exist.
140void SPIRVPassConfig::addPostRegAlloc() {
141 // Do not work with vregs instead of physical regs.
142 disablePass(PassID: &MachineCopyPropagationID);
143 disablePass(PassID: &PostRAMachineSinkingID);
144 disablePass(PassID: &PostRASchedulerID);
145 disablePass(PassID: &FuncletLayoutID);
146 disablePass(PassID: &StackMapLivenessID);
147 disablePass(PassID: &PatchableFunctionID);
148 disablePass(PassID: &ShrinkWrapID);
149 disablePass(PassID: &LiveDebugValuesID);
150 disablePass(PassID: &MachineLateInstrsCleanupID);
151 disablePass(PassID: &RemoveLoadsIntoFakeUsesID);
152
153 // Do not work with OpPhi.
154 disablePass(PassID: &BranchFolderPassID);
155 disablePass(PassID: &MachineBlockPlacementID);
156
157 TargetPassConfig::addPostRegAlloc();
158}
159
160TargetTransformInfo
161SPIRVTargetMachine::getTargetTransformInfo(const Function &F) const {
162 return TargetTransformInfo(std::make_unique<SPIRVTTIImpl>(args: this, args: F));
163}
164
165TargetPassConfig *SPIRVTargetMachine::createPassConfig(PassManagerBase &PM) {
166 return new SPIRVPassConfig(*this, PM);
167}
168
169void SPIRVPassConfig::addIRPasses() {
170 addPass(P: createAtomicExpandLegacyPass());
171
172 TargetPassConfig::addIRPasses();
173
174 if (TM.getSubtargetImpl()->isShader()) {
175 if (getOptLevel() != CodeGenOptLevel::None)
176 addPass(P: createSPIRVFinalizeShaderLinkagePass(TM));
177 } else {
178 // Variadic function calls aren't supported in shader code.
179 // This needs to come before SPIRVPrepareFunctions because this
180 // may introduce intrinsic calls.
181 addPass(P: createExpandVariadicsPass(ExpandVariadicsMode::Lowering));
182 }
183
184 addPass(P: createSPIRVRegularizerPass());
185 addPass(P: createSPIRVCtorDtorLoweringLegacyPass());
186 addPass(P: createSPIRVPrepareFunctionsPass(TM));
187 addPass(P: createSPIRVPrepareGlobalsPass());
188}
189
190void SPIRVPassConfig::addISelPrepare() {
191 if (TM.getSubtargetImpl()->isShader()) {
192 // Vulkan does not allow address space casts. This pass is run to remove
193 // address space casts that can be removed.
194 // If an address space cast is not removed while targeting Vulkan, lowering
195 // will fail during MIR lowering.
196 addPass(P: createInferAddressSpacesPass());
197
198 // 1. Simplify loop for subsequent transformations. After this steps, loops
199 // have the following properties:
200 // - loops have a single entry edge (pre-header to loop header).
201 // - all loop exits are dominated by the loop pre-header.
202 // - loops have a single back-edge.
203 addPass(P: createLoopSimplifyPass());
204
205 // 2. Removes registers whose lifetime spans across basic blocks. Also
206 // removes phi nodes. This will greatly simplify the next steps.
207 addPass(P: createRegToMemWrapperPass());
208
209 // 3. Merge the convergence region exit nodes into one. After this step,
210 // regions are single-entry, single-exit. This will help determine the
211 // correct merge block.
212 addPass(P: createSPIRVMergeRegionExitTargetsPass());
213
214 // 4. Structurize.
215 addPass(P: createSPIRVStructurizerPass());
216
217 // 5. Reduce the amount of variables required by pushing some operations
218 // back to virtual registers.
219 addPass(P: createPromoteMemoryToRegisterPass());
220 } else {
221 // Canonicalize loops so they have a single latch and preheader.
222 // This enables OpLoopMerge emission for non-shader targets.
223 addPass(P: createLoopSimplifyPass());
224 }
225 SPIRVTargetMachine &TM = getTM<SPIRVTargetMachine>();
226 addPass(P: createStripConvergenceIntrinsicsPass());
227 addPass(P: createSPIRVLegalizeImplicitBindingPass());
228 addPass(P: createSPIRVLegalizeZeroSizeArraysPass(TM));
229 addPass(P: createSPIRVCBufferAccessLegacyPass());
230 addPass(P: createSPIRVPushConstantAccessLegacyPass(TM: &TM));
231 addPass(P: createSPIRVEmitIntrinsicsPass(TM));
232 if (TM.getSubtargetImpl()->isLogicalSPIRV())
233 addPass(P: createSPIRVLegalizePointerCastPass(TM: &TM));
234 TargetPassConfig::addISelPrepare();
235}
236
237bool SPIRVPassConfig::addIRTranslator() {
238 addPass(P: new IRTranslatorLegacy(getOptLevel()));
239 return false;
240}
241
242void SPIRVPassConfig::addPreLegalizeMachineIR() {
243 addPass(P: createSPIRVPreLegalizerCombinerLegacyPass());
244 addPass(P: createSPIRVPreLegalizerLegacyPass());
245}
246
247// Use the default legalizer.
248bool SPIRVPassConfig::addLegalizeMachineIR() {
249 addPass(P: new LegalizerLegacy());
250 addPass(P: createSPIRVPostLegalizerLegacyPass());
251 return false;
252}
253
254// Do not add the RegBankSelect pass, as we only ever need virtual registers.
255bool SPIRVPassConfig::addRegBankSelect() {
256 disablePass(PassID: &RegBankSelectLegacy::ID);
257 return false;
258}
259
260// Deprecated flag kept for backward compatibility. NSDI emission is now handled
261// by SPIRVNonSemanticDebugHandler, registered in SPIRVAsmPrinter::
262// doInitialization() when the module contains debug info (llvm.dbg.cu).
263// TODO: Remove this option after a deprecation period. Callers that used
264// -spv-emit-nonsemantic-debug-info should switch to -g.
265static cl::opt<bool> SPVEnableNonSemanticDI(
266 "spv-emit-nonsemantic-debug-info",
267 cl::desc("Deprecated. Use -g to emit SPIR-V NonSemantic.Shader.DebugInfo "
268 "instructions"),
269 cl::Optional, cl::init(Val: false));
270
271// Add the custom SPIRVInstructionSelect from above.
272bool SPIRVPassConfig::addGlobalInstructionSelect() {
273 addPass(P: new InstructionSelectLegacy(getOptLevel(),
274 /*RequireRegBankSelection=*/false));
275 return false;
276}
277