MLRegallocPriorityAdvisor.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. //===- MLRegAllocPriorityAdvisor.cpp - ML priority 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 priority advisor and reward injection pass
  10. //
  11. //===----------------------------------------------------------------------===//
  12. #include "AllocationOrder.h"
  13. #include "RegAllocGreedy.h"
  14. #include "RegAllocPriorityAdvisor.h"
  15. #include "llvm/Analysis/AliasAnalysis.h"
  16. #include "llvm/Analysis/MLModelRunner.h"
  17. #include "llvm/Analysis/ReleaseModeModelRunner.h"
  18. #include "llvm/Analysis/TensorSpec.h"
  19. #include "llvm/CodeGen/CalcSpillWeights.h"
  20. #include "llvm/CodeGen/LiveRegMatrix.h"
  21. #include "llvm/CodeGen/MachineBlockFrequencyInfo.h"
  22. #include "llvm/CodeGen/MachineFunction.h"
  23. #include "llvm/CodeGen/MachineLoopInfo.h"
  24. #include "llvm/CodeGen/MachineRegisterInfo.h"
  25. #include "llvm/CodeGen/Passes.h"
  26. #include "llvm/CodeGen/RegisterClassInfo.h"
  27. #include "llvm/CodeGen/SlotIndexes.h"
  28. #include "llvm/CodeGen/VirtRegMap.h"
  29. #include "llvm/InitializePasses.h"
  30. #include "llvm/Pass.h"
  31. #include "llvm/PassRegistry.h"
  32. #include "llvm/Support/CommandLine.h"
  33. #if defined(LLVM_HAVE_TFLITE)
  34. #include "llvm/Analysis/ModelUnderTrainingRunner.h"
  35. #include "llvm/Analysis/NoInferenceModelRunner.h"
  36. #include "llvm/Analysis/Utils/TrainingLogger.h"
  37. #endif
  38. using namespace llvm;
  39. // Options that only make sense in development mode
  40. #ifdef LLVM_HAVE_TFLITE
  41. #include "RegAllocScore.h"
  42. #include "llvm/Analysis/Utils/TFUtils.h"
  43. static cl::opt<std::string> TrainingLog(
  44. "regalloc-priority-training-log", cl::Hidden,
  45. cl::desc("Training log for the register allocator priority model"));
  46. static cl::opt<std::string> ModelUnderTraining(
  47. "regalloc-priority-model", cl::Hidden,
  48. cl::desc("The model being trained for register allocation priority"));
  49. #endif // #ifdef LLVM_HAVE_TFLITE
  50. namespace llvm {
  51. static const std::vector<int64_t> PerLiveRangeShape{1};
  52. #define RA_PRIORITY_FEATURES_LIST(M) \
  53. M(int64_t, li_size, PerLiveRangeShape, "size") \
  54. M(int64_t, stage, PerLiveRangeShape, "stage") \
  55. M(float, weight, PerLiveRangeShape, "weight")
  56. #define DecisionName "priority"
  57. // Named features index.
  58. enum FeatureIDs {
  59. #define _FEATURE_IDX(_, name, __, ___) name,
  60. RA_PRIORITY_FEATURES_LIST(_FEATURE_IDX)
  61. #undef _FEATURE_IDX
  62. FeatureCount
  63. };
  64. class MLPriorityAdvisor : public RegAllocPriorityAdvisor {
  65. public:
  66. MLPriorityAdvisor(const MachineFunction &MF, const RAGreedy &RA,
  67. SlotIndexes *const Indexes, MLModelRunner *Runner);
  68. protected:
  69. const RegAllocPriorityAdvisor &getDefaultAdvisor() const {
  70. return static_cast<const RegAllocPriorityAdvisor &>(DefaultAdvisor);
  71. }
  72. // The assumption is that if the Runner could not be constructed, we emit-ed
  73. // error, and we shouldn't be asking for it here.
  74. const MLModelRunner &getRunner() const { return *Runner; }
  75. float getPriorityImpl(const LiveInterval &LI) const;
  76. unsigned getPriority(const LiveInterval &LI) const override;
  77. private:
  78. const DefaultPriorityAdvisor DefaultAdvisor;
  79. MLModelRunner *const Runner;
  80. };
  81. #define _DECL_FEATURES(type, name, shape, _) \
  82. TensorSpec::createSpec<type>(#name, shape),
  83. static const std::vector<TensorSpec> InputFeatures{
  84. {RA_PRIORITY_FEATURES_LIST(_DECL_FEATURES)},
  85. };
  86. #undef _DECL_FEATURES
  87. // ===================================
  88. // Release (AOT) - specifics
  89. // ===================================
  90. class ReleaseModePriorityAdvisorAnalysis final
  91. : public RegAllocPriorityAdvisorAnalysis {
  92. public:
  93. ReleaseModePriorityAdvisorAnalysis()
  94. : RegAllocPriorityAdvisorAnalysis(AdvisorMode::Release) {}
  95. // support for isa<> and dyn_cast.
  96. static bool classof(const RegAllocPriorityAdvisorAnalysis *R) {
  97. return R->getAdvisorMode() == AdvisorMode::Release;
  98. }
  99. private:
  100. void getAnalysisUsage(AnalysisUsage &AU) const override {
  101. AU.setPreservesAll();
  102. AU.addRequired<SlotIndexes>();
  103. RegAllocPriorityAdvisorAnalysis::getAnalysisUsage(AU);
  104. }
  105. std::unique_ptr<RegAllocPriorityAdvisor>
  106. getAdvisor(const MachineFunction &MF, const RAGreedy &RA) override {
  107. if (!Runner)
  108. Runner = std::make_unique<ReleaseModeModelRunner<NoopSavedModelImpl>>(
  109. MF.getFunction().getContext(), InputFeatures, DecisionName);
  110. return std::make_unique<MLPriorityAdvisor>(
  111. MF, RA, &getAnalysis<SlotIndexes>(), Runner.get());
  112. }
  113. std::unique_ptr<ReleaseModeModelRunner<NoopSavedModelImpl>> Runner;
  114. };
  115. // ===================================
  116. // Development mode-specifics
  117. // ===================================
  118. //
  119. // Features we log
  120. #ifdef LLVM_HAVE_TFLITE
  121. static const TensorSpec Output =
  122. TensorSpec::createSpec<float>(DecisionName, {1});
  123. static const TensorSpec Reward = TensorSpec::createSpec<float>("reward", {1});
  124. #define _DECL_TRAIN_FEATURES(type, name, shape, _) \
  125. TensorSpec::createSpec<type>(std::string("action_") + #name, shape),
  126. static const std::vector<TensorSpec> TrainingInputFeatures{
  127. {RA_PRIORITY_FEATURES_LIST(_DECL_TRAIN_FEATURES)
  128. TensorSpec::createSpec<float>("action_discount", {1}),
  129. TensorSpec::createSpec<int32_t>("action_step_type", {1}),
  130. TensorSpec::createSpec<float>("action_reward", {1})}};
  131. #undef _DECL_TRAIN_FEATURES
  132. class DevelopmentModePriorityAdvisor : public MLPriorityAdvisor {
  133. public:
  134. DevelopmentModePriorityAdvisor(const MachineFunction &MF, const RAGreedy &RA,
  135. SlotIndexes *const Indexes,
  136. MLModelRunner *Runner, Logger *Log)
  137. : MLPriorityAdvisor(MF, RA, Indexes, Runner), Log(Log) {}
  138. private:
  139. unsigned getPriority(const LiveInterval &LI) const override;
  140. Logger *const Log;
  141. };
  142. class DevelopmentModePriorityAdvisorAnalysis final
  143. : public RegAllocPriorityAdvisorAnalysis {
  144. public:
  145. DevelopmentModePriorityAdvisorAnalysis()
  146. : RegAllocPriorityAdvisorAnalysis(AdvisorMode::Development) {}
  147. // support for isa<> and dyn_cast.
  148. static bool classof(const RegAllocPriorityAdvisorAnalysis *R) {
  149. return R->getAdvisorMode() == AdvisorMode::Development;
  150. }
  151. void logRewardIfNeeded(const MachineFunction &MF,
  152. llvm::function_ref<float()> GetReward) override {
  153. if (!Log)
  154. return;
  155. // The function pass manager would run all the function passes for a
  156. // function, so we assume the last context belongs to this function. If
  157. // this invariant ever changes, we can implement at that time switching
  158. // contexts. At this point, it'd be an error
  159. if (Log->currentContext() != MF.getName()) {
  160. MF.getFunction().getContext().emitError(
  161. "The training log context shouldn't have had changed.");
  162. }
  163. if (Log->hasObservationInProgress())
  164. Log->logReward<float>(GetReward());
  165. }
  166. private:
  167. void getAnalysisUsage(AnalysisUsage &AU) const override {
  168. AU.setPreservesAll();
  169. AU.addRequired<SlotIndexes>();
  170. RegAllocPriorityAdvisorAnalysis::getAnalysisUsage(AU);
  171. }
  172. // Save all the logs (when requested).
  173. bool doInitialization(Module &M) override {
  174. LLVMContext &Ctx = M.getContext();
  175. if (ModelUnderTraining.empty() && TrainingLog.empty()) {
  176. Ctx.emitError("Regalloc development mode should be requested with at "
  177. "least logging enabled and/or a training model");
  178. return false;
  179. }
  180. if (ModelUnderTraining.empty())
  181. Runner = std::make_unique<NoInferenceModelRunner>(Ctx, InputFeatures);
  182. else
  183. Runner = ModelUnderTrainingRunner::createAndEnsureValid(
  184. Ctx, ModelUnderTraining, DecisionName, TrainingInputFeatures);
  185. if (!Runner) {
  186. Ctx.emitError("Regalloc: could not set up the model runner");
  187. return false;
  188. }
  189. if (TrainingLog.empty())
  190. return false;
  191. std::error_code EC;
  192. auto OS = std::make_unique<raw_fd_ostream>(TrainingLog, EC);
  193. if (EC) {
  194. M.getContext().emitError(EC.message() + ":" + TrainingLog);
  195. return false;
  196. }
  197. std::vector<TensorSpec> LFS = InputFeatures;
  198. if (auto *MUTR = dyn_cast<ModelUnderTrainingRunner>(Runner.get()))
  199. append_range(LFS, MUTR->extraOutputsForLoggingSpecs());
  200. // We always log the output; in particular, if we're not evaluating, we
  201. // don't have an output spec json file. That's why we handle the
  202. // 'normal' output separately.
  203. LFS.push_back(Output);
  204. Log = std::make_unique<Logger>(std::move(OS), LFS, Reward,
  205. /*IncludeReward*/ true);
  206. return false;
  207. }
  208. std::unique_ptr<RegAllocPriorityAdvisor>
  209. getAdvisor(const MachineFunction &MF, const RAGreedy &RA) override {
  210. if (!Runner)
  211. return nullptr;
  212. if (Log) {
  213. Log->switchContext(MF.getName());
  214. }
  215. return std::make_unique<DevelopmentModePriorityAdvisor>(
  216. MF, RA, &getAnalysis<SlotIndexes>(), Runner.get(), Log.get());
  217. }
  218. std::unique_ptr<MLModelRunner> Runner;
  219. std::unique_ptr<Logger> Log;
  220. };
  221. #endif //#ifdef LLVM_HAVE_TFLITE
  222. } // namespace llvm
  223. RegAllocPriorityAdvisorAnalysis *llvm::createReleaseModePriorityAdvisor() {
  224. return new ReleaseModePriorityAdvisorAnalysis();
  225. }
  226. MLPriorityAdvisor::MLPriorityAdvisor(const MachineFunction &MF,
  227. const RAGreedy &RA,
  228. SlotIndexes *const Indexes,
  229. MLModelRunner *Runner)
  230. : RegAllocPriorityAdvisor(MF, RA, Indexes), DefaultAdvisor(MF, RA, Indexes),
  231. Runner(std::move(Runner)) {
  232. assert(this->Runner);
  233. }
  234. float MLPriorityAdvisor::getPriorityImpl(const LiveInterval &LI) const {
  235. const unsigned Size = LI.getSize();
  236. LiveRangeStage Stage = RA.getExtraInfo().getStage(LI);
  237. *Runner->getTensor<int64_t>(0) = static_cast<int64_t>(Size);
  238. *Runner->getTensor<int64_t>(1) = static_cast<int64_t>(Stage);
  239. *Runner->getTensor<float>(2) = static_cast<float>(LI.weight());
  240. return Runner->evaluate<float>();
  241. }
  242. unsigned MLPriorityAdvisor::getPriority(const LiveInterval &LI) const {
  243. return static_cast<unsigned>(getPriorityImpl(LI));
  244. }
  245. #ifdef LLVM_HAVE_TFLITE
  246. RegAllocPriorityAdvisorAnalysis *llvm::createDevelopmentModePriorityAdvisor() {
  247. return new DevelopmentModePriorityAdvisorAnalysis();
  248. }
  249. unsigned
  250. DevelopmentModePriorityAdvisor::getPriority(const LiveInterval &LI) const {
  251. double Prio = 0;
  252. if (isa<ModelUnderTrainingRunner>(getRunner())) {
  253. Prio = MLPriorityAdvisor::getPriorityImpl(LI);
  254. } else {
  255. Prio = getDefaultAdvisor().getPriority(LI);
  256. }
  257. if (TrainingLog.empty())
  258. return Prio;
  259. // TODO(mtrofin): when we support optional rewards, this can go away. In the
  260. // meantime, we log the "pretend" reward (0) for the previous observation
  261. // before starting a new one.
  262. if (Log->hasObservationInProgress())
  263. Log->logReward<float>(0.0);
  264. Log->startObservation();
  265. size_t CurrentFeature = 0;
  266. for (; CurrentFeature < InputFeatures.size(); ++CurrentFeature) {
  267. Log->logTensorValue(CurrentFeature,
  268. reinterpret_cast<const char *>(
  269. getRunner().getTensorUntyped(CurrentFeature)));
  270. }
  271. if (auto *MUTR = dyn_cast<ModelUnderTrainingRunner>(&getRunner())) {
  272. for (size_t I = 0; I < MUTR->extraOutputsForLoggingSpecs().size();
  273. ++I, ++CurrentFeature)
  274. Log->logTensorValue(
  275. CurrentFeature,
  276. reinterpret_cast<const char *>(MUTR->getUntypedExtraOutputValue(I)));
  277. }
  278. float Ret = static_cast<float>(Prio);
  279. Log->logTensorValue(CurrentFeature, reinterpret_cast<const char *>(&Ret));
  280. Log->endObservation();
  281. return static_cast<unsigned>(Prio);
  282. }
  283. #endif // #ifdef LLVM_HAVE_TFLITE