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