diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2022-03-20 11:40:34 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2022-05-14 11:43:05 +0000 |
commit | 349cc55c9796c4596a5b9904cd3281af295f878f (patch) | |
tree | 410c5a785075730a35f1272ca6a7adf72222ad03 /contrib/llvm-project/llvm/lib/Transforms/IPO/FunctionAttrs.cpp | |
parent | cb2ae6163174b90e999326ecec3699ee093a5d43 (diff) | |
parent | c0981da47d5696fe36474fcf86b4ce03ae3ff818 (diff) |
Diffstat (limited to 'contrib/llvm-project/llvm/lib/Transforms/IPO/FunctionAttrs.cpp')
-rw-r--r-- | contrib/llvm-project/llvm/lib/Transforms/IPO/FunctionAttrs.cpp | 440 |
1 files changed, 323 insertions, 117 deletions
diff --git a/contrib/llvm-project/llvm/lib/Transforms/IPO/FunctionAttrs.cpp b/contrib/llvm-project/llvm/lib/Transforms/IPO/FunctionAttrs.cpp index ca8660a98ded..cde78713b554 100644 --- a/contrib/llvm-project/llvm/lib/Transforms/IPO/FunctionAttrs.cpp +++ b/contrib/llvm-project/llvm/lib/Transforms/IPO/FunctionAttrs.cpp @@ -14,10 +14,12 @@ #include "llvm/Transforms/IPO/FunctionAttrs.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SCCIterator.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/SmallSet.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/Statistic.h" #include "llvm/Analysis/AssumptionCache.h" @@ -82,6 +84,11 @@ STATISTIC(NumNoFree, "Number of functions marked as nofree"); STATISTIC(NumWillReturn, "Number of functions marked as willreturn"); STATISTIC(NumNoSync, "Number of functions marked as nosync"); +STATISTIC(NumThinLinkNoRecurse, + "Number of functions marked as norecurse during thinlink"); +STATISTIC(NumThinLinkNoUnwind, + "Number of functions marked as nounwind during thinlink"); + static cl::opt<bool> EnableNonnullArgPropagation( "enable-nonnull-arg-prop", cl::init(true), cl::Hidden, cl::desc("Try to propagate nonnull argument attributes from callsites to " @@ -95,6 +102,10 @@ static cl::opt<bool> DisableNoFreeInference( "disable-nofree-inference", cl::Hidden, cl::desc("Stop inferring nofree attribute during function-attrs pass")); +static cl::opt<bool> DisableThinLTOPropagation( + "disable-thinlto-funcattrs", cl::init(true), cl::Hidden, + cl::desc("Don't propagate function-attrs in thinLTO")); + namespace { using SCCNodeSet = SmallSetVector<Function *, 8>; @@ -131,12 +142,10 @@ static MemoryAccessKind checkFunctionMemoryAccess(Function &F, bool ThisBody, // Scan the function body for instructions that may read or write memory. bool ReadsMemory = false; bool WritesMemory = false; - for (inst_iterator II = inst_begin(F), E = inst_end(F); II != E; ++II) { - Instruction *I = &*II; - + for (Instruction &I : instructions(F)) { // Some instructions can be ignored even if they read or write memory. // Detect these now, skipping to the next instruction if one is found. - if (auto *Call = dyn_cast<CallBase>(I)) { + if (auto *Call = dyn_cast<CallBase>(&I)) { // Ignore calls to functions in the same SCC, as long as the call sites // don't have operand bundles. Calls with operand bundles are allowed to // have memory effects not described by the memory effects of the call @@ -170,14 +179,13 @@ static MemoryAccessKind checkFunctionMemoryAccess(Function &F, bool ThisBody, // Check whether all pointer arguments point to local memory, and // ignore calls that only access local memory. - for (auto CI = Call->arg_begin(), CE = Call->arg_end(); CI != CE; ++CI) { - Value *Arg = *CI; + for (const Use &U : Call->args()) { + const Value *Arg = U; if (!Arg->getType()->isPtrOrPtrVectorTy()) continue; - AAMDNodes AAInfo; - I->getAAMetadata(AAInfo); - MemoryLocation Loc = MemoryLocation::getBeforeOrAfter(Arg, AAInfo); + MemoryLocation Loc = + MemoryLocation::getBeforeOrAfter(Arg, I.getAAMetadata()); // Skip accesses to local or constant memory as they don't impact the // externally visible mod/ref behavior. @@ -192,21 +200,21 @@ static MemoryAccessKind checkFunctionMemoryAccess(Function &F, bool ThisBody, ReadsMemory = true; } continue; - } else if (LoadInst *LI = dyn_cast<LoadInst>(I)) { + } else if (LoadInst *LI = dyn_cast<LoadInst>(&I)) { // Ignore non-volatile loads from local memory. (Atomic is okay here.) if (!LI->isVolatile()) { MemoryLocation Loc = MemoryLocation::get(LI); if (AAR.pointsToConstantMemory(Loc, /*OrLocal=*/true)) continue; } - } else if (StoreInst *SI = dyn_cast<StoreInst>(I)) { + } else if (StoreInst *SI = dyn_cast<StoreInst>(&I)) { // Ignore non-volatile stores to local memory. (Atomic is okay here.) if (!SI->isVolatile()) { MemoryLocation Loc = MemoryLocation::get(SI); if (AAR.pointsToConstantMemory(Loc, /*OrLocal=*/true)) continue; } - } else if (VAArgInst *VI = dyn_cast<VAArgInst>(I)) { + } else if (VAArgInst *VI = dyn_cast<VAArgInst>(&I)) { // Ignore vaargs on local memory. MemoryLocation Loc = MemoryLocation::get(VI); if (AAR.pointsToConstantMemory(Loc, /*OrLocal=*/true)) @@ -217,10 +225,10 @@ static MemoryAccessKind checkFunctionMemoryAccess(Function &F, bool ThisBody, // read or write memory. // // Writes memory, remember that. - WritesMemory |= I->mayWriteToMemory(); + WritesMemory |= I.mayWriteToMemory(); // If this instruction may read memory, remember that. - ReadsMemory |= I->mayReadFromMemory(); + ReadsMemory |= I.mayReadFromMemory(); } if (WritesMemory) { @@ -240,7 +248,8 @@ MemoryAccessKind llvm::computeFunctionBodyMemoryAccess(Function &F, /// Deduce readonly/readnone attributes for the SCC. template <typename AARGetterT> -static bool addReadAttrs(const SCCNodeSet &SCCNodes, AARGetterT &&AARGetter) { +static void addReadAttrs(const SCCNodeSet &SCCNodes, AARGetterT &&AARGetter, + SmallSet<Function *, 8> &Changed) { // Check if any of the functions in the SCC read or write memory. If they // write memory then they can't be marked readnone or readonly. bool ReadsMemory = false; @@ -255,7 +264,7 @@ static bool addReadAttrs(const SCCNodeSet &SCCNodes, AARGetterT &&AARGetter) { switch (checkFunctionMemoryAccess(*F, F->hasExactDefinition(), AAR, SCCNodes)) { case MAK_MayWrite: - return false; + return; case MAK_ReadOnly: ReadsMemory = true; break; @@ -271,11 +280,10 @@ static bool addReadAttrs(const SCCNodeSet &SCCNodes, AARGetterT &&AARGetter) { // If the SCC contains both functions that read and functions that write, then // we cannot add readonly attributes. if (ReadsMemory && WritesMemory) - return false; + return; // Success! Functions in this SCC do not access memory, or only read memory. // Give them the appropriate attribute. - bool MadeChange = false; for (Function *F : SCCNodes) { if (F->doesNotAccessMemory()) @@ -289,7 +297,7 @@ static bool addReadAttrs(const SCCNodeSet &SCCNodes, AARGetterT &&AARGetter) { if (F->doesNotReadMemory() && WritesMemory) continue; - MadeChange = true; + Changed.insert(F); // Clear out any existing attributes. AttrBuilder AttrsToRemove; @@ -303,7 +311,7 @@ static bool addReadAttrs(const SCCNodeSet &SCCNodes, AARGetterT &&AARGetter) { AttrsToRemove.addAttribute(Attribute::InaccessibleMemOnly); AttrsToRemove.addAttribute(Attribute::InaccessibleMemOrArgMemOnly); } - F->removeAttributes(AttributeList::FunctionIndex, AttrsToRemove); + F->removeFnAttrs(AttrsToRemove); // Add in the new attribute. if (WritesMemory && !ReadsMemory) @@ -318,8 +326,195 @@ static bool addReadAttrs(const SCCNodeSet &SCCNodes, AARGetterT &&AARGetter) { else ++NumReadNone; } +} + +// Compute definitive function attributes for a function taking into account +// prevailing definitions and linkage types +static FunctionSummary *calculatePrevailingSummary( + ValueInfo VI, + DenseMap<ValueInfo, FunctionSummary *> &CachedPrevailingSummary, + function_ref<bool(GlobalValue::GUID, const GlobalValueSummary *)> + IsPrevailing) { + + if (CachedPrevailingSummary.count(VI)) + return CachedPrevailingSummary[VI]; + + /// At this point, prevailing symbols have been resolved. The following leads + /// to returning a conservative result: + /// - Multiple instances with local linkage. Normally local linkage would be + /// unique per module + /// as the GUID includes the module path. We could have a guid alias if + /// there wasn't any distinguishing path when each file was compiled, but + /// that should be rare so we'll punt on those. + + /// These next 2 cases should not happen and will assert: + /// - Multiple instances with external linkage. This should be caught in + /// symbol resolution + /// - Non-existent FunctionSummary for Aliasee. This presents a hole in our + /// knowledge meaning we have to go conservative. + + /// Otherwise, we calculate attributes for a function as: + /// 1. If we have a local linkage, take its attributes. If there's somehow + /// multiple, bail and go conservative. + /// 2. If we have an external/WeakODR/LinkOnceODR linkage check that it is + /// prevailing, take its attributes. + /// 3. If we have a Weak/LinkOnce linkage the copies can have semantic + /// differences. However, if the prevailing copy is known it will be used + /// so take its attributes. If the prevailing copy is in a native file + /// all IR copies will be dead and propagation will go conservative. + /// 4. AvailableExternally summaries without a prevailing copy are known to + /// occur in a couple of circumstances: + /// a. An internal function gets imported due to its caller getting + /// imported, it becomes AvailableExternally but no prevailing + /// definition exists. Because it has to get imported along with its + /// caller the attributes will be captured by propagating on its + /// caller. + /// b. C++11 [temp.explicit]p10 can generate AvailableExternally + /// definitions of explicitly instanced template declarations + /// for inlining which are ultimately dropped from the TU. Since this + /// is localized to the TU the attributes will have already made it to + /// the callers. + /// These are edge cases and already captured by their callers so we + /// ignore these for now. If they become relevant to optimize in the + /// future this can be revisited. + /// 5. Otherwise, go conservative. + + CachedPrevailingSummary[VI] = nullptr; + FunctionSummary *Local = nullptr; + FunctionSummary *Prevailing = nullptr; + + for (const auto &GVS : VI.getSummaryList()) { + if (!GVS->isLive()) + continue; + + FunctionSummary *FS = dyn_cast<FunctionSummary>(GVS->getBaseObject()); + // Virtual and Unknown (e.g. indirect) calls require going conservative + if (!FS || FS->fflags().HasUnknownCall) + return nullptr; + + const auto &Linkage = GVS->linkage(); + if (GlobalValue::isLocalLinkage(Linkage)) { + if (Local) { + LLVM_DEBUG( + dbgs() + << "ThinLTO FunctionAttrs: Multiple Local Linkage, bailing on " + "function " + << VI.name() << " from " << FS->modulePath() << ". Previous module " + << Local->modulePath() << "\n"); + return nullptr; + } + Local = FS; + } else if (GlobalValue::isExternalLinkage(Linkage)) { + assert(IsPrevailing(VI.getGUID(), GVS.get())); + Prevailing = FS; + break; + } else if (GlobalValue::isWeakODRLinkage(Linkage) || + GlobalValue::isLinkOnceODRLinkage(Linkage) || + GlobalValue::isWeakAnyLinkage(Linkage) || + GlobalValue::isLinkOnceAnyLinkage(Linkage)) { + if (IsPrevailing(VI.getGUID(), GVS.get())) { + Prevailing = FS; + break; + } + } else if (GlobalValue::isAvailableExternallyLinkage(Linkage)) { + // TODO: Handle these cases if they become meaningful + continue; + } + } + + if (Local) { + assert(!Prevailing); + CachedPrevailingSummary[VI] = Local; + } else if (Prevailing) { + assert(!Local); + CachedPrevailingSummary[VI] = Prevailing; + } - return MadeChange; + return CachedPrevailingSummary[VI]; +} + +bool llvm::thinLTOPropagateFunctionAttrs( + ModuleSummaryIndex &Index, + function_ref<bool(GlobalValue::GUID, const GlobalValueSummary *)> + IsPrevailing) { + // TODO: implement addNoAliasAttrs once + // there's more information about the return type in the summary + if (DisableThinLTOPropagation) + return false; + + DenseMap<ValueInfo, FunctionSummary *> CachedPrevailingSummary; + bool Changed = false; + + auto PropagateAttributes = [&](std::vector<ValueInfo> &SCCNodes) { + // Assume we can propagate unless we discover otherwise + FunctionSummary::FFlags InferredFlags; + InferredFlags.NoRecurse = (SCCNodes.size() == 1); + InferredFlags.NoUnwind = true; + + for (auto &V : SCCNodes) { + FunctionSummary *CallerSummary = + calculatePrevailingSummary(V, CachedPrevailingSummary, IsPrevailing); + + // Function summaries can fail to contain information such as declarations + if (!CallerSummary) + return; + + if (CallerSummary->fflags().MayThrow) + InferredFlags.NoUnwind = false; + + for (const auto &Callee : CallerSummary->calls()) { + FunctionSummary *CalleeSummary = calculatePrevailingSummary( + Callee.first, CachedPrevailingSummary, IsPrevailing); + + if (!CalleeSummary) + return; + + if (!CalleeSummary->fflags().NoRecurse) + InferredFlags.NoRecurse = false; + + if (!CalleeSummary->fflags().NoUnwind) + InferredFlags.NoUnwind = false; + + if (!InferredFlags.NoUnwind && !InferredFlags.NoRecurse) + break; + } + } + + if (InferredFlags.NoUnwind || InferredFlags.NoRecurse) { + Changed = true; + for (auto &V : SCCNodes) { + if (InferredFlags.NoRecurse) { + LLVM_DEBUG(dbgs() << "ThinLTO FunctionAttrs: Propagated NoRecurse to " + << V.name() << "\n"); + ++NumThinLinkNoRecurse; + } + + if (InferredFlags.NoUnwind) { + LLVM_DEBUG(dbgs() << "ThinLTO FunctionAttrs: Propagated NoUnwind to " + << V.name() << "\n"); + ++NumThinLinkNoUnwind; + } + + for (auto &S : V.getSummaryList()) { + if (auto *FS = dyn_cast<FunctionSummary>(S.get())) { + if (InferredFlags.NoRecurse) + FS->setNoRecurse(); + + if (InferredFlags.NoUnwind) + FS->setNoUnwind(); + } + } + } + } + }; + + // Call propagation functions on each SCC in the Index + for (scc_iterator<ModuleSummaryIndex *> I = scc_begin(&Index); !I.isAtEnd(); + ++I) { + std::vector<ValueInfo> Nodes(*I); + PropagateAttributes(Nodes); + } + return Changed; } namespace { @@ -395,7 +590,7 @@ struct ArgumentUsesTracker : public CaptureTracker { assert(UseIndex < CB->data_operands_size() && "Indirect function calls should have been filtered above!"); - if (UseIndex >= CB->getNumArgOperands()) { + if (UseIndex >= CB->arg_size()) { // Data operand, but not a argument operand -- must be a bundle operand assert(CB->hasOperandBundles() && "Must be!"); @@ -530,7 +725,7 @@ determinePointerReadAttrs(Argument *A, assert(UseIndex < CB.data_operands_size() && "Data operand use expected!"); - bool IsOperandBundleUse = UseIndex >= CB.getNumArgOperands(); + bool IsOperandBundleUse = UseIndex >= CB.arg_size(); if (UseIndex >= F->arg_size() && !IsOperandBundleUse) { assert(F->isVarArg() && "More params than args in non-varargs call"); @@ -581,9 +776,8 @@ determinePointerReadAttrs(Argument *A, } /// Deduce returned attributes for the SCC. -static bool addArgumentReturnedAttrs(const SCCNodeSet &SCCNodes) { - bool Changed = false; - +static void addArgumentReturnedAttrs(const SCCNodeSet &SCCNodes, + SmallSet<Function *, 8> &Changed) { // Check each function in turn, determining if an argument is always returned. for (Function *F : SCCNodes) { // We can infer and propagate function attributes only when we know that the @@ -623,11 +817,9 @@ static bool addArgumentReturnedAttrs(const SCCNodeSet &SCCNodes) { auto *A = cast<Argument>(RetArg); A->addAttr(Attribute::Returned); ++NumReturned; - Changed = true; + Changed.insert(F); } } - - return Changed; } /// If a callsite has arguments that are also arguments to the parent function, @@ -693,9 +885,8 @@ static bool addReadAttr(Argument *A, Attribute::AttrKind R) { } /// Deduce nocapture attributes for the SCC. -static bool addArgumentAttrs(const SCCNodeSet &SCCNodes) { - bool Changed = false; - +static void addArgumentAttrs(const SCCNodeSet &SCCNodes, + SmallSet<Function *, 8> &Changed) { ArgumentGraph AG; // Check each function in turn, determining which pointer arguments are not @@ -707,7 +898,8 @@ static bool addArgumentAttrs(const SCCNodeSet &SCCNodes) { if (!F->hasExactDefinition()) continue; - Changed |= addArgumentAttrsFromCallsites(*F); + if (addArgumentAttrsFromCallsites(*F)) + Changed.insert(F); // Functions that are readonly (or readnone) and nounwind and don't return // a value can't capture arguments. Don't analyze them. @@ -718,7 +910,7 @@ static bool addArgumentAttrs(const SCCNodeSet &SCCNodes) { if (A->getType()->isPointerTy() && !A->hasNoCaptureAttr()) { A->addAttr(Attribute::NoCapture); ++NumNoCapture; - Changed = true; + Changed.insert(F); } } continue; @@ -737,7 +929,7 @@ static bool addArgumentAttrs(const SCCNodeSet &SCCNodes) { // If it's trivially not captured, mark it nocapture now. A->addAttr(Attribute::NoCapture); ++NumNoCapture; - Changed = true; + Changed.insert(F); } else { // If it's not trivially captured and not trivially not captured, // then it must be calling into another function in our SCC. Save @@ -761,7 +953,8 @@ static bool addArgumentAttrs(const SCCNodeSet &SCCNodes) { Self.insert(&*A); Attribute::AttrKind R = determinePointerReadAttrs(&*A, Self); if (R != Attribute::None) - Changed = addReadAttr(A, R); + if (addReadAttr(A, R)) + Changed.insert(F); } } } @@ -785,7 +978,7 @@ static bool addArgumentAttrs(const SCCNodeSet &SCCNodes) { Argument *A = ArgumentSCC[0]->Definition; A->addAttr(Attribute::NoCapture); ++NumNoCapture; - Changed = true; + Changed.insert(A->getParent()); } continue; } @@ -827,7 +1020,7 @@ static bool addArgumentAttrs(const SCCNodeSet &SCCNodes) { Argument *A = ArgumentSCC[i]->Definition; A->addAttr(Attribute::NoCapture); ++NumNoCapture; - Changed = true; + Changed.insert(A->getParent()); } // We also want to compute readonly/readnone. With a small number of false @@ -858,12 +1051,11 @@ static bool addArgumentAttrs(const SCCNodeSet &SCCNodes) { if (ReadAttr != Attribute::None) { for (unsigned i = 0, e = ArgumentSCC.size(); i != e; ++i) { Argument *A = ArgumentSCC[i]->Definition; - Changed = addReadAttr(A, ReadAttr); + if (addReadAttr(A, ReadAttr)) + Changed.insert(A->getParent()); } } } - - return Changed; } /// Tests whether a function is "malloc-like". @@ -934,7 +1126,8 @@ static bool isFunctionMallocLike(Function *F, const SCCNodeSet &SCCNodes) { } /// Deduce noalias attributes for the SCC. -static bool addNoAliasAttrs(const SCCNodeSet &SCCNodes) { +static void addNoAliasAttrs(const SCCNodeSet &SCCNodes, + SmallSet<Function *, 8> &Changed) { // Check each function in turn, determining which functions return noalias // pointers. for (Function *F : SCCNodes) { @@ -946,7 +1139,7 @@ static bool addNoAliasAttrs(const SCCNodeSet &SCCNodes) { // definition we'll get at link time is *exactly* the definition we see now. // For more details, see GlobalValue::mayBeDerefined. if (!F->hasExactDefinition()) - return false; + return; // We annotate noalias return values, which are only applicable to // pointer types. @@ -954,10 +1147,9 @@ static bool addNoAliasAttrs(const SCCNodeSet &SCCNodes) { continue; if (!isFunctionMallocLike(F, SCCNodes)) - return false; + return; } - bool MadeChange = false; for (Function *F : SCCNodes) { if (F->returnDoesNotAlias() || !F->getReturnType()->isPointerTy()) @@ -965,10 +1157,8 @@ static bool addNoAliasAttrs(const SCCNodeSet &SCCNodes) { F->setReturnDoesNotAlias(); ++NumNoAlias; - MadeChange = true; + Changed.insert(F); } - - return MadeChange; } /// Tests whether this function is known to not return null. @@ -1044,26 +1234,24 @@ static bool isReturnNonNull(Function *F, const SCCNodeSet &SCCNodes, } /// Deduce nonnull attributes for the SCC. -static bool addNonNullAttrs(const SCCNodeSet &SCCNodes) { +static void addNonNullAttrs(const SCCNodeSet &SCCNodes, + SmallSet<Function *, 8> &Changed) { // Speculative that all functions in the SCC return only nonnull // pointers. We may refute this as we analyze functions. bool SCCReturnsNonNull = true; - bool MadeChange = false; - // Check each function in turn, determining which functions return nonnull // pointers. for (Function *F : SCCNodes) { // Already nonnull. - if (F->getAttributes().hasAttribute(AttributeList::ReturnIndex, - Attribute::NonNull)) + if (F->getAttributes().hasRetAttr(Attribute::NonNull)) continue; // We can infer and propagate function attributes only when we know that the // definition we'll get at link time is *exactly* the definition we see now. // For more details, see GlobalValue::mayBeDerefined. if (!F->hasExactDefinition()) - return false; + return; // We annotate nonnull return values, which are only applicable to // pointer types. @@ -1077,9 +1265,9 @@ static bool addNonNullAttrs(const SCCNodeSet &SCCNodes) { // which prevents us from speculating about the entire SCC LLVM_DEBUG(dbgs() << "Eagerly marking " << F->getName() << " as nonnull\n"); - F->addAttribute(AttributeList::ReturnIndex, Attribute::NonNull); + F->addRetAttr(Attribute::NonNull); ++NumNonNullReturn; - MadeChange = true; + Changed.insert(F); } continue; } @@ -1090,19 +1278,16 @@ static bool addNonNullAttrs(const SCCNodeSet &SCCNodes) { if (SCCReturnsNonNull) { for (Function *F : SCCNodes) { - if (F->getAttributes().hasAttribute(AttributeList::ReturnIndex, - Attribute::NonNull) || + if (F->getAttributes().hasRetAttr(Attribute::NonNull) || !F->getReturnType()->isPointerTy()) continue; LLVM_DEBUG(dbgs() << "SCC marking " << F->getName() << " as nonnull\n"); - F->addAttribute(AttributeList::ReturnIndex, Attribute::NonNull); + F->addRetAttr(Attribute::NonNull); ++NumNonNullReturn; - MadeChange = true; + Changed.insert(F); } } - - return MadeChange; } namespace { @@ -1155,12 +1340,13 @@ public: InferenceDescriptors.push_back(AttrInference); } - bool run(const SCCNodeSet &SCCNodes); + void run(const SCCNodeSet &SCCNodes, SmallSet<Function *, 8> &Changed); }; /// Perform all the requested attribute inference actions according to the /// attribute predicates stored before. -bool AttributeInferer::run(const SCCNodeSet &SCCNodes) { +void AttributeInferer::run(const SCCNodeSet &SCCNodes, + SmallSet<Function *, 8> &Changed) { SmallVector<InferenceDescriptor, 4> InferInSCC = InferenceDescriptors; // Go through all the functions in SCC and check corresponding attribute // assumptions for each of them. Attributes that are invalid for this SCC @@ -1169,7 +1355,7 @@ bool AttributeInferer::run(const SCCNodeSet &SCCNodes) { // No attributes whose assumptions are still valid - done. if (InferInSCC.empty()) - return false; + return; // Check if our attributes ever need scanning/can be scanned. llvm::erase_if(InferInSCC, [F](const InferenceDescriptor &ID) { @@ -1212,9 +1398,8 @@ bool AttributeInferer::run(const SCCNodeSet &SCCNodes) { } if (InferInSCC.empty()) - return false; + return; - bool Changed = false; for (Function *F : SCCNodes) // At this point InferInSCC contains only functions that were either: // - explicitly skipped from scan/inference, or @@ -1223,10 +1408,9 @@ bool AttributeInferer::run(const SCCNodeSet &SCCNodes) { for (auto &ID : InferInSCC) { if (ID.SkipFunction(*F)) continue; - Changed = true; + Changed.insert(F); ID.SetAttribute(*F); } - return Changed; } struct SCCNodesResult { @@ -1243,7 +1427,7 @@ static bool InstrBreaksNonConvergent(Instruction &I, // Breaks non-convergent assumption if CS is a convergent call to a function // not in the SCC. return CB && CB->isConvergent() && - SCCNodes.count(CB->getCalledFunction()) == 0; + !SCCNodes.contains(CB->getCalledFunction()); } /// Helper for NoUnwind inference predicate InstrBreaksAttribute. @@ -1282,7 +1466,8 @@ static bool InstrBreaksNoFree(Instruction &I, const SCCNodeSet &SCCNodes) { /// Attempt to remove convergent function attribute when possible. /// /// Returns true if any changes to function attributes were made. -static bool inferConvergent(const SCCNodeSet &SCCNodes) { +static void inferConvergent(const SCCNodeSet &SCCNodes, + SmallSet<Function *, 8> &Changed) { AttributeInferer AI; // Request to remove the convergent attribute from all functions in the SCC @@ -1305,7 +1490,7 @@ static bool inferConvergent(const SCCNodeSet &SCCNodes) { }, /* RequiresExactDefinition= */ false}); // Perform all the requested attribute inference actions. - return AI.run(SCCNodes); + AI.run(SCCNodes, Changed); } /// Infer attributes from all functions in the SCC by scanning every @@ -1314,7 +1499,8 @@ static bool inferConvergent(const SCCNodeSet &SCCNodes) { /// - addition of NoUnwind attribute /// /// Returns true if any changes to function attributes were made. -static bool inferAttrsFromFunctionBodies(const SCCNodeSet &SCCNodes) { +static void inferAttrsFromFunctionBodies(const SCCNodeSet &SCCNodes, + SmallSet<Function *, 8> &Changed) { AttributeInferer AI; if (!DisableNoUnwindInference) @@ -1363,19 +1549,20 @@ static bool inferAttrsFromFunctionBodies(const SCCNodeSet &SCCNodes) { /* RequiresExactDefinition= */ true}); // Perform all the requested attribute inference actions. - return AI.run(SCCNodes); + AI.run(SCCNodes, Changed); } -static bool addNoRecurseAttrs(const SCCNodeSet &SCCNodes) { +static void addNoRecurseAttrs(const SCCNodeSet &SCCNodes, + SmallSet<Function *, 8> &Changed) { // Try and identify functions that do not recurse. // If the SCC contains multiple nodes we know for sure there is recursion. if (SCCNodes.size() != 1) - return false; + return; Function *F = *SCCNodes.begin(); if (!F || !F->hasExactDefinition() || F->doesNotRecurse()) - return false; + return; // If all of the calls in F are identifiable and are to norecurse functions, F // is norecurse. This check also detects self-recursion as F is not currently @@ -1386,7 +1573,7 @@ static bool addNoRecurseAttrs(const SCCNodeSet &SCCNodes) { Function *Callee = CB->getCalledFunction(); if (!Callee || Callee == F || !Callee->doesNotRecurse()) // Function calls a potentially recursive function. - return false; + return; } // Every call was to a non-recursive function other than this function, and @@ -1394,7 +1581,7 @@ static bool addNoRecurseAttrs(const SCCNodeSet &SCCNodes) { // recurse. F->setDoesNotRecurse(); ++NumNoRecurse; - return true; + Changed.insert(F); } static bool instructionDoesNotReturn(Instruction &I) { @@ -1412,9 +1599,8 @@ static bool basicBlockCanReturn(BasicBlock &BB) { } // Set the noreturn function attribute if possible. -static bool addNoReturnAttrs(const SCCNodeSet &SCCNodes) { - bool Changed = false; - +static void addNoReturnAttrs(const SCCNodeSet &SCCNodes, + SmallSet<Function *, 8> &Changed) { for (Function *F : SCCNodes) { if (!F || !F->hasExactDefinition() || F->hasFnAttribute(Attribute::Naked) || F->doesNotReturn()) @@ -1424,11 +1610,9 @@ static bool addNoReturnAttrs(const SCCNodeSet &SCCNodes) { // FIXME: this doesn't handle recursion or unreachable blocks. if (none_of(*F, basicBlockCanReturn)) { F->setDoesNotReturn(); - Changed = true; + Changed.insert(F); } } - - return Changed; } static bool functionWillReturn(const Function &F) { @@ -1461,19 +1645,16 @@ static bool functionWillReturn(const Function &F) { } // Set the willreturn function attribute if possible. -static bool addWillReturn(const SCCNodeSet &SCCNodes) { - bool Changed = false; - +static void addWillReturn(const SCCNodeSet &SCCNodes, + SmallSet<Function *, 8> &Changed) { for (Function *F : SCCNodes) { if (!F || F->willReturn() || !functionWillReturn(*F)) continue; F->setWillReturn(); NumWillReturn++; - Changed = true; + Changed.insert(F); } - - return Changed; } // Return true if this is an atomic which has an ordering stronger than @@ -1532,7 +1713,8 @@ static bool InstrBreaksNoSync(Instruction &I, const SCCNodeSet &SCCNodes) { } // Infer the nosync attribute. -static bool addNoSyncAttr(const SCCNodeSet &SCCNodes) { +static void addNoSyncAttr(const SCCNodeSet &SCCNodes, + SmallSet<Function *, 8> &Changed) { AttributeInferer AI; AI.registerAttrInference(AttributeInferer::InferenceDescriptor{ Attribute::NoSync, @@ -1549,14 +1731,15 @@ static bool addNoSyncAttr(const SCCNodeSet &SCCNodes) { ++NumNoSync; }, /* RequiresExactDefinition= */ true}); - return AI.run(SCCNodes); + AI.run(SCCNodes, Changed); } static SCCNodesResult createSCCNodeSet(ArrayRef<Function *> Functions) { SCCNodesResult Res; Res.HasUnknownCall = false; for (Function *F : Functions) { - if (!F || F->hasOptNone() || F->hasFnAttribute(Attribute::Naked)) { + if (!F || F->hasOptNone() || F->hasFnAttribute(Attribute::Naked) || + F->isPresplitCoroutine()) { // Treat any function we're trying not to optimize as if it were an // indirect call and omit it from the node set used below. Res.HasUnknownCall = true; @@ -1582,32 +1765,33 @@ static SCCNodesResult createSCCNodeSet(ArrayRef<Function *> Functions) { } template <typename AARGetterT> -static bool deriveAttrsInPostOrder(ArrayRef<Function *> Functions, - AARGetterT &&AARGetter) { +static SmallSet<Function *, 8> +deriveAttrsInPostOrder(ArrayRef<Function *> Functions, AARGetterT &&AARGetter) { SCCNodesResult Nodes = createSCCNodeSet(Functions); - bool Changed = false; // Bail if the SCC only contains optnone functions. if (Nodes.SCCNodes.empty()) - return Changed; + return {}; + + SmallSet<Function *, 8> Changed; - Changed |= addArgumentReturnedAttrs(Nodes.SCCNodes); - Changed |= addReadAttrs(Nodes.SCCNodes, AARGetter); - Changed |= addArgumentAttrs(Nodes.SCCNodes); - Changed |= inferConvergent(Nodes.SCCNodes); - Changed |= addNoReturnAttrs(Nodes.SCCNodes); - Changed |= addWillReturn(Nodes.SCCNodes); + addArgumentReturnedAttrs(Nodes.SCCNodes, Changed); + addReadAttrs(Nodes.SCCNodes, AARGetter, Changed); + addArgumentAttrs(Nodes.SCCNodes, Changed); + inferConvergent(Nodes.SCCNodes, Changed); + addNoReturnAttrs(Nodes.SCCNodes, Changed); + addWillReturn(Nodes.SCCNodes, Changed); // If we have no external nodes participating in the SCC, we can deduce some // more precise attributes as well. if (!Nodes.HasUnknownCall) { - Changed |= addNoAliasAttrs(Nodes.SCCNodes); - Changed |= addNonNullAttrs(Nodes.SCCNodes); - Changed |= inferAttrsFromFunctionBodies(Nodes.SCCNodes); - Changed |= addNoRecurseAttrs(Nodes.SCCNodes); + addNoAliasAttrs(Nodes.SCCNodes, Changed); + addNonNullAttrs(Nodes.SCCNodes, Changed); + inferAttrsFromFunctionBodies(Nodes.SCCNodes, Changed); + addNoRecurseAttrs(Nodes.SCCNodes, Changed); } - Changed |= addNoSyncAttr(Nodes.SCCNodes); + addNoSyncAttr(Nodes.SCCNodes, Changed); // Finally, infer the maximal set of attributes from the ones we've inferred // above. This is handling the cases where one attribute on a signature @@ -1615,7 +1799,8 @@ static bool deriveAttrsInPostOrder(ArrayRef<Function *> Functions, // the later is missing (or simply less sophisticated). for (Function *F : Nodes.SCCNodes) if (F) - Changed |= inferAttributesFromOthers(*F); + if (inferAttributesFromOthers(*F)) + Changed.insert(F); return Changed; } @@ -1638,14 +1823,35 @@ PreservedAnalyses PostOrderFunctionAttrsPass::run(LazyCallGraph::SCC &C, Functions.push_back(&N.getFunction()); } - if (deriveAttrsInPostOrder(Functions, AARGetter)) { - // We have not changed the call graph or removed/added functions. - PreservedAnalyses PA; - PA.preserve<FunctionAnalysisManagerCGSCCProxy>(); - return PA; + auto ChangedFunctions = deriveAttrsInPostOrder(Functions, AARGetter); + if (ChangedFunctions.empty()) + return PreservedAnalyses::all(); + + // Invalidate analyses for modified functions so that we don't have to + // invalidate all analyses for all functions in this SCC. + PreservedAnalyses FuncPA; + // We haven't changed the CFG for modified functions. + FuncPA.preserveSet<CFGAnalyses>(); + for (Function *Changed : ChangedFunctions) { + FAM.invalidate(*Changed, FuncPA); + // Also invalidate any direct callers of changed functions since analyses + // may care about attributes of direct callees. For example, MemorySSA cares + // about whether or not a call's callee modifies memory and queries that + // through function attributes. + for (auto *U : Changed->users()) { + if (auto *Call = dyn_cast<CallBase>(U)) { + if (Call->getCalledFunction() == Changed) + FAM.invalidate(*Call->getFunction(), FuncPA); + } + } } - return PreservedAnalyses::all(); + PreservedAnalyses PA; + // We have not added or removed functions. + PA.preserve<FunctionAnalysisManagerCGSCCProxy>(); + // We already invalidated all relevant function analyses above. + PA.preserveSet<AllAnalysesOn<Function>>(); + return PA; } namespace { @@ -1690,7 +1896,7 @@ static bool runImpl(CallGraphSCC &SCC, AARGetterT AARGetter) { Functions.push_back(I->getFunction()); } - return deriveAttrsInPostOrder(Functions, AARGetter); + return !deriveAttrsInPostOrder(Functions, AARGetter).empty(); } bool PostOrderFunctionAttrsLegacyPass::runOnSCC(CallGraphSCC &SCC) { |