[llvm] f12fb2f - [HLSL] Analyze updateCounter usage (#135669)

via llvm-commits llvm-commits at lists.llvm.org
Thu Apr 24 12:17:27 PDT 2025


Author: Ashley Coleman
Date: 2025-04-24T13:17:24-06:00
New Revision: f12fb2ff74ee2a1a5d7d4ae6ecc7dd1ee7b622cb

URL: https://github.com/llvm/llvm-project/commit/f12fb2ff74ee2a1a5d7d4ae6ecc7dd1ee7b622cb
DIFF: https://github.com/llvm/llvm-project/commit/f12fb2ff74ee2a1a5d7d4ae6ecc7dd1ee7b622cb.diff

LOG: [HLSL] Analyze updateCounter usage (#135669)

Fixes https://github.com/llvm/llvm-project/issues/135667

Analyze and annotate `ResourceInfo` with the derived direction of calls
to updateCounter (if any).

This change only sets the value. Any diagnostics that should be raised
must be done somewhere else.

Added: 
    

Modified: 
    llvm/include/llvm/Analysis/DXILResource.h
    llvm/lib/Analysis/DXILResource.cpp
    llvm/test/Analysis/DXILResource/buffer-frombinding.ll
    llvm/unittests/Target/DirectX/UniqueResourceFromUseTests.cpp

Removed: 
    


################################################################################
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<dxil::ResourceInfo *> findByUse(const Value *Key);
 
 public:
   using iterator = SmallVector<dxil::ResourceInfo>::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<dxil::ResourceInfo> 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<std::tuple<CallInst *, ResourceInfo, ResourceTypeInfo>> 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<CallInst>(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<ConstantInt>(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<ResourceInfo *> 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<dxil::ResourceInfo>
-DXILResourceMap::findByUse(const Value *Key) const {
+SmallVector<dxil::ResourceInfo *> DXILResourceMap::findByUse(const Value *Key) {
   if (const PHINode *Phi = dyn_cast<PHINode>(Key)) {
-    SmallVector<dxil::ResourceInfo> Children;
+    SmallVector<dxil::ResourceInfo *> 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<dxil::ResourceInfo> Children;
+  SmallVector<dxil::ResourceInfo *> 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<S> 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<uint4> 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<int> 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<float4> 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<float4> BufferArray[10] : register(u0, space4)
   ; RWBuffer<float4> 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<float4> 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<float4> 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<Module> parseAsm(StringRef Asm) {
+    SMDiagnostic Error;
+    std::unique_ptr<Module> 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<DXILResourceAnalysis>(*M);
 
-  const DXILResourceMap &DRM = MAM->getResult<DXILResourceAnalysis>(*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<CallInst>(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<DXILResourceAnalysis>(*M);
 
-  const DXILResourceMap &DRM = MAM->getResult<DXILResourceAnalysis>(*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<CallInst>(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<DXILResourceAnalysis>(*M);
 
-  const DXILResourceMap &DRM = MAM->getResult<DXILResourceAnalysis>(*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<CallInst>(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 
diff erent 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<DXILResourceAnalysis>(*M);
+
+  ResourceCounterDirection Dirs[2] = {ResourceCounterDirection::Decrement,
+                                      ResourceCounterDirection::Increment};
+  ResourceCounterDirection *Dir = Dirs;
 
-  const DXILResourceMap &DRM = MAM->getResult<DXILResourceAnalysis>(*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<CallInst>(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 
diff erent 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<DXILResourceAnalysis>(*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<CallInst>(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";
   }
 }
 


        


More information about the llvm-commits mailing list