181ad6265SDimitry Andric //===- SPIRVTargetMachine.cpp - Define TargetMachine for SPIR-V -*- C++ -*-===// 281ad6265SDimitry Andric // 381ad6265SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 481ad6265SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 581ad6265SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 681ad6265SDimitry Andric // 781ad6265SDimitry Andric //===----------------------------------------------------------------------===// 881ad6265SDimitry Andric // 981ad6265SDimitry Andric // Implements the info about SPIR-V target spec. 1081ad6265SDimitry Andric // 1181ad6265SDimitry Andric //===----------------------------------------------------------------------===// 1281ad6265SDimitry Andric 1381ad6265SDimitry Andric #include "SPIRVTargetMachine.h" 1481ad6265SDimitry Andric #include "SPIRV.h" 1581ad6265SDimitry Andric #include "SPIRVCallLowering.h" 1681ad6265SDimitry Andric #include "SPIRVGlobalRegistry.h" 1781ad6265SDimitry Andric #include "SPIRVLegalizerInfo.h" 1881ad6265SDimitry Andric #include "SPIRVTargetObjectFile.h" 1981ad6265SDimitry Andric #include "SPIRVTargetTransformInfo.h" 2081ad6265SDimitry Andric #include "TargetInfo/SPIRVTargetInfo.h" 2181ad6265SDimitry Andric #include "llvm/CodeGen/GlobalISel/IRTranslator.h" 2281ad6265SDimitry Andric #include "llvm/CodeGen/GlobalISel/InstructionSelect.h" 2381ad6265SDimitry Andric #include "llvm/CodeGen/GlobalISel/Legalizer.h" 2481ad6265SDimitry Andric #include "llvm/CodeGen/GlobalISel/RegBankSelect.h" 2581ad6265SDimitry Andric #include "llvm/CodeGen/Passes.h" 2681ad6265SDimitry Andric #include "llvm/CodeGen/TargetLoweringObjectFileImpl.h" 2781ad6265SDimitry Andric #include "llvm/CodeGen/TargetPassConfig.h" 2881ad6265SDimitry Andric #include "llvm/InitializePasses.h" 2981ad6265SDimitry Andric #include "llvm/MC/TargetRegistry.h" 3081ad6265SDimitry Andric #include "llvm/Pass.h" 3181ad6265SDimitry Andric #include "llvm/Target/TargetOptions.h" 321db9f3b2SDimitry Andric #include "llvm/Transforms/Utils.h" 33bdd1243dSDimitry Andric #include <optional> 3481ad6265SDimitry Andric 3581ad6265SDimitry Andric using namespace llvm; 3681ad6265SDimitry Andric 3781ad6265SDimitry Andric extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeSPIRVTarget() { 3881ad6265SDimitry Andric // Register the target. 3981ad6265SDimitry Andric RegisterTargetMachine<SPIRVTargetMachine> X(getTheSPIRV32Target()); 4081ad6265SDimitry Andric RegisterTargetMachine<SPIRVTargetMachine> Y(getTheSPIRV64Target()); 415f757f3fSDimitry Andric RegisterTargetMachine<SPIRVTargetMachine> Z(getTheSPIRVLogicalTarget()); 4281ad6265SDimitry Andric 4381ad6265SDimitry Andric PassRegistry &PR = *PassRegistry::getPassRegistry(); 4481ad6265SDimitry Andric initializeGlobalISel(PR); 4581ad6265SDimitry Andric initializeSPIRVModuleAnalysisPass(PR); 46*0fca6ea1SDimitry Andric initializeSPIRVConvergenceRegionAnalysisWrapperPassPass(PR); 4781ad6265SDimitry Andric } 4881ad6265SDimitry Andric 4981ad6265SDimitry Andric static std::string computeDataLayout(const Triple &TT) { 5081ad6265SDimitry Andric const auto Arch = TT.getArch(); 515f757f3fSDimitry Andric // TODO: this probably needs to be revisited: 525f757f3fSDimitry Andric // Logical SPIR-V has no pointer size, so any fixed pointer size would be 535f757f3fSDimitry Andric // wrong. The choice to default to 32 or 64 is just motivated by another 545f757f3fSDimitry Andric // memory model used for graphics: PhysicalStorageBuffer64. But it shouldn't 555f757f3fSDimitry Andric // mean anything. 5681ad6265SDimitry Andric if (Arch == Triple::spirv32) 5781ad6265SDimitry Andric return "e-p:32:32-i64:64-v16:16-v24:32-v32:32-v48:64-" 58*0fca6ea1SDimitry Andric "v96:128-v192:256-v256:256-v512:512-v1024:1024-G1"; 59*0fca6ea1SDimitry Andric if (TT.getVendor() == Triple::VendorType::AMD && 60*0fca6ea1SDimitry Andric TT.getOS() == Triple::OSType::AMDHSA) 6181ad6265SDimitry Andric return "e-i64:64-v16:16-v24:32-v32:32-v48:64-" 62*0fca6ea1SDimitry Andric "v96:128-v192:256-v256:256-v512:512-v1024:1024-G1-P4-A0"; 63*0fca6ea1SDimitry Andric return "e-i64:64-v16:16-v24:32-v32:32-v48:64-" 64*0fca6ea1SDimitry Andric "v96:128-v192:256-v256:256-v512:512-v1024:1024-G1"; 6581ad6265SDimitry Andric } 6681ad6265SDimitry Andric 67bdd1243dSDimitry Andric static Reloc::Model getEffectiveRelocModel(std::optional<Reloc::Model> RM) { 6881ad6265SDimitry Andric if (!RM) 6981ad6265SDimitry Andric return Reloc::PIC_; 7081ad6265SDimitry Andric return *RM; 7181ad6265SDimitry Andric } 7281ad6265SDimitry Andric 7381ad6265SDimitry Andric // Pin SPIRVTargetObjectFile's vtables to this file. 7481ad6265SDimitry Andric SPIRVTargetObjectFile::~SPIRVTargetObjectFile() {} 7581ad6265SDimitry Andric 7681ad6265SDimitry Andric SPIRVTargetMachine::SPIRVTargetMachine(const Target &T, const Triple &TT, 7781ad6265SDimitry Andric StringRef CPU, StringRef FS, 7881ad6265SDimitry Andric const TargetOptions &Options, 79bdd1243dSDimitry Andric std::optional<Reloc::Model> RM, 80bdd1243dSDimitry Andric std::optional<CodeModel::Model> CM, 815f757f3fSDimitry Andric CodeGenOptLevel OL, bool JIT) 8281ad6265SDimitry Andric : LLVMTargetMachine(T, computeDataLayout(TT), TT, CPU, FS, Options, 8381ad6265SDimitry Andric getEffectiveRelocModel(RM), 8481ad6265SDimitry Andric getEffectiveCodeModel(CM, CodeModel::Small), OL), 85bdd1243dSDimitry Andric TLOF(std::make_unique<SPIRVTargetObjectFile>()), 8681ad6265SDimitry Andric Subtarget(TT, CPU.str(), FS.str(), *this) { 8781ad6265SDimitry Andric initAsmInfo(); 8881ad6265SDimitry Andric setGlobalISel(true); 8981ad6265SDimitry Andric setFastISel(false); 9081ad6265SDimitry Andric setO0WantsFastISel(false); 9181ad6265SDimitry Andric setRequiresStructuredCFG(false); 9281ad6265SDimitry Andric } 9381ad6265SDimitry Andric 9481ad6265SDimitry Andric namespace { 9581ad6265SDimitry Andric // SPIR-V Code Generator Pass Configuration Options. 9681ad6265SDimitry Andric class SPIRVPassConfig : public TargetPassConfig { 9781ad6265SDimitry Andric public: 9881ad6265SDimitry Andric SPIRVPassConfig(SPIRVTargetMachine &TM, PassManagerBase &PM) 995f757f3fSDimitry Andric : TargetPassConfig(TM, PM), TM(TM) {} 10081ad6265SDimitry Andric 10181ad6265SDimitry Andric SPIRVTargetMachine &getSPIRVTargetMachine() const { 10281ad6265SDimitry Andric return getTM<SPIRVTargetMachine>(); 10381ad6265SDimitry Andric } 10481ad6265SDimitry Andric void addIRPasses() override; 10581ad6265SDimitry Andric void addISelPrepare() override; 10681ad6265SDimitry Andric 10781ad6265SDimitry Andric bool addIRTranslator() override; 10881ad6265SDimitry Andric void addPreLegalizeMachineIR() override; 10981ad6265SDimitry Andric bool addLegalizeMachineIR() override; 11081ad6265SDimitry Andric bool addRegBankSelect() override; 11181ad6265SDimitry Andric bool addGlobalInstructionSelect() override; 11281ad6265SDimitry Andric 11381ad6265SDimitry Andric FunctionPass *createTargetRegisterAllocator(bool) override; 11481ad6265SDimitry Andric void addFastRegAlloc() override {} 11581ad6265SDimitry Andric void addOptimizedRegAlloc() override {} 11681ad6265SDimitry Andric 11781ad6265SDimitry Andric void addPostRegAlloc() override; 1185f757f3fSDimitry Andric 1195f757f3fSDimitry Andric private: 1205f757f3fSDimitry Andric const SPIRVTargetMachine &TM; 12181ad6265SDimitry Andric }; 12281ad6265SDimitry Andric } // namespace 12381ad6265SDimitry Andric 12481ad6265SDimitry Andric // We do not use physical registers, and maintain virtual registers throughout 12581ad6265SDimitry Andric // the entire pipeline, so return nullptr to disable register allocation. 12681ad6265SDimitry Andric FunctionPass *SPIRVPassConfig::createTargetRegisterAllocator(bool) { 12781ad6265SDimitry Andric return nullptr; 12881ad6265SDimitry Andric } 12981ad6265SDimitry Andric 13081ad6265SDimitry Andric // Disable passes that break from assuming no virtual registers exist. 13181ad6265SDimitry Andric void SPIRVPassConfig::addPostRegAlloc() { 13281ad6265SDimitry Andric // Do not work with vregs instead of physical regs. 13381ad6265SDimitry Andric disablePass(&MachineCopyPropagationID); 13481ad6265SDimitry Andric disablePass(&PostRAMachineSinkingID); 13581ad6265SDimitry Andric disablePass(&PostRASchedulerID); 13681ad6265SDimitry Andric disablePass(&FuncletLayoutID); 13781ad6265SDimitry Andric disablePass(&StackMapLivenessID); 13881ad6265SDimitry Andric disablePass(&PatchableFunctionID); 13981ad6265SDimitry Andric disablePass(&ShrinkWrapID); 14081ad6265SDimitry Andric disablePass(&LiveDebugValuesID); 141bdd1243dSDimitry Andric disablePass(&MachineLateInstrsCleanupID); 14281ad6265SDimitry Andric 14381ad6265SDimitry Andric // Do not work with OpPhi. 14481ad6265SDimitry Andric disablePass(&BranchFolderPassID); 14581ad6265SDimitry Andric disablePass(&MachineBlockPlacementID); 14681ad6265SDimitry Andric 14781ad6265SDimitry Andric TargetPassConfig::addPostRegAlloc(); 14881ad6265SDimitry Andric } 14981ad6265SDimitry Andric 15081ad6265SDimitry Andric TargetTransformInfo 15181ad6265SDimitry Andric SPIRVTargetMachine::getTargetTransformInfo(const Function &F) const { 15281ad6265SDimitry Andric return TargetTransformInfo(SPIRVTTIImpl(this, F)); 15381ad6265SDimitry Andric } 15481ad6265SDimitry Andric 15581ad6265SDimitry Andric TargetPassConfig *SPIRVTargetMachine::createPassConfig(PassManagerBase &PM) { 15681ad6265SDimitry Andric return new SPIRVPassConfig(*this, PM); 15781ad6265SDimitry Andric } 15881ad6265SDimitry Andric 159fcaf7f86SDimitry Andric void SPIRVPassConfig::addIRPasses() { 1601db9f3b2SDimitry Andric if (TM.getSubtargetImpl()->isVulkanEnv()) { 1611db9f3b2SDimitry Andric // Once legalized, we need to structurize the CFG to follow the spec. 1621db9f3b2SDimitry Andric // This is done through the following 8 steps. 1631db9f3b2SDimitry Andric // TODO(#75801): add the remaining steps. 1641db9f3b2SDimitry Andric 1651db9f3b2SDimitry Andric // 1. Simplify loop for subsequent transformations. After this steps, loops 1661db9f3b2SDimitry Andric // have the following properties: 1671db9f3b2SDimitry Andric // - loops have a single entry edge (pre-header to loop header). 1681db9f3b2SDimitry Andric // - all loop exits are dominated by the loop pre-header. 1691db9f3b2SDimitry Andric // - loops have a single back-edge. 1701db9f3b2SDimitry Andric addPass(createLoopSimplifyPass()); 171*0fca6ea1SDimitry Andric 172*0fca6ea1SDimitry Andric // 2. Merge the convergence region exit nodes into one. After this step, 173*0fca6ea1SDimitry Andric // regions are single-entry, single-exit. This will help determine the 174*0fca6ea1SDimitry Andric // correct merge block. 175*0fca6ea1SDimitry Andric addPass(createSPIRVMergeRegionExitTargetsPass()); 1761db9f3b2SDimitry Andric } 1771db9f3b2SDimitry Andric 178fcaf7f86SDimitry Andric TargetPassConfig::addIRPasses(); 179bdd1243dSDimitry Andric addPass(createSPIRVRegularizerPass()); 1805f757f3fSDimitry Andric addPass(createSPIRVPrepareFunctionsPass(TM)); 1817a6dacacSDimitry Andric addPass(createSPIRVStripConvergenceIntrinsicsPass()); 182fcaf7f86SDimitry Andric } 18381ad6265SDimitry Andric 18481ad6265SDimitry Andric void SPIRVPassConfig::addISelPrepare() { 18581ad6265SDimitry Andric addPass(createSPIRVEmitIntrinsicsPass(&getTM<SPIRVTargetMachine>())); 18681ad6265SDimitry Andric TargetPassConfig::addISelPrepare(); 18781ad6265SDimitry Andric } 18881ad6265SDimitry Andric 18981ad6265SDimitry Andric bool SPIRVPassConfig::addIRTranslator() { 19081ad6265SDimitry Andric addPass(new IRTranslator(getOptLevel())); 19181ad6265SDimitry Andric return false; 19281ad6265SDimitry Andric } 19381ad6265SDimitry Andric 19481ad6265SDimitry Andric void SPIRVPassConfig::addPreLegalizeMachineIR() { 19581ad6265SDimitry Andric addPass(createSPIRVPreLegalizerPass()); 19681ad6265SDimitry Andric } 19781ad6265SDimitry Andric 198bdd1243dSDimitry Andric // Use the default legalizer. 19981ad6265SDimitry Andric bool SPIRVPassConfig::addLegalizeMachineIR() { 20081ad6265SDimitry Andric addPass(new Legalizer()); 201*0fca6ea1SDimitry Andric addPass(createSPIRVPostLegalizerPass()); 20281ad6265SDimitry Andric return false; 20381ad6265SDimitry Andric } 20481ad6265SDimitry Andric 205bdd1243dSDimitry Andric // Do not add the RegBankSelect pass, as we only ever need virtual registers. 20681ad6265SDimitry Andric bool SPIRVPassConfig::addRegBankSelect() { 20781ad6265SDimitry Andric disablePass(&RegBankSelect::ID); 20881ad6265SDimitry Andric return false; 20981ad6265SDimitry Andric } 21081ad6265SDimitry Andric 21181ad6265SDimitry Andric namespace { 21281ad6265SDimitry Andric // A custom subclass of InstructionSelect, which is mostly the same except from 21381ad6265SDimitry Andric // not requiring RegBankSelect to occur previously. 21481ad6265SDimitry Andric class SPIRVInstructionSelect : public InstructionSelect { 21581ad6265SDimitry Andric // We don't use register banks, so unset the requirement for them 21681ad6265SDimitry Andric MachineFunctionProperties getRequiredProperties() const override { 21781ad6265SDimitry Andric return InstructionSelect::getRequiredProperties().reset( 21881ad6265SDimitry Andric MachineFunctionProperties::Property::RegBankSelected); 21981ad6265SDimitry Andric } 22081ad6265SDimitry Andric }; 22181ad6265SDimitry Andric } // namespace 22281ad6265SDimitry Andric 223bdd1243dSDimitry Andric // Add the custom SPIRVInstructionSelect from above. 22481ad6265SDimitry Andric bool SPIRVPassConfig::addGlobalInstructionSelect() { 22581ad6265SDimitry Andric addPass(new SPIRVInstructionSelect()); 22681ad6265SDimitry Andric return false; 22781ad6265SDimitry Andric } 228