| //===- ModuleSummaryAnalysis.cpp - Module summary index builder -----------===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This pass builds a ModuleSummaryIndex object for the module, to be written |
| // to bitcode or LLVM assembly. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "llvm/Analysis/ModuleSummaryAnalysis.h" |
| #include "llvm/ADT/ArrayRef.h" |
| #include "llvm/ADT/DenseSet.h" |
| #include "llvm/ADT/MapVector.h" |
| #include "llvm/ADT/STLExtras.h" |
| #include "llvm/ADT/SetVector.h" |
| #include "llvm/ADT/SmallPtrSet.h" |
| #include "llvm/ADT/SmallVector.h" |
| #include "llvm/ADT/StringRef.h" |
| #include "llvm/Analysis/BlockFrequencyInfo.h" |
| #include "llvm/Analysis/BranchProbabilityInfo.h" |
| #include "llvm/Analysis/IndirectCallPromotionAnalysis.h" |
| #include "llvm/Analysis/LoopInfo.h" |
| #include "llvm/Analysis/ProfileSummaryInfo.h" |
| #include "llvm/Analysis/TypeMetadataUtils.h" |
| #include "llvm/IR/Attributes.h" |
| #include "llvm/IR/BasicBlock.h" |
| #include "llvm/IR/CallSite.h" |
| #include "llvm/IR/Constant.h" |
| #include "llvm/IR/Constants.h" |
| #include "llvm/IR/Dominators.h" |
| #include "llvm/IR/Function.h" |
| #include "llvm/IR/GlobalAlias.h" |
| #include "llvm/IR/GlobalValue.h" |
| #include "llvm/IR/GlobalVariable.h" |
| #include "llvm/IR/Instructions.h" |
| #include "llvm/IR/IntrinsicInst.h" |
| #include "llvm/IR/Intrinsics.h" |
| #include "llvm/IR/Metadata.h" |
| #include "llvm/IR/Module.h" |
| #include "llvm/IR/ModuleSummaryIndex.h" |
| #include "llvm/IR/Use.h" |
| #include "llvm/IR/User.h" |
| #include "llvm/Object/ModuleSymbolTable.h" |
| #include "llvm/Object/SymbolicFile.h" |
| #include "llvm/Pass.h" |
| #include "llvm/Support/Casting.h" |
| #include "llvm/Support/CommandLine.h" |
| #include <algorithm> |
| #include <cassert> |
| #include <cstdint> |
| #include <vector> |
| |
| using namespace llvm; |
| |
| #define DEBUG_TYPE "module-summary-analysis" |
| |
| // Option to force edges cold which will block importing when the |
| // -import-cold-multiplier is set to 0. Useful for debugging. |
| FunctionSummary::ForceSummaryHotnessType ForceSummaryEdgesCold = |
| FunctionSummary::FSHT_None; |
| cl::opt<FunctionSummary::ForceSummaryHotnessType, true> FSEC( |
| "force-summary-edges-cold", cl::Hidden, cl::location(ForceSummaryEdgesCold), |
| cl::desc("Force all edges in the function summary to cold"), |
| cl::values(clEnumValN(FunctionSummary::FSHT_None, "none", "None."), |
| clEnumValN(FunctionSummary::FSHT_AllNonCritical, |
| "all-non-critical", "All non-critical edges."), |
| clEnumValN(FunctionSummary::FSHT_All, "all", "All edges."))); |
| |
| // Walk through the operands of a given User via worklist iteration and populate |
| // the set of GlobalValue references encountered. Invoked either on an |
| // Instruction or a GlobalVariable (which walks its initializer). |
| // Return true if any of the operands contains blockaddress. This is important |
| // to know when computing summary for global var, because if global variable |
| // references basic block address we can't import it separately from function |
| // containing that basic block. For simplicity we currently don't import such |
| // global vars at all. When importing function we aren't interested if any |
| // instruction in it takes an address of any basic block, because instruction |
| // can only take an address of basic block located in the same function. |
| static bool findRefEdges(ModuleSummaryIndex &Index, const User *CurUser, |
| SetVector<ValueInfo> &RefEdges, |
| SmallPtrSet<const User *, 8> &Visited) { |
| bool HasBlockAddress = false; |
| SmallVector<const User *, 32> Worklist; |
| Worklist.push_back(CurUser); |
| |
| while (!Worklist.empty()) { |
| const User *U = Worklist.pop_back_val(); |
| |
| if (!Visited.insert(U).second) |
| continue; |
| |
| ImmutableCallSite CS(U); |
| |
| for (const auto &OI : U->operands()) { |
| const User *Operand = dyn_cast<User>(OI); |
| if (!Operand) |
| continue; |
| if (isa<BlockAddress>(Operand)) { |
| HasBlockAddress = true; |
| continue; |
| } |
| if (auto *GV = dyn_cast<GlobalValue>(Operand)) { |
| // We have a reference to a global value. This should be added to |
| // the reference set unless it is a callee. Callees are handled |
| // specially by WriteFunction and are added to a separate list. |
| if (!(CS && CS.isCallee(&OI))) |
| RefEdges.insert(Index.getOrInsertValueInfo(GV)); |
| continue; |
| } |
| Worklist.push_back(Operand); |
| } |
| } |
| return HasBlockAddress; |
| } |
| |
| static CalleeInfo::HotnessType getHotness(uint64_t ProfileCount, |
| ProfileSummaryInfo *PSI) { |
| if (!PSI) |
| return CalleeInfo::HotnessType::Unknown; |
| if (PSI->isHotCount(ProfileCount)) |
| return CalleeInfo::HotnessType::Hot; |
| if (PSI->isColdCount(ProfileCount)) |
| return CalleeInfo::HotnessType::Cold; |
| return CalleeInfo::HotnessType::None; |
| } |
| |
| static bool isNonRenamableLocal(const GlobalValue &GV) { |
| return GV.hasSection() && GV.hasLocalLinkage(); |
| } |
| |
| /// Determine whether this call has all constant integer arguments (excluding |
| /// "this") and summarize it to VCalls or ConstVCalls as appropriate. |
| static void addVCallToSet(DevirtCallSite Call, GlobalValue::GUID Guid, |
| SetVector<FunctionSummary::VFuncId> &VCalls, |
| SetVector<FunctionSummary::ConstVCall> &ConstVCalls) { |
| std::vector<uint64_t> Args; |
| // Start from the second argument to skip the "this" pointer. |
| for (auto &Arg : make_range(Call.CS.arg_begin() + 1, Call.CS.arg_end())) { |
| auto *CI = dyn_cast<ConstantInt>(Arg); |
| if (!CI || CI->getBitWidth() > 64) { |
| VCalls.insert({Guid, Call.Offset}); |
| return; |
| } |
| Args.push_back(CI->getZExtValue()); |
| } |
| ConstVCalls.insert({{Guid, Call.Offset}, std::move(Args)}); |
| } |
| |
| /// If this intrinsic call requires that we add information to the function |
| /// summary, do so via the non-constant reference arguments. |
| static void addIntrinsicToSummary( |
| const CallInst *CI, SetVector<GlobalValue::GUID> &TypeTests, |
| SetVector<FunctionSummary::VFuncId> &TypeTestAssumeVCalls, |
| SetVector<FunctionSummary::VFuncId> &TypeCheckedLoadVCalls, |
| SetVector<FunctionSummary::ConstVCall> &TypeTestAssumeConstVCalls, |
| SetVector<FunctionSummary::ConstVCall> &TypeCheckedLoadConstVCalls) { |
| switch (CI->getCalledFunction()->getIntrinsicID()) { |
| case Intrinsic::type_test: { |
| auto *TypeMDVal = cast<MetadataAsValue>(CI->getArgOperand(1)); |
| auto *TypeId = dyn_cast<MDString>(TypeMDVal->getMetadata()); |
| if (!TypeId) |
| break; |
| GlobalValue::GUID Guid = GlobalValue::getGUID(TypeId->getString()); |
| |
| // Produce a summary from type.test intrinsics. We only summarize type.test |
| // intrinsics that are used other than by an llvm.assume intrinsic. |
| // Intrinsics that are assumed are relevant only to the devirtualization |
| // pass, not the type test lowering pass. |
| bool HasNonAssumeUses = llvm::any_of(CI->uses(), [](const Use &CIU) { |
| auto *AssumeCI = dyn_cast<CallInst>(CIU.getUser()); |
| if (!AssumeCI) |
| return true; |
| Function *F = AssumeCI->getCalledFunction(); |
| return !F || F->getIntrinsicID() != Intrinsic::assume; |
| }); |
| if (HasNonAssumeUses) |
| TypeTests.insert(Guid); |
| |
| SmallVector<DevirtCallSite, 4> DevirtCalls; |
| SmallVector<CallInst *, 4> Assumes; |
| findDevirtualizableCallsForTypeTest(DevirtCalls, Assumes, CI); |
| for (auto &Call : DevirtCalls) |
| addVCallToSet(Call, Guid, TypeTestAssumeVCalls, |
| TypeTestAssumeConstVCalls); |
| |
| break; |
| } |
| |
| case Intrinsic::type_checked_load: { |
| auto *TypeMDVal = cast<MetadataAsValue>(CI->getArgOperand(2)); |
| auto *TypeId = dyn_cast<MDString>(TypeMDVal->getMetadata()); |
| if (!TypeId) |
| break; |
| GlobalValue::GUID Guid = GlobalValue::getGUID(TypeId->getString()); |
| |
| SmallVector<DevirtCallSite, 4> DevirtCalls; |
| SmallVector<Instruction *, 4> LoadedPtrs; |
| SmallVector<Instruction *, 4> Preds; |
| bool HasNonCallUses = false; |
| findDevirtualizableCallsForTypeCheckedLoad(DevirtCalls, LoadedPtrs, Preds, |
| HasNonCallUses, CI); |
| // Any non-call uses of the result of llvm.type.checked.load will |
| // prevent us from optimizing away the llvm.type.test. |
| if (HasNonCallUses) |
| TypeTests.insert(Guid); |
| for (auto &Call : DevirtCalls) |
| addVCallToSet(Call, Guid, TypeCheckedLoadVCalls, |
| TypeCheckedLoadConstVCalls); |
| |
| break; |
| } |
| default: |
| break; |
| } |
| } |
| |
| static void |
| computeFunctionSummary(ModuleSummaryIndex &Index, const Module &M, |
| const Function &F, BlockFrequencyInfo *BFI, |
| ProfileSummaryInfo *PSI, bool HasLocalsInUsedOrAsm, |
| DenseSet<GlobalValue::GUID> &CantBePromoted) { |
| // Summary not currently supported for anonymous functions, they should |
| // have been named. |
| assert(F.hasName()); |
| |
| unsigned NumInsts = 0; |
| // Map from callee ValueId to profile count. Used to accumulate profile |
| // counts for all static calls to a given callee. |
| MapVector<ValueInfo, CalleeInfo> CallGraphEdges; |
| SetVector<ValueInfo> RefEdges; |
| SetVector<GlobalValue::GUID> TypeTests; |
| SetVector<FunctionSummary::VFuncId> TypeTestAssumeVCalls, |
| TypeCheckedLoadVCalls; |
| SetVector<FunctionSummary::ConstVCall> TypeTestAssumeConstVCalls, |
| TypeCheckedLoadConstVCalls; |
| ICallPromotionAnalysis ICallAnalysis; |
| SmallPtrSet<const User *, 8> Visited; |
| |
| // Add personality function, prefix data and prologue data to function's ref |
| // list. |
| findRefEdges(Index, &F, RefEdges, Visited); |
| |
| bool HasInlineAsmMaybeReferencingInternal = false; |
| for (const BasicBlock &BB : F) |
| for (const Instruction &I : BB) { |
| if (isa<DbgInfoIntrinsic>(I)) |
| continue; |
| ++NumInsts; |
| findRefEdges(Index, &I, RefEdges, Visited); |
| auto CS = ImmutableCallSite(&I); |
| if (!CS) |
| continue; |
| |
| const auto *CI = dyn_cast<CallInst>(&I); |
| // Since we don't know exactly which local values are referenced in inline |
| // assembly, conservatively mark the function as possibly referencing |
| // a local value from inline assembly to ensure we don't export a |
| // reference (which would require renaming and promotion of the |
| // referenced value). |
| if (HasLocalsInUsedOrAsm && CI && CI->isInlineAsm()) |
| HasInlineAsmMaybeReferencingInternal = true; |
| |
| auto *CalledValue = CS.getCalledValue(); |
| auto *CalledFunction = CS.getCalledFunction(); |
| if (CalledValue && !CalledFunction) { |
| CalledValue = CalledValue->stripPointerCastsNoFollowAliases(); |
| // Stripping pointer casts can reveal a called function. |
| CalledFunction = dyn_cast<Function>(CalledValue); |
| } |
| // Check if this is an alias to a function. If so, get the |
| // called aliasee for the checks below. |
| if (auto *GA = dyn_cast<GlobalAlias>(CalledValue)) { |
| assert(!CalledFunction && "Expected null called function in callsite for alias"); |
| CalledFunction = dyn_cast<Function>(GA->getBaseObject()); |
| } |
| // Check if this is a direct call to a known function or a known |
| // intrinsic, or an indirect call with profile data. |
| if (CalledFunction) { |
| if (CI && CalledFunction->isIntrinsic()) { |
| addIntrinsicToSummary( |
| CI, TypeTests, TypeTestAssumeVCalls, TypeCheckedLoadVCalls, |
| TypeTestAssumeConstVCalls, TypeCheckedLoadConstVCalls); |
| continue; |
| } |
| // We should have named any anonymous globals |
| assert(CalledFunction->hasName()); |
| auto ScaledCount = PSI->getProfileCount(&I, BFI); |
| auto Hotness = ScaledCount ? getHotness(ScaledCount.getValue(), PSI) |
| : CalleeInfo::HotnessType::Unknown; |
| if (ForceSummaryEdgesCold != FunctionSummary::FSHT_None) |
| Hotness = CalleeInfo::HotnessType::Cold; |
| |
| // Use the original CalledValue, in case it was an alias. We want |
| // to record the call edge to the alias in that case. Eventually |
| // an alias summary will be created to associate the alias and |
| // aliasee. |
| auto &ValueInfo = CallGraphEdges[Index.getOrInsertValueInfo( |
| cast<GlobalValue>(CalledValue))]; |
| ValueInfo.updateHotness(Hotness); |
| // Add the relative block frequency to CalleeInfo if there is no profile |
| // information. |
| if (BFI != nullptr && Hotness == CalleeInfo::HotnessType::Unknown) { |
| uint64_t BBFreq = BFI->getBlockFreq(&BB).getFrequency(); |
| uint64_t EntryFreq = BFI->getEntryFreq(); |
| ValueInfo.updateRelBlockFreq(BBFreq, EntryFreq); |
| } |
| } else { |
| // Skip inline assembly calls. |
| if (CI && CI->isInlineAsm()) |
| continue; |
| // Skip direct calls. |
| if (!CalledValue || isa<Constant>(CalledValue)) |
| continue; |
| |
| // Check if the instruction has a callees metadata. If so, add callees |
| // to CallGraphEdges to reflect the references from the metadata, and |
| // to enable importing for subsequent indirect call promotion and |
| // inlining. |
| if (auto *MD = I.getMetadata(LLVMContext::MD_callees)) { |
| for (auto &Op : MD->operands()) { |
| Function *Callee = mdconst::extract_or_null<Function>(Op); |
| if (Callee) |
| CallGraphEdges[Index.getOrInsertValueInfo(Callee)]; |
| } |
| } |
| |
| uint32_t NumVals, NumCandidates; |
| uint64_t TotalCount; |
| auto CandidateProfileData = |
| ICallAnalysis.getPromotionCandidatesForInstruction( |
| &I, NumVals, TotalCount, NumCandidates); |
| for (auto &Candidate : CandidateProfileData) |
| CallGraphEdges[Index.getOrInsertValueInfo(Candidate.Value)] |
| .updateHotness(getHotness(Candidate.Count, PSI)); |
| } |
| } |
| |
| // Explicit add hot edges to enforce importing for designated GUIDs for |
| // sample PGO, to enable the same inlines as the profiled optimized binary. |
| for (auto &I : F.getImportGUIDs()) |
| CallGraphEdges[Index.getOrInsertValueInfo(I)].updateHotness( |
| ForceSummaryEdgesCold == FunctionSummary::FSHT_All |
| ? CalleeInfo::HotnessType::Cold |
| : CalleeInfo::HotnessType::Critical); |
| |
| bool NonRenamableLocal = isNonRenamableLocal(F); |
| bool NotEligibleForImport = |
| NonRenamableLocal || HasInlineAsmMaybeReferencingInternal || |
| // Inliner doesn't handle variadic functions. |
| // FIXME: refactor this to use the same code that inliner is using. |
| F.isVarArg() || |
| // Don't try to import functions with noinline attribute. |
| F.getAttributes().hasFnAttribute(Attribute::NoInline); |
| GlobalValueSummary::GVFlags Flags(F.getLinkage(), NotEligibleForImport, |
| /* Live = */ false, F.isDSOLocal()); |
| FunctionSummary::FFlags FunFlags{ |
| F.hasFnAttribute(Attribute::ReadNone), |
| F.hasFnAttribute(Attribute::ReadOnly), |
| F.hasFnAttribute(Attribute::NoRecurse), |
| F.returnDoesNotAlias(), |
| }; |
| auto FuncSummary = llvm::make_unique<FunctionSummary>( |
| Flags, NumInsts, FunFlags, RefEdges.takeVector(), |
| CallGraphEdges.takeVector(), TypeTests.takeVector(), |
| TypeTestAssumeVCalls.takeVector(), TypeCheckedLoadVCalls.takeVector(), |
| TypeTestAssumeConstVCalls.takeVector(), |
| TypeCheckedLoadConstVCalls.takeVector()); |
| if (NonRenamableLocal) |
| CantBePromoted.insert(F.getGUID()); |
| Index.addGlobalValueSummary(F, std::move(FuncSummary)); |
| } |
| |
| static void |
| computeVariableSummary(ModuleSummaryIndex &Index, const GlobalVariable &V, |
| DenseSet<GlobalValue::GUID> &CantBePromoted) { |
| SetVector<ValueInfo> RefEdges; |
| SmallPtrSet<const User *, 8> Visited; |
| bool HasBlockAddress = findRefEdges(Index, &V, RefEdges, Visited); |
| bool NonRenamableLocal = isNonRenamableLocal(V); |
| GlobalValueSummary::GVFlags Flags(V.getLinkage(), NonRenamableLocal, |
| /* Live = */ false, V.isDSOLocal()); |
| auto GVarSummary = |
| llvm::make_unique<GlobalVarSummary>(Flags, RefEdges.takeVector()); |
| if (NonRenamableLocal) |
| CantBePromoted.insert(V.getGUID()); |
| if (HasBlockAddress) |
| GVarSummary->setNotEligibleToImport(); |
| Index.addGlobalValueSummary(V, std::move(GVarSummary)); |
| } |
| |
| static void |
| computeAliasSummary(ModuleSummaryIndex &Index, const GlobalAlias &A, |
| DenseSet<GlobalValue::GUID> &CantBePromoted) { |
| bool NonRenamableLocal = isNonRenamableLocal(A); |
| GlobalValueSummary::GVFlags Flags(A.getLinkage(), NonRenamableLocal, |
| /* Live = */ false, A.isDSOLocal()); |
| auto AS = llvm::make_unique<AliasSummary>(Flags); |
| auto *Aliasee = A.getBaseObject(); |
| auto *AliaseeSummary = Index.getGlobalValueSummary(*Aliasee); |
| assert(AliaseeSummary && "Alias expects aliasee summary to be parsed"); |
| AS->setAliasee(AliaseeSummary); |
| if (NonRenamableLocal) |
| CantBePromoted.insert(A.getGUID()); |
| Index.addGlobalValueSummary(A, std::move(AS)); |
| } |
| |
| // Set LiveRoot flag on entries matching the given value name. |
| static void setLiveRoot(ModuleSummaryIndex &Index, StringRef Name) { |
| if (ValueInfo VI = Index.getValueInfo(GlobalValue::getGUID(Name))) |
| for (auto &Summary : VI.getSummaryList()) |
| Summary->setLive(true); |
| } |
| |
| ModuleSummaryIndex llvm::buildModuleSummaryIndex( |
| const Module &M, |
| std::function<BlockFrequencyInfo *(const Function &F)> GetBFICallback, |
| ProfileSummaryInfo *PSI) { |
| assert(PSI); |
| ModuleSummaryIndex Index(/*HaveGVs=*/true); |
| |
| // Identify the local values in the llvm.used and llvm.compiler.used sets, |
| // which should not be exported as they would then require renaming and |
| // promotion, but we may have opaque uses e.g. in inline asm. We collect them |
| // here because we use this information to mark functions containing inline |
| // assembly calls as not importable. |
| SmallPtrSet<GlobalValue *, 8> LocalsUsed; |
| SmallPtrSet<GlobalValue *, 8> Used; |
| // First collect those in the llvm.used set. |
| collectUsedGlobalVariables(M, Used, /*CompilerUsed*/ false); |
| // Next collect those in the llvm.compiler.used set. |
| collectUsedGlobalVariables(M, Used, /*CompilerUsed*/ true); |
| DenseSet<GlobalValue::GUID> CantBePromoted; |
| for (auto *V : Used) { |
| if (V->hasLocalLinkage()) { |
| LocalsUsed.insert(V); |
| CantBePromoted.insert(V->getGUID()); |
| } |
| } |
| |
| bool HasLocalInlineAsmSymbol = false; |
| if (!M.getModuleInlineAsm().empty()) { |
| // Collect the local values defined by module level asm, and set up |
| // summaries for these symbols so that they can be marked as NoRename, |
| // to prevent export of any use of them in regular IR that would require |
| // renaming within the module level asm. Note we don't need to create a |
| // summary for weak or global defs, as they don't need to be flagged as |
| // NoRename, and defs in module level asm can't be imported anyway. |
| // Also, any values used but not defined within module level asm should |
| // be listed on the llvm.used or llvm.compiler.used global and marked as |
| // referenced from there. |
| ModuleSymbolTable::CollectAsmSymbols( |
| M, [&](StringRef Name, object::BasicSymbolRef::Flags Flags) { |
| // Symbols not marked as Weak or Global are local definitions. |
| if (Flags & (object::BasicSymbolRef::SF_Weak | |
| object::BasicSymbolRef::SF_Global)) |
| return; |
| HasLocalInlineAsmSymbol = true; |
| GlobalValue *GV = M.getNamedValue(Name); |
| if (!GV) |
| return; |
| assert(GV->isDeclaration() && "Def in module asm already has definition"); |
| GlobalValueSummary::GVFlags GVFlags(GlobalValue::InternalLinkage, |
| /* NotEligibleToImport = */ true, |
| /* Live = */ true, |
| /* Local */ GV->isDSOLocal()); |
| CantBePromoted.insert(GV->getGUID()); |
| // Create the appropriate summary type. |
| if (Function *F = dyn_cast<Function>(GV)) { |
| std::unique_ptr<FunctionSummary> Summary = |
| llvm::make_unique<FunctionSummary>( |
| GVFlags, 0, |
| FunctionSummary::FFlags{ |
| F->hasFnAttribute(Attribute::ReadNone), |
| F->hasFnAttribute(Attribute::ReadOnly), |
| F->hasFnAttribute(Attribute::NoRecurse), |
| F->returnDoesNotAlias()}, |
| ArrayRef<ValueInfo>{}, ArrayRef<FunctionSummary::EdgeTy>{}, |
| ArrayRef<GlobalValue::GUID>{}, |
| ArrayRef<FunctionSummary::VFuncId>{}, |
| ArrayRef<FunctionSummary::VFuncId>{}, |
| ArrayRef<FunctionSummary::ConstVCall>{}, |
| ArrayRef<FunctionSummary::ConstVCall>{}); |
| Index.addGlobalValueSummary(*GV, std::move(Summary)); |
| } else { |
| std::unique_ptr<GlobalVarSummary> Summary = |
| llvm::make_unique<GlobalVarSummary>(GVFlags, |
| ArrayRef<ValueInfo>{}); |
| Index.addGlobalValueSummary(*GV, std::move(Summary)); |
| } |
| }); |
| } |
| |
| // Compute summaries for all functions defined in module, and save in the |
| // index. |
| for (auto &F : M) { |
| if (F.isDeclaration()) |
| continue; |
| |
| BlockFrequencyInfo *BFI = nullptr; |
| std::unique_ptr<BlockFrequencyInfo> BFIPtr; |
| if (GetBFICallback) |
| BFI = GetBFICallback(F); |
| else if (F.hasProfileData()) { |
| LoopInfo LI{DominatorTree(const_cast<Function &>(F))}; |
| BranchProbabilityInfo BPI{F, LI}; |
| BFIPtr = llvm::make_unique<BlockFrequencyInfo>(F, BPI, LI); |
| BFI = BFIPtr.get(); |
| } |
| |
| computeFunctionSummary(Index, M, F, BFI, PSI, |
| !LocalsUsed.empty() || HasLocalInlineAsmSymbol, |
| CantBePromoted); |
| } |
| |
| // Compute summaries for all variables defined in module, and save in the |
| // index. |
| for (const GlobalVariable &G : M.globals()) { |
| if (G.isDeclaration()) |
| continue; |
| computeVariableSummary(Index, G, CantBePromoted); |
| } |
| |
| // Compute summaries for all aliases defined in module, and save in the |
| // index. |
| for (const GlobalAlias &A : M.aliases()) |
| computeAliasSummary(Index, A, CantBePromoted); |
| |
| for (auto *V : LocalsUsed) { |
| auto *Summary = Index.getGlobalValueSummary(*V); |
| assert(Summary && "Missing summary for global value"); |
| Summary->setNotEligibleToImport(); |
| } |
| |
| // The linker doesn't know about these LLVM produced values, so we need |
| // to flag them as live in the index to ensure index-based dead value |
| // analysis treats them as live roots of the analysis. |
| setLiveRoot(Index, "llvm.used"); |
| setLiveRoot(Index, "llvm.compiler.used"); |
| setLiveRoot(Index, "llvm.global_ctors"); |
| setLiveRoot(Index, "llvm.global_dtors"); |
| setLiveRoot(Index, "llvm.global.annotations"); |
| |
| bool IsThinLTO = true; |
| if (auto *MD = |
| mdconst::extract_or_null<ConstantInt>(M.getModuleFlag("ThinLTO"))) |
| IsThinLTO = MD->getZExtValue(); |
| |
| for (auto &GlobalList : Index) { |
| // Ignore entries for references that are undefined in the current module. |
| if (GlobalList.second.SummaryList.empty()) |
| continue; |
| |
| assert(GlobalList.second.SummaryList.size() == 1 && |
| "Expected module's index to have one summary per GUID"); |
| auto &Summary = GlobalList.second.SummaryList[0]; |
| if (!IsThinLTO) { |
| Summary->setNotEligibleToImport(); |
| continue; |
| } |
| |
| bool AllRefsCanBeExternallyReferenced = |
| llvm::all_of(Summary->refs(), [&](const ValueInfo &VI) { |
| return !CantBePromoted.count(VI.getGUID()); |
| }); |
| if (!AllRefsCanBeExternallyReferenced) { |
| Summary->setNotEligibleToImport(); |
| continue; |
| } |
| |
| if (auto *FuncSummary = dyn_cast<FunctionSummary>(Summary.get())) { |
| bool AllCallsCanBeExternallyReferenced = llvm::all_of( |
| FuncSummary->calls(), [&](const FunctionSummary::EdgeTy &Edge) { |
| return !CantBePromoted.count(Edge.first.getGUID()); |
| }); |
| if (!AllCallsCanBeExternallyReferenced) |
| Summary->setNotEligibleToImport(); |
| } |
| } |
| |
| return Index; |
| } |
| |
| AnalysisKey ModuleSummaryIndexAnalysis::Key; |
| |
| ModuleSummaryIndex |
| ModuleSummaryIndexAnalysis::run(Module &M, ModuleAnalysisManager &AM) { |
| ProfileSummaryInfo &PSI = AM.getResult<ProfileSummaryAnalysis>(M); |
| auto &FAM = AM.getResult<FunctionAnalysisManagerModuleProxy>(M).getManager(); |
| return buildModuleSummaryIndex( |
| M, |
| [&FAM](const Function &F) { |
| return &FAM.getResult<BlockFrequencyAnalysis>( |
| *const_cast<Function *>(&F)); |
| }, |
| &PSI); |
| } |
| |
| char ModuleSummaryIndexWrapperPass::ID = 0; |
| |
| INITIALIZE_PASS_BEGIN(ModuleSummaryIndexWrapperPass, "module-summary-analysis", |
| "Module Summary Analysis", false, true) |
| INITIALIZE_PASS_DEPENDENCY(BlockFrequencyInfoWrapperPass) |
| INITIALIZE_PASS_DEPENDENCY(ProfileSummaryInfoWrapperPass) |
| INITIALIZE_PASS_END(ModuleSummaryIndexWrapperPass, "module-summary-analysis", |
| "Module Summary Analysis", false, true) |
| |
| ModulePass *llvm::createModuleSummaryIndexWrapperPass() { |
| return new ModuleSummaryIndexWrapperPass(); |
| } |
| |
| ModuleSummaryIndexWrapperPass::ModuleSummaryIndexWrapperPass() |
| : ModulePass(ID) { |
| initializeModuleSummaryIndexWrapperPassPass(*PassRegistry::getPassRegistry()); |
| } |
| |
| bool ModuleSummaryIndexWrapperPass::runOnModule(Module &M) { |
| auto &PSI = *getAnalysis<ProfileSummaryInfoWrapperPass>().getPSI(); |
| Index.emplace(buildModuleSummaryIndex( |
| M, |
| [this](const Function &F) { |
| return &(this->getAnalysis<BlockFrequencyInfoWrapperPass>( |
| *const_cast<Function *>(&F)) |
| .getBFI()); |
| }, |
| &PSI)); |
| return false; |
| } |
| |
| bool ModuleSummaryIndexWrapperPass::doFinalization(Module &M) { |
| Index.reset(); |
| return false; |
| } |
| |
| void ModuleSummaryIndexWrapperPass::getAnalysisUsage(AnalysisUsage &AU) const { |
| AU.setPreservesAll(); |
| AU.addRequired<BlockFrequencyInfoWrapperPass>(); |
| AU.addRequired<ProfileSummaryInfoWrapperPass>(); |
| } |