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