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