#include "llvm/Analysis/InlineCost.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/Analysis/AssumptionCache.h"
#include "llvm/Analysis/BlockFrequencyInfo.h"
#include "llvm/Analysis/CodeMetrics.h"
#include "llvm/Analysis/ConstantFolding.h"
#include "llvm/Analysis/InstructionSimplify.h"
#include "llvm/Analysis/LoopInfo.h"
#include "llvm/Analysis/OptimizationRemarkEmitter.h"
#include "llvm/Analysis/ProfileSummaryInfo.h"
#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/Analysis/TargetTransformInfo.h"
#include "llvm/Analysis/ValueTracking.h"
#include "llvm/Config/llvm-config.h"
#include "llvm/IR/AssemblyAnnotationWriter.h"
#include "llvm/IR/CallingConv.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/Dominators.h"
#include "llvm/IR/GetElementPtrTypeIterator.h"
#include "llvm/IR/GlobalAlias.h"
#include "llvm/IR/InstVisitor.h"
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/IR/Operator.h"
#include "llvm/IR/PatternMatch.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/FormattedStream.h"
#include "llvm/Support/raw_ostream.h"
#include <limits>
using namespace llvm;
#define DEBUG_TYPE "inline-cost"
STATISTIC(NumCallsAnalyzed, "Number of call sites analyzed");
static cl::opt<int>
DefaultThreshold("inlinedefault-threshold", cl::Hidden, cl::init(225),
cl::desc("Default amount of inlining to perform"));
static cl::opt<bool> IgnoreTTIInlineCompatible(
"ignore-tti-inline-compatible", cl::Hidden, cl::init(false),
cl::desc("Ignore TTI attributes compatibility check between callee/caller "
"during inline cost calculation"));
static cl::opt<bool> PrintInstructionComments(
"print-instruction-comments", cl::Hidden, cl::init(false),
cl::desc("Prints comments for instruction based on inline cost analysis"));
static cl::opt<int> InlineThreshold(
"inline-threshold", cl::Hidden, cl::init(225),
cl::desc("Control the amount of inlining to perform (default = 225)"));
static cl::opt<int> HintThreshold(
"inlinehint-threshold", cl::Hidden, cl::init(325),
cl::desc("Threshold for inlining functions with inline hint"));
static cl::opt<int>
ColdCallSiteThreshold("inline-cold-callsite-threshold", cl::Hidden,
cl::init(45),
cl::desc("Threshold for inlining cold callsites"));
static cl::opt<bool> InlineEnableCostBenefitAnalysis(
"inline-enable-cost-benefit-analysis", cl::Hidden, cl::init(false),
cl::desc("Enable the cost-benefit analysis for the inliner"));
static cl::opt<int> InlineSavingsMultiplier(
"inline-savings-multiplier", cl::Hidden, cl::init(8),
cl::desc("Multiplier to multiply cycle savings by during inlining"));
static cl::opt<int>
InlineSizeAllowance("inline-size-allowance", cl::Hidden, cl::init(100),
cl::desc("The maximum size of a callee that get's "
"inlined without sufficient cycle savings"));
static cl::opt<int> ColdThreshold(
"inlinecold-threshold", cl::Hidden, cl::init(45),
cl::desc("Threshold for inlining functions with cold attribute"));
static cl::opt<int>
HotCallSiteThreshold("hot-callsite-threshold", cl::Hidden, cl::init(3000),
cl::desc("Threshold for hot callsites "));
static cl::opt<int> LocallyHotCallSiteThreshold(
"locally-hot-callsite-threshold", cl::Hidden, cl::init(525),
cl::desc("Threshold for locally hot callsites "));
static cl::opt<int> ColdCallSiteRelFreq(
"cold-callsite-rel-freq", cl::Hidden, cl::init(2),
cl::desc("Maximum block frequency, expressed as a percentage of caller's "
"entry frequency, for a callsite to be cold in the absence of "
"profile information."));
static cl::opt<int> HotCallSiteRelFreq(
"hot-callsite-rel-freq", cl::Hidden, cl::init(60),
cl::desc("Minimum block frequency, expressed as a multiple of caller's "
"entry frequency, for a callsite to be hot in the absence of "
"profile information."));
static cl::opt<int> CallPenalty(
"inline-call-penalty", cl::Hidden, cl::init(25),
cl::desc("Call penalty that is applied per callsite when inlining"));
static cl::opt<size_t>
StackSizeThreshold("inline-max-stacksize", cl::Hidden,
cl::init(std::numeric_limits<size_t>::max()),
cl::desc("Do not inline functions with a stack size "
"that exceeds the specified limit"));
static cl::opt<size_t>
RecurStackSizeThreshold("recursive-inline-max-stacksize", cl::Hidden,
cl::init(InlineConstants::TotalAllocaSizeRecursiveCaller),
cl::desc("Do not inline recursive functions with a stack "
"size that exceeds the specified limit"));
static cl::opt<bool> OptComputeFullInlineCost(
"inline-cost-full", cl::Hidden,
cl::desc("Compute the full inline cost of a call site even when the cost "
"exceeds the threshold."));
static cl::opt<bool> InlineCallerSupersetNoBuiltin(
"inline-caller-superset-nobuiltin", cl::Hidden, cl::init(true),
cl::desc("Allow inlining when caller has a superset of callee's nobuiltin "
"attributes."));
static cl::opt<bool> DisableGEPConstOperand(
"disable-gep-const-evaluation", cl::Hidden, cl::init(false),
cl::desc("Disables evaluation of GetElementPtr with constant operands"));
namespace llvm {
Optional<int> getStringFnAttrAsInt(CallBase &CB, StringRef AttrKind) {
Attribute Attr = CB.getFnAttr(AttrKind);
int AttrValue;
if (Attr.getValueAsString().getAsInteger(10, AttrValue))
return None;
return AttrValue;
}
}
namespace {
class InlineCostCallAnalyzer;
struct InstructionCostDetail {
int CostBefore = 0;
int CostAfter = 0;
int ThresholdBefore = 0;
int ThresholdAfter = 0;
int getThresholdDelta() const { return ThresholdAfter - ThresholdBefore; }
int getCostDelta() const { return CostAfter - CostBefore; }
bool hasThresholdChanged() const { return ThresholdAfter != ThresholdBefore; }
};
class InlineCostAnnotationWriter : public AssemblyAnnotationWriter {
private:
InlineCostCallAnalyzer *const ICCA;
public:
InlineCostAnnotationWriter(InlineCostCallAnalyzer *ICCA) : ICCA(ICCA) {}
void emitInstructionAnnot(const Instruction *I,
formatted_raw_ostream &OS) override;
};
class CallAnalyzer : public InstVisitor<CallAnalyzer, bool> {
typedef InstVisitor<CallAnalyzer, bool> Base;
friend class InstVisitor<CallAnalyzer, bool>;
protected:
virtual ~CallAnalyzer() = default;
const TargetTransformInfo &TTI;
function_ref<AssumptionCache &(Function &)> GetAssumptionCache;
function_ref<BlockFrequencyInfo &(Function &)> GetBFI;
ProfileSummaryInfo *PSI;
Function &F;
const DataLayout &DL;
OptimizationRemarkEmitter *ORE;
CallBase &CandidateCall;
virtual void onBlockStart(const BasicBlock *BB) {}
virtual void onBlockAnalyzed(const BasicBlock *BB) {}
virtual void onInstructionAnalysisStart(const Instruction *I) {}
virtual void onInstructionAnalysisFinish(const Instruction *I) {}
virtual InlineResult finalizeAnalysis() { return InlineResult::success(); }
virtual bool shouldStop() { return false; }
virtual InlineResult onAnalysisStart() { return InlineResult::success(); }
virtual void onDisableSROA(AllocaInst *Arg) {}
virtual void onDisableLoadElimination() {}
virtual bool onCallBaseVisitStart(CallBase &Call) { return true; }
virtual void onCallPenalty() {}
virtual void onLoadEliminationOpportunity() {}
virtual void onCallArgumentSetup(const CallBase &Call) {}
virtual void onLoadRelativeIntrinsic() {}
virtual void onLoweredCall(Function *F, CallBase &Call, bool IsIndirectCall) {
}
virtual bool onJumpTable(unsigned JumpTableSize) { return true; }
virtual bool onCaseCluster(unsigned NumCaseCluster) { return true; }
virtual void onFinalizeSwitch(unsigned JumpTableSize,
unsigned NumCaseCluster) {}
virtual void onMissedSimplification() {}
virtual void onInitializeSROAArg(AllocaInst *Arg) {}
virtual void onAggregateSROAUse(AllocaInst *V) {}
bool handleSROA(Value *V, bool DoNotDisable) {
if (auto *SROAArg = getSROAArgForValueOrNull(V)) {
if (DoNotDisable) {
onAggregateSROAUse(SROAArg);
return true;
}
disableSROAForArg(SROAArg);
}
return false;
}
bool IsCallerRecursive = false;
bool IsRecursiveCall = false;
bool ExposesReturnsTwice = false;
bool HasDynamicAlloca = false;
bool ContainsNoDuplicateCall = false;
bool HasReturn = false;
bool HasIndirectBr = false;
bool HasUninlineableIntrinsic = false;
bool InitsVargArgs = false;
uint64_t AllocatedSize = 0;
unsigned NumInstructions = 0;
unsigned NumVectorInstructions = 0;
DenseMap<Value *, Constant *> SimplifiedValues;
DenseMap<Value *, AllocaInst *> SROAArgValues;
DenseSet<AllocaInst *> EnabledSROAAllocas;
DenseMap<Value *, std::pair<Value *, APInt>> ConstantOffsetPtrs;
SmallPtrSet<BasicBlock *, 16> DeadBlocks;
DenseMap<BasicBlock *, BasicBlock *> KnownSuccessors;
bool EnableLoadElimination = true;
bool AllowRecursiveCall = false;
SmallPtrSet<Value *, 16> LoadAddrSet;
AllocaInst *getSROAArgForValueOrNull(Value *V) const {
auto It = SROAArgValues.find(V);
if (It == SROAArgValues.end() || EnabledSROAAllocas.count(It->second) == 0)
return nullptr;
return It->second;
}
bool isAllocaDerivedArg(Value *V);
void disableSROAForArg(AllocaInst *SROAArg);
void disableSROA(Value *V);
void findDeadBlocks(BasicBlock *CurrBB, BasicBlock *NextBB);
void disableLoadElimination();
bool isGEPFree(GetElementPtrInst &GEP);
bool canFoldInboundsGEP(GetElementPtrInst &I);
bool accumulateGEPOffset(GEPOperator &GEP, APInt &Offset);
bool simplifyCallSite(Function *F, CallBase &Call);
bool simplifyInstruction(Instruction &I);
bool simplifyIntrinsicCallIsConstant(CallBase &CB);
ConstantInt *stripAndComputeInBoundsConstantOffsets(Value *&V);
bool paramHasAttr(Argument *A, Attribute::AttrKind Attr);
bool isKnownNonNullInCallee(Value *V);
bool allowSizeGrowth(CallBase &Call);
InlineResult analyzeBlock(BasicBlock *BB,
SmallPtrSetImpl<const Value *> &EphValues);
void visit(Module *);
void visit(Module &);
void visit(Function *);
void visit(Function &);
void visit(BasicBlock *);
void visit(BasicBlock &);
bool visitInstruction(Instruction &I);
bool visitAlloca(AllocaInst &I);
bool visitPHI(PHINode &I);
bool visitGetElementPtr(GetElementPtrInst &I);
bool visitBitCast(BitCastInst &I);
bool visitPtrToInt(PtrToIntInst &I);
bool visitIntToPtr(IntToPtrInst &I);
bool visitCastInst(CastInst &I);
bool visitCmpInst(CmpInst &I);
bool visitSub(BinaryOperator &I);
bool visitBinaryOperator(BinaryOperator &I);
bool visitFNeg(UnaryOperator &I);
bool visitLoad(LoadInst &I);
bool visitStore(StoreInst &I);
bool visitExtractValue(ExtractValueInst &I);
bool visitInsertValue(InsertValueInst &I);
bool visitCallBase(CallBase &Call);
bool visitReturnInst(ReturnInst &RI);
bool visitBranchInst(BranchInst &BI);
bool visitSelectInst(SelectInst &SI);
bool visitSwitchInst(SwitchInst &SI);
bool visitIndirectBrInst(IndirectBrInst &IBI);
bool visitResumeInst(ResumeInst &RI);
bool visitCleanupReturnInst(CleanupReturnInst &RI);
bool visitCatchReturnInst(CatchReturnInst &RI);
bool visitUnreachableInst(UnreachableInst &I);
public:
CallAnalyzer(Function &Callee, CallBase &Call, const TargetTransformInfo &TTI,
function_ref<AssumptionCache &(Function &)> GetAssumptionCache,
function_ref<BlockFrequencyInfo &(Function &)> GetBFI = nullptr,
ProfileSummaryInfo *PSI = nullptr,
OptimizationRemarkEmitter *ORE = nullptr)
: TTI(TTI), GetAssumptionCache(GetAssumptionCache), GetBFI(GetBFI),
PSI(PSI), F(Callee), DL(F.getParent()->getDataLayout()), ORE(ORE),
CandidateCall(Call) {}
InlineResult analyze();
Optional<Constant *> getSimplifiedValue(Instruction *I) {
if (SimplifiedValues.find(I) != SimplifiedValues.end())
return SimplifiedValues[I];
return None;
}
unsigned NumConstantArgs = 0;
unsigned NumConstantOffsetPtrArgs = 0;
unsigned NumAllocaArgs = 0;
unsigned NumConstantPtrCmps = 0;
unsigned NumConstantPtrDiffs = 0;
unsigned NumInstructionsSimplified = 0;
void dump();
};
int64_t getExpectedNumberOfCompare(int NumCaseCluster) {
return 3 * static_cast<int64_t>(NumCaseCluster) / 2 - 1;
}
class InlineCostCallAnalyzer final : public CallAnalyzer {
const int CostUpperBound = INT_MAX - InlineConstants::InstrCost - 1;
const bool ComputeFullInlineCost;
int LoadEliminationCost = 0;
int VectorBonus = 0;
int SingleBBBonus = 0;
const InlineParams &Params;
DenseMap<const Instruction *, InstructionCostDetail> InstructionCostDetailMap;
int Threshold = 0;
const bool BoostIndirectCalls;
const bool IgnoreThreshold;
const bool CostBenefitAnalysisEnabled;
int Cost = 0;
int CostAtBBStart = 0;
int ColdSize = 0;
bool DecidedByCostThreshold = false;
bool DecidedByCostBenefit = false;
Optional<CostBenefitPair> CostBenefit = None;
bool SingleBB = true;
unsigned SROACostSavings = 0;
unsigned SROACostSavingsLost = 0;
DenseMap<AllocaInst *, int> SROAArgCosts;
bool isColdCallSite(CallBase &Call, BlockFrequencyInfo *CallerBFI);
void updateThreshold(CallBase &Call, Function &Callee);
Optional<int> getHotCallSiteThreshold(CallBase &Call,
BlockFrequencyInfo *CallerBFI);
void addCost(int64_t Inc, int64_t UpperBound = INT_MAX) {
assert(UpperBound > 0 && UpperBound <= INT_MAX && "invalid upper bound");
Cost = std::min<int>(UpperBound, Cost + Inc);
}
void onDisableSROA(AllocaInst *Arg) override {
auto CostIt = SROAArgCosts.find(Arg);
if (CostIt == SROAArgCosts.end())
return;
addCost(CostIt->second);
SROACostSavings -= CostIt->second;
SROACostSavingsLost += CostIt->second;
SROAArgCosts.erase(CostIt);
}
void onDisableLoadElimination() override {
addCost(LoadEliminationCost);
LoadEliminationCost = 0;
}
bool onCallBaseVisitStart(CallBase &Call) override {
if (Optional<int> AttrCallThresholdBonus =
getStringFnAttrAsInt(Call, "call-threshold-bonus"))
Threshold += *AttrCallThresholdBonus;
if (Optional<int> AttrCallCost =
getStringFnAttrAsInt(Call, "call-inline-cost")) {
addCost(*AttrCallCost);
return false;
}
return true;
}
void onCallPenalty() override { addCost(CallPenalty); }
void onCallArgumentSetup(const CallBase &Call) override {
addCost(Call.arg_size() * InlineConstants::InstrCost);
}
void onLoadRelativeIntrinsic() override {
addCost(3 * InlineConstants::InstrCost);
}
void onLoweredCall(Function *F, CallBase &Call,
bool IsIndirectCall) override {
addCost(Call.arg_size() * InlineConstants::InstrCost);
if (IsIndirectCall && BoostIndirectCalls) {
auto IndirectCallParams = Params;
IndirectCallParams.DefaultThreshold =
InlineConstants::IndirectCallThreshold;
InlineCostCallAnalyzer CA(*F, Call, IndirectCallParams, TTI,
GetAssumptionCache, GetBFI, PSI, ORE, false);
if (CA.analyze().isSuccess()) {
Cost -= std::max(0, CA.getThreshold() - CA.getCost());
}
} else
addCost(CallPenalty);
}
void onFinalizeSwitch(unsigned JumpTableSize,
unsigned NumCaseCluster) override {
if (JumpTableSize) {
int64_t JTCost =
static_cast<int64_t>(JumpTableSize) * InlineConstants::InstrCost +
4 * InlineConstants::InstrCost;
addCost(JTCost, static_cast<int64_t>(CostUpperBound));
return;
}
if (NumCaseCluster <= 3) {
addCost(NumCaseCluster * 2 * InlineConstants::InstrCost);
return;
}
int64_t ExpectedNumberOfCompare =
getExpectedNumberOfCompare(NumCaseCluster);
int64_t SwitchCost =
ExpectedNumberOfCompare * 2 * InlineConstants::InstrCost;
addCost(SwitchCost, static_cast<int64_t>(CostUpperBound));
}
void onMissedSimplification() override {
addCost(InlineConstants::InstrCost);
}
void onInitializeSROAArg(AllocaInst *Arg) override {
assert(Arg != nullptr &&
"Should not initialize SROA costs for null value.");
SROAArgCosts[Arg] = 0;
}
void onAggregateSROAUse(AllocaInst *SROAArg) override {
auto CostIt = SROAArgCosts.find(SROAArg);
assert(CostIt != SROAArgCosts.end() &&
"expected this argument to have a cost");
CostIt->second += InlineConstants::InstrCost;
SROACostSavings += InlineConstants::InstrCost;
}
void onBlockStart(const BasicBlock *BB) override { CostAtBBStart = Cost; }
void onBlockAnalyzed(const BasicBlock *BB) override {
if (CostBenefitAnalysisEnabled) {
assert(GetBFI && "GetBFI must be available");
BlockFrequencyInfo *BFI = &(GetBFI(F));
assert(BFI && "BFI must be available");
auto ProfileCount = BFI->getBlockProfileCount(BB);
assert(ProfileCount);
if (ProfileCount.value() == 0)
ColdSize += Cost - CostAtBBStart;
}
auto *TI = BB->getTerminator();
if (SingleBB && TI->getNumSuccessors() > 1) {
Threshold -= SingleBBBonus;
SingleBB = false;
}
}
void onInstructionAnalysisStart(const Instruction *I) override {
if (!PrintInstructionComments)
return;
InstructionCostDetailMap[I].CostBefore = Cost;
InstructionCostDetailMap[I].ThresholdBefore = Threshold;
}
void onInstructionAnalysisFinish(const Instruction *I) override {
if (!PrintInstructionComments)
return;
InstructionCostDetailMap[I].CostAfter = Cost;
InstructionCostDetailMap[I].ThresholdAfter = Threshold;
}
bool isCostBenefitAnalysisEnabled() {
if (!PSI || !PSI->hasProfileSummary())
return false;
if (!GetBFI)
return false;
if (InlineEnableCostBenefitAnalysis.getNumOccurrences()) {
if (!InlineEnableCostBenefitAnalysis)
return false;
} else {
if (!PSI->hasInstrumentationProfile())
return false;
}
auto *Caller = CandidateCall.getParent()->getParent();
if (!Caller->getEntryCount())
return false;
BlockFrequencyInfo *CallerBFI = &(GetBFI(*Caller));
if (!CallerBFI)
return false;
if (!PSI->isHotCallSite(CandidateCall, CallerBFI))
return false;
auto EntryCount = F.getEntryCount();
if (!EntryCount || !EntryCount->getCount())
return false;
BlockFrequencyInfo *CalleeBFI = &(GetBFI(F));
if (!CalleeBFI)
return false;
return true;
}
Optional<bool> costBenefitAnalysis() {
if (!CostBenefitAnalysisEnabled)
return None;
if (Threshold == 0)
return None;
assert(GetBFI);
BlockFrequencyInfo *CalleeBFI = &(GetBFI(F));
assert(CalleeBFI);
APInt CycleSavings(128, 0);
for (auto &BB : F) {
APInt CurrentSavings(128, 0);
for (auto &I : BB) {
if (BranchInst *BI = dyn_cast<BranchInst>(&I)) {
if (BI->isConditional() &&
isa_and_nonnull<ConstantInt>(
SimplifiedValues.lookup(BI->getCondition()))) {
CurrentSavings += InlineConstants::InstrCost;
}
} else if (Value *V = dyn_cast<Value>(&I)) {
if (SimplifiedValues.count(V)) {
CurrentSavings += InlineConstants::InstrCost;
}
}
}
auto ProfileCount = CalleeBFI->getBlockProfileCount(&BB);
assert(ProfileCount);
CurrentSavings *= ProfileCount.value();
CycleSavings += CurrentSavings;
}
auto EntryProfileCount = F.getEntryCount();
assert(EntryProfileCount && EntryProfileCount->getCount());
auto EntryCount = EntryProfileCount->getCount();
CycleSavings += EntryCount / 2;
CycleSavings = CycleSavings.udiv(EntryCount);
auto *CallerBB = CandidateCall.getParent();
BlockFrequencyInfo *CallerBFI = &(GetBFI(*(CallerBB->getParent())));
CycleSavings += getCallsiteCost(this->CandidateCall, DL);
CycleSavings *= *CallerBFI->getBlockProfileCount(CallerBB);
int Size = Cost - ColdSize;
Size = Size > InlineSizeAllowance ? Size - InlineSizeAllowance : 1;
CostBenefit.emplace(APInt(128, Size), CycleSavings);
APInt LHS = CycleSavings;
LHS *= InlineSavingsMultiplier;
APInt RHS(128, PSI->getOrCompHotCountThreshold());
RHS *= Size;
return LHS.uge(RHS);
}
InlineResult finalizeAnalysis() override {
auto *Caller = CandidateCall.getFunction();
if (Caller->hasMinSize()) {
DominatorTree DT(F);
LoopInfo LI(DT);
int NumLoops = 0;
for (Loop *L : LI) {
if (DeadBlocks.count(L->getHeader()))
continue;
NumLoops++;
}
addCost(NumLoops * InlineConstants::LoopPenalty);
}
if (NumVectorInstructions <= NumInstructions / 10)
Threshold -= VectorBonus;
else if (NumVectorInstructions <= NumInstructions / 2)
Threshold -= VectorBonus / 2;
if (Optional<int> AttrCost =
getStringFnAttrAsInt(CandidateCall, "function-inline-cost"))
Cost = *AttrCost;
if (Optional<int> AttrCostMult = getStringFnAttrAsInt(
CandidateCall,
InlineConstants::FunctionInlineCostMultiplierAttributeName))
Cost *= *AttrCostMult;
if (Optional<int> AttrThreshold =
getStringFnAttrAsInt(CandidateCall, "function-inline-threshold"))
Threshold = *AttrThreshold;
if (auto Result = costBenefitAnalysis()) {
DecidedByCostBenefit = true;
if (*Result)
return InlineResult::success();
else
return InlineResult::failure("Cost over threshold.");
}
if (IgnoreThreshold)
return InlineResult::success();
DecidedByCostThreshold = true;
return Cost < std::max(1, Threshold)
? InlineResult::success()
: InlineResult::failure("Cost over threshold.");
}
bool shouldStop() override {
if (IgnoreThreshold || ComputeFullInlineCost)
return false;
if (Cost < Threshold)
return false;
DecidedByCostThreshold = true;
return true;
}
void onLoadEliminationOpportunity() override {
LoadEliminationCost += InlineConstants::InstrCost;
}
InlineResult onAnalysisStart() override {
assert(NumInstructions == 0);
assert(NumVectorInstructions == 0);
updateThreshold(CandidateCall, F);
assert(Threshold >= 0);
assert(SingleBBBonus >= 0);
assert(VectorBonus >= 0);
Threshold += (SingleBBBonus + VectorBonus);
addCost(-getCallsiteCost(this->CandidateCall, DL));
if (F.getCallingConv() == CallingConv::Cold)
Cost += InlineConstants::ColdccPenalty;
LLVM_DEBUG(dbgs() << " Initial cost: " << Cost << "\n");
if (Cost >= Threshold && !ComputeFullInlineCost)
return InlineResult::failure("high cost");
return InlineResult::success();
}
public:
InlineCostCallAnalyzer(
Function &Callee, CallBase &Call, const InlineParams &Params,
const TargetTransformInfo &TTI,
function_ref<AssumptionCache &(Function &)> GetAssumptionCache,
function_ref<BlockFrequencyInfo &(Function &)> GetBFI = nullptr,
ProfileSummaryInfo *PSI = nullptr,
OptimizationRemarkEmitter *ORE = nullptr, bool BoostIndirect = true,
bool IgnoreThreshold = false)
: CallAnalyzer(Callee, Call, TTI, GetAssumptionCache, GetBFI, PSI, ORE),
ComputeFullInlineCost(OptComputeFullInlineCost ||
Params.ComputeFullInlineCost || ORE ||
isCostBenefitAnalysisEnabled()),
Params(Params), Threshold(Params.DefaultThreshold),
BoostIndirectCalls(BoostIndirect), IgnoreThreshold(IgnoreThreshold),
CostBenefitAnalysisEnabled(isCostBenefitAnalysisEnabled()),
Writer(this) {
AllowRecursiveCall = *Params.AllowRecursiveCall;
}
InlineCostAnnotationWriter Writer;
void dump();
void print(raw_ostream &OS);
Optional<InstructionCostDetail> getCostDetails(const Instruction *I) {
if (InstructionCostDetailMap.find(I) != InstructionCostDetailMap.end())
return InstructionCostDetailMap[I];
return None;
}
virtual ~InlineCostCallAnalyzer() = default;
int getThreshold() const { return Threshold; }
int getCost() const { return Cost; }
Optional<CostBenefitPair> getCostBenefitPair() { return CostBenefit; }
bool wasDecidedByCostBenefit() const { return DecidedByCostBenefit; }
bool wasDecidedByCostThreshold() const { return DecidedByCostThreshold; }
};
class InlineCostFeaturesAnalyzer final : public CallAnalyzer {
private:
InlineCostFeatures Cost = {};
static constexpr int JTCostMultiplier = 4;
static constexpr int CaseClusterCostMultiplier = 2;
static constexpr int SwitchCostMultiplier = 2;
unsigned SROACostSavingOpportunities = 0;
int VectorBonus = 0;
int SingleBBBonus = 0;
int Threshold = 5;
DenseMap<AllocaInst *, unsigned> SROACosts;
void increment(InlineCostFeatureIndex Feature, int64_t Delta = 1) {
Cost[static_cast<size_t>(Feature)] += Delta;
}
void set(InlineCostFeatureIndex Feature, int64_t Value) {
Cost[static_cast<size_t>(Feature)] = Value;
}
void onDisableSROA(AllocaInst *Arg) override {
auto CostIt = SROACosts.find(Arg);
if (CostIt == SROACosts.end())
return;
increment(InlineCostFeatureIndex::SROALosses, CostIt->second);
SROACostSavingOpportunities -= CostIt->second;
SROACosts.erase(CostIt);
}
void onDisableLoadElimination() override {
set(InlineCostFeatureIndex::LoadElimination, 1);
}
void onCallPenalty() override {
increment(InlineCostFeatureIndex::CallPenalty, CallPenalty);
}
void onCallArgumentSetup(const CallBase &Call) override {
increment(InlineCostFeatureIndex::CallArgumentSetup,
Call.arg_size() * InlineConstants::InstrCost);
}
void onLoadRelativeIntrinsic() override {
increment(InlineCostFeatureIndex::LoadRelativeIntrinsic,
3 * InlineConstants::InstrCost);
}
void onLoweredCall(Function *F, CallBase &Call,
bool IsIndirectCall) override {
increment(InlineCostFeatureIndex::LoweredCallArgSetup,
Call.arg_size() * InlineConstants::InstrCost);
if (IsIndirectCall) {
InlineParams IndirectCallParams = { 0,
{},
{},
{},
{},
{},
{},
{},
true,
true};
IndirectCallParams.DefaultThreshold =
InlineConstants::IndirectCallThreshold;
InlineCostCallAnalyzer CA(*F, Call, IndirectCallParams, TTI,
GetAssumptionCache, GetBFI, PSI, ORE, false,
true);
if (CA.analyze().isSuccess()) {
increment(InlineCostFeatureIndex::NestedInlineCostEstimate,
CA.getCost());
increment(InlineCostFeatureIndex::NestedInlines, 1);
}
} else {
onCallPenalty();
}
}
void onFinalizeSwitch(unsigned JumpTableSize,
unsigned NumCaseCluster) override {
if (JumpTableSize) {
int64_t JTCost =
static_cast<int64_t>(JumpTableSize) * InlineConstants::InstrCost +
JTCostMultiplier * InlineConstants::InstrCost;
increment(InlineCostFeatureIndex::JumpTablePenalty, JTCost);
return;
}
if (NumCaseCluster <= 3) {
increment(InlineCostFeatureIndex::CaseClusterPenalty,
NumCaseCluster * CaseClusterCostMultiplier *
InlineConstants::InstrCost);
return;
}
int64_t ExpectedNumberOfCompare =
getExpectedNumberOfCompare(NumCaseCluster);
int64_t SwitchCost = ExpectedNumberOfCompare * SwitchCostMultiplier *
InlineConstants::InstrCost;
increment(InlineCostFeatureIndex::SwitchPenalty, SwitchCost);
}
void onMissedSimplification() override {
increment(InlineCostFeatureIndex::UnsimplifiedCommonInstructions,
InlineConstants::InstrCost);
}
void onInitializeSROAArg(AllocaInst *Arg) override { SROACosts[Arg] = 0; }
void onAggregateSROAUse(AllocaInst *Arg) override {
SROACosts.find(Arg)->second += InlineConstants::InstrCost;
SROACostSavingOpportunities += InlineConstants::InstrCost;
}
void onBlockAnalyzed(const BasicBlock *BB) override {
if (BB->getTerminator()->getNumSuccessors() > 1)
set(InlineCostFeatureIndex::IsMultipleBlocks, 1);
Threshold -= SingleBBBonus;
}
InlineResult finalizeAnalysis() override {
auto *Caller = CandidateCall.getFunction();
if (Caller->hasMinSize()) {
DominatorTree DT(F);
LoopInfo LI(DT);
for (Loop *L : LI) {
if (DeadBlocks.count(L->getHeader()))
continue;
increment(InlineCostFeatureIndex::NumLoops,
InlineConstants::LoopPenalty);
}
}
set(InlineCostFeatureIndex::DeadBlocks, DeadBlocks.size());
set(InlineCostFeatureIndex::SimplifiedInstructions,
NumInstructionsSimplified);
set(InlineCostFeatureIndex::ConstantArgs, NumConstantArgs);
set(InlineCostFeatureIndex::ConstantOffsetPtrArgs,
NumConstantOffsetPtrArgs);
set(InlineCostFeatureIndex::SROASavings, SROACostSavingOpportunities);
if (NumVectorInstructions <= NumInstructions / 10)
Threshold -= VectorBonus;
else if (NumVectorInstructions <= NumInstructions / 2)
Threshold -= VectorBonus / 2;
set(InlineCostFeatureIndex::Threshold, Threshold);
return InlineResult::success();
}
bool shouldStop() override { return false; }
void onLoadEliminationOpportunity() override {
increment(InlineCostFeatureIndex::LoadElimination, 1);
}
InlineResult onAnalysisStart() override {
increment(InlineCostFeatureIndex::CallSiteCost,
-1 * getCallsiteCost(this->CandidateCall, DL));
set(InlineCostFeatureIndex::ColdCcPenalty,
(F.getCallingConv() == CallingConv::Cold));
set(InlineCostFeatureIndex::LastCallToStaticBonus,
(F.hasLocalLinkage() && F.hasOneLiveUse() &&
&F == CandidateCall.getCalledFunction()));
int SingleBBBonusPercent = 50;
int VectorBonusPercent = TTI.getInlinerVectorBonusPercent();
Threshold += TTI.adjustInliningThreshold(&CandidateCall);
Threshold *= TTI.getInliningThresholdMultiplier();
SingleBBBonus = Threshold * SingleBBBonusPercent / 100;
VectorBonus = Threshold * VectorBonusPercent / 100;
Threshold += (SingleBBBonus + VectorBonus);
return InlineResult::success();
}
public:
InlineCostFeaturesAnalyzer(
const TargetTransformInfo &TTI,
function_ref<AssumptionCache &(Function &)> &GetAssumptionCache,
function_ref<BlockFrequencyInfo &(Function &)> GetBFI,
ProfileSummaryInfo *PSI, OptimizationRemarkEmitter *ORE, Function &Callee,
CallBase &Call)
: CallAnalyzer(Callee, Call, TTI, GetAssumptionCache, GetBFI, PSI) {}
const InlineCostFeatures &features() const { return Cost; }
};
}
bool CallAnalyzer::isAllocaDerivedArg(Value *V) {
return SROAArgValues.count(V);
}
void CallAnalyzer::disableSROAForArg(AllocaInst *SROAArg) {
onDisableSROA(SROAArg);
EnabledSROAAllocas.erase(SROAArg);
disableLoadElimination();
}
void InlineCostAnnotationWriter::emitInstructionAnnot(
const Instruction *I, formatted_raw_ostream &OS) {
Optional<InstructionCostDetail> Record = ICCA->getCostDetails(I);
if (!Record)
OS << "; No analysis for the instruction";
else {
OS << "; cost before = " << Record->CostBefore
<< ", cost after = " << Record->CostAfter
<< ", threshold before = " << Record->ThresholdBefore
<< ", threshold after = " << Record->ThresholdAfter << ", ";
OS << "cost delta = " << Record->getCostDelta();
if (Record->hasThresholdChanged())
OS << ", threshold delta = " << Record->getThresholdDelta();
}
auto C = ICCA->getSimplifiedValue(const_cast<Instruction *>(I));
if (C) {
OS << ", simplified to ";
(*C)->print(OS, true);
}
OS << "\n";
}
void CallAnalyzer::disableSROA(Value *V) {
if (auto *SROAArg = getSROAArgForValueOrNull(V)) {
disableSROAForArg(SROAArg);
}
}
void CallAnalyzer::disableLoadElimination() {
if (EnableLoadElimination) {
onDisableLoadElimination();
EnableLoadElimination = false;
}
}
bool CallAnalyzer::accumulateGEPOffset(GEPOperator &GEP, APInt &Offset) {
unsigned IntPtrWidth = DL.getIndexTypeSizeInBits(GEP.getType());
assert(IntPtrWidth == Offset.getBitWidth());
for (gep_type_iterator GTI = gep_type_begin(GEP), GTE = gep_type_end(GEP);
GTI != GTE; ++GTI) {
ConstantInt *OpC = dyn_cast<ConstantInt>(GTI.getOperand());
if (!OpC)
if (Constant *SimpleOp = SimplifiedValues.lookup(GTI.getOperand()))
OpC = dyn_cast<ConstantInt>(SimpleOp);
if (!OpC)
return false;
if (OpC->isZero())
continue;
if (StructType *STy = GTI.getStructTypeOrNull()) {
unsigned ElementIdx = OpC->getZExtValue();
const StructLayout *SL = DL.getStructLayout(STy);
Offset += APInt(IntPtrWidth, SL->getElementOffset(ElementIdx));
continue;
}
APInt TypeSize(IntPtrWidth, DL.getTypeAllocSize(GTI.getIndexedType()));
Offset += OpC->getValue().sextOrTrunc(IntPtrWidth) * TypeSize;
}
return true;
}
bool CallAnalyzer::isGEPFree(GetElementPtrInst &GEP) {
SmallVector<Value *, 4> Operands;
Operands.push_back(GEP.getOperand(0));
for (const Use &Op : GEP.indices())
if (Constant *SimpleOp = SimplifiedValues.lookup(Op))
Operands.push_back(SimpleOp);
else
Operands.push_back(Op);
return TTI.getUserCost(&GEP, Operands,
TargetTransformInfo::TCK_SizeAndLatency) ==
TargetTransformInfo::TCC_Free;
}
bool CallAnalyzer::visitAlloca(AllocaInst &I) {
disableSROA(I.getOperand(0));
if (I.isArrayAllocation()) {
Constant *Size = SimplifiedValues.lookup(I.getArraySize());
if (auto *AllocSize = dyn_cast_or_null<ConstantInt>(Size)) {
Type *Ty = I.getAllocatedType();
AllocatedSize = SaturatingMultiplyAdd(
AllocSize->getLimitedValue(),
DL.getTypeAllocSize(Ty).getKnownMinSize(), AllocatedSize);
if (AllocatedSize > InlineConstants::MaxSimplifiedDynamicAllocaToInline)
HasDynamicAlloca = true;
return false;
}
}
if (I.isStaticAlloca()) {
Type *Ty = I.getAllocatedType();
AllocatedSize =
SaturatingAdd(DL.getTypeAllocSize(Ty).getKnownMinSize(), AllocatedSize);
}
if (!I.isStaticAlloca())
HasDynamicAlloca = true;
return false;
}
bool CallAnalyzer::visitPHI(PHINode &I) {
APInt ZeroOffset = APInt::getZero(DL.getPointerSizeInBits(0));
bool CheckSROA = I.getType()->isPointerTy();
Constant *FirstC = nullptr;
std::pair<Value *, APInt> FirstBaseAndOffset = {nullptr, ZeroOffset};
Value *FirstV = nullptr;
for (unsigned i = 0, e = I.getNumIncomingValues(); i != e; ++i) {
BasicBlock *Pred = I.getIncomingBlock(i);
if (DeadBlocks.count(Pred))
continue;
BasicBlock *KnownSuccessor = KnownSuccessors[Pred];
if (KnownSuccessor && KnownSuccessor != I.getParent())
continue;
Value *V = I.getIncomingValue(i);
if (&I == V)
continue;
Constant *C = dyn_cast<Constant>(V);
if (!C)
C = SimplifiedValues.lookup(V);
std::pair<Value *, APInt> BaseAndOffset = {nullptr, ZeroOffset};
if (!C && CheckSROA)
BaseAndOffset = ConstantOffsetPtrs.lookup(V);
if (!C && !BaseAndOffset.first)
return true;
if (FirstC) {
if (FirstC == C)
continue;
return true;
}
if (FirstV) {
if (FirstBaseAndOffset == BaseAndOffset)
continue;
return true;
}
if (C) {
FirstC = C;
continue;
}
FirstV = V;
FirstBaseAndOffset = BaseAndOffset;
}
if (FirstC) {
SimplifiedValues[&I] = FirstC;
return true;
}
if (FirstBaseAndOffset.first) {
ConstantOffsetPtrs[&I] = FirstBaseAndOffset;
if (auto *SROAArg = getSROAArgForValueOrNull(FirstV))
SROAArgValues[&I] = SROAArg;
}
return true;
}
bool CallAnalyzer::canFoldInboundsGEP(GetElementPtrInst &I) {
std::pair<Value *, APInt> BaseAndOffset =
ConstantOffsetPtrs.lookup(I.getPointerOperand());
if (!BaseAndOffset.first)
return false;
if (!accumulateGEPOffset(cast<GEPOperator>(I), BaseAndOffset.second))
return false;
ConstantOffsetPtrs[&I] = BaseAndOffset;
return true;
}
bool CallAnalyzer::visitGetElementPtr(GetElementPtrInst &I) {
auto *SROAArg = getSROAArgForValueOrNull(I.getPointerOperand());
auto IsGEPOffsetConstant = [&](GetElementPtrInst &GEP) {
for (const Use &Op : GEP.indices())
if (!isa<Constant>(Op) && !SimplifiedValues.lookup(Op))
return false;
return true;
};
if (!DisableGEPConstOperand)
if (simplifyInstruction(I))
return true;
if ((I.isInBounds() && canFoldInboundsGEP(I)) || IsGEPOffsetConstant(I)) {
if (SROAArg)
SROAArgValues[&I] = SROAArg;
return true;
}
if (SROAArg)
disableSROAForArg(SROAArg);
return isGEPFree(I);
}
bool CallAnalyzer::simplifyInstruction(Instruction &I) {
SmallVector<Constant *> COps;
for (Value *Op : I.operands()) {
Constant *COp = dyn_cast<Constant>(Op);
if (!COp)
COp = SimplifiedValues.lookup(Op);
if (!COp)
return false;
COps.push_back(COp);
}
auto *C = ConstantFoldInstOperands(&I, COps, DL);
if (!C)
return false;
SimplifiedValues[&I] = C;
return true;
}
bool CallAnalyzer::simplifyIntrinsicCallIsConstant(CallBase &CB) {
Value *Arg = CB.getArgOperand(0);
auto *C = dyn_cast<Constant>(Arg);
if (!C)
C = dyn_cast_or_null<Constant>(SimplifiedValues.lookup(Arg));
Type *RT = CB.getFunctionType()->getReturnType();
SimplifiedValues[&CB] = ConstantInt::get(RT, C ? 1 : 0);
return true;
}
bool CallAnalyzer::visitBitCast(BitCastInst &I) {
if (simplifyInstruction(I))
return true;
std::pair<Value *, APInt> BaseAndOffset =
ConstantOffsetPtrs.lookup(I.getOperand(0));
if (BaseAndOffset.first)
ConstantOffsetPtrs[&I] = BaseAndOffset;
if (auto *SROAArg = getSROAArgForValueOrNull(I.getOperand(0)))
SROAArgValues[&I] = SROAArg;
return true;
}
bool CallAnalyzer::visitPtrToInt(PtrToIntInst &I) {
if (simplifyInstruction(I))
return true;
unsigned IntegerSize = I.getType()->getScalarSizeInBits();
unsigned AS = I.getOperand(0)->getType()->getPointerAddressSpace();
if (IntegerSize == DL.getPointerSizeInBits(AS)) {
std::pair<Value *, APInt> BaseAndOffset =
ConstantOffsetPtrs.lookup(I.getOperand(0));
if (BaseAndOffset.first)
ConstantOffsetPtrs[&I] = BaseAndOffset;
}
if (auto *SROAArg = getSROAArgForValueOrNull(I.getOperand(0)))
SROAArgValues[&I] = SROAArg;
return TTI.getUserCost(&I, TargetTransformInfo::TCK_SizeAndLatency) ==
TargetTransformInfo::TCC_Free;
}
bool CallAnalyzer::visitIntToPtr(IntToPtrInst &I) {
if (simplifyInstruction(I))
return true;
Value *Op = I.getOperand(0);
unsigned IntegerSize = Op->getType()->getScalarSizeInBits();
if (IntegerSize <= DL.getPointerTypeSizeInBits(I.getType())) {
std::pair<Value *, APInt> BaseAndOffset = ConstantOffsetPtrs.lookup(Op);
if (BaseAndOffset.first)
ConstantOffsetPtrs[&I] = BaseAndOffset;
}
if (auto *SROAArg = getSROAArgForValueOrNull(Op))
SROAArgValues[&I] = SROAArg;
return TTI.getUserCost(&I, TargetTransformInfo::TCK_SizeAndLatency) ==
TargetTransformInfo::TCC_Free;
}
bool CallAnalyzer::visitCastInst(CastInst &I) {
if (simplifyInstruction(I))
return true;
disableSROA(I.getOperand(0));
switch (I.getOpcode()) {
case Instruction::FPTrunc:
case Instruction::FPExt:
case Instruction::UIToFP:
case Instruction::SIToFP:
case Instruction::FPToUI:
case Instruction::FPToSI:
if (TTI.getFPOpCost(I.getType()) == TargetTransformInfo::TCC_Expensive)
onCallPenalty();
break;
default:
break;
}
return TTI.getUserCost(&I, TargetTransformInfo::TCK_SizeAndLatency) ==
TargetTransformInfo::TCC_Free;
}
bool CallAnalyzer::paramHasAttr(Argument *A, Attribute::AttrKind Attr) {
return CandidateCall.paramHasAttr(A->getArgNo(), Attr);
}
bool CallAnalyzer::isKnownNonNullInCallee(Value *V) {
if (Argument *A = dyn_cast<Argument>(V))
if (paramHasAttr(A, Attribute::NonNull))
return true;
if (isAllocaDerivedArg(V))
return true;
return false;
}
bool CallAnalyzer::allowSizeGrowth(CallBase &Call) {
if (InvokeInst *II = dyn_cast<InvokeInst>(&Call)) {
if (isa<UnreachableInst>(II->getNormalDest()->getTerminator()))
return false;
} else if (isa<UnreachableInst>(Call.getParent()->getTerminator()))
return false;
return true;
}
bool InlineCostCallAnalyzer::isColdCallSite(CallBase &Call,
BlockFrequencyInfo *CallerBFI) {
if (PSI && PSI->hasProfileSummary())
return PSI->isColdCallSite(Call, CallerBFI);
if (!CallerBFI)
return false;
const BranchProbability ColdProb(ColdCallSiteRelFreq, 100);
auto CallSiteBB = Call.getParent();
auto CallSiteFreq = CallerBFI->getBlockFreq(CallSiteBB);
auto CallerEntryFreq =
CallerBFI->getBlockFreq(&(Call.getCaller()->getEntryBlock()));
return CallSiteFreq < CallerEntryFreq * ColdProb;
}
Optional<int>
InlineCostCallAnalyzer::getHotCallSiteThreshold(CallBase &Call,
BlockFrequencyInfo *CallerBFI) {
if (PSI && PSI->hasProfileSummary() && PSI->isHotCallSite(Call, CallerBFI))
return Params.HotCallSiteThreshold;
if (!CallerBFI || !Params.LocallyHotCallSiteThreshold)
return None;
auto CallSiteBB = Call.getParent();
auto CallSiteFreq = CallerBFI->getBlockFreq(CallSiteBB).getFrequency();
auto CallerEntryFreq = CallerBFI->getEntryFreq();
if (CallSiteFreq >= CallerEntryFreq * HotCallSiteRelFreq)
return Params.LocallyHotCallSiteThreshold;
return None;
}
void InlineCostCallAnalyzer::updateThreshold(CallBase &Call, Function &Callee) {
if (!allowSizeGrowth(Call)) {
Threshold = 0;
return;
}
Function *Caller = Call.getCaller();
auto MinIfValid = [](int A, Optional<int> B) {
return B ? std::min(A, B.value()) : A;
};
auto MaxIfValid = [](int A, Optional<int> B) {
return B ? std::max(A, B.value()) : A;
};
int SingleBBBonusPercent = 50;
int VectorBonusPercent = TTI.getInlinerVectorBonusPercent();
int LastCallToStaticBonus = InlineConstants::LastCallToStaticBonus;
auto DisallowAllBonuses = [&]() {
SingleBBBonusPercent = 0;
VectorBonusPercent = 0;
LastCallToStaticBonus = 0;
};
if (Caller->hasMinSize()) {
Threshold = MinIfValid(Threshold, Params.OptMinSizeThreshold);
SingleBBBonusPercent = 0;
VectorBonusPercent = 0;
} else if (Caller->hasOptSize())
Threshold = MinIfValid(Threshold, Params.OptSizeThreshold);
if (!Caller->hasMinSize()) {
if (Callee.hasFnAttribute(Attribute::InlineHint))
Threshold = MaxIfValid(Threshold, Params.HintThreshold);
BlockFrequencyInfo *CallerBFI = GetBFI ? &(GetBFI(*Caller)) : nullptr;
auto HotCallSiteThreshold = getHotCallSiteThreshold(Call, CallerBFI);
if (!Caller->hasOptSize() && HotCallSiteThreshold) {
LLVM_DEBUG(dbgs() << "Hot callsite.\n");
Threshold = *HotCallSiteThreshold;
} else if (isColdCallSite(Call, CallerBFI)) {
LLVM_DEBUG(dbgs() << "Cold callsite.\n");
DisallowAllBonuses();
Threshold = MinIfValid(Threshold, Params.ColdCallSiteThreshold);
} else if (PSI) {
if (PSI->isFunctionEntryHot(&Callee)) {
LLVM_DEBUG(dbgs() << "Hot callee.\n");
Threshold = MaxIfValid(Threshold, Params.HintThreshold);
} else if (PSI->isFunctionEntryCold(&Callee)) {
LLVM_DEBUG(dbgs() << "Cold callee.\n");
DisallowAllBonuses();
Threshold = MinIfValid(Threshold, Params.ColdThreshold);
}
}
}
Threshold += TTI.adjustInliningThreshold(&Call);
Threshold *= TTI.getInliningThresholdMultiplier();
SingleBBBonus = Threshold * SingleBBBonusPercent / 100;
VectorBonus = Threshold * VectorBonusPercent / 100;
bool OnlyOneCallAndLocalLinkage = F.hasLocalLinkage() && F.hasOneLiveUse() &&
&F == Call.getCalledFunction();
if (OnlyOneCallAndLocalLinkage)
Cost -= LastCallToStaticBonus;
}
bool CallAnalyzer::visitCmpInst(CmpInst &I) {
Value *LHS = I.getOperand(0), *RHS = I.getOperand(1);
if (simplifyInstruction(I))
return true;
if (I.getOpcode() == Instruction::FCmp)
return false;
Value *LHSBase, *RHSBase;
APInt LHSOffset, RHSOffset;
std::tie(LHSBase, LHSOffset) = ConstantOffsetPtrs.lookup(LHS);
if (LHSBase) {
std::tie(RHSBase, RHSOffset) = ConstantOffsetPtrs.lookup(RHS);
if (RHSBase && LHSBase == RHSBase) {
Constant *CLHS = ConstantInt::get(LHS->getContext(), LHSOffset);
Constant *CRHS = ConstantInt::get(RHS->getContext(), RHSOffset);
if (Constant *C = ConstantExpr::getICmp(I.getPredicate(), CLHS, CRHS)) {
SimplifiedValues[&I] = C;
++NumConstantPtrCmps;
return true;
}
}
}
if (I.isEquality() && isa<ConstantPointerNull>(I.getOperand(1)) &&
isKnownNonNullInCallee(I.getOperand(0))) {
bool IsNotEqual = I.getPredicate() == CmpInst::ICMP_NE;
SimplifiedValues[&I] = IsNotEqual ? ConstantInt::getTrue(I.getType())
: ConstantInt::getFalse(I.getType());
return true;
}
return handleSROA(I.getOperand(0), isa<ConstantPointerNull>(I.getOperand(1)));
}
bool CallAnalyzer::visitSub(BinaryOperator &I) {
Value *LHS = I.getOperand(0), *RHS = I.getOperand(1);
Value *LHSBase, *RHSBase;
APInt LHSOffset, RHSOffset;
std::tie(LHSBase, LHSOffset) = ConstantOffsetPtrs.lookup(LHS);
if (LHSBase) {
std::tie(RHSBase, RHSOffset) = ConstantOffsetPtrs.lookup(RHS);
if (RHSBase && LHSBase == RHSBase) {
Constant *CLHS = ConstantInt::get(LHS->getContext(), LHSOffset);
Constant *CRHS = ConstantInt::get(RHS->getContext(), RHSOffset);
if (Constant *C = ConstantExpr::getSub(CLHS, CRHS)) {
SimplifiedValues[&I] = C;
++NumConstantPtrDiffs;
return true;
}
}
}
return Base::visitSub(I);
}
bool CallAnalyzer::visitBinaryOperator(BinaryOperator &I) {
Value *LHS = I.getOperand(0), *RHS = I.getOperand(1);
Constant *CLHS = dyn_cast<Constant>(LHS);
if (!CLHS)
CLHS = SimplifiedValues.lookup(LHS);
Constant *CRHS = dyn_cast<Constant>(RHS);
if (!CRHS)
CRHS = SimplifiedValues.lookup(RHS);
Value *SimpleV = nullptr;
if (auto FI = dyn_cast<FPMathOperator>(&I))
SimpleV = simplifyBinOp(I.getOpcode(), CLHS ? CLHS : LHS, CRHS ? CRHS : RHS,
FI->getFastMathFlags(), DL);
else
SimpleV =
simplifyBinOp(I.getOpcode(), CLHS ? CLHS : LHS, CRHS ? CRHS : RHS, DL);
if (Constant *C = dyn_cast_or_null<Constant>(SimpleV))
SimplifiedValues[&I] = C;
if (SimpleV)
return true;
disableSROA(LHS);
disableSROA(RHS);
using namespace llvm::PatternMatch;
if (I.getType()->isFloatingPointTy() &&
TTI.getFPOpCost(I.getType()) == TargetTransformInfo::TCC_Expensive &&
!match(&I, m_FNeg(m_Value())))
onCallPenalty();
return false;
}
bool CallAnalyzer::visitFNeg(UnaryOperator &I) {
Value *Op = I.getOperand(0);
Constant *COp = dyn_cast<Constant>(Op);
if (!COp)
COp = SimplifiedValues.lookup(Op);
Value *SimpleV = simplifyFNegInst(
COp ? COp : Op, cast<FPMathOperator>(I).getFastMathFlags(), DL);
if (Constant *C = dyn_cast_or_null<Constant>(SimpleV))
SimplifiedValues[&I] = C;
if (SimpleV)
return true;
disableSROA(Op);
return false;
}
bool CallAnalyzer::visitLoad(LoadInst &I) {
if (handleSROA(I.getPointerOperand(), I.isSimple()))
return true;
if (EnableLoadElimination &&
!LoadAddrSet.insert(I.getPointerOperand()).second && I.isUnordered()) {
onLoadEliminationOpportunity();
return true;
}
return false;
}
bool CallAnalyzer::visitStore(StoreInst &I) {
if (handleSROA(I.getPointerOperand(), I.isSimple()))
return true;
disableLoadElimination();
return false;
}
bool CallAnalyzer::visitExtractValue(ExtractValueInst &I) {
if (simplifyInstruction(I))
return true;
return Base::visitExtractValue(I);
}
bool CallAnalyzer::visitInsertValue(InsertValueInst &I) {
if (simplifyInstruction(I))
return true;
return Base::visitInsertValue(I);
}
bool CallAnalyzer::simplifyCallSite(Function *F, CallBase &Call) {
if (!canConstantFoldCallTo(&Call, F))
return false;
SmallVector<Constant *, 4> ConstantArgs;
ConstantArgs.reserve(Call.arg_size());
for (Value *I : Call.args()) {
Constant *C = dyn_cast<Constant>(I);
if (!C)
C = dyn_cast_or_null<Constant>(SimplifiedValues.lookup(I));
if (!C)
return false;
ConstantArgs.push_back(C);
}
if (Constant *C = ConstantFoldCall(&Call, F, ConstantArgs)) {
SimplifiedValues[&Call] = C;
return true;
}
return false;
}
bool CallAnalyzer::visitCallBase(CallBase &Call) {
if (!onCallBaseVisitStart(Call))
return true;
if (Call.hasFnAttr(Attribute::ReturnsTwice) &&
!F.hasFnAttribute(Attribute::ReturnsTwice)) {
ExposesReturnsTwice = true;
return false;
}
if (isa<CallInst>(Call) && cast<CallInst>(Call).cannotDuplicate())
ContainsNoDuplicateCall = true;
Function *F = Call.getCalledFunction();
bool IsIndirectCall = !F;
if (IsIndirectCall) {
Value *Callee = Call.getCalledOperand();
F = dyn_cast_or_null<Function>(SimplifiedValues.lookup(Callee));
if (!F || F->getFunctionType() != Call.getFunctionType()) {
onCallArgumentSetup(Call);
if (!Call.onlyReadsMemory())
disableLoadElimination();
return Base::visitCallBase(Call);
}
}
assert(F && "Expected a call to a known function");
if (simplifyCallSite(F, Call))
return true;
if (IntrinsicInst *II = dyn_cast<IntrinsicInst>(&Call)) {
switch (II->getIntrinsicID()) {
default:
if (!Call.onlyReadsMemory() && !isAssumeLikeIntrinsic(II))
disableLoadElimination();
return Base::visitCallBase(Call);
case Intrinsic::load_relative:
onLoadRelativeIntrinsic();
return false;
case Intrinsic::memset:
case Intrinsic::memcpy:
case Intrinsic::memmove:
disableLoadElimination();
return false;
case Intrinsic::icall_branch_funnel:
case Intrinsic::localescape:
HasUninlineableIntrinsic = true;
return false;
case Intrinsic::vastart:
InitsVargArgs = true;
return false;
case Intrinsic::launder_invariant_group:
case Intrinsic::strip_invariant_group:
if (auto *SROAArg = getSROAArgForValueOrNull(II->getOperand(0)))
SROAArgValues[II] = SROAArg;
return true;
case Intrinsic::is_constant:
return simplifyIntrinsicCallIsConstant(Call);
}
}
if (F == Call.getFunction()) {
IsRecursiveCall = true;
if (!AllowRecursiveCall)
return false;
}
if (TTI.isLoweredToCall(F)) {
onLoweredCall(F, Call, IsIndirectCall);
}
if (!(Call.onlyReadsMemory() || (IsIndirectCall && F->onlyReadsMemory())))
disableLoadElimination();
return Base::visitCallBase(Call);
}
bool CallAnalyzer::visitReturnInst(ReturnInst &RI) {
bool Free = !HasReturn;
HasReturn = true;
return Free;
}
bool CallAnalyzer::visitBranchInst(BranchInst &BI) {
return BI.isUnconditional() || isa<ConstantInt>(BI.getCondition()) ||
isa_and_nonnull<ConstantInt>(
SimplifiedValues.lookup(BI.getCondition()));
}
bool CallAnalyzer::visitSelectInst(SelectInst &SI) {
bool CheckSROA = SI.getType()->isPointerTy();
Value *TrueVal = SI.getTrueValue();
Value *FalseVal = SI.getFalseValue();
Constant *TrueC = dyn_cast<Constant>(TrueVal);
if (!TrueC)
TrueC = SimplifiedValues.lookup(TrueVal);
Constant *FalseC = dyn_cast<Constant>(FalseVal);
if (!FalseC)
FalseC = SimplifiedValues.lookup(FalseVal);
Constant *CondC =
dyn_cast_or_null<Constant>(SimplifiedValues.lookup(SI.getCondition()));
if (!CondC) {
if (TrueC == FalseC && TrueC) {
SimplifiedValues[&SI] = TrueC;
return true;
}
if (!CheckSROA)
return Base::visitSelectInst(SI);
std::pair<Value *, APInt> TrueBaseAndOffset =
ConstantOffsetPtrs.lookup(TrueVal);
std::pair<Value *, APInt> FalseBaseAndOffset =
ConstantOffsetPtrs.lookup(FalseVal);
if (TrueBaseAndOffset == FalseBaseAndOffset && TrueBaseAndOffset.first) {
ConstantOffsetPtrs[&SI] = TrueBaseAndOffset;
if (auto *SROAArg = getSROAArgForValueOrNull(TrueVal))
SROAArgValues[&SI] = SROAArg;
return true;
}
return Base::visitSelectInst(SI);
}
Value *SelectedV = CondC->isAllOnesValue() ? TrueVal
: (CondC->isNullValue()) ? FalseVal
: nullptr;
if (!SelectedV) {
if (TrueC && FalseC) {
if (auto *C = ConstantExpr::getSelect(CondC, TrueC, FalseC)) {
SimplifiedValues[&SI] = C;
return true;
}
}
return Base::visitSelectInst(SI);
}
if (Constant *SelectedC = dyn_cast<Constant>(SelectedV)) {
SimplifiedValues[&SI] = SelectedC;
return true;
}
if (!CheckSROA)
return true;
std::pair<Value *, APInt> BaseAndOffset =
ConstantOffsetPtrs.lookup(SelectedV);
if (BaseAndOffset.first) {
ConstantOffsetPtrs[&SI] = BaseAndOffset;
if (auto *SROAArg = getSROAArgForValueOrNull(SelectedV))
SROAArgValues[&SI] = SROAArg;
}
return true;
}
bool CallAnalyzer::visitSwitchInst(SwitchInst &SI) {
if (isa<ConstantInt>(SI.getCondition()))
return true;
if (Value *V = SimplifiedValues.lookup(SI.getCondition()))
if (isa<ConstantInt>(V))
return true;
unsigned JumpTableSize = 0;
BlockFrequencyInfo *BFI = GetBFI ? &(GetBFI(F)) : nullptr;
unsigned NumCaseCluster =
TTI.getEstimatedNumberOfCaseClusters(SI, JumpTableSize, PSI, BFI);
onFinalizeSwitch(JumpTableSize, NumCaseCluster);
return false;
}
bool CallAnalyzer::visitIndirectBrInst(IndirectBrInst &IBI) {
HasIndirectBr = true;
return false;
}
bool CallAnalyzer::visitResumeInst(ResumeInst &RI) {
return false;
}
bool CallAnalyzer::visitCleanupReturnInst(CleanupReturnInst &CRI) {
return false;
}
bool CallAnalyzer::visitCatchReturnInst(CatchReturnInst &CRI) {
return false;
}
bool CallAnalyzer::visitUnreachableInst(UnreachableInst &I) {
return true; }
bool CallAnalyzer::visitInstruction(Instruction &I) {
if (TTI.getUserCost(&I, TargetTransformInfo::TCK_SizeAndLatency) ==
TargetTransformInfo::TCC_Free)
return true;
for (const Use &Op : I.operands())
disableSROA(Op);
return false;
}
InlineResult
CallAnalyzer::analyzeBlock(BasicBlock *BB,
SmallPtrSetImpl<const Value *> &EphValues) {
for (Instruction &I : *BB) {
if (I.isDebugOrPseudoInst())
continue;
if (EphValues.count(&I))
continue;
++NumInstructions;
if (isa<ExtractElementInst>(I) || I.getType()->isVectorTy())
++NumVectorInstructions;
onInstructionAnalysisStart(&I);
if (Base::visit(&I))
++NumInstructionsSimplified;
else
onMissedSimplification();
onInstructionAnalysisFinish(&I);
using namespace ore;
InlineResult IR = InlineResult::success();
if (IsRecursiveCall && !AllowRecursiveCall)
IR = InlineResult::failure("recursive");
else if (ExposesReturnsTwice)
IR = InlineResult::failure("exposes returns twice");
else if (HasDynamicAlloca)
IR = InlineResult::failure("dynamic alloca");
else if (HasIndirectBr)
IR = InlineResult::failure("indirect branch");
else if (HasUninlineableIntrinsic)
IR = InlineResult::failure("uninlinable intrinsic");
else if (InitsVargArgs)
IR = InlineResult::failure("varargs");
if (!IR.isSuccess()) {
if (ORE)
ORE->emit([&]() {
return OptimizationRemarkMissed(DEBUG_TYPE, "NeverInline",
&CandidateCall)
<< NV("Callee", &F) << " has uninlinable pattern ("
<< NV("InlineResult", IR.getFailureReason())
<< ") and cost is not fully computed";
});
return IR;
}
if (IsCallerRecursive && AllocatedSize > RecurStackSizeThreshold) {
auto IR =
InlineResult::failure("recursive and allocates too much stack space");
if (ORE)
ORE->emit([&]() {
return OptimizationRemarkMissed(DEBUG_TYPE, "NeverInline",
&CandidateCall)
<< NV("Callee", &F) << " is "
<< NV("InlineResult", IR.getFailureReason())
<< ". Cost is not fully computed";
});
return IR;
}
if (shouldStop())
return InlineResult::failure(
"Call site analysis is not favorable to inlining.");
}
return InlineResult::success();
}
ConstantInt *CallAnalyzer::stripAndComputeInBoundsConstantOffsets(Value *&V) {
if (!V->getType()->isPointerTy())
return nullptr;
unsigned AS = V->getType()->getPointerAddressSpace();
unsigned IntPtrWidth = DL.getIndexSizeInBits(AS);
APInt Offset = APInt::getZero(IntPtrWidth);
SmallPtrSet<Value *, 4> Visited;
Visited.insert(V);
do {
if (GEPOperator *GEP = dyn_cast<GEPOperator>(V)) {
if (!GEP->isInBounds() || !accumulateGEPOffset(*GEP, Offset))
return nullptr;
V = GEP->getPointerOperand();
} else if (Operator::getOpcode(V) == Instruction::BitCast) {
V = cast<Operator>(V)->getOperand(0);
} else if (GlobalAlias *GA = dyn_cast<GlobalAlias>(V)) {
if (GA->isInterposable())
break;
V = GA->getAliasee();
} else {
break;
}
assert(V->getType()->isPointerTy() && "Unexpected operand type!");
} while (Visited.insert(V).second);
Type *IdxPtrTy = DL.getIndexType(V->getType());
return cast<ConstantInt>(ConstantInt::get(IdxPtrTy, Offset));
}
void CallAnalyzer::findDeadBlocks(BasicBlock *CurrBB, BasicBlock *NextBB) {
auto IsEdgeDead = [&](BasicBlock *Pred, BasicBlock *Succ) {
return (DeadBlocks.count(Pred) ||
(KnownSuccessors[Pred] && KnownSuccessors[Pred] != Succ));
};
auto IsNewlyDead = [&](BasicBlock *BB) {
return (!DeadBlocks.count(BB) &&
llvm::all_of(predecessors(BB),
[&](BasicBlock *P) { return IsEdgeDead(P, BB); }));
};
for (BasicBlock *Succ : successors(CurrBB)) {
if (Succ == NextBB || !IsNewlyDead(Succ))
continue;
SmallVector<BasicBlock *, 4> NewDead;
NewDead.push_back(Succ);
while (!NewDead.empty()) {
BasicBlock *Dead = NewDead.pop_back_val();
if (DeadBlocks.insert(Dead).second)
for (BasicBlock *S : successors(Dead))
if (IsNewlyDead(S))
NewDead.push_back(S);
}
}
}
InlineResult CallAnalyzer::analyze() {
++NumCallsAnalyzed;
auto Result = onAnalysisStart();
if (!Result.isSuccess())
return Result;
if (F.empty())
return InlineResult::success();
Function *Caller = CandidateCall.getFunction();
for (User *U : Caller->users()) {
CallBase *Call = dyn_cast<CallBase>(U);
if (Call && Call->getFunction() == Caller) {
IsCallerRecursive = true;
break;
}
}
auto CAI = CandidateCall.arg_begin();
for (Argument &FAI : F.args()) {
assert(CAI != CandidateCall.arg_end());
if (Constant *C = dyn_cast<Constant>(CAI))
SimplifiedValues[&FAI] = C;
Value *PtrArg = *CAI;
if (ConstantInt *C = stripAndComputeInBoundsConstantOffsets(PtrArg)) {
ConstantOffsetPtrs[&FAI] = std::make_pair(PtrArg, C->getValue());
if (auto *SROAArg = dyn_cast<AllocaInst>(PtrArg)) {
SROAArgValues[&FAI] = SROAArg;
onInitializeSROAArg(SROAArg);
EnabledSROAAllocas.insert(SROAArg);
}
}
++CAI;
}
NumConstantArgs = SimplifiedValues.size();
NumConstantOffsetPtrArgs = ConstantOffsetPtrs.size();
NumAllocaArgs = SROAArgValues.size();
SmallPtrSet<const Value *, 32> EphValues;
CodeMetrics::collectEphemeralValues(&F, &GetAssumptionCache(F), EphValues);
typedef SetVector<BasicBlock *, SmallVector<BasicBlock *, 16>,
SmallPtrSet<BasicBlock *, 16>>
BBSetVector;
BBSetVector BBWorklist;
BBWorklist.insert(&F.getEntryBlock());
for (unsigned Idx = 0; Idx != BBWorklist.size(); ++Idx) {
if (shouldStop())
break;
BasicBlock *BB = BBWorklist[Idx];
if (BB->empty())
continue;
onBlockStart(BB);
if (BB->hasAddressTaken())
for (User *U : BlockAddress::get(&*BB)->users())
if (!isa<CallBrInst>(*U))
return InlineResult::failure("blockaddress used outside of callbr");
InlineResult IR = analyzeBlock(BB, EphValues);
if (!IR.isSuccess())
return IR;
Instruction *TI = BB->getTerminator();
if (BranchInst *BI = dyn_cast<BranchInst>(TI)) {
if (BI->isConditional()) {
Value *Cond = BI->getCondition();
if (ConstantInt *SimpleCond =
dyn_cast_or_null<ConstantInt>(SimplifiedValues.lookup(Cond))) {
BasicBlock *NextBB = BI->getSuccessor(SimpleCond->isZero() ? 1 : 0);
BBWorklist.insert(NextBB);
KnownSuccessors[BB] = NextBB;
findDeadBlocks(BB, NextBB);
continue;
}
}
} else if (SwitchInst *SI = dyn_cast<SwitchInst>(TI)) {
Value *Cond = SI->getCondition();
if (ConstantInt *SimpleCond =
dyn_cast_or_null<ConstantInt>(SimplifiedValues.lookup(Cond))) {
BasicBlock *NextBB = SI->findCaseValue(SimpleCond)->getCaseSuccessor();
BBWorklist.insert(NextBB);
KnownSuccessors[BB] = NextBB;
findDeadBlocks(BB, NextBB);
continue;
}
}
for (unsigned TIdx = 0, TSize = TI->getNumSuccessors(); TIdx != TSize;
++TIdx)
BBWorklist.insert(TI->getSuccessor(TIdx));
onBlockAnalyzed(BB);
}
bool OnlyOneCallAndLocalLinkage = F.hasLocalLinkage() && F.hasOneLiveUse() &&
&F == CandidateCall.getCalledFunction();
if (!OnlyOneCallAndLocalLinkage && ContainsNoDuplicateCall)
return InlineResult::failure("noduplicate");
if (AllocatedSize > StackSizeThreshold)
return InlineResult::failure("stacksize");
return finalizeAnalysis();
}
void InlineCostCallAnalyzer::print(raw_ostream &OS) {
#define DEBUG_PRINT_STAT(x) OS << " " #x ": " << x << "\n"
if (PrintInstructionComments)
F.print(OS, &Writer);
DEBUG_PRINT_STAT(NumConstantArgs);
DEBUG_PRINT_STAT(NumConstantOffsetPtrArgs);
DEBUG_PRINT_STAT(NumAllocaArgs);
DEBUG_PRINT_STAT(NumConstantPtrCmps);
DEBUG_PRINT_STAT(NumConstantPtrDiffs);
DEBUG_PRINT_STAT(NumInstructionsSimplified);
DEBUG_PRINT_STAT(NumInstructions);
DEBUG_PRINT_STAT(SROACostSavings);
DEBUG_PRINT_STAT(SROACostSavingsLost);
DEBUG_PRINT_STAT(LoadEliminationCost);
DEBUG_PRINT_STAT(ContainsNoDuplicateCall);
DEBUG_PRINT_STAT(Cost);
DEBUG_PRINT_STAT(Threshold);
#undef DEBUG_PRINT_STAT
}
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
LLVM_DUMP_METHOD void InlineCostCallAnalyzer::dump() { print(dbgs()); }
#endif
static bool functionsHaveCompatibleAttributes(
Function *Caller, Function *Callee, TargetTransformInfo &TTI,
function_ref<const TargetLibraryInfo &(Function &)> &GetTLI) {
auto CalleeTLI = GetTLI(*Callee);
return (IgnoreTTIInlineCompatible ||
TTI.areInlineCompatible(Caller, Callee)) &&
GetTLI(*Caller).areInlineCompatible(CalleeTLI,
InlineCallerSupersetNoBuiltin) &&
AttributeFuncs::areInlineCompatible(*Caller, *Callee);
}
int llvm::getCallsiteCost(CallBase &Call, const DataLayout &DL) {
int Cost = 0;
for (unsigned I = 0, E = Call.arg_size(); I != E; ++I) {
if (Call.isByValArgument(I)) {
PointerType *PTy = cast<PointerType>(Call.getArgOperand(I)->getType());
unsigned TypeSize = DL.getTypeSizeInBits(Call.getParamByValType(I));
unsigned AS = PTy->getAddressSpace();
unsigned PointerSize = DL.getPointerSizeInBits(AS);
unsigned NumStores = (TypeSize + PointerSize - 1) / PointerSize;
NumStores = std::min(NumStores, 8U);
Cost += 2 * NumStores * InlineConstants::InstrCost;
} else {
Cost += InlineConstants::InstrCost;
}
}
Cost += InlineConstants::InstrCost + CallPenalty;
return Cost;
}
InlineCost llvm::getInlineCost(
CallBase &Call, const InlineParams &Params, TargetTransformInfo &CalleeTTI,
function_ref<AssumptionCache &(Function &)> GetAssumptionCache,
function_ref<const TargetLibraryInfo &(Function &)> GetTLI,
function_ref<BlockFrequencyInfo &(Function &)> GetBFI,
ProfileSummaryInfo *PSI, OptimizationRemarkEmitter *ORE) {
return getInlineCost(Call, Call.getCalledFunction(), Params, CalleeTTI,
GetAssumptionCache, GetTLI, GetBFI, PSI, ORE);
}
Optional<int> llvm::getInliningCostEstimate(
CallBase &Call, TargetTransformInfo &CalleeTTI,
function_ref<AssumptionCache &(Function &)> GetAssumptionCache,
function_ref<BlockFrequencyInfo &(Function &)> GetBFI,
ProfileSummaryInfo *PSI, OptimizationRemarkEmitter *ORE) {
const InlineParams Params = { 0,
{},
{},
{},
{},
{},
{},
{},
true,
true};
InlineCostCallAnalyzer CA(*Call.getCalledFunction(), Call, Params, CalleeTTI,
GetAssumptionCache, GetBFI, PSI, ORE, true,
true);
auto R = CA.analyze();
if (!R.isSuccess())
return None;
return CA.getCost();
}
Optional<InlineCostFeatures> llvm::getInliningCostFeatures(
CallBase &Call, TargetTransformInfo &CalleeTTI,
function_ref<AssumptionCache &(Function &)> GetAssumptionCache,
function_ref<BlockFrequencyInfo &(Function &)> GetBFI,
ProfileSummaryInfo *PSI, OptimizationRemarkEmitter *ORE) {
InlineCostFeaturesAnalyzer CFA(CalleeTTI, GetAssumptionCache, GetBFI, PSI,
ORE, *Call.getCalledFunction(), Call);
auto R = CFA.analyze();
if (!R.isSuccess())
return None;
return CFA.features();
}
Optional<InlineResult> llvm::getAttributeBasedInliningDecision(
CallBase &Call, Function *Callee, TargetTransformInfo &CalleeTTI,
function_ref<const TargetLibraryInfo &(Function &)> GetTLI) {
if (!Callee)
return InlineResult::failure("indirect call");
if (Callee->isPresplitCoroutine())
return InlineResult::failure("unsplited coroutine call");
unsigned AllocaAS = Callee->getParent()->getDataLayout().getAllocaAddrSpace();
for (unsigned I = 0, E = Call.arg_size(); I != E; ++I)
if (Call.isByValArgument(I)) {
PointerType *PTy = cast<PointerType>(Call.getArgOperand(I)->getType());
if (PTy->getAddressSpace() != AllocaAS)
return InlineResult::failure("byval arguments without alloca"
" address space");
}
if (Call.hasFnAttr(Attribute::AlwaysInline)) {
if (Call.getAttributes().hasFnAttr(Attribute::NoInline))
return InlineResult::failure("noinline call site attribute");
auto IsViable = isInlineViable(*Callee);
if (IsViable.isSuccess())
return InlineResult::success();
return InlineResult::failure(IsViable.getFailureReason());
}
Function *Caller = Call.getCaller();
if (!functionsHaveCompatibleAttributes(Caller, Callee, CalleeTTI, GetTLI))
return InlineResult::failure("conflicting attributes");
if (Caller->hasOptNone())
return InlineResult::failure("optnone attribute");
if (!Caller->nullPointerIsDefined() && Callee->nullPointerIsDefined())
return InlineResult::failure("nullptr definitions incompatible");
if (Callee->isInterposable())
return InlineResult::failure("interposable");
if (Callee->hasFnAttribute(Attribute::NoInline))
return InlineResult::failure("noinline function attribute");
if (Call.isNoInline())
return InlineResult::failure("noinline call site attribute");
return None;
}
InlineCost llvm::getInlineCost(
CallBase &Call, Function *Callee, const InlineParams &Params,
TargetTransformInfo &CalleeTTI,
function_ref<AssumptionCache &(Function &)> GetAssumptionCache,
function_ref<const TargetLibraryInfo &(Function &)> GetTLI,
function_ref<BlockFrequencyInfo &(Function &)> GetBFI,
ProfileSummaryInfo *PSI, OptimizationRemarkEmitter *ORE) {
auto UserDecision =
llvm::getAttributeBasedInliningDecision(Call, Callee, CalleeTTI, GetTLI);
if (UserDecision) {
if (UserDecision->isSuccess())
return llvm::InlineCost::getAlways("always inline attribute");
return llvm::InlineCost::getNever(UserDecision->getFailureReason());
}
LLVM_DEBUG(llvm::dbgs() << " Analyzing call of " << Callee->getName()
<< "... (caller:" << Call.getCaller()->getName()
<< ")\n");
InlineCostCallAnalyzer CA(*Callee, Call, Params, CalleeTTI,
GetAssumptionCache, GetBFI, PSI, ORE);
InlineResult ShouldInline = CA.analyze();
LLVM_DEBUG(CA.dump());
if (CA.wasDecidedByCostBenefit()) {
if (ShouldInline.isSuccess())
return InlineCost::getAlways("benefit over cost",
CA.getCostBenefitPair());
else
return InlineCost::getNever("cost over benefit", CA.getCostBenefitPair());
}
if (CA.wasDecidedByCostThreshold())
return InlineCost::get(CA.getCost(), CA.getThreshold());
return ShouldInline.isSuccess()
? InlineCost::getAlways("empty function")
: InlineCost::getNever(ShouldInline.getFailureReason());
}
InlineResult llvm::isInlineViable(Function &F) {
bool ReturnsTwice = F.hasFnAttribute(Attribute::ReturnsTwice);
for (BasicBlock &BB : F) {
if (isa<IndirectBrInst>(BB.getTerminator()))
return InlineResult::failure("contains indirect branches");
if (BB.hasAddressTaken())
for (User *U : BlockAddress::get(&BB)->users())
if (!isa<CallBrInst>(*U))
return InlineResult::failure("blockaddress used outside of callbr");
for (auto &II : BB) {
CallBase *Call = dyn_cast<CallBase>(&II);
if (!Call)
continue;
Function *Callee = Call->getCalledFunction();
if (&F == Callee)
return InlineResult::failure("recursive call");
if (!ReturnsTwice && isa<CallInst>(Call) &&
cast<CallInst>(Call)->canReturnTwice())
return InlineResult::failure("exposes returns-twice attribute");
if (Callee)
switch (Callee->getIntrinsicID()) {
default:
break;
case llvm::Intrinsic::icall_branch_funnel:
return InlineResult::failure(
"disallowed inlining of @llvm.icall.branch.funnel");
case llvm::Intrinsic::localescape:
return InlineResult::failure(
"disallowed inlining of @llvm.localescape");
case llvm::Intrinsic::vastart:
return InlineResult::failure(
"contains VarArgs initialized with va_start");
}
}
}
return InlineResult::success();
}
InlineParams llvm::getInlineParams(int Threshold) {
InlineParams Params;
if (InlineThreshold.getNumOccurrences() > 0)
Params.DefaultThreshold = InlineThreshold;
else
Params.DefaultThreshold = Threshold;
Params.HintThreshold = HintThreshold;
Params.HotCallSiteThreshold = HotCallSiteThreshold;
if (LocallyHotCallSiteThreshold.getNumOccurrences() > 0)
Params.LocallyHotCallSiteThreshold = LocallyHotCallSiteThreshold;
Params.ColdCallSiteThreshold = ColdCallSiteThreshold;
if (InlineThreshold.getNumOccurrences() == 0) {
Params.OptMinSizeThreshold = InlineConstants::OptMinSizeThreshold;
Params.OptSizeThreshold = InlineConstants::OptSizeThreshold;
Params.ColdThreshold = ColdThreshold;
} else if (ColdThreshold.getNumOccurrences() > 0) {
Params.ColdThreshold = ColdThreshold;
}
return Params;
}
InlineParams llvm::getInlineParams() {
return getInlineParams(DefaultThreshold);
}
static int computeThresholdFromOptLevels(unsigned OptLevel,
unsigned SizeOptLevel) {
if (OptLevel > 2)
return InlineConstants::OptAggressiveThreshold;
if (SizeOptLevel == 1) return InlineConstants::OptSizeThreshold;
if (SizeOptLevel == 2) return InlineConstants::OptMinSizeThreshold;
return DefaultThreshold;
}
InlineParams llvm::getInlineParams(unsigned OptLevel, unsigned SizeOptLevel) {
auto Params =
getInlineParams(computeThresholdFromOptLevels(OptLevel, SizeOptLevel));
if (OptLevel > 2)
Params.LocallyHotCallSiteThreshold = LocallyHotCallSiteThreshold;
return Params;
}
PreservedAnalyses
InlineCostAnnotationPrinterPass::run(Function &F,
FunctionAnalysisManager &FAM) {
PrintInstructionComments = true;
std::function<AssumptionCache &(Function &)> GetAssumptionCache =
[&](Function &F) -> AssumptionCache & {
return FAM.getResult<AssumptionAnalysis>(F);
};
Module *M = F.getParent();
ProfileSummaryInfo PSI(*M);
DataLayout DL(M);
TargetTransformInfo TTI(DL);
const InlineParams Params = llvm::getInlineParams();
for (BasicBlock &BB : F) {
for (Instruction &I : BB) {
if (CallInst *CI = dyn_cast<CallInst>(&I)) {
Function *CalledFunction = CI->getCalledFunction();
if (!CalledFunction || CalledFunction->isDeclaration())
continue;
OptimizationRemarkEmitter ORE(CalledFunction);
InlineCostCallAnalyzer ICCA(*CalledFunction, *CI, Params, TTI,
GetAssumptionCache, nullptr, &PSI, &ORE);
ICCA.analyze();
OS << " Analyzing call of " << CalledFunction->getName()
<< "... (caller:" << CI->getCaller()->getName() << ")\n";
ICCA.print(OS);
OS << "\n";
}
}
}
return PreservedAnalyses::all();
}