From b35f0af3c7a6402f04c259cf90e7c34ffbdbf9c3 Mon Sep 17 00:00:00 2001 From: David Bolvansky Date: Wed, 1 Aug 2018 07:37:16 +0000 Subject: [PATCH] Enrich inline messages Summary: This patch improves Inliner to provide causes/reasons for negative inline decisions. 1. It adds one new message field to InlineCost to report causes for Always and Never instances. All Never and Always instantiations must provide a simple message. 2. Several functions that used to return the inlining results as boolean are changed to return InlineResult which carries the cause for negative decision. 3. Changed remark priniting and debug output messages to provide the additional messages and related inline cost. 4. Adjusted tests for changed printing. Patch by: yrouban (Yevgeny Rouban) Reviewers: craig.topper, sammccall, sgraenitz, NutshellySima, shchenz, chandlerc, apilipenko, javed.absar, tejohnson, dblaikie, sanjoy, eraman, xbolva00 Reviewed By: tejohnson, xbolva00 Subscribers: xbolva00, llvm-commits, arsenm, mehdi_amini, eraman, haicheng, steven_wu, dexonsmith Differential Revision: https://reviews.llvm.org/D49412 llvm-svn: 338494 --- include/llvm/Analysis/InlineCost.h | 35 ++++- include/llvm/IR/DiagnosticInfo.h | 1 + include/llvm/Transforms/Utils/Cloning.h | 18 ++- lib/Analysis/InlineCost.cpp | 93 ++++++++------ lib/Target/AMDGPU/AMDGPUInline.cpp | 17 ++- lib/Transforms/IPO/AlwaysInliner.cpp | 4 +- lib/Transforms/IPO/Inliner.cpp | 120 ++++++++++-------- lib/Transforms/Utils/InlineFunction.cpp | 29 +++-- ...diagnostic-handler-remarks-with-hotness.ll | 7 +- .../X86/diagnostic-handler-remarks.ll | 5 +- ...diagnostic-handler-remarks-with-hotness.ll | 5 +- test/LTO/X86/diagnostic-handler-remarks.ll | 5 +- ...diagnostic-handler-remarks-with-hotness.ll | 10 +- .../ThinLTO/X86/diagnostic-handler-remarks.ll | 10 +- test/Transforms/Inline/ARM/inline-fp.ll | 24 ++-- .../optimization-remarks-hotness-threshold.ll | 2 +- .../optimization-remarks-passed-yaml.ll | 7 +- .../optimization-remarks-with-hotness.ll | 4 +- .../Transforms/Inline/optimization-remarks.ll | 4 +- test/tools/gold/X86/opt-remarks.ll | 6 +- 20 files changed, 244 insertions(+), 162 deletions(-) diff --git a/include/llvm/Analysis/InlineCost.h b/include/llvm/Analysis/InlineCost.h index 8c412057fb8..529fb75bec9 100644 --- a/include/llvm/Analysis/InlineCost.h +++ b/include/llvm/Analysis/InlineCost.h @@ -74,8 +74,15 @@ class InlineCost { /// 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 @@ public: 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,12 +119,30 @@ public: 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. int getCostDelta() const { return Threshold - getCost(); } }; +/// InlineResult is basically true or false. For false results the message +/// describes a reason why it is decided not to 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; } +}; + /// Thresholds to tune inline cost analysis. The inline cost analysis decides /// the condition to apply a threshold and applies it. Otherwise, /// DefaultThreshold is used. If a threshold is Optional, it is applied only diff --git a/include/llvm/IR/DiagnosticInfo.h b/include/llvm/IR/DiagnosticInfo.h index 81d4ae84bf0..b8fdae2fcad 100644 --- a/include/llvm/IR/DiagnosticInfo.h +++ b/include/llvm/IR/DiagnosticInfo.h @@ -414,6 +414,7 @@ public: 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); diff --git a/include/llvm/Transforms/Utils/Cloning.h b/include/llvm/Transforms/Utils/Cloning.h index 7531fb2d69b..e4d6053b70b 100644 --- a/include/llvm/Transforms/Utils/Cloning.h +++ b/include/llvm/Transforms/Utils/Cloning.h @@ -22,6 +22,7 @@ #include "llvm/ADT/Twine.h" #include "llvm/Analysis/AliasAnalysis.h" #include "llvm/Analysis/AssumptionCache.h" +#include "llvm/Analysis/InlineCost.h" #include "llvm/IR/CallSite.h" #include "llvm/IR/ValueHandle.h" #include "llvm/Transforms/Utils/ValueMapper.h" @@ -232,13 +233,16 @@ public: /// 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. diff --git a/lib/Analysis/InlineCost.cpp b/lib/Analysis/InlineCost.cpp index a6cccc3b591..1d879f8aee9 100644 --- a/lib/Analysis/InlineCost.cpp +++ b/lib/Analysis/InlineCost.cpp @@ -227,7 +227,8 @@ class CallAnalyzer : public InstVisitor { 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 @@ public: 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,9 @@ bool CallAnalyzer::visitInstruction(Instruction &I) { /// 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 +1576,29 @@ bool CallAnalyzer::analyzeBlock(BasicBlock *BB, 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"; + << NV("Callee", &F) << " 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 +1606,15 @@ bool CallAnalyzer::analyzeBlock(BasicBlock *BB, // 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 +1710,7 @@ void CallAnalyzer::findDeadBlocks(BasicBlock *CurrBB, BasicBlock *NextBB) { /// 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 +1745,7 @@ bool CallAnalyzer::analyzeCall(CallSite CS) { // 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 +1824,13 @@ bool CallAnalyzer::analyzeCall(CallSite CS) { // 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 +1883,7 @@ bool CallAnalyzer::analyzeCall(CallSite CS) { // 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 +1977,7 @@ InlineCost llvm::getInlineCost( // 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 +1989,59 @@ InlineCost llvm::getInlineCost( 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. 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(); + // 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"); 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()); } diff --git a/lib/Target/AMDGPU/AMDGPUInline.cpp b/lib/Target/AMDGPU/AMDGPUInline.cpp index 35dd9eb0a47..7060ff353f4 100644 --- a/lib/Target/AMDGPU/AMDGPUInline.cpp +++ b/lib/Target/AMDGPU/AMDGPUInline.cpp @@ -174,18 +174,23 @@ InlineCost AMDGPUInliner::getInlineCost(CallSite CS) { 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); diff --git a/lib/Transforms/IPO/AlwaysInliner.cpp b/lib/Transforms/IPO/AlwaysInliner.cpp index 3b735ddd192..07138718ce2 100644 --- a/lib/Transforms/IPO/AlwaysInliner.cpp +++ b/lib/Transforms/IPO/AlwaysInliner.cpp @@ -150,7 +150,7 @@ InlineCost AlwaysInlinerLegacyPass::getInlineCost(CallSite CS) { // 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"); } diff --git a/lib/Transforms/IPO/Inliner.cpp b/lib/Transforms/IPO/Inliner.cpp index 3da0c2e83eb..fdf47b7bd62 100644 --- a/lib/Transforms/IPO/Inliner.cpp +++ b/lib/Transforms/IPO/Inliner.cpp @@ -64,6 +64,7 @@ #include #include #include +#include #include #include #include @@ -275,8 +276,9 @@ static bool InlineCallIfPossible( // 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 +288,7 @@ static bool InlineCallIfPossible( 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 +367,33 @@ shouldBeDeferred(Function *Caller, CallSite CS, InlineCost IC, return false; } +static std::basic_ostream &operator<<(std::basic_ostream &R, + const ore::NV &Arg) { + return R << Arg.Val; +} + +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 << ": " << ore::NV("Reason", Reason); + return R; +} + +static std::string inlineCostStr(const InlineCost &IC) { + std::stringstream Remark; + Remark << IC; + return Remark.str(); +} + /// 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 +408,32 @@ shouldInline(CallSite CS, function_ref GetInlineCost, 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)"; + << NV("Caller", Caller) << " 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 +454,7 @@ shouldInline(CallSite CS, function_ref GetInlineCost, 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 +486,18 @@ bool LegacyInlinerBase::runOnSCC(CallGraphSCC &SCC) { 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 +622,9 @@ inlineCallsImpl(CallGraphSCC &SCC, CallGraph &CG, 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 +644,21 @@ inlineCallsImpl(CallGraphSCC &SCC, CallGraph &CG, // 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. @@ -971,7 +996,7 @@ PreservedAnalyses InlinerPass::run(LazyCallGraph::SCC &InitialC, 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 +1012,19 @@ PreservedAnalyses InlinerPass::run(LazyCallGraph::SCC &InitialC, 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()) { diff --git a/lib/Transforms/Utils/InlineFunction.cpp b/lib/Transforms/Utils/InlineFunction.cpp index ddc6e07e2f5..f8226f529ee 100644 --- a/lib/Transforms/Utils/InlineFunction.cpp +++ b/lib/Transforms/Utils/InlineFunction.cpp @@ -84,13 +84,15 @@ PreserveAlignmentAssumptions("preserve-alignment-assumptions-during-inlining", 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,9 +1493,10 @@ static void updateCalleeCount(BlockFrequencyInfo *CallerBFI, BasicBlock *CallBB, /// 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, - AAResults *CalleeAAR, bool InsertLifetime, - Function *ForwardVarArgsTo) { +llvm::InlineResult llvm::InlineFunction(CallSite CS, InlineFunctionInfo &IFI, + AAResults *CalleeAAR, + bool InsertLifetime, + Function *ForwardVarArgsTo) { Instruction *TheCall = CS.getInstruction(); assert(TheCall->getParent() && TheCall->getFunction() && "Instruction not in function!"); @@ -1504,7 +1507,7 @@ bool llvm::InlineFunction(CallSite CS, InlineFunctionInfo &IFI, 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 +1521,7 @@ bool llvm::InlineFunction(CallSite CS, InlineFunctionInfo &IFI, if (Tag == LLVMContext::OB_funclet) continue; - return false; + return "unsupported operand bundle"; } } @@ -1537,7 +1540,7 @@ bool llvm::InlineFunction(CallSite CS, InlineFunctionInfo &IFI, 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 +1564,7 @@ bool llvm::InlineFunction(CallSite CS, InlineFunctionInfo &IFI, // 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 +1589,7 @@ bool llvm::InlineFunction(CallSite CS, InlineFunctionInfo &IFI, // for catchpads. for (const BasicBlock &CalledBB : *CalledFunc) { if (isa(CalledBB.getFirstNonPHI())) - return false; + return "catch in cleanup funclet"; } } } else if (isAsynchronousEHPersonality(Personality)) { @@ -1594,7 +1597,7 @@ bool llvm::InlineFunction(CallSite CS, InlineFunctionInfo &IFI, // funclet in the callee. for (const BasicBlock &CalledBB : *CalledFunc) { if (CalledBB.isEHPad()) - return false; + return "SEH in cleanup funclet"; } } } diff --git a/test/LTO/Resolution/X86/diagnostic-handler-remarks-with-hotness.ll b/test/LTO/Resolution/X86/diagnostic-handler-remarks-with-hotness.ll index c45dd4bcd0e..9932f527dc6 100644 --- a/test/LTO/Resolution/X86/diagnostic-handler-remarks-with-hotness.ll +++ b/test/LTO/Resolution/X86/diagnostic-handler-remarks-with-hotness.ll @@ -25,14 +25,15 @@ ; 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: - String: ', threshold=' ; YAML-NEXT: - Threshold: '337' ; YAML-NEXT: - String: ')' ; YAML-NEXT: ... -; CHECK: tinkywinky inlined into main with cost=-15000 (threshold=337) (hotness: 300) +; CHECK: tinkywinky inlined into main with (cost=-15000, threshold=337) (hotness: 300) target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-scei-ps4" diff --git a/test/LTO/Resolution/X86/diagnostic-handler-remarks.ll b/test/LTO/Resolution/X86/diagnostic-handler-remarks.ll index 8a3c97a8438..03db4a722b5 100644 --- a/test/LTO/Resolution/X86/diagnostic-handler-remarks.ll +++ b/test/LTO/Resolution/X86/diagnostic-handler-remarks.ll @@ -26,9 +26,10 @@ ; 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: - String: ', threshold=' ; YAML-NEXT: - Threshold: '337' ; YAML-NEXT: - String: ')' ; YAML-NEXT: ... diff --git a/test/LTO/X86/diagnostic-handler-remarks-with-hotness.ll b/test/LTO/X86/diagnostic-handler-remarks-with-hotness.ll index 81116996ae2..d1dddf1a71f 100644 --- a/test/LTO/X86/diagnostic-handler-remarks-with-hotness.ll +++ b/test/LTO/X86/diagnostic-handler-remarks-with-hotness.ll @@ -17,9 +17,10 @@ ; 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: - String: ', threshold=' ; YAML-NEXT: - Threshold: '337' ; YAML-NEXT: - String: ')' ; YAML-NEXT: ... diff --git a/test/LTO/X86/diagnostic-handler-remarks.ll b/test/LTO/X86/diagnostic-handler-remarks.ll index b6d02675bf1..94f88ec43c5 100644 --- a/test/LTO/X86/diagnostic-handler-remarks.ll +++ b/test/LTO/X86/diagnostic-handler-remarks.ll @@ -53,9 +53,10 @@ ; 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: - String: ', threshold=' ; YAML-NEXT: - Threshold: '337' ; YAML-NEXT: - String: ')' ; YAML-NEXT: ... diff --git a/test/ThinLTO/X86/diagnostic-handler-remarks-with-hotness.ll b/test/ThinLTO/X86/diagnostic-handler-remarks-with-hotness.ll index 0f0e26e53ca..239e6efe3a1 100644 --- a/test/ThinLTO/X86/diagnostic-handler-remarks-with-hotness.ll +++ b/test/ThinLTO/X86/diagnostic-handler-remarks-with-hotness.ll @@ -25,9 +25,10 @@ ; 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: - String: ', threshold=' ; YAML1-NEXT: - Threshold: '337' ; YAML1-NEXT: - String: ')' ; YAML1-NEXT: ... @@ -43,9 +44,10 @@ ; 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: - String: ', threshold=' ; YAML2-NEXT: - Threshold: '337' ; YAML2-NEXT: - String: ')' ; YAML2-NEXT: ... diff --git a/test/ThinLTO/X86/diagnostic-handler-remarks.ll b/test/ThinLTO/X86/diagnostic-handler-remarks.ll index 2fd2cf8102b..d4606ba999d 100644 --- a/test/ThinLTO/X86/diagnostic-handler-remarks.ll +++ b/test/ThinLTO/X86/diagnostic-handler-remarks.ll @@ -22,9 +22,10 @@ ; 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: - String: ', threshold=' ; YAML1-NEXT: - Threshold: '337' ; YAML1-NEXT: - String: ')' ; YAML1-NEXT: ... @@ -40,9 +41,10 @@ ; 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: - String: ', threshold=' ; YAML2-NEXT: - Threshold: '337' ; YAML2-NEXT: - String: ')' ; YAML2-NEXT: ... diff --git a/test/Transforms/Inline/ARM/inline-fp.ll b/test/Transforms/Inline/ARM/inline-fp.ll index b4e76dfc7d2..be3dd2a93fd 100644 --- a/test/Transforms/Inline/ARM/inline-fp.ll +++ b/test/Transforms/Inline/ARM/inline-fp.ll @@ -6,26 +6,26 @@ ; 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: 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) -; FULLFP-DAG: single inlined into test_single with cost=0 (threshold=75) -; FULLFP-DAG: single inlined into test_single with cost=-15000 (threshold=75) -; FULLFP-DAG: single_cheap inlined into test_single_cheap with cost=-15 (threshold=75) -; 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 inlined into test_single with (cost=0, threshold=75) +; FULLFP-DAG: single inlined into test_single with (cost=-15000, threshold=75) +; FULLFP-DAG: single_cheap inlined into test_single_cheap with (cost=-15, threshold=75) +; 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) -; 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: 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) diff --git a/test/Transforms/Inline/optimization-remarks-hotness-threshold.ll b/test/Transforms/Inline/optimization-remarks-hotness-threshold.ll index 6ae9543754b..16d7db33412 100644 --- a/test/Transforms/Inline/optimization-remarks-hotness-threshold.ll +++ b/test/Transforms/Inline/optimization-remarks-hotness-threshold.ll @@ -14,7 +14,7 @@ ; 4 return foo(); ; 5 } -; CHECK: remark: /tmp/s.c:4:10: foo inlined into bar with cost={{[0-9\-]+}} (threshold={{[0-9]+}}) +; CHECK: remark: /tmp/s.c:4:10: foo inlined into bar with (cost={{[0-9\-]+}}, threshold={{[0-9]+}}) ; THRESHOLD-NOT: remark ; ModuleID = '/tmp/s.c' diff --git a/test/Transforms/Inline/optimization-remarks-passed-yaml.ll b/test/Transforms/Inline/optimization-remarks-passed-yaml.ll index e0ba213b6de..0ac76354a2b 100644 --- a/test/Transforms/Inline/optimization-remarks-passed-yaml.ll +++ b/test/Transforms/Inline/optimization-remarks-passed-yaml.ll @@ -17,7 +17,7 @@ ; 4 return foo(); ; 5 } -; CHECK: remark: /tmp/s.c:4:10: foo inlined into bar with cost={{[0-9\-]+}} (threshold={{[0-9]+}}) (hotness: 30) +; CHECK: remark: /tmp/s.c:4:10: foo inlined into bar with (cost={{[0-9\-]+}}, threshold={{[0-9]+}}) (hotness: 30) ; YAML: --- !Passed ; YAML-NEXT: Pass: inline @@ -31,9 +31,10 @@ ; 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: - String: ', threshold=' ; YAML-NEXT: - Threshold: '{{[0-9]+}}' ; YAML-NEXT: - String: ')' ; YAML-NEXT: ... diff --git a/test/Transforms/Inline/optimization-remarks-with-hotness.ll b/test/Transforms/Inline/optimization-remarks-with-hotness.ll index 3614c3b5230..1a1c0f4bac1 100644 --- a/test/Transforms/Inline/optimization-remarks-with-hotness.ll +++ b/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 { diff --git a/test/Transforms/Inline/optimization-remarks.ll b/test/Transforms/Inline/optimization-remarks.ll index 16f782682e1..72e90aefa61 100644 --- a/test/Transforms/Inline/optimization-remarks.ll +++ b/test/Transforms/Inline/optimization-remarks.ll @@ -14,8 +14,8 @@ ; 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: foo inlined into bar with (cost=always): always inline attribute +; 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 { diff --git a/test/tools/gold/X86/opt-remarks.ll b/test/tools/gold/X86/opt-remarks.ll index 2cca9bf5d2f..7a83df4af53 100644 --- a/test/tools/gold/X86/opt-remarks.ll +++ b/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'