1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322
|
//===- InlineOrder.cpp - Inlining order abstraction -*- C++ ---*-----------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "llvm/Analysis/InlineOrder.h"
#include "llvm/Analysis/AssumptionCache.h"
#include "llvm/Analysis/BlockFrequencyInfo.h"
#include "llvm/Analysis/GlobalsModRef.h"
#include "llvm/Analysis/InlineAdvisor.h"
#include "llvm/Analysis/InlineCost.h"
#include "llvm/Analysis/OptimizationRemarkEmitter.h"
#include "llvm/Analysis/ProfileSummaryInfo.h"
#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/Analysis/TargetTransformInfo.h"
#include "llvm/Support/CommandLine.h"
using namespace llvm;
#define DEBUG_TYPE "inline-order"
enum class InlinePriorityMode : int { Size, Cost, CostBenefit, ML };
static cl::opt<InlinePriorityMode> UseInlinePriority(
"inline-priority-mode", cl::init(InlinePriorityMode::Size), cl::Hidden,
cl::desc("Choose the priority mode to use in module inline"),
cl::values(clEnumValN(InlinePriorityMode::Size, "size",
"Use callee size priority."),
clEnumValN(InlinePriorityMode::Cost, "cost",
"Use inline cost priority."),
clEnumValN(InlinePriorityMode::CostBenefit, "cost-benefit",
"Use cost-benefit ratio."),
clEnumValN(InlinePriorityMode::ML, "ml", "Use ML.")));
static cl::opt<int> ModuleInlinerTopPriorityThreshold(
"module-inliner-top-priority-threshold", cl::Hidden, cl::init(0),
cl::desc("The cost threshold for call sites that get inlined without the "
"cost-benefit analysis"));
namespace {
llvm::InlineCost getInlineCostWrapper(CallBase &CB,
FunctionAnalysisManager &FAM,
const InlineParams &Params) {
Function &Caller = *CB.getCaller();
ProfileSummaryInfo *PSI =
FAM.getResult<ModuleAnalysisManagerFunctionProxy>(Caller)
.getCachedResult<ProfileSummaryAnalysis>(
*CB.getParent()->getParent()->getParent());
auto &ORE = FAM.getResult<OptimizationRemarkEmitterAnalysis>(Caller);
auto GetAssumptionCache = [&](Function &F) -> AssumptionCache & {
return FAM.getResult<AssumptionAnalysis>(F);
};
auto GetBFI = [&](Function &F) -> BlockFrequencyInfo & {
return FAM.getResult<BlockFrequencyAnalysis>(F);
};
auto GetTLI = [&](Function &F) -> const TargetLibraryInfo & {
return FAM.getResult<TargetLibraryAnalysis>(F);
};
Function &Callee = *CB.getCalledFunction();
auto &CalleeTTI = FAM.getResult<TargetIRAnalysis>(Callee);
bool RemarksEnabled =
Callee.getContext().getDiagHandlerPtr()->isMissedOptRemarkEnabled(
DEBUG_TYPE);
return getInlineCost(CB, Params, CalleeTTI, GetAssumptionCache, GetTLI,
GetBFI, PSI, RemarksEnabled ? &ORE : nullptr);
}
class SizePriority {
public:
SizePriority() = default;
SizePriority(const CallBase *CB, FunctionAnalysisManager &,
const InlineParams &) {
Function *Callee = CB->getCalledFunction();
Size = Callee->getInstructionCount();
}
static bool isMoreDesirable(const SizePriority &P1, const SizePriority &P2) {
return P1.Size < P2.Size;
}
private:
unsigned Size = UINT_MAX;
};
class CostPriority {
public:
CostPriority() = default;
CostPriority(const CallBase *CB, FunctionAnalysisManager &FAM,
const InlineParams &Params) {
auto IC = getInlineCostWrapper(const_cast<CallBase &>(*CB), FAM, Params);
if (IC.isVariable())
Cost = IC.getCost();
else
Cost = IC.isNever() ? INT_MAX : INT_MIN;
}
static bool isMoreDesirable(const CostPriority &P1, const CostPriority &P2) {
return P1.Cost < P2.Cost;
}
private:
int Cost = INT_MAX;
};
class CostBenefitPriority {
public:
CostBenefitPriority() = default;
CostBenefitPriority(const CallBase *CB, FunctionAnalysisManager &FAM,
const InlineParams &Params) {
auto IC = getInlineCostWrapper(const_cast<CallBase &>(*CB), FAM, Params);
if (IC.isVariable())
Cost = IC.getCost();
else
Cost = IC.isNever() ? INT_MAX : INT_MIN;
StaticBonusApplied = IC.getStaticBonusApplied();
CostBenefit = IC.getCostBenefit();
}
static bool isMoreDesirable(const CostBenefitPriority &P1,
const CostBenefitPriority &P2) {
// We prioritize call sites in the dictionary order of the following
// priorities:
//
// 1. Those call sites that are expected to reduce the caller size when
// inlined. Within them, we prioritize those call sites with bigger
// reduction.
//
// 2. Those call sites that have gone through the cost-benefit analysis.
// Currently, they are limited to hot call sites. Within them, we
// prioritize those call sites with higher benefit-to-cost ratios.
//
// 3. Remaining call sites are prioritized according to their costs.
// We add back StaticBonusApplied to determine whether we expect the caller
// to shrink (even if we don't delete the callee).
bool P1ReducesCallerSize =
P1.Cost + P1.StaticBonusApplied < ModuleInlinerTopPriorityThreshold;
bool P2ReducesCallerSize =
P2.Cost + P2.StaticBonusApplied < ModuleInlinerTopPriorityThreshold;
if (P1ReducesCallerSize || P2ReducesCallerSize) {
// If one reduces the caller size while the other doesn't, then return
// true iff P1 reduces the caller size.
if (P1ReducesCallerSize != P2ReducesCallerSize)
return P1ReducesCallerSize;
// If they both reduce the caller size, pick the one with the smaller
// cost.
return P1.Cost < P2.Cost;
}
bool P1HasCB = P1.CostBenefit.has_value();
bool P2HasCB = P2.CostBenefit.has_value();
if (P1HasCB || P2HasCB) {
// If one has undergone the cost-benefit analysis while the other hasn't,
// then return true iff P1 has.
if (P1HasCB != P2HasCB)
return P1HasCB;
// If they have undergone the cost-benefit analysis, then pick the one
// with a higher benefit-to-cost ratio.
APInt LHS = P1.CostBenefit->getBenefit() * P2.CostBenefit->getCost();
APInt RHS = P2.CostBenefit->getBenefit() * P1.CostBenefit->getCost();
return LHS.ugt(RHS);
}
// Remaining call sites are ordered according to their costs.
return P1.Cost < P2.Cost;
}
private:
int Cost = INT_MAX;
int StaticBonusApplied = 0;
std::optional<CostBenefitPair> CostBenefit;
};
class MLPriority {
public:
MLPriority() = default;
MLPriority(const CallBase *CB, FunctionAnalysisManager &FAM,
const InlineParams &Params) {
auto IC = getInlineCostWrapper(const_cast<CallBase &>(*CB), FAM, Params);
if (IC.isVariable())
Cost = IC.getCost();
else
Cost = IC.isNever() ? INT_MAX : INT_MIN;
}
static bool isMoreDesirable(const MLPriority &P1, const MLPriority &P2) {
return P1.Cost < P2.Cost;
}
private:
int Cost = INT_MAX;
};
template <typename PriorityT>
class PriorityInlineOrder : public InlineOrder<std::pair<CallBase *, int>> {
using T = std::pair<CallBase *, int>;
bool hasLowerPriority(const CallBase *L, const CallBase *R) const {
const auto I1 = Priorities.find(L);
const auto I2 = Priorities.find(R);
assert(I1 != Priorities.end() && I2 != Priorities.end());
return PriorityT::isMoreDesirable(I2->second, I1->second);
}
bool updateAndCheckDecreased(const CallBase *CB) {
auto It = Priorities.find(CB);
const auto OldPriority = It->second;
It->second = PriorityT(CB, FAM, Params);
const auto NewPriority = It->second;
return PriorityT::isMoreDesirable(OldPriority, NewPriority);
}
// A call site could become less desirable for inlining because of the size
// growth from prior inlining into the callee. This method is used to lazily
// update the desirability of a call site if it's decreasing. It is only
// called on pop(), not every time the desirability changes. When the
// desirability of the front call site decreases, an updated one would be
// pushed right back into the heap. For simplicity, those cases where the
// desirability of a call site increases are ignored here.
void pop_heap_adjust() {
std::pop_heap(Heap.begin(), Heap.end(), isLess);
while (updateAndCheckDecreased(Heap.back())) {
std::push_heap(Heap.begin(), Heap.end(), isLess);
std::pop_heap(Heap.begin(), Heap.end(), isLess);
}
}
public:
PriorityInlineOrder(FunctionAnalysisManager &FAM, const InlineParams &Params)
: FAM(FAM), Params(Params) {
isLess = [&](const CallBase *L, const CallBase *R) {
return hasLowerPriority(L, R);
};
}
size_t size() override { return Heap.size(); }
void push(const T &Elt) override {
CallBase *CB = Elt.first;
const int InlineHistoryID = Elt.second;
Heap.push_back(CB);
Priorities[CB] = PriorityT(CB, FAM, Params);
std::push_heap(Heap.begin(), Heap.end(), isLess);
InlineHistoryMap[CB] = InlineHistoryID;
}
T pop() override {
assert(size() > 0);
pop_heap_adjust();
CallBase *CB = Heap.pop_back_val();
T Result = std::make_pair(CB, InlineHistoryMap[CB]);
InlineHistoryMap.erase(CB);
return Result;
}
void erase_if(function_ref<bool(T)> Pred) override {
auto PredWrapper = [=](CallBase *CB) -> bool {
return Pred(std::make_pair(CB, InlineHistoryMap[CB]));
};
llvm::erase_if(Heap, PredWrapper);
std::make_heap(Heap.begin(), Heap.end(), isLess);
}
private:
SmallVector<CallBase *, 16> Heap;
std::function<bool(const CallBase *L, const CallBase *R)> isLess;
DenseMap<CallBase *, int> InlineHistoryMap;
DenseMap<const CallBase *, PriorityT> Priorities;
FunctionAnalysisManager &FAM;
const InlineParams &Params;
};
} // namespace
AnalysisKey llvm::PluginInlineOrderAnalysis::Key;
bool llvm::PluginInlineOrderAnalysis::HasBeenRegistered;
std::unique_ptr<InlineOrder<std::pair<CallBase *, int>>>
llvm::getDefaultInlineOrder(FunctionAnalysisManager &FAM,
const InlineParams &Params,
ModuleAnalysisManager &MAM, Module &M) {
switch (UseInlinePriority) {
case InlinePriorityMode::Size:
LLVM_DEBUG(dbgs() << " Current used priority: Size priority ---- \n");
return std::make_unique<PriorityInlineOrder<SizePriority>>(FAM, Params);
case InlinePriorityMode::Cost:
LLVM_DEBUG(dbgs() << " Current used priority: Cost priority ---- \n");
return std::make_unique<PriorityInlineOrder<CostPriority>>(FAM, Params);
case InlinePriorityMode::CostBenefit:
LLVM_DEBUG(
dbgs() << " Current used priority: cost-benefit priority ---- \n");
return std::make_unique<PriorityInlineOrder<CostBenefitPriority>>(FAM,
Params);
case InlinePriorityMode::ML:
LLVM_DEBUG(dbgs() << " Current used priority: ML priority ---- \n");
return std::make_unique<PriorityInlineOrder<MLPriority>>(FAM, Params);
}
return nullptr;
}
std::unique_ptr<InlineOrder<std::pair<CallBase *, int>>>
llvm::getInlineOrder(FunctionAnalysisManager &FAM, const InlineParams &Params,
ModuleAnalysisManager &MAM, Module &M) {
if (llvm::PluginInlineOrderAnalysis::isRegistered()) {
LLVM_DEBUG(dbgs() << " Current used priority: plugin ---- \n");
return MAM.getResult<PluginInlineOrderAnalysis>(M).Factory(FAM, Params, MAM,
M);
}
return getDefaultInlineOrder(FAM, Params, MAM, M);
}
|