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