Index: include/llvm/Analysis/InlineCost.h =================================================================== --- include/llvm/Analysis/InlineCost.h +++ include/llvm/Analysis/InlineCost.h @@ -74,8 +74,15 @@ /// The adjusted threshold against which this cost was computed. const int Threshold; + /// Must be set for Always and Never instances. + const char* Reason = nullptr; + // Trivial constructor, interesting logic in the factory functions below. - InlineCost(int Cost, int Threshold) : Cost(Cost), Threshold(Threshold) {} + InlineCost(int Cost, int Threshold, const char* Reason = nullptr) : + Cost(Cost), Threshold(Threshold), Reason(Reason) { + assert((isVariable() || Reason) && + "Reason must be provided for Never or Always"); + } public: static InlineCost get(int Cost, int Threshold) { @@ -83,11 +90,11 @@ assert(Cost < NeverInlineCost && "Cost crosses sentinel value"); return InlineCost(Cost, Threshold); } - static InlineCost getAlways() { - return InlineCost(AlwaysInlineCost, 0); + static InlineCost getAlways(const char* Reason) { + return InlineCost(AlwaysInlineCost, 0, Reason); } - static InlineCost getNever() { - return InlineCost(NeverInlineCost, 0); + static InlineCost getNever(const char* Reason) { + return InlineCost(NeverInlineCost, 0, Reason); } /// Test whether the inline cost is low enough for inlining. @@ -112,6 +119,13 @@ return Threshold; } + /// Get the reason of Always or Never. + const char* getReason() const { + assert((Reason || isVariable()) && + "InlineCost reason must be set for Always or Never"); + return Reason; + } + /// Get the cost delta from the threshold for inlining. /// Only valid if the cost is of the variable kind. Returns a negative /// value if the cost is too high to inline. Index: include/llvm/IR/DiagnosticInfo.h =================================================================== --- include/llvm/IR/DiagnosticInfo.h +++ include/llvm/IR/DiagnosticInfo.h @@ -414,6 +414,7 @@ Argument(StringRef Key, const Value *V); Argument(StringRef Key, const Type *T); Argument(StringRef Key, StringRef S); + Argument(StringRef Key, const char* S) : Argument(Key, StringRef(S)) {}; Argument(StringRef Key, int N); Argument(StringRef Key, float N); Argument(StringRef Key, long N); Index: include/llvm/Transforms/Utils/Cloning.h =================================================================== --- include/llvm/Transforms/Utils/Cloning.h +++ include/llvm/Transforms/Utils/Cloning.h @@ -213,6 +213,17 @@ } }; +/// InlineResult is basically true or false. For false results the message +/// describes a reason why it is decided to not inline. +struct InlineResult { + const char* message = nullptr; + InlineResult(bool result, const char* message = nullptr) + : message(result ? nullptr : (message ? message : "cost > threshold")) {} + InlineResult(const char* message = nullptr) : message(message) {} + operator bool() const { return !message;} + operator const char*() const { return message; } +}; + /// InlineFunction - This function inlines the called function into the basic /// block of the caller. This returns false if it is not possible to inline /// this call. The program is still in a well defined state if this occurs @@ -232,13 +243,13 @@ /// and all varargs at the callsite will be passed to any calls to /// ForwardVarArgsTo. The caller of InlineFunction has to make sure any varargs /// are only used by ForwardVarArgsTo. -bool InlineFunction(CallInst *C, InlineFunctionInfo &IFI, - AAResults *CalleeAAR = nullptr, bool InsertLifetime = true); -bool InlineFunction(InvokeInst *II, InlineFunctionInfo &IFI, - AAResults *CalleeAAR = nullptr, bool InsertLifetime = true); -bool InlineFunction(CallSite CS, InlineFunctionInfo &IFI, - AAResults *CalleeAAR = nullptr, bool InsertLifetime = true, - Function *ForwardVarArgsTo = nullptr); +InlineResult InlineFunction(CallInst *C, InlineFunctionInfo &IFI, + AAResults *CalleeAAR = nullptr, bool InsertLifetime = true); +InlineResult InlineFunction(InvokeInst *II, InlineFunctionInfo &IFI, + AAResults *CalleeAAR = nullptr, bool InsertLifetime = true); +InlineResult InlineFunction(CallSite CS, InlineFunctionInfo &IFI, + AAResults *CalleeAAR = nullptr, bool InsertLifetime = true, + Function *ForwardVarArgsTo = nullptr); /// Clones a loop \p OrigLoop. Returns the loop and the blocks in \p /// Blocks. Index: lib/Analysis/InlineCost.cpp =================================================================== --- lib/Analysis/InlineCost.cpp +++ lib/Analysis/InlineCost.cpp @@ -37,6 +37,7 @@ #include "llvm/IR/Operator.h" #include "llvm/Support/Debug.h" #include "llvm/Support/raw_ostream.h" +#include "llvm/Transforms/Utils/Cloning.h" using namespace llvm; @@ -227,7 +228,7 @@ BlockFrequencyInfo *CallerBFI); // Custom analysis routines. - bool analyzeBlock(BasicBlock *BB, SmallPtrSetImpl &EphValues); + InlineResult analyzeBlock(BasicBlock *BB, SmallPtrSetImpl &EphValues); // Disable several entry points to the visitor so we don't accidentally use // them by declaring but not defining them here. @@ -290,7 +291,7 @@ NumInstructionsSimplified(0), SROACostSavings(0), SROACostSavingsLost(0) {} - bool analyzeCall(CallSite CS); + InlineResult analyzeCall(CallSite CS); int getThreshold() { return Threshold; } int getCost() { return Cost; } @@ -1541,8 +1542,8 @@ /// aborts early if the threshold has been exceeded or an impossible to inline /// construct has been detected. It returns false if inlining is no longer /// viable, and true if inlining remains viable. -bool CallAnalyzer::analyzeBlock(BasicBlock *BB, - SmallPtrSetImpl &EphValues) { +InlineResult CallAnalyzer::analyzeBlock(BasicBlock *BB, + SmallPtrSetImpl &EphValues) { for (BasicBlock::iterator I = BB->begin(), E = BB->end(); I != E; ++I) { // FIXME: Currently, the number of instructions in a function regardless of // our ability to simplify them during inline to constants or dead code, @@ -1574,16 +1575,24 @@ using namespace ore; // If the visit this instruction detected an uninlinable pattern, abort. - if (IsRecursiveCall || ExposesReturnsTwice || HasDynamicAlloca || - HasIndirectBr || HasUninlineableIntrinsic || UsesVarArgs) { + InlineResult IR; + if (IsRecursiveCall) IR = "recursive"; + else if (ExposesReturnsTwice) IR = "exposes returns twice"; + else if (HasDynamicAlloca) IR = "dynamic alloca"; + else if (HasIndirectBr) IR = "indirect branch"; + else if (HasUninlineableIntrinsic) IR = "uninlinable intrinsic"; + else if (UsesVarArgs) IR = "varargs"; + if (!IR) { if (ORE) ORE->emit([&]() { return OptimizationRemarkMissed(DEBUG_TYPE, "NeverInline", CandidateCS.getInstruction()) << NV("Callee", &F) - << " has uninlinable pattern and cost is not fully computed"; + << " has uninlinable pattern (" + << NV("InlineResult", IR.message) + << ") and cost is not fully computed"; }); - return false; + return IR; } // If the caller is a recursive function then we don't want to inline @@ -1591,15 +1600,16 @@ // the caller stack usage dramatically. if (IsCallerRecursive && AllocatedSize > InlineConstants::TotalAllocaSizeRecursiveCaller) { + InlineResult IR = "recursive and allocates too much stack space"; if (ORE) ORE->emit([&]() { return OptimizationRemarkMissed(DEBUG_TYPE, "NeverInline", CandidateCS.getInstruction()) - << NV("Callee", &F) - << " is recursive and allocates too much stack space. Cost is " - "not fully computed"; + << NV("Callee", &F) << " is " + << NV("InlineResult", IR.message) + << ". Cost is not fully computed"; }); - return false; + return IR; } // Check if we've past the maximum possible threshold so we don't spin in @@ -1695,7 +1705,7 @@ /// factors and heuristics. If this method returns false but the computed cost /// is below the computed threshold, then inlining was forcibly disabled by /// some artifact of the routine. -bool CallAnalyzer::analyzeCall(CallSite CS) { +InlineResult CallAnalyzer::analyzeCall(CallSite CS) { ++NumCallsAnalyzed; // Perform some tweaks to the cost and threshold based on the direct @@ -1730,7 +1740,7 @@ // Check if we're done. This can happen due to bonuses and penalties. if (Cost >= Threshold && !ComputeFullInlineCost) - return false; + return "high cost"; if (F.empty()) return true; @@ -1809,12 +1819,13 @@ // site. If the blockaddress escapes the function, e.g., via a global // variable, inlining may lead to an invalid cross-function reference. if (BB->hasAddressTaken()) - return false; + return "blockaddress"; // Analyze the cost of this block. If we blow through the threshold, this // returns false, and we can bail on out. - if (!analyzeBlock(BB, EphValues)) - return false; + InlineResult IR = analyzeBlock(BB, EphValues); + if (!IR) + return IR; TerminatorInst *TI = BB->getTerminator(); @@ -1867,7 +1878,7 @@ // inlining this would cause the removal of the caller (so the instruction // is not actually duplicated, just moved). if (!OnlyOneCallAndLocalLinkage && ContainsNoDuplicateCall) - return false; + return "noduplicate"; // We applied the maximum possible vector bonus at the beginning. Now, // subtract the excess bonus, if any, from the Threshold before @@ -1961,7 +1972,7 @@ // Cannot inline indirect calls. if (!Callee) - return llvm::InlineCost::getNever(); + return llvm::InlineCost::getNever("indirect call"); // Never inline calls with byval arguments that does not have the alloca // address space. Since byval arguments can be replaced with a copy to an @@ -1973,54 +1984,60 @@ if (CS.isByValArgument(I)) { PointerType *PTy = cast(CS.getArgument(I)->getType()); if (PTy->getAddressSpace() != AllocaAS) - return llvm::InlineCost::getNever(); + return llvm::InlineCost::getNever("byval arguments without alloca" + " address space"); } // Calls to functions with always-inline attributes should be inlined // whenever possible. if (CS.hasFnAttr(Attribute::AlwaysInline)) { if (isInlineViable(*Callee)) - return llvm::InlineCost::getAlways(); - return llvm::InlineCost::getNever(); + return llvm::InlineCost::getAlways("always inline attribute"); + return llvm::InlineCost::getNever("inapplicable always inline attribute"); } // Never inline functions with conflicting attributes (unless callee has // always-inline attribute). Function *Caller = CS.getCaller(); if (!functionsHaveCompatibleAttributes(Caller, Callee, CalleeTTI)) - return llvm::InlineCost::getNever(); + return llvm::InlineCost::getNever("conflicting attributes"); // Don't inline this call if the caller has the optnone attribute. if (Caller->hasFnAttribute(Attribute::OptimizeNone)) - return llvm::InlineCost::getNever(); + return llvm::InlineCost::getNever("optnone attribute"); // Don't inline a function that treats null pointer as valid into a caller // that does not have this attribute. if (!Caller->nullPointerIsDefined() && Callee->nullPointerIsDefined()) - return llvm::InlineCost::getNever(); + return llvm::InlineCost::getNever("nullptr definitions incompatible"); + + // Don't inline functions which can be interposed at link-time. + if (Callee->isInterposable()) + return llvm::InlineCost::getNever("interposable"); + + // Don't inline functions marked noinline. + if (Callee->hasFnAttribute(Attribute::NoInline)) + return llvm::InlineCost::getNever("noinline function attribute"); + + // Don't inline call sites marked noinline. + if (CS.isNoInline()) + return llvm::InlineCost::getNever("noinline call site attribute"); - // Don't inline functions which can be interposed at link-time. Don't inline - // functions marked noinline or call sites marked noinline. - // Note: inlining non-exact non-interposable functions is fine, since we know - // we have *a* correct implementation of the source level function. - if (Callee->isInterposable() || Callee->hasFnAttribute(Attribute::NoInline) || - CS.isNoInline()) - return llvm::InlineCost::getNever(); LLVM_DEBUG(llvm::dbgs() << " Analyzing call of " << Callee->getName() << "... (caller:" << Caller->getName() << ")\n"); CallAnalyzer CA(CalleeTTI, GetAssumptionCache, GetBFI, PSI, ORE, *Callee, CS, Params); - bool ShouldInline = CA.analyzeCall(CS); + InlineResult ShouldInline = CA.analyzeCall(CS); LLVM_DEBUG(CA.dump()); // Check if there was a reason to force inlining or no inlining. if (!ShouldInline && CA.getCost() < CA.getThreshold()) - return InlineCost::getNever(); + return InlineCost::getNever(ShouldInline.message); if (ShouldInline && CA.getCost() >= CA.getThreshold()) - return InlineCost::getAlways(); + return InlineCost::getAlways("empty function"); return llvm::InlineCost::get(CA.getCost(), CA.getThreshold()); } Index: lib/Target/AMDGPU/AMDGPUInline.cpp =================================================================== --- lib/Target/AMDGPU/AMDGPUInline.cpp +++ lib/Target/AMDGPU/AMDGPUInline.cpp @@ -174,18 +174,23 @@ Function *Caller = CS.getCaller(); TargetTransformInfo &TTI = TTIWP->getTTI(*Callee); - if (!Callee || Callee->isDeclaration() || CS.isNoInline() || - !TTI.areInlineCompatible(Caller, Callee)) - return llvm::InlineCost::getNever(); + if (!Callee || Callee->isDeclaration()) + return llvm::InlineCost::getNever("undefined callee"); + + if (CS.isNoInline()) + return llvm::InlineCost::getNever("noinline"); + + if (!TTI.areInlineCompatible(Caller, Callee)) + return llvm::InlineCost::getNever("incompatible"); if (CS.hasFnAttr(Attribute::AlwaysInline)) { if (isInlineViable(*Callee)) - return llvm::InlineCost::getAlways(); - return llvm::InlineCost::getNever(); + return llvm::InlineCost::getAlways("alwaysinline viable"); + return llvm::InlineCost::getNever("alwaysinline unviable"); } if (isWrapperOnlyCall(CS)) - return llvm::InlineCost::getAlways(); + return llvm::InlineCost::getAlways("wrapper-only call"); InlineParams LocalParams = Params; LocalParams.DefaultThreshold = (int)getInlineThreshold(CS); Index: lib/Transforms/IPO/AlwaysInliner.cpp =================================================================== --- lib/Transforms/IPO/AlwaysInliner.cpp +++ lib/Transforms/IPO/AlwaysInliner.cpp @@ -150,7 +150,7 @@ // declarations. if (Callee && !Callee->isDeclaration() && CS.hasFnAttr(Attribute::AlwaysInline) && isInlineViable(*Callee)) - return InlineCost::getAlways(); + return InlineCost::getAlways("always inliner"); - return InlineCost::getNever(); + return InlineCost::getNever("always inliner"); } Index: lib/Transforms/IPO/Inliner.cpp =================================================================== --- lib/Transforms/IPO/Inliner.cpp +++ lib/Transforms/IPO/Inliner.cpp @@ -275,8 +275,9 @@ // Try to inline the function. Get the list of static allocas that were // inlined. - if (!InlineFunction(CS, IFI, &AAR, InsertLifetime)) - return false; + InlineResult IR = InlineFunction(CS, IFI, &AAR, InsertLifetime); + if (!IR) + return IR; if (InlinerFunctionImportStats != InlinerFunctionImportStatsOpts::No) ImportedFunctionsStats.recordInline(*Caller, *Callee); @@ -286,7 +287,7 @@ if (!DisableInlinedAllocaMerging) mergeInlinedArrayAllocas(Caller, IFI, InlinedArrayAllocas, InlineHistory); - return true; + return IR; // success } /// Return true if inlining of CS can block the caller from being @@ -365,6 +366,42 @@ return false; } +static std::string inlineCostStr(InlineCost& IC) { + std::string Message; + + if (IC.isAlways()) { + Message += "cost=always"; + } else if (IC.isNever()) { + Message += "cost=never"; + } else { + Message += "cost="; + Message += std::to_string(IC.getCost()); + Message += " (threshold="; + Message += std::to_string(IC.getThreshold()); + Message += ")"; + } + if (const char* Reason = IC.getReason()) + Message += std::string(": ") + Reason; + + return Message; +} + +template +RemarkT& operator<< (RemarkT &&R, const InlineCost& IC) { + using namespace ore; + if (IC.isAlways()) { + R << "cost=always"; + } else if (IC.isNever()) { + R << "cost=never"; + } else { + R << "cost=" << ore::NV("Cost", IC.getCost()) + << " (threshold=" << ore::NV("Threshold", IC.getThreshold()) << ")"; + } + if (const char* Reason = IC.getReason()) + R << std::string(": ") << ore::NV("Reason", Reason); + return R; +} + /// Return the cost only if the inliner should attempt to inline at the given /// CallSite. If we return the cost, we will emit an optimisation remark later /// using that cost, so we won't do so from this function. @@ -379,35 +416,33 @@ Function *Caller = CS.getCaller(); if (IC.isAlways()) { - LLVM_DEBUG(dbgs() << " Inlining: cost=always" + LLVM_DEBUG(dbgs() << " Inlining: " << inlineCostStr(IC) << ", Call: " << *CS.getInstruction() << "\n"); return IC; } if (IC.isNever()) { - LLVM_DEBUG(dbgs() << " NOT Inlining: cost=never" + LLVM_DEBUG(dbgs() << " NOT Inlining: " << inlineCostStr(IC) << ", Call: " << *CS.getInstruction() << "\n"); ORE.emit([&]() { return OptimizationRemarkMissed(DEBUG_TYPE, "NeverInline", Call) << NV("Callee", Callee) << " not inlined into " << NV("Caller", Caller) - << " because it should never be inlined (cost=never)"; + << " because it should never be inlined " << IC; }); - return None; + return IC; } if (!IC) { - LLVM_DEBUG(dbgs() << " NOT Inlining: cost=" << IC.getCost() - << ", thres=" << IC.getThreshold() + LLVM_DEBUG(dbgs() << " NOT Inlining: " << inlineCostStr(IC) << ", Call: " << *CS.getInstruction() << "\n"); ORE.emit([&]() { return OptimizationRemarkMissed(DEBUG_TYPE, "TooCostly", Call) << NV("Callee", Callee) << " not inlined into " - << NV("Caller", Caller) << " because too costly to inline (cost=" - << NV("Cost", IC.getCost()) - << ", threshold=" << NV("Threshold", IC.getThreshold()) << ")"; + << NV("Caller", Caller) << " because too costly to inline (" + << IC << ")"; }); - return None; + return IC; } int TotalSecondaryCost = 0; @@ -428,8 +463,7 @@ return None; } - LLVM_DEBUG(dbgs() << " Inlining: cost=" << IC.getCost() - << ", thres=" << IC.getThreshold() + LLVM_DEBUG(dbgs() << " Inlining: " << inlineCostStr(IC) << ", Call: " << *CS.getInstruction() << '\n'); return IC; } @@ -461,6 +495,18 @@ return inlineCalls(SCC); } +static void emit_inlined_into(OptimizationRemarkEmitter &ORE, DebugLoc &DLoc, + const BasicBlock *Block, const Function &Callee, + const Function &Caller,const InlineCost& IC) { + ORE.emit([&]() { + bool AlwaysInline = IC.isAlways(); + StringRef RemarkName = AlwaysInline ? "AlwaysInline" : "Inlined"; + return OptimizationRemark(DEBUG_TYPE, RemarkName, DLoc, Block) + << ore::NV("Callee", &Callee) << " inlined into " + << ore::NV("Caller", &Caller) << " with " << IC; + }); +} + static bool inlineCallsImpl(CallGraphSCC &SCC, CallGraph &CG, std::function GetAssumptionCache, @@ -585,8 +631,9 @@ Optional OIC = shouldInline(CS, GetInlineCost, ORE); // If the policy determines that we should inline this function, // delete the call instead. - if (!OIC) + if (!OIC || !*OIC) { continue; + } // If this call site is dead and it is to a readonly function, we should // just delete the call instead of trying to inline it, regardless of @@ -606,34 +653,24 @@ // Attempt to inline the function. using namespace ore; - if (!InlineCallIfPossible(CS, InlineInfo, InlinedArrayAllocas, - InlineHistoryID, InsertLifetime, AARGetter, - ImportedFunctionsStats)) { + InlineResult IR = InlineCallIfPossible(CS, InlineInfo, + InlinedArrayAllocas, + InlineHistoryID, InsertLifetime, + AARGetter, + ImportedFunctionsStats); + if (!IR) { ORE.emit([&]() { return OptimizationRemarkMissed(DEBUG_TYPE, "NotInlined", DLoc, Block) << NV("Callee", Callee) << " will not be inlined into " - << NV("Caller", Caller); + << NV("Caller", Caller) << ": " + << NV("Reason", IR.message); }); continue; } ++NumInlined; - ORE.emit([&]() { - bool AlwaysInline = OIC->isAlways(); - StringRef RemarkName = AlwaysInline ? "AlwaysInline" : "Inlined"; - OptimizationRemark R(DEBUG_TYPE, RemarkName, DLoc, Block); - R << NV("Callee", Callee) << " inlined into "; - R << NV("Caller", Caller); - if (AlwaysInline) - R << " with cost=always"; - else { - R << " with cost=" << NV("Cost", OIC->getCost()); - R << " (threshold=" << NV("Threshold", OIC->getThreshold()); - R << ")"; - } - return R; - }); + emit_inlined_into(ORE, DLoc, Block, *Callee, *Caller, *OIC); // If inlining this function gave us any new call sites, throw them // onto our worklist to process. They are useful inline candidates. @@ -658,7 +695,7 @@ // could invalidate the CGSCC iterator. CG[Callee]->getNumReferences() == 0) { LLVM_DEBUG(dbgs() << " -> Deleting dead function: " - << Callee->getName() << "\n"); + << Callee->getName() << "\n"); CallGraphNode *CalleeNode = CG[Callee]; // Remove any call graph edges from the callee to its callees. @@ -713,7 +750,7 @@ /// Remove dead functions that are not included in DNR (Do Not Remove) list. bool LegacyInlinerBase::removeDeadFunctions(CallGraph &CG, - bool AlwaysInlineOnly) { + bool AlwaysInlineOnly) { SmallVector FunctionsToRemove; SmallVector DeadFunctionsInComdats; @@ -784,8 +821,8 @@ // in. array_pod_sort(FunctionsToRemove.begin(), FunctionsToRemove.end()); FunctionsToRemove.erase( - std::unique(FunctionsToRemove.begin(), FunctionsToRemove.end()), - FunctionsToRemove.end()); + std::unique(FunctionsToRemove.begin(), FunctionsToRemove.end()), + FunctionsToRemove.end()); for (CallGraphNode *CGN : FunctionsToRemove) { delete CG.removeFunctionFromModule(CGN); ++NumDeleted; @@ -846,13 +883,13 @@ SmallVector, 16> Calls; FunctionAnalysisManager &FAM = - AM.getResult(InitialC, CG) + AM.getResult(InitialC, CG) .getManager(); // Populate the initial list of calls in this SCC. for (auto &N : InitialC) { auto &ORE = - FAM.getResult(N.getFunction()); + FAM.getResult(N.getFunction()); // We want to generally process call sites top-down in order for // simplifications stemming from replacing the call with the returned value // after inlining to be visible to subsequent inlining decisions. @@ -971,7 +1008,7 @@ Optional OIC = shouldInline(CS, GetInlineCost, ORE); // Check whether we want to inline this callsite. - if (!OIC) + if (!OIC || !*OIC) continue; // Setup the data structure used to plumb customization into the @@ -987,32 +1024,20 @@ using namespace ore; - if (!InlineFunction(CS, IFI)) { + InlineResult IR = InlineFunction(CS, IFI); + if (!IR) { ORE.emit([&]() { return OptimizationRemarkMissed(DEBUG_TYPE, "NotInlined", DLoc, Block) << NV("Callee", &Callee) << " will not be inlined into " - << NV("Caller", &F); + << NV("Caller", &F) << ": " + << NV("Reason", IR.message); }); continue; } DidInline = true; InlinedCallees.insert(&Callee); - ORE.emit([&]() { - bool AlwaysInline = OIC->isAlways(); - StringRef RemarkName = AlwaysInline ? "AlwaysInline" : "Inlined"; - OptimizationRemark R(DEBUG_TYPE, RemarkName, DLoc, Block); - R << NV("Callee", &Callee) << " inlined into "; - R << NV("Caller", &F); - if (AlwaysInline) - R << " with cost=always"; - else { - R << " with cost=" << NV("Cost", OIC->getCost()); - R << " (threshold=" << NV("Threshold", OIC->getThreshold()); - R << ")"; - } - return R; - }); + emit_inlined_into(ORE, DLoc, Block, Callee, F, *OIC); // Add any new callsites to defined functions to the worklist. if (!IFI.InlinedCallSites.empty()) { Index: lib/Transforms/Utils/InlineFunction.cpp =================================================================== --- lib/Transforms/Utils/InlineFunction.cpp +++ lib/Transforms/Utils/InlineFunction.cpp @@ -84,13 +84,15 @@ cl::init(true), cl::Hidden, cl::desc("Convert align attributes to assumptions during inlining.")); -bool llvm::InlineFunction(CallInst *CI, InlineFunctionInfo &IFI, - AAResults *CalleeAAR, bool InsertLifetime) { +llvm::InlineResult llvm::InlineFunction(CallInst *CI, InlineFunctionInfo &IFI, + AAResults *CalleeAAR, + bool InsertLifetime) { return InlineFunction(CallSite(CI), IFI, CalleeAAR, InsertLifetime); } -bool llvm::InlineFunction(InvokeInst *II, InlineFunctionInfo &IFI, - AAResults *CalleeAAR, bool InsertLifetime) { +llvm::InlineResult llvm::InlineFunction(InvokeInst *II, InlineFunctionInfo &IFI, + AAResults *CalleeAAR, + bool InsertLifetime) { return InlineFunction(CallSite(II), IFI, CalleeAAR, InsertLifetime); } @@ -1491,7 +1493,7 @@ /// instruction 'call B' is inlined, and 'B' calls 'C', then the call to 'C' now /// exists in the instruction stream. Similarly this will inline a recursive /// function by one level. -bool llvm::InlineFunction(CallSite CS, InlineFunctionInfo &IFI, +llvm::InlineResult llvm::InlineFunction(CallSite CS, InlineFunctionInfo &IFI, AAResults *CalleeAAR, bool InsertLifetime, Function *ForwardVarArgsTo) { Instruction *TheCall = CS.getInstruction(); @@ -1504,7 +1506,7 @@ Function *CalledFunc = CS.getCalledFunction(); if (!CalledFunc || // Can't inline external function or indirect CalledFunc->isDeclaration()) // call! - return false; + return "external or indirect"; // The inliner does not know how to inline through calls with operand bundles // in general ... @@ -1518,7 +1520,7 @@ if (Tag == LLVMContext::OB_funclet) continue; - return false; + return "unsupported operand bundle"; } } @@ -1537,7 +1539,7 @@ if (!Caller->hasGC()) Caller->setGC(CalledFunc->getGC()); else if (CalledFunc->getGC() != Caller->getGC()) - return false; + return "incompatible GC"; } // Get the personality function from the callee if it contains a landing pad. @@ -1561,7 +1563,7 @@ // TODO: This isn't 100% true. Some personality functions are proper // supersets of others and can be used in place of the other. else if (CalledPersonality != CallerPersonality) - return false; + return "incompatible personality"; } // We need to figure out which funclet the callsite was in so that we may @@ -1586,7 +1588,7 @@ // for catchpads. for (const BasicBlock &CalledBB : *CalledFunc) { if (isa(CalledBB.getFirstNonPHI())) - return false; + return "catch in cleanup funclet"; } } } else if (isAsynchronousEHPersonality(Personality)) { @@ -1594,7 +1596,7 @@ // funclet in the callee. for (const BasicBlock &CalledBB : *CalledFunc) { if (CalledBB.isEHPad()) - return false; + return "SEH in cleanup funclet"; } } } Index: test/LTO/Resolution/X86/diagnostic-handler-remarks-with-hotness.ll =================================================================== --- test/LTO/Resolution/X86/diagnostic-handler-remarks-with-hotness.ll +++ test/LTO/Resolution/X86/diagnostic-handler-remarks-with-hotness.ll @@ -25,7 +25,8 @@ ; YAML-NEXT: - Callee: tinkywinky ; YAML-NEXT: - String: ' inlined into ' ; YAML-NEXT: - Caller: main -; YAML-NEXT: - String: ' with cost=' +; YAML-NEXT: - String: ' with ' +; YAML-NEXT: - String: 'cost=' ; YAML-NEXT: - Cost: '-15000' ; YAML-NEXT: - String: ' (threshold=' ; YAML-NEXT: - Threshold: '337' Index: test/LTO/Resolution/X86/diagnostic-handler-remarks.ll =================================================================== --- test/LTO/Resolution/X86/diagnostic-handler-remarks.ll +++ test/LTO/Resolution/X86/diagnostic-handler-remarks.ll @@ -26,7 +26,8 @@ ; YAML-NEXT: - Callee: tinkywinky ; YAML-NEXT: - String: ' inlined into ' ; YAML-NEXT: - Caller: main -; YAML-NEXT: - String: ' with cost=' +; YAML-NEXT: - String: ' with ' +; YAML-NEXT: - String: 'cost=' ; YAML-NEXT: - Cost: '-15000' ; YAML-NEXT: - String: ' (threshold=' ; YAML-NEXT: - Threshold: '337' Index: test/LTO/X86/diagnostic-handler-remarks-with-hotness.ll =================================================================== --- test/LTO/X86/diagnostic-handler-remarks-with-hotness.ll +++ test/LTO/X86/diagnostic-handler-remarks-with-hotness.ll @@ -17,7 +17,8 @@ ; YAML-NEXT: - Callee: foo ; YAML-NEXT: - String: ' inlined into ' ; YAML-NEXT: - Caller: main -; YAML-NEXT: - String: ' with cost=' +; YAML-NEXT: - String: ' with ' +; YAML-NEXT: - String: 'cost=' ; YAML-NEXT: - Cost: '-15000' ; YAML-NEXT: - String: ' (threshold=' ; YAML-NEXT: - Threshold: '337' Index: test/LTO/X86/diagnostic-handler-remarks.ll =================================================================== --- test/LTO/X86/diagnostic-handler-remarks.ll +++ test/LTO/X86/diagnostic-handler-remarks.ll @@ -53,7 +53,8 @@ ; YAML-NEXT: - Callee: foo ; YAML-NEXT: - String: ' inlined into ' ; YAML-NEXT: - Caller: main -; YAML-NEXT: - String: ' with cost=' +; YAML-NEXT: - String: ' with ' +; YAML-NEXT: - String: 'cost=' ; YAML-NEXT: - Cost: '-15000' ; YAML-NEXT: - String: ' (threshold=' ; YAML-NEXT: - Threshold: '337' Index: test/ThinLTO/X86/diagnostic-handler-remarks-with-hotness.ll =================================================================== --- test/ThinLTO/X86/diagnostic-handler-remarks-with-hotness.ll +++ test/ThinLTO/X86/diagnostic-handler-remarks-with-hotness.ll @@ -25,7 +25,8 @@ ; YAML1-NEXT: - Callee: foo ; YAML1-NEXT: - String: ' inlined into ' ; YAML1-NEXT: - Caller: main -; YAML1-NEXT: - String: ' with cost=' +; YAML1-NEXT: - String: ' with ' +; YAML1-NEXT: - String: 'cost=' ; YAML1-NEXT: - Cost: '-30' ; YAML1-NEXT: - String: ' (threshold=' ; YAML1-NEXT: - Threshold: '337' @@ -43,7 +44,8 @@ ; YAML2-NEXT: - Callee: bar ; YAML2-NEXT: - String: ' inlined into ' ; YAML2-NEXT: - Caller: foo -; YAML2-NEXT: - String: ' with cost=' +; YAML2-NEXT: - String: ' with ' +; YAML2-NEXT: - String: 'cost=' ; YAML2-NEXT: - Cost: '-30' ; YAML2-NEXT: - String: ' (threshold=' ; YAML2-NEXT: - Threshold: '337' Index: test/ThinLTO/X86/diagnostic-handler-remarks.ll =================================================================== --- test/ThinLTO/X86/diagnostic-handler-remarks.ll +++ test/ThinLTO/X86/diagnostic-handler-remarks.ll @@ -22,7 +22,8 @@ ; YAML1-NEXT: - Callee: foo ; YAML1-NEXT: - String: ' inlined into ' ; YAML1-NEXT: - Caller: main -; YAML1-NEXT: - String: ' with cost=' +; YAML1-NEXT: - String: ' with ' +; YAML1-NEXT: - String: 'cost=' ; YAML1-NEXT: - Cost: '-30' ; YAML1-NEXT: - String: ' (threshold=' ; YAML1-NEXT: - Threshold: '337' @@ -40,7 +41,8 @@ ; YAML2-NEXT: - Callee: bar ; YAML2-NEXT: - String: ' inlined into ' ; YAML2-NEXT: - Caller: foo -; YAML2-NEXT: - String: ' with cost=' +; YAML2-NEXT: - String: ' with ' +; YAML2-NEXT: - String: 'cost=' ; YAML2-NEXT: - Cost: '-30' ; YAML2-NEXT: - String: ' (threshold=' ; YAML2-NEXT: - Threshold: '337' Index: test/Transforms/Inline/ARM/inline-fp.ll =================================================================== --- test/Transforms/Inline/ARM/inline-fp.ll +++ test/Transforms/Inline/ARM/inline-fp.ll @@ -4,14 +4,14 @@ ; Make sure that soft float implementations are calculated as being more expensive ; to the inliner. -; NOFP-DAG: single not inlined into test_single because too costly to inline (cost=125, threshold=75) -; NOFP-DAG: single not inlined into test_single because too costly to inline (cost=125, threshold=75) +; NOFP-DAG: single not inlined into test_single because too costly to inline (cost=125 (threshold=75)) +; NOFP-DAG: single not inlined into test_single because too costly to inline (cost=125 (threshold=75)) ; NOFP-DAG: single_cheap inlined into test_single_cheap with cost=-15 (threshold=75) ; NOFP-DAG: single_cheap inlined into test_single_cheap with cost=-15015 (threshold=75) -; NOFP-DAG: double not inlined into test_double because too costly to inline (cost=125, threshold=75) -; NOFP-DAG: double not inlined into test_double because too costly to inline (cost=125, threshold=75) -; NOFP-DAG: single_force_soft not inlined into test_single_force_soft because too costly to inline (cost=125, threshold=75) -; NOFP-DAG: single_force_soft not inlined into test_single_force_soft because too costly to inline (cost=125, threshold=75) +; NOFP-DAG: double not inlined into test_double because too costly to inline (cost=125 (threshold=75)) +; NOFP-DAG: double not inlined into test_double because too costly to inline (cost=125 (threshold=75)) +; NOFP-DAG: single_force_soft not inlined into test_single_force_soft because too costly to inline (cost=125 (threshold=75)) +; NOFP-DAG: single_force_soft not inlined into test_single_force_soft because too costly to inline (cost=125 (threshold=75)) ; FULLFP-DAG: single inlined into test_single with cost=0 (threshold=75) ; FULLFP-DAG: single inlined into test_single with cost=-15000 (threshold=75) @@ -19,17 +19,17 @@ ; FULLFP-DAG: single_cheap inlined into test_single_cheap with cost=-15015 (threshold=75) ; FULLFP-DAG: double inlined into test_double with cost=0 (threshold=75) ; FULLFP-DAG: double inlined into test_double with cost=-15000 (threshold=75) -; FULLFP-DAG: single_force_soft not inlined into test_single_force_soft because too costly to inline (cost=125, threshold=75) -; FULLFP-DAG: single_force_soft not inlined into test_single_force_soft because too costly to inline (cost=125, threshold=75) +; FULLFP-DAG: single_force_soft not inlined into test_single_force_soft because too costly to inline (cost=125 (threshold=75)) +; FULLFP-DAG: single_force_soft not inlined into test_single_force_soft because too costly to inline (cost=125 (threshold=75)) ; SINGLEFP-DAG: single inlined into test_single with cost=0 (threshold=75) ; SINGLEFP-DAG: single inlined into test_single with cost=-15000 (threshold=75) ; SINGLEFP-DAG: single_cheap inlined into test_single_cheap with cost=-15 (threshold=75) ; SINGLEFP-DAG: single_cheap inlined into test_single_cheap with cost=-15015 (threshold=75) -; SINGLEFP-DAG: double not inlined into test_double because too costly to inline (cost=125, threshold=75) -; SINGLEFP-DAG: double not inlined into test_double because too costly to inline (cost=125, threshold=75) -; SINGLEFP-DAG: single_force_soft not inlined into test_single_force_soft because too costly to inline (cost=125, threshold=75) -; SINGLEFP-DAG: single_force_soft not inlined into test_single_force_soft because too costly to inline (cost=125, threshold=75) +; SINGLEFP-DAG: double not inlined into test_double because too costly to inline (cost=125 (threshold=75)) +; SINGLEFP-DAG: double not inlined into test_double because too costly to inline (cost=125 (threshold=75)) +; SINGLEFP-DAG: single_force_soft not inlined into test_single_force_soft because too costly to inline (cost=125 (threshold=75)) +; SINGLEFP-DAG: single_force_soft not inlined into test_single_force_soft because too costly to inline (cost=125 (threshold=75)) define i32 @test_single(i32 %a, i8 %b, i32 %c, i8 %d) #0 { %call = call float @single(i32 %a, i8 zeroext %b) Index: test/Transforms/Inline/optimization-remarks-passed-yaml.ll =================================================================== --- test/Transforms/Inline/optimization-remarks-passed-yaml.ll +++ test/Transforms/Inline/optimization-remarks-passed-yaml.ll @@ -31,7 +31,8 @@ ; YAML-NEXT: - String: ' inlined into ' ; YAML-NEXT: - Caller: bar ; YAML-NEXT: DebugLoc: { File: /tmp/s.c, Line: 3, Column: 0 } -; YAML-NEXT: - String: ' with cost=' +; YAML-NEXT: - String: ' with ' +; YAML-NEXT: - String: 'cost=' ; YAML-NEXT: - Cost: '{{[0-9\-]+}}' ; YAML-NEXT: - String: ' (threshold=' ; YAML-NEXT: - Threshold: '{{[0-9]+}}' Index: test/Transforms/Inline/optimization-remarks-with-hotness.ll =================================================================== --- test/Transforms/Inline/optimization-remarks-with-hotness.ll +++ test/Transforms/Inline/optimization-remarks-with-hotness.ll @@ -5,8 +5,8 @@ ; RUN: -pass-remarks-analysis=inline -pass-remarks-with-hotness -S 2>&1 \ ; RUN: | FileCheck %s -; CHECK: foo inlined into bar with cost=always (hotness: 30) -; CHECK: foz not inlined into bar because it should never be inlined (cost=never) (hotness: 30) +; CHECK: foo inlined into bar with cost=always: always inline attribute (hotness: 30) +; CHECK: foz not inlined into bar because it should never be inlined cost=never: noinline function attribute (hotness: 30) ; Function Attrs: alwaysinline nounwind uwtable define i32 @foo() #0 !prof !1 { Index: test/Transforms/Inline/optimization-remarks.ll =================================================================== --- test/Transforms/Inline/optimization-remarks.ll +++ test/Transforms/Inline/optimization-remarks.ll @@ -15,7 +15,7 @@ ; HOTNESS: fox will not be inlined into bar because its definition is unavailable ; NO_HOTNESS-NOT: fox will not be inlined into bar because its definition is unavailable ; CHECK: foo inlined into bar with cost=always -; CHECK: foz not inlined into bar because it should never be inlined (cost=never) +; CHECK: foz not inlined into bar because it should never be inlined cost=never: noinline function attribute ; Function Attrs: alwaysinline nounwind uwtable define i32 @foo(i32 %x, i32 %y) #0 !prof !1 { Index: test/tools/gold/X86/opt-remarks.ll =================================================================== --- test/tools/gold/X86/opt-remarks.ll +++ test/tools/gold/X86/opt-remarks.ll @@ -34,7 +34,8 @@ ; YAML-NEXT: - Callee: f ; YAML-NEXT: - String: ' inlined into ' ; YAML-NEXT: - Caller: _start -; YAML-NEXT: - String: ' with cost=' +; YAML-NEXT: - String: ' with ' +; YAML-NEXT: - String: 'cost=' ; YAML-NEXT: - Cost: '0' ; YAML-NEXT: - String: ' (threshold=' ; YAML-NEXT: - Threshold: '337' @@ -51,7 +52,8 @@ ; YAML-HOT-NEXT: - Callee: f ; YAML-HOT-NEXT: - String: ' inlined into ' ; YAML-HOT-NEXT: - Caller: _start -; YAML-HOT-NEXT: - String: ' with cost=' +; YAML-HOT-NEXT: - String: ' with' +; YAML-HOT-NEXT: - String: 'cost=' ; YAML-HOT-NEXT: - Cost: '0' ; YAML-HOT-NEXT: - String: ' (threshold=' ; YAML-HOT-NEXT: - Threshold: '337'