[llvm] [DirectX] Prevent `dxil-resource-access` from entering an infinite phi loop (PR #211345)

Finn Plummer via llvm-commits llvm-commits at lists.llvm.org
Mon Aug 17 11:46:15 PDT 2026


https://github.com/inbelic updated https://github.com/llvm/llvm-project/pull/211345

>From b2861b830318e337618bc2d28e3878942c348f8d Mon Sep 17 00:00:00 2001
From: Finn Plummer <mail at inbelic.dev>
Date: Tue, 21 Jul 2026 23:24:31 +0000
Subject: [PATCH 1/3] prevent infinite loop in collectusedhandles

---
 .../lib/Target/DirectX/DXILResourceAccess.cpp |  7 ++-
 .../ResourceAccess/looped-phi-nodes.ll        | 46 +++++++++++++++++++
 2 files changed, 51 insertions(+), 2 deletions(-)
 create mode 100644 llvm/test/CodeGen/DirectX/ResourceAccess/looped-phi-nodes.ll

diff --git a/llvm/lib/Target/DirectX/DXILResourceAccess.cpp b/llvm/lib/Target/DirectX/DXILResourceAccess.cpp
index 25d860e615c17..f4b252aa1408a 100644
--- a/llvm/lib/Target/DirectX/DXILResourceAccess.cpp
+++ b/llvm/lib/Target/DirectX/DXILResourceAccess.cpp
@@ -562,6 +562,7 @@ static const std::array<Intrinsic::ID, 2> HandleIntrins = {
 static SmallVector<IntrinsicInst *> collectUsedHandles(Value *Ptr) {
   SmallVector<Value *> Worklist = {Ptr};
   SmallVector<IntrinsicInst *> Handles;
+  SmallSetVector<Value *, 4> VisitedPhis;
 
   while (!Worklist.empty()) {
     Value *X = Worklist.pop_back_val();
@@ -569,10 +570,12 @@ static SmallVector<IntrinsicInst *> collectUsedHandles(Value *Ptr) {
     if (!X->getType()->isPointerTy() && !X->getType()->isTargetExtTy())
       return {}; // Early exit on store/load into non-resource
 
-    if (auto *Phi = dyn_cast<PHINode>(X))
+    if (auto *Phi = dyn_cast<PHINode>(X)) {
+      if (!VisitedPhis.insert(X))
+        continue;
       for (Use &V : Phi->incoming_values())
         Worklist.push_back(V.get());
-    else if (auto *Select = dyn_cast<SelectInst>(X))
+    } else if (auto *Select = dyn_cast<SelectInst>(X))
       for (Value *V : {Select->getTrueValue(), Select->getFalseValue()})
         Worklist.push_back(V);
     else if (auto *II = dyn_cast<IntrinsicInst>(X)) {
diff --git a/llvm/test/CodeGen/DirectX/ResourceAccess/looped-phi-nodes.ll b/llvm/test/CodeGen/DirectX/ResourceAccess/looped-phi-nodes.ll
new file mode 100644
index 0000000000000..f812bd000fd5c
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/ResourceAccess/looped-phi-nodes.ll
@@ -0,0 +1,46 @@
+; RUN: not opt -S -dxil-resource-type -dxil-resource-access -disable-verify \
+; RUN:  -mtriple=dxil-pc-shadermodel6.3-library %s 2>&1 | FileCheck %s
+
+; Ensure that a cyclic loop of resource ptrs reports a fatal error and exits
+; compilation, rather than hanging or crashing later on the illegal access.
+;
+; This might be generated by something like:
+;
+;   RWBuffer<float> bufA : register(u0);
+;   RWBuffer<float> bufB : register(u1);
+;   ...
+;   for (int i = 0; i < N; ++i) {
+;     dst[tid] = src[tid] * 0.5f;
+;     RWBuffer<float> tmp = src; src = dst; dst = tmp;
+;   }
+
+; CHECK: error: Resource access is not guaranteed to map to a unique global resource
+
+ at .str = private unnamed_addr constant [5 x i8] c"bufA\00", align 1
+ at .str.2 = private unnamed_addr constant [5 x i8] c"bufB\00", align 1
+
+define void @main(i32 %tid, i32 %n) {
+entry:
+  %0 = tail call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_f32_1_0_0t(i32 0, i32 0, i32 1, i32 0, ptr nonnull @.str)
+  %1 = tail call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_f32_1_0_0t(i32 0, i32 1, i32 1, i32 0, ptr nonnull @.str.2)
+  br label %for.cond
+
+for.cond:
+  %dst = phi target("dx.TypedBuffer", float, 1, 0, 0) [ %1, %entry ], [ %src, %for.body ]
+  %src = phi target("dx.TypedBuffer", float, 1, 0, 0) [ %0, %entry ], [ %dst, %for.body ]
+  %i = phi i32 [ 0, %entry ], [ %inc, %for.body ]
+  %exit = icmp eq i32 %i, %n
+  br i1 %exit, label %end, label %for.body
+
+for.body:
+  %srcptr = call noundef nonnull align 4 dereferenceable(4) ptr @llvm.dx.resource.getpointer.p0.tdx.TypedBuffer_f32_1_0_0t(target("dx.TypedBuffer", float, 1, 0, 0) %src, i32 %tid)
+  %v = load float, ptr %srcptr, align 4
+  %mul = fmul float %v, 5.000000e-01
+  %dstptr = call noundef nonnull align 4 dereferenceable(4) ptr @llvm.dx.resource.getpointer.p0.tdx.TypedBuffer_f32_1_0_0t(target("dx.TypedBuffer", float, 1, 0, 0) %dst, i32 %tid)
+  store float %mul, ptr %dstptr, align 4
+  %inc = add nuw i32 %i, 1
+  br label %for.cond
+
+end:
+  ret void
+}

>From 234372acfd0b30913a130b35214f7b98a3fe9855 Mon Sep 17 00:00:00 2001
From: Finn Plummer <mail at inbelic.dev>
Date: Thu, 23 Jul 2026 00:24:08 +0000
Subject: [PATCH 2/3] prevent infinite loop in getaccessindices

---
 .../lib/Target/DirectX/DXILResourceAccess.cpp |  42 +++++--
 .../looped-phi-nodes-resolved.ll              | 119 ++++++++++++++++++
 2 files changed, 153 insertions(+), 8 deletions(-)
 create mode 100644 llvm/test/CodeGen/DirectX/ResourceAccess/looped-phi-nodes-resolved.ll

diff --git a/llvm/lib/Target/DirectX/DXILResourceAccess.cpp b/llvm/lib/Target/DirectX/DXILResourceAccess.cpp
index f4b252aa1408a..f08b2c5a90950 100644
--- a/llvm/lib/Target/DirectX/DXILResourceAccess.cpp
+++ b/llvm/lib/Target/DirectX/DXILResourceAccess.cpp
@@ -8,6 +8,7 @@
 
 #include "DXILResourceAccess.h"
 #include "DirectX.h"
+#include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/SetVector.h"
 #include "llvm/Analysis/DXILResource.h"
 #include "llvm/Analysis/VectorUtils.h"
@@ -23,8 +24,10 @@
 #include "llvm/IR/IntrinsicsDirectX.h"
 #include "llvm/IR/LLVMContext.h"
 #include "llvm/IR/User.h"
+#include "llvm/IR/ValueHandle.h"
 #include "llvm/InitializePasses.h"
 #include "llvm/Support/FormatVariadic.h"
+#include "llvm/Transforms/Utils/Local.h"
 #include "llvm/Transforms/Utils/ValueMapper.h"
 
 #define DEBUG_TYPE "dxil-resource-access"
@@ -625,7 +628,8 @@ struct AccessIndices {
 //  - GetPtrIdx is the index of dx.resource.getpointer
 //  - HandleIdx is the index of dx.resource.handlefrom.*
 static AccessIndices
-getAccessIndices(Instruction *I, SmallSetVector<Instruction *, 16> &DeadInsts) {
+getAccessIndices(Instruction *I, SmallSetVector<Instruction *, 16> &DeadInsts,
+                 SmallDenseMap<PHINode *, PHINode *> &VisitedPhis) {
   if (auto *II = dyn_cast<IntrinsicInst>(I)) {
     if (llvm::is_contained(HandleIntrins, II->getIntrinsicID())) {
       DeadInsts.insert(II);
@@ -634,7 +638,7 @@ getAccessIndices(Instruction *I, SmallSetVector<Instruction *, 16> &DeadInsts) {
 
     if (II->getIntrinsicID() == Intrinsic::dx_resource_getpointer) {
       auto *V = dyn_cast<Instruction>(II->getArgOperand(/*Handle=*/0));
-      auto AccessIdx = getAccessIndices(V, DeadInsts);
+      auto AccessIdx = getAccessIndices(V, DeadInsts, VisitedPhis);
       assert(!AccessIdx.hasGetPtrIdx() &&
              "Encountered multiple dx.resource.getpointers in ptr chain?");
       AccessIdx.GetPtrIdx = II->getArgOperand(1);
@@ -645,6 +649,10 @@ getAccessIndices(Instruction *I, SmallSetVector<Instruction *, 16> &DeadInsts) {
   }
 
   if (auto *Phi = dyn_cast<PHINode>(I)) {
+    // If we're already building indices for this phi, return a ref to the phi
+    if (auto It = VisitedPhis.find(Phi); It != VisitedPhis.end())
+      return {nullptr, It->second};
+
     unsigned NumEdges = Phi->getNumIncomingValues();
     assert(NumEdges != 0 && "Malformed Phi Node");
 
@@ -652,11 +660,15 @@ getAccessIndices(Instruction *I, SmallSetVector<Instruction *, 16> &DeadInsts) {
     PHINode *GetPtrPhi = PHINode::Create(Builder.getInt32Ty(), NumEdges);
     PHINode *HandlePhi = PHINode::Create(Builder.getInt32Ty(), NumEdges);
 
+    // Register a ref to this phi for a recursive phi
+    if (Phi->getType()->isTargetExtTy())
+      VisitedPhis[Phi] = HandlePhi;
+
     bool HasGetPtr = true;
     for (unsigned Idx = 0; Idx < NumEdges; Idx++) {
       auto *BB = Phi->getIncomingBlock(Idx);
       auto *V = dyn_cast<Instruction>(Phi->getIncomingValue(Idx));
-      auto AccessIdx = getAccessIndices(V, DeadInsts);
+      auto AccessIdx = getAccessIndices(V, DeadInsts, VisitedPhis);
       HasGetPtr &= AccessIdx.hasGetPtrIdx();
       if (HasGetPtr)
         GetPtrPhi->addIncoming(AccessIdx.GetPtrIdx, BB);
@@ -676,10 +688,10 @@ getAccessIndices(Instruction *I, SmallSetVector<Instruction *, 16> &DeadInsts) {
 
   if (auto *Select = dyn_cast<SelectInst>(I)) {
     auto *TrueV = dyn_cast<Instruction>(Select->getTrueValue());
-    auto TrueAccessIdx = getAccessIndices(TrueV, DeadInsts);
+    auto TrueAccessIdx = getAccessIndices(TrueV, DeadInsts, VisitedPhis);
 
     auto *FalseV = dyn_cast<Instruction>(Select->getFalseValue());
-    auto FalseAccessIdx = getAccessIndices(FalseV, DeadInsts);
+    auto FalseAccessIdx = getAccessIndices(FalseV, DeadInsts, VisitedPhis);
 
     IRBuilder<> Builder(Select);
     Value *GetPtrSelect = nullptr;
@@ -701,8 +713,9 @@ getAccessIndices(Instruction *I, SmallSetVector<Instruction *, 16> &DeadInsts) {
 
 static void
 replaceHandleWithIndices(Instruction *Ptr, IntrinsicInst *OldHandle,
-                         SmallSetVector<Instruction *, 16> &DeadInsts) {
-  auto AccessIdx = getAccessIndices(Ptr, DeadInsts);
+                         SmallSetVector<Instruction *, 16> &DeadInsts,
+                         SmallDenseMap<PHINode *, PHINode *> &VisitedPhis) {
+  auto AccessIdx = getAccessIndices(Ptr, DeadInsts, VisitedPhis);
   assert(AccessIdx.hasGetPtrIdx() && AccessIdx.hasHandleIdx() &&
          "Couldn't retrieve indices. This is guaranteed by getAccessIndices");
 
@@ -731,6 +744,8 @@ replaceHandleWithIndices(Instruction *Ptr, IntrinsicInst *OldHandle,
 // Returns true if any changes are made.
 static bool legalizeResourceHandles(Function &F, DXILResourceTypeMap &DRTM) {
   SmallSetVector<Instruction *, 16> DeadInsts;
+  SmallDenseMap<PHINode *, PHINode *> VisitedPhis;
+
   for (BasicBlock &BB : make_early_inc_range(F)) {
     for (Instruction &I : BB) {
       if (auto *PtrOp = getStoreLoadPointerOperand(&I)) {
@@ -750,19 +765,30 @@ static bool legalizeResourceHandles(Function &F, DXILResourceTypeMap &DRTM) {
           continue;
         }
 
-        replaceHandleWithIndices(PtrOp, Handles[0], DeadInsts);
+        replaceHandleWithIndices(PtrOp, Handles[0], DeadInsts, VisitedPhis);
       }
     }
   }
 
   bool MadeChanges = false;
 
+  // Set up the phis to track if they are erased below
+  SmallVector<WeakTrackingVH> ResourcePhis;
+  for (const auto &HandleToIndex : VisitedPhis)
+    ResourcePhis.push_back(HandleToIndex.first);
+
   for (auto *I : llvm::reverse(DeadInsts))
     if (I->hasNUses(0)) { // Handle can still be used outside of replaced path
       I->eraseFromParent();
       MadeChanges = true;
     }
 
+  // Any remaining phi nodes are now looped with another phi node and have no
+  // other uses
+  for (WeakTrackingVH &VH : ResourcePhis)
+    if (VH) // True if not removed above or already in this loop
+      MadeChanges |= RecursivelyDeleteDeadPHINode(cast<PHINode>(VH));
+
   return MadeChanges;
 }
 
diff --git a/llvm/test/CodeGen/DirectX/ResourceAccess/looped-phi-nodes-resolved.ll b/llvm/test/CodeGen/DirectX/ResourceAccess/looped-phi-nodes-resolved.ll
new file mode 100644
index 0000000000000..2737663f6175a
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/ResourceAccess/looped-phi-nodes-resolved.ll
@@ -0,0 +1,119 @@
+; RUN: opt -S -dxil-resource-type -dxil-resource-access -disable-verify \
+; RUN:  -mtriple=dxil-pc-shadermodel6.3-library %s | FileCheck %s
+
+; This tests that we can resolve the case when cycling through a dynamic
+; resource, this might be generated by something like:
+;
+;   RWBuffer<float> buf[3] : register(u0);
+;   cbuffer C { uint N; }
+;   ...
+;   RWBuffer<float> src = buf[0];
+;   RWBuffer<float> dst = buf[1];
+;   RWBuffer<float> mid = buf[2];
+;   for (uint i = 0; i < N; ++i) {
+;     dst[tid] = src[tid] * 0.5f;
+;     RWBuffer<float> tmp = src; src = mid; mid = dst; dst = tmp;
+;   }
+
+; CHECK-LABEL: define void @main_same_binding
+; CHECK:      for.cond:
+; CHECK-NEXT:   %[[SRC:.*]] = phi i32 [ 0, %entry ], [ %[[MID:.*]], %for.body ]
+; CHECK-NEXT:   %[[DST:.*]] = phi i32 [ 1, %entry ], [ %[[SRC]], %for.body ]
+; CHECK-NEXT:   %[[MID]] = phi i32 [ 2, %entry ], [ %[[DST]], %for.body ]
+; CHECK-NOT:    phi target(
+; CHECK:      for.body:
+; CHECK-NEXT:   %[[SRCH:.*]] = tail call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_f32_1_0_0t(i32 0, i32 0, i32 3, i32 %[[SRC]], ptr nonnull @.str.buf)
+; CHECK-NEXT:   %{{.*}} = call { float, i1 } @llvm.dx.resource.load.typedbuffer.f32.tdx.TypedBuffer_f32_1_0_0t(target("dx.TypedBuffer", float, 1, 0, 0) %[[SRCH]], i32 %tid)
+; CHECK:        %[[DSTH:.*]] = tail call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_f32_1_0_0t(i32 0, i32 0, i32 3, i32 %[[DST]], ptr nonnull @.str.buf)
+; CHECK-NEXT:   call void @llvm.dx.resource.store.typedbuffer.tdx.TypedBuffer_f32_1_0_0t.f32(target("dx.TypedBuffer", float, 1, 0, 0) %[[DSTH]], i32 %tid, float %mul)
+
+ at .str.buf = private unnamed_addr constant [4 x i8] c"buf\00", align 1
+ at In.str = private unnamed_addr constant [3 x i8] c"In\00", align 1
+ at Out.str = private unnamed_addr constant [4 x i8] c"Out\00", align 1
+
+define void @main_same_binding(i32 %tid, i32 %n) {
+entry:
+  %0 = tail call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_f32_1_0_0t(i32 0, i32 0, i32 3, i32 0, ptr nonnull @.str.buf)
+  %1 = tail call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_f32_1_0_0t(i32 0, i32 0, i32 3, i32 1, ptr nonnull @.str.buf)
+  %2 = tail call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_f32_1_0_0t(i32 0, i32 0, i32 3, i32 2, ptr nonnull @.str.buf)
+  br label %for.cond
+
+for.cond:
+  %src = phi target("dx.TypedBuffer", float, 1, 0, 0) [ %0, %entry ], [ %mid, %for.body ]
+  %dst = phi target("dx.TypedBuffer", float, 1, 0, 0) [ %1, %entry ], [ %src, %for.body ]
+  %mid = phi target("dx.TypedBuffer", float, 1, 0, 0) [ %2, %entry ], [ %dst, %for.body ]
+  %i = phi i32 [ 0, %entry ], [ %inc, %for.body ]
+  %exit = icmp eq i32 %i, %n
+  br i1 %exit, label %end, label %for.body
+
+for.body:
+  %srcptr = call noundef nonnull align 4 dereferenceable(4) ptr @llvm.dx.resource.getpointer.p0.tdx.TypedBuffer_f32_1_0_0t(target("dx.TypedBuffer", float, 1, 0, 0) %src, i32 %tid)
+  %v = load float, ptr %srcptr, align 4
+  %mul = fmul float %v, 5.000000e-01
+  %dstptr = call noundef nonnull align 4 dereferenceable(4) ptr @llvm.dx.resource.getpointer.p0.tdx.TypedBuffer_f32_1_0_0t(target("dx.TypedBuffer", float, 1, 0, 0) %dst, i32 %tid)
+  store float %mul, ptr %dstptr, align 4
+  %inc = add nuw i32 %i, 1
+  br label %for.cond
+
+end:
+  ret void
+}
+
+; This tests that we can resolve the case when there is a phi choice of dynamic
+; resources obfuscated by a phi loop, this might be generated by something like:
+;
+;   RWBuffer<float> In[2] : register(u0);
+;   RWBuffer<float> Out : register(u2);
+;   ...
+;   RWBuffer<float> R = In[0];
+;   for (uint i = 0; i < id.y; ++i)
+;     if (i & 1) R = In[1];
+;   Out[id.x] = R[id.x];
+
+; CHECK-LABEL: define void @main_conditional_update
+; CHECK:      entry:
+; CHECK-NEXT:   %[[OUT:.*]] = tail call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_f32_1_0_0t(i32 0, i32 2, i32 1, i32 0, ptr nonnull @Out.str)
+; CHECK-NEXT:   br label %for.cond
+; CHECK:      for.cond:
+; CHECK-NEXT:   %[[R:.*]] = phi i32 [ 0, %entry ], [ %[[RNEXT:.*]], %for.inc ]
+; CHECK-NOT:    phi target(
+; CHECK:      for.inc:
+; CHECK-NEXT:   %[[RNEXT]] = phi i32 [ 1, %if.then ], [ %[[R]], %for.body ]
+; CHECK:      for.end:
+; CHECK-NEXT:   %[[INH:.*]] = tail call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_f32_1_0_0t(i32 0, i32 0, i32 2, i32 %[[R]], ptr nonnull @In.str)
+; CHECK-NEXT:   %[[LOAD:.*]] = call { float, i1 } @llvm.dx.resource.load.typedbuffer.f32.tdx.TypedBuffer_f32_1_0_0t(target("dx.TypedBuffer", float, 1, 0, 0) %[[INH]], i32 %x)
+; CHECK:        call void @llvm.dx.resource.store.typedbuffer.tdx.TypedBuffer_f32_1_0_0t.f32(target("dx.TypedBuffer", float, 1, 0, 0) %[[OUT]], i32 %x, float %{{.*}})
+
+define void @main_conditional_update(i32 %x, i32 %y) {
+entry:
+  %in0 = tail call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_f32_1_0_0t(i32 0, i32 0, i32 2, i32 0, ptr nonnull @In.str)
+  %in1 = tail call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_f32_1_0_0t(i32 0, i32 0, i32 2, i32 1, ptr nonnull @In.str)
+  %out = tail call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_f32_1_0_0t(i32 0, i32 2, i32 1, i32 0, ptr nonnull @Out.str)
+  br label %for.cond
+
+for.cond:
+  %r = phi target("dx.TypedBuffer", float, 1, 0, 0) [ %in0, %entry ], [ %r.next, %for.inc ]
+  %i = phi i32 [ 0, %entry ], [ %inc, %for.inc ]
+  %cmp = icmp ult i32 %i, %y
+  br i1 %cmp, label %for.body, label %for.end
+
+for.body:
+  %and = and i32 %i, 1
+  %odd = icmp ne i32 %and, 0
+  br i1 %odd, label %if.then, label %for.inc
+
+if.then:
+  br label %for.inc
+
+for.inc:
+  %r.next = phi target("dx.TypedBuffer", float, 1, 0, 0) [ %in1, %if.then ], [ %r, %for.body ]
+  %inc = add nuw i32 %i, 1
+  br label %for.cond
+
+for.end:
+  %rptr = call noundef nonnull align 4 dereferenceable(4) ptr @llvm.dx.resource.getpointer.p0.tdx.TypedBuffer_f32_1_0_0t(target("dx.TypedBuffer", float, 1, 0, 0) %r, i32 %x)
+  %v = load float, ptr %rptr, align 4
+  %outptr = call noundef nonnull align 4 dereferenceable(4) ptr @llvm.dx.resource.getpointer.p0.tdx.TypedBuffer_f32_1_0_0t(target("dx.TypedBuffer", float, 1, 0, 0) %out, i32 %x)
+  store float %v, ptr %outptr, align 4
+  ret void
+}

>From fce40d8086a00e64a974917314149d0fb1dc7f7c Mon Sep 17 00:00:00 2001
From: Finn Plummer <mail at inbelic.dev>
Date: Mon, 17 Aug 2026 18:45:39 +0000
Subject: [PATCH 3/3] review: use smallset where possible

---
 llvm/lib/Target/DirectX/DXILResourceAccess.cpp | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/llvm/lib/Target/DirectX/DXILResourceAccess.cpp b/llvm/lib/Target/DirectX/DXILResourceAccess.cpp
index 1c8811638cddc..4328066fa01b8 100644
--- a/llvm/lib/Target/DirectX/DXILResourceAccess.cpp
+++ b/llvm/lib/Target/DirectX/DXILResourceAccess.cpp
@@ -10,6 +10,7 @@
 #include "DirectX.h"
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/SetVector.h"
+#include "llvm/ADT/SmallSet.h"
 #include "llvm/Analysis/DXILResource.h"
 #include "llvm/Analysis/VectorUtils.h"
 #include "llvm/Frontend/HLSL/HLSLResource.h"
@@ -731,7 +732,7 @@ static const std::array<Intrinsic::ID, 2> HandleIntrins = {
 static SmallVector<IntrinsicInst *> collectUsedHandles(Value *Ptr) {
   SmallVector<Value *> Worklist = {Ptr};
   SmallVector<IntrinsicInst *> Handles;
-  SmallSetVector<Value *, 4> VisitedPhis;
+  SmallSet<Value *, 4> VisitedPhis;
 
   while (!Worklist.empty()) {
     Value *X = Worklist.pop_back_val();
@@ -740,10 +741,11 @@ static SmallVector<IntrinsicInst *> collectUsedHandles(Value *Ptr) {
       return {}; // Early exit on store/load into non-resource
 
     if (auto *Phi = dyn_cast<PHINode>(X)) {
-      if (!VisitedPhis.insert(X))
+      if (VisitedPhis.contains(X))
         continue;
       for (Use &V : Phi->incoming_values())
         Worklist.push_back(V.get());
+      VisitedPhis.insert(Phi);
     } else if (auto *Select = dyn_cast<SelectInst>(X))
       for (Value *V : {Select->getTrueValue(), Select->getFalseValue()})
         Worklist.push_back(V);



More information about the llvm-commits mailing list