1 //===- RegAllocEvictionAdvisor.h - Interference resolution ------*- C++ -*-===// 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 #ifndef LLVM_CODEGEN_REGALLOCEVICTIONADVISOR_H 10 #define LLVM_CODEGEN_REGALLOCEVICTIONADVISOR_H 11 12 #include "llvm/ADT/Any.h" 13 #include "llvm/ADT/ArrayRef.h" 14 #include "llvm/ADT/SmallSet.h" 15 #include "llvm/ADT/StringRef.h" 16 #include "llvm/CodeGen/MachineBlockFrequencyInfo.h" 17 #include "llvm/CodeGen/MachineLoopInfo.h" 18 #include "llvm/CodeGen/Register.h" 19 #include "llvm/Config/llvm-config.h" 20 #include "llvm/IR/PassManager.h" 21 #include "llvm/MC/MCRegister.h" 22 #include "llvm/Pass.h" 23 #include "llvm/Support/Compiler.h" 24 25 namespace llvm { 26 class AllocationOrder; 27 class LiveInterval; 28 class LiveIntervals; 29 class LiveRegMatrix; 30 class MachineFunction; 31 class MachineRegisterInfo; 32 class RegisterClassInfo; 33 class TargetRegisterInfo; 34 class VirtRegMap; 35 36 using SmallVirtRegSet = SmallSet<Register, 16>; 37 38 // Live ranges pass through a number of stages as we try to allocate them. 39 // Some of the stages may also create new live ranges: 40 // 41 // - Region splitting. 42 // - Per-block splitting. 43 // - Local splitting. 44 // - Spilling. 45 // 46 // Ranges produced by one of the stages skip the previous stages when they are 47 // dequeued. This improves performance because we can skip interference checks 48 // that are unlikely to give any results. It also guarantees that the live 49 // range splitting algorithm terminates, something that is otherwise hard to 50 // ensure. 51 enum LiveRangeStage { 52 /// Newly created live range that has never been queued. 53 RS_New, 54 55 /// Only attempt assignment and eviction. Then requeue as RS_Split. 56 RS_Assign, 57 58 /// Attempt live range splitting if assignment is impossible. 59 RS_Split, 60 61 /// Attempt more aggressive live range splitting that is guaranteed to make 62 /// progress. This is used for split products that may not be making 63 /// progress. 64 RS_Split2, 65 66 /// Live range will be spilled. No more splitting will be attempted. 67 RS_Spill, 68 69 /// There is nothing more we can do to this live range. Abort compilation 70 /// if it can't be assigned. 71 RS_Done 72 }; 73 74 /// Cost of evicting interference - used by default advisor, and the eviction 75 /// chain heuristic in RegAllocGreedy. 76 // FIXME: this can be probably made an implementation detail of the default 77 // advisor, if the eviction chain logic can be refactored. 78 struct EvictionCost { 79 unsigned BrokenHints = 0; ///< Total number of broken hints. 80 float MaxWeight = 0; ///< Maximum spill weight evicted. 81 82 EvictionCost() = default; 83 isMaxEvictionCost84 bool isMax() const { return BrokenHints == ~0u; } 85 setMaxEvictionCost86 void setMax() { BrokenHints = ~0u; } 87 setBrokenHintsEvictionCost88 void setBrokenHints(unsigned NHints) { BrokenHints = NHints; } 89 90 bool operator<(const EvictionCost &O) const { 91 return std::tie(BrokenHints, MaxWeight) < 92 std::tie(O.BrokenHints, O.MaxWeight); 93 } 94 }; 95 96 /// Interface to the eviction advisor, which is responsible for making a 97 /// decision as to which live ranges should be evicted (if any). 98 class RAGreedy; 99 class RegAllocEvictionAdvisor { 100 public: 101 RegAllocEvictionAdvisor(const RegAllocEvictionAdvisor &) = delete; 102 RegAllocEvictionAdvisor(RegAllocEvictionAdvisor &&) = delete; 103 virtual ~RegAllocEvictionAdvisor() = default; 104 105 /// Find a physical register that can be freed by evicting the FixedRegisters, 106 /// or return NoRegister. The eviction decision is assumed to be correct (i.e. 107 /// no fixed live ranges are evicted) and profitable. 108 virtual MCRegister tryFindEvictionCandidate( 109 const LiveInterval &VirtReg, const AllocationOrder &Order, 110 uint8_t CostPerUseLimit, const SmallVirtRegSet &FixedRegisters) const = 0; 111 112 /// Find out if we can evict the live ranges occupying the given PhysReg, 113 /// which is a hint (preferred register) for VirtReg. 114 virtual bool 115 canEvictHintInterference(const LiveInterval &VirtReg, MCRegister PhysReg, 116 const SmallVirtRegSet &FixedRegisters) const = 0; 117 118 /// Returns true if the given \p PhysReg is a callee saved register and has 119 /// not been used for allocation yet. 120 bool isUnusedCalleeSavedReg(MCRegister PhysReg) const; 121 122 protected: 123 RegAllocEvictionAdvisor(const MachineFunction &MF, const RAGreedy &RA); 124 125 bool canReassign(const LiveInterval &VirtReg, MCRegister FromReg) const; 126 127 // Get the upper limit of elements in the given Order we need to analize. 128 // TODO: is this heuristic, we could consider learning it. 129 std::optional<unsigned> getOrderLimit(const LiveInterval &VirtReg, 130 const AllocationOrder &Order, 131 unsigned CostPerUseLimit) const; 132 133 // Determine if it's worth trying to allocate this reg, given the 134 // CostPerUseLimit 135 // TODO: this is a heuristic component we could consider learning, too. 136 bool canAllocatePhysReg(unsigned CostPerUseLimit, MCRegister PhysReg) const; 137 138 const MachineFunction &MF; 139 const RAGreedy &RA; 140 LiveRegMatrix *const Matrix; 141 LiveIntervals *const LIS; 142 VirtRegMap *const VRM; 143 MachineRegisterInfo *const MRI; 144 const TargetRegisterInfo *const TRI; 145 const RegisterClassInfo &RegClassInfo; 146 const ArrayRef<uint8_t> RegCosts; 147 148 /// Run or not the local reassignment heuristic. This information is 149 /// obtained from the TargetSubtargetInfo. 150 const bool EnableLocalReassign; 151 }; 152 153 /// Common provider for legacy and new pass managers. 154 /// This keeps the state for logging, and sets up and holds the provider. 155 /// The legacy pass itself used to keep the logging state and provider, 156 /// so this extraction helps the NPM analysis to reuse the logic. 157 /// TODO: Coalesce this with the NPM analysis when legacy PM is removed. 158 class RegAllocEvictionAdvisorProvider { 159 public: 160 enum class AdvisorMode : int { Default, Release, Development }; RegAllocEvictionAdvisorProvider(AdvisorMode Mode,LLVMContext & Ctx)161 RegAllocEvictionAdvisorProvider(AdvisorMode Mode, LLVMContext &Ctx) 162 : Ctx(Ctx), Mode(Mode) {} 163 164 virtual ~RegAllocEvictionAdvisorProvider() = default; 165 logRewardIfNeeded(const MachineFunction & MF,llvm::function_ref<float ()> GetReward)166 virtual void logRewardIfNeeded(const MachineFunction &MF, 167 llvm::function_ref<float()> GetReward) {} 168 169 virtual std::unique_ptr<RegAllocEvictionAdvisor> 170 getAdvisor(const MachineFunction &MF, const RAGreedy &RA, 171 MachineBlockFrequencyInfo *MBFI, MachineLoopInfo *Loops) = 0; 172 getAdvisorMode()173 AdvisorMode getAdvisorMode() const { return Mode; } 174 175 protected: 176 LLVMContext &Ctx; 177 178 private: 179 const AdvisorMode Mode; 180 }; 181 182 /// ImmutableAnalysis abstraction for fetching the Eviction Advisor. We model it 183 /// as an analysis to decouple the user from the implementation insofar as 184 /// dependencies on other analyses goes. The motivation for it being an 185 /// immutable pass is twofold: 186 /// - in the ML implementation case, the evaluator is stateless but (especially 187 /// in the development mode) expensive to set up. With an immutable pass, we set 188 /// it up once. 189 /// - in the 'development' mode ML case, we want to capture the training log 190 /// during allocation (this is a log of features encountered and decisions 191 /// made), and then measure a score, potentially a few steps after allocation 192 /// completes. So we need the properties of an immutable pass to keep the logger 193 /// state around until we can make that measurement. 194 /// 195 /// Because we need to offer additional services in 'development' mode, the 196 /// implementations of this analysis need to implement RTTI support. 197 class RegAllocEvictionAdvisorAnalysisLegacy : public ImmutablePass { 198 public: 199 enum class AdvisorMode : int { Default, Release, Development }; 200 RegAllocEvictionAdvisorAnalysisLegacy(AdvisorMode Mode)201 RegAllocEvictionAdvisorAnalysisLegacy(AdvisorMode Mode) 202 : ImmutablePass(ID), Mode(Mode) {}; 203 static char ID; 204 205 /// Get an advisor for the given context (i.e. machine function, etc) getProvider()206 RegAllocEvictionAdvisorProvider &getProvider() { return *Provider; } 207 getAdvisorMode()208 AdvisorMode getAdvisorMode() const { return Mode; } logRewardIfNeeded(const MachineFunction & MF,function_ref<float ()> GetReward)209 virtual void logRewardIfNeeded(const MachineFunction &MF, 210 function_ref<float()> GetReward) {}; 211 212 protected: 213 // This analysis preserves everything, and subclasses may have additional 214 // requirements. getAnalysisUsage(AnalysisUsage & AU)215 void getAnalysisUsage(AnalysisUsage &AU) const override { 216 AU.setPreservesAll(); 217 } 218 std::unique_ptr<RegAllocEvictionAdvisorProvider> Provider; 219 220 private: 221 StringRef getPassName() const override; 222 const AdvisorMode Mode; 223 }; 224 225 /// A MachineFunction analysis for fetching the Eviction Advisor. 226 /// This sets up the Provider lazily and caches it. 227 /// - in the ML implementation case, the evaluator is stateless but (especially 228 /// in the development mode) expensive to set up. With a Module Analysis, we 229 /// `require` it and set it up once. 230 /// - in the 'development' mode ML case, we want to capture the training log 231 /// during allocation (this is a log of features encountered and decisions 232 /// made), and then measure a score, potentially a few steps after allocation 233 /// completes. So we need a Module analysis to keep the logger state around 234 /// until we can make that measurement. 235 class RegAllocEvictionAdvisorAnalysis 236 : public AnalysisInfoMixin<RegAllocEvictionAdvisorAnalysis> { 237 static AnalysisKey Key; 238 friend AnalysisInfoMixin<RegAllocEvictionAdvisorAnalysis>; 239 240 public: 241 struct Result { 242 // owned by this analysis 243 RegAllocEvictionAdvisorProvider *Provider; 244 invalidateResult245 bool invalidate(MachineFunction &MF, const PreservedAnalyses &PA, 246 MachineFunctionAnalysisManager::Invalidator &Inv) { 247 // Provider is stateless and constructed only once. Do not get 248 // invalidated. 249 return false; 250 } 251 }; 252 253 Result run(MachineFunction &MF, MachineFunctionAnalysisManager &MAM); 254 255 private: 256 void 257 initializeProvider(RegAllocEvictionAdvisorAnalysisLegacy::AdvisorMode Mode, 258 LLVMContext &Ctx); 259 260 std::unique_ptr<RegAllocEvictionAdvisorProvider> Provider; 261 }; 262 263 /// Specialization for the API used by the analysis infrastructure to create 264 /// an instance of the eviction advisor. 265 template <> Pass *callDefaultCtor<RegAllocEvictionAdvisorAnalysisLegacy>(); 266 267 RegAllocEvictionAdvisorAnalysisLegacy *createReleaseModeAdvisorAnalysisLegacy(); 268 269 RegAllocEvictionAdvisorAnalysisLegacy * 270 createDevelopmentModeAdvisorAnalysisLegacy(); 271 272 LLVM_ATTRIBUTE_RETURNS_NONNULL RegAllocEvictionAdvisorProvider * 273 createReleaseModeAdvisorProvider(LLVMContext &Ctx); 274 275 RegAllocEvictionAdvisorProvider * 276 createDevelopmentModeAdvisorProvider(LLVMContext &Ctx); 277 278 // TODO: move to RegAllocEvictionAdvisor.cpp when we move implementation 279 // out of RegAllocGreedy.cpp 280 class DefaultEvictionAdvisor : public RegAllocEvictionAdvisor { 281 public: DefaultEvictionAdvisor(const MachineFunction & MF,const RAGreedy & RA)282 DefaultEvictionAdvisor(const MachineFunction &MF, const RAGreedy &RA) 283 : RegAllocEvictionAdvisor(MF, RA) {} 284 285 private: 286 MCRegister tryFindEvictionCandidate(const LiveInterval &, 287 const AllocationOrder &, uint8_t, 288 const SmallVirtRegSet &) const override; 289 bool canEvictHintInterference(const LiveInterval &, MCRegister, 290 const SmallVirtRegSet &) const override; 291 bool canEvictInterferenceBasedOnCost(const LiveInterval &, MCRegister, bool, 292 EvictionCost &, 293 const SmallVirtRegSet &) const; 294 bool shouldEvict(const LiveInterval &A, bool, const LiveInterval &B, 295 bool) const; 296 }; 297 } // namespace llvm 298 299 #endif // LLVM_CODEGEN_REGALLOCEVICTIONADVISOR_H 300