[llvm] [DirectX][ResourceAccess] Legalize resource handles into unique global resources (PR #176797)
via llvm-commits
llvm-commits at lists.llvm.org
Mon Jan 19 10:29:29 PST 2026
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-backend-directx
Author: Finn Plummer (inbelic)
<details>
<summary>Changes</summary>
This change replaces the propagation of handles (or corresponding ptr) through control flow with an index into the unique global resource it is accessing.
This legalizes code-generation of handle selection and various optimizations on the created ptr into the resource, as illustrated [here](https://godbolt.org/z/bhhqde77c).
This specifically resolves all indexing into a global resource array, but also undoes the sink of a handle/ptr in the GVN pass.
By handling GVN, this resolves https://github.com/llvm/llvm-project/issues/165288.
Note: This does not handle the cases when a local resource appears to be assigned into by different global resources. To guarantee correct code-generation we are dependent on the assumptions provided by: https://github.com/llvm/llvm-project/pull/176793 and the existing `phiNodeRemap` function to undo the sinking of resource access instructions, introduced [here](https://github.com/llvm/llvm-project/pull/154620).
---
Patch is 37.64 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/176797.diff
5 Files Affected:
- (modified) llvm/lib/Target/DirectX/DXILResourceAccess.cpp (+235-19)
- (renamed) llvm/test/CodeGen/DirectX/ResourceAccess/hoist-from-getptr.ll ()
- (renamed) llvm/test/CodeGen/DirectX/ResourceAccess/issue-152348.ll ()
- (added) llvm/test/CodeGen/DirectX/ResourceAccess/legalize-handle-cases.ll (+219)
- (added) llvm/test/CodeGen/DirectX/ResourceAccess/legalize-handle-to-index.ll (+161)
``````````diff
diff --git a/llvm/lib/Target/DirectX/DXILResourceAccess.cpp b/llvm/lib/Target/DirectX/DXILResourceAccess.cpp
index 3d75d7455101f..22f39f964b99a 100644
--- a/llvm/lib/Target/DirectX/DXILResourceAccess.cpp
+++ b/llvm/lib/Target/DirectX/DXILResourceAccess.cpp
@@ -22,6 +22,7 @@
#include "llvm/IR/User.h"
#include "llvm/InitializePasses.h"
#include "llvm/Support/FormatVariadic.h"
+#include "llvm/Transforms/Scalar.h"
#include "llvm/Transforms/Utils/ValueMapper.h"
#define DEBUG_TYPE "dxil-resource-access"
@@ -57,7 +58,7 @@ static Value *traverseGEPOffsets(const DataLayout &DL, IRBuilder<> &Builder,
GEPOffset = *GEP->idx_begin();
} else if (NumIndices == 2) {
// If we have two indices, this should be an access through a pointer.
- auto IndexIt = GEP->idx_begin();
+ auto *IndexIt = GEP->idx_begin();
assert(cast<ConstantInt>(IndexIt)->getZExtValue() == 0 &&
"GEP is not indexing through pointer");
GEPOffset = *(++IndexIt);
@@ -419,6 +420,196 @@ static void createLoadIntrinsic(IntrinsicInst *II, LoadInst *LI,
llvm_unreachable("Unhandled case in switch");
}
+static Instruction *getPointerOperand(Instruction *AI) {
+ if (auto *LI = dyn_cast<LoadInst>(AI))
+ return dyn_cast<Instruction>(LI->getPointerOperand());
+ if (auto *SI = dyn_cast<StoreInst>(AI))
+ return dyn_cast<Instruction>(SI->getPointerOperand());
+
+ return nullptr;
+}
+
+static const std::array<Intrinsic::ID, 2> HandleIntrins = {
+ Intrinsic::dx_resource_handlefrombinding,
+ Intrinsic::dx_resource_handlefromimplicitbinding,
+};
+
+static SmallVector<IntrinsicInst *> collectUsedHandles(Value *Ptr) {
+ SmallVector<Value *> Worklist = {Ptr};
+ SmallVector<IntrinsicInst *> Handles;
+
+ while (!Worklist.empty()) {
+ Value *X = Worklist.pop_back_val();
+
+ if (!X->getType()->isPointerTy() && !X->getType()->isTargetExtTy())
+ return {}; // Early exit on store/load into non-resource
+
+ if (auto *Phi = dyn_cast<PHINode>(X))
+ for (Use &V : Phi->incoming_values())
+ Worklist.push_back(V.get());
+ 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)) {
+ Intrinsic::ID IID = II->getIntrinsicID();
+
+ if (IID == Intrinsic::dx_resource_getpointer)
+ Worklist.push_back(II->getArgOperand(/*Handle=*/0));
+
+ if (llvm::is_contained(HandleIntrins, IID))
+ Handles.push_back(II);
+ }
+ }
+
+ return Handles;
+}
+
+static hlsl::Binding getBinding(IntrinsicInst *Handle,
+ DXILResourceTypeMap &DRTM) {
+ auto *HandleTy = cast<TargetExtType>(Handle->getType());
+ dxil::ResourceClass Class = DRTM[HandleTy].getResourceClass();
+ uint32_t Space = cast<ConstantInt>(Handle->getArgOperand(0))->getZExtValue();
+ uint32_t LowerBound =
+ cast<ConstantInt>(Handle->getArgOperand(1))->getZExtValue();
+ int32_t Size = cast<ConstantInt>(Handle->getArgOperand(2))->getZExtValue();
+ uint32_t UpperBound = Size < 0 ? UINT32_MAX : LowerBound + Size - 1;
+
+ return hlsl::Binding(Class, Space, LowerBound, UpperBound, nullptr);
+}
+
+static bool haveCommonBinding(ArrayRef<IntrinsicInst *> Handles,
+ DXILResourceTypeMap &DRTM) {
+ unsigned NumHandles = Handles.size();
+ if (NumHandles <= 1)
+ return false; // No-legalization required
+
+ hlsl::Binding B = getBinding(Handles[0], DRTM);
+ for (unsigned I = 1; I < NumHandles; I++)
+ if (B != getBinding(Handles[I], DRTM))
+ return false; // No-legalization is possible
+
+ return true;
+}
+
+// getHandleIndicies traverses up the control flow that a ptr came from and
+// propogates back the GetPtrIdx and HandleIdx:
+//
+// - GetPtrIdx is the index of dx.resource.getpointer
+// - HandleIdx is the index of dx.resource.handlefrom.*
+static std::pair<Value *, Value *>
+getHandleIndicies(Instruction *I,
+ SmallSetVector<Instruction *, 16> &DeadInsts) {
+ if (auto *II = dyn_cast<IntrinsicInst>(I)) {
+ if (llvm::is_contained(HandleIntrins, II->getIntrinsicID())) {
+ DeadInsts.insert(II);
+ return {nullptr, II->getArgOperand(/*Index=*/3)};
+ }
+
+ if (II->getIntrinsicID() == Intrinsic::dx_resource_getpointer) {
+ auto *V = dyn_cast<Instruction>(II->getArgOperand(/*Handle=*/0));
+ auto Idx = getHandleIndicies(V, DeadInsts);
+ assert(Idx.first == nullptr &&
+ "Encountered multiple dx.resource.getpointers in ptr chain?");
+
+ DeadInsts.insert(II);
+ return {II->getArgOperand(1), Idx.second};
+ }
+ }
+
+ if (auto *Phi = dyn_cast<PHINode>(I)) {
+ unsigned NumEdges = Phi->getNumIncomingValues();
+ assert(NumEdges != 0 && "Malformed Phi Node");
+
+ IRBuilder<> Builder(Phi);
+ PHINode *GetPtrPhi = PHINode::Create(Builder.getInt32Ty(), NumEdges);
+ PHINode *HandlePhi = PHINode::Create(Builder.getInt32Ty(), NumEdges);
+
+ bool HasGetPtr = true;
+ for (unsigned I = 0; I < NumEdges; I++) {
+ auto *BB = Phi->getIncomingBlock(I);
+ auto *V = dyn_cast<Instruction>(Phi->getIncomingValue(I));
+ auto [GetPtrIdx, HandleIdx] = getHandleIndicies(V, DeadInsts);
+ HasGetPtr &= GetPtrIdx != nullptr;
+ if (HasGetPtr)
+ GetPtrPhi->addIncoming(GetPtrIdx, BB);
+ HandlePhi->addIncoming(HandleIdx, BB);
+ }
+
+ if (HasGetPtr)
+ Builder.Insert(GetPtrPhi);
+ else
+ GetPtrPhi = nullptr;
+
+ Builder.Insert(HandlePhi);
+
+ DeadInsts.insert(Phi);
+ return {GetPtrPhi, HandlePhi};
+ }
+
+ if (auto *Select = dyn_cast<SelectInst>(I)) {
+ auto *TrueV = dyn_cast<Instruction>(Select->getTrueValue());
+ auto [TrueGetPtrIdx, TrueHandleIdx] = getHandleIndicies(TrueV, DeadInsts);
+
+ auto *FalseV = dyn_cast<Instruction>(Select->getFalseValue());
+ auto [FalseGetPtrIdx, FalseHandleIdx] =
+ getHandleIndicies(FalseV, DeadInsts);
+
+ IRBuilder<> Builder(Select);
+ Value *GetPtrSelect = nullptr;
+
+ if (TrueGetPtrIdx && FalseGetPtrIdx)
+ GetPtrSelect = Builder.CreateSelect(Select->getCondition(), TrueGetPtrIdx,
+ FalseGetPtrIdx);
+
+ auto *HandleSelect = Builder.CreateSelect(Select->getCondition(),
+ TrueHandleIdx, FalseHandleIdx);
+ DeadInsts.insert(Select);
+ return {GetPtrSelect, HandleSelect};
+ }
+
+ llvm_unreachable("collectUsedHandles should assure this does not occur");
+}
+
+static void
+replaceHandleWithIndicies(Instruction *Ptr, IntrinsicInst *OldHandle,
+ SmallSetVector<Instruction *, 16> &DeadInsts) {
+ auto [GetPtrIdx, HandleIdx] = getHandleIndicies(Ptr, DeadInsts);
+
+ IRBuilder<> Builder(Ptr);
+ IntrinsicInst *Handle = cast<IntrinsicInst>(OldHandle->clone());
+ Handle->setArgOperand(/*Index=*/3, HandleIdx);
+ Builder.Insert(Handle);
+
+ auto *GetPtr = Builder.CreateIntrinsic(
+ Ptr->getType(), Intrinsic::dx_resource_getpointer, {Handle, GetPtrIdx});
+
+ Ptr->replaceAllUsesWith(GetPtr);
+ DeadInsts.insert(Ptr);
+}
+
+static bool tryReplaceHandlesWithIndices(Function &F,
+ DXILResourceTypeMap &DRTM) {
+ SmallSetVector<Instruction *, 16> DeadInsts;
+ for (BasicBlock &BB : make_early_inc_range(F))
+ for (Instruction &I : BB)
+ if (auto *PtrOp = getPointerOperand(&I)) {
+ SmallVector<IntrinsicInst *> Handles = collectUsedHandles(PtrOp);
+ if (Handles.size() <= 1)
+ continue;
+ // Can replace with an index into handle call
+ if (haveCommonBinding(Handles, DRTM))
+ replaceHandleWithIndicies(PtrOp, Handles[0], DeadInsts);
+ }
+
+ bool MadeChanges = DeadInsts.size() > 0;
+
+ for (auto *I : llvm::reverse(DeadInsts))
+ if (I->hasNUses(0)) // Handle maybe used elsewhere aside from replaced path
+ I->eraseFromParent();
+
+ return MadeChanges;
+}
+
static SmallVector<Instruction *> collectBlockUseDef(Instruction *Start) {
SmallPtrSet<Instruction *, 32> Visited;
SmallVector<Instruction *, 32> Worklist;
@@ -525,6 +716,42 @@ static void phiNodeReplacement(IntrinsicInst *II,
CurrBBDeadInsts.clear();
}
+static bool hoistGetPtrUses(Function &F, DXILResourceTypeMap &DRTM) {
+ SetVector<BasicBlock *> DeadBB;
+ SmallVector<Instruction *> PrevBBDeadInsts;
+
+ for (BasicBlock &BB : make_early_inc_range(F))
+ for (Instruction &I : make_early_inc_range(BB))
+ if (auto *II = dyn_cast<IntrinsicInst>(&I))
+ if (II->getIntrinsicID() == Intrinsic::dx_resource_getpointer)
+ phiNodeReplacement(II, PrevBBDeadInsts, DeadBB);
+
+ bool MadeChanges = DeadBB.size() > 0;
+
+ for (auto *Dead : PrevBBDeadInsts)
+ Dead->eraseFromParent();
+ PrevBBDeadInsts.clear();
+ for (auto *Dead : DeadBB)
+ Dead->eraseFromParent();
+ DeadBB.clear();
+
+ return MadeChanges;
+}
+
+static bool legalizeResourceHandles(Function &F, DXILResourceTypeMap &DRTM) {
+ // Try to replace dx.resource.handlefrom.*.binding and dx.resource.getpointer
+ // calls with their respective index values and propogate the index values to
+ // be used at resource access. This legalizes the use of handles when:
+ // - A local resource is created from an Index into a global binding
+ // - GVN sink of store/load of a ptr/handle
+ bool MadeReplacements = tryReplaceHandlesWithIndices(F, DRTM);
+ // Since a Convergent op can't sink through control flow, and GVN is handled
+ // above, we can now undo any InstCombine optimizations that caused a
+ // dx.resource.getpointer ptr to sink by hoisting it back up.
+ bool MadeHoistChanges = hoistGetPtrUses(F, DRTM);
+ return MadeReplacements || MadeHoistChanges;
+}
+
static void replaceAccess(IntrinsicInst *II, dxil::ResourceTypeInfo &RTI) {
SmallVector<User *> Worklist;
for (User *U : II->users())
@@ -560,27 +787,13 @@ static void replaceAccess(IntrinsicInst *II, dxil::ResourceTypeInfo &RTI) {
static bool transformResourcePointers(Function &F, DXILResourceTypeMap &DRTM) {
SmallVector<std::pair<IntrinsicInst *, dxil::ResourceTypeInfo>> Resources;
- SetVector<BasicBlock *> DeadBB;
- SmallVector<Instruction *> PrevBBDeadInsts;
- for (BasicBlock &BB : make_early_inc_range(F)) {
- for (Instruction &I : make_early_inc_range(BB))
- if (auto *II = dyn_cast<IntrinsicInst>(&I))
- if (II->getIntrinsicID() == Intrinsic::dx_resource_getpointer)
- phiNodeReplacement(II, PrevBBDeadInsts, DeadBB);
-
+ for (BasicBlock &BB : make_early_inc_range(F))
for (Instruction &I : BB)
if (auto *II = dyn_cast<IntrinsicInst>(&I))
if (II->getIntrinsicID() == Intrinsic::dx_resource_getpointer) {
auto *HandleTy = cast<TargetExtType>(II->getArgOperand(0)->getType());
Resources.emplace_back(II, DRTM[HandleTy]);
}
- }
- for (auto *Dead : PrevBBDeadInsts)
- Dead->eraseFromParent();
- PrevBBDeadInsts.clear();
- for (auto *Dead : DeadBB)
- Dead->eraseFromParent();
- DeadBB.clear();
for (auto &[II, RI] : Resources)
replaceAccess(II, RI);
@@ -595,8 +808,9 @@ PreservedAnalyses DXILResourceAccess::run(Function &F,
MAMProxy.getCachedResult<DXILResourceTypeAnalysis>(*F.getParent());
assert(DRTM && "DXILResourceTypeAnalysis must be available");
- bool MadeChanges = transformResourcePointers(F, *DRTM);
- if (!MadeChanges)
+ bool MadeHandleChanges = legalizeResourceHandles(F, *DRTM);
+ bool MadeResourceChanges = transformResourcePointers(F, *DRTM);
+ if (!(MadeHandleChanges || MadeResourceChanges))
return PreservedAnalyses::all();
PreservedAnalyses PA;
@@ -611,7 +825,9 @@ class DXILResourceAccessLegacy : public FunctionPass {
bool runOnFunction(Function &F) override {
DXILResourceTypeMap &DRTM =
getAnalysis<DXILResourceTypeWrapperPass>().getResourceTypeMap();
- return transformResourcePointers(F, DRTM);
+ bool MadeHandleChanges = legalizeResourceHandles(F, DRTM);
+ bool MadeResourceChanges = transformResourcePointers(F, DRTM);
+ return MadeHandleChanges || MadeResourceChanges;
}
StringRef getPassName() const override { return "DXIL Resource Access"; }
DXILResourceAccessLegacy() : FunctionPass(ID) {}
diff --git a/llvm/test/CodeGen/DirectX/phi-node-replacement.ll b/llvm/test/CodeGen/DirectX/ResourceAccess/hoist-from-getptr.ll
similarity index 100%
rename from llvm/test/CodeGen/DirectX/phi-node-replacement.ll
rename to llvm/test/CodeGen/DirectX/ResourceAccess/hoist-from-getptr.ll
diff --git a/llvm/test/CodeGen/DirectX/issue-152348.ll b/llvm/test/CodeGen/DirectX/ResourceAccess/issue-152348.ll
similarity index 100%
rename from llvm/test/CodeGen/DirectX/issue-152348.ll
rename to llvm/test/CodeGen/DirectX/ResourceAccess/issue-152348.ll
diff --git a/llvm/test/CodeGen/DirectX/ResourceAccess/legalize-handle-cases.ll b/llvm/test/CodeGen/DirectX/ResourceAccess/legalize-handle-cases.ll
new file mode 100644
index 0000000000000..8991536bd4797
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/ResourceAccess/legalize-handle-cases.ll
@@ -0,0 +1,219 @@
+; RUN: opt -S -dxil-resource-type -dxil-resource-access -disable-verify \
+; RUN: -mtriple=dxil-pc-shadermodel6.3-library %s | FileCheck %s
+
+; The file contains examples of hlsl snippets that will generate invalid dxil
+; resource access, either through code-gen or by an InstCombine/GVN sink
+; optimization
+
+; NOTE: The below resources are generated with:
+;
+; RWBuffer<int> In : register(u0);
+; RWStructuredBuffer<int> Out0 : register(u1);
+; RWStructuredBuffer<int> Out1 : register(u2);
+; RWStructuredBuffer<int> OutArr[];
+
+; cbuffer c {
+; bool cond;
+; };
+
+%__cblayout_c = type <{ i32 }>
+
+ at .str = internal unnamed_addr constant [3 x i8] c"In\00", align 1
+ at .str.2 = internal unnamed_addr constant [5 x i8] c"Out0\00", align 1
+ at .str.3 = internal unnamed_addr constant [5 x i8] c"Out1\00", align 1
+ at c.cb = local_unnamed_addr global target("dx.CBuffer", %__cblayout_c) poison
+ at c.str = internal unnamed_addr constant [2 x i8] c"c\00", align 1
+ at OutArr.str = internal unnamed_addr constant [7 x i8] c"OutArr\00", align 1
+
+; Local select into global resource array:
+;
+; RWStructuredBuffer<int> Out = cond ? OutArr[0] : OutArr[1];
+; Out[GI] = WaveActiveMax(In[GI]);
+;
+; CHECK-LABEL: @select_global_resource_array()
+define void @select_global_resource_array() {
+entry:
+ %c.cb_h.i.i = tail call target("dx.CBuffer", %__cblayout_c) @llvm.dx.resource.handlefromimplicitbinding.tdx.CBuffer_s___cblayout_cst(i32 4, i32 0, i32 1, i32 0, ptr nonnull @c.str)
+ store target("dx.CBuffer", %__cblayout_c) %c.cb_h.i.i, ptr @c.cb, align 4
+ %c.cb = load target("dx.CBuffer", %__cblayout_c), ptr @c.cb, align 4
+ %0 = call ptr addrspace(2) @llvm.dx.resource.getpointer.p2.tdx.CBuffer_s___cblayout_cst(target("dx.CBuffer", %__cblayout_c) %c.cb, i32 0)
+ %1 = load i32, ptr addrspace(2) %0, align 4
+ %loadedv.i = trunc nuw i32 %1 to i1
+ br i1 %loadedv.i, label %cond.true.i, label %cond.false.i
+
+cond.true.i:
+; CHECK: cond.true.i:
+; CHECK-NEXT: br label %cond.end.i
+ %2 = tail call target("dx.RawBuffer", i32, 1, 0) @llvm.dx.resource.handlefromimplicitbinding.tdx.RawBuffer_i32_1_0t(i32 2, i32 0, i32 -1, i32 0, ptr nonnull @OutArr.str)
+ br label %cond.end.i
+
+cond.false.i:
+; CHECK: cond.false.i:
+; CHECK-NEXT: br label %cond.end.i
+ %3 = tail call target("dx.RawBuffer", i32, 1, 0) @llvm.dx.resource.handlefromimplicitbinding.tdx.RawBuffer_i32_1_0t(i32 2, i32 0, i32 -1, i32 1, ptr nonnull @OutArr.str)
+ br label %cond.end.i
+
+cond.end.i:
+; CHECK: cond.end.i
+; CHECK-NEXT: %[[HANDLE_IDX:.*]] = phi i32 [ 0, %cond.true.i ], [ 1, %cond.false.i ]
+; CHECK: %[[TID:.*]] = tail call i32 @llvm.dx.flattened.thread.id.in.group()
+; CHECK: %[[WAVE_MAX:.*]] = tail call i32 @llvm.dx.wave.reduce.max.i32(i32 %{{.*}})
+; CHECK-NEXT: %[[HANDLE:.*]] = tail call target("dx.RawBuffer", i32, 1, 0)
+; CHECK-SAME: @llvm.dx.resource.handlefromimplicitbinding.tdx.RawBuffer_i32_1_0t(i32 2, i32 0, i32 -1, i32 %[[HANDLE_IDX]], ptr nonnull @OutArr.str)
+; CHECK-NEXT: call void @llvm.dx.resource.store.rawbuffer.tdx.RawBuffer_i32_1_0t.i32(target("dx.RawBuffer", i32, 1, 0) %[[HANDLE]], i32 %[[TID]], i32 0, i32 %[[WAVE_MAX]])
+; CHECK-NEXT: ret void
+ %cond.i.sroa.speculated = phi target("dx.RawBuffer", i32, 1, 0) [ %2, %cond.true.i ], [ %3, %cond.false.i ]
+ %4 = tail call target("dx.TypedBuffer", i32, 1, 0, 1) @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_i32_1_0_1t(i32 0, i32 0, i32 1, i32 0, ptr nonnull @.str)
+ %5 = tail call i32 @llvm.dx.flattened.thread.id.in.group()
+ %6 = tail call noundef nonnull align 4 dereferenceable(4) ptr @llvm.dx.resource.getpointer.p0.tdx.TypedBuffer_i32_1_0_1t(target("dx.TypedBuffer", i32, 1, 0, 1) %4, i32 %5)
+ %7 = load i32, ptr %6, align 4
+ %hlsl.wave.active.max.i = tail call i32 @llvm.dx.wave.reduce.max.i32(i32 %7)
+ %8 = tail call noundef nonnull align 4 dereferenceable(4) ptr @llvm.dx.resource.getpointer.p0.tdx.RawBuffer_i32_1_0t(target("dx.RawBuffer", i32, 1, 0) %cond.i.sroa.speculated, i32 %5)
+ store i32 %hlsl.wave.active.max.i, ptr %8, align 4
+ ret void
+}
+
+; GVN Sink of handle ptr
+;
+; if (cond) {
+; Out0[GI] = WaveActiveSum(In[GI]);
+; } else {
+; Out0[0] = In[GI];
+; }
+; Out0[GI] = WaveActiveSum(In[GI]);
+;
+; CHECK-LABEL: @gvn_sink()
+define void @gvn_sink() {
+entry:
+ %0 = tail call target("dx.TypedBuffer", i32, 1, 0, 1) @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_i32_1_0_1t(i32 0, i32 0, i32 1, i32 0, ptr nonnull @.str)
+ %1 = tail call target("dx.RawBuffer", i32, 1, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_i32_1_0t(i32 0, i32 1, i32 1, i32 0, ptr nonnull @.str.2)
+ %c.cb_h.i.i = tail call target("dx.CBuffer", %__cblayout_c) @llvm.dx.resource.handlefromimplicitbinding.tdx.CBuffer_s___cblayout_cst(i32 4, i32 0, i32 1, i32 0, ptr nonnull @c.str)
+ store target("dx.CBuffer", %__cblayout_c) %c.cb_h.i.i, ptr @c.cb, align 4
+ %2 = tail call i32 @llvm.dx.flattened.thread.id.in.group()
+ %c.cb = load target("dx.CBuffer", %__cblayout_c), ptr @c.cb, align 4
+ %3 = call ptr addrspace(2) @llvm.dx.resource.getpointer.p2.tdx.CBuffer_s___cblayout_cst(target("dx.CBuffer", %__cblayout_c) %c.cb, i32 0)
+ %4 = load i32, ptr addrspace(2) %3, align 4
+ %loadedv.i = trunc nuw i32 %4 to i1
+ %5 = tail call noundef nonnull align 4 dereferenceable(4) ptr @llvm.dx.resource.getpointer.p0.tdx.TypedBuffer_i32_1_0_1t(target("dx.TypedBuffer", i32, 1, 0, 1) %0, i32 %2)
+ %6 = load i32, ptr %5, align 4
+ br i1 %loadedv.i, label %if.then.i, label %if.else.i
+
+if.then.i:
+ %hlsl.wave.active.sum.i = tail call i32 @llvm.dx.wave.reduce.sum.i32(i32 %6)
+ %7 = tail call noundef nonnull align 4 dereferenceable(4) ptr @llvm.dx.resource.getpointer.p0.tdx.RawBuffer_i32_1_0t(target("dx.RawBuffer", i32, 1, 0) %1, i32 %2)
+ store i32 %hlsl.wave.active.sum.i, ptr %7, align 4
+ br label %_Z4mainj.exit
+
+if.else.i:
+ %8 = tail call noundef nonnull align 4 dereferenceable(4) ptr @llvm.dx.resource.getpointer.p0.tdx.RawBuffer_i32_1_0t(target("dx.RawBuffer", i32, 1, 0) %1, i32 0)
+ store i32 %6, ptr %8, align 4
+ %.pre = tail call noundef nonnull align 4 dereferenceable(4) ptr @llvm.dx.resource.getpointer.p0.tdx.RawBuffer_i32_1_0t(target("dx.RawBuffer", i32, 1, 0) %1, i32 %2)
+ br label %_Z4mainj.exit
+
+_Z4mainj.exit:
+; CHECK: _Z4mainj.exit:
+; CHECK-NEXT: %[[TID:.*]] = phi i32 [ %2, %if.then.i ], [ %2, %if.else.i ]
+; CHECK-NEXT: %[[HANDLE_IDX:.*]] = phi i32 [ 0, %if.then.i ], [ 0, %if.else.i ]
+; CHECK-NEXT: %[[HANDLE:.*]] = tail call target("dx.RawBuffer", i32, 1, 0)
+; CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_i32_1_0t(i32 0, i32 1, i32 1, i32 %[[HANDLE_IDX]], ptr nonnull @.str.2)
+; CHECK: %[[WAVE_SUM:.*]] = tail call i32 @llvm.dx.wave.reduce.sum.i32(i32 {{.*}})
+; CHECK-NEXT: call void @llvm.dx.resource.store.rawbuffer.tdx.RawBuffer_i32_1_0t.i32(
+; CHECK-SAME: target("dx.RawBuffer", i32, 1, 0) %[[HANDLE]], i32 %[[TID]], i32 0, i32 %[[WAVE_SUM]])
+; CHECK-NEXT: ret void
+ %.pre-phi1 = phi ptr [ %7, %if.then.i ], [ %.pre, %if.else.i ]
+ %9 = load i32, ptr %5, align 4
+ %hlsl.wave.active.sum5.i = tail call i32 @llvm.dx.wave.reduce.sum.i32(i32 %9)
+ store i32 %hlsl.wave.active.sum5.i, ptr %.pre-phi1, align 4
+ ret void
+}
+
+; Using a local a...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/176797
More information about the llvm-commits
mailing list