170f8d0acSMircea Trofin //===- DevelopmentModeInlineAdvisor.cpp - runtime-loadable model runner --===// 270f8d0acSMircea Trofin // 3c874dd53SChristopher Di Bella // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4c874dd53SChristopher Di Bella // See https://llvm.org/LICENSE.txt for license information. 5c874dd53SChristopher Di Bella // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 670f8d0acSMircea Trofin // 770f8d0acSMircea Trofin //===----------------------------------------------------------------------===// 870f8d0acSMircea Trofin // 970f8d0acSMircea Trofin // This file implements a model runner using Tensorflow C APIs, allowing the 1070f8d0acSMircea Trofin // loading of a model from a command line option. 1170f8d0acSMircea Trofin // 1270f8d0acSMircea Trofin //===----------------------------------------------------------------------===// 134fe912f1SNico Weber #include "llvm/Config/config.h" 144fe912f1SNico Weber #if defined(LLVM_HAVE_TF_API) 154fe912f1SNico Weber 165f4ae564SJan Svoboda #include "llvm/ADT/BitVector.h" 1770f8d0acSMircea Trofin #include "llvm/Analysis/CallGraph.h" 1870f8d0acSMircea Trofin #include "llvm/Analysis/InlineSizeEstimatorAnalysis.h" 1970f8d0acSMircea Trofin #include "llvm/Analysis/MLInlineAdvisor.h" 2004f2712eSMircea Trofin #include "llvm/Analysis/ModelUnderTrainingRunner.h" 21059e0347SMircea Trofin #include "llvm/Analysis/NoInferenceModelRunner.h" 2270f8d0acSMircea Trofin #include "llvm/Analysis/Utils/TFUtils.h" 2370f8d0acSMircea Trofin #include "llvm/IR/LLVMContext.h" 2470f8d0acSMircea Trofin #include "llvm/Support/CommandLine.h" 2570f8d0acSMircea Trofin #include "llvm/Support/ManagedStatic.h" 2670f8d0acSMircea Trofin 2770f8d0acSMircea Trofin #include <vector> 2870f8d0acSMircea Trofin 2970f8d0acSMircea Trofin using namespace llvm; 3070f8d0acSMircea Trofin 3170f8d0acSMircea Trofin static cl::opt<std::string> TrainingLog( 3270f8d0acSMircea Trofin "training-log", cl::Hidden, 3370f8d0acSMircea Trofin cl::desc("Path where the development - mode inlining log is saved.")); 3470f8d0acSMircea Trofin 3570f8d0acSMircea Trofin static cl::opt<std::string> TFModelUnderTrainingPath( 3670f8d0acSMircea Trofin "ml-inliner-model-under-training", cl::Hidden, 3762fc44caSMircea Trofin cl::desc(R"(Path to SavedModel from the previous training iteration. 3862fc44caSMircea Trofin The directory is also expected to contain a JSON specification of the 3962fc44caSMircea Trofin outputs expected to be logged, where the first entry must be the 4062fc44caSMircea Trofin inlining decision. The file containing the specification should be 4162fc44caSMircea Trofin called output_spec.json. The expected JSON value is an array of 4262fc44caSMircea Trofin dictionaries. Each dictionary should have 2 keys: 4362fc44caSMircea Trofin 4462fc44caSMircea Trofin - "tensor_spec, followed by the TensorSpec description of the 4562fc44caSMircea Trofin output; and 4662fc44caSMircea Trofin - "logging_name", a string indicating the name to use when 4762fc44caSMircea Trofin logging the output values. 4862fc44caSMircea Trofin 4962fc44caSMircea Trofin Example: 5062fc44caSMircea Trofin [ 5162fc44caSMircea Trofin { 5262fc44caSMircea Trofin "logging_name" : "some_name", 5362fc44caSMircea Trofin "tensor_spec" : { 5462fc44caSMircea Trofin "name" : "model_name", 5562fc44caSMircea Trofin "port" : 0, 5662fc44caSMircea Trofin "shape" : [2, 3], 5762fc44caSMircea Trofin "type" : "float" 5862fc44caSMircea Trofin } 5962fc44caSMircea Trofin } 6062fc44caSMircea Trofin ] 6162fc44caSMircea Trofin 6262fc44caSMircea Trofin The first value must always correspond to the decision.)")); 6362fc44caSMircea Trofin 6462fc44caSMircea Trofin static cl::opt<std::string> TFOutputSpecOverride( 6562fc44caSMircea Trofin "ml-inliner-output-spec-override", cl::Hidden, 6662fc44caSMircea Trofin cl::desc("Override the path to the output spec json file. See " 6762fc44caSMircea Trofin "-ml-inliner-model-under-training documentation for the " 6862fc44caSMircea Trofin "specification of that file.")); 6970f8d0acSMircea Trofin 7070f8d0acSMircea Trofin static cl::opt<std::string> TFFeedPrefix("ml-inliner-trained-model-feed-prefix", 7170f8d0acSMircea Trofin cl::Hidden, cl::init("action_"), 7270f8d0acSMircea Trofin cl::desc("Prefix for feature names.")); 7370f8d0acSMircea Trofin 7470f8d0acSMircea Trofin namespace { 7570f8d0acSMircea Trofin /// An InlineEvent, used by TrainingLogger. 7670f8d0acSMircea Trofin struct InlineEvent { 7770f8d0acSMircea Trofin /// What the default policy's decision would have been. 7836bb1fb1SMircea Trofin int64_t DefaultDecision = 0; 7970f8d0acSMircea Trofin 8070f8d0acSMircea Trofin /// What we advised. When training off the default policy, this is the same as 8170f8d0acSMircea Trofin /// DefaultDecision. 8236bb1fb1SMircea Trofin int64_t AdvisedDecision = 0; 8370f8d0acSMircea Trofin 8470f8d0acSMircea Trofin /// What actually happened. This would be 'false' in the case of an inline 8570f8d0acSMircea Trofin /// error, even if AdvisedDecision were true, otherwise it agrees with 8670f8d0acSMircea Trofin /// AdvisedDecision. 8770f8d0acSMircea Trofin bool Effect = false; 8870f8d0acSMircea Trofin 8970f8d0acSMircea Trofin /// What the change in size was: size_after - size_before 9070f8d0acSMircea Trofin int64_t Reward = 0; 9170f8d0acSMircea Trofin }; 9270f8d0acSMircea Trofin 9370f8d0acSMircea Trofin /// Collect data we may use for training a model, and write it as a textual 9470f8d0acSMircea Trofin /// Tensorflow SequenceExample 9570f8d0acSMircea Trofin /// (https://www.tensorflow.org/api_docs/python/tf/train/SequenceExample) 9670f8d0acSMircea Trofin /// protobuf (https://developers.google.com/protocol-buffers). 9770f8d0acSMircea Trofin /// Because this is a protobuf, we cannot just stream the events as they come. 9870f8d0acSMircea Trofin /// Internally, TrainingLogger stores data in column-major format, because that 9970f8d0acSMircea Trofin /// lines up with how TF SequenceExample represents it. 10070f8d0acSMircea Trofin class TrainingLogger final { 10170f8d0acSMircea Trofin public: 10262fc44caSMircea Trofin TrainingLogger(StringRef LogFileName, const ModelUnderTrainingRunner *MUTR); 10370f8d0acSMircea Trofin 10470f8d0acSMircea Trofin /// Log one inlining event. 10570f8d0acSMircea Trofin void logInlineEvent(const InlineEvent &Event, 10665b6dbf9SMircea Trofin const MLModelRunner &ModelRunner); 10770f8d0acSMircea Trofin 10865b6dbf9SMircea Trofin /// Print the stored tensors. 109d5c81be3SMircea Trofin void print(); 11070f8d0acSMircea Trofin 11170f8d0acSMircea Trofin private: 112d5c81be3SMircea Trofin StringRef LogFileName; 11362fc44caSMircea Trofin const ModelUnderTrainingRunner *const MUTR; 11436bb1fb1SMircea Trofin std::unique_ptr<Logger> L; 1155f4ae564SJan Svoboda BitVector Effects; 11636bb1fb1SMircea Trofin /// There's at least one output. We'll set this to a different value if MUTR 11736bb1fb1SMircea Trofin /// is avaliable. 11836bb1fb1SMircea Trofin size_t OutputCount = 1; 11936bb1fb1SMircea Trofin /// Set these 2 clearly OOB, to make sure we set them later. 12036bb1fb1SMircea Trofin size_t DefaultDecisionPos = std::numeric_limits<size_t>::max(); 12136bb1fb1SMircea Trofin size_t DecisionPos = std::numeric_limits<size_t>::max(); 12270f8d0acSMircea Trofin }; 12370f8d0acSMircea Trofin 12470f8d0acSMircea Trofin /// An extension of the MLInlineAdvisor for the 'development' mode, targeting 12570f8d0acSMircea Trofin /// the offline training scenario. Note that training happens outside of the 12670f8d0acSMircea Trofin /// compiler, this facility is concerned with producing training data ("logs"). 12770f8d0acSMircea Trofin /// This InlineAdvisor can operate in the following modes: 12870f8d0acSMircea Trofin /// 12970f8d0acSMircea Trofin /// 1) collect logs for the default policy. This is useful for bootstrapping 13070f8d0acSMircea Trofin /// training, which will be considerably faster by starting from a reasonable 13170f8d0acSMircea Trofin /// policy. 13270f8d0acSMircea Trofin /// 13370f8d0acSMircea Trofin /// 2) collect logs for the ML policy, using a model from a previous 13470f8d0acSMircea Trofin /// training. Potentially, that model uses internally some small random 13570f8d0acSMircea Trofin /// perturbation of its weights, to induce exploration (setting this up is the 13670f8d0acSMircea Trofin /// responsibility of the training algorithm). The logs would then be used to 13770f8d0acSMircea Trofin /// retrain and improve on this model. 13870f8d0acSMircea Trofin /// 13970f8d0acSMircea Trofin /// 3) use the provided model, with no logging. This is useful for end to end 14070f8d0acSMircea Trofin /// validation - the model, in this case, is a release candidate and shouldn't 14170f8d0acSMircea Trofin /// have random perturbations. It is a convenience feature: rather than needing 14270f8d0acSMircea Trofin /// to take the release candidate model and compile it in 'release' mode, 14370f8d0acSMircea Trofin /// validate it, then potentially discard it, it's easier to just pass the model 14470f8d0acSMircea Trofin /// to the compiler, albeit compilation would be slower, as a one-off. Once the 14570f8d0acSMircea Trofin /// model behaves satisfactorily, it can be compiled AOT, for efficiency, in 14670f8d0acSMircea Trofin /// release mode. The expectation is that a well-trained model provides a good 14770f8d0acSMircea Trofin /// policy over a sufficiently diverse codebase, over many changes (i.e. 14870f8d0acSMircea Trofin /// training happens seldom). 14970f8d0acSMircea Trofin class DevelopmentModeMLInlineAdvisor : public MLInlineAdvisor { 15070f8d0acSMircea Trofin public: 15170f8d0acSMircea Trofin DevelopmentModeMLInlineAdvisor( 15270f8d0acSMircea Trofin Module &M, ModuleAnalysisManager &MAM, 15370f8d0acSMircea Trofin std::unique_ptr<MLModelRunner> ModelRunner, 154a120fdd3SMircea Trofin std::function<bool(CallBase &)> GetDefaultAdvice, 155d5c81be3SMircea Trofin std::unique_ptr<TrainingLogger> Logger); 15670f8d0acSMircea Trofin 15770f8d0acSMircea Trofin size_t getTotalSizeEstimate(); 15870f8d0acSMircea Trofin 15970f8d0acSMircea Trofin virtual ~DevelopmentModeMLInlineAdvisor(); updateNativeSizeEstimate(int64_t Change)1608c63df24SMircea Trofin void updateNativeSizeEstimate(int64_t Change) { 1618c63df24SMircea Trofin *CurrentNativeSize += Change; 1628c63df24SMircea Trofin } 16370f8d0acSMircea Trofin void resetNativeSize(Function *F) { 1640d06b14fSMircea Trofin PreservedAnalyses PA = PreservedAnalyses::all(); 1650d06b14fSMircea Trofin PA.abandon<InlineSizeEstimatorAnalysis>(); 1660d06b14fSMircea Trofin FAM.invalidate(*F, PA); 16770f8d0acSMircea Trofin } 16870f8d0acSMircea Trofin 16970f8d0acSMircea Trofin std::unique_ptr<MLInlineAdvice> 17070f8d0acSMircea Trofin getAdviceFromModel(CallBase &CB, OptimizationRemarkEmitter &ORE) override; 17170f8d0acSMircea Trofin 1728c63df24SMircea Trofin Optional<size_t> getNativeSizeEstimate(const Function &F) const; 17370f8d0acSMircea Trofin 17470f8d0acSMircea Trofin private: 175d5c81be3SMircea Trofin bool isLogging() const { return !!Logger; } 176e8049dc3SMircea Trofin std::unique_ptr<MLInlineAdvice> getMandatoryAdviceImpl(CallBase &CB) override; 17770f8d0acSMircea Trofin 17870f8d0acSMircea Trofin std::function<bool(CallBase &)> GetDefaultAdvice; 17970f8d0acSMircea Trofin const bool IsDoingInference; 180d5c81be3SMircea Trofin std::unique_ptr<TrainingLogger> Logger; 18170f8d0acSMircea Trofin 1828c63df24SMircea Trofin const Optional<int32_t> InitialNativeSize; 1838c63df24SMircea Trofin Optional<int32_t> CurrentNativeSize; 18470f8d0acSMircea Trofin }; 18570f8d0acSMircea Trofin 18670f8d0acSMircea Trofin /// A variant of MLInlineAdvice that tracks all non-trivial inlining 18770f8d0acSMircea Trofin /// decisions, for training/logging. 18870f8d0acSMircea Trofin class LoggingMLInlineAdvice : public MLInlineAdvice { 18970f8d0acSMircea Trofin public: 19070f8d0acSMircea Trofin LoggingMLInlineAdvice(DevelopmentModeMLInlineAdvisor *Advisor, CallBase &CB, 19170f8d0acSMircea Trofin OptimizationRemarkEmitter &ORE, bool Recommendation, 1928c63df24SMircea Trofin TrainingLogger &Logger, 1938c63df24SMircea Trofin Optional<size_t> CallerSizeEstimateBefore, 1948c63df24SMircea Trofin Optional<size_t> CalleeSizeEstimateBefore, 1958c63df24SMircea Trofin bool DefaultDecision, bool Mandatory = false) 19670f8d0acSMircea Trofin : MLInlineAdvice(Advisor, CB, ORE, Recommendation), Logger(Logger), 19770f8d0acSMircea Trofin CallerSizeEstimateBefore(CallerSizeEstimateBefore), 19870f8d0acSMircea Trofin CalleeSizeEstimateBefore(CalleeSizeEstimateBefore), 19987fb7aa1SMircea Trofin DefaultDecision(DefaultDecision), Mandatory(Mandatory) {} 20070f8d0acSMircea Trofin 20170f8d0acSMircea Trofin virtual ~LoggingMLInlineAdvice() = default; 20270f8d0acSMircea Trofin 20370f8d0acSMircea Trofin private: 20470f8d0acSMircea Trofin DevelopmentModeMLInlineAdvisor *getAdvisor() const { 20570f8d0acSMircea Trofin return static_cast<DevelopmentModeMLInlineAdvisor *>(Advisor); 20670f8d0acSMircea Trofin } 20770f8d0acSMircea Trofin void recordInliningImpl() override { 20870f8d0acSMircea Trofin MLInlineAdvice::recordInliningImpl(); 20970f8d0acSMircea Trofin getAdvisor()->resetNativeSize(Caller); 21070f8d0acSMircea Trofin int Reward = std::numeric_limits<int>::max(); 2118c63df24SMircea Trofin if (InlineSizeEstimatorAnalysis::isEvaluatorRequested() && 2128c63df24SMircea Trofin !getAdvisor()->isForcedToStop()) { 2138c63df24SMircea Trofin int NativeSizeAfter = *getAdvisor()->getNativeSizeEstimate(*Caller) + 2148c63df24SMircea Trofin *CalleeSizeEstimateBefore; 21570f8d0acSMircea Trofin Reward = NativeSizeAfter - 2168c63df24SMircea Trofin (*CallerSizeEstimateBefore + *CalleeSizeEstimateBefore); 21770f8d0acSMircea Trofin getAdvisor()->updateNativeSizeEstimate(Reward); 21870f8d0acSMircea Trofin } 21970f8d0acSMircea Trofin log(Reward, /*Success=*/true); 22070f8d0acSMircea Trofin } 22170f8d0acSMircea Trofin 22270f8d0acSMircea Trofin void recordInliningWithCalleeDeletedImpl() override { 22370f8d0acSMircea Trofin MLInlineAdvice::recordInliningWithCalleeDeletedImpl(); 22470f8d0acSMircea Trofin getAdvisor()->resetNativeSize(Caller); 2258c63df24SMircea Trofin if (InlineSizeEstimatorAnalysis::isEvaluatorRequested() && 2268c63df24SMircea Trofin !getAdvisor()->isForcedToStop()) { 2278c63df24SMircea Trofin int NativeSizeAfter = *getAdvisor()->getNativeSizeEstimate(*Caller); 22870f8d0acSMircea Trofin int Reward = NativeSizeAfter - 2298c63df24SMircea Trofin (*CallerSizeEstimateBefore + *CalleeSizeEstimateBefore); 23070f8d0acSMircea Trofin getAdvisor()->updateNativeSizeEstimate(Reward); 23170f8d0acSMircea Trofin log(Reward, /*Success=*/true); 2321055c5e1SMircea Trofin } else { 2331055c5e1SMircea Trofin log(NoReward, /*Success=*/true); 23470f8d0acSMircea Trofin } 23570f8d0acSMircea Trofin } 23670f8d0acSMircea Trofin 23770f8d0acSMircea Trofin void recordUnsuccessfulInliningImpl(const InlineResult &Result) override { 23870f8d0acSMircea Trofin MLInlineAdvice::recordUnsuccessfulInliningImpl(Result); 23970f8d0acSMircea Trofin log(NoReward, /*Success=*/false); 24070f8d0acSMircea Trofin } 24170f8d0acSMircea Trofin 24270f8d0acSMircea Trofin void recordUnattemptedInliningImpl() override { 24370f8d0acSMircea Trofin MLInlineAdvice::recordUnattemptedInliningImpl(); 24470f8d0acSMircea Trofin log(NoReward, /*Success=*/false); 24570f8d0acSMircea Trofin } 24670f8d0acSMircea Trofin 24770f8d0acSMircea Trofin void log(int64_t Reward, bool Success) { 24887fb7aa1SMircea Trofin if (Mandatory) 24987fb7aa1SMircea Trofin return; 25070f8d0acSMircea Trofin InlineEvent Event; 25170f8d0acSMircea Trofin Event.AdvisedDecision = isInliningRecommended(); 25270f8d0acSMircea Trofin Event.DefaultDecision = DefaultDecision; 25370f8d0acSMircea Trofin Event.Effect = Success; 25470f8d0acSMircea Trofin Event.Reward = Reward; 25570f8d0acSMircea Trofin Logger.logInlineEvent(Event, getAdvisor()->getModelRunner()); 25670f8d0acSMircea Trofin } 25770f8d0acSMircea Trofin 25870f8d0acSMircea Trofin static const int64_t NoReward = 0; 25970f8d0acSMircea Trofin TrainingLogger &Logger; 2608c63df24SMircea Trofin const Optional<size_t> CallerSizeEstimateBefore; 2618c63df24SMircea Trofin const Optional<size_t> CalleeSizeEstimateBefore; 26236bb1fb1SMircea Trofin const int64_t DefaultDecision; 26336bb1fb1SMircea Trofin const int64_t Mandatory; 26470f8d0acSMircea Trofin }; 26570f8d0acSMircea Trofin 26604f2712eSMircea Trofin static const std::vector<TensorSpec> TrainingOnlyFeatures{ 26771059257SMircea Trofin TensorSpec::createSpec<int64_t>(TFFeedPrefix + "inlining_default", {1}), 26871059257SMircea Trofin TensorSpec::createSpec<float>(TFFeedPrefix + "discount", {1}), 26971059257SMircea Trofin TensorSpec::createSpec<float>(TFFeedPrefix + "reward", {1}), 27071059257SMircea Trofin TensorSpec::createSpec<int32_t>(TFFeedPrefix + "step_type", {1})}; 27104f2712eSMircea Trofin 27204f2712eSMircea Trofin static const std::vector<TensorSpec> getInputFeatures() { 27304f2712eSMircea Trofin std::vector<TensorSpec> InputSpecs; 27404f2712eSMircea Trofin for (size_t I = 0; I < NumberOfFeatures; ++I) 275*c35ad9eeSMircea Trofin InputSpecs.push_back(TensorSpec::createSpec<int64_t>( 276*c35ad9eeSMircea Trofin TFFeedPrefix + FeatureMap[I].name(), FeatureMap[I].shape())); 27704f2712eSMircea Trofin append_range(InputSpecs, TrainingOnlyFeatures); 27804f2712eSMircea Trofin return InputSpecs; 27904f2712eSMircea Trofin } 28004f2712eSMircea Trofin 28170f8d0acSMircea Trofin } // namespace 28270f8d0acSMircea Trofin 28362fc44caSMircea Trofin TrainingLogger::TrainingLogger(StringRef LogFileName, 28462fc44caSMircea Trofin const ModelUnderTrainingRunner *MUTR) 28562fc44caSMircea Trofin : LogFileName(LogFileName), MUTR(MUTR) { 28662fc44caSMircea Trofin // The first output is the inlining decision. 28736bb1fb1SMircea Trofin if (MUTR) 288b51e844fSMircea Trofin OutputCount = MUTR->outputLoggedFeatureSpecs().size(); 289b51e844fSMircea Trofin std::vector<LoggedFeatureSpec> FT; 29036bb1fb1SMircea Trofin 29136bb1fb1SMircea Trofin for (size_t I = 0; I < NumberOfFeatures; ++I) 292*c35ad9eeSMircea Trofin FT.push_back({FeatureMap.at(I), None}); 293b51e844fSMircea Trofin if (MUTR && MUTR->outputLoggedFeatureSpecs().size() > 1) 294a3254904SKazu Hirata append_range(FT, drop_begin(MUTR->outputLoggedFeatureSpecs())); 29536bb1fb1SMircea Trofin 29636bb1fb1SMircea Trofin DefaultDecisionPos = FT.size(); 29736bb1fb1SMircea Trofin FT.push_back( 29836bb1fb1SMircea Trofin {TensorSpec::createSpec<int64_t>(DefaultDecisionName, {1}), None}); 29936bb1fb1SMircea Trofin 30036bb1fb1SMircea Trofin DecisionPos = FT.size(); 30136bb1fb1SMircea Trofin FT.push_back({TensorSpec::createSpec<int64_t>(DecisionName, {1}), None}); 30236bb1fb1SMircea Trofin 30336bb1fb1SMircea Trofin L = std::make_unique<Logger>( 30436bb1fb1SMircea Trofin FT, TensorSpec::createSpec<int64_t>(RewardName, {1}), 30536bb1fb1SMircea Trofin InlineSizeEstimatorAnalysis::isEvaluatorRequested()); 30665b6dbf9SMircea Trofin } 30765b6dbf9SMircea Trofin 30865b6dbf9SMircea Trofin /// Log one inlining event. 30965b6dbf9SMircea Trofin void TrainingLogger::logInlineEvent(const InlineEvent &Event, 31065b6dbf9SMircea Trofin const MLModelRunner &ModelRunner) { 31136bb1fb1SMircea Trofin size_t CurrentFeature = 0; 31236bb1fb1SMircea Trofin for (; CurrentFeature < NumberOfFeatures; ++CurrentFeature) { 313059e0347SMircea Trofin int64_t F = *ModelRunner.getTensor<int64_t>(CurrentFeature); 31455e12f70SMircea Trofin L->logInt64Value(CurrentFeature, &F); 31536bb1fb1SMircea Trofin } 316211117b6SMircea Trofin 31736bb1fb1SMircea Trofin for (size_t I = 1; I < OutputCount; ++I) { 31862fc44caSMircea Trofin const auto &Result = *MUTR->lastEvaluationResult(); 31962fc44caSMircea Trofin const char *RawData = 32062fc44caSMircea Trofin reinterpret_cast<const char *>(Result.getUntypedTensorValue(I)); 32155e12f70SMircea Trofin L->logSpecifiedTensorValue(CurrentFeature, RawData); 32236bb1fb1SMircea Trofin ++CurrentFeature; 32362fc44caSMircea Trofin } 32436bb1fb1SMircea Trofin 32536bb1fb1SMircea Trofin assert(CurrentFeature == DefaultDecisionPos); 32655e12f70SMircea Trofin L->logInt64Value(DefaultDecisionPos, &Event.DefaultDecision); 32755e12f70SMircea Trofin L->logInt64Value(DecisionPos, &Event.AdvisedDecision); 32836bb1fb1SMircea Trofin if (InlineSizeEstimatorAnalysis::isEvaluatorRequested()) 32955e12f70SMircea Trofin L->logInt64Reward(Event.Reward); 33036bb1fb1SMircea Trofin 33136bb1fb1SMircea Trofin // For debugging / later use 33236bb1fb1SMircea Trofin Effects.push_back(Event.Effect); 33365b6dbf9SMircea Trofin } 33465b6dbf9SMircea Trofin 335d5c81be3SMircea Trofin void TrainingLogger::print() { 336d5c81be3SMircea Trofin std::error_code EC; 337d5c81be3SMircea Trofin raw_fd_ostream OutFile(LogFileName, EC); 338ae1a2a09SMircea Trofin L->flush(OutFile); 33965b6dbf9SMircea Trofin } 34065b6dbf9SMircea Trofin 34170f8d0acSMircea Trofin DevelopmentModeMLInlineAdvisor::DevelopmentModeMLInlineAdvisor( 34270f8d0acSMircea Trofin Module &M, ModuleAnalysisManager &MAM, 34370f8d0acSMircea Trofin std::unique_ptr<MLModelRunner> ModelRunner, 344a120fdd3SMircea Trofin std::function<bool(CallBase &)> GetDefaultAdvice, 345d5c81be3SMircea Trofin std::unique_ptr<TrainingLogger> Logger) 34670f8d0acSMircea Trofin : MLInlineAdvisor(M, MAM, std::move(ModelRunner)), 347a120fdd3SMircea Trofin GetDefaultAdvice(GetDefaultAdvice), 348a120fdd3SMircea Trofin IsDoingInference(isa<ModelUnderTrainingRunner>(getModelRunner())), 349d5c81be3SMircea Trofin Logger(std::move(Logger)), 35070f8d0acSMircea Trofin InitialNativeSize(isLogging() ? getTotalSizeEstimate() : 0), 35170f8d0acSMircea Trofin CurrentNativeSize(InitialNativeSize) { 35270f8d0acSMircea Trofin // We cannot have the case of neither inference nor logging. 35370f8d0acSMircea Trofin assert(IsDoingInference || isLogging()); 35470f8d0acSMircea Trofin } 35570f8d0acSMircea Trofin 35670f8d0acSMircea Trofin DevelopmentModeMLInlineAdvisor::~DevelopmentModeMLInlineAdvisor() { 357d5c81be3SMircea Trofin if (isLogging()) 358d5c81be3SMircea Trofin Logger->print(); 35970f8d0acSMircea Trofin } 36070f8d0acSMircea Trofin 3618c63df24SMircea Trofin Optional<size_t> 36270f8d0acSMircea Trofin DevelopmentModeMLInlineAdvisor::getNativeSizeEstimate(const Function &F) const { 3638c63df24SMircea Trofin if (!InlineSizeEstimatorAnalysis::isEvaluatorRequested()) 3648c63df24SMircea Trofin return None; 36570f8d0acSMircea Trofin auto &R = 36670f8d0acSMircea Trofin FAM.getResult<InlineSizeEstimatorAnalysis>(const_cast<Function &>(F)); 36770f8d0acSMircea Trofin if (!R) { 36870f8d0acSMircea Trofin F.getParent()->getContext().emitError( 36970f8d0acSMircea Trofin "Native size estimator is not present."); 37070f8d0acSMircea Trofin return 0; 37170f8d0acSMircea Trofin } 37270f8d0acSMircea Trofin return *R; 37370f8d0acSMircea Trofin } 37470f8d0acSMircea Trofin 37570f8d0acSMircea Trofin std::unique_ptr<MLInlineAdvice> 376e8049dc3SMircea Trofin DevelopmentModeMLInlineAdvisor::getMandatoryAdviceImpl(CallBase &CB) { 37770f8d0acSMircea Trofin return std::make_unique<LoggingMLInlineAdvice>( 37870f8d0acSMircea Trofin /*Advisor=*/this, 379e8049dc3SMircea Trofin /*CB=*/CB, /*ORE=*/getCallerORE(CB), /*Recommendation=*/true, 380e8049dc3SMircea Trofin /*Logger=*/*Logger, 38170f8d0acSMircea Trofin /*CallerSizeEstimateBefore=*/getNativeSizeEstimate(*CB.getCaller()), 38270f8d0acSMircea Trofin /*CalleeSizeEstimateBefore=*/ 38370f8d0acSMircea Trofin getNativeSizeEstimate(*CB.getCalledFunction()), 38487fb7aa1SMircea Trofin /*DefaultDecision=*/true, /*Mandatory*/ true); 38570f8d0acSMircea Trofin } 38670f8d0acSMircea Trofin 38770f8d0acSMircea Trofin std::unique_ptr<MLInlineAdvice> 38870f8d0acSMircea Trofin DevelopmentModeMLInlineAdvisor::getAdviceFromModel( 38970f8d0acSMircea Trofin CallBase &CB, OptimizationRemarkEmitter &ORE) { 39070f8d0acSMircea Trofin if (IsDoingInference && !isLogging()) 39170f8d0acSMircea Trofin return MLInlineAdvisor::getAdviceFromModel(CB, ORE); 39270f8d0acSMircea Trofin 39370f8d0acSMircea Trofin bool DefaultAdvice = GetDefaultAdvice(CB); 394059e0347SMircea Trofin auto Recommendation = 395059e0347SMircea Trofin IsDoingInference ? static_cast<bool>(ModelRunner->evaluate<int64_t>()) 396059e0347SMircea Trofin : DefaultAdvice; 39770f8d0acSMircea Trofin return std::make_unique<LoggingMLInlineAdvice>( 39870f8d0acSMircea Trofin /*Advisor=*/this, 39970f8d0acSMircea Trofin /*CB=*/CB, /*ORE=*/ORE, /*Recommendation=*/Recommendation, 400d5c81be3SMircea Trofin /*Logger=*/*Logger, 40170f8d0acSMircea Trofin /*CallerSizeEstimateBefore=*/getNativeSizeEstimate(*CB.getCaller()), 40270f8d0acSMircea Trofin /*CalleeSizeEstimateBefore=*/ 40370f8d0acSMircea Trofin getNativeSizeEstimate(*CB.getCalledFunction()), 40470f8d0acSMircea Trofin /*DefaultDecision=*/DefaultAdvice); 40570f8d0acSMircea Trofin } 40670f8d0acSMircea Trofin 40770f8d0acSMircea Trofin size_t DevelopmentModeMLInlineAdvisor::getTotalSizeEstimate() { 4088c63df24SMircea Trofin if (!InlineSizeEstimatorAnalysis::isEvaluatorRequested()) 4098c63df24SMircea Trofin return 0; 41070f8d0acSMircea Trofin size_t Ret = 0; 41170f8d0acSMircea Trofin for (auto &F : M) { 41270f8d0acSMircea Trofin if (F.isDeclaration()) 41370f8d0acSMircea Trofin continue; 4148c63df24SMircea Trofin Ret += *getNativeSizeEstimate(F); 41570f8d0acSMircea Trofin } 41670f8d0acSMircea Trofin return Ret; 41770f8d0acSMircea Trofin } 41870f8d0acSMircea Trofin 41970f8d0acSMircea Trofin std::unique_ptr<InlineAdvisor> llvm::getDevelopmentModeAdvisor( 42070f8d0acSMircea Trofin Module &M, ModuleAnalysisManager &MAM, 42170f8d0acSMircea Trofin std::function<bool(CallBase &)> GetDefaultAdvice) { 42270f8d0acSMircea Trofin auto &Ctx = M.getContext(); 42370f8d0acSMircea Trofin std::unique_ptr<MLModelRunner> Runner; 42470f8d0acSMircea Trofin if (TFModelUnderTrainingPath.empty()) 42504f2712eSMircea Trofin Runner.reset(new NoInferenceModelRunner(Ctx, getInputFeatures())); 426a120fdd3SMircea Trofin else 427a120fdd3SMircea Trofin Runner = ModelUnderTrainingRunner::createAndEnsureValid( 428a120fdd3SMircea Trofin Ctx, TFModelUnderTrainingPath, DecisionName, getInputFeatures(), 429a120fdd3SMircea Trofin TFOutputSpecOverride); 430a120fdd3SMircea Trofin if (!Runner) 43170f8d0acSMircea Trofin return nullptr; 432d5c81be3SMircea Trofin std::unique_ptr<TrainingLogger> Logger; 433d5c81be3SMircea Trofin if (!TrainingLog.empty()) 434a120fdd3SMircea Trofin Logger = std::make_unique<TrainingLogger>( 435a120fdd3SMircea Trofin TrainingLog, dyn_cast<ModelUnderTrainingRunner>(Runner.get())); 436d5c81be3SMircea Trofin 43770f8d0acSMircea Trofin return std::make_unique<DevelopmentModeMLInlineAdvisor>( 438a120fdd3SMircea Trofin M, MAM, std::move(Runner), GetDefaultAdvice, std::move(Logger)); 43970f8d0acSMircea Trofin } 4404fe912f1SNico Weber #endif // defined(LLVM_HAVE_TF_API) 441