| //===- HLSLBinding.cpp - Representation for resource bindings in HLSL -----===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "llvm/Frontend/HLSL/HLSLBinding.h" | |
| #include "llvm/ADT/STLExtras.h" | |
| using namespace llvm; | |
| using namespace hlsl; | |
| std::optional<uint32_t> | |
| BindingInfo::findAvailableBinding(dxil::ResourceClass RC, uint32_t Space, | |
| int32_t Size) { | |
| BindingSpaces &BS = getBindingSpaces(RC); | |
| RegisterSpace &RS = BS.getOrInsertSpace(Space); | |
| return RS.findAvailableBinding(Size); | |
| } | |
| BindingInfo::RegisterSpace & | |
| BindingInfo::BindingSpaces::getOrInsertSpace(uint32_t Space) { | |
| for (auto It = Spaces.begin(), End = Spaces.end(); It != End; ++It) { | |
| if (It->Space == Space) | |
| return *It; | |
| if (It->Space < Space) | |
| continue; | |
| return *Spaces.insert(It, Space); | |
| } | |
| return Spaces.emplace_back(Space); | |
| } | |
| std::optional<uint32_t> | |
| BindingInfo::RegisterSpace::findAvailableBinding(int32_t Size) { | |
| assert((Size == -1 || Size > 0) && "invalid size"); | |
| if (FreeRanges.empty()) | |
| return std::nullopt; | |
| // unbounded array | |
| if (Size == -1) { | |
| BindingRange &Last = FreeRanges.back(); | |
| if (Last.UpperBound != ~0u) | |
| // this space is already occupied by an unbounded array | |
| return std::nullopt; | |
| uint32_t RegSlot = Last.LowerBound; | |
| FreeRanges.pop_back(); | |
| return RegSlot; | |
| } | |
| // single resource or fixed-size array | |
| for (BindingRange &R : FreeRanges) { | |
| // compare the size as uint64_t to prevent overflow for range (0, ~0u) | |
| if ((uint64_t)R.UpperBound - R.LowerBound + 1 < (uint64_t)Size) | |
| continue; | |
| uint32_t RegSlot = R.LowerBound; | |
| // This might create a range where (LowerBound == UpperBound + 1). When | |
| // that happens, the next time this function is called the range will | |
| // skipped over by the check above (at this point Size is always > 0). | |
| R.LowerBound += Size; | |
| return RegSlot; | |
| } | |
| return std::nullopt; | |
| } | |
| BindingInfo BindingInfoBuilder::calculateBindingInfo( | |
| llvm::function_ref<void(const BindingInfoBuilder &Builder, | |
| const Binding &Overlapping)> | |
| ReportOverlap) { | |
| // sort all the collected bindings | |
| llvm::stable_sort(Bindings); | |
| // remove duplicates | |
| Binding *NewEnd = llvm::unique(Bindings); | |
| if (NewEnd != Bindings.end()) | |
| Bindings.erase(NewEnd, Bindings.end()); | |
| BindingInfo Info; | |
| // Go over the sorted bindings and build up lists of free register ranges | |
| // for each binding type and used spaces. Bindings are sorted by resource | |
| // class, space, and lower bound register slot. | |
| BindingInfo::BindingSpaces *BS = | |
| &Info.getBindingSpaces(dxil::ResourceClass::SRV); | |
| for (const Binding &B : Bindings) { | |
| if (BS->RC != B.RC) | |
| // move to the next resource class spaces | |
| BS = &Info.getBindingSpaces(B.RC); | |
| BindingInfo::RegisterSpace *S = BS->Spaces.empty() | |
| ? &BS->Spaces.emplace_back(B.Space) | |
| : &BS->Spaces.back(); | |
| assert(S->Space <= B.Space && "bindings not sorted correctly?"); | |
| if (B.Space != S->Space) | |
| // add new space | |
| S = &BS->Spaces.emplace_back(B.Space); | |
| // The space is full - there are no free slots left, or the rest of the | |
| // slots are taken by an unbounded array. Report the overlapping to the | |
| // caller. | |
| if (S->FreeRanges.empty() || S->FreeRanges.back().UpperBound < ~0u) { | |
| ReportOverlap(*this, B); | |
| continue; | |
| } | |
| // adjust the last free range lower bound, split it in two, or remove it | |
| BindingInfo::BindingRange &LastFreeRange = S->FreeRanges.back(); | |
| if (LastFreeRange.LowerBound == B.LowerBound) { | |
| if (B.UpperBound < ~0u) | |
| LastFreeRange.LowerBound = B.UpperBound + 1; | |
| else | |
| S->FreeRanges.pop_back(); | |
| } else if (LastFreeRange.LowerBound < B.LowerBound) { | |
| LastFreeRange.UpperBound = B.LowerBound - 1; | |
| if (B.UpperBound < ~0u) | |
| S->FreeRanges.emplace_back(B.UpperBound + 1, ~0u); | |
| } else { | |
| // We don't have room here. Report the overlapping binding to the caller | |
| // and mark any extra space this binding would use as unavailable. | |
| ReportOverlap(*this, B); | |
| if (B.UpperBound < ~0u) | |
| LastFreeRange.LowerBound = | |
| std::max(LastFreeRange.LowerBound, B.UpperBound + 1); | |
| else | |
| S->FreeRanges.pop_back(); | |
| } | |
| } | |
| return Info; | |
| } | |
| const Binding & | |
| BindingInfoBuilder::findOverlapping(const Binding &ReportedBinding) const { | |
| for (const Binding &Other : Bindings) | |
| if (ReportedBinding.LowerBound <= Other.UpperBound && | |
| Other.LowerBound <= ReportedBinding.UpperBound) | |
| return Other; | |
| llvm_unreachable("Searching for overlap for binding that does not overlap"); | |
| } |