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
LLVMInitializeSPIRVTarget()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
computeDataLayout(const Triple & TT)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
getEffectiveRelocModel(std::optional<Reloc::Model> RM)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.
~SPIRVTargetObjectFile()7481ad6265SDimitry Andric SPIRVTargetObjectFile::~SPIRVTargetObjectFile() {}
7581ad6265SDimitry Andric
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)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:
SPIRVPassConfig(SPIRVTargetMachine & TM,PassManagerBase & PM)9881ad6265SDimitry Andric SPIRVPassConfig(SPIRVTargetMachine &TM, PassManagerBase &PM)
995f757f3fSDimitry Andric : TargetPassConfig(TM, PM), TM(TM) {}
10081ad6265SDimitry Andric
getSPIRVTargetMachine() const10181ad6265SDimitry 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;
addFastRegAlloc()11481ad6265SDimitry Andric void addFastRegAlloc() override {}
addOptimizedRegAlloc()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.
createTargetRegisterAllocator(bool)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.
addPostRegAlloc()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
getTargetTransformInfo(const Function & F) const15181ad6265SDimitry Andric SPIRVTargetMachine::getTargetTransformInfo(const Function &F) const {
15281ad6265SDimitry Andric return TargetTransformInfo(SPIRVTTIImpl(this, F));
15381ad6265SDimitry Andric }
15481ad6265SDimitry Andric
createPassConfig(PassManagerBase & PM)15581ad6265SDimitry Andric TargetPassConfig *SPIRVTargetMachine::createPassConfig(PassManagerBase &PM) {
15681ad6265SDimitry Andric return new SPIRVPassConfig(*this, PM);
15781ad6265SDimitry Andric }
15881ad6265SDimitry Andric
addIRPasses()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
addISelPrepare()18481ad6265SDimitry Andric void SPIRVPassConfig::addISelPrepare() {
18581ad6265SDimitry Andric addPass(createSPIRVEmitIntrinsicsPass(&getTM<SPIRVTargetMachine>()));
18681ad6265SDimitry Andric TargetPassConfig::addISelPrepare();
18781ad6265SDimitry Andric }
18881ad6265SDimitry Andric
addIRTranslator()18981ad6265SDimitry Andric bool SPIRVPassConfig::addIRTranslator() {
19081ad6265SDimitry Andric addPass(new IRTranslator(getOptLevel()));
19181ad6265SDimitry Andric return false;
19281ad6265SDimitry Andric }
19381ad6265SDimitry Andric
addPreLegalizeMachineIR()19481ad6265SDimitry Andric void SPIRVPassConfig::addPreLegalizeMachineIR() {
19581ad6265SDimitry Andric addPass(createSPIRVPreLegalizerPass());
19681ad6265SDimitry Andric }
19781ad6265SDimitry Andric
198bdd1243dSDimitry Andric // Use the default legalizer.
addLegalizeMachineIR()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.
addRegBankSelect()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
getRequiredProperties() const21681ad6265SDimitry 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.
addGlobalInstructionSelect()22481ad6265SDimitry Andric bool SPIRVPassConfig::addGlobalInstructionSelect() {
22581ad6265SDimitry Andric addPass(new SPIRVInstructionSelect());
22681ad6265SDimitry Andric return false;
22781ad6265SDimitry Andric }
228