From 6640a01ca1d18a8f56ac2c466921b9be8b707b71 Mon Sep 17 00:00:00 2001 From: Ashley Coleman Date: Mon, 14 Apr 2025 14:19:32 -0600 Subject: [PATCH 1/3] [HLSL] Anaylze updateCounter usage --- llvm/include/llvm/Analysis/DXILResource.h | 2 +- llvm/lib/Analysis/DXILResource.cpp | 51 +++- .../DXILResource/buffer-frombinding.ll | 6 +- .../DirectX/UniqueResourceFromUseTests.cpp | 231 ++++++++++++++---- 4 files changed, 240 insertions(+), 50 deletions(-) diff --git a/llvm/include/llvm/Analysis/DXILResource.h b/llvm/include/llvm/Analysis/DXILResource.h index 96e90e563e230..a8124caf64420 100644 --- a/llvm/include/llvm/Analysis/DXILResource.h +++ b/llvm/include/llvm/Analysis/DXILResource.h @@ -463,7 +463,7 @@ class DXILResourceMap { /// 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; + SmallVector findByUse(const Value *Key); const_iterator find(const CallInst *Key) const { auto Pos = CallMap.find(Key); diff --git a/llvm/lib/Analysis/DXILResource.cpp b/llvm/lib/Analysis/DXILResource.cpp index 1c4348321c1d0..d2392ff929611 100644 --- a/llvm/lib/Analysis/DXILResource.cpp +++ b/llvm/lib/Analysis/DXILResource.cpp @@ -697,6 +697,9 @@ 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) { SmallVector> CIToInfos; @@ -775,6 +778,42 @@ void DXILResourceMap::populate(Module &M, DXILResourceTypeMap &DRTM) { // Adjust the resource binding to use the next ID. RI.setBindingID(NextID++); } + + 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) + RBInfo->CounterDirection = Direction; + else + RBInfo->CounterDirection = ResourceCounterDirection::Invalid; + } + } + } } void DXILResourceMap::print(raw_ostream &OS, DXILResourceTypeMap &DRTM, @@ -793,10 +832,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 +848,10 @@ 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); + auto Pos = CallMap.find(CI); + ResourceInfo *It = &Infos[Pos->second]; assert(It != Infos.end() && "HandleFromBinding must be in resource map"); - return {*It}; + return {It}; } default: break; @@ -821,7 +860,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..ea683bb4e5783 100644 --- a/llvm/test/Analysis/DXILResource/buffer-frombinding.ll +++ b/llvm/test/Analysis/DXILResource/buffer-frombinding.ll @@ -69,6 +69,7 @@ define void @test_typedbuffer() { %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) + 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 +77,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 @@ -92,6 +93,7 @@ define void @test_typedbuffer() { %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) + 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 +101,7 @@ 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 diff --git a/llvm/unittests/Target/DirectX/UniqueResourceFromUseTests.cpp b/llvm/unittests/Target/DirectX/UniqueResourceFromUseTests.cpp index 675a3dc19b912..d1ebfc3b1da41 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,9 +39,17 @@ 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; } }; @@ -47,22 +57,18 @@ TEST_F(UniqueResourceFromUseTest, TestTrivialUse) { 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) + %handle = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding(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) 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); - const DXILResourceMap &DRM = MAM->getResult(*M); + DXILResourceMap &DRM = MAM->getResult(*M); for (const Function &F : M->functions()) { if (F.getName() != "a.func") { continue; @@ -77,7 +83,7 @@ declare void @a.func(target("dx.RawBuffer", float, 1, 0) %handle) ASSERT_EQ(Bindings.size(), 1u) << "Handle should resolve into one resource"; - auto Binding = Bindings[0].getBinding(); + auto Binding = Bindings[0]->getBinding(); EXPECT_EQ(0u, Binding.RecordID); EXPECT_EQ(1u, Binding.Space); EXPECT_EQ(2u, Binding.LowerBound); @@ -94,7 +100,7 @@ declare void @a.func(target("dx.RawBuffer", float, 1, 0) %handle) TEST_F(UniqueResourceFromUseTest, TestIndirectUse) { 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) + %handle = 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) @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) @@ -102,17 +108,13 @@ define void @foo() { 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); - const DXILResourceMap &DRM = MAM->getResult(*M); + DXILResourceMap &DRM = MAM->getResult(*M); for (const Function &F : M->functions()) { if (F.getName() != "a.func") { continue; @@ -127,7 +129,7 @@ declare target("dx.RawBuffer", float, 1, 0) @ind.func(target("dx.RawBuffer", flo ASSERT_EQ(Bindings.size(), 1u) << "Handle should resolve into one resource"; - auto Binding = Bindings[0].getBinding(); + auto Binding = Bindings[0]->getBinding(); EXPECT_EQ(0u, Binding.RecordID); EXPECT_EQ(1u, Binding.Space); EXPECT_EQ(2u, Binding.LowerBound); @@ -144,10 +146,10 @@ declare target("dx.RawBuffer", float, 1, 0) @ind.func(target("dx.RawBuffer", flo TEST_F(UniqueResourceFromUseTest, TestAmbigousIndirectUse) { 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) + %foo = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding(i32 1, i32 1, i32 1, i32 1, i1 false) + %bar = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding(i32 2, i32 2, i32 2, i32 2, i1 false) + %baz = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding(i32 3, i32 3, i32 3, i32 3, i1 false) + %bat = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding(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) @@ -155,17 +157,13 @@ define void @foo() { 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); - const DXILResourceMap &DRM = MAM->getResult(*M); + DXILResourceMap &DRM = MAM->getResult(*M); for (const Function &F : M->functions()) { if (F.getName() != "a.func") { continue; @@ -180,25 +178,25 @@ declare target("dx.RawBuffer", float, 1, 0) @ind.func(target("dx.RawBuffer", flo ASSERT_EQ(Bindings.size(), 4u) << "Handle should resolve into four resources"; - auto Binding = Bindings[0].getBinding(); + 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(); + 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(); + 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(); + Binding = Bindings[3]->getBinding(); EXPECT_EQ(3u, Binding.RecordID); EXPECT_EQ(4u, Binding.Space); EXPECT_EQ(4u, Binding.LowerBound); @@ -216,8 +214,8 @@ TEST_F(UniqueResourceFromUseTest, TestConditionalUse) { StringRef Assembly = R"( define void @foo(i32 %n) { 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) + %x = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding(i32 1, i32 1, i32 1, i32 1, i1 false) + %y = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding(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 @@ -235,17 +233,13 @@ bb.exit: 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); - const DXILResourceMap &DRM = MAM->getResult(*M); + DXILResourceMap &DRM = MAM->getResult(*M); for (const Function &F : M->functions()) { if (F.getName() != "a.func") { continue; @@ -260,13 +254,13 @@ declare target("dx.RawBuffer", float, 1, 0) @ind.func(target("dx.RawBuffer", flo ASSERT_EQ(Bindings.size(), 2u) << "Handle should resolve into four resources"; - auto Binding = Bindings[0].getBinding(); + 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(); + Binding = Bindings[1]->getBinding(); EXPECT_EQ(1u, Binding.RecordID); EXPECT_EQ(4u, Binding.Space); EXPECT_EQ(4u, Binding.LowerBound); @@ -280,4 +274,159 @@ declare target("dx.RawBuffer", float, 1, 0) @ind.func(target("dx.RawBuffer", flo } } +// 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(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 +} + )"; + + 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 auto *const Binding = DRM.find(CI); + ASSERT_EQ(Binding->CounterDirection, ResourceCounterDirection::Decrement); + } + } +} + +// 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 @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 +} + )"; + + 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 auto *const Binding = DRM.find(CI); + ASSERT_EQ(Binding->CounterDirection, ResourceCounterDirection::Increment); + } + } +} + +// 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 @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 +} + )"; + + 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 auto *const Binding = DRM.find(CI); + ASSERT_EQ(Binding->CounterDirection, ResourceCounterDirection::Unknown); + } + } +} + +// Test that multiple different resources with unique incs/decs aren't +// marked invalid +TEST_F(UniqueResourceFromUseTest, TestResourceCounterMultiple) { + StringRef Assembly = R"( +define void @main() { +entry: + %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 +} + )"; + + auto M = parseAsm(Assembly); + + DXILResourceMap &DRM = MAM->getResult(*M); + + ResourceCounterDirection Dirs[2] = {ResourceCounterDirection::Decrement, + ResourceCounterDirection::Increment}; + ResourceCounterDirection *Dir = Dirs; + + 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 auto *const Binding = DRM.find(CI); + ASSERT_EQ(Binding->CounterDirection, *Dir); + Dir++; + } + } +} + +// 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 auto *const Binding = DRM.find(CI); + ASSERT_EQ(Binding->CounterDirection, ResourceCounterDirection::Invalid); + } + } +} + } // namespace From cf26a2e0258af8c73db068349b172ef9e2e70fb9 Mon Sep 17 00:00:00 2001 From: Ashley Coleman Date: Wed, 23 Apr 2025 16:24:14 -0600 Subject: [PATCH 2/3] Address Comments --- llvm/include/llvm/Analysis/DXILResource.h | 6 +++++- llvm/lib/Analysis/DXILResource.cpp | 20 +++++++++++------- .../DirectX/UniqueResourceFromUseTests.cpp | 21 ++++++++----------- 3 files changed, 27 insertions(+), 20 deletions(-) diff --git a/llvm/include/llvm/Analysis/DXILResource.h b/llvm/include/llvm/Analysis/DXILResource.h index a8124caf64420..9a731a1b6c555 100644 --- a/llvm/include/llvm/Analysis/DXILResource.h +++ b/llvm/include/llvm/Analysis/DXILResource.h @@ -439,8 +439,12 @@ 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); public: using iterator = SmallVector::iterator; diff --git a/llvm/lib/Analysis/DXILResource.cpp b/llvm/lib/Analysis/DXILResource.cpp index d2392ff929611..e5fe2bba8dc4a 100644 --- a/llvm/lib/Analysis/DXILResource.cpp +++ b/llvm/lib/Analysis/DXILResource.cpp @@ -701,7 +701,8 @@ 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()) { @@ -778,7 +779,9 @@ void DXILResourceMap::populate(Module &M, DXILResourceTypeMap &DRTM) { // Adjust the resource binding to use the next ID. RI.setBindingID(NextID++); } +} +void DXILResourceMap::populateCounterDirections(Module &M) { for (Function &F : M.functions()) { if (!isUpdateCounterIntrinsic(F)) continue; @@ -806,16 +809,20 @@ void DXILResourceMap::populate(Module &M, DXILResourceTypeMap &DRTM) { Value *HandleArg = CI->getArgOperand(0); SmallVector RBInfos = findByUse(HandleArg); for (ResourceInfo *RBInfo : RBInfos) { - if (RBInfo->CounterDirection == ResourceCounterDirection::Unknown || - RBInfo->CounterDirection == Direction) + if (RBInfo->CounterDirection == ResourceCounterDirection::Unknown) RBInfo->CounterDirection = Direction; - else + 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) { @@ -849,9 +856,8 @@ SmallVector DXILResourceMap::findByUse(const Value *Key) { // Found the create, return the binding case Intrinsic::dx_resource_handlefrombinding: { auto Pos = CallMap.find(CI); - ResourceInfo *It = &Infos[Pos->second]; - assert(It != Infos.end() && "HandleFromBinding must be in resource map"); - return {It}; + assert(Pos != CallMap.end() && "HandleFromBinding must be in resource map"); + return {&Infos[Pos->second]}; } default: break; diff --git a/llvm/unittests/Target/DirectX/UniqueResourceFromUseTests.cpp b/llvm/unittests/Target/DirectX/UniqueResourceFromUseTests.cpp index d1ebfc3b1da41..caba1718ed8ea 100644 --- a/llvm/unittests/Target/DirectX/UniqueResourceFromUseTests.cpp +++ b/llvm/unittests/Target/DirectX/UniqueResourceFromUseTests.cpp @@ -293,9 +293,8 @@ define void @main() { DXILResourceMap &DRM = MAM->getResult(*M); for (const Function &F : M->functions()) { - if (F.getIntrinsicID() != Intrinsic::dx_resource_handlefrombinding) { + if (F.getIntrinsicID() != Intrinsic::dx_resource_handlefrombinding) continue; - } for (const User *U : F.users()) { const CallInst *CI = cast(U); @@ -324,9 +323,8 @@ define void @main() { DXILResourceMap &DRM = MAM->getResult(*M); for (const Function &F : M->functions()) { - if (F.getIntrinsicID() != Intrinsic::dx_resource_handlefrombinding) { + if (F.getIntrinsicID() != Intrinsic::dx_resource_handlefrombinding) continue; - } for (const User *U : F.users()) { const CallInst *CI = cast(U); @@ -352,9 +350,8 @@ define void @main() { DXILResourceMap &DRM = MAM->getResult(*M); for (const Function &F : M->functions()) { - if (F.getIntrinsicID() != Intrinsic::dx_resource_handlefrombinding) { + if (F.getIntrinsicID() != Intrinsic::dx_resource_handlefrombinding) continue; - } for (const User *U : F.users()) { const CallInst *CI = cast(U); @@ -387,15 +384,16 @@ define void @main() { ResourceCounterDirection *Dir = Dirs; for (const Function &F : M->functions()) { - if (F.getIntrinsicID() != Intrinsic::dx_resource_handlefrombinding) { + 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_EQ(Binding->CounterDirection, *Dir); - Dir++; + ASSERT_TRUE(ExpectedDirsIndex < 2); + ASSERT_EQ(Binding->CounterDirection, Dir[ExpectedDirsIndex]); + ExpectedDirsIndex++; } } } @@ -417,9 +415,8 @@ define void @main() { DXILResourceMap &DRM = MAM->getResult(*M); for (const Function &F : M->functions()) { - if (F.getIntrinsicID() != Intrinsic::dx_resource_handlefrombinding) { + if (F.getIntrinsicID() != Intrinsic::dx_resource_handlefrombinding) continue; - } for (const User *U : F.users()) { const CallInst *CI = cast(U); From a2935b74b84b6bf01e6e2171e932bd4fb32bdbae Mon Sep 17 00:00:00 2001 From: Ashley Coleman Date: Wed, 23 Apr 2025 16:59:06 -0600 Subject: [PATCH 3/3] Address Comments --- llvm/include/llvm/Analysis/DXILResource.h | 14 +- .../DXILResource/buffer-frombinding.ll | 44 ++-- .../DirectX/UniqueResourceFromUseTests.cpp | 221 ------------------ 3 files changed, 35 insertions(+), 244 deletions(-) diff --git a/llvm/include/llvm/Analysis/DXILResource.h b/llvm/include/llvm/Analysis/DXILResource.h index 9a731a1b6c555..a82cfe26331aa 100644 --- a/llvm/include/llvm/Analysis/DXILResource.h +++ b/llvm/include/llvm/Analysis/DXILResource.h @@ -446,6 +446,13 @@ class DXILResourceMap { /// 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; using const_iterator = SmallVector::const_iterator; @@ -462,13 +469,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_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/test/Analysis/DXILResource/buffer-frombinding.ll b/llvm/test/Analysis/DXILResource/buffer-frombinding.ll index ea683bb4e5783..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,7 @@ 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: @@ -87,12 +82,10 @@ 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: @@ -108,8 +101,27 @@ define void @test_typedbuffer() { ; 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 + ; CHECK: Element Type: f32 + ; 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 @@ -121,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 caba1718ed8ea..249e1045b847f 100644 --- a/llvm/unittests/Target/DirectX/UniqueResourceFromUseTests.cpp +++ b/llvm/unittests/Target/DirectX/UniqueResourceFromUseTests.cpp @@ -53,227 +53,6 @@ class UniqueResourceFromUseTest : public testing::Test { } }; -TEST_F(UniqueResourceFromUseTest, TestTrivialUse) { - 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 void @a.func(target("dx.RawBuffer", float, 1, 0) %handle) - call void @a.func(target("dx.RawBuffer", float, 1, 0) %handle) - ret void -} - -declare void @a.func(target("dx.RawBuffer", float, 1, 0) %handle) - )"; - - auto M = parseAsm(Assembly); - - DXILResourceMap &DRM = MAM->getResult(*M); - for (const Function &F : M->functions()) { - if (F.getName() != "a.func") { - 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++; - } - - EXPECT_EQ(2u, CalledResources) - << "Expected 2 resolved call to create resource"; - } -} - -TEST_F(UniqueResourceFromUseTest, TestIndirectUse) { - StringRef Assembly = R"( -define void @foo() { - %handle = 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) @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) - ret void -} - -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) - )"; - - auto M = parseAsm(Assembly); - - DXILResourceMap &DRM = MAM->getResult(*M); - for (const Function &F : M->functions()) { - if (F.getName() != "a.func") { - 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++; - } - - EXPECT_EQ(1u, CalledResources) - << "Expected 1 resolved call to create resource"; - } -} - -TEST_F(UniqueResourceFromUseTest, TestAmbigousIndirectUse) { - StringRef Assembly = R"( -define void @foo() { - %foo = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding(i32 1, i32 1, i32 1, i32 1, i1 false) - %bar = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding(i32 2, i32 2, i32 2, i32 2, i1 false) - %baz = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding(i32 3, i32 3, i32 3, i32 3, i1 false) - %bat = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding(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) - ret void -} - -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) - )"; - - auto M = parseAsm(Assembly); - - DXILResourceMap &DRM = MAM->getResult(*M); - for (const Function &F : M->functions()) { - if (F.getName() != "a.func") { - 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++; - } - - EXPECT_EQ(1u, CalledResources) - << "Expected 1 resolved call to create resource"; - } -} - -TEST_F(UniqueResourceFromUseTest, TestConditionalUse) { - StringRef Assembly = R"( -define void @foo(i32 %n) { -entry: - %x = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding(i32 1, i32 1, i32 1, i32 1, i1 false) - %y = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding(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) - ret void -} - -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) - )"; - - auto M = parseAsm(Assembly); - - DXILResourceMap &DRM = MAM->getResult(*M); - for (const Function &F : M->functions()) { - if (F.getName() != "a.func") { - 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(), 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++; - } - - EXPECT_EQ(1u, CalledResources) - << "Expected 1 resolved call to create resource"; - } -} - // 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) {