1//===- MLRegAllocEvictAdvisor.cpp - ML eviction 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 eviction advisor and reward injection pass
10//
11//===----------------------------------------------------------------------===//
12
13#include "AllocationOrder.h"
14#include "RegAllocGreedy.h"
15#include "llvm/Analysis/MLModelRunner.h"
16#include "llvm/Analysis/TensorSpec.h"
17#include "llvm/CodeGen/RegAllocEvictionAdvisor.h"
18#if defined(LLVM_HAVE_TF_AOT_REGALLOCEVICTMODEL) || defined(LLVM_HAVE_TFLITE)
19#include "llvm/Analysis/ModelUnderTrainingRunner.h"
20#include "llvm/Analysis/NoInferenceModelRunner.h"
21#include "llvm/Analysis/Utils/TrainingLogger.h"
22#endif
23#include "MLRegAllocEvictAdvisor.h"
24#include "llvm/Analysis/ReleaseModeModelRunner.h"
25#include "llvm/Analysis/Utils/MLGOUtils.h"
26#include "llvm/CodeGen/CalcSpillWeights.h"
27#include "llvm/CodeGen/LiveRegMatrix.h"
28#include "llvm/CodeGen/MachineBlockFrequencyInfo.h"
29#include "llvm/CodeGen/MachineFunction.h"
30#include "llvm/CodeGen/MachineLoopInfo.h"
31#include "llvm/CodeGen/MachineRegisterInfo.h"
32#include "llvm/CodeGen/Passes.h"
33#include "llvm/CodeGen/RegisterClassInfo.h"
34#include "llvm/CodeGen/VirtRegMap.h"
35#include "llvm/IR/Module.h"
36#include "llvm/InitializePasses.h"
37#include "llvm/Pass.h"
38#include "llvm/Support/CommandLine.h"
39#include "llvm/Support/ErrorHandling.h"
40
41#include <array>
42#include <bitset>
43#include <memory>
44
45using namespace llvm;
46
47#define DEBUG_TYPE "ml-regalloc"
48
49// Generated header in release (AOT) mode
50#if defined(LLVM_HAVE_TF_AOT_REGALLOCEVICTMODEL)
51#include "RegAllocEvictModel.h"
52using CompiledModelType = RegAllocEvictModel;
53#else
54using CompiledModelType = NoopSavedModelImpl;
55#endif
56
57#if defined(LLVM_HAVE_MLIR_LOWERING_REGALLOC)
58constexpr bool HaveMLIRLoweringRegAlloc = true;
59#include "llvm/Analysis/EmitCModelRunner.h"
60#include "llvm/CodeGen/RegAllocEvictModels.h"
61
62enum class MLGORegAllocModelChoice {
63 Default,
64#define MLGO_MODEL(CLASS_NAME, CLI_FLAG) CLASS_NAME,
65#include "llvm/CodeGen/RegAllocEvictModels.def"
66};
67
68static llvm::cl::opt<MLGORegAllocModelChoice> SelectedMLGORegAllocModel(
69 "regalloc-mlgo-model",
70 llvm::cl::desc("Select the MLGO model to execute for register allocation:"),
71 llvm::cl::init(MLGORegAllocModelChoice::Default),
72 llvm::cl::values(clEnumValN(MLGORegAllocModelChoice::Default, "default",
73 "Use standard heuristic")
74#define MLGO_MODEL(CLASS_NAME, CLI_FLAG) \
75 , clEnumValN(MLGORegAllocModelChoice::CLASS_NAME, CLI_FLAG, \
76 "Use the " CLI_FLAG " MLGO model")
77#include "llvm/CodeGen/RegAllocEvictModels.def"
78 ));
79
80static std::unique_ptr<MLModelRunner>
81createMLGORegAllocModelRunner(LLVMContext &Ctx,
82 const std::vector<TensorSpec> &InputFeatures) {
83 switch (SelectedMLGORegAllocModel) {
84 case MLGORegAllocModelChoice::Default:
85 return nullptr;
86#define MLGO_MODEL(CLASS_NAME, CLI_FLAG) \
87 case MLGORegAllocModelChoice::CLASS_NAME: \
88 return std::make_unique<EmitCModelRunner<CLASS_NAME>>(Ctx, InputFeatures);
89#include "llvm/CodeGen/RegAllocEvictModels.def"
90 }
91 llvm_unreachable("Unknown MLGO model type!");
92}
93#else
94constexpr bool HaveMLIRLoweringRegAlloc = false;
95enum class MLGORegAllocModelChoice { Default };
96static const MLGORegAllocModelChoice SelectedMLGORegAllocModel =
97 MLGORegAllocModelChoice::Default;
98static inline std::unique_ptr<MLModelRunner>
99createMLGORegAllocModelRunner(LLVMContext &, const std::vector<TensorSpec> &) {
100 return nullptr;
101}
102#endif
103
104static cl::opt<std::string> InteractiveChannelBaseName(
105 "regalloc-evict-interactive-channel-base", cl::Hidden,
106 cl::desc(
107 "Base file path for the interactive mode. The incoming filename should "
108 "have the name <regalloc-evict-interactive-channel-base>.in, while the "
109 "outgoing name should be "
110 "<regalloc-evict-interactive-channel-base>.out"));
111
112static cl::opt<unsigned> MaxEvictionCount(
113 "mlregalloc-max-eviction-count", cl::Hidden,
114 cl::desc("The maximum number of times a live range can be "
115 "evicted before preventing it from being evicted"),
116 cl::init(Val: 100));
117
118// Options that only make sense in development mode
119#ifdef LLVM_HAVE_TFLITE
120#include "RegAllocScore.h"
121#include "llvm/Analysis/Utils/TFUtils.h"
122
123static cl::opt<std::string> TrainingLog(
124 "regalloc-training-log", cl::Hidden,
125 cl::desc("Training log for the register allocator eviction model"));
126
127static cl::opt<std::string> ModelUnderTraining(
128 "regalloc-model", cl::Hidden,
129 cl::desc("The model being trained for register allocation eviction"));
130
131#endif // #ifdef LLVM_HAVE_TFLITE
132
133/// The score injection pass.
134/// This pass calculates the score for a function and inserts it in the log, but
135/// this happens only in development mode. It's a no-op otherwise.
136namespace llvm {
137extern cl::opt<unsigned> EvictInterferenceCutoff;
138} // namespace llvm
139
140namespace {
141class RegAllocScoring : public MachineFunctionPass {
142public:
143 static char ID;
144
145 RegAllocScoring() : MachineFunctionPass(ID) {}
146
147 ~RegAllocScoring() override = default;
148
149 StringRef getPassName() const override {
150 return "Register Allocation Pass Scoring";
151 }
152
153 /// RegAllocReward analysis usage.
154 void getAnalysisUsage(AnalysisUsage &AU) const override {
155 AU.setPreservesAll();
156 AU.addRequired<RegAllocEvictionAdvisorAnalysisLegacy>();
157 AU.addRequired<RegAllocPriorityAdvisorAnalysisLegacy>();
158 AU.addRequired<MachineBlockFrequencyInfoWrapperPass>();
159 MachineFunctionPass::getAnalysisUsage(AU);
160 }
161
162 /// Performs this pass
163 bool runOnMachineFunction(MachineFunction &) override;
164};
165} // namespace
166
167char RegAllocScoring::ID = 0;
168FunctionPass *llvm::createRegAllocScoringPass() {
169 return new RegAllocScoring();
170}
171
172INITIALIZE_PASS(RegAllocScoring, "regallocscoringpass",
173 "Register Allocation Scoring Pass", false, false)
174
175// ===================================
176// Common ML Advisor declarations
177// ===================================
178namespace {
179// Most features are as described above, so we'll reuse this vector in defining
180// them.
181static const std::vector<int64_t> PerLiveRangeShape{1, NumberOfInterferences};
182
183// --------------
184// Features table
185// --------------
186// For each interfering live range (incl. the candidate) we collect a number of
187// features. However, because the features are of different types (and because
188// of ML best practices), we organize the tensors per feature, not per
189// candidate. Each such tensor has a scalar value corresponding to the
190// interferring live range at that position, in the order in AllocationOrder.
191// The last position corresponds to the virt reg seeking allocation.
192// Exception to all that is the progression feature, which is just a scalar (see
193// its documentation for details).
194// Note on naming: the "_by_max" are normalized using the largest value of that
195// tensor, as observed in the current decision making stage (i.e. for the
196// current call to the advisor's tryFindEvictionCandidate)
197//
198// The feature list format: type, name, shape, documentation.
199// Note: we can really just use int64 and float, hence the modeling of some
200// bools as int64 values.
201#define RA_EVICT_FEATURES_LIST(M) \
202 M(int64_t, mask, PerLiveRangeShape, \
203 "boolean values, 0 for unavailable candidates (i.e. if a position is 0, " \
204 "it " \
205 "can't be evicted)") \
206 M(int64_t, is_free, PerLiveRangeShape, \
207 "boolean values, 1 if this phys reg is actually free (no interferences)") \
208 M(float, nr_urgent, PerLiveRangeShape, \
209 "number of 'urgent' intervals, normalized. Urgent are those that are OK " \
210 "to break cascades") \
211 M(float, nr_broken_hints, PerLiveRangeShape, \
212 "if this position were evicted, how many broken hints would there be") \
213 M(int64_t, is_hint, PerLiveRangeShape, \
214 "is this a preferred phys reg for the candidate") \
215 M(int64_t, is_local, PerLiveRangeShape, \
216 "is this live range local to a basic block") \
217 M(float, nr_rematerializable, PerLiveRangeShape, \
218 "nr rematerializable ranges") \
219 M(float, nr_defs_and_uses, PerLiveRangeShape, \
220 "bb freq - weighed nr defs and uses") \
221 M(float, weighed_reads_by_max, PerLiveRangeShape, \
222 "bb freq - weighed nr of reads, normalized") \
223 M(float, weighed_writes_by_max, PerLiveRangeShape, \
224 "bb feq - weighed nr of writes, normalized") \
225 M(float, weighed_read_writes_by_max, PerLiveRangeShape, \
226 "bb freq - weighed nr of uses that are both read and writes, normalized") \
227 M(float, weighed_indvars_by_max, PerLiveRangeShape, \
228 "bb freq - weighed nr of uses that are indvars, normalized") \
229 M(float, hint_weights_by_max, PerLiveRangeShape, \
230 "bb freq - weighed nr of uses that are hints, normalized") \
231 M(float, start_bb_freq_by_max, PerLiveRangeShape, \
232 "the freq in the start block, normalized") \
233 M(float, end_bb_freq_by_max, PerLiveRangeShape, \
234 "freq of end block, normalized") \
235 M(float, hottest_bb_freq_by_max, PerLiveRangeShape, \
236 "hottest BB freq, normalized") \
237 M(float, liverange_size, PerLiveRangeShape, \
238 "size (instr index diff) of the LR") \
239 M(float, use_def_density, PerLiveRangeShape, \
240 "the max weight, as computed by the manual heuristic") \
241 M(int64_t, max_stage, PerLiveRangeShape, \
242 "largest stage of an interval in this LR") \
243 M(int64_t, min_stage, PerLiveRangeShape, \
244 "lowest stage of an interval in this LR") \
245 M(float, progress, {1}, "ratio of current queue size to initial size")
246
247// The model learns to pick one of the mask == 1 interferences. This is the
248// name of the output tensor. The contract with the model is that the output
249// will be guaranteed to be to a mask == 1 position. Using a macro here to
250// avoid 'not used' warnings (and keep cond compilation to a minimum)
251#define DecisionName "index_to_evict"
252static const TensorSpec DecisionSpec =
253 TensorSpec::createSpec<int64_t>(DecisionName, Shape: {1});
254
255// Named features index.
256enum FeatureIDs {
257#define _FEATURE_IDX_SIMPLE(_, name, __, ___) name
258#define _FEATURE_IDX(A, B, C, D) _FEATURE_IDX_SIMPLE(A, B, C, D),
259 RA_EVICT_FEATURES_LIST(_FEATURE_IDX) FeatureCount,
260#undef _FEATURE_IDX
261#undef _FEATURE_IDX_SIMPLE
262};
263
264// The ML advisor will typically have a sparse input to the evaluator, because
265// various phys regs won't be available. It's easier (maintenance-wise) to
266// bulk-reset the state of the evaluator each time we are about to use it
267// again.
268template <typename T> size_t getTotalSize(const std::vector<int64_t> &Shape) {
269 size_t Ret = sizeof(T);
270 for (const auto V : Shape)
271 Ret *= V;
272 return Ret;
273}
274
275void resetInputs(MLModelRunner &Runner) {
276#define _RESET(TYPE, NAME, SHAPE, __) \
277 std::memset(Runner.getTensorUntyped(FeatureIDs::NAME), 0, \
278 getTotalSize<TYPE>(SHAPE));
279 RA_EVICT_FEATURES_LIST(_RESET)
280#undef _RESET
281}
282
283// Per-live interval components that get aggregated into the feature values
284// that will be passed to the evaluator.
285struct LIFeatureComponents {
286 double R = 0;
287 double W = 0;
288 double RW = 0;
289 double IndVarUpdates = 0;
290 double HintWeights = 0.0;
291 int64_t NumDefsAndUses = 0;
292 float HottestBlockFreq = 0.0;
293 bool IsRemat = false;
294};
295
296using CandidateRegList =
297 std::array<std::pair<MCRegister, bool>, NumberOfInterferences>;
298using FeaturesListNormalizer =
299 llvm::SmallVector<float, FeatureIDs::FeatureCount>;
300
301/// The ML evictor (commonalities between release and development mode)
302class MLEvictAdvisor : public RegAllocEvictionAdvisor {
303public:
304 MLEvictAdvisor(const MachineFunction &MF, const RAGreedy &RA,
305 MLModelRunner *Runner, const MachineBlockFrequencyInfo &MBFI,
306 const MachineLoopInfo &Loops);
307
308protected:
309 const RegAllocEvictionAdvisor &getDefaultAdvisor() const {
310 return static_cast<const RegAllocEvictionAdvisor &>(DefaultAdvisor);
311 }
312
313 // The assumption is that if the Runner could not be constructed, we emit-ed
314 // error, and we shouldn't be asking for it here.
315 const MLModelRunner &getRunner() const { return *Runner; }
316
317 /// This just calls Evaluate on the Runner, but in the development mode
318 /// case, if we're just capturing the log of the default advisor, it needs
319 /// to call the latter instead, so we need to pass all the necessary
320 /// parameters for it. In the development case, it will also log.
321 virtual int64_t
322 tryFindEvictionCandidatePosition(const LiveInterval &VirtReg,
323 const AllocationOrder &Order,
324 unsigned OrderLimit, uint8_t CostPerUseLimit,
325 const SmallVirtRegSet &FixedRegisters) const;
326
327 /// Load the features of the given VirtReg (allocated or not) at column Pos,
328 /// but if that can't be evicted, return false instead.
329 bool
330 loadInterferenceFeatures(const LiveInterval &VirtReg, MCRegister PhysReg,
331 bool IsHint, const SmallVirtRegSet &FixedRegisters,
332 llvm::SmallVectorImpl<float> &Largest, size_t Pos,
333 SmallVectorImpl<LRStartEndInfo> &LRPosInfo) const;
334
335private:
336 static float getInitialQueueSize(const MachineFunction &MF);
337
338 MCRegister tryFindEvictionCandidate(
339 const LiveInterval &VirtReg, const AllocationOrder &Order,
340 uint8_t CostPerUseLimit,
341 const SmallVirtRegSet &FixedRegisters) const override;
342
343 void extractFeatures(const SmallVectorImpl<const LiveInterval *> &Intervals,
344 llvm::SmallVectorImpl<float> &Largest, size_t Pos,
345 int64_t IsHint, int64_t LocalIntfsCount, float NumUrgent,
346 SmallVectorImpl<LRStartEndInfo> &LRPosInfo) const;
347
348 // Point-in-time: we didn't learn this, so we always delegate to the
349 // default.
350 bool canEvictHintInterference(
351 const LiveInterval &VirtReg, MCRegister PhysReg,
352 const SmallVirtRegSet &FixedRegisters) const override {
353 return getDefaultAdvisor().canEvictHintInterference(VirtReg, PhysReg,
354 FixedRegisters);
355 }
356
357 const LIFeatureComponents &
358 getLIFeatureComponents(const LiveInterval &LI) const;
359
360 // Hold on to a default advisor for:
361 // 1) the implementation of canEvictHintInterference, because we didn't
362 // learn that nuance yet; 2) for bootstrapping (logging) in the development
363 // mode case.
364 const DefaultEvictionAdvisor DefaultAdvisor;
365 MLModelRunner *const Runner;
366 const MachineBlockFrequencyInfo &MBFI;
367 const MachineLoopInfo &Loops;
368
369 // Indices of those features we don't want to normalize.
370 // This could be static and shared, but its initialization is non-trivial.
371 std::bitset<FeatureIDs::FeatureCount> DoNotNormalize;
372 const float InitialQSize;
373
374 using RegID = unsigned;
375 mutable DenseMap<RegID, LIFeatureComponents> CachedFeatures;
376
377 mutable DenseMap<unsigned, unsigned> VirtRegEvictionCounts;
378
379 void onEviction(Register RegBeingEvicted) const {
380 // If we cannot find the virtual register in the map, we just assume it has
381 // not been evicted before and thus has a value of zero (which is what the
382 // subscript operator returns by default).
383 ++VirtRegEvictionCounts[RegBeingEvicted.id()];
384 }
385
386 unsigned getEvictionCount(Register Reg) const {
387 auto EvictionCountIt = VirtRegEvictionCounts.find(Val: Reg.id());
388 if (EvictionCountIt != VirtRegEvictionCounts.end())
389 return EvictionCountIt->second;
390 return 0;
391 }
392};
393
394#define _DECL_FEATURES(type, name, shape, _) \
395 TensorSpec::createSpec<type>(#name, shape),
396
397// ===================================
398// Release (AOT) - specifics
399// ===================================
400/// Common provider for legacy and new pass managers.
401class ReleaseModeEvictionAdvisorProvider final
402 : public RegAllocEvictionAdvisorProvider {
403public:
404 ReleaseModeEvictionAdvisorProvider(LLVMContext &Ctx)
405 : RegAllocEvictionAdvisorProvider(AdvisorMode::Release, Ctx) {
406 InputFeatures = {RA_EVICT_FEATURES_LIST(_DECL_FEATURES)};
407 }
408 // support for isa<> and dyn_cast.
409 static bool classof(const RegAllocEvictionAdvisorProvider *R) {
410 return R->getAdvisorMode() == AdvisorMode::Release;
411 }
412
413 std::unique_ptr<RegAllocEvictionAdvisor>
414 getAdvisor(const MachineFunction &MF, const RAGreedy &RA,
415 MachineBlockFrequencyInfo *MBFI, MachineLoopInfo *Loops) override {
416 if (!Runner) {
417 Runner = createReleaseModeModelRunner<CompiledModelType,
418 HaveMLIRLoweringRegAlloc>(
419 Ctx&: MF.getFunction().getContext(), InputFeatures, DecisionName,
420 InteractiveChannelBaseName, InteractiveDecisionSpec: DecisionSpec,
421 CreateEmitCModelRunner&: createMLGORegAllocModelRunner);
422 }
423 assert(MBFI && Loops &&
424 "Invalid provider state: must have analysis available");
425 return std::make_unique<MLEvictAdvisor>(args: MF, args: RA, args: Runner.get(), args&: *MBFI,
426 args&: *Loops);
427 }
428
429private:
430 std::vector<TensorSpec> InputFeatures;
431 std::unique_ptr<MLModelRunner> Runner;
432};
433
434class ReleaseModeEvictionAdvisorAnalysisLegacy final
435 : public RegAllocEvictionAdvisorAnalysisLegacy {
436public:
437 ReleaseModeEvictionAdvisorAnalysisLegacy()
438 : RegAllocEvictionAdvisorAnalysisLegacy(AdvisorMode::Release) {}
439
440 void logRewardIfNeeded(const MachineFunction &MF,
441 llvm::function_ref<float()> GetReward) override {
442 // No-op in release mode
443 }
444
445 bool doInitialization(Module &M) override {
446 Provider =
447 std::make_unique<ReleaseModeEvictionAdvisorProvider>(args&: M.getContext());
448 return false;
449 }
450
451 static bool classof(const RegAllocEvictionAdvisorAnalysisLegacy *R) {
452 return R->getAdvisorMode() == AdvisorMode::Release;
453 }
454
455 void getAnalysisUsage(AnalysisUsage &AU) const override {
456 AU.addRequired<MachineBlockFrequencyInfoWrapperPass>();
457 RegAllocEvictionAdvisorAnalysisLegacy::getAnalysisUsage(AU);
458 }
459};
460
461// ===================================
462// Development mode-specifics
463// ===================================
464//
465// Features we log
466#ifdef LLVM_HAVE_TFLITE
467static const TensorSpec Reward = TensorSpec::createSpec<float>("reward", {1});
468
469// Features we bind on the model. The tensor names have a prefix, and we also
470// need to include some tensors that are expected to be present by the
471// training algo.
472// TODO: can we just get rid of these?
473#define _DECL_TRAIN_FEATURES(type, name, shape, _) \
474 TensorSpec::createSpec<type>(std::string("action_") + #name, shape),
475
476class DevelopmentModeEvictAdvisor : public MLEvictAdvisor {
477public:
478 DevelopmentModeEvictAdvisor(const MachineFunction &MF, const RAGreedy &RA,
479 MLModelRunner *Runner,
480 const MachineBlockFrequencyInfo &MBFI,
481 const MachineLoopInfo &Loops, Logger *Log)
482 : MLEvictAdvisor(MF, RA, Runner, MBFI, Loops), Log(Log) {}
483
484private:
485 int64_t tryFindEvictionCandidatePosition(
486 const LiveInterval &VirtReg, const AllocationOrder &Order,
487 unsigned OrderLimit, uint8_t CostPerUseLimit,
488 const SmallVirtRegSet &FixedRegisters) const override;
489
490 Logger *const Log;
491};
492
493class DevelopmentModeEvictionAdvisorProvider final
494 : public RegAllocEvictionAdvisorProvider {
495public:
496 DevelopmentModeEvictionAdvisorProvider(LLVMContext &Ctx)
497 : RegAllocEvictionAdvisorProvider(AdvisorMode::Development, Ctx) {
498 InputFeatures = {RA_EVICT_FEATURES_LIST(_DECL_FEATURES)};
499 TrainingInputFeatures = {
500 RA_EVICT_FEATURES_LIST(_DECL_TRAIN_FEATURES)
501 TensorSpec::createSpec<float>("action_discount", {1}),
502 TensorSpec::createSpec<int32_t>("action_step_type", {1}),
503 TensorSpec::createSpec<float>("action_reward", {1})};
504 if (ModelUnderTraining.empty() && TrainingLog.empty()) {
505 Ctx.emitError("Regalloc development mode should be requested with at "
506 "least logging enabled and/or a training model");
507 return;
508 }
509 if (ModelUnderTraining.empty())
510 Runner = std::make_unique<NoInferenceModelRunner>(Ctx, InputFeatures);
511 else
512 Runner = ModelUnderTrainingRunner::createAndEnsureValid(
513 Ctx, ModelUnderTraining, DecisionName, TrainingInputFeatures);
514 if (!Runner) {
515 Ctx.emitError("Regalloc: could not set up the model runner");
516 return;
517 }
518 if (TrainingLog.empty())
519 return;
520 std::error_code EC;
521 auto OS = std::make_unique<raw_fd_ostream>(TrainingLog, EC);
522 if (EC) {
523 Ctx.emitError(EC.message() + ":" + TrainingLog);
524 return;
525 }
526 std::vector<TensorSpec> LFS = InputFeatures;
527 if (auto *MUTR = dyn_cast<ModelUnderTrainingRunner>(Runner.get()))
528 append_range(LFS, MUTR->extraOutputsForLoggingSpecs());
529 // We always log the output; in particular, if we're not evaluating, we
530 // don't have an output spec json file. That's why we handle the
531 // 'normal' output separately.
532 LFS.push_back(DecisionSpec);
533
534 Log = std::make_unique<Logger>(std::move(OS), LFS, Reward,
535 /*IncludeReward*/ true);
536 return;
537 }
538
539 // support for isa<> and dyn_cast.
540 static bool classof(const RegAllocEvictionAdvisorProvider *R) {
541 return R->getAdvisorMode() == AdvisorMode::Development;
542 }
543
544 void logRewardIfNeeded(const MachineFunction &MF,
545 llvm::function_ref<float()> GetReward) override {
546 if (!Log || !Log->hasAnyObservationForContext(MF.getName()))
547 return;
548 // The function pass manager would run all the function passes for a
549 // function, so we assume the last context belongs to this function. If
550 // this invariant ever changes, we can implement at that time switching
551 // contexts. At this point, it'd be an error
552 if (Log->currentContext() != MF.getName()) {
553 MF.getFunction().getContext().emitError(
554 "The training log context shouldn't have had changed.");
555 }
556 if (Log->hasObservationInProgress())
557 Log->logReward<float>(GetReward());
558 }
559
560 std::unique_ptr<RegAllocEvictionAdvisor>
561 getAdvisor(const MachineFunction &MF, const RAGreedy &RA,
562 MachineBlockFrequencyInfo *MBFI, MachineLoopInfo *Loops) override {
563 if (!Runner)
564 return nullptr;
565 if (Log)
566 Log->switchContext(MF.getName());
567 assert(MBFI && Loops &&
568 "Invalid provider state: must have analysis available");
569 return std::make_unique<DevelopmentModeEvictAdvisor>(
570 MF, RA, Runner.get(), *MBFI, *Loops, Log.get());
571 }
572
573private:
574 std::vector<TensorSpec> InputFeatures;
575 std::vector<TensorSpec> TrainingInputFeatures;
576
577 std::unique_ptr<MLModelRunner> Runner;
578 std::unique_ptr<Logger> Log;
579};
580
581class DevelopmentModeEvictionAdvisorAnalysisLegacy final
582 : public RegAllocEvictionAdvisorAnalysisLegacy {
583public:
584 DevelopmentModeEvictionAdvisorAnalysisLegacy()
585 : RegAllocEvictionAdvisorAnalysisLegacy(AdvisorMode::Development) {}
586
587 bool doInitialization(Module &M) override {
588 Provider = std::make_unique<DevelopmentModeEvictionAdvisorProvider>(
589 M.getContext());
590 return false;
591 }
592
593 void logRewardIfNeeded(const MachineFunction &MF,
594 llvm::function_ref<float()> GetReward) override {
595 Provider->logRewardIfNeeded(MF, GetReward);
596 }
597
598 // support for isa<> and dyn_cast.
599 static bool classof(const RegAllocEvictionAdvisorAnalysisLegacy *R) {
600 return R->getAdvisorMode() == AdvisorMode::Development;
601 }
602
603 void getAnalysisUsage(AnalysisUsage &AU) const override {
604 AU.addRequired<MachineBlockFrequencyInfoWrapperPass>();
605 RegAllocEvictionAdvisorAnalysisLegacy::getAnalysisUsage(AU);
606 }
607};
608
609#endif // #ifdef LLVM_HAVE_TFLITE
610} // namespace
611
612float MLEvictAdvisor::getInitialQueueSize(const MachineFunction &MF) {
613 auto &MRI = MF.getRegInfo();
614 unsigned NumUsedRegs = 0;
615 for (unsigned I = 0, E = MRI.getNumVirtRegs(); I != E; ++I) {
616 Register Reg = Register::index2VirtReg(Index: I);
617 if (!MRI.reg_nodbg_empty(RegNo: Reg))
618 ++NumUsedRegs;
619 }
620 return static_cast<float>(NumUsedRegs);
621}
622
623MLEvictAdvisor::MLEvictAdvisor(const MachineFunction &MF, const RAGreedy &RA,
624 MLModelRunner *Runner,
625 const MachineBlockFrequencyInfo &MBFI,
626 const MachineLoopInfo &Loops)
627 : RegAllocEvictionAdvisor(MF, RA), DefaultAdvisor(MF, RA),
628 Runner(std::move(Runner)), MBFI(MBFI), Loops(Loops),
629 InitialQSize(MLEvictAdvisor::getInitialQueueSize(MF)) {
630 assert(this->Runner);
631 Runner->switchContext(Name: MF.getName());
632 DoNotNormalize.set(position: FeatureIDs::mask);
633 DoNotNormalize.set(position: FeatureIDs::is_free);
634 DoNotNormalize.set(position: FeatureIDs::is_hint);
635 DoNotNormalize.set(position: FeatureIDs::is_local);
636 DoNotNormalize.set(position: FeatureIDs::min_stage);
637 DoNotNormalize.set(position: FeatureIDs::max_stage);
638 DoNotNormalize.set(position: FeatureIDs::progress);
639}
640
641int64_t MLEvictAdvisor::tryFindEvictionCandidatePosition(
642 const LiveInterval &, const AllocationOrder &, unsigned, uint8_t,
643 const SmallVirtRegSet &) const {
644 int64_t Ret = Runner->evaluate<int64_t>();
645 assert(Ret >= 0);
646 assert(Ret <= CandidateVirtRegPos);
647 return Ret;
648}
649
650bool MLEvictAdvisor::loadInterferenceFeatures(
651 const LiveInterval &VirtReg, MCRegister PhysReg, bool IsHint,
652 const SmallVirtRegSet &FixedRegisters,
653 llvm::SmallVectorImpl<float> &Largest, size_t Pos,
654 llvm::SmallVectorImpl<LRStartEndInfo> &LRPosInfo) const {
655 // It is only possible to evict virtual register interference.
656 if (Matrix->checkInterference(VirtReg, PhysReg) > LiveRegMatrix::IK_VirtReg) {
657 // leave unavailable
658 return false;
659 }
660
661 const bool IsLocal = LIS->intervalIsInOneMBB(LI: VirtReg);
662 int64_t LocalIntfs = 0;
663 float NumUrgent = 0.0f;
664
665 // The cascade tracking is the same as in the default advisor
666 unsigned Cascade = RA.getExtraInfo().getCascadeOrCurrentNext(Reg: VirtReg.reg());
667
668 SmallVector<const LiveInterval *, MaxInterferences> InterferingIntervals;
669 for (MCRegUnit Unit : TRI->regunits(Reg: PhysReg)) {
670 LiveIntervalUnion::Query &Q = Matrix->query(LR: VirtReg, RegUnit: Unit);
671 // Different from the default heuristic, we don't make any assumptions
672 // about what having more than 10 results in the query may mean.
673 const auto &IFIntervals = Q.interferingVRegs(MaxInterferingRegs: EvictInterferenceCutoff);
674 if (IFIntervals.empty() && InterferingIntervals.empty())
675 continue;
676 if (IFIntervals.size() >= EvictInterferenceCutoff)
677 return false;
678 InterferingIntervals.append(in_start: IFIntervals.begin(), in_end: IFIntervals.end());
679 for (const LiveInterval *Intf : reverse(C: IFIntervals)) {
680 assert(Intf->reg().isVirtual() &&
681 "Only expecting virtual register interference from query");
682 // This is the same set of legality checks as in the default case: don't
683 // try to evict fixed regs or 'done' ones. Also don't break cascades,
684 // except in the urgent case, with the same nuances used in the default
685 // heuristic.
686 // We could try sharing this between the advisors, but it may end up
687 // more complex than it is right now.
688 if (FixedRegisters.count(V: Intf->reg()))
689 return false;
690 if (RA.getExtraInfo().getStage(VirtReg: *Intf) == RS_Done)
691 return false;
692 bool Urgent =
693 !VirtReg.isSpillable() &&
694 (Intf->isSpillable() ||
695 RegClassInfo.getNumAllocatableRegs(RC: MRI->getRegClass(Reg: VirtReg.reg())) <
696 RegClassInfo.getNumAllocatableRegs(
697 RC: MRI->getRegClass(Reg: Intf->reg())));
698
699 unsigned IntfCascade = RA.getExtraInfo().getCascade(Reg: Intf->reg());
700 // There is a potential that the model could be adversarial and
701 // continually evict live ranges over and over again, leading to a
702 // large amount of compile time being spent in regalloc. If we hit the
703 // threshold, prevent the range from being evicted. We still let the
704 // range through if it is urgent as we are required to produce an
705 // eviction if the candidate is not spillable.
706 if (getEvictionCount(Reg: Intf->reg()) > MaxEvictionCount && !Urgent)
707 return false;
708
709 // Only evict older cascades or live ranges without a cascade.
710 if (Cascade <= IntfCascade) {
711 if (!Urgent)
712 return false;
713 ++NumUrgent;
714 }
715
716 LocalIntfs += (IsLocal && LIS->intervalIsInOneMBB(LI: *Intf) &&
717 (!EnableLocalReassign || !canReassign(VirtReg: *Intf, FromReg: PhysReg)));
718 }
719 }
720 // OK, so if we made it this far, this LR is an eviction candidate, load its
721 // features.
722 extractFeatures(Intervals: InterferingIntervals, Largest, Pos, IsHint, LocalIntfsCount: LocalIntfs,
723 NumUrgent, LRPosInfo);
724 return true;
725}
726
727MCRegister MLEvictAdvisor::tryFindEvictionCandidate(
728 const LiveInterval &VirtReg, const AllocationOrder &Order,
729 uint8_t CostPerUseLimit, const SmallVirtRegSet &FixedRegisters) const {
730 auto MaybeOrderLimit = getOrderLimit(VirtReg, Order, CostPerUseLimit);
731 if (!MaybeOrderLimit)
732 return MCRegister::NoRegister;
733 unsigned OrderLimit = *MaybeOrderLimit;
734
735 // The heuristic sets initial costs such as, if CostPerUseLimit is
736 // max<uint8_t>, then any of the costs of the legally-evictable intervals
737 // would be lower. When that happens, one of those will be selected.
738 // Therefore, we allow the candidate be selected, unless the candidate is
739 // unspillable, in which case it would be incorrect to not find a register
740 // for it.
741 const bool MustFindEviction =
742 (!VirtReg.isSpillable() && CostPerUseLimit == static_cast<uint8_t>(~0u));
743 // Number of available candidates - if 0, no need to continue.
744 size_t Available = 0;
745 // Make sure we don't have leftover partial state from an attempt where we
746 // had no available candidates and bailed out early.
747 resetInputs(Runner&: *Runner);
748
749 // Track the index->register mapping because AllocationOrder doesn't do that
750 // and we'd have to scan it.
751 // Also track their mask, to write asserts/debug.
752 CandidateRegList Regs;
753 Regs.fill(u: {0, false});
754
755 // Track the largest value of features seen during this eviction session. We
756 // only normalize (some of) the float features, but it's just simpler to
757 // dimension 'Largest' to all the features, especially since we have the
758 // 'DoNotNormalize' list.
759 FeaturesListNormalizer Largest(FeatureIDs::FeatureCount, 0.0);
760
761 // Same overal idea as in the default eviction policy - we visit the values
762 // of AllocationOrder one at a time. If it's not legally available, we mask
763 // off the corresponding feature column (==do nothing because we already
764 // reset all the features to 0) Use Pos to capture the column we load
765 // features at - in AllocationOrder order.
766 size_t Pos = 0;
767 SmallVector<LRStartEndInfo, NumberOfInterferences> LRPosInfo;
768 for (auto I = Order.begin(), E = Order.getOrderLimitEnd(OrderLimit); I != E;
769 ++I, ++Pos) {
770 MCRegister PhysReg = *I;
771 assert(!Regs[Pos].second);
772 assert(PhysReg);
773 if (!canAllocatePhysReg(CostPerUseLimit, PhysReg)) {
774 continue;
775 }
776 if (loadInterferenceFeatures(VirtReg, PhysReg, IsHint: I.isHint(), FixedRegisters,
777 Largest, Pos, LRPosInfo)) {
778 ++Available;
779 Regs[Pos] = std::make_pair(x&: PhysReg, y: true);
780 }
781 }
782 if (Available == 0) {
783 // Nothing to decide, nothing to learn.
784 assert(!MustFindEviction);
785 return MCRegister::NoRegister;
786 }
787 const size_t ValidPosLimit = Pos;
788 // If we must find eviction, the candidate should be masked out of the
789 // decision making process.
790 Regs[CandidateVirtRegPos].second = !MustFindEviction;
791 if (!MustFindEviction)
792 extractFeatures(Intervals: SmallVector<const LiveInterval *, 1>(1, &VirtReg), Largest,
793 Pos: CandidateVirtRegPos, /*IsHint*/ 0,
794 /*LocalIntfsCount*/ 0,
795 /*NumUrgent*/ 0.0, LRPosInfo);
796 assert(InitialQSize > 0.0 && "We couldn't have gotten here if we had "
797 "nothing to allocate initially.");
798 // Normalize the features.
799 for (auto &V : Largest)
800 V = V ? V : 1.0;
801 for (size_t FeatureIndex = 0; FeatureIndex < FeatureIDs::FeatureCount;
802 ++FeatureIndex) {
803 if (DoNotNormalize.test(position: FeatureIndex))
804 continue;
805 for (size_t Pos = 0; Pos < NumberOfInterferences; ++Pos) {
806 Runner->getTensor<float>(FeatureID: FeatureIndex)[Pos] /= Largest[FeatureIndex];
807 }
808 }
809 *Runner->getTensor<float>(FeatureID: FeatureIDs::progress) =
810 static_cast<float>(RA.getQueueSize()) / InitialQSize;
811
812 // Get a decision.
813 size_t CandidatePos = tryFindEvictionCandidatePosition(
814 VirtReg, Order, OrderLimit, CostPerUseLimit, FixedRegisters);
815 // The contract with the ML side is that CandidatePos is mask == 1 (i.e.
816 // Regs[CandidatePos].second)
817 assert(Regs[CandidatePos].second);
818 if (CandidatePos == CandidateVirtRegPos) {
819 onEviction(RegBeingEvicted: VirtReg.reg());
820 assert(!MustFindEviction);
821 return MCRegister::NoRegister;
822 }
823 assert(CandidatePos < ValidPosLimit);
824 (void)ValidPosLimit;
825
826 // Update information about how many times the virtual registers being
827 // evicted have been evicted so that we can prevent the model from evicting
828 // the same ranges continually and eating compile time.
829 for (MCRegUnit Unit : TRI->regunits(Reg: Regs[CandidatePos].first)) {
830 LiveIntervalUnion::Query &Q = Matrix->query(LR: VirtReg, RegUnit: Unit);
831 const auto &IFIntervals = Q.interferingVRegs(MaxInterferingRegs: EvictInterferenceCutoff);
832 for (const LiveInterval *Intf : reverse(C: IFIntervals)) {
833 onEviction(RegBeingEvicted: Intf->reg());
834 }
835 }
836
837 return Regs[CandidatePos].first;
838}
839
840const LIFeatureComponents &
841MLEvictAdvisor::getLIFeatureComponents(const LiveInterval &LI) const {
842 RegID ID = LI.reg().id();
843 LIFeatureComponents Empty;
844 auto I = CachedFeatures.insert(KV: std::make_pair(x&: ID, y&: Empty));
845 LIFeatureComponents &Ret = I.first->getSecond();
846 if (!I.second)
847 return Ret;
848
849 SmallPtrSet<MachineInstr *, 8> Visited;
850 const TargetRegisterInfo &TRI = *MF.getSubtarget().getRegisterInfo();
851
852 for (MachineRegisterInfo::reg_instr_nodbg_iterator
853 I = MRI->reg_instr_nodbg_begin(RegNo: LI.reg()),
854 E = MRI->reg_instr_nodbg_end();
855 I != E;) {
856 MachineInstr *MI = &*(I++);
857
858 ++Ret.NumDefsAndUses;
859 if (!Visited.insert(Ptr: MI).second)
860 continue;
861
862 if (MI->isIdentityCopy() || MI->isImplicitDef())
863 continue;
864
865 bool Reads, Writes;
866 std::tie(args&: Reads, args&: Writes) = MI->readsWritesVirtualRegister(Reg: LI.reg());
867
868 float Freq = MBFI.getBlockFreqRelativeToEntryBlock(MBB: MI->getParent());
869 Ret.HottestBlockFreq = std::max(a: Freq, b: Ret.HottestBlockFreq);
870
871 Ret.R += (Reads && !Writes) * Freq;
872 Ret.W += (!Reads && Writes) * Freq;
873 Ret.RW += (Reads && Writes) * Freq;
874
875 auto *MBB = MI->getParent();
876 auto *Loop = Loops.getLoopFor(BB: MBB);
877 bool IsExiting = Loop ? Loop->isLoopExiting(BB: MBB) : false;
878
879 if (Writes && IsExiting && LIS->isLiveOutOfMBB(LR: LI, mbb: MBB))
880 Ret.IndVarUpdates += Freq;
881
882 if (MI->isCopy() && VirtRegAuxInfo::copyHint(MI, Reg: LI.reg(), TRI, MRI: *MRI))
883 Ret.HintWeights += Freq;
884 }
885 Ret.IsRemat = VirtRegAuxInfo::isRematerializable(
886 LI, LIS: *LIS, VRM: *VRM, MRI: *MRI, TII: *MF.getSubtarget().getInstrInfo());
887 return Ret;
888}
889
890// Overall, this currently mimics what we do for weight calculation, but instead
891// of accummulating the various features, we keep them separate.
892void MLEvictAdvisor::extractFeatures(
893 const SmallVectorImpl<const LiveInterval *> &Intervals,
894 llvm::SmallVectorImpl<float> &Largest, size_t Pos, int64_t IsHint,
895 int64_t LocalIntfsCount, float NumUrgent,
896 SmallVectorImpl<LRStartEndInfo> &LRPosInfo) const {
897 int64_t NumDefsAndUses = 0;
898 int64_t NumBrokenHints = 0;
899 double R = 0.0;
900 double W = 0.0;
901 double RW = 0.0;
902 double IndVarUpdates = 0.0;
903 double HintWeights = 0.0;
904 float StartBBFreq = 0.0;
905 float EndBBFreq = 0.0;
906 float HottestBlockFreq = 0.0;
907 int32_t NumRematerializable = 0;
908 float TotalWeight = 0.0;
909
910 SlotIndex EndSI = LIS->getSlotIndexes()->getZeroIndex();
911 SlotIndex StartSI = LIS->getSlotIndexes()->getLastIndex();
912 int64_t MaxStage = 0;
913 int64_t MinStage =
914 Intervals.empty() ? 0 : std::numeric_limits<int64_t>::max();
915
916 for (const auto *L : Intervals) {
917 const LiveInterval &LI = *L;
918 MaxStage = std::max<int64_t>(
919 a: MaxStage, b: static_cast<int64_t>(RA.getExtraInfo().getStage(VirtReg: LI)));
920 MinStage = std::min<int64_t>(
921 a: MinStage, b: static_cast<int64_t>(RA.getExtraInfo().getStage(VirtReg: LI)));
922
923 TotalWeight = std::max(a: TotalWeight, b: LI.weight());
924
925 if (LI.beginIndex() < StartSI)
926 StartSI = LI.beginIndex();
927
928 if (LI.endIndex() > EndSI)
929 EndSI = LI.endIndex();
930 const LIFeatureComponents &LIFC = getLIFeatureComponents(LI);
931 NumBrokenHints += VRM->hasPreferredPhys(VirtReg: LI.reg());
932
933 NumDefsAndUses += LIFC.NumDefsAndUses;
934 HottestBlockFreq = std::max(a: HottestBlockFreq, b: LIFC.HottestBlockFreq);
935 R += LIFC.R;
936 W += LIFC.W;
937 RW += LIFC.RW;
938
939 IndVarUpdates += LIFC.IndVarUpdates;
940
941 HintWeights += LIFC.HintWeights;
942 NumRematerializable += LIFC.IsRemat;
943 }
944 size_t Size = 0;
945 if (!Intervals.empty()) {
946 StartBBFreq =
947 MBFI.getBlockFreqRelativeToEntryBlock(MBB: LIS->getMBBFromIndex(index: StartSI));
948 if (EndSI >= LIS->getSlotIndexes()->getLastIndex())
949 EndSI = LIS->getSlotIndexes()->getLastIndex().getPrevIndex();
950 EndBBFreq =
951 MBFI.getBlockFreqRelativeToEntryBlock(MBB: LIS->getMBBFromIndex(index: EndSI));
952 Size = StartSI.distance(other: EndSI);
953 }
954 // Set the features at the column 'Pos'.
955#define SET(ID, TYPE, VAL) \
956 do { \
957 Runner->getTensor<TYPE>(FeatureIDs::ID)[Pos] = static_cast<TYPE>(VAL); \
958 if (!DoNotNormalize.test(FeatureIDs::ID)) \
959 Largest[FeatureIDs::ID] = \
960 std::max(Largest[FeatureIDs::ID], static_cast<float>(VAL)); \
961 } while (false)
962 SET(mask, int64_t, 1);
963 SET(is_free, int64_t, Intervals.empty());
964 SET(nr_urgent, float, NumUrgent);
965 SET(nr_broken_hints, float, NumBrokenHints);
966 SET(is_hint, int64_t, IsHint);
967 SET(is_local, int64_t, LocalIntfsCount);
968 SET(nr_rematerializable, float, NumRematerializable);
969 SET(nr_defs_and_uses, float, NumDefsAndUses);
970 SET(weighed_reads_by_max, float, R);
971 SET(weighed_writes_by_max, float, W);
972 SET(weighed_read_writes_by_max, float, RW);
973 SET(weighed_indvars_by_max, float, IndVarUpdates);
974 SET(hint_weights_by_max, float, HintWeights);
975 SET(start_bb_freq_by_max, float, StartBBFreq);
976 SET(end_bb_freq_by_max, float, EndBBFreq);
977 SET(hottest_bb_freq_by_max, float, HottestBlockFreq);
978 SET(liverange_size, float, Size);
979 SET(use_def_density, float, TotalWeight);
980 SET(max_stage, int64_t, MaxStage);
981 SET(min_stage, int64_t, MinStage);
982#undef SET
983}
984
985// Development mode-specific implementations
986#ifdef LLVM_HAVE_TFLITE
987
988RegAllocEvictionAdvisorAnalysisLegacy *
989llvm::createDevelopmentModeAdvisorAnalysisLegacy() {
990 return new DevelopmentModeEvictionAdvisorAnalysisLegacy();
991}
992
993int64_t DevelopmentModeEvictAdvisor::tryFindEvictionCandidatePosition(
994 const LiveInterval &VirtReg, const AllocationOrder &Order,
995 unsigned OrderLimit, uint8_t CostPerUseLimit,
996 const SmallVirtRegSet &FixedRegisters) const {
997 int64_t Ret = 0;
998 if (isa<ModelUnderTrainingRunner>(getRunner())) {
999 Ret = MLEvictAdvisor::tryFindEvictionCandidatePosition(
1000 VirtReg, Order, OrderLimit, CostPerUseLimit, FixedRegisters);
1001 } else {
1002 MCRegister PhysReg = getDefaultAdvisor().tryFindEvictionCandidate(
1003 VirtReg, Order, CostPerUseLimit, FixedRegisters);
1004 // Find the index of the selected PhysReg. We need it for logging,
1005 // otherwise this is wasted cycles (but so would starting development mode
1006 // without a model nor logging)
1007 if (!PhysReg)
1008 Ret = CandidateVirtRegPos;
1009 else
1010 for (auto I = Order.begin(), E = Order.getOrderLimitEnd(OrderLimit);
1011 I != E; ++I, ++Ret)
1012 if (*I == PhysReg)
1013 break;
1014 }
1015 if (TrainingLog.empty())
1016 return Ret;
1017 // TODO(mtrofin): when we support optional rewards, this can go away. In the
1018 // meantime, we log the "pretend" reward (0) for the previous observation
1019 // before starting a new one.
1020 if (Log->hasObservationInProgress())
1021 Log->logReward<float>(0.0);
1022
1023 Log->startObservation();
1024 size_t CurrentFeature = 0;
1025 size_t FeatureCount = FeatureIDs::FeatureCount;
1026 for (; CurrentFeature < FeatureCount; ++CurrentFeature) {
1027 Log->logTensorValue(CurrentFeature,
1028 reinterpret_cast<const char *>(
1029 getRunner().getTensorUntyped(CurrentFeature)));
1030 }
1031 if (auto *MUTR = dyn_cast<ModelUnderTrainingRunner>(&getRunner()))
1032 for (size_t I = 0; I < MUTR->extraOutputsForLoggingSpecs().size();
1033 ++I, ++CurrentFeature)
1034 Log->logTensorValue(
1035 CurrentFeature,
1036 reinterpret_cast<const char *>(MUTR->getUntypedExtraOutputValue(I)));
1037 // The output is right after the features and the extra outputs
1038 Log->logTensorValue(CurrentFeature, reinterpret_cast<const char *>(&Ret));
1039 Log->endObservation();
1040 return Ret;
1041}
1042
1043bool RegAllocScoring::runOnMachineFunction(MachineFunction &MF) {
1044 std::optional<float> CachedReward;
1045 auto GetReward = [&]() {
1046 if (!CachedReward)
1047 CachedReward = static_cast<float>(
1048 calculateRegAllocScore(
1049 MF, getAnalysis<MachineBlockFrequencyInfoWrapperPass>().getMBFI())
1050 .getScore());
1051 return *CachedReward;
1052 };
1053
1054 getAnalysis<RegAllocEvictionAdvisorAnalysisLegacy>().logRewardIfNeeded(
1055 MF, GetReward);
1056 getAnalysis<RegAllocPriorityAdvisorAnalysisLegacy>().logRewardIfNeeded(
1057 MF, GetReward);
1058 return false;
1059}
1060#endif // #ifdef LLVM_HAVE_TFLITE
1061
1062RegAllocEvictionAdvisorProvider *
1063llvm::createReleaseModeAdvisorProvider(LLVMContext &Ctx) {
1064 return isReleaseModelValid<CompiledModelType>(InteractiveChannelBaseName,
1065 SelectedModel: SelectedMLGORegAllocModel)
1066 ? new ReleaseModeEvictionAdvisorProvider(Ctx)
1067 : nullptr;
1068}
1069
1070RegAllocEvictionAdvisorProvider *
1071llvm::createDevelopmentModeAdvisorProvider(LLVMContext &Ctx) {
1072#if defined(LLVM_HAVE_TFLITE)
1073 return new DevelopmentModeEvictionAdvisorProvider(Ctx);
1074#endif
1075 return nullptr;
1076}
1077
1078RegAllocEvictionAdvisorAnalysisLegacy *
1079llvm::createReleaseModeAdvisorAnalysisLegacy() {
1080 return isReleaseModelValid<CompiledModelType>(InteractiveChannelBaseName,
1081 SelectedModel: SelectedMLGORegAllocModel)
1082 ? new ReleaseModeEvictionAdvisorAnalysisLegacy()
1083 : nullptr;
1084}
1085
1086// In all cases except development mode, we don't need scoring.
1087#if !defined(LLVM_HAVE_TFLITE)
1088bool RegAllocScoring::runOnMachineFunction(MachineFunction &) { return false; }
1089#endif
1090