118f8106fSJoel E. Denny //===- KernelInfo.cpp - Kernel Analysis -----------------------------------===// 218f8106fSJoel E. Denny // 318f8106fSJoel E. Denny // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 418f8106fSJoel E. Denny // See https://llvm.org/LICENSE.txt for license information. 518f8106fSJoel E. Denny // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 618f8106fSJoel E. Denny // 718f8106fSJoel E. Denny //===----------------------------------------------------------------------===// 818f8106fSJoel E. Denny // 918f8106fSJoel E. Denny // This file defines the KernelInfoPrinter class used to emit remarks about 1018f8106fSJoel E. Denny // function properties from a GPU kernel. 1118f8106fSJoel E. Denny // 1218f8106fSJoel E. Denny //===----------------------------------------------------------------------===// 1318f8106fSJoel E. Denny 1418f8106fSJoel E. Denny #include "llvm/Analysis/KernelInfo.h" 1518f8106fSJoel E. Denny #include "llvm/ADT/SmallString.h" 1618f8106fSJoel E. Denny #include "llvm/ADT/StringExtras.h" 1718f8106fSJoel E. Denny #include "llvm/Analysis/OptimizationRemarkEmitter.h" 18*953354c7SBenjamin Kramer #include "llvm/Analysis/TargetTransformInfo.h" 1918f8106fSJoel E. Denny #include "llvm/IR/DebugInfo.h" 2018f8106fSJoel E. Denny #include "llvm/IR/Dominators.h" 2118f8106fSJoel E. Denny #include "llvm/IR/Instructions.h" 2218f8106fSJoel E. Denny #include "llvm/IR/Metadata.h" 2318f8106fSJoel E. Denny #include "llvm/IR/Module.h" 2418f8106fSJoel E. Denny #include "llvm/IR/PassManager.h" 2518f8106fSJoel E. Denny 2618f8106fSJoel E. Denny using namespace llvm; 2718f8106fSJoel E. Denny 2818f8106fSJoel E. Denny #define DEBUG_TYPE "kernel-info" 2918f8106fSJoel E. Denny 3018f8106fSJoel E. Denny namespace { 3118f8106fSJoel E. Denny 3218f8106fSJoel E. Denny /// Data structure holding function info for kernels. 3318f8106fSJoel E. Denny class KernelInfo { 3418f8106fSJoel E. Denny void updateForBB(const BasicBlock &BB, OptimizationRemarkEmitter &ORE); 3518f8106fSJoel E. Denny 3618f8106fSJoel E. Denny public: 3718f8106fSJoel E. Denny static void emitKernelInfo(Function &F, FunctionAnalysisManager &FAM, 3818f8106fSJoel E. Denny TargetMachine *TM); 3918f8106fSJoel E. Denny 4018f8106fSJoel E. Denny /// Whether the function has external linkage and is not a kernel function. 4118f8106fSJoel E. Denny bool ExternalNotKernel = false; 4218f8106fSJoel E. Denny 4318f8106fSJoel E. Denny /// Launch bounds. 4418f8106fSJoel E. Denny SmallVector<std::pair<StringRef, int64_t>> LaunchBounds; 4518f8106fSJoel E. Denny 4618f8106fSJoel E. Denny /// The number of alloca instructions inside the function, the number of those 4718f8106fSJoel E. Denny /// with allocation sizes that cannot be determined at compile time, and the 4818f8106fSJoel E. Denny /// sum of the sizes that can be. 4918f8106fSJoel E. Denny /// 5018f8106fSJoel E. Denny /// With the current implementation for at least some GPU archs, 5118f8106fSJoel E. Denny /// AllocasDyn > 0 might not be possible, but we report AllocasDyn anyway in 5218f8106fSJoel E. Denny /// case the implementation changes. 5318f8106fSJoel E. Denny int64_t Allocas = 0; 5418f8106fSJoel E. Denny int64_t AllocasDyn = 0; 5518f8106fSJoel E. Denny int64_t AllocasStaticSizeSum = 0; 5618f8106fSJoel E. Denny 5718f8106fSJoel E. Denny /// Number of direct/indirect calls (anything derived from CallBase). 5818f8106fSJoel E. Denny int64_t DirectCalls = 0; 5918f8106fSJoel E. Denny int64_t IndirectCalls = 0; 6018f8106fSJoel E. Denny 6118f8106fSJoel E. Denny /// Number of direct calls made from this function to other functions 6218f8106fSJoel E. Denny /// defined in this module. 6318f8106fSJoel E. Denny int64_t DirectCallsToDefinedFunctions = 0; 6418f8106fSJoel E. Denny 6518f8106fSJoel E. Denny /// Number of direct calls to inline assembly. 6618f8106fSJoel E. Denny int64_t InlineAssemblyCalls = 0; 6718f8106fSJoel E. Denny 6818f8106fSJoel E. Denny /// Number of calls of type InvokeInst. 6918f8106fSJoel E. Denny int64_t Invokes = 0; 7018f8106fSJoel E. Denny 7118f8106fSJoel E. Denny /// Target-specific flat address space. 7218f8106fSJoel E. Denny unsigned FlatAddrspace; 7318f8106fSJoel E. Denny 7418f8106fSJoel E. Denny /// Number of flat address space memory accesses (via load, store, etc.). 7518f8106fSJoel E. Denny int64_t FlatAddrspaceAccesses = 0; 7618f8106fSJoel E. Denny }; 7718f8106fSJoel E. Denny 7818f8106fSJoel E. Denny } // end anonymous namespace 7918f8106fSJoel E. Denny 8018f8106fSJoel E. Denny static void identifyCallee(OptimizationRemark &R, const Module *M, 8118f8106fSJoel E. Denny const Value *V, StringRef Kind = "") { 8218f8106fSJoel E. Denny SmallString<100> Name; // might be function name or asm expression 8318f8106fSJoel E. Denny if (const Function *F = dyn_cast<Function>(V)) { 8418f8106fSJoel E. Denny if (auto *SubProgram = F->getSubprogram()) { 8518f8106fSJoel E. Denny if (SubProgram->isArtificial()) 8618f8106fSJoel E. Denny R << "artificial "; 8718f8106fSJoel E. Denny Name = SubProgram->getName(); 8818f8106fSJoel E. Denny } 8918f8106fSJoel E. Denny } 9018f8106fSJoel E. Denny if (Name.empty()) { 9118f8106fSJoel E. Denny raw_svector_ostream OS(Name); 9218f8106fSJoel E. Denny V->printAsOperand(OS, /*PrintType=*/false, M); 9318f8106fSJoel E. Denny } 9418f8106fSJoel E. Denny if (!Kind.empty()) 9518f8106fSJoel E. Denny R << Kind << " "; 9618f8106fSJoel E. Denny R << "'" << Name << "'"; 9718f8106fSJoel E. Denny } 9818f8106fSJoel E. Denny 9918f8106fSJoel E. Denny static void identifyFunction(OptimizationRemark &R, const Function &F) { 10018f8106fSJoel E. Denny identifyCallee(R, F.getParent(), &F, "function"); 10118f8106fSJoel E. Denny } 10218f8106fSJoel E. Denny 10318f8106fSJoel E. Denny static void remarkAlloca(OptimizationRemarkEmitter &ORE, const Function &Caller, 10418f8106fSJoel E. Denny const AllocaInst &Alloca, 10518f8106fSJoel E. Denny TypeSize::ScalarTy StaticSize) { 10618f8106fSJoel E. Denny ORE.emit([&] { 10718f8106fSJoel E. Denny StringRef DbgName; 10818f8106fSJoel E. Denny DebugLoc Loc; 10918f8106fSJoel E. Denny bool Artificial = false; 11018f8106fSJoel E. Denny auto DVRs = findDVRDeclares(&const_cast<AllocaInst &>(Alloca)); 11118f8106fSJoel E. Denny if (!DVRs.empty()) { 11218f8106fSJoel E. Denny const DbgVariableRecord &DVR = **DVRs.begin(); 11318f8106fSJoel E. Denny DbgName = DVR.getVariable()->getName(); 11418f8106fSJoel E. Denny Loc = DVR.getDebugLoc(); 11518f8106fSJoel E. Denny Artificial = DVR.Variable->isArtificial(); 11618f8106fSJoel E. Denny } 11718f8106fSJoel E. Denny OptimizationRemark R(DEBUG_TYPE, "Alloca", DiagnosticLocation(Loc), 11818f8106fSJoel E. Denny Alloca.getParent()); 11918f8106fSJoel E. Denny R << "in "; 12018f8106fSJoel E. Denny identifyFunction(R, Caller); 12118f8106fSJoel E. Denny R << ", "; 12218f8106fSJoel E. Denny if (Artificial) 12318f8106fSJoel E. Denny R << "artificial "; 12418f8106fSJoel E. Denny SmallString<20> ValName; 12518f8106fSJoel E. Denny raw_svector_ostream OS(ValName); 12618f8106fSJoel E. Denny Alloca.printAsOperand(OS, /*PrintType=*/false, Caller.getParent()); 12718f8106fSJoel E. Denny R << "alloca ('" << ValName << "') "; 12818f8106fSJoel E. Denny if (!DbgName.empty()) 12918f8106fSJoel E. Denny R << "for '" << DbgName << "' "; 13018f8106fSJoel E. Denny else 13118f8106fSJoel E. Denny R << "without debug info "; 13218f8106fSJoel E. Denny R << "with "; 13318f8106fSJoel E. Denny if (StaticSize) 13418f8106fSJoel E. Denny R << "static size of " << itostr(StaticSize) << " bytes"; 13518f8106fSJoel E. Denny else 13618f8106fSJoel E. Denny R << "dynamic size"; 13718f8106fSJoel E. Denny return R; 13818f8106fSJoel E. Denny }); 13918f8106fSJoel E. Denny } 14018f8106fSJoel E. Denny 14118f8106fSJoel E. Denny static void remarkCall(OptimizationRemarkEmitter &ORE, const Function &Caller, 14218f8106fSJoel E. Denny const CallBase &Call, StringRef CallKind, 14318f8106fSJoel E. Denny StringRef RemarkKind) { 14418f8106fSJoel E. Denny ORE.emit([&] { 14518f8106fSJoel E. Denny OptimizationRemark R(DEBUG_TYPE, RemarkKind, &Call); 14618f8106fSJoel E. Denny R << "in "; 14718f8106fSJoel E. Denny identifyFunction(R, Caller); 14818f8106fSJoel E. Denny R << ", " << CallKind << ", callee is "; 14918f8106fSJoel E. Denny identifyCallee(R, Caller.getParent(), Call.getCalledOperand()); 15018f8106fSJoel E. Denny return R; 15118f8106fSJoel E. Denny }); 15218f8106fSJoel E. Denny } 15318f8106fSJoel E. Denny 15418f8106fSJoel E. Denny static void remarkFlatAddrspaceAccess(OptimizationRemarkEmitter &ORE, 15518f8106fSJoel E. Denny const Function &Caller, 15618f8106fSJoel E. Denny const Instruction &Inst) { 15718f8106fSJoel E. Denny ORE.emit([&] { 15818f8106fSJoel E. Denny OptimizationRemark R(DEBUG_TYPE, "FlatAddrspaceAccess", &Inst); 15918f8106fSJoel E. Denny R << "in "; 16018f8106fSJoel E. Denny identifyFunction(R, Caller); 16118f8106fSJoel E. Denny if (const IntrinsicInst *II = dyn_cast<IntrinsicInst>(&Inst)) { 16218f8106fSJoel E. Denny R << ", '" << II->getCalledFunction()->getName() << "' call"; 16318f8106fSJoel E. Denny } else { 16418f8106fSJoel E. Denny R << ", '" << Inst.getOpcodeName() << "' instruction"; 16518f8106fSJoel E. Denny } 16618f8106fSJoel E. Denny if (!Inst.getType()->isVoidTy()) { 16718f8106fSJoel E. Denny SmallString<20> Name; 16818f8106fSJoel E. Denny raw_svector_ostream OS(Name); 16918f8106fSJoel E. Denny Inst.printAsOperand(OS, /*PrintType=*/false, Caller.getParent()); 17018f8106fSJoel E. Denny R << " ('" << Name << "')"; 17118f8106fSJoel E. Denny } 17218f8106fSJoel E. Denny R << " accesses memory in flat address space"; 17318f8106fSJoel E. Denny return R; 17418f8106fSJoel E. Denny }); 17518f8106fSJoel E. Denny } 17618f8106fSJoel E. Denny 17718f8106fSJoel E. Denny void KernelInfo::updateForBB(const BasicBlock &BB, 17818f8106fSJoel E. Denny OptimizationRemarkEmitter &ORE) { 17918f8106fSJoel E. Denny const Function &F = *BB.getParent(); 18018f8106fSJoel E. Denny const Module &M = *F.getParent(); 18118f8106fSJoel E. Denny const DataLayout &DL = M.getDataLayout(); 18218f8106fSJoel E. Denny for (const Instruction &I : BB.instructionsWithoutDebug()) { 18318f8106fSJoel E. Denny if (const AllocaInst *Alloca = dyn_cast<AllocaInst>(&I)) { 18418f8106fSJoel E. Denny ++Allocas; 18518f8106fSJoel E. Denny TypeSize::ScalarTy StaticSize = 0; 18618f8106fSJoel E. Denny if (std::optional<TypeSize> Size = Alloca->getAllocationSize(DL)) { 18718f8106fSJoel E. Denny StaticSize = Size->getFixedValue(); 1885dae05f6SSimon Pilgrim assert(StaticSize <= 1895dae05f6SSimon Pilgrim (TypeSize::ScalarTy)std::numeric_limits<int64_t>::max()); 19018f8106fSJoel E. Denny AllocasStaticSizeSum += StaticSize; 19118f8106fSJoel E. Denny } else { 19218f8106fSJoel E. Denny ++AllocasDyn; 19318f8106fSJoel E. Denny } 19418f8106fSJoel E. Denny remarkAlloca(ORE, F, *Alloca, StaticSize); 19518f8106fSJoel E. Denny } else if (const CallBase *Call = dyn_cast<CallBase>(&I)) { 19618f8106fSJoel E. Denny SmallString<40> CallKind; 19718f8106fSJoel E. Denny SmallString<40> RemarkKind; 19818f8106fSJoel E. Denny if (Call->isIndirectCall()) { 19918f8106fSJoel E. Denny ++IndirectCalls; 20018f8106fSJoel E. Denny CallKind += "indirect"; 20118f8106fSJoel E. Denny RemarkKind += "Indirect"; 20218f8106fSJoel E. Denny } else { 20318f8106fSJoel E. Denny ++DirectCalls; 20418f8106fSJoel E. Denny CallKind += "direct"; 20518f8106fSJoel E. Denny RemarkKind += "Direct"; 20618f8106fSJoel E. Denny } 20718f8106fSJoel E. Denny if (isa<InvokeInst>(Call)) { 20818f8106fSJoel E. Denny ++Invokes; 20918f8106fSJoel E. Denny CallKind += " invoke"; 21018f8106fSJoel E. Denny RemarkKind += "Invoke"; 21118f8106fSJoel E. Denny } else { 21218f8106fSJoel E. Denny CallKind += " call"; 21318f8106fSJoel E. Denny RemarkKind += "Call"; 21418f8106fSJoel E. Denny } 21518f8106fSJoel E. Denny if (!Call->isIndirectCall()) { 21618f8106fSJoel E. Denny if (const Function *Callee = Call->getCalledFunction()) { 21718f8106fSJoel E. Denny if (!Callee->isIntrinsic() && !Callee->isDeclaration()) { 21818f8106fSJoel E. Denny ++DirectCallsToDefinedFunctions; 21918f8106fSJoel E. Denny CallKind += " to defined function"; 22018f8106fSJoel E. Denny RemarkKind += "ToDefinedFunction"; 22118f8106fSJoel E. Denny } 22218f8106fSJoel E. Denny } else if (Call->isInlineAsm()) { 22318f8106fSJoel E. Denny ++InlineAssemblyCalls; 22418f8106fSJoel E. Denny CallKind += " to inline assembly"; 22518f8106fSJoel E. Denny RemarkKind += "ToInlineAssembly"; 22618f8106fSJoel E. Denny } 22718f8106fSJoel E. Denny } 22818f8106fSJoel E. Denny remarkCall(ORE, F, *Call, CallKind, RemarkKind); 22918f8106fSJoel E. Denny if (const AnyMemIntrinsic *MI = dyn_cast<AnyMemIntrinsic>(Call)) { 23018f8106fSJoel E. Denny if (MI->getDestAddressSpace() == FlatAddrspace) { 23118f8106fSJoel E. Denny ++FlatAddrspaceAccesses; 23218f8106fSJoel E. Denny remarkFlatAddrspaceAccess(ORE, F, I); 23318f8106fSJoel E. Denny } else if (const AnyMemTransferInst *MT = 23418f8106fSJoel E. Denny dyn_cast<AnyMemTransferInst>(MI)) { 23518f8106fSJoel E. Denny if (MT->getSourceAddressSpace() == FlatAddrspace) { 23618f8106fSJoel E. Denny ++FlatAddrspaceAccesses; 23718f8106fSJoel E. Denny remarkFlatAddrspaceAccess(ORE, F, I); 23818f8106fSJoel E. Denny } 23918f8106fSJoel E. Denny } 24018f8106fSJoel E. Denny } 24118f8106fSJoel E. Denny } else if (const LoadInst *Load = dyn_cast<LoadInst>(&I)) { 24218f8106fSJoel E. Denny if (Load->getPointerAddressSpace() == FlatAddrspace) { 24318f8106fSJoel E. Denny ++FlatAddrspaceAccesses; 24418f8106fSJoel E. Denny remarkFlatAddrspaceAccess(ORE, F, I); 24518f8106fSJoel E. Denny } 24618f8106fSJoel E. Denny } else if (const StoreInst *Store = dyn_cast<StoreInst>(&I)) { 24718f8106fSJoel E. Denny if (Store->getPointerAddressSpace() == FlatAddrspace) { 24818f8106fSJoel E. Denny ++FlatAddrspaceAccesses; 24918f8106fSJoel E. Denny remarkFlatAddrspaceAccess(ORE, F, I); 25018f8106fSJoel E. Denny } 25118f8106fSJoel E. Denny } else if (const AtomicRMWInst *At = dyn_cast<AtomicRMWInst>(&I)) { 25218f8106fSJoel E. Denny if (At->getPointerAddressSpace() == FlatAddrspace) { 25318f8106fSJoel E. Denny ++FlatAddrspaceAccesses; 25418f8106fSJoel E. Denny remarkFlatAddrspaceAccess(ORE, F, I); 25518f8106fSJoel E. Denny } 25618f8106fSJoel E. Denny } else if (const AtomicCmpXchgInst *At = dyn_cast<AtomicCmpXchgInst>(&I)) { 25718f8106fSJoel E. Denny if (At->getPointerAddressSpace() == FlatAddrspace) { 25818f8106fSJoel E. Denny ++FlatAddrspaceAccesses; 25918f8106fSJoel E. Denny remarkFlatAddrspaceAccess(ORE, F, I); 26018f8106fSJoel E. Denny } 26118f8106fSJoel E. Denny } 26218f8106fSJoel E. Denny } 26318f8106fSJoel E. Denny } 26418f8106fSJoel E. Denny 26518f8106fSJoel E. Denny static void remarkProperty(OptimizationRemarkEmitter &ORE, const Function &F, 26618f8106fSJoel E. Denny StringRef Name, int64_t Value) { 26718f8106fSJoel E. Denny ORE.emit([&] { 26818f8106fSJoel E. Denny OptimizationRemark R(DEBUG_TYPE, Name, &F); 26918f8106fSJoel E. Denny R << "in "; 27018f8106fSJoel E. Denny identifyFunction(R, F); 27118f8106fSJoel E. Denny R << ", " << Name << " = " << itostr(Value); 27218f8106fSJoel E. Denny return R; 27318f8106fSJoel E. Denny }); 27418f8106fSJoel E. Denny } 27518f8106fSJoel E. Denny 27618f8106fSJoel E. Denny static std::optional<int64_t> parseFnAttrAsInteger(Function &F, 27718f8106fSJoel E. Denny StringRef Name) { 27818f8106fSJoel E. Denny if (!F.hasFnAttribute(Name)) 27918f8106fSJoel E. Denny return std::nullopt; 28018f8106fSJoel E. Denny return F.getFnAttributeAsParsedInteger(Name); 28118f8106fSJoel E. Denny } 28218f8106fSJoel E. Denny 28318f8106fSJoel E. Denny void KernelInfo::emitKernelInfo(Function &F, FunctionAnalysisManager &FAM, 28418f8106fSJoel E. Denny TargetMachine *TM) { 28518f8106fSJoel E. Denny KernelInfo KI; 28618f8106fSJoel E. Denny TargetTransformInfo &TheTTI = FAM.getResult<TargetIRAnalysis>(F); 28718f8106fSJoel E. Denny KI.FlatAddrspace = TheTTI.getFlatAddressSpace(); 28818f8106fSJoel E. Denny 28918f8106fSJoel E. Denny // Record function properties. 29018f8106fSJoel E. Denny KI.ExternalNotKernel = F.hasExternalLinkage() && !F.hasKernelCallingConv(); 29118f8106fSJoel E. Denny for (StringRef Name : {"omp_target_num_teams", "omp_target_thread_limit"}) { 29218f8106fSJoel E. Denny if (auto Val = parseFnAttrAsInteger(F, Name)) 29318f8106fSJoel E. Denny KI.LaunchBounds.push_back({Name, *Val}); 29418f8106fSJoel E. Denny } 29518f8106fSJoel E. Denny TheTTI.collectKernelLaunchBounds(F, KI.LaunchBounds); 29618f8106fSJoel E. Denny 29718f8106fSJoel E. Denny auto &ORE = FAM.getResult<OptimizationRemarkEmitterAnalysis>(F); 29818f8106fSJoel E. Denny for (const auto &BB : F) 29918f8106fSJoel E. Denny KI.updateForBB(BB, ORE); 30018f8106fSJoel E. Denny 30118f8106fSJoel E. Denny #define REMARK_PROPERTY(PROP_NAME) \ 30218f8106fSJoel E. Denny remarkProperty(ORE, F, #PROP_NAME, KI.PROP_NAME) 30318f8106fSJoel E. Denny REMARK_PROPERTY(ExternalNotKernel); 30418f8106fSJoel E. Denny for (auto LB : KI.LaunchBounds) 30518f8106fSJoel E. Denny remarkProperty(ORE, F, LB.first, LB.second); 30618f8106fSJoel E. Denny REMARK_PROPERTY(Allocas); 30718f8106fSJoel E. Denny REMARK_PROPERTY(AllocasStaticSizeSum); 30818f8106fSJoel E. Denny REMARK_PROPERTY(AllocasDyn); 30918f8106fSJoel E. Denny REMARK_PROPERTY(DirectCalls); 31018f8106fSJoel E. Denny REMARK_PROPERTY(IndirectCalls); 31118f8106fSJoel E. Denny REMARK_PROPERTY(DirectCallsToDefinedFunctions); 31218f8106fSJoel E. Denny REMARK_PROPERTY(InlineAssemblyCalls); 31318f8106fSJoel E. Denny REMARK_PROPERTY(Invokes); 31418f8106fSJoel E. Denny REMARK_PROPERTY(FlatAddrspaceAccesses); 31518f8106fSJoel E. Denny #undef REMARK_PROPERTY 31618f8106fSJoel E. Denny 31718f8106fSJoel E. Denny return; 31818f8106fSJoel E. Denny } 31918f8106fSJoel E. Denny 32018f8106fSJoel E. Denny PreservedAnalyses KernelInfoPrinter::run(Function &F, 32118f8106fSJoel E. Denny FunctionAnalysisManager &AM) { 32218f8106fSJoel E. Denny // Skip it if remarks are not enabled as it will do nothing useful. 32318f8106fSJoel E. Denny if (F.getContext().getDiagHandlerPtr()->isPassedOptRemarkEnabled(DEBUG_TYPE)) 32418f8106fSJoel E. Denny KernelInfo::emitKernelInfo(F, AM, TM); 32518f8106fSJoel E. Denny return PreservedAnalyses::all(); 32618f8106fSJoel E. Denny } 327