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 21 using namespace llvm; 22 23 #define DEBUG_TYPE "inline-order" 24 25 enum class InlinePriorityMode : int { Size, Cost, OptRatio }; 26 27 static cl::opt<InlinePriorityMode> UseInlinePriority( 28 "inline-priority-mode", cl::init(InlinePriorityMode::Size), cl::Hidden, 29 cl::desc("Choose the priority mode to use in module inline"), 30 cl::values(clEnumValN(InlinePriorityMode::Size, "size", 31 "Use callee size priority."), 32 clEnumValN(InlinePriorityMode::Cost, "cost", 33 "Use inline cost priority."))); 34 35 namespace { 36 37 llvm::InlineCost getInlineCostWrapper(CallBase &CB, 38 FunctionAnalysisManager &FAM, 39 const InlineParams &Params) { 40 Function &Caller = *CB.getCaller(); 41 ProfileSummaryInfo *PSI = 42 FAM.getResult<ModuleAnalysisManagerFunctionProxy>(Caller) 43 .getCachedResult<ProfileSummaryAnalysis>( 44 *CB.getParent()->getParent()->getParent()); 45 46 auto &ORE = FAM.getResult<OptimizationRemarkEmitterAnalysis>(Caller); 47 auto GetAssumptionCache = [&](Function &F) -> AssumptionCache & { 48 return FAM.getResult<AssumptionAnalysis>(F); 49 }; 50 auto GetBFI = [&](Function &F) -> BlockFrequencyInfo & { 51 return FAM.getResult<BlockFrequencyAnalysis>(F); 52 }; 53 auto GetTLI = [&](Function &F) -> const TargetLibraryInfo & { 54 return FAM.getResult<TargetLibraryAnalysis>(F); 55 }; 56 57 Function &Callee = *CB.getCalledFunction(); 58 auto &CalleeTTI = FAM.getResult<TargetIRAnalysis>(Callee); 59 bool RemarksEnabled = 60 Callee.getContext().getDiagHandlerPtr()->isMissedOptRemarkEnabled( 61 DEBUG_TYPE); 62 return getInlineCost(CB, Params, CalleeTTI, GetAssumptionCache, GetTLI, 63 GetBFI, PSI, RemarksEnabled ? &ORE : nullptr); 64 } 65 66 class InlinePriority { 67 public: 68 virtual ~InlinePriority() = default; 69 virtual bool hasLowerPriority(const CallBase *L, const CallBase *R) const = 0; 70 virtual void update(const CallBase *CB) = 0; 71 virtual bool updateAndCheckDecreased(const CallBase *CB) = 0; 72 }; 73 74 class SizePriority : public InlinePriority { 75 using PriorityT = unsigned; 76 DenseMap<const CallBase *, PriorityT> Priorities; 77 78 PriorityT evaluate(const CallBase *CB) { 79 Function *Callee = CB->getCalledFunction(); 80 return Callee->getInstructionCount(); 81 } 82 83 bool isMoreDesirable(const PriorityT &P1, const PriorityT &P2) const { 84 return P1 < P2; 85 } 86 87 public: 88 bool hasLowerPriority(const CallBase *L, const CallBase *R) const override { 89 const auto I1 = Priorities.find(L); 90 const auto I2 = Priorities.find(R); 91 assert(I1 != Priorities.end() && I2 != Priorities.end()); 92 return isMoreDesirable(I2->second, I1->second); 93 } 94 95 // Update the priority associated with CB. 96 void update(const CallBase *CB) override { Priorities[CB] = evaluate(CB); }; 97 98 bool updateAndCheckDecreased(const CallBase *CB) override { 99 auto It = Priorities.find(CB); 100 const auto OldPriority = It->second; 101 It->second = evaluate(CB); 102 const auto NewPriority = It->second; 103 return isMoreDesirable(OldPriority, NewPriority); 104 } 105 }; 106 107 class CostPriority : public InlinePriority { 108 using PriorityT = int; 109 DenseMap<const CallBase *, PriorityT> Priorities; 110 std::function<InlineCost(const CallBase *)> getInlineCost; 111 112 PriorityT evaluate(const CallBase *CB) { 113 auto IC = getInlineCost(CB); 114 int cost = 0; 115 if (IC.isVariable()) 116 cost = IC.getCost(); 117 else 118 cost = IC.isNever() ? INT_MAX : INT_MIN; 119 return cost; 120 } 121 122 bool isMoreDesirable(const PriorityT &P1, const PriorityT &P2) const { 123 return P1 < P2; 124 } 125 126 public: 127 CostPriority() = delete; 128 CostPriority(std::function<InlineCost(const CallBase *)> getInlineCost) 129 : getInlineCost(getInlineCost){}; 130 131 bool hasLowerPriority(const CallBase *L, const CallBase *R) const override { 132 const auto I1 = Priorities.find(L); 133 const auto I2 = Priorities.find(R); 134 assert(I1 != Priorities.end() && I2 != Priorities.end()); 135 return isMoreDesirable(I2->second, I1->second); 136 } 137 138 // Update the priority associated with CB. 139 void update(const CallBase *CB) override { Priorities[CB] = evaluate(CB); }; 140 141 bool updateAndCheckDecreased(const CallBase *CB) override { 142 auto It = Priorities.find(CB); 143 const auto OldPriority = It->second; 144 It->second = evaluate(CB); 145 const auto NewPriority = It->second; 146 return isMoreDesirable(OldPriority, NewPriority); 147 } 148 }; 149 150 class PriorityInlineOrder : public InlineOrder<std::pair<CallBase *, int>> { 151 using T = std::pair<CallBase *, int>; 152 using reference = T &; 153 using const_reference = const T &; 154 155 // A call site could become less desirable for inlining because of the size 156 // growth from prior inlining into the callee. This method is used to lazily 157 // update the desirability of a call site if it's decreasing. It is only 158 // called on pop() or front(), not every time the desirability changes. When 159 // the desirability of the front call site decreases, an updated one would be 160 // pushed right back into the heap. For simplicity, those cases where 161 // the desirability of a call site increases are ignored here. 162 void adjust() { 163 while (PriorityPtr->updateAndCheckDecreased(Heap.front())) { 164 std::pop_heap(Heap.begin(), Heap.end(), isLess); 165 std::push_heap(Heap.begin(), Heap.end(), isLess); 166 } 167 } 168 169 public: 170 PriorityInlineOrder(std::unique_ptr<InlinePriority> PriorityPtr) 171 : PriorityPtr(std::move(PriorityPtr)) { 172 isLess = [this](const CallBase *L, const CallBase *R) { 173 return this->PriorityPtr->hasLowerPriority(L, R); 174 }; 175 } 176 177 size_t size() override { return Heap.size(); } 178 179 void push(const T &Elt) override { 180 CallBase *CB = Elt.first; 181 const int InlineHistoryID = Elt.second; 182 183 Heap.push_back(CB); 184 PriorityPtr->update(CB); 185 std::push_heap(Heap.begin(), Heap.end(), isLess); 186 InlineHistoryMap[CB] = InlineHistoryID; 187 } 188 189 T pop() override { 190 assert(size() > 0); 191 adjust(); 192 193 CallBase *CB = Heap.front(); 194 T Result = std::make_pair(CB, InlineHistoryMap[CB]); 195 InlineHistoryMap.erase(CB); 196 std::pop_heap(Heap.begin(), Heap.end(), isLess); 197 Heap.pop_back(); 198 return Result; 199 } 200 201 void erase_if(function_ref<bool(T)> Pred) override { 202 auto PredWrapper = [=](CallBase *CB) -> bool { 203 return Pred(std::make_pair(CB, 0)); 204 }; 205 llvm::erase_if(Heap, PredWrapper); 206 std::make_heap(Heap.begin(), Heap.end(), isLess); 207 } 208 209 private: 210 SmallVector<CallBase *, 16> Heap; 211 std::function<bool(const CallBase *L, const CallBase *R)> isLess; 212 DenseMap<CallBase *, int> InlineHistoryMap; 213 std::unique_ptr<InlinePriority> PriorityPtr; 214 }; 215 216 } // namespace 217 218 std::unique_ptr<InlineOrder<std::pair<CallBase *, int>>> 219 llvm::getInlineOrder(FunctionAnalysisManager &FAM, const InlineParams &Params) { 220 switch (UseInlinePriority) { 221 case InlinePriorityMode::Size: 222 LLVM_DEBUG(dbgs() << " Current used priority: Size priority ---- \n"); 223 return std::make_unique<PriorityInlineOrder>( 224 std::make_unique<SizePriority>()); 225 226 case InlinePriorityMode::Cost: 227 LLVM_DEBUG(dbgs() << " Current used priority: Cost priority ---- \n"); 228 return std::make_unique<PriorityInlineOrder>( 229 std::make_unique<CostPriority>([&](const CallBase *CB) -> InlineCost { 230 return getInlineCostWrapper(const_cast<CallBase &>(*CB), FAM, Params); 231 })); 232 233 default: 234 llvm_unreachable("Unsupported Inline Priority Mode"); 235 break; 236 } 237 return nullptr; 238 } 239