Skip to content

Commit 78d9816

Browse files
authored
[DirectX] Add llvm.dx.resource.getdimensions.x intrinsic and lowering to DXIL (#161753)
Introduces LLVM intrinsic `llvm.dx.resource.getdimensions.x` and its lowering to DXIL op `op.dx.getDimensions`. The intrinsic will be used to implement `GetDimension` for buffers. The lowering is using `undef` value since it is required by the DXIL format which is based on LLVM 3.7. Proposal update: llvm/wg-hlsl#350 Closes #112982
1 parent 333c758 commit 78d9816

File tree

7 files changed

+152
-0
lines changed

7 files changed

+152
-0
lines changed

llvm/docs/DirectX/DXILResources.rst

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -746,3 +746,92 @@ Examples:
746746
@llvm.dx.resource.load.cbufferrow.8(
747747
target("dx.CBuffer", target("dx.Layout", {i16}, 2, 0)) %buffer,
748748
i32 %index)
749+
750+
Resource dimensions
751+
-------------------
752+
753+
*relevant types: Textures and Buffer*
754+
755+
The `getDimensions`_ DXIL operation returns the dimensions of a texture or
756+
buffer resource. It returns a `Dimensions`_ type, which is a struct
757+
containing four ``i32`` values. The values in the struct represent the size
758+
of each dimension of the resource, and when aplicable the number of array
759+
elements or number of samples. The mapping is defined in the
760+
`getDimensions`_ documentation.
761+
762+
The LLVM IR representation of this operation has several forms
763+
depending on the resource type and the specific ``getDimensions`` query.
764+
The intrinsics return a scalar or anonymous struct with up to 4 `i32`
765+
elements. The intrinsic names include suffixes to indicate the number of
766+
elements in the return value. The suffix `.x` indicates a single `i32`
767+
return value, `.xy` indicates a struct with two `i32` values, and `.xyz`
768+
indicates a struct with three `i32` values.
769+
770+
Intrinsics representing queries on multisampled texture resources include
771+
`.ms.` in their name and their return value includes an additional `i32` for
772+
the number of samples.
773+
774+
Intrinsics with `mip_level` argument and `.levels.` in their name are used
775+
for texture resources with multiple MIP levels. Their return
776+
struct includes an additional `i32` for the number of levels the resource has.
777+
778+
.. code-block:: llvm
779+
780+
i32 @llvm.dx.resource.getdimensions.x( target("dx.*") handle )
781+
{i32, i32} @llvm.dx.resource.getdimensions.xy( target("dx.*") handle )
782+
{i32, i32, i32} @llvm.dx.resource.getdimensions.xyz( target("dx.*") handle )
783+
{i32, i32} @llvm.dx.resource.getdimensions.levels.x( target("dx.*") handle, i32 mip_level )
784+
{i32, i32, i32} @llvm.dx.resource.getdimensions.levels.xy( target("dx.*") handle, i32 mip_level )
785+
{i32, i32, i32, i32} @llvm.dx.resource.getdimensions.levels.xyz( target("dx.*") handle, i32 mip_level )
786+
{i32, i32, i32} @llvm.dx.resource.getdimensions.ms.xy( target("dx.*") handle )
787+
{i32, i32, i32, i32} @llvm.dx.resource.getdimensions.ms.xyz( target("dx.*") handle )
788+
789+
.. list-table:: ``@llvm.dx.resource.getdimensions.*``
790+
:header-rows: 1
791+
792+
* - Argument
793+
-
794+
- Type
795+
- Description
796+
* - Return value
797+
-
798+
- `i32`, `{i32, i32}`, `{i32, i32, i32}`, or `{i32, i32, i32, i32}`
799+
- Width, height, and depth of the resource (based on the specific suffix), and a number of levels or samples where aplicable.
800+
* - ``%handle``
801+
- 0
802+
- ``target(dx.*)``
803+
- Resource handle
804+
* - ``%mip_level``
805+
- 1
806+
- ``i32``
807+
- MIP level for the requested dimensions.
808+
809+
Examples:
810+
811+
.. code-block:: llvm
812+
813+
; RWBuffer<float4>
814+
%dim = call i32 @llvm.dx.resource.getdimensions.x(target("dx.TypedBuffer", <4 x float>, 1, 0, 0) %handle)
815+
816+
; Texture2D
817+
%0 = call {i32, i32} @llvm.dx.resource.getdimensions.xy(target("dx.Texture", ...) %tex2d)
818+
%tex2d_width = extractvalue {i32, i32} %0, 0
819+
%tex2d_height = extractvalue {i32, i32} %0, 1
820+
821+
; Texture2DArray with levels
822+
%1 = call {i32, i32, i32, i32} @llvm.dx.resource.getdimensions.levels.xyz(
823+
target("dx.Texture", ...) %tex2darray, i32 1)
824+
%tex2darray_width = extractvalue {i32, i32, i32, i32} %1, 0
825+
%tex2darray_height = extractvalue {i32, i32, i32, i32} %1, 1
826+
%tex2darray_elem_count = extractvalue {i32, i32, i32, i32} %1, 2
827+
%tex2darray_levels_count = extractvalue {i32, i32, i32, i32} %1, 3
828+
829+
; Texture2DMS
830+
%2 = call {i32, i32, i32} @llvm.dx.resource.getdimensions.ms.xy(
831+
target("dx.Texture", ...) %tex2dms)
832+
%tex2dms_width = extractvalue {i32, i32, i32} %2, 0
833+
%tex2dms_height = extractvalue {i32, i32, i32} %2, 1
834+
%tex2dms_samples_count = extractvalue {i32, i32, i32} %2, 2
835+
836+
.. _Dimensions: https://github.com/microsoft/DirectXShaderCompiler/blob/main/docs/DXIL.rst#resource-operation-return-types
837+
.. _getDimensions: https://github.com/microsoft/DirectXShaderCompiler/blob/main/docs/DXIL.rst#getdimensions

llvm/include/llvm/IR/IntrinsicsDirectX.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,9 @@ def int_dx_resource_updatecounter
7777
: DefaultAttrsIntrinsic<[llvm_i32_ty], [llvm_any_ty, llvm_i8_ty],
7878
[IntrInaccessibleMemOrArgMemOnly]>;
7979

80+
def int_dx_resource_getdimensions_x
81+
: DefaultAttrsIntrinsic<[llvm_i32_ty], [llvm_any_ty], [IntrReadMem]>;
82+
8083
// Cast between target extension handle types and dxil-style opaque handles
8184
def int_dx_resource_casthandle : Intrinsic<[llvm_any_ty], [llvm_any_ty]>;
8285

llvm/include/llvm/IR/IntrinsicsSPIRV.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,9 @@ def int_spv_rsqrt : DefaultAttrsIntrinsic<[LLVMMatchType<0>], [llvm_anyfloat_ty]
167167
: DefaultAttrsIntrinsic<[llvm_i32_ty], [llvm_any_ty, llvm_i8_ty],
168168
[IntrInaccessibleMemOrArgMemOnly]>;
169169

170+
def int_spv_resource_getdimensions_x
171+
: DefaultAttrsIntrinsic<[llvm_i32_ty], [llvm_any_ty], [IntrReadMem]>;
172+
170173
def int_spv_resource_getpointer
171174
: DefaultAttrsIntrinsic<[llvm_anyptr_ty], [llvm_any_ty, llvm_i32_ty],
172175
[IntrNoMem]>;

llvm/lib/Target/DirectX/DXIL.td

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ def ResBindTy : DXILOpParamType;
5757
def ResPropsTy : DXILOpParamType;
5858
def SplitDoubleTy : DXILOpParamType;
5959
def BinaryWithCarryTy : DXILOpParamType;
60+
def DimensionsTy : DXILOpParamType;
6061

6162
class DXILOpClass;
6263

@@ -901,6 +902,13 @@ def CheckAccessFullyMapped : DXILOp<71, checkAccessFullyMapped> {
901902
let attributes = [Attributes<DXIL1_0, [ReadOnly]>];
902903
}
903904

905+
def GetDimensions : DXILOp<72, getDimensions> {
906+
let Doc = "gets the dimensions of a buffer or texture";
907+
let arguments = [HandleTy, Int32Ty];
908+
let result = DimensionsTy;
909+
let stages = [Stages<DXIL1_0, [all_stages]>];
910+
}
911+
904912
def Barrier : DXILOp<80, barrier> {
905913
let Doc = "inserts a memory barrier in the shader";
906914
let intrinsics = [

llvm/lib/Target/DirectX/DXILOpBuilder.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,12 @@ static StructType *getBinaryWithCarryType(LLVMContext &Context) {
261261
return StructType::create({Int32Ty, Int1Ty}, "dx.types.i32c");
262262
}
263263

264+
static StructType *getDimensionsType(LLVMContext &Ctx) {
265+
Type *Int32Ty = Type::getInt32Ty(Ctx);
266+
return getOrCreateStructType("dx.types.Dimensions",
267+
{Int32Ty, Int32Ty, Int32Ty, Int32Ty}, Ctx);
268+
}
269+
264270
static Type *getTypeFromOpParamType(OpParamType Kind, LLVMContext &Ctx,
265271
Type *OverloadTy) {
266272
switch (Kind) {
@@ -318,6 +324,8 @@ static Type *getTypeFromOpParamType(OpParamType Kind, LLVMContext &Ctx,
318324
return getSplitDoubleType(Ctx);
319325
case OpParamType::BinaryWithCarryTy:
320326
return getBinaryWithCarryType(Ctx);
327+
case OpParamType::DimensionsTy:
328+
return getDimensionsType(Ctx);
321329
}
322330
llvm_unreachable("Invalid parameter kind");
323331
return nullptr;

llvm/lib/Target/DirectX/DXILOpLowering.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -627,6 +627,28 @@ class OpLowerer {
627627
});
628628
}
629629

630+
[[nodiscard]] bool lowerGetDimensionsX(Function &F) {
631+
IRBuilder<> &IRB = OpBuilder.getIRB();
632+
Type *Int32Ty = IRB.getInt32Ty();
633+
634+
return replaceFunction(F, [&](CallInst *CI) -> Error {
635+
IRB.SetInsertPoint(CI);
636+
Value *Handle =
637+
createTmpHandleCast(CI->getArgOperand(0), OpBuilder.getHandleType());
638+
Value *Undef = UndefValue::get(Int32Ty);
639+
640+
Expected<CallInst *> OpCall = OpBuilder.tryCreateOp(
641+
OpCode::GetDimensions, {Handle, Undef}, CI->getName(), Int32Ty);
642+
if (Error E = OpCall.takeError())
643+
return E;
644+
Value *Dim = IRB.CreateExtractValue(*OpCall, 0);
645+
646+
CI->replaceAllUsesWith(Dim);
647+
CI->eraseFromParent();
648+
return Error::success();
649+
});
650+
}
651+
630652
[[nodiscard]] bool lowerGetPointer(Function &F) {
631653
// These should have already been handled in DXILResourceAccess, so we can
632654
// just clean up the dead prototype.
@@ -934,6 +956,9 @@ class OpLowerer {
934956
case Intrinsic::dx_resource_updatecounter:
935957
HasErrors |= lowerUpdateCounter(F);
936958
break;
959+
case Intrinsic::dx_resource_getdimensions_x:
960+
HasErrors |= lowerGetDimensionsX(F);
961+
break;
937962
case Intrinsic::ctpop:
938963
HasErrors |= lowerCtpopToCountBits(F);
939964
break;
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
; RUN: opt -S -dxil-op-lower %s | FileCheck %s
2+
3+
target triple = "dxil-pc-shadermodel6.6-compute"
4+
5+
define i32 @test_getdimensions_no_mips() {
6+
; CHECK: %[[HANDLE:.*]] = call %dx.types.Handle @dx.op.createHandleFromBinding(i32 217,
7+
; CHECK-NEXT: %[[ANNOT_HANDLE:.*]] = call %dx.types.Handle @dx.op.annotateHandle(i32 216, %dx.types.Handle %[[HANDLE]]
8+
%handle = call target("dx.TypedBuffer", <4 x float>, 0, 0, 0) @llvm.dx.resource.handlefrombinding(i32 0, i32 0, i32 1, i32 0, ptr null)
9+
10+
; CHECK-NEXT: %[[RETVAL:.*]] = call %dx.types.Dimensions @dx.op.getDimensions(i32 72, %dx.types.Handle %[[ANNOT_HANDLE]], i32 undef)
11+
; CHECK-NEXT: %[[DIM:.*]] = extractvalue %dx.types.Dimensions %[[RETVAL]], 0
12+
%1 = call i32 @llvm.dx.resource.getdimensions.x(target("dx.TypedBuffer", <4 x float>, 0, 0, 0) %handle)
13+
14+
; CHECK-NEXT: ret i32 %[[DIM]]
15+
ret i32 %1
16+
}

0 commit comments

Comments
 (0)