brintos

brintos / llvm-project-archived public Read only

0
0
Text · 11.1 KiB · 93dd7ce Raw
325 lines · cpp
1//===- KernelInfo.cpp - Kernel Analysis -----------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// This file defines the KernelInfoPrinter class used to emit remarks about10// function properties from a GPU kernel.11//12//===----------------------------------------------------------------------===//13 14#include "llvm/Analysis/KernelInfo.h"15#include "llvm/ADT/SmallString.h"16#include "llvm/ADT/StringExtras.h"17#include "llvm/Analysis/OptimizationRemarkEmitter.h"18#include "llvm/Analysis/TargetTransformInfo.h"19#include "llvm/IR/DebugInfo.h"20#include "llvm/IR/Dominators.h"21#include "llvm/IR/Instructions.h"22#include "llvm/IR/Metadata.h"23#include "llvm/IR/Module.h"24#include "llvm/IR/PassManager.h"25 26using namespace llvm;27 28#define DEBUG_TYPE "kernel-info"29 30namespace {31 32/// Data structure holding function info for kernels.33class KernelInfo {34  void updateForBB(const BasicBlock &BB, OptimizationRemarkEmitter &ORE);35 36public:37  static void emitKernelInfo(Function &F, FunctionAnalysisManager &FAM,38                             TargetMachine *TM);39 40  /// Whether the function has external linkage and is not a kernel function.41  bool ExternalNotKernel = false;42 43  /// Launch bounds.44  SmallVector<std::pair<StringRef, int64_t>> LaunchBounds;45 46  /// The number of alloca instructions inside the function, the number of those47  /// with allocation sizes that cannot be determined at compile time, and the48  /// sum of the sizes that can be.49  ///50  /// With the current implementation for at least some GPU archs,51  /// AllocasDyn > 0 might not be possible, but we report AllocasDyn anyway in52  /// case the implementation changes.53  int64_t Allocas = 0;54  int64_t AllocasDyn = 0;55  int64_t AllocasStaticSizeSum = 0;56 57  /// Number of direct/indirect calls (anything derived from CallBase).58  int64_t DirectCalls = 0;59  int64_t IndirectCalls = 0;60 61  /// Number of direct calls made from this function to other functions62  /// defined in this module.63  int64_t DirectCallsToDefinedFunctions = 0;64 65  /// Number of direct calls to inline assembly.66  int64_t InlineAssemblyCalls = 0;67 68  /// Number of calls of type InvokeInst.69  int64_t Invokes = 0;70 71  /// Target-specific flat address space.72  unsigned FlatAddrspace;73 74  /// Number of flat address space memory accesses (via load, store, etc.).75  int64_t FlatAddrspaceAccesses = 0;76};77 78} // end anonymous namespace79 80static void identifyCallee(OptimizationRemark &R, const Module *M,81                           const Value *V, StringRef Kind = "") {82  SmallString<100> Name; // might be function name or asm expression83  if (const Function *F = dyn_cast<Function>(V)) {84    if (auto *SubProgram = F->getSubprogram()) {85      if (SubProgram->isArtificial())86        R << "artificial ";87      Name = SubProgram->getName();88    }89  }90  if (Name.empty()) {91    raw_svector_ostream OS(Name);92    V->printAsOperand(OS, /*PrintType=*/false, M);93  }94  if (!Kind.empty())95    R << Kind << " ";96  R << "'" << Name << "'";97}98 99static void identifyFunction(OptimizationRemark &R, const Function &F) {100  identifyCallee(R, F.getParent(), &F, "function");101}102 103static void remarkAlloca(OptimizationRemarkEmitter &ORE, const Function &Caller,104                         const AllocaInst &Alloca,105                         TypeSize::ScalarTy StaticSize) {106  ORE.emit([&] {107    StringRef DbgName;108    DebugLoc Loc;109    bool Artificial = false;110    auto DVRs = findDVRDeclares(&const_cast<AllocaInst &>(Alloca));111    if (!DVRs.empty()) {112      const DbgVariableRecord &DVR = **DVRs.begin();113      DbgName = DVR.getVariable()->getName();114      Loc = DVR.getDebugLoc();115      Artificial = DVR.Variable->isArtificial();116    }117    OptimizationRemark R(DEBUG_TYPE, "Alloca", DiagnosticLocation(Loc),118                         Alloca.getParent());119    R << "in ";120    identifyFunction(R, Caller);121    R << ", ";122    if (Artificial)123      R << "artificial ";124    SmallString<20> ValName;125    raw_svector_ostream OS(ValName);126    Alloca.printAsOperand(OS, /*PrintType=*/false, Caller.getParent());127    R << "alloca ('" << ValName << "') ";128    if (!DbgName.empty())129      R << "for '" << DbgName << "' ";130    else131      R << "without debug info ";132    R << "with ";133    if (StaticSize)134      R << "static size of " << itostr(StaticSize) << " bytes";135    else136      R << "dynamic size";137    return R;138  });139}140 141static void remarkCall(OptimizationRemarkEmitter &ORE, const Function &Caller,142                       const CallBase &Call, StringRef CallKind,143                       StringRef RemarkKind) {144  ORE.emit([&] {145    OptimizationRemark R(DEBUG_TYPE, RemarkKind, &Call);146    R << "in ";147    identifyFunction(R, Caller);148    R << ", " << CallKind << ", callee is ";149    identifyCallee(R, Caller.getParent(), Call.getCalledOperand());150    return R;151  });152}153 154static void remarkFlatAddrspaceAccess(OptimizationRemarkEmitter &ORE,155                                      const Function &Caller,156                                      const Instruction &Inst) {157  ORE.emit([&] {158    OptimizationRemark R(DEBUG_TYPE, "FlatAddrspaceAccess", &Inst);159    R << "in ";160    identifyFunction(R, Caller);161    if (const IntrinsicInst *II = dyn_cast<IntrinsicInst>(&Inst)) {162      R << ", '" << II->getCalledFunction()->getName() << "' call";163    } else {164      R << ", '" << Inst.getOpcodeName() << "' instruction";165    }166    if (!Inst.getType()->isVoidTy()) {167      SmallString<20> Name;168      raw_svector_ostream OS(Name);169      Inst.printAsOperand(OS, /*PrintType=*/false, Caller.getParent());170      R << " ('" << Name << "')";171    }172    R << " accesses memory in flat address space";173    return R;174  });175}176 177void KernelInfo::updateForBB(const BasicBlock &BB,178                             OptimizationRemarkEmitter &ORE) {179  const Function &F = *BB.getParent();180  const Module &M = *F.getParent();181  const DataLayout &DL = M.getDataLayout();182  for (const Instruction &I : BB.instructionsWithoutDebug()) {183    if (const AllocaInst *Alloca = dyn_cast<AllocaInst>(&I)) {184      ++Allocas;185      TypeSize::ScalarTy StaticSize = 0;186      if (std::optional<TypeSize> Size = Alloca->getAllocationSize(DL)) {187        StaticSize = Size->getFixedValue();188        assert(StaticSize <=189               (TypeSize::ScalarTy)std::numeric_limits<int64_t>::max());190        AllocasStaticSizeSum += StaticSize;191      } else {192        ++AllocasDyn;193      }194      remarkAlloca(ORE, F, *Alloca, StaticSize);195    } else if (const CallBase *Call = dyn_cast<CallBase>(&I)) {196      SmallString<40> CallKind;197      SmallString<40> RemarkKind;198      if (Call->isIndirectCall()) {199        ++IndirectCalls;200        CallKind += "indirect";201        RemarkKind += "Indirect";202      } else {203        ++DirectCalls;204        CallKind += "direct";205        RemarkKind += "Direct";206      }207      if (isa<InvokeInst>(Call)) {208        ++Invokes;209        CallKind += " invoke";210        RemarkKind += "Invoke";211      } else {212        CallKind += " call";213        RemarkKind += "Call";214      }215      if (!Call->isIndirectCall()) {216        if (const Function *Callee = Call->getCalledFunction()) {217          if (!Callee->isIntrinsic() && !Callee->isDeclaration()) {218            ++DirectCallsToDefinedFunctions;219            CallKind += " to defined function";220            RemarkKind += "ToDefinedFunction";221          }222        } else if (Call->isInlineAsm()) {223          ++InlineAssemblyCalls;224          CallKind += " to inline assembly";225          RemarkKind += "ToInlineAssembly";226        }227      }228      remarkCall(ORE, F, *Call, CallKind, RemarkKind);229      if (const AnyMemIntrinsic *MI = dyn_cast<AnyMemIntrinsic>(Call)) {230        if (MI->getDestAddressSpace() == FlatAddrspace) {231          ++FlatAddrspaceAccesses;232          remarkFlatAddrspaceAccess(ORE, F, I);233        } else if (const AnyMemTransferInst *MT =234                       dyn_cast<AnyMemTransferInst>(MI)) {235          if (MT->getSourceAddressSpace() == FlatAddrspace) {236            ++FlatAddrspaceAccesses;237            remarkFlatAddrspaceAccess(ORE, F, I);238          }239        }240      }241    } else if (const LoadInst *Load = dyn_cast<LoadInst>(&I)) {242      if (Load->getPointerAddressSpace() == FlatAddrspace) {243        ++FlatAddrspaceAccesses;244        remarkFlatAddrspaceAccess(ORE, F, I);245      }246    } else if (const StoreInst *Store = dyn_cast<StoreInst>(&I)) {247      if (Store->getPointerAddressSpace() == FlatAddrspace) {248        ++FlatAddrspaceAccesses;249        remarkFlatAddrspaceAccess(ORE, F, I);250      }251    } else if (const AtomicRMWInst *At = dyn_cast<AtomicRMWInst>(&I)) {252      if (At->getPointerAddressSpace() == FlatAddrspace) {253        ++FlatAddrspaceAccesses;254        remarkFlatAddrspaceAccess(ORE, F, I);255      }256    } else if (const AtomicCmpXchgInst *At = dyn_cast<AtomicCmpXchgInst>(&I)) {257      if (At->getPointerAddressSpace() == FlatAddrspace) {258        ++FlatAddrspaceAccesses;259        remarkFlatAddrspaceAccess(ORE, F, I);260      }261    }262  }263}264 265static void remarkProperty(OptimizationRemarkEmitter &ORE, const Function &F,266                           StringRef Name, int64_t Value) {267  ORE.emit([&] {268    OptimizationRemark R(DEBUG_TYPE, Name, &F);269    R << "in ";270    identifyFunction(R, F);271    R << ", " << Name << " = " << itostr(Value);272    return R;273  });274}275 276static std::optional<int64_t> parseFnAttrAsInteger(Function &F,277                                                   StringRef Name) {278  if (!F.hasFnAttribute(Name))279    return std::nullopt;280  return F.getFnAttributeAsParsedInteger(Name);281}282 283void KernelInfo::emitKernelInfo(Function &F, FunctionAnalysisManager &FAM,284                                TargetMachine *TM) {285  KernelInfo KI;286  TargetTransformInfo &TheTTI = FAM.getResult<TargetIRAnalysis>(F);287  KI.FlatAddrspace = TheTTI.getFlatAddressSpace();288 289  // Record function properties.290  KI.ExternalNotKernel = F.hasExternalLinkage() && !F.hasKernelCallingConv();291  for (StringRef Name : {"omp_target_num_teams", "omp_target_thread_limit"}) {292    if (auto Val = parseFnAttrAsInteger(F, Name))293      KI.LaunchBounds.push_back({Name, *Val});294  }295  TheTTI.collectKernelLaunchBounds(F, KI.LaunchBounds);296 297  auto &ORE = FAM.getResult<OptimizationRemarkEmitterAnalysis>(F);298  for (const auto &BB : F)299    KI.updateForBB(BB, ORE);300 301#define REMARK_PROPERTY(PROP_NAME)                                             \302  remarkProperty(ORE, F, #PROP_NAME, KI.PROP_NAME)303  REMARK_PROPERTY(ExternalNotKernel);304  for (auto LB : KI.LaunchBounds)305    remarkProperty(ORE, F, LB.first, LB.second);306  REMARK_PROPERTY(Allocas);307  REMARK_PROPERTY(AllocasStaticSizeSum);308  REMARK_PROPERTY(AllocasDyn);309  REMARK_PROPERTY(DirectCalls);310  REMARK_PROPERTY(IndirectCalls);311  REMARK_PROPERTY(DirectCallsToDefinedFunctions);312  REMARK_PROPERTY(InlineAssemblyCalls);313  REMARK_PROPERTY(Invokes);314  REMARK_PROPERTY(FlatAddrspaceAccesses);315#undef REMARK_PROPERTY316}317 318PreservedAnalyses KernelInfoPrinter::run(Function &F,319                                         FunctionAnalysisManager &AM) {320  // Skip it if remarks are not enabled as it will do nothing useful.321  if (F.getContext().getDiagHandlerPtr()->isPassedOptRemarkEnabled(DEBUG_TYPE))322    KernelInfo::emitKernelInfo(F, AM, TM);323  return PreservedAnalyses::all();324}325