xref: /openbsd-src/gnu/llvm/llvm/lib/CodeGen/RegAllocEvictionAdvisor.h (revision d415bd752c734aee168c4ee86ff32e8cc249eb16)
1*d415bd75Srobert //===- RegAllocEvictionAdvisor.h - Interference resolution ------*- C++ -*-===//
2*d415bd75Srobert //
3*d415bd75Srobert // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*d415bd75Srobert // See https://llvm.org/LICENSE.txt for license information.
5*d415bd75Srobert // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*d415bd75Srobert //
7*d415bd75Srobert //===----------------------------------------------------------------------===//
8*d415bd75Srobert 
9*d415bd75Srobert #ifndef LLVM_CODEGEN_REGALLOCEVICTIONADVISOR_H
10*d415bd75Srobert #define LLVM_CODEGEN_REGALLOCEVICTIONADVISOR_H
11*d415bd75Srobert 
12*d415bd75Srobert #include "llvm/ADT/ArrayRef.h"
13*d415bd75Srobert #include "llvm/ADT/SmallSet.h"
14*d415bd75Srobert #include "llvm/ADT/StringRef.h"
15*d415bd75Srobert #include "llvm/CodeGen/Register.h"
16*d415bd75Srobert #include "llvm/Config/llvm-config.h"
17*d415bd75Srobert #include "llvm/MC/MCRegister.h"
18*d415bd75Srobert #include "llvm/Pass.h"
19*d415bd75Srobert 
20*d415bd75Srobert namespace llvm {
21*d415bd75Srobert class AllocationOrder;
22*d415bd75Srobert class LiveInterval;
23*d415bd75Srobert class LiveIntervals;
24*d415bd75Srobert class LiveRegMatrix;
25*d415bd75Srobert class MachineFunction;
26*d415bd75Srobert class MachineRegisterInfo;
27*d415bd75Srobert class RegisterClassInfo;
28*d415bd75Srobert class TargetRegisterInfo;
29*d415bd75Srobert class VirtRegMap;
30*d415bd75Srobert 
31*d415bd75Srobert using SmallVirtRegSet = SmallSet<Register, 16>;
32*d415bd75Srobert 
33*d415bd75Srobert // Live ranges pass through a number of stages as we try to allocate them.
34*d415bd75Srobert // Some of the stages may also create new live ranges:
35*d415bd75Srobert //
36*d415bd75Srobert // - Region splitting.
37*d415bd75Srobert // - Per-block splitting.
38*d415bd75Srobert // - Local splitting.
39*d415bd75Srobert // - Spilling.
40*d415bd75Srobert //
41*d415bd75Srobert // Ranges produced by one of the stages skip the previous stages when they are
42*d415bd75Srobert // dequeued. This improves performance because we can skip interference checks
43*d415bd75Srobert // that are unlikely to give any results. It also guarantees that the live
44*d415bd75Srobert // range splitting algorithm terminates, something that is otherwise hard to
45*d415bd75Srobert // ensure.
46*d415bd75Srobert enum LiveRangeStage {
47*d415bd75Srobert   /// Newly created live range that has never been queued.
48*d415bd75Srobert   RS_New,
49*d415bd75Srobert 
50*d415bd75Srobert   /// Only attempt assignment and eviction. Then requeue as RS_Split.
51*d415bd75Srobert   RS_Assign,
52*d415bd75Srobert 
53*d415bd75Srobert   /// Attempt live range splitting if assignment is impossible.
54*d415bd75Srobert   RS_Split,
55*d415bd75Srobert 
56*d415bd75Srobert   /// Attempt more aggressive live range splitting that is guaranteed to make
57*d415bd75Srobert   /// progress.  This is used for split products that may not be making
58*d415bd75Srobert   /// progress.
59*d415bd75Srobert   RS_Split2,
60*d415bd75Srobert 
61*d415bd75Srobert   /// Live range will be spilled.  No more splitting will be attempted.
62*d415bd75Srobert   RS_Spill,
63*d415bd75Srobert 
64*d415bd75Srobert   /// Live range is in memory. Because of other evictions, it might get moved
65*d415bd75Srobert   /// in a register in the end.
66*d415bd75Srobert   RS_Memory,
67*d415bd75Srobert 
68*d415bd75Srobert   /// There is nothing more we can do to this live range.  Abort compilation
69*d415bd75Srobert   /// if it can't be assigned.
70*d415bd75Srobert   RS_Done
71*d415bd75Srobert };
72*d415bd75Srobert 
73*d415bd75Srobert /// Cost of evicting interference - used by default advisor, and the eviction
74*d415bd75Srobert /// chain heuristic in RegAllocGreedy.
75*d415bd75Srobert // FIXME: this can be probably made an implementation detail of the default
76*d415bd75Srobert // advisor, if the eviction chain logic can be refactored.
77*d415bd75Srobert struct EvictionCost {
78*d415bd75Srobert   unsigned BrokenHints = 0; ///< Total number of broken hints.
79*d415bd75Srobert   float MaxWeight = 0;      ///< Maximum spill weight evicted.
80*d415bd75Srobert 
81*d415bd75Srobert   EvictionCost() = default;
82*d415bd75Srobert 
isMaxEvictionCost83*d415bd75Srobert   bool isMax() const { return BrokenHints == ~0u; }
84*d415bd75Srobert 
setMaxEvictionCost85*d415bd75Srobert   void setMax() { BrokenHints = ~0u; }
86*d415bd75Srobert 
setBrokenHintsEvictionCost87*d415bd75Srobert   void setBrokenHints(unsigned NHints) { BrokenHints = NHints; }
88*d415bd75Srobert 
89*d415bd75Srobert   bool operator<(const EvictionCost &O) const {
90*d415bd75Srobert     return std::tie(BrokenHints, MaxWeight) <
91*d415bd75Srobert            std::tie(O.BrokenHints, O.MaxWeight);
92*d415bd75Srobert   }
93*d415bd75Srobert };
94*d415bd75Srobert 
95*d415bd75Srobert /// Interface to the eviction advisor, which is responsible for making a
96*d415bd75Srobert /// decision as to which live ranges should be evicted (if any).
97*d415bd75Srobert class RAGreedy;
98*d415bd75Srobert class RegAllocEvictionAdvisor {
99*d415bd75Srobert public:
100*d415bd75Srobert   RegAllocEvictionAdvisor(const RegAllocEvictionAdvisor &) = delete;
101*d415bd75Srobert   RegAllocEvictionAdvisor(RegAllocEvictionAdvisor &&) = delete;
102*d415bd75Srobert   virtual ~RegAllocEvictionAdvisor() = default;
103*d415bd75Srobert 
104*d415bd75Srobert   /// Find a physical register that can be freed by evicting the FixedRegisters,
105*d415bd75Srobert   /// or return NoRegister. The eviction decision is assumed to be correct (i.e.
106*d415bd75Srobert   /// no fixed live ranges are evicted) and profitable.
107*d415bd75Srobert   virtual MCRegister tryFindEvictionCandidate(
108*d415bd75Srobert       const LiveInterval &VirtReg, const AllocationOrder &Order,
109*d415bd75Srobert       uint8_t CostPerUseLimit, const SmallVirtRegSet &FixedRegisters) const = 0;
110*d415bd75Srobert 
111*d415bd75Srobert   /// Find out if we can evict the live ranges occupying the given PhysReg,
112*d415bd75Srobert   /// which is a hint (preferred register) for VirtReg.
113*d415bd75Srobert   virtual bool
114*d415bd75Srobert   canEvictHintInterference(const LiveInterval &VirtReg, MCRegister PhysReg,
115*d415bd75Srobert                            const SmallVirtRegSet &FixedRegisters) const = 0;
116*d415bd75Srobert 
117*d415bd75Srobert   /// Returns true if the given \p PhysReg is a callee saved register and has
118*d415bd75Srobert   /// not been used for allocation yet.
119*d415bd75Srobert   bool isUnusedCalleeSavedReg(MCRegister PhysReg) const;
120*d415bd75Srobert 
121*d415bd75Srobert protected:
122*d415bd75Srobert   RegAllocEvictionAdvisor(const MachineFunction &MF, const RAGreedy &RA);
123*d415bd75Srobert 
124*d415bd75Srobert   Register canReassign(const LiveInterval &VirtReg, Register PrevReg) const;
125*d415bd75Srobert 
126*d415bd75Srobert   // Get the upper limit of elements in the given Order we need to analize.
127*d415bd75Srobert   // TODO: is this heuristic,  we could consider learning it.
128*d415bd75Srobert   std::optional<unsigned> getOrderLimit(const LiveInterval &VirtReg,
129*d415bd75Srobert                                         const AllocationOrder &Order,
130*d415bd75Srobert                                         unsigned CostPerUseLimit) const;
131*d415bd75Srobert 
132*d415bd75Srobert   // Determine if it's worth trying to allocate this reg, given the
133*d415bd75Srobert   // CostPerUseLimit
134*d415bd75Srobert   // TODO: this is a heuristic component we could consider learning, too.
135*d415bd75Srobert   bool canAllocatePhysReg(unsigned CostPerUseLimit, MCRegister PhysReg) const;
136*d415bd75Srobert 
137*d415bd75Srobert   const MachineFunction &MF;
138*d415bd75Srobert   const RAGreedy &RA;
139*d415bd75Srobert   LiveRegMatrix *const Matrix;
140*d415bd75Srobert   LiveIntervals *const LIS;
141*d415bd75Srobert   VirtRegMap *const VRM;
142*d415bd75Srobert   MachineRegisterInfo *const MRI;
143*d415bd75Srobert   const TargetRegisterInfo *const TRI;
144*d415bd75Srobert   const RegisterClassInfo &RegClassInfo;
145*d415bd75Srobert   const ArrayRef<uint8_t> RegCosts;
146*d415bd75Srobert 
147*d415bd75Srobert   /// Run or not the local reassignment heuristic. This information is
148*d415bd75Srobert   /// obtained from the TargetSubtargetInfo.
149*d415bd75Srobert   const bool EnableLocalReassign;
150*d415bd75Srobert };
151*d415bd75Srobert 
152*d415bd75Srobert /// ImmutableAnalysis abstraction for fetching the Eviction Advisor. We model it
153*d415bd75Srobert /// as an analysis to decouple the user from the implementation insofar as
154*d415bd75Srobert /// dependencies on other analyses goes. The motivation for it being an
155*d415bd75Srobert /// immutable pass is twofold:
156*d415bd75Srobert /// - in the ML implementation case, the evaluator is stateless but (especially
157*d415bd75Srobert /// in the development mode) expensive to set up. With an immutable pass, we set
158*d415bd75Srobert /// it up once.
159*d415bd75Srobert /// - in the 'development' mode ML case, we want to capture the training log
160*d415bd75Srobert /// during allocation (this is a log of features encountered and decisions
161*d415bd75Srobert /// made), and then measure a score, potentially a few steps after allocation
162*d415bd75Srobert /// completes. So we need the properties of an immutable pass to keep the logger
163*d415bd75Srobert /// state around until we can make that measurement.
164*d415bd75Srobert ///
165*d415bd75Srobert /// Because we need to offer additional services in 'development' mode, the
166*d415bd75Srobert /// implementations of this analysis need to implement RTTI support.
167*d415bd75Srobert class RegAllocEvictionAdvisorAnalysis : public ImmutablePass {
168*d415bd75Srobert public:
169*d415bd75Srobert   enum class AdvisorMode : int { Default, Release, Development };
170*d415bd75Srobert 
RegAllocEvictionAdvisorAnalysis(AdvisorMode Mode)171*d415bd75Srobert   RegAllocEvictionAdvisorAnalysis(AdvisorMode Mode)
172*d415bd75Srobert       : ImmutablePass(ID), Mode(Mode){};
173*d415bd75Srobert   static char ID;
174*d415bd75Srobert 
175*d415bd75Srobert   /// Get an advisor for the given context (i.e. machine function, etc)
176*d415bd75Srobert   virtual std::unique_ptr<RegAllocEvictionAdvisor>
177*d415bd75Srobert   getAdvisor(const MachineFunction &MF, const RAGreedy &RA) = 0;
getAdvisorMode()178*d415bd75Srobert   AdvisorMode getAdvisorMode() const { return Mode; }
logRewardIfNeeded(const MachineFunction & MF,llvm::function_ref<float ()> GetReward)179*d415bd75Srobert   virtual void logRewardIfNeeded(const MachineFunction &MF,
180*d415bd75Srobert                                  llvm::function_ref<float()> GetReward){};
181*d415bd75Srobert 
182*d415bd75Srobert protected:
183*d415bd75Srobert   // This analysis preserves everything, and subclasses may have additional
184*d415bd75Srobert   // requirements.
getAnalysisUsage(AnalysisUsage & AU)185*d415bd75Srobert   void getAnalysisUsage(AnalysisUsage &AU) const override {
186*d415bd75Srobert     AU.setPreservesAll();
187*d415bd75Srobert   }
188*d415bd75Srobert 
189*d415bd75Srobert private:
190*d415bd75Srobert   StringRef getPassName() const override;
191*d415bd75Srobert   const AdvisorMode Mode;
192*d415bd75Srobert };
193*d415bd75Srobert 
194*d415bd75Srobert /// Specialization for the API used by the analysis infrastructure to create
195*d415bd75Srobert /// an instance of the eviction advisor.
196*d415bd75Srobert template <> Pass *callDefaultCtor<RegAllocEvictionAdvisorAnalysis>();
197*d415bd75Srobert 
198*d415bd75Srobert RegAllocEvictionAdvisorAnalysis *createReleaseModeAdvisor();
199*d415bd75Srobert 
200*d415bd75Srobert RegAllocEvictionAdvisorAnalysis *createDevelopmentModeAdvisor();
201*d415bd75Srobert 
202*d415bd75Srobert // TODO: move to RegAllocEvictionAdvisor.cpp when we move implementation
203*d415bd75Srobert // out of RegAllocGreedy.cpp
204*d415bd75Srobert class DefaultEvictionAdvisor : public RegAllocEvictionAdvisor {
205*d415bd75Srobert public:
DefaultEvictionAdvisor(const MachineFunction & MF,const RAGreedy & RA)206*d415bd75Srobert   DefaultEvictionAdvisor(const MachineFunction &MF, const RAGreedy &RA)
207*d415bd75Srobert       : RegAllocEvictionAdvisor(MF, RA) {}
208*d415bd75Srobert 
209*d415bd75Srobert private:
210*d415bd75Srobert   MCRegister tryFindEvictionCandidate(const LiveInterval &,
211*d415bd75Srobert                                       const AllocationOrder &, uint8_t,
212*d415bd75Srobert                                       const SmallVirtRegSet &) const override;
213*d415bd75Srobert   bool canEvictHintInterference(const LiveInterval &, MCRegister,
214*d415bd75Srobert                                 const SmallVirtRegSet &) const override;
215*d415bd75Srobert   bool canEvictInterferenceBasedOnCost(const LiveInterval &, MCRegister, bool,
216*d415bd75Srobert                                        EvictionCost &,
217*d415bd75Srobert                                        const SmallVirtRegSet &) const;
218*d415bd75Srobert   bool shouldEvict(const LiveInterval &A, bool, const LiveInterval &B,
219*d415bd75Srobert                    bool) const;
220*d415bd75Srobert };
221*d415bd75Srobert } // namespace llvm
222*d415bd75Srobert 
223*d415bd75Srobert #endif // LLVM_CODEGEN_REGALLOCEVICTIONADVISOR_H
224