1d52e775bSLiqiang Tao //===- InlineOrder.cpp - Inlining order abstraction -*- C++ ---*-----------===// 2d52e775bSLiqiang Tao // 3d52e775bSLiqiang Tao // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4d52e775bSLiqiang Tao // See https://llvm.org/LICENSE.txt for license information. 5d52e775bSLiqiang Tao // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6d52e775bSLiqiang Tao // 7d52e775bSLiqiang Tao //===----------------------------------------------------------------------===// 8d52e775bSLiqiang Tao 9d52e775bSLiqiang Tao #include "llvm/Analysis/InlineOrder.h" 10d52e775bSLiqiang Tao #include "llvm/Analysis/AssumptionCache.h" 11d52e775bSLiqiang Tao #include "llvm/Analysis/BlockFrequencyInfo.h" 12d52e775bSLiqiang Tao #include "llvm/Analysis/GlobalsModRef.h" 13d52e775bSLiqiang Tao #include "llvm/Analysis/InlineAdvisor.h" 14d52e775bSLiqiang Tao #include "llvm/Analysis/InlineCost.h" 15d52e775bSLiqiang Tao #include "llvm/Analysis/OptimizationRemarkEmitter.h" 16d52e775bSLiqiang Tao #include "llvm/Analysis/ProfileSummaryInfo.h" 17d52e775bSLiqiang Tao #include "llvm/Analysis/TargetLibraryInfo.h" 18d52e775bSLiqiang Tao #include "llvm/Analysis/TargetTransformInfo.h" 195faf4bf1SKazu Hirata #include "llvm/Support/CommandLine.h" 20d52e775bSLiqiang Tao 21d52e775bSLiqiang Tao using namespace llvm; 22d52e775bSLiqiang Tao 23d52e775bSLiqiang Tao #define DEBUG_TYPE "inline-order" 24d52e775bSLiqiang Tao 252d6ec146SKazu Hirata enum class InlinePriorityMode : int { Size, Cost, CostBenefit, ML }; 265faf4bf1SKazu Hirata 275faf4bf1SKazu Hirata static cl::opt<InlinePriorityMode> UseInlinePriority( 285faf4bf1SKazu Hirata "inline-priority-mode", cl::init(InlinePriorityMode::Size), cl::Hidden, 295faf4bf1SKazu Hirata cl::desc("Choose the priority mode to use in module inline"), 305faf4bf1SKazu Hirata cl::values(clEnumValN(InlinePriorityMode::Size, "size", 315faf4bf1SKazu Hirata "Use callee size priority."), 325faf4bf1SKazu Hirata clEnumValN(InlinePriorityMode::Cost, "cost", 334e9dd210SKazu Hirata "Use inline cost priority."), 344e9dd210SKazu Hirata clEnumValN(InlinePriorityMode::CostBenefit, "cost-benefit", 352d6ec146SKazu Hirata "Use cost-benefit ratio."), 3665f7ebe7Sibricchi clEnumValN(InlinePriorityMode::ML, "ml", "Use ML."))); 374e9dd210SKazu Hirata 384e9dd210SKazu Hirata static cl::opt<int> ModuleInlinerTopPriorityThreshold( 39d40fd9e1SSiu Chi Chan "module-inliner-top-priority-threshold", cl::Hidden, cl::init(0), 404e9dd210SKazu Hirata cl::desc("The cost threshold for call sites that get inlined without the " 414e9dd210SKazu Hirata "cost-benefit analysis")); 425faf4bf1SKazu Hirata 43e0bc76ebSKazu Hirata namespace { 44e0bc76ebSKazu Hirata 4500d98269SKazu Hirata llvm::InlineCost getInlineCostWrapper(CallBase &CB, 4600d98269SKazu Hirata FunctionAnalysisManager &FAM, 4700d98269SKazu Hirata const InlineParams &Params) { 4800d98269SKazu Hirata Function &Caller = *CB.getCaller(); 4900d98269SKazu Hirata ProfileSummaryInfo *PSI = 5000d98269SKazu Hirata FAM.getResult<ModuleAnalysisManagerFunctionProxy>(Caller) 5100d98269SKazu Hirata .getCachedResult<ProfileSummaryAnalysis>( 5200d98269SKazu Hirata *CB.getParent()->getParent()->getParent()); 5300d98269SKazu Hirata 5400d98269SKazu Hirata auto &ORE = FAM.getResult<OptimizationRemarkEmitterAnalysis>(Caller); 5500d98269SKazu Hirata auto GetAssumptionCache = [&](Function &F) -> AssumptionCache & { 5600d98269SKazu Hirata return FAM.getResult<AssumptionAnalysis>(F); 5700d98269SKazu Hirata }; 5800d98269SKazu Hirata auto GetBFI = [&](Function &F) -> BlockFrequencyInfo & { 5900d98269SKazu Hirata return FAM.getResult<BlockFrequencyAnalysis>(F); 6000d98269SKazu Hirata }; 6100d98269SKazu Hirata auto GetTLI = [&](Function &F) -> const TargetLibraryInfo & { 6200d98269SKazu Hirata return FAM.getResult<TargetLibraryAnalysis>(F); 6300d98269SKazu Hirata }; 6400d98269SKazu Hirata 6500d98269SKazu Hirata Function &Callee = *CB.getCalledFunction(); 6600d98269SKazu Hirata auto &CalleeTTI = FAM.getResult<TargetIRAnalysis>(Callee); 6700d98269SKazu Hirata bool RemarksEnabled = 6800d98269SKazu Hirata Callee.getContext().getDiagHandlerPtr()->isMissedOptRemarkEnabled( 6900d98269SKazu Hirata DEBUG_TYPE); 7000d98269SKazu Hirata return getInlineCost(CB, Params, CalleeTTI, GetAssumptionCache, GetTLI, 7100d98269SKazu Hirata GetBFI, PSI, RemarksEnabled ? &ORE : nullptr); 7200d98269SKazu Hirata } 7300d98269SKazu Hirata 740a0ccc85SKazu Hirata class SizePriority { 75e0bc76ebSKazu Hirata public: 760a0ccc85SKazu Hirata SizePriority() = default; 770a0ccc85SKazu Hirata SizePriority(const CallBase *CB, FunctionAnalysisManager &, 780a0ccc85SKazu Hirata const InlineParams &) { 79e0bc76ebSKazu Hirata Function *Callee = CB->getCalledFunction(); 800a0ccc85SKazu Hirata Size = Callee->getInstructionCount(); 81e0bc76ebSKazu Hirata } 82e0bc76ebSKazu Hirata 830a0ccc85SKazu Hirata static bool isMoreDesirable(const SizePriority &P1, const SizePriority &P2) { 840a0ccc85SKazu Hirata return P1.Size < P2.Size; 85e0bc76ebSKazu Hirata } 86e0bc76ebSKazu Hirata 870a0ccc85SKazu Hirata private: 88ba7cf9d1SKazu Hirata unsigned Size = UINT_MAX; 89e0bc76ebSKazu Hirata }; 90e0bc76ebSKazu Hirata 910a0ccc85SKazu Hirata class CostPriority { 920a0ccc85SKazu Hirata public: 930a0ccc85SKazu Hirata CostPriority() = default; 940a0ccc85SKazu Hirata CostPriority(const CallBase *CB, FunctionAnalysisManager &FAM, 950a0ccc85SKazu Hirata const InlineParams &Params) { 960a0ccc85SKazu Hirata auto IC = getInlineCostWrapper(const_cast<CallBase &>(*CB), FAM, Params); 97e0bc76ebSKazu Hirata if (IC.isVariable()) 9871b12030SKazu Hirata Cost = IC.getCost(); 99e0bc76ebSKazu Hirata else 10071b12030SKazu Hirata Cost = IC.isNever() ? INT_MAX : INT_MIN; 101e0bc76ebSKazu Hirata } 102e0bc76ebSKazu Hirata 1030a0ccc85SKazu Hirata static bool isMoreDesirable(const CostPriority &P1, const CostPriority &P2) { 1040a0ccc85SKazu Hirata return P1.Cost < P2.Cost; 105e0bc76ebSKazu Hirata } 106e0bc76ebSKazu Hirata 1070a0ccc85SKazu Hirata private: 108ba7cf9d1SKazu Hirata int Cost = INT_MAX; 1090a0ccc85SKazu Hirata }; 110e0bc76ebSKazu Hirata 1114e9dd210SKazu Hirata class CostBenefitPriority { 1124e9dd210SKazu Hirata public: 1134e9dd210SKazu Hirata CostBenefitPriority() = default; 1144e9dd210SKazu Hirata CostBenefitPriority(const CallBase *CB, FunctionAnalysisManager &FAM, 1154e9dd210SKazu Hirata const InlineParams &Params) { 1164e9dd210SKazu Hirata auto IC = getInlineCostWrapper(const_cast<CallBase &>(*CB), FAM, Params); 117d3127889SXiangyang (Mark) Guo if (IC.isVariable()) 1184e9dd210SKazu Hirata Cost = IC.getCost(); 119d3127889SXiangyang (Mark) Guo else 120d3127889SXiangyang (Mark) Guo Cost = IC.isNever() ? INT_MAX : INT_MIN; 1214e9dd210SKazu Hirata StaticBonusApplied = IC.getStaticBonusApplied(); 1224e9dd210SKazu Hirata CostBenefit = IC.getCostBenefit(); 1234e9dd210SKazu Hirata } 1244e9dd210SKazu Hirata 1254e9dd210SKazu Hirata static bool isMoreDesirable(const CostBenefitPriority &P1, 1264e9dd210SKazu Hirata const CostBenefitPriority &P2) { 1274e9dd210SKazu Hirata // We prioritize call sites in the dictionary order of the following 1284e9dd210SKazu Hirata // priorities: 1294e9dd210SKazu Hirata // 1304e9dd210SKazu Hirata // 1. Those call sites that are expected to reduce the caller size when 1314e9dd210SKazu Hirata // inlined. Within them, we prioritize those call sites with bigger 1324e9dd210SKazu Hirata // reduction. 1334e9dd210SKazu Hirata // 1344e9dd210SKazu Hirata // 2. Those call sites that have gone through the cost-benefit analysis. 1354e9dd210SKazu Hirata // Currently, they are limited to hot call sites. Within them, we 1364e9dd210SKazu Hirata // prioritize those call sites with higher benefit-to-cost ratios. 1374e9dd210SKazu Hirata // 1384e9dd210SKazu Hirata // 3. Remaining call sites are prioritized according to their costs. 1394e9dd210SKazu Hirata 1404e9dd210SKazu Hirata // We add back StaticBonusApplied to determine whether we expect the caller 1414e9dd210SKazu Hirata // to shrink (even if we don't delete the callee). 1424e9dd210SKazu Hirata bool P1ReducesCallerSize = 1434e9dd210SKazu Hirata P1.Cost + P1.StaticBonusApplied < ModuleInlinerTopPriorityThreshold; 1444e9dd210SKazu Hirata bool P2ReducesCallerSize = 1454e9dd210SKazu Hirata P2.Cost + P2.StaticBonusApplied < ModuleInlinerTopPriorityThreshold; 1464e9dd210SKazu Hirata if (P1ReducesCallerSize || P2ReducesCallerSize) { 1474e9dd210SKazu Hirata // If one reduces the caller size while the other doesn't, then return 1484e9dd210SKazu Hirata // true iff P1 reduces the caller size. 1494e9dd210SKazu Hirata if (P1ReducesCallerSize != P2ReducesCallerSize) 1504e9dd210SKazu Hirata return P1ReducesCallerSize; 1514e9dd210SKazu Hirata 1524e9dd210SKazu Hirata // If they both reduce the caller size, pick the one with the smaller 1534e9dd210SKazu Hirata // cost. 1544e9dd210SKazu Hirata return P1.Cost < P2.Cost; 1554e9dd210SKazu Hirata } 1564e9dd210SKazu Hirata 1574e9dd210SKazu Hirata bool P1HasCB = P1.CostBenefit.has_value(); 1584e9dd210SKazu Hirata bool P2HasCB = P2.CostBenefit.has_value(); 1594e9dd210SKazu Hirata if (P1HasCB || P2HasCB) { 1604e9dd210SKazu Hirata // If one has undergone the cost-benefit analysis while the other hasn't, 1614e9dd210SKazu Hirata // then return true iff P1 has. 1624e9dd210SKazu Hirata if (P1HasCB != P2HasCB) 1634e9dd210SKazu Hirata return P1HasCB; 1644e9dd210SKazu Hirata 1654e9dd210SKazu Hirata // If they have undergone the cost-benefit analysis, then pick the one 1664e9dd210SKazu Hirata // with a higher benefit-to-cost ratio. 1674e9dd210SKazu Hirata APInt LHS = P1.CostBenefit->getBenefit() * P2.CostBenefit->getCost(); 1684e9dd210SKazu Hirata APInt RHS = P2.CostBenefit->getBenefit() * P1.CostBenefit->getCost(); 1694e9dd210SKazu Hirata return LHS.ugt(RHS); 1704e9dd210SKazu Hirata } 1714e9dd210SKazu Hirata 1724e9dd210SKazu Hirata // Remaining call sites are ordered according to their costs. 1734e9dd210SKazu Hirata return P1.Cost < P2.Cost; 1744e9dd210SKazu Hirata } 1754e9dd210SKazu Hirata 1764e9dd210SKazu Hirata private: 177ba7cf9d1SKazu Hirata int Cost = INT_MAX; 178ba7cf9d1SKazu Hirata int StaticBonusApplied = 0; 179d4b6fcb3SFangrui Song std::optional<CostBenefitPair> CostBenefit; 1804e9dd210SKazu Hirata }; 1814e9dd210SKazu Hirata 1822d6ec146SKazu Hirata class MLPriority { 1832d6ec146SKazu Hirata public: 1842d6ec146SKazu Hirata MLPriority() = default; 1852d6ec146SKazu Hirata MLPriority(const CallBase *CB, FunctionAnalysisManager &FAM, 1862d6ec146SKazu Hirata const InlineParams &Params) { 1872d6ec146SKazu Hirata auto IC = getInlineCostWrapper(const_cast<CallBase &>(*CB), FAM, Params); 1882d6ec146SKazu Hirata if (IC.isVariable()) 1892d6ec146SKazu Hirata Cost = IC.getCost(); 1902d6ec146SKazu Hirata else 1912d6ec146SKazu Hirata Cost = IC.isNever() ? INT_MAX : INT_MIN; 1922d6ec146SKazu Hirata } 1932d6ec146SKazu Hirata 1942d6ec146SKazu Hirata static bool isMoreDesirable(const MLPriority &P1, const MLPriority &P2) { 1952d6ec146SKazu Hirata return P1.Cost < P2.Cost; 1962d6ec146SKazu Hirata } 1972d6ec146SKazu Hirata 1982d6ec146SKazu Hirata private: 1992d6ec146SKazu Hirata int Cost = INT_MAX; 2002d6ec146SKazu Hirata }; 2012d6ec146SKazu Hirata 2020a0ccc85SKazu Hirata template <typename PriorityT> 2030a0ccc85SKazu Hirata class PriorityInlineOrder : public InlineOrder<std::pair<CallBase *, int>> { 2040a0ccc85SKazu Hirata using T = std::pair<CallBase *, int>; 2050a0ccc85SKazu Hirata 2060a0ccc85SKazu Hirata bool hasLowerPriority(const CallBase *L, const CallBase *R) const { 207e0bc76ebSKazu Hirata const auto I1 = Priorities.find(L); 208e0bc76ebSKazu Hirata const auto I2 = Priorities.find(R); 209e0bc76ebSKazu Hirata assert(I1 != Priorities.end() && I2 != Priorities.end()); 2100a0ccc85SKazu Hirata return PriorityT::isMoreDesirable(I2->second, I1->second); 211e0bc76ebSKazu Hirata } 212e0bc76ebSKazu Hirata 2130a0ccc85SKazu Hirata bool updateAndCheckDecreased(const CallBase *CB) { 214e0bc76ebSKazu Hirata auto It = Priorities.find(CB); 215e0bc76ebSKazu Hirata const auto OldPriority = It->second; 2160a0ccc85SKazu Hirata It->second = PriorityT(CB, FAM, Params); 217e0bc76ebSKazu Hirata const auto NewPriority = It->second; 2180a0ccc85SKazu Hirata return PriorityT::isMoreDesirable(OldPriority, NewPriority); 219e0bc76ebSKazu Hirata } 220e0bc76ebSKazu Hirata 221e0bc76ebSKazu Hirata // A call site could become less desirable for inlining because of the size 222e0bc76ebSKazu Hirata // growth from prior inlining into the callee. This method is used to lazily 223e0bc76ebSKazu Hirata // update the desirability of a call site if it's decreasing. It is only 22481d651e7SKazu Hirata // called on pop(), not every time the desirability changes. When the 22581d651e7SKazu Hirata // desirability of the front call site decreases, an updated one would be 22681d651e7SKazu Hirata // pushed right back into the heap. For simplicity, those cases where the 22781d651e7SKazu Hirata // desirability of a call site increases are ignored here. 228f65cd04aSKazu Hirata void pop_heap_adjust() { 229e0bc76ebSKazu Hirata std::pop_heap(Heap.begin(), Heap.end(), isLess); 23033430205SKazu Hirata while (updateAndCheckDecreased(Heap.back())) { 231e0bc76ebSKazu Hirata std::push_heap(Heap.begin(), Heap.end(), isLess); 23233430205SKazu Hirata std::pop_heap(Heap.begin(), Heap.end(), isLess); 233e0bc76ebSKazu Hirata } 234e0bc76ebSKazu Hirata } 235e0bc76ebSKazu Hirata 236e0bc76ebSKazu Hirata public: 2370a0ccc85SKazu Hirata PriorityInlineOrder(FunctionAnalysisManager &FAM, const InlineParams &Params) 2380a0ccc85SKazu Hirata : FAM(FAM), Params(Params) { 2390a0ccc85SKazu Hirata isLess = [&](const CallBase *L, const CallBase *R) { 2400a0ccc85SKazu Hirata return hasLowerPriority(L, R); 241e0bc76ebSKazu Hirata }; 242e0bc76ebSKazu Hirata } 243e0bc76ebSKazu Hirata 244e0bc76ebSKazu Hirata size_t size() override { return Heap.size(); } 245e0bc76ebSKazu Hirata 246e0bc76ebSKazu Hirata void push(const T &Elt) override { 247e0bc76ebSKazu Hirata CallBase *CB = Elt.first; 248e0bc76ebSKazu Hirata const int InlineHistoryID = Elt.second; 249e0bc76ebSKazu Hirata 250e0bc76ebSKazu Hirata Heap.push_back(CB); 2510a0ccc85SKazu Hirata Priorities[CB] = PriorityT(CB, FAM, Params); 252e0bc76ebSKazu Hirata std::push_heap(Heap.begin(), Heap.end(), isLess); 253e0bc76ebSKazu Hirata InlineHistoryMap[CB] = InlineHistoryID; 254e0bc76ebSKazu Hirata } 255e0bc76ebSKazu Hirata 256e0bc76ebSKazu Hirata T pop() override { 257e0bc76ebSKazu Hirata assert(size() > 0); 258f65cd04aSKazu Hirata pop_heap_adjust(); 259e0bc76ebSKazu Hirata 2603fb3df36SKazu Hirata CallBase *CB = Heap.pop_back_val(); 261e0bc76ebSKazu Hirata T Result = std::make_pair(CB, InlineHistoryMap[CB]); 262e0bc76ebSKazu Hirata InlineHistoryMap.erase(CB); 263e0bc76ebSKazu Hirata return Result; 264e0bc76ebSKazu Hirata } 265e0bc76ebSKazu Hirata 266e0bc76ebSKazu Hirata void erase_if(function_ref<bool(T)> Pred) override { 267e0bc76ebSKazu Hirata auto PredWrapper = [=](CallBase *CB) -> bool { 26806ca52e2SVincent Lee return Pred(std::make_pair(CB, InlineHistoryMap[CB])); 269e0bc76ebSKazu Hirata }; 270e0bc76ebSKazu Hirata llvm::erase_if(Heap, PredWrapper); 271e0bc76ebSKazu Hirata std::make_heap(Heap.begin(), Heap.end(), isLess); 272e0bc76ebSKazu Hirata } 273e0bc76ebSKazu Hirata 274e0bc76ebSKazu Hirata private: 275e0bc76ebSKazu Hirata SmallVector<CallBase *, 16> Heap; 276e0bc76ebSKazu Hirata std::function<bool(const CallBase *L, const CallBase *R)> isLess; 277e0bc76ebSKazu Hirata DenseMap<CallBase *, int> InlineHistoryMap; 2780a0ccc85SKazu Hirata DenseMap<const CallBase *, PriorityT> Priorities; 2790a0ccc85SKazu Hirata FunctionAnalysisManager &FAM; 2800a0ccc85SKazu Hirata const InlineParams &Params; 281e0bc76ebSKazu Hirata }; 282e0bc76ebSKazu Hirata 283e0bc76ebSKazu Hirata } // namespace 284e0bc76ebSKazu Hirata 28565f7ebe7Sibricchi AnalysisKey llvm::PluginInlineOrderAnalysis::Key; 28665f7ebe7Sibricchi 287d52e775bSLiqiang Tao std::unique_ptr<InlineOrder<std::pair<CallBase *, int>>> 28865f7ebe7Sibricchi llvm::getDefaultInlineOrder(FunctionAnalysisManager &FAM, 28965f7ebe7Sibricchi const InlineParams &Params, 29065f7ebe7Sibricchi ModuleAnalysisManager &MAM, Module &M) { 291d52e775bSLiqiang Tao switch (UseInlinePriority) { 292d52e775bSLiqiang Tao case InlinePriorityMode::Size: 293d52e775bSLiqiang Tao LLVM_DEBUG(dbgs() << " Current used priority: Size priority ---- \n"); 2940a0ccc85SKazu Hirata return std::make_unique<PriorityInlineOrder<SizePriority>>(FAM, Params); 295d52e775bSLiqiang Tao 296d52e775bSLiqiang Tao case InlinePriorityMode::Cost: 297d52e775bSLiqiang Tao LLVM_DEBUG(dbgs() << " Current used priority: Cost priority ---- \n"); 2980a0ccc85SKazu Hirata return std::make_unique<PriorityInlineOrder<CostPriority>>(FAM, Params); 299d52e775bSLiqiang Tao 3004e9dd210SKazu Hirata case InlinePriorityMode::CostBenefit: 3014e9dd210SKazu Hirata LLVM_DEBUG( 3024e9dd210SKazu Hirata dbgs() << " Current used priority: cost-benefit priority ---- \n"); 30365f7ebe7Sibricchi return std::make_unique<PriorityInlineOrder<CostBenefitPriority>>(FAM, 30465f7ebe7Sibricchi Params); 3052d6ec146SKazu Hirata case InlinePriorityMode::ML: 30665f7ebe7Sibricchi LLVM_DEBUG(dbgs() << " Current used priority: ML priority ---- \n"); 3072d6ec146SKazu Hirata return std::make_unique<PriorityInlineOrder<MLPriority>>(FAM, Params); 308d52e775bSLiqiang Tao } 309d52e775bSLiqiang Tao return nullptr; 310d52e775bSLiqiang Tao } 31165f7ebe7Sibricchi 31265f7ebe7Sibricchi std::unique_ptr<InlineOrder<std::pair<CallBase *, int>>> 31365f7ebe7Sibricchi llvm::getInlineOrder(FunctionAnalysisManager &FAM, const InlineParams &Params, 31465f7ebe7Sibricchi ModuleAnalysisManager &MAM, Module &M) { 315*ab4253f6SMichele Scandale if (MAM.isPassRegistered<PluginInlineOrderAnalysis>()) { 31665f7ebe7Sibricchi LLVM_DEBUG(dbgs() << " Current used priority: plugin ---- \n"); 31765f7ebe7Sibricchi return MAM.getResult<PluginInlineOrderAnalysis>(M).Factory(FAM, Params, MAM, 31865f7ebe7Sibricchi M); 31965f7ebe7Sibricchi } 32065f7ebe7Sibricchi return getDefaultInlineOrder(FAM, Params, MAM, M); 32165f7ebe7Sibricchi } 322