diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2024-07-27 23:34:35 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2024-10-23 18:26:01 +0000 |
commit | 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583 (patch) | |
tree | 6cf5ab1f05330c6773b1f3f64799d56a9c7a1faa /contrib/llvm-project/llvm/lib/Analysis/ModuleSummaryAnalysis.cpp | |
parent | 6b9f7133aba44189d9625c352bc2c2a59baf18ef (diff) | |
parent | ac9a064cb179f3425b310fa2847f8764ac970a4d (diff) |
Diffstat (limited to 'contrib/llvm-project/llvm/lib/Analysis/ModuleSummaryAnalysis.cpp')
-rw-r--r-- | contrib/llvm-project/llvm/lib/Analysis/ModuleSummaryAnalysis.cpp | 126 |
1 files changed, 101 insertions, 25 deletions
diff --git a/contrib/llvm-project/llvm/lib/Analysis/ModuleSummaryAnalysis.cpp b/contrib/llvm-project/llvm/lib/Analysis/ModuleSummaryAnalysis.cpp index 1f15e9478324..e9490ccba821 100644 --- a/contrib/llvm-project/llvm/lib/Analysis/ModuleSummaryAnalysis.cpp +++ b/contrib/llvm-project/llvm/lib/Analysis/ModuleSummaryAnalysis.cpp @@ -22,6 +22,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/Analysis/BlockFrequencyInfo.h" #include "llvm/Analysis/BranchProbabilityInfo.h" +#include "llvm/Analysis/ConstantFolding.h" #include "llvm/Analysis/IndirectCallPromotionAnalysis.h" #include "llvm/Analysis/LoopInfo.h" #include "llvm/Analysis/MemoryProfileInfo.h" @@ -82,6 +83,10 @@ static cl::opt<std::string> ModuleSummaryDotFile( extern cl::opt<bool> ScalePartialSampleProfileWorkingSetSize; +extern cl::opt<unsigned> MaxNumVTableAnnotations; + +extern cl::opt<bool> MemProfReportHintedSizes; + // 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). @@ -92,9 +97,12 @@ extern cl::opt<bool> ScalePartialSampleProfileWorkingSetSize; // 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. +// Set `RefLocalLinkageIFunc` to true if the analyzed value references a +// local-linkage ifunc. static bool findRefEdges(ModuleSummaryIndex &Index, const User *CurUser, SetVector<ValueInfo, std::vector<ValueInfo>> &RefEdges, - SmallPtrSet<const User *, 8> &Visited) { + SmallPtrSet<const User *, 8> &Visited, + bool &RefLocalLinkageIFunc) { bool HasBlockAddress = false; SmallVector<const User *, 32> Worklist; if (Visited.insert(CurUser).second) @@ -116,14 +124,37 @@ static bool findRefEdges(ModuleSummaryIndex &Index, const User *CurUser, // 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 (!(CB && CB->isCallee(&OI))) + if (!(CB && CB->isCallee(&OI))) { + // If an ifunc has local linkage, do not add it into ref edges, and + // sets `RefLocalLinkageIFunc` to true. The referencer is not eligible + // for import. An ifunc doesn't have summary and ThinLTO cannot + // promote it; importing the referencer may cause linkage errors. + if (auto *GI = dyn_cast_if_present<GlobalIFunc>(GV); + GI && GI->hasLocalLinkage()) { + RefLocalLinkageIFunc = true; + continue; + } RefEdges.insert(Index.getOrInsertValueInfo(GV)); + } continue; } if (Visited.insert(Operand).second) Worklist.push_back(Operand); } } + + const Instruction *I = dyn_cast<Instruction>(CurUser); + if (I) { + uint64_t TotalCount = 0; + // MaxNumVTableAnnotations is the maximum number of vtables annotated on + // the instruction. + auto ValueDataArray = getValueProfDataFromInst( + *I, IPVK_VTableTarget, MaxNumVTableAnnotations, TotalCount); + + for (const auto &V : ValueDataArray) + RefEdges.insert(Index.getOrInsertValueInfo(/* VTableGUID = */ + V.Value)); + } return HasBlockAddress; } @@ -292,7 +323,8 @@ static void computeFunctionSummary( // Add personality function, prefix data and prologue data to function's ref // list. - findRefEdges(Index, &F, RefEdges, Visited); + bool HasLocalIFuncCallOrRef = false; + findRefEdges(Index, &F, RefEdges, Visited, HasLocalIFuncCallOrRef); std::vector<const Instruction *> NonVolatileLoads; std::vector<const Instruction *> NonVolatileStores; @@ -305,7 +337,6 @@ static void computeFunctionSummary( bool HasInlineAsmMaybeReferencingInternal = false; bool HasIndirBranchToBlockAddress = false; - bool HasIFuncCall = false; bool HasUnknownCall = false; bool MayThrow = false; for (const BasicBlock &BB : F) { @@ -351,11 +382,11 @@ static void computeFunctionSummary( // of calling it we should add GV to RefEdges directly. RefEdges.insert(Index.getOrInsertValueInfo(GV)); else if (auto *U = dyn_cast<User>(Stored)) - findRefEdges(Index, U, RefEdges, Visited); + findRefEdges(Index, U, RefEdges, Visited, HasLocalIFuncCallOrRef); continue; } } - findRefEdges(Index, &I, RefEdges, Visited); + findRefEdges(Index, &I, RefEdges, Visited, HasLocalIFuncCallOrRef); const auto *CB = dyn_cast<CallBase>(&I); if (!CB) { if (I.mayThrow()) @@ -429,7 +460,7 @@ static void computeFunctionSummary( // Non-local ifunc is not cloned and does not have the issue. if (auto *GI = dyn_cast_if_present<GlobalIFunc>(CalledValue)) if (GI->hasLocalLinkage()) - HasIFuncCall = true; + HasLocalIFuncCallOrRef = true; // Skip inline assembly calls. if (CI && CI->isInlineAsm()) continue; @@ -449,11 +480,11 @@ static void computeFunctionSummary( } } - uint32_t NumVals, NumCandidates; + uint32_t NumCandidates; uint64_t TotalCount; auto CandidateProfileData = - ICallAnalysis.getPromotionCandidatesForInstruction( - &I, NumVals, TotalCount, NumCandidates); + ICallAnalysis.getPromotionCandidatesForInstruction(&I, TotalCount, + NumCandidates); for (const auto &Candidate : CandidateProfileData) CallGraphEdges[Index.getOrInsertValueInfo(Candidate.Value)] .updateHotness(getHotness(Candidate.Count, PSI)); @@ -488,6 +519,7 @@ static void computeFunctionSummary( auto *MemProfMD = I.getMetadata(LLVMContext::MD_memprof); if (MemProfMD) { std::vector<MIBInfo> MIBs; + std::vector<uint64_t> TotalSizes; for (auto &MDOp : MemProfMD->operands()) { auto *MIBMD = cast<const MDNode>(MDOp); MDNode *StackNode = getMIBStackNode(MIBMD); @@ -507,8 +539,17 @@ static void computeFunctionSummary( } MIBs.push_back( MIBInfo(getMIBAllocType(MIBMD), std::move(StackIdIndices))); + if (MemProfReportHintedSizes) { + auto TotalSize = getMIBTotalSize(MIBMD); + assert(TotalSize); + TotalSizes.push_back(TotalSize); + } } Allocs.push_back(AllocInfo(std::move(MIBs))); + if (MemProfReportHintedSizes) { + assert(Allocs.back().MIBs.size() == TotalSizes.size()); + Allocs.back().TotalSizes = std::move(TotalSizes); + } } else if (!InstCallsite.empty()) { SmallVector<unsigned> StackIdIndices; for (auto StackId : InstCallsite) @@ -534,7 +575,7 @@ static void computeFunctionSummary( SmallPtrSet<const User *, 8> &Cache) { for (const auto *I : Instrs) { Cache.erase(I); - findRefEdges(Index, I, Edges, Cache); + findRefEdges(Index, I, Edges, Cache, HasLocalIFuncCallOrRef); } }; @@ -610,12 +651,13 @@ static void computeFunctionSummary( #endif bool NonRenamableLocal = isNonRenamableLocal(F); - bool NotEligibleForImport = NonRenamableLocal || - HasInlineAsmMaybeReferencingInternal || - HasIndirBranchToBlockAddress || HasIFuncCall; + bool NotEligibleForImport = + NonRenamableLocal || HasInlineAsmMaybeReferencingInternal || + HasIndirBranchToBlockAddress || HasLocalIFuncCallOrRef; GlobalValueSummary::GVFlags Flags( F.getLinkage(), F.getVisibility(), NotEligibleForImport, - /* Live = */ false, F.isDSOLocal(), F.canBeOmittedFromSymbolTable()); + /* Live = */ false, F.isDSOLocal(), F.canBeOmittedFromSymbolTable(), + GlobalValueSummary::ImportKind::Definition); FunctionSummary::FFlags FunFlags{ F.doesNotAccessMemory(), F.onlyReadsMemory() && !F.doesNotAccessMemory(), F.hasFnAttribute(Attribute::NoRecurse), F.returnDoesNotAlias(), @@ -647,7 +689,8 @@ static void computeFunctionSummary( /// within the initializer. static void findFuncPointers(const Constant *I, uint64_t StartingOffset, const Module &M, ModuleSummaryIndex &Index, - VTableFuncList &VTableFuncs) { + VTableFuncList &VTableFuncs, + const GlobalVariable &OrigGV) { // First check if this is a function pointer. if (I->getType()->isPointerTy()) { auto C = I->stripPointerCasts(); @@ -675,7 +718,7 @@ static void findFuncPointers(const Constant *I, uint64_t StartingOffset, auto Offset = SL->getElementOffset(EI.index()); unsigned Op = SL->getElementContainingOffset(Offset); findFuncPointers(cast<Constant>(I->getOperand(Op)), - StartingOffset + Offset, M, Index, VTableFuncs); + StartingOffset + Offset, M, Index, VTableFuncs, OrigGV); } } else if (auto *C = dyn_cast<ConstantArray>(I)) { ArrayType *ATy = C->getType(); @@ -683,7 +726,34 @@ static void findFuncPointers(const Constant *I, uint64_t StartingOffset, uint64_t EltSize = DL.getTypeAllocSize(EltTy); for (unsigned i = 0, e = ATy->getNumElements(); i != e; ++i) { findFuncPointers(cast<Constant>(I->getOperand(i)), - StartingOffset + i * EltSize, M, Index, VTableFuncs); + StartingOffset + i * EltSize, M, Index, VTableFuncs, + OrigGV); + } + } else if (const auto *CE = dyn_cast<ConstantExpr>(I)) { + // For relative vtables, the next sub-component should be a trunc. + if (CE->getOpcode() != Instruction::Trunc || + !(CE = dyn_cast<ConstantExpr>(CE->getOperand(0)))) + return; + + // If this constant can be reduced to the offset between a function and a + // global, then we know this is a valid virtual function if the RHS is the + // original vtable we're scanning through. + if (CE->getOpcode() == Instruction::Sub) { + GlobalValue *LHS, *RHS; + APSInt LHSOffset, RHSOffset; + if (IsConstantOffsetFromGlobal(CE->getOperand(0), LHS, LHSOffset, DL) && + IsConstantOffsetFromGlobal(CE->getOperand(1), RHS, RHSOffset, DL) && + RHS == &OrigGV && + + // For relative vtables, this component should point to the callable + // function without any offsets. + LHSOffset == 0 && + + // Also, the RHS should always point to somewhere within the vtable. + RHSOffset <= + static_cast<uint64_t>(DL.getTypeAllocSize(OrigGV.getInitializer()->getType()))) { + findFuncPointers(LHS, StartingOffset, M, Index, VTableFuncs, OrigGV); + } } } } @@ -696,7 +766,7 @@ static void computeVTableFuncs(ModuleSummaryIndex &Index, return; findFuncPointers(V.getInitializer(), /*StartingOffset=*/0, M, Index, - VTableFuncs); + VTableFuncs, V); #ifndef NDEBUG // Validate that the VTableFuncs list is ordered by offset. @@ -737,11 +807,15 @@ static void computeVariableSummary(ModuleSummaryIndex &Index, SmallVectorImpl<MDNode *> &Types) { SetVector<ValueInfo, std::vector<ValueInfo>> RefEdges; SmallPtrSet<const User *, 8> Visited; - bool HasBlockAddress = findRefEdges(Index, &V, RefEdges, Visited); + bool RefLocalIFunc = false; + bool HasBlockAddress = + findRefEdges(Index, &V, RefEdges, Visited, RefLocalIFunc); + const bool NotEligibleForImport = (HasBlockAddress || RefLocalIFunc); bool NonRenamableLocal = isNonRenamableLocal(V); GlobalValueSummary::GVFlags Flags( V.getLinkage(), V.getVisibility(), NonRenamableLocal, - /* Live = */ false, V.isDSOLocal(), V.canBeOmittedFromSymbolTable()); + /* Live = */ false, V.isDSOLocal(), V.canBeOmittedFromSymbolTable(), + GlobalValueSummary::Definition); VTableFuncList VTableFuncs; // If splitting is not enabled, then we compute the summary information @@ -770,7 +844,7 @@ static void computeVariableSummary(ModuleSummaryIndex &Index, RefEdges.takeVector()); if (NonRenamableLocal) CantBePromoted.insert(V.getGUID()); - if (HasBlockAddress) + if (NotEligibleForImport) GVarSummary->setNotEligibleToImport(); if (!VTableFuncs.empty()) GVarSummary->setVTableFuncs(VTableFuncs); @@ -787,7 +861,8 @@ static void computeAliasSummary(ModuleSummaryIndex &Index, const GlobalAlias &A, bool NonRenamableLocal = isNonRenamableLocal(A); GlobalValueSummary::GVFlags Flags( A.getLinkage(), A.getVisibility(), NonRenamableLocal, - /* Live = */ false, A.isDSOLocal(), A.canBeOmittedFromSymbolTable()); + /* Live = */ false, A.isDSOLocal(), A.canBeOmittedFromSymbolTable(), + GlobalValueSummary::Definition); auto AS = std::make_unique<AliasSummary>(Flags); auto AliaseeVI = Index.getValueInfo(Aliasee->getGUID()); assert(AliaseeVI && "Alias expects aliasee summary to be available"); @@ -867,7 +942,8 @@ ModuleSummaryIndex llvm::buildModuleSummaryIndex( GlobalValue::InternalLinkage, GlobalValue::DefaultVisibility, /* NotEligibleToImport = */ true, /* Live = */ true, - /* Local */ GV->isDSOLocal(), GV->canBeOmittedFromSymbolTable()); + /* Local */ GV->isDSOLocal(), GV->canBeOmittedFromSymbolTable(), + GlobalValueSummary::Definition); CantBePromoted.insert(GV->getGUID()); // Create the appropriate summary type. if (Function *F = dyn_cast<Function>(GV)) { @@ -1006,7 +1082,7 @@ ModuleSummaryIndex llvm::buildModuleSummaryIndex( if (!ModuleSummaryDotFile.empty()) { std::error_code EC; - raw_fd_ostream OSDot(ModuleSummaryDotFile, EC, sys::fs::OpenFlags::OF_None); + raw_fd_ostream OSDot(ModuleSummaryDotFile, EC, sys::fs::OpenFlags::OF_Text); if (EC) report_fatal_error(Twine("Failed to open dot file ") + ModuleSummaryDotFile + ": " + EC.message() + "\n"); |