blob: df3d492c9fbabeb7228d3d51227887990a11a374 [file] [edit]
//===-- 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");
}