LLVM 22.0.0git
MLInlineAdvisor.cpp
Go to the documentation of this file.
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//===----------------------------------------------------------------------===//
31#include "llvm/IR/Dominators.h"
33#include "llvm/IR/Module.h"
34#include "llvm/IR/PassManager.h"
36
37using namespace llvm;
38
40 "inliner-interactive-channel-base", cl::Hidden,
42 "Base file path for the interactive mode. The incoming filename should "
43 "have the name <inliner-interactive-channel-base>.in, while the "
44 "outgoing name should be <inliner-interactive-channel-base>.out"));
45static const std::string InclDefaultMsg =
46 (Twine("In interactive mode, also send the default policy decision: ") +
48 .str();
49static cl::opt<bool>
50 InteractiveIncludeDefault("inliner-interactive-include-default", cl::Hidden,
52
54
56 "ml-inliner-skip-policy", cl::Hidden, cl::init(SkipMLPolicyCriteria::Never),
59 "if-caller-not-cold", "if the caller is not cold")));
60
61static cl::opt<std::string> ModelSelector("ml-inliner-model-selector",
62 cl::Hidden, cl::init(""));
63
64#if defined(LLVM_HAVE_TF_AOT_INLINERSIZEMODEL)
65// codegen-ed file
66#include "InlinerSizeModel.h" // NOLINT
67using CompiledModelType = llvm::InlinerSizeModel;
68#else
70#endif
71
72std::unique_ptr<InlineAdvisor>
74 std::function<bool(CallBase &)> GetDefaultAdvice) {
75 if (!llvm::isEmbeddedModelEvaluatorValid<CompiledModelType>() &&
77 return nullptr;
78 auto RunnerFactory = [&](const std::vector<TensorSpec> &InputFeatures)
79 -> std::unique_ptr<MLModelRunner> {
80 std::unique_ptr<MLModelRunner> AOTRunner;
82 AOTRunner = std::make_unique<ReleaseModeModelRunner<CompiledModelType>>(
83 M.getContext(), InputFeatures, DecisionName,
85 else {
86 AOTRunner = std::make_unique<InteractiveModelRunner>(
87 M.getContext(), InputFeatures, InlineDecisionSpec,
90 }
91 return AOTRunner;
92 };
93 return std::make_unique<MLInlineAdvisor>(M, MAM, RunnerFactory,
94 GetDefaultAdvice);
95}
96
97#define DEBUG_TYPE "inline-ml"
98
100 "ml-advisor-size-increase-threshold", cl::Hidden,
101 cl::desc("Maximum factor by which expected native size may increase before "
102 "blocking any further inlining."),
103 cl::init(2.0));
104
106 "ml-advisor-keep-fpi-cache", cl::Hidden,
107 cl::desc(
108 "For test - keep the ML Inline advisor's FunctionPropertiesInfo cache"),
109 cl::init(false));
110
111const std::vector<TensorSpec> &MLInlineAdvisor::getInitialFeatureMap() {
112 // clang-format off
113static std::vector<TensorSpec> FeatureMap{
114#define POPULATE_NAMES(DTYPE, SHAPE, NAME, __) TensorSpec::createSpec<DTYPE>(#NAME, SHAPE),
115// InlineCost features - these must come first
117
118// Non-cost features
120#undef POPULATE_NAMES
121};
122 // clang-format on
123 return FeatureMap;
124}
125
126const char *const llvm::DecisionName = "inlining_decision";
128 TensorSpec::createSpec<int64_t>(DecisionName, {1});
129const char *const llvm::DefaultDecisionName = "inlining_default";
131 TensorSpec::createSpec<int64_t>(DefaultDecisionName, {1});
132const char *const llvm::RewardName = "delta_size";
133
135 if (auto *CS = dyn_cast<CallBase>(&I))
136 if (Function *Callee = CS->getCalledFunction()) {
137 if (!Callee->isDeclaration()) {
138 return CS;
139 }
140 }
141 return nullptr;
142}
143
146 std::function<
147 std::unique_ptr<MLModelRunner>(const std::vector<TensorSpec> &)>
148 GetModelRunner,
149 std::function<bool(CallBase &)> GetDefaultAdvice)
151 M, MAM.getResult<FunctionAnalysisManagerModuleProxy>(M).getManager()),
152 GetDefaultAdvice(GetDefaultAdvice), FeatureMap(getInitialFeatureMap()),
153 CG(MAM.getResult<LazyCallGraphAnalysis>(M)),
154 UseIR2Vec(MAM.getCachedResult<IR2VecVocabAnalysis>(M) != nullptr),
155 InitialIRSize(getModuleIRSize()), CurrentIRSize(InitialIRSize),
156 PSI(MAM.getResult<ProfileSummaryAnalysis>(M)) {
157 // Extract the 'call site height' feature - the position of a call site
158 // relative to the farthest statically reachable SCC node. We don't mutate
159 // this value while inlining happens. Empirically, this feature proved
160 // critical in behavioral cloning - i.e. training a model to mimic the manual
161 // heuristic's decisions - and, thus, equally important for training for
162 // improvement.
163 CallGraph CGraph(M);
164 for (auto I = scc_begin(&CGraph); !I.isAtEnd(); ++I) {
165 const std::vector<CallGraphNode *> &CGNodes = *I;
166 unsigned Level = 0;
167 for (auto *CGNode : CGNodes) {
168 Function *F = CGNode->getFunction();
169 if (!F || F->isDeclaration())
170 continue;
171 for (auto &I : instructions(F)) {
172 if (auto *CS = getInlinableCS(I)) {
173 auto *Called = CS->getCalledFunction();
174 auto Pos = FunctionLevels.find(&CG.get(*Called));
175 // In bottom up traversal, an inlinable callee is either in the
176 // same SCC, or to a function in a visited SCC. So not finding its
177 // level means we haven't visited it yet, meaning it's in this SCC.
178 if (Pos == FunctionLevels.end())
179 continue;
180 Level = std::max(Level, Pos->second + 1);
181 }
182 }
183 }
184 for (auto *CGNode : CGNodes) {
185 Function *F = CGNode->getFunction();
186 if (F && !F->isDeclaration())
187 FunctionLevels[&CG.get(*F)] = Level;
188 }
189 }
190 for (auto KVP : FunctionLevels) {
191 AllNodes.insert(KVP.first);
192 EdgeCount += getLocalCalls(KVP.first->getFunction());
193 }
194 NodeCount = AllNodes.size();
195
196 if (auto *IR2VecVocabResult = MAM.getCachedResult<IR2VecVocabAnalysis>(M)) {
197 if (!IR2VecVocabResult->isValid()) {
198 M.getContext().emitError("IR2VecVocabAnalysis is not valid");
199 return;
200 }
201 // Add the IR2Vec features to the feature map
202 auto IR2VecDim = IR2VecVocabResult->getDimension();
203 FeatureMap.push_back(
204 TensorSpec::createSpec<float>("callee_embedding", {IR2VecDim}));
205 FeatureMap.push_back(
206 TensorSpec::createSpec<float>("caller_embedding", {IR2VecDim}));
207 }
210
211 ModelRunner = GetModelRunner(getFeatureMap());
212 if (!ModelRunner) {
213 M.getContext().emitError("Could not create model runner");
214 return;
215 }
216 ModelRunner->switchContext("");
217}
218
220 return CG.lookup(F) ? FunctionLevels.at(CG.lookup(F)) : 0;
221}
222
224 if (!CurSCC || ForceStop)
225 return;
226 FPICache.clear();
227 // Function passes executed between InlinerPass runs may have changed the
228 // module-wide features.
229 // The cgscc pass manager rules are such that:
230 // - if a pass leads to merging SCCs, then the pipeline is restarted on the
231 // merged SCC
232 // - if a pass leads to splitting the SCC, then we continue with one of the
233 // splits
234 // This means that the NodesInLastSCC is a superset (not strict) of the nodes
235 // that subsequent passes would have processed
236 // - in addition, if new Nodes were created by a pass (e.g. CoroSplit),
237 // they'd be adjacent to Nodes in the last SCC. So we just need to check the
238 // boundary of Nodes in NodesInLastSCC for Nodes we haven't seen. We don't
239 // care about the nature of the Edge (call or ref). `FunctionLevels`-wise, we
240 // record them at the same level as the original node (this is a choice, may
241 // need revisiting).
242 // - nodes are only deleted at the end of a call graph walk where they are
243 // batch deleted, so we shouldn't see any dead nodes here.
244 while (!NodesInLastSCC.empty()) {
245 const auto *N = *NodesInLastSCC.begin();
246 assert(!N->isDead());
247 NodesInLastSCC.erase(N);
248 EdgeCount += getLocalCalls(N->getFunction());
249 const auto NLevel = FunctionLevels.at(N);
250 for (const auto &E : *(*N)) {
251 const auto *AdjNode = &E.getNode();
252 assert(!AdjNode->isDead() && !AdjNode->getFunction().isDeclaration());
253 auto I = AllNodes.insert(AdjNode);
254 // We've discovered a new function.
255 if (I.second) {
256 ++NodeCount;
257 NodesInLastSCC.insert(AdjNode);
258 FunctionLevels[AdjNode] = NLevel;
259 }
260 }
261 }
262
263 EdgeCount -= EdgesOfLastSeenNodes;
264 EdgesOfLastSeenNodes = 0;
265
266 // (Re)use NodesInLastSCC to remember the nodes in the SCC right now,
267 // in case the SCC is split before onPassExit and some nodes are split out
268 assert(NodesInLastSCC.empty());
269 for (const auto &N : *CurSCC)
270 NodesInLastSCC.insert(&N);
271}
272
274 // No need to keep this around - function passes will invalidate it.
275 if (!KeepFPICache)
276 FPICache.clear();
277 if (!CurSCC || ForceStop)
278 return;
279 // Keep track of the nodes and edges we last saw. Then, in onPassEntry,
280 // we update the node count and edge count from the subset of these nodes that
281 // survived.
282 EdgesOfLastSeenNodes = 0;
283
284 // Check on nodes that were in SCC onPassEntry
285 for (const LazyCallGraph::Node *N : NodesInLastSCC) {
286 assert(!N->isDead());
287 EdgesOfLastSeenNodes += getLocalCalls(N->getFunction());
288 }
289
290 // Check on nodes that may have got added to SCC
291 for (const auto &N : *CurSCC) {
292 assert(!N.isDead());
293 auto I = NodesInLastSCC.insert(&N);
294 if (I.second)
295 EdgesOfLastSeenNodes += getLocalCalls(N.getFunction());
296 }
297 assert(NodeCount >= NodesInLastSCC.size());
298 assert(EdgeCount >= EdgesOfLastSeenNodes);
299}
300
303}
304
305// Update the internal state of the advisor, and force invalidate feature
306// analysis. Currently, we maintain minimal (and very simple) global state - the
307// number of functions and the number of static calls. We also keep track of the
308// total IR size in this module, to stop misbehaving policies at a certain bloat
309// factor (SizeIncreaseThreshold)
311 bool CalleeWasDeleted) {
312 assert(!ForceStop);
313 Function *Caller = Advice.getCaller();
314 Function *Callee = Advice.getCallee();
315 // The caller features aren't valid anymore.
316 {
319 PA.abandon<LoopAnalysis>();
320 FAM.invalidate(*Caller, PA);
321 }
323 int64_t IRSizeAfter =
324 getIRSize(*Caller) + (CalleeWasDeleted ? 0 : Advice.CalleeIRSize);
325 CurrentIRSize += IRSizeAfter - (Advice.CallerIRSize + Advice.CalleeIRSize);
326 if (CurrentIRSize > SizeIncreaseThreshold * InitialIRSize)
327 ForceStop = true;
328
329 // We can delta-update module-wide features. We know the inlining only changed
330 // the caller, and maybe the callee (by deleting the latter).
331 // Nodes are simple to update.
332 // For edges, we 'forget' the edges that the caller and callee used to have
333 // before inlining, and add back what they currently have together.
334 int64_t NewCallerAndCalleeEdges =
336
337 // A dead function's node is not actually removed from the call graph until
338 // the end of the call graph walk, but the node no longer belongs to any valid
339 // SCC.
340 if (CalleeWasDeleted) {
341 --NodeCount;
342 NodesInLastSCC.erase(CG.lookup(*Callee));
343 DeadFunctions.insert(Callee);
344 } else {
345 NewCallerAndCalleeEdges +=
347 }
348 EdgeCount += (NewCallerAndCalleeEdges - Advice.CallerAndCalleeEdges);
349 assert(CurrentIRSize >= 0 && EdgeCount >= 0 && NodeCount >= 0);
350}
351
352int64_t MLInlineAdvisor::getModuleIRSize() const {
353 int64_t Ret = 0;
354 for (auto &F : M)
355 if (!F.isDeclaration())
356 Ret += getIRSize(F);
357 return Ret;
358}
359
361 auto InsertPair = FPICache.try_emplace(&F);
362 if (!InsertPair.second)
363 return InsertPair.first->second;
364 InsertPair.first->second = FAM.getResult<FunctionPropertiesAnalysis>(F);
365 return InsertPair.first->second;
366}
367
368std::unique_ptr<InlineAdvice> MLInlineAdvisor::getAdviceImpl(CallBase &CB) {
369 if (auto Skip = getSkipAdviceIfUnreachableCallsite(CB))
370 return Skip;
371
372 auto &Caller = *CB.getCaller();
373 auto &Callee = *CB.getCalledFunction();
374
375 auto GetAssumptionCache = [&](Function &F) -> AssumptionCache & {
377 };
378 auto &TIR = FAM.getResult<TargetIRAnalysis>(Callee);
380
381 if (SkipPolicy == SkipMLPolicyCriteria::IfCallerIsNotCold) {
382 if (!PSI.isFunctionEntryCold(&Caller))
383 return std::make_unique<InlineAdvice>(this, CB, ORE,
384 GetDefaultAdvice(CB));
385 }
386 auto MandatoryKind = InlineAdvisor::getMandatoryKind(CB, FAM, ORE);
387 // If this is a "never inline" case, there won't be any changes to internal
388 // state we need to track, so we can just return the base InlineAdvice, which
389 // will do nothing interesting.
390 // Same thing if this is a recursive case.
391 if (MandatoryKind == InlineAdvisor::MandatoryInliningKind::Never ||
392 &Caller == &Callee)
393 return getMandatoryAdvice(CB, false);
394
395 bool Mandatory =
397
398 // If we need to stop, we won't want to track anymore any state changes, so
399 // we just return the base InlineAdvice, which acts as a noop.
400 if (ForceStop) {
401 ORE.emit([&] {
402 return OptimizationRemarkMissed(DEBUG_TYPE, "ForceStop", &CB)
403 << "Won't attempt inlining because module size grew too much.";
404 });
405 return std::make_unique<InlineAdvice>(this, CB, ORE, Mandatory);
406 }
407
408 int CostEstimate = 0;
409 if (!Mandatory) {
410 auto IsCallSiteInlinable =
411 llvm::getInliningCostEstimate(CB, TIR, GetAssumptionCache);
412 if (!IsCallSiteInlinable) {
413 // We can't inline this for correctness reasons, so return the base
414 // InlineAdvice, as we don't care about tracking any state changes (which
415 // won't happen).
416 return std::make_unique<InlineAdvice>(this, CB, ORE, false);
417 }
418 CostEstimate = *IsCallSiteInlinable;
419 }
420
421 const auto CostFeatures =
422 llvm::getInliningCostFeatures(CB, TIR, GetAssumptionCache);
423 if (!CostFeatures) {
424 return std::make_unique<InlineAdvice>(this, CB, ORE, false);
425 }
426
427 if (Mandatory)
428 return getMandatoryAdvice(CB, true);
429
430 auto NumCtantParams = 0;
431 for (auto I = CB.arg_begin(), E = CB.arg_end(); I != E; ++I) {
432 NumCtantParams += (isa<Constant>(*I));
433 }
434
435 auto &CallerBefore = getCachedFPI(Caller);
436 auto &CalleeBefore = getCachedFPI(Callee);
437
438 *ModelRunner->getTensor<int64_t>(FeatureIndex::callee_basic_block_count) =
439 CalleeBefore.BasicBlockCount;
440 *ModelRunner->getTensor<int64_t>(FeatureIndex::callsite_height) =
442 *ModelRunner->getTensor<int64_t>(FeatureIndex::node_count) = NodeCount;
443 *ModelRunner->getTensor<int64_t>(FeatureIndex::nr_ctant_params) =
444 NumCtantParams;
445 *ModelRunner->getTensor<int64_t>(FeatureIndex::edge_count) = EdgeCount;
446 *ModelRunner->getTensor<int64_t>(FeatureIndex::caller_users) =
447 CallerBefore.Uses;
448 *ModelRunner->getTensor<int64_t>(
449 FeatureIndex::caller_conditionally_executed_blocks) =
450 CallerBefore.BlocksReachedFromConditionalInstruction;
451 *ModelRunner->getTensor<int64_t>(FeatureIndex::caller_basic_block_count) =
452 CallerBefore.BasicBlockCount;
453 *ModelRunner->getTensor<int64_t>(
454 FeatureIndex::callee_conditionally_executed_blocks) =
455 CalleeBefore.BlocksReachedFromConditionalInstruction;
456 *ModelRunner->getTensor<int64_t>(FeatureIndex::callee_users) =
457 CalleeBefore.Uses;
458 *ModelRunner->getTensor<int64_t>(FeatureIndex::cost_estimate) = CostEstimate;
459 *ModelRunner->getTensor<int64_t>(FeatureIndex::is_callee_avail_external) =
460 Callee.hasAvailableExternallyLinkage();
461 *ModelRunner->getTensor<int64_t>(FeatureIndex::is_caller_avail_external) =
462 Caller.hasAvailableExternallyLinkage();
463
464 if (UseIR2Vec) {
465 // Python side expects float embeddings. The IR2Vec embeddings are doubles
466 // as of now due to the restriction of fromJSON method used by the
467 // readVocabulary method in ir2vec::Embeddings.
468 auto setEmbedding = [&](const ir2vec::Embedding &Embedding,
469 FeatureIndex Index) {
470 llvm::transform(Embedding, ModelRunner->getTensor<float>(Index),
471 [](double Val) { return static_cast<float>(Val); });
472 };
473
474 setEmbedding(CalleeBefore.getFunctionEmbedding(),
476 setEmbedding(CallerBefore.getFunctionEmbedding(),
478 }
479
480 // Add the cost features
481 for (size_t I = 0;
482 I < static_cast<size_t>(InlineCostFeatureIndex::NumberOfFeatures); ++I) {
483 *ModelRunner->getTensor<int64_t>(inlineCostFeatureToMlFeature(
484 static_cast<InlineCostFeatureIndex>(I))) = CostFeatures->at(I);
485 }
486 // This one would have been set up to be right at the end.
488 *ModelRunner->getTensor<int64_t>(getFeatureMap().size() - 1) =
490 return getAdviceFromModel(CB, ORE);
491}
492
493std::unique_ptr<MLInlineAdvice>
496 return std::make_unique<MLInlineAdvice>(
497 this, CB, ORE, static_cast<bool>(ModelRunner->evaluate<int64_t>()));
498}
499
500std::unique_ptr<InlineAdvice>
501MLInlineAdvisor::getSkipAdviceIfUnreachableCallsite(CallBase &CB) {
503 .isReachableFromEntry(CB.getParent()))
504 return std::make_unique<InlineAdvice>(this, CB, getCallerORE(CB), false);
505 return nullptr;
506}
507
508std::unique_ptr<InlineAdvice> MLInlineAdvisor::getMandatoryAdvice(CallBase &CB,
509 bool Advice) {
510 // Make sure we track inlinings in all cases - mandatory or not.
511 if (auto Skip = getSkipAdviceIfUnreachableCallsite(CB))
512 return Skip;
513 if (Advice && !ForceStop)
514 return getMandatoryAdviceImpl(CB);
515
516 // If this is a "never inline" case, there won't be any changes to internal
517 // state we need to track, so we can just return the base InlineAdvice, which
518 // will do nothing interesting.
519 // Same if we are forced to stop - we don't track anymore.
520 return std::make_unique<InlineAdvice>(this, CB, getCallerORE(CB), Advice);
521}
522
523std::unique_ptr<MLInlineAdvice>
525 return std::make_unique<MLInlineAdvice>(this, CB, getCallerORE(CB), true);
526}
527
528void MLInlineAdvisor::print(raw_ostream &OS) const {
529 OS << "[MLInlineAdvisor] Nodes: " << NodeCount << " Edges: " << EdgeCount
530 << " EdgesOfLastSeenNodes: " << EdgesOfLastSeenNodes << "\n";
531 OS << "[MLInlineAdvisor] FPI:\n";
532 for (auto I : FPICache) {
533 OS << I.first->getName() << ":\n";
534 I.second.print(OS);
535 OS << "\n";
536 }
537 OS << "\n";
538 OS << "[MLInlineAdvisor] FuncLevels:\n";
539 for (auto I : FunctionLevels)
540 OS << (DeadFunctions.contains(&I.first->getFunction())
541 ? "<deleted>"
542 : I.first->getFunction().getName())
543 << " : " << I.second << "\n";
544
545 OS << "\n";
546}
547
550 bool Recommendation)
551 : InlineAdvice(Advisor, CB, ORE, Recommendation),
552 CallerIRSize(Advisor->isForcedToStop() ? 0 : Advisor->getIRSize(*Caller)),
553 CalleeIRSize(Advisor->isForcedToStop() ? 0 : Advisor->getIRSize(*Callee)),
554 CallerAndCalleeEdges(Advisor->isForcedToStop()
555 ? 0
556 : (Advisor->getLocalCalls(*Caller) +
557 Advisor->getLocalCalls(*Callee))),
558 PreInlineCallerFPI(Advisor->getCachedFPI(*Caller)) {
559 if (Recommendation)
560 FPU.emplace(Advisor->getCachedFPI(*getCaller()), CB);
561}
562
563void MLInlineAdvice::reportContextForRemark(
565 using namespace ore;
566 OR << NV("Callee", Callee->getName());
567 for (size_t I = 0; I < getAdvisor()->getFeatureMap().size(); ++I)
568 OR << NV(getAdvisor()->getFeatureMap()[I].name(),
569 *getAdvisor()->getModelRunner().getTensor<int64_t>(I));
570 OR << NV("ShouldInline", isInliningRecommended());
571}
572
574 FPU->finish(FAM);
575}
576
578 ORE.emit([&]() {
579 OptimizationRemark R(DEBUG_TYPE, "InliningSuccess", DLoc, Block);
580 reportContextForRemark(R);
581 return R;
582 });
583 getAdvisor()->onSuccessfulInlining(*this, /*CalleeWasDeleted*/ false);
584}
585
587 ORE.emit([&]() {
588 OptimizationRemark R(DEBUG_TYPE, "InliningSuccessWithCalleeDeleted", DLoc,
589 Block);
590 reportContextForRemark(R);
591 return R;
592 });
593 getAdvisor()->onSuccessfulInlining(*this, /*CalleeWasDeleted*/ true);
594}
595
597 const InlineResult &Result) {
598 getAdvisor()->getCachedFPI(*Caller) = PreInlineCallerFPI;
599 ORE.emit([&]() {
600 OptimizationRemarkMissed R(DEBUG_TYPE, "InliningAttemptedAndUnsuccessful",
601 DLoc, Block);
602 reportContextForRemark(R);
603 return R;
604 });
605}
607 assert(!FPU);
608 ORE.emit([&]() {
609 OptimizationRemarkMissed R(DEBUG_TYPE, "IniningNotAttempted", DLoc, Block);
610 reportContextForRemark(R);
611 return R;
612 });
613}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
Expand Atomic instructions
This file provides interfaces used to build and manipulate a call graph, which is a very useful tool ...
#define clEnumValN(ENUMVAL, FLAGNAME, DESC)
Definition: CommandLine.h:687
#define DEBUG_TYPE
Module.h This file contains the declarations for the Module class.
This header defines various interfaces for pass management in LLVM.
#define INLINE_COST_FEATURE_ITERATOR(M)
#define INLINE_FEATURE_ITERATOR(M)
Implements a lazy call graph analysis and related passes for the new pass manager.
#define F(x, y, z)
Definition: MD5.cpp:55
#define I(x, y, z)
Definition: MD5.cpp:58
static cl::opt< bool > KeepFPICache("ml-advisor-keep-fpi-cache", cl::Hidden, cl::desc("For test - keep the ML Inline advisor's FunctionPropertiesInfo cache"), cl::init(false))
static cl::opt< std::string > ModelSelector("ml-inliner-model-selector", cl::Hidden, cl::init(""))
CallBase * getInlinableCS(Instruction &I)
SkipMLPolicyCriteria
static cl::opt< std::string > InteractiveChannelBaseName("inliner-interactive-channel-base", cl::Hidden, cl::desc("Base file path for the interactive mode. The incoming filename should " "have the name <inliner-interactive-channel-base>.in, while the " "outgoing name should be <inliner-interactive-channel-base>.out"))
#define POPULATE_NAMES(DTYPE, SHAPE, NAME, __)
static cl::opt< float > SizeIncreaseThreshold("ml-advisor-size-increase-threshold", cl::Hidden, cl::desc("Maximum factor by which expected native size may increase before " "blocking any further inlining."), cl::init(2.0))
static const std::string InclDefaultMsg
static cl::opt< SkipMLPolicyCriteria > SkipPolicy("ml-inliner-skip-policy", cl::Hidden, cl::init(SkipMLPolicyCriteria::Never), cl::values(clEnumValN(SkipMLPolicyCriteria::Never, "never", "never"), clEnumValN(SkipMLPolicyCriteria::IfCallerIsNotCold, "if-caller-not-cold", "if the caller is not cold")))
static cl::opt< bool > InteractiveIncludeDefault("inliner-interactive-include-default", cl::Hidden, cl::desc(InclDefaultMsg))
#define DecisionName
FunctionAnalysisManager FAM
ModuleAnalysisManager MAM
if(PassOpts->AAPipeline)
This builds on the llvm/ADT/GraphTraits.h file to find the strongly connected components (SCCs) of a ...
raw_pwrite_stream & OS
This pass exposes codegen information to IR-level passes.
A container for analyses that lazily runs them and caches their results.
Definition: PassManager.h:255
void invalidate(IRUnitT &IR, const PreservedAnalyses &PA)
Invalidate cached analyses for an IR unit.
PassT::Result * getCachedResult(IRUnitT &IR) const
Get the cached result of an analysis pass for a given IR unit.
Definition: PassManager.h:431
PassT::Result & getResult(IRUnitT &IR, ExtraArgTs... ExtraArgs)
Get the result of an analysis pass for a given IR unit.
Definition: PassManager.h:412
A function analysis which provides an AssumptionCache.
A cache of @llvm.assume calls within a function.
Base class for all callable instructions (InvokeInst and CallInst) Holds everything related to callin...
Definition: InstrTypes.h:1116
Function * getCalledFunction() const
Returns the function called, or null if this is an indirect function invocation or the function signa...
Definition: InstrTypes.h:1348
User::op_iterator arg_begin()
Return the iterator pointing to the beginning of the argument list.
Definition: InstrTypes.h:1267
User::op_iterator arg_end()
Return the iterator pointing to the end of the argument list.
Definition: InstrTypes.h:1273
LLVM_ABI Function * getCaller()
Helper to get the caller (the parent function).
The basic data container for the call graph of a Module of IR.
Definition: CallGraph.h:72
Common features for diagnostics dealing with optimization remarks that are used by both IR and MIR pa...
Analysis pass which computes a DominatorTree.
Definition: Dominators.h:284
int64_t DirectCallsToDefinedFunctions
Number of direct calls made from this function to other functions defined in this module.
This analysis provides the vocabulary for IR2Vec.
Definition: IR2Vec.h:415
Capture state between an inlining decision having had been made, and its impact being observable.
Definition: InlineAdvisor.h:75
Function *const Callee
Function *const Caller
Caller and Callee are pre-inlining.
const BasicBlock *const Block
OptimizationRemarkEmitter & ORE
InlineAdvisor *const Advisor
const DebugLoc DLoc
bool isInliningRecommended() const
Get the inlining recommendation.
Interface for deciding whether to inline a call site or not.
OptimizationRemarkEmitter & getCallerORE(CallBase &CB)
FunctionAnalysisManager & FAM
static MandatoryInliningKind getMandatoryKind(CallBase &CB, FunctionAnalysisManager &FAM, OptimizationRemarkEmitter &ORE)
InlineResult is basically true or false.
Definition: InlineCost.h:181
An analysis over an "outer" IR unit that provides access to an analysis manager over an "inner" IR un...
Definition: PassManager.h:585
LLVM_ABI void emitError(const Instruction *I, const Twine &ErrorStr)
emitError - Emit an error message to the currently installed error handler with optional location inf...
An analysis pass which computes the call graph for a module.
A node in the call graph.
An SCC of the call graph.
Node & get(Function &F)
Get a graph node for a given function, scanning it to populate the graph data as necessary.
Node * lookup(const Function &F) const
Lookup a function in the graph which has already been scanned and added.
Analysis pass that exposes the LoopInfo for a function.
Definition: LoopInfo.h:570
InlineAdvice that tracks changes post inlining.
void updateCachedCallerFPI(FunctionAnalysisManager &FAM) const
const int64_t CallerIRSize
MLInlineAdvice(MLInlineAdvisor *Advisor, CallBase &CB, OptimizationRemarkEmitter &ORE, bool Recommendation)
const int64_t CalleeIRSize
void recordInliningImpl() override
Function * getCaller() const
const int64_t CallerAndCalleeEdges
void recordUnsuccessfulInliningImpl(const InlineResult &Result) override
Function * getCallee() const
void recordInliningWithCalleeDeletedImpl() override
void recordUnattemptedInliningImpl() override
const std::vector< TensorSpec > & getFeatureMap() const
std::unique_ptr< MLModelRunner > ModelRunner
FunctionPropertiesInfo & getCachedFPI(Function &) const
void onPassExit(LazyCallGraph::SCC *SCC) override
This must be called when the Inliner pass is exited, as function passes may be run subsequently.
const MLModelRunner & getModelRunner() const
void onSuccessfulInlining(const MLInlineAdvice &Advice, bool CalleeWasDeleted)
static const std::vector< TensorSpec > & getInitialFeatureMap()
virtual std::unique_ptr< MLInlineAdvice > getMandatoryAdviceImpl(CallBase &CB)
void onPassEntry(LazyCallGraph::SCC *SCC) override
This must be called when the Inliner pass is entered, to allow the InlineAdvisor update internal stat...
MLInlineAdvisor(Module &M, ModuleAnalysisManager &MAM, std::function< std::unique_ptr< MLModelRunner >(const std::vector< TensorSpec > &)> GetModelRunner, std::function< bool(CallBase &)> GetDefaultAdvice)
int64_t getLocalCalls(Function &F)
std::vector< TensorSpec > FeatureMap
virtual std::unique_ptr< MLInlineAdvice > getAdviceFromModel(CallBase &CB, OptimizationRemarkEmitter &ORE)
int64_t getIRSize(Function &F) const
std::function< bool(CallBase &)> GetDefaultAdvice
std::unique_ptr< InlineAdvice > getAdviceImpl(CallBase &CB) override
std::unique_ptr< InlineAdvice > getMandatoryAdvice(CallBase &CB, bool Advice) override
unsigned getInitialFunctionLevel(const Function &F) const
T * getTensor(I FeatureID)
Definition: MLModelRunner.h:37
A Module instance is used to store all the information related to an LLVM module.
Definition: Module.h:67
LLVMContext & getContext() const
Get the global data context.
Definition: Module.h:285
A mock class satisfying the interface expected by ReleaseModeModelRunner for its TGen parameter.
The optimization diagnostic interface.
LLVM_ABI void emit(DiagnosticInfoOptimizationBase &OptDiag)
Output the remark via the diagnostic handler and to the optimization record file.
Diagnostic information for missed-optimization remarks.
Diagnostic information for applied optimization remarks.
A set of analyses that are preserved following a run of a transformation pass.
Definition: Analysis.h:112
static PreservedAnalyses all()
Construct a special preserved set that preserves all passes.
Definition: Analysis.h:118
PreservedAnalyses & abandon()
Mark an analysis as abandoned.
Definition: Analysis.h:171
An analysis pass based on the new PM to deliver ProfileSummaryInfo.
LLVM_ABI bool isFunctionEntryCold(const Function *F) const
Returns true if F has cold function entry.
Analysis pass providing the TargetTransformInfo.
Twine - A lightweight data structure for efficiently representing the concatenation of temporary valu...
Definition: Twine.h:82
LLVM_ABI StringRef getName() const
Return a constant reference to the value's name.
Definition: Value.cpp:322
std::pair< iterator, bool > insert(const ValueT &V)
Definition: DenseSet.h:194
bool contains(const_arg_type_t< ValueT > V) const
Check if the set contains the given element.
Definition: DenseSet.h:169
const ParentTy * getParent() const
Definition: ilist_node.h:34
This class implements an extremely fast bulk output stream that can only output to a stream.
Definition: raw_ostream.h:53
ValuesClass values(OptsTy... Options)
Helper to build a ValuesClass by forwarding a variable number of arguments as an initializer list to ...
Definition: CommandLine.h:712
initializer< Ty > init(const Ty &Val)
Definition: CommandLine.h:444
This is an optimization pass for GlobalISel generic memory operations.
Definition: AddressRanges.h:18
constexpr FeatureIndex inlineCostFeatureToMlFeature(InlineCostFeatureIndex Feature)
LLVM_ABI const char *const DefaultDecisionName
scc_iterator< T > scc_begin(const T &G)
Construct the begin iterator for a deduced graph type T.
Definition: SCCIterator.h:233
LLVM_ABI std::unique_ptr< InlineAdvisor > getReleaseModeAdvisor(Module &M, ModuleAnalysisManager &MAM, std::function< bool(CallBase &)> GetDefaultAdvice)
OutputIt transform(R &&Range, OutputIt d_first, UnaryFunction F)
Wrapper function around std::transform to apply a function to a range and store the result elsewhere.
Definition: STLExtras.h:1987
LLVM_ABI const TensorSpec DefaultDecisionSpec
LLVM_ABI const char *const DecisionName
static const std::vector< TensorSpec > InputFeatures
LLVM_ABI std::optional< InlineCostFeatures > getInliningCostFeatures(CallBase &Call, TargetTransformInfo &CalleeTTI, function_ref< AssumptionCache &(Function &)> GetAssumptionCache, function_ref< BlockFrequencyInfo &(Function &)> GetBFI=nullptr, function_ref< const TargetLibraryInfo &(Function &)> GetTLI=nullptr, ProfileSummaryInfo *PSI=nullptr, OptimizationRemarkEmitter *ORE=nullptr)
Get the expanded cost features.
LLVM_ABI const TensorSpec InlineDecisionSpec
LLVM_ABI const char *const RewardName
LLVM_ABI std::optional< int > getInliningCostEstimate(CallBase &Call, TargetTransformInfo &CalleeTTI, function_ref< AssumptionCache &(Function &)> GetAssumptionCache, function_ref< BlockFrequencyInfo &(Function &)> GetBFI=nullptr, function_ref< const TargetLibraryInfo &(Function &)> GetTLI=nullptr, ProfileSummaryInfo *PSI=nullptr, OptimizationRemarkEmitter *ORE=nullptr)
Get the cost estimate ignoring thresholds.
#define N
ReleaseModeModelRunner - production mode implementation of the MLModelRunner.
EmbeddedModelRunnerOptions & setModelSelector(StringRef Value)
Embedding is a datatype that wraps std::vector<double>.
Definition: IR2Vec.h:79