| //===- SPIRVTargetMachine.cpp - Define TargetMachine for SPIR-V -*- C++ -*-===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // | |
| // Implements the info about SPIR-V target spec. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "SPIRVTargetMachine.h" | |
| #include "Analysis/SPIRVConvergenceRegionAnalysis.h" | |
| #include "SPIRV.h" | |
| #include "SPIRVGlobalRegistry.h" | |
| #include "SPIRVLegalizerInfo.h" | |
| #include "SPIRVTargetObjectFile.h" | |
| #include "SPIRVTargetTransformInfo.h" | |
| #include "TargetInfo/SPIRVTargetInfo.h" | |
| #include "llvm/CodeGen/GlobalISel/IRTranslator.h" | |
| #include "llvm/CodeGen/GlobalISel/InstructionSelect.h" | |
| #include "llvm/CodeGen/GlobalISel/Legalizer.h" | |
| #include "llvm/CodeGen/GlobalISel/RegBankSelect.h" | |
| #include "llvm/CodeGen/Passes.h" | |
| #include "llvm/CodeGen/TargetPassConfig.h" | |
| #include "llvm/InitializePasses.h" | |
| #include "llvm/MC/TargetRegistry.h" | |
| #include "llvm/Pass.h" | |
| #include "llvm/Passes/PassBuilder.h" | |
| #include "llvm/Support/CodeGen.h" | |
| #include "llvm/Support/Compiler.h" | |
| #include "llvm/Target/TargetOptions.h" | |
| #include "llvm/Transforms/IPO/ExpandVariadics.h" | |
| #include "llvm/Transforms/Scalar.h" | |
| #include "llvm/Transforms/Utils.h" | |
| #include <optional> | |
| using namespace llvm; | |
| extern "C" LLVM_ABI LLVM_EXTERNAL_VISIBILITY void LLVMInitializeSPIRVTarget() { | |
| // Register the target. | |
| RegisterTargetMachine<SPIRVTargetMachine> X(getTheSPIRV32Target()); | |
| RegisterTargetMachine<SPIRVTargetMachine> Y(getTheSPIRV64Target()); | |
| RegisterTargetMachine<SPIRVTargetMachine> Z(getTheSPIRVLogicalTarget()); | |
| PassRegistry &PR = *PassRegistry::getPassRegistry(); | |
| initializeGlobalISel(PR); | |
| initializeSPIRVModuleAnalysisWrapperPassPass(PR); | |
| initializeSPIRVAsmPrinterPass(PR); | |
| initializeSPIRVConvergenceRegionAnalysisWrapperPassPass(PR); | |
| initializeSPIRVStructurizerPass(PR); | |
| initializeSPIRVCBufferAccessLegacyPass(PR); | |
| initializeSPIRVPushConstantAccessLegacyPass(PR); | |
| initializeSPIRVPreLegalizerCombinerLegacyPass(PR); | |
| initializeSPIRVLegalizePointerCastLegacyPass(PR); | |
| initializeSPIRVLegalizeZeroSizeArraysLegacyPass(PR); | |
| initializeSPIRVRegularizerLegacyPass(PR); | |
| initializeSPIRVPreLegalizerLegacyPass(PR); | |
| initializeSPIRVPostLegalizerLegacyPass(PR); | |
| initializeSPIRVPrepareModuleAnalysisLegacyPass(PR); | |
| initializeSPIRVMergeRegionExitTargetsLegacyPass(PR); | |
| initializeSPIRVEmitIntrinsicsLegacyPass(PR); | |
| initializeSPIRVPrepareFunctionsLegacyPass(PR); | |
| initializeSPIRVPrepareGlobalsLegacyPass(PR); | |
| initializeSPIRVLegalizeResourceBindingLegacyPass(PR); | |
| initializeSPIRVCtorDtorLoweringLegacyPass(PR); | |
| initializeSPIRVFinalizeShaderLinkageLegacyPass(PR); | |
| } | |
| static Reloc::Model getEffectiveRelocModel(std::optional<Reloc::Model> RM) { | |
| if (!RM) | |
| return Reloc::PIC_; | |
| return *RM; | |
| } | |
| // Pin SPIRVTargetObjectFile's vtables to this file. | |
| SPIRVTargetObjectFile::~SPIRVTargetObjectFile() = default; | |
| SPIRVTargetMachine::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) | |
| : CodeGenTargetMachineImpl(T, TT, CPU, FS, Options, | |
| getEffectiveRelocModel(RM), | |
| getEffectiveCodeModel(CM, CodeModel::Small), OL), | |
| TLOF(std::make_unique<SPIRVTargetObjectFile>()), | |
| Subtarget(TT, CPU.str(), FS.str(), *this) { | |
| initAsmInfo(); | |
| setGlobalISel(true); | |
| setFastISel(false); | |
| setO0WantsFastISel(false); | |
| setRequiresStructuredCFG(false); | |
| } | |
| namespace { | |
| // SPIR-V Code Generator Pass Configuration Options. | |
| class SPIRVPassConfig : public TargetPassConfig { | |
| public: | |
| SPIRVPassConfig(SPIRVTargetMachine &TM, PassManagerBase &PM) | |
| : TargetPassConfig(TM, PM), TM(TM) {} | |
| SPIRVTargetMachine &getSPIRVTargetMachine() const { | |
| return getTM<SPIRVTargetMachine>(); | |
| } | |
| void addMachineSSAOptimization() override; | |
| void addIRPasses() override; | |
| void addISelPrepare() override; | |
| bool addIRTranslator() override; | |
| void addPreLegalizeMachineIR() override; | |
| bool addLegalizeMachineIR() override; | |
| bool addRegBankSelect() override; | |
| bool addGlobalInstructionSelect() override; | |
| FunctionPass *createTargetRegisterAllocator(bool) override; | |
| void addFastRegAlloc() override {} | |
| void addOptimizedRegAlloc() override {} | |
| void addPostRegAlloc() override; | |
| void addPreEmitPass2() override; | |
| private: | |
| const SPIRVTargetMachine &TM; | |
| }; | |
| } // namespace | |
| // We do not use physical registers, and maintain virtual registers throughout | |
| // the entire pipeline, so return nullptr to disable register allocation. | |
| FunctionPass *SPIRVPassConfig::createTargetRegisterAllocator(bool) { | |
| return nullptr; | |
| } | |
| // A place to disable passes that may break CFG. | |
| void SPIRVPassConfig::addMachineSSAOptimization() { | |
| TargetPassConfig::addMachineSSAOptimization(); | |
| } | |
| // Disable passes that break from assuming no virtual registers exist. | |
| void SPIRVPassConfig::addPostRegAlloc() { | |
| // Do not work with vregs instead of physical regs. | |
| disablePass(&MachineCopyPropagationID); | |
| disablePass(&PostRAMachineSinkingID); | |
| disablePass(&PostRASchedulerID); | |
| disablePass(&FuncletLayoutID); | |
| disablePass(&StackMapLivenessID); | |
| disablePass(&PatchableFunctionID); | |
| disablePass(&ShrinkWrapID); | |
| disablePass(&LiveDebugValuesID); | |
| disablePass(&MachineLateInstrsCleanupID); | |
| disablePass(&RemoveLoadsIntoFakeUsesID); | |
| // Do not work with OpPhi. | |
| disablePass(&BranchFolderPassID); | |
| disablePass(&MachineBlockPlacementID); | |
| TargetPassConfig::addPostRegAlloc(); | |
| } | |
| void SPIRVPassConfig::addPreEmitPass2() { | |
| addPass(createSPIRVPrepareModuleAnalysisLegacyPass()); | |
| } | |
| TargetTransformInfo | |
| SPIRVTargetMachine::getTargetTransformInfo(const Function &F) const { | |
| return TargetTransformInfo(std::make_unique<SPIRVTTIImpl>(this, F)); | |
| } | |
| TargetPassConfig *SPIRVTargetMachine::createPassConfig(PassManagerBase &PM) { | |
| return new SPIRVPassConfig(*this, PM); | |
| } | |
| void SPIRVPassConfig::addIRPasses() { | |
| addPass(createAtomicExpandLegacyPass()); | |
| TargetPassConfig::addIRPasses(); | |
| if (TM.getSubtargetImpl()->isShader()) { | |
| if (getOptLevel() != CodeGenOptLevel::None) | |
| addPass(createSPIRVFinalizeShaderLinkagePass(TM)); | |
| } else { | |
| // Variadic function calls aren't supported in shader code. | |
| // This needs to come before SPIRVPrepareFunctions because this | |
| // may introduce intrinsic calls. | |
| addPass(createExpandVariadicsPass(ExpandVariadicsMode::Lowering)); | |
| } | |
| addPass(createSPIRVRegularizerPass()); | |
| addPass(createSPIRVCtorDtorLoweringLegacyPass()); | |
| addPass(createSPIRVPrepareFunctionsPass(TM)); | |
| addPass(createSPIRVPrepareGlobalsPass()); | |
| } | |
| void SPIRVPassConfig::addISelPrepare() { | |
| if (TM.getSubtargetImpl()->isShader()) { | |
| // Vulkan does not allow address space casts. This pass is run to remove | |
| // address space casts that can be removed. | |
| // If an address space cast is not removed while targeting Vulkan, lowering | |
| // will fail during MIR lowering. | |
| addPass(createInferAddressSpacesPass()); | |
| // 1. Simplify loop for subsequent transformations. After this steps, loops | |
| // have the following properties: | |
| // - loops have a single entry edge (pre-header to loop header). | |
| // - all loop exits are dominated by the loop pre-header. | |
| // - loops have a single back-edge. | |
| addPass(createLoopSimplifyPass()); | |
| // 2. Removes registers whose lifetime spans across basic blocks. Also | |
| // removes phi nodes. This will greatly simplify the next steps. | |
| addPass(createRegToMemWrapperPass()); | |
| // 3. Merge the convergence region exit nodes into one. After this step, | |
| // regions are single-entry, single-exit. This will help determine the | |
| // correct merge block. | |
| addPass(createSPIRVMergeRegionExitTargetsPass()); | |
| // 4. Structurize. | |
| addPass(createSPIRVStructurizerPass()); | |
| // 5. Reduce the amount of variables required by pushing some operations | |
| // back to virtual registers. | |
| addPass(createPromoteMemoryToRegisterPass()); | |
| } else { | |
| // Canonicalize loops so they have a single latch and preheader. | |
| // This enables OpLoopMerge emission for non-shader targets. | |
| addPass(createLoopSimplifyPass()); | |
| } | |
| SPIRVTargetMachine &TM = getTM<SPIRVTargetMachine>(); | |
| addPass(createStripConvergenceIntrinsicsPass()); | |
| addPass(createSPIRVLegalizeResourceBindingPass()); | |
| addPass(createSPIRVLegalizeZeroSizeArraysPass(TM)); | |
| addPass(createSPIRVCBufferAccessLegacyPass()); | |
| addPass(createSPIRVPushConstantAccessLegacyPass(&TM)); | |
| addPass(createSPIRVEmitIntrinsicsPass(TM)); | |
| if (TM.getSubtargetImpl()->isLogicalSPIRV()) | |
| addPass(createSPIRVLegalizePointerCastPass(&TM)); | |
| TargetPassConfig::addISelPrepare(); | |
| } | |
| bool SPIRVPassConfig::addIRTranslator() { | |
| addPass(new IRTranslatorLegacy(getOptLevel())); | |
| return false; | |
| } | |
| void SPIRVPassConfig::addPreLegalizeMachineIR() { | |
| addPass(createSPIRVPreLegalizerCombinerLegacyPass()); | |
| addPass(createSPIRVPreLegalizerLegacyPass()); | |
| } | |
| // Use the default legalizer. | |
| bool SPIRVPassConfig::addLegalizeMachineIR() { | |
| addPass(new LegalizerLegacy()); | |
| addPass(createSPIRVPostLegalizerLegacyPass()); | |
| return false; | |
| } | |
| // Do not add the RegBankSelect pass, as we only ever need virtual registers. | |
| bool SPIRVPassConfig::addRegBankSelect() { | |
| disablePass(&RegBankSelectLegacy::ID); | |
| return false; | |
| } | |
| // Deprecated flag kept for backward compatibility. NSDI emission is now handled | |
| // by SPIRVNonSemanticDebugHandler, registered in SPIRVAsmPrinter:: | |
| // doInitialization() when the module contains debug info (llvm.dbg.cu). | |
| // TODO: Remove this option after a deprecation period. Callers that used | |
| // -spv-emit-nonsemantic-debug-info should switch to -g. | |
| static cl::opt<bool> SPVEnableNonSemanticDI( | |
| "spv-emit-nonsemantic-debug-info", | |
| cl::desc("Deprecated. Use -g to emit SPIR-V NonSemantic.Shader.DebugInfo " | |
| "instructions"), | |
| cl::init(false)); | |
| // Add the custom SPIRVInstructionSelect from above. | |
| bool SPIRVPassConfig::addGlobalInstructionSelect() { | |
| addPass(new InstructionSelectLegacy(getOptLevel(), | |
| /*RequireRegBankSelection=*/false)); | |
| return false; | |
| } |