Index: include/llvm/IR/DiagnosticInfo.h =================================================================== --- include/llvm/IR/DiagnosticInfo.h +++ include/llvm/IR/DiagnosticInfo.h @@ -60,6 +60,7 @@ DK_OptimizationRemarkAnalysisAliasing, DK_OptimizationFailure, DK_MIRParser, + DK_PGOProfile, DK_FirstPluginKind }; @@ -251,6 +252,31 @@ const Twine &Msg; }; +/// Diagnostic information for the PGO profiler. +class DiagnosticInfoPGOProfile : public DiagnosticInfo { +public: + DiagnosticInfoPGOProfile(const char *FileName, const Twine &Msg, + DiagnosticSeverity Severity = DS_Error) + : DiagnosticInfo(DK_PGOProfile, Severity), FileName(FileName), Msg(Msg) {} + + /// \see DiagnosticInfo::print. + void print(DiagnosticPrinter &DP) const override; + + static bool classof(const DiagnosticInfo *DI) { + return DI->getKind() == DK_PGOProfile; + } + + const char *getFileName() const { return FileName; } + const Twine &getMsg() const { return Msg; } + +private: + /// Name of the input file associated with this diagnostic. + const char *FileName; + + /// Message to report. + const Twine &Msg; +}; + /// Common features for diagnostics dealing with optimization remarks. class DiagnosticInfoOptimizationBase : public DiagnosticInfo { public: Index: include/llvm/InitializePasses.h =================================================================== --- include/llvm/InitializePasses.h +++ include/llvm/InitializePasses.h @@ -113,12 +113,14 @@ void initializeDominatorTreeWrapperPassPass(PassRegistry&); void initializeEarlyIfConverterPass(PassRegistry&); void initializeEdgeBundlesPass(PassRegistry&); -void initializeExpandPostRAPass(PassRegistry&); +void initializeExpandPostRAPass(PassRegistry &); void initializeAAResultsWrapperPassPass(PassRegistry &); -void initializeGCOVProfilerPass(PassRegistry&); -void initializeInstrProfilingPass(PassRegistry&); -void initializeAddressSanitizerPass(PassRegistry&); -void initializeAddressSanitizerModulePass(PassRegistry&); +void initializeGCOVProfilerPass(PassRegistry &); +void initializePGOIRInstrumentationGenPass(PassRegistry &); +void initializePGOIRInstrumentationUsePass(PassRegistry &); +void initializeInstrProfilingPass(PassRegistry &); +void initializeAddressSanitizerPass(PassRegistry &); +void initializeAddressSanitizerModulePass(PassRegistry &); void initializeMemorySanitizerPass(PassRegistry&); void initializeThreadSanitizerPass(PassRegistry&); void initializeSanitizerCoverageModulePass(PassRegistry&); Index: include/llvm/LinkAllPasses.h =================================================================== --- include/llvm/LinkAllPasses.h +++ include/llvm/LinkAllPasses.h @@ -82,12 +82,14 @@ (void) llvm::createDivergenceAnalysisPass(); (void) llvm::createDomOnlyPrinterPass(); (void) llvm::createDomPrinterPass(); - (void) llvm::createDomOnlyViewerPass(); - (void) llvm::createDomViewerPass(); - (void) llvm::createGCOVProfilerPass(); - (void) llvm::createInstrProfilingPass(); - (void) llvm::createFunctionInliningPass(); - (void) llvm::createAlwaysInlinerPass(); + (void)llvm::createDomOnlyViewerPass(); + (void)llvm::createDomViewerPass(); + (void)llvm::createGCOVProfilerPass(); + (void)llvm::createPGOIRInstrumentationGenPass(); + (void)llvm::createPGOIRInstrumentationUsePass(); + (void)llvm::createInstrProfilingPass(); + (void)llvm::createFunctionInliningPass(); + (void)llvm::createAlwaysInlinerPass(); (void) llvm::createGlobalDCEPass(); (void) llvm::createGlobalOptimizerPass(); (void) llvm::createGlobalsAAWrapperPass(); Index: include/llvm/Support/CFGMST.h =================================================================== --- /dev/null +++ include/llvm/Support/CFGMST.h @@ -0,0 +1,213 @@ +//===- CFGMST.cpp - Minimum Spanning Tree for CFG --------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements Union-find algorithm to compute Minimum Spanning Tree +// for a given CFG. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/DenseMap.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/BranchProbability.h" +#include "llvm/Transforms/Utils/BasicBlockUtils.h" +#include "llvm/Analysis/CFG.h" +#include "llvm/Analysis/BranchProbabilityInfo.h" +#include "llvm/Analysis/BlockFrequencyInfo.h" +#include +#include +#include + +namespace llvm { + +#define DEBUG_TYPE "cfgmst" + +template class CFGMST { +public: + Function &F; + + // Store all the edges in CFG. It may contains some stale edges + // when Removed is set. + std::vector> AllEdges; + + struct CompareEdgeWeight { + bool operator()(const std::unique_ptr &lhs, + const std::unique_ptr &rhs) const { + return lhs->Weight > rhs->Weight; + } + }; + + // This map records the auxiliary information for each BB. + DenseMap> BBInfos; + + // find the root group of the G and compress the path from G to the root. + BBInfo *findAndCompressGroup(BBInfo *G) { + if (G->Group != G) + G->Group = findAndCompressGroup(static_cast(G->Group)); + return static_cast(G->Group); + } + + // Union BB1 and BB2 into the same group and return true. + // Returns false if BB1 and BB2 are already in the same group. + bool unionGroups(const BasicBlock *BB1, const BasicBlock *BB2) { + BBInfo *BB1G = findAndCompressGroup(getBBInfo(BB1).get()); + BBInfo *BB2G = findAndCompressGroup(getBBInfo(BB2).get()); + + if (BB1G == BB2G) + return false; + + // Make the smaller rank tree a direct child or the root of high rank tree. + if (BB1G->Rank < BB2G->Rank) + BB1G->Group = BB2G; + else { + BB2G->Group = BB1G; + // If the ranks are the same, increment root of one tree by one. + if (BB1G->Rank == BB2G->Rank) + BB1G->Rank++; + } + return true; + } + + // Give BB, return the auxiliary information. + const std::unique_ptr &getBBInfo(const BasicBlock *BB) const { + auto It = BBInfos.find(BB); + assert(It != BBInfos.end()); + return It->second; + } + + // Traverse the CFG using a stack. Find all the edges and assign the weight. + // Edges with large weight will be put into MST first so they are less likely + // to be instrumented. + void buildEdges() { + DEBUG(dbgs() << "Build Edge on " << F.getName() << "\n"); + + const BasicBlock *BB = &(F.getEntryBlock()); + // Add a fake edge to the entry. + addEdge(0, BB, BFI->getEntryFreq()); + + // Special handling for single BB functions. + if (succ_empty(BB)) { + addEdge(0, BB, BFI->getEntryFreq()); + return; + } + + for (Function::iterator BB = F.begin(), E = F.end(); BB != E; ++BB) { + TerminatorInst *TI = BB->getTerminator(); + uint64_t BBWeight = BFI->getBlockFreq(BB).getFrequency(); + uint64_t Weight; + if (int successors = TI->getNumSuccessors()) { + for (int i = 0; i != successors; ++i) { + bool Critical = false; + BasicBlock *TargetBB = TI->getSuccessor(i); + if ((Critical = isCriticalEdge(TI, i))) + Weight = + BPI->getEdgeProbability(BB, TargetBB).scale(1000 * BBWeight); + else + Weight = BPI->getEdgeProbability(BB, TargetBB).scale(BBWeight); + // Weight = BlockFrequency::getMaxFrequency(); + addEdge(BB, TargetBB, Weight)->IsCritical = Critical; + DEBUG(dbgs() << " Edge: from " << BB->getName() << " to " + << TargetBB->getName() << " w=" << Weight << "\n"); + } + } else { + addEdge(BB, 0, BBWeight); + DEBUG(dbgs() << " Edge: from " << BB->getName() << " to exit" + << " w = " << BBWeight << "\n"); + } + } + } + + // Sort CFG edges based on its weight. + void sortEdges() { + std::sort(AllEdges.begin(), AllEdges.end(), CompareEdgeWeight()); + } + + // Traverse all the edges and compute the Minimum Weight Spanning Tree + // using union-find algorithm. + void computeMinimumSpanningTree() { + // First, put all the critical edge with landing-pad as the Dest to MST. + // This works around the insufficient support of critical edges split + // when destination BB is a landing pad. + // Clang instrumentation in theory needs the same support. But it just + // ignores this issue and instrumenting on the source BB. The coverage + // result of Clang instrumentation is incorrect for these cases. + for (auto &Ei : AllEdges) { + if (Ei->Removed) + continue; + if (Ei->IsCritical) { + if (Ei->DestBB && Ei->DestBB->isLandingPad()) { + if (unionGroups(Ei->SrcBB, Ei->DestBB)) + Ei->InMST = true; + } + } + } + + for (auto &Ei : AllEdges) { + if (Ei->Removed) + continue; + if (unionGroups(Ei->SrcBB, Ei->DestBB)) + Ei->InMST = true; + } + } + + // Dump the Debug information about the instrumentation. + void dumpEdges(const StringRef Message = StringRef()) const { + if (!Message.empty()) + DEBUG(dbgs() << Message << "\n"); + DEBUG(dbgs() << " Number of Basic Blocks: " << BBInfos.size() << "\n"); + for (auto &BI : BBInfos) { + const BasicBlock *BB = BI.first; + DEBUG(dbgs() << " BB: " + << (BI.second->Index == 0 ? "FakeNode" : BB->getName()) + << BI.second->infoString() << "\n"); + } + + DEBUG(dbgs() << " Number of Edges: " << AllEdges.size() + << " (*: Instrument, C: CriticalEdge, -: Removed)\n"); + uint32_t Count = 0; + for (auto &EI : AllEdges) { + DEBUG(dbgs() << " Edge " << Count++ << ": " + << getBBInfo(EI->SrcBB)->Index << "-->" + << getBBInfo(EI->DestBB)->Index << EI->infoString() << "\n"); + } + } + + // Add edge to AllEdges with with Weight. + const std::unique_ptr &addEdge(const BasicBlock *Src, + const BasicBlock *Dest, unsigned W = 2) { + if (BBInfos.find(Src) == BBInfos.end()) { + unsigned Index = BBInfos.size(); + BBInfos.insert(std::make_pair( + Src, std::move(std::unique_ptr(new BBInfo(Index))))); + } + if (BBInfos.find(Dest) == BBInfos.end()) { + unsigned Index = BBInfos.size(); + BBInfos.insert(std::make_pair( + Dest, std::move(std::unique_ptr(new BBInfo(Index))))); + } + AllEdges.push_back( + std::move(std::unique_ptr(new Edge(Src, Dest, W)))); + return AllEdges[AllEdges.size() - 1]; + } + + BranchProbabilityInfo *BPI; + BlockFrequencyInfo *BFI; + +public: + CFGMST(Function &Func, BranchProbabilityInfo *_BPI = nullptr, + BlockFrequencyInfo *_BFI = nullptr) + : F(Func), BPI(_BPI), BFI(_BFI) { + buildEdges(); + sortEdges(); + computeMinimumSpanningTree(); + } +}; + +#undef DEBUG_TYPE // "cfgmst" +} // end namespace llvm Index: include/llvm/Transforms/IPO/PassManagerBuilder.h =================================================================== --- include/llvm/Transforms/IPO/PassManagerBuilder.h +++ include/llvm/Transforms/IPO/PassManagerBuilder.h @@ -147,6 +147,7 @@ void addInitialAliasAnalysisPasses(legacy::PassManagerBase &PM) const; void addLTOOptimizationPasses(legacy::PassManagerBase &PM); void addLateLTOOptimizationPasses(legacy::PassManagerBase &PM); + void addPGOIRInstrPasses(legacy::PassManagerBase &MPM); public: /// populateFunctionPassManager - This fills in the function pass manager, Index: include/llvm/Transforms/Instrumentation.h =================================================================== --- include/llvm/Transforms/Instrumentation.h +++ include/llvm/Transforms/Instrumentation.h @@ -16,6 +16,10 @@ #include "llvm/ADT/StringRef.h" #include "llvm/IR/BasicBlock.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/GlobalVariable.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/Module.h" #include #if defined(__GNUC__) && defined(__linux__) && !defined(ANDROID) @@ -76,8 +80,13 @@ // all of the function body's blocks. bool ExitBlockBeforeBody; }; -ModulePass *createGCOVProfilerPass(const GCOVOptions &Options = - GCOVOptions::getDefault()); +ModulePass * +createGCOVProfilerPass(const GCOVOptions &Options = GCOVOptions::getDefault()); + +// PGO IR Instrumention +ModulePass *createPGOIRInstrumentationGenPass(); +ModulePass * +createPGOIRInstrumentationUsePass(StringRef Filename = StringRef("")); /// Options for the frontend instrumentation based profiling pass. struct InstrProfOptions { @@ -147,6 +156,82 @@ /// protect against stack-based overflow vulnerabilities. FunctionPass *createSafeStackPass(const TargetMachine *TM = nullptr); +/// \brief Calculate what to divide by to scale counts. +/// +/// Given the maximum count, calculate a divisor that will scale all the +/// weights to strictly less than UINT32_MAX. +static inline uint64_t calculateCountScale(uint64_t MaxCount) { + return MaxCount < UINT32_MAX ? 1 : MaxCount / UINT32_MAX + 1; +} + +/// \brief Scale an individual branch count (and add 1). +/// +/// Scale a 64-bit weight down to 32-bits using \c Scale. +/// +/// According to Laplace's Rule of Succession, it is better to compute the +/// count based on the count plus 1, so universally add 1 to the value. +/// +/// \pre \c Scale was calculated by \a calculateCountScale() with a count no +/// greater than \c Count. +static inline uint32_t scaleBranchCount(uint64_t Count, uint64_t Scale) { + uint64_t Scaled = Count / Scale + 1; + assert(Scaled <= UINT32_MAX && "overflow 32-bits"); + return Scaled; +} + +// set the function name and prefix module name for local linkage to avoid +// the potential name conflict. +static inline std::string PGOSetFuncName(Module &M, StringRef Name, + GlobalValue::LinkageTypes Linkage) { + StringRef RawFuncName = Name; + std::string FuncName; + + // Function names may be prefixed with a binary '1' to indicate + // that the back-end should not modify the symbols due to any platform + // naming convention. Do not include that '1' in the PGO profile name. + if (RawFuncName[0] == '\1') + RawFuncName = RawFuncName.substr(1); + + FuncName = RawFuncName; + + if (GlobalValue::isLocalLinkage(Linkage)) { + if (M.getName().empty()) + FuncName = FuncName.insert(0, ":"); + else + FuncName = FuncName.insert(0, M.getName().str() + ":"); + } + return FuncName; +} + +static inline std::string PGOSetFuncName(Module &M, Function &Fn) { + return PGOSetFuncName(M, Fn.getName(), Fn.getLinkage()); +} + +static inline GlobalVariable *PGOCreateFuncNameVar(Module &M, Function &F, + std::string FuncName) { + GlobalVariable *FuncNameVar; + GlobalValue::LinkageTypes Linkage = F.getLinkage(); + // Usually, we want to match the function's linkage, but + // available_externally and extern_weak both have the wrong semantics. + if (Linkage == GlobalValue::ExternalWeakLinkage) + Linkage = GlobalValue::LinkOnceAnyLinkage; + else if (Linkage == GlobalValue::AvailableExternallyLinkage) + Linkage = GlobalValue::LinkOnceODRLinkage; + else if (Linkage == GlobalValue::InternalLinkage || + Linkage == GlobalValue::ExternalLinkage) + Linkage = GlobalValue::PrivateLinkage; + + auto *Value = ConstantDataArray::getString(M.getContext(), FuncName, false); + FuncNameVar = new GlobalVariable(M, Value->getType(), true, Linkage, Value, + "__llvm_profile_name_" + FuncName); + + // Hide the symbol so that we correctly get a copy for each executable. + if (!GlobalValue::isLocalLinkage(FuncNameVar->getLinkage())) + FuncNameVar->setVisibility(GlobalValue::HiddenVisibility); + + return FuncNameVar; +} + } // End llvm namespace #endif Index: lib/IR/DiagnosticInfo.cpp =================================================================== --- lib/IR/DiagnosticInfo.cpp +++ lib/IR/DiagnosticInfo.cpp @@ -130,6 +130,12 @@ DP << getMsg(); } +void DiagnosticInfoPGOProfile::print(DiagnosticPrinter &DP) const { + if (getFileName()) + DP << getFileName() << ": "; + DP << getMsg(); +} + bool DiagnosticInfoOptimizationBase::isLocationAvailable() const { return getDebugLoc(); } Index: lib/Transforms/IPO/LLVMBuild.txt =================================================================== --- lib/Transforms/IPO/LLVMBuild.txt +++ lib/Transforms/IPO/LLVMBuild.txt @@ -20,4 +20,4 @@ name = IPO parent = Transforms library_name = ipo -required_libraries = Analysis Core InstCombine ProfileData Scalar Support TransformUtils Vectorize +required_libraries = Analysis Core InstCombine ProfileData Scalar Support TransformUtils Vectorize Instrumentation Index: lib/Transforms/IPO/PassManagerBuilder.cpp =================================================================== --- lib/Transforms/IPO/PassManagerBuilder.cpp +++ lib/Transforms/IPO/PassManagerBuilder.cpp @@ -32,6 +32,7 @@ #include "llvm/Transforms/IPO.h" #include "llvm/Transforms/Scalar.h" #include "llvm/Transforms/Vectorize.h" +#include "llvm/Transforms/Instrumentation.h" using namespace llvm; @@ -99,23 +100,34 @@ cl::desc( "Enable the GlobalsModRef AliasAnalysis outside of the LTO pipeline.")); +static cl::opt RunPGOIRInstrGen( + "pgo-ir-instr-generate", cl::init(""), cl::Hidden, + cl::desc("Enable generate phrase of PGO IR instrumentation " + "and specify the path of profile data file")); + +static cl::opt + RunPGOIRInstrUse("pgo-ir-instr-use", cl::init(""), cl::Hidden, + cl::value_desc("filename"), + cl::desc("Enable use phrase of PGO IR instrumentation " + " and specify the path of profile data file")); + PassManagerBuilder::PassManagerBuilder() { - OptLevel = 2; - SizeLevel = 0; - LibraryInfo = nullptr; - Inliner = nullptr; - DisableUnitAtATime = false; - DisableUnrollLoops = false; - BBVectorize = RunBBVectorization; - SLPVectorize = RunSLPVectorization; - LoopVectorize = RunLoopVectorization; - RerollLoops = RunLoopRerolling; - LoadCombine = RunLoadCombine; - DisableGVNLoadPRE = false; - VerifyInput = false; - VerifyOutput = false; - MergeFunctions = false; - PrepareForLTO = false; + OptLevel = 2; + SizeLevel = 0; + LibraryInfo = nullptr; + Inliner = nullptr; + DisableUnitAtATime = false; + DisableUnrollLoops = false; + BBVectorize = RunBBVectorization; + SLPVectorize = RunSLPVectorization; + LoopVectorize = RunLoopVectorization; + RerollLoops = RunLoopRerolling; + LoadCombine = RunLoadCombine; + DisableGVNLoadPRE = false; + VerifyInput = false; + VerifyOutput = false; + MergeFunctions = false; + PrepareForLTO = false; } PassManagerBuilder::~PassManagerBuilder() { @@ -179,11 +191,24 @@ FPM.add(createLowerExpectIntrinsicPass()); } +// Do PGO IR instrumentation generate or use operation as optiton +// specified. +void PassManagerBuilder::addPGOIRInstrPasses(legacy::PassManagerBase &MPM) { + if (!RunPGOIRInstrGen.empty()) { + MPM.add(createPGOIRInstrumentationGenPass()); + InstrProfOptions Options; + Options.InstrProfileOutput = RunPGOIRInstrGen; + MPM.add(createInstrProfilingPass(Options)); + } else if (!RunPGOIRInstrUse.empty()) + MPM.add(createPGOIRInstrumentationUsePass(RunPGOIRInstrUse)); +} + void PassManagerBuilder::populateModulePassManager( legacy::PassManagerBase &MPM) { // If all optimizations are disabled, just run the always-inline pass and, // if enabled, the function merging pass. if (OptLevel == 0) { + addPGOIRInstrPasses(MPM); if (Inliner) { MPM.add(Inliner); Inliner = nullptr; @@ -219,9 +244,11 @@ MPM.add(createInstructionCombiningPass());// Clean up after IPCP & DAE addExtensionsToPM(EP_Peephole, MPM); - MPM.add(createCFGSimplificationPass()); // Clean up after IPCP & DAE + MPM.add(createCFGSimplificationPass()); // Clean up after IPCP & DAE } + addPGOIRInstrPasses(MPM); + if (EnableNonLTOGlobalsModRef) // We add a module alias analysis pass here. In part due to bugs in the // analysis infrastructure this "works" in that the analysis stays alive Index: lib/Transforms/Instrumentation/CMakeLists.txt =================================================================== --- lib/Transforms/Instrumentation/CMakeLists.txt +++ lib/Transforms/Instrumentation/CMakeLists.txt @@ -6,6 +6,7 @@ MemorySanitizer.cpp Instrumentation.cpp InstrProfiling.cpp + PGOIRInstr.cpp SafeStack.cpp SanitizerCoverage.cpp ThreadSanitizer.cpp Index: lib/Transforms/Instrumentation/Instrumentation.cpp =================================================================== --- lib/Transforms/Instrumentation/Instrumentation.cpp +++ lib/Transforms/Instrumentation/Instrumentation.cpp @@ -60,6 +60,8 @@ initializeAddressSanitizerModulePass(Registry); initializeBoundsCheckingPass(Registry); initializeGCOVProfilerPass(Registry); + initializePGOIRInstrumentationGenPass(Registry); + initializePGOIRInstrumentationUsePass(Registry); initializeInstrProfilingPass(Registry); initializeMemorySanitizerPass(Registry); initializeThreadSanitizerPass(Registry); Index: lib/Transforms/Instrumentation/LLVMBuild.txt =================================================================== --- lib/Transforms/Instrumentation/LLVMBuild.txt +++ lib/Transforms/Instrumentation/LLVMBuild.txt @@ -19,4 +19,4 @@ type = Library name = Instrumentation parent = Transforms -required_libraries = Analysis Core MC Support TransformUtils +required_libraries = Analysis Core MC Support TransformUtils ProfileData Index: lib/Transforms/Instrumentation/PGOIRInstr.cpp =================================================================== --- /dev/null +++ lib/Transforms/Instrumentation/PGOIRInstr.cpp @@ -0,0 +1,705 @@ +//===- PGOIRInstru.cpp - IR level Instrumentation for PGO --------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This pass implements IR level instrumentation for PGO. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Transforms/Instrumentation.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/IR/InstIterator.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/IntrinsicInst.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/MDBuilder.h" +#include "llvm/IR/DiagnosticInfo.h" +#include "llvm/Pass.h" +#include "llvm/IR/Module.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/BranchProbability.h" +#include "llvm/Support/CFGMST.h" +#include "llvm/Transforms/Utils/BasicBlockUtils.h" +#include "llvm/ProfileData/InstrProfReader.h" +#include "llvm/Analysis/CFG.h" +#include "llvm/Analysis/BranchProbabilityInfo.h" +#include "llvm/Analysis/BlockFrequencyInfo.h" +#include +#include +#include + +using namespace llvm; + +#define DEBUG_TYPE "pgo-ir-instr" + +STATISTIC(NumOfPGOInstrument, "Number of edges instrumented."); +STATISTIC(NumOfPGOEdge, "Number of edges."); +STATISTIC(NumOfPGOBB, "Number of basic-blocks."); +STATISTIC(NumOfPGOSplit, "Number of critical edge splits."); +STATISTIC(NumOfPGOFunc, "Number of functions having valid profile counts."); +STATISTIC(NumOfPGOMismatch, "Number of functions having mismatch profile."); +STATISTIC(NumOfPGOMissing, "Number of functions without profile."); + +namespace { +class PGOIRInstrumentationGen : public ModulePass { +public: + static char ID; + + PGOIRInstrumentationGen() : ModulePass(ID) { + initializePGOIRInstrumentationGenPass(*PassRegistry::getPassRegistry()); + } + + const char *getPassName() const override { + return "PGOIRInstrumentationGenPass"; + } + +private: + bool runOnModule(Module &M) override; + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.addRequired(); + AU.addRequired(); + } +}; + +class PGOIRInstrumentationUse : public ModulePass { +public: + static char ID; + + // Provide the profile filename as the parameter. + PGOIRInstrumentationUse(StringRef Filename = StringRef("")) + : ModulePass(ID), ProfileFileName(Filename) { + initializePGOIRInstrumentationUsePass(*PassRegistry::getPassRegistry()); + } + + const char *getPassName() const override { + return "PGOIRInstrumentationUsePass"; + } + +private: + StringRef ProfileFileName; + std::unique_ptr PGOReader; + bool runOnModule(Module &M) override; + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.addRequired(); + AU.addRequired(); + } +}; +} // end anonymous namespace + +char PGOIRInstrumentationGen::ID = 0; +INITIALIZE_PASS_BEGIN(PGOIRInstrumentationGen, "pgo-ir-instr-gen", + "IR level instrumentation for PGO.", false, false) +INITIALIZE_PASS_DEPENDENCY(BlockFrequencyInfoWrapperPass) +INITIALIZE_PASS_DEPENDENCY(BranchProbabilityInfoWrapperPass) +INITIALIZE_PASS_END(PGOIRInstrumentationGen, "pgo-ir-instr-gen", + "IR level instrumentation for PGO.", false, false) + +ModulePass *llvm::createPGOIRInstrumentationGenPass() { + return new PGOIRInstrumentationGen(); +} + +char PGOIRInstrumentationUse::ID = 0; +INITIALIZE_PASS_BEGIN(PGOIRInstrumentationUse, "pgo-ir-instr-use", + "Read IR level instrumentation profile in PGO.", false, false) +INITIALIZE_PASS_DEPENDENCY(BlockFrequencyInfoWrapperPass) +INITIALIZE_PASS_DEPENDENCY(BranchProbabilityInfoWrapperPass) +INITIALIZE_PASS_END(PGOIRInstrumentationUse, "pgo-ir-instr-use", + "Read IR level instrumentation profile in PGO.", false, false) + +ModulePass *llvm::createPGOIRInstrumentationUsePass(StringRef Filename) { + return new PGOIRInstrumentationUse(Filename); +} + +namespace llvm { +/// \brief An MST based instrumentation for PGO +/// +/// Implements a Minimum Spanning Tree (MST) based instrumentation for PGO +/// in the function level. +// +// This class implements the CFG edges. Note the CFG can be a multi-graph. +struct PGOInstrumentationEdge { + const BasicBlock *SrcBB; + const BasicBlock *DestBB; + unsigned Weight; + bool InMST; + bool Removed; + bool IsCritical; + PGOInstrumentationEdge(const BasicBlock *Src, const BasicBlock *Dest, + unsigned W = 1) + : SrcBB(Src), DestBB(Dest), Weight(W), InMST(false), Removed(false), + IsCritical(false) {} + // Return the information string of an edge. + virtual const StringRef infoString() { + std::string Str = (Removed ? "-" : " "); + Str += (InMST ? " " : "*"); + Str += (IsCritical ? "c" : " "); + Str += " W=" + std::to_string(Weight); + return StringRef(Str); + } +}; + +// This class stores the auxiliary information for each BB. +struct PGOInstrumentationBBInfo { + PGOInstrumentationBBInfo *Group; + uint32_t Index; + uint32_t Rank; + + PGOInstrumentationBBInfo(unsigned IX) : Group(this), Index(IX), Rank(0) {} + + // Return the information string of this object. + virtual const StringRef infoString() const { + return StringRef("Index=" + std::to_string(Index)); + } +}; + +// This class implements the CFG edges. Note the CFG can be a multi-graph. +template class PGOIRInstrumentationFunc { +private: + void computeCFGHash(); + +protected: + Function &F; + Module *M; + std::string FuncName; + GlobalVariable *FuncNameVar; + + // CFG hash value for this function. + uint64_t FunctionHash; + + // The Minimum Spanning Tree of function CFG. + CFGMST Mst; + + // Give an edge, find the BB that will be instrumented. + // Return NULL if no BB to be instrumented. + BasicBlock *getInstrBB(const std::unique_ptr &E); + + const std::unique_ptr &getBBInfo(const BasicBlock *BB) const { + return Mst.getBBInfo(BB); + } + +public: + PGOIRInstrumentationFunc(Function &Func, Module *Modu, + BranchProbabilityInfo *_BPI = nullptr, + BlockFrequencyInfo *_BFI = nullptr) + : F(Func), M(Modu), FunctionHash(0), Mst(F, _BPI, _BFI) { + FuncName = PGOSetFuncName(*M, F); + computeCFGHash(); + std::string Message = "Dump Function " + FuncName + + " after CFGMST: \t Hash: " + + std::to_string(FunctionHash); + Mst.dumpEdges(Message); + + NumOfPGOBB += Mst.BBInfos.size(); + for (auto &Ei : Mst.AllEdges) { + if (Ei->Removed) + continue; + NumOfPGOEdge++; + if (!Ei->InMST) + NumOfPGOInstrument++; + } + }; +}; + +// Compute the CRC32 of unsigned integers. +static inline unsigned crc32UnsignedBits(unsigned Chksum, unsigned Value, + unsigned Bits) { + for (unsigned i = Bits; i--; Value <<= 1) { + unsigned Feedback = (Value ^ Chksum) & 0x80000000 ? 0x04c11db7 : 0; + Chksum <<= 1; + Chksum ^= Feedback; + } + return Chksum; +} + +// Compute Hash value for the CFG: the lower 32 bits are CRC32 of the index +// value of each BB in the CFG. The higher 32 bits record the number of edges. +template +void PGOIRInstrumentationFunc::computeCFGHash() { + uint32_t Chksum = 0; + for (auto &BB : F) { + const TerminatorInst *TI = BB.getTerminator(); + for (unsigned s = 0, e = TI->getNumSuccessors(); s != e; ++s) { + BasicBlock *Succ = TI->getSuccessor(s); + unsigned Index = getBBInfo(Succ)->Index; + Chksum = crc32UnsignedBits(Chksum, Index, 32); + } + } + FunctionHash = Mst.AllEdges.size() << 32 | Chksum; +} + +template +BasicBlock *PGOIRInstrumentationFunc::getInstrBB( + const std::unique_ptr &E) { + if (E->InMST || E->Removed) + return NULL; + + BasicBlock *SrcBB = const_cast(E->SrcBB); + BasicBlock *DestBB = const_cast(E->DestBB); + // For a fake edge, instrument the real BB. + if (SrcBB == NULL) + return DestBB; + if (DestBB == NULL) + return SrcBB; + + // Instrument the SrcBB if it has a single successor, + // otherwise, the DestBB if this is not a critical edge. + TerminatorInst *TI = SrcBB->getTerminator(); + if (TI->getNumSuccessors() <= 1) + return SrcBB; + if (!E->IsCritical) + return DestBB; + + // For a critical edge, we have to split. Instrument the newly + // created BB. + NumOfPGOSplit++; + DEBUG(dbgs() << "Split critical edge: " << getBBInfo(SrcBB)->Index << " --> " + << getBBInfo(DestBB)->Index << "\n"); + unsigned SuccNum = GetSuccessorNumber(SrcBB, DestBB); + BasicBlock *InstrBB = SplitCriticalEdge(TI, SuccNum); + assert(InstrBB && "Critical edge is not split"); + + E->Removed = true; + return InstrBB; +} + +class PGOIRInstrumentationGenFunc + : public PGOIRInstrumentationFunc { +private: + // Add the instrumentation intrinsic calls. + void instrumentCFG(); + +public: + PGOIRInstrumentationGenFunc(Function &Func, Module *Modu, + BranchProbabilityInfo *_BPI, + BlockFrequencyInfo *_BFI) + : PGOIRInstrumentationFunc(Func, Modu, _BPI, _BFI) { + FuncNameVar = PGOCreateFuncNameVar(*M, F, FuncName); + instrumentCFG(); + } +}; + +// Visit all edge and instrument the edges not in MST. +// Critical edges will be split. +void PGOIRInstrumentationGenFunc::instrumentCFG() { + unsigned NumCounters = 0; + for (auto &Ei : Mst.AllEdges) { + if (!Ei->InMST && !Ei->Removed) + NumCounters++; + } + + for (unsigned i = 0, j = 0, E = Mst.AllEdges.size(); i < E; i++) { + const std::unique_ptr &Ei = Mst.AllEdges[i]; + BasicBlock *InstrBB = getInstrBB(Ei); + if (!InstrBB) + continue; + + IRBuilder<> Builder(InstrBB->getFirstInsertionPt()); + assert(Builder.GetInsertPoint() && "Cannot get the Instrumentation point"); + auto *I8PtrTy = Type::getInt8PtrTy(M->getContext()); + Builder.CreateCall( + Intrinsic::getDeclaration(M, Intrinsic::instrprof_increment), + {llvm::ConstantExpr::getBitCast(FuncNameVar, I8PtrTy), + Builder.getInt64(FunctionHash), Builder.getInt32(NumCounters), + Builder.getInt32(j++)}); + } +} + +struct PGOInstrumentationUseEdge : public PGOInstrumentationEdge { + bool CountValid; + uint64_t CountValue; + PGOInstrumentationUseEdge(const BasicBlock *Src, const BasicBlock *Dest, + unsigned W = 1) + : PGOInstrumentationEdge(Src, Dest, W), CountValid(false), CountValue(0) { + } + + // Set edge count value + void setEdgeCount(uint64_t Value) { + CountValue = Value; + CountValid = true; + } + + // Return the informaton string for this object. + const StringRef infoString() { + if (!CountValid) + return PGOInstrumentationEdge::infoString(); + std::string Str = PGOInstrumentationEdge::infoString().str() + " Count=" + + std::to_string(CountValue); + return StringRef(Str); + } +}; + +typedef SmallVector DirectEdges; + +// This class stores the auxiliary information for each BB. +struct PGOInstrumentationUseBBInfo : public PGOInstrumentationBBInfo { + uint64_t CountValue; + bool CountValid; + uint32_t UnknownCountInEdge; + uint32_t UnknownCountOutEdge; + DirectEdges InEdges; + DirectEdges OutEdges; + PGOInstrumentationUseBBInfo(unsigned IX) + : PGOInstrumentationBBInfo(IX), CountValue(0), CountValid(false), + UnknownCountInEdge(0), UnknownCountOutEdge(0) {} + PGOInstrumentationUseBBInfo(unsigned IX, uint64_t C) + : PGOInstrumentationBBInfo(IX), CountValue(C), CountValid(true), + UnknownCountInEdge(0), UnknownCountOutEdge(0) {} + + // Set the profile count value for this BB. + void setBBInfoCount(uint64_t Value) { + CountValue = Value; + CountValid = true; + } + + // Return the information string of this object. + const StringRef infoString() { + if (!CountValid) + return PGOInstrumentationBBInfo::infoString(); + std::string Str = PGOInstrumentationBBInfo::infoString().str() + + " Count=" + std::to_string(CountValue); + return StringRef(Str); + } +}; + +// Sum up the count values for all the edges. +static uint64_t +sumEdgeCount(const SmallVector Edges) { + uint64_t Total = 0; + for (auto &Ei : Edges) { + if (Ei->Removed) + continue; + Total += Ei->CountValue; + } + return Total; +} + +class PGOIRInstrumentationUseFunc + : public PGOIRInstrumentationFunc { +private: + // The maximum count value in the profile. This is only used in PGO use + // compilation. + uint64_t ProgramMaxCount; + + // Find the Instrumented BB and set the value. + void setInstrumentedCounts(const std::vector *CountFromProfile); + + // Read counts for the instrumented BB from profile. + bool readCounters(const std::unique_ptr &PGOReader); + + // Populate the counts for all BBs. + void populateCounters(); + + // Set the branch weights based on the count values. + void setBranchWeights(); + + // Set the edge counter value for the unknown edge -- there should be only + // one unknown edge. + void setUnknownCountEdge(DirectEdges &Edges, uint64_t Value); + + // Set the hot/cold inline hints based on the count values. + void applyFunctionAttributes(uint64_t EntryCount, uint64_t MaxCount) { + if (ProgramMaxCount == 0) + return; + // Threshold of the hot functions. + const BranchProbability HotFunctionThreshold(1, 100); + // Threshold of the cold functions. + const BranchProbability ColdFunctionThreshold(2, 10000); + if (EntryCount >= HotFunctionThreshold.scale(ProgramMaxCount)) + F.addFnAttr(llvm::Attribute::InlineHint); + else if (MaxCount <= ColdFunctionThreshold.scale(ProgramMaxCount)) + F.addFnAttr(llvm::Attribute::Cold); + } + +public: + PGOIRInstrumentationUseFunc( + Function &Func, Module *Modu, + const std::unique_ptr &PGOReader, + BranchProbabilityInfo *_BPI = nullptr, BlockFrequencyInfo *_BFI = nullptr) + : PGOIRInstrumentationFunc(Func, Modu, _BPI, _BFI) { + if (readCounters(PGOReader)) { + populateCounters(); + setBranchWeights(); + std::string Message = "Dump Function " + FuncName + + " after reading profile: \t Hash: " + + std::to_string(FunctionHash); + Mst.dumpEdges(Message); + } + }; +}; + +// Visit all the edges and assign the count value for the instrumented +// edges and the BB. +void PGOIRInstrumentationUseFunc::setInstrumentedCounts( + const std::vector *CountFromProfile) { + for (unsigned i = 0, j = 0, E = Mst.AllEdges.size(); i < E; i++) { + const std::unique_ptr &Ei = Mst.AllEdges[i]; + BasicBlock *InstrBB = getInstrBB(Ei); + if (!InstrBB) { + continue; + } + uint64_t CountValue = (*CountFromProfile)[j++]; + + if (Ei->Removed) { + unsigned Index = Mst.BBInfos.size(); + Mst.BBInfos.insert(std::make_pair( + InstrBB, std::move(std::unique_ptr( + new PGOInstrumentationUseBBInfo(Index))))); + BasicBlock *SrcBB = const_cast(Ei->SrcBB); + BasicBlock *DestBB = const_cast(Ei->DestBB); + + // Add new edge of SrcBB->InstrBB. + const std::unique_ptr &NewEdge = + Mst.addEdge(SrcBB, InstrBB); + NewEdge->setEdgeCount(CountValue); + + // Add new edge of InstrBB->DestBB. + const std::unique_ptr &NewEdge1 = + Mst.addEdge(InstrBB, DestBB); + NewEdge1->setEdgeCount(CountValue); + NewEdge1->InMST = true; + } else + Ei->setEdgeCount(CountValue); + + getBBInfo(InstrBB)->setBBInfoCount(CountValue); + } +} + +// Set the count value for the unknown edges. There should be one and only one +// unknown edge in Edges vector. +void PGOIRInstrumentationUseFunc::setUnknownCountEdge(DirectEdges &Edges, + uint64_t Value) { + for (auto &Ei : Edges) { + if (Ei->CountValid) + continue; + Ei->setEdgeCount(Value); + + getBBInfo(Ei->SrcBB)->UnknownCountOutEdge--; + getBBInfo(Ei->DestBB)->UnknownCountInEdge--; + return; + } + llvm_unreachable("Cannot find the unknown count edge"); +} + +// Read the profile from ProfileFileName and assign the value to the +// instrumented BB and the edges. This function also updates ProgramMaxCount. +// Return true if the profile are successfully read, and false on errors. +bool PGOIRInstrumentationUseFunc::readCounters( + const std::unique_ptr &PGOReader) { + auto &Ctx = M->getContext(); + std::vector CountFromProfile; + if (std::error_code EC = PGOReader->getFunctionCounts(FuncName, FunctionHash, + CountFromProfile)) { + if (EC == instrprof_error::unknown_function) + NumOfPGOMissing++; + else if (EC == instrprof_error::hash_mismatch || + EC == llvm::instrprof_error::malformed) + NumOfPGOMismatch++; + + std::string Msg = EC.message() + std::string(" ") + F.getName().str(); + Ctx.diagnose( + DiagnosticInfoPGOProfile(M->getName().data(), Msg, DS_Warning)); + return false; + } else + NumOfPGOFunc++; + + DEBUG(dbgs() << CountFromProfile.size() << " counts\n"); + uint64_t ValueSum = 0; + for (unsigned i = 0, E = CountFromProfile.size(); i < E; i++) { + DEBUG(dbgs() << " " << i << ": " << CountFromProfile[i] << "\n"); + ValueSum += CountFromProfile[i]; + } + + DEBUG(dbgs() << "SUM = " << ValueSum << "\n"); + + getBBInfo(reinterpret_cast(NULL))->UnknownCountOutEdge = 2; + getBBInfo(reinterpret_cast(NULL))->UnknownCountInEdge = 2; + + setInstrumentedCounts(&CountFromProfile); + + // ProgramMaxCount is computed in llvm-profdata merge operation. + // This IR instrumentation handles it differently from Clang-based + // instrumentation. + ProgramMaxCount = PGOReader->getMaximumFunctionCount(); + return true; +} + +// Populate the counters from instrumented BBs to all BBs. +// In the end of this operation, all BBs should have a valid count value. +void PGOIRInstrumentationUseFunc::populateCounters() { + // First set up Count variable for all BBs. + for (auto &Ei : Mst.AllEdges) { + if (Ei->Removed) + continue; + + BasicBlock *SrcBB = const_cast(Ei->SrcBB); + BasicBlock *DestBB = const_cast(Ei->DestBB); + const std::unique_ptr &SrcInfo = + getBBInfo(SrcBB); + const std::unique_ptr &DestInfo = + getBBInfo(DestBB); + SrcInfo->OutEdges.push_back(Ei.get()); + DestInfo->InEdges.push_back(Ei.get()); + SrcInfo->UnknownCountOutEdge++; + DestInfo->UnknownCountInEdge++; + + if (!Ei->CountValid) + continue; + DestInfo->UnknownCountInEdge--; + SrcInfo->UnknownCountOutEdge--; + } + + bool Changes = true; + unsigned NumPasses = 0; + + // For efficient traversal, it's better to start from the end as most + // of the instrumented edges are at the end. There is no existing reverse + // iterator. So we build a reverse list first. + std::vector BBInReverseOrder; + std::vector::iterator It = BBInReverseOrder.begin(); + for (auto &BB : F) + It = BBInReverseOrder.insert(It, &BB); + + while (Changes) { + NumPasses++; + Changes = false; + for (auto &BB : BBInReverseOrder) { + const std::unique_ptr &Count = getBBInfo(BB); + if (!Count->CountValid) { + if (Count->UnknownCountOutEdge == 0) { + Count->CountValue = sumEdgeCount(Count->OutEdges); + Count->CountValid = true; + Changes = true; + } else if (Count->UnknownCountInEdge == 0) { + Count->CountValue = sumEdgeCount(Count->InEdges); + Count->CountValid = true; + Changes = true; + } + } + if (Count->CountValid) { + if (Count->UnknownCountOutEdge == 1) { + uint64_t Total = Count->CountValue - sumEdgeCount(Count->OutEdges); + setUnknownCountEdge(Count->OutEdges, Total); + Changes = true; + } + if (Count->UnknownCountInEdge == 1) { + uint64_t Total = Count->CountValue - sumEdgeCount(Count->InEdges); + setUnknownCountEdge(Count->InEdges, Total); + Changes = true; + } + } + } + } + + DEBUG(dbgs() << "Populate counts in " << NumPasses << " passes."); + // Assert every BB has a valid counter. + uint64_t FuncEntryCount = getBBInfo(F.begin())->CountValue; + uint64_t FuncMaxCount = FuncEntryCount; + for (auto &BB : F) { + assert(getBBInfo(&BB)->CountValid && "BB count is not valid"); + uint64_t Count = getBBInfo(&BB)->CountValue; + if (Count > FuncMaxCount) + FuncMaxCount = Count; + } + applyFunctionAttributes(FuncEntryCount, FuncMaxCount); +} + +// Assign the scaled count values to the BB with multiple out edges. +void PGOIRInstrumentationUseFunc::setBranchWeights() { + // Generate MD_prof metadata for every branch instruction. + DEBUG(dbgs() << "\nSetting branch weights.\n"); + MDBuilder MDB(M->getContext()); + for (auto &BB : F) { + TerminatorInst *TI = BB.getTerminator(); + if (TI->getNumSuccessors() < 2) + continue; + if (!isa(TI) && !isa(TI)) + continue; + if (getBBInfo(&BB)->CountValue == 0) + continue; + + // We have a non-zero Branch BB. + const std::unique_ptr &BBCountIt = + getBBInfo(&BB); + unsigned Size = BBCountIt->OutEdges.size(); + SmallVector EdgeCounts; + EdgeCounts.resize(Size); + for (unsigned s = 0; s < Size; s++) + EdgeCounts[s] = 0; + uint64_t MaxCount; + for (unsigned s = 0; s < Size; s++) { + const PGOInstrumentationUseEdge *E = BBCountIt->OutEdges[s]; + const BasicBlock *SrcBB = E->SrcBB; + const BasicBlock *DestBB = E->DestBB; + if (DestBB == 0) + continue; + unsigned SuccNum = GetSuccessorNumber(const_cast(SrcBB), + const_cast(DestBB)); + uint64_t EdgeCount = E->CountValue; + if (EdgeCount > MaxCount) + MaxCount = EdgeCount; + EdgeCounts[SuccNum] = EdgeCount; + } + assert(MaxCount > 0 && "Bad max count"); + uint64_t Scale = calculateCountScale(MaxCount); + SmallVector Weights; + for (unsigned s = 0; s < Size; s++) + Weights.push_back(scaleBranchCount(EdgeCounts[s], Scale)); + + TI->setMetadata(llvm::LLVMContext::MD_prof, + MDB.createBranchWeights(Weights)); + } +} +} // end namespace llvm + +bool PGOIRInstrumentationGen::runOnModule(Module &M) { + for (auto &F : M) { + if (F.isDeclaration()) + continue; + BranchProbabilityInfo *BPI = + &(getAnalysis(F).getBPI()); + BlockFrequencyInfo *BFI = + &(getAnalysis(F).getBFI()); + PGOIRInstrumentationGenFunc Func(F, &M, BPI, BFI); + } + return true; +} + +bool PGOIRInstrumentationUse::runOnModule(Module &M) { + DEBUG(dbgs() << "Read in profile counters: "); + auto &Ctx = M.getContext(); + // Read the counter array from file. + auto ReaderOrErr = IndexedInstrProfReader::create(ProfileFileName); + if (std::error_code EC = ReaderOrErr.getError()) { + Ctx.diagnose( + DiagnosticInfoPGOProfile(ProfileFileName.data(), EC.message())); + return false; + } + + PGOReader = std::move(ReaderOrErr.get()); + if (!PGOReader) { + Ctx.diagnose(DiagnosticInfoPGOProfile(ProfileFileName.data(), + "Cannot get PGOReader")); + return false; + } + + for (auto &F : M) { + if (F.isDeclaration()) + continue; + BranchProbabilityInfo *BPI = + &(getAnalysis(F).getBPI()); + BlockFrequencyInfo *BFI = + &(getAnalysis(F).getBFI()); + PGOIRInstrumentationUseFunc Func(F, &M, PGOReader, BPI, BFI); + } + return true; +}