ResourceManager.cpp 14 KB

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