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