diff --git a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp index 935375e89393..2ad7403d3ad3 100644 --- a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp +++ b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp @@ -19,6 +19,7 @@ #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" @@ -249,7 +250,8 @@ MemoryAccessKind llvm::computeFunctionBodyMemoryAccess(Function &F, /// Deduce readonly/readnone attributes for the SCC. template -static bool addReadAttrs(const SCCNodeSet &SCCNodes, AARGetterT &&AARGetter) { +static void addReadAttrs(const SCCNodeSet &SCCNodes, AARGetterT &&AARGetter, + SmallSet &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; @@ -264,7 +266,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; @@ -280,11 +282,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()) @@ -298,7 +299,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; @@ -327,8 +328,6 @@ static bool addReadAttrs(const SCCNodeSet &SCCNodes, AARGetterT &&AARGetter) { else ++NumReadNone; } - - return MadeChange; } // Compute definitive function attributes for a function taking into account @@ -779,9 +778,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 &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 @@ -821,11 +819,9 @@ static bool addArgumentReturnedAttrs(const SCCNodeSet &SCCNodes) { auto *A = cast(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, @@ -891,9 +887,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 &Changed) { ArgumentGraph AG; // Check each function in turn, determining which pointer arguments are not @@ -905,7 +900,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. @@ -916,7 +912,7 @@ static bool addArgumentAttrs(const SCCNodeSet &SCCNodes) { if (A->getType()->isPointerTy() && !A->hasNoCaptureAttr()) { A->addAttr(Attribute::NoCapture); ++NumNoCapture; - Changed = true; + Changed.insert(F); } } continue; @@ -935,7 +931,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 @@ -959,7 +955,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); } } } @@ -983,7 +980,7 @@ static bool addArgumentAttrs(const SCCNodeSet &SCCNodes) { Argument *A = ArgumentSCC[0]->Definition; A->addAttr(Attribute::NoCapture); ++NumNoCapture; - Changed = true; + Changed.insert(A->getParent()); } continue; } @@ -1025,7 +1022,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 @@ -1056,12 +1053,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". @@ -1132,7 +1128,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 &Changed) { // Check each function in turn, determining which functions return noalias // pointers. for (Function *F : SCCNodes) { @@ -1144,7 +1141,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. @@ -1152,10 +1149,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()) @@ -1163,10 +1159,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. @@ -1242,13 +1236,12 @@ 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 &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) { @@ -1260,7 +1253,7 @@ static bool addNonNullAttrs(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 nonnull return values, which are only applicable to // pointer types. @@ -1276,7 +1269,7 @@ static bool addNonNullAttrs(const SCCNodeSet &SCCNodes) { << " as nonnull\n"); F->addRetAttr(Attribute::NonNull); ++NumNonNullReturn; - MadeChange = true; + Changed.insert(F); } continue; } @@ -1294,11 +1287,9 @@ static bool addNonNullAttrs(const SCCNodeSet &SCCNodes) { LLVM_DEBUG(dbgs() << "SCC marking " << F->getName() << " as nonnull\n"); F->addRetAttr(Attribute::NonNull); ++NumNonNullReturn; - MadeChange = true; + Changed.insert(F); } } - - return MadeChange; } namespace { @@ -1351,12 +1342,13 @@ public: InferenceDescriptors.push_back(AttrInference); } - bool run(const SCCNodeSet &SCCNodes); + void run(const SCCNodeSet &SCCNodes, SmallSet &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 &Changed) { SmallVector 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 @@ -1365,7 +1357,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) { @@ -1408,9 +1400,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 @@ -1419,10 +1410,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 { @@ -1478,7 +1468,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 &Changed) { AttributeInferer AI; // Request to remove the convergent attribute from all functions in the SCC @@ -1501,7 +1492,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 @@ -1510,7 +1501,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 &Changed) { AttributeInferer AI; if (!DisableNoUnwindInference) @@ -1559,19 +1551,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 &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 @@ -1582,7 +1575,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 @@ -1590,7 +1583,7 @@ static bool addNoRecurseAttrs(const SCCNodeSet &SCCNodes) { // recurse. F->setDoesNotRecurse(); ++NumNoRecurse; - return true; + Changed.insert(F); } static bool instructionDoesNotReturn(Instruction &I) { @@ -1608,9 +1601,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 &Changed) { for (Function *F : SCCNodes) { if (!F || !F->hasExactDefinition() || F->hasFnAttribute(Attribute::Naked) || F->doesNotReturn()) @@ -1620,11 +1612,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) { @@ -1657,19 +1647,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 &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 @@ -1728,7 +1715,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 &Changed) { AttributeInferer AI; AI.registerAttrInference(AttributeInferer::InferenceDescriptor{ Attribute::NoSync, @@ -1745,7 +1733,7 @@ static bool addNoSyncAttr(const SCCNodeSet &SCCNodes) { ++NumNoSync; }, /* RequiresExactDefinition= */ true}); - return AI.run(SCCNodes); + AI.run(SCCNodes, Changed); } static SCCNodesResult createSCCNodeSet(ArrayRef Functions) { @@ -1779,32 +1767,33 @@ static SCCNodesResult createSCCNodeSet(ArrayRef Functions) { } template -static bool deriveAttrsInPostOrder(ArrayRef Functions, - AARGetterT &&AARGetter) { +static SmallSet +deriveAttrsInPostOrder(ArrayRef 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 {}; - 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); + SmallSet 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, 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 @@ -1812,7 +1801,8 @@ static bool deriveAttrsInPostOrder(ArrayRef 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; } @@ -1835,14 +1825,14 @@ 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(); - return PA; - } + auto ChangedFunctions = deriveAttrsInPostOrder(Functions, AARGetter); + if (ChangedFunctions.empty()) + return PreservedAnalyses::all(); - return PreservedAnalyses::all(); + PreservedAnalyses PA; + // We have not added or removed functions. + PA.preserve(); + return PA; } namespace { @@ -1887,7 +1877,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) {