Index: lib/Transforms/IPO/Inliner.cpp =================================================================== --- lib/Transforms/IPO/Inliner.cpp +++ lib/Transforms/IPO/Inliner.cpp @@ -113,6 +113,11 @@ "printing of statistics for each inlined function")), cl::Hidden, cl::desc("Enable inliner stats for imported functions")); +/// Flag to add inline messages as callsite attributes 'inline-remark'. +static cl::opt + InlineRemarkAttribute("inline-remark-attribute", cl::init(false), + cl::Hidden); + LegacyInlinerBase::LegacyInlinerBase(char &ID) : CallGraphSCCPass(ID) {} LegacyInlinerBase::LegacyInlinerBase(char &ID, bool InsertLifetime) @@ -388,13 +393,11 @@ return R; } -#ifndef NDEBUG static std::string inlineCostStr(const InlineCost &IC) { std::stringstream Remark; Remark << IC; return Remark.str(); } -#endif /// 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 @@ -500,6 +503,13 @@ }); } +static void setInlineRemark(CallSite &CS, StringRef message) { + if (InlineRemarkAttribute) { + Attribute attr = Attribute::get(CS->getContext(), "inline-remark", message); + CS.addAttribute(AttributeList::FunctionIndex, attr); + } +} + static bool inlineCallsImpl(CallGraphSCC &SCC, CallGraph &CG, std::function GetAssumptionCache, @@ -549,6 +559,7 @@ if (Callee->isDeclaration()) { using namespace ore; + setInlineRemark(CS, "unavailable definition"); ORE.emit([&]() { return OptimizationRemarkMissed(DEBUG_TYPE, "NoDefinition", &I) << NV("Callee", Callee) << " will not be inlined into " @@ -612,8 +623,10 @@ // infinitely inline. InlineHistoryID = CallSites[CSi].second; if (InlineHistoryID != -1 && - InlineHistoryIncludes(Callee, InlineHistoryID, InlineHistory)) + InlineHistoryIncludes(Callee, InlineHistoryID, InlineHistory)) { + setInlineRemark(CS, "recursive"); continue; + } } // FIXME for new PM: because of the old PM we currently generate ORE and @@ -624,7 +637,13 @@ Optional OIC = shouldInline(CS, GetInlineCost, ORE); // If the policy determines that we should inline this function, // delete the call instead. - if (!OIC || !*OIC) { + if (!OIC.hasValue()) { + setInlineRemark(CS, "deferred"); + continue; + } + + if (!OIC.getValue()) { + setInlineRemark(CS, inlineCostStr(*OIC)); // OIC explains continue; } @@ -635,6 +654,7 @@ if (IsTriviallyDead) { LLVM_DEBUG(dbgs() << " -> Deleting dead call: " << *Instr << "\n"); // Update the call graph by deleting the edge from Callee to Caller. + setInlineRemark(CS, "trivially dead"); CG[Caller]->removeCallEdgeFor(CS); Instr->eraseFromParent(); ++NumCallsDeleted; @@ -650,6 +670,7 @@ CS, InlineInfo, InlinedArrayAllocas, InlineHistoryID, InsertLifetime, AARGetter, ImportedFunctionsStats); if (!IR) { + setInlineRemark(CS, std::string(IR) + "; " + inlineCostStr(*OIC)); ORE.emit([&]() { return OptimizationRemarkMissed(DEBUG_TYPE, "NotInlined", DLoc, Block) @@ -892,6 +913,7 @@ Calls.push_back({CS, -1}); else if (!isa(I)) { using namespace ore; + setInlineRemark(CS, "unavailable definition"); ORE.emit([&]() { return OptimizationRemarkMissed(DEBUG_TYPE, "NoDefinition", &I) << NV("Callee", Callee) << " will not be inlined into " @@ -935,8 +957,10 @@ LazyCallGraph::Node &N = *CG.lookup(F); if (CG.lookupSCC(N) != C) continue; - if (F.hasFnAttribute(Attribute::OptimizeNone)) + if (F.hasFnAttribute(Attribute::OptimizeNone)) { + setInlineRemark(Calls[i].first, "optnone attribute"); continue; + } LLVM_DEBUG(dbgs() << "Inlining calls in: " << F.getName() << "\n"); @@ -980,8 +1004,10 @@ Function &Callee = *CS.getCalledFunction(); if (InlineHistoryID != -1 && - InlineHistoryIncludes(&Callee, InlineHistoryID, InlineHistory)) + InlineHistoryIncludes(&Callee, InlineHistoryID, InlineHistory)) { + setInlineRemark(CS, "recursive"); continue; + } // Check if this inlining may repeat breaking an SCC apart that has // already been split once before. In that case, inlining here may @@ -993,13 +1019,21 @@ LLVM_DEBUG(dbgs() << "Skipping inlining internal SCC edge from a node " "previously split out of this SCC by inlining: " << F.getName() << " -> " << Callee.getName() << "\n"); + setInlineRemark(CS, "recursive SCC split"); continue; } Optional OIC = shouldInline(CS, GetInlineCost, ORE); // Check whether we want to inline this callsite. - if (!OIC || !*OIC) + if (!OIC.hasValue()) { + setInlineRemark(CS, "deferred"); continue; + } + + if (!OIC.getValue()) { + setInlineRemark(CS, inlineCostStr(*OIC)); // OIC explains + continue; + } // Setup the data structure used to plumb customization into the // `InlineFunction` routine. @@ -1016,6 +1050,7 @@ InlineResult IR = InlineFunction(CS, IFI); if (!IR) { + setInlineRemark(CS, std::string(IR.message) + "; " + inlineCostStr(*OIC)); ORE.emit([&]() { return OptimizationRemarkMissed(DEBUG_TYPE, "NotInlined", DLoc, Block) << NV("Callee", &Callee) << " will not be inlined into " Index: test/Transforms/Inline/inline-remark.ll =================================================================== --- test/Transforms/Inline/inline-remark.ll +++ test/Transforms/Inline/inline-remark.ll @@ -0,0 +1,33 @@ +; RUN: opt < %s -inline -inline-remark-attribute --inline-threshold=-2 -S | FileCheck %s + +; Test that the inliner adds inline remark attributes to non-inlined callsites. + +define void @foo() { + call void @bar(i1 true) + ret void +} + +define void @bar(i1 %p) { + br i1 %p, label %bb1, label %bb2 + +bb1: + call void @foo() + ret void + +bb2: + call void @bar(i1 true) + ret void +} + +;; Test 1 - Correctly handle phis in outer landing pads. +define void @test1() { +; CHECK-LABEL: @test1 +; CHECK-NEXT: call void @bar(i1 true) [[ATTR1:#[0-9]+]] +; CHECK-NEXT: call void @bar(i1 false) [[ATTR2:#[0-9]+]] + call void @bar(i1 true) + call void @bar(i1 false) + ret void +} + +; CHECK: attributes [[ATTR1]] = { "inline-remark"="(cost=-5, threshold=-6)" } +; CHECK: attributes [[ATTR2]] = { "inline-remark"="(cost=never): recursive" }