LLVM 24.0.0git
MLRegAllocPriorityAdvisor.cpp
Go to the documentation of this file.
1//===- MLRegAllocPriorityAdvisor.cpp - ML priority advisor-----------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// Implementation of the ML priority advisor and reward injection pass
10//
11//===----------------------------------------------------------------------===//
12
13#include "AllocationOrder.h"
14#include "RegAllocGreedy.h"
26#include "llvm/CodeGen/Passes.h"
32#include "llvm/Pass.h"
33#include "llvm/PassRegistry.h"
35
36#include <cmath>
37#include <limits>
38
39#if defined(LLVM_HAVE_TFLITE)
43#include "llvm/IR/Module.h"
44#endif
45
46using namespace llvm;
47
49 "regalloc-priority-interactive-channel-base", cl::Hidden,
51 "Base file path for the interactive mode. The incoming filename should "
52 "have the name <regalloc-priority-interactive-channel-base>.in, while "
53 "the outgoing name should be "
54 "<regalloc-priority-interactive-channel-base>.out"));
55
57
62
63// Options that only make sense in development mode
64#ifdef LLVM_HAVE_TFLITE
65#include "RegAllocScore.h"
67
68static cl::opt<std::string> TrainingLog(
69 "regalloc-priority-training-log", cl::Hidden,
70 cl::desc("Training log for the register allocator priority model"));
71
72static cl::opt<std::string> ModelUnderTraining(
73 "regalloc-priority-model", cl::Hidden,
74 cl::desc("The model being trained for register allocation priority"));
75
76#endif // #ifdef LLVM_HAVE_TFLITE
77
78namespace llvm {
79
80static const std::vector<int64_t> PerLiveRangeShape{1};
81
82#define RA_PRIORITY_FEATURES_LIST(M) \
83 M(int64_t, li_size, PerLiveRangeShape, "size") \
84 M(int64_t, stage, PerLiveRangeShape, "stage") \
85 M(float, weight, PerLiveRangeShape, "weight")
86
87#define DecisionName "priority"
90
91
92// Named features index.
94#define _FEATURE_IDX(_, name, __, ___) name,
96#undef _FEATURE_IDX
98};
99
101public:
103 SlotIndexes *const Indexes, MLModelRunner *Runner);
104
105protected:
107 return static_cast<const RegAllocPriorityAdvisor &>(DefaultAdvisor);
108 }
109
110 // The assumption is that if the Runner could not be constructed, we emit-ed
111 // error, and we shouldn't be asking for it here.
112 const MLModelRunner &getRunner() const { return *Runner; }
113 float getPriorityImpl(const LiveInterval &LI) const;
114 unsigned getPriority(const LiveInterval &LI) const override;
115
116private:
117 const DefaultPriorityAdvisor DefaultAdvisor;
118 MLModelRunner *const Runner;
119};
120
121#define _DECL_FEATURES(type, name, shape, _) \
122 TensorSpec::createSpec<type>(#name, shape),
123
124static const std::vector<TensorSpec> InputFeatures{
126};
127#undef _DECL_FEATURES
128
129// ===================================
130// Release (AOT) - specifics
131// ===================================
134public:
137 std::unique_ptr<RegAllocPriorityAdvisor>
139 SlotIndexes &SI) override {
140 if (!Runner) {
141 if (InteractiveChannelBaseName.empty())
142 Runner = std::make_unique<ReleaseModeModelRunner<CompiledModelType>>(
144 else
145 Runner = std::make_unique<InteractiveModelRunner>(
149 }
150 return std::make_unique<MLPriorityAdvisor>(MF, RA, &SI, Runner.get());
151 }
152
153private:
154 std::unique_ptr<MLModelRunner> Runner;
155};
156
159public:
162 // support for isa<> and dyn_cast.
164 return R->getAdvisorMode() == AdvisorMode::Release;
165 }
166
167private:
168 void getAnalysisUsage(AnalysisUsage &AU) const override {
169 AU.setPreservesAll();
172 }
173
174 bool doInitialization(Module &M) override {
175 Provider = std::make_unique<ReleaseModePriorityAdvisorProvider>();
176 return false;
177 }
178};
179
180// ===================================
181// Development mode-specifics
182// ===================================
183//
184// Features we log
185#ifdef LLVM_HAVE_TFLITE
186static const TensorSpec Reward = TensorSpec::createSpec<float>("reward", {1});
187
188#define _DECL_TRAIN_FEATURES(type, name, shape, _) \
189 TensorSpec::createSpec<type>(std::string("action_") + #name, shape),
190
191static const std::vector<TensorSpec> TrainingInputFeatures{
192 {RA_PRIORITY_FEATURES_LIST(_DECL_TRAIN_FEATURES)
193 TensorSpec::createSpec<float>("action_discount", {1}),
194 TensorSpec::createSpec<int32_t>("action_step_type", {1}),
195 TensorSpec::createSpec<float>("action_reward", {1})}};
196#undef _DECL_TRAIN_FEATURES
197
198class DevelopmentModePriorityAdvisor : public MLPriorityAdvisor {
199public:
200 DevelopmentModePriorityAdvisor(const MachineFunction &MF, const RAGreedy &RA,
201 SlotIndexes *const Indexes,
202 MLModelRunner *Runner, Logger *Log)
203 : MLPriorityAdvisor(MF, RA, Indexes, Runner), Log(Log) {}
204
205private:
206 unsigned getPriority(const LiveInterval &LI) const override;
207 Logger *const Log;
208};
209
210class DevelopmentModePriorityAdvisorProvider final
212
213public:
214 // Save all the logs (when requested).
215 DevelopmentModePriorityAdvisorProvider(LLVMContext &Ctx)
216 : RegAllocPriorityAdvisorProvider(AdvisorMode::Development) {
217 if (ModelUnderTraining.empty() && TrainingLog.empty()) {
218 Ctx.emitError("Regalloc development mode should be requested with at "
219 "least logging enabled and/or a training model");
220 return;
221 }
222 if (ModelUnderTraining.empty())
223 Runner = std::make_unique<NoInferenceModelRunner>(Ctx, InputFeatures);
224 else
225 Runner = ModelUnderTrainingRunner::createAndEnsureValid(
226 Ctx, ModelUnderTraining, DecisionName, TrainingInputFeatures);
227 if (!Runner) {
228 Ctx.emitError("Regalloc: could not set up the model runner");
229 return;
230 }
231 if (TrainingLog.empty())
232 return;
233 std::error_code EC;
234 auto OS = std::make_unique<raw_fd_ostream>(TrainingLog, EC);
235 if (EC) {
236 Ctx.emitError(EC.message() + ":" + TrainingLog);
237 return;
238 }
239 std::vector<TensorSpec> LFS = InputFeatures;
240 if (auto *MUTR = dyn_cast<ModelUnderTrainingRunner>(Runner.get()))
241 append_range(LFS, MUTR->extraOutputsForLoggingSpecs());
242 // We always log the output; in particular, if we're not evaluating, we
243 // don't have an output spec json file. That's why we handle the
244 // 'normal' output separately.
245 LFS.push_back(DecisionSpec);
246
247 Log = std::make_unique<Logger>(std::move(OS), LFS, Reward,
248 /*IncludeReward*/ true);
249 }
250
251 void logRewardIfNeeded(const MachineFunction &MF,
252 llvm::function_ref<float()> GetReward) override {
253 if (!Log || !Log->hasAnyObservationForContext(MF.getName()))
254 return;
255 // The function pass manager would run all the function passes for a
256 // function, so we assume the last context belongs to this function. If
257 // this invariant ever changes, we can implement at that time switching
258 // contexts. At this point, it'd be an error
259 if (Log->currentContext() != MF.getName()) {
261 "The training log context shouldn't have had changed.");
262 }
263 if (Log->hasObservationInProgress())
264 Log->logReward<float>(GetReward());
265 }
266
267 std::unique_ptr<RegAllocPriorityAdvisor>
268 getAdvisor(const MachineFunction &MF, const RAGreedy &RA,
269 SlotIndexes &SI) override {
270 if (!Runner)
271 return nullptr;
272 if (Log) {
273 Log->switchContext(MF.getName());
274 }
275 return std::make_unique<DevelopmentModePriorityAdvisor>(
276 MF, RA, &SI, Runner.get(), Log.get());
277 }
278
279 std::unique_ptr<MLModelRunner> Runner;
280 std::unique_ptr<Logger> Log;
281};
282
283class DevelopmentModePriorityAdvisorAnalysisLegacy final
285public:
286 DevelopmentModePriorityAdvisorAnalysisLegacy()
287 : RegAllocPriorityAdvisorAnalysisLegacy(AdvisorMode::Development) {}
288
289 // support for isa<> and dyn_cast.
290 static bool classof(const RegAllocPriorityAdvisorAnalysisLegacy *R) {
291 return R->getAdvisorMode() == AdvisorMode::Development;
292 }
293
294 void logRewardIfNeeded(const MachineFunction &MF,
295 llvm::function_ref<float()> GetReward) override {
296 Provider->logRewardIfNeeded(MF, GetReward);
297 }
298
299private:
300 void getAnalysisUsage(AnalysisUsage &AU) const override {
301 AU.setPreservesAll();
302 AU.addRequired<SlotIndexesWrapperPass>();
304 }
305
306 // Save all the logs (when requested).
307 bool doInitialization(Module &M) override {
308 Provider = std::make_unique<DevelopmentModePriorityAdvisorProvider>(
309 M.getContext());
310 return false;
311 ;
312 }
313};
314#endif //#ifdef LLVM_HAVE_TFLITE
315
316} // namespace llvm
317
324
326 const RAGreedy &RA,
327 SlotIndexes *const Indexes,
328 MLModelRunner *Runner)
329 : RegAllocPriorityAdvisor(MF, RA, Indexes), DefaultAdvisor(MF, RA, Indexes),
330 Runner(std::move(Runner)) {
331 assert(this->Runner);
332 Runner->switchContext(MF.getName());
333}
334
335// Converting a NaN or an out-of-range float advice to unsigned is undefined.
336// Saturate instead. A NaN is a model error, so also assert on it.
337static unsigned convertAdviceToPriority(double Advice) {
338 assert(!std::isnan(Advice) && "model produced a NaN priority");
339 if (!(Advice > 0.0))
340 return 0;
341 if (Advice >= static_cast<double>(std::numeric_limits<unsigned>::max()))
342 return std::numeric_limits<unsigned>::max();
343 return static_cast<unsigned>(Advice);
344}
345
347 const unsigned Size = LI.getSize();
348 LiveRangeStage Stage = RA.getExtraInfo().getStage(LI);
349
350 *Runner->getTensor<int64_t>(0) = static_cast<int64_t>(Size);
351 *Runner->getTensor<int64_t>(1) = static_cast<int64_t>(Stage);
352 *Runner->getTensor<float>(2) = static_cast<float>(LI.weight());
353
354 return Runner->evaluate<float>();
355}
356
360
361#ifdef LLVM_HAVE_TFLITE
364 return new DevelopmentModePriorityAdvisorAnalysisLegacy();
365}
366
367unsigned
368DevelopmentModePriorityAdvisor::getPriority(const LiveInterval &LI) const {
369 unsigned Prio = 0;
370
371 if (isa<ModelUnderTrainingRunner>(getRunner())) {
373 } else {
374 Prio = getDefaultAdvisor().getPriority(LI);
375 }
376
377 if (TrainingLog.empty())
378 return Prio;
379
380 // TODO(mtrofin): when we support optional rewards, this can go away. In the
381 // meantime, we log the "pretend" reward (0) for the previous observation
382 // before starting a new one.
383 if (Log->hasObservationInProgress())
384 Log->logReward<float>(0.0);
385
386 Log->startObservation();
387 size_t CurrentFeature = 0;
388 for (; CurrentFeature < InputFeatures.size(); ++CurrentFeature) {
389 Log->logTensorValue(CurrentFeature,
390 reinterpret_cast<const char *>(
391 getRunner().getTensorUntyped(CurrentFeature)));
392 }
393
394 if (auto *MUTR = dyn_cast<ModelUnderTrainingRunner>(&getRunner())) {
395 for (size_t I = 0; I < MUTR->extraOutputsForLoggingSpecs().size();
396 ++I, ++CurrentFeature)
397 Log->logTensorValue(
398 CurrentFeature,
399 reinterpret_cast<const char *>(MUTR->getUntypedExtraOutputValue(I)));
400 }
401
402 float Ret = static_cast<float>(Prio);
403 Log->logTensorValue(CurrentFeature, reinterpret_cast<const char *>(&Ret));
404 Log->endObservation();
405
406 return Prio;
407}
408
411 return new DevelopmentModePriorityAdvisorProvider(Ctx);
412}
413
414#endif // #ifdef LLVM_HAVE_TFLITE
415
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
Module.h This file contains the declarations for the Module class.
#define I(x, y, z)
Definition MD5.cpp:57
NoopSavedModelImpl CompiledModelType
static cl::opt< std::string > InteractiveChannelBaseName("inliner-interactive-channel-base", cl::Hidden, cl::desc("Base file path for the interactive mode. The incoming filename should " "have the name <inliner-interactive-channel-base>.in, while the " "outgoing name should be <inliner-interactive-channel-base>.out"))
#define _FEATURE_IDX(A, B, C, D)
#define _DECL_FEATURES(type, name, shape, _)
#define DecisionName
static bool hasReleaseModePriorityModel()
static cl::opt< std::string > InteractiveChannelBaseName("regalloc-priority-interactive-channel-base", cl::Hidden, cl::desc("Base file path for the interactive mode. The incoming filename should " "have the name <regalloc-priority-interactive-channel-base>.in, while " "the outgoing name should be " "<regalloc-priority-interactive-channel-base>.out"))
static unsigned convertAdviceToPriority(double Advice)
#define RA_PRIORITY_FEATURES_LIST(M)
Machine Check Debug Module
if(PassOpts->AAPipeline)
SI optimize exec mask operations pre RA
Func getContext().diagnose(DiagnosticInfoUnsupported(Func
Represent the analysis usage information of a pass.
AnalysisUsage & addRequired()
void setPreservesAll()
Set by analyses that do not transform their input at all.
LLVMContext & getContext() const
getContext - Return a reference to the LLVMContext associated with this function.
Definition Function.cpp:356
This is an important class for using LLVM in a threaded context.
Definition LLVMContext.h:68
LLVM_ABI void emitError(const Instruction *I, const Twine &ErrorStr)
emitError - Emit an error message to the currently installed error handler with optional location inf...
LiveInterval - This class represents the liveness of a register, or stack slot.
float weight() const
LLVM_ABI unsigned getSize() const
getSize - Returns the sum of sizes of all the LiveRange's.
MLModelRunner interface: abstraction of a mechanism for evaluating a ML model.
const MLModelRunner & getRunner() const
MLPriorityAdvisor(const MachineFunction &MF, const RAGreedy &RA, SlotIndexes *const Indexes, MLModelRunner *Runner)
const RegAllocPriorityAdvisor & getDefaultAdvisor() const
unsigned getPriority(const LiveInterval &LI) const override
Find the priority value for a live range.
float getPriorityImpl(const LiveInterval &LI) const
StringRef getName() const
getName - Return the name of the corresponding LLVM function.
Function & getFunction()
Return the LLVM function that this machine code represents.
A Module instance is used to store all the information related to an LLVM module.
Definition Module.h:68
A mock class satisfying the interface expected by ReleaseModeModelRunner for its TGen parameter.
void getAnalysisUsage(AnalysisUsage &AU) const override
getAnalysisUsage - This function should be overriden by passes that need analysis information to do t...
RegAllocPriorityAdvisorProvider::AdvisorMode AdvisorMode
std::unique_ptr< RegAllocPriorityAdvisorProvider > Provider
Common provider for getting the priority advisor and logging rewards.
RegAllocPriorityAdvisor(const RegAllocPriorityAdvisor &)=delete
static bool classof(const RegAllocPriorityAdvisorAnalysisLegacy *R)
std::unique_ptr< RegAllocPriorityAdvisor > getAdvisor(const MachineFunction &MF, const RAGreedy &RA, SlotIndexes &SI) override
SlotIndexes pass.
static TensorSpec createSpec(const std::string &Name, const std::vector< int64_t > &Shape, int Port=0)
Definition TensorSpec.h:65
This is an optimization pass for GlobalISel generic memory operations.
bool isEmbeddedModelEvaluatorValid()
auto size(R &&Range, std::enable_if_t< std::is_base_of< std::random_access_iterator_tag, typename std::iterator_traits< decltype(Range.begin())>::iterator_category >::value, void > *=nullptr)
Get the size of a range.
Definition STLExtras.h:1685
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:643
void append_range(Container &C, Range &&R)
Wrapper function to append range R to container C.
Definition STLExtras.h:2224
LLVM_ABI RegAllocPriorityAdvisorAnalysisLegacy * createReleaseModePriorityAdvisorAnalysis()
static const TensorSpec DecisionSpec
LLVM_ABI const char *const DecisionName
static const std::vector< TensorSpec > InputFeatures
bool isa(const From &Val)
isa<X> - Return true if the parameter to the template is an instance of one of the template type argu...
Definition Casting.h:547
LLVM_ABI RegAllocPriorityAdvisorProvider * createReleaseModePriorityAdvisorProvider()
OutputIt move(R &&Range, OutputIt Out)
Provide wrappers to std::move which take ranges instead of having to pass begin/end explicitly.
Definition STLExtras.h:1933
LLVM_ATTRIBUTE_RETURNS_NONNULL LLVM_ABI RegAllocPriorityAdvisorProvider * createDevelopmentModePriorityAdvisorProvider(LLVMContext &Ctx)
LLVM_ABI RegAllocPriorityAdvisorAnalysisLegacy * createDevelopmentModePriorityAdvisorAnalysis()
static const std::vector< int64_t > PerLiveRangeShape
Implement std::hash so that hash_code can be used in STL containers.
Definition BitVector.h:878