MLRegallocEvictAdvisor.cpp 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897
  1. //===- MLRegAllocEvictAdvisor.cpp - ML eviction advisor -------------------===//
  2. //
  3. // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  4. // See https://llvm.org/LICENSE.txt for license information.
  5. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  6. //
  7. //===----------------------------------------------------------------------===//
  8. //
  9. // Implementation of the ML eviction advisor and reward injection pass
  10. //
  11. //===----------------------------------------------------------------------===//
  12. #include "RegAllocEvictionAdvisor.h"
  13. #include "RegAllocGreedy.h"
  14. #include "RegAllocScore.h"
  15. #include "llvm/Analysis/AliasAnalysis.h"
  16. #include "llvm/Analysis/MLModelRunner.h"
  17. #include "llvm/Analysis/ModelUnderTrainingRunner.h"
  18. #include "llvm/Analysis/NoInferenceModelRunner.h"
  19. #include "llvm/Analysis/ReleaseModeModelRunner.h"
  20. #include "llvm/Analysis/Utils/TFUtils.h"
  21. #include "llvm/CodeGen/CalcSpillWeights.h"
  22. #include "llvm/CodeGen/MachineBasicBlock.h"
  23. #include "llvm/CodeGen/MachineBlockFrequencyInfo.h"
  24. #include "llvm/CodeGen/MachineFunction.h"
  25. #include "llvm/CodeGen/MachineLoopInfo.h"
  26. #include "llvm/CodeGen/MachineRegisterInfo.h"
  27. #include "llvm/CodeGen/Passes.h"
  28. #include "llvm/CodeGen/RegisterClassInfo.h"
  29. #include "llvm/CodeGen/VirtRegMap.h"
  30. #include "llvm/Config/config.h"
  31. #include "llvm/InitializePasses.h"
  32. #include "llvm/Pass.h"
  33. #include "llvm/PassRegistry.h"
  34. #include "llvm/Support/CommandLine.h"
  35. #include "llvm/Support/ErrorHandling.h"
  36. #include "llvm/Target/TargetMachine.h"
  37. #include <array>
  38. #include <memory>
  39. using namespace llvm;
  40. #define DEBUG_TYPE "ml-regalloc"
  41. // Generated header in release (AOT) mode
  42. #if defined(LLVM_HAVE_TF_AOT_REGALLOCEVICTMODEL)
  43. #error #include "RegallocEvictModel.h"
  44. #endif
  45. // Options that only make sense in development mode
  46. #ifdef LLVM_HAVE_TF_API
  47. static cl::opt<std::string> TrainingLog(
  48. "regalloc-training-log", cl::Hidden,
  49. cl::desc("Training log for the register allocator eviction model"));
  50. static cl::opt<std::string> ModelUnderTraining(
  51. "regalloc-model", cl::Hidden,
  52. cl::desc("The model being trained for register allocation eviction"));
  53. #endif // #ifdef LLVM_HAVE_TF_API
  54. /// The score injection pass.
  55. /// This pass calculates the score for a function and inserts it in the log, but
  56. /// this happens only in development mode. It's a no-op otherwise.
  57. namespace llvm {
  58. class RegAllocScoring : public MachineFunctionPass {
  59. public:
  60. static char ID;
  61. RegAllocScoring() : MachineFunctionPass(ID) {
  62. initializeRegAllocScoringPass(*PassRegistry::getPassRegistry());
  63. }
  64. ~RegAllocScoring() override = default;
  65. StringRef getPassName() const override {
  66. return "Register Allocation Pass Scoring";
  67. }
  68. /// RegAllocReward analysis usage.
  69. void getAnalysisUsage(AnalysisUsage &AU) const override {
  70. AU.setPreservesAll();
  71. AU.addRequired<RegAllocEvictionAdvisorAnalysis>();
  72. AU.addRequired<MachineBlockFrequencyInfo>();
  73. AU.addRequired<AAResultsWrapperPass>();
  74. MachineFunctionPass::getAnalysisUsage(AU);
  75. }
  76. /// Performs this pass
  77. bool runOnMachineFunction(MachineFunction &) override;
  78. };
  79. char RegAllocScoring::ID = 0;
  80. FunctionPass *createRegAllocScoringPass() { return new RegAllocScoring(); }
  81. } // namespace llvm
  82. INITIALIZE_PASS(RegAllocScoring, "regallocscoringpass",
  83. "Register Allocation Scoring Pass", false, false)
  84. // ===================================
  85. // Common ML Advisor declarations
  86. // ===================================
  87. namespace {
  88. // This is the maximum number of interfererring ranges. That's the number of
  89. // distinct AllocationOrder values, which comes from MCRegisterClass::RegsSize.
  90. // For X86, that's 32.
  91. // TODO: find a way to get this, statically, in a programmatic way.
  92. static const int64_t MaxInterferences = 32;
  93. // Logically, we can think of the feature set given to the evaluator as a 2D
  94. // matrix. The rows are the features (see next). The columns correspond to the
  95. // interferences. We treat the candidate virt reg as an 'interference', too, as
  96. // its feature set is the same as that of the interferring ranges. So we'll have
  97. // MaxInterferences + 1 columns and by convention, we will use the last column
  98. // for the virt reg seeking allocation.
  99. static const int64_t CandidateVirtRegPos = MaxInterferences;
  100. static const int64_t NumberOfInterferences = CandidateVirtRegPos + 1;
  101. // Most features are as described above, so we'll reuse this vector in defining
  102. // them.
  103. static const std::vector<int64_t> PerLiveRangeShape{1, NumberOfInterferences};
  104. // --------------
  105. // Features table
  106. // --------------
  107. // For each interfering live range (incl. the candidate) we collect a number of
  108. // features. However, because the features are of different types (and because
  109. // of ML best practices), we organize the tensors per feature, not per
  110. // candidate. Each such tensor has a scalar value corresponding to the
  111. // interferring live range at that position, in the order in AllocationOrder.
  112. // The last position corresponds to the virt reg seeking allocation.
  113. // Exception to all that is the progression feature, which is just a scalar (see
  114. // its documentation for details).
  115. // Note on naming: the "_by_max" are normalized using the largest value of that
  116. // tensor, as observed in the current decision making stage (i.e. for the
  117. // current call to the advisor's tryFindEvictionCandidate)
  118. //
  119. // The feature list format: type, name, shape, documentation.
  120. // Note: we can really just use int64 and float, hence the modeling of some
  121. // bools as int64 values.
  122. #define RA_EVICT_FEATURES_LIST(M) \
  123. M(int64_t, mask, PerLiveRangeShape, \
  124. "boolean values, 0 for unavailable candidates (i.e. if a position is 0, " \
  125. "it " \
  126. "can't be evicted)") \
  127. M(int64_t, is_free, PerLiveRangeShape, \
  128. "boolean values, 1 if this phys reg is actually free (no interferences)") \
  129. M(float, nr_urgent, PerLiveRangeShape, \
  130. "number of 'urgent' intervals, normalized. Urgent are those that are OK " \
  131. "to break cascades") \
  132. M(float, nr_broken_hints, PerLiveRangeShape, \
  133. "if this position were evicted, how many broken hints would there be") \
  134. M(int64_t, is_hint, PerLiveRangeShape, \
  135. "is this a preferred phys reg for the candidate") \
  136. M(int64_t, is_local, PerLiveRangeShape, \
  137. "is this live range local to a basic block") \
  138. M(float, nr_rematerializable, PerLiveRangeShape, \
  139. "nr rematerializable ranges") \
  140. M(float, nr_defs_and_uses, PerLiveRangeShape, \
  141. "bb freq - weighed nr defs and uses") \
  142. M(float, weighed_reads_by_max, PerLiveRangeShape, \
  143. "bb freq - weighed nr of reads, normalized") \
  144. M(float, weighed_writes_by_max, PerLiveRangeShape, \
  145. "bb feq - weighed nr of writes, normalized") \
  146. M(float, weighed_read_writes_by_max, PerLiveRangeShape, \
  147. "bb freq - weighed nr of uses that are both read and writes, normalized") \
  148. M(float, weighed_indvars_by_max, PerLiveRangeShape, \
  149. "bb freq - weighed nr of uses that are indvars, normalized") \
  150. M(float, hint_weights_by_max, PerLiveRangeShape, \
  151. "bb freq - weighed nr of uses that are hints, normalized") \
  152. M(float, start_bb_freq_by_max, PerLiveRangeShape, \
  153. "the freq in the start block, normalized") \
  154. M(float, end_bb_freq_by_max, PerLiveRangeShape, \
  155. "freq of end block, normalized") \
  156. M(float, hottest_bb_freq_by_max, PerLiveRangeShape, \
  157. "hottest BB freq, normalized") \
  158. M(float, liverange_size, PerLiveRangeShape, \
  159. "size (instr index diff) of the LR") \
  160. M(float, use_def_density, PerLiveRangeShape, \
  161. "the max weight, as computed by the manual heuristic") \
  162. M(int64_t, max_stage, PerLiveRangeShape, \
  163. "largest stage of an interval in this LR") \
  164. M(int64_t, min_stage, PerLiveRangeShape, \
  165. "lowest stage of an interval in this LR") \
  166. M(float, progress, {1}, "ratio of current queue size to initial size")
  167. // The model learns to pick one of the mask == 1 interferences. This is the name
  168. // of the output tensor.
  169. // The contract with the model is that the output will be guaranteed to be to a
  170. // mask == 1 position.
  171. // Using a macro here to avoid 'not used' warnings (and keep cond compilation to
  172. // a minimum)
  173. #define DecisionName "index_to_evict"
  174. // Named features index.
  175. enum FeatureIDs {
  176. #define _FEATURE_IDX(_, name, __, ___) name,
  177. RA_EVICT_FEATURES_LIST(_FEATURE_IDX)
  178. #undef _FEATURE_IDX
  179. FeatureCount
  180. };
  181. // The ML advisor will typically have a sparse input to the evaluator, because
  182. // various phys regs won't be available. It's easier (maintenance-wise) to
  183. // bulk-reset the state of the evaluator each time we are about to use it again.
  184. template <typename T> size_t getTotalSize(const std::vector<int64_t> &Shape) {
  185. size_t Ret = sizeof(T);
  186. for (const auto V : Shape)
  187. Ret *= V;
  188. return Ret;
  189. }
  190. void resetInputs(MLModelRunner &Runner) {
  191. #define _RESET(TYPE, NAME, SHAPE, __) \
  192. std::memset(Runner.getTensorUntyped(FeatureIDs::NAME), 0, \
  193. getTotalSize<TYPE>(SHAPE));
  194. RA_EVICT_FEATURES_LIST(_RESET)
  195. #undef _RESET
  196. }
  197. // Per-live interval components that get aggregated into the feature values that
  198. // will be passed to the evaluator.
  199. struct LIFeatureComponents {
  200. double R = 0;
  201. double W = 0;
  202. double RW = 0;
  203. double IndVarUpdates = 0;
  204. double HintWeights = 0.0;
  205. int64_t NrDefsAndUses = 0;
  206. float HottestBlockFreq = 0.0;
  207. bool IsRemat = false;
  208. };
  209. using CandidateRegList =
  210. std::array<std::pair<MCRegister, bool>, NumberOfInterferences>;
  211. using FeaturesListNormalizer = std::array<float, FeatureIDs::FeatureCount>;
  212. /// The ML evictor (commonalities between release and development mode)
  213. class MLEvictAdvisor : public RegAllocEvictionAdvisor {
  214. public:
  215. MLEvictAdvisor(MachineFunction &MF, const RAGreedy &RA, MLModelRunner *Runner,
  216. const MachineBlockFrequencyInfo &MBFI,
  217. const MachineLoopInfo &Loops);
  218. protected:
  219. const RegAllocEvictionAdvisor &getDefaultAdvisor() const {
  220. return static_cast<const RegAllocEvictionAdvisor &>(DefaultAdvisor);
  221. }
  222. // The assumption is that if the Runner could not be constructed, we emit-ed
  223. // error, and we shouldn't be asking for it here.
  224. const MLModelRunner &getRunner() const { return *Runner; }
  225. /// This just calls Evaluate on the Runner, but in the development mode case,
  226. /// if we're just capturing the log of the default advisor, it needs to call
  227. /// the latter instead, so we need to pass all the necessary parameters for
  228. /// it. In the development case, it will also log.
  229. virtual int64_t tryFindEvictionCandidatePosition(
  230. LiveInterval &VirtReg, const AllocationOrder &Order, unsigned OrderLimit,
  231. uint8_t CostPerUseLimit, const SmallVirtRegSet &FixedRegisters) const;
  232. /// Load the features of the given VirtReg (allocated or not) at column Pos,
  233. /// but if that can't be evicted, return false instead.
  234. bool
  235. loadInterferenceFeatures(LiveInterval &VirtReg, MCRegister PhysReg,
  236. bool IsHint, const SmallVirtRegSet &FixedRegisters,
  237. std::array<float, FeatureIDs::FeatureCount> &Largest,
  238. size_t Pos) const;
  239. private:
  240. static float getInitialQueueSize(const MachineFunction &MF);
  241. MCRegister tryFindEvictionCandidate(
  242. LiveInterval &VirtReg, const AllocationOrder &Order,
  243. uint8_t CostPerUseLimit,
  244. const SmallVirtRegSet &FixedRegisters) const override;
  245. void extractFeatures(const SmallVectorImpl<LiveInterval *> &Intervals,
  246. std::array<float, FeatureIDs::FeatureCount> &Largest,
  247. size_t Pos, int64_t IsHint, int64_t LocalIntfsCount,
  248. float NrUrgent) const;
  249. // Point-in-time: we didn't learn this, so we always delegate to the default.
  250. bool canEvictHintInterference(
  251. LiveInterval &VirtReg, MCRegister PhysReg,
  252. const SmallVirtRegSet &FixedRegisters) const override {
  253. return getDefaultAdvisor().canEvictHintInterference(VirtReg, PhysReg,
  254. FixedRegisters);
  255. }
  256. const LIFeatureComponents
  257. getLIFeatureComponents(const LiveInterval &LI) const;
  258. // Hold on to a default advisor for:
  259. // 1) the implementation of canEvictHintInterference, because we didn't learn
  260. // that nuance yet;
  261. // 2) for bootstrapping (logging) in the development mode case.
  262. const DefaultEvictionAdvisor DefaultAdvisor;
  263. MLModelRunner *const Runner;
  264. const MachineBlockFrequencyInfo &MBFI;
  265. const MachineLoopInfo &Loops;
  266. // Indices of those features we don't want to normalize.
  267. // This could be static and shared, but its initialization is non-trivial.
  268. std::bitset<FeatureIDs::FeatureCount> DoNotNormalize;
  269. const float InitialQSize;
  270. };
  271. // ===================================
  272. // Release (AOT) - specifics
  273. // ===================================
  274. #if defined(LLVM_HAVE_TF_AOT_REGALLOCEVICTMODEL)
  275. const std::array<std::string, FeatureIDs::FeatureCount> FeatureNames{
  276. #define _GETNAME(_, NAME, __, ___) #NAME,
  277. RA_EVICT_FEATURES_LIST(_GETNAME)
  278. #undef _GETNAME
  279. };
  280. class ReleaseModeEvictionAdvisorAnalysis final
  281. : public RegAllocEvictionAdvisorAnalysis {
  282. public:
  283. ReleaseModeEvictionAdvisorAnalysis()
  284. : RegAllocEvictionAdvisorAnalysis(AdvisorMode::Release) {}
  285. // support for isa<> and dyn_cast.
  286. static bool classof(const RegAllocEvictionAdvisorAnalysis *R) {
  287. return R->getAdvisorMode() == AdvisorMode::Release;
  288. }
  289. private:
  290. void getAnalysisUsage(AnalysisUsage &AU) const override {
  291. AU.addRequired<MachineBlockFrequencyInfo>();
  292. AU.addRequired<MachineLoopInfo>();
  293. RegAllocEvictionAdvisorAnalysis::getAnalysisUsage(AU);
  294. }
  295. std::unique_ptr<RegAllocEvictionAdvisor>
  296. getAdvisor(MachineFunction &MF, const RAGreedy &RA) override {
  297. if (!Runner)
  298. Runner = std::make_unique<ReleaseModeModelRunner<RegallocEvictModel>>(
  299. MF.getFunction().getContext(), FeatureNames, DecisionName);
  300. return std::make_unique<MLEvictAdvisor>(
  301. MF, RA, Runner.get(), getAnalysis<MachineBlockFrequencyInfo>(),
  302. getAnalysis<MachineLoopInfo>());
  303. }
  304. std::unique_ptr<ReleaseModeModelRunner<RegallocEvictModel>> Runner;
  305. };
  306. #endif
  307. // ===================================
  308. // Development mode-specifics
  309. // ===================================
  310. //
  311. // Features we log
  312. #ifdef LLVM_HAVE_TF_API
  313. #define _DECL_FEATURES(type, name, shape, _) \
  314. TensorSpec::createSpec<type>(#name, shape),
  315. static const std::vector<TensorSpec> InputFeatures{
  316. {RA_EVICT_FEATURES_LIST(_DECL_FEATURES)},
  317. };
  318. #undef _DECL_FEATURES
  319. static const TensorSpec Output =
  320. TensorSpec::createSpec<int64_t>(DecisionName, {1});
  321. static const TensorSpec Reward = TensorSpec::createSpec<float>("reward", {1});
  322. // Features we bind on the model. The tensor names have a prefix, and we also
  323. // need to include some tensors that are expected to be present by the training
  324. // algo.
  325. // TODO: can we just get rid of these?
  326. #define _DECL_TRAIN_FEATURES(type, name, shape, _) \
  327. TensorSpec::createSpec<type>(std::string("action_") + #name, shape),
  328. static const std::vector<TensorSpec> TrainingInputFeatures{
  329. {RA_EVICT_FEATURES_LIST(_DECL_TRAIN_FEATURES)
  330. TensorSpec::createSpec<float>("action_discount", {1}),
  331. TensorSpec::createSpec<int32_t>("action_step_type", {1}),
  332. TensorSpec::createSpec<float>("action_reward", {1})}};
  333. #undef _DECL_TRAIN_FEATURES
  334. class DevelopmentModeEvictAdvisor : public MLEvictAdvisor {
  335. public:
  336. DevelopmentModeEvictAdvisor(MachineFunction &MF, const RAGreedy &RA,
  337. MLModelRunner *Runner,
  338. const MachineBlockFrequencyInfo &MBFI,
  339. const MachineLoopInfo &Loops, Logger *Log)
  340. : MLEvictAdvisor(MF, RA, Runner, MBFI, Loops), Log(Log) {}
  341. private:
  342. int64_t tryFindEvictionCandidatePosition(
  343. LiveInterval &VirtReg, const AllocationOrder &Order, unsigned OrderLimit,
  344. uint8_t CostPerUseLimit,
  345. const SmallVirtRegSet &FixedRegisters) const override;
  346. Logger *const Log;
  347. };
  348. class DevelopmentModeEvictionAdvisorAnalysis final
  349. : public RegAllocEvictionAdvisorAnalysis {
  350. public:
  351. DevelopmentModeEvictionAdvisorAnalysis()
  352. : RegAllocEvictionAdvisorAnalysis(AdvisorMode::Development) {}
  353. // support for isa<> and dyn_cast.
  354. static bool classof(const RegAllocEvictionAdvisorAnalysis *R) {
  355. return R->getAdvisorMode() == AdvisorMode::Development;
  356. }
  357. /// get the logger for the given function, or nullptr if we didn't collect
  358. /// one. This is used to inject the score by the RegAllocScoring pass.
  359. Logger *getLogger(const MachineFunction &MF) const {
  360. auto I = LogMap.find(MF.getName());
  361. if (I == LogMap.end())
  362. return nullptr;
  363. return I->second.get();
  364. }
  365. private:
  366. void getAnalysisUsage(AnalysisUsage &AU) const override {
  367. AU.addRequired<MachineBlockFrequencyInfo>();
  368. AU.addRequired<MachineLoopInfo>();
  369. RegAllocEvictionAdvisorAnalysis::getAnalysisUsage(AU);
  370. }
  371. // Save all the logs (when requested).
  372. bool doFinalization(Module &M) override {
  373. if (TrainingLog.empty())
  374. return false;
  375. std::error_code EC;
  376. auto OS = std::make_unique<raw_fd_ostream>(TrainingLog, EC);
  377. if (EC) {
  378. M.getContext().emitError(EC.message() + ":" + TrainingLog);
  379. return false;
  380. }
  381. Logger::flushLogs(*OS, LogMap);
  382. return false;
  383. }
  384. std::unique_ptr<RegAllocEvictionAdvisor>
  385. getAdvisor(MachineFunction &MF, const RAGreedy &RA) override {
  386. LLVMContext &Ctx = MF.getFunction().getContext();
  387. if (ModelUnderTraining.empty() && TrainingLog.empty()) {
  388. Ctx.emitError("Regalloc development mode should be requested with at "
  389. "least logging enabled and/or a training model");
  390. return nullptr;
  391. }
  392. if (!Runner) {
  393. if (ModelUnderTraining.empty())
  394. Runner = std::make_unique<NoInferenceModelRunner>(Ctx, InputFeatures);
  395. else
  396. Runner = ModelUnderTrainingRunner::createAndEnsureValid(
  397. Ctx, ModelUnderTraining, DecisionName, TrainingInputFeatures);
  398. if (!Runner) {
  399. Ctx.emitError("Regalloc: could not set up the model runner");
  400. return nullptr;
  401. }
  402. }
  403. Logger *Log = nullptr;
  404. if (!TrainingLog.empty()) {
  405. std::vector<LoggedFeatureSpec> LFS;
  406. for (const auto &FS : InputFeatures)
  407. LFS.push_back({FS, None});
  408. if (auto *MUTR = dyn_cast<ModelUnderTrainingRunner>(Runner.get()))
  409. if (MUTR->outputLoggedFeatureSpecs().size() > 1)
  410. append_range(LFS, drop_begin(MUTR->outputLoggedFeatureSpecs()));
  411. // We always log the output; in particular, if we're not evaluating, we
  412. // don't have an output spec json file. That's why we handle the
  413. // 'normal' output separately.
  414. LFS.push_back({Output, None});
  415. auto I = LogMap.insert(std::make_pair(
  416. MF.getFunction().getName(),
  417. std::make_unique<Logger>(LFS, Reward, /*IncludeReward*/ true)));
  418. assert(I.second);
  419. Log = I.first->second.get();
  420. }
  421. return std::make_unique<DevelopmentModeEvictAdvisor>(
  422. MF, RA, Runner.get(), getAnalysis<MachineBlockFrequencyInfo>(),
  423. getAnalysis<MachineLoopInfo>(), Log);
  424. }
  425. std::unique_ptr<MLModelRunner> Runner;
  426. StringMap<std::unique_ptr<Logger>> LogMap;
  427. };
  428. #endif //#ifdef LLVM_HAVE_TF_API
  429. } // namespace
  430. float MLEvictAdvisor::getInitialQueueSize(const MachineFunction &MF) {
  431. auto &MRI = MF.getRegInfo();
  432. float Ret = 0.0;
  433. for (unsigned I = 0, E = MRI.getNumVirtRegs(); I != E; ++I) {
  434. Register Reg = Register::index2VirtReg(I);
  435. if (MRI.reg_nodbg_empty(Reg))
  436. continue;
  437. ++Ret;
  438. }
  439. return Ret;
  440. }
  441. MLEvictAdvisor::MLEvictAdvisor(MachineFunction &MF, const RAGreedy &RA,
  442. MLModelRunner *Runner,
  443. const MachineBlockFrequencyInfo &MBFI,
  444. const MachineLoopInfo &Loops)
  445. : RegAllocEvictionAdvisor(MF, RA), DefaultAdvisor(MF, RA),
  446. Runner(std::move(Runner)), MBFI(MBFI), Loops(Loops),
  447. InitialQSize(MLEvictAdvisor::getInitialQueueSize(MF)) {
  448. assert(this->Runner);
  449. DoNotNormalize.set(FeatureIDs::mask);
  450. DoNotNormalize.set(FeatureIDs::is_free);
  451. DoNotNormalize.set(FeatureIDs::is_hint);
  452. DoNotNormalize.set(FeatureIDs::is_local);
  453. DoNotNormalize.set(FeatureIDs::min_stage);
  454. DoNotNormalize.set(FeatureIDs::max_stage);
  455. DoNotNormalize.set(FeatureIDs::progress);
  456. }
  457. int64_t MLEvictAdvisor::tryFindEvictionCandidatePosition(
  458. LiveInterval &, const AllocationOrder &, unsigned, uint8_t,
  459. const SmallVirtRegSet &) const {
  460. int64_t Ret = Runner->evaluate<int64_t>();
  461. assert(Ret >= 0);
  462. assert(Ret <= CandidateVirtRegPos);
  463. return Ret;
  464. }
  465. bool MLEvictAdvisor::loadInterferenceFeatures(
  466. LiveInterval &VirtReg, MCRegister PhysReg, bool IsHint,
  467. const SmallVirtRegSet &FixedRegisters, FeaturesListNormalizer &Largest,
  468. size_t Pos) const {
  469. // It is only possible to evict virtual register interference.
  470. if (Matrix->checkInterference(VirtReg, PhysReg) > LiveRegMatrix::IK_VirtReg) {
  471. // leave unavailable
  472. return false;
  473. }
  474. const bool IsLocal = LIS->intervalIsInOneMBB(VirtReg);
  475. int64_t LocalIntfs = 0;
  476. float NrUrgent = 0.0f;
  477. // The cascade tracking is the same as in the default advisor
  478. unsigned Cascade = RA.getExtraInfo().getCascadeOrCurrentNext(VirtReg.reg());
  479. SmallVector<LiveInterval *, MaxInterferences> InterferingIntervals;
  480. for (MCRegUnitIterator Units(PhysReg, TRI); Units.isValid(); ++Units) {
  481. LiveIntervalUnion::Query &Q = Matrix->query(VirtReg, *Units);
  482. // Different from the default heuristic, we don't make any assumptions about
  483. // what having more than 10 results in the query may mean.
  484. const auto &IFIntervals = Q.interferingVRegs();
  485. if (IFIntervals.empty() && InterferingIntervals.empty())
  486. continue;
  487. InterferingIntervals.append(IFIntervals.begin(), IFIntervals.end());
  488. for (LiveInterval *Intf : reverse(IFIntervals)) {
  489. assert(Register::isVirtualRegister(Intf->reg()) &&
  490. "Only expecting virtual register interference from query");
  491. // This is the same set of legality checks as in the default case: don't
  492. // try to evict fixed regs or 'done' ones. Also don't break cascades,
  493. // except in the urgent case, with the same nuances used in the default
  494. // heuristic.
  495. // We could try sharing this between the advisors, but it may end up
  496. // more complex than it is right now.
  497. if (FixedRegisters.count(Intf->reg()))
  498. return false;
  499. if (RA.getExtraInfo().getStage(*Intf) == RS_Done)
  500. return false;
  501. bool Urgent =
  502. !VirtReg.isSpillable() &&
  503. (Intf->isSpillable() ||
  504. RegClassInfo.getNumAllocatableRegs(MRI->getRegClass(VirtReg.reg())) <
  505. RegClassInfo.getNumAllocatableRegs(
  506. MRI->getRegClass(Intf->reg())));
  507. // Only evict older cascades or live ranges without a cascade.
  508. unsigned IntfCascade = RA.getExtraInfo().getCascade(Intf->reg());
  509. if (Cascade <= IntfCascade) {
  510. if (!Urgent)
  511. return false;
  512. ++NrUrgent;
  513. }
  514. LocalIntfs += (IsLocal && LIS->intervalIsInOneMBB(*Intf) &&
  515. (!EnableLocalReassign || !canReassign(*Intf, PhysReg)));
  516. }
  517. }
  518. // OK, so if we made it this far, this LR is an eviction candidate, load its
  519. // features.
  520. extractFeatures(InterferingIntervals, Largest, Pos, IsHint, LocalIntfs,
  521. NrUrgent);
  522. return true;
  523. }
  524. MCRegister MLEvictAdvisor::tryFindEvictionCandidate(
  525. LiveInterval &VirtReg, const AllocationOrder &Order,
  526. uint8_t CostPerUseLimit, const SmallVirtRegSet &FixedRegisters) const {
  527. auto MaybeOrderLimit = getOrderLimit(VirtReg, Order, CostPerUseLimit);
  528. if (!MaybeOrderLimit)
  529. return MCRegister::NoRegister;
  530. unsigned OrderLimit = *MaybeOrderLimit;
  531. // The heuristic sets initial costs such as, if CostPerUseLimit is
  532. // max<uint8_t>, then any of the costs of the legally-evictable intervals
  533. // would be lower. When that happens, one of those will be selected.
  534. // Therefore, we allow the candidate be selected, unless the candidate is
  535. // unspillable, in which case it would be incorrect to not find a register for
  536. // it.
  537. const bool MustFindEviction =
  538. (!VirtReg.isSpillable() && CostPerUseLimit == static_cast<uint8_t>(~0u));
  539. // Number of available candidates - if 0, no need to continue.
  540. size_t Available = 0;
  541. // Make sure we don't have leftover partial state from an attempt where we had
  542. // no available candidates and bailed out early.
  543. resetInputs(*Runner);
  544. // Track the index->register mapping because AllocationOrder doesn't do that
  545. // and we'd have to scan it.
  546. // Also track their mask, to write asserts/debug.
  547. CandidateRegList Regs;
  548. Regs.fill({0, false});
  549. // Track the largest value of features seen during this eviction session. We
  550. // only normalize (some of) the float features, but it's just simpler to
  551. // dimension 'Largest' to all the features, especially since we have the
  552. // 'DoNotNormalize' list.
  553. FeaturesListNormalizer Largest;
  554. Largest.fill(0.0);
  555. // Same overal idea as in the default eviction policy - we visit the values of
  556. // AllocationOrder one at a time. If it's not legally available, we mask off
  557. // the corresponding feature column (==do nothing because we already reset all
  558. // the features to 0)
  559. // Use Pos to capture the column we load features at - in AllocationOrder
  560. // order.
  561. size_t Pos = 0;
  562. for (auto I = Order.begin(), E = Order.getOrderLimitEnd(OrderLimit); I != E;
  563. ++I, ++Pos) {
  564. MCRegister PhysReg = *I;
  565. assert(!Regs[Pos].second);
  566. assert(PhysReg);
  567. if (!canAllocatePhysReg(CostPerUseLimit, PhysReg)) {
  568. continue;
  569. }
  570. if (loadInterferenceFeatures(VirtReg, PhysReg, I.isHint(), FixedRegisters,
  571. Largest, Pos)) {
  572. ++Available;
  573. Regs[Pos] = std::make_pair(PhysReg, true);
  574. }
  575. }
  576. if (Available == 0) {
  577. // Nothing to decide, nothing to learn.
  578. assert(!MustFindEviction);
  579. return MCRegister::NoRegister;
  580. }
  581. const size_t ValidPosLimit = Pos;
  582. // If we must find eviction, the candidate should be masked out of the
  583. // decision making process.
  584. Regs[CandidateVirtRegPos].second = !MustFindEviction;
  585. if (!MustFindEviction)
  586. extractFeatures(SmallVector<LiveInterval *, 1>(1, &VirtReg), Largest,
  587. CandidateVirtRegPos, /*IsHint*/ 0, /*LocalIntfsCount*/ 0,
  588. /*NrUrgent*/ 0.0);
  589. assert(InitialQSize > 0.0 && "We couldn't have gotten here if we had "
  590. "nothing to allocate initially.");
  591. // Normalize the features.
  592. for (auto &V : Largest)
  593. V = V ? V : 1.0;
  594. for (size_t FeatureIndex = 0; FeatureIndex < FeatureIDs::FeatureCount;
  595. ++FeatureIndex) {
  596. if (DoNotNormalize.test(FeatureIndex))
  597. continue;
  598. for (size_t Pos = 0; Pos < NumberOfInterferences; ++Pos) {
  599. Runner->getTensor<float>(FeatureIndex)[Pos] /= Largest[FeatureIndex];
  600. }
  601. }
  602. *Runner->getTensor<float>(FeatureIDs::progress) =
  603. static_cast<float>(RA.getQueueSize()) / InitialQSize;
  604. // Get a decision.
  605. size_t CandidatePos = tryFindEvictionCandidatePosition(
  606. VirtReg, Order, OrderLimit, CostPerUseLimit, FixedRegisters);
  607. // The contract with the ML side is that CandidatePos is mask == 1 (i.e.
  608. // Regs[CandidatePos].second)
  609. assert(Regs[CandidatePos].second);
  610. if (CandidatePos == CandidateVirtRegPos) {
  611. assert(!MustFindEviction);
  612. return MCRegister::NoRegister;
  613. }
  614. assert(CandidatePos < ValidPosLimit);
  615. (void)ValidPosLimit;
  616. return Regs[CandidatePos].first;
  617. }
  618. const LIFeatureComponents
  619. MLEvictAdvisor::getLIFeatureComponents(const LiveInterval &LI) const {
  620. LIFeatureComponents Ret;
  621. SmallPtrSet<MachineInstr *, 8> Visited;
  622. const TargetRegisterInfo &TRI = *MF.getSubtarget().getRegisterInfo();
  623. for (MachineRegisterInfo::reg_instr_nodbg_iterator
  624. I = MRI->reg_instr_nodbg_begin(LI.reg()),
  625. E = MRI->reg_instr_nodbg_end();
  626. I != E;) {
  627. MachineInstr *MI = &*(I++);
  628. ++Ret.NrDefsAndUses;
  629. if (!Visited.insert(MI).second)
  630. continue;
  631. if (MI->isIdentityCopy() || MI->isImplicitDef())
  632. continue;
  633. bool Reads, Writes;
  634. std::tie(Reads, Writes) = MI->readsWritesVirtualRegister(LI.reg());
  635. float Freq = MBFI.getBlockFreqRelativeToEntryBlock(MI->getParent());
  636. Ret.HottestBlockFreq = std::max(Freq, Ret.HottestBlockFreq);
  637. Ret.R += (Reads && !Writes) * Freq;
  638. Ret.W += (!Reads && Writes) * Freq;
  639. Ret.RW += (Reads && Writes) * Freq;
  640. auto *MBB = MI->getParent();
  641. auto *Loop = Loops.getLoopFor(MBB);
  642. bool IsExiting = Loop ? Loop->isLoopExiting(MBB) : false;
  643. if (Writes && IsExiting && LIS->isLiveOutOfMBB(LI, MBB))
  644. Ret.IndVarUpdates += Freq;
  645. if (MI->isCopy() && VirtRegAuxInfo::copyHint(MI, LI.reg(), TRI, *MRI))
  646. Ret.HintWeights += Freq;
  647. }
  648. Ret.IsRemat = VirtRegAuxInfo::isRematerializable(
  649. LI, *LIS, *VRM, *MF.getSubtarget().getInstrInfo());
  650. return Ret;
  651. }
  652. // Overall, this currently mimics what we do for weight calculation, but instead
  653. // of accummulating the various features, we keep them separate.
  654. void MLEvictAdvisor::extractFeatures(
  655. const SmallVectorImpl<LiveInterval *> &Intervals,
  656. std::array<float, FeatureIDs::FeatureCount> &Largest, size_t Pos,
  657. int64_t IsHint, int64_t LocalIntfsCount, float NrUrgent) const {
  658. int64_t NrDefsAndUses = 0;
  659. int64_t NrBrokenHints = 0;
  660. double R = 0.0;
  661. double W = 0.0;
  662. double RW = 0.0;
  663. double IndVarUpdates = 0.0;
  664. double HintWeights = 0.0;
  665. float StartBBFreq = 0.0;
  666. float EndBBFreq = 0.0;
  667. float HottestBlockFreq = 0.0;
  668. int32_t NrRematerializable = 0;
  669. float TotalWeight = 0.0;
  670. SlotIndex EndSI = LIS->getSlotIndexes()->getZeroIndex();
  671. SlotIndex StartSI = LIS->getSlotIndexes()->getLastIndex();
  672. int64_t MaxStage = 0;
  673. int64_t MinStage =
  674. Intervals.empty() ? 0 : std::numeric_limits<int64_t>::max();
  675. for (const auto *L : Intervals) {
  676. const LiveInterval &LI = *L;
  677. MaxStage = std::max<int64_t>(
  678. MaxStage, static_cast<int64_t>(RA.getExtraInfo().getStage(LI)));
  679. MinStage = std::min<int64_t>(
  680. MinStage, static_cast<int64_t>(RA.getExtraInfo().getStage(LI)));
  681. TotalWeight = std::max(TotalWeight, LI.weight());
  682. if (LI.beginIndex() < StartSI)
  683. StartSI = LI.beginIndex();
  684. if (LI.endIndex() > EndSI)
  685. EndSI = LI.endIndex();
  686. const LIFeatureComponents LIFC = getLIFeatureComponents(LI);
  687. NrBrokenHints += VRM->hasPreferredPhys(LI.reg());
  688. NrDefsAndUses += LIFC.NrDefsAndUses;
  689. HottestBlockFreq = std::max(HottestBlockFreq, LIFC.HottestBlockFreq);
  690. R += LIFC.R;
  691. W += LIFC.W;
  692. RW += LIFC.RW;
  693. IndVarUpdates += LIFC.IndVarUpdates;
  694. HintWeights += LIFC.HintWeights;
  695. NrRematerializable += LIFC.IsRemat;
  696. }
  697. size_t Size = 0;
  698. if (!Intervals.empty()) {
  699. StartBBFreq =
  700. MBFI.getBlockFreqRelativeToEntryBlock(LIS->getMBBFromIndex(StartSI));
  701. if (EndSI >= LIS->getSlotIndexes()->getLastIndex())
  702. EndSI = LIS->getSlotIndexes()->getLastIndex().getPrevIndex();
  703. EndBBFreq =
  704. MBFI.getBlockFreqRelativeToEntryBlock(LIS->getMBBFromIndex(EndSI));
  705. Size = StartSI.distance(EndSI);
  706. }
  707. // Set the features at the column 'Pos'.
  708. #define SET(ID, TYPE, VAL) \
  709. do { \
  710. Runner->getTensor<TYPE>(FeatureIDs::ID)[Pos] = static_cast<TYPE>(VAL); \
  711. if (!DoNotNormalize.test(FeatureIDs::ID)) \
  712. Largest[FeatureIDs::ID] = \
  713. std::max(Largest[FeatureIDs::ID], static_cast<float>(VAL)); \
  714. } while (false)
  715. SET(mask, int64_t, 1);
  716. SET(is_free, int64_t, Intervals.empty());
  717. SET(nr_urgent, float, NrUrgent);
  718. SET(nr_broken_hints, float, NrBrokenHints);
  719. SET(is_hint, int64_t, IsHint);
  720. SET(is_local, int64_t, LocalIntfsCount);
  721. SET(nr_rematerializable, float, NrRematerializable);
  722. SET(nr_defs_and_uses, float, NrDefsAndUses);
  723. SET(weighed_reads_by_max, float, R);
  724. SET(weighed_writes_by_max, float, W);
  725. SET(weighed_read_writes_by_max, float, RW);
  726. SET(weighed_indvars_by_max, float, IndVarUpdates);
  727. SET(hint_weights_by_max, float, HintWeights);
  728. SET(start_bb_freq_by_max, float, StartBBFreq);
  729. SET(end_bb_freq_by_max, float, EndBBFreq);
  730. SET(hottest_bb_freq_by_max, float, HottestBlockFreq);
  731. SET(liverange_size, float, Size);
  732. SET(use_def_density, float, TotalWeight);
  733. SET(max_stage, int64_t, MaxStage);
  734. SET(min_stage, int64_t, MinStage);
  735. #undef SET
  736. }
  737. // Development mode-specific implementations
  738. #ifdef LLVM_HAVE_TF_API
  739. RegAllocEvictionAdvisorAnalysis *llvm::createDevelopmentModeAdvisor() {
  740. return new DevelopmentModeEvictionAdvisorAnalysis();
  741. }
  742. int64_t DevelopmentModeEvictAdvisor::tryFindEvictionCandidatePosition(
  743. LiveInterval &VirtReg, const AllocationOrder &Order, unsigned OrderLimit,
  744. uint8_t CostPerUseLimit, const SmallVirtRegSet &FixedRegisters) const {
  745. int64_t Ret = 0;
  746. if (isa<ModelUnderTrainingRunner>(getRunner())) {
  747. Ret = MLEvictAdvisor::tryFindEvictionCandidatePosition(
  748. VirtReg, Order, OrderLimit, CostPerUseLimit, FixedRegisters);
  749. } else {
  750. MCRegister PhysReg = getDefaultAdvisor().tryFindEvictionCandidate(
  751. VirtReg, Order, CostPerUseLimit, FixedRegisters);
  752. // Find the index of the selected PhysReg. We need it for logging, otherwise
  753. // this is wasted cycles (but so would starting development mode without a
  754. // model nor logging)
  755. if (!PhysReg)
  756. Ret = CandidateVirtRegPos;
  757. else
  758. for (auto I = Order.begin(), E = Order.getOrderLimitEnd(OrderLimit);
  759. I != E; ++I, ++Ret)
  760. if (*I == PhysReg)
  761. break;
  762. }
  763. if (TrainingLog.empty())
  764. return Ret;
  765. size_t CurrentFeature = 0;
  766. for (; CurrentFeature < FeatureIDs::FeatureCount; ++CurrentFeature) {
  767. Log->logSpecifiedTensorValue(
  768. CurrentFeature, reinterpret_cast<const char *>(
  769. getRunner().getTensorUntyped(CurrentFeature)));
  770. }
  771. if (auto *MUTR = dyn_cast<ModelUnderTrainingRunner>(&getRunner()))
  772. for (size_t I = 1; I < MUTR->outputLoggedFeatureSpecs().size();
  773. ++I, ++CurrentFeature)
  774. Log->logSpecifiedTensorValue(
  775. CurrentFeature,
  776. reinterpret_cast<const char *>(
  777. MUTR->lastEvaluationResult()->getUntypedTensorValue(I)));
  778. // The output is right after the features and the extra outputs
  779. Log->logInt64Value(CurrentFeature, &Ret);
  780. return Ret;
  781. }
  782. bool RegAllocScoring::runOnMachineFunction(MachineFunction &MF) {
  783. if (auto *DevModeAnalysis = dyn_cast<DevelopmentModeEvictionAdvisorAnalysis>(
  784. &getAnalysis<RegAllocEvictionAdvisorAnalysis>()))
  785. if (auto *Log = DevModeAnalysis->getLogger(MF))
  786. Log->logFloatFinalReward(static_cast<float>(
  787. calculateRegAllocScore(
  788. MF, getAnalysis<MachineBlockFrequencyInfo>(),
  789. getAnalysis<AAResultsWrapperPass>().getAAResults())
  790. .getScore()));
  791. return false;
  792. }
  793. #endif // #ifdef LLVM_HAVE_TF_API
  794. #if defined(LLVM_HAVE_TF_AOT_REGALLOCEVICTMODEL)
  795. RegAllocEvictionAdvisorAnalysis *llvm::createReleaseModeAdvisor() {
  796. return new ReleaseModeEvictionAdvisorAnalysis();
  797. }
  798. #endif
  799. // In all cases except development mode, we don't need scoring.
  800. #if !defined(LLVM_HAVE_TF_API)
  801. bool RegAllocScoring::runOnMachineFunction(MachineFunction &) { return false; }
  802. #endif