1 //===- MLInlineAdvisor.cpp - machine learned InlineAdvisor ----------------===//
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 // This file implements the interface between the inliner and a learned model.
10 // It delegates model evaluation to either the AOT compiled model (the
11 // 'release' mode) or a runtime-loaded model (the 'development' case).
12 //
13 //===----------------------------------------------------------------------===//
14 #include "llvm/Analysis/MLInlineAdvisor.h"
15 #include "llvm/ADT/SCCIterator.h"
16 #include "llvm/Analysis/AssumptionCache.h"
17 #include "llvm/Analysis/BlockFrequencyInfo.h"
18 #include "llvm/Analysis/CallGraph.h"
19 #include "llvm/Analysis/FunctionPropertiesAnalysis.h"
20 #include "llvm/Analysis/InlineCost.h"
21 #include "llvm/Analysis/InlineModelFeatureMaps.h"
22 #include "llvm/Analysis/InteractiveModelRunner.h"
23 #include "llvm/Analysis/LazyCallGraph.h"
24 #include "llvm/Analysis/LoopInfo.h"
25 #include "llvm/Analysis/MLModelRunner.h"
26 #include "llvm/Analysis/OptimizationRemarkEmitter.h"
27 #include "llvm/Analysis/ProfileSummaryInfo.h"
28 #include "llvm/Analysis/ReleaseModeModelRunner.h"
29 #include "llvm/Analysis/TargetTransformInfo.h"
30 #include "llvm/IR/Dominators.h"
31 #include "llvm/IR/InstIterator.h"
32 #include "llvm/IR/Module.h"
33 #include "llvm/IR/PassManager.h"
34 #include "llvm/Support/CommandLine.h"
35
36 using namespace llvm;
37
38 static cl::opt<std::string> InteractiveChannelBaseName(
39 "inliner-interactive-channel-base", cl::Hidden,
40 cl::desc(
41 "Base file path for the interactive mode. The incoming filename should "
42 "have the name <inliner-interactive-channel-base>.in, while the "
43 "outgoing name should be <inliner-interactive-channel-base>.out"));
44 static const std::string InclDefaultMsg =
45 (Twine("In interactive mode, also send the default policy decision: ") +
46 DefaultDecisionName + ".")
47 .str();
48 static cl::opt<bool>
49 InteractiveIncludeDefault("inliner-interactive-include-default", cl::Hidden,
50 cl::desc(InclDefaultMsg));
51
52 enum class SkipMLPolicyCriteria { Never, IfCallerIsNotCold };
53
54 static cl::opt<SkipMLPolicyCriteria> SkipPolicy(
55 "ml-inliner-skip-policy", cl::Hidden, cl::init(SkipMLPolicyCriteria::Never),
56 cl::values(clEnumValN(SkipMLPolicyCriteria::Never, "never", "never"),
57 clEnumValN(SkipMLPolicyCriteria::IfCallerIsNotCold,
58 "if-caller-not-cold", "if the caller is not cold")));
59
60 static cl::opt<std::string> ModelSelector("ml-inliner-model-selector",
61 cl::Hidden, cl::init(""));
62
63 #if defined(LLVM_HAVE_TF_AOT_INLINERSIZEMODEL)
64 // codegen-ed file
65 #include "InlinerSizeModel.h" // NOLINT
66 using CompiledModelType = llvm::InlinerSizeModel;
67 #else
68 using CompiledModelType = NoopSavedModelImpl;
69 #endif
70
71 std::unique_ptr<InlineAdvisor>
getReleaseModeAdvisor(Module & M,ModuleAnalysisManager & MAM,std::function<bool (CallBase &)> GetDefaultAdvice)72 llvm::getReleaseModeAdvisor(Module &M, ModuleAnalysisManager &MAM,
73 std::function<bool(CallBase &)> GetDefaultAdvice) {
74 if (!llvm::isEmbeddedModelEvaluatorValid<CompiledModelType>() &&
75 InteractiveChannelBaseName.empty())
76 return nullptr;
77 std::unique_ptr<MLModelRunner> AOTRunner;
78 if (InteractiveChannelBaseName.empty())
79 AOTRunner = std::make_unique<ReleaseModeModelRunner<CompiledModelType>>(
80 M.getContext(), FeatureMap, DecisionName,
81 EmbeddedModelRunnerOptions().setModelSelector(ModelSelector));
82 else {
83 auto Features = FeatureMap;
84 if (InteractiveIncludeDefault)
85 Features.push_back(DefaultDecisionSpec);
86 AOTRunner = std::make_unique<InteractiveModelRunner>(
87 M.getContext(), Features, InlineDecisionSpec,
88 InteractiveChannelBaseName + ".out",
89 InteractiveChannelBaseName + ".in");
90 }
91 return std::make_unique<MLInlineAdvisor>(M, MAM, std::move(AOTRunner),
92 GetDefaultAdvice);
93 }
94
95 #define DEBUG_TYPE "inline-ml"
96
97 static cl::opt<float> SizeIncreaseThreshold(
98 "ml-advisor-size-increase-threshold", cl::Hidden,
99 cl::desc("Maximum factor by which expected native size may increase before "
100 "blocking any further inlining."),
101 cl::init(2.0));
102
103 static cl::opt<bool> KeepFPICache(
104 "ml-advisor-keep-fpi-cache", cl::Hidden,
105 cl::desc(
106 "For test - keep the ML Inline advisor's FunctionPropertiesInfo cache"),
107 cl::init(false));
108
109 // clang-format off
110 const std::vector<TensorSpec> llvm::FeatureMap{
111 #define POPULATE_NAMES(DTYPE, SHAPE, NAME, __) TensorSpec::createSpec<DTYPE>(#NAME, SHAPE),
112 // InlineCost features - these must come first
113 INLINE_COST_FEATURE_ITERATOR(POPULATE_NAMES)
114
115 // Non-cost features
116 INLINE_FEATURE_ITERATOR(POPULATE_NAMES)
117 #undef POPULATE_NAMES
118 };
119 // clang-format on
120
121 const char *const llvm::DecisionName = "inlining_decision";
122 const TensorSpec llvm::InlineDecisionSpec =
123 TensorSpec::createSpec<int64_t>(DecisionName, {1});
124 const char *const llvm::DefaultDecisionName = "inlining_default";
125 const TensorSpec llvm::DefaultDecisionSpec =
126 TensorSpec::createSpec<int64_t>(DefaultDecisionName, {1});
127 const char *const llvm::RewardName = "delta_size";
128
getInlinableCS(Instruction & I)129 CallBase *getInlinableCS(Instruction &I) {
130 if (auto *CS = dyn_cast<CallBase>(&I))
131 if (Function *Callee = CS->getCalledFunction()) {
132 if (!Callee->isDeclaration()) {
133 return CS;
134 }
135 }
136 return nullptr;
137 }
138
MLInlineAdvisor(Module & M,ModuleAnalysisManager & MAM,std::unique_ptr<MLModelRunner> Runner,std::function<bool (CallBase &)> GetDefaultAdvice)139 MLInlineAdvisor::MLInlineAdvisor(
140 Module &M, ModuleAnalysisManager &MAM,
141 std::unique_ptr<MLModelRunner> Runner,
142 std::function<bool(CallBase &)> GetDefaultAdvice)
143 : InlineAdvisor(
144 M, MAM.getResult<FunctionAnalysisManagerModuleProxy>(M).getManager()),
145 ModelRunner(std::move(Runner)), GetDefaultAdvice(GetDefaultAdvice),
146 CG(MAM.getResult<LazyCallGraphAnalysis>(M)),
147 InitialIRSize(getModuleIRSize()), CurrentIRSize(InitialIRSize),
148 PSI(MAM.getResult<ProfileSummaryAnalysis>(M)) {
149 assert(ModelRunner);
150 ModelRunner->switchContext("");
151 // Extract the 'call site height' feature - the position of a call site
152 // relative to the farthest statically reachable SCC node. We don't mutate
153 // this value while inlining happens. Empirically, this feature proved
154 // critical in behavioral cloning - i.e. training a model to mimic the manual
155 // heuristic's decisions - and, thus, equally important for training for
156 // improvement.
157 CallGraph CGraph(M);
158 for (auto I = scc_begin(&CGraph); !I.isAtEnd(); ++I) {
159 const std::vector<CallGraphNode *> &CGNodes = *I;
160 unsigned Level = 0;
161 for (auto *CGNode : CGNodes) {
162 Function *F = CGNode->getFunction();
163 if (!F || F->isDeclaration())
164 continue;
165 for (auto &I : instructions(F)) {
166 if (auto *CS = getInlinableCS(I)) {
167 auto *Called = CS->getCalledFunction();
168 auto Pos = FunctionLevels.find(&CG.get(*Called));
169 // In bottom up traversal, an inlinable callee is either in the
170 // same SCC, or to a function in a visited SCC. So not finding its
171 // level means we haven't visited it yet, meaning it's in this SCC.
172 if (Pos == FunctionLevels.end())
173 continue;
174 Level = std::max(Level, Pos->second + 1);
175 }
176 }
177 }
178 for (auto *CGNode : CGNodes) {
179 Function *F = CGNode->getFunction();
180 if (F && !F->isDeclaration())
181 FunctionLevels[&CG.get(*F)] = Level;
182 }
183 }
184 for (auto KVP : FunctionLevels) {
185 AllNodes.insert(KVP.first);
186 EdgeCount += getLocalCalls(KVP.first->getFunction());
187 }
188 NodeCount = AllNodes.size();
189 }
190
getInitialFunctionLevel(const Function & F) const191 unsigned MLInlineAdvisor::getInitialFunctionLevel(const Function &F) const {
192 return CG.lookup(F) ? FunctionLevels.at(CG.lookup(F)) : 0;
193 }
194
onPassEntry(LazyCallGraph::SCC * CurSCC)195 void MLInlineAdvisor::onPassEntry(LazyCallGraph::SCC *CurSCC) {
196 if (!CurSCC || ForceStop)
197 return;
198 FPICache.clear();
199 // Function passes executed between InlinerPass runs may have changed the
200 // module-wide features.
201 // The cgscc pass manager rules are such that:
202 // - if a pass leads to merging SCCs, then the pipeline is restarted on the
203 // merged SCC
204 // - if a pass leads to splitting the SCC, then we continue with one of the
205 // splits
206 // This means that the NodesInLastSCC is a superset (not strict) of the nodes
207 // that subsequent passes would have processed
208 // - in addition, if new Nodes were created by a pass (e.g. CoroSplit),
209 // they'd be adjacent to Nodes in the last SCC. So we just need to check the
210 // boundary of Nodes in NodesInLastSCC for Nodes we haven't seen. We don't
211 // care about the nature of the Edge (call or ref). `FunctionLevels`-wise, we
212 // record them at the same level as the original node (this is a choice, may
213 // need revisiting).
214 // - nodes are only deleted at the end of a call graph walk where they are
215 // batch deleted, so we shouldn't see any dead nodes here.
216 while (!NodesInLastSCC.empty()) {
217 const auto *N = *NodesInLastSCC.begin();
218 assert(!N->isDead());
219 NodesInLastSCC.erase(N);
220 EdgeCount += getLocalCalls(N->getFunction());
221 const auto NLevel = FunctionLevels.at(N);
222 for (const auto &E : *(*N)) {
223 const auto *AdjNode = &E.getNode();
224 assert(!AdjNode->isDead() && !AdjNode->getFunction().isDeclaration());
225 auto I = AllNodes.insert(AdjNode);
226 // We've discovered a new function.
227 if (I.second) {
228 ++NodeCount;
229 NodesInLastSCC.insert(AdjNode);
230 FunctionLevels[AdjNode] = NLevel;
231 }
232 }
233 }
234
235 EdgeCount -= EdgesOfLastSeenNodes;
236 EdgesOfLastSeenNodes = 0;
237
238 // (Re)use NodesInLastSCC to remember the nodes in the SCC right now,
239 // in case the SCC is split before onPassExit and some nodes are split out
240 assert(NodesInLastSCC.empty());
241 for (const auto &N : *CurSCC)
242 NodesInLastSCC.insert(&N);
243 }
244
onPassExit(LazyCallGraph::SCC * CurSCC)245 void MLInlineAdvisor::onPassExit(LazyCallGraph::SCC *CurSCC) {
246 // No need to keep this around - function passes will invalidate it.
247 if (!KeepFPICache)
248 FPICache.clear();
249 if (!CurSCC || ForceStop)
250 return;
251 // Keep track of the nodes and edges we last saw. Then, in onPassEntry,
252 // we update the node count and edge count from the subset of these nodes that
253 // survived.
254 EdgesOfLastSeenNodes = 0;
255
256 // Check on nodes that were in SCC onPassEntry
257 for (const LazyCallGraph::Node *N : NodesInLastSCC) {
258 assert(!N->isDead());
259 EdgesOfLastSeenNodes += getLocalCalls(N->getFunction());
260 }
261
262 // Check on nodes that may have got added to SCC
263 for (const auto &N : *CurSCC) {
264 assert(!N.isDead());
265 auto I = NodesInLastSCC.insert(&N);
266 if (I.second)
267 EdgesOfLastSeenNodes += getLocalCalls(N.getFunction());
268 }
269 assert(NodeCount >= NodesInLastSCC.size());
270 assert(EdgeCount >= EdgesOfLastSeenNodes);
271 }
272
getLocalCalls(Function & F)273 int64_t MLInlineAdvisor::getLocalCalls(Function &F) {
274 return getCachedFPI(F).DirectCallsToDefinedFunctions;
275 }
276
277 // Update the internal state of the advisor, and force invalidate feature
278 // analysis. Currently, we maintain minimal (and very simple) global state - the
279 // number of functions and the number of static calls. We also keep track of the
280 // total IR size in this module, to stop misbehaving policies at a certain bloat
281 // factor (SizeIncreaseThreshold)
onSuccessfulInlining(const MLInlineAdvice & Advice,bool CalleeWasDeleted)282 void MLInlineAdvisor::onSuccessfulInlining(const MLInlineAdvice &Advice,
283 bool CalleeWasDeleted) {
284 assert(!ForceStop);
285 Function *Caller = Advice.getCaller();
286 Function *Callee = Advice.getCallee();
287 // The caller features aren't valid anymore.
288 {
289 PreservedAnalyses PA = PreservedAnalyses::all();
290 PA.abandon<FunctionPropertiesAnalysis>();
291 PA.abandon<DominatorTreeAnalysis>();
292 PA.abandon<LoopAnalysis>();
293 FAM.invalidate(*Caller, PA);
294 }
295 Advice.updateCachedCallerFPI(FAM);
296 int64_t IRSizeAfter =
297 getIRSize(*Caller) + (CalleeWasDeleted ? 0 : Advice.CalleeIRSize);
298 CurrentIRSize += IRSizeAfter - (Advice.CallerIRSize + Advice.CalleeIRSize);
299 if (CurrentIRSize > SizeIncreaseThreshold * InitialIRSize)
300 ForceStop = true;
301
302 // We can delta-update module-wide features. We know the inlining only changed
303 // the caller, and maybe the callee (by deleting the latter).
304 // Nodes are simple to update.
305 // For edges, we 'forget' the edges that the caller and callee used to have
306 // before inlining, and add back what they currently have together.
307 int64_t NewCallerAndCalleeEdges =
308 getCachedFPI(*Caller).DirectCallsToDefinedFunctions;
309
310 // A dead function's node is not actually removed from the call graph until
311 // the end of the call graph walk, but the node no longer belongs to any valid
312 // SCC.
313 if (CalleeWasDeleted) {
314 --NodeCount;
315 NodesInLastSCC.erase(CG.lookup(*Callee));
316 DeadFunctions.insert(Callee);
317 } else {
318 NewCallerAndCalleeEdges +=
319 getCachedFPI(*Callee).DirectCallsToDefinedFunctions;
320 }
321 EdgeCount += (NewCallerAndCalleeEdges - Advice.CallerAndCalleeEdges);
322 assert(CurrentIRSize >= 0 && EdgeCount >= 0 && NodeCount >= 0);
323 }
324
getModuleIRSize() const325 int64_t MLInlineAdvisor::getModuleIRSize() const {
326 int64_t Ret = 0;
327 for (auto &F : M)
328 if (!F.isDeclaration())
329 Ret += getIRSize(F);
330 return Ret;
331 }
332
getCachedFPI(Function & F) const333 FunctionPropertiesInfo &MLInlineAdvisor::getCachedFPI(Function &F) const {
334 auto InsertPair =
335 FPICache.insert(std::make_pair(&F, FunctionPropertiesInfo()));
336 if (!InsertPair.second)
337 return InsertPair.first->second;
338 InsertPair.first->second = FAM.getResult<FunctionPropertiesAnalysis>(F);
339 return InsertPair.first->second;
340 }
341
getAdviceImpl(CallBase & CB)342 std::unique_ptr<InlineAdvice> MLInlineAdvisor::getAdviceImpl(CallBase &CB) {
343 if (auto Skip = getSkipAdviceIfUnreachableCallsite(CB))
344 return Skip;
345
346 auto &Caller = *CB.getCaller();
347 auto &Callee = *CB.getCalledFunction();
348
349 auto GetAssumptionCache = [&](Function &F) -> AssumptionCache & {
350 return FAM.getResult<AssumptionAnalysis>(F);
351 };
352 auto &TIR = FAM.getResult<TargetIRAnalysis>(Callee);
353 auto &ORE = FAM.getResult<OptimizationRemarkEmitterAnalysis>(Caller);
354
355 if (SkipPolicy == SkipMLPolicyCriteria::IfCallerIsNotCold) {
356 if (!PSI.isFunctionEntryCold(&Caller))
357 return std::make_unique<InlineAdvice>(this, CB, ORE,
358 GetDefaultAdvice(CB));
359 }
360 auto MandatoryKind = InlineAdvisor::getMandatoryKind(CB, FAM, ORE);
361 // If this is a "never inline" case, there won't be any changes to internal
362 // state we need to track, so we can just return the base InlineAdvice, which
363 // will do nothing interesting.
364 // Same thing if this is a recursive case.
365 if (MandatoryKind == InlineAdvisor::MandatoryInliningKind::Never ||
366 &Caller == &Callee)
367 return getMandatoryAdvice(CB, false);
368
369 bool Mandatory =
370 MandatoryKind == InlineAdvisor::MandatoryInliningKind::Always;
371
372 // If we need to stop, we won't want to track anymore any state changes, so
373 // we just return the base InlineAdvice, which acts as a noop.
374 if (ForceStop) {
375 ORE.emit([&] {
376 return OptimizationRemarkMissed(DEBUG_TYPE, "ForceStop", &CB)
377 << "Won't attempt inlining because module size grew too much.";
378 });
379 return std::make_unique<InlineAdvice>(this, CB, ORE, Mandatory);
380 }
381
382 int CostEstimate = 0;
383 if (!Mandatory) {
384 auto IsCallSiteInlinable =
385 llvm::getInliningCostEstimate(CB, TIR, GetAssumptionCache);
386 if (!IsCallSiteInlinable) {
387 // We can't inline this for correctness reasons, so return the base
388 // InlineAdvice, as we don't care about tracking any state changes (which
389 // won't happen).
390 return std::make_unique<InlineAdvice>(this, CB, ORE, false);
391 }
392 CostEstimate = *IsCallSiteInlinable;
393 }
394
395 const auto CostFeatures =
396 llvm::getInliningCostFeatures(CB, TIR, GetAssumptionCache);
397 if (!CostFeatures) {
398 return std::make_unique<InlineAdvice>(this, CB, ORE, false);
399 }
400
401 if (Mandatory)
402 return getMandatoryAdvice(CB, true);
403
404 auto NrCtantParams = 0;
405 for (auto I = CB.arg_begin(), E = CB.arg_end(); I != E; ++I) {
406 NrCtantParams += (isa<Constant>(*I));
407 }
408
409 auto &CallerBefore = getCachedFPI(Caller);
410 auto &CalleeBefore = getCachedFPI(Callee);
411
412 *ModelRunner->getTensor<int64_t>(FeatureIndex::callee_basic_block_count) =
413 CalleeBefore.BasicBlockCount;
414 *ModelRunner->getTensor<int64_t>(FeatureIndex::callsite_height) =
415 getInitialFunctionLevel(Caller);
416 *ModelRunner->getTensor<int64_t>(FeatureIndex::node_count) = NodeCount;
417 *ModelRunner->getTensor<int64_t>(FeatureIndex::nr_ctant_params) =
418 NrCtantParams;
419 *ModelRunner->getTensor<int64_t>(FeatureIndex::edge_count) = EdgeCount;
420 *ModelRunner->getTensor<int64_t>(FeatureIndex::caller_users) =
421 CallerBefore.Uses;
422 *ModelRunner->getTensor<int64_t>(
423 FeatureIndex::caller_conditionally_executed_blocks) =
424 CallerBefore.BlocksReachedFromConditionalInstruction;
425 *ModelRunner->getTensor<int64_t>(FeatureIndex::caller_basic_block_count) =
426 CallerBefore.BasicBlockCount;
427 *ModelRunner->getTensor<int64_t>(
428 FeatureIndex::callee_conditionally_executed_blocks) =
429 CalleeBefore.BlocksReachedFromConditionalInstruction;
430 *ModelRunner->getTensor<int64_t>(FeatureIndex::callee_users) =
431 CalleeBefore.Uses;
432 *ModelRunner->getTensor<int64_t>(FeatureIndex::cost_estimate) = CostEstimate;
433 *ModelRunner->getTensor<int64_t>(FeatureIndex::is_callee_avail_external) =
434 Callee.hasAvailableExternallyLinkage();
435 *ModelRunner->getTensor<int64_t>(FeatureIndex::is_caller_avail_external) =
436 Caller.hasAvailableExternallyLinkage();
437
438 // Add the cost features
439 for (size_t I = 0;
440 I < static_cast<size_t>(InlineCostFeatureIndex::NumberOfFeatures); ++I) {
441 *ModelRunner->getTensor<int64_t>(inlineCostFeatureToMlFeature(
442 static_cast<InlineCostFeatureIndex>(I))) = CostFeatures->at(I);
443 }
444 // This one would have been set up to be right at the end.
445 if (!InteractiveChannelBaseName.empty() && InteractiveIncludeDefault)
446 *ModelRunner->getTensor<int64_t>(InlineCostFeatureIndex::NumberOfFeatures) =
447 GetDefaultAdvice(CB);
448 return getAdviceFromModel(CB, ORE);
449 }
450
451 std::unique_ptr<MLInlineAdvice>
getAdviceFromModel(CallBase & CB,OptimizationRemarkEmitter & ORE)452 MLInlineAdvisor::getAdviceFromModel(CallBase &CB,
453 OptimizationRemarkEmitter &ORE) {
454 return std::make_unique<MLInlineAdvice>(
455 this, CB, ORE, static_cast<bool>(ModelRunner->evaluate<int64_t>()));
456 }
457
458 std::unique_ptr<InlineAdvice>
getSkipAdviceIfUnreachableCallsite(CallBase & CB)459 MLInlineAdvisor::getSkipAdviceIfUnreachableCallsite(CallBase &CB) {
460 if (!FAM.getResult<DominatorTreeAnalysis>(*CB.getCaller())
461 .isReachableFromEntry(CB.getParent()))
462 return std::make_unique<InlineAdvice>(this, CB, getCallerORE(CB), false);
463 return nullptr;
464 }
465
getMandatoryAdvice(CallBase & CB,bool Advice)466 std::unique_ptr<InlineAdvice> MLInlineAdvisor::getMandatoryAdvice(CallBase &CB,
467 bool Advice) {
468 // Make sure we track inlinings in all cases - mandatory or not.
469 if (auto Skip = getSkipAdviceIfUnreachableCallsite(CB))
470 return Skip;
471 if (Advice && !ForceStop)
472 return getMandatoryAdviceImpl(CB);
473
474 // If this is a "never inline" case, there won't be any changes to internal
475 // state we need to track, so we can just return the base InlineAdvice, which
476 // will do nothing interesting.
477 // Same if we are forced to stop - we don't track anymore.
478 return std::make_unique<InlineAdvice>(this, CB, getCallerORE(CB), Advice);
479 }
480
481 std::unique_ptr<MLInlineAdvice>
getMandatoryAdviceImpl(CallBase & CB)482 MLInlineAdvisor::getMandatoryAdviceImpl(CallBase &CB) {
483 return std::make_unique<MLInlineAdvice>(this, CB, getCallerORE(CB), true);
484 }
485
print(raw_ostream & OS) const486 void MLInlineAdvisor::print(raw_ostream &OS) const {
487 OS << "[MLInlineAdvisor] Nodes: " << NodeCount << " Edges: " << EdgeCount
488 << " EdgesOfLastSeenNodes: " << EdgesOfLastSeenNodes << "\n";
489 OS << "[MLInlineAdvisor] FPI:\n";
490 for (auto I : FPICache) {
491 OS << I.first->getName() << ":\n";
492 I.second.print(OS);
493 OS << "\n";
494 }
495 OS << "\n";
496 OS << "[MLInlineAdvisor] FuncLevels:\n";
497 for (auto I : FunctionLevels)
498 OS << (DeadFunctions.contains(&I.first->getFunction())
499 ? "<deleted>"
500 : I.first->getFunction().getName())
501 << " : " << I.second << "\n";
502
503 OS << "\n";
504 }
505
MLInlineAdvice(MLInlineAdvisor * Advisor,CallBase & CB,OptimizationRemarkEmitter & ORE,bool Recommendation)506 MLInlineAdvice::MLInlineAdvice(MLInlineAdvisor *Advisor, CallBase &CB,
507 OptimizationRemarkEmitter &ORE,
508 bool Recommendation)
509 : InlineAdvice(Advisor, CB, ORE, Recommendation),
510 CallerIRSize(Advisor->isForcedToStop() ? 0 : Advisor->getIRSize(*Caller)),
511 CalleeIRSize(Advisor->isForcedToStop() ? 0 : Advisor->getIRSize(*Callee)),
512 CallerAndCalleeEdges(Advisor->isForcedToStop()
513 ? 0
514 : (Advisor->getLocalCalls(*Caller) +
515 Advisor->getLocalCalls(*Callee))),
516 PreInlineCallerFPI(Advisor->getCachedFPI(*Caller)) {
517 if (Recommendation)
518 FPU.emplace(Advisor->getCachedFPI(*getCaller()), CB);
519 }
520
reportContextForRemark(DiagnosticInfoOptimizationBase & OR)521 void MLInlineAdvice::reportContextForRemark(
522 DiagnosticInfoOptimizationBase &OR) {
523 using namespace ore;
524 OR << NV("Callee", Callee->getName());
525 for (size_t I = 0; I < NumberOfFeatures; ++I)
526 OR << NV(FeatureMap[I].name(),
527 *getAdvisor()->getModelRunner().getTensor<int64_t>(I));
528 OR << NV("ShouldInline", isInliningRecommended());
529 }
530
updateCachedCallerFPI(FunctionAnalysisManager & FAM) const531 void MLInlineAdvice::updateCachedCallerFPI(FunctionAnalysisManager &FAM) const {
532 FPU->finish(FAM);
533 }
534
recordInliningImpl()535 void MLInlineAdvice::recordInliningImpl() {
536 ORE.emit([&]() {
537 OptimizationRemark R(DEBUG_TYPE, "InliningSuccess", DLoc, Block);
538 reportContextForRemark(R);
539 return R;
540 });
541 getAdvisor()->onSuccessfulInlining(*this, /*CalleeWasDeleted*/ false);
542 }
543
recordInliningWithCalleeDeletedImpl()544 void MLInlineAdvice::recordInliningWithCalleeDeletedImpl() {
545 ORE.emit([&]() {
546 OptimizationRemark R(DEBUG_TYPE, "InliningSuccessWithCalleeDeleted", DLoc,
547 Block);
548 reportContextForRemark(R);
549 return R;
550 });
551 getAdvisor()->onSuccessfulInlining(*this, /*CalleeWasDeleted*/ true);
552 }
553
recordUnsuccessfulInliningImpl(const InlineResult & Result)554 void MLInlineAdvice::recordUnsuccessfulInliningImpl(
555 const InlineResult &Result) {
556 getAdvisor()->getCachedFPI(*Caller) = PreInlineCallerFPI;
557 ORE.emit([&]() {
558 OptimizationRemarkMissed R(DEBUG_TYPE, "InliningAttemptedAndUnsuccessful",
559 DLoc, Block);
560 reportContextForRemark(R);
561 return R;
562 });
563 }
recordUnattemptedInliningImpl()564 void MLInlineAdvice::recordUnattemptedInliningImpl() {
565 assert(!FPU);
566 ORE.emit([&]() {
567 OptimizationRemarkMissed R(DEBUG_TYPE, "IniningNotAttempted", DLoc, Block);
568 reportContextForRemark(R);
569 return R;
570 });
571 }
572