xref: /freebsd/contrib/llvm-project/llvm/include/llvm/CodeGen/RegAllocEvictionAdvisor.h (revision 700637cbb5e582861067a11aaca4d053546871d2)
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