diff --git a/clang/test/CodeGenHLSL/resource-bindings.hlsl b/clang/test/CodeGenHLSL/resource-bindings.hlsl new file mode 100644 index 0000000000000..bfec90e1871f8 --- /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 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 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 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 T3S0 : register(t3); diff --git a/llvm/docs/DirectX/DXILResources.rst b/llvm/docs/DirectX/DXILResources.rst index ad8ede9c59fbf..dcec9611d8aaa 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 Global[3] : register(u6, space5) + ; RWBuffer 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 9f124394363a3..047c5412e1f3a 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 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 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 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 BindArgs{ResBind, CI->getArgOperand(3), - CI->getArgOperand(4)}; + std::array BindArgs{ResBind, IndexOp, CI->getArgOperand(4)}; Expected 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 40b3b2c712272..234d4e035bf1d 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 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 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 Buf[24] : register(t3, space5) ; Buffer 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 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 Buf[] : register(t0) + ; Buffer Buf[] : register(t7) ; Buffer 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 bce324509184b..aa143dfa8211d 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 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 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 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 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 Buf[] : register(t0) + ; Buffer Buf[] : register(t7) ; Buffer 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