[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