Skip to content

Commit bb88fd1

Browse files
authored
[DirectX] Calculate resource binding offsets using the lower bound (#117303)
In the DXIL CreateHandle and CreateHandleFromBinding ops, resource bindings are indexed from the beginning of the binding space, not from the binding itself. Translate from an index into the binding to one from the beginning of the space when lowering to these operations.
1 parent b0bc467 commit bb88fd1

File tree

5 files changed

+72
-33
lines changed

5 files changed

+72
-33
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// RUN: %clang_cc1 -triple dxil--shadermodel6.6-compute -x hlsl -finclude-default-header -emit-llvm -o - %s | FileCheck %s
2+
3+
// CHECK: define internal void @_init_resource_bindings() {
4+
5+
// CHECK: %U0S0_h = call target("dx.TypedBuffer", <4 x float>, 1, 0, 0) @llvm.dx.handle.fromBinding.tdx.TypedBuffer_v4f32_1_0_0t(i32 0, i32 0, i32 1, i32 0, i1 false)
6+
RWBuffer<float4> U0S0 : register(u0);
7+
8+
// CHECK: %U5S3_h = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.handle.fromBinding.tdx.TypedBuffer_f32_1_0_0t(i32 3, i32 5, i32 1, i32 0, i1 false)
9+
RWBuffer<float> U5S3 : register(u5, space3);
10+
11+
// CHECK: %T2S2_h = call target("dx.RawBuffer", i32, 0, 0) @llvm.dx.handle.fromBinding.tdx.RawBuffer_i32_0_0t(i32 2, i32 2, i32 1, i32 0, i1 false)
12+
StructuredBuffer<int> T2S2 : register(t2, space2);
13+
struct S {
14+
float4 f;
15+
int i;
16+
};
17+
18+
// CHECK: %T3S0_h = call target("dx.RawBuffer", %struct.S = type { <4 x float>, i32, [12 x i8] }, 0, 0) @llvm.dx.handle.fromBinding.tdx.RawBuffer_s_struct.Ss_0_0t(i32 0, i32 3, i32 1, i32 0, i1 false)
19+
StructuredBuffer<S> T3S0 : register(t3);

llvm/docs/DirectX/DXILResources.rst

+11-4
Original file line numberDiff line numberDiff line change
@@ -162,9 +162,10 @@ the subsequent ``dx.op.annotateHandle`` operation in. Note that we don't have
162162
an analogue for `dx.op.createHandle`_, since ``dx.op.createHandleFromBinding``
163163
subsumes it.
164164

165-
For simplicity of lowering, we match DXIL in using an index from the beginning
166-
of the binding space rather than an index from the lower bound of the binding
167-
itself.
165+
We diverge from DXIL and index from the beginning of the binding rather than
166+
indexing from the beginning of the binding space. This matches the semantics
167+
more clearly and avoids a non-obvious invariant in what constitutes valid
168+
arguments.
168169

169170
.. _dx.op.createHandle: https://github.com/microsoft/DirectXShaderCompiler/blob/main/docs/DXIL.rst#resource-handles
170171

@@ -194,7 +195,7 @@ itself.
194195
* - ``%index``
195196
- 4
196197
- ``i32``
197-
- Index from the beginning of the binding space to access.
198+
- Index from the beginning of the binding.
198199
* - ``%non-uniform``
199200
- 5
200201
- i1
@@ -233,6 +234,12 @@ Examples:
233234
@llvm.dx.handle.fromBinding.tdx.RawBuffer_i8_0_0t(
234235
i32 1, i32 8, i32 1, i32 0, i1 false)
235236
237+
; RWBuffer<float4> Global[3] : register(u6, space5)
238+
; RWBuffer<float4> Buf = Global[2];
239+
%buf = call target("dx.TypedBuffer", <4 x float>, 1, 0, 0)
240+
@llvm.dx.handle.fromBinding.tdx.TypedBuffer_f32_1_0(
241+
i32 5, i32 6, i32 3, i32 2, i1 false)
242+
236243
.. list-table:: ``@llvm.dx.handle.fromHeap``
237244
:header-rows: 1
238245

llvm/lib/Target/DirectX/DXILOpLowering.cpp

+14-3
Original file line numberDiff line numberDiff line change
@@ -236,9 +236,14 @@ class OpLowerer {
236236
dxil::ResourceInfo &RI = *It;
237237
const auto &Binding = RI.getBinding();
238238

239+
Value *IndexOp = CI->getArgOperand(3);
240+
if (Binding.LowerBound != 0)
241+
IndexOp = IRB.CreateAdd(IndexOp,
242+
ConstantInt::get(Int32Ty, Binding.LowerBound));
243+
239244
std::array<Value *, 4> Args{
240245
ConstantInt::get(Int8Ty, llvm::to_underlying(RI.getResourceClass())),
241-
ConstantInt::get(Int32Ty, Binding.RecordID), CI->getArgOperand(3),
246+
ConstantInt::get(Int32Ty, Binding.RecordID), IndexOp,
242247
CI->getArgOperand(4)};
243248
Expected<CallInst *> OpCall =
244249
OpBuilder.tryCreateOp(OpCode::CreateHandle, Args, CI->getName());
@@ -257,6 +262,7 @@ class OpLowerer {
257262

258263
[[nodiscard]] bool lowerToBindAndAnnotateHandle(Function &F) {
259264
IRBuilder<> &IRB = OpBuilder.getIRB();
265+
Type *Int32Ty = IRB.getInt32Ty();
260266

261267
return replaceFunction(F, [&](CallInst *CI) -> Error {
262268
IRB.SetInsertPoint(CI);
@@ -266,6 +272,12 @@ class OpLowerer {
266272
dxil::ResourceInfo &RI = *It;
267273

268274
const auto &Binding = RI.getBinding();
275+
276+
Value *IndexOp = CI->getArgOperand(3);
277+
if (Binding.LowerBound != 0)
278+
IndexOp = IRB.CreateAdd(IndexOp,
279+
ConstantInt::get(Int32Ty, Binding.LowerBound));
280+
269281
std::pair<uint32_t, uint32_t> Props = RI.getAnnotateProps();
270282

271283
// For `CreateHandleFromBinding` we need the upper bound rather than the
@@ -276,8 +288,7 @@ class OpLowerer {
276288
: Binding.LowerBound + Binding.Size - 1;
277289
Constant *ResBind = OpBuilder.getResBind(
278290
Binding.LowerBound, UpperBound, Binding.Space, RI.getResourceClass());
279-
std::array<Value *, 3> BindArgs{ResBind, CI->getArgOperand(3),
280-
CI->getArgOperand(4)};
291+
std::array<Value *, 3> BindArgs{ResBind, IndexOp, CI->getArgOperand(4)};
281292
Expected<CallInst *> OpBind = OpBuilder.tryCreateOp(
282293
OpCode::CreateHandleFromBinding, BindArgs, CI->getName());
283294
if (Error E = OpBind.takeError())

llvm/test/CodeGen/DirectX/CreateHandle.ll

+14-13
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
; CHECK-PRETTY: Type Format Dim ID HLSL Bind Count
55
; CHECK-PRETTY: ---------- ------- ----------- ------- -------------- ---------
6-
; CHECK-PRETTY: SRV f32 buf T0 t0 unbounded
6+
; CHECK-PRETTY: SRV f32 buf T0 t7 unbounded
77
; CHECK-PRETTY: SRV byte r/o T1 t8,space1 1
88
; CHECK-PRETTY: SRV struct r/o T2 t2,space4 1
99
; CHECK-PRETTY: SRV u32 buf T3 t3,space5 24
@@ -18,44 +18,45 @@ define void @test_buffers() {
1818
; RWBuffer<float4> Buf : register(u5, space3)
1919
%typed0 = call target("dx.TypedBuffer", <4 x float>, 1, 0, 0)
2020
@llvm.dx.handle.fromBinding.tdx.TypedBuffer_v4f32_1_0_0(
21-
i32 3, i32 5, i32 1, i32 4, i1 false)
22-
; CHECK: call %dx.types.Handle @dx.op.createHandle(i32 57, i8 1, i32 1, i32 4, i1 false)
21+
i32 3, i32 5, i32 1, i32 0, i1 false)
22+
; CHECK: call %dx.types.Handle @dx.op.createHandle(i32 57, i8 1, i32 1, i32 5, i1 false)
2323
; CHECK-NOT: @llvm.dx.cast.handle
2424

2525
; RWBuffer<int> Buf : register(u7, space2)
2626
%typed1 = call target("dx.TypedBuffer", i32, 1, 0, 1)
2727
@llvm.dx.handle.fromBinding.tdx.TypedBuffer_i32_1_0_1t(
28-
i32 2, i32 7, i32 1, i32 6, i1 false)
29-
; CHECK: call %dx.types.Handle @dx.op.createHandle(i32 57, i8 1, i32 0, i32 6, i1 false)
28+
i32 2, i32 7, i32 1, i32 0, i1 false)
29+
; CHECK: call %dx.types.Handle @dx.op.createHandle(i32 57, i8 1, i32 0, i32 7, i1 false)
3030

3131
; Buffer<uint4> Buf[24] : register(t3, space5)
3232
; Buffer<uint4> typed2 = Buf[4]
3333
; Note that the index below is 3 + 4 = 7
3434
%typed2 = call target("dx.TypedBuffer", <4 x i32>, 0, 0, 0)
3535
@llvm.dx.handle.fromBinding.tdx.TypedBuffer_i32_0_0_0t(
36-
i32 5, i32 3, i32 24, i32 7, i1 false)
36+
i32 5, i32 3, i32 24, i32 4, i1 false)
3737
; CHECK: call %dx.types.Handle @dx.op.createHandle(i32 57, i8 0, i32 3, i32 7, i1 false)
3838

3939
; struct S { float4 a; uint4 b; };
4040
; StructuredBuffer<S> Buf : register(t2, space4)
4141
%struct0 = call target("dx.RawBuffer", {<4 x float>, <4 x i32>}, 0, 0)
4242
@llvm.dx.handle.fromBinding.tdx.RawBuffer_sl_v4f32v4i32s_0_0t(
43-
i32 4, i32 2, i32 1, i32 10, i1 true)
44-
; CHECK: call %dx.types.Handle @dx.op.createHandle(i32 57, i8 0, i32 2, i32 10, i1 true)
43+
i32 4, i32 2, i32 1, i32 0, i1 true)
44+
; CHECK: call %dx.types.Handle @dx.op.createHandle(i32 57, i8 0, i32 2, i32 2, i1 true)
4545

4646
; ByteAddressBuffer Buf : register(t8, space1)
4747
%byteaddr0 = call target("dx.RawBuffer", i8, 0, 0)
4848
@llvm.dx.handle.fromBinding.tdx.RawBuffer_i8_0_0t(
49-
i32 1, i32 8, i32 1, i32 12, i1 false)
50-
; CHECK: call %dx.types.Handle @dx.op.createHandle(i32 57, i8 0, i32 1, i32 12, i1 false)
49+
i32 1, i32 8, i32 1, i32 0, i1 false)
50+
; CHECK: call %dx.types.Handle @dx.op.createHandle(i32 57, i8 0, i32 1, i32 8, i1 false)
5151

52-
; Buffer<float4> Buf[] : register(t0)
52+
; Buffer<float4> Buf[] : register(t7)
5353
; Buffer<float4> typed3 = Buf[ix]
5454
%typed3_ix = call i32 @some_val()
5555
%typed3 = call target("dx.TypedBuffer", <4 x float>, 0, 0, 0)
5656
@llvm.dx.handle.fromBinding.tdx.TypedBuffer_v4f32_0_0_0t(
57-
i32 0, i32 0, i32 -1, i32 %typed3_ix, i1 false)
58-
; CHECK: call %dx.types.Handle @dx.op.createHandle(i32 57, i8 0, i32 0, i32 %typed3_ix, i1 false)
57+
i32 0, i32 7, i32 -1, i32 %typed3_ix, i1 false)
58+
; CHECK: %[[IX:.*]] = add i32 %typed3_ix, 7
59+
; CHECK: call %dx.types.Handle @dx.op.createHandle(i32 57, i8 0, i32 0, i32 %[[IX]], i1 false)
5960

6061
ret void
6162
}

llvm/test/CodeGen/DirectX/CreateHandleFromBinding.ll

+14-13
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
; CHECK-PRETTY: Type Format Dim ID HLSL Bind Count
55
; CHECK-PRETTY: ---------- ------- ----------- ------- -------------- ---------
6-
; CHECK-PRETTY: SRV f32 buf T0 t0 unbounded
6+
; CHECK-PRETTY: SRV f32 buf T0 t7 unbounded
77
; CHECK-PRETTY: SRV byte r/o T1 t8,space1 1
88
; CHECK-PRETTY: SRV struct r/o T2 t2,space4 1
99
; CHECK-PRETTY: SRV u32 buf T3 t3,space5 24
@@ -18,48 +18,49 @@ define void @test_bindings() {
1818
; RWBuffer<float4> Buf : register(u5, space3)
1919
%typed0 = call target("dx.TypedBuffer", <4 x float>, 1, 0, 0)
2020
@llvm.dx.handle.fromBinding.tdx.TypedBuffer_v4f32_1_0_0(
21-
i32 3, i32 5, i32 1, i32 4, i1 false)
22-
; CHECK: [[BUF0:%.*]] = call %dx.types.Handle @dx.op.createHandleFromBinding(i32 217, %dx.types.ResBind { i32 5, i32 5, i32 3, i8 1 }, i32 4, i1 false)
21+
i32 3, i32 5, i32 1, i32 0, i1 false)
22+
; CHECK: [[BUF0:%.*]] = call %dx.types.Handle @dx.op.createHandleFromBinding(i32 217, %dx.types.ResBind { i32 5, i32 5, i32 3, i8 1 }, i32 5, i1 false)
2323
; CHECK: call %dx.types.Handle @dx.op.annotateHandle(i32 216, %dx.types.Handle [[BUF0]], %dx.types.ResourceProperties { i32 4106, i32 1033 })
2424

2525
; RWBuffer<int> Buf : register(u7, space2)
2626
%typed1 = call target("dx.TypedBuffer", i32, 1, 0, 1)
2727
@llvm.dx.handle.fromBinding.tdx.TypedBuffer_i32_1_0_0t(
28-
i32 2, i32 7, i32 1, i32 6, i1 false)
29-
; CHECK: [[BUF1:%.*]] = call %dx.types.Handle @dx.op.createHandleFromBinding(i32 217, %dx.types.ResBind { i32 7, i32 7, i32 2, i8 1 }, i32 6, i1 false)
28+
i32 2, i32 7, i32 1, i32 0, i1 false)
29+
; CHECK: [[BUF1:%.*]] = call %dx.types.Handle @dx.op.createHandleFromBinding(i32 217, %dx.types.ResBind { i32 7, i32 7, i32 2, i8 1 }, i32 7, i1 false)
3030
; CHECK: call %dx.types.Handle @dx.op.annotateHandle(i32 216, %dx.types.Handle [[BUF1]], %dx.types.ResourceProperties { i32 4106, i32 260 })
3131

3232
; Buffer<uint4> Buf[24] : register(t3, space5)
3333
; Buffer<uint4> typed2 = Buf[4]
3434
; Note that the index below is 3 + 4 = 7
3535
%typed2 = call target("dx.TypedBuffer", <4 x i32>, 0, 0, 0)
3636
@llvm.dx.handle.fromBinding.tdx.TypedBuffer_i32_0_0_0t(
37-
i32 5, i32 3, i32 24, i32 7, i1 false)
37+
i32 5, i32 3, i32 24, i32 4, i1 false)
3838
; CHECK: [[BUF2:%.*]] = call %dx.types.Handle @dx.op.createHandleFromBinding(i32 217, %dx.types.ResBind { i32 3, i32 26, i32 5, i8 0 }, i32 7, i1 false)
3939
; CHECK: call %dx.types.Handle @dx.op.annotateHandle(i32 216, %dx.types.Handle [[BUF2]], %dx.types.ResourceProperties { i32 10, i32 1029 })
4040

4141
; struct S { float4 a; uint4 b; };
4242
; StructuredBuffer<S> Buf : register(t2, space4)
4343
%struct0 = call target("dx.RawBuffer", {<4 x float>, <4 x i32>}, 0, 0)
4444
@llvm.dx.handle.fromBinding.tdx.RawBuffer_sl_v4f32v4i32s_0_0t(
45-
i32 4, i32 2, i32 1, i32 10, i1 true)
46-
; CHECK: [[BUF3:%.*]] = call %dx.types.Handle @dx.op.createHandleFromBinding(i32 217, %dx.types.ResBind { i32 2, i32 2, i32 4, i8 0 }, i32 10, i1 true)
45+
i32 4, i32 2, i32 1, i32 0, i1 true)
46+
; CHECK: [[BUF3:%.*]] = call %dx.types.Handle @dx.op.createHandleFromBinding(i32 217, %dx.types.ResBind { i32 2, i32 2, i32 4, i8 0 }, i32 2, i1 true)
4747
; CHECK: = call %dx.types.Handle @dx.op.annotateHandle(i32 216, %dx.types.Handle [[BUF3]], %dx.types.ResourceProperties { i32 1036, i32 32 })
4848

4949
; ByteAddressBuffer Buf : register(t8, space1)
5050
%byteaddr0 = call target("dx.RawBuffer", i8, 0, 0)
5151
@llvm.dx.handle.fromBinding.tdx.RawBuffer_i8_0_0t(
52-
i32 1, i32 8, i32 1, i32 12, i1 false)
53-
; CHECK: [[BUF4:%.*]] = call %dx.types.Handle @dx.op.createHandleFromBinding(i32 217, %dx.types.ResBind { i32 8, i32 8, i32 1, i8 0 }, i32 12, i1 false)
52+
i32 1, i32 8, i32 1, i32 0, i1 false)
53+
; CHECK: [[BUF4:%.*]] = call %dx.types.Handle @dx.op.createHandleFromBinding(i32 217, %dx.types.ResBind { i32 8, i32 8, i32 1, i8 0 }, i32 8, i1 false)
5454
; CHECK: call %dx.types.Handle @dx.op.annotateHandle(i32 216, %dx.types.Handle [[BUF4]], %dx.types.ResourceProperties { i32 11, i32 0 })
5555

56-
; Buffer<float4> Buf[] : register(t0)
56+
; Buffer<float4> Buf[] : register(t7)
5757
; Buffer<float4> typed3 = Buf[ix]
5858
%typed3_ix = call i32 @some_val()
5959
%typed3 = call target("dx.TypedBuffer", <4 x float>, 0, 0, 0)
6060
@llvm.dx.handle.fromBinding.tdx.TypedBuffer_v4f32_0_0_0t(
61-
i32 0, i32 0, i32 -1, i32 %typed3_ix, i1 false)
62-
; CHECK: [[BUF5:%.*]] = call %dx.types.Handle @dx.op.createHandleFromBinding(i32 217, %dx.types.ResBind { i32 0, i32 -1, i32 0, i8 0 }, i32 %typed3_ix, i1 false)
61+
i32 0, i32 7, i32 -1, i32 %typed3_ix, i1 false)
62+
; CHECK: %[[IX:.*]] = add i32 %typed3_ix, 7
63+
; CHECK: [[BUF5:%.*]] = call %dx.types.Handle @dx.op.createHandleFromBinding(i32 217, %dx.types.ResBind { i32 7, i32 -1, i32 0, i8 0 }, i32 %[[IX]], i1 false)
6364
; CHECK: call %dx.types.Handle @dx.op.annotateHandle(i32 216, %dx.types.Handle [[BUF5]], %dx.types.ResourceProperties { i32 10, i32 1033 })
6465

6566
ret void

0 commit comments

Comments
 (0)