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