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: 338969
This commit is contained in:
David Bolvansky 2018-08-05 14:53:08 +00:00
parent f59a8262a8
commit 7dcd99ba17
20 changed files with 244 additions and 162 deletions

View File

@ -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

View File

@ -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);

View File

@ -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.

View File

@ -227,7 +227,8 @@ class CallAnalyzer : public InstVisitor<CallAnalyzer, bool> {
BlockFrequencyInfo *CallerBFI);
// Custom analysis routines.
bool analyzeBlock(BasicBlock *BB, SmallPtrSetImpl<const Value *> &EphValues);
InlineResult analyzeBlock(BasicBlock *BB,
SmallPtrSetImpl<const Value *> &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<const Value *> &EphValues) {
InlineResult
CallAnalyzer::analyzeBlock(BasicBlock *BB,
SmallPtrSetImpl<const Value *> &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<PointerType>(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());
}

View File

@ -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);

View File

@ -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");
}

View File

@ -64,6 +64,7 @@
#include <algorithm>
#include <cassert>
#include <functional>
#include <sstream>
#include <tuple>
#include <utility>
#include <vector>
@ -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<char> &operator<<(std::basic_ostream<char> &R,
const ore::NV &Arg) {
return R << Arg.Val;
}
template <class RemarkT>
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<InlineCost(CallSite CS)> 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<InlineCost(CallSite CS)> 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<AssumptionCache &(Function &)> GetAssumptionCache,
@ -585,8 +622,9 @@ inlineCallsImpl(CallGraphSCC &SCC, CallGraph &CG,
Optional<InlineCost> 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<InlineCost> 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()) {

View File

@ -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<CatchSwitchInst>(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";
}
}
}

View File

@ -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"

View File

@ -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: ...

View File

@ -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: ...

View File

@ -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: ...

View File

@ -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: ...

View File

@ -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: ...

View File

@ -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)

View File

@ -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'

View File

@ -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: ...

View File

@ -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 {

View File

@ -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 {

View File

@ -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'