xref: /freebsd-src/contrib/llvm-project/llvm/lib/CodeGen/MLRegAllocEvictAdvisor.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
15f757f3fSDimitry Andric //===- MLRegAllocEvictAdvisor.cpp - ML eviction advisor -------------------===//
25f757f3fSDimitry Andric //
35f757f3fSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
45f757f3fSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
55f757f3fSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
65f757f3fSDimitry Andric //
75f757f3fSDimitry Andric //===----------------------------------------------------------------------===//
85f757f3fSDimitry Andric //
95f757f3fSDimitry Andric // Implementation of the ML eviction advisor and reward injection pass
105f757f3fSDimitry Andric //
115f757f3fSDimitry Andric //===----------------------------------------------------------------------===//
125f757f3fSDimitry Andric 
135f757f3fSDimitry Andric #include "AllocationOrder.h"
145f757f3fSDimitry Andric #include "RegAllocEvictionAdvisor.h"
155f757f3fSDimitry Andric #include "RegAllocGreedy.h"
165f757f3fSDimitry Andric #include "llvm/Analysis/InteractiveModelRunner.h"
175f757f3fSDimitry Andric #include "llvm/Analysis/MLModelRunner.h"
185f757f3fSDimitry Andric #include "llvm/Analysis/TensorSpec.h"
195f757f3fSDimitry Andric #if defined(LLVM_HAVE_TF_AOT_REGALLOCEVICTMODEL) || defined(LLVM_HAVE_TFLITE)
205f757f3fSDimitry Andric #include "llvm/Analysis/ModelUnderTrainingRunner.h"
215f757f3fSDimitry Andric #include "llvm/Analysis/NoInferenceModelRunner.h"
225f757f3fSDimitry Andric #include "llvm/Analysis/Utils/TrainingLogger.h"
235f757f3fSDimitry Andric #endif
245f757f3fSDimitry Andric #include "MLRegAllocEvictAdvisor.h"
255f757f3fSDimitry Andric #include "llvm/Analysis/ReleaseModeModelRunner.h"
265f757f3fSDimitry Andric #include "llvm/CodeGen/CalcSpillWeights.h"
275f757f3fSDimitry Andric #include "llvm/CodeGen/LiveRegMatrix.h"
285f757f3fSDimitry Andric #include "llvm/CodeGen/MachineBlockFrequencyInfo.h"
295f757f3fSDimitry Andric #include "llvm/CodeGen/MachineFunction.h"
305f757f3fSDimitry Andric #include "llvm/CodeGen/MachineLoopInfo.h"
315f757f3fSDimitry Andric #include "llvm/CodeGen/MachineRegisterInfo.h"
325f757f3fSDimitry Andric #include "llvm/CodeGen/Passes.h"
335f757f3fSDimitry Andric #include "llvm/CodeGen/RegisterClassInfo.h"
345f757f3fSDimitry Andric #include "llvm/CodeGen/VirtRegMap.h"
35*0fca6ea1SDimitry Andric #include "llvm/IR/Module.h"
365f757f3fSDimitry Andric #include "llvm/InitializePasses.h"
375f757f3fSDimitry Andric #include "llvm/Pass.h"
385f757f3fSDimitry Andric #include "llvm/PassRegistry.h"
395f757f3fSDimitry Andric #include "llvm/Support/CommandLine.h"
405f757f3fSDimitry Andric #include "llvm/Support/ErrorHandling.h"
415f757f3fSDimitry Andric 
425f757f3fSDimitry Andric #include <array>
435f757f3fSDimitry Andric #include <bitset>
445f757f3fSDimitry Andric #include <memory>
455f757f3fSDimitry Andric 
465f757f3fSDimitry Andric using namespace llvm;
475f757f3fSDimitry Andric 
485f757f3fSDimitry Andric #define DEBUG_TYPE "ml-regalloc"
495f757f3fSDimitry Andric 
505f757f3fSDimitry Andric // Generated header in release (AOT) mode
515f757f3fSDimitry Andric #if defined(LLVM_HAVE_TF_AOT_REGALLOCEVICTMODEL)
525f757f3fSDimitry Andric #include "RegAllocEvictModel.h"
535f757f3fSDimitry Andric using CompiledModelType = RegAllocEvictModel;
545f757f3fSDimitry Andric #else
555f757f3fSDimitry Andric using CompiledModelType = NoopSavedModelImpl;
565f757f3fSDimitry Andric #endif
575f757f3fSDimitry Andric 
585f757f3fSDimitry Andric static cl::opt<std::string> InteractiveChannelBaseName(
595f757f3fSDimitry Andric     "regalloc-evict-interactive-channel-base", cl::Hidden,
605f757f3fSDimitry Andric     cl::desc(
615f757f3fSDimitry Andric         "Base file path for the interactive mode. The incoming filename should "
625f757f3fSDimitry Andric         "have the name <regalloc-evict-interactive-channel-base>.in, while the "
635f757f3fSDimitry Andric         "outgoing name should be "
645f757f3fSDimitry Andric         "<regalloc-evict-interactive-channel-base>.out"));
655f757f3fSDimitry Andric 
665f757f3fSDimitry Andric // Options that only make sense in development mode
675f757f3fSDimitry Andric #ifdef LLVM_HAVE_TFLITE
685f757f3fSDimitry Andric #include "RegAllocScore.h"
695f757f3fSDimitry Andric #include "llvm/Analysis/Utils/TFUtils.h"
705f757f3fSDimitry Andric 
715f757f3fSDimitry Andric static cl::opt<std::string> TrainingLog(
725f757f3fSDimitry Andric     "regalloc-training-log", cl::Hidden,
735f757f3fSDimitry Andric     cl::desc("Training log for the register allocator eviction model"));
745f757f3fSDimitry Andric 
755f757f3fSDimitry Andric static cl::opt<std::string> ModelUnderTraining(
765f757f3fSDimitry Andric     "regalloc-model", cl::Hidden,
775f757f3fSDimitry Andric     cl::desc("The model being trained for register allocation eviction"));
785f757f3fSDimitry Andric 
795f757f3fSDimitry Andric static cl::opt<bool> EnableDevelopmentFeatures(
805f757f3fSDimitry Andric     "regalloc-enable-development-features", cl::Hidden,
815f757f3fSDimitry Andric     cl::desc("Whether or not to enable features under development for the ML "
825f757f3fSDimitry Andric              "regalloc advisor"));
835f757f3fSDimitry Andric 
845f757f3fSDimitry Andric #else
855f757f3fSDimitry Andric static const bool EnableDevelopmentFeatures = false;
865f757f3fSDimitry Andric #endif // #ifdef LLVM_HAVE_TFLITE
875f757f3fSDimitry Andric 
885f757f3fSDimitry Andric /// The score injection pass.
895f757f3fSDimitry Andric /// This pass calculates the score for a function and inserts it in the log, but
905f757f3fSDimitry Andric /// this happens only in development mode. It's a no-op otherwise.
915f757f3fSDimitry Andric namespace llvm {
925f757f3fSDimitry Andric extern cl::opt<unsigned> EvictInterferenceCutoff;
935f757f3fSDimitry Andric 
945f757f3fSDimitry Andric class RegAllocScoring : public MachineFunctionPass {
955f757f3fSDimitry Andric public:
965f757f3fSDimitry Andric   static char ID;
975f757f3fSDimitry Andric 
985f757f3fSDimitry Andric   RegAllocScoring() : MachineFunctionPass(ID) {
995f757f3fSDimitry Andric     initializeRegAllocScoringPass(*PassRegistry::getPassRegistry());
1005f757f3fSDimitry Andric   }
1015f757f3fSDimitry Andric 
1025f757f3fSDimitry Andric   ~RegAllocScoring() override = default;
1035f757f3fSDimitry Andric 
1045f757f3fSDimitry Andric   StringRef getPassName() const override {
1055f757f3fSDimitry Andric     return "Register Allocation Pass Scoring";
1065f757f3fSDimitry Andric   }
1075f757f3fSDimitry Andric 
1085f757f3fSDimitry Andric   /// RegAllocReward analysis usage.
1095f757f3fSDimitry Andric   void getAnalysisUsage(AnalysisUsage &AU) const override {
1105f757f3fSDimitry Andric     AU.setPreservesAll();
1115f757f3fSDimitry Andric     AU.addRequired<RegAllocEvictionAdvisorAnalysis>();
1125f757f3fSDimitry Andric     AU.addRequired<RegAllocPriorityAdvisorAnalysis>();
113*0fca6ea1SDimitry Andric     AU.addRequired<MachineBlockFrequencyInfoWrapperPass>();
1145f757f3fSDimitry Andric     MachineFunctionPass::getAnalysisUsage(AU);
1155f757f3fSDimitry Andric   }
1165f757f3fSDimitry Andric 
1175f757f3fSDimitry Andric   /// Performs this pass
1185f757f3fSDimitry Andric   bool runOnMachineFunction(MachineFunction &) override;
1195f757f3fSDimitry Andric };
1205f757f3fSDimitry Andric 
1215f757f3fSDimitry Andric char RegAllocScoring::ID = 0;
1225f757f3fSDimitry Andric FunctionPass *createRegAllocScoringPass() { return new RegAllocScoring(); }
1235f757f3fSDimitry Andric 
1245f757f3fSDimitry Andric } // namespace llvm
1255f757f3fSDimitry Andric 
1265f757f3fSDimitry Andric INITIALIZE_PASS(RegAllocScoring, "regallocscoringpass",
1275f757f3fSDimitry Andric                 "Register Allocation Scoring Pass", false, false)
1285f757f3fSDimitry Andric 
1295f757f3fSDimitry Andric // ===================================
1305f757f3fSDimitry Andric // Common ML Advisor declarations
1315f757f3fSDimitry Andric // ===================================
1325f757f3fSDimitry Andric namespace {
1335f757f3fSDimitry Andric // The model can only accept a specified number of opcodes and will error it if
1345f757f3fSDimitry Andric // fed an opcode it hasn't seen before. This constant sets the current cutoff.
1355f757f3fSDimitry Andric static const int OpcodeValueCutoff = 17716;
1365f757f3fSDimitry Andric 
1375f757f3fSDimitry Andric // Most features are as described above, so we'll reuse this vector in defining
1385f757f3fSDimitry Andric // them.
1395f757f3fSDimitry Andric static const std::vector<int64_t> PerLiveRangeShape{1, NumberOfInterferences};
1405f757f3fSDimitry Andric 
1415f757f3fSDimitry Andric // --------------
1425f757f3fSDimitry Andric // Features table
1435f757f3fSDimitry Andric // --------------
1445f757f3fSDimitry Andric // For each interfering live range (incl. the candidate) we collect a number of
1455f757f3fSDimitry Andric // features. However, because the features are of different types (and because
1465f757f3fSDimitry Andric // of ML best practices), we organize the tensors per feature, not per
1475f757f3fSDimitry Andric // candidate. Each such tensor has a scalar value corresponding to the
1485f757f3fSDimitry Andric // interferring live range at that position, in the order in AllocationOrder.
1495f757f3fSDimitry Andric // The last position corresponds to the virt reg seeking allocation.
1505f757f3fSDimitry Andric // Exception to all that is the progression feature, which is just a scalar (see
1515f757f3fSDimitry Andric // its documentation for details).
1525f757f3fSDimitry Andric // Note on naming: the "_by_max" are normalized using the largest value of that
1535f757f3fSDimitry Andric // tensor, as observed in the current decision making stage (i.e. for the
1545f757f3fSDimitry Andric // current call to the advisor's tryFindEvictionCandidate)
1555f757f3fSDimitry Andric //
1565f757f3fSDimitry Andric // The feature list format: type, name, shape, documentation.
1575f757f3fSDimitry Andric // Note: we can really just use int64 and float, hence the modeling of some
1585f757f3fSDimitry Andric // bools as int64 values.
1595f757f3fSDimitry Andric #define RA_EVICT_FEATURES_LIST(M)                                              \
1605f757f3fSDimitry Andric   M(int64_t, mask, PerLiveRangeShape,                                          \
1615f757f3fSDimitry Andric     "boolean values, 0 for unavailable candidates (i.e. if a position is 0, "  \
1625f757f3fSDimitry Andric     "it "                                                                      \
1635f757f3fSDimitry Andric     "can't be evicted)")                                                       \
1645f757f3fSDimitry Andric   M(int64_t, is_free, PerLiveRangeShape,                                       \
1655f757f3fSDimitry Andric     "boolean values, 1 if this phys reg is actually free (no interferences)")  \
1665f757f3fSDimitry Andric   M(float, nr_urgent, PerLiveRangeShape,                                       \
1675f757f3fSDimitry Andric     "number of 'urgent' intervals, normalized. Urgent are those that are OK "  \
1685f757f3fSDimitry Andric     "to break cascades")                                                       \
1695f757f3fSDimitry Andric   M(float, nr_broken_hints, PerLiveRangeShape,                                 \
1705f757f3fSDimitry Andric     "if this position were evicted, how many broken hints would there be")     \
1715f757f3fSDimitry Andric   M(int64_t, is_hint, PerLiveRangeShape,                                       \
1725f757f3fSDimitry Andric     "is this a preferred phys reg for the candidate")                          \
1735f757f3fSDimitry Andric   M(int64_t, is_local, PerLiveRangeShape,                                      \
1745f757f3fSDimitry Andric     "is this live range local to a basic block")                               \
1755f757f3fSDimitry Andric   M(float, nr_rematerializable, PerLiveRangeShape,                             \
1765f757f3fSDimitry Andric     "nr rematerializable ranges")                                              \
1775f757f3fSDimitry Andric   M(float, nr_defs_and_uses, PerLiveRangeShape,                                \
1785f757f3fSDimitry Andric     "bb freq - weighed nr defs and uses")                                      \
1795f757f3fSDimitry Andric   M(float, weighed_reads_by_max, PerLiveRangeShape,                            \
1805f757f3fSDimitry Andric     "bb freq - weighed nr of reads, normalized")                               \
1815f757f3fSDimitry Andric   M(float, weighed_writes_by_max, PerLiveRangeShape,                           \
1825f757f3fSDimitry Andric     "bb feq - weighed nr of writes, normalized")                               \
1835f757f3fSDimitry Andric   M(float, weighed_read_writes_by_max, PerLiveRangeShape,                      \
1845f757f3fSDimitry Andric     "bb freq - weighed nr of uses that are both read and writes, normalized")  \
1855f757f3fSDimitry Andric   M(float, weighed_indvars_by_max, PerLiveRangeShape,                          \
1865f757f3fSDimitry Andric     "bb freq - weighed nr of uses that are indvars, normalized")               \
1875f757f3fSDimitry Andric   M(float, hint_weights_by_max, PerLiveRangeShape,                             \
1885f757f3fSDimitry Andric     "bb freq - weighed nr of uses that are hints, normalized")                 \
1895f757f3fSDimitry Andric   M(float, start_bb_freq_by_max, PerLiveRangeShape,                            \
1905f757f3fSDimitry Andric     "the freq in the start block, normalized")                                 \
1915f757f3fSDimitry Andric   M(float, end_bb_freq_by_max, PerLiveRangeShape,                              \
1925f757f3fSDimitry Andric     "freq of end block, normalized")                                           \
1935f757f3fSDimitry Andric   M(float, hottest_bb_freq_by_max, PerLiveRangeShape,                          \
1945f757f3fSDimitry Andric     "hottest BB freq, normalized")                                             \
1955f757f3fSDimitry Andric   M(float, liverange_size, PerLiveRangeShape,                                  \
1965f757f3fSDimitry Andric     "size (instr index diff) of the LR")                                       \
1975f757f3fSDimitry Andric   M(float, use_def_density, PerLiveRangeShape,                                 \
1985f757f3fSDimitry Andric     "the max weight, as computed by the manual heuristic")                     \
1995f757f3fSDimitry Andric   M(int64_t, max_stage, PerLiveRangeShape,                                     \
2005f757f3fSDimitry Andric     "largest stage of an interval in this LR")                                 \
2015f757f3fSDimitry Andric   M(int64_t, min_stage, PerLiveRangeShape,                                     \
2025f757f3fSDimitry Andric     "lowest stage of an interval in this LR")                                  \
2035f757f3fSDimitry Andric   M(float, progress, {1}, "ratio of current queue size to initial size")
2045f757f3fSDimitry Andric 
2055f757f3fSDimitry Andric #ifdef LLVM_HAVE_TFLITE
2065f757f3fSDimitry Andric #define RA_EVICT_FIRST_DEVELOPMENT_FEATURE(M)                                  \
2075f757f3fSDimitry Andric   M(int64_t, instructions, InstructionsShape,                                  \
2085f757f3fSDimitry Andric     "Opcodes of the instructions covered by the eviction problem")
2095f757f3fSDimitry Andric 
2105f757f3fSDimitry Andric #define RA_EVICT_REST_DEVELOPMENT_FEATURES(M)                                  \
2115f757f3fSDimitry Andric   M(int64_t, instructions_mapping, InstructionsMappingShape,                   \
2125f757f3fSDimitry Andric     "A binary matrix mapping LRs to instruction opcodes")                      \
2135f757f3fSDimitry Andric   M(float, mbb_frequencies, MBBFrequencyShape,                                 \
2145f757f3fSDimitry Andric     "A vector of machine basic block frequencies")                             \
2155f757f3fSDimitry Andric   M(int64_t, mbb_mapping, InstructionsShape,                                   \
216*0fca6ea1SDimitry Andric     "A vector of indices mapping instructions to MBBs")
2175f757f3fSDimitry Andric #else
2185f757f3fSDimitry Andric #define RA_EVICT_FIRST_DEVELOPMENT_FEATURE(M)
2195f757f3fSDimitry Andric #define RA_EVICT_REST_DEVELOPMENT_FEATURES(M)
2205f757f3fSDimitry Andric #endif
2215f757f3fSDimitry Andric 
2225f757f3fSDimitry Andric // The model learns to pick one of the mask == 1 interferences. This is the
2235f757f3fSDimitry Andric // name of the output tensor. The contract with the model is that the output
2245f757f3fSDimitry Andric // will be guaranteed to be to a mask == 1 position. Using a macro here to
2255f757f3fSDimitry Andric // avoid 'not used' warnings (and keep cond compilation to a minimum)
2265f757f3fSDimitry Andric #define DecisionName "index_to_evict"
2275f757f3fSDimitry Andric static const TensorSpec DecisionSpec =
2285f757f3fSDimitry Andric     TensorSpec::createSpec<int64_t>(DecisionName, {1});
2295f757f3fSDimitry Andric 
2305f757f3fSDimitry Andric // Named features index.
2315f757f3fSDimitry Andric enum FeatureIDs {
2325f757f3fSDimitry Andric #define _FEATURE_IDX_SIMPLE(_, name, __, ___) name
2335f757f3fSDimitry Andric #define _FEATURE_IDX(A, B, C, D) _FEATURE_IDX_SIMPLE(A, B, C, D),
2345f757f3fSDimitry Andric   RA_EVICT_FEATURES_LIST(_FEATURE_IDX) FeatureCount,
2355f757f3fSDimitry Andric #ifdef LLVM_HAVE_TFLITE
2365f757f3fSDimitry Andric   RA_EVICT_FIRST_DEVELOPMENT_FEATURE(_FEATURE_IDX_SIMPLE) = FeatureCount,
2375f757f3fSDimitry Andric #else
2385f757f3fSDimitry Andric   RA_EVICT_FIRST_DEVELOPMENT_FEATURE(_FEATURE_IDX)
2395f757f3fSDimitry Andric #endif // #ifdef LLVM_HAVE_TFLITE
2405f757f3fSDimitry Andric   RA_EVICT_REST_DEVELOPMENT_FEATURES(_FEATURE_IDX) FeaturesWithDevelopmentCount
2415f757f3fSDimitry Andric #undef _FEATURE_IDX
2425f757f3fSDimitry Andric #undef _FEATURE_IDX_SIMPLE
2435f757f3fSDimitry Andric };
2445f757f3fSDimitry Andric 
2455f757f3fSDimitry Andric // The ML advisor will typically have a sparse input to the evaluator, because
2465f757f3fSDimitry Andric // various phys regs won't be available. It's easier (maintenance-wise) to
2475f757f3fSDimitry Andric // bulk-reset the state of the evaluator each time we are about to use it
2485f757f3fSDimitry Andric // again.
2495f757f3fSDimitry Andric template <typename T> size_t getTotalSize(const std::vector<int64_t> &Shape) {
2505f757f3fSDimitry Andric   size_t Ret = sizeof(T);
2515f757f3fSDimitry Andric   for (const auto V : Shape)
2525f757f3fSDimitry Andric     Ret *= V;
2535f757f3fSDimitry Andric   return Ret;
2545f757f3fSDimitry Andric }
2555f757f3fSDimitry Andric 
2565f757f3fSDimitry Andric void resetInputs(MLModelRunner &Runner) {
2575f757f3fSDimitry Andric #define _RESET(TYPE, NAME, SHAPE, __)                                          \
2585f757f3fSDimitry Andric   std::memset(Runner.getTensorUntyped(FeatureIDs::NAME), 0,                    \
2595f757f3fSDimitry Andric               getTotalSize<TYPE>(SHAPE));
2605f757f3fSDimitry Andric   RA_EVICT_FEATURES_LIST(_RESET)
2615f757f3fSDimitry Andric   if (EnableDevelopmentFeatures) {
2625f757f3fSDimitry Andric     RA_EVICT_FIRST_DEVELOPMENT_FEATURE(_RESET)
2635f757f3fSDimitry Andric     RA_EVICT_REST_DEVELOPMENT_FEATURES(_RESET)
2645f757f3fSDimitry Andric #undef _RESET
2655f757f3fSDimitry Andric   }
2665f757f3fSDimitry Andric }
2675f757f3fSDimitry Andric 
2685f757f3fSDimitry Andric // Per-live interval components that get aggregated into the feature values
2695f757f3fSDimitry Andric // that will be passed to the evaluator.
2705f757f3fSDimitry Andric struct LIFeatureComponents {
2715f757f3fSDimitry Andric   double R = 0;
2725f757f3fSDimitry Andric   double W = 0;
2735f757f3fSDimitry Andric   double RW = 0;
2745f757f3fSDimitry Andric   double IndVarUpdates = 0;
2755f757f3fSDimitry Andric   double HintWeights = 0.0;
2765f757f3fSDimitry Andric   int64_t NrDefsAndUses = 0;
2775f757f3fSDimitry Andric   float HottestBlockFreq = 0.0;
2785f757f3fSDimitry Andric   bool IsRemat = false;
2795f757f3fSDimitry Andric };
2805f757f3fSDimitry Andric 
2815f757f3fSDimitry Andric using CandidateRegList =
2825f757f3fSDimitry Andric     std::array<std::pair<MCRegister, bool>, NumberOfInterferences>;
2835f757f3fSDimitry Andric using FeaturesListNormalizer =
2845f757f3fSDimitry Andric     llvm::SmallVector<float, FeatureIDs::FeatureCount>;
2855f757f3fSDimitry Andric 
2865f757f3fSDimitry Andric /// The ML evictor (commonalities between release and development mode)
2875f757f3fSDimitry Andric class MLEvictAdvisor : public RegAllocEvictionAdvisor {
2885f757f3fSDimitry Andric public:
2895f757f3fSDimitry Andric   MLEvictAdvisor(const MachineFunction &MF, const RAGreedy &RA,
2905f757f3fSDimitry Andric                  MLModelRunner *Runner, const MachineBlockFrequencyInfo &MBFI,
2915f757f3fSDimitry Andric                  const MachineLoopInfo &Loops);
2925f757f3fSDimitry Andric 
2935f757f3fSDimitry Andric protected:
2945f757f3fSDimitry Andric   const RegAllocEvictionAdvisor &getDefaultAdvisor() const {
2955f757f3fSDimitry Andric     return static_cast<const RegAllocEvictionAdvisor &>(DefaultAdvisor);
2965f757f3fSDimitry Andric   }
2975f757f3fSDimitry Andric 
2985f757f3fSDimitry Andric   // The assumption is that if the Runner could not be constructed, we emit-ed
2995f757f3fSDimitry Andric   // error, and we shouldn't be asking for it here.
3005f757f3fSDimitry Andric   const MLModelRunner &getRunner() const { return *Runner; }
3015f757f3fSDimitry Andric 
3025f757f3fSDimitry Andric   /// This just calls Evaluate on the Runner, but in the development mode
3035f757f3fSDimitry Andric   /// case, if we're just capturing the log of the default advisor, it needs
3045f757f3fSDimitry Andric   /// to call the latter instead, so we need to pass all the necessary
3055f757f3fSDimitry Andric   /// parameters for it. In the development case, it will also log.
3065f757f3fSDimitry Andric   virtual int64_t
3075f757f3fSDimitry Andric   tryFindEvictionCandidatePosition(const LiveInterval &VirtReg,
3085f757f3fSDimitry Andric                                    const AllocationOrder &Order,
3095f757f3fSDimitry Andric                                    unsigned OrderLimit, uint8_t CostPerUseLimit,
3105f757f3fSDimitry Andric                                    const SmallVirtRegSet &FixedRegisters) const;
3115f757f3fSDimitry Andric 
3125f757f3fSDimitry Andric   /// Load the features of the given VirtReg (allocated or not) at column Pos,
3135f757f3fSDimitry Andric   /// but if  that can't be evicted, return false instead.
3145f757f3fSDimitry Andric   bool
3155f757f3fSDimitry Andric   loadInterferenceFeatures(const LiveInterval &VirtReg, MCRegister PhysReg,
3165f757f3fSDimitry Andric                            bool IsHint, const SmallVirtRegSet &FixedRegisters,
3175f757f3fSDimitry Andric                            llvm::SmallVectorImpl<float> &Largest, size_t Pos,
3185f757f3fSDimitry Andric                            SmallVectorImpl<LRStartEndInfo> &LRPosInfo) const;
3195f757f3fSDimitry Andric 
3205f757f3fSDimitry Andric private:
3215f757f3fSDimitry Andric   static float getInitialQueueSize(const MachineFunction &MF);
3225f757f3fSDimitry Andric 
3235f757f3fSDimitry Andric   MCRegister tryFindEvictionCandidate(
3245f757f3fSDimitry Andric       const LiveInterval &VirtReg, const AllocationOrder &Order,
3255f757f3fSDimitry Andric       uint8_t CostPerUseLimit,
3265f757f3fSDimitry Andric       const SmallVirtRegSet &FixedRegisters) const override;
3275f757f3fSDimitry Andric 
3285f757f3fSDimitry Andric   void extractFeatures(const SmallVectorImpl<const LiveInterval *> &Intervals,
3295f757f3fSDimitry Andric                        llvm::SmallVectorImpl<float> &Largest, size_t Pos,
3305f757f3fSDimitry Andric                        int64_t IsHint, int64_t LocalIntfsCount, float NrUrgent,
3315f757f3fSDimitry Andric                        SmallVectorImpl<LRStartEndInfo> &LRPosInfo) const;
3325f757f3fSDimitry Andric 
3335f757f3fSDimitry Andric   // Point-in-time: we didn't learn this, so we always delegate to the
3345f757f3fSDimitry Andric   // default.
3355f757f3fSDimitry Andric   bool canEvictHintInterference(
3365f757f3fSDimitry Andric       const LiveInterval &VirtReg, MCRegister PhysReg,
3375f757f3fSDimitry Andric       const SmallVirtRegSet &FixedRegisters) const override {
3385f757f3fSDimitry Andric     return getDefaultAdvisor().canEvictHintInterference(VirtReg, PhysReg,
3395f757f3fSDimitry Andric                                                         FixedRegisters);
3405f757f3fSDimitry Andric   }
3415f757f3fSDimitry Andric 
3425f757f3fSDimitry Andric   const LIFeatureComponents &
3435f757f3fSDimitry Andric   getLIFeatureComponents(const LiveInterval &LI) const;
3445f757f3fSDimitry Andric 
3455f757f3fSDimitry Andric   // Hold on to a default advisor for:
3465f757f3fSDimitry Andric   // 1) the implementation of canEvictHintInterference, because we didn't
3475f757f3fSDimitry Andric   // learn that nuance yet; 2) for bootstrapping (logging) in the development
3485f757f3fSDimitry Andric   // mode case.
3495f757f3fSDimitry Andric   const DefaultEvictionAdvisor DefaultAdvisor;
3505f757f3fSDimitry Andric   MLModelRunner *const Runner;
3515f757f3fSDimitry Andric   const MachineBlockFrequencyInfo &MBFI;
3525f757f3fSDimitry Andric   const MachineLoopInfo &Loops;
3535f757f3fSDimitry Andric 
3545f757f3fSDimitry Andric   // Indices of those features we don't want to normalize.
3555f757f3fSDimitry Andric   // This could be static and shared, but its initialization is non-trivial.
3565f757f3fSDimitry Andric   std::bitset<FeatureIDs::FeatureCount> DoNotNormalize;
3575f757f3fSDimitry Andric   const float InitialQSize;
3585f757f3fSDimitry Andric 
3595f757f3fSDimitry Andric   using RegID = unsigned;
3605f757f3fSDimitry Andric   mutable DenseMap<RegID, LIFeatureComponents> CachedFeatures;
3615f757f3fSDimitry Andric };
3625f757f3fSDimitry Andric 
3635f757f3fSDimitry Andric #define _DECL_FEATURES(type, name, shape, _)                                   \
3645f757f3fSDimitry Andric   TensorSpec::createSpec<type>(#name, shape),
3655f757f3fSDimitry Andric 
3665f757f3fSDimitry Andric // ===================================
3675f757f3fSDimitry Andric // Release (AOT) - specifics
3685f757f3fSDimitry Andric // ===================================
3695f757f3fSDimitry Andric class ReleaseModeEvictionAdvisorAnalysis final
3705f757f3fSDimitry Andric     : public RegAllocEvictionAdvisorAnalysis {
3715f757f3fSDimitry Andric public:
3725f757f3fSDimitry Andric   ReleaseModeEvictionAdvisorAnalysis()
3735f757f3fSDimitry Andric       : RegAllocEvictionAdvisorAnalysis(AdvisorMode::Release) {
3745f757f3fSDimitry Andric     if (EnableDevelopmentFeatures) {
3755f757f3fSDimitry Andric       InputFeatures = {RA_EVICT_FEATURES_LIST(
3765f757f3fSDimitry Andric           _DECL_FEATURES) RA_EVICT_FIRST_DEVELOPMENT_FEATURE(_DECL_FEATURES)
3775f757f3fSDimitry Andric                            RA_EVICT_REST_DEVELOPMENT_FEATURES(_DECL_FEATURES)};
3785f757f3fSDimitry Andric     } else {
3795f757f3fSDimitry Andric       InputFeatures = {RA_EVICT_FEATURES_LIST(_DECL_FEATURES)};
3805f757f3fSDimitry Andric     }
3815f757f3fSDimitry Andric   }
3825f757f3fSDimitry Andric   // support for isa<> and dyn_cast.
3835f757f3fSDimitry Andric   static bool classof(const RegAllocEvictionAdvisorAnalysis *R) {
3845f757f3fSDimitry Andric     return R->getAdvisorMode() == AdvisorMode::Release;
3855f757f3fSDimitry Andric   }
3865f757f3fSDimitry Andric 
3875f757f3fSDimitry Andric private:
3885f757f3fSDimitry Andric   std::vector<TensorSpec> InputFeatures;
3895f757f3fSDimitry Andric 
3905f757f3fSDimitry Andric   void getAnalysisUsage(AnalysisUsage &AU) const override {
391*0fca6ea1SDimitry Andric     AU.addRequired<MachineBlockFrequencyInfoWrapperPass>();
392*0fca6ea1SDimitry Andric     AU.addRequired<MachineLoopInfoWrapperPass>();
3935f757f3fSDimitry Andric     RegAllocEvictionAdvisorAnalysis::getAnalysisUsage(AU);
3945f757f3fSDimitry Andric   }
3955f757f3fSDimitry Andric 
3965f757f3fSDimitry Andric   std::unique_ptr<RegAllocEvictionAdvisor>
3975f757f3fSDimitry Andric   getAdvisor(const MachineFunction &MF, const RAGreedy &RA) override {
3985f757f3fSDimitry Andric     if (!Runner) {
3995f757f3fSDimitry Andric       if (InteractiveChannelBaseName.empty())
4005f757f3fSDimitry Andric         Runner = std::make_unique<ReleaseModeModelRunner<CompiledModelType>>(
4015f757f3fSDimitry Andric             MF.getFunction().getContext(), InputFeatures, DecisionName);
4025f757f3fSDimitry Andric       else
4035f757f3fSDimitry Andric         Runner = std::make_unique<InteractiveModelRunner>(
4045f757f3fSDimitry Andric             MF.getFunction().getContext(), InputFeatures, DecisionSpec,
4055f757f3fSDimitry Andric             InteractiveChannelBaseName + ".out",
4065f757f3fSDimitry Andric             InteractiveChannelBaseName + ".in");
4075f757f3fSDimitry Andric     }
4085f757f3fSDimitry Andric     return std::make_unique<MLEvictAdvisor>(
409*0fca6ea1SDimitry Andric         MF, RA, Runner.get(),
410*0fca6ea1SDimitry Andric         getAnalysis<MachineBlockFrequencyInfoWrapperPass>().getMBFI(),
411*0fca6ea1SDimitry Andric         getAnalysis<MachineLoopInfoWrapperPass>().getLI());
4125f757f3fSDimitry Andric   }
4135f757f3fSDimitry Andric   std::unique_ptr<MLModelRunner> Runner;
4145f757f3fSDimitry Andric };
4155f757f3fSDimitry Andric 
4165f757f3fSDimitry Andric // ===================================
4175f757f3fSDimitry Andric // Development mode-specifics
4185f757f3fSDimitry Andric // ===================================
4195f757f3fSDimitry Andric //
4205f757f3fSDimitry Andric // Features we log
4215f757f3fSDimitry Andric #ifdef LLVM_HAVE_TFLITE
4225f757f3fSDimitry Andric static const TensorSpec Reward = TensorSpec::createSpec<float>("reward", {1});
4235f757f3fSDimitry Andric 
4245f757f3fSDimitry Andric // Features we bind on the model. The tensor names have a prefix, and we also
4255f757f3fSDimitry Andric // need to include some tensors that are expected to be present by the
4265f757f3fSDimitry Andric // training algo.
4275f757f3fSDimitry Andric // TODO: can we just get rid of these?
4285f757f3fSDimitry Andric #define _DECL_TRAIN_FEATURES(type, name, shape, _)                             \
4295f757f3fSDimitry Andric   TensorSpec::createSpec<type>(std::string("action_") + #name, shape),
4305f757f3fSDimitry Andric 
4315f757f3fSDimitry Andric class DevelopmentModeEvictAdvisor : public MLEvictAdvisor {
4325f757f3fSDimitry Andric public:
4335f757f3fSDimitry Andric   DevelopmentModeEvictAdvisor(const MachineFunction &MF, const RAGreedy &RA,
4345f757f3fSDimitry Andric                               MLModelRunner *Runner,
4355f757f3fSDimitry Andric                               const MachineBlockFrequencyInfo &MBFI,
4365f757f3fSDimitry Andric                               const MachineLoopInfo &Loops, Logger *Log)
4375f757f3fSDimitry Andric       : MLEvictAdvisor(MF, RA, Runner, MBFI, Loops), Log(Log) {}
4385f757f3fSDimitry Andric 
4395f757f3fSDimitry Andric private:
4405f757f3fSDimitry Andric   int64_t tryFindEvictionCandidatePosition(
4415f757f3fSDimitry Andric       const LiveInterval &VirtReg, const AllocationOrder &Order,
4425f757f3fSDimitry Andric       unsigned OrderLimit, uint8_t CostPerUseLimit,
4435f757f3fSDimitry Andric       const SmallVirtRegSet &FixedRegisters) const override;
4445f757f3fSDimitry Andric 
4455f757f3fSDimitry Andric   Logger *const Log;
4465f757f3fSDimitry Andric };
4475f757f3fSDimitry Andric 
4485f757f3fSDimitry Andric class DevelopmentModeEvictionAdvisorAnalysis final
4495f757f3fSDimitry Andric     : public RegAllocEvictionAdvisorAnalysis {
4505f757f3fSDimitry Andric public:
4515f757f3fSDimitry Andric   DevelopmentModeEvictionAdvisorAnalysis()
4525f757f3fSDimitry Andric       : RegAllocEvictionAdvisorAnalysis(AdvisorMode::Development) {
4535f757f3fSDimitry Andric     if (EnableDevelopmentFeatures) {
4545f757f3fSDimitry Andric       InputFeatures = {RA_EVICT_FEATURES_LIST(
4555f757f3fSDimitry Andric           _DECL_FEATURES) RA_EVICT_FIRST_DEVELOPMENT_FEATURE(_DECL_FEATURES)
4565f757f3fSDimitry Andric                            RA_EVICT_REST_DEVELOPMENT_FEATURES(_DECL_FEATURES)};
4575f757f3fSDimitry Andric       TrainingInputFeatures = {
4585f757f3fSDimitry Andric           RA_EVICT_FEATURES_LIST(_DECL_TRAIN_FEATURES)
4595f757f3fSDimitry Andric               RA_EVICT_FIRST_DEVELOPMENT_FEATURE(_DECL_TRAIN_FEATURES)
4605f757f3fSDimitry Andric                   RA_EVICT_REST_DEVELOPMENT_FEATURES(_DECL_TRAIN_FEATURES)
4615f757f3fSDimitry Andric                       TensorSpec::createSpec<float>("action_discount", {1}),
4625f757f3fSDimitry Andric           TensorSpec::createSpec<int32_t>("action_step_type", {1}),
4635f757f3fSDimitry Andric           TensorSpec::createSpec<float>("action_reward", {1})};
4645f757f3fSDimitry Andric     } else {
4655f757f3fSDimitry Andric       InputFeatures = {RA_EVICT_FEATURES_LIST(_DECL_FEATURES)};
4665f757f3fSDimitry Andric       TrainingInputFeatures = {
4675f757f3fSDimitry Andric           RA_EVICT_FEATURES_LIST(_DECL_TRAIN_FEATURES)
4685f757f3fSDimitry Andric               TensorSpec::createSpec<float>("action_discount", {1}),
4695f757f3fSDimitry Andric           TensorSpec::createSpec<int32_t>("action_step_type", {1}),
4705f757f3fSDimitry Andric           TensorSpec::createSpec<float>("action_reward", {1})};
4715f757f3fSDimitry Andric     }
4725f757f3fSDimitry Andric   }
4735f757f3fSDimitry Andric   // support for isa<> and dyn_cast.
4745f757f3fSDimitry Andric   static bool classof(const RegAllocEvictionAdvisorAnalysis *R) {
4755f757f3fSDimitry Andric     return R->getAdvisorMode() == AdvisorMode::Development;
4765f757f3fSDimitry Andric   }
4775f757f3fSDimitry Andric 
4785f757f3fSDimitry Andric   void logRewardIfNeeded(const MachineFunction &MF,
4795f757f3fSDimitry Andric                          llvm::function_ref<float()> GetReward) override {
4805f757f3fSDimitry Andric     if (!Log || !Log->hasAnyObservationForContext(MF.getName()))
4815f757f3fSDimitry Andric       return;
4825f757f3fSDimitry Andric     // The function pass manager would run all the function passes for a
4835f757f3fSDimitry Andric     // function, so we assume the last context belongs to this function. If
4845f757f3fSDimitry Andric     // this invariant ever changes, we can implement at that time switching
4855f757f3fSDimitry Andric     // contexts. At this point, it'd be an error
4865f757f3fSDimitry Andric     if (Log->currentContext() != MF.getName()) {
4875f757f3fSDimitry Andric       MF.getFunction().getContext().emitError(
4885f757f3fSDimitry Andric           "The training log context shouldn't have had changed.");
4895f757f3fSDimitry Andric     }
4905f757f3fSDimitry Andric     if (Log->hasObservationInProgress())
4915f757f3fSDimitry Andric       Log->logReward<float>(GetReward());
4925f757f3fSDimitry Andric   }
4935f757f3fSDimitry Andric 
4945f757f3fSDimitry Andric private:
4955f757f3fSDimitry Andric   std::vector<TensorSpec> InputFeatures;
4965f757f3fSDimitry Andric   std::vector<TensorSpec> TrainingInputFeatures;
4975f757f3fSDimitry Andric 
4985f757f3fSDimitry Andric   void getAnalysisUsage(AnalysisUsage &AU) const override {
499*0fca6ea1SDimitry Andric     AU.addRequired<MachineBlockFrequencyInfoWrapperPass>();
500*0fca6ea1SDimitry Andric     AU.addRequired<MachineLoopInfoWrapperPass>();
5015f757f3fSDimitry Andric     RegAllocEvictionAdvisorAnalysis::getAnalysisUsage(AU);
5025f757f3fSDimitry Andric   }
5035f757f3fSDimitry Andric 
5045f757f3fSDimitry Andric   bool doInitialization(Module &M) override {
5055f757f3fSDimitry Andric     LLVMContext &Ctx = M.getContext();
5065f757f3fSDimitry Andric     if (ModelUnderTraining.empty() && TrainingLog.empty()) {
5075f757f3fSDimitry Andric       Ctx.emitError("Regalloc development mode should be requested with at "
5085f757f3fSDimitry Andric                     "least logging enabled and/or a training model");
5095f757f3fSDimitry Andric       return false;
5105f757f3fSDimitry Andric     }
5115f757f3fSDimitry Andric     if (ModelUnderTraining.empty())
5125f757f3fSDimitry Andric       Runner = std::make_unique<NoInferenceModelRunner>(Ctx, InputFeatures);
5135f757f3fSDimitry Andric     else
5145f757f3fSDimitry Andric       Runner = ModelUnderTrainingRunner::createAndEnsureValid(
5155f757f3fSDimitry Andric           Ctx, ModelUnderTraining, DecisionName, TrainingInputFeatures);
5165f757f3fSDimitry Andric     if (!Runner) {
5175f757f3fSDimitry Andric       Ctx.emitError("Regalloc: could not set up the model runner");
5185f757f3fSDimitry Andric       return false;
5195f757f3fSDimitry Andric     }
5205f757f3fSDimitry Andric     if (TrainingLog.empty())
5215f757f3fSDimitry Andric       return false;
5225f757f3fSDimitry Andric     std::error_code EC;
5235f757f3fSDimitry Andric     auto OS = std::make_unique<raw_fd_ostream>(TrainingLog, EC);
5245f757f3fSDimitry Andric     if (EC) {
5255f757f3fSDimitry Andric       M.getContext().emitError(EC.message() + ":" + TrainingLog);
5265f757f3fSDimitry Andric       return false;
5275f757f3fSDimitry Andric     }
5285f757f3fSDimitry Andric     std::vector<TensorSpec> LFS = InputFeatures;
5295f757f3fSDimitry Andric     if (auto *MUTR = dyn_cast<ModelUnderTrainingRunner>(Runner.get()))
5305f757f3fSDimitry Andric       append_range(LFS, MUTR->extraOutputsForLoggingSpecs());
5315f757f3fSDimitry Andric     // We always log the output; in particular, if we're not evaluating, we
5325f757f3fSDimitry Andric     // don't have an output spec json file. That's why we handle the
5335f757f3fSDimitry Andric     // 'normal' output separately.
5345f757f3fSDimitry Andric     LFS.push_back(DecisionSpec);
5355f757f3fSDimitry Andric 
5365f757f3fSDimitry Andric     Log = std::make_unique<Logger>(std::move(OS), LFS, Reward,
5375f757f3fSDimitry Andric                                    /*IncludeReward*/ true);
5385f757f3fSDimitry Andric     return false;
5395f757f3fSDimitry Andric   }
5405f757f3fSDimitry Andric 
5415f757f3fSDimitry Andric   std::unique_ptr<RegAllocEvictionAdvisor>
5425f757f3fSDimitry Andric   getAdvisor(const MachineFunction &MF, const RAGreedy &RA) override {
5435f757f3fSDimitry Andric     if (!Runner)
5445f757f3fSDimitry Andric       return nullptr;
5455f757f3fSDimitry Andric     if (Log)
5465f757f3fSDimitry Andric       Log->switchContext(MF.getName());
5475f757f3fSDimitry Andric     return std::make_unique<DevelopmentModeEvictAdvisor>(
548*0fca6ea1SDimitry Andric         MF, RA, Runner.get(),
549*0fca6ea1SDimitry Andric         getAnalysis<MachineBlockFrequencyInfoWrapperPass>().getMBFI(),
550*0fca6ea1SDimitry Andric         getAnalysis<MachineLoopInfoWrapperPass>().getLI(), Log.get());
5515f757f3fSDimitry Andric   }
5525f757f3fSDimitry Andric 
5535f757f3fSDimitry Andric   std::unique_ptr<MLModelRunner> Runner;
5545f757f3fSDimitry Andric   std::unique_ptr<Logger> Log;
5555f757f3fSDimitry Andric };
5565f757f3fSDimitry Andric 
5575f757f3fSDimitry Andric #endif //#ifdef LLVM_HAVE_TFLITE
5585f757f3fSDimitry Andric } // namespace
5595f757f3fSDimitry Andric 
5605f757f3fSDimitry Andric float MLEvictAdvisor::getInitialQueueSize(const MachineFunction &MF) {
5615f757f3fSDimitry Andric   auto &MRI = MF.getRegInfo();
5625f757f3fSDimitry Andric   float Ret = 0.0;
5635f757f3fSDimitry Andric   for (unsigned I = 0, E = MRI.getNumVirtRegs(); I != E; ++I) {
5645f757f3fSDimitry Andric     Register Reg = Register::index2VirtReg(I);
5655f757f3fSDimitry Andric     if (MRI.reg_nodbg_empty(Reg))
5665f757f3fSDimitry Andric       continue;
5675f757f3fSDimitry Andric     ++Ret;
5685f757f3fSDimitry Andric   }
5695f757f3fSDimitry Andric   return Ret;
5705f757f3fSDimitry Andric }
5715f757f3fSDimitry Andric 
5725f757f3fSDimitry Andric MLEvictAdvisor::MLEvictAdvisor(const MachineFunction &MF, const RAGreedy &RA,
5735f757f3fSDimitry Andric                                MLModelRunner *Runner,
5745f757f3fSDimitry Andric                                const MachineBlockFrequencyInfo &MBFI,
5755f757f3fSDimitry Andric                                const MachineLoopInfo &Loops)
5765f757f3fSDimitry Andric     : RegAllocEvictionAdvisor(MF, RA), DefaultAdvisor(MF, RA),
5775f757f3fSDimitry Andric       Runner(std::move(Runner)), MBFI(MBFI), Loops(Loops),
5785f757f3fSDimitry Andric       InitialQSize(MLEvictAdvisor::getInitialQueueSize(MF)) {
5795f757f3fSDimitry Andric   assert(this->Runner);
5805f757f3fSDimitry Andric   Runner->switchContext(MF.getName());
5815f757f3fSDimitry Andric   DoNotNormalize.set(FeatureIDs::mask);
5825f757f3fSDimitry Andric   DoNotNormalize.set(FeatureIDs::is_free);
5835f757f3fSDimitry Andric   DoNotNormalize.set(FeatureIDs::is_hint);
5845f757f3fSDimitry Andric   DoNotNormalize.set(FeatureIDs::is_local);
5855f757f3fSDimitry Andric   DoNotNormalize.set(FeatureIDs::min_stage);
5865f757f3fSDimitry Andric   DoNotNormalize.set(FeatureIDs::max_stage);
5875f757f3fSDimitry Andric   DoNotNormalize.set(FeatureIDs::progress);
5885f757f3fSDimitry Andric }
5895f757f3fSDimitry Andric 
5905f757f3fSDimitry Andric int64_t MLEvictAdvisor::tryFindEvictionCandidatePosition(
5915f757f3fSDimitry Andric     const LiveInterval &, const AllocationOrder &, unsigned, uint8_t,
5925f757f3fSDimitry Andric     const SmallVirtRegSet &) const {
5935f757f3fSDimitry Andric   int64_t Ret = Runner->evaluate<int64_t>();
5945f757f3fSDimitry Andric   assert(Ret >= 0);
5955f757f3fSDimitry Andric   assert(Ret <= CandidateVirtRegPos);
5965f757f3fSDimitry Andric   return Ret;
5975f757f3fSDimitry Andric }
5985f757f3fSDimitry Andric 
5995f757f3fSDimitry Andric bool MLEvictAdvisor::loadInterferenceFeatures(
6005f757f3fSDimitry Andric     const LiveInterval &VirtReg, MCRegister PhysReg, bool IsHint,
6015f757f3fSDimitry Andric     const SmallVirtRegSet &FixedRegisters,
6025f757f3fSDimitry Andric     llvm::SmallVectorImpl<float> &Largest, size_t Pos,
6035f757f3fSDimitry Andric     llvm::SmallVectorImpl<LRStartEndInfo> &LRPosInfo) const {
6045f757f3fSDimitry Andric   // It is only possible to evict virtual register interference.
6055f757f3fSDimitry Andric   if (Matrix->checkInterference(VirtReg, PhysReg) > LiveRegMatrix::IK_VirtReg) {
6065f757f3fSDimitry Andric     // leave unavailable
6075f757f3fSDimitry Andric     return false;
6085f757f3fSDimitry Andric   }
6095f757f3fSDimitry Andric 
6105f757f3fSDimitry Andric   const bool IsLocal = LIS->intervalIsInOneMBB(VirtReg);
6115f757f3fSDimitry Andric   int64_t LocalIntfs = 0;
6125f757f3fSDimitry Andric   float NrUrgent = 0.0f;
6135f757f3fSDimitry Andric 
6145f757f3fSDimitry Andric   // The cascade tracking is the same as in the default advisor
6155f757f3fSDimitry Andric   unsigned Cascade = RA.getExtraInfo().getCascadeOrCurrentNext(VirtReg.reg());
6165f757f3fSDimitry Andric 
6175f757f3fSDimitry Andric   SmallVector<const LiveInterval *, MaxInterferences> InterferingIntervals;
6185f757f3fSDimitry Andric   for (MCRegUnit Unit : TRI->regunits(PhysReg)) {
6195f757f3fSDimitry Andric     LiveIntervalUnion::Query &Q = Matrix->query(VirtReg, Unit);
6205f757f3fSDimitry Andric     // Different from the default heuristic, we don't make any assumptions
6215f757f3fSDimitry Andric     // about what having more than 10 results in the query may mean.
6225f757f3fSDimitry Andric     const auto &IFIntervals = Q.interferingVRegs(EvictInterferenceCutoff);
6235f757f3fSDimitry Andric     if (IFIntervals.empty() && InterferingIntervals.empty())
6245f757f3fSDimitry Andric       continue;
6255f757f3fSDimitry Andric     if (IFIntervals.size() >= EvictInterferenceCutoff)
6265f757f3fSDimitry Andric       return false;
6275f757f3fSDimitry Andric     InterferingIntervals.append(IFIntervals.begin(), IFIntervals.end());
6285f757f3fSDimitry Andric     for (const LiveInterval *Intf : reverse(IFIntervals)) {
6295f757f3fSDimitry Andric       assert(Intf->reg().isVirtual() &&
6305f757f3fSDimitry Andric              "Only expecting virtual register interference from query");
6315f757f3fSDimitry Andric       // This is the same set of legality checks as in the default case: don't
6325f757f3fSDimitry Andric       // try to evict fixed regs or 'done' ones. Also don't break cascades,
6335f757f3fSDimitry Andric       // except in the urgent case, with the same nuances used in the default
6345f757f3fSDimitry Andric       // heuristic.
6355f757f3fSDimitry Andric       // We could try sharing this between the advisors, but it may end up
6365f757f3fSDimitry Andric       // more complex than it is right now.
6375f757f3fSDimitry Andric       if (FixedRegisters.count(Intf->reg()))
6385f757f3fSDimitry Andric         return false;
6395f757f3fSDimitry Andric       if (RA.getExtraInfo().getStage(*Intf) == RS_Done)
6405f757f3fSDimitry Andric         return false;
6415f757f3fSDimitry Andric       bool Urgent =
6425f757f3fSDimitry Andric           !VirtReg.isSpillable() &&
6435f757f3fSDimitry Andric           (Intf->isSpillable() ||
6445f757f3fSDimitry Andric            RegClassInfo.getNumAllocatableRegs(MRI->getRegClass(VirtReg.reg())) <
6455f757f3fSDimitry Andric                RegClassInfo.getNumAllocatableRegs(
6465f757f3fSDimitry Andric                    MRI->getRegClass(Intf->reg())));
6475f757f3fSDimitry Andric       // Only evict older cascades or live ranges without a cascade.
6485f757f3fSDimitry Andric       unsigned IntfCascade = RA.getExtraInfo().getCascade(Intf->reg());
6495f757f3fSDimitry Andric       if (Cascade <= IntfCascade) {
6505f757f3fSDimitry Andric         if (!Urgent)
6515f757f3fSDimitry Andric           return false;
6525f757f3fSDimitry Andric         ++NrUrgent;
6535f757f3fSDimitry Andric       }
6545f757f3fSDimitry Andric 
6555f757f3fSDimitry Andric       LocalIntfs += (IsLocal && LIS->intervalIsInOneMBB(*Intf) &&
6565f757f3fSDimitry Andric                      (!EnableLocalReassign || !canReassign(*Intf, PhysReg)));
6575f757f3fSDimitry Andric     }
6585f757f3fSDimitry Andric   }
6595f757f3fSDimitry Andric   // OK, so if we made it this far, this LR is an eviction candidate, load its
6605f757f3fSDimitry Andric   // features.
6615f757f3fSDimitry Andric   extractFeatures(InterferingIntervals, Largest, Pos, IsHint, LocalIntfs,
6625f757f3fSDimitry Andric                   NrUrgent, LRPosInfo);
6635f757f3fSDimitry Andric   return true;
6645f757f3fSDimitry Andric }
6655f757f3fSDimitry Andric 
6665f757f3fSDimitry Andric MCRegister MLEvictAdvisor::tryFindEvictionCandidate(
6675f757f3fSDimitry Andric     const LiveInterval &VirtReg, const AllocationOrder &Order,
6685f757f3fSDimitry Andric     uint8_t CostPerUseLimit, const SmallVirtRegSet &FixedRegisters) const {
6695f757f3fSDimitry Andric   auto MaybeOrderLimit = getOrderLimit(VirtReg, Order, CostPerUseLimit);
6705f757f3fSDimitry Andric   if (!MaybeOrderLimit)
6715f757f3fSDimitry Andric     return MCRegister::NoRegister;
6725f757f3fSDimitry Andric   unsigned OrderLimit = *MaybeOrderLimit;
6735f757f3fSDimitry Andric 
6745f757f3fSDimitry Andric   // The heuristic sets initial costs such as, if CostPerUseLimit is
6755f757f3fSDimitry Andric   // max<uint8_t>, then any of the costs of the legally-evictable intervals
6765f757f3fSDimitry Andric   // would be lower. When that happens, one of those will be selected.
6775f757f3fSDimitry Andric   // Therefore, we allow the candidate be selected, unless the candidate is
6785f757f3fSDimitry Andric   // unspillable, in which case it would be incorrect to not find a register
6795f757f3fSDimitry Andric   // for it.
6805f757f3fSDimitry Andric   const bool MustFindEviction =
6815f757f3fSDimitry Andric       (!VirtReg.isSpillable() && CostPerUseLimit == static_cast<uint8_t>(~0u));
6825f757f3fSDimitry Andric   // Number of available candidates - if 0, no need to continue.
6835f757f3fSDimitry Andric   size_t Available = 0;
6845f757f3fSDimitry Andric   // Make sure we don't have leftover partial state from an attempt where we
6855f757f3fSDimitry Andric   // had no available candidates and bailed out early.
6865f757f3fSDimitry Andric   resetInputs(*Runner);
6875f757f3fSDimitry Andric 
6885f757f3fSDimitry Andric   // Track the index->register mapping because AllocationOrder doesn't do that
6895f757f3fSDimitry Andric   // and we'd have to scan it.
6905f757f3fSDimitry Andric   // Also track their mask, to write asserts/debug.
6915f757f3fSDimitry Andric   CandidateRegList Regs;
6925f757f3fSDimitry Andric   Regs.fill({0, false});
6935f757f3fSDimitry Andric 
6945f757f3fSDimitry Andric   // Track the largest value of features seen during this eviction session. We
6955f757f3fSDimitry Andric   // only normalize (some of) the float features, but it's just simpler to
6965f757f3fSDimitry Andric   // dimension 'Largest' to all the features, especially since we have the
6975f757f3fSDimitry Andric   // 'DoNotNormalize' list.
6985f757f3fSDimitry Andric   FeaturesListNormalizer Largest(FeatureIDs::FeatureCount, 0.0);
6995f757f3fSDimitry Andric 
7005f757f3fSDimitry Andric   // Same overal idea as in the default eviction policy - we visit the values
7015f757f3fSDimitry Andric   // of AllocationOrder one at a time. If it's not legally available, we mask
7025f757f3fSDimitry Andric   // off the corresponding feature column (==do nothing because we already
7035f757f3fSDimitry Andric   // reset all the features to 0) Use Pos to capture the column we load
7045f757f3fSDimitry Andric   // features at - in AllocationOrder order.
7055f757f3fSDimitry Andric   size_t Pos = 0;
7065f757f3fSDimitry Andric   SmallVector<LRStartEndInfo, NumberOfInterferences> LRPosInfo;
7075f757f3fSDimitry Andric   for (auto I = Order.begin(), E = Order.getOrderLimitEnd(OrderLimit); I != E;
7085f757f3fSDimitry Andric        ++I, ++Pos) {
7095f757f3fSDimitry Andric     MCRegister PhysReg = *I;
7105f757f3fSDimitry Andric     assert(!Regs[Pos].second);
7115f757f3fSDimitry Andric     assert(PhysReg);
7125f757f3fSDimitry Andric     if (!canAllocatePhysReg(CostPerUseLimit, PhysReg)) {
7135f757f3fSDimitry Andric       continue;
7145f757f3fSDimitry Andric     }
7155f757f3fSDimitry Andric     if (loadInterferenceFeatures(VirtReg, PhysReg, I.isHint(), FixedRegisters,
7165f757f3fSDimitry Andric                                  Largest, Pos, LRPosInfo)) {
7175f757f3fSDimitry Andric       ++Available;
7185f757f3fSDimitry Andric       Regs[Pos] = std::make_pair(PhysReg, true);
7195f757f3fSDimitry Andric     }
7205f757f3fSDimitry Andric   }
7215f757f3fSDimitry Andric   if (Available == 0) {
7225f757f3fSDimitry Andric     // Nothing to decide, nothing to learn.
7235f757f3fSDimitry Andric     assert(!MustFindEviction);
7245f757f3fSDimitry Andric     return MCRegister::NoRegister;
7255f757f3fSDimitry Andric   }
7265f757f3fSDimitry Andric   const size_t ValidPosLimit = Pos;
7275f757f3fSDimitry Andric   // If we must find eviction, the candidate should be masked out of the
7285f757f3fSDimitry Andric   // decision making process.
7295f757f3fSDimitry Andric   Regs[CandidateVirtRegPos].second = !MustFindEviction;
7305f757f3fSDimitry Andric   if (!MustFindEviction)
7315f757f3fSDimitry Andric     extractFeatures(SmallVector<const LiveInterval *, 1>(1, &VirtReg), Largest,
7325f757f3fSDimitry Andric                     CandidateVirtRegPos, /*IsHint*/ 0,
7335f757f3fSDimitry Andric                     /*LocalIntfsCount*/ 0,
7345f757f3fSDimitry Andric                     /*NrUrgent*/ 0.0, LRPosInfo);
7355f757f3fSDimitry Andric   assert(InitialQSize > 0.0 && "We couldn't have gotten here if we had "
7365f757f3fSDimitry Andric                                "nothing to allocate initially.");
7375f757f3fSDimitry Andric #ifdef LLVM_HAVE_TFLITE
7385f757f3fSDimitry Andric   if (EnableDevelopmentFeatures) {
7395f757f3fSDimitry Andric     extractInstructionFeatures(
7405f757f3fSDimitry Andric         LRPosInfo, Runner,
7415f757f3fSDimitry Andric         [this](SlotIndex InputIndex) -> int {
7425f757f3fSDimitry Andric           auto *CurrentMachineInstruction =
7435f757f3fSDimitry Andric               LIS->getInstructionFromIndex(InputIndex);
7445f757f3fSDimitry Andric           if (!CurrentMachineInstruction) {
7455f757f3fSDimitry Andric             return -1;
7465f757f3fSDimitry Andric           }
7475f757f3fSDimitry Andric           return CurrentMachineInstruction->getOpcode();
7485f757f3fSDimitry Andric         },
7495f757f3fSDimitry Andric         [this](SlotIndex InputIndex) -> float {
7505f757f3fSDimitry Andric           auto *CurrentMachineInstruction =
7515f757f3fSDimitry Andric               LIS->getInstructionFromIndex(InputIndex);
7525f757f3fSDimitry Andric           return MBFI.getBlockFreqRelativeToEntryBlock(
7535f757f3fSDimitry Andric               CurrentMachineInstruction->getParent());
7545f757f3fSDimitry Andric         },
7555f757f3fSDimitry Andric         [this](SlotIndex InputIndex) -> MachineBasicBlock * {
7565f757f3fSDimitry Andric           auto *CurrentMachineInstruction =
7575f757f3fSDimitry Andric               LIS->getInstructionFromIndex(InputIndex);
7585f757f3fSDimitry Andric           return CurrentMachineInstruction->getParent();
7595f757f3fSDimitry Andric         },
7605f757f3fSDimitry Andric         FeatureIDs::instructions, FeatureIDs::instructions_mapping,
7615f757f3fSDimitry Andric         FeatureIDs::mbb_frequencies, FeatureIDs::mbb_mapping,
7625f757f3fSDimitry Andric         LIS->getSlotIndexes()->getLastIndex());
7635f757f3fSDimitry Andric   }
7645f757f3fSDimitry Andric #endif // #ifdef LLVM_HAVE_TFLITE
7655f757f3fSDimitry Andric   // Normalize the features.
7665f757f3fSDimitry Andric   for (auto &V : Largest)
7675f757f3fSDimitry Andric     V = V ? V : 1.0;
7685f757f3fSDimitry Andric   for (size_t FeatureIndex = 0; FeatureIndex < FeatureIDs::FeatureCount;
7695f757f3fSDimitry Andric        ++FeatureIndex) {
7705f757f3fSDimitry Andric     if (DoNotNormalize.test(FeatureIndex))
7715f757f3fSDimitry Andric       continue;
7725f757f3fSDimitry Andric     for (size_t Pos = 0; Pos < NumberOfInterferences; ++Pos) {
7735f757f3fSDimitry Andric       Runner->getTensor<float>(FeatureIndex)[Pos] /= Largest[FeatureIndex];
7745f757f3fSDimitry Andric     }
7755f757f3fSDimitry Andric   }
7765f757f3fSDimitry Andric   *Runner->getTensor<float>(FeatureIDs::progress) =
7775f757f3fSDimitry Andric       static_cast<float>(RA.getQueueSize()) / InitialQSize;
7785f757f3fSDimitry Andric 
7795f757f3fSDimitry Andric   // Get a decision.
7805f757f3fSDimitry Andric   size_t CandidatePos = tryFindEvictionCandidatePosition(
7815f757f3fSDimitry Andric       VirtReg, Order, OrderLimit, CostPerUseLimit, FixedRegisters);
7825f757f3fSDimitry Andric   // The contract with the ML side is that CandidatePos is mask == 1 (i.e.
7835f757f3fSDimitry Andric   // Regs[CandidatePos].second)
7845f757f3fSDimitry Andric   assert(Regs[CandidatePos].second);
7855f757f3fSDimitry Andric   if (CandidatePos == CandidateVirtRegPos) {
7865f757f3fSDimitry Andric     assert(!MustFindEviction);
7875f757f3fSDimitry Andric     return MCRegister::NoRegister;
7885f757f3fSDimitry Andric   }
7895f757f3fSDimitry Andric   assert(CandidatePos < ValidPosLimit);
7905f757f3fSDimitry Andric   (void)ValidPosLimit;
7915f757f3fSDimitry Andric   return Regs[CandidatePos].first;
7925f757f3fSDimitry Andric }
7935f757f3fSDimitry Andric 
7945f757f3fSDimitry Andric const LIFeatureComponents &
7955f757f3fSDimitry Andric MLEvictAdvisor::getLIFeatureComponents(const LiveInterval &LI) const {
7965f757f3fSDimitry Andric   RegID ID = LI.reg().id();
7975f757f3fSDimitry Andric   LIFeatureComponents Empty;
7985f757f3fSDimitry Andric   auto I = CachedFeatures.insert(std::make_pair(ID, Empty));
7995f757f3fSDimitry Andric   LIFeatureComponents &Ret = I.first->getSecond();
8005f757f3fSDimitry Andric   if (!I.second)
8015f757f3fSDimitry Andric     return Ret;
8025f757f3fSDimitry Andric 
8035f757f3fSDimitry Andric   SmallPtrSet<MachineInstr *, 8> Visited;
8045f757f3fSDimitry Andric   const TargetRegisterInfo &TRI = *MF.getSubtarget().getRegisterInfo();
8055f757f3fSDimitry Andric 
8065f757f3fSDimitry Andric   for (MachineRegisterInfo::reg_instr_nodbg_iterator
8075f757f3fSDimitry Andric            I = MRI->reg_instr_nodbg_begin(LI.reg()),
8085f757f3fSDimitry Andric            E = MRI->reg_instr_nodbg_end();
8095f757f3fSDimitry Andric        I != E;) {
8105f757f3fSDimitry Andric     MachineInstr *MI = &*(I++);
8115f757f3fSDimitry Andric 
8125f757f3fSDimitry Andric     ++Ret.NrDefsAndUses;
8135f757f3fSDimitry Andric     if (!Visited.insert(MI).second)
8145f757f3fSDimitry Andric       continue;
8155f757f3fSDimitry Andric 
8165f757f3fSDimitry Andric     if (MI->isIdentityCopy() || MI->isImplicitDef())
8175f757f3fSDimitry Andric       continue;
8185f757f3fSDimitry Andric 
8195f757f3fSDimitry Andric     bool Reads, Writes;
8205f757f3fSDimitry Andric     std::tie(Reads, Writes) = MI->readsWritesVirtualRegister(LI.reg());
8215f757f3fSDimitry Andric 
8225f757f3fSDimitry Andric     float Freq = MBFI.getBlockFreqRelativeToEntryBlock(MI->getParent());
8235f757f3fSDimitry Andric     Ret.HottestBlockFreq = std::max(Freq, Ret.HottestBlockFreq);
8245f757f3fSDimitry Andric 
8255f757f3fSDimitry Andric     Ret.R += (Reads && !Writes) * Freq;
8265f757f3fSDimitry Andric     Ret.W += (!Reads && Writes) * Freq;
8275f757f3fSDimitry Andric     Ret.RW += (Reads && Writes) * Freq;
8285f757f3fSDimitry Andric 
8295f757f3fSDimitry Andric     auto *MBB = MI->getParent();
8305f757f3fSDimitry Andric     auto *Loop = Loops.getLoopFor(MBB);
8315f757f3fSDimitry Andric     bool IsExiting = Loop ? Loop->isLoopExiting(MBB) : false;
8325f757f3fSDimitry Andric 
8335f757f3fSDimitry Andric     if (Writes && IsExiting && LIS->isLiveOutOfMBB(LI, MBB))
8345f757f3fSDimitry Andric       Ret.IndVarUpdates += Freq;
8355f757f3fSDimitry Andric 
8365f757f3fSDimitry Andric     if (MI->isCopy() && VirtRegAuxInfo::copyHint(MI, LI.reg(), TRI, *MRI))
8375f757f3fSDimitry Andric       Ret.HintWeights += Freq;
8385f757f3fSDimitry Andric   }
8395f757f3fSDimitry Andric   Ret.IsRemat = VirtRegAuxInfo::isRematerializable(
8405f757f3fSDimitry Andric       LI, *LIS, *VRM, *MF.getSubtarget().getInstrInfo());
8415f757f3fSDimitry Andric   return Ret;
8425f757f3fSDimitry Andric }
8435f757f3fSDimitry Andric 
8445f757f3fSDimitry Andric // Overall, this currently mimics what we do for weight calculation, but instead
8455f757f3fSDimitry Andric // of accummulating the various features, we keep them separate.
8465f757f3fSDimitry Andric void MLEvictAdvisor::extractFeatures(
8475f757f3fSDimitry Andric     const SmallVectorImpl<const LiveInterval *> &Intervals,
8485f757f3fSDimitry Andric     llvm::SmallVectorImpl<float> &Largest, size_t Pos, int64_t IsHint,
8495f757f3fSDimitry Andric     int64_t LocalIntfsCount, float NrUrgent,
8505f757f3fSDimitry Andric     SmallVectorImpl<LRStartEndInfo> &LRPosInfo) const {
8515f757f3fSDimitry Andric   int64_t NrDefsAndUses = 0;
8525f757f3fSDimitry Andric   int64_t NrBrokenHints = 0;
8535f757f3fSDimitry Andric   double R = 0.0;
8545f757f3fSDimitry Andric   double W = 0.0;
8555f757f3fSDimitry Andric   double RW = 0.0;
8565f757f3fSDimitry Andric   double IndVarUpdates = 0.0;
8575f757f3fSDimitry Andric   double HintWeights = 0.0;
8585f757f3fSDimitry Andric   float StartBBFreq = 0.0;
8595f757f3fSDimitry Andric   float EndBBFreq = 0.0;
8605f757f3fSDimitry Andric   float HottestBlockFreq = 0.0;
8615f757f3fSDimitry Andric   int32_t NrRematerializable = 0;
8625f757f3fSDimitry Andric   float TotalWeight = 0.0;
8635f757f3fSDimitry Andric 
8645f757f3fSDimitry Andric   SlotIndex EndSI = LIS->getSlotIndexes()->getZeroIndex();
8655f757f3fSDimitry Andric   SlotIndex StartSI = LIS->getSlotIndexes()->getLastIndex();
8665f757f3fSDimitry Andric   int64_t MaxStage = 0;
8675f757f3fSDimitry Andric   int64_t MinStage =
8685f757f3fSDimitry Andric       Intervals.empty() ? 0 : std::numeric_limits<int64_t>::max();
8695f757f3fSDimitry Andric 
8705f757f3fSDimitry Andric   for (const auto *L : Intervals) {
8715f757f3fSDimitry Andric     const LiveInterval &LI = *L;
8725f757f3fSDimitry Andric     MaxStage = std::max<int64_t>(
8735f757f3fSDimitry Andric         MaxStage, static_cast<int64_t>(RA.getExtraInfo().getStage(LI)));
8745f757f3fSDimitry Andric     MinStage = std::min<int64_t>(
8755f757f3fSDimitry Andric         MinStage, static_cast<int64_t>(RA.getExtraInfo().getStage(LI)));
8765f757f3fSDimitry Andric 
8775f757f3fSDimitry Andric     TotalWeight = std::max(TotalWeight, LI.weight());
8785f757f3fSDimitry Andric 
8795f757f3fSDimitry Andric     if (LI.beginIndex() < StartSI)
8805f757f3fSDimitry Andric       StartSI = LI.beginIndex();
8815f757f3fSDimitry Andric 
8825f757f3fSDimitry Andric     if (LI.endIndex() > EndSI)
8835f757f3fSDimitry Andric       EndSI = LI.endIndex();
8845f757f3fSDimitry Andric     const LIFeatureComponents &LIFC = getLIFeatureComponents(LI);
8855f757f3fSDimitry Andric     NrBrokenHints += VRM->hasPreferredPhys(LI.reg());
8865f757f3fSDimitry Andric 
8875f757f3fSDimitry Andric     NrDefsAndUses += LIFC.NrDefsAndUses;
8885f757f3fSDimitry Andric     HottestBlockFreq = std::max(HottestBlockFreq, LIFC.HottestBlockFreq);
8895f757f3fSDimitry Andric     R += LIFC.R;
8905f757f3fSDimitry Andric     W += LIFC.W;
8915f757f3fSDimitry Andric     RW += LIFC.RW;
8925f757f3fSDimitry Andric 
8935f757f3fSDimitry Andric     IndVarUpdates += LIFC.IndVarUpdates;
8945f757f3fSDimitry Andric 
8955f757f3fSDimitry Andric     HintWeights += LIFC.HintWeights;
8965f757f3fSDimitry Andric     NrRematerializable += LIFC.IsRemat;
8975f757f3fSDimitry Andric 
8985f757f3fSDimitry Andric     if (EnableDevelopmentFeatures) {
8995f757f3fSDimitry Andric       for (auto CurrentSegment : LI) {
9005f757f3fSDimitry Andric         LRPosInfo.push_back(
9015f757f3fSDimitry Andric             LRStartEndInfo{CurrentSegment.start, CurrentSegment.end, Pos});
9025f757f3fSDimitry Andric       }
9035f757f3fSDimitry Andric     }
9045f757f3fSDimitry Andric   }
9055f757f3fSDimitry Andric   size_t Size = 0;
9065f757f3fSDimitry Andric   if (!Intervals.empty()) {
9075f757f3fSDimitry Andric     StartBBFreq =
9085f757f3fSDimitry Andric         MBFI.getBlockFreqRelativeToEntryBlock(LIS->getMBBFromIndex(StartSI));
9095f757f3fSDimitry Andric     if (EndSI >= LIS->getSlotIndexes()->getLastIndex())
9105f757f3fSDimitry Andric       EndSI = LIS->getSlotIndexes()->getLastIndex().getPrevIndex();
9115f757f3fSDimitry Andric     EndBBFreq =
9125f757f3fSDimitry Andric         MBFI.getBlockFreqRelativeToEntryBlock(LIS->getMBBFromIndex(EndSI));
9135f757f3fSDimitry Andric     Size = StartSI.distance(EndSI);
9145f757f3fSDimitry Andric   }
9155f757f3fSDimitry Andric   // Set the features at the column 'Pos'.
9165f757f3fSDimitry Andric #define SET(ID, TYPE, VAL)                                                     \
9175f757f3fSDimitry Andric   do {                                                                         \
9185f757f3fSDimitry Andric     Runner->getTensor<TYPE>(FeatureIDs::ID)[Pos] = static_cast<TYPE>(VAL);     \
9195f757f3fSDimitry Andric     if (!DoNotNormalize.test(FeatureIDs::ID))                                  \
9205f757f3fSDimitry Andric       Largest[FeatureIDs::ID] =                                                \
9215f757f3fSDimitry Andric           std::max(Largest[FeatureIDs::ID], static_cast<float>(VAL));          \
9225f757f3fSDimitry Andric   } while (false)
9235f757f3fSDimitry Andric   SET(mask, int64_t, 1);
9245f757f3fSDimitry Andric   SET(is_free, int64_t, Intervals.empty());
9255f757f3fSDimitry Andric   SET(nr_urgent, float, NrUrgent);
9265f757f3fSDimitry Andric   SET(nr_broken_hints, float, NrBrokenHints);
9275f757f3fSDimitry Andric   SET(is_hint, int64_t, IsHint);
9285f757f3fSDimitry Andric   SET(is_local, int64_t, LocalIntfsCount);
9295f757f3fSDimitry Andric   SET(nr_rematerializable, float, NrRematerializable);
9305f757f3fSDimitry Andric   SET(nr_defs_and_uses, float, NrDefsAndUses);
9315f757f3fSDimitry Andric   SET(weighed_reads_by_max, float, R);
9325f757f3fSDimitry Andric   SET(weighed_writes_by_max, float, W);
9335f757f3fSDimitry Andric   SET(weighed_read_writes_by_max, float, RW);
9345f757f3fSDimitry Andric   SET(weighed_indvars_by_max, float, IndVarUpdates);
9355f757f3fSDimitry Andric   SET(hint_weights_by_max, float, HintWeights);
9365f757f3fSDimitry Andric   SET(start_bb_freq_by_max, float, StartBBFreq);
9375f757f3fSDimitry Andric   SET(end_bb_freq_by_max, float, EndBBFreq);
9385f757f3fSDimitry Andric   SET(hottest_bb_freq_by_max, float, HottestBlockFreq);
9395f757f3fSDimitry Andric   SET(liverange_size, float, Size);
9405f757f3fSDimitry Andric   SET(use_def_density, float, TotalWeight);
9415f757f3fSDimitry Andric   SET(max_stage, int64_t, MaxStage);
9425f757f3fSDimitry Andric   SET(min_stage, int64_t, MinStage);
9435f757f3fSDimitry Andric #undef SET
9445f757f3fSDimitry Andric }
9455f757f3fSDimitry Andric 
9465f757f3fSDimitry Andric void extractInstructionFeatures(
9475f757f3fSDimitry Andric     SmallVectorImpl<LRStartEndInfo> &LRPosInfo, MLModelRunner *RegallocRunner,
9485f757f3fSDimitry Andric     function_ref<int(SlotIndex)> GetOpcode,
9495f757f3fSDimitry Andric     function_ref<float(SlotIndex)> GetMBBFreq,
9505f757f3fSDimitry Andric     function_ref<MachineBasicBlock *(SlotIndex)> GetMBBReference,
9515f757f3fSDimitry Andric     const int InstructionsIndex, const int InstructionsMappingIndex,
9525f757f3fSDimitry Andric     const int MBBFreqIndex, const int MBBMappingIndex,
9535f757f3fSDimitry Andric     const SlotIndex LastIndex) {
9545f757f3fSDimitry Andric   // This function extracts instruction based features relevant to the eviction
9555f757f3fSDimitry Andric   // problem currently being solved. This function ends up extracting two
9565f757f3fSDimitry Andric   // tensors.
9575f757f3fSDimitry Andric   // 1 - A vector of size max instruction count. It contains the opcodes of the
9585f757f3fSDimitry Andric   // instructions spanned by all the intervals in the current instance of the
9595f757f3fSDimitry Andric   // eviction problem.
9605f757f3fSDimitry Andric   // 2 - A binary mapping matrix of size (LR count * max
9615f757f3fSDimitry Andric   // instruction count) which maps where the LRs are live to the actual opcodes
9625f757f3fSDimitry Andric   // for which they are live.
9635f757f3fSDimitry Andric   // 3 - A vector of size max supported MBB count storing MBB frequencies,
9645f757f3fSDimitry Andric   // encompassing all of the MBBs covered by the eviction problem.
9655f757f3fSDimitry Andric   // 4 - A vector of size max instruction count of indices to members of the MBB
9665f757f3fSDimitry Andric   // frequency vector, mapping each instruction to its associated MBB.
9675f757f3fSDimitry Andric 
9685f757f3fSDimitry Andric   // Start off by sorting the segments based on the beginning slot index.
9695f757f3fSDimitry Andric   std::sort(
9705f757f3fSDimitry Andric       LRPosInfo.begin(), LRPosInfo.end(),
9715f757f3fSDimitry Andric       [](LRStartEndInfo A, LRStartEndInfo B) { return A.Begin < B.Begin; });
9725f757f3fSDimitry Andric   size_t InstructionIndex = 0;
9735f757f3fSDimitry Andric   size_t CurrentSegmentIndex = 0;
9745f757f3fSDimitry Andric   SlotIndex CurrentIndex = LRPosInfo[0].Begin;
9755f757f3fSDimitry Andric   std::map<MachineBasicBlock *, size_t> VisitedMBBs;
9765f757f3fSDimitry Andric   size_t CurrentMBBIndex = 0;
9775f757f3fSDimitry Andric   // This loop processes all the segments sequentially by starting at the
9785f757f3fSDimitry Andric   // beginning slot index of the first segment, iterating through all the slot
9795f757f3fSDimitry Andric   // indices before the end slot index of that segment (while checking for
9805f757f3fSDimitry Andric   // overlaps with segments that start at greater slot indices). After hitting
9815f757f3fSDimitry Andric   // that end index, the current segment being processed gets bumped until they
9825f757f3fSDimitry Andric   // are all processed or the max instruction count is hit, where everything is
9835f757f3fSDimitry Andric   // just truncated.
9845f757f3fSDimitry Andric   while (true) {
9855f757f3fSDimitry Andric     // If the index that we are currently at is within the current segment and
9865f757f3fSDimitry Andric     // we haven't hit the max instruction count, continue processing the current
9875f757f3fSDimitry Andric     // segment.
9885f757f3fSDimitry Andric     while (CurrentIndex <= LRPosInfo[CurrentSegmentIndex].End &&
9895f757f3fSDimitry Andric            InstructionIndex < ModelMaxSupportedInstructionCount) {
9905f757f3fSDimitry Andric       int CurrentOpcode = GetOpcode(CurrentIndex);
9915f757f3fSDimitry Andric       // If the current machine instruction is null, skip it
9925f757f3fSDimitry Andric       if (CurrentOpcode == -1) {
9935f757f3fSDimitry Andric         // If we're currently at the last index in the SlotIndex analysis,
9945f757f3fSDimitry Andric         // we can't go any further, so return from the function
9955f757f3fSDimitry Andric         if (CurrentIndex >= LastIndex) {
9965f757f3fSDimitry Andric           return;
9975f757f3fSDimitry Andric         }
9985f757f3fSDimitry Andric         CurrentIndex = CurrentIndex.getNextIndex();
9995f757f3fSDimitry Andric         continue;
10005f757f3fSDimitry Andric       }
10015f757f3fSDimitry Andric       MachineBasicBlock *CurrentMBBReference = GetMBBReference(CurrentIndex);
10025f757f3fSDimitry Andric       if (VisitedMBBs.count(CurrentMBBReference) == 0) {
10035f757f3fSDimitry Andric         VisitedMBBs[CurrentMBBReference] = CurrentMBBIndex;
10045f757f3fSDimitry Andric         ++CurrentMBBIndex;
10055f757f3fSDimitry Andric       }
10065f757f3fSDimitry Andric       extractMBBFrequency(CurrentIndex, InstructionIndex, VisitedMBBs,
10075f757f3fSDimitry Andric                           GetMBBFreq, CurrentMBBReference, RegallocRunner,
10085f757f3fSDimitry Andric                           MBBFreqIndex, MBBMappingIndex);
10095f757f3fSDimitry Andric       // Current code assumes we're not going to get any disjointed segments
10105f757f3fSDimitry Andric       assert(LRPosInfo[CurrentSegmentIndex].Begin <= CurrentIndex);
10115f757f3fSDimitry Andric       RegallocRunner->getTensor<int64_t>(InstructionsIndex)[InstructionIndex] =
10125f757f3fSDimitry Andric           CurrentOpcode < OpcodeValueCutoff ? CurrentOpcode : 0;
10135f757f3fSDimitry Andric       // set value in the binary mapping matrix for the current instruction
10145f757f3fSDimitry Andric       auto CurrentSegmentPosition = LRPosInfo[CurrentSegmentIndex].Pos;
10155f757f3fSDimitry Andric       RegallocRunner->getTensor<int64_t>(
10165f757f3fSDimitry Andric           InstructionsMappingIndex)[CurrentSegmentPosition *
10175f757f3fSDimitry Andric                                         ModelMaxSupportedInstructionCount +
10185f757f3fSDimitry Andric                                     InstructionIndex] = 1;
10195f757f3fSDimitry Andric       // All of the segments are sorted based on the beginning slot index, but
10205f757f3fSDimitry Andric       // this doesn't mean that the beginning slot index of the next segment is
10215f757f3fSDimitry Andric       // after the end segment of the one being currently processed. This while
10225f757f3fSDimitry Andric       // loop checks for overlapping segments and modifies the portion of the
10235f757f3fSDimitry Andric       // column in the mapping matrix for the currently processed instruction
10245f757f3fSDimitry Andric       // for the LR it is checking. Also make sure that the beginning of the
10255f757f3fSDimitry Andric       // current segment we're checking for overlap in is less than the current
10265f757f3fSDimitry Andric       // index, otherwise we're done checking overlaps.
10275f757f3fSDimitry Andric       size_t OverlapCheckCurrentSegment = CurrentSegmentIndex + 1;
10285f757f3fSDimitry Andric       while (OverlapCheckCurrentSegment < LRPosInfo.size() &&
10295f757f3fSDimitry Andric              LRPosInfo[OverlapCheckCurrentSegment].Begin <= CurrentIndex) {
10305f757f3fSDimitry Andric         auto OverlapCurrentSegmentPosition =
10315f757f3fSDimitry Andric             LRPosInfo[OverlapCheckCurrentSegment].Pos;
10325f757f3fSDimitry Andric         if (LRPosInfo[OverlapCheckCurrentSegment].End >= CurrentIndex) {
10335f757f3fSDimitry Andric           RegallocRunner->getTensor<int64_t>(
10345f757f3fSDimitry Andric               InstructionsMappingIndex)[OverlapCurrentSegmentPosition *
10355f757f3fSDimitry Andric                                             ModelMaxSupportedInstructionCount +
10365f757f3fSDimitry Andric                                         InstructionIndex] = 1;
10375f757f3fSDimitry Andric         }
10385f757f3fSDimitry Andric         ++OverlapCheckCurrentSegment;
10395f757f3fSDimitry Andric       }
10405f757f3fSDimitry Andric       ++InstructionIndex;
10415f757f3fSDimitry Andric       if (CurrentIndex >= LastIndex) {
10425f757f3fSDimitry Andric         return;
10435f757f3fSDimitry Andric       }
10445f757f3fSDimitry Andric       CurrentIndex = CurrentIndex.getNextIndex();
10455f757f3fSDimitry Andric     }
10465f757f3fSDimitry Andric     // if we've just finished processing through the last segment or if we've
10475f757f3fSDimitry Andric     // hit the maximum number of instructions, break out of the loop.
10485f757f3fSDimitry Andric     if (CurrentSegmentIndex == LRPosInfo.size() - 1 ||
10495f757f3fSDimitry Andric         InstructionIndex >= ModelMaxSupportedInstructionCount) {
10505f757f3fSDimitry Andric       break;
10515f757f3fSDimitry Andric     }
10525f757f3fSDimitry Andric     // If the segments are not overlapping, we need to move to the beginning
10535f757f3fSDimitry Andric     // index of the next segment to avoid having instructions not attached to
10545f757f3fSDimitry Andric     // any register.
10555f757f3fSDimitry Andric     if (LRPosInfo[CurrentSegmentIndex + 1].Begin >
10565f757f3fSDimitry Andric         LRPosInfo[CurrentSegmentIndex].End) {
10575f757f3fSDimitry Andric       CurrentIndex = LRPosInfo[CurrentSegmentIndex + 1].Begin;
10585f757f3fSDimitry Andric     }
10595f757f3fSDimitry Andric     ++CurrentSegmentIndex;
10605f757f3fSDimitry Andric   }
10615f757f3fSDimitry Andric }
10625f757f3fSDimitry Andric 
10635f757f3fSDimitry Andric void extractMBBFrequency(const SlotIndex CurrentIndex,
10645f757f3fSDimitry Andric                          const size_t CurrentInstructionIndex,
10655f757f3fSDimitry Andric                          std::map<MachineBasicBlock *, size_t> &VisitedMBBs,
10665f757f3fSDimitry Andric                          function_ref<float(SlotIndex)> GetMBBFreq,
10675f757f3fSDimitry Andric                          MachineBasicBlock *CurrentMBBReference,
10685f757f3fSDimitry Andric                          MLModelRunner *RegallocRunner, const int MBBFreqIndex,
10695f757f3fSDimitry Andric                          const int MBBMappingIndex) {
10705f757f3fSDimitry Andric   size_t CurrentMBBIndex = VisitedMBBs[CurrentMBBReference];
10715f757f3fSDimitry Andric   float CurrentMBBFreq = GetMBBFreq(CurrentIndex);
10725f757f3fSDimitry Andric   if (CurrentMBBIndex < ModelMaxSupportedMBBCount) {
10735f757f3fSDimitry Andric     RegallocRunner->getTensor<float>(MBBFreqIndex)[CurrentMBBIndex] =
10745f757f3fSDimitry Andric         CurrentMBBFreq;
10755f757f3fSDimitry Andric     RegallocRunner->getTensor<int64_t>(
10765f757f3fSDimitry Andric         MBBMappingIndex)[CurrentInstructionIndex] = CurrentMBBIndex;
10775f757f3fSDimitry Andric   }
10785f757f3fSDimitry Andric }
10795f757f3fSDimitry Andric 
10805f757f3fSDimitry Andric // Development mode-specific implementations
10815f757f3fSDimitry Andric #ifdef LLVM_HAVE_TFLITE
10825f757f3fSDimitry Andric 
10835f757f3fSDimitry Andric RegAllocEvictionAdvisorAnalysis *llvm::createDevelopmentModeAdvisor() {
10845f757f3fSDimitry Andric   return new DevelopmentModeEvictionAdvisorAnalysis();
10855f757f3fSDimitry Andric }
10865f757f3fSDimitry Andric 
10875f757f3fSDimitry Andric int64_t DevelopmentModeEvictAdvisor::tryFindEvictionCandidatePosition(
10885f757f3fSDimitry Andric     const LiveInterval &VirtReg, const AllocationOrder &Order,
10895f757f3fSDimitry Andric     unsigned OrderLimit, uint8_t CostPerUseLimit,
10905f757f3fSDimitry Andric     const SmallVirtRegSet &FixedRegisters) const {
10915f757f3fSDimitry Andric   int64_t Ret = 0;
10925f757f3fSDimitry Andric   if (isa<ModelUnderTrainingRunner>(getRunner())) {
10935f757f3fSDimitry Andric     Ret = MLEvictAdvisor::tryFindEvictionCandidatePosition(
10945f757f3fSDimitry Andric         VirtReg, Order, OrderLimit, CostPerUseLimit, FixedRegisters);
10955f757f3fSDimitry Andric   } else {
10965f757f3fSDimitry Andric     MCRegister PhysReg = getDefaultAdvisor().tryFindEvictionCandidate(
10975f757f3fSDimitry Andric         VirtReg, Order, CostPerUseLimit, FixedRegisters);
10985f757f3fSDimitry Andric     // Find the index of the selected PhysReg. We need it for logging,
10995f757f3fSDimitry Andric     // otherwise this is wasted cycles (but so would starting development mode
11005f757f3fSDimitry Andric     // without a model nor logging)
11015f757f3fSDimitry Andric     if (!PhysReg)
11025f757f3fSDimitry Andric       Ret = CandidateVirtRegPos;
11035f757f3fSDimitry Andric     else
11045f757f3fSDimitry Andric       for (auto I = Order.begin(), E = Order.getOrderLimitEnd(OrderLimit);
11055f757f3fSDimitry Andric            I != E; ++I, ++Ret)
11065f757f3fSDimitry Andric         if (*I == PhysReg)
11075f757f3fSDimitry Andric           break;
11085f757f3fSDimitry Andric   }
11095f757f3fSDimitry Andric   if (TrainingLog.empty())
11105f757f3fSDimitry Andric     return Ret;
11115f757f3fSDimitry Andric   // TODO(mtrofin): when we support optional rewards, this can go away. In the
11125f757f3fSDimitry Andric   // meantime, we log the "pretend" reward (0) for the previous observation
11135f757f3fSDimitry Andric   // before starting a new one.
11145f757f3fSDimitry Andric   if (Log->hasObservationInProgress())
11155f757f3fSDimitry Andric     Log->logReward<float>(0.0);
11165f757f3fSDimitry Andric 
11175f757f3fSDimitry Andric   Log->startObservation();
11185f757f3fSDimitry Andric   size_t CurrentFeature = 0;
11195f757f3fSDimitry Andric   size_t FeatureCount = EnableDevelopmentFeatures
11205f757f3fSDimitry Andric                             ? FeatureIDs::FeaturesWithDevelopmentCount
11215f757f3fSDimitry Andric                             : FeatureIDs::FeatureCount;
11225f757f3fSDimitry Andric   for (; CurrentFeature < FeatureCount; ++CurrentFeature) {
11235f757f3fSDimitry Andric     Log->logTensorValue(CurrentFeature,
11245f757f3fSDimitry Andric                         reinterpret_cast<const char *>(
11255f757f3fSDimitry Andric                             getRunner().getTensorUntyped(CurrentFeature)));
11265f757f3fSDimitry Andric   }
11275f757f3fSDimitry Andric   if (auto *MUTR = dyn_cast<ModelUnderTrainingRunner>(&getRunner()))
11285f757f3fSDimitry Andric     for (size_t I = 0; I < MUTR->extraOutputsForLoggingSpecs().size();
11295f757f3fSDimitry Andric          ++I, ++CurrentFeature)
11305f757f3fSDimitry Andric       Log->logTensorValue(
11315f757f3fSDimitry Andric           CurrentFeature,
11325f757f3fSDimitry Andric           reinterpret_cast<const char *>(MUTR->getUntypedExtraOutputValue(I)));
11335f757f3fSDimitry Andric   // The output is right after the features and the extra outputs
11345f757f3fSDimitry Andric   Log->logTensorValue(CurrentFeature, reinterpret_cast<const char *>(&Ret));
11355f757f3fSDimitry Andric   Log->endObservation();
11365f757f3fSDimitry Andric   return Ret;
11375f757f3fSDimitry Andric }
11385f757f3fSDimitry Andric 
11395f757f3fSDimitry Andric bool RegAllocScoring::runOnMachineFunction(MachineFunction &MF) {
11405f757f3fSDimitry Andric   std::optional<float> CachedReward;
11415f757f3fSDimitry Andric   auto GetReward = [&]() {
11425f757f3fSDimitry Andric     if (!CachedReward)
11435f757f3fSDimitry Andric       CachedReward = static_cast<float>(
1144*0fca6ea1SDimitry Andric           calculateRegAllocScore(
1145*0fca6ea1SDimitry Andric               MF, getAnalysis<MachineBlockFrequencyInfoWrapperPass>().getMBFI())
11465f757f3fSDimitry Andric               .getScore());
11475f757f3fSDimitry Andric     return *CachedReward;
11485f757f3fSDimitry Andric   };
11495f757f3fSDimitry Andric 
11505f757f3fSDimitry Andric   getAnalysis<RegAllocEvictionAdvisorAnalysis>().logRewardIfNeeded(MF,
11515f757f3fSDimitry Andric                                                                    GetReward);
11525f757f3fSDimitry Andric   getAnalysis<RegAllocPriorityAdvisorAnalysis>().logRewardIfNeeded(MF,
11535f757f3fSDimitry Andric                                                                    GetReward);
11545f757f3fSDimitry Andric   return false;
11555f757f3fSDimitry Andric }
11565f757f3fSDimitry Andric #endif // #ifdef LLVM_HAVE_TFLITE
11575f757f3fSDimitry Andric 
11585f757f3fSDimitry Andric RegAllocEvictionAdvisorAnalysis *llvm::createReleaseModeAdvisor() {
11595f757f3fSDimitry Andric   return llvm::isEmbeddedModelEvaluatorValid<CompiledModelType>() ||
11605f757f3fSDimitry Andric                  !InteractiveChannelBaseName.empty()
11615f757f3fSDimitry Andric              ? new ReleaseModeEvictionAdvisorAnalysis()
11625f757f3fSDimitry Andric              : nullptr;
11635f757f3fSDimitry Andric }
11645f757f3fSDimitry Andric 
11655f757f3fSDimitry Andric // In all cases except development mode, we don't need scoring.
11665f757f3fSDimitry Andric #if !defined(LLVM_HAVE_TFLITE)
11675f757f3fSDimitry Andric bool RegAllocScoring::runOnMachineFunction(MachineFunction &) { return false; }
11685f757f3fSDimitry Andric #endif
1169