1af732203SDimitry Andric //===- DevelopmentModeInlineAdvisor.cpp - runtime-loadable model runner  --===//
2af732203SDimitry Andric //
3af732203SDimitry Andric //                     The LLVM Compiler Infrastructure
4af732203SDimitry Andric //
5af732203SDimitry Andric // This file is distributed under the University of Illinois Open Source
6af732203SDimitry Andric // License. See LICENSE.TXT for details.
7af732203SDimitry Andric //
8af732203SDimitry Andric //===----------------------------------------------------------------------===//
9af732203SDimitry Andric //
10af732203SDimitry Andric // This file implements a model runner using Tensorflow C APIs, allowing the
11af732203SDimitry Andric // loading of a model from a command line option.
12af732203SDimitry Andric //
13af732203SDimitry Andric //===----------------------------------------------------------------------===//
14af732203SDimitry Andric #include "llvm/Config/config.h"
15af732203SDimitry Andric #if defined(LLVM_HAVE_TF_API)
16af732203SDimitry Andric 
17af732203SDimitry Andric #include "llvm/Analysis/CallGraph.h"
18af732203SDimitry Andric #include "llvm/Analysis/InlineSizeEstimatorAnalysis.h"
19af732203SDimitry Andric #include "llvm/Analysis/MLInlineAdvisor.h"
20af732203SDimitry Andric #include "llvm/Analysis/Utils/TFUtils.h"
21af732203SDimitry Andric #include "llvm/IR/LLVMContext.h"
22af732203SDimitry Andric #include "llvm/Support/CommandLine.h"
23af732203SDimitry Andric #include "llvm/Support/ManagedStatic.h"
24af732203SDimitry Andric 
25af732203SDimitry Andric #include <vector>
26af732203SDimitry Andric 
27af732203SDimitry Andric using namespace llvm;
28af732203SDimitry Andric 
29af732203SDimitry Andric static cl::opt<std::string> TrainingLog(
30af732203SDimitry Andric     "training-log", cl::Hidden,
31af732203SDimitry Andric     cl::desc("Path where the development - mode inlining log is saved."));
32af732203SDimitry Andric 
33af732203SDimitry Andric static cl::opt<std::string> TFModelUnderTrainingPath(
34af732203SDimitry Andric     "ml-inliner-model-under-training", cl::Hidden,
35af732203SDimitry Andric     cl::desc(R"(Path to SavedModel from the previous training iteration.
36af732203SDimitry Andric The directory is also expected to contain a JSON specification of the
37af732203SDimitry Andric outputs expected to be logged, where the first entry must be the
38af732203SDimitry Andric inlining decision. The file containing the specification should be
39af732203SDimitry Andric called output_spec.json. The expected JSON value is an array of
40af732203SDimitry Andric dictionaries. Each dictionary should have 2 keys:
41af732203SDimitry Andric 
42af732203SDimitry Andric - "tensor_spec, followed by the TensorSpec description of the
43af732203SDimitry Andric output; and
44af732203SDimitry Andric - "logging_name", a string indicating the name to use when
45af732203SDimitry Andric logging the output values.
46af732203SDimitry Andric 
47af732203SDimitry Andric Example:
48af732203SDimitry Andric [
49af732203SDimitry Andric   {
50af732203SDimitry Andric     "logging_name" : "some_name",
51af732203SDimitry Andric     "tensor_spec" : {
52af732203SDimitry Andric       "name" : "model_name",
53af732203SDimitry Andric       "port" : 0,
54af732203SDimitry Andric       "shape" : [2, 3],
55af732203SDimitry Andric       "type" : "float"
56af732203SDimitry Andric       }
57af732203SDimitry Andric   }
58af732203SDimitry Andric ]
59af732203SDimitry Andric 
60af732203SDimitry Andric The first value must always correspond to the decision.)"));
61af732203SDimitry Andric 
62af732203SDimitry Andric static cl::opt<std::string> TFOutputSpecOverride(
63af732203SDimitry Andric     "ml-inliner-output-spec-override", cl::Hidden,
64af732203SDimitry Andric     cl::desc("Override the path to the output spec json file. See "
65af732203SDimitry Andric              "-ml-inliner-model-under-training documentation for the "
66af732203SDimitry Andric              "specification of that file."));
67af732203SDimitry Andric 
68af732203SDimitry Andric static cl::opt<std::string> TFFeedPrefix("ml-inliner-trained-model-feed-prefix",
69af732203SDimitry Andric                                          cl::Hidden, cl::init("action_"),
70af732203SDimitry Andric                                          cl::desc("Prefix for feature names."));
71af732203SDimitry Andric 
72af732203SDimitry Andric namespace {
73af732203SDimitry Andric /// An InlineEvent, used by TrainingLogger.
74af732203SDimitry Andric struct InlineEvent {
75af732203SDimitry Andric   /// What the default policy's decision would have been.
76af732203SDimitry Andric   int64_t DefaultDecision = 0;
77af732203SDimitry Andric 
78af732203SDimitry Andric   /// What we advised. When training off the default policy, this is the same as
79af732203SDimitry Andric   /// DefaultDecision.
80af732203SDimitry Andric   int64_t AdvisedDecision = 0;
81af732203SDimitry Andric 
82af732203SDimitry Andric   /// What actually happened. This would be 'false' in the case of an inline
83af732203SDimitry Andric   /// error, even if AdvisedDecision were true, otherwise it agrees with
84af732203SDimitry Andric   /// AdvisedDecision.
85af732203SDimitry Andric   bool Effect = false;
86af732203SDimitry Andric 
87af732203SDimitry Andric   /// What the change in size was: size_after - size_before
88af732203SDimitry Andric   int64_t Reward = 0;
89af732203SDimitry Andric };
90af732203SDimitry Andric 
91af732203SDimitry Andric /// Collect data we may use for training a model, and write it as a textual
92af732203SDimitry Andric /// Tensorflow SequenceExample
93af732203SDimitry Andric /// (https://www.tensorflow.org/api_docs/python/tf/train/SequenceExample)
94af732203SDimitry Andric /// protobuf (https://developers.google.com/protocol-buffers).
95af732203SDimitry Andric /// Because this is a protobuf, we cannot just stream the events as they come.
96af732203SDimitry Andric /// Internally, TrainingLogger stores data in column-major format, because that
97af732203SDimitry Andric /// lines up with how TF SequenceExample represents it.
98af732203SDimitry Andric class ModelUnderTrainingRunner;
99af732203SDimitry Andric class TrainingLogger final {
100af732203SDimitry Andric public:
101af732203SDimitry Andric   TrainingLogger(StringRef LogFileName, const ModelUnderTrainingRunner *MUTR);
102af732203SDimitry Andric 
103af732203SDimitry Andric   /// Log one inlining event.
104af732203SDimitry Andric   void logInlineEvent(const InlineEvent &Event,
105af732203SDimitry Andric                       const MLModelRunner &ModelRunner);
106af732203SDimitry Andric 
107af732203SDimitry Andric   /// Print the stored tensors.
108af732203SDimitry Andric   void print();
109af732203SDimitry Andric 
110af732203SDimitry Andric private:
111af732203SDimitry Andric   StringRef LogFileName;
112af732203SDimitry Andric   const ModelUnderTrainingRunner *const MUTR;
113af732203SDimitry Andric   std::unique_ptr<Logger> L;
114af732203SDimitry Andric   std::vector<bool> Effects;
115af732203SDimitry Andric   /// There's at least one output. We'll set this to a different value if MUTR
116af732203SDimitry Andric   /// is avaliable.
117af732203SDimitry Andric   size_t OutputCount = 1;
118af732203SDimitry Andric   /// Set these 2 clearly OOB, to make sure we set them later.
119af732203SDimitry Andric   size_t DefaultDecisionPos = std::numeric_limits<size_t>::max();
120af732203SDimitry Andric   size_t DecisionPos = std::numeric_limits<size_t>::max();
121af732203SDimitry Andric };
122af732203SDimitry Andric 
123af732203SDimitry Andric /// An extension of the MLInlineAdvisor for the 'development' mode, targeting
124af732203SDimitry Andric /// the offline training scenario. Note that training happens outside of the
125af732203SDimitry Andric /// compiler, this facility is concerned with producing training data ("logs").
126af732203SDimitry Andric /// This InlineAdvisor can operate in the following modes:
127af732203SDimitry Andric ///
128af732203SDimitry Andric /// 1) collect logs for the default policy. This is useful for bootstrapping
129af732203SDimitry Andric /// training, which will be considerably faster by starting from a reasonable
130af732203SDimitry Andric /// policy.
131af732203SDimitry Andric ///
132af732203SDimitry Andric /// 2) collect logs for the ML policy, using a model from a previous
133af732203SDimitry Andric /// training. Potentially, that model uses internally some small random
134af732203SDimitry Andric /// perturbation of its weights, to induce exploration (setting this up is the
135af732203SDimitry Andric /// responsibility of the training algorithm). The logs would then be used to
136af732203SDimitry Andric /// retrain and improve on this model.
137af732203SDimitry Andric ///
138af732203SDimitry Andric /// 3) use the provided model, with no logging. This is useful for end to end
139af732203SDimitry Andric /// validation - the model, in this case, is a release candidate and shouldn't
140af732203SDimitry Andric /// have random perturbations. It is a convenience feature: rather than needing
141af732203SDimitry Andric /// to take the release candidate model and compile it in 'release' mode,
142af732203SDimitry Andric /// validate it, then potentially discard it, it's easier to just pass the model
143af732203SDimitry Andric /// to the compiler, albeit compilation would be slower, as a one-off. Once the
144af732203SDimitry Andric /// model behaves satisfactorily, it can be compiled AOT, for efficiency, in
145af732203SDimitry Andric /// release mode. The expectation is that a well-trained model provides a good
146af732203SDimitry Andric /// policy over a sufficiently diverse codebase, over many changes (i.e.
147af732203SDimitry Andric /// training happens seldom).
148af732203SDimitry Andric class DevelopmentModeMLInlineAdvisor : public MLInlineAdvisor {
149af732203SDimitry Andric public:
150af732203SDimitry Andric   DevelopmentModeMLInlineAdvisor(
151af732203SDimitry Andric       Module &M, ModuleAnalysisManager &MAM,
152af732203SDimitry Andric       std::unique_ptr<MLModelRunner> ModelRunner,
153af732203SDimitry Andric       std::function<bool(CallBase &)> GetDefaultAdvice, bool IsDoingInference,
154af732203SDimitry Andric       std::unique_ptr<TrainingLogger> Logger);
155af732203SDimitry Andric 
156af732203SDimitry Andric   size_t getTotalSizeEstimate();
157af732203SDimitry Andric 
158af732203SDimitry Andric   virtual ~DevelopmentModeMLInlineAdvisor();
updateNativeSizeEstimate(int64_t Change)159af732203SDimitry Andric   void updateNativeSizeEstimate(int64_t Change) {
160af732203SDimitry Andric     *CurrentNativeSize += Change;
161af732203SDimitry Andric   }
162af732203SDimitry Andric   void resetNativeSize(Function *F) {
163*5f7ddb14SDimitry Andric     PreservedAnalyses PA = PreservedAnalyses::all();
164*5f7ddb14SDimitry Andric     PA.abandon<InlineSizeEstimatorAnalysis>();
165*5f7ddb14SDimitry Andric     FAM.invalidate(*F, PA);
166af732203SDimitry Andric   }
167af732203SDimitry Andric 
168af732203SDimitry Andric   std::unique_ptr<MLInlineAdvice>
169af732203SDimitry Andric   getAdviceFromModel(CallBase &CB, OptimizationRemarkEmitter &ORE) override;
170af732203SDimitry Andric 
171af732203SDimitry Andric   Optional<size_t> getNativeSizeEstimate(const Function &F) const;
172af732203SDimitry Andric 
173af732203SDimitry Andric private:
174af732203SDimitry Andric   bool isLogging() const { return !!Logger; }
175af732203SDimitry Andric   std::unique_ptr<MLInlineAdvice> getMandatoryAdviceImpl(CallBase &CB) override;
176af732203SDimitry Andric 
177af732203SDimitry Andric   std::function<bool(CallBase &)> GetDefaultAdvice;
178af732203SDimitry Andric   const bool IsDoingInference;
179af732203SDimitry Andric   std::unique_ptr<TrainingLogger> Logger;
180af732203SDimitry Andric 
181af732203SDimitry Andric   const Optional<int32_t> InitialNativeSize;
182af732203SDimitry Andric   Optional<int32_t> CurrentNativeSize;
183af732203SDimitry Andric };
184af732203SDimitry Andric 
185af732203SDimitry Andric /// A variant of MLInlineAdvice that tracks all non-trivial inlining
186af732203SDimitry Andric /// decisions, for training/logging.
187af732203SDimitry Andric class LoggingMLInlineAdvice : public MLInlineAdvice {
188af732203SDimitry Andric public:
189af732203SDimitry Andric   LoggingMLInlineAdvice(DevelopmentModeMLInlineAdvisor *Advisor, CallBase &CB,
190af732203SDimitry Andric                         OptimizationRemarkEmitter &ORE, bool Recommendation,
191af732203SDimitry Andric                         TrainingLogger &Logger,
192af732203SDimitry Andric                         Optional<size_t> CallerSizeEstimateBefore,
193af732203SDimitry Andric                         Optional<size_t> CalleeSizeEstimateBefore,
194af732203SDimitry Andric                         bool DefaultDecision, bool Mandatory = false)
195af732203SDimitry Andric       : MLInlineAdvice(Advisor, CB, ORE, Recommendation), Logger(Logger),
196af732203SDimitry Andric         CallerSizeEstimateBefore(CallerSizeEstimateBefore),
197af732203SDimitry Andric         CalleeSizeEstimateBefore(CalleeSizeEstimateBefore),
198af732203SDimitry Andric         DefaultDecision(DefaultDecision), Mandatory(Mandatory) {}
199af732203SDimitry Andric 
200af732203SDimitry Andric   virtual ~LoggingMLInlineAdvice() = default;
201af732203SDimitry Andric 
202af732203SDimitry Andric private:
203af732203SDimitry Andric   DevelopmentModeMLInlineAdvisor *getAdvisor() const {
204af732203SDimitry Andric     return static_cast<DevelopmentModeMLInlineAdvisor *>(Advisor);
205af732203SDimitry Andric   }
206af732203SDimitry Andric   void recordInliningImpl() override {
207af732203SDimitry Andric     MLInlineAdvice::recordInliningImpl();
208af732203SDimitry Andric     getAdvisor()->resetNativeSize(Caller);
209af732203SDimitry Andric     int Reward = std::numeric_limits<int>::max();
210af732203SDimitry Andric     if (InlineSizeEstimatorAnalysis::isEvaluatorRequested() &&
211af732203SDimitry Andric         !getAdvisor()->isForcedToStop()) {
212af732203SDimitry Andric       int NativeSizeAfter = *getAdvisor()->getNativeSizeEstimate(*Caller) +
213af732203SDimitry Andric                             *CalleeSizeEstimateBefore;
214af732203SDimitry Andric       Reward = NativeSizeAfter -
215af732203SDimitry Andric                (*CallerSizeEstimateBefore + *CalleeSizeEstimateBefore);
216af732203SDimitry Andric       getAdvisor()->updateNativeSizeEstimate(Reward);
217af732203SDimitry Andric     }
218af732203SDimitry Andric     log(Reward, /*Success=*/true);
219af732203SDimitry Andric   }
220af732203SDimitry Andric 
221af732203SDimitry Andric   void recordInliningWithCalleeDeletedImpl() override {
222af732203SDimitry Andric     MLInlineAdvice::recordInliningWithCalleeDeletedImpl();
223af732203SDimitry Andric     getAdvisor()->resetNativeSize(Caller);
224af732203SDimitry Andric     if (InlineSizeEstimatorAnalysis::isEvaluatorRequested() &&
225af732203SDimitry Andric         !getAdvisor()->isForcedToStop()) {
226af732203SDimitry Andric       int NativeSizeAfter = *getAdvisor()->getNativeSizeEstimate(*Caller);
227af732203SDimitry Andric       int Reward = NativeSizeAfter -
228af732203SDimitry Andric                    (*CallerSizeEstimateBefore + *CalleeSizeEstimateBefore);
229af732203SDimitry Andric       getAdvisor()->updateNativeSizeEstimate(Reward);
230af732203SDimitry Andric       log(Reward, /*Success=*/true);
231af732203SDimitry Andric     }
232af732203SDimitry Andric   }
233af732203SDimitry Andric 
234af732203SDimitry Andric   void recordUnsuccessfulInliningImpl(const InlineResult &Result) override {
235af732203SDimitry Andric     MLInlineAdvice::recordUnsuccessfulInliningImpl(Result);
236af732203SDimitry Andric     log(NoReward, /*Success=*/false);
237af732203SDimitry Andric   }
238af732203SDimitry Andric 
239af732203SDimitry Andric   void recordUnattemptedInliningImpl() override {
240af732203SDimitry Andric     MLInlineAdvice::recordUnattemptedInliningImpl();
241af732203SDimitry Andric     log(NoReward, /*Success=*/false);
242af732203SDimitry Andric   }
243af732203SDimitry Andric 
244af732203SDimitry Andric   void log(int64_t Reward, bool Success) {
245af732203SDimitry Andric     if (Mandatory)
246af732203SDimitry Andric       return;
247af732203SDimitry Andric     InlineEvent Event;
248af732203SDimitry Andric     Event.AdvisedDecision = isInliningRecommended();
249af732203SDimitry Andric     Event.DefaultDecision = DefaultDecision;
250af732203SDimitry Andric     Event.Effect = Success;
251af732203SDimitry Andric     Event.Reward = Reward;
252af732203SDimitry Andric     Logger.logInlineEvent(Event, getAdvisor()->getModelRunner());
253af732203SDimitry Andric   }
254af732203SDimitry Andric 
255af732203SDimitry Andric   static const int64_t NoReward = 0;
256af732203SDimitry Andric   TrainingLogger &Logger;
257af732203SDimitry Andric   const Optional<size_t> CallerSizeEstimateBefore;
258af732203SDimitry Andric   const Optional<size_t> CalleeSizeEstimateBefore;
259af732203SDimitry Andric   const int64_t DefaultDecision;
260af732203SDimitry Andric   const int64_t Mandatory;
261af732203SDimitry Andric };
262af732203SDimitry Andric 
263af732203SDimitry Andric /// A pseudo model runner. We use it to store feature values when collecting
264af732203SDimitry Andric /// logs for the default policy, but never ask it to 'run'.
265af732203SDimitry Andric class NoInferenceModelRunner : public MLModelRunner {
266af732203SDimitry Andric public:
267af732203SDimitry Andric   NoInferenceModelRunner(LLVMContext &Ctx)
268af732203SDimitry Andric       : MLModelRunner(Ctx), Features(NumberOfFeatures) {}
269af732203SDimitry Andric   void setFeature(FeatureIndex Index, int64_t Value) override {
270af732203SDimitry Andric     Features[static_cast<int>(Index)] = Value;
271af732203SDimitry Andric   }
272af732203SDimitry Andric 
273af732203SDimitry Andric   int64_t getFeature(int Index) const override { return Features[Index]; }
274af732203SDimitry Andric   bool run() override {
275af732203SDimitry Andric     llvm_unreachable("We shouldn't call run on this model runner.");
276af732203SDimitry Andric   }
277af732203SDimitry Andric 
278af732203SDimitry Andric private:
279af732203SDimitry Andric   InlineFeatures Features;
280af732203SDimitry Andric };
281af732203SDimitry Andric 
282af732203SDimitry Andric /// ModelUnderTrainingRunner - training mode implementation. It uses TF C APIs
283af732203SDimitry Andric /// to dynamically load and evaluate a TF SavedModel
284af732203SDimitry Andric /// (https://www.tensorflow.org/guide/saved_model). Runtime performance is
285af732203SDimitry Andric /// sacrificed for ease of use while training.
286af732203SDimitry Andric class ModelUnderTrainingRunner final : public MLModelRunner {
287af732203SDimitry Andric public:
288af732203SDimitry Andric   ModelUnderTrainingRunner(LLVMContext &Ctx, const std::string &ModelPath);
289af732203SDimitry Andric 
290af732203SDimitry Andric   bool run() override;
291af732203SDimitry Andric 
292af732203SDimitry Andric   // Disallows copy and assign.
293af732203SDimitry Andric   ModelUnderTrainingRunner(const ModelUnderTrainingRunner &) = delete;
294af732203SDimitry Andric   ModelUnderTrainingRunner &
295af732203SDimitry Andric   operator=(const ModelUnderTrainingRunner &) = delete;
296af732203SDimitry Andric 
297af732203SDimitry Andric   void setFeature(FeatureIndex Index, int64_t Value) override;
298af732203SDimitry Andric   int64_t getFeature(int Index) const override;
299af732203SDimitry Andric   bool isValid() const { return !!Evaluator; }
300af732203SDimitry Andric 
301af732203SDimitry Andric   const std::vector<LoggedFeatureSpec> &outputLoggedFeatureSpecs() const {
302af732203SDimitry Andric     return OutputSpecs;
303af732203SDimitry Andric   }
304af732203SDimitry Andric 
305af732203SDimitry Andric   const Optional<TFModelEvaluator::EvaluationResult> &
306af732203SDimitry Andric   lastEvaluationResult() const {
307af732203SDimitry Andric     return LastEvaluationResult;
308af732203SDimitry Andric   }
309af732203SDimitry Andric 
310af732203SDimitry Andric private:
311af732203SDimitry Andric   std::unique_ptr<TFModelEvaluator> Evaluator;
312af732203SDimitry Andric   std::vector<LoggedFeatureSpec> OutputSpecs;
313af732203SDimitry Andric   Optional<TFModelEvaluator::EvaluationResult> LastEvaluationResult;
314af732203SDimitry Andric 
315af732203SDimitry Andric   // The training framework needs some additional features.
316af732203SDimitry Andric   const std::vector<TensorSpec> TrainingOnlyFeatures{
317af732203SDimitry Andric       TensorSpec::createSpec<int64_t>(TFFeedPrefix + "inlining_default", {1}),
318af732203SDimitry Andric       TensorSpec::createSpec<float>(TFFeedPrefix + "discount", {1}),
319af732203SDimitry Andric       TensorSpec::createSpec<float>(TFFeedPrefix + "reward", {1}),
320af732203SDimitry Andric       TensorSpec::createSpec<int32_t>(TFFeedPrefix + "step_type", {1})};
321af732203SDimitry Andric };
322af732203SDimitry Andric } // namespace
323af732203SDimitry Andric 
324af732203SDimitry Andric TrainingLogger::TrainingLogger(StringRef LogFileName,
325af732203SDimitry Andric                                const ModelUnderTrainingRunner *MUTR)
326af732203SDimitry Andric     : LogFileName(LogFileName), MUTR(MUTR) {
327af732203SDimitry Andric   // The first output is the inlining decision.
328af732203SDimitry Andric   if (MUTR)
329af732203SDimitry Andric     OutputCount = MUTR->outputLoggedFeatureSpecs().size();
330af732203SDimitry Andric   std::vector<LoggedFeatureSpec> FT;
331af732203SDimitry Andric 
332af732203SDimitry Andric   for (size_t I = 0; I < NumberOfFeatures; ++I)
333af732203SDimitry Andric     FT.push_back(
334af732203SDimitry Andric         {TensorSpec::createSpec<int64_t>(FeatureNameMap.at(I), {1}), None});
335af732203SDimitry Andric   if (MUTR && MUTR->outputLoggedFeatureSpecs().size() > 1)
336af732203SDimitry Andric     append_range(FT, drop_begin(MUTR->outputLoggedFeatureSpecs()));
337af732203SDimitry Andric 
338af732203SDimitry Andric   DefaultDecisionPos = FT.size();
339af732203SDimitry Andric   FT.push_back(
340af732203SDimitry Andric       {TensorSpec::createSpec<int64_t>(DefaultDecisionName, {1}), None});
341af732203SDimitry Andric 
342af732203SDimitry Andric   DecisionPos = FT.size();
343af732203SDimitry Andric   FT.push_back({TensorSpec::createSpec<int64_t>(DecisionName, {1}), None});
344af732203SDimitry Andric 
345af732203SDimitry Andric   L = std::make_unique<Logger>(
346af732203SDimitry Andric       FT, TensorSpec::createSpec<int64_t>(RewardName, {1}),
347af732203SDimitry Andric       InlineSizeEstimatorAnalysis::isEvaluatorRequested());
348af732203SDimitry Andric }
349af732203SDimitry Andric 
350af732203SDimitry Andric /// Log one inlining event.
351af732203SDimitry Andric void TrainingLogger::logInlineEvent(const InlineEvent &Event,
352af732203SDimitry Andric                                     const MLModelRunner &ModelRunner) {
353af732203SDimitry Andric   size_t CurrentFeature = 0;
354af732203SDimitry Andric   for (; CurrentFeature < NumberOfFeatures; ++CurrentFeature) {
355af732203SDimitry Andric     int64_t F = ModelRunner.getFeature(CurrentFeature);
356*5f7ddb14SDimitry Andric     L->logInt64Value(CurrentFeature, &F);
357af732203SDimitry Andric   }
358af732203SDimitry Andric 
359af732203SDimitry Andric   for (size_t I = 1; I < OutputCount; ++I) {
360af732203SDimitry Andric     const auto &Result = *MUTR->lastEvaluationResult();
361af732203SDimitry Andric     const char *RawData =
362af732203SDimitry Andric         reinterpret_cast<const char *>(Result.getUntypedTensorValue(I));
363*5f7ddb14SDimitry Andric     L->logSpecifiedTensorValue(CurrentFeature, RawData);
364af732203SDimitry Andric     ++CurrentFeature;
365af732203SDimitry Andric   }
366af732203SDimitry Andric 
367af732203SDimitry Andric   assert(CurrentFeature == DefaultDecisionPos);
368*5f7ddb14SDimitry Andric   L->logInt64Value(DefaultDecisionPos, &Event.DefaultDecision);
369*5f7ddb14SDimitry Andric   L->logInt64Value(DecisionPos, &Event.AdvisedDecision);
370af732203SDimitry Andric   if (InlineSizeEstimatorAnalysis::isEvaluatorRequested())
371*5f7ddb14SDimitry Andric     L->logInt64Reward(Event.Reward);
372af732203SDimitry Andric 
373af732203SDimitry Andric   // For debugging / later use
374af732203SDimitry Andric   Effects.push_back(Event.Effect);
375af732203SDimitry Andric }
376af732203SDimitry Andric 
377af732203SDimitry Andric void TrainingLogger::print() {
378af732203SDimitry Andric   std::error_code EC;
379af732203SDimitry Andric   raw_fd_ostream OutFile(LogFileName, EC);
380af732203SDimitry Andric   L->print(OutFile);
381af732203SDimitry Andric }
382af732203SDimitry Andric 
383af732203SDimitry Andric DevelopmentModeMLInlineAdvisor::DevelopmentModeMLInlineAdvisor(
384af732203SDimitry Andric     Module &M, ModuleAnalysisManager &MAM,
385af732203SDimitry Andric     std::unique_ptr<MLModelRunner> ModelRunner,
386af732203SDimitry Andric     std::function<bool(CallBase &)> GetDefaultAdvice, bool IsDoingInference,
387af732203SDimitry Andric     std::unique_ptr<TrainingLogger> Logger)
388af732203SDimitry Andric     : MLInlineAdvisor(M, MAM, std::move(ModelRunner)),
389af732203SDimitry Andric       GetDefaultAdvice(GetDefaultAdvice), IsDoingInference(IsDoingInference),
390af732203SDimitry Andric       Logger(std::move(Logger)),
391af732203SDimitry Andric       InitialNativeSize(isLogging() ? getTotalSizeEstimate() : 0),
392af732203SDimitry Andric       CurrentNativeSize(InitialNativeSize) {
393af732203SDimitry Andric   // We cannot have the case of neither inference nor logging.
394af732203SDimitry Andric   assert(IsDoingInference || isLogging());
395af732203SDimitry Andric }
396af732203SDimitry Andric 
397af732203SDimitry Andric DevelopmentModeMLInlineAdvisor::~DevelopmentModeMLInlineAdvisor() {
398af732203SDimitry Andric   if (isLogging())
399af732203SDimitry Andric     Logger->print();
400af732203SDimitry Andric }
401af732203SDimitry Andric 
402af732203SDimitry Andric Optional<size_t>
403af732203SDimitry Andric DevelopmentModeMLInlineAdvisor::getNativeSizeEstimate(const Function &F) const {
404af732203SDimitry Andric   if (!InlineSizeEstimatorAnalysis::isEvaluatorRequested())
405af732203SDimitry Andric     return None;
406af732203SDimitry Andric   auto &R =
407af732203SDimitry Andric       FAM.getResult<InlineSizeEstimatorAnalysis>(const_cast<Function &>(F));
408af732203SDimitry Andric   if (!R) {
409af732203SDimitry Andric     F.getParent()->getContext().emitError(
410af732203SDimitry Andric         "Native size estimator is not present.");
411af732203SDimitry Andric     return 0;
412af732203SDimitry Andric   }
413af732203SDimitry Andric   return *R;
414af732203SDimitry Andric }
415af732203SDimitry Andric 
416af732203SDimitry Andric std::unique_ptr<MLInlineAdvice>
417af732203SDimitry Andric DevelopmentModeMLInlineAdvisor::getMandatoryAdviceImpl(CallBase &CB) {
418af732203SDimitry Andric   return std::make_unique<LoggingMLInlineAdvice>(
419af732203SDimitry Andric       /*Advisor=*/this,
420af732203SDimitry Andric       /*CB=*/CB, /*ORE=*/getCallerORE(CB), /*Recommendation=*/true,
421af732203SDimitry Andric       /*Logger=*/*Logger,
422af732203SDimitry Andric       /*CallerSizeEstimateBefore=*/getNativeSizeEstimate(*CB.getCaller()),
423af732203SDimitry Andric       /*CalleeSizeEstimateBefore=*/
424af732203SDimitry Andric       getNativeSizeEstimate(*CB.getCalledFunction()),
425af732203SDimitry Andric       /*DefaultDecision=*/true, /*Mandatory*/ true);
426af732203SDimitry Andric }
427af732203SDimitry Andric 
428af732203SDimitry Andric std::unique_ptr<MLInlineAdvice>
429af732203SDimitry Andric DevelopmentModeMLInlineAdvisor::getAdviceFromModel(
430af732203SDimitry Andric     CallBase &CB, OptimizationRemarkEmitter &ORE) {
431af732203SDimitry Andric   if (IsDoingInference && !isLogging())
432af732203SDimitry Andric     return MLInlineAdvisor::getAdviceFromModel(CB, ORE);
433af732203SDimitry Andric 
434af732203SDimitry Andric   bool DefaultAdvice = GetDefaultAdvice(CB);
435af732203SDimitry Andric   auto Recommendation = IsDoingInference ? ModelRunner->run() : DefaultAdvice;
436af732203SDimitry Andric   return std::make_unique<LoggingMLInlineAdvice>(
437af732203SDimitry Andric       /*Advisor=*/this,
438af732203SDimitry Andric       /*CB=*/CB, /*ORE=*/ORE, /*Recommendation=*/Recommendation,
439af732203SDimitry Andric       /*Logger=*/*Logger,
440af732203SDimitry Andric       /*CallerSizeEstimateBefore=*/getNativeSizeEstimate(*CB.getCaller()),
441af732203SDimitry Andric       /*CalleeSizeEstimateBefore=*/
442af732203SDimitry Andric       getNativeSizeEstimate(*CB.getCalledFunction()),
443af732203SDimitry Andric       /*DefaultDecision=*/DefaultAdvice);
444af732203SDimitry Andric }
445af732203SDimitry Andric 
446af732203SDimitry Andric size_t DevelopmentModeMLInlineAdvisor::getTotalSizeEstimate() {
447af732203SDimitry Andric   if (!InlineSizeEstimatorAnalysis::isEvaluatorRequested())
448af732203SDimitry Andric     return 0;
449af732203SDimitry Andric   size_t Ret = 0;
450af732203SDimitry Andric   for (auto &F : M) {
451af732203SDimitry Andric     if (F.isDeclaration())
452af732203SDimitry Andric       continue;
453af732203SDimitry Andric     if (isFunctionDeleted(&F))
454af732203SDimitry Andric       continue;
455af732203SDimitry Andric     Ret += *getNativeSizeEstimate(F);
456af732203SDimitry Andric   }
457af732203SDimitry Andric   return Ret;
458af732203SDimitry Andric }
459af732203SDimitry Andric 
460af732203SDimitry Andric ModelUnderTrainingRunner::ModelUnderTrainingRunner(LLVMContext &Ctx,
461af732203SDimitry Andric                                                    const std::string &ModelPath)
462af732203SDimitry Andric     : MLModelRunner(Ctx) {
463af732203SDimitry Andric   std::vector<TensorSpec> InputSpecs;
464af732203SDimitry Andric   for (size_t I = 0; I < NumberOfFeatures; ++I)
465af732203SDimitry Andric     InputSpecs.push_back(
466af732203SDimitry Andric         TensorSpec::createSpec<int64_t>(TFFeedPrefix + FeatureNameMap[I], {1}));
467af732203SDimitry Andric   append_range(InputSpecs, TrainingOnlyFeatures);
468af732203SDimitry Andric   if (auto MaybeOutSpecs =
469af732203SDimitry Andric           loadOutputSpecs(Ctx, DecisionName, ModelPath, TFOutputSpecOverride))
470af732203SDimitry Andric     OutputSpecs = std::move(*MaybeOutSpecs);
471af732203SDimitry Andric   else
472af732203SDimitry Andric     return;
473af732203SDimitry Andric 
474af732203SDimitry Andric   Evaluator = std::make_unique<TFModelEvaluator>(
475af732203SDimitry Andric       ModelPath, InputSpecs, [&](size_t I) { return OutputSpecs[I].Spec; },
476af732203SDimitry Andric       OutputSpecs.size());
477af732203SDimitry Andric   if (!Evaluator || !Evaluator->isValid()) {
478af732203SDimitry Andric     Ctx.emitError("Failed to create inliner saved model evaluator");
479af732203SDimitry Andric     Evaluator.reset();
480af732203SDimitry Andric     return;
481af732203SDimitry Andric   }
482af732203SDimitry Andric }
483af732203SDimitry Andric 
484af732203SDimitry Andric bool ModelUnderTrainingRunner::run() {
485af732203SDimitry Andric   LastEvaluationResult = Evaluator->evaluate();
486af732203SDimitry Andric   if (!LastEvaluationResult.hasValue()) {
487af732203SDimitry Andric     Ctx.emitError("Error evaluating model.");
488af732203SDimitry Andric     return false;
489af732203SDimitry Andric   }
490af732203SDimitry Andric   int64_t Decision = *LastEvaluationResult->getTensorValue<int64_t>(0);
491af732203SDimitry Andric   return static_cast<bool>(Decision);
492af732203SDimitry Andric }
493af732203SDimitry Andric 
494af732203SDimitry Andric int64_t ModelUnderTrainingRunner::getFeature(int Index) const {
495af732203SDimitry Andric   return *Evaluator->getInput<int64_t>(Index);
496af732203SDimitry Andric }
497af732203SDimitry Andric 
498af732203SDimitry Andric void ModelUnderTrainingRunner::setFeature(FeatureIndex Index, int64_t Value) {
499af732203SDimitry Andric   size_t NumericIndex = static_cast<size_t>(Index);
500af732203SDimitry Andric   *(Evaluator->getInput<int64_t>(NumericIndex)) = Value;
501af732203SDimitry Andric }
502af732203SDimitry Andric 
503af732203SDimitry Andric std::unique_ptr<InlineAdvisor> llvm::getDevelopmentModeAdvisor(
504af732203SDimitry Andric     Module &M, ModuleAnalysisManager &MAM,
505af732203SDimitry Andric     std::function<bool(CallBase &)> GetDefaultAdvice) {
506af732203SDimitry Andric   auto &Ctx = M.getContext();
507af732203SDimitry Andric   std::unique_ptr<MLModelRunner> Runner;
508af732203SDimitry Andric   ModelUnderTrainingRunner *MUTRPtr = nullptr;
509af732203SDimitry Andric   bool IsDoingInference = false;
510af732203SDimitry Andric   if (TFModelUnderTrainingPath.empty())
511af732203SDimitry Andric     Runner.reset(new NoInferenceModelRunner(Ctx));
512af732203SDimitry Andric   else {
513af732203SDimitry Andric     auto MUTR = std::make_unique<ModelUnderTrainingRunner>(
514af732203SDimitry Andric         Ctx, TFModelUnderTrainingPath);
515af732203SDimitry Andric     if (!MUTR || !MUTR->isValid()) {
516af732203SDimitry Andric       Ctx.emitError("Could not load the policy model from the provided path");
517af732203SDimitry Andric       return nullptr;
518af732203SDimitry Andric     }
519af732203SDimitry Andric     IsDoingInference = true;
520af732203SDimitry Andric     MUTRPtr = MUTR.get();
521af732203SDimitry Andric     Runner = std::move(MUTR);
522af732203SDimitry Andric   }
523af732203SDimitry Andric   std::unique_ptr<TrainingLogger> Logger;
524af732203SDimitry Andric   if (!TrainingLog.empty())
525af732203SDimitry Andric     Logger = std::make_unique<TrainingLogger>(TrainingLog, MUTRPtr);
526af732203SDimitry Andric 
527af732203SDimitry Andric   return std::make_unique<DevelopmentModeMLInlineAdvisor>(
528af732203SDimitry Andric       M, MAM, std::move(Runner), GetDefaultAdvice, IsDoingInference,
529af732203SDimitry Andric       std::move(Logger));
530af732203SDimitry Andric }
531af732203SDimitry Andric #endif // defined(LLVM_HAVE_TF_API)
532