[llvm-branch-commits] [llvm] [DirectX] Lower `llvm.dx.resource.handlefromheap` intrinsic (PR #216459)
Helena Kotas via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Wed Aug 19 11:30:00 PDT 2026
https://github.com/hekota updated https://github.com/llvm/llvm-project/pull/216459
>From eb8060ffbba3c02d3aef0d72047235eeba5014bf Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Sat, 15 Aug 2026 00:00:28 -0700
Subject: [PATCH 1/2] [DirectX] Lower `llvm.dx.resource.handlefromheap`
intrinsic
Fixes #91406
---
llvm/lib/Target/DirectX/DXIL.td | 8 ++++
llvm/lib/Target/DirectX/DXILOpLowering.cpp | 45 +++++++++++++++++++
.../CodeGen/DirectX/CreateHandleHeap-NURI.ll | 33 ++++++++++++++
llvm/test/CodeGen/DirectX/CreateHandleHeap.ll | 32 +++++++++++++
4 files changed, 118 insertions(+)
create mode 100644 llvm/test/CodeGen/DirectX/CreateHandleHeap-NURI.ll
create mode 100644 llvm/test/CodeGen/DirectX/CreateHandleHeap.ll
diff --git a/llvm/lib/Target/DirectX/DXIL.td b/llvm/lib/Target/DirectX/DXIL.td
index bbf7cddb5c17c..4beafd0c619b0 100644
--- a/llvm/lib/Target/DirectX/DXIL.td
+++ b/llvm/lib/Target/DirectX/DXIL.td
@@ -1531,3 +1531,11 @@ def CreateHandleFromBinding : DXILOp<217, createHandleFromBinding> {
let stages = [Stages<DXIL1_6, [all_stages]>];
let attributes = [Attributes<DXIL1_0, [ReadNone]>];
}
+
+def CreateHandleFromHeap : DXILOp<218, createHandleFromHeap> {
+ let Doc = "create resource handle from heap";
+ let arguments = [Int32Ty, Int1Ty, Int1Ty];
+ let result = HandleTy;
+ let stages = [Stages<DXIL1_6, [all_stages]>];
+ let attributes = [Attributes<DXIL1_0, [ReadNone]>];
+}
diff --git a/llvm/lib/Target/DirectX/DXILOpLowering.cpp b/llvm/lib/Target/DirectX/DXILOpLowering.cpp
index dc15c02481468..b0e58c6559c9d 100644
--- a/llvm/lib/Target/DirectX/DXILOpLowering.cpp
+++ b/llvm/lib/Target/DirectX/DXILOpLowering.cpp
@@ -433,6 +433,48 @@ class OpLowerer {
return lowerToBindAndAnnotateHandle(F);
}
+ bool lowerHandleFromHeap(Function &F) {
+ IRBuilder<> &IRB = OpBuilder.getIRB();
+ Type *Int1Ty = IRB.getInt1Ty();
+
+ return replaceFunction(F, [&](CallInst *CI) -> Error {
+ IRB.SetInsertPoint(CI);
+
+ auto *It = DRM.find(CI);
+ assert(It != DRM.end() && "Resource not in map?");
+ dxil::ResourceInfo &RI = *It;
+ dxil::ResourceTypeInfo &RTI = DRTM[RI.getHandleTy()];
+
+ Value *IndexOp = CI->getArgOperand(0);
+ Value *IsSamplerHeap = CI->getArgOperand(1);
+
+ std::pair<uint32_t, uint32_t> Props =
+ RI.getAnnotateProps(*F.getParent(), RTI);
+
+ bool NonUniformIndex = hasNonUniformIndex(IndexOp);
+ Constant *NonUniformOp = ConstantInt::get(Int1Ty, NonUniformIndex);
+
+ std::array<Value *, 3> Args{IndexOp, IsSamplerHeap, NonUniformOp};
+ Expected<CallInst *> OpCreateHandle = OpBuilder.tryCreateOp(
+ OpCode::CreateHandleFromHeap, Args, CI->getName());
+ if (Error E = OpCreateHandle.takeError())
+ return E;
+
+ std::array<Value *, 2> AnnotateArgs{
+ *OpCreateHandle, OpBuilder.getResProps(Props.first, Props.second)};
+ Expected<CallInst *> OpAnnotate = OpBuilder.tryCreateOp(
+ OpCode::AnnotateHandle, AnnotateArgs,
+ CI->hasName() ? CI->getName() + "_annot" : Twine());
+ if (Error E = OpAnnotate.takeError())
+ return E;
+
+ Value *Cast = createTmpHandleCast(*OpAnnotate, CI->getType());
+ CI->replaceAllUsesWith(Cast);
+ CI->eraseFromParent();
+ return Error::success();
+ });
+ }
+
/// Replace uses of \c Intrin with the values in the `dx.ResRet` of \c Op.
/// Since we expect to be post-scalarization, make an effort to avoid vectors.
Error replaceResRetUses(CallInst *Intrin, CallInst *Op, bool HasCheckBit) {
@@ -1281,6 +1323,9 @@ class OpLowerer {
case Intrinsic::dx_resource_handlefrombinding:
HasErrors |= lowerHandleFromBinding(F);
break;
+ case Intrinsic::dx_resource_handlefromheap:
+ HasErrors |= lowerHandleFromHeap(F);
+ break;
case Intrinsic::dx_resource_getbasepointer:
case Intrinsic::dx_resource_getpointer:
HasErrors |= lowerGetPointer(F);
diff --git a/llvm/test/CodeGen/DirectX/CreateHandleHeap-NURI.ll b/llvm/test/CodeGen/DirectX/CreateHandleHeap-NURI.ll
new file mode 100644
index 0000000000000..ab77035da3616
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/CreateHandleHeap-NURI.ll
@@ -0,0 +1,33 @@
+; RUN: opt -S -passes=dxil-op-lower %s | FileCheck %s
+
+target triple = "dxil-pc-shadermodel6.6-compute"
+
+declare i32 @some_val();
+
+define void @test_with_nuri() {
+
+ %val = call i32 @some_val()
+ %foo = alloca i32, align 4
+
+ ; int val = some_val();
+ ; RWBuffer<float4> Buf = ResourceDescriptorHeap[NonUniformResourceIndex(val)]
+ %nuri = tail call noundef i32 @llvm.dx.resource.nonuniformindex(i32 %val)
+ %typed0 = call target("dx.TypedBuffer", <4 x float>, 1, 0, 0)
+ @llvm.dx.resource.handlefromheap.tdx.TypedBuffer_v4f32_1_0_0(i32 %nuri, i1 false)
+ ; CHECK: [[HANDLE0:%.*]] = call %dx.types.Handle @dx.op.createHandleFromHeap(i32 218, i32 %val, i1 false, i1 true)
+ ; CHECK: [[ANNOT_HANDLE0:%.*]] = call %dx.types.Handle @dx.op.annotateHandle(i32 216, %dx.types.Handle [[HANDLE0]], %dx.types.ResourceProperties { i32 4106, i32 1033 })
+
+ ; SamplerState Samp = SamplerDescriptorHeap[NonUniformResourceIndex(val + 1) % 10];
+ %add1 = add i32 %val, 1
+ %nuri2 = tail call noundef i32 @llvm.dx.resource.nonuniformindex(i32 %add1)
+ %rem1 = urem i32 %nuri2, 10
+ %samp = call target("dx.Sampler", 0)
+ @llvm.dx.resource.handlefromheap.tdx.Sampler_0(i32 %rem1, i1 true)
+ ; CHECK: [[HANDLE1:%.*]] = call %dx.types.Handle @dx.op.createHandleFromHeap(i32 218, i32 %rem1, i1 true, i1 true)
+ ; CHECK: [[ANNOT_HANDLE1:%.*]] = call %dx.types.Handle @dx.op.annotateHandle(i32 216, %dx.types.Handle [[HANDLE1]], %dx.types.ResourceProperties { i32 14, i32 0 })
+
+ ret void
+}
+
+; CHECK: declare %dx.types.Handle @dx.op.createHandleFromHeap(i32, i32, i1, i1)
+; CHECK: declare %dx.types.Handle @dx.op.annotateHandle(i32, %dx.types.Handle, %dx.types.ResourceProperties)
diff --git a/llvm/test/CodeGen/DirectX/CreateHandleHeap.ll b/llvm/test/CodeGen/DirectX/CreateHandleHeap.ll
new file mode 100644
index 0000000000000..ac7b36a58c0f0
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/CreateHandleHeap.ll
@@ -0,0 +1,32 @@
+; RUN: opt -S -passes=dxil-op-lower %s | FileCheck %s
+
+target triple = "dxil-pc-shadermodel6.6-compute"
+
+declare i32 @some_val();
+
+define void @test() {
+ ; RWBuffer<float4> Buf = ResourceDescriptorHeap[3]
+ %typed = call target("dx.TypedBuffer", <4 x float>, 1, 0, 0)
+ @llvm.dx.resource.handlefromheap.tdx.TypedBuffer_v4f32_1_0_0(i32 3, i1 false)
+ ; CHECK: [[HANDLE0:%.*]] = call %dx.types.Handle @dx.op.createHandleFromHeap(i32 218, i32 3, i1 false, i1 false)
+ ; CHECK: [[ANNOT_HANDLE0:%.*]] = call %dx.types.Handle @dx.op.annotateHandle(i32 216, %dx.types.Handle [[HANDLE0]], %dx.types.ResourceProperties { i32 4106, i32 1033 })
+
+ ; int val = some_val();
+ ; RWBuffer<float4> Buf = ResourceDescriptorHeap[val]
+ %val = call i32 @some_val()
+ %typed1 = call target("dx.TypedBuffer", <4 x float>, 1, 0, 0)
+ @llvm.dx.resource.handlefromheap.tdx.TypedBuffer_v4f32_1_0_0(i32 %val, i1 false)
+ ; CHECK: [[HANDLE0:%.*]] = call %dx.types.Handle @dx.op.createHandleFromHeap(i32 218, i32 %val, i1 false, i1 false)
+ ; CHECK: [[ANNOT_HANDLE0:%.*]] = call %dx.types.Handle @dx.op.annotateHandle(i32 216, %dx.types.Handle [[HANDLE0]], %dx.types.ResourceProperties { i32 4106, i32 1033 })
+
+ ; SamplerState Samp = SamplerDescriptorHeap[val];
+ %samp = call target("dx.Sampler", 0)
+ @llvm.dx.resource.handlefromheap.tdx.Sampler_0(i32 %val, i1 true)
+ ; CHECK: [[HANDLE1:%.*]] = call %dx.types.Handle @dx.op.createHandleFromHeap(i32 218, i32 %val, i1 true, i1 false)
+ ; CHECK: [[ANNOT_HANDLE1:%.*]] = call %dx.types.Handle @dx.op.annotateHandle(i32 216, %dx.types.Handle [[HANDLE1]], %dx.types.ResourceProperties { i32 14, i32 0 })
+
+ ret void
+}
+
+; CHECK: declare %dx.types.Handle @dx.op.createHandleFromHeap(i32, i32, i1, i1)
+; CHECK: declare %dx.types.Handle @dx.op.annotateHandle(i32, %dx.types.Handle, %dx.types.ResourceProperties)
>From 692cd474fdabcc67dcf2347c2e6597097bf49d46 Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Wed, 19 Aug 2026 11:29:38 -0700
Subject: [PATCH 2/2] add validation that isSamplerHeap argument is constant
---
llvm/include/llvm/IR/IntrinsicsDirectX.td | 2 +-
.../CodeGen/DirectX/CreateHandleHeap-error.ll | 16 ++++++++++++++++
2 files changed, 17 insertions(+), 1 deletion(-)
create mode 100644 llvm/test/CodeGen/DirectX/CreateHandleHeap-error.ll
diff --git a/llvm/include/llvm/IR/IntrinsicsDirectX.td b/llvm/include/llvm/IR/IntrinsicsDirectX.td
index 944d17202c5e0..328d2e044c5dd 100644
--- a/llvm/include/llvm/IR/IntrinsicsDirectX.td
+++ b/llvm/include/llvm/IR/IntrinsicsDirectX.td
@@ -44,7 +44,7 @@ def int_dx_resource_handlefromheap
: DefaultAttrsIntrinsic<
[llvm_any_ty],
[llvm_i32_ty, llvm_i1_ty],
- [IntrNoMem]>;
+ [IntrNoMem, ImmArg<ArgIndex<1>>]>;
def int_dx_resource_getpointer
: DefaultAttrsIntrinsic<[llvm_anyptr_ty], [llvm_any_ty, llvm_any_ty],
diff --git a/llvm/test/CodeGen/DirectX/CreateHandleHeap-error.ll b/llvm/test/CodeGen/DirectX/CreateHandleHeap-error.ll
new file mode 100644
index 0000000000000..da4f964fe46e9
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/CreateHandleHeap-error.ll
@@ -0,0 +1,16 @@
+; RUN: not opt -S -passes=verify %s -disable-output 2>&1 | FileCheck %s
+
+target triple = "dxil-pc-shadermodel6.6-compute"
+
+; CHECK; immarg operand has non-immediate parameter
+; CHECK: error: input module is broken!
+
+declare i1 @some_val();
+
+define void @test() {
+ %val = call i1 @some_val()
+ %handle = call target("dx.TypedBuffer", <4 x float>, 1, 0, 0)
+ @llvm.dx.resource.handlefromheap.tdx.TypedBuffer_v4f32_1_0_0(i32 3, i1 %val)
+
+ ret void
+}
More information about the llvm-branch-commits
mailing list