xref: /freebsd/contrib/llvm-project/llvm/lib/Analysis/DevelopmentModeInlineAdvisor.cpp (revision 04eeddc0aa8e0a417a16eaf9d7d095207f4a8623)
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 //===----------------------------------------------------------------------===//
13e8d8bef9SDimitry Andric #include "llvm/Config/config.h"
14*04eeddc0SDimitry Andric #include "llvm/Support/Casting.h"
15e8d8bef9SDimitry Andric #if defined(LLVM_HAVE_TF_API)
16e8d8bef9SDimitry Andric 
17*04eeddc0SDimitry 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"
24e8d8bef9SDimitry Andric #include "llvm/IR/LLVMContext.h"
25e8d8bef9SDimitry Andric #include "llvm/Support/CommandLine.h"
26e8d8bef9SDimitry Andric #include "llvm/Support/ManagedStatic.h"
27e8d8bef9SDimitry Andric 
28e8d8bef9SDimitry Andric #include <vector>
29e8d8bef9SDimitry Andric 
30e8d8bef9SDimitry Andric using namespace llvm;
31e8d8bef9SDimitry Andric 
32e8d8bef9SDimitry Andric static cl::opt<std::string> TrainingLog(
33e8d8bef9SDimitry Andric     "training-log", cl::Hidden,
34e8d8bef9SDimitry Andric     cl::desc("Path where the development - mode inlining log is saved."));
35e8d8bef9SDimitry Andric 
36e8d8bef9SDimitry Andric static cl::opt<std::string> TFModelUnderTrainingPath(
37e8d8bef9SDimitry Andric     "ml-inliner-model-under-training", cl::Hidden,
38e8d8bef9SDimitry Andric     cl::desc(R"(Path to SavedModel from the previous training iteration.
39e8d8bef9SDimitry Andric The directory is also expected to contain a JSON specification of the
40e8d8bef9SDimitry Andric outputs expected to be logged, where the first entry must be the
41e8d8bef9SDimitry Andric inlining decision. The file containing the specification should be
42e8d8bef9SDimitry Andric called output_spec.json. The expected JSON value is an array of
43e8d8bef9SDimitry Andric dictionaries. Each dictionary should have 2 keys:
44e8d8bef9SDimitry Andric 
45e8d8bef9SDimitry Andric - "tensor_spec, followed by the TensorSpec description of the
46e8d8bef9SDimitry Andric output; and
47e8d8bef9SDimitry Andric - "logging_name", a string indicating the name to use when
48e8d8bef9SDimitry Andric logging the output values.
49e8d8bef9SDimitry Andric 
50e8d8bef9SDimitry Andric Example:
51e8d8bef9SDimitry Andric [
52e8d8bef9SDimitry Andric   {
53e8d8bef9SDimitry Andric     "logging_name" : "some_name",
54e8d8bef9SDimitry Andric     "tensor_spec" : {
55e8d8bef9SDimitry Andric       "name" : "model_name",
56e8d8bef9SDimitry Andric       "port" : 0,
57e8d8bef9SDimitry Andric       "shape" : [2, 3],
58e8d8bef9SDimitry Andric       "type" : "float"
59e8d8bef9SDimitry Andric       }
60e8d8bef9SDimitry Andric   }
61e8d8bef9SDimitry Andric ]
62e8d8bef9SDimitry Andric 
63e8d8bef9SDimitry Andric The first value must always correspond to the decision.)"));
64e8d8bef9SDimitry Andric 
65e8d8bef9SDimitry Andric static cl::opt<std::string> TFOutputSpecOverride(
66e8d8bef9SDimitry Andric     "ml-inliner-output-spec-override", cl::Hidden,
67e8d8bef9SDimitry Andric     cl::desc("Override the path to the output spec json file. See "
68e8d8bef9SDimitry Andric              "-ml-inliner-model-under-training documentation for the "
69e8d8bef9SDimitry Andric              "specification of that file."));
70e8d8bef9SDimitry Andric 
71e8d8bef9SDimitry Andric static cl::opt<std::string> TFFeedPrefix("ml-inliner-trained-model-feed-prefix",
72e8d8bef9SDimitry Andric                                          cl::Hidden, cl::init("action_"),
73e8d8bef9SDimitry Andric                                          cl::desc("Prefix for feature names."));
74e8d8bef9SDimitry Andric 
75e8d8bef9SDimitry Andric namespace {
76e8d8bef9SDimitry Andric /// An InlineEvent, used by TrainingLogger.
77e8d8bef9SDimitry Andric struct InlineEvent {
78e8d8bef9SDimitry Andric   /// What the default policy's decision would have been.
79e8d8bef9SDimitry Andric   int64_t DefaultDecision = 0;
80e8d8bef9SDimitry Andric 
81e8d8bef9SDimitry Andric   /// What we advised. When training off the default policy, this is the same as
82e8d8bef9SDimitry Andric   /// DefaultDecision.
83e8d8bef9SDimitry Andric   int64_t AdvisedDecision = 0;
84e8d8bef9SDimitry Andric 
85e8d8bef9SDimitry Andric   /// What actually happened. This would be 'false' in the case of an inline
86e8d8bef9SDimitry Andric   /// error, even if AdvisedDecision were true, otherwise it agrees with
87e8d8bef9SDimitry Andric   /// AdvisedDecision.
88e8d8bef9SDimitry Andric   bool Effect = false;
89e8d8bef9SDimitry Andric 
90e8d8bef9SDimitry Andric   /// What the change in size was: size_after - size_before
91e8d8bef9SDimitry Andric   int64_t Reward = 0;
92e8d8bef9SDimitry Andric };
93e8d8bef9SDimitry Andric 
94e8d8bef9SDimitry Andric /// Collect data we may use for training a model, and write it as a textual
95e8d8bef9SDimitry Andric /// Tensorflow SequenceExample
96e8d8bef9SDimitry Andric /// (https://www.tensorflow.org/api_docs/python/tf/train/SequenceExample)
97e8d8bef9SDimitry Andric /// protobuf (https://developers.google.com/protocol-buffers).
98e8d8bef9SDimitry Andric /// Because this is a protobuf, we cannot just stream the events as they come.
99e8d8bef9SDimitry Andric /// Internally, TrainingLogger stores data in column-major format, because that
100e8d8bef9SDimitry Andric /// lines up with how TF SequenceExample represents it.
101e8d8bef9SDimitry Andric class TrainingLogger final {
102e8d8bef9SDimitry Andric public:
103e8d8bef9SDimitry Andric   TrainingLogger(StringRef LogFileName, const ModelUnderTrainingRunner *MUTR);
104e8d8bef9SDimitry Andric 
105e8d8bef9SDimitry Andric   /// Log one inlining event.
106e8d8bef9SDimitry Andric   void logInlineEvent(const InlineEvent &Event,
107e8d8bef9SDimitry Andric                       const MLModelRunner &ModelRunner);
108e8d8bef9SDimitry Andric 
109e8d8bef9SDimitry Andric   /// Print the stored tensors.
110e8d8bef9SDimitry Andric   void print();
111e8d8bef9SDimitry Andric 
112e8d8bef9SDimitry Andric private:
113e8d8bef9SDimitry Andric   StringRef LogFileName;
114e8d8bef9SDimitry Andric   const ModelUnderTrainingRunner *const MUTR;
115e8d8bef9SDimitry Andric   std::unique_ptr<Logger> L;
116*04eeddc0SDimitry Andric   BitVector Effects;
117e8d8bef9SDimitry Andric   /// There's at least one output. We'll set this to a different value if MUTR
118e8d8bef9SDimitry Andric   /// is avaliable.
119e8d8bef9SDimitry Andric   size_t OutputCount = 1;
120e8d8bef9SDimitry Andric   /// Set these 2 clearly OOB, to make sure we set them later.
121e8d8bef9SDimitry Andric   size_t DefaultDecisionPos = std::numeric_limits<size_t>::max();
122e8d8bef9SDimitry Andric   size_t DecisionPos = std::numeric_limits<size_t>::max();
123e8d8bef9SDimitry Andric };
124e8d8bef9SDimitry Andric 
125e8d8bef9SDimitry Andric /// An extension of the MLInlineAdvisor for the 'development' mode, targeting
126e8d8bef9SDimitry Andric /// the offline training scenario. Note that training happens outside of the
127e8d8bef9SDimitry Andric /// compiler, this facility is concerned with producing training data ("logs").
128e8d8bef9SDimitry Andric /// This InlineAdvisor can operate in the following modes:
129e8d8bef9SDimitry Andric ///
130e8d8bef9SDimitry Andric /// 1) collect logs for the default policy. This is useful for bootstrapping
131e8d8bef9SDimitry Andric /// training, which will be considerably faster by starting from a reasonable
132e8d8bef9SDimitry Andric /// policy.
133e8d8bef9SDimitry Andric ///
134e8d8bef9SDimitry Andric /// 2) collect logs for the ML policy, using a model from a previous
135e8d8bef9SDimitry Andric /// training. Potentially, that model uses internally some small random
136e8d8bef9SDimitry Andric /// perturbation of its weights, to induce exploration (setting this up is the
137e8d8bef9SDimitry Andric /// responsibility of the training algorithm). The logs would then be used to
138e8d8bef9SDimitry Andric /// retrain and improve on this model.
139e8d8bef9SDimitry Andric ///
140e8d8bef9SDimitry Andric /// 3) use the provided model, with no logging. This is useful for end to end
141e8d8bef9SDimitry Andric /// validation - the model, in this case, is a release candidate and shouldn't
142e8d8bef9SDimitry Andric /// have random perturbations. It is a convenience feature: rather than needing
143e8d8bef9SDimitry Andric /// to take the release candidate model and compile it in 'release' mode,
144e8d8bef9SDimitry Andric /// validate it, then potentially discard it, it's easier to just pass the model
145e8d8bef9SDimitry Andric /// to the compiler, albeit compilation would be slower, as a one-off. Once the
146e8d8bef9SDimitry Andric /// model behaves satisfactorily, it can be compiled AOT, for efficiency, in
147e8d8bef9SDimitry Andric /// release mode. The expectation is that a well-trained model provides a good
148e8d8bef9SDimitry Andric /// policy over a sufficiently diverse codebase, over many changes (i.e.
149e8d8bef9SDimitry Andric /// training happens seldom).
150e8d8bef9SDimitry Andric class DevelopmentModeMLInlineAdvisor : public MLInlineAdvisor {
151e8d8bef9SDimitry Andric public:
152e8d8bef9SDimitry Andric   DevelopmentModeMLInlineAdvisor(
153e8d8bef9SDimitry Andric       Module &M, ModuleAnalysisManager &MAM,
154e8d8bef9SDimitry Andric       std::unique_ptr<MLModelRunner> ModelRunner,
155*04eeddc0SDimitry Andric       std::function<bool(CallBase &)> GetDefaultAdvice,
156e8d8bef9SDimitry Andric       std::unique_ptr<TrainingLogger> Logger);
157e8d8bef9SDimitry Andric 
158e8d8bef9SDimitry Andric   size_t getTotalSizeEstimate();
159e8d8bef9SDimitry Andric 
160e8d8bef9SDimitry Andric   virtual ~DevelopmentModeMLInlineAdvisor();
161e8d8bef9SDimitry Andric   void updateNativeSizeEstimate(int64_t Change) {
162e8d8bef9SDimitry Andric     *CurrentNativeSize += Change;
163e8d8bef9SDimitry Andric   }
164e8d8bef9SDimitry Andric   void resetNativeSize(Function *F) {
165fe6060f1SDimitry Andric     PreservedAnalyses PA = PreservedAnalyses::all();
166fe6060f1SDimitry Andric     PA.abandon<InlineSizeEstimatorAnalysis>();
167fe6060f1SDimitry Andric     FAM.invalidate(*F, PA);
168e8d8bef9SDimitry Andric   }
169e8d8bef9SDimitry Andric 
170e8d8bef9SDimitry Andric   std::unique_ptr<MLInlineAdvice>
171e8d8bef9SDimitry Andric   getAdviceFromModel(CallBase &CB, OptimizationRemarkEmitter &ORE) override;
172e8d8bef9SDimitry Andric 
173e8d8bef9SDimitry Andric   Optional<size_t> getNativeSizeEstimate(const Function &F) const;
174e8d8bef9SDimitry Andric 
175e8d8bef9SDimitry Andric private:
176e8d8bef9SDimitry Andric   bool isLogging() const { return !!Logger; }
177e8d8bef9SDimitry Andric   std::unique_ptr<MLInlineAdvice> getMandatoryAdviceImpl(CallBase &CB) override;
178e8d8bef9SDimitry Andric 
179e8d8bef9SDimitry Andric   std::function<bool(CallBase &)> GetDefaultAdvice;
180e8d8bef9SDimitry Andric   const bool IsDoingInference;
181e8d8bef9SDimitry Andric   std::unique_ptr<TrainingLogger> Logger;
182e8d8bef9SDimitry Andric 
183e8d8bef9SDimitry Andric   const Optional<int32_t> InitialNativeSize;
184e8d8bef9SDimitry Andric   Optional<int32_t> CurrentNativeSize;
185e8d8bef9SDimitry Andric };
186e8d8bef9SDimitry Andric 
187e8d8bef9SDimitry Andric /// A variant of MLInlineAdvice that tracks all non-trivial inlining
188e8d8bef9SDimitry Andric /// decisions, for training/logging.
189e8d8bef9SDimitry Andric class LoggingMLInlineAdvice : public MLInlineAdvice {
190e8d8bef9SDimitry Andric public:
191e8d8bef9SDimitry Andric   LoggingMLInlineAdvice(DevelopmentModeMLInlineAdvisor *Advisor, CallBase &CB,
192e8d8bef9SDimitry Andric                         OptimizationRemarkEmitter &ORE, bool Recommendation,
193e8d8bef9SDimitry Andric                         TrainingLogger &Logger,
194e8d8bef9SDimitry Andric                         Optional<size_t> CallerSizeEstimateBefore,
195e8d8bef9SDimitry Andric                         Optional<size_t> CalleeSizeEstimateBefore,
196e8d8bef9SDimitry Andric                         bool DefaultDecision, bool Mandatory = false)
197e8d8bef9SDimitry Andric       : MLInlineAdvice(Advisor, CB, ORE, Recommendation), Logger(Logger),
198e8d8bef9SDimitry Andric         CallerSizeEstimateBefore(CallerSizeEstimateBefore),
199e8d8bef9SDimitry Andric         CalleeSizeEstimateBefore(CalleeSizeEstimateBefore),
200e8d8bef9SDimitry Andric         DefaultDecision(DefaultDecision), Mandatory(Mandatory) {}
201e8d8bef9SDimitry Andric 
202e8d8bef9SDimitry Andric   virtual ~LoggingMLInlineAdvice() = default;
203e8d8bef9SDimitry Andric 
204e8d8bef9SDimitry Andric private:
205e8d8bef9SDimitry Andric   DevelopmentModeMLInlineAdvisor *getAdvisor() const {
206e8d8bef9SDimitry Andric     return static_cast<DevelopmentModeMLInlineAdvisor *>(Advisor);
207e8d8bef9SDimitry Andric   }
208e8d8bef9SDimitry Andric   void recordInliningImpl() override {
209e8d8bef9SDimitry Andric     MLInlineAdvice::recordInliningImpl();
210e8d8bef9SDimitry Andric     getAdvisor()->resetNativeSize(Caller);
211e8d8bef9SDimitry Andric     int Reward = std::numeric_limits<int>::max();
212e8d8bef9SDimitry Andric     if (InlineSizeEstimatorAnalysis::isEvaluatorRequested() &&
213e8d8bef9SDimitry Andric         !getAdvisor()->isForcedToStop()) {
214e8d8bef9SDimitry Andric       int NativeSizeAfter = *getAdvisor()->getNativeSizeEstimate(*Caller) +
215e8d8bef9SDimitry Andric                             *CalleeSizeEstimateBefore;
216e8d8bef9SDimitry Andric       Reward = NativeSizeAfter -
217e8d8bef9SDimitry Andric                (*CallerSizeEstimateBefore + *CalleeSizeEstimateBefore);
218e8d8bef9SDimitry Andric       getAdvisor()->updateNativeSizeEstimate(Reward);
219e8d8bef9SDimitry Andric     }
220e8d8bef9SDimitry Andric     log(Reward, /*Success=*/true);
221e8d8bef9SDimitry Andric   }
222e8d8bef9SDimitry Andric 
223e8d8bef9SDimitry Andric   void recordInliningWithCalleeDeletedImpl() override {
224e8d8bef9SDimitry Andric     MLInlineAdvice::recordInliningWithCalleeDeletedImpl();
225e8d8bef9SDimitry Andric     getAdvisor()->resetNativeSize(Caller);
226e8d8bef9SDimitry Andric     if (InlineSizeEstimatorAnalysis::isEvaluatorRequested() &&
227e8d8bef9SDimitry Andric         !getAdvisor()->isForcedToStop()) {
228e8d8bef9SDimitry Andric       int NativeSizeAfter = *getAdvisor()->getNativeSizeEstimate(*Caller);
229e8d8bef9SDimitry Andric       int Reward = NativeSizeAfter -
230e8d8bef9SDimitry Andric                    (*CallerSizeEstimateBefore + *CalleeSizeEstimateBefore);
231e8d8bef9SDimitry Andric       getAdvisor()->updateNativeSizeEstimate(Reward);
232e8d8bef9SDimitry Andric       log(Reward, /*Success=*/true);
233349cc55cSDimitry Andric     } else {
234349cc55cSDimitry Andric       log(NoReward, /*Success=*/true);
235e8d8bef9SDimitry Andric     }
236e8d8bef9SDimitry Andric   }
237e8d8bef9SDimitry Andric 
238e8d8bef9SDimitry Andric   void recordUnsuccessfulInliningImpl(const InlineResult &Result) override {
239e8d8bef9SDimitry Andric     MLInlineAdvice::recordUnsuccessfulInliningImpl(Result);
240e8d8bef9SDimitry Andric     log(NoReward, /*Success=*/false);
241e8d8bef9SDimitry Andric   }
242e8d8bef9SDimitry Andric 
243e8d8bef9SDimitry Andric   void recordUnattemptedInliningImpl() override {
244e8d8bef9SDimitry Andric     MLInlineAdvice::recordUnattemptedInliningImpl();
245e8d8bef9SDimitry Andric     log(NoReward, /*Success=*/false);
246e8d8bef9SDimitry Andric   }
247e8d8bef9SDimitry Andric 
248e8d8bef9SDimitry Andric   void log(int64_t Reward, bool Success) {
249e8d8bef9SDimitry Andric     if (Mandatory)
250e8d8bef9SDimitry Andric       return;
251e8d8bef9SDimitry Andric     InlineEvent Event;
252e8d8bef9SDimitry Andric     Event.AdvisedDecision = isInliningRecommended();
253e8d8bef9SDimitry Andric     Event.DefaultDecision = DefaultDecision;
254e8d8bef9SDimitry Andric     Event.Effect = Success;
255e8d8bef9SDimitry Andric     Event.Reward = Reward;
256e8d8bef9SDimitry Andric     Logger.logInlineEvent(Event, getAdvisor()->getModelRunner());
257e8d8bef9SDimitry Andric   }
258e8d8bef9SDimitry Andric 
259e8d8bef9SDimitry Andric   static const int64_t NoReward = 0;
260e8d8bef9SDimitry Andric   TrainingLogger &Logger;
261e8d8bef9SDimitry Andric   const Optional<size_t> CallerSizeEstimateBefore;
262e8d8bef9SDimitry Andric   const Optional<size_t> CalleeSizeEstimateBefore;
263e8d8bef9SDimitry Andric   const int64_t DefaultDecision;
264e8d8bef9SDimitry Andric   const int64_t Mandatory;
265e8d8bef9SDimitry Andric };
266e8d8bef9SDimitry Andric 
2670eae32dcSDimitry Andric static const std::vector<TensorSpec> TrainingOnlyFeatures{
268e8d8bef9SDimitry Andric     TensorSpec::createSpec<int64_t>(TFFeedPrefix + "inlining_default", {1}),
269e8d8bef9SDimitry Andric     TensorSpec::createSpec<float>(TFFeedPrefix + "discount", {1}),
270e8d8bef9SDimitry Andric     TensorSpec::createSpec<float>(TFFeedPrefix + "reward", {1}),
271e8d8bef9SDimitry Andric     TensorSpec::createSpec<int32_t>(TFFeedPrefix + "step_type", {1})};
2720eae32dcSDimitry Andric 
2730eae32dcSDimitry Andric static const std::vector<TensorSpec> getInputFeatures() {
2740eae32dcSDimitry Andric   std::vector<TensorSpec> InputSpecs;
2750eae32dcSDimitry Andric   for (size_t I = 0; I < NumberOfFeatures; ++I)
2760eae32dcSDimitry Andric     InputSpecs.push_back(
2770eae32dcSDimitry Andric         TensorSpec::createSpec<int64_t>(TFFeedPrefix + FeatureNameMap[I], {1}));
2780eae32dcSDimitry Andric   append_range(InputSpecs, TrainingOnlyFeatures);
2790eae32dcSDimitry Andric   return InputSpecs;
2800eae32dcSDimitry Andric }
2810eae32dcSDimitry Andric 
282e8d8bef9SDimitry Andric } // namespace
283e8d8bef9SDimitry Andric 
284e8d8bef9SDimitry Andric TrainingLogger::TrainingLogger(StringRef LogFileName,
285e8d8bef9SDimitry Andric                                const ModelUnderTrainingRunner *MUTR)
286e8d8bef9SDimitry Andric     : LogFileName(LogFileName), MUTR(MUTR) {
287e8d8bef9SDimitry Andric   // The first output is the inlining decision.
288e8d8bef9SDimitry Andric   if (MUTR)
289e8d8bef9SDimitry Andric     OutputCount = MUTR->outputLoggedFeatureSpecs().size();
290e8d8bef9SDimitry Andric   std::vector<LoggedFeatureSpec> FT;
291e8d8bef9SDimitry Andric 
292e8d8bef9SDimitry Andric   for (size_t I = 0; I < NumberOfFeatures; ++I)
293e8d8bef9SDimitry Andric     FT.push_back(
294e8d8bef9SDimitry Andric         {TensorSpec::createSpec<int64_t>(FeatureNameMap.at(I), {1}), None});
295e8d8bef9SDimitry Andric   if (MUTR && MUTR->outputLoggedFeatureSpecs().size() > 1)
296e8d8bef9SDimitry Andric     append_range(FT, drop_begin(MUTR->outputLoggedFeatureSpecs()));
297e8d8bef9SDimitry Andric 
298e8d8bef9SDimitry Andric   DefaultDecisionPos = FT.size();
299e8d8bef9SDimitry Andric   FT.push_back(
300e8d8bef9SDimitry Andric       {TensorSpec::createSpec<int64_t>(DefaultDecisionName, {1}), None});
301e8d8bef9SDimitry Andric 
302e8d8bef9SDimitry Andric   DecisionPos = FT.size();
303e8d8bef9SDimitry Andric   FT.push_back({TensorSpec::createSpec<int64_t>(DecisionName, {1}), None});
304e8d8bef9SDimitry Andric 
305e8d8bef9SDimitry Andric   L = std::make_unique<Logger>(
306e8d8bef9SDimitry Andric       FT, TensorSpec::createSpec<int64_t>(RewardName, {1}),
307e8d8bef9SDimitry Andric       InlineSizeEstimatorAnalysis::isEvaluatorRequested());
308e8d8bef9SDimitry Andric }
309e8d8bef9SDimitry Andric 
310e8d8bef9SDimitry Andric /// Log one inlining event.
311e8d8bef9SDimitry Andric void TrainingLogger::logInlineEvent(const InlineEvent &Event,
312e8d8bef9SDimitry Andric                                     const MLModelRunner &ModelRunner) {
313e8d8bef9SDimitry Andric   size_t CurrentFeature = 0;
314e8d8bef9SDimitry Andric   for (; CurrentFeature < NumberOfFeatures; ++CurrentFeature) {
3150eae32dcSDimitry Andric     int64_t F = *ModelRunner.getTensor<int64_t>(CurrentFeature);
316fe6060f1SDimitry Andric     L->logInt64Value(CurrentFeature, &F);
317e8d8bef9SDimitry Andric   }
318e8d8bef9SDimitry Andric 
319e8d8bef9SDimitry Andric   for (size_t I = 1; I < OutputCount; ++I) {
320e8d8bef9SDimitry Andric     const auto &Result = *MUTR->lastEvaluationResult();
321e8d8bef9SDimitry Andric     const char *RawData =
322e8d8bef9SDimitry Andric         reinterpret_cast<const char *>(Result.getUntypedTensorValue(I));
323fe6060f1SDimitry Andric     L->logSpecifiedTensorValue(CurrentFeature, RawData);
324e8d8bef9SDimitry Andric     ++CurrentFeature;
325e8d8bef9SDimitry Andric   }
326e8d8bef9SDimitry Andric 
327e8d8bef9SDimitry Andric   assert(CurrentFeature == DefaultDecisionPos);
328fe6060f1SDimitry Andric   L->logInt64Value(DefaultDecisionPos, &Event.DefaultDecision);
329fe6060f1SDimitry Andric   L->logInt64Value(DecisionPos, &Event.AdvisedDecision);
330e8d8bef9SDimitry Andric   if (InlineSizeEstimatorAnalysis::isEvaluatorRequested())
331fe6060f1SDimitry Andric     L->logInt64Reward(Event.Reward);
332e8d8bef9SDimitry Andric 
333e8d8bef9SDimitry Andric   // For debugging / later use
334e8d8bef9SDimitry Andric   Effects.push_back(Event.Effect);
335e8d8bef9SDimitry Andric }
336e8d8bef9SDimitry Andric 
337e8d8bef9SDimitry Andric void TrainingLogger::print() {
338e8d8bef9SDimitry Andric   std::error_code EC;
339e8d8bef9SDimitry Andric   raw_fd_ostream OutFile(LogFileName, EC);
340349cc55cSDimitry Andric   L->flush(OutFile);
341e8d8bef9SDimitry Andric }
342e8d8bef9SDimitry Andric 
343e8d8bef9SDimitry Andric DevelopmentModeMLInlineAdvisor::DevelopmentModeMLInlineAdvisor(
344e8d8bef9SDimitry Andric     Module &M, ModuleAnalysisManager &MAM,
345e8d8bef9SDimitry Andric     std::unique_ptr<MLModelRunner> ModelRunner,
346*04eeddc0SDimitry Andric     std::function<bool(CallBase &)> GetDefaultAdvice,
347e8d8bef9SDimitry Andric     std::unique_ptr<TrainingLogger> Logger)
348e8d8bef9SDimitry Andric     : MLInlineAdvisor(M, MAM, std::move(ModelRunner)),
349*04eeddc0SDimitry Andric       GetDefaultAdvice(GetDefaultAdvice),
350*04eeddc0SDimitry Andric       IsDoingInference(isa<ModelUnderTrainingRunner>(getModelRunner())),
351e8d8bef9SDimitry Andric       Logger(std::move(Logger)),
352e8d8bef9SDimitry Andric       InitialNativeSize(isLogging() ? getTotalSizeEstimate() : 0),
353e8d8bef9SDimitry Andric       CurrentNativeSize(InitialNativeSize) {
354e8d8bef9SDimitry Andric   // We cannot have the case of neither inference nor logging.
355e8d8bef9SDimitry Andric   assert(IsDoingInference || isLogging());
356e8d8bef9SDimitry Andric }
357e8d8bef9SDimitry Andric 
358e8d8bef9SDimitry Andric DevelopmentModeMLInlineAdvisor::~DevelopmentModeMLInlineAdvisor() {
359e8d8bef9SDimitry Andric   if (isLogging())
360e8d8bef9SDimitry Andric     Logger->print();
361e8d8bef9SDimitry Andric }
362e8d8bef9SDimitry Andric 
363e8d8bef9SDimitry Andric Optional<size_t>
364e8d8bef9SDimitry Andric DevelopmentModeMLInlineAdvisor::getNativeSizeEstimate(const Function &F) const {
365e8d8bef9SDimitry Andric   if (!InlineSizeEstimatorAnalysis::isEvaluatorRequested())
366e8d8bef9SDimitry Andric     return None;
367e8d8bef9SDimitry Andric   auto &R =
368e8d8bef9SDimitry Andric       FAM.getResult<InlineSizeEstimatorAnalysis>(const_cast<Function &>(F));
369e8d8bef9SDimitry Andric   if (!R) {
370e8d8bef9SDimitry Andric     F.getParent()->getContext().emitError(
371e8d8bef9SDimitry Andric         "Native size estimator is not present.");
372e8d8bef9SDimitry Andric     return 0;
373e8d8bef9SDimitry Andric   }
374e8d8bef9SDimitry Andric   return *R;
375e8d8bef9SDimitry Andric }
376e8d8bef9SDimitry Andric 
377e8d8bef9SDimitry Andric std::unique_ptr<MLInlineAdvice>
378e8d8bef9SDimitry Andric DevelopmentModeMLInlineAdvisor::getMandatoryAdviceImpl(CallBase &CB) {
379e8d8bef9SDimitry Andric   return std::make_unique<LoggingMLInlineAdvice>(
380e8d8bef9SDimitry Andric       /*Advisor=*/this,
381e8d8bef9SDimitry Andric       /*CB=*/CB, /*ORE=*/getCallerORE(CB), /*Recommendation=*/true,
382e8d8bef9SDimitry Andric       /*Logger=*/*Logger,
383e8d8bef9SDimitry Andric       /*CallerSizeEstimateBefore=*/getNativeSizeEstimate(*CB.getCaller()),
384e8d8bef9SDimitry Andric       /*CalleeSizeEstimateBefore=*/
385e8d8bef9SDimitry Andric       getNativeSizeEstimate(*CB.getCalledFunction()),
386e8d8bef9SDimitry Andric       /*DefaultDecision=*/true, /*Mandatory*/ true);
387e8d8bef9SDimitry Andric }
388e8d8bef9SDimitry Andric 
389e8d8bef9SDimitry Andric std::unique_ptr<MLInlineAdvice>
390e8d8bef9SDimitry Andric DevelopmentModeMLInlineAdvisor::getAdviceFromModel(
391e8d8bef9SDimitry Andric     CallBase &CB, OptimizationRemarkEmitter &ORE) {
392e8d8bef9SDimitry Andric   if (IsDoingInference && !isLogging())
393e8d8bef9SDimitry Andric     return MLInlineAdvisor::getAdviceFromModel(CB, ORE);
394e8d8bef9SDimitry Andric 
395e8d8bef9SDimitry Andric   bool DefaultAdvice = GetDefaultAdvice(CB);
3960eae32dcSDimitry Andric   auto Recommendation =
3970eae32dcSDimitry Andric       IsDoingInference ? static_cast<bool>(ModelRunner->evaluate<int64_t>())
3980eae32dcSDimitry Andric                        : DefaultAdvice;
399e8d8bef9SDimitry Andric   return std::make_unique<LoggingMLInlineAdvice>(
400e8d8bef9SDimitry Andric       /*Advisor=*/this,
401e8d8bef9SDimitry Andric       /*CB=*/CB, /*ORE=*/ORE, /*Recommendation=*/Recommendation,
402e8d8bef9SDimitry Andric       /*Logger=*/*Logger,
403e8d8bef9SDimitry Andric       /*CallerSizeEstimateBefore=*/getNativeSizeEstimate(*CB.getCaller()),
404e8d8bef9SDimitry Andric       /*CalleeSizeEstimateBefore=*/
405e8d8bef9SDimitry Andric       getNativeSizeEstimate(*CB.getCalledFunction()),
406e8d8bef9SDimitry Andric       /*DefaultDecision=*/DefaultAdvice);
407e8d8bef9SDimitry Andric }
408e8d8bef9SDimitry Andric 
409e8d8bef9SDimitry Andric size_t DevelopmentModeMLInlineAdvisor::getTotalSizeEstimate() {
410e8d8bef9SDimitry Andric   if (!InlineSizeEstimatorAnalysis::isEvaluatorRequested())
411e8d8bef9SDimitry Andric     return 0;
412e8d8bef9SDimitry Andric   size_t Ret = 0;
413e8d8bef9SDimitry Andric   for (auto &F : M) {
414e8d8bef9SDimitry Andric     if (F.isDeclaration())
415e8d8bef9SDimitry Andric       continue;
416e8d8bef9SDimitry Andric     Ret += *getNativeSizeEstimate(F);
417e8d8bef9SDimitry Andric   }
418e8d8bef9SDimitry Andric   return Ret;
419e8d8bef9SDimitry Andric }
420e8d8bef9SDimitry Andric 
421e8d8bef9SDimitry Andric std::unique_ptr<InlineAdvisor> llvm::getDevelopmentModeAdvisor(
422e8d8bef9SDimitry Andric     Module &M, ModuleAnalysisManager &MAM,
423e8d8bef9SDimitry Andric     std::function<bool(CallBase &)> GetDefaultAdvice) {
424e8d8bef9SDimitry Andric   auto &Ctx = M.getContext();
425e8d8bef9SDimitry Andric   std::unique_ptr<MLModelRunner> Runner;
426e8d8bef9SDimitry Andric   if (TFModelUnderTrainingPath.empty())
4270eae32dcSDimitry Andric     Runner.reset(new NoInferenceModelRunner(Ctx, getInputFeatures()));
428*04eeddc0SDimitry Andric   else
429*04eeddc0SDimitry Andric     Runner = ModelUnderTrainingRunner::createAndEnsureValid(
430*04eeddc0SDimitry Andric         Ctx, TFModelUnderTrainingPath, DecisionName, getInputFeatures(),
431*04eeddc0SDimitry Andric         TFOutputSpecOverride);
432*04eeddc0SDimitry Andric   if (!Runner)
433e8d8bef9SDimitry Andric     return nullptr;
434e8d8bef9SDimitry Andric   std::unique_ptr<TrainingLogger> Logger;
435e8d8bef9SDimitry Andric   if (!TrainingLog.empty())
436*04eeddc0SDimitry Andric     Logger = std::make_unique<TrainingLogger>(
437*04eeddc0SDimitry Andric         TrainingLog, dyn_cast<ModelUnderTrainingRunner>(Runner.get()));
438e8d8bef9SDimitry Andric 
439e8d8bef9SDimitry Andric   return std::make_unique<DevelopmentModeMLInlineAdvisor>(
440*04eeddc0SDimitry Andric       M, MAM, std::move(Runner), GetDefaultAdvice, std::move(Logger));
441e8d8bef9SDimitry Andric }
442e8d8bef9SDimitry Andric #endif // defined(LLVM_HAVE_TF_API)
443