| //===-- NVPTXTargetMachine.cpp - Define TargetMachine for NVPTX -----------===// |
| // |
| // 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 |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // Top-level implementation for the NVPTX target. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "NVPTXTargetMachine.h" |
| #include "NVPTX.h" |
| #include "NVPTXAliasAnalysis.h" |
| #include "NVPTXMachineFunctionInfo.h" |
| #include "NVPTXTargetObjectFile.h" |
| #include "NVPTXTargetTransformInfo.h" |
| #include "TargetInfo/NVPTXTargetInfo.h" |
| #include "llvm/Analysis/TargetTransformInfo.h" |
| #include "llvm/CodeGen/Passes.h" |
| #include "llvm/CodeGen/TargetPassConfig.h" |
| #include "llvm/IR/IntrinsicsNVPTX.h" |
| #include "llvm/MC/TargetRegistry.h" |
| #include "llvm/Pass.h" |
| #include "llvm/Support/CommandLine.h" |
| #include "llvm/Support/Compiler.h" |
| #include "llvm/Target/TargetMachine.h" |
| #include "llvm/Target/TargetOptions.h" |
| #include "llvm/TargetParser/Triple.h" |
| #include "llvm/Transforms/IPO/ExpandVariadics.h" |
| #include "llvm/Transforms/Scalar.h" |
| #include "llvm/Transforms/Scalar/GVN.h" |
| #include "llvm/Transforms/Vectorize/LoadStoreVectorizer.h" |
| #include <cassert> |
| #include <optional> |
| #include <string> |
| |
| using namespace llvm; |
| |
| // LSV is still relatively new; this switch lets us turn it off in case we |
| // encounter (or suspect) a bug. |
| cl::opt<bool> |
| DisableLoadStoreVectorizer("disable-nvptx-load-store-vectorizer", |
| cl::desc("Disable load/store vectorizer"), |
| cl::init(false), cl::Hidden); |
| |
| // NVPTX IR Peephole is a new pass; this option will lets us turn it off in case |
| // we encounter some issues. |
| cl::opt<bool> DisableNVPTXIRPeephole("disable-nvptx-ir-peephole", |
| cl::desc("Disable NVPTX IR Peephole"), |
| cl::init(false), cl::Hidden); |
| |
| // TODO: Remove this flag when we are confident with no regressions. |
| static cl::opt<bool> DisableRequireStructuredCFG( |
| "disable-nvptx-require-structured-cfg", |
| cl::desc("Transitional flag to turn off NVPTX's requirement on preserving " |
| "structured CFG. The requirement should be disabled only when " |
| "unexpected regressions happen."), |
| cl::init(false), cl::Hidden); |
| |
| extern "C" LLVM_ABI LLVM_EXTERNAL_VISIBILITY void LLVMInitializeNVPTXTarget() { |
| // Register the target. |
| RegisterTargetMachine<NVPTXTargetMachine> X(getTheNVPTXTarget32()); |
| RegisterTargetMachine<NVPTXTargetMachine> Y(getTheNVPTXTarget64()); |
| |
| PassRegistry &PR = *PassRegistry::getPassRegistry(); |
| // FIXME: This pass is really intended to be invoked during IR optimization, |
| // but it's very NVPTX-specific. |
| initializeNVVMReflectLegacyPassPass(PR); |
| initializeNVVMIntrRangePass(PR); |
| initializeGenericToNVVMLegacyPassPass(PR); |
| initializeNVPTXAllocaHoistingLegacyPassPass(PR); |
| initializeNVPTXAsmPrinterPass(PR); |
| initializeNVPTXAssignValidGlobalNamesLegacyPassPass(PR); |
| initializeNVPTXAtomicLowerLegacyPassPass(PR); |
| initializeNVPTXLowerArgsLegacyPassPass(PR); |
| initializeNVPTXPromoteParamAlignLegacyPassPass(PR); |
| initializeNVPTXMarkKernelPtrsGlobalLegacyPassPass(PR); |
| initializeNVPTXLowerAllocaLegacyPassPass(PR); |
| initializeNVPTXLowerUnreachableLegacyPassPass(PR); |
| initializeNVPTXCtorDtorLoweringLegacyPass(PR); |
| initializeNVPTXLowerAggrCopiesLegacyPassPass(PR); |
| initializeNVPTXProxyRegErasureLegacyPassPass(PR); |
| initializeNVPTXForwardParamsLegacyPassPass(PR); |
| initializeNVPTXAddressFolderLegacyPassPass(PR); |
| initializeNVPTXDAGToDAGISelLegacyPass(PR); |
| initializeNVPTXAAWrapperPassPass(PR); |
| initializeNVPTXExternalAAWrapperPass(PR); |
| initializeNVPTXPeepholeLegacyPassPass(PR); |
| initializeNVPTXTagInvariantLoadLegacyPassPass(PR); |
| initializeNVPTXIRPeepholePass(PR); |
| initializeNVPTXPrologEpilogLegacyPassPass(PR); |
| } |
| |
| NVPTXTargetMachine::NVPTXTargetMachine(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) |
| // The pic relocation model is used regardless of what the client has |
| // specified, as it is the only relocation model currently supported. |
| : CodeGenTargetMachineImpl(T, TT, CPU, FS, Options, Reloc::PIC_, |
| getEffectiveCodeModel(CM, CodeModel::Small), OL), |
| TLOF(std::make_unique<NVPTXTargetObjectFile>()), |
| Subtarget(TT, CPU, FS, *this) { |
| if (!DisableRequireStructuredCFG) |
| setRequiresStructuredCFG(true); |
| // NVPTX does not produce verifier-clean MIR yet; see isMachineVerifierClean() |
| // for the legacy pass manager equivalent. |
| setEnableDefaultMachineVerifier(false); |
| initAsmInfo(); |
| } |
| |
| NVPTXTargetMachine::~NVPTXTargetMachine() = default; |
| |
| namespace { |
| |
| /// NVPTXPassConfig mirrors the NewPM implementation in NVPTXCodeGenPassBuilder |
| /// in NVPTXCodeGenPassBuilder.cpp; the two must be kept in sync until this path |
| /// is removed. |
| class NVPTXPassConfig : public TargetPassConfig { |
| public: |
| NVPTXPassConfig(NVPTXTargetMachine &TM, PassManagerBase &PM) |
| : TargetPassConfig(TM, PM) {} |
| |
| NVPTXTargetMachine &getNVPTXTargetMachine() const { |
| return getTM<NVPTXTargetMachine>(); |
| } |
| |
| void addIRPasses() override; |
| bool addInstSelector() override; |
| void addPreRegAlloc() override; |
| void addPostRegAlloc() override; |
| |
| FunctionPass *createTargetRegisterAllocator(bool) override; |
| void addFastRegAlloc() override; |
| void addOptimizedRegAlloc() override; |
| |
| bool addRegAssignAndRewriteFast() override { |
| llvm_unreachable("should not be used"); |
| } |
| |
| bool addRegAssignAndRewriteOptimized() override { |
| llvm_unreachable("should not be used"); |
| } |
| |
| private: |
| // If the opt level is aggressive, add GVN; otherwise, add EarlyCSE. This |
| // function is only called in opt mode. |
| void addEarlyCSEOrGVNPass(); |
| |
| // Add passes that propagate special memory spaces. |
| void addAddressSpaceInferencePasses(); |
| |
| // Add passes that perform straight-line scalar optimizations. |
| void addStraightLineScalarOptimizationPasses(); |
| }; |
| |
| } // end anonymous namespace |
| |
| TargetPassConfig *NVPTXTargetMachine::createPassConfig(PassManagerBase &PM) { |
| return new NVPTXPassConfig(*this, PM); |
| } |
| |
| MachineFunctionInfo *NVPTXTargetMachine::createMachineFunctionInfo( |
| BumpPtrAllocator &Allocator, const Function &F, |
| const TargetSubtargetInfo *STI) const { |
| return NVPTXMachineFunctionInfo::create<NVPTXMachineFunctionInfo>(Allocator, |
| F, STI); |
| } |
| |
| void NVPTXTargetMachine::registerEarlyDefaultAliasAnalyses(AAManager &AAM) { |
| AAM.registerFunctionAnalysis<NVPTXAA>(); |
| } |
| |
| TargetTransformInfo |
| NVPTXTargetMachine::getTargetTransformInfo(const Function &F) const { |
| return TargetTransformInfo(std::make_unique<NVPTXTTIImpl>(this, F)); |
| } |
| |
| std::pair<const Value *, unsigned> |
| NVPTXTargetMachine::getPredicatedAddrSpace(const Value *V) const { |
| if (auto *II = dyn_cast<IntrinsicInst>(V)) { |
| switch (II->getIntrinsicID()) { |
| case Intrinsic::nvvm_isspacep_const: |
| return std::make_pair(II->getArgOperand(0), llvm::ADDRESS_SPACE_CONST); |
| case Intrinsic::nvvm_isspacep_global: |
| return std::make_pair(II->getArgOperand(0), llvm::ADDRESS_SPACE_GLOBAL); |
| case Intrinsic::nvvm_isspacep_local: |
| return std::make_pair(II->getArgOperand(0), llvm::ADDRESS_SPACE_LOCAL); |
| case Intrinsic::nvvm_isspacep_shared: |
| return std::make_pair(II->getArgOperand(0), llvm::ADDRESS_SPACE_SHARED); |
| case Intrinsic::nvvm_isspacep_shared_cluster: |
| return std::make_pair(II->getArgOperand(0), |
| llvm::ADDRESS_SPACE_SHARED_CLUSTER); |
| default: |
| break; |
| } |
| } |
| return std::make_pair(nullptr, -1); |
| } |
| |
| void NVPTXPassConfig::addEarlyCSEOrGVNPass() { |
| if (getOptLevel() == CodeGenOptLevel::Aggressive) |
| // Disable scalar PRE due to Register Pressure increase |
| addPass(createGVNPass(/*ScalarPRE=*/false)); |
| else |
| addPass(createEarlyCSEPass()); |
| } |
| |
| void NVPTXPassConfig::addAddressSpaceInferencePasses() { |
| // NVPTXLowerArgs emits alloca for byval parameters which can often |
| // be eliminated by SROA. |
| addPass(createSROAPass(/*PreserveCFG=*/true, |
| /*AggregateToVector=*/true)); |
| addPass(createNVPTXLowerAllocaLegacyPass()); |
| // TODO: Consider running InferAddressSpaces during opt, earlier in the |
| // compilation flow. |
| addPass(createInferAddressSpacesPass()); |
| addPass(createNVPTXAtomicLowerLegacyPass()); |
| } |
| |
| void NVPTXPassConfig::addStraightLineScalarOptimizationPasses() { |
| addPass(createSeparateConstOffsetFromGEPPass()); |
| addPass(createSpeculativeExecutionPass()); |
| // ReassociateGEPs exposes more opportunites for SLSR. See |
| // the example in reassociate-geps-and-slsr.ll. |
| addPass(createStraightLineStrengthReducePass()); |
| // SeparateConstOffsetFromGEP and SLSR creates common expressions which GVN or |
| // EarlyCSE can reuse. GVN generates significantly better code than EarlyCSE |
| // for some of our benchmarks. |
| addEarlyCSEOrGVNPass(); |
| // Run NaryReassociate after EarlyCSE/GVN to be more effective. |
| addPass(createNaryReassociatePass()); |
| // NaryReassociate on GEPs creates redundant common expressions, so run |
| // EarlyCSE after it. |
| addPass(createEarlyCSEPass()); |
| } |
| |
| void NVPTXPassConfig::addIRPasses() { |
| // The following passes are known to not play well with virtual regs hanging |
| // around after register allocation (which in our case, is *all* registers). |
| // We explicitly disable them here. We do, however, need some functionality |
| // of the PrologEpilogCodeInserter pass, so we emulate that behavior in the |
| // NVPTXPrologEpilog pass (see NVPTXPrologEpilogPass.cpp). |
| disablePass(&PrologEpilogCodeInserterID); |
| disablePass(&MachineLateInstrsCleanupID); |
| disablePass(&MachineCopyPropagationID); |
| disablePass(&TailDuplicateLegacyID); |
| disablePass(&StackMapLivenessID); |
| disablePass(&PostRAMachineSinkingID); |
| disablePass(&PostRASchedulerID); |
| disablePass(&FuncletLayoutID); |
| disablePass(&PatchableFunctionID); |
| disablePass(&ShrinkWrapID); |
| disablePass(&RemoveLoadsIntoFakeUsesID); |
| |
| addPass(createNVPTXAAWrapperPass()); |
| addPass(createNVPTXExternalAAWrapperPass()); |
| |
| // NVVMReflectPass is added in addEarlyAsPossiblePasses, so hopefully running |
| // it here does nothing. But since we need it for correctness when lowering |
| // to NVPTX, run it here too, in case whoever built our pass pipeline didn't |
| // call addEarlyAsPossiblePasses. |
| const NVPTXSubtarget &ST = *getTM<NVPTXTargetMachine>().getSubtargetImpl(); |
| addPass(createNVVMReflectPass(ST.getSmVersion())); |
| |
| if (getOptLevel() != CodeGenOptLevel::None) |
| addPass(createNVPTXImageOptimizerLegacyPass()); |
| addPass(createNVPTXAssignValidGlobalNamesLegacyPass()); |
| addPass(createGenericToNVVMLegacyPass()); |
| |
| // Lower variadic calls before address space inference. |
| addPass(createExpandVariadicsPass(ExpandVariadicsMode::Lowering)); |
| |
| // NVPTXLowerArgs is required for correctness and should be run right |
| // before the address space inference passes. |
| if (getNVPTXTargetMachine().getDrvInterface() == NVPTX::CUDA) |
| addPass(createNVPTXMarkKernelPtrsGlobalPass()); |
| addPass(createNVPTXPromoteParamAlignPass()); |
| addPass(createNVPTXLowerArgsPass()); |
| if (getOptLevel() != CodeGenOptLevel::None) { |
| addAddressSpaceInferencePasses(); |
| addStraightLineScalarOptimizationPasses(); |
| } else { |
| // Required for correct stack lowering |
| addPass(createNVPTXLowerAllocaLegacyPass()); |
| } |
| |
| addPass(createAtomicExpandLegacyPass()); |
| addPass(createNVPTXCtorDtorLoweringLegacyPass()); |
| |
| // === LSR and other generic IR passes === |
| TargetPassConfig::addIRPasses(); |
| // EarlyCSE is not always strong enough to clean up what LSR produces. For |
| // example, GVN can combine |
| // |
| // %0 = add %a, %b |
| // %1 = add %b, %a |
| // |
| // and |
| // |
| // %0 = shl nsw %a, 2 |
| // %1 = shl %a, 2 |
| // |
| // but EarlyCSE can do neither of them. |
| if (getOptLevel() != CodeGenOptLevel::None) { |
| addEarlyCSEOrGVNPass(); |
| if (!DisableLoadStoreVectorizer) |
| addPass(createLoadStoreVectorizerPass()); |
| addPass(createSROAPass(/*PreserveCFG=*/true, |
| /*AggregateToVector=*/true)); |
| addPass(createNVPTXTagInvariantLoadsPass()); |
| if (!DisableNVPTXIRPeephole) |
| addPass(createNVPTXIRPeepholePass()); |
| } |
| |
| if (ST.hasPTXASUnreachableBug()) { |
| // Run LowerUnreachable to WAR a ptxas bug. See the commit description of |
| // 1ee4d880e8760256c606fe55b7af85a4f70d006d for more details. |
| const auto &Options = getNVPTXTargetMachine().Options; |
| addPass(createNVPTXLowerUnreachableLegacyPass(Options.TrapUnreachable, |
| Options.NoTrapAfterNoreturn)); |
| } |
| } |
| |
| bool NVPTXPassConfig::addInstSelector() { |
| addPass(createNVPTXLowerAggrCopiesLegacyPass()); |
| addPass(createNVPTXAllocaHoistingLegacyPass()); |
| addPass(createNVPTXISelDag(getNVPTXTargetMachine(), getOptLevel())); |
| addPass(createNVPTXReplaceImageHandlesLegacyPass()); |
| |
| return false; |
| } |
| |
| void NVPTXPassConfig::addPreRegAlloc() { |
| addPass(createNVPTXForwardParamsLegacyPass()); |
| if (getOptLevel() != CodeGenOptLevel::None) |
| addPass(createNVPTXAddressFolderLegacyPass()); |
| // Remove Proxy Register pseudo instructions used to keep `callseq_end` alive. |
| addPass(createNVPTXProxyRegErasureLegacyPass()); |
| } |
| |
| void NVPTXPassConfig::addPostRegAlloc() { |
| addPass(createNVPTXPrologEpilogLegacyPass()); |
| if (getOptLevel() != CodeGenOptLevel::None) { |
| // NVPTXPrologEpilogPass calculates frame object offset and replace frame |
| // index with VRFrame register. NVPTXPeephole need to be run after that and |
| // will replace VRFrame with VRFrameLocal when possible. |
| addPass(createNVPTXPeepholeLegacyPass()); |
| } |
| } |
| |
| FunctionPass *NVPTXPassConfig::createTargetRegisterAllocator(bool) { |
| return nullptr; // No reg alloc |
| } |
| |
| void NVPTXPassConfig::addFastRegAlloc() { |
| addPass(&PHIEliminationID); |
| addPass(&TwoAddressInstructionPassID); |
| } |
| |
| void NVPTXPassConfig::addOptimizedRegAlloc() { |
| addPass(&ProcessImplicitDefsID); |
| addPass(&MachineLoopInfoID); |
| addPass(&PHIEliminationID); |
| |
| addPass(&TwoAddressInstructionPassID); |
| addPass(&RegisterCoalescerID); |
| |
| // PreRA instruction scheduling. |
| if (addPass(&MachineSchedulerID)) |
| printAndVerify("After Machine Scheduling"); |
| |
| addPass(&StackSlotColoringID); |
| |
| // FIXME: Needs physical registers |
| // addPass(&MachineLICMID); |
| |
| printAndVerify("After StackSlotColoring"); |
| } |