Index: include/llvm/Transforms/IPO/InlineReport.h =================================================================== --- include/llvm/Transforms/IPO/InlineReport.h +++ include/llvm/Transforms/IPO/InlineReport.h @@ -30,8 +30,7 @@ typedef enum { Basic = 1, // Print basic information like what was inlined - Reasons = 2, // Add reasons for inlining or not inlining - // (to be implemented) + Reasons = 2, // Add reasons for inlining or not inlining SameLine = 4, // Put the reasons and the call site on the same lime LineCol = 8, // Print the line and column of the call sites // if we had appropriate source position information @@ -58,9 +57,10 @@ // \brief Constructor for InlineReportCallSite // The source file is given by 'M'. The line and column info by 'Dloc' explicit InlineReportCallSite(InlineReportFunction* IRCallee, bool IsInlined, - Module* Module, DebugLoc* DLoc, Instruction* I) : IRCallee(IRCallee), - IsInlined(IsInlined), InlineCost(-1), OuterInlineCost (-1), - InlineThreshold (-1), Call (I), M (Module) { + InlineReportTypes::InlineReason Reason, Module* Module, DebugLoc* DLoc, + Instruction* I) : IRCallee(IRCallee), IsInlined(IsInlined), Reason(Reason), + InlineCost(-1), OuterInlineCost (-1), InlineThreshold (-1), Call (I), + M (Module) { Line = DLoc && DLoc->get() ? DLoc->getLine() : 0; Col = DLoc && DLoc->get() ? DLoc->getCol() : 0; Children.clear(); @@ -75,6 +75,10 @@ InlineReportCallSite* cloneBase(const ValueToValueMapTy& IIMap); InlineReportFunction* getIRCallee() const { return IRCallee; } + InlineReportTypes::InlineReason getReason() const + { return Reason; } + void setReason(InlineReportTypes::InlineReason MyReason) + { Reason = MyReason; } bool getIsInlined() const { return IsInlined; } void setIsInlined(bool Inlined) { IsInlined = Inlined; } @@ -117,6 +121,7 @@ private: InlineReportFunction* IRCallee; bool IsInlined; + InlineReportTypes::InlineReason Reason; int InlineCost; int OuterInlineCost; int InlineThreshold; @@ -225,10 +230,10 @@ // \brief Create an InlineReportCallSite to represent CS InlineReportCallSite* addCallSite(Function* F, CallSite* CS, Module* M); - // \brief Create an InlineReportCallSite to represent CS, if one does + // \brief Create an InlineReportCallSite to represent CS, if one does // not already exist - InlineReportCallSite* addNewCallSite(Function* F, CallSite* CS, - Module* M); + InlineReportCallSite* addNewCallSite(Function* F, CallSite* CS, + Module* M); // \brief Indicate that the Function is dead void setDead(Function *F); @@ -239,6 +244,16 @@ void inlineCallSite(Instruction* NI, InlineReportCallSite* IRCS, Module* M, Function* Callee, InlineFunctionInfo& InlineInfo); + /// \brief Record the reason a call site is or is not inlined. + void setReasonNotInlined(const CallSite& CS, + InlineReportTypes::InlineReason Reason); + void setReasonNotInlined(const CallSite& CS, const InlineCost& IC); + void setReasonNotInlined(const CallSite& CS, const InlineCost& IC, + int TotalSecondaryCost); + void setReasonIsInlined(const CallSite& CS, + InlineReportTypes::InlineReason Reason); + void setReasonIsInlined(const CallSite& CS, const InlineCost& IC); + /// \brief Print the inlining report at the given level. void print() const; @@ -318,7 +333,9 @@ InlineReportInstructionCallSiteMap::const_iterator MapIt; MapIt = IR->IRInstructionCallSiteMap.find(I); if (MapIt != IR->IRInstructionCallSiteMap.end()) { + InlineReportCallSite* IRCS = MapIt->second; IR->IRInstructionCallSiteMap.erase(MapIt); + IRCS->setReason(InlineReportTypes::NinlrDeleted); } } } Index: include/llvm/Transforms/Utils/Cloning.h =================================================================== --- include/llvm/Transforms/Utils/Cloning.h +++ include/llvm/Transforms/Utils/Cloning.h @@ -18,6 +18,7 @@ #ifndef LLVM_TRANSFORMS_UTILS_CLONING_H #define LLVM_TRANSFORMS_UTILS_CLONING_H +#include "llvm/Analysis/InlineCost.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/Twine.h" #include "llvm/Analysis/AliasAnalysis.h" @@ -222,6 +223,16 @@ bool InlineFunction(CallSite CS, InlineFunctionInfo &IFI, AAResults *CalleeAAR = nullptr, bool InsertLifetime = true); +bool InlineFunction(CallInst *C, InlineFunctionInfo &IFI, + InlineReportTypes::InlineReason* Reason, + AAResults *CalleeAAR = nullptr, bool InsertLifetime = true); +bool InlineFunction(InvokeInst *II, InlineFunctionInfo &IFI, + InlineReportTypes::InlineReason* Reason, + AAResults *CalleeAAR = nullptr, bool InsertLifetime = true); +bool InlineFunction(CallSite CS, InlineFunctionInfo &IFI, + InlineReportTypes::InlineReason *Reason, + AAResults *CalleeAAR = nullptr, bool InsertLifetime = true); + /// \brief Clones a loop \p OrigLoop. Returns the loop and the blocks in \p /// Blocks. /// Index: lib/Transforms/IPO/InlineAlways.cpp =================================================================== --- lib/Transforms/IPO/InlineAlways.cpp +++ lib/Transforms/IPO/InlineAlways.cpp @@ -97,7 +97,7 @@ InlineReason Reason; if (Callee && !Callee->isDeclaration() && CS.hasFnAttr(Attribute::AlwaysInline) && isInlineViable(*Callee, Reason)) - return InlineCost::getAlways(); + return InlineCost::getAlways(InlrAlwaysInline); - return InlineCost::getNever(); + return InlineCost::getNever(NinlrNotAlwaysInline); } Index: lib/Transforms/IPO/InlineReport.cpp =================================================================== --- lib/Transforms/IPO/InlineReport.cpp +++ lib/Transforms/IPO/InlineReport.cpp @@ -26,7 +26,7 @@ /// N is a bit mask with the following interpretation of the bits /// 0: No inlining report /// 1: Simple inlining report -/// 2: Add inlining reasons (to be implemented) +/// 2: Add inlining reasons (to be implemented) /// 4: Put the inlining reasons on the same line as the call sites /// 8: Print the line and column info for each call site if available /// 16: Print the file for each call site @@ -40,6 +40,134 @@ // The functions below implement the printing of the inlining report // +// The reasons that a call site is inlined or not inlinined fall into +// several categories. These are indicated by the InlPtrType for each +// reason. +// +// The simplest category is those reasons which are absolute: we inlined +// or didn't inline the call site exactly because of this. In this case, +// printing a simple text string suffices to describe why the call site +// was or was not inlined. These reasons have the InlPrtType InlPtrSimple. +// +// Sometimes, however, the real reason a call site was or was not inlined +// is because the values of the cost and the threshold for that call site. +// In these cases, if the cost <= threshold, the inlining was done, but +// if cost > threshold it was not. But there are often large bonuses and +// penalities that contribute to the value of the cost and/or threshold. +// In such cases, reporting the principal reason the cost and/or threshold +// was adjusted provides a more meaningful reason than simply citing the +// cost and threshold numbers, and we do that. These reasons have the +// InlPtrType InlPtrCost. +// +// Finally, there are some reasons that can't be adequately displayed by +// either of the above two techniques. These have the InlPtrType +// InlPrtSpecial. The handling of them is done directly within the report +// printing functions themselves. + +typedef enum { + InlPrtNone, // Used for sentinels and the generic value "InlrNoReason" + // No text is expected to be printed for these. + InlPrtSimple, // Print only the text for the (non-)inlining reason + InlPrtCost, // Print the text and cost info for the (non-)inlining reason + InlPrtSpecial // The function InlineReportCallSite::print needs to have + // special cased code to handle it +} InlPrtType; + +typedef struct { + InlPrtType Type; // Classification of inlining reason + const char* Message; // Text message for inlining reason (or nullptr) +} InlPrtRecord; + +/// +/// \brief A table of entries, one for each possible (non-)inlining reason +/// +const static InlPrtRecord InlineReasonText[NinlrLast + 1] = { + // InlrFirst, + InlPrtNone, nullptr, + // InlrNoReason, + InlPrtNone, nullptr, + // InlrAlwaysInline, + InlPrtSimple, "Callee is always inline", + // InlrSingleLocalCall, + InlPrtCost, "Callee has single callsite and local linkage", + // InlrSingleBasicBlock, + InlPrtCost, "Callee is single basic block", + // InlrEmptyFunction, + InlPrtCost, "Callee is empty", + // InlrVectorBonus, + InlPrtCost, "Callee has vector instructions", + // InlrProfitable, + InlPrtCost, "Inlining is profitable", + // InlrLast, + InlPrtNone, nullptr, + // NinlrFirst, + InlPrtNone, nullptr, + // NinlrNoReason, + InlPrtSimple, "Not tested for inlining", + // NinlrColdCC, + InlPrtCost, "Callee has cold calling convention", + // NinlrDeleted, + InlPrtSpecial, nullptr, + // NinlrDuplicateCall, + InlPrtSimple, "Callee cannot be called more than once", + // NinlrDynamicAlloca, + InlPrtCost, "Callee has dynamic alloca", + // NinlrExtern, + InlPrtSpecial, nullptr, + // NinlrIndirect, + InlPrtSpecial, "Call site is indirect", + // NinlrIndirectBranch, + InlPrtCost, "Callee has indirect branch", + // NinlrBlockAddress, + InlPrtCost, "Callee has block address", + // NinlrCallsLocalEscape, + InlPrtCost, "Callee calls localescape", + // NinlrNeverInline, + InlPrtSimple, "Callee is never inline", + // NinlrIntrinsic, + InlPrtSimple,"Callee is intrinsic", + // NinlrOuterInlining, + InlPrtSpecial, "High outer inlining cost", + // NinlrRecursive, + InlPrtSimple, "Callee has recursion", + // NinlrReturnsTwice, + InlPrtSimple, "Callee has returns twice instruction", + // NinlrTooMuchStack, + InlPrtCost, "Callee uses too much stack space", + // NinlrVarargs, + InlPrtSimple, "Callee is varargs", + // NinlrMismatchedAttributes, + InlPrtSimple, "Caller/Callee mismatched attributes", + // NinlrMismatchedGC + InlPrtSimple, "Caller/Callee garbage collector mismatch", + // NinlrMismatchedPersonality, + InlPrtSimple, "Caller/Callee personality mismatch", + // NinlrNoinlineAttribute + InlPrtSimple, "Callee has noinline attribute", + // NinlrNoinlineCallsite, + InlPrtSimple, "Callsite is noinline", + // NinlrNoReturn, + InlPrtSimple, "Callee is noreturn", + // NinlrOptNone, + InlPrtSimple, "Callee is opt none", + // NinlrInterposable + InlPrtSimple, "Callee is interposable", + // NinlrNotAlwaysInline, + InlPrtSimple, "Callee is not always_inline", + // NinlrNewlyCreated, + InlPrtSimple, "Newly created callsite", + // NinlrNotProfitable, + InlPrtCost, "Inlining is not profitable", + // NinlrOpBundles, + InlPrtSimple, "Cannot inline call with operand bundle", + // NinlrMSVCEH, + InlPrtSimple, "Microsoft EH prevents inlining", + // NinlrSEH, + InlPrtSimple, "Structured EH prevents inlining", + // NinlrLast + InlPrtNone, nullptr +}; + // // Member functions for class InlineReportCallSite // @@ -56,7 +184,7 @@ const InlineReportCallSite& Base, Instruction* NI) { InlineReportCallSite* NewCS = new InlineReportCallSite(Base.IRCallee, - Base.IsInlined, Base.M, nullptr, NI); + Base.IsInlined, Base.Reason, Base.M, nullptr, NI); NewCS->IsInlined = Base.IsInlined; NewCS->InlineCost = Base.InlineCost; NewCS->OuterInlineCost = Base.OuterInlineCost; @@ -97,6 +225,55 @@ } /// +/// \brief Print a simple message +/// +/// message: The message being printed +/// indentCount: The number of indentations before printing the message +/// level: The level N from '-inline-report=N' +/// +static void printSimpleMessage(const char* Message, unsigned IndentCount, + unsigned Level, bool IsInline) { + if (Level & InlineReportOptions::Reasons) { + if (Level & InlineReportOptions::SameLine) { + llvm::errs() << " "; + } + else { + llvm::errs() << "\n"; + printIndentCount(IndentCount + 1); + } + llvm::errs() << (IsInline ? "<<" : "[["); + llvm::errs() << Message; + llvm::errs() << (IsInline ? ">>" : "]]"); + llvm::errs() << "\n"; + } + else { + llvm::errs() << "\n"; + } +} + +/// +/// \brief Print the inlining cost and threshold values +/// +void InlineReportCallSite::printCostAndThreshold(void) { + llvm::errs() << " (" << getInlineCost(); + if (getIsInlined()) { + llvm::errs() << "<="; + } + else { + llvm::errs() << ">"; + } + llvm::errs() << getInlineThreshold() << ")"; +} + +/// +/// \brief Print the outer inlining cost and threshold values +/// +void InlineReportCallSite::printOuterCostAndThreshold(void) { + llvm::errs() << " (" << getOuterInlineCost() << ">" << getInlineCost() + << ">" << getInlineThreshold() << ")"; +} + +/// /// \brief Print the linkage info for a function 'F' as a single letter, /// if the 'Level' specifies InlineReportOptions::Linkage. /// For an explanation of the meaning of these letters, see InlineReport.h. @@ -134,16 +311,56 @@ /// level: The level N from '-inline-report=N' /// void InlineReportCallSite::print(unsigned IndentCount, unsigned Level) { + assert(InlineReasonText[getReason()].Type != InlPrtNone); printIndentCount(IndentCount); if (getIsInlined()) { llvm::errs() << "-> INLINE: "; printCalleeNameModuleLineCol(Level); + if (InlineReasonText[getReason()].Type == InlPrtCost) { + printCostAndThreshold(); + } + printSimpleMessage(InlineReasonText[getReason()].Message, + IndentCount, Level, true); } else { - llvm::errs() << "-> "; - printCalleeNameModuleLineCol(Level); + if (InlineReasonText[getReason()].Type == InlPrtSpecial) { + switch (getReason()) { + case NinlrDeleted: + llvm::errs() << "-> DELETE: "; + printCalleeNameModuleLineCol(Level); + llvm::errs() << "\n"; + break; + case NinlrExtern: + llvm::errs() << "-> EXTERN: "; + printCalleeNameModuleLineCol(Level); + llvm::errs() << "\n"; + break; + case NinlrIndirect: + llvm::errs() << "-> INDIRECT: "; + printCalleeNameModuleLineCol(Level); + printSimpleMessage(InlineReasonText[getReason()].Message, + IndentCount, Level, false); + break; + case NinlrOuterInlining: + printCalleeNameModuleLineCol(Level); + printOuterCostAndThreshold(); + printSimpleMessage(InlineReasonText[getReason()].Message, + IndentCount, Level, false); + break; + default: + assert(0); + } + } + else { + llvm::errs() << "-> "; + printCalleeNameModuleLineCol(Level); + if (InlineReasonText[getReason()].Type == InlPrtCost) { + printCostAndThreshold(); + } + printSimpleMessage(InlineReasonText[getReason()].Message, + IndentCount, Level, false); + } } - llvm::errs() << "\n"; } // @@ -177,7 +394,7 @@ } InlineReportFunctionMap::const_iterator MapIt = IRFunctionMap.find(F); if (MapIt != IRFunctionMap.end()) { - InlineReportFunction* IRF = MapIt->second; + InlineReportFunction* IRF = MapIt->second; makeCurrent(M, F); return IRF; } @@ -212,23 +429,23 @@ ? addFunction(Callee, M) : MapItC->second; } InlineReportCallSite* IRCS = new InlineReportCallSite(IRFC, false, - M, &DLoc, I); + NinlrNoReason, M, &DLoc, I); IRF->addCallSite(IRCS); IRInstructionCallSiteMap.insert(std::make_pair(I, IRCS)); addCallback(I); return IRCS; } -InlineReportCallSite* InlineReport::addNewCallSite(Function* F, CallSite* CS, +InlineReportCallSite* InlineReport::addNewCallSite(Function* F, CallSite* CS, Module* M) { - if (Level == 0) { - return nullptr; + if (Level == 0) { + return nullptr; } InlineReportCallSite* IRCS = getCallSite(CS); - if (IRCS != nullptr) - return IRCS; - return addCallSite(F, CS, M); -} + if (IRCS != nullptr) + return IRCS; + return addCallSite(F, CS, M); +} void InlineReport::setDead(Function* F) { if (Level == 0) { @@ -313,10 +530,93 @@ IRCS->setCall(nullptr); } +void InlineReport::setReasonIsInlined(const CallSite& CS, InlineReason Reason) { + if (Level == 0) { + return; + } + assert(IsInlinedReason(Reason)); + Instruction* NI = CS.getInstruction(); + InlineReportInstructionCallSiteMap::const_iterator + MapIt = IRInstructionCallSiteMap.find(NI); + assert(MapIt != IRInstructionCallSiteMap.end()); + InlineReportCallSite* IRCS = MapIt->second; + IRCS->setReason(Reason); +} + +void InlineReport::setReasonIsInlined(const CallSite& CS, + const InlineCost& IC) { + if (Level == 0) { + return; + } + InlineReason Reason = IC.getInlineReason(); + assert(IsInlinedReason(Reason)); + Instruction* NI = CS.getInstruction(); + InlineReportInstructionCallSiteMap::const_iterator + MapIt = IRInstructionCallSiteMap.find(NI); + assert(MapIt != IRInstructionCallSiteMap.end()); + InlineReportCallSite* IRCS = MapIt->second; + IRCS->setReason(IC.getInlineReason()); + IRCS->setInlineCost(IC.getCost()); + IRCS->setInlineThreshold(IC.getCost() + IC.getCostDelta()); +} + +void InlineReport::setReasonNotInlined(const CallSite& CS, + InlineReason Reason) { + if (Level == 0) { + return; + } + assert(IsNotInlinedReason(Reason)); + Instruction* NI = CS.getInstruction(); + InlineReportInstructionCallSiteMap::const_iterator + MapIt = IRInstructionCallSiteMap.find(NI); + assert(MapIt != IRInstructionCallSiteMap.end()); + InlineReportCallSite* IRCS = MapIt->second; + IRCS->setReason(Reason); +} + +void InlineReport::setReasonNotInlined(const CallSite& CS, + const InlineCost& IC) { + if (Level == 0) { + return; + } + InlineReason Reason = IC.getInlineReason(); + assert(IsNotInlinedReason(Reason)); + Instruction* NI = CS.getInstruction(); + InlineReportInstructionCallSiteMap::const_iterator + MapIt = IRInstructionCallSiteMap.find(NI); + assert(MapIt != IRInstructionCallSiteMap.end()); + InlineReportCallSite* IRCS = MapIt->second; + IRCS->setReason(Reason); + IRCS->setInlineCost(IC.getCost()); + IRCS->setInlineThreshold(IC.getCost() + IC.getCostDelta()); +} + +void InlineReport::setReasonNotInlined(const CallSite& CS, + const InlineCost& IC, int TotalSecondaryCost) { + if (Level == 0) { + return; + } + InlineReason Reason = IC.getInlineReason(); + assert(Reason == NinlrOuterInlining); + setReasonNotInlined(CS, IC); + Instruction* NI = CS.getInstruction(); + InlineReportInstructionCallSiteMap::const_iterator + MapIt = IRInstructionCallSiteMap.find(NI); + assert(MapIt != IRInstructionCallSiteMap.end()); + InlineReportCallSite* IRCS = MapIt->second; + IRCS->setOuterInlineCost(TotalSecondaryCost); +} + void InlineReport::printOptionValues(void) const { llvm::errs() << "Option Values:\n"; llvm::errs() << " inline-threshold: " << llvm::getDefaultInlineThreshold() << "\n"; + llvm::errs() << " inlinehint-threshold: " + << llvm::getHintThreshold() << "\n"; + llvm::errs() << " inlinecold-threshold: " + << llvm::getColdThreshold() << "\n"; + llvm::errs() << " inlineoptsize-threshold: " + << llvm::getOptSizeThreshold() << "\n"; llvm::errs() << "\n"; } @@ -466,6 +766,7 @@ } InlineReportCallSite* IRCS = addCallSite(F, &CS, M); assert(IRCS != nullptr); + IRCS->setReason(NinlrNewlyCreated); } } IRF->setCurrent(true); @@ -473,7 +774,7 @@ void InlineReport::makeAllNotCurrent(void) { if (Level == 0) { - return; + return; } InlineReportFunctionMap::const_iterator It, E; for (It = IRFunctionMap.begin(), E = IRFunctionMap.end(); It != E; ++It) { @@ -503,7 +804,7 @@ InlineReportCallSite* InlineReport::getCallSite(CallSite* CS) { if (Level == 0) { return nullptr; - } + } Instruction* NI = CS->getInstruction(); InlineReportInstructionCallSiteMap::const_iterator MapItC = IRInstructionCallSiteMap.find(NI); Index: lib/Transforms/IPO/Inliner.cpp =================================================================== --- lib/Transforms/IPO/Inliner.cpp +++ lib/Transforms/IPO/Inliner.cpp @@ -34,6 +34,7 @@ #include "llvm/Transforms/Utils/Cloning.h" #include "llvm/Transforms/Utils/Local.h" using namespace llvm; +using namespace InlineReportTypes; #define DEBUG_TYPE "inline" @@ -80,7 +81,8 @@ /// any new allocas to the set if not possible. static bool InlineCallIfPossible(Pass &P, CallSite CS, InlineFunctionInfo &IFI, InlinedArrayAllocasTy &InlinedArrayAllocas, - int InlineHistory, bool InsertLifetime) { + int InlineHistory, bool InsertLifetime, + InlineReason* IR) { Function *Callee = CS.getCalledFunction(); Function *Caller = CS.getCaller(); @@ -94,8 +96,9 @@ // Try to inline the function. Get the list of static allocas that were // inlined. - if (!InlineFunction(CS, IFI, &AAR, InsertLifetime)) + if (!InlineFunction(CS, IFI, IR, &AAR, InsertLifetime)) { return false; + } AttributeFuncs::mergeAttributesForInlining(*Caller, *Callee); @@ -241,6 +244,7 @@ << ", Call: " << *CS.getInstruction() << "\n"); emitAnalysis(CS, Twine(CS.getCalledFunction()->getName()) + " should always be inlined (cost=always)"); + getReport().setReasonIsInlined(CS, InlrAlwaysInline); return true; } @@ -249,6 +253,7 @@ << ", Call: " << *CS.getInstruction() << "\n"); emitAnalysis(CS, Twine(CS.getCalledFunction()->getName() + " should never be inlined (cost=never)")); + getReport().setReasonNotInlined(CS, NinlrNeverInline); return false; } @@ -261,6 +266,7 @@ " too costly to inline (cost=") + Twine(IC.getCost()) + ", threshold=" + Twine(IC.getCostDelta() + IC.getCost()) + ")"); + getReport().setReasonNotInlined(CS, IC); return false; } @@ -332,6 +338,8 @@ CS.getCalledFunction()->getName() + " increases the cost of inlining " + CS.getCaller()->getName() + " in other contexts")); + IC.setInlineReason(NinlrOuterInlining); + getReport().setReasonNotInlined(CS, IC, TotalSecondaryCost); return false; } } @@ -343,6 +351,7 @@ CS, CS.getCalledFunction()->getName() + Twine(" can be inlined into ") + CS.getCaller()->getName() + " with cost=" + Twine(IC.getCost()) + " (threshold=" + Twine(IC.getCostDelta() + IC.getCost()) + ")"); + getReport().setReasonIsInlined(CS, IC); return true; } @@ -365,7 +374,7 @@ ACT = &getAnalysis(); auto &TLI = getAnalysis().getTLI(); - CG.registerCGReport(&Report); + CG.registerCGReport(&Report); SmallPtrSet SCCFunctions; DEBUG(dbgs() << "Inliner visiting SCC:"); @@ -401,16 +410,19 @@ continue; getReport().addNewCallSite(F, &CS, &CG.getModule()); - if (isa(I)) { + if (isa(I)) { + getReport().setReasonNotInlined(CS, NinlrIntrinsic); continue; - } + } // If this is a direct call to an external function, we can never inline // it. If it is an indirect call, inlining may resolve it to be a // direct call, so we keep it. if (Function *Callee = CS.getCalledFunction()) - if (Callee->isDeclaration()) + if (Callee->isDeclaration()) { + getReport().setReasonNotInlined(CS, NinlrExtern); continue; + } CallSites.push_back(std::make_pair(CS, -1)); } @@ -459,12 +471,20 @@ DEBUG(dbgs() << " -> Deleting dead call: " << *CS.getInstruction() << "\n"); // Update the call graph by deleting the edge from Callee to Caller. + getReport().setReasonNotInlined(CS, NinlrDeleted); CG[Caller]->removeCallEdgeFor(CS); CS.getInstruction()->eraseFromParent(); ++NumCallsDeleted; } else { // We can only inline direct calls to non-declarations. - if (!Callee || Callee->isDeclaration()) continue; + if (!Callee) { + getReport().setReasonNotInlined(CS, NinlrIndirect); + continue; + } + if (Callee->isDeclaration()) { + getReport().setReasonNotInlined(CS, NinlrExtern); + continue; + } // If this call site was obtained by inlining another function, verify // that the include path for the function did not include the callee @@ -473,8 +493,10 @@ // infinitely inline. int InlineHistoryID = CallSites[CSi].second; if (InlineHistoryID != -1 && - InlineHistoryIncludes(Callee, InlineHistoryID, InlineHistory)) + InlineHistoryIncludes(Callee, InlineHistoryID, InlineHistory)) { + getReport().setReasonNotInlined(CS, NinlrRecursive); continue; + } LLVMContext &CallerCtx = Caller->getContext(); @@ -495,9 +517,11 @@ InlineReportCallSite* IRCS = getReport().getCallSite(&CS); Instruction* NI = CS.getInstruction(); getReport().setActiveInlineInstruction(NI); + InlineReason Reason = NinlrNoReason; if (!InlineCallIfPossible(*this, CS, InlineInfo, InlinedArrayAllocas, - InlineHistoryID, InsertLifetime)) { + InlineHistoryID, InsertLifetime, &Reason)) { getReport().setActiveInlineInstruction(nullptr); + getReport().setReasonNotInlined(CS, Reason); emitOptimizationRemarkMissed(CallerCtx, DEBUG_TYPE, *Caller, DLoc, Twine(Callee->getName() + " will not be inlined into " + Index: lib/Transforms/Utils/InlineFunction.cpp =================================================================== --- lib/Transforms/Utils/InlineFunction.cpp +++ lib/Transforms/Utils/InlineFunction.cpp @@ -44,6 +44,7 @@ #include using namespace llvm; +using namespace InlineReportTypes; static cl::opt EnableNoAliasConversion("enable-noalias-to-md-conversion", cl::init(true), @@ -56,12 +57,25 @@ cl::desc("Convert align attributes to assumptions during inlining.")); bool llvm::InlineFunction(CallInst *CI, InlineFunctionInfo &IFI, + InlineReason* Reason, AAResults *CalleeAAR, bool InsertLifetime) { - return InlineFunction(CallSite(CI), IFI, CalleeAAR, InsertLifetime); + return InlineFunction(CallSite(CI), IFI, Reason, CalleeAAR, InsertLifetime); } bool llvm::InlineFunction(InvokeInst *II, InlineFunctionInfo &IFI, + InlineReason* Reason, + AAResults *CalleeAAR, bool InsertLifetime) { + return InlineFunction(CallSite(II), IFI, Reason, CalleeAAR, InsertLifetime); +} + +bool llvm::InlineFunction(CallInst *CI, InlineFunctionInfo &IFI, AAResults *CalleeAAR, bool InsertLifetime) { - return InlineFunction(CallSite(II), IFI, CalleeAAR, InsertLifetime); + InlineReason Reason = NinlrNoReason; + return InlineFunction(CallSite(CI), IFI, &Reason, CalleeAAR, InsertLifetime); +} +bool llvm::InlineFunction(InvokeInst *II, InlineFunctionInfo &IFI, + AAResults *CalleeAAR, bool InsertLifetime) { + InlineReason Reason = NinlrNoReason; + return InlineFunction(CallSite(II), IFI, &Reason, CalleeAAR, InsertLifetime); } namespace { @@ -1329,7 +1343,8 @@ /// 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) { + InlineReason* Reason, + AAResults *CalleeAAR, bool InsertLifetime) { Instruction *TheCall = CS.getInstruction(); assert(TheCall->getParent() && TheCall->getParent()->getParent() && "Instruction not in function!"); @@ -1342,6 +1357,22 @@ CalledFunc->isDeclaration() || // call, or call to a vararg function! CalledFunc->getFunctionType()->isVarArg()) return false; + if (!CalledFunc) { // Can't inline external function or indirect + *Reason = NinlrIndirect; + return false; + } + + if (CalledFunc->isDeclaration()) { // Can't inline indirect call + *Reason = NinlrExtern; + return false; + } + + if (CalledFunc->getFunctionType()->isVarArg()) { + // Can't inline call to a vararg function! + *Reason = NinlrVarargs; + return false; + } + // The inliner does not know how to inline through calls with operand bundles // in general ... if (CS.hasOperandBundles()) { @@ -1353,7 +1384,7 @@ // ... and "funclet" operand bundles. if (Tag == LLVMContext::OB_funclet) continue; - + *Reason = NinlrOpBundles; return false; } } @@ -1372,8 +1403,10 @@ if (CalledFunc->hasGC()) { if (!Caller->hasGC()) Caller->setGC(CalledFunc->getGC()); - else if (CalledFunc->getGC() != Caller->getGC()) + else if (CalledFunc->getGC() != Caller->getGC()) { + *Reason = NinlrMismatchedGC; return false; + } } // Get the personality function from the callee if it contains a landing pad. @@ -1396,8 +1429,10 @@ // inlining. Otherwise, we can't inline. // 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) + else if (CalledPersonality != CallerPersonality) { + *Reason = NinlrMismatchedPersonality; return false; + } } // We need to figure out which funclet the callsite was in so that we may @@ -1421,16 +1456,20 @@ // Ok, the call site is within a cleanuppad. Let's check the callee // for catchpads. for (const BasicBlock &CalledBB : *CalledFunc) { - if (isa(CalledBB.getFirstNonPHI())) + if (isa(CalledBB.getFirstNonPHI())) { + *Reason = NinlrMSVCEH; return false; + } } } } else if (isAsynchronousEHPersonality(Personality)) { // SEH is even less tolerant, there may not be any sort of exceptional // funclet in the callee. for (const BasicBlock &CalledBB : *CalledFunc) { - if (CalledBB.isEHPad()) + if (CalledBB.isEHPad()) { + *Reason = NinlrSEH; return false; + } } } } @@ -1948,6 +1987,7 @@ Returns[0]->eraseFromParent(); // We are now done with the inlining. + *Reason = InlrNoReason; return true; } @@ -2102,6 +2142,13 @@ PHI->eraseFromParent(); } } - + *Reason = InlrNoReason; return true; } + +bool llvm::InlineFunction(CallSite CS, InlineFunctionInfo &IFI, + AAResults *CalleeAAR, bool InsertLifetime) { + InlineReason Reason; + return llvm::InlineFunction(CS, IFI, &Reason, CalleeAAR, InsertLifetime); +} +