diff --git a/llvm/include/llvm/Analysis/DXILResource.h b/llvm/include/llvm/Analysis/DXILResource.h index 9f0b5f18d127d..1d871a448c16c 100644 --- a/llvm/include/llvm/Analysis/DXILResource.h +++ b/llvm/include/llvm/Analysis/DXILResource.h @@ -457,8 +457,19 @@ class DXILResourceMap { unsigned FirstCBuffer = 0; unsigned FirstSampler = 0; - /// Populate the map given the resource binding calls in the given module. + /// Populate all the resource instance data. void populate(Module &M, DXILResourceTypeMap &DRTM); + /// Populate the map given the resource binding calls in the given module. + void populateResourceInfos(Module &M, DXILResourceTypeMap &DRTM); + /// Analyze and populate the directions of the resource counters. + void populateCounterDirections(Module &M); + + /// Resolves a resource handle into a vector of ResourceInfos that + /// represent the possible unique creations of the handle. Certain cases are + /// ambiguous so multiple creation instructions may be returned. The resulting + /// ResourceInfo can be used to depuplicate unique handles that + /// reference the same resource + SmallVector findByUse(const Value *Key); public: using iterator = SmallVector::iterator; @@ -476,13 +487,6 @@ class DXILResourceMap { return Pos == CallMap.end() ? Infos.end() : (Infos.begin() + Pos->second); } - /// Resolves a resource handle into a vector of ResourceInfos that - /// represent the possible unique creations of the handle. Certain cases are - /// ambiguous so multiple creation instructions may be returned. The resulting - /// ResourceInfo can be used to depuplicate unique handles that - /// reference the same resource - SmallVector findByUse(const Value *Key) const; - const_iterator find(const CallInst *Key) const { auto Pos = CallMap.find(Key); return Pos == CallMap.end() ? Infos.end() : (Infos.begin() + Pos->second); diff --git a/llvm/lib/Analysis/DXILResource.cpp b/llvm/lib/Analysis/DXILResource.cpp index 6334f5d82213d..cc1d931c9e077 100644 --- a/llvm/lib/Analysis/DXILResource.cpp +++ b/llvm/lib/Analysis/DXILResource.cpp @@ -697,8 +697,12 @@ bool DXILResourceTypeMap::invalidate(Module &M, const PreservedAnalyses &PA, } //===----------------------------------------------------------------------===// +static bool isUpdateCounterIntrinsic(Function &F) { + return F.getIntrinsicID() == Intrinsic::dx_resource_updatecounter; +} -void DXILResourceMap::populate(Module &M, DXILResourceTypeMap &DRTM) { +void DXILResourceMap::populateResourceInfos(Module &M, + DXILResourceTypeMap &DRTM) { SmallVector> CIToInfos; for (Function &F : M.functions()) { @@ -777,6 +781,48 @@ void DXILResourceMap::populate(Module &M, DXILResourceTypeMap &DRTM) { } } +void DXILResourceMap::populateCounterDirections(Module &M) { + for (Function &F : M.functions()) { + if (!isUpdateCounterIntrinsic(F)) + continue; + + LLVM_DEBUG(dbgs() << "Update Counter Function: " << F.getName() << "\n"); + + for (const User *U : F.users()) { + const CallInst *CI = dyn_cast(U); + assert(CI && "Users of dx_resource_updateCounter must be call instrs"); + + // Determine if the use is an increment or decrement + Value *CountArg = CI->getArgOperand(1); + ConstantInt *CountValue = cast(CountArg); + int64_t CountLiteral = CountValue->getSExtValue(); + + // 0 is an unknown direction and shouldn't result in an insert + if (CountLiteral == 0) + continue; + + ResourceCounterDirection Direction = ResourceCounterDirection::Decrement; + if (CountLiteral > 0) + Direction = ResourceCounterDirection::Increment; + + // Collect all potential creation points for the handle arg + Value *HandleArg = CI->getArgOperand(0); + SmallVector RBInfos = findByUse(HandleArg); + for (ResourceInfo *RBInfo : RBInfos) { + if (RBInfo->CounterDirection == ResourceCounterDirection::Unknown) + RBInfo->CounterDirection = Direction; + else if (RBInfo->CounterDirection != Direction) + RBInfo->CounterDirection = ResourceCounterDirection::Invalid; + } + } + } +} + +void DXILResourceMap::populate(Module &M, DXILResourceTypeMap &DRTM) { + populateResourceInfos(M, DRTM); + populateCounterDirections(M); +} + void DXILResourceMap::print(raw_ostream &OS, DXILResourceTypeMap &DRTM, const DataLayout &DL) const { for (unsigned I = 0, E = Infos.size(); I != E; ++I) { @@ -793,10 +839,9 @@ void DXILResourceMap::print(raw_ostream &OS, DXILResourceTypeMap &DRTM, } } -SmallVector -DXILResourceMap::findByUse(const Value *Key) const { +SmallVector DXILResourceMap::findByUse(const Value *Key) { if (const PHINode *Phi = dyn_cast(Key)) { - SmallVector Children; + SmallVector Children; for (const Value *V : Phi->operands()) { Children.append(findByUse(V)); } @@ -810,9 +855,9 @@ DXILResourceMap::findByUse(const Value *Key) const { switch (CI->getIntrinsicID()) { // Found the create, return the binding case Intrinsic::dx_resource_handlefrombinding: { - const auto *It = find(CI); - assert(It != Infos.end() && "HandleFromBinding must be in resource map"); - return {*It}; + auto Pos = CallMap.find(CI); + assert(Pos != CallMap.end() && "HandleFromBinding must be in resource map"); + return {&Infos[Pos->second]}; } default: break; @@ -821,7 +866,7 @@ DXILResourceMap::findByUse(const Value *Key) const { // Check if any of the parameters are the resource we are following. If so // keep searching. If none of them are return an empty list const Type *UseType = CI->getType(); - SmallVector Children; + SmallVector Children; for (const Value *V : CI->args()) { if (V->getType() != UseType) continue; diff --git a/llvm/test/Analysis/DXILResource/buffer-frombinding.ll b/llvm/test/Analysis/DXILResource/buffer-frombinding.ll index 81c8b5530afb6..378b1ff120885 100644 --- a/llvm/test/Analysis/DXILResource/buffer-frombinding.ll +++ b/llvm/test/Analysis/DXILResource/buffer-frombinding.ll @@ -5,8 +5,7 @@ define void @test_typedbuffer() { ; ByteAddressBuffer Buf : register(t8, space1) %srv0 = call target("dx.RawBuffer", void, 0, 0) - @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_i8_0_0t( - i32 1, i32 8, i32 1, i32 0, i1 false) + @llvm.dx.resource.handlefrombinding(i32 1, i32 8, i32 1, i32 0, i1 false) ; CHECK: Resource [[SRV0:[0-9]+]]: ; CHECK: Binding: ; CHECK: Record ID: 0 @@ -19,8 +18,7 @@ define void @test_typedbuffer() { ; struct S { float4 a; uint4 b; }; ; StructuredBuffer Buf : register(t2, space4) %srv1 = call target("dx.RawBuffer", {<4 x float>, <4 x i32>}, 0, 0) - @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_sl_v4f32v4i32s_0_0t( - i32 4, i32 2, i32 1, i32 0, i1 false) + @llvm.dx.resource.handlefrombinding(i32 4, i32 2, i32 1, i32 0, i1 false) ; CHECK: Resource [[SRV1:[0-9]+]]: ; CHECK: Binding: ; CHECK: Record ID: 1 @@ -34,8 +32,7 @@ define void @test_typedbuffer() { ; Buffer Buf[24] : register(t3, space5) %srv2 = call target("dx.TypedBuffer", <4 x i32>, 0, 0, 0) - @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_i32_0_0t( - i32 5, i32 3, i32 24, i32 0, i1 false) + @llvm.dx.resource.handlefrombinding(i32 5, i32 3, i32 24, i32 0, i1 false) ; CHECK: Resource [[SRV2:[0-9]+]]: ; CHECK: Binding: ; CHECK: Record ID: 2 @@ -49,8 +46,7 @@ define void @test_typedbuffer() { ; RWBuffer Buf : register(u7, space2) %uav0 = call target("dx.TypedBuffer", i32, 1, 0, 1) - @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_i32_1_0t( - i32 2, i32 7, i32 1, i32 0, i1 false) + @llvm.dx.resource.handlefrombinding(i32 2, i32 7, i32 1, i32 0, i1 false) ; CHECK: Resource [[UAV0:[0-9]+]]: ; CHECK: Binding: ; CHECK: Record ID: 0 @@ -67,8 +63,8 @@ define void @test_typedbuffer() { ; RWBuffer Buf : register(u5, space3) %uav1 = call target("dx.TypedBuffer", <4 x float>, 1, 0, 0) - @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_f32_1_0( - i32 3, i32 5, i32 1, i32 0, i1 false) + @llvm.dx.resource.handlefrombinding(i32 3, i32 5, i32 1, i32 0, i1 false) + call i32 @llvm.dx.resource.updatecounter(target("dx.TypedBuffer", <4 x float>, 1, 0, 0) %uav1, i8 -1) ; CHECK: Resource [[UAV1:[0-9]+]]: ; CHECK: Binding: ; CHECK: Record ID: 1 @@ -76,7 +72,7 @@ define void @test_typedbuffer() { ; CHECK: Lower Bound: 5 ; CHECK: Size: 1 ; CHECK: Globally Coherent: 0 - ; CHECK: Counter Direction: Unknown + ; CHECK: Counter Direction: Decrement ; CHECK: Class: UAV ; CHECK: Kind: TypedBuffer ; CHECK: IsROV: 0 @@ -86,12 +82,11 @@ define void @test_typedbuffer() { ; RWBuffer BufferArray[10] : register(u0, space4) ; RWBuffer Buf = BufferArray[0] %uav2_1 = call target("dx.TypedBuffer", <4 x float>, 1, 0, 0) - @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_f32_1_0( - i32 4, i32 0, i32 10, i32 0, i1 false) + @llvm.dx.resource.handlefrombinding(i32 4, i32 0, i32 10, i32 0, i1 false) ; RWBuffer Buf = BufferArray[5] %uav2_2 = call target("dx.TypedBuffer", <4 x float>, 1, 0, 0) - @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_f32_1_0( - i32 4, i32 0, i32 10, i32 5, i1 false) + @llvm.dx.resource.handlefrombinding(i32 4, i32 0, i32 10, i32 5, i1 false) + call i32 @llvm.dx.resource.updatecounter(target("dx.TypedBuffer", <4 x float>, 1, 0, 0) %uav2_2, i8 1) ; CHECK: Resource [[UAV2:[0-9]+]]: ; CHECK: Binding: ; CHECK: Record ID: 2 @@ -99,7 +94,26 @@ define void @test_typedbuffer() { ; CHECK: Lower Bound: 0 ; CHECK: Size: 10 ; CHECK: Globally Coherent: 0 - ; CHECK: Counter Direction: Unknown + ; CHECK: Counter Direction: Increment + ; CHECK: Class: UAV + ; CHECK: Kind: TypedBuffer + ; CHECK: IsROV: 0 + ; CHECK: Element Type: f32 + ; CHECK: Element Count: 4 + + ; RWBuffer Buf : register(u0, space5) + %uav3 = call target("dx.TypedBuffer", <4 x float>, 1, 0, 0) + @llvm.dx.resource.handlefrombinding(i32 5, i32 0, i32 1, i32 0, i1 false) + call i32 @llvm.dx.resource.updatecounter(target("dx.TypedBuffer", <4 x float>, 1, 0, 0) %uav3, i8 -1) + call i32 @llvm.dx.resource.updatecounter(target("dx.TypedBuffer", <4 x float>, 1, 0, 0) %uav3, i8 1) + ; CHECK: Resource [[UAV3:[0-9]+]]: + ; CHECK: Binding: + ; CHECK: Record ID: 3 + ; CHECK: Space: 5 + ; CHECK: Lower Bound: 0 + ; CHECK: Size: 1 + ; CHECK: Globally Coherent: 0 + ; CHECK: Counter Direction: Invalid ; CHECK: Class: UAV ; CHECK: Kind: TypedBuffer ; CHECK: IsROV: 0 @@ -107,7 +121,7 @@ define void @test_typedbuffer() { ; CHECK: Element Count: 4 %cb0 = call target("dx.CBuffer", {float}) - @llvm.dx.resource.handlefrombinding(i32 1, i32 0, i32 1, i32 0, i1 false) + @llvm.dx.resource.handlefrombinding(i32 1, i32 0, i32 1, i32 0, i1 false) ; CHECK: Resource [[CB0:[0-9]+]]: ; CHECK: Binding: ; CHECK: Record ID: 0 @@ -119,7 +133,7 @@ define void @test_typedbuffer() { ; CHECK: CBuffer size: 4 %cb1 = call target("dx.CBuffer", target("dx.Layout", {float}, 4, 0)) - @llvm.dx.resource.handlefrombinding(i32 1, i32 8, i32 1, i32 0, i1 false) + @llvm.dx.resource.handlefrombinding(i32 1, i32 8, i32 1, i32 0, i1 false) ; CHECK: Resource [[CB1:[0-9]+]]: ; CHECK: Binding: ; CHECK: Record ID: 1 diff --git a/llvm/unittests/Target/DirectX/UniqueResourceFromUseTests.cpp b/llvm/unittests/Target/DirectX/UniqueResourceFromUseTests.cpp index 675a3dc19b912..249e1045b847f 100644 --- a/llvm/unittests/Target/DirectX/UniqueResourceFromUseTests.cpp +++ b/llvm/unittests/Target/DirectX/UniqueResourceFromUseTests.cpp @@ -11,6 +11,7 @@ #include "llvm/AsmParser/Parser.h" #include "llvm/CodeGen/CommandFlags.h" #include "llvm/IR/Instructions.h" +#include "llvm/IR/IntrinsicsDirectX.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Module.h" #include "llvm/IR/Type.h" @@ -28,8 +29,9 @@ class UniqueResourceFromUseTest : public testing::Test { protected: PassBuilder *PB; ModuleAnalysisManager *MAM; - + LLVMContext *Context; virtual void SetUp() { + Context = new LLVMContext(); MAM = new ModuleAnalysisManager(); PB = new PassBuilder(); PB->registerModuleAnalyses(*MAM); @@ -37,246 +39,169 @@ class UniqueResourceFromUseTest : public testing::Test { MAM->registerPass([&] { return DXILResourceAnalysis(); }); } + std::unique_ptr parseAsm(StringRef Asm) { + SMDiagnostic Error; + std::unique_ptr M = parseAssemblyString(Asm, Error, *Context); + EXPECT_TRUE(M) << "Bad assembly?: " << Error.getMessage(); + return M; + } + virtual void TearDown() { delete PB; delete MAM; + delete Context; } }; -TEST_F(UniqueResourceFromUseTest, TestTrivialUse) { +// Test that several calls to decrement on the same resource don't raise a +// Diagnositic and resolves to a single decrement entry +TEST_F(UniqueResourceFromUseTest, TestResourceCounterDecrement) { StringRef Assembly = R"( define void @main() { entry: - %handle = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_1_0t(i32 1, i32 2, i32 3, i32 4, i1 false) - call void @a.func(target("dx.RawBuffer", float, 1, 0) %handle) - call void @a.func(target("dx.RawBuffer", float, 1, 0) %handle) + %handle = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding(i32 1, i32 2, i32 3, i32 4, i1 false) + call i32 @llvm.dx.resource.updatecounter(target("dx.RawBuffer", float, 1, 0) %handle, i8 -1) + call i32 @llvm.dx.resource.updatecounter(target("dx.RawBuffer", float, 1, 0) %handle, i8 -1) + call i32 @llvm.dx.resource.updatecounter(target("dx.RawBuffer", float, 1, 0) %handle, i8 -1) ret void } - -declare target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_1_0t(i32, i32, i32, i32, i1) -declare void @a.func(target("dx.RawBuffer", float, 1, 0) %handle) )"; - LLVMContext Context; - SMDiagnostic Error; - auto M = parseAssemblyString(Assembly, Error, Context); - ASSERT_TRUE(M) << "Bad assembly?"; + auto M = parseAsm(Assembly); + + DXILResourceMap &DRM = MAM->getResult(*M); - const DXILResourceMap &DRM = MAM->getResult(*M); for (const Function &F : M->functions()) { - if (F.getName() != "a.func") { + if (F.getIntrinsicID() != Intrinsic::dx_resource_handlefrombinding) continue; - } - - unsigned CalledResources = 0; for (const User *U : F.users()) { const CallInst *CI = cast(U); - const Value *Handle = CI->getArgOperand(0); - const auto Bindings = DRM.findByUse(Handle); - ASSERT_EQ(Bindings.size(), 1u) - << "Handle should resolve into one resource"; - - auto Binding = Bindings[0].getBinding(); - EXPECT_EQ(0u, Binding.RecordID); - EXPECT_EQ(1u, Binding.Space); - EXPECT_EQ(2u, Binding.LowerBound); - EXPECT_EQ(3u, Binding.Size); - - CalledResources++; + const auto *const Binding = DRM.find(CI); + ASSERT_EQ(Binding->CounterDirection, ResourceCounterDirection::Decrement); } - - EXPECT_EQ(2u, CalledResources) - << "Expected 2 resolved call to create resource"; } } -TEST_F(UniqueResourceFromUseTest, TestIndirectUse) { +// Test that several calls to increment on the same resource don't raise a +// Diagnositic and resolves to a single increment entry +TEST_F(UniqueResourceFromUseTest, TestResourceCounterIncrement) { StringRef Assembly = R"( -define void @foo() { - %handle = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_1_0t(i32 1, i32 2, i32 3, i32 4, i1 false) - %handle2 = call target("dx.RawBuffer", float, 1, 0) @ind.func(target("dx.RawBuffer", float, 1, 0) %handle) - %handle3 = call target("dx.RawBuffer", float, 1, 0) @ind.func(target("dx.RawBuffer", float, 1, 0) %handle2) - %handle4 = call target("dx.RawBuffer", float, 1, 0) @ind.func(target("dx.RawBuffer", float, 1, 0) %handle3) - call void @a.func(target("dx.RawBuffer", float, 1, 0) %handle4) +define void @main() { +entry: + %handle = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding(i32 1, i32 2, i32 3, i32 4, i1 false) + call i32 @llvm.dx.resource.updatecounter(target("dx.RawBuffer", float, 1, 0) %handle, i8 1) + call i32 @llvm.dx.resource.updatecounter(target("dx.RawBuffer", float, 1, 0) %handle, i8 1) + call i32 @llvm.dx.resource.updatecounter(target("dx.RawBuffer", float, 1, 0) %handle, i8 1) ret void } - -declare target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_1_0t(i32, i32, i32, i32, i1) -declare void @a.func(target("dx.RawBuffer", float, 1, 0) %handle) -declare target("dx.RawBuffer", float, 1, 0) @ind.func(target("dx.RawBuffer", float, 1, 0) %handle) )"; - LLVMContext Context; - SMDiagnostic Error; - auto M = parseAssemblyString(Assembly, Error, Context); - ASSERT_TRUE(M) << "Bad assembly?"; + auto M = parseAsm(Assembly); + + DXILResourceMap &DRM = MAM->getResult(*M); - const DXILResourceMap &DRM = MAM->getResult(*M); for (const Function &F : M->functions()) { - if (F.getName() != "a.func") { + if (F.getIntrinsicID() != Intrinsic::dx_resource_handlefrombinding) continue; - } - - unsigned CalledResources = 0; for (const User *U : F.users()) { const CallInst *CI = cast(U); - const Value *Handle = CI->getArgOperand(0); - const auto Bindings = DRM.findByUse(Handle); - ASSERT_EQ(Bindings.size(), 1u) - << "Handle should resolve into one resource"; - - auto Binding = Bindings[0].getBinding(); - EXPECT_EQ(0u, Binding.RecordID); - EXPECT_EQ(1u, Binding.Space); - EXPECT_EQ(2u, Binding.LowerBound); - EXPECT_EQ(3u, Binding.Size); - - CalledResources++; + const auto *const Binding = DRM.find(CI); + ASSERT_EQ(Binding->CounterDirection, ResourceCounterDirection::Increment); } - - EXPECT_EQ(1u, CalledResources) - << "Expected 1 resolved call to create resource"; } } -TEST_F(UniqueResourceFromUseTest, TestAmbigousIndirectUse) { +// Test that looking up a resource that doesn't have the counter updated +// resoves to unknown +TEST_F(UniqueResourceFromUseTest, TestResourceCounterUnknown) { StringRef Assembly = R"( -define void @foo() { - %foo = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_1_0t(i32 1, i32 1, i32 1, i32 1, i1 false) - %bar = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_1_0t(i32 2, i32 2, i32 2, i32 2, i1 false) - %baz = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_1_0t(i32 3, i32 3, i32 3, i32 3, i1 false) - %bat = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_1_0t(i32 4, i32 4, i32 4, i32 4, i1 false) - %a = call target("dx.RawBuffer", float, 1, 0) @ind.func(target("dx.RawBuffer", float, 1, 0) %foo, target("dx.RawBuffer", float, 1, 0) %bar) - %b = call target("dx.RawBuffer", float, 1, 0) @ind.func(target("dx.RawBuffer", float, 1, 0) %baz, target("dx.RawBuffer", float, 1, 0) %bat) - %handle = call target("dx.RawBuffer", float, 1, 0) @ind.func(target("dx.RawBuffer", float, 1, 0) %a, target("dx.RawBuffer", float, 1, 0) %b) - call void @a.func(target("dx.RawBuffer", float, 1, 0) %handle) +define void @main() { +entry: + %handle = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding(i32 1, i32 2, i32 3, i32 4, i1 false) ret void } - -declare target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_1_0t(i32, i32, i32, i32, i1) -declare void @a.func(target("dx.RawBuffer", float, 1, 0) %handle) -declare target("dx.RawBuffer", float, 1, 0) @ind.func(target("dx.RawBuffer", float, 1, 0) %x, target("dx.RawBuffer", float, 1, 0) %y) )"; - LLVMContext Context; - SMDiagnostic Error; - auto M = parseAssemblyString(Assembly, Error, Context); - ASSERT_TRUE(M) << "Bad assembly?"; + auto M = parseAsm(Assembly); + + DXILResourceMap &DRM = MAM->getResult(*M); - const DXILResourceMap &DRM = MAM->getResult(*M); for (const Function &F : M->functions()) { - if (F.getName() != "a.func") { + if (F.getIntrinsicID() != Intrinsic::dx_resource_handlefrombinding) continue; - } - - unsigned CalledResources = 0; for (const User *U : F.users()) { const CallInst *CI = cast(U); - const Value *Handle = CI->getArgOperand(0); - const auto Bindings = DRM.findByUse(Handle); - ASSERT_EQ(Bindings.size(), 4u) - << "Handle should resolve into four resources"; - - auto Binding = Bindings[0].getBinding(); - EXPECT_EQ(0u, Binding.RecordID); - EXPECT_EQ(1u, Binding.Space); - EXPECT_EQ(1u, Binding.LowerBound); - EXPECT_EQ(1u, Binding.Size); - - Binding = Bindings[1].getBinding(); - EXPECT_EQ(1u, Binding.RecordID); - EXPECT_EQ(2u, Binding.Space); - EXPECT_EQ(2u, Binding.LowerBound); - EXPECT_EQ(2u, Binding.Size); - - Binding = Bindings[2].getBinding(); - EXPECT_EQ(2u, Binding.RecordID); - EXPECT_EQ(3u, Binding.Space); - EXPECT_EQ(3u, Binding.LowerBound); - EXPECT_EQ(3u, Binding.Size); - - Binding = Bindings[3].getBinding(); - EXPECT_EQ(3u, Binding.RecordID); - EXPECT_EQ(4u, Binding.Space); - EXPECT_EQ(4u, Binding.LowerBound); - EXPECT_EQ(4u, Binding.Size); - - CalledResources++; + const auto *const Binding = DRM.find(CI); + ASSERT_EQ(Binding->CounterDirection, ResourceCounterDirection::Unknown); } - - EXPECT_EQ(1u, CalledResources) - << "Expected 1 resolved call to create resource"; } } -TEST_F(UniqueResourceFromUseTest, TestConditionalUse) { +// Test that multiple different resources with unique incs/decs aren't +// marked invalid +TEST_F(UniqueResourceFromUseTest, TestResourceCounterMultiple) { StringRef Assembly = R"( -define void @foo(i32 %n) { +define void @main() { entry: - %x = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_1_0t(i32 1, i32 1, i32 1, i32 1, i1 false) - %y = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_1_0t(i32 4, i32 4, i32 4, i32 4, i1 false) - %cond = icmp eq i32 %n, 0 - br i1 %cond, label %bb.true, label %bb.false - -bb.true: - %handle_t = call target("dx.RawBuffer", float, 1, 0) @ind.func(target("dx.RawBuffer", float, 1, 0) %x) - br label %bb.exit - -bb.false: - %handle_f = call target("dx.RawBuffer", float, 1, 0) @ind.func(target("dx.RawBuffer", float, 1, 0) %y) - br label %bb.exit - -bb.exit: - %handle = phi target("dx.RawBuffer", float, 1, 0) [ %handle_t, %bb.true ], [ %handle_f, %bb.false ] - call void @a.func(target("dx.RawBuffer", float, 1, 0) %handle) + %handle1 = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding(i32 1, i32 2, i32 3, i32 4, i1 false) + %handle2 = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding(i32 4, i32 3, i32 2, i32 1, i1 false) + call i32 @llvm.dx.resource.updatecounter(target("dx.RawBuffer", float, 1, 0) %handle1, i8 -1) + call i32 @llvm.dx.resource.updatecounter(target("dx.RawBuffer", float, 1, 0) %handle2, i8 1) ret void } - -declare target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_1_0t(i32, i32, i32, i32, i1) -declare void @a.func(target("dx.RawBuffer", float, 1, 0) %handle) -declare target("dx.RawBuffer", float, 1, 0) @ind.func(target("dx.RawBuffer", float, 1, 0) %x) )"; - LLVMContext Context; - SMDiagnostic Error; - auto M = parseAssemblyString(Assembly, Error, Context); - ASSERT_TRUE(M) << "Bad assembly?"; + auto M = parseAsm(Assembly); + + DXILResourceMap &DRM = MAM->getResult(*M); + + ResourceCounterDirection Dirs[2] = {ResourceCounterDirection::Decrement, + ResourceCounterDirection::Increment}; + ResourceCounterDirection *Dir = Dirs; - const DXILResourceMap &DRM = MAM->getResult(*M); for (const Function &F : M->functions()) { - if (F.getName() != "a.func") { + if (F.getIntrinsicID() != Intrinsic::dx_resource_handlefrombinding) continue; + + uint32_t ExpectedDirsIndex = 0; + for (const User *U : F.users()) { + const CallInst *CI = cast(U); + const auto *const Binding = DRM.find(CI); + ASSERT_TRUE(ExpectedDirsIndex < 2); + ASSERT_EQ(Binding->CounterDirection, Dir[ExpectedDirsIndex]); + ExpectedDirsIndex++; } + } +} - unsigned CalledResources = 0; +// Test that single different resources with unique incs/decs is marked invalid +TEST_F(UniqueResourceFromUseTest, TestResourceCounterInvalid) { + StringRef Assembly = R"( +define void @main() { +entry: + %handle = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding(i32 1, i32 2, i32 3, i32 4, i1 false) + call i32 @llvm.dx.resource.updatecounter(target("dx.RawBuffer", float, 1, 0) %handle, i8 -1) + call i32 @llvm.dx.resource.updatecounter(target("dx.RawBuffer", float, 1, 0) %handle, i8 1) + ret void +} + )"; + + auto M = parseAsm(Assembly); + + DXILResourceMap &DRM = MAM->getResult(*M); + + for (const Function &F : M->functions()) { + if (F.getIntrinsicID() != Intrinsic::dx_resource_handlefrombinding) + continue; for (const User *U : F.users()) { const CallInst *CI = cast(U); - const Value *Handle = CI->getArgOperand(0); - const auto Bindings = DRM.findByUse(Handle); - ASSERT_EQ(Bindings.size(), 2u) - << "Handle should resolve into four resources"; - - auto Binding = Bindings[0].getBinding(); - EXPECT_EQ(0u, Binding.RecordID); - EXPECT_EQ(1u, Binding.Space); - EXPECT_EQ(1u, Binding.LowerBound); - EXPECT_EQ(1u, Binding.Size); - - Binding = Bindings[1].getBinding(); - EXPECT_EQ(1u, Binding.RecordID); - EXPECT_EQ(4u, Binding.Space); - EXPECT_EQ(4u, Binding.LowerBound); - EXPECT_EQ(4u, Binding.Size); - - CalledResources++; + const auto *const Binding = DRM.find(CI); + ASSERT_EQ(Binding->CounterDirection, ResourceCounterDirection::Invalid); } - - EXPECT_EQ(1u, CalledResources) - << "Expected 1 resolved call to create resource"; } }