168ac7b17SMircea Trofin //===- MLRegAllocEvictAdvisor.cpp - ML eviction advisor -------------------===// 268ac7b17SMircea Trofin // 368ac7b17SMircea Trofin // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 468ac7b17SMircea Trofin // See https://llvm.org/LICENSE.txt for license information. 568ac7b17SMircea Trofin // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 668ac7b17SMircea Trofin // 768ac7b17SMircea Trofin //===----------------------------------------------------------------------===// 868ac7b17SMircea Trofin // 968ac7b17SMircea Trofin // Implementation of the ML eviction advisor and reward injection pass 1068ac7b17SMircea Trofin // 1168ac7b17SMircea Trofin //===----------------------------------------------------------------------===// 1268ac7b17SMircea Trofin 1368ac7b17SMircea Trofin #include "RegAllocEvictionAdvisor.h" 1468ac7b17SMircea Trofin #include "llvm/Analysis/MLModelRunner.h" 1568ac7b17SMircea Trofin #include "llvm/Analysis/ModelUnderTrainingRunner.h" 1668ac7b17SMircea Trofin #include "llvm/Analysis/NoInferenceModelRunner.h" 1768ac7b17SMircea Trofin #include "llvm/Analysis/Utils/TFUtils.h" 1868ac7b17SMircea Trofin #include "llvm/CodeGen/CalcSpillWeights.h" 1968ac7b17SMircea Trofin #include "llvm/CodeGen/MachineBlockFrequencyInfo.h" 2068ac7b17SMircea Trofin #include "llvm/CodeGen/MachineFunction.h" 2168ac7b17SMircea Trofin #include "llvm/CodeGen/MachineLoopInfo.h" 2268ac7b17SMircea Trofin #include "llvm/CodeGen/RegisterClassInfo.h" 2368ac7b17SMircea Trofin #include "llvm/CodeGen/VirtRegMap.h" 24*3150bce0SMircea Trofin #include "llvm/Config/config.h" 2568ac7b17SMircea Trofin #include "llvm/InitializePasses.h" 2668ac7b17SMircea Trofin #include "llvm/Pass.h" 2768ac7b17SMircea Trofin #include "llvm/PassRegistry.h" 2868ac7b17SMircea Trofin #include "llvm/Support/CommandLine.h" 2968ac7b17SMircea Trofin #include "llvm/Support/ErrorHandling.h" 3068ac7b17SMircea Trofin #include "llvm/Target/TargetMachine.h" 3168ac7b17SMircea Trofin 3268ac7b17SMircea Trofin #include <memory> 3368ac7b17SMircea Trofin 3468ac7b17SMircea Trofin using namespace llvm; 3568ac7b17SMircea Trofin 3668ac7b17SMircea Trofin #define DEBUG_TYPE "ml-regalloc" 3768ac7b17SMircea Trofin 38*3150bce0SMircea Trofin #if defined(LLVM_HAVE_TF_AOT) || defined(LLVM_HAVE_TF_API) 3968ac7b17SMircea Trofin namespace { 4068ac7b17SMircea Trofin // This is the maximum number of interfererring ranges. That's the number of 4168ac7b17SMircea Trofin // distinct AllocationOrder values, which comes from MCRegisterClass::RegsSize. 4268ac7b17SMircea Trofin // For X86, that's 32. 4368ac7b17SMircea Trofin // TODO: find a way to get this, statically, in a programmatic way. 4468ac7b17SMircea Trofin static const int64_t MaxInterferences = 32; 4568ac7b17SMircea Trofin 4668ac7b17SMircea Trofin // Logically, we can think of the feature set given to the evaluator as a 2D 4768ac7b17SMircea Trofin // matrix. The rows are the features (see next). The columns correspond to the 4868ac7b17SMircea Trofin // interferences. We treat the candidate virt reg as an 'interference', too, as 4968ac7b17SMircea Trofin // its feature set is the same as that of the interferring ranges. So we'll have 5068ac7b17SMircea Trofin // MaxInterferences + 1 columns and by convention, we will use the last column 5168ac7b17SMircea Trofin // for the virt reg seeking allocation. 5268ac7b17SMircea Trofin static const int64_t CandidateVirtRegPos = MaxInterferences; 5368ac7b17SMircea Trofin static const int64_t NumberOfInterferences = CandidateVirtRegPos + 1; 5468ac7b17SMircea Trofin 5568ac7b17SMircea Trofin // Most features are as described above, so we'll reuse this vector in defining 5668ac7b17SMircea Trofin // them. 5768ac7b17SMircea Trofin static const std::vector<int64_t> PerLiveRangeShape{1, NumberOfInterferences}; 5868ac7b17SMircea Trofin 5968ac7b17SMircea Trofin // -------------- 6068ac7b17SMircea Trofin // Features table 6168ac7b17SMircea Trofin // -------------- 6268ac7b17SMircea Trofin // For each interfering live range (incl. the candidate) we collect a number of 6368ac7b17SMircea Trofin // features. However, because the features are of different types (and because 6468ac7b17SMircea Trofin // of ML best practices), we organize the tensors per feature, not per 6568ac7b17SMircea Trofin // candidate. Each such tensor has a scalar value corresponding to the 6668ac7b17SMircea Trofin // interferring live range at that position, in the order in AllocationOrder. 6768ac7b17SMircea Trofin // The last position corresponds to the virt reg seeking allocation. 6868ac7b17SMircea Trofin // Exception to all that is the progression feature, which is just a scalar (see 6968ac7b17SMircea Trofin // its documentation for details). 7068ac7b17SMircea Trofin // Note on naming: the "_by_max" are normalized using the largest value of that 7168ac7b17SMircea Trofin // tensor, as observed in the current decision making stage (i.e. for the 7268ac7b17SMircea Trofin // current call to the advisor's tryFindEvictionCandidate) 7368ac7b17SMircea Trofin // 7468ac7b17SMircea Trofin // The feature list format: type, name, shape, documentation. 7568ac7b17SMircea Trofin // Note: we can really just use int64 and float, hence the modeling of some 7668ac7b17SMircea Trofin // bools as int64 values. 7768ac7b17SMircea Trofin #define RA_EVICT_FEATURES_LIST(M) \ 7868ac7b17SMircea Trofin M(int64_t, mask, PerLiveRangeShape, \ 7968ac7b17SMircea Trofin "boolean values, 0 for unavailable candidates (i.e. if a position is 0, " \ 8068ac7b17SMircea Trofin "it " \ 8168ac7b17SMircea Trofin "can't be evicted)") \ 8268ac7b17SMircea Trofin M(int64_t, is_free, PerLiveRangeShape, \ 8368ac7b17SMircea Trofin "boolean values, 1 if this phys reg is actually free (no interferences)") \ 8468ac7b17SMircea Trofin M(float, nr_urgent, PerLiveRangeShape, \ 8568ac7b17SMircea Trofin "number of 'urgent' intervals, normalized. Urgent are those that are OK " \ 8668ac7b17SMircea Trofin "to break cascades") \ 8768ac7b17SMircea Trofin M(float, nr_broken_hints, PerLiveRangeShape, \ 8868ac7b17SMircea Trofin "if this position were evicted, how many broken hints would there be") \ 8968ac7b17SMircea Trofin M(int64_t, is_hint, PerLiveRangeShape, \ 9068ac7b17SMircea Trofin "is this a preferred phys reg for the candidate") \ 9168ac7b17SMircea Trofin M(int64_t, is_local, PerLiveRangeShape, \ 9268ac7b17SMircea Trofin "is this live range local to a basic block") \ 9368ac7b17SMircea Trofin M(float, nr_rematerializable, PerLiveRangeShape, \ 9468ac7b17SMircea Trofin "nr rematerializable ranges") \ 9568ac7b17SMircea Trofin M(float, nr_defs_and_uses, PerLiveRangeShape, \ 9668ac7b17SMircea Trofin "bb freq - weighed nr defs and uses") \ 9768ac7b17SMircea Trofin M(float, weighed_reads_by_max, PerLiveRangeShape, \ 9868ac7b17SMircea Trofin "bb freq - weighed nr of reads, normalized") \ 9968ac7b17SMircea Trofin M(float, weighed_writes_by_max, PerLiveRangeShape, \ 10068ac7b17SMircea Trofin "bb feq - weighed nr of writes, normalized") \ 10168ac7b17SMircea Trofin M(float, weighed_read_writes_by_max, PerLiveRangeShape, \ 10268ac7b17SMircea Trofin "bb freq - weighed nr of uses that are both read and writes, normalized") \ 10368ac7b17SMircea Trofin M(float, weighed_indvars_by_max, PerLiveRangeShape, \ 10468ac7b17SMircea Trofin "bb freq - weighed nr of uses that are indvars, normalized") \ 10568ac7b17SMircea Trofin M(float, hint_weights_by_max, PerLiveRangeShape, \ 10668ac7b17SMircea Trofin "bb freq - weighed nr of uses that are hints, normalized") \ 10768ac7b17SMircea Trofin M(float, start_bb_freq_by_max, PerLiveRangeShape, \ 10868ac7b17SMircea Trofin "the freq in the start block, normalized") \ 10968ac7b17SMircea Trofin M(float, end_bb_freq_by_max, PerLiveRangeShape, \ 11068ac7b17SMircea Trofin "freq of end block, normalized") \ 11168ac7b17SMircea Trofin M(float, hottest_bb_freq_by_max, PerLiveRangeShape, \ 11268ac7b17SMircea Trofin "hottest BB freq, normalized") \ 11368ac7b17SMircea Trofin M(float, liverange_size, PerLiveRangeShape, \ 11468ac7b17SMircea Trofin "size (instr index diff) of the LR") \ 11568ac7b17SMircea Trofin M(float, use_def_density, PerLiveRangeShape, \ 11668ac7b17SMircea Trofin "the max weight, as computed by the manual heuristic") \ 11768ac7b17SMircea Trofin M(int64_t, max_stage, PerLiveRangeShape, \ 11868ac7b17SMircea Trofin "largest stage of an interval in this LR") \ 11968ac7b17SMircea Trofin M(int64_t, min_stage, PerLiveRangeShape, \ 12068ac7b17SMircea Trofin "lowest stage of an interval in this LR") \ 12168ac7b17SMircea Trofin M(float, progress, {1}, "ratio of current queue size to initial size") 12268ac7b17SMircea Trofin 12368ac7b17SMircea Trofin // The model learns to pick one of the mask == 1 interferences. This is the name 12468ac7b17SMircea Trofin // of the output tensor. 12568ac7b17SMircea Trofin // The contract with the model is that the output will be guaranteed to be to a 12668ac7b17SMircea Trofin // mask == 1 position. 12768ac7b17SMircea Trofin const char *const DecisionName = "index_to_evict"; 12868ac7b17SMircea Trofin 12968ac7b17SMircea Trofin // Named features index. 13068ac7b17SMircea Trofin enum FeatureIDs { 13168ac7b17SMircea Trofin #define _FEATURE_IDX(_, name, __, ___) name, 13268ac7b17SMircea Trofin RA_EVICT_FEATURES_LIST(_FEATURE_IDX) 13368ac7b17SMircea Trofin #undef _FEATURE_IDX 13468ac7b17SMircea Trofin FeatureCount 13568ac7b17SMircea Trofin }; 13668ac7b17SMircea Trofin 13768ac7b17SMircea Trofin // The ML advisor will typically have a sparse input to the evaluator, because 13868ac7b17SMircea Trofin // various phys regs won't be available. It's easier (maintenance-wise) to 13968ac7b17SMircea Trofin // bulk-reset the state of the evaluator each time we are about to use it again. 14068ac7b17SMircea Trofin template <typename T> size_t getTotalSize(const std::vector<int64_t> &Shape) { 14168ac7b17SMircea Trofin size_t Ret = sizeof(T); 14268ac7b17SMircea Trofin for (const auto V : Shape) 14368ac7b17SMircea Trofin Ret *= V; 14468ac7b17SMircea Trofin return Ret; 14568ac7b17SMircea Trofin } 14668ac7b17SMircea Trofin 14768ac7b17SMircea Trofin void resetInputs(MLModelRunner &Runner) { 14868ac7b17SMircea Trofin #define _RESET(TYPE, NAME, SHAPE, __) \ 14968ac7b17SMircea Trofin std::memset(Runner.getTensorUntyped(FeatureIDs::NAME), 0, \ 15068ac7b17SMircea Trofin getTotalSize<TYPE>(SHAPE)); 15168ac7b17SMircea Trofin RA_EVICT_FEATURES_LIST(_RESET) 15268ac7b17SMircea Trofin #undef _RESET 15368ac7b17SMircea Trofin } 15468ac7b17SMircea Trofin 15568ac7b17SMircea Trofin // Development mode-specifics 15668ac7b17SMircea Trofin #ifdef LLVM_HAVE_TF_API 15768ac7b17SMircea Trofin #define _DECL_FEATURES(type, name, shape, _) \ 15868ac7b17SMircea Trofin TensorSpec::createSpec<type>(#name, shape), 15968ac7b17SMircea Trofin 16068ac7b17SMircea Trofin static const std::vector<TensorSpec> InputFeatures{ 16168ac7b17SMircea Trofin {RA_EVICT_FEATURES_LIST(_DECL_FEATURES)}}; 16268ac7b17SMircea Trofin #undef _DECL_FEATURES 16368ac7b17SMircea Trofin static const TensorSpec Output = 16468ac7b17SMircea Trofin TensorSpec::createSpec<int64_t>(DecisionName, {1}); 165*3150bce0SMircea Trofin static const TensorSpec Reward = TensorSpec::createSpec<int64_t>("reward", {1}); 16668ac7b17SMircea Trofin 16768ac7b17SMircea Trofin #endif //#ifdef LLVM_HAVE_TF_API 16868ac7b17SMircea Trofin } // namespace 16968ac7b17SMircea Trofin #endif // defined(LLVM_HAVE_TF_AOT) || defined(LLVM_HAVE_TF_API) 170