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