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