xref: /src/contrib/llvm-project/llvm/lib/Analysis/DevelopmentModeInlineAdvisor.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
1b60736ecSDimitry Andric //===- DevelopmentModeInlineAdvisor.cpp - runtime-loadable model runner  --===//
2b60736ecSDimitry Andric //
3c0981da4SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4c0981da4SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5c0981da4SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6b60736ecSDimitry Andric //
7b60736ecSDimitry Andric //===----------------------------------------------------------------------===//
8b60736ecSDimitry Andric //
9b1c73532SDimitry Andric // This file implements a model runner using TFLite, allowing the
10b60736ecSDimitry Andric // loading of a model from a command line option.
11b60736ecSDimitry Andric //
12b60736ecSDimitry Andric //===----------------------------------------------------------------------===//
13e3b55780SDimitry Andric #include "llvm/Analysis/TensorSpec.h"
14b60736ecSDimitry Andric #include "llvm/Config/config.h"
15e3b55780SDimitry Andric #if defined(LLVM_HAVE_TFLITE)
16b60736ecSDimitry Andric 
176f8fc217SDimitry Andric #include "llvm/ADT/BitVector.h"
18b60736ecSDimitry Andric #include "llvm/Analysis/CallGraph.h"
19b60736ecSDimitry Andric #include "llvm/Analysis/InlineSizeEstimatorAnalysis.h"
20b60736ecSDimitry Andric #include "llvm/Analysis/MLInlineAdvisor.h"
2177fc4c14SDimitry Andric #include "llvm/Analysis/ModelUnderTrainingRunner.h"
2277fc4c14SDimitry Andric #include "llvm/Analysis/NoInferenceModelRunner.h"
23b60736ecSDimitry Andric #include "llvm/Analysis/Utils/TFUtils.h"
24e3b55780SDimitry Andric #include "llvm/Analysis/Utils/TrainingLogger.h"
25b60736ecSDimitry Andric #include "llvm/IR/LLVMContext.h"
26ac9a064cSDimitry Andric #include "llvm/IR/Module.h"
27b60736ecSDimitry Andric #include "llvm/Support/CommandLine.h"
28b60736ecSDimitry Andric #include "llvm/Support/ManagedStatic.h"
29b60736ecSDimitry Andric 
30b60736ecSDimitry Andric #include <vector>
31e3b55780SDimitry Andric #include <optional>
32b60736ecSDimitry Andric 
33b60736ecSDimitry Andric using namespace llvm;
34b60736ecSDimitry Andric 
35b60736ecSDimitry Andric static cl::opt<std::string> TrainingLog(
36b60736ecSDimitry Andric     "training-log", cl::Hidden,
37b60736ecSDimitry Andric     cl::desc("Path where the development - mode inlining log is saved."));
38b60736ecSDimitry Andric 
39b60736ecSDimitry Andric static cl::opt<std::string> TFModelUnderTrainingPath(
40b60736ecSDimitry Andric     "ml-inliner-model-under-training", cl::Hidden,
41b60736ecSDimitry Andric     cl::desc(R"(Path to SavedModel from the previous training iteration.
42b60736ecSDimitry Andric The directory is also expected to contain a JSON specification of the
43b60736ecSDimitry Andric outputs expected to be logged, where the first entry must be the
44b60736ecSDimitry Andric inlining decision. The file containing the specification should be
45b60736ecSDimitry Andric called output_spec.json. The expected JSON value is an array of
46b60736ecSDimitry Andric dictionaries. Each dictionary should have 2 keys:
47b60736ecSDimitry Andric 
48b60736ecSDimitry Andric - "tensor_spec, followed by the TensorSpec description of the
49b60736ecSDimitry Andric output; and
50b60736ecSDimitry Andric - "logging_name", a string indicating the name to use when
51b60736ecSDimitry Andric logging the output values.
52b60736ecSDimitry Andric 
53b60736ecSDimitry Andric Example:
54b60736ecSDimitry Andric [
55b60736ecSDimitry Andric   {
56b60736ecSDimitry Andric     "logging_name" : "some_name",
57b60736ecSDimitry Andric     "tensor_spec" : {
58b60736ecSDimitry Andric       "name" : "model_name",
59b60736ecSDimitry Andric       "port" : 0,
60b60736ecSDimitry Andric       "shape" : [2, 3],
61b60736ecSDimitry Andric       "type" : "float"
62b60736ecSDimitry Andric       }
63b60736ecSDimitry Andric   }
64b60736ecSDimitry Andric ]
65b60736ecSDimitry Andric 
66b60736ecSDimitry Andric The first value must always correspond to the decision.)"));
67b60736ecSDimitry Andric 
68b60736ecSDimitry Andric static cl::opt<std::string> TFOutputSpecOverride(
69b60736ecSDimitry Andric     "ml-inliner-output-spec-override", cl::Hidden,
70b60736ecSDimitry Andric     cl::desc("Override the path to the output spec json file. See "
71b60736ecSDimitry Andric              "-ml-inliner-model-under-training documentation for the "
72b60736ecSDimitry Andric              "specification of that file."));
73b60736ecSDimitry Andric 
74b60736ecSDimitry Andric static cl::opt<std::string> TFFeedPrefix("ml-inliner-trained-model-feed-prefix",
75b60736ecSDimitry Andric                                          cl::Hidden, cl::init("action_"),
76b60736ecSDimitry Andric                                          cl::desc("Prefix for feature names."));
77b60736ecSDimitry Andric 
78b60736ecSDimitry Andric namespace {
79b60736ecSDimitry Andric /// An InlineEvent, used by TrainingLogger.
80b60736ecSDimitry Andric struct InlineEvent {
81b60736ecSDimitry Andric   /// What the default policy's decision would have been.
82b60736ecSDimitry Andric   int64_t DefaultDecision = 0;
83b60736ecSDimitry Andric 
84b60736ecSDimitry Andric   /// What we advised. When training off the default policy, this is the same as
85b60736ecSDimitry Andric   /// DefaultDecision.
86b60736ecSDimitry Andric   int64_t AdvisedDecision = 0;
87b60736ecSDimitry Andric 
88b60736ecSDimitry Andric   /// What actually happened. This would be 'false' in the case of an inline
89b60736ecSDimitry Andric   /// error, even if AdvisedDecision were true, otherwise it agrees with
90b60736ecSDimitry Andric   /// AdvisedDecision.
91b60736ecSDimitry Andric   bool Effect = false;
92b60736ecSDimitry Andric 
93b60736ecSDimitry Andric   /// What the change in size was: size_after - size_before
94b60736ecSDimitry Andric   int64_t Reward = 0;
95b60736ecSDimitry Andric };
96b60736ecSDimitry Andric 
97e3b55780SDimitry Andric /// Collect data we may use for training a model.
98b60736ecSDimitry Andric class TrainingLogger final {
99b60736ecSDimitry Andric public:
100b60736ecSDimitry Andric   TrainingLogger(StringRef LogFileName, const ModelUnderTrainingRunner *MUTR);
101b60736ecSDimitry Andric 
102b60736ecSDimitry Andric   /// Log one inlining event.
103b60736ecSDimitry Andric   void logInlineEvent(const InlineEvent &Event,
104b60736ecSDimitry Andric                       const MLModelRunner &ModelRunner);
105b60736ecSDimitry Andric 
106b60736ecSDimitry Andric private:
107b60736ecSDimitry Andric   StringRef LogFileName;
108b60736ecSDimitry Andric   const ModelUnderTrainingRunner *const MUTR;
109b60736ecSDimitry Andric   std::unique_ptr<Logger> L;
1106f8fc217SDimitry Andric   BitVector Effects;
111b60736ecSDimitry Andric   /// Set these 2 clearly OOB, to make sure we set them later.
112b60736ecSDimitry Andric   size_t DefaultDecisionPos = std::numeric_limits<size_t>::max();
113b60736ecSDimitry Andric   size_t DecisionPos = std::numeric_limits<size_t>::max();
114b60736ecSDimitry Andric };
115b60736ecSDimitry Andric 
116b60736ecSDimitry Andric /// An extension of the MLInlineAdvisor for the 'development' mode, targeting
117b60736ecSDimitry Andric /// the offline training scenario. Note that training happens outside of the
118b60736ecSDimitry Andric /// compiler, this facility is concerned with producing training data ("logs").
119b60736ecSDimitry Andric /// This InlineAdvisor can operate in the following modes:
120b60736ecSDimitry Andric ///
121b60736ecSDimitry Andric /// 1) collect logs for the default policy. This is useful for bootstrapping
122b60736ecSDimitry Andric /// training, which will be considerably faster by starting from a reasonable
123b60736ecSDimitry Andric /// policy.
124b60736ecSDimitry Andric ///
125b60736ecSDimitry Andric /// 2) collect logs for the ML policy, using a model from a previous
126b60736ecSDimitry Andric /// training. Potentially, that model uses internally some small random
127b60736ecSDimitry Andric /// perturbation of its weights, to induce exploration (setting this up is the
128b60736ecSDimitry Andric /// responsibility of the training algorithm). The logs would then be used to
129b60736ecSDimitry Andric /// retrain and improve on this model.
130b60736ecSDimitry Andric ///
131b60736ecSDimitry Andric /// 3) use the provided model, with no logging. This is useful for end to end
132b60736ecSDimitry Andric /// validation - the model, in this case, is a release candidate and shouldn't
133b60736ecSDimitry Andric /// have random perturbations. It is a convenience feature: rather than needing
134b60736ecSDimitry Andric /// to take the release candidate model and compile it in 'release' mode,
135b60736ecSDimitry Andric /// validate it, then potentially discard it, it's easier to just pass the model
136b60736ecSDimitry Andric /// to the compiler, albeit compilation would be slower, as a one-off. Once the
137b60736ecSDimitry Andric /// model behaves satisfactorily, it can be compiled AOT, for efficiency, in
138b60736ecSDimitry Andric /// release mode. The expectation is that a well-trained model provides a good
139b60736ecSDimitry Andric /// policy over a sufficiently diverse codebase, over many changes (i.e.
140b60736ecSDimitry Andric /// training happens seldom).
141b60736ecSDimitry Andric class DevelopmentModeMLInlineAdvisor : public MLInlineAdvisor {
142b60736ecSDimitry Andric public:
143b60736ecSDimitry Andric   DevelopmentModeMLInlineAdvisor(
144b60736ecSDimitry Andric       Module &M, ModuleAnalysisManager &MAM,
145b60736ecSDimitry Andric       std::unique_ptr<MLModelRunner> ModelRunner,
1466f8fc217SDimitry Andric       std::function<bool(CallBase &)> GetDefaultAdvice,
147b60736ecSDimitry Andric       std::unique_ptr<TrainingLogger> Logger);
148b60736ecSDimitry Andric 
149b60736ecSDimitry Andric   size_t getTotalSizeEstimate();
150b60736ecSDimitry Andric 
updateNativeSizeEstimate(int64_t Change)151b60736ecSDimitry Andric   void updateNativeSizeEstimate(int64_t Change) {
152b60736ecSDimitry Andric     *CurrentNativeSize += Change;
153b60736ecSDimitry Andric   }
154b60736ecSDimitry Andric   void resetNativeSize(Function *F) {
155344a3780SDimitry Andric     PreservedAnalyses PA = PreservedAnalyses::all();
156344a3780SDimitry Andric     PA.abandon<InlineSizeEstimatorAnalysis>();
157344a3780SDimitry Andric     FAM.invalidate(*F, PA);
158b60736ecSDimitry Andric   }
159b60736ecSDimitry Andric 
160b60736ecSDimitry Andric   std::unique_ptr<MLInlineAdvice>
161b60736ecSDimitry Andric   getAdviceFromModel(CallBase &CB, OptimizationRemarkEmitter &ORE) override;
162b60736ecSDimitry Andric 
163e3b55780SDimitry Andric   std::optional<size_t> getNativeSizeEstimate(const Function &F) const;
164b60736ecSDimitry Andric 
165b60736ecSDimitry Andric private:
166b60736ecSDimitry Andric   bool isLogging() const { return !!Logger; }
167b60736ecSDimitry Andric   std::unique_ptr<MLInlineAdvice> getMandatoryAdviceImpl(CallBase &CB) override;
168b60736ecSDimitry Andric 
169b60736ecSDimitry Andric   const bool IsDoingInference;
170b60736ecSDimitry Andric   std::unique_ptr<TrainingLogger> Logger;
171b60736ecSDimitry Andric 
172e3b55780SDimitry Andric   const std::optional<int32_t> InitialNativeSize;
173e3b55780SDimitry Andric   std::optional<int32_t> CurrentNativeSize;
174b60736ecSDimitry Andric };
175b60736ecSDimitry Andric 
176b60736ecSDimitry Andric /// A variant of MLInlineAdvice that tracks all non-trivial inlining
177b60736ecSDimitry Andric /// decisions, for training/logging.
178b60736ecSDimitry Andric class LoggingMLInlineAdvice : public MLInlineAdvice {
179b60736ecSDimitry Andric public:
180b60736ecSDimitry Andric   LoggingMLInlineAdvice(DevelopmentModeMLInlineAdvisor *Advisor, CallBase &CB,
181b60736ecSDimitry Andric                         OptimizationRemarkEmitter &ORE, bool Recommendation,
182b60736ecSDimitry Andric                         TrainingLogger &Logger,
183e3b55780SDimitry Andric                         std::optional<size_t> CallerSizeEstimateBefore,
184e3b55780SDimitry Andric                         std::optional<size_t> CalleeSizeEstimateBefore,
185b60736ecSDimitry Andric                         bool DefaultDecision, bool Mandatory = false)
186b60736ecSDimitry Andric       : MLInlineAdvice(Advisor, CB, ORE, Recommendation), Logger(Logger),
187b60736ecSDimitry Andric         CallerSizeEstimateBefore(CallerSizeEstimateBefore),
188b60736ecSDimitry Andric         CalleeSizeEstimateBefore(CalleeSizeEstimateBefore),
189b60736ecSDimitry Andric         DefaultDecision(DefaultDecision), Mandatory(Mandatory) {}
190b60736ecSDimitry Andric 
191b60736ecSDimitry Andric   virtual ~LoggingMLInlineAdvice() = default;
192b60736ecSDimitry Andric 
193b60736ecSDimitry Andric private:
194b60736ecSDimitry Andric   DevelopmentModeMLInlineAdvisor *getAdvisor() const {
195b60736ecSDimitry Andric     return static_cast<DevelopmentModeMLInlineAdvisor *>(Advisor);
196b60736ecSDimitry Andric   }
197b60736ecSDimitry Andric   void recordInliningImpl() override {
198b60736ecSDimitry Andric     MLInlineAdvice::recordInliningImpl();
199b60736ecSDimitry Andric     getAdvisor()->resetNativeSize(Caller);
200b60736ecSDimitry Andric     int Reward = std::numeric_limits<int>::max();
201b60736ecSDimitry Andric     if (InlineSizeEstimatorAnalysis::isEvaluatorRequested() &&
202b60736ecSDimitry Andric         !getAdvisor()->isForcedToStop()) {
203b60736ecSDimitry Andric       int NativeSizeAfter = *getAdvisor()->getNativeSizeEstimate(*Caller) +
204b60736ecSDimitry Andric                             *CalleeSizeEstimateBefore;
205b60736ecSDimitry Andric       Reward = NativeSizeAfter -
206b60736ecSDimitry Andric                (*CallerSizeEstimateBefore + *CalleeSizeEstimateBefore);
207b60736ecSDimitry Andric       getAdvisor()->updateNativeSizeEstimate(Reward);
208b60736ecSDimitry Andric     }
209b60736ecSDimitry Andric     log(Reward, /*Success=*/true);
210b60736ecSDimitry Andric   }
211b60736ecSDimitry Andric 
212b60736ecSDimitry Andric   void recordInliningWithCalleeDeletedImpl() override {
213b60736ecSDimitry Andric     MLInlineAdvice::recordInliningWithCalleeDeletedImpl();
214b60736ecSDimitry Andric     getAdvisor()->resetNativeSize(Caller);
215b60736ecSDimitry Andric     if (InlineSizeEstimatorAnalysis::isEvaluatorRequested() &&
216b60736ecSDimitry Andric         !getAdvisor()->isForcedToStop()) {
217b60736ecSDimitry Andric       int NativeSizeAfter = *getAdvisor()->getNativeSizeEstimate(*Caller);
218b60736ecSDimitry Andric       int Reward = NativeSizeAfter -
219b60736ecSDimitry Andric                    (*CallerSizeEstimateBefore + *CalleeSizeEstimateBefore);
220b60736ecSDimitry Andric       getAdvisor()->updateNativeSizeEstimate(Reward);
221b60736ecSDimitry Andric       log(Reward, /*Success=*/true);
222c0981da4SDimitry Andric     } else {
223c0981da4SDimitry Andric       log(NoReward, /*Success=*/true);
224b60736ecSDimitry Andric     }
225b60736ecSDimitry Andric   }
226b60736ecSDimitry Andric 
227b60736ecSDimitry Andric   void recordUnsuccessfulInliningImpl(const InlineResult &Result) override {
228b60736ecSDimitry Andric     MLInlineAdvice::recordUnsuccessfulInliningImpl(Result);
229b60736ecSDimitry Andric     log(NoReward, /*Success=*/false);
230b60736ecSDimitry Andric   }
231b60736ecSDimitry Andric 
232b60736ecSDimitry Andric   void recordUnattemptedInliningImpl() override {
233b60736ecSDimitry Andric     MLInlineAdvice::recordUnattemptedInliningImpl();
234b60736ecSDimitry Andric     log(NoReward, /*Success=*/false);
235b60736ecSDimitry Andric   }
236b60736ecSDimitry Andric 
237b60736ecSDimitry Andric   void log(int64_t Reward, bool Success) {
238b60736ecSDimitry Andric     if (Mandatory)
239b60736ecSDimitry Andric       return;
240b60736ecSDimitry Andric     InlineEvent Event;
241b60736ecSDimitry Andric     Event.AdvisedDecision = isInliningRecommended();
242b60736ecSDimitry Andric     Event.DefaultDecision = DefaultDecision;
243b60736ecSDimitry Andric     Event.Effect = Success;
244b60736ecSDimitry Andric     Event.Reward = Reward;
245b60736ecSDimitry Andric     Logger.logInlineEvent(Event, getAdvisor()->getModelRunner());
246b60736ecSDimitry Andric   }
247b60736ecSDimitry Andric 
248b60736ecSDimitry Andric   static const int64_t NoReward = 0;
249b60736ecSDimitry Andric   TrainingLogger &Logger;
250e3b55780SDimitry Andric   const std::optional<size_t> CallerSizeEstimateBefore;
251e3b55780SDimitry Andric   const std::optional<size_t> CalleeSizeEstimateBefore;
252b60736ecSDimitry Andric   const int64_t DefaultDecision;
253b60736ecSDimitry Andric   const int64_t Mandatory;
254b60736ecSDimitry Andric };
255b60736ecSDimitry Andric 
25677fc4c14SDimitry Andric static const std::vector<TensorSpec> TrainingOnlyFeatures{
257b60736ecSDimitry Andric     TensorSpec::createSpec<float>(TFFeedPrefix + "discount", {1}),
258b60736ecSDimitry Andric     TensorSpec::createSpec<float>(TFFeedPrefix + "reward", {1}),
259b60736ecSDimitry Andric     TensorSpec::createSpec<int32_t>(TFFeedPrefix + "step_type", {1})};
26077fc4c14SDimitry Andric 
26177fc4c14SDimitry Andric static const std::vector<TensorSpec> getInputFeatures() {
26277fc4c14SDimitry Andric   std::vector<TensorSpec> InputSpecs;
26377fc4c14SDimitry Andric   for (size_t I = 0; I < NumberOfFeatures; ++I)
264145449b1SDimitry Andric     InputSpecs.push_back(TensorSpec::createSpec<int64_t>(
265145449b1SDimitry Andric         TFFeedPrefix + FeatureMap[I].name(), FeatureMap[I].shape()));
26677fc4c14SDimitry Andric   append_range(InputSpecs, TrainingOnlyFeatures);
26777fc4c14SDimitry Andric   return InputSpecs;
26877fc4c14SDimitry Andric }
26977fc4c14SDimitry Andric 
270b60736ecSDimitry Andric } // namespace
271b60736ecSDimitry Andric 
272b60736ecSDimitry Andric TrainingLogger::TrainingLogger(StringRef LogFileName,
273b60736ecSDimitry Andric                                const ModelUnderTrainingRunner *MUTR)
274b60736ecSDimitry Andric     : LogFileName(LogFileName), MUTR(MUTR) {
275b60736ecSDimitry Andric   // The first output is the inlining decision.
276e3b55780SDimitry Andric   std::vector<TensorSpec> FT(FeatureMap.begin(), FeatureMap.end());
277b60736ecSDimitry Andric 
278e3b55780SDimitry Andric   if (MUTR)
279e3b55780SDimitry Andric     append_range(FT, MUTR->extraOutputsForLoggingSpecs());
280b60736ecSDimitry Andric 
281b60736ecSDimitry Andric   DefaultDecisionPos = FT.size();
2827fa27ce4SDimitry Andric   FT.push_back(DefaultDecisionSpec);
283b60736ecSDimitry Andric 
284b60736ecSDimitry Andric   DecisionPos = FT.size();
2857fa27ce4SDimitry Andric   FT.push_back(InlineDecisionSpec);
286e3b55780SDimitry Andric   std::error_code EC;
287e3b55780SDimitry Andric   auto OS = std::make_unique<raw_fd_ostream>(TrainingLog, EC);
288e3b55780SDimitry Andric   if (EC)
289e3b55780SDimitry Andric     dbgs() << (EC.message() + ":" + TrainingLog);
290b60736ecSDimitry Andric 
291b60736ecSDimitry Andric   L = std::make_unique<Logger>(
292e3b55780SDimitry Andric       std::move(OS), FT, TensorSpec::createSpec<int64_t>(RewardName, {1}),
293b60736ecSDimitry Andric       InlineSizeEstimatorAnalysis::isEvaluatorRequested());
294e3b55780SDimitry Andric   L->switchContext("");
295b60736ecSDimitry Andric }
296b60736ecSDimitry Andric 
297b60736ecSDimitry Andric /// Log one inlining event.
298b60736ecSDimitry Andric void TrainingLogger::logInlineEvent(const InlineEvent &Event,
299b60736ecSDimitry Andric                                     const MLModelRunner &ModelRunner) {
300e3b55780SDimitry Andric   L->startObservation();
301b60736ecSDimitry Andric   size_t CurrentFeature = 0;
302e3b55780SDimitry Andric   for (; CurrentFeature < NumberOfFeatures; ++CurrentFeature)
303e3b55780SDimitry Andric     L->logTensorValue(CurrentFeature,
304e3b55780SDimitry Andric                       reinterpret_cast<const char *>(
305e3b55780SDimitry Andric                           ModelRunner.getTensorUntyped(CurrentFeature)));
306b60736ecSDimitry Andric 
307e3b55780SDimitry Andric   if (MUTR)
308e3b55780SDimitry Andric     for (size_t I = 0; I < MUTR->extraOutputsForLoggingSpecs().size(); ++I) {
309b60736ecSDimitry Andric       const char *RawData =
310e3b55780SDimitry Andric           reinterpret_cast<const char *>(MUTR->getUntypedExtraOutputValue(I));
311e3b55780SDimitry Andric       L->logTensorValue(CurrentFeature, RawData);
312b60736ecSDimitry Andric       ++CurrentFeature;
313b60736ecSDimitry Andric     }
314b60736ecSDimitry Andric 
315b60736ecSDimitry Andric   assert(CurrentFeature == DefaultDecisionPos);
316e3b55780SDimitry Andric   L->logTensorValue(DefaultDecisionPos,
317e3b55780SDimitry Andric                     reinterpret_cast<const char *>(&Event.DefaultDecision));
318e3b55780SDimitry Andric   L->logTensorValue(DecisionPos,
319e3b55780SDimitry Andric                     reinterpret_cast<const char *>(&Event.AdvisedDecision));
320e3b55780SDimitry Andric   L->endObservation();
321b60736ecSDimitry Andric   if (InlineSizeEstimatorAnalysis::isEvaluatorRequested())
322e3b55780SDimitry Andric     L->logReward(Event.Reward);
323b60736ecSDimitry Andric 
324b60736ecSDimitry Andric   // For debugging / later use
325b60736ecSDimitry Andric   Effects.push_back(Event.Effect);
326b60736ecSDimitry Andric }
327b60736ecSDimitry Andric 
328b60736ecSDimitry Andric DevelopmentModeMLInlineAdvisor::DevelopmentModeMLInlineAdvisor(
329b60736ecSDimitry Andric     Module &M, ModuleAnalysisManager &MAM,
330b60736ecSDimitry Andric     std::unique_ptr<MLModelRunner> ModelRunner,
3316f8fc217SDimitry Andric     std::function<bool(CallBase &)> GetDefaultAdvice,
332b60736ecSDimitry Andric     std::unique_ptr<TrainingLogger> Logger)
3337fa27ce4SDimitry Andric     : MLInlineAdvisor(M, MAM, std::move(ModelRunner), GetDefaultAdvice),
3346f8fc217SDimitry Andric       IsDoingInference(isa<ModelUnderTrainingRunner>(getModelRunner())),
335b60736ecSDimitry Andric       Logger(std::move(Logger)),
336b60736ecSDimitry Andric       InitialNativeSize(isLogging() ? getTotalSizeEstimate() : 0),
337b60736ecSDimitry Andric       CurrentNativeSize(InitialNativeSize) {
338b60736ecSDimitry Andric   // We cannot have the case of neither inference nor logging.
339b60736ecSDimitry Andric   assert(IsDoingInference || isLogging());
340b60736ecSDimitry Andric }
341b60736ecSDimitry Andric 
342e3b55780SDimitry Andric std::optional<size_t>
343b60736ecSDimitry Andric DevelopmentModeMLInlineAdvisor::getNativeSizeEstimate(const Function &F) const {
344b60736ecSDimitry Andric   if (!InlineSizeEstimatorAnalysis::isEvaluatorRequested())
345e3b55780SDimitry Andric     return std::nullopt;
346b60736ecSDimitry Andric   auto &R =
347b60736ecSDimitry Andric       FAM.getResult<InlineSizeEstimatorAnalysis>(const_cast<Function &>(F));
348b60736ecSDimitry Andric   if (!R) {
349b60736ecSDimitry Andric     F.getParent()->getContext().emitError(
350b60736ecSDimitry Andric         "Native size estimator is not present.");
351b60736ecSDimitry Andric     return 0;
352b60736ecSDimitry Andric   }
353b60736ecSDimitry Andric   return *R;
354b60736ecSDimitry Andric }
355b60736ecSDimitry Andric 
356b60736ecSDimitry Andric std::unique_ptr<MLInlineAdvice>
357b60736ecSDimitry Andric DevelopmentModeMLInlineAdvisor::getMandatoryAdviceImpl(CallBase &CB) {
358b60736ecSDimitry Andric   return std::make_unique<LoggingMLInlineAdvice>(
359b60736ecSDimitry Andric       /*Advisor=*/this,
360b60736ecSDimitry Andric       /*CB=*/CB, /*ORE=*/getCallerORE(CB), /*Recommendation=*/true,
361b60736ecSDimitry Andric       /*Logger=*/*Logger,
362b60736ecSDimitry Andric       /*CallerSizeEstimateBefore=*/getNativeSizeEstimate(*CB.getCaller()),
363b60736ecSDimitry Andric       /*CalleeSizeEstimateBefore=*/
364b60736ecSDimitry Andric       getNativeSizeEstimate(*CB.getCalledFunction()),
365b60736ecSDimitry Andric       /*DefaultDecision=*/true, /*Mandatory*/ true);
366b60736ecSDimitry Andric }
367b60736ecSDimitry Andric 
368b60736ecSDimitry Andric std::unique_ptr<MLInlineAdvice>
369b60736ecSDimitry Andric DevelopmentModeMLInlineAdvisor::getAdviceFromModel(
370b60736ecSDimitry Andric     CallBase &CB, OptimizationRemarkEmitter &ORE) {
371b60736ecSDimitry Andric   if (IsDoingInference && !isLogging())
372b60736ecSDimitry Andric     return MLInlineAdvisor::getAdviceFromModel(CB, ORE);
373b60736ecSDimitry Andric 
374b60736ecSDimitry Andric   bool DefaultAdvice = GetDefaultAdvice(CB);
37577fc4c14SDimitry Andric   auto Recommendation =
37677fc4c14SDimitry Andric       IsDoingInference ? static_cast<bool>(ModelRunner->evaluate<int64_t>())
37777fc4c14SDimitry Andric                        : DefaultAdvice;
378b60736ecSDimitry Andric   return std::make_unique<LoggingMLInlineAdvice>(
379b60736ecSDimitry Andric       /*Advisor=*/this,
380b60736ecSDimitry Andric       /*CB=*/CB, /*ORE=*/ORE, /*Recommendation=*/Recommendation,
381b60736ecSDimitry Andric       /*Logger=*/*Logger,
382b60736ecSDimitry Andric       /*CallerSizeEstimateBefore=*/getNativeSizeEstimate(*CB.getCaller()),
383b60736ecSDimitry Andric       /*CalleeSizeEstimateBefore=*/
384b60736ecSDimitry Andric       getNativeSizeEstimate(*CB.getCalledFunction()),
385b60736ecSDimitry Andric       /*DefaultDecision=*/DefaultAdvice);
386b60736ecSDimitry Andric }
387b60736ecSDimitry Andric 
388b60736ecSDimitry Andric size_t DevelopmentModeMLInlineAdvisor::getTotalSizeEstimate() {
389b60736ecSDimitry Andric   if (!InlineSizeEstimatorAnalysis::isEvaluatorRequested())
390b60736ecSDimitry Andric     return 0;
391b60736ecSDimitry Andric   size_t Ret = 0;
392b60736ecSDimitry Andric   for (auto &F : M) {
393b60736ecSDimitry Andric     if (F.isDeclaration())
394b60736ecSDimitry Andric       continue;
395b60736ecSDimitry Andric     Ret += *getNativeSizeEstimate(F);
396b60736ecSDimitry Andric   }
397b60736ecSDimitry Andric   return Ret;
398b60736ecSDimitry Andric }
399b60736ecSDimitry Andric 
400b60736ecSDimitry Andric std::unique_ptr<InlineAdvisor> llvm::getDevelopmentModeAdvisor(
401b60736ecSDimitry Andric     Module &M, ModuleAnalysisManager &MAM,
402b60736ecSDimitry Andric     std::function<bool(CallBase &)> GetDefaultAdvice) {
403b60736ecSDimitry Andric   auto &Ctx = M.getContext();
404b60736ecSDimitry Andric   std::unique_ptr<MLModelRunner> Runner;
405b60736ecSDimitry Andric   if (TFModelUnderTrainingPath.empty())
40677fc4c14SDimitry Andric     Runner.reset(new NoInferenceModelRunner(Ctx, getInputFeatures()));
4076f8fc217SDimitry Andric   else
4086f8fc217SDimitry Andric     Runner = ModelUnderTrainingRunner::createAndEnsureValid(
4096f8fc217SDimitry Andric         Ctx, TFModelUnderTrainingPath, DecisionName, getInputFeatures(),
4106f8fc217SDimitry Andric         TFOutputSpecOverride);
4116f8fc217SDimitry Andric   if (!Runner)
412b60736ecSDimitry Andric     return nullptr;
413b60736ecSDimitry Andric   std::unique_ptr<TrainingLogger> Logger;
414b60736ecSDimitry Andric   if (!TrainingLog.empty())
4156f8fc217SDimitry Andric     Logger = std::make_unique<TrainingLogger>(
4166f8fc217SDimitry Andric         TrainingLog, dyn_cast<ModelUnderTrainingRunner>(Runner.get()));
417b60736ecSDimitry Andric 
418b60736ecSDimitry Andric   return std::make_unique<DevelopmentModeMLInlineAdvisor>(
4196f8fc217SDimitry Andric       M, MAM, std::move(Runner), GetDefaultAdvice, std::move(Logger));
420b60736ecSDimitry Andric }
421e3b55780SDimitry Andric #endif // defined(LLVM_HAVE_TFLITE)
422