blob: c3f893660ebfde99d34358c27fa71c181f11ddba [file] [log] [blame]
Matt Davis673412e2018-08-24 22:59:13 +00001//===--------------------- ResourceManager.h --------------------*- C++ -*-===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9/// \file
10///
11/// The classes here represent processor resource units and their management
12/// strategy. These classes are managed by the Scheduler.
13///
14//===----------------------------------------------------------------------===//
15
16#ifndef LLVM_TOOLS_LLVM_MCA_RESOURCE_MANAGER_H
17#define LLVM_TOOLS_LLVM_MCA_RESOURCE_MANAGER_H
18
19#include "Instruction.h"
20#include "llvm/ADT/ArrayRef.h"
21#include "llvm/ADT/DenseMap.h"
22#include "llvm/ADT/SmallVector.h"
23#include "llvm/MC/MCSchedule.h"
24
25namespace mca {
26
27/// Used to notify the internal state of a processor resource.
28///
29/// A processor resource is available if it is not reserved, and there are
30/// available slots in the buffer. A processor resource is unavailable if it
31/// is either reserved, or the associated buffer is full. A processor resource
32/// with a buffer size of -1 is always available if it is not reserved.
33///
34/// Values of type ResourceStateEvent are returned by method
35/// ResourceState::isBufferAvailable(), which is used to query the internal
36/// state of a resource.
37///
38/// The naming convention for resource state events is:
39/// * Event names start with prefix RS_
40/// * Prefix RS_ is followed by a string describing the actual resource state.
41enum ResourceStateEvent {
42 RS_BUFFER_AVAILABLE,
43 RS_BUFFER_UNAVAILABLE,
44 RS_RESERVED
45};
46
47/// Resource allocation strategy used by hardware scheduler resources.
48class ResourceStrategy {
49 ResourceStrategy(const ResourceStrategy &) = delete;
50 ResourceStrategy &operator=(const ResourceStrategy &) = delete;
51
52public:
53 ResourceStrategy() {}
54 virtual ~ResourceStrategy();
55
56 /// Selects a processor resource unit from a ReadyMask.
57 virtual uint64_t select(uint64_t ReadyMask) = 0;
58
59 /// Called by the ResourceManager when a processor resource group, or a
60 /// processor resource with multiple units has become unavailable.
61 ///
62 /// The default strategy uses this information to bias its selection logic.
63 virtual void used(uint64_t ResourceMask) {}
64};
65
66/// Default resource allocation strategy used by processor resource groups and
67/// processor resources with multiple units.
68class DefaultResourceStrategy final : public ResourceStrategy {
69 /// A Mask of resource unit identifiers.
70 ///
71 /// There is one bit set for every available resource unit.
72 /// It defaults to the value of field ResourceSizeMask in ResourceState.
73 const unsigned ResourceUnitMask;
74
75 /// A simple round-robin selector for processor resource units.
76 /// Each bit of this mask identifies a sub resource within a group.
77 ///
78 /// As an example, lets assume that this is a default policy for a
79 /// processor resource group composed by the following three units:
80 /// ResourceA -- 0b001
81 /// ResourceB -- 0b010
82 /// ResourceC -- 0b100
83 ///
84 /// Field NextInSequenceMask is used to select the next unit from the set of
85 /// resource units. It defaults to the value of field `ResourceUnitMasks` (in
86 /// this example, it defaults to mask '0b111').
87 ///
88 /// The round-robin selector would firstly select 'ResourceC', then
89 /// 'ResourceB', and eventually 'ResourceA'. When a resource R is used, the
90 /// corresponding bit in NextInSequenceMask is cleared. For example, if
91 /// 'ResourceC' is selected, then the new value of NextInSequenceMask becomes
92 /// 0xb011.
93 ///
94 /// When NextInSequenceMask becomes zero, it is automatically reset to the
95 /// default value (i.e. ResourceUnitMask).
96 uint64_t NextInSequenceMask;
97
98 /// This field is used to track resource units that are used (i.e. selected)
99 /// by other groups other than the one associated with this strategy object.
100 ///
101 /// In LLVM processor resource groups are allowed to partially (or fully)
102 /// overlap. That means, a same unit may be visible to multiple groups.
103 /// This field keeps track of uses that have originated from outside of
104 /// this group. The idea is to bias the selection strategy, so that resources
105 /// that haven't been used by other groups get prioritized.
106 ///
107 /// The end goal is to (try to) keep the resource distribution as much uniform
108 /// as possible. By construction, this mask only tracks one-level of resource
109 /// usage. Therefore, this strategy is expected to be less accurate when same
110 /// units are used multiple times by other groups within a single round of
111 /// select.
112 ///
113 /// Note: an LRU selector would have a better accuracy at the cost of being
114 /// slightly more expensive (mostly in terms of runtime cost). Methods
115 /// 'select' and 'used', are always in the hot execution path of llvm-mca.
116 /// Therefore, a slow implementation of 'select' would have a negative impact
117 /// on the overall performance of the tool.
118 uint64_t RemovedFromNextInSequence;
119
120 void skipMask(uint64_t Mask);
121
122public:
123 DefaultResourceStrategy(uint64_t UnitMask)
124 : ResourceStrategy(), ResourceUnitMask(UnitMask),
125 NextInSequenceMask(UnitMask), RemovedFromNextInSequence(0) {}
126 virtual ~DefaultResourceStrategy() = default;
127
128 uint64_t select(uint64_t ReadyMask) override;
129 void used(uint64_t Mask) override;
130};
131
132/// A processor resource descriptor.
133///
134/// There is an instance of this class for every processor resource defined by
135/// the machine scheduling model.
136/// Objects of class ResourceState dynamically track the usage of processor
137/// resource units.
138class ResourceState {
139 /// An index to the MCProcResourceDesc entry in the processor model.
140 const unsigned ProcResourceDescIndex;
141 /// A resource mask. This is generated by the tool with the help of
142 /// function `mca::createProcResourceMasks' (see Support.h).
143 const uint64_t ResourceMask;
144
145 /// A ProcResource can have multiple units.
146 ///
147 /// For processor resource groups,
148 /// this field default to the value of field `ResourceMask`; the number of
149 /// bits set is equal to the cardinality of the group. For normal (i.e.
150 /// non-group) resources, the number of bits set in this mask is equivalent
151 /// to the number of units declared by the processor model (see field
152 /// 'NumUnits' in 'ProcResourceUnits').
153 uint64_t ResourceSizeMask;
154
155 /// A mask of ready units.
156 uint64_t ReadyMask;
157
158 /// Buffered resources will have this field set to a positive number different
159 /// than zero. A buffered resource behaves like a reservation station
160 /// implementing its own buffer for out-of-order execution.
161 ///
162 /// A BufferSize of 1 is used by scheduler resources that force in-order
163 /// execution.
164 ///
165 /// A BufferSize of 0 is used to model in-order issue/dispatch resources.
166 /// Since in-order issue/dispatch resources don't implement buffers, dispatch
167 /// events coincide with issue events.
168 /// Also, no other instruction ca be dispatched/issue while this resource is
169 /// in use. Only when all the "resource cycles" are consumed (after the issue
170 /// event), a new instruction ca be dispatched.
171 const int BufferSize;
172
173 /// Available slots in the buffer (zero, if this is not a buffered resource).
174 unsigned AvailableSlots;
175
176 /// This field is set if this resource is currently reserved.
177 ///
178 /// Resources can be reserved for a number of cycles.
179 /// Instructions can still be dispatched to reserved resources. However,
180 /// istructions dispatched to a reserved resource cannot be issued to the
181 /// underlying units (i.e. pipelines) until the resource is released.
182 bool Unavailable;
183
184 /// Checks for the availability of unit 'SubResMask' in the group.
185 bool isSubResourceReady(uint64_t SubResMask) const {
186 return ReadyMask & SubResMask;
187 }
188
189public:
190 ResourceState(const llvm::MCProcResourceDesc &Desc, unsigned Index,
191 uint64_t Mask);
192
193 unsigned getProcResourceID() const { return ProcResourceDescIndex; }
194 uint64_t getResourceMask() const { return ResourceMask; }
195 uint64_t getReadyMask() const { return ReadyMask; }
196 int getBufferSize() const { return BufferSize; }
197
198 bool isBuffered() const { return BufferSize > 0; }
199 bool isInOrder() const { return BufferSize == 1; }
200
201 /// Returns true if this is an in-order dispatch/issue resource.
202 bool isADispatchHazard() const { return BufferSize == 0; }
203 bool isReserved() const { return Unavailable; }
204
205 void setReserved() { Unavailable = true; }
206 void clearReserved() { Unavailable = false; }
207
208 /// Returs true if this resource is not reserved, and if there are at least
209 /// `NumUnits` available units.
210 bool isReady(unsigned NumUnits = 1) const;
211
212 bool isAResourceGroup() const {
213 return llvm::countPopulation(ResourceMask) > 1;
214 }
215
216 bool containsResource(uint64_t ID) const { return ResourceMask & ID; }
217
218 void markSubResourceAsUsed(uint64_t ID) {
219 assert(isSubResourceReady(ID));
220 ReadyMask ^= ID;
221 }
222
223 void releaseSubResource(uint64_t ID) {
224 assert(!isSubResourceReady(ID));
225 ReadyMask ^= ID;
226 }
227
228 unsigned getNumUnits() const {
229 return isAResourceGroup() ? 1U : llvm::countPopulation(ResourceSizeMask);
230 }
231
232 /// Checks if there is an available slot in the resource buffer.
233 ///
234 /// Returns RS_BUFFER_AVAILABLE if this is not a buffered resource, or if
235 /// there is a slot available.
236 ///
237 /// Returns RS_RESERVED if this buffered resource is a dispatch hazard, and it
238 /// is reserved.
239 ///
240 /// Returns RS_BUFFER_UNAVAILABLE if there are no available slots.
241 ResourceStateEvent isBufferAvailable() const;
242
243 /// Reserve a slot in the buffer.
244 void reserveBuffer() {
245 if (AvailableSlots)
246 AvailableSlots--;
247 }
248
249 /// Release a slot in the buffer.
250 void releaseBuffer() {
251 if (BufferSize > 0)
252 AvailableSlots++;
253 assert(AvailableSlots <= static_cast<unsigned>(BufferSize));
254 }
255
256#ifndef NDEBUG
257 void dump() const;
258#endif
259};
260
261/// A resource unit identifier.
262///
263/// This is used to identify a specific processor resource unit using a pair
264/// of indices where the 'first' index is a processor resource mask, and the
265/// 'second' index is an index for a "sub-resource" (i.e. unit).
266typedef std::pair<uint64_t, uint64_t> ResourceRef;
267
268// First: a MCProcResourceDesc index identifying a buffered resource.
269// Second: max number of buffer entries used in this resource.
270typedef std::pair<unsigned, unsigned> BufferUsageEntry;
271
272/// A resource manager for processor resource units and groups.
273///
274/// This class owns all the ResourceState objects, and it is responsible for
275/// acting on requests from a Scheduler by updating the internal state of
276/// ResourceState objects.
277/// This class doesn't know about instruction itineraries and functional units.
278/// In future, it can be extended to support itineraries too through the same
279/// public interface.
280class ResourceManager {
281 // The resource manager owns all the ResourceState.
282 std::vector<std::unique_ptr<ResourceState>> Resources;
283 std::vector<std::unique_ptr<ResourceStrategy>> Strategies;
284
285 // Keeps track of which resources are busy, and how many cycles are left
286 // before those become usable again.
287 llvm::SmallDenseMap<ResourceRef, unsigned> BusyResources;
288
289 // A table to map processor resource IDs to processor resource masks.
290 llvm::SmallVector<uint64_t, 8> ProcResID2Mask;
291
292 // Returns the actual resource unit that will be used.
293 ResourceRef selectPipe(uint64_t ResourceID);
294
295 void use(const ResourceRef &RR);
296 void release(const ResourceRef &RR);
297
298 unsigned getNumUnits(uint64_t ResourceID) const;
299
300 // Overrides the selection strategy for the processor resource with the given
301 // mask.
302 void setCustomStrategyImpl(std::unique_ptr<ResourceStrategy> S,
303 uint64_t ResourceMask);
304
305public:
306 ResourceManager(const llvm::MCSchedModel &SM);
307 virtual ~ResourceManager() = default;
308
309 // Overrides the selection strategy for the resource at index ResourceID in
310 // the MCProcResourceDesc table.
311 void setCustomStrategy(std::unique_ptr<ResourceStrategy> S,
312 unsigned ResourceID) {
313 assert(ResourceID < ProcResID2Mask.size() &&
314 "Invalid resource index in input!");
315 return setCustomStrategyImpl(std::move(S), ProcResID2Mask[ResourceID]);
316 }
317
318 // Returns RS_BUFFER_AVAILABLE if buffered resources are not reserved, and if
319 // there are enough available slots in the buffers.
320 ResourceStateEvent canBeDispatched(llvm::ArrayRef<uint64_t> Buffers) const;
321
322 // Return the processor resource identifier associated to this Mask.
323 unsigned resolveResourceMask(uint64_t Mask) const;
324
325 // Consume a slot in every buffered resource from array 'Buffers'. Resource
326 // units that are dispatch hazards (i.e. BufferSize=0) are marked as reserved.
327 void reserveBuffers(llvm::ArrayRef<uint64_t> Buffers);
328
329 // Release buffer entries previously allocated by method reserveBuffers.
330 void releaseBuffers(llvm::ArrayRef<uint64_t> Buffers);
331
332 // Reserve a processor resource. A reserved resource is not available for
333 // instruction issue until it is released.
334 void reserveResource(uint64_t ResourceID);
335
336 // Release a previously reserved processor resource.
337 void releaseResource(uint64_t ResourceID);
338
339 // Returns true if all resources are in-order, and there is at least one
340 // resource which is a dispatch hazard (BufferSize = 0).
341 bool mustIssueImmediately(const InstrDesc &Desc) const;
342
343 bool canBeIssued(const InstrDesc &Desc) const;
344
345 void issueInstruction(
346 const InstrDesc &Desc,
347 llvm::SmallVectorImpl<std::pair<ResourceRef, double>> &Pipes);
348
349 void cycleEvent(llvm::SmallVectorImpl<ResourceRef> &ResourcesFreed);
350
351#ifndef NDEBUG
352 void dump() const {
353 for (const std::unique_ptr<ResourceState> &Resource : Resources)
354 Resource->dump();
355 }
356#endif
357};
358} // namespace mca
359
360#endif // LLVM_TOOLS_LLVM_MCA_RESOURCE_MANAGER_H