[clang] bb88fd1 - [DirectX] Calculate resource binding offsets using the lower bound (#117303)

via cfe-commits cfe-commits at lists.llvm.org
Mon Nov 25 10:44:04 PST 2024


Author: Justin Bogner
Date: 2024-11-25T10:44:01-08:00
New Revision: bb88fd171a6be892cec36969860a9034b48b8656

URL: https://github.com/llvm/llvm-project/commit/bb88fd171a6be892cec36969860a9034b48b8656
DIFF: https://github.com/llvm/llvm-project/commit/bb88fd171a6be892cec36969860a9034b48b8656.diff

LOG: [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.

Added: 
    clang/test/CodeGenHLSL/resource-bindings.hlsl

Modified: 
    llvm/docs/DirectX/DXILResources.rst
    llvm/lib/Target/DirectX/DXILOpLowering.cpp
    llvm/test/CodeGen/DirectX/CreateHandle.ll
    llvm/test/CodeGen/DirectX/CreateHandleFromBinding.ll

Removed: 
    


################################################################################
diff  --git a/clang/test/CodeGenHLSL/resource-bindings.hlsl b/clang/test/CodeGenHLSL/resource-bindings.hlsl
new file mode 100644
index 00000000000000..bfec90e1871f8b
--- /dev/null
+++ b/clang/test/CodeGenHLSL/resource-bindings.hlsl
@@ -0,0 +1,19 @@
+// RUN: %clang_cc1 -triple dxil--shadermodel6.6-compute -x hlsl -finclude-default-header -emit-llvm -o - %s | FileCheck %s
+
+// CHECK: define internal void @_init_resource_bindings() {
+
+// 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)
+RWBuffer<float4> U0S0 : register(u0);
+
+// 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)
+RWBuffer<float> U5S3 : register(u5, space3);
+
+// 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)
+StructuredBuffer<int> T2S2 : register(t2, space2);
+struct S {
+  float4 f;
+  int i;
+};
+
+// 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)
+StructuredBuffer<S> T3S0 : register(t3);

diff  --git a/llvm/docs/DirectX/DXILResources.rst b/llvm/docs/DirectX/DXILResources.rst
index ad8ede9c59fbfa..dcec9611d8aaa0 100644
--- a/llvm/docs/DirectX/DXILResources.rst
+++ b/llvm/docs/DirectX/DXILResources.rst
@@ -162,9 +162,10 @@ the subsequent ``dx.op.annotateHandle`` operation in. Note that we don't have
 an analogue for `dx.op.createHandle`_, since ``dx.op.createHandleFromBinding``
 subsumes it.
 
-For simplicity of lowering, we match DXIL in using an index from the beginning
-of the binding space rather than an index from the lower bound of the binding
-itself.
+We diverge from DXIL and index from the beginning of the binding rather than
+indexing from the beginning of the binding space. This matches the semantics
+more clearly and avoids a non-obvious invariant in what constitutes valid
+arguments.
 
 .. _dx.op.createHandle: https://github.com/microsoft/DirectXShaderCompiler/blob/main/docs/DXIL.rst#resource-handles
 
@@ -194,7 +195,7 @@ itself.
    * - ``%index``
      - 4
      - ``i32``
-     - Index from the beginning of the binding space to access.
+     - Index from the beginning of the binding.
    * - ``%non-uniform``
      - 5
      - i1
@@ -233,6 +234,12 @@ Examples:
                @llvm.dx.handle.fromBinding.tdx.RawBuffer_i8_0_0t(
                    i32 1, i32 8, i32 1, i32 0, i1 false)
 
+   ; RWBuffer<float4> Global[3] : register(u6, space5)
+   ; RWBuffer<float4> Buf = Global[2];
+   %buf = call target("dx.TypedBuffer", <4 x float>, 1, 0, 0)
+               @llvm.dx.handle.fromBinding.tdx.TypedBuffer_f32_1_0(
+                   i32 5, i32 6, i32 3, i32 2, i1 false)
+
 .. list-table:: ``@llvm.dx.handle.fromHeap``
    :header-rows: 1
 

diff  --git a/llvm/lib/Target/DirectX/DXILOpLowering.cpp b/llvm/lib/Target/DirectX/DXILOpLowering.cpp
index 9f124394363a38..047c5412e1f3ae 100644
--- a/llvm/lib/Target/DirectX/DXILOpLowering.cpp
+++ b/llvm/lib/Target/DirectX/DXILOpLowering.cpp
@@ -236,9 +236,14 @@ class OpLowerer {
       dxil::ResourceInfo &RI = *It;
       const auto &Binding = RI.getBinding();
 
+      Value *IndexOp = CI->getArgOperand(3);
+      if (Binding.LowerBound != 0)
+        IndexOp = IRB.CreateAdd(IndexOp,
+                                ConstantInt::get(Int32Ty, Binding.LowerBound));
+
       std::array<Value *, 4> Args{
           ConstantInt::get(Int8Ty, llvm::to_underlying(RI.getResourceClass())),
-          ConstantInt::get(Int32Ty, Binding.RecordID), CI->getArgOperand(3),
+          ConstantInt::get(Int32Ty, Binding.RecordID), IndexOp,
           CI->getArgOperand(4)};
       Expected<CallInst *> OpCall =
           OpBuilder.tryCreateOp(OpCode::CreateHandle, Args, CI->getName());
@@ -257,6 +262,7 @@ class OpLowerer {
 
   [[nodiscard]] bool lowerToBindAndAnnotateHandle(Function &F) {
     IRBuilder<> &IRB = OpBuilder.getIRB();
+    Type *Int32Ty = IRB.getInt32Ty();
 
     return replaceFunction(F, [&](CallInst *CI) -> Error {
       IRB.SetInsertPoint(CI);
@@ -266,6 +272,12 @@ class OpLowerer {
       dxil::ResourceInfo &RI = *It;
 
       const auto &Binding = RI.getBinding();
+
+      Value *IndexOp = CI->getArgOperand(3);
+      if (Binding.LowerBound != 0)
+        IndexOp = IRB.CreateAdd(IndexOp,
+                                ConstantInt::get(Int32Ty, Binding.LowerBound));
+
       std::pair<uint32_t, uint32_t> Props = RI.getAnnotateProps();
 
       // For `CreateHandleFromBinding` we need the upper bound rather than the
@@ -276,8 +288,7 @@ class OpLowerer {
                                 : Binding.LowerBound + Binding.Size - 1;
       Constant *ResBind = OpBuilder.getResBind(
           Binding.LowerBound, UpperBound, Binding.Space, RI.getResourceClass());
-      std::array<Value *, 3> BindArgs{ResBind, CI->getArgOperand(3),
-                                      CI->getArgOperand(4)};
+      std::array<Value *, 3> BindArgs{ResBind, IndexOp, CI->getArgOperand(4)};
       Expected<CallInst *> OpBind = OpBuilder.tryCreateOp(
           OpCode::CreateHandleFromBinding, BindArgs, CI->getName());
       if (Error E = OpBind.takeError())

diff  --git a/llvm/test/CodeGen/DirectX/CreateHandle.ll b/llvm/test/CodeGen/DirectX/CreateHandle.ll
index 40b3b2c7122722..234d4e035bf1d5 100644
--- a/llvm/test/CodeGen/DirectX/CreateHandle.ll
+++ b/llvm/test/CodeGen/DirectX/CreateHandle.ll
@@ -3,7 +3,7 @@
 
 ; CHECK-PRETTY:       Type  Format         Dim      ID      HLSL Bind     Count
 ; CHECK-PRETTY: ---------- ------- ----------- ------- -------------- ---------
-; CHECK-PRETTY:        SRV     f32         buf      T0      t0        unbounded
+; CHECK-PRETTY:        SRV     f32         buf      T0      t7        unbounded
 ; CHECK-PRETTY:        SRV    byte         r/o      T1      t8,space1         1
 ; CHECK-PRETTY:        SRV  struct         r/o      T2      t2,space4         1
 ; CHECK-PRETTY:        SRV     u32         buf      T3      t3,space5        24
@@ -18,44 +18,45 @@ define void @test_buffers() {
   ; RWBuffer<float4> Buf : register(u5, space3)
   %typed0 = call target("dx.TypedBuffer", <4 x float>, 1, 0, 0)
               @llvm.dx.handle.fromBinding.tdx.TypedBuffer_v4f32_1_0_0(
-                  i32 3, i32 5, i32 1, i32 4, i1 false)
-  ; CHECK: call %dx.types.Handle @dx.op.createHandle(i32 57, i8 1, i32 1, i32 4, i1 false)
+                  i32 3, i32 5, i32 1, i32 0, i1 false)
+  ; CHECK: call %dx.types.Handle @dx.op.createHandle(i32 57, i8 1, i32 1, i32 5, i1 false)
   ; CHECK-NOT: @llvm.dx.cast.handle
 
   ; RWBuffer<int> Buf : register(u7, space2)
   %typed1 = call target("dx.TypedBuffer", i32, 1, 0, 1)
       @llvm.dx.handle.fromBinding.tdx.TypedBuffer_i32_1_0_1t(
-          i32 2, i32 7, i32 1, i32 6, i1 false)
-  ; CHECK: call %dx.types.Handle @dx.op.createHandle(i32 57, i8 1, i32 0, i32 6, i1 false)
+          i32 2, i32 7, i32 1, i32 0, i1 false)
+  ; CHECK: call %dx.types.Handle @dx.op.createHandle(i32 57, i8 1, i32 0, i32 7, i1 false)
 
   ; Buffer<uint4> Buf[24] : register(t3, space5)
   ; Buffer<uint4> typed2 = Buf[4]
   ; Note that the index below is 3 + 4 = 7
   %typed2 = call target("dx.TypedBuffer", <4 x i32>, 0, 0, 0)
       @llvm.dx.handle.fromBinding.tdx.TypedBuffer_i32_0_0_0t(
-          i32 5, i32 3, i32 24, i32 7, i1 false)
+          i32 5, i32 3, i32 24, i32 4, i1 false)
   ; CHECK: call %dx.types.Handle @dx.op.createHandle(i32 57, i8 0, i32 3, i32 7, i1 false)
 
   ; struct S { float4 a; uint4 b; };
   ; StructuredBuffer<S> Buf : register(t2, space4)
   %struct0 = call target("dx.RawBuffer", {<4 x float>, <4 x i32>}, 0, 0)
       @llvm.dx.handle.fromBinding.tdx.RawBuffer_sl_v4f32v4i32s_0_0t(
-          i32 4, i32 2, i32 1, i32 10, i1 true)
-  ; CHECK: call %dx.types.Handle @dx.op.createHandle(i32 57, i8 0, i32 2, i32 10, i1 true)
+          i32 4, i32 2, i32 1, i32 0, i1 true)
+  ; CHECK: call %dx.types.Handle @dx.op.createHandle(i32 57, i8 0, i32 2, i32 2, i1 true)
 
   ; ByteAddressBuffer Buf : register(t8, space1)
   %byteaddr0 = call target("dx.RawBuffer", i8, 0, 0)
       @llvm.dx.handle.fromBinding.tdx.RawBuffer_i8_0_0t(
-          i32 1, i32 8, i32 1, i32 12, i1 false)
-  ; CHECK: call %dx.types.Handle @dx.op.createHandle(i32 57, i8 0, i32 1, i32 12, i1 false)
+          i32 1, i32 8, i32 1, i32 0, i1 false)
+  ; CHECK: call %dx.types.Handle @dx.op.createHandle(i32 57, i8 0, i32 1, i32 8, i1 false)
 
-  ; Buffer<float4> Buf[] : register(t0)
+  ; Buffer<float4> Buf[] : register(t7)
   ; Buffer<float4> typed3 = Buf[ix]
   %typed3_ix = call i32 @some_val()
   %typed3 = call target("dx.TypedBuffer", <4 x float>, 0, 0, 0)
       @llvm.dx.handle.fromBinding.tdx.TypedBuffer_v4f32_0_0_0t(
-          i32 0, i32 0, i32 -1, i32 %typed3_ix, i1 false)
-  ; CHECK: call %dx.types.Handle @dx.op.createHandle(i32 57, i8 0, i32 0, i32 %typed3_ix, i1 false)
+          i32 0, i32 7, i32 -1, i32 %typed3_ix, i1 false)
+  ; CHECK: %[[IX:.*]] = add i32 %typed3_ix, 7
+  ; CHECK: call %dx.types.Handle @dx.op.createHandle(i32 57, i8 0, i32 0, i32 %[[IX]], i1 false)
 
   ret void
 }

diff  --git a/llvm/test/CodeGen/DirectX/CreateHandleFromBinding.ll b/llvm/test/CodeGen/DirectX/CreateHandleFromBinding.ll
index bce324509184be..aa143dfa8211d0 100644
--- a/llvm/test/CodeGen/DirectX/CreateHandleFromBinding.ll
+++ b/llvm/test/CodeGen/DirectX/CreateHandleFromBinding.ll
@@ -3,7 +3,7 @@
 
 ; CHECK-PRETTY:       Type  Format         Dim      ID      HLSL Bind     Count
 ; CHECK-PRETTY: ---------- ------- ----------- ------- -------------- ---------
-; CHECK-PRETTY:        SRV     f32         buf      T0      t0        unbounded
+; CHECK-PRETTY:        SRV     f32         buf      T0      t7        unbounded
 ; CHECK-PRETTY:        SRV    byte         r/o      T1      t8,space1         1
 ; CHECK-PRETTY:        SRV  struct         r/o      T2      t2,space4         1
 ; CHECK-PRETTY:        SRV     u32         buf      T3      t3,space5        24
@@ -18,15 +18,15 @@ define void @test_bindings() {
   ; RWBuffer<float4> Buf : register(u5, space3)
   %typed0 = call target("dx.TypedBuffer", <4 x float>, 1, 0, 0)
               @llvm.dx.handle.fromBinding.tdx.TypedBuffer_v4f32_1_0_0(
-                  i32 3, i32 5, i32 1, i32 4, i1 false)
-  ; 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)
+                  i32 3, i32 5, i32 1, i32 0, i1 false)
+  ; 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)
   ; CHECK: call %dx.types.Handle @dx.op.annotateHandle(i32 216, %dx.types.Handle [[BUF0]], %dx.types.ResourceProperties { i32 4106, i32 1033 })
 
   ; RWBuffer<int> Buf : register(u7, space2)
   %typed1 = call target("dx.TypedBuffer", i32, 1, 0, 1)
       @llvm.dx.handle.fromBinding.tdx.TypedBuffer_i32_1_0_0t(
-          i32 2, i32 7, i32 1, i32 6, i1 false)
-  ; 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)
+          i32 2, i32 7, i32 1, i32 0, i1 false)
+  ; 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)
   ; CHECK: call %dx.types.Handle @dx.op.annotateHandle(i32 216, %dx.types.Handle [[BUF1]], %dx.types.ResourceProperties { i32 4106, i32 260 })
 
   ; Buffer<uint4> Buf[24] : register(t3, space5)
@@ -34,7 +34,7 @@ define void @test_bindings() {
   ; Note that the index below is 3 + 4 = 7
   %typed2 = call target("dx.TypedBuffer", <4 x i32>, 0, 0, 0)
       @llvm.dx.handle.fromBinding.tdx.TypedBuffer_i32_0_0_0t(
-          i32 5, i32 3, i32 24, i32 7, i1 false)
+          i32 5, i32 3, i32 24, i32 4, i1 false)
   ; 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)
   ; CHECK: call %dx.types.Handle @dx.op.annotateHandle(i32 216, %dx.types.Handle [[BUF2]], %dx.types.ResourceProperties { i32 10, i32 1029 })
 
@@ -42,24 +42,25 @@ define void @test_bindings() {
   ; StructuredBuffer<S> Buf : register(t2, space4)
   %struct0 = call target("dx.RawBuffer", {<4 x float>, <4 x i32>}, 0, 0)
       @llvm.dx.handle.fromBinding.tdx.RawBuffer_sl_v4f32v4i32s_0_0t(
-          i32 4, i32 2, i32 1, i32 10, i1 true)
-  ; 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)
+          i32 4, i32 2, i32 1, i32 0, i1 true)
+  ; 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)
   ; CHECK: = call %dx.types.Handle @dx.op.annotateHandle(i32 216, %dx.types.Handle [[BUF3]], %dx.types.ResourceProperties { i32 1036, i32 32 })
 
   ; ByteAddressBuffer Buf : register(t8, space1)
   %byteaddr0 = call target("dx.RawBuffer", i8, 0, 0)
       @llvm.dx.handle.fromBinding.tdx.RawBuffer_i8_0_0t(
-          i32 1, i32 8, i32 1, i32 12, i1 false)
-  ; 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)
+          i32 1, i32 8, i32 1, i32 0, i1 false)
+  ; 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)
   ; CHECK: call %dx.types.Handle @dx.op.annotateHandle(i32 216, %dx.types.Handle [[BUF4]], %dx.types.ResourceProperties { i32 11, i32 0 })
 
-  ; Buffer<float4> Buf[] : register(t0)
+  ; Buffer<float4> Buf[] : register(t7)
   ; Buffer<float4> typed3 = Buf[ix]
   %typed3_ix = call i32 @some_val()
   %typed3 = call target("dx.TypedBuffer", <4 x float>, 0, 0, 0)
       @llvm.dx.handle.fromBinding.tdx.TypedBuffer_v4f32_0_0_0t(
-          i32 0, i32 0, i32 -1, i32 %typed3_ix, i1 false)
-  ; 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)
+          i32 0, i32 7, i32 -1, i32 %typed3_ix, i1 false)
+  ; CHECK: %[[IX:.*]] = add i32 %typed3_ix, 7
+  ; 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)
   ; CHECK: call %dx.types.Handle @dx.op.annotateHandle(i32 216, %dx.types.Handle [[BUF5]], %dx.types.ResourceProperties { i32 10, i32 1033 })
 
   ret void


        


More information about the cfe-commits mailing list