1//===--------------------- ResourceManager.cpp ------------------*- 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/// \file
9///
10/// The classes here represent processor resource units and their management
11/// strategy.  These classes are managed by the Scheduler.
12///
13//===----------------------------------------------------------------------===//
14
15#include "llvm/MCA/HardwareUnits/ResourceManager.h"
16#include "llvm/MCA/Support.h"
17#include "llvm/Support/Debug.h"
18#include "llvm/Support/raw_ostream.h"
19
20namespace llvm {
21namespace mca {
22
23#define DEBUG_TYPE "llvm-mca"
24ResourceStrategy::~ResourceStrategy() = default;
25
26static uint64_t selectImpl(uint64_t CandidateMask,
27                           uint64_t &NextInSequenceMask) {
28  // The upper bit set in CandidateMask identifies our next candidate resource.
29  CandidateMask = 1ULL << getResourceStateIndex(CandidateMask);
30  NextInSequenceMask &= (CandidateMask | (CandidateMask - 1));
31  return CandidateMask;
32}
33
34uint64_t DefaultResourceStrategy::select(uint64_t ReadyMask) {
35  // This method assumes that ReadyMask cannot be zero.
36  uint64_t CandidateMask = ReadyMask & NextInSequenceMask;
37  if (CandidateMask)
38    return selectImpl(CandidateMask, NextInSequenceMask);
39
40  NextInSequenceMask = ResourceUnitMask ^ RemovedFromNextInSequence;
41  RemovedFromNextInSequence = 0;
42  CandidateMask = ReadyMask & NextInSequenceMask;
43  if (CandidateMask)
44    return selectImpl(CandidateMask, NextInSequenceMask);
45
46  NextInSequenceMask = ResourceUnitMask;
47  CandidateMask = ReadyMask & NextInSequenceMask;
48  return selectImpl(CandidateMask, NextInSequenceMask);
49}
50
51void DefaultResourceStrategy::used(uint64_t Mask) {
52  if (Mask > NextInSequenceMask) {
53    RemovedFromNextInSequence |= Mask;
54    return;
55  }
56
57  NextInSequenceMask &= (~Mask);
58  if (NextInSequenceMask)
59    return;
60
61  NextInSequenceMask = ResourceUnitMask ^ RemovedFromNextInSequence;
62  RemovedFromNextInSequence = 0;
63}
64
65ResourceState::ResourceState(const MCProcResourceDesc &Desc, unsigned Index,
66                             uint64_t Mask)
67    : ProcResourceDescIndex(Index), ResourceMask(Mask),
68      BufferSize(Desc.BufferSize), IsAGroup(countPopulation(ResourceMask) > 1) {
69  if (IsAGroup) {
70    ResourceSizeMask =
71        ResourceMask ^ 1ULL << getResourceStateIndex(ResourceMask);
72  } else {
73    ResourceSizeMask = (1ULL << Desc.NumUnits) - 1;
74  }
75  ReadyMask = ResourceSizeMask;
76  AvailableSlots = BufferSize == -1 ? 0U : static_cast<unsigned>(BufferSize);
77  Unavailable = false;
78}
79
80bool ResourceState::isReady(unsigned NumUnits) const {
81  return (!isReserved() || isADispatchHazard()) &&
82         countPopulation(ReadyMask) >= NumUnits;
83}
84
85ResourceStateEvent ResourceState::isBufferAvailable() const {
86  if (isADispatchHazard() && isReserved())
87    return RS_RESERVED;
88  if (!isBuffered() || AvailableSlots)
89    return RS_BUFFER_AVAILABLE;
90  return RS_BUFFER_UNAVAILABLE;
91}
92
93#ifndef NDEBUG
94void ResourceState::dump() const {
95  dbgs() << "MASK=" << format_hex(ResourceMask, 16)
96         << ", SZMASK=" << format_hex(ResourceSizeMask, 16)
97         << ", RDYMASK=" << format_hex(ReadyMask, 16)
98         << ", BufferSize=" << BufferSize
99         << ", AvailableSlots=" << AvailableSlots
100         << ", Reserved=" << Unavailable << '\n';
101}
102#endif
103
104static std::unique_ptr<ResourceStrategy>
105getStrategyFor(const ResourceState &RS) {
106  if (RS.isAResourceGroup() || RS.getNumUnits() > 1)
107    return std::make_unique<DefaultResourceStrategy>(RS.getReadyMask());
108  return std::unique_ptr<ResourceStrategy>(nullptr);
109}
110
111ResourceManager::ResourceManager(const MCSchedModel &SM)
112    : Resources(SM.getNumProcResourceKinds() - 1),
113      Strategies(SM.getNumProcResourceKinds() - 1),
114      Resource2Groups(SM.getNumProcResourceKinds() - 1, 0),
115      ProcResID2Mask(SM.getNumProcResourceKinds(), 0),
116      ResIndex2ProcResID(SM.getNumProcResourceKinds() - 1, 0),
117      ProcResUnitMask(0), ReservedResourceGroups(0),
118      AvailableBuffers(~0ULL), ReservedBuffers(0) {
119  computeProcResourceMasks(SM, ProcResID2Mask);
120
121  // initialize vector ResIndex2ProcResID.
122  for (unsigned I = 1, E = SM.getNumProcResourceKinds(); I < E; ++I) {
123    unsigned Index = getResourceStateIndex(ProcResID2Mask[I]);
124    ResIndex2ProcResID[Index] = I;
125  }
126
127  for (unsigned I = 1, E = SM.getNumProcResourceKinds(); I < E; ++I) {
128    uint64_t Mask = ProcResID2Mask[I];
129    unsigned Index = getResourceStateIndex(Mask);
130    Resources[Index] =
131        std::make_unique<ResourceState>(*SM.getProcResource(I), I, Mask);
132    Strategies[Index] = getStrategyFor(*Resources[Index]);
133  }
134
135  for (unsigned I = 1, E = SM.getNumProcResourceKinds(); I < E; ++I) {
136    uint64_t Mask = ProcResID2Mask[I];
137    unsigned Index = getResourceStateIndex(Mask);
138    const ResourceState &RS = *Resources[Index];
139    if (!RS.isAResourceGroup()) {
140      ProcResUnitMask |= Mask;
141      continue;
142    }
143
144    uint64_t GroupMaskIdx = 1ULL << Index;
145    Mask -= GroupMaskIdx;
146    while (Mask) {
147      // Extract lowest set isolated bit.
148      uint64_t Unit = Mask & (-Mask);
149      unsigned IndexUnit = getResourceStateIndex(Unit);
150      Resource2Groups[IndexUnit] |= GroupMaskIdx;
151      Mask ^= Unit;
152    }
153  }
154
155  AvailableProcResUnits = ProcResUnitMask;
156}
157
158void ResourceManager::setCustomStrategyImpl(std::unique_ptr<ResourceStrategy> S,
159                                            uint64_t ResourceMask) {
160  unsigned Index = getResourceStateIndex(ResourceMask);
161  assert(Index < Resources.size() && "Invalid processor resource index!");
162  assert(S && "Unexpected null strategy in input!");
163  Strategies[Index] = std::move(S);
164}
165
166unsigned ResourceManager::resolveResourceMask(uint64_t Mask) const {
167  return ResIndex2ProcResID[getResourceStateIndex(Mask)];
168}
169
170unsigned ResourceManager::getNumUnits(uint64_t ResourceID) const {
171  return Resources[getResourceStateIndex(ResourceID)]->getNumUnits();
172}
173
174// Returns the actual resource consumed by this Use.
175// First, is the primary resource ID.
176// Second, is the specific sub-resource ID.
177ResourceRef ResourceManager::selectPipe(uint64_t ResourceID) {
178  unsigned Index = getResourceStateIndex(ResourceID);
179  assert(Index < Resources.size() && "Invalid resource use!");
180  ResourceState &RS = *Resources[Index];
181  assert(RS.isReady() && "No available units to select!");
182
183  // Special case where RS is not a group, and it only declares a single
184  // resource unit.
185  if (!RS.isAResourceGroup() && RS.getNumUnits() == 1)
186    return std::make_pair(ResourceID, RS.getReadyMask());
187
188  uint64_t SubResourceID = Strategies[Index]->select(RS.getReadyMask());
189  if (RS.isAResourceGroup())
190    return selectPipe(SubResourceID);
191  return std::make_pair(ResourceID, SubResourceID);
192}
193
194void ResourceManager::use(const ResourceRef &RR) {
195  // Mark the sub-resource referenced by RR as used.
196  unsigned RSID = getResourceStateIndex(RR.first);
197  ResourceState &RS = *Resources[RSID];
198  RS.markSubResourceAsUsed(RR.second);
199  // Remember to update the resource strategy for non-group resources with
200  // multiple units.
201  if (RS.getNumUnits() > 1)
202    Strategies[RSID]->used(RR.second);
203
204  // If there are still available units in RR.first,
205  // then we are done.
206  if (RS.isReady())
207    return;
208
209  AvailableProcResUnits ^= RR.first;
210
211  // Notify groups that RR.first is no longer available.
212  uint64_t Users = Resource2Groups[RSID];
213  while (Users) {
214    // Extract lowest set isolated bit.
215    unsigned GroupIndex = getResourceStateIndex(Users & (-Users));
216    ResourceState &CurrentUser = *Resources[GroupIndex];
217    CurrentUser.markSubResourceAsUsed(RR.first);
218    Strategies[GroupIndex]->used(RR.first);
219    // Reset lowest set bit.
220    Users &= Users - 1;
221  }
222}
223
224void ResourceManager::release(const ResourceRef &RR) {
225  unsigned RSID = getResourceStateIndex(RR.first);
226  ResourceState &RS = *Resources[RSID];
227  bool WasFullyUsed = !RS.isReady();
228  RS.releaseSubResource(RR.second);
229  if (!WasFullyUsed)
230    return;
231
232  AvailableProcResUnits ^= RR.first;
233
234  // Notify groups that RR.first is now available again.
235  uint64_t Users = Resource2Groups[RSID];
236  while (Users) {
237    unsigned GroupIndex = getResourceStateIndex(Users & (-Users));
238    ResourceState &CurrentUser = *Resources[GroupIndex];
239    CurrentUser.releaseSubResource(RR.first);
240    Users &= Users - 1;
241  }
242}
243
244ResourceStateEvent
245ResourceManager::canBeDispatched(uint64_t ConsumedBuffers) const {
246  if (ConsumedBuffers & ReservedBuffers)
247    return ResourceStateEvent::RS_RESERVED;
248  if (ConsumedBuffers & (~AvailableBuffers))
249    return ResourceStateEvent::RS_BUFFER_UNAVAILABLE;
250  return ResourceStateEvent::RS_BUFFER_AVAILABLE;
251}
252
253void ResourceManager::reserveBuffers(uint64_t ConsumedBuffers) {
254  while (ConsumedBuffers) {
255    uint64_t CurrentBuffer = ConsumedBuffers & (-ConsumedBuffers);
256    ResourceState &RS = *Resources[getResourceStateIndex(CurrentBuffer)];
257    ConsumedBuffers ^= CurrentBuffer;
258    assert(RS.isBufferAvailable() == ResourceStateEvent::RS_BUFFER_AVAILABLE);
259    if (!RS.reserveBuffer())
260      AvailableBuffers ^= CurrentBuffer;
261    if (RS.isADispatchHazard()) {
262      // Reserve this buffer now, and release it once pipeline resources
263      // consumed by the instruction become available again.
264      // We do this to simulate an in-order dispatch/issue of instructions.
265      ReservedBuffers ^= CurrentBuffer;
266    }
267  }
268}
269
270void ResourceManager::releaseBuffers(uint64_t ConsumedBuffers) {
271  AvailableBuffers |= ConsumedBuffers;
272  while (ConsumedBuffers) {
273    uint64_t CurrentBuffer = ConsumedBuffers & (-ConsumedBuffers);
274    ResourceState &RS = *Resources[getResourceStateIndex(CurrentBuffer)];
275    ConsumedBuffers ^= CurrentBuffer;
276    RS.releaseBuffer();
277    // Do not unreserve dispatch hazard resource buffers. Wait until all
278    // pipeline resources have been freed too.
279  }
280}
281
282uint64_t ResourceManager::checkAvailability(const InstrDesc &Desc) const {
283  uint64_t BusyResourceMask = 0;
284  for (const std::pair<uint64_t, ResourceUsage> &E : Desc.Resources) {
285    unsigned NumUnits = E.second.isReserved() ? 0U : E.second.NumUnits;
286    unsigned Index = getResourceStateIndex(E.first);
287    if (!Resources[Index]->isReady(NumUnits))
288      BusyResourceMask |= E.first;
289  }
290
291  BusyResourceMask &= ProcResUnitMask;
292  if (BusyResourceMask)
293    return BusyResourceMask;
294  return Desc.UsedProcResGroups & ReservedResourceGroups;
295}
296
297void ResourceManager::issueInstruction(
298    const InstrDesc &Desc,
299    SmallVectorImpl<std::pair<ResourceRef, ResourceCycles>> &Pipes) {
300  for (const std::pair<uint64_t, ResourceUsage> &R : Desc.Resources) {
301    const CycleSegment &CS = R.second.CS;
302    if (!CS.size()) {
303      releaseResource(R.first);
304      continue;
305    }
306
307    assert(CS.begin() == 0 && "Invalid {Start, End} cycles!");
308    if (!R.second.isReserved()) {
309      ResourceRef Pipe = selectPipe(R.first);
310      use(Pipe);
311      BusyResources[Pipe] += CS.size();
312      Pipes.emplace_back(std::pair<ResourceRef, ResourceCycles>(
313          Pipe, ResourceCycles(CS.size())));
314    } else {
315      assert((countPopulation(R.first) > 1) && "Expected a group!");
316      // Mark this group as reserved.
317      assert(R.second.isReserved());
318      reserveResource(R.first);
319      BusyResources[ResourceRef(R.first, R.first)] += CS.size();
320    }
321  }
322}
323
324void ResourceManager::cycleEvent(SmallVectorImpl<ResourceRef> &ResourcesFreed) {
325  for (std::pair<ResourceRef, unsigned> &BR : BusyResources) {
326    if (BR.second)
327      BR.second--;
328    if (!BR.second) {
329      // Release this resource.
330      const ResourceRef &RR = BR.first;
331
332      if (countPopulation(RR.first) == 1)
333        release(RR);
334      releaseResource(RR.first);
335      ResourcesFreed.push_back(RR);
336    }
337  }
338
339  for (const ResourceRef &RF : ResourcesFreed)
340    BusyResources.erase(RF);
341}
342
343void ResourceManager::reserveResource(uint64_t ResourceID) {
344  const unsigned Index = getResourceStateIndex(ResourceID);
345  ResourceState &Resource = *Resources[Index];
346  assert(Resource.isAResourceGroup() && !Resource.isReserved() &&
347         "Unexpected resource state found!");
348  Resource.setReserved();
349  ReservedResourceGroups ^= 1ULL << Index;
350}
351
352void ResourceManager::releaseResource(uint64_t ResourceID) {
353  const unsigned Index = getResourceStateIndex(ResourceID);
354  ResourceState &Resource = *Resources[Index];
355  Resource.clearReserved();
356  if (Resource.isAResourceGroup())
357    ReservedResourceGroups ^= 1ULL << Index;
358  // Now it is safe to release dispatch/issue resources.
359  if (Resource.isADispatchHazard())
360    ReservedBuffers ^= 1ULL << Index;
361}
362
363} // namespace mca
364} // namespace llvm
365