1 //===- MLRegAllocEvictAdvisor.cpp - ML eviction advisor -------------------===//
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
7 //===----------------------------------------------------------------------===//
9 // Implementation of the ML eviction advisor and reward injection pass
11 //===----------------------------------------------------------------------===//
13 #include "AllocationOrder.h"
14 #include "RegAllocEvictionAdvisor.h"
15 #include "RegAllocGreedy.h"
16 #include "llvm/Analysis/InteractiveModelRunner.h"
17 #include "llvm/Analysis/MLModelRunner.h"
18 #include "llvm/Analysis/TensorSpec.h"
19 #if defined(LLVM_HAVE_TF_AOT_REGALLOCEVICTMODEL) || defined(LLVM_HAVE_TFLITE)
20 #include "llvm/Analysis/ModelUnderTrainingRunner.h"
21 #include "llvm/Analysis/NoInferenceModelRunner.h"
22 #include "llvm/Analysis/Utils/TrainingLogger.h"
24 #include "MLRegAllocEvictAdvisor.h"
25 #include "llvm/Analysis/ReleaseModeModelRunner.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/InitializePasses.h"
36 #include "llvm/Pass.h"
37 #include "llvm/PassRegistry.h"
38 #include "llvm/Support/CommandLine.h"
39 #include "llvm/Support/ErrorHandling.h"
47 #define DEBUG_TYPE "ml-regalloc"
49 // Generated header in release (AOT) mode
50 #if defined(LLVM_HAVE_TF_AOT_REGALLOCEVICTMODEL)
51 #include "RegAllocEvictModel.h"
52 using CompiledModelType
= RegAllocEvictModel
;
54 using CompiledModelType
= NoopSavedModelImpl
;
57 static cl::opt
<std::string
> InteractiveChannelBaseName(
58 "regalloc-evict-interactive-channel-base", cl::Hidden
,
60 "Base file path for the interactive mode. The incoming filename should "
61 "have the name <regalloc-evict-interactive-channel-base>.in, while the "
62 "outgoing name should be "
63 "<regalloc-evict-interactive-channel-base>.out"));
65 // Options that only make sense in development mode
66 #ifdef LLVM_HAVE_TFLITE
67 #include "RegAllocScore.h"
68 #include "llvm/Analysis/Utils/TFUtils.h"
70 static cl::opt
<std::string
> TrainingLog(
71 "regalloc-training-log", cl::Hidden
,
72 cl::desc("Training log for the register allocator eviction model"));
74 static cl::opt
<std::string
> ModelUnderTraining(
75 "regalloc-model", cl::Hidden
,
76 cl::desc("The model being trained for register allocation eviction"));
78 static cl::opt
<bool> EnableDevelopmentFeatures(
79 "regalloc-enable-development-features", cl::Hidden
,
80 cl::desc("Whether or not to enable features under development for the ML "
84 static const bool EnableDevelopmentFeatures
= false;
85 #endif // #ifdef LLVM_HAVE_TFLITE
87 /// The score injection pass.
88 /// This pass calculates the score for a function and inserts it in the log, but
89 /// this happens only in development mode. It's a no-op otherwise.
91 extern cl::opt
<unsigned> EvictInterferenceCutoff
;
93 class RegAllocScoring
: public MachineFunctionPass
{
97 RegAllocScoring() : MachineFunctionPass(ID
) {
98 initializeRegAllocScoringPass(*PassRegistry::getPassRegistry());
101 ~RegAllocScoring() override
= default;
103 StringRef
getPassName() const override
{
104 return "Register Allocation Pass Scoring";
107 /// RegAllocReward analysis usage.
108 void getAnalysisUsage(AnalysisUsage
&AU
) const override
{
109 AU
.setPreservesAll();
110 AU
.addRequired
<RegAllocEvictionAdvisorAnalysis
>();
111 AU
.addRequired
<RegAllocPriorityAdvisorAnalysis
>();
112 AU
.addRequired
<MachineBlockFrequencyInfo
>();
113 MachineFunctionPass::getAnalysisUsage(AU
);
116 /// Performs this pass
117 bool runOnMachineFunction(MachineFunction
&) override
;
120 char RegAllocScoring::ID
= 0;
121 FunctionPass
*createRegAllocScoringPass() { return new RegAllocScoring(); }
125 INITIALIZE_PASS(RegAllocScoring
, "regallocscoringpass",
126 "Register Allocation Scoring Pass", false, false)
128 // ===================================
129 // Common ML Advisor declarations
130 // ===================================
132 // The model can only accept a specified number of opcodes and will error it if
133 // fed an opcode it hasn't seen before. This constant sets the current cutoff.
134 static const int OpcodeValueCutoff
= 17716;
136 // Most features are as described above, so we'll reuse this vector in defining
138 static const std::vector
<int64_t> PerLiveRangeShape
{1, NumberOfInterferences
};
143 // For each interfering live range (incl. the candidate) we collect a number of
144 // features. However, because the features are of different types (and because
145 // of ML best practices), we organize the tensors per feature, not per
146 // candidate. Each such tensor has a scalar value corresponding to the
147 // interferring live range at that position, in the order in AllocationOrder.
148 // The last position corresponds to the virt reg seeking allocation.
149 // Exception to all that is the progression feature, which is just a scalar (see
150 // its documentation for details).
151 // Note on naming: the "_by_max" are normalized using the largest value of that
152 // tensor, as observed in the current decision making stage (i.e. for the
153 // current call to the advisor's tryFindEvictionCandidate)
155 // The feature list format: type, name, shape, documentation.
156 // Note: we can really just use int64 and float, hence the modeling of some
157 // bools as int64 values.
158 #define RA_EVICT_FEATURES_LIST(M) \
159 M(int64_t, mask, PerLiveRangeShape, \
160 "boolean values, 0 for unavailable candidates (i.e. if a position is 0, " \
162 "can't be evicted)") \
163 M(int64_t, is_free, PerLiveRangeShape, \
164 "boolean values, 1 if this phys reg is actually free (no interferences)") \
165 M(float, nr_urgent, PerLiveRangeShape, \
166 "number of 'urgent' intervals, normalized. Urgent are those that are OK " \
167 "to break cascades") \
168 M(float, nr_broken_hints, PerLiveRangeShape, \
169 "if this position were evicted, how many broken hints would there be") \
170 M(int64_t, is_hint, PerLiveRangeShape, \
171 "is this a preferred phys reg for the candidate") \
172 M(int64_t, is_local, PerLiveRangeShape, \
173 "is this live range local to a basic block") \
174 M(float, nr_rematerializable, PerLiveRangeShape, \
175 "nr rematerializable ranges") \
176 M(float, nr_defs_and_uses, PerLiveRangeShape, \
177 "bb freq - weighed nr defs and uses") \
178 M(float, weighed_reads_by_max, PerLiveRangeShape, \
179 "bb freq - weighed nr of reads, normalized") \
180 M(float, weighed_writes_by_max, PerLiveRangeShape, \
181 "bb feq - weighed nr of writes, normalized") \
182 M(float, weighed_read_writes_by_max, PerLiveRangeShape, \
183 "bb freq - weighed nr of uses that are both read and writes, normalized") \
184 M(float, weighed_indvars_by_max, PerLiveRangeShape, \
185 "bb freq - weighed nr of uses that are indvars, normalized") \
186 M(float, hint_weights_by_max, PerLiveRangeShape, \
187 "bb freq - weighed nr of uses that are hints, normalized") \
188 M(float, start_bb_freq_by_max, PerLiveRangeShape, \
189 "the freq in the start block, normalized") \
190 M(float, end_bb_freq_by_max, PerLiveRangeShape, \
191 "freq of end block, normalized") \
192 M(float, hottest_bb_freq_by_max, PerLiveRangeShape, \
193 "hottest BB freq, normalized") \
194 M(float, liverange_size, PerLiveRangeShape, \
195 "size (instr index diff) of the LR") \
196 M(float, use_def_density, PerLiveRangeShape, \
197 "the max weight, as computed by the manual heuristic") \
198 M(int64_t, max_stage, PerLiveRangeShape, \
199 "largest stage of an interval in this LR") \
200 M(int64_t, min_stage, PerLiveRangeShape, \
201 "lowest stage of an interval in this LR") \
202 M(float, progress, {1}, "ratio of current queue size to initial size")
204 #ifdef LLVM_HAVE_TFLITE
205 #define RA_EVICT_FIRST_DEVELOPMENT_FEATURE(M) \
206 M(int64_t, instructions, InstructionsShape, \
207 "Opcodes of the instructions covered by the eviction problem")
209 #define RA_EVICT_REST_DEVELOPMENT_FEATURES(M) \
210 M(int64_t, instructions_mapping, InstructionsMappingShape, \
211 "A binary matrix mapping LRs to instruction opcodes") \
212 M(float, mbb_frequencies, MBBFrequencyShape, \
213 "A vector of machine basic block frequencies") \
214 M(int64_t, mbb_mapping, InstructionsShape, \
215 "A vector of indicies mapping instructions to MBBs")
217 #define RA_EVICT_FIRST_DEVELOPMENT_FEATURE(M)
218 #define RA_EVICT_REST_DEVELOPMENT_FEATURES(M)
221 // The model learns to pick one of the mask == 1 interferences. This is the
222 // name of the output tensor. The contract with the model is that the output
223 // will be guaranteed to be to a mask == 1 position. Using a macro here to
224 // avoid 'not used' warnings (and keep cond compilation to a minimum)
225 #define DecisionName "index_to_evict"
226 static const TensorSpec DecisionSpec
=
227 TensorSpec::createSpec
<int64_t>(DecisionName
, {1});
229 // Named features index.
231 #define _FEATURE_IDX_SIMPLE(_, name, __, ___) name
232 #define _FEATURE_IDX(A, B, C, D) _FEATURE_IDX_SIMPLE(A, B, C, D),
233 RA_EVICT_FEATURES_LIST(_FEATURE_IDX
) FeatureCount
,
234 #ifdef LLVM_HAVE_TFLITE
235 RA_EVICT_FIRST_DEVELOPMENT_FEATURE(_FEATURE_IDX_SIMPLE
) = FeatureCount
,
237 RA_EVICT_FIRST_DEVELOPMENT_FEATURE(_FEATURE_IDX
)
238 #endif // #ifdef LLVM_HAVE_TFLITE
239 RA_EVICT_REST_DEVELOPMENT_FEATURES(_FEATURE_IDX
) FeaturesWithDevelopmentCount
241 #undef _FEATURE_IDX_SIMPLE
244 // The ML advisor will typically have a sparse input to the evaluator, because
245 // various phys regs won't be available. It's easier (maintenance-wise) to
246 // bulk-reset the state of the evaluator each time we are about to use it
248 template <typename T
> size_t getTotalSize(const std::vector
<int64_t> &Shape
) {
249 size_t Ret
= sizeof(T
);
250 for (const auto V
: Shape
)
255 void resetInputs(MLModelRunner
&Runner
) {
256 #define _RESET(TYPE, NAME, SHAPE, __) \
257 std::memset(Runner.getTensorUntyped(FeatureIDs::NAME), 0, \
258 getTotalSize<TYPE>(SHAPE));
259 RA_EVICT_FEATURES_LIST(_RESET
)
260 if (EnableDevelopmentFeatures
) {
261 RA_EVICT_FIRST_DEVELOPMENT_FEATURE(_RESET
)
262 RA_EVICT_REST_DEVELOPMENT_FEATURES(_RESET
)
267 // Per-live interval components that get aggregated into the feature values
268 // that will be passed to the evaluator.
269 struct LIFeatureComponents
{
273 double IndVarUpdates
= 0;
274 double HintWeights
= 0.0;
275 int64_t NrDefsAndUses
= 0;
276 float HottestBlockFreq
= 0.0;
277 bool IsRemat
= false;
280 using CandidateRegList
=
281 std::array
<std::pair
<MCRegister
, bool>, NumberOfInterferences
>;
282 using FeaturesListNormalizer
=
283 llvm::SmallVector
<float, FeatureIDs::FeatureCount
>;
285 /// The ML evictor (commonalities between release and development mode)
286 class MLEvictAdvisor
: public RegAllocEvictionAdvisor
{
288 MLEvictAdvisor(const MachineFunction
&MF
, const RAGreedy
&RA
,
289 MLModelRunner
*Runner
, const MachineBlockFrequencyInfo
&MBFI
,
290 const MachineLoopInfo
&Loops
);
293 const RegAllocEvictionAdvisor
&getDefaultAdvisor() const {
294 return static_cast<const RegAllocEvictionAdvisor
&>(DefaultAdvisor
);
297 // The assumption is that if the Runner could not be constructed, we emit-ed
298 // error, and we shouldn't be asking for it here.
299 const MLModelRunner
&getRunner() const { return *Runner
; }
301 /// This just calls Evaluate on the Runner, but in the development mode
302 /// case, if we're just capturing the log of the default advisor, it needs
303 /// to call the latter instead, so we need to pass all the necessary
304 /// parameters for it. In the development case, it will also log.
306 tryFindEvictionCandidatePosition(const LiveInterval
&VirtReg
,
307 const AllocationOrder
&Order
,
308 unsigned OrderLimit
, uint8_t CostPerUseLimit
,
309 const SmallVirtRegSet
&FixedRegisters
) const;
311 /// Load the features of the given VirtReg (allocated or not) at column Pos,
312 /// but if that can't be evicted, return false instead.
314 loadInterferenceFeatures(const LiveInterval
&VirtReg
, MCRegister PhysReg
,
315 bool IsHint
, const SmallVirtRegSet
&FixedRegisters
,
316 llvm::SmallVectorImpl
<float> &Largest
, size_t Pos
,
317 SmallVectorImpl
<LRStartEndInfo
> &LRPosInfo
) const;
320 static float getInitialQueueSize(const MachineFunction
&MF
);
322 MCRegister
tryFindEvictionCandidate(
323 const LiveInterval
&VirtReg
, const AllocationOrder
&Order
,
324 uint8_t CostPerUseLimit
,
325 const SmallVirtRegSet
&FixedRegisters
) const override
;
327 void extractFeatures(const SmallVectorImpl
<const LiveInterval
*> &Intervals
,
328 llvm::SmallVectorImpl
<float> &Largest
, size_t Pos
,
329 int64_t IsHint
, int64_t LocalIntfsCount
, float NrUrgent
,
330 SmallVectorImpl
<LRStartEndInfo
> &LRPosInfo
) const;
332 // Point-in-time: we didn't learn this, so we always delegate to the
334 bool canEvictHintInterference(
335 const LiveInterval
&VirtReg
, MCRegister PhysReg
,
336 const SmallVirtRegSet
&FixedRegisters
) const override
{
337 return getDefaultAdvisor().canEvictHintInterference(VirtReg
, PhysReg
,
341 const LIFeatureComponents
&
342 getLIFeatureComponents(const LiveInterval
&LI
) const;
344 // Hold on to a default advisor for:
345 // 1) the implementation of canEvictHintInterference, because we didn't
346 // learn that nuance yet; 2) for bootstrapping (logging) in the development
348 const DefaultEvictionAdvisor DefaultAdvisor
;
349 MLModelRunner
*const Runner
;
350 const MachineBlockFrequencyInfo
&MBFI
;
351 const MachineLoopInfo
&Loops
;
353 // Indices of those features we don't want to normalize.
354 // This could be static and shared, but its initialization is non-trivial.
355 std::bitset
<FeatureIDs::FeatureCount
> DoNotNormalize
;
356 const float InitialQSize
;
358 using RegID
= unsigned;
359 mutable DenseMap
<RegID
, LIFeatureComponents
> CachedFeatures
;
362 #define _DECL_FEATURES(type, name, shape, _) \
363 TensorSpec::createSpec<type>(#name, shape),
365 // ===================================
366 // Release (AOT) - specifics
367 // ===================================
368 class ReleaseModeEvictionAdvisorAnalysis final
369 : public RegAllocEvictionAdvisorAnalysis
{
371 ReleaseModeEvictionAdvisorAnalysis()
372 : RegAllocEvictionAdvisorAnalysis(AdvisorMode::Release
) {
373 if (EnableDevelopmentFeatures
) {
374 InputFeatures
= {RA_EVICT_FEATURES_LIST(
375 _DECL_FEATURES
) RA_EVICT_FIRST_DEVELOPMENT_FEATURE(_DECL_FEATURES
)
376 RA_EVICT_REST_DEVELOPMENT_FEATURES(_DECL_FEATURES
)};
378 InputFeatures
= {RA_EVICT_FEATURES_LIST(_DECL_FEATURES
)};
381 // support for isa<> and dyn_cast.
382 static bool classof(const RegAllocEvictionAdvisorAnalysis
*R
) {
383 return R
->getAdvisorMode() == AdvisorMode::Release
;
387 std::vector
<TensorSpec
> InputFeatures
;
389 void getAnalysisUsage(AnalysisUsage
&AU
) const override
{
390 AU
.addRequired
<MachineBlockFrequencyInfo
>();
391 AU
.addRequired
<MachineLoopInfo
>();
392 RegAllocEvictionAdvisorAnalysis::getAnalysisUsage(AU
);
395 std::unique_ptr
<RegAllocEvictionAdvisor
>
396 getAdvisor(const MachineFunction
&MF
, const RAGreedy
&RA
) override
{
398 if (InteractiveChannelBaseName
.empty())
399 Runner
= std::make_unique
<ReleaseModeModelRunner
<CompiledModelType
>>(
400 MF
.getFunction().getContext(), InputFeatures
, DecisionName
);
402 Runner
= std::make_unique
<InteractiveModelRunner
>(
403 MF
.getFunction().getContext(), InputFeatures
, DecisionSpec
,
404 InteractiveChannelBaseName
+ ".out",
405 InteractiveChannelBaseName
+ ".in");
407 return std::make_unique
<MLEvictAdvisor
>(
408 MF
, RA
, Runner
.get(), getAnalysis
<MachineBlockFrequencyInfo
>(),
409 getAnalysis
<MachineLoopInfo
>());
411 std::unique_ptr
<MLModelRunner
> Runner
;
414 // ===================================
415 // Development mode-specifics
416 // ===================================
419 #ifdef LLVM_HAVE_TFLITE
420 static const TensorSpec Reward
= TensorSpec::createSpec
<float>("reward", {1});
422 // Features we bind on the model. The tensor names have a prefix, and we also
423 // need to include some tensors that are expected to be present by the
425 // TODO: can we just get rid of these?
426 #define _DECL_TRAIN_FEATURES(type, name, shape, _) \
427 TensorSpec::createSpec<type>(std::string("action_") + #name, shape),
429 class DevelopmentModeEvictAdvisor
: public MLEvictAdvisor
{
431 DevelopmentModeEvictAdvisor(const MachineFunction
&MF
, const RAGreedy
&RA
,
432 MLModelRunner
*Runner
,
433 const MachineBlockFrequencyInfo
&MBFI
,
434 const MachineLoopInfo
&Loops
, Logger
*Log
)
435 : MLEvictAdvisor(MF
, RA
, Runner
, MBFI
, Loops
), Log(Log
) {}
438 int64_t tryFindEvictionCandidatePosition(
439 const LiveInterval
&VirtReg
, const AllocationOrder
&Order
,
440 unsigned OrderLimit
, uint8_t CostPerUseLimit
,
441 const SmallVirtRegSet
&FixedRegisters
) const override
;
446 class DevelopmentModeEvictionAdvisorAnalysis final
447 : public RegAllocEvictionAdvisorAnalysis
{
449 DevelopmentModeEvictionAdvisorAnalysis()
450 : RegAllocEvictionAdvisorAnalysis(AdvisorMode::Development
) {
451 if (EnableDevelopmentFeatures
) {
452 InputFeatures
= {RA_EVICT_FEATURES_LIST(
453 _DECL_FEATURES
) RA_EVICT_FIRST_DEVELOPMENT_FEATURE(_DECL_FEATURES
)
454 RA_EVICT_REST_DEVELOPMENT_FEATURES(_DECL_FEATURES
)};
455 TrainingInputFeatures
= {
456 RA_EVICT_FEATURES_LIST(_DECL_TRAIN_FEATURES
)
457 RA_EVICT_FIRST_DEVELOPMENT_FEATURE(_DECL_TRAIN_FEATURES
)
458 RA_EVICT_REST_DEVELOPMENT_FEATURES(_DECL_TRAIN_FEATURES
)
459 TensorSpec::createSpec
<float>("action_discount", {1}),
460 TensorSpec::createSpec
<int32_t>("action_step_type", {1}),
461 TensorSpec::createSpec
<float>("action_reward", {1})};
463 InputFeatures
= {RA_EVICT_FEATURES_LIST(_DECL_FEATURES
)};
464 TrainingInputFeatures
= {
465 RA_EVICT_FEATURES_LIST(_DECL_TRAIN_FEATURES
)
466 TensorSpec::createSpec
<float>("action_discount", {1}),
467 TensorSpec::createSpec
<int32_t>("action_step_type", {1}),
468 TensorSpec::createSpec
<float>("action_reward", {1})};
471 // support for isa<> and dyn_cast.
472 static bool classof(const RegAllocEvictionAdvisorAnalysis
*R
) {
473 return R
->getAdvisorMode() == AdvisorMode::Development
;
476 void logRewardIfNeeded(const MachineFunction
&MF
,
477 llvm::function_ref
<float()> GetReward
) override
{
478 if (!Log
|| !Log
->hasAnyObservationForContext(MF
.getName()))
480 // The function pass manager would run all the function passes for a
481 // function, so we assume the last context belongs to this function. If
482 // this invariant ever changes, we can implement at that time switching
483 // contexts. At this point, it'd be an error
484 if (Log
->currentContext() != MF
.getName()) {
485 MF
.getFunction().getContext().emitError(
486 "The training log context shouldn't have had changed.");
488 if (Log
->hasObservationInProgress())
489 Log
->logReward
<float>(GetReward());
493 std::vector
<TensorSpec
> InputFeatures
;
494 std::vector
<TensorSpec
> TrainingInputFeatures
;
496 void getAnalysisUsage(AnalysisUsage
&AU
) const override
{
497 AU
.addRequired
<MachineBlockFrequencyInfo
>();
498 AU
.addRequired
<MachineLoopInfo
>();
499 RegAllocEvictionAdvisorAnalysis::getAnalysisUsage(AU
);
502 bool doInitialization(Module
&M
) override
{
503 LLVMContext
&Ctx
= M
.getContext();
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");
509 if (ModelUnderTraining
.empty())
510 Runner
= std::make_unique
<NoInferenceModelRunner
>(Ctx
, InputFeatures
);
512 Runner
= ModelUnderTrainingRunner::createAndEnsureValid(
513 Ctx
, ModelUnderTraining
, DecisionName
, TrainingInputFeatures
);
515 Ctx
.emitError("Regalloc: could not set up the model runner");
518 if (TrainingLog
.empty())
521 auto OS
= std::make_unique
<raw_fd_ostream
>(TrainingLog
, EC
);
523 M
.getContext().emitError(EC
.message() + ":" + TrainingLog
);
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
);
534 Log
= std::make_unique
<Logger
>(std::move(OS
), LFS
, Reward
,
535 /*IncludeReward*/ true);
539 std::unique_ptr
<RegAllocEvictionAdvisor
>
540 getAdvisor(const MachineFunction
&MF
, const RAGreedy
&RA
) override
{
544 Log
->switchContext(MF
.getName());
545 return std::make_unique
<DevelopmentModeEvictAdvisor
>(
546 MF
, RA
, Runner
.get(), getAnalysis
<MachineBlockFrequencyInfo
>(),
547 getAnalysis
<MachineLoopInfo
>(), Log
.get());
550 std::unique_ptr
<MLModelRunner
> Runner
;
551 std::unique_ptr
<Logger
> Log
;
554 #endif //#ifdef LLVM_HAVE_TFLITE
557 float MLEvictAdvisor::getInitialQueueSize(const MachineFunction
&MF
) {
558 auto &MRI
= MF
.getRegInfo();
560 for (unsigned I
= 0, E
= MRI
.getNumVirtRegs(); I
!= E
; ++I
) {
561 Register Reg
= Register::index2VirtReg(I
);
562 if (MRI
.reg_nodbg_empty(Reg
))
569 MLEvictAdvisor::MLEvictAdvisor(const MachineFunction
&MF
, const RAGreedy
&RA
,
570 MLModelRunner
*Runner
,
571 const MachineBlockFrequencyInfo
&MBFI
,
572 const MachineLoopInfo
&Loops
)
573 : RegAllocEvictionAdvisor(MF
, RA
), DefaultAdvisor(MF
, RA
),
574 Runner(std::move(Runner
)), MBFI(MBFI
), Loops(Loops
),
575 InitialQSize(MLEvictAdvisor::getInitialQueueSize(MF
)) {
576 assert(this->Runner
);
577 Runner
->switchContext(MF
.getName());
578 DoNotNormalize
.set(FeatureIDs::mask
);
579 DoNotNormalize
.set(FeatureIDs::is_free
);
580 DoNotNormalize
.set(FeatureIDs::is_hint
);
581 DoNotNormalize
.set(FeatureIDs::is_local
);
582 DoNotNormalize
.set(FeatureIDs::min_stage
);
583 DoNotNormalize
.set(FeatureIDs::max_stage
);
584 DoNotNormalize
.set(FeatureIDs::progress
);
587 int64_t MLEvictAdvisor::tryFindEvictionCandidatePosition(
588 const LiveInterval
&, const AllocationOrder
&, unsigned, uint8_t,
589 const SmallVirtRegSet
&) const {
590 int64_t Ret
= Runner
->evaluate
<int64_t>();
592 assert(Ret
<= CandidateVirtRegPos
);
596 bool MLEvictAdvisor::loadInterferenceFeatures(
597 const LiveInterval
&VirtReg
, MCRegister PhysReg
, bool IsHint
,
598 const SmallVirtRegSet
&FixedRegisters
,
599 llvm::SmallVectorImpl
<float> &Largest
, size_t Pos
,
600 llvm::SmallVectorImpl
<LRStartEndInfo
> &LRPosInfo
) const {
601 // It is only possible to evict virtual register interference.
602 if (Matrix
->checkInterference(VirtReg
, PhysReg
) > LiveRegMatrix::IK_VirtReg
) {
607 const bool IsLocal
= LIS
->intervalIsInOneMBB(VirtReg
);
608 int64_t LocalIntfs
= 0;
609 float NrUrgent
= 0.0f
;
611 // The cascade tracking is the same as in the default advisor
612 unsigned Cascade
= RA
.getExtraInfo().getCascadeOrCurrentNext(VirtReg
.reg());
614 SmallVector
<const LiveInterval
*, MaxInterferences
> InterferingIntervals
;
615 for (MCRegUnit Unit
: TRI
->regunits(PhysReg
)) {
616 LiveIntervalUnion::Query
&Q
= Matrix
->query(VirtReg
, Unit
);
617 // Different from the default heuristic, we don't make any assumptions
618 // about what having more than 10 results in the query may mean.
619 const auto &IFIntervals
= Q
.interferingVRegs(EvictInterferenceCutoff
);
620 if (IFIntervals
.empty() && InterferingIntervals
.empty())
622 if (IFIntervals
.size() >= EvictInterferenceCutoff
)
624 InterferingIntervals
.append(IFIntervals
.begin(), IFIntervals
.end());
625 for (const LiveInterval
*Intf
: reverse(IFIntervals
)) {
626 assert(Intf
->reg().isVirtual() &&
627 "Only expecting virtual register interference from query");
628 // This is the same set of legality checks as in the default case: don't
629 // try to evict fixed regs or 'done' ones. Also don't break cascades,
630 // except in the urgent case, with the same nuances used in the default
632 // We could try sharing this between the advisors, but it may end up
633 // more complex than it is right now.
634 if (FixedRegisters
.count(Intf
->reg()))
636 if (RA
.getExtraInfo().getStage(*Intf
) == RS_Done
)
639 !VirtReg
.isSpillable() &&
640 (Intf
->isSpillable() ||
641 RegClassInfo
.getNumAllocatableRegs(MRI
->getRegClass(VirtReg
.reg())) <
642 RegClassInfo
.getNumAllocatableRegs(
643 MRI
->getRegClass(Intf
->reg())));
644 // Only evict older cascades or live ranges without a cascade.
645 unsigned IntfCascade
= RA
.getExtraInfo().getCascade(Intf
->reg());
646 if (Cascade
<= IntfCascade
) {
652 LocalIntfs
+= (IsLocal
&& LIS
->intervalIsInOneMBB(*Intf
) &&
653 (!EnableLocalReassign
|| !canReassign(*Intf
, PhysReg
)));
656 // OK, so if we made it this far, this LR is an eviction candidate, load its
658 extractFeatures(InterferingIntervals
, Largest
, Pos
, IsHint
, LocalIntfs
,
659 NrUrgent
, LRPosInfo
);
663 MCRegister
MLEvictAdvisor::tryFindEvictionCandidate(
664 const LiveInterval
&VirtReg
, const AllocationOrder
&Order
,
665 uint8_t CostPerUseLimit
, const SmallVirtRegSet
&FixedRegisters
) const {
666 auto MaybeOrderLimit
= getOrderLimit(VirtReg
, Order
, CostPerUseLimit
);
667 if (!MaybeOrderLimit
)
668 return MCRegister::NoRegister
;
669 unsigned OrderLimit
= *MaybeOrderLimit
;
671 // The heuristic sets initial costs such as, if CostPerUseLimit is
672 // max<uint8_t>, then any of the costs of the legally-evictable intervals
673 // would be lower. When that happens, one of those will be selected.
674 // Therefore, we allow the candidate be selected, unless the candidate is
675 // unspillable, in which case it would be incorrect to not find a register
677 const bool MustFindEviction
=
678 (!VirtReg
.isSpillable() && CostPerUseLimit
== static_cast<uint8_t>(~0u));
679 // Number of available candidates - if 0, no need to continue.
680 size_t Available
= 0;
681 // Make sure we don't have leftover partial state from an attempt where we
682 // had no available candidates and bailed out early.
683 resetInputs(*Runner
);
685 // Track the index->register mapping because AllocationOrder doesn't do that
686 // and we'd have to scan it.
687 // Also track their mask, to write asserts/debug.
688 CandidateRegList Regs
;
689 Regs
.fill({0, false});
691 // Track the largest value of features seen during this eviction session. We
692 // only normalize (some of) the float features, but it's just simpler to
693 // dimension 'Largest' to all the features, especially since we have the
694 // 'DoNotNormalize' list.
695 FeaturesListNormalizer
Largest(FeatureIDs::FeatureCount
, 0.0);
697 // Same overal idea as in the default eviction policy - we visit the values
698 // of AllocationOrder one at a time. If it's not legally available, we mask
699 // off the corresponding feature column (==do nothing because we already
700 // reset all the features to 0) Use Pos to capture the column we load
701 // features at - in AllocationOrder order.
703 SmallVector
<LRStartEndInfo
, NumberOfInterferences
> LRPosInfo
;
704 for (auto I
= Order
.begin(), E
= Order
.getOrderLimitEnd(OrderLimit
); I
!= E
;
706 MCRegister PhysReg
= *I
;
707 assert(!Regs
[Pos
].second
);
709 if (!canAllocatePhysReg(CostPerUseLimit
, PhysReg
)) {
712 if (loadInterferenceFeatures(VirtReg
, PhysReg
, I
.isHint(), FixedRegisters
,
713 Largest
, Pos
, LRPosInfo
)) {
715 Regs
[Pos
] = std::make_pair(PhysReg
, true);
718 if (Available
== 0) {
719 // Nothing to decide, nothing to learn.
720 assert(!MustFindEviction
);
721 return MCRegister::NoRegister
;
723 const size_t ValidPosLimit
= Pos
;
724 // If we must find eviction, the candidate should be masked out of the
725 // decision making process.
726 Regs
[CandidateVirtRegPos
].second
= !MustFindEviction
;
727 if (!MustFindEviction
)
728 extractFeatures(SmallVector
<const LiveInterval
*, 1>(1, &VirtReg
), Largest
,
729 CandidateVirtRegPos
, /*IsHint*/ 0,
730 /*LocalIntfsCount*/ 0,
731 /*NrUrgent*/ 0.0, LRPosInfo
);
732 assert(InitialQSize
> 0.0 && "We couldn't have gotten here if we had "
733 "nothing to allocate initially.");
734 #ifdef LLVM_HAVE_TFLITE
735 if (EnableDevelopmentFeatures
) {
736 extractInstructionFeatures(
738 [this](SlotIndex InputIndex
) -> int {
739 auto *CurrentMachineInstruction
=
740 LIS
->getInstructionFromIndex(InputIndex
);
741 if (!CurrentMachineInstruction
) {
744 return CurrentMachineInstruction
->getOpcode();
746 [this](SlotIndex InputIndex
) -> float {
747 auto *CurrentMachineInstruction
=
748 LIS
->getInstructionFromIndex(InputIndex
);
749 return MBFI
.getBlockFreqRelativeToEntryBlock(
750 CurrentMachineInstruction
->getParent());
752 [this](SlotIndex InputIndex
) -> MachineBasicBlock
* {
753 auto *CurrentMachineInstruction
=
754 LIS
->getInstructionFromIndex(InputIndex
);
755 return CurrentMachineInstruction
->getParent();
757 FeatureIDs::instructions
, FeatureIDs::instructions_mapping
,
758 FeatureIDs::mbb_frequencies
, FeatureIDs::mbb_mapping
,
759 LIS
->getSlotIndexes()->getLastIndex());
761 #endif // #ifdef LLVM_HAVE_TFLITE
762 // Normalize the features.
763 for (auto &V
: Largest
)
765 for (size_t FeatureIndex
= 0; FeatureIndex
< FeatureIDs::FeatureCount
;
767 if (DoNotNormalize
.test(FeatureIndex
))
769 for (size_t Pos
= 0; Pos
< NumberOfInterferences
; ++Pos
) {
770 Runner
->getTensor
<float>(FeatureIndex
)[Pos
] /= Largest
[FeatureIndex
];
773 *Runner
->getTensor
<float>(FeatureIDs::progress
) =
774 static_cast<float>(RA
.getQueueSize()) / InitialQSize
;
777 size_t CandidatePos
= tryFindEvictionCandidatePosition(
778 VirtReg
, Order
, OrderLimit
, CostPerUseLimit
, FixedRegisters
);
779 // The contract with the ML side is that CandidatePos is mask == 1 (i.e.
780 // Regs[CandidatePos].second)
781 assert(Regs
[CandidatePos
].second
);
782 if (CandidatePos
== CandidateVirtRegPos
) {
783 assert(!MustFindEviction
);
784 return MCRegister::NoRegister
;
786 assert(CandidatePos
< ValidPosLimit
);
788 return Regs
[CandidatePos
].first
;
791 const LIFeatureComponents
&
792 MLEvictAdvisor::getLIFeatureComponents(const LiveInterval
&LI
) const {
793 RegID ID
= LI
.reg().id();
794 LIFeatureComponents Empty
;
795 auto I
= CachedFeatures
.insert(std::make_pair(ID
, Empty
));
796 LIFeatureComponents
&Ret
= I
.first
->getSecond();
800 SmallPtrSet
<MachineInstr
*, 8> Visited
;
801 const TargetRegisterInfo
&TRI
= *MF
.getSubtarget().getRegisterInfo();
803 for (MachineRegisterInfo::reg_instr_nodbg_iterator
804 I
= MRI
->reg_instr_nodbg_begin(LI
.reg()),
805 E
= MRI
->reg_instr_nodbg_end();
807 MachineInstr
*MI
= &*(I
++);
810 if (!Visited
.insert(MI
).second
)
813 if (MI
->isIdentityCopy() || MI
->isImplicitDef())
817 std::tie(Reads
, Writes
) = MI
->readsWritesVirtualRegister(LI
.reg());
819 float Freq
= MBFI
.getBlockFreqRelativeToEntryBlock(MI
->getParent());
820 Ret
.HottestBlockFreq
= std::max(Freq
, Ret
.HottestBlockFreq
);
822 Ret
.R
+= (Reads
&& !Writes
) * Freq
;
823 Ret
.W
+= (!Reads
&& Writes
) * Freq
;
824 Ret
.RW
+= (Reads
&& Writes
) * Freq
;
826 auto *MBB
= MI
->getParent();
827 auto *Loop
= Loops
.getLoopFor(MBB
);
828 bool IsExiting
= Loop
? Loop
->isLoopExiting(MBB
) : false;
830 if (Writes
&& IsExiting
&& LIS
->isLiveOutOfMBB(LI
, MBB
))
831 Ret
.IndVarUpdates
+= Freq
;
833 if (MI
->isCopy() && VirtRegAuxInfo::copyHint(MI
, LI
.reg(), TRI
, *MRI
))
834 Ret
.HintWeights
+= Freq
;
836 Ret
.IsRemat
= VirtRegAuxInfo::isRematerializable(
837 LI
, *LIS
, *VRM
, *MF
.getSubtarget().getInstrInfo());
841 // Overall, this currently mimics what we do for weight calculation, but instead
842 // of accummulating the various features, we keep them separate.
843 void MLEvictAdvisor::extractFeatures(
844 const SmallVectorImpl
<const LiveInterval
*> &Intervals
,
845 llvm::SmallVectorImpl
<float> &Largest
, size_t Pos
, int64_t IsHint
,
846 int64_t LocalIntfsCount
, float NrUrgent
,
847 SmallVectorImpl
<LRStartEndInfo
> &LRPosInfo
) const {
848 int64_t NrDefsAndUses
= 0;
849 int64_t NrBrokenHints
= 0;
853 double IndVarUpdates
= 0.0;
854 double HintWeights
= 0.0;
855 float StartBBFreq
= 0.0;
856 float EndBBFreq
= 0.0;
857 float HottestBlockFreq
= 0.0;
858 int32_t NrRematerializable
= 0;
859 float TotalWeight
= 0.0;
861 SlotIndex EndSI
= LIS
->getSlotIndexes()->getZeroIndex();
862 SlotIndex StartSI
= LIS
->getSlotIndexes()->getLastIndex();
863 int64_t MaxStage
= 0;
865 Intervals
.empty() ? 0 : std::numeric_limits
<int64_t>::max();
867 for (const auto *L
: Intervals
) {
868 const LiveInterval
&LI
= *L
;
869 MaxStage
= std::max
<int64_t>(
870 MaxStage
, static_cast<int64_t>(RA
.getExtraInfo().getStage(LI
)));
871 MinStage
= std::min
<int64_t>(
872 MinStage
, static_cast<int64_t>(RA
.getExtraInfo().getStage(LI
)));
874 TotalWeight
= std::max(TotalWeight
, LI
.weight());
876 if (LI
.beginIndex() < StartSI
)
877 StartSI
= LI
.beginIndex();
879 if (LI
.endIndex() > EndSI
)
880 EndSI
= LI
.endIndex();
881 const LIFeatureComponents
&LIFC
= getLIFeatureComponents(LI
);
882 NrBrokenHints
+= VRM
->hasPreferredPhys(LI
.reg());
884 NrDefsAndUses
+= LIFC
.NrDefsAndUses
;
885 HottestBlockFreq
= std::max(HottestBlockFreq
, LIFC
.HottestBlockFreq
);
890 IndVarUpdates
+= LIFC
.IndVarUpdates
;
892 HintWeights
+= LIFC
.HintWeights
;
893 NrRematerializable
+= LIFC
.IsRemat
;
895 if (EnableDevelopmentFeatures
) {
896 for (auto CurrentSegment
: LI
) {
898 LRStartEndInfo
{CurrentSegment
.start
, CurrentSegment
.end
, Pos
});
903 if (!Intervals
.empty()) {
905 MBFI
.getBlockFreqRelativeToEntryBlock(LIS
->getMBBFromIndex(StartSI
));
906 if (EndSI
>= LIS
->getSlotIndexes()->getLastIndex())
907 EndSI
= LIS
->getSlotIndexes()->getLastIndex().getPrevIndex();
909 MBFI
.getBlockFreqRelativeToEntryBlock(LIS
->getMBBFromIndex(EndSI
));
910 Size
= StartSI
.distance(EndSI
);
912 // Set the features at the column 'Pos'.
913 #define SET(ID, TYPE, VAL) \
915 Runner->getTensor<TYPE>(FeatureIDs::ID)[Pos] = static_cast<TYPE>(VAL); \
916 if (!DoNotNormalize.test(FeatureIDs::ID)) \
917 Largest[FeatureIDs::ID] = \
918 std::max(Largest[FeatureIDs::ID], static_cast<float>(VAL)); \
920 SET(mask
, int64_t, 1);
921 SET(is_free
, int64_t, Intervals
.empty());
922 SET(nr_urgent
, float, NrUrgent
);
923 SET(nr_broken_hints
, float, NrBrokenHints
);
924 SET(is_hint
, int64_t, IsHint
);
925 SET(is_local
, int64_t, LocalIntfsCount
);
926 SET(nr_rematerializable
, float, NrRematerializable
);
927 SET(nr_defs_and_uses
, float, NrDefsAndUses
);
928 SET(weighed_reads_by_max
, float, R
);
929 SET(weighed_writes_by_max
, float, W
);
930 SET(weighed_read_writes_by_max
, float, RW
);
931 SET(weighed_indvars_by_max
, float, IndVarUpdates
);
932 SET(hint_weights_by_max
, float, HintWeights
);
933 SET(start_bb_freq_by_max
, float, StartBBFreq
);
934 SET(end_bb_freq_by_max
, float, EndBBFreq
);
935 SET(hottest_bb_freq_by_max
, float, HottestBlockFreq
);
936 SET(liverange_size
, float, Size
);
937 SET(use_def_density
, float, TotalWeight
);
938 SET(max_stage
, int64_t, MaxStage
);
939 SET(min_stage
, int64_t, MinStage
);
943 void extractInstructionFeatures(
944 SmallVectorImpl
<LRStartEndInfo
> &LRPosInfo
, MLModelRunner
*RegallocRunner
,
945 function_ref
<int(SlotIndex
)> GetOpcode
,
946 function_ref
<float(SlotIndex
)> GetMBBFreq
,
947 function_ref
<MachineBasicBlock
*(SlotIndex
)> GetMBBReference
,
948 const int InstructionsIndex
, const int InstructionsMappingIndex
,
949 const int MBBFreqIndex
, const int MBBMappingIndex
,
950 const SlotIndex LastIndex
) {
951 // This function extracts instruction based features relevant to the eviction
952 // problem currently being solved. This function ends up extracting two
954 // 1 - A vector of size max instruction count. It contains the opcodes of the
955 // instructions spanned by all the intervals in the current instance of the
957 // 2 - A binary mapping matrix of size (LR count * max
958 // instruction count) which maps where the LRs are live to the actual opcodes
959 // for which they are live.
960 // 3 - A vector of size max supported MBB count storing MBB frequencies,
961 // encompassing all of the MBBs covered by the eviction problem.
962 // 4 - A vector of size max instruction count of indices to members of the MBB
963 // frequency vector, mapping each instruction to its associated MBB.
965 // Start off by sorting the segments based on the beginning slot index.
967 LRPosInfo
.begin(), LRPosInfo
.end(),
968 [](LRStartEndInfo A
, LRStartEndInfo B
) { return A
.Begin
< B
.Begin
; });
969 size_t InstructionIndex
= 0;
970 size_t CurrentSegmentIndex
= 0;
971 SlotIndex CurrentIndex
= LRPosInfo
[0].Begin
;
972 std::map
<MachineBasicBlock
*, size_t> VisitedMBBs
;
973 size_t CurrentMBBIndex
= 0;
974 // This loop processes all the segments sequentially by starting at the
975 // beginning slot index of the first segment, iterating through all the slot
976 // indices before the end slot index of that segment (while checking for
977 // overlaps with segments that start at greater slot indices). After hitting
978 // that end index, the current segment being processed gets bumped until they
979 // are all processed or the max instruction count is hit, where everything is
982 // If the index that we are currently at is within the current segment and
983 // we haven't hit the max instruction count, continue processing the current
985 while (CurrentIndex
<= LRPosInfo
[CurrentSegmentIndex
].End
&&
986 InstructionIndex
< ModelMaxSupportedInstructionCount
) {
987 int CurrentOpcode
= GetOpcode(CurrentIndex
);
988 // If the current machine instruction is null, skip it
989 if (CurrentOpcode
== -1) {
990 // If we're currently at the last index in the SlotIndex analysis,
991 // we can't go any further, so return from the function
992 if (CurrentIndex
>= LastIndex
) {
995 CurrentIndex
= CurrentIndex
.getNextIndex();
998 MachineBasicBlock
*CurrentMBBReference
= GetMBBReference(CurrentIndex
);
999 if (VisitedMBBs
.count(CurrentMBBReference
) == 0) {
1000 VisitedMBBs
[CurrentMBBReference
] = CurrentMBBIndex
;
1003 extractMBBFrequency(CurrentIndex
, InstructionIndex
, VisitedMBBs
,
1004 GetMBBFreq
, CurrentMBBReference
, RegallocRunner
,
1005 MBBFreqIndex
, MBBMappingIndex
);
1006 // Current code assumes we're not going to get any disjointed segments
1007 assert(LRPosInfo
[CurrentSegmentIndex
].Begin
<= CurrentIndex
);
1008 RegallocRunner
->getTensor
<int64_t>(InstructionsIndex
)[InstructionIndex
] =
1009 CurrentOpcode
< OpcodeValueCutoff
? CurrentOpcode
: 0;
1010 // set value in the binary mapping matrix for the current instruction
1011 auto CurrentSegmentPosition
= LRPosInfo
[CurrentSegmentIndex
].Pos
;
1012 RegallocRunner
->getTensor
<int64_t>(
1013 InstructionsMappingIndex
)[CurrentSegmentPosition
*
1014 ModelMaxSupportedInstructionCount
+
1015 InstructionIndex
] = 1;
1016 // All of the segments are sorted based on the beginning slot index, but
1017 // this doesn't mean that the beginning slot index of the next segment is
1018 // after the end segment of the one being currently processed. This while
1019 // loop checks for overlapping segments and modifies the portion of the
1020 // column in the mapping matrix for the currently processed instruction
1021 // for the LR it is checking. Also make sure that the beginning of the
1022 // current segment we're checking for overlap in is less than the current
1023 // index, otherwise we're done checking overlaps.
1024 size_t OverlapCheckCurrentSegment
= CurrentSegmentIndex
+ 1;
1025 while (OverlapCheckCurrentSegment
< LRPosInfo
.size() &&
1026 LRPosInfo
[OverlapCheckCurrentSegment
].Begin
<= CurrentIndex
) {
1027 auto OverlapCurrentSegmentPosition
=
1028 LRPosInfo
[OverlapCheckCurrentSegment
].Pos
;
1029 if (LRPosInfo
[OverlapCheckCurrentSegment
].End
>= CurrentIndex
) {
1030 RegallocRunner
->getTensor
<int64_t>(
1031 InstructionsMappingIndex
)[OverlapCurrentSegmentPosition
*
1032 ModelMaxSupportedInstructionCount
+
1033 InstructionIndex
] = 1;
1035 ++OverlapCheckCurrentSegment
;
1038 if (CurrentIndex
>= LastIndex
) {
1041 CurrentIndex
= CurrentIndex
.getNextIndex();
1043 // if we've just finished processing through the last segment or if we've
1044 // hit the maximum number of instructions, break out of the loop.
1045 if (CurrentSegmentIndex
== LRPosInfo
.size() - 1 ||
1046 InstructionIndex
>= ModelMaxSupportedInstructionCount
) {
1049 // If the segments are not overlapping, we need to move to the beginning
1050 // index of the next segment to avoid having instructions not attached to
1052 if (LRPosInfo
[CurrentSegmentIndex
+ 1].Begin
>
1053 LRPosInfo
[CurrentSegmentIndex
].End
) {
1054 CurrentIndex
= LRPosInfo
[CurrentSegmentIndex
+ 1].Begin
;
1056 ++CurrentSegmentIndex
;
1060 void extractMBBFrequency(const SlotIndex CurrentIndex
,
1061 const size_t CurrentInstructionIndex
,
1062 std::map
<MachineBasicBlock
*, size_t> &VisitedMBBs
,
1063 function_ref
<float(SlotIndex
)> GetMBBFreq
,
1064 MachineBasicBlock
*CurrentMBBReference
,
1065 MLModelRunner
*RegallocRunner
, const int MBBFreqIndex
,
1066 const int MBBMappingIndex
) {
1067 size_t CurrentMBBIndex
= VisitedMBBs
[CurrentMBBReference
];
1068 float CurrentMBBFreq
= GetMBBFreq(CurrentIndex
);
1069 if (CurrentMBBIndex
< ModelMaxSupportedMBBCount
) {
1070 RegallocRunner
->getTensor
<float>(MBBFreqIndex
)[CurrentMBBIndex
] =
1072 RegallocRunner
->getTensor
<int64_t>(
1073 MBBMappingIndex
)[CurrentInstructionIndex
] = CurrentMBBIndex
;
1077 // Development mode-specific implementations
1078 #ifdef LLVM_HAVE_TFLITE
1080 RegAllocEvictionAdvisorAnalysis
*llvm::createDevelopmentModeAdvisor() {
1081 return new DevelopmentModeEvictionAdvisorAnalysis();
1084 int64_t DevelopmentModeEvictAdvisor::tryFindEvictionCandidatePosition(
1085 const LiveInterval
&VirtReg
, const AllocationOrder
&Order
,
1086 unsigned OrderLimit
, uint8_t CostPerUseLimit
,
1087 const SmallVirtRegSet
&FixedRegisters
) const {
1089 if (isa
<ModelUnderTrainingRunner
>(getRunner())) {
1090 Ret
= MLEvictAdvisor::tryFindEvictionCandidatePosition(
1091 VirtReg
, Order
, OrderLimit
, CostPerUseLimit
, FixedRegisters
);
1093 MCRegister PhysReg
= getDefaultAdvisor().tryFindEvictionCandidate(
1094 VirtReg
, Order
, CostPerUseLimit
, FixedRegisters
);
1095 // Find the index of the selected PhysReg. We need it for logging,
1096 // otherwise this is wasted cycles (but so would starting development mode
1097 // without a model nor logging)
1099 Ret
= CandidateVirtRegPos
;
1101 for (auto I
= Order
.begin(), E
= Order
.getOrderLimitEnd(OrderLimit
);
1106 if (TrainingLog
.empty())
1108 // TODO(mtrofin): when we support optional rewards, this can go away. In the
1109 // meantime, we log the "pretend" reward (0) for the previous observation
1110 // before starting a new one.
1111 if (Log
->hasObservationInProgress())
1112 Log
->logReward
<float>(0.0);
1114 Log
->startObservation();
1115 size_t CurrentFeature
= 0;
1116 size_t FeatureCount
= EnableDevelopmentFeatures
1117 ? FeatureIDs::FeaturesWithDevelopmentCount
1118 : FeatureIDs::FeatureCount
;
1119 for (; CurrentFeature
< FeatureCount
; ++CurrentFeature
) {
1120 Log
->logTensorValue(CurrentFeature
,
1121 reinterpret_cast<const char *>(
1122 getRunner().getTensorUntyped(CurrentFeature
)));
1124 if (auto *MUTR
= dyn_cast
<ModelUnderTrainingRunner
>(&getRunner()))
1125 for (size_t I
= 0; I
< MUTR
->extraOutputsForLoggingSpecs().size();
1126 ++I
, ++CurrentFeature
)
1127 Log
->logTensorValue(
1129 reinterpret_cast<const char *>(MUTR
->getUntypedExtraOutputValue(I
)));
1130 // The output is right after the features and the extra outputs
1131 Log
->logTensorValue(CurrentFeature
, reinterpret_cast<const char *>(&Ret
));
1132 Log
->endObservation();
1136 bool RegAllocScoring::runOnMachineFunction(MachineFunction
&MF
) {
1137 std::optional
<float> CachedReward
;
1138 auto GetReward
= [&]() {
1140 CachedReward
= static_cast<float>(
1141 calculateRegAllocScore(MF
, getAnalysis
<MachineBlockFrequencyInfo
>())
1143 return *CachedReward
;
1146 getAnalysis
<RegAllocEvictionAdvisorAnalysis
>().logRewardIfNeeded(MF
,
1148 getAnalysis
<RegAllocPriorityAdvisorAnalysis
>().logRewardIfNeeded(MF
,
1152 #endif // #ifdef LLVM_HAVE_TFLITE
1154 RegAllocEvictionAdvisorAnalysis
*llvm::createReleaseModeAdvisor() {
1155 return llvm::isEmbeddedModelEvaluatorValid
<CompiledModelType
>() ||
1156 !InteractiveChannelBaseName
.empty()
1157 ? new ReleaseModeEvictionAdvisorAnalysis()
1161 // In all cases except development mode, we don't need scoring.
1162 #if !defined(LLVM_HAVE_TFLITE)
1163 bool RegAllocScoring::runOnMachineFunction(MachineFunction
&) { return false; }