diff --git a/llvm/include/llvm/Analysis/DXILResource.h b/llvm/include/llvm/Analysis/DXILResource.h index 956dcbcc33305..93c6bfb057ef5 100644 --- a/llvm/include/llvm/Analysis/DXILResource.h +++ b/llvm/include/llvm/Analysis/DXILResource.h @@ -12,6 +12,7 @@ #include "llvm/ADT/MapVector.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" +#include "llvm/Frontend/HLSL/HLSLBinding.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/GlobalVariable.h" #include "llvm/IR/PassManager.h" @@ -633,86 +634,25 @@ LLVM_ABI ModulePass *createDXILResourceWrapperPassPass(); // register slots to resources with implicit bindings, and in a // post-optimization validation pass that will raise diagnostic about // overlapping bindings. -// -// For example for these resource bindings: -// -// RWBuffer A[10] : register(u3); -// RWBuffer B[] : register(u5, space2) -// -// The analysis result for UAV binding type will look like this: -// -// UAVSpaces { -// ResClass = ResourceClass::UAV, -// Spaces = { -// { Space = 0, FreeRanges = {{ 0, 2 }, { 13, UINT32_MAX }} }, -// { Space = 2, FreeRanges = {{ 0, 4 }} } -// } -// } -// class DXILResourceBindingInfo { -public: - struct BindingRange { - uint32_t LowerBound; - uint32_t UpperBound; - BindingRange(uint32_t LB, uint32_t UB) : LowerBound(LB), UpperBound(UB) {} - }; - - struct RegisterSpace { - uint32_t Space; - SmallVector FreeRanges; - RegisterSpace(uint32_t Space) : Space(Space) { - FreeRanges.emplace_back(0, UINT32_MAX); - } - // Size == -1 means unbounded array - LLVM_ABI std::optional findAvailableBinding(int32_t Size); - }; - - struct BindingSpaces { - dxil::ResourceClass RC; - llvm::SmallVector Spaces; - BindingSpaces(dxil::ResourceClass RC) : RC(RC) {} - LLVM_ABI RegisterSpace &getOrInsertSpace(uint32_t Space); - }; - -private: - BindingSpaces SRVSpaces, UAVSpaces, CBufferSpaces, SamplerSpaces; - bool ImplicitBinding; - bool OverlappingBinding; + hlsl::BindingInfo Bindings; + bool HasImplicitBinding = false; + bool HasOverlappingBinding = false; // Populate the resource binding info given explicit resource binding calls // in the module. void populate(Module &M, DXILResourceTypeMap &DRTM); public: - DXILResourceBindingInfo() - : SRVSpaces(dxil::ResourceClass::SRV), - UAVSpaces(dxil::ResourceClass::UAV), - CBufferSpaces(dxil::ResourceClass::CBuffer), - SamplerSpaces(dxil::ResourceClass::Sampler), ImplicitBinding(false), - OverlappingBinding(false) {} - - bool hasImplicitBinding() const { return ImplicitBinding; } - void setHasImplicitBinding(bool Value) { ImplicitBinding = Value; } - bool hasOverlappingBinding() const { return OverlappingBinding; } - - BindingSpaces &getBindingSpaces(dxil::ResourceClass RC) { - switch (RC) { - case dxil::ResourceClass::SRV: - return SRVSpaces; - case dxil::ResourceClass::UAV: - return UAVSpaces; - case dxil::ResourceClass::CBuffer: - return CBufferSpaces; - case dxil::ResourceClass::Sampler: - return SamplerSpaces; - } + bool hasImplicitBinding() const { return HasImplicitBinding; } + void setHasImplicitBinding(bool Value) { HasImplicitBinding = Value; } + bool hasOverlappingBinding() const { return HasOverlappingBinding; } + void setHasOverlappingBinding(bool Value) { HasOverlappingBinding = Value; } - llvm_unreachable("Invalid resource class"); - } - - // Size == -1 means unbounded array LLVM_ABI std::optional - findAvailableBinding(dxil::ResourceClass RC, uint32_t Space, int32_t Size); + findAvailableBinding(dxil::ResourceClass RC, uint32_t Space, int32_t Size) { + return Bindings.findAvailableBinding(RC, Space, Size); + } friend class DXILResourceBindingAnalysis; friend class DXILResourceBindingWrapperPass; diff --git a/llvm/include/llvm/Frontend/HLSL/HLSLBinding.h b/llvm/include/llvm/Frontend/HLSL/HLSLBinding.h new file mode 100644 index 0000000000000..70a2eeb632f1b --- /dev/null +++ b/llvm/include/llvm/Frontend/HLSL/HLSLBinding.h @@ -0,0 +1,162 @@ +//===- HLSLBinding.h - 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 +// +//===----------------------------------------------------------------------===// +/// +/// \file This file contains objects to represent resource bindings. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FRONTEND_HLSL_HLSLBINDING_H +#define LLVM_FRONTEND_HLSL_HLSLBINDING_H + +#include "llvm/ADT/STLFunctionalExtras.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/Support/DXILABI.h" +#include "llvm/Support/ErrorHandling.h" + +namespace llvm { +namespace hlsl { + +/// BindingInfo represents the ranges of bindings and free space for each +/// `dxil::ResourceClass`. This can represent HLSL-level bindings as well as +/// bindings described in root signatures, and can be used for analysis of +/// overlapping or missing bindings as well as for finding space for implicit +/// bindings. +/// +/// As an example, given these resource bindings: +/// +/// RWBuffer A[10] : register(u3); +/// RWBuffer B[] : register(u5, space2) +/// +/// The binding info for UAV bindings should look like this: +/// +/// UAVSpaces { +/// ResClass = ResourceClass::UAV, +/// Spaces = { +/// { Space = 0u, FreeRanges = {{ 0u, 2u }, { 13u, ~0u }} }, +/// { Space = 2u, FreeRanges = {{ 0u, 4u }} } +/// } +/// } +class BindingInfo { +public: + struct BindingRange { + uint32_t LowerBound; + uint32_t UpperBound; + BindingRange(uint32_t LB, uint32_t UB) : LowerBound(LB), UpperBound(UB) {} + }; + + struct RegisterSpace { + uint32_t Space; + SmallVector FreeRanges; + RegisterSpace(uint32_t Space) : Space(Space) { + FreeRanges.emplace_back(0, ~0u); + } + // Size == -1 means unbounded array + LLVM_ABI std::optional findAvailableBinding(int32_t Size); + }; + + struct BindingSpaces { + dxil::ResourceClass RC; + llvm::SmallVector Spaces; + BindingSpaces(dxil::ResourceClass RC) : RC(RC) {} + LLVM_ABI RegisterSpace &getOrInsertSpace(uint32_t Space); + }; + +private: + BindingSpaces SRVSpaces{dxil::ResourceClass::SRV}; + BindingSpaces UAVSpaces{dxil::ResourceClass::UAV}; + BindingSpaces CBufferSpaces{dxil::ResourceClass::CBuffer}; + BindingSpaces SamplerSpaces{dxil::ResourceClass::Sampler}; + +public: + BindingSpaces &getBindingSpaces(dxil::ResourceClass RC) { + switch (RC) { + case dxil::ResourceClass::SRV: + return SRVSpaces; + case dxil::ResourceClass::UAV: + return UAVSpaces; + case dxil::ResourceClass::CBuffer: + return CBufferSpaces; + case dxil::ResourceClass::Sampler: + return SamplerSpaces; + } + + llvm_unreachable("Invalid resource class"); + } + const BindingSpaces &getBindingSpaces(dxil::ResourceClass RC) const { + return const_cast(this)->getBindingSpaces(RC); + } + + // Size == -1 means unbounded array + LLVM_ABI std::optional + findAvailableBinding(dxil::ResourceClass RC, uint32_t Space, int32_t Size); + + friend class BindingInfoBuilder; +}; + +/// Builder class for creating a /c BindingInfo. +class BindingInfoBuilder { +public: + struct Binding { + dxil::ResourceClass RC; + uint32_t Space; + uint32_t LowerBound; + uint32_t UpperBound; + const void *Cookie; + + Binding(dxil::ResourceClass RC, uint32_t Space, uint32_t LowerBound, + uint32_t UpperBound, const void *Cookie) + : RC(RC), Space(Space), LowerBound(LowerBound), UpperBound(UpperBound), + Cookie(Cookie) {} + + bool isUnbounded() const { return UpperBound == ~0U; } + + bool operator==(const Binding &RHS) const { + return std::tie(RC, Space, LowerBound, UpperBound, Cookie) == + std::tie(RHS.RC, RHS.Space, RHS.LowerBound, RHS.UpperBound, + RHS.Cookie); + } + bool operator!=(const Binding &RHS) const { return !(*this == RHS); } + + bool operator<(const Binding &RHS) const { + return std::tie(RC, Space, LowerBound) < + std::tie(RHS.RC, RHS.Space, RHS.LowerBound); + } + }; + +private: + SmallVector Bindings; + +public: + void trackBinding(dxil::ResourceClass RC, uint32_t Space, uint32_t LowerBound, + uint32_t UpperBound, const void *Cookie) { + Bindings.emplace_back(RC, Space, LowerBound, UpperBound, Cookie); + } + /// Calculate the binding info - \c ReportOverlap will be called once for each + /// overlapping binding. + BindingInfo calculateBindingInfo( + llvm::function_ref + ReportOverlap); + + /// Calculate the binding info - \c HasOverlap will be set to indicate whether + /// there are any overlapping bindings. + BindingInfo calculateBindingInfo(bool &HasOverlap) { + HasOverlap = false; + return calculateBindingInfo( + [&HasOverlap](auto, auto) { HasOverlap = true; }); + } + + /// For use in the \c ReportOverlap callback of \c calculateBindingInfo - + /// finds a binding that the \c ReportedBinding overlaps with. + const Binding &findOverlapping(const Binding &ReportedBinding) const; +}; + +} // namespace hlsl +} // namespace llvm + +#endif // LLVM_FRONTEND_HLSL_HLSLBINDING_H diff --git a/llvm/lib/Analysis/CMakeLists.txt b/llvm/lib/Analysis/CMakeLists.txt index cfde787b17790..16dd6f8b86006 100644 --- a/llvm/lib/Analysis/CMakeLists.txt +++ b/llvm/lib/Analysis/CMakeLists.txt @@ -175,6 +175,7 @@ add_llvm_component_library(LLVMAnalysis LINK_COMPONENTS BinaryFormat Core + FrontendHLSL Object ProfileData Support diff --git a/llvm/lib/Analysis/DXILResource.cpp b/llvm/lib/Analysis/DXILResource.cpp index 1959ab6e510a3..629fa7cddb9d4 100644 --- a/llvm/lib/Analysis/DXILResource.cpp +++ b/llvm/lib/Analysis/DXILResource.cpp @@ -995,18 +995,7 @@ SmallVector DXILResourceMap::findByUse(const Value *Key) { //===----------------------------------------------------------------------===// void DXILResourceBindingInfo::populate(Module &M, DXILResourceTypeMap &DRTM) { - struct Binding { - ResourceClass RC; - uint32_t Space; - uint32_t LowerBound; - uint32_t UpperBound; - Value *Name; - Binding(ResourceClass RC, uint32_t Space, uint32_t LowerBound, - uint32_t UpperBound, Value *Name) - : RC(RC), Space(Space), LowerBound(LowerBound), UpperBound(UpperBound), - Name(Name) {} - }; - SmallVector Bindings; + hlsl::BindingInfoBuilder Builder; // collect all of the llvm.dx.resource.handlefrombinding calls; // make a note if there is llvm.dx.resource.handlefromimplicitbinding @@ -1036,133 +1025,20 @@ void DXILResourceBindingInfo::populate(Module &M, DXILResourceTypeMap &DRTM) { assert((Size < 0 || (unsigned)LowerBound + Size - 1 <= UINT32_MAX) && "upper bound register overflow"); uint32_t UpperBound = Size < 0 ? UINT32_MAX : LowerBound + Size - 1; - Bindings.emplace_back(RTI.getResourceClass(), Space, LowerBound, - UpperBound, Name); + Builder.trackBinding(RTI.getResourceClass(), Space, LowerBound, + UpperBound, Name); } break; } case Intrinsic::dx_resource_handlefromimplicitbinding: { - ImplicitBinding = true; + HasImplicitBinding = true; break; } } } - // sort all the collected bindings - llvm::stable_sort(Bindings, [](auto &LHS, auto &RHS) { - return std::tie(LHS.RC, LHS.Space, LHS.LowerBound) < - std::tie(RHS.RC, RHS.Space, RHS.LowerBound); - }); - - // remove duplicates - Binding *NewEnd = llvm::unique(Bindings, [](auto &LHS, auto &RHS) { - return std::tie(LHS.RC, LHS.Space, LHS.LowerBound, LHS.UpperBound, - LHS.Name) == std::tie(RHS.RC, RHS.Space, RHS.LowerBound, - RHS.UpperBound, RHS.Name); - }); - if (NewEnd != Bindings.end()) - Bindings.erase(NewEnd); - - // 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. - BindingSpaces *BS = &SRVSpaces; - for (const Binding &B : Bindings) { - if (BS->RC != B.RC) - // move to the next resource class spaces - BS = &getBindingSpaces(B.RC); - - 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. Set flag to report overlapping - // binding later. - if (S->FreeRanges.empty() || S->FreeRanges.back().UpperBound < UINT32_MAX) { - OverlappingBinding = true; - continue; - } - - // adjust the last free range lower bound, split it in two, or remove it - BindingRange &LastFreeRange = S->FreeRanges.back(); - if (LastFreeRange.LowerBound == B.LowerBound) { - if (B.UpperBound < UINT32_MAX) - LastFreeRange.LowerBound = B.UpperBound + 1; - else - S->FreeRanges.pop_back(); - } else if (LastFreeRange.LowerBound < B.LowerBound) { - LastFreeRange.UpperBound = B.LowerBound - 1; - if (B.UpperBound < UINT32_MAX) - S->FreeRanges.emplace_back(B.UpperBound + 1, UINT32_MAX); - } else { - OverlappingBinding = true; - if (B.UpperBound < UINT32_MAX) - LastFreeRange.LowerBound = - std::max(LastFreeRange.LowerBound, B.UpperBound + 1); - else - S->FreeRanges.pop_back(); - } - } -} - -// returns std::nulopt if binding could not be found in given space -std::optional -DXILResourceBindingInfo::findAvailableBinding(dxil::ResourceClass RC, - uint32_t Space, int32_t Size) { - BindingSpaces &BS = getBindingSpaces(RC); - RegisterSpace &RS = BS.getOrInsertSpace(Space); - return RS.findAvailableBinding(Size); -} - -DXILResourceBindingInfo::RegisterSpace & -DXILResourceBindingInfo::BindingSpaces::getOrInsertSpace(uint32_t Space) { - for (auto *I = Spaces.begin(); I != Spaces.end(); ++I) { - if (I->Space == Space) - return *I; - if (I->Space < Space) - continue; - return *Spaces.insert(I, Space); - } - return Spaces.emplace_back(Space); -} - -std::optional -DXILResourceBindingInfo::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 != UINT32_MAX) - // 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, - // UINT32_MAX) - 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; + Bindings = Builder.calculateBindingInfo( + [this](auto, auto) { this->HasOverlappingBinding = true; }); } //===----------------------------------------------------------------------===// diff --git a/llvm/lib/Frontend/HLSL/CMakeLists.txt b/llvm/lib/Frontend/HLSL/CMakeLists.txt index 534346920ff19..3d225770e8d5b 100644 --- a/llvm/lib/Frontend/HLSL/CMakeLists.txt +++ b/llvm/lib/Frontend/HLSL/CMakeLists.txt @@ -1,5 +1,6 @@ add_llvm_component_library(LLVMFrontendHLSL CBuffer.cpp + HLSLBinding.cpp HLSLResource.cpp HLSLRootSignature.cpp RootSignatureMetadata.cpp diff --git a/llvm/lib/Frontend/HLSL/HLSLBinding.cpp b/llvm/lib/Frontend/HLSL/HLSLBinding.cpp new file mode 100644 index 0000000000000..d581311f22028 --- /dev/null +++ b/llvm/lib/Frontend/HLSL/HLSLBinding.cpp @@ -0,0 +1,142 @@ +//===- 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 +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 +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 + ReportOverlap) { + // sort all the collected bindings + llvm::stable_sort(Bindings); + + // remove duplicates + Binding *NewEnd = llvm::unique(Bindings); + if (NewEnd != Bindings.end()) + Bindings.erase(NewEnd); + + 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 BindingInfoBuilder::Binding &BindingInfoBuilder::findOverlapping( + const BindingInfoBuilder::Binding &ReportedBinding) const { + for (const BindingInfoBuilder::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"); +} diff --git a/llvm/unittests/Frontend/CMakeLists.txt b/llvm/unittests/Frontend/CMakeLists.txt index 281d509227a46..6e4ba5dc0c019 100644 --- a/llvm/unittests/Frontend/CMakeLists.txt +++ b/llvm/unittests/Frontend/CMakeLists.txt @@ -11,6 +11,7 @@ set(LLVM_LINK_COMPONENTS ) add_llvm_unittest(LLVMFrontendTests + HLSLBindingTest.cpp HLSLRootSignatureDumpTest.cpp HLSLRootSignatureRangesTest.cpp OpenACCTest.cpp diff --git a/llvm/unittests/Frontend/HLSLBindingTest.cpp b/llvm/unittests/Frontend/HLSLBindingTest.cpp new file mode 100644 index 0000000000000..bd6a0ff724cf4 --- /dev/null +++ b/llvm/unittests/Frontend/HLSLBindingTest.cpp @@ -0,0 +1,273 @@ +//===------ HLSLBindingTest.cpp - Resource binding tests ------------------===// +// +// 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/ArrayRef.h" +#include "llvm/Support/DXILABI.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +using namespace llvm; +using namespace llvm::dxil; + +MATCHER_P(HasSpecificValue, Value, "") { + return arg.has_value() && *arg == Value; +} + +static void +checkExpectedSpaceAndFreeRanges(hlsl::BindingInfo::RegisterSpace &RegSpace, + uint32_t ExpSpace, + ArrayRef ExpValues) { + EXPECT_EQ(RegSpace.Space, ExpSpace); + EXPECT_EQ(RegSpace.FreeRanges.size() * 2, ExpValues.size()); + unsigned I = 0; + for (auto &R : RegSpace.FreeRanges) { + EXPECT_EQ(R.LowerBound, ExpValues[I]); + EXPECT_EQ(R.UpperBound, ExpValues[I + 1]); + I += 2; + } +} + +TEST(HLSLBindingTest, TestTrivialCase) { + hlsl::BindingInfoBuilder Builder; + + Builder.trackBinding(ResourceClass::UAV, /*Space=*/0, /*LowerBound=*/5, + /*UpperBound=*/5, /*Cookie=*/nullptr); + bool HasOverlap; + hlsl::BindingInfo Info = Builder.calculateBindingInfo(HasOverlap); + + EXPECT_FALSE(HasOverlap); + + // check that UAV has exactly one gap + hlsl::BindingInfo::BindingSpaces &UAVSpaces = + Info.getBindingSpaces(ResourceClass::UAV); + EXPECT_EQ(UAVSpaces.RC, ResourceClass::UAV); + EXPECT_EQ(UAVSpaces.Spaces.size(), 1u); + checkExpectedSpaceAndFreeRanges(UAVSpaces.Spaces[0], 0, {0u, 4u, 6u, ~0u}); + + // check that other kinds of register spaces are all available + for (auto RC : + {ResourceClass::SRV, ResourceClass::CBuffer, ResourceClass::Sampler}) { + hlsl::BindingInfo::BindingSpaces &Spaces = Info.getBindingSpaces(RC); + EXPECT_EQ(Spaces.RC, RC); + EXPECT_EQ(Spaces.Spaces.size(), 0u); + } +} + +TEST(HLSLBindingTest, TestManyBindings) { + hlsl::BindingInfoBuilder Builder; + + // cbuffer CB : register(b3) { int a; } + // RWBuffer A[5] : register(u10, space20); + // StructuredBuffer B : register(t5); + // RWBuffer C : register(u5); + // StructuredBuffer D[5] : register(t0); + // RWBuffer E[2] : register(u2); + // SamplerState S1 : register(s5, space2); + // SamplerState S2 : register(s4, space2); + Builder.trackBinding(ResourceClass::CBuffer, /*Space=*/0, /*LowerBound=*/3, + /*UpperBound=*/3, /*Cookie=*/nullptr); + Builder.trackBinding(ResourceClass::UAV, /*Space=*/20, /*LowerBound=*/10, + /*UpperBound=*/14, /*Cookie=*/nullptr); + Builder.trackBinding(ResourceClass::SRV, /*Space=*/0, /*LowerBound=*/5, + /*UpperBound=*/5, /*Cookie=*/nullptr); + Builder.trackBinding(ResourceClass::UAV, /*Space=*/0, /*LowerBound=*/5, + /*UpperBound=*/5, /*Cookie=*/nullptr); + Builder.trackBinding(ResourceClass::SRV, /*Space=*/0, /*LowerBound=*/0, + /*UpperBound=*/4, /*Cookie=*/nullptr); + Builder.trackBinding(ResourceClass::UAV, /*Space=*/0, /*LowerBound=*/2, + /*UpperBound=*/3, /*Cookie=*/nullptr); + Builder.trackBinding(ResourceClass::Sampler, /*Space=*/2, /*LowerBound=*/5, + /*UpperBound=*/5, /*Cookie=*/nullptr); + Builder.trackBinding(ResourceClass::Sampler, /*Space=*/2, /*LowerBound=*/4, + /*UpperBound=*/4, /*Cookie=*/nullptr); + bool HasOverlap; + hlsl::BindingInfo Info = Builder.calculateBindingInfo(HasOverlap); + + EXPECT_FALSE(HasOverlap); + + hlsl::BindingInfo::BindingSpaces &SRVSpaces = + Info.getBindingSpaces(ResourceClass::SRV); + EXPECT_EQ(SRVSpaces.RC, ResourceClass::SRV); + EXPECT_EQ(SRVSpaces.Spaces.size(), 1u); + // verify that consecutive bindings are merged + // (SRVSpaces has only one free space range {6, ~0u}). + checkExpectedSpaceAndFreeRanges(SRVSpaces.Spaces[0], 0, {6u, ~0u}); + + hlsl::BindingInfo::BindingSpaces &UAVSpaces = + Info.getBindingSpaces(ResourceClass::UAV); + EXPECT_EQ(UAVSpaces.RC, ResourceClass::UAV); + EXPECT_EQ(UAVSpaces.Spaces.size(), 2u); + checkExpectedSpaceAndFreeRanges(UAVSpaces.Spaces[0], 0, + {0u, 1u, 4u, 4u, 6u, ~0u}); + checkExpectedSpaceAndFreeRanges(UAVSpaces.Spaces[1], 20, {0u, 9u, 15u, ~0u}); + + hlsl::BindingInfo::BindingSpaces &CBufferSpaces = + Info.getBindingSpaces(ResourceClass::CBuffer); + EXPECT_EQ(CBufferSpaces.RC, ResourceClass::CBuffer); + EXPECT_EQ(CBufferSpaces.Spaces.size(), 1u); + checkExpectedSpaceAndFreeRanges(CBufferSpaces.Spaces[0], 0, + {0u, 2u, 4u, ~0u}); + + hlsl::BindingInfo::BindingSpaces &SamplerSpaces = + Info.getBindingSpaces(ResourceClass::Sampler); + EXPECT_EQ(SamplerSpaces.RC, ResourceClass::Sampler); + EXPECT_EQ(SamplerSpaces.Spaces.size(), 1u); + checkExpectedSpaceAndFreeRanges(SamplerSpaces.Spaces[0], 2, + {0u, 3u, 6u, ~0u}); +} + +TEST(HLSLBindingTest, TestUnboundedAndOverlap) { + hlsl::BindingInfoBuilder Builder; + + // StructuredBuffer A[] : register(t5); + // StructuredBuffer B[3] : register(t0); + // StructuredBuffer C[] : register(t0, space2); + // StructuredBuffer D : register(t4, space2); /* overlapping */ + Builder.trackBinding(ResourceClass::SRV, /*Space=*/0, /*LowerBound=*/5, + /*UpperBound=*/~0u, /*Cookie=*/nullptr); + Builder.trackBinding(ResourceClass::SRV, /*Space=*/0, /*LowerBound=*/0, + /*UpperBound=*/2, /*Cookie=*/nullptr); + Builder.trackBinding(ResourceClass::SRV, /*Space=*/2, /*LowerBound=*/0, + /*UpperBound=*/~0u, /*Cookie=*/nullptr); + Builder.trackBinding(ResourceClass::SRV, /*Space=*/2, /*LowerBound=*/4, + /*UpperBound=*/4, /*Cookie=*/nullptr); + bool HasOverlap; + hlsl::BindingInfo Info = Builder.calculateBindingInfo(HasOverlap); + + EXPECT_TRUE(HasOverlap); + + hlsl::BindingInfo::BindingSpaces &SRVSpaces = + Info.getBindingSpaces(ResourceClass::SRV); + EXPECT_EQ(SRVSpaces.RC, ResourceClass::SRV); + EXPECT_EQ(SRVSpaces.Spaces.size(), 2u); + checkExpectedSpaceAndFreeRanges(SRVSpaces.Spaces[0], 0, {3, 4}); + checkExpectedSpaceAndFreeRanges(SRVSpaces.Spaces[1], 2, {}); +} + +TEST(HLSLBindingTest, TestExactOverlap) { + hlsl::BindingInfoBuilder Builder; + + // Since the bindings overlap exactly we need sigil values to differentiate + // them. + char ID1; + char ID2; + + // StructuredBuffer A : register(t5); + // StructuredBuffer B : register(t5); + Builder.trackBinding(ResourceClass::SRV, /*Space=*/0, /*LowerBound=*/5, + /*UpperBound=*/5, /*Cookie=*/&ID1); + Builder.trackBinding(ResourceClass::SRV, /*Space=*/0, /*LowerBound=*/5, + /*UpperBound=*/5, /*Cookie=*/&ID2); + bool HasOverlap; + hlsl::BindingInfo Info = Builder.calculateBindingInfo(HasOverlap); + + EXPECT_TRUE(HasOverlap); + + hlsl::BindingInfo::BindingSpaces &SRVSpaces = + Info.getBindingSpaces(ResourceClass::SRV); + EXPECT_EQ(SRVSpaces.RC, ResourceClass::SRV); + EXPECT_EQ(SRVSpaces.Spaces.size(), 1u); + checkExpectedSpaceAndFreeRanges(SRVSpaces.Spaces[0], 0, {0u, 4u, 6u, ~0u}); +} + +TEST(HLSLBindingTest, TestEndOfRange) { + hlsl::BindingInfoBuilder Builder; + + // RWBuffer A : register(u4294967295); /* UINT32_MAX */ + // RWBuffer B[10] : register(u4294967286, space1); + // /* range (UINT32_MAX - 9, UINT32_MAX )*/ + // RWBuffer C[10] : register(u2147483647, space2); + // /* range (INT32_MAX, INT32_MAX + 9) */ + Builder.trackBinding(ResourceClass::UAV, /*Space=*/0, /*LowerBound=*/~0u, + /*UpperBound=*/~0u, /*Cookie=*/nullptr); + Builder.trackBinding(ResourceClass::UAV, /*Space=*/1, /*LowerBound=*/~0u - 9u, + /*UpperBound=*/~0u, /*Cookie=*/nullptr); + Builder.trackBinding(ResourceClass::UAV, /*Space=*/2, + /*LowerBound=*/2147483647u, + /*UpperBound=*/2147483647u + 9u, /*Cookie=*/nullptr); + bool HasOverlap; + hlsl::BindingInfo Info = Builder.calculateBindingInfo(HasOverlap); + + EXPECT_FALSE(HasOverlap); + + hlsl::BindingInfo::BindingSpaces &UAVSpaces = + Info.getBindingSpaces(ResourceClass::UAV); + EXPECT_EQ(UAVSpaces.RC, ResourceClass::UAV); + EXPECT_EQ(UAVSpaces.Spaces.size(), 3u); + checkExpectedSpaceAndFreeRanges( + UAVSpaces.Spaces[0], 0, {0, std::numeric_limits::max() - 1}); + checkExpectedSpaceAndFreeRanges( + UAVSpaces.Spaces[1], 1, {0, std::numeric_limits::max() - 10}); + checkExpectedSpaceAndFreeRanges( + UAVSpaces.Spaces[2], 2, + {0, static_cast(std::numeric_limits::max()) - 1u, + static_cast(std::numeric_limits::max()) + 10u, + std::numeric_limits::max()}); +} + +TEST(HLSLBindingTest, TestFindAvailable) { + hlsl::BindingInfoBuilder Builder; + + // RWBuffer A : register(u5); + // RWBuffer B : register(u5, space1); + // RWBuffer C : register(u11, space1); + // RWBuffer D[] : register(u1, space2); + Builder.trackBinding(ResourceClass::UAV, /*Space=*/0, /*LowerBound=*/5u, + /*UpperBound=*/5u, /*Cookie=*/nullptr); + Builder.trackBinding(ResourceClass::UAV, /*Space=*/1, /*LowerBound=*/2u, + /*UpperBound=*/2u, /*Cookie=*/nullptr); + Builder.trackBinding(ResourceClass::UAV, /*Space=*/1, /*LowerBound=*/6u, + /*UpperBound=*/6u, /*Cookie=*/nullptr); + Builder.trackBinding(ResourceClass::UAV, /*Space=*/2, /*LowerBound=*/1u, + /*UpperBound=*/~0u, /*Cookie=*/nullptr); + Builder.trackBinding(ResourceClass::UAV, /*Space=*/3, /*LowerBound=*/~0u - 1, + /*UpperBound=*/~0u - 1, /*Cookie=*/nullptr); + bool HasOverlap; + hlsl::BindingInfo Info = Builder.calculateBindingInfo(HasOverlap); + + EXPECT_FALSE(HasOverlap); + + // In space 0, we find room for a small binding at the beginning and + // a large binding after `A`'s binding. + std::optional V = + Info.findAvailableBinding(ResourceClass::UAV, /*Space=*/0, /*Size=*/1); + EXPECT_THAT(V, HasSpecificValue(0u)); + V = Info.findAvailableBinding(ResourceClass::UAV, /*Space=*/0, /*Size=*/100); + EXPECT_THAT(V, HasSpecificValue(6u)); + + // In space 1, we try to fit larger bindings in the fill the gaps. Note that + // we do this largest to smallest and observe that the gaps that are earlier + // still exist. + V = Info.findAvailableBinding(ResourceClass::UAV, /*Space=*/1, /*Size=*/4); + EXPECT_THAT(V, HasSpecificValue(7u)); + V = Info.findAvailableBinding(ResourceClass::UAV, /*Space=*/1, /*Size=*/3); + EXPECT_THAT(V, HasSpecificValue(3u)); + V = Info.findAvailableBinding(ResourceClass::UAV, /*Space=*/1, /*Size=*/2); + EXPECT_THAT(V, HasSpecificValue(0u)); + // At this point, we've used all of the contiguous space up to 11u + V = Info.findAvailableBinding(ResourceClass::UAV, /*Space=*/1, /*Size=*/1); + EXPECT_THAT(V, HasSpecificValue(11u)); + + // Space 2 is mostly full, we can only fit into the room at the beginning. + V = Info.findAvailableBinding(ResourceClass::UAV, /*Space=*/2, /*Size=*/2); + EXPECT_FALSE(V.has_value()); + V = Info.findAvailableBinding(ResourceClass::UAV, /*Space=*/2, /*Size=*/1); + EXPECT_THAT(V, HasSpecificValue(0u)); + + // Finding space for an unbounded array is a bit funnier. it always needs to + // go a the end of the available space. + V = Info.findAvailableBinding(ResourceClass::UAV, /*Space=*/3, + /*Size=*/~0u); + // Note that we end up with a size 1 array here, starting at ~0u. + EXPECT_THAT(V, HasSpecificValue(~0u)); + V = Info.findAvailableBinding(ResourceClass::UAV, /*Space=*/4, + /*Size=*/~0u); + // In an empty space we find the slot at the beginning. + EXPECT_THAT(V, HasSpecificValue(0u)); +} diff --git a/llvm/unittests/Target/DirectX/ResourceBindingAnalysisTests.cpp b/llvm/unittests/Target/DirectX/ResourceBindingAnalysisTests.cpp index 6cd1a48d8d5fd..d4715bee51d1d 100644 --- a/llvm/unittests/Target/DirectX/ResourceBindingAnalysisTests.cpp +++ b/llvm/unittests/Target/DirectX/ResourceBindingAnalysisTests.cpp @@ -44,19 +44,6 @@ class ResourceBindingAnalysisTest : public testing::Test { delete MAM; delete Context; } - - void checkExpectedSpaceAndFreeRanges( - DXILResourceBindingInfo::RegisterSpace &RegSpace, uint32_t ExpSpace, - ArrayRef ExpValues) { - EXPECT_EQ(RegSpace.Space, ExpSpace); - EXPECT_EQ(RegSpace.FreeRanges.size() * 2, ExpValues.size()); - unsigned I = 0; - for (auto &R : RegSpace.FreeRanges) { - EXPECT_EQ(R.LowerBound, ExpValues[I]); - EXPECT_EQ(R.UpperBound, ExpValues[I + 1]); - I += 2; - } - } }; TEST_F(ResourceBindingAnalysisTest, TestTrivialCase) { @@ -76,103 +63,16 @@ define void @main() { EXPECT_EQ(false, DRBI.hasImplicitBinding()); EXPECT_EQ(false, DRBI.hasOverlappingBinding()); - - // check that UAV has exactly one gap - DXILResourceBindingInfo::BindingSpaces &UAVSpaces = - DRBI.getBindingSpaces(ResourceClass::UAV); - EXPECT_EQ(UAVSpaces.RC, ResourceClass::UAV); - EXPECT_EQ(UAVSpaces.Spaces.size(), 1u); - checkExpectedSpaceAndFreeRanges(UAVSpaces.Spaces[0], 0, - {0, 4, 6, UINT32_MAX}); - - // check that other kinds of register spaces are all available - for (auto RC : - {ResourceClass::SRV, ResourceClass::CBuffer, ResourceClass::Sampler}) { - DXILResourceBindingInfo::BindingSpaces &Spaces = DRBI.getBindingSpaces(RC); - EXPECT_EQ(Spaces.RC, RC); - EXPECT_EQ(Spaces.Spaces.size(), 0u); - } -} - -TEST_F(ResourceBindingAnalysisTest, TestManyBindings) { - // cbuffer CB : register(b3) { int a; } - // RWBuffer A[5] : register(u10, space20); - // StructuredBuffer B : register(t5); - // RWBuffer C : register(u5); - // StructuredBuffer D[5] : register(t0); - // RWBuffer E[2] : register(u2); - // SamplerState S1 : register(s5, space2); - // SamplerState S2 : register(s4, space2); - StringRef Assembly = R"( -%__cblayout_CB = type <{ i32 }> -define void @main() { -entry: - %handleCB = call target("dx.CBuffer", target("dx.Layout", %__cblayout_CB, 4, 0)) @llvm.dx.resource.handlefrombinding(i32 0, i32 3, i32 1, i32 0, i1 false, ptr null) - %handleA = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding(i32 20, i32 10, i32 5, i32 0, i1 false, ptr null) - %handleB = call target("dx.RawBuffer", i32, 0, 0) @llvm.dx.resource.handlefrombinding(i32 0, i32 5, i32 1, i32 0, i1 false, ptr null) - %handleC = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding(i32 0, i32 5, i32 1, i32 0, i1 false, ptr null) - %handleD = call target("dx.RawBuffer", i32, 0, 0) @llvm.dx.resource.handlefrombinding(i32 0, i32 0, i32 5, i32 4, i1 false, ptr null) - %handleE = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding(i32 0, i32 2, i32 2, i32 0, i1 false, ptr null) - %handleS1 = call target("dx.Sampler", 0) @llvm.dx.resource.handlefrombinding(i32 2, i32 5, i32 1, i32 0, i1 false, ptr null) - %handleS2 = call target("dx.Sampler", 0) @llvm.dx.resource.handlefrombinding(i32 2, i32 4, i32 1, i32 0, i1 false, ptr null) - ; duplicate binding for the same resource - %handleD2 = call target("dx.RawBuffer", i32, 0, 0) @llvm.dx.resource.handlefrombinding(i32 0, i32 0, i32 5, i32 4, i1 false, ptr null) - ret void -} - )"; - - auto M = parseAsm(Assembly); - - DXILResourceBindingInfo &DRBI = - MAM->getResult(*M); - - EXPECT_EQ(false, DRBI.hasImplicitBinding()); - EXPECT_EQ(false, DRBI.hasOverlappingBinding()); - - DXILResourceBindingInfo::BindingSpaces &SRVSpaces = - DRBI.getBindingSpaces(ResourceClass::SRV); - EXPECT_EQ(SRVSpaces.RC, ResourceClass::SRV); - EXPECT_EQ(SRVSpaces.Spaces.size(), 1u); - // verify that consecutive bindings are merged - // (SRVSpaces has only one free space range {6, UINT32_MAX}). - checkExpectedSpaceAndFreeRanges(SRVSpaces.Spaces[0], 0, {6, UINT32_MAX}); - - DXILResourceBindingInfo::BindingSpaces &UAVSpaces = - DRBI.getBindingSpaces(ResourceClass::UAV); - EXPECT_EQ(UAVSpaces.RC, ResourceClass::UAV); - EXPECT_EQ(UAVSpaces.Spaces.size(), 2u); - checkExpectedSpaceAndFreeRanges(UAVSpaces.Spaces[0], 0, - {0, 1, 4, 4, 6, UINT32_MAX}); - checkExpectedSpaceAndFreeRanges(UAVSpaces.Spaces[1], 20, - {0, 9, 15, UINT32_MAX}); - - DXILResourceBindingInfo::BindingSpaces &CBufferSpaces = - DRBI.getBindingSpaces(ResourceClass::CBuffer); - EXPECT_EQ(CBufferSpaces.RC, ResourceClass::CBuffer); - EXPECT_EQ(CBufferSpaces.Spaces.size(), 1u); - checkExpectedSpaceAndFreeRanges(CBufferSpaces.Spaces[0], 0, - {0, 2, 4, UINT32_MAX}); - - DXILResourceBindingInfo::BindingSpaces &SamplerSpaces = - DRBI.getBindingSpaces(ResourceClass::Sampler); - EXPECT_EQ(SamplerSpaces.RC, ResourceClass::Sampler); - EXPECT_EQ(SamplerSpaces.Spaces.size(), 1u); - checkExpectedSpaceAndFreeRanges(SamplerSpaces.Spaces[0], 2, - {0, 3, 6, UINT32_MAX}); } -TEST_F(ResourceBindingAnalysisTest, TestUnboundedAndOverlap) { - // StructuredBuffer A[] : register(t5); - // StructuredBuffer B[3] : register(t0); - // StructuredBuffer C[] : register(t0, space2); - // StructuredBuffer D : register(t4, space2); /* overlapping */ +TEST_F(ResourceBindingAnalysisTest, TestOverlap) { + // StructuredBuffer A[] : register(t0, space2); + // StructuredBuffer B : register(t4, space2); /* overlapping */ StringRef Assembly = R"( define void @main() { entry: - %handleA = call target("dx.RawBuffer", float, 0, 0) @llvm.dx.resource.handlefrombinding(i32 0, i32 5, i32 -1, i32 10, i1 false, ptr null) - %handleB = call target("dx.RawBuffer", float, 0, 0) @llvm.dx.resource.handlefrombinding(i32 0, i32 0, i32 3, i32 0, i1 false, ptr null) - %handleC = call target("dx.RawBuffer", float, 0, 0) @llvm.dx.resource.handlefrombinding(i32 2, i32 0, i32 -1, i32 100, i1 false, ptr null) - %handleD = call target("dx.RawBuffer", float, 0, 0) @llvm.dx.resource.handlefrombinding(i32 2, i32 4, i32 1, i32 0, i1 false, ptr null) + %handleA = call target("dx.RawBuffer", float, 0, 0) @llvm.dx.resource.handlefrombinding(i32 2, i32 0, i32 -1, i32 100, i1 false, ptr null) + %handleB = call target("dx.RawBuffer", float, 0, 0) @llvm.dx.resource.handlefrombinding(i32 2, i32 4, i32 1, i32 0, i1 false, ptr null) ret void } )"; @@ -184,13 +84,6 @@ define void @main() { EXPECT_EQ(false, DRBI.hasImplicitBinding()); EXPECT_EQ(true, DRBI.hasOverlappingBinding()); - - DXILResourceBindingInfo::BindingSpaces &SRVSpaces = - DRBI.getBindingSpaces(ResourceClass::SRV); - EXPECT_EQ(SRVSpaces.RC, ResourceClass::SRV); - EXPECT_EQ(SRVSpaces.Spaces.size(), 2u); - checkExpectedSpaceAndFreeRanges(SRVSpaces.Spaces[0], 0, {3, 4}); - checkExpectedSpaceAndFreeRanges(SRVSpaces.Spaces[1], 2, {}); } TEST_F(ResourceBindingAnalysisTest, TestExactOverlap) { @@ -214,49 +107,6 @@ define void @main() { EXPECT_EQ(false, DRBI.hasImplicitBinding()); EXPECT_EQ(true, DRBI.hasOverlappingBinding()); - - DXILResourceBindingInfo::BindingSpaces &SRVSpaces = - DRBI.getBindingSpaces(ResourceClass::SRV); - EXPECT_EQ(SRVSpaces.RC, ResourceClass::SRV); - EXPECT_EQ(SRVSpaces.Spaces.size(), 1u); - checkExpectedSpaceAndFreeRanges(SRVSpaces.Spaces[0], 0, - {0, 4, 6, UINT32_MAX}); -} - -TEST_F(ResourceBindingAnalysisTest, TestEndOfRange) { - // RWBuffer A : register(u4294967295); /* UINT32_MAX */ - // RWBuffer B[10] : register(u4294967286, space1); - // /* range (UINT32_MAX - 9, UINT32_MAX )*/ - // RWBuffer C[10] : register(u2147483647, space2); - // /* range (INT32_MAX, INT32_MAX + 9) */ - StringRef Assembly = R"( -%__cblayout_CB = type <{ i32 }> -define void @main() { -entry: - %handleA = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding(i32 0, i32 -1, i32 1, i32 0, i1 false, ptr null) - %handleB = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding(i32 1, i32 -10, i32 10, i32 50, i1 false, ptr null) - %handleC = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding(i32 2, i32 2147483647, i32 10, i32 100, i1 false, ptr null) - ret void -} - )"; - - auto M = parseAsm(Assembly); - - DXILResourceBindingInfo &DRBI = - MAM->getResult(*M); - - EXPECT_EQ(false, DRBI.hasImplicitBinding()); - EXPECT_EQ(false, DRBI.hasOverlappingBinding()); - - DXILResourceBindingInfo::BindingSpaces &UAVSpaces = - DRBI.getBindingSpaces(ResourceClass::UAV); - EXPECT_EQ(UAVSpaces.RC, ResourceClass::UAV); - EXPECT_EQ(UAVSpaces.Spaces.size(), 3u); - checkExpectedSpaceAndFreeRanges(UAVSpaces.Spaces[0], 0, {0, UINT32_MAX - 1}); - checkExpectedSpaceAndFreeRanges(UAVSpaces.Spaces[1], 1, {0, UINT32_MAX - 10}); - checkExpectedSpaceAndFreeRanges( - UAVSpaces.Spaces[2], 2, - {0, (uint32_t)INT32_MAX - 1, (uint32_t)INT32_MAX + 10, UINT32_MAX}); } TEST_F(ResourceBindingAnalysisTest, TestImplicitFlag) { @@ -275,15 +125,8 @@ define void @main() { DXILResourceBindingInfo &DRBI = MAM->getResult(*M); - EXPECT_EQ(true, DRBI.hasImplicitBinding()); - EXPECT_EQ(false, DRBI.hasOverlappingBinding()); - - DXILResourceBindingInfo::BindingSpaces &UAVSpaces = - DRBI.getBindingSpaces(ResourceClass::UAV); - EXPECT_EQ(UAVSpaces.RC, ResourceClass::UAV); - EXPECT_EQ(UAVSpaces.Spaces.size(), 1u); - checkExpectedSpaceAndFreeRanges(UAVSpaces.Spaces[0], 100, - {0, 4, 6, UINT32_MAX}); + EXPECT_TRUE(DRBI.hasImplicitBinding()); + EXPECT_FALSE(DRBI.hasOverlappingBinding()); } } // namespace