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 // 928bb2193SMircea Trofin // This file implements a model runner using TFLite, allowing the 1070f8d0acSMircea Trofin // loading of a model from a command line option. 1170f8d0acSMircea Trofin // 1270f8d0acSMircea Trofin //===----------------------------------------------------------------------===// 131ee3bb17SMircea Trofin #include "llvm/Analysis/TensorSpec.h" 144fe912f1SNico Weber #include "llvm/Config/config.h" 15edc83a15SKazu Hirata #if defined(LLVM_HAVE_TFLITE) 164fe912f1SNico Weber 175f4ae564SJan Svoboda #include "llvm/ADT/BitVector.h" 1870f8d0acSMircea Trofin #include "llvm/Analysis/CallGraph.h" 1970f8d0acSMircea Trofin #include "llvm/Analysis/InlineSizeEstimatorAnalysis.h" 2070f8d0acSMircea Trofin #include "llvm/Analysis/MLInlineAdvisor.h" 2104f2712eSMircea Trofin #include "llvm/Analysis/ModelUnderTrainingRunner.h" 22059e0347SMircea Trofin #include "llvm/Analysis/NoInferenceModelRunner.h" 2370f8d0acSMircea Trofin #include "llvm/Analysis/Utils/TFUtils.h" 240cb9746aSMircea Trofin #include "llvm/Analysis/Utils/TrainingLogger.h" 2570f8d0acSMircea Trofin #include "llvm/IR/LLVMContext.h" 26*38341993SMircea Trofin #include "llvm/IR/Module.h" 2770f8d0acSMircea Trofin #include "llvm/Support/CommandLine.h" 2870f8d0acSMircea Trofin #include "llvm/Support/ManagedStatic.h" 2970f8d0acSMircea Trofin 3070f8d0acSMircea Trofin #include <vector> 319c444f70SKazu Hirata #include <optional> 3270f8d0acSMircea Trofin 3370f8d0acSMircea Trofin using namespace llvm; 3470f8d0acSMircea Trofin 3570f8d0acSMircea Trofin static cl::opt<std::string> TrainingLog( 3670f8d0acSMircea Trofin "training-log", cl::Hidden, 3770f8d0acSMircea Trofin cl::desc("Path where the development - mode inlining log is saved.")); 3870f8d0acSMircea Trofin 3970f8d0acSMircea Trofin static cl::opt<std::string> TFModelUnderTrainingPath( 4070f8d0acSMircea Trofin "ml-inliner-model-under-training", cl::Hidden, 4162fc44caSMircea Trofin cl::desc(R"(Path to SavedModel from the previous training iteration. 4262fc44caSMircea Trofin The directory is also expected to contain a JSON specification of the 4362fc44caSMircea Trofin outputs expected to be logged, where the first entry must be the 4462fc44caSMircea Trofin inlining decision. The file containing the specification should be 4562fc44caSMircea Trofin called output_spec.json. The expected JSON value is an array of 4662fc44caSMircea Trofin dictionaries. Each dictionary should have 2 keys: 4762fc44caSMircea Trofin 4862fc44caSMircea Trofin - "tensor_spec, followed by the TensorSpec description of the 4962fc44caSMircea Trofin output; and 5062fc44caSMircea Trofin - "logging_name", a string indicating the name to use when 5162fc44caSMircea Trofin logging the output values. 5262fc44caSMircea Trofin 5362fc44caSMircea Trofin Example: 5462fc44caSMircea Trofin [ 5562fc44caSMircea Trofin { 5662fc44caSMircea Trofin "logging_name" : "some_name", 5762fc44caSMircea Trofin "tensor_spec" : { 5862fc44caSMircea Trofin "name" : "model_name", 5962fc44caSMircea Trofin "port" : 0, 6062fc44caSMircea Trofin "shape" : [2, 3], 6162fc44caSMircea Trofin "type" : "float" 6262fc44caSMircea Trofin } 6362fc44caSMircea Trofin } 6462fc44caSMircea Trofin ] 6562fc44caSMircea Trofin 6662fc44caSMircea Trofin The first value must always correspond to the decision.)")); 6762fc44caSMircea Trofin 6862fc44caSMircea Trofin static cl::opt<std::string> TFOutputSpecOverride( 6962fc44caSMircea Trofin "ml-inliner-output-spec-override", cl::Hidden, 7062fc44caSMircea Trofin cl::desc("Override the path to the output spec json file. See " 7162fc44caSMircea Trofin "-ml-inliner-model-under-training documentation for the " 7262fc44caSMircea Trofin "specification of that file.")); 7370f8d0acSMircea Trofin 7470f8d0acSMircea Trofin static cl::opt<std::string> TFFeedPrefix("ml-inliner-trained-model-feed-prefix", 7570f8d0acSMircea Trofin cl::Hidden, cl::init("action_"), 7670f8d0acSMircea Trofin cl::desc("Prefix for feature names.")); 7770f8d0acSMircea Trofin 7870f8d0acSMircea Trofin namespace { 7970f8d0acSMircea Trofin /// An InlineEvent, used by TrainingLogger. 8070f8d0acSMircea Trofin struct InlineEvent { 8170f8d0acSMircea Trofin /// What the default policy's decision would have been. 8236bb1fb1SMircea Trofin int64_t DefaultDecision = 0; 8370f8d0acSMircea Trofin 8470f8d0acSMircea Trofin /// What we advised. When training off the default policy, this is the same as 8570f8d0acSMircea Trofin /// DefaultDecision. 8636bb1fb1SMircea Trofin int64_t AdvisedDecision = 0; 8770f8d0acSMircea Trofin 8870f8d0acSMircea Trofin /// What actually happened. This would be 'false' in the case of an inline 8970f8d0acSMircea Trofin /// error, even if AdvisedDecision were true, otherwise it agrees with 9070f8d0acSMircea Trofin /// AdvisedDecision. 9170f8d0acSMircea Trofin bool Effect = false; 9270f8d0acSMircea Trofin 9370f8d0acSMircea Trofin /// What the change in size was: size_after - size_before 9470f8d0acSMircea Trofin int64_t Reward = 0; 9570f8d0acSMircea Trofin }; 9670f8d0acSMircea Trofin 975898be19SMircea Trofin /// Collect data we may use for training a model. 9870f8d0acSMircea Trofin class TrainingLogger final { 9970f8d0acSMircea Trofin public: 10062fc44caSMircea Trofin TrainingLogger(StringRef LogFileName, const ModelUnderTrainingRunner *MUTR); 10170f8d0acSMircea Trofin 10270f8d0acSMircea Trofin /// Log one inlining event. 10370f8d0acSMircea Trofin void logInlineEvent(const InlineEvent &Event, 10465b6dbf9SMircea Trofin const MLModelRunner &ModelRunner); 10570f8d0acSMircea Trofin 10670f8d0acSMircea Trofin private: 107d5c81be3SMircea Trofin StringRef LogFileName; 10862fc44caSMircea Trofin const ModelUnderTrainingRunner *const MUTR; 10936bb1fb1SMircea Trofin std::unique_ptr<Logger> L; 1105f4ae564SJan Svoboda BitVector Effects; 11136bb1fb1SMircea Trofin /// Set these 2 clearly OOB, to make sure we set them later. 11236bb1fb1SMircea Trofin size_t DefaultDecisionPos = std::numeric_limits<size_t>::max(); 11336bb1fb1SMircea Trofin size_t DecisionPos = std::numeric_limits<size_t>::max(); 11470f8d0acSMircea Trofin }; 11570f8d0acSMircea Trofin 11670f8d0acSMircea Trofin /// An extension of the MLInlineAdvisor for the 'development' mode, targeting 11770f8d0acSMircea Trofin /// the offline training scenario. Note that training happens outside of the 11870f8d0acSMircea Trofin /// compiler, this facility is concerned with producing training data ("logs"). 11970f8d0acSMircea Trofin /// This InlineAdvisor can operate in the following modes: 12070f8d0acSMircea Trofin /// 12170f8d0acSMircea Trofin /// 1) collect logs for the default policy. This is useful for bootstrapping 12270f8d0acSMircea Trofin /// training, which will be considerably faster by starting from a reasonable 12370f8d0acSMircea Trofin /// policy. 12470f8d0acSMircea Trofin /// 12570f8d0acSMircea Trofin /// 2) collect logs for the ML policy, using a model from a previous 12670f8d0acSMircea Trofin /// training. Potentially, that model uses internally some small random 12770f8d0acSMircea Trofin /// perturbation of its weights, to induce exploration (setting this up is the 12870f8d0acSMircea Trofin /// responsibility of the training algorithm). The logs would then be used to 12970f8d0acSMircea Trofin /// retrain and improve on this model. 13070f8d0acSMircea Trofin /// 13170f8d0acSMircea Trofin /// 3) use the provided model, with no logging. This is useful for end to end 13270f8d0acSMircea Trofin /// validation - the model, in this case, is a release candidate and shouldn't 13370f8d0acSMircea Trofin /// have random perturbations. It is a convenience feature: rather than needing 13470f8d0acSMircea Trofin /// to take the release candidate model and compile it in 'release' mode, 13570f8d0acSMircea Trofin /// validate it, then potentially discard it, it's easier to just pass the model 13670f8d0acSMircea Trofin /// to the compiler, albeit compilation would be slower, as a one-off. Once the 13770f8d0acSMircea Trofin /// model behaves satisfactorily, it can be compiled AOT, for efficiency, in 13870f8d0acSMircea Trofin /// release mode. The expectation is that a well-trained model provides a good 13970f8d0acSMircea Trofin /// policy over a sufficiently diverse codebase, over many changes (i.e. 14070f8d0acSMircea Trofin /// training happens seldom). 14170f8d0acSMircea Trofin class DevelopmentModeMLInlineAdvisor : public MLInlineAdvisor { 14270f8d0acSMircea Trofin public: 14370f8d0acSMircea Trofin DevelopmentModeMLInlineAdvisor( 14470f8d0acSMircea Trofin Module &M, ModuleAnalysisManager &MAM, 14570f8d0acSMircea Trofin std::unique_ptr<MLModelRunner> ModelRunner, 146a120fdd3SMircea Trofin std::function<bool(CallBase &)> GetDefaultAdvice, 147d5c81be3SMircea Trofin std::unique_ptr<TrainingLogger> Logger); 14870f8d0acSMircea Trofin 14970f8d0acSMircea Trofin size_t getTotalSizeEstimate(); 15070f8d0acSMircea Trofin updateNativeSizeEstimate(int64_t Change)1518c63df24SMircea Trofin void updateNativeSizeEstimate(int64_t Change) { 1528c63df24SMircea Trofin *CurrentNativeSize += Change; 1538c63df24SMircea Trofin } 15470f8d0acSMircea Trofin void resetNativeSize(Function *F) { 1550d06b14fSMircea Trofin PreservedAnalyses PA = PreservedAnalyses::all(); 1560d06b14fSMircea Trofin PA.abandon<InlineSizeEstimatorAnalysis>(); 1570d06b14fSMircea Trofin FAM.invalidate(*F, PA); 15870f8d0acSMircea Trofin } 15970f8d0acSMircea Trofin 16070f8d0acSMircea Trofin std::unique_ptr<MLInlineAdvice> 16170f8d0acSMircea Trofin getAdviceFromModel(CallBase &CB, OptimizationRemarkEmitter &ORE) override; 16270f8d0acSMircea Trofin 163d4b6fcb3SFangrui Song std::optional<size_t> getNativeSizeEstimate(const Function &F) const; 16470f8d0acSMircea Trofin 16570f8d0acSMircea Trofin private: 166d5c81be3SMircea Trofin bool isLogging() const { return !!Logger; } 167e8049dc3SMircea Trofin std::unique_ptr<MLInlineAdvice> getMandatoryAdviceImpl(CallBase &CB) override; 16870f8d0acSMircea Trofin 16970f8d0acSMircea Trofin const bool IsDoingInference; 170d5c81be3SMircea Trofin std::unique_ptr<TrainingLogger> Logger; 17170f8d0acSMircea Trofin 172d4b6fcb3SFangrui Song const std::optional<int32_t> InitialNativeSize; 173d4b6fcb3SFangrui Song std::optional<int32_t> CurrentNativeSize; 17470f8d0acSMircea Trofin }; 17570f8d0acSMircea Trofin 17670f8d0acSMircea Trofin /// A variant of MLInlineAdvice that tracks all non-trivial inlining 17770f8d0acSMircea Trofin /// decisions, for training/logging. 17870f8d0acSMircea Trofin class LoggingMLInlineAdvice : public MLInlineAdvice { 17970f8d0acSMircea Trofin public: 18070f8d0acSMircea Trofin LoggingMLInlineAdvice(DevelopmentModeMLInlineAdvisor *Advisor, CallBase &CB, 18170f8d0acSMircea Trofin OptimizationRemarkEmitter &ORE, bool Recommendation, 1828c63df24SMircea Trofin TrainingLogger &Logger, 183d4b6fcb3SFangrui Song std::optional<size_t> CallerSizeEstimateBefore, 184d4b6fcb3SFangrui Song std::optional<size_t> CalleeSizeEstimateBefore, 1858c63df24SMircea Trofin bool DefaultDecision, bool Mandatory = false) 18670f8d0acSMircea Trofin : MLInlineAdvice(Advisor, CB, ORE, Recommendation), Logger(Logger), 18770f8d0acSMircea Trofin CallerSizeEstimateBefore(CallerSizeEstimateBefore), 18870f8d0acSMircea Trofin CalleeSizeEstimateBefore(CalleeSizeEstimateBefore), 18987fb7aa1SMircea Trofin DefaultDecision(DefaultDecision), Mandatory(Mandatory) {} 19070f8d0acSMircea Trofin 19170f8d0acSMircea Trofin virtual ~LoggingMLInlineAdvice() = default; 19270f8d0acSMircea Trofin 19370f8d0acSMircea Trofin private: 19470f8d0acSMircea Trofin DevelopmentModeMLInlineAdvisor *getAdvisor() const { 19570f8d0acSMircea Trofin return static_cast<DevelopmentModeMLInlineAdvisor *>(Advisor); 19670f8d0acSMircea Trofin } 19770f8d0acSMircea Trofin void recordInliningImpl() override { 19870f8d0acSMircea Trofin MLInlineAdvice::recordInliningImpl(); 19970f8d0acSMircea Trofin getAdvisor()->resetNativeSize(Caller); 20070f8d0acSMircea Trofin int Reward = std::numeric_limits<int>::max(); 2018c63df24SMircea Trofin if (InlineSizeEstimatorAnalysis::isEvaluatorRequested() && 2028c63df24SMircea Trofin !getAdvisor()->isForcedToStop()) { 2038c63df24SMircea Trofin int NativeSizeAfter = *getAdvisor()->getNativeSizeEstimate(*Caller) + 2048c63df24SMircea Trofin *CalleeSizeEstimateBefore; 20570f8d0acSMircea Trofin Reward = NativeSizeAfter - 2068c63df24SMircea Trofin (*CallerSizeEstimateBefore + *CalleeSizeEstimateBefore); 20770f8d0acSMircea Trofin getAdvisor()->updateNativeSizeEstimate(Reward); 20870f8d0acSMircea Trofin } 20970f8d0acSMircea Trofin log(Reward, /*Success=*/true); 21070f8d0acSMircea Trofin } 21170f8d0acSMircea Trofin 21270f8d0acSMircea Trofin void recordInliningWithCalleeDeletedImpl() override { 21370f8d0acSMircea Trofin MLInlineAdvice::recordInliningWithCalleeDeletedImpl(); 21470f8d0acSMircea Trofin getAdvisor()->resetNativeSize(Caller); 2158c63df24SMircea Trofin if (InlineSizeEstimatorAnalysis::isEvaluatorRequested() && 2168c63df24SMircea Trofin !getAdvisor()->isForcedToStop()) { 2178c63df24SMircea Trofin int NativeSizeAfter = *getAdvisor()->getNativeSizeEstimate(*Caller); 21870f8d0acSMircea Trofin int Reward = NativeSizeAfter - 2198c63df24SMircea Trofin (*CallerSizeEstimateBefore + *CalleeSizeEstimateBefore); 22070f8d0acSMircea Trofin getAdvisor()->updateNativeSizeEstimate(Reward); 22170f8d0acSMircea Trofin log(Reward, /*Success=*/true); 2221055c5e1SMircea Trofin } else { 2231055c5e1SMircea Trofin log(NoReward, /*Success=*/true); 22470f8d0acSMircea Trofin } 22570f8d0acSMircea Trofin } 22670f8d0acSMircea Trofin 22770f8d0acSMircea Trofin void recordUnsuccessfulInliningImpl(const InlineResult &Result) override { 22870f8d0acSMircea Trofin MLInlineAdvice::recordUnsuccessfulInliningImpl(Result); 22970f8d0acSMircea Trofin log(NoReward, /*Success=*/false); 23070f8d0acSMircea Trofin } 23170f8d0acSMircea Trofin 23270f8d0acSMircea Trofin void recordUnattemptedInliningImpl() override { 23370f8d0acSMircea Trofin MLInlineAdvice::recordUnattemptedInliningImpl(); 23470f8d0acSMircea Trofin log(NoReward, /*Success=*/false); 23570f8d0acSMircea Trofin } 23670f8d0acSMircea Trofin 23770f8d0acSMircea Trofin void log(int64_t Reward, bool Success) { 23887fb7aa1SMircea Trofin if (Mandatory) 23987fb7aa1SMircea Trofin return; 24070f8d0acSMircea Trofin InlineEvent Event; 24170f8d0acSMircea Trofin Event.AdvisedDecision = isInliningRecommended(); 24270f8d0acSMircea Trofin Event.DefaultDecision = DefaultDecision; 24370f8d0acSMircea Trofin Event.Effect = Success; 24470f8d0acSMircea Trofin Event.Reward = Reward; 24570f8d0acSMircea Trofin Logger.logInlineEvent(Event, getAdvisor()->getModelRunner()); 24670f8d0acSMircea Trofin } 24770f8d0acSMircea Trofin 24870f8d0acSMircea Trofin static const int64_t NoReward = 0; 24970f8d0acSMircea Trofin TrainingLogger &Logger; 250d4b6fcb3SFangrui Song const std::optional<size_t> CallerSizeEstimateBefore; 251d4b6fcb3SFangrui Song const std::optional<size_t> CalleeSizeEstimateBefore; 25236bb1fb1SMircea Trofin const int64_t DefaultDecision; 25336bb1fb1SMircea Trofin const int64_t Mandatory; 25470f8d0acSMircea Trofin }; 25570f8d0acSMircea Trofin 25604f2712eSMircea Trofin static const std::vector<TensorSpec> TrainingOnlyFeatures{ 25771059257SMircea Trofin TensorSpec::createSpec<float>(TFFeedPrefix + "discount", {1}), 25871059257SMircea Trofin TensorSpec::createSpec<float>(TFFeedPrefix + "reward", {1}), 25971059257SMircea Trofin TensorSpec::createSpec<int32_t>(TFFeedPrefix + "step_type", {1})}; 26004f2712eSMircea Trofin 26104f2712eSMircea Trofin static const std::vector<TensorSpec> getInputFeatures() { 26204f2712eSMircea Trofin std::vector<TensorSpec> InputSpecs; 26304f2712eSMircea Trofin for (size_t I = 0; I < NumberOfFeatures; ++I) 264c35ad9eeSMircea Trofin InputSpecs.push_back(TensorSpec::createSpec<int64_t>( 265c35ad9eeSMircea Trofin TFFeedPrefix + FeatureMap[I].name(), FeatureMap[I].shape())); 26604f2712eSMircea Trofin append_range(InputSpecs, TrainingOnlyFeatures); 26704f2712eSMircea Trofin return InputSpecs; 26804f2712eSMircea Trofin } 26904f2712eSMircea Trofin 27070f8d0acSMircea Trofin } // namespace 27170f8d0acSMircea Trofin 27262fc44caSMircea Trofin TrainingLogger::TrainingLogger(StringRef LogFileName, 27362fc44caSMircea Trofin const ModelUnderTrainingRunner *MUTR) 27462fc44caSMircea Trofin : LogFileName(LogFileName), MUTR(MUTR) { 27562fc44caSMircea Trofin // The first output is the inlining decision. 2761ee3bb17SMircea Trofin std::vector<TensorSpec> FT(FeatureMap.begin(), FeatureMap.end()); 27736bb1fb1SMircea Trofin 2781ee3bb17SMircea Trofin if (MUTR) 2791ee3bb17SMircea Trofin append_range(FT, MUTR->extraOutputsForLoggingSpecs()); 28036bb1fb1SMircea Trofin 28136bb1fb1SMircea Trofin DefaultDecisionPos = FT.size(); 282ab2e7666SMircea Trofin FT.push_back(DefaultDecisionSpec); 28336bb1fb1SMircea Trofin 28436bb1fb1SMircea Trofin DecisionPos = FT.size(); 2855fd51fcbSMircea Trofin FT.push_back(InlineDecisionSpec); 2866d11baf0SMircea Trofin std::error_code EC; 2876d11baf0SMircea Trofin auto OS = std::make_unique<raw_fd_ostream>(TrainingLog, EC); 2886d11baf0SMircea Trofin if (EC) 2896d11baf0SMircea Trofin dbgs() << (EC.message() + ":" + TrainingLog); 29036bb1fb1SMircea Trofin 29136bb1fb1SMircea Trofin L = std::make_unique<Logger>( 2926d11baf0SMircea Trofin std::move(OS), FT, TensorSpec::createSpec<int64_t>(RewardName, {1}), 29336bb1fb1SMircea Trofin InlineSizeEstimatorAnalysis::isEvaluatorRequested()); 2946d11baf0SMircea Trofin L->switchContext(""); 29565b6dbf9SMircea Trofin } 29665b6dbf9SMircea Trofin 29765b6dbf9SMircea Trofin /// Log one inlining event. 29865b6dbf9SMircea Trofin void TrainingLogger::logInlineEvent(const InlineEvent &Event, 29965b6dbf9SMircea Trofin const MLModelRunner &ModelRunner) { 3006d11baf0SMircea Trofin L->startObservation(); 30136bb1fb1SMircea Trofin size_t CurrentFeature = 0; 3026d11baf0SMircea Trofin for (; CurrentFeature < NumberOfFeatures; ++CurrentFeature) 3036d11baf0SMircea Trofin L->logTensorValue(CurrentFeature, 3046d11baf0SMircea Trofin reinterpret_cast<const char *>( 3056d11baf0SMircea Trofin ModelRunner.getTensorUntyped(CurrentFeature))); 306211117b6SMircea Trofin 3071ee3bb17SMircea Trofin if (MUTR) 3081ee3bb17SMircea Trofin for (size_t I = 0; I < MUTR->extraOutputsForLoggingSpecs().size(); ++I) { 30962fc44caSMircea Trofin const char *RawData = 3101ee3bb17SMircea Trofin reinterpret_cast<const char *>(MUTR->getUntypedExtraOutputValue(I)); 3116d11baf0SMircea Trofin L->logTensorValue(CurrentFeature, RawData); 31236bb1fb1SMircea Trofin ++CurrentFeature; 31362fc44caSMircea Trofin } 31436bb1fb1SMircea Trofin 31536bb1fb1SMircea Trofin assert(CurrentFeature == DefaultDecisionPos); 3166d11baf0SMircea Trofin L->logTensorValue(DefaultDecisionPos, 3176d11baf0SMircea Trofin reinterpret_cast<const char *>(&Event.DefaultDecision)); 3186d11baf0SMircea Trofin L->logTensorValue(DecisionPos, 3196d11baf0SMircea Trofin reinterpret_cast<const char *>(&Event.AdvisedDecision)); 3206d11baf0SMircea Trofin L->endObservation(); 32136bb1fb1SMircea Trofin if (InlineSizeEstimatorAnalysis::isEvaluatorRequested()) 3226d11baf0SMircea Trofin L->logReward(Event.Reward); 32336bb1fb1SMircea Trofin 32436bb1fb1SMircea Trofin // For debugging / later use 32536bb1fb1SMircea Trofin Effects.push_back(Event.Effect); 32665b6dbf9SMircea Trofin } 32765b6dbf9SMircea Trofin 32870f8d0acSMircea Trofin DevelopmentModeMLInlineAdvisor::DevelopmentModeMLInlineAdvisor( 32970f8d0acSMircea Trofin Module &M, ModuleAnalysisManager &MAM, 33070f8d0acSMircea Trofin std::unique_ptr<MLModelRunner> ModelRunner, 331a120fdd3SMircea Trofin std::function<bool(CallBase &)> GetDefaultAdvice, 332d5c81be3SMircea Trofin std::unique_ptr<TrainingLogger> Logger) 333ab2e7666SMircea Trofin : MLInlineAdvisor(M, MAM, std::move(ModelRunner), GetDefaultAdvice), 334a120fdd3SMircea Trofin IsDoingInference(isa<ModelUnderTrainingRunner>(getModelRunner())), 335d5c81be3SMircea Trofin Logger(std::move(Logger)), 33670f8d0acSMircea Trofin InitialNativeSize(isLogging() ? getTotalSizeEstimate() : 0), 33770f8d0acSMircea Trofin CurrentNativeSize(InitialNativeSize) { 33870f8d0acSMircea Trofin // We cannot have the case of neither inference nor logging. 33970f8d0acSMircea Trofin assert(IsDoingInference || isLogging()); 34070f8d0acSMircea Trofin } 34170f8d0acSMircea Trofin 342d4b6fcb3SFangrui Song std::optional<size_t> 34370f8d0acSMircea Trofin DevelopmentModeMLInlineAdvisor::getNativeSizeEstimate(const Function &F) const { 3448c63df24SMircea Trofin if (!InlineSizeEstimatorAnalysis::isEvaluatorRequested()) 3459c444f70SKazu Hirata return std::nullopt; 34670f8d0acSMircea Trofin auto &R = 34770f8d0acSMircea Trofin FAM.getResult<InlineSizeEstimatorAnalysis>(const_cast<Function &>(F)); 34870f8d0acSMircea Trofin if (!R) { 34970f8d0acSMircea Trofin F.getParent()->getContext().emitError( 35070f8d0acSMircea Trofin "Native size estimator is not present."); 35170f8d0acSMircea Trofin return 0; 35270f8d0acSMircea Trofin } 35370f8d0acSMircea Trofin return *R; 35470f8d0acSMircea Trofin } 35570f8d0acSMircea Trofin 35670f8d0acSMircea Trofin std::unique_ptr<MLInlineAdvice> 357e8049dc3SMircea Trofin DevelopmentModeMLInlineAdvisor::getMandatoryAdviceImpl(CallBase &CB) { 35870f8d0acSMircea Trofin return std::make_unique<LoggingMLInlineAdvice>( 35970f8d0acSMircea Trofin /*Advisor=*/this, 360e8049dc3SMircea Trofin /*CB=*/CB, /*ORE=*/getCallerORE(CB), /*Recommendation=*/true, 361e8049dc3SMircea Trofin /*Logger=*/*Logger, 36270f8d0acSMircea Trofin /*CallerSizeEstimateBefore=*/getNativeSizeEstimate(*CB.getCaller()), 36370f8d0acSMircea Trofin /*CalleeSizeEstimateBefore=*/ 36470f8d0acSMircea Trofin getNativeSizeEstimate(*CB.getCalledFunction()), 36587fb7aa1SMircea Trofin /*DefaultDecision=*/true, /*Mandatory*/ true); 36670f8d0acSMircea Trofin } 36770f8d0acSMircea Trofin 36870f8d0acSMircea Trofin std::unique_ptr<MLInlineAdvice> 36970f8d0acSMircea Trofin DevelopmentModeMLInlineAdvisor::getAdviceFromModel( 37070f8d0acSMircea Trofin CallBase &CB, OptimizationRemarkEmitter &ORE) { 37170f8d0acSMircea Trofin if (IsDoingInference && !isLogging()) 37270f8d0acSMircea Trofin return MLInlineAdvisor::getAdviceFromModel(CB, ORE); 37370f8d0acSMircea Trofin 37470f8d0acSMircea Trofin bool DefaultAdvice = GetDefaultAdvice(CB); 375059e0347SMircea Trofin auto Recommendation = 376059e0347SMircea Trofin IsDoingInference ? static_cast<bool>(ModelRunner->evaluate<int64_t>()) 377059e0347SMircea Trofin : DefaultAdvice; 37870f8d0acSMircea Trofin return std::make_unique<LoggingMLInlineAdvice>( 37970f8d0acSMircea Trofin /*Advisor=*/this, 38070f8d0acSMircea Trofin /*CB=*/CB, /*ORE=*/ORE, /*Recommendation=*/Recommendation, 381d5c81be3SMircea Trofin /*Logger=*/*Logger, 38270f8d0acSMircea Trofin /*CallerSizeEstimateBefore=*/getNativeSizeEstimate(*CB.getCaller()), 38370f8d0acSMircea Trofin /*CalleeSizeEstimateBefore=*/ 38470f8d0acSMircea Trofin getNativeSizeEstimate(*CB.getCalledFunction()), 38570f8d0acSMircea Trofin /*DefaultDecision=*/DefaultAdvice); 38670f8d0acSMircea Trofin } 38770f8d0acSMircea Trofin 38870f8d0acSMircea Trofin size_t DevelopmentModeMLInlineAdvisor::getTotalSizeEstimate() { 3898c63df24SMircea Trofin if (!InlineSizeEstimatorAnalysis::isEvaluatorRequested()) 3908c63df24SMircea Trofin return 0; 39170f8d0acSMircea Trofin size_t Ret = 0; 39270f8d0acSMircea Trofin for (auto &F : M) { 39370f8d0acSMircea Trofin if (F.isDeclaration()) 39470f8d0acSMircea Trofin continue; 3958c63df24SMircea Trofin Ret += *getNativeSizeEstimate(F); 39670f8d0acSMircea Trofin } 39770f8d0acSMircea Trofin return Ret; 39870f8d0acSMircea Trofin } 39970f8d0acSMircea Trofin 40070f8d0acSMircea Trofin std::unique_ptr<InlineAdvisor> llvm::getDevelopmentModeAdvisor( 40170f8d0acSMircea Trofin Module &M, ModuleAnalysisManager &MAM, 40270f8d0acSMircea Trofin std::function<bool(CallBase &)> GetDefaultAdvice) { 40370f8d0acSMircea Trofin auto &Ctx = M.getContext(); 40470f8d0acSMircea Trofin std::unique_ptr<MLModelRunner> Runner; 40570f8d0acSMircea Trofin if (TFModelUnderTrainingPath.empty()) 40604f2712eSMircea Trofin Runner.reset(new NoInferenceModelRunner(Ctx, getInputFeatures())); 407a120fdd3SMircea Trofin else 408a120fdd3SMircea Trofin Runner = ModelUnderTrainingRunner::createAndEnsureValid( 409a120fdd3SMircea Trofin Ctx, TFModelUnderTrainingPath, DecisionName, getInputFeatures(), 410a120fdd3SMircea Trofin TFOutputSpecOverride); 411a120fdd3SMircea Trofin if (!Runner) 41270f8d0acSMircea Trofin return nullptr; 413d5c81be3SMircea Trofin std::unique_ptr<TrainingLogger> Logger; 414d5c81be3SMircea Trofin if (!TrainingLog.empty()) 415a120fdd3SMircea Trofin Logger = std::make_unique<TrainingLogger>( 416a120fdd3SMircea Trofin TrainingLog, dyn_cast<ModelUnderTrainingRunner>(Runner.get())); 417d5c81be3SMircea Trofin 41870f8d0acSMircea Trofin return std::make_unique<DevelopmentModeMLInlineAdvisor>( 419a120fdd3SMircea Trofin M, MAM, std::move(Runner), GetDefaultAdvice, std::move(Logger)); 42070f8d0acSMircea Trofin } 421edc83a15SKazu Hirata #endif // defined(LLVM_HAVE_TFLITE) 422