使用 TableGen 表示的 DXIL 操作规范¶
简介¶
DirectXShaderCompiler 除其他信息外,还在 hctdb.py 中封装了各种 DXIL 操作。DXIL 操作以以下 两种方式之一表示
使用 LLVM 指令。
使用 LLVM 外部函数。这些在 LLVM IR 中表示如下
“标准” LLVM 内联函数(例如,
llvm.sin.*
)和HLSL 内联函数(在
llvm/include/llvm/IR/IntrinsicsDirectX.td
中定义为 LLVM 内联函数,例如,llvm.dx.*
)
在本文档中,这些统称为 LLVM 内联函数。
以下是 DXIL Ops 属性的完整列表,以及在 hctdb.py
中使用的相应字段名称。DXIL Op 由一组关联的属性表示。这些属性在 DXIL 后端 Pass 以及其他使用场景(如验证、DXIL 读取器等)中使用。
DXIL 后端 Pass 中使用的属性
操作名称 (
dxil_op
)记录操作的字符串 (
doc
) - 这不是严格必需的,但包含在内是为了提高可读性和操作文档的可读性。映射到操作的通用或 HLSL 特定的内联函数 (
llvm_name
)。唯一整数 ID (
dxil_opid
)操作类,表示操作的名称和函数签名 (
dxil_class
)。此字符串是 DXIL Op 函数名称的组成部分,并以格式dx.op.<class-name>.<overload-type>
构建。根据与驱动程序的现有约定,每个 DXIL Op 调用目标函数名称都必须符合此格式。操作的有效重载类型列表 (
oload_types
)。支持操作所需的 DXIL 版本。
所需的最低着色器模型 (
shader_model
)。链接器转换所需的最低着色器模型 (
shader_model_translated
)适用于的着色器阶段列表 (
shader_stages
),如果适用于所有阶段,则为空。操作的内存访问属性 (
fn_attr
)。指示操作是否为以下类型的布尔属性
某种导数 (
is_derivative
)需要梯度计算 (
is_gradient
)是采样器反馈 (
is_feedback
)需要在波内、跨通道功能中使用 (
is_wave
)要求其所有输入在波中都是统一的 (
requires_uniform_inputs
)。是屏障操作 (
is_barrier
)。
动机¶
DXIL 后端 Pass 依赖于 DXIL 操作的各种属性。例如,DXILOpLowering
Pass 将需要诸如 LLVM 内联函数要降低到的 DXIL 操作的信息,以及有效的重载和参数类型等。TableGen 文件 - llvm/lib/Target/DirectX/DXIL.td
- 用于通过指定上面列出的属性来表示 DXIL 操作。DXIL.td
旨在成为 DXIL 操作的单一参考源,主要用于在 llvm-project
仓库中实现 DXIL 后端中的 Pass - 类似于 DirectXShadeCompiler
仓库的 hctdb.py
。但是,当前设计不打算封装 hctdb.py
中存在的各种验证规则,但这些规则与 DXIL 操作无关。它需要具有丰富的表示能力,TableGen 后端(如 DXILEmitter
)可以依赖这些能力。此外,DXIL Op 规范应该易于阅读和理解。
本文档提供了将 DXIL Ops 规范设计为 TableGen 类 DXILOp
的方案,方法是指定上面确定的属性。
DXIL 操作规范¶
DXIL 操作使用 TableGen 类 DXILOp
表示。DXIL 操作属性指定为 DXILOp
类的字段,如下所述。
每个 DXIL 操作都表示为 TableGen 记录。每个记录的名称表示操作名称。
操作的文档字符串。
映射到操作的 LLVM 内联函数表示为在 Intrinsics.td 中定义的
Intrinsic
。唯一操作 ID 由整数表示。
DXIL 操作类表示如下
// Abstraction of DXIL Operation class. class DXILOpClass;
具体的操作记录,例如
unary
,通过从DXILOpClass
继承来定义。定义了一组类型名称,用于表示返回类型和参数类型,所有类型名称都从
DXILOpParamType
继承。这些类型名称表示简单类型(如int32Ty
)、DXIL 类型(如dx.types.Handle
)以及特殊的overloadTy
类型,该类型可以是Overloads
允许的任何类型,如下所述。操作返回类型表示为
DXILOpParamType
,参数表示为相同的列表。没有返回值的操作应将VoidTy
指定为其返回类型。基于 DXIL 版本的有效操作重载类型指定为
Overloads
记录的列表。Overloads
类的表示将在后面的章节中描述。基于 DXIL 版本的有效着色器阶段指定为
Stages
记录的列表。Stages
类的表示将在后面的章节中描述。DXIL 操作的各种属性表示为
Attributes
类记录的list
。Attributes
类的表示将在后面的章节中描述。
DXIL 特有的类型¶
本文档中使用的类型表示法,即 <size>Ty
,对应于 LLVM 类型 llvm_<size>_ty
的 TableGen 记录。除了上面描述的 overloadTy
之外,resRetF32Ty
用于表示资源返回类型,handleTy
用于表示句柄类型。
DXIL 操作规范¶
DXIL 操作由以下 TableGen 类表示,该类封装了上面描述的其属性的各种 TableGen 表示形式。
// Abstraction DXIL Operation
class DXILOp<int opcode, DXILOpClass opclass> {
// A short description of the operation
string Doc = "";
// Opcode of DXIL Operation
int OpCode = opcode;
// Class of DXIL Operation.
DXILOpClass OpClass = opclass;
// LLVM Intrinsic DXIL Operation maps to
Intrinsic LLVMIntrinsic = ?;
// Result type of the op.
DXILOpParamType result;
// List of argument types of the op. Default to 0 arguments.
list<DXILOpParamType> arguments = [];
// List of valid overload types predicated by DXIL version
list<Overloads> overloads;
// List of valid shader stages predicated by DXIL version
list<Stages> stages;
// List of valid attributes predicated by DXIL version
list<Attributes> attributes = [];
}
版本规范¶
DXIL 版本用于指定各种依赖于版本的操作属性,而不是着色器模型版本。
封装 Major
和 Minor
版本号的 Version
类定义如下
// Abstract class to represent major and minor version values
class Version<int major, int minor> {
int Major = major;
int Minor = minor;
}
有效 DXIL 版本的具体表示形式定义如下
// Definition of DXIL Version 1.0 - 1.8
foreach i = 0...8 in {
def DXIL1_#i : Version<1, i>;
}
着色器阶段规范¶
各种着色器阶段,如 compute
、pixel
、vertex
等,表示如下
// Shader stages
class DXILShaderStage;
def compute : DXILShaderStage;
def pixel : DXILShaderStage;
def vertex : DXILShaderStage;
...
着色器属性规范¶
各种操作内存访问和布尔属性,如 ReadNone
、IsWave
等,表示如下
class DXILAttribute;
def ReadOnly : DXILOpAttributes;
def ReadNone : DXILOpAttributes;
def IsWave : DXILOpAttributes;
...
版本化属性规范¶
DXIL 操作属性,如有效的重载类型、着色器阶段和属性,都依赖于 DXIL 版本。这些属性表示为版本化属性的列表。
重载类型规范¶
class DXILOp
的 overloads
字段用于表示基于 DXIL 版本的有效操作重载,表示为以下类的记录列表
class Overloads<Version minver, list<DXILOpParamType> ols> {
Version dxil_version = minver;
list<DXILOpParamType> overload_types = ols;
}
以下是 DXIL1_0
和 DXIL1_2
的有效重载类型的规范示例。
overloads = [
Overloads<DXIL1_0, [halfTy, floatTy]>,
Overloads<DXIL1_2, [halfTy, floatTy, doubleTy]>
];
空列表表示操作不支持任何重载类型。
阶段规范¶
class DXILOp
的 stages
字段用于表示基于 DXIL 版本的有效操作阶段,表示为以下类的记录列表
class Stages<Version minver, list<DXILShaderStage> sts> {
Version dxil_version = minver;
list<DXILShaderStage> shader_stages = sts;
}
以下是 DXIL1_0
、DXIL1_2
、DXIL1_4
和 DXIL1_6
的有效阶段的规范示例。
stages = [
Stages<DXIL1_0, [compute, pixel]>,
Stages<DXIL1_2, [compute, pixel, mesh]>,
Stages<DXIL1_4, [all_stages]>,
Stages<DXIL1_6, [removed]>
];
除了标准着色器阶段之外,还定义了以下两个伪阶段记录。
all_stages
表示该操作在指定的 DXIL 版本及更高版本中的所有阶段均有效。removed
表示在指定的 DXIL 版本及更高版本中取消对该操作的支持。
需要指定非空的支持阶段列表。如果某个操作在所有 DXIL 版本和所有阶段都受支持,则需要将其指定为
stages = [Stages<DXIL1_0, [all_stages]>];
属性规范¶
class DXILOp
的 attributes
字段用于表示基于 DXIL 版本的有效操作属性,表示为以下类的记录列表
class Attributes<MinVersion minver, list<DXILAttribute> attrs> {
MinVersion dxil_version = ver;
list<DXILAttribute> attributes = attrs;
}
以下是 DXIL1_0
的有效属性的规范示例。
attributes = [Attributes<DXIL1_0, [ReadNone]];
attributes
的空列表表示没有操作属性。
多版本化属性的解释¶
每个版本化属性都声明指定的重载类型、阶段或属性记录对于预测的 DXIL 版本有效。仅应用对应于最新最小 DXIL 版本的属性。请注意,如上面的示例所示,在更高 DXIL 版本中仍然有效的任何重载类型、阶段或属性都需要完整指定。例如,考虑以下有效重载类型的规范
overloads = [
Overloads<DXIL1_0, [halfTy, floatTy]>,
Overloads<DXIL1_2, [halfTy, floatTy, doubleTy]>
];
它指定重载类型 halfTy
和 floatTy
对于 DXIL 版本 1.0 及更高版本有效。它还指定在 DXIL 版本 1.2 及更高版本中额外支持 doubleTy
。
这提供了独立于列表中其他版本化规范指定属性的灵活性。
DXIL 操作规范示例¶
以下示例说明了一些 DXIL Ops 的规范。
Sin
操作 - 在所有 DXIL 版本和所有阶段都有效的操作,并且具有基于 DXIL 版本的有效重载类型。
def Sin : DXILOp<13, unary> {
let Doc = "Returns sine(theta) for theta in radians.";
let LLVMIntrinsic = int_sin;
let result = overloadTy;
let arguments = [overloadTy];
let overloads = [Overloads<DXIL1_0, [halfTy, floatTy]>];
let stages = [Stages<DXIL1_0, [all_stages]>];
let attributes = [Attributes<DXIL1_0, [ReadNone]>];
}
FlattenedThreadIdInGroup
- 没有参数、没有重载类型以及基于 DXIL 版本的有效阶段和属性的操作。
def FlattenedThreadIdInGroup : DXILOp<96, flattenedThreadIdInGroup> {
let Doc = "Provides a flattened index for a given thread within a given "
"group (SV_GroupIndex)";
let LLVMIntrinsic = int_dx_flattened_thread_id_in_group;
let result = i32Ty;
let stages = [Stages<DXIL1_0, [compute, mesh, amplification, node]>];
let attributes = [Attributes<DXIL1_0, [ReadNone]>];
}
RawBufferStore
- 具有 void
返回类型、基于 DXIL 版本的有效重载类型以及在所有 DXIL 版本和阶段都有效的操作。
def RawBufferStore : DXILOp<140, rawBufferStore> {
let Doc = "Writes to a RWByteAddressBuffer or RWStructuredBuffer.";
let result = voidTy;
let arguments = [dxil_resource_ty, i32Ty, i32Ty, overloadTy,
overloadTy, overloadTy, overloadTy, i8Ty, i32Ty];
let overloads = [
Overloads<DXIL1_2, [halfTy, floatTy, i16Ty, i32Ty]>,
Overloads<DXIL1_3>,[halfTy, floatTy, doubleTy,
i16Ty, i32Ty, i64Ty]>
];
let stages = [Stages<DXIL1_2, all_stages>];
let attributes = [Attributes<DXIL1_0, [ReadOnly]>];
}
DerivCoarseX
- 没有重载类型和基于 DXIL 版本的阶段的操作。
def DerivCoarseX : DXILOp<83, unary> {
let doc = "Computes the rate of change per stamp in x direction.";
let LLVMIntrinsic = int_dx_ddx;
let result = overloadTy;
let arguments = [overloadTy];
let stages = [
Stages<DXIL1_0, [library, pixel]>,
Stages<DXIL1_6, [library, pixel, amplification, compute, mesh]>
];
let attributes = [Attributes<DXIL1_0, [ReadNone]>];
}
CreateHandle
- 没有重载类型、没有关联的 LLVMIntrinsic
以及基于 DXIL 版本的阶段的操作。
def CreateHandle : DXILOp<57, createHandle> {
let doc = "Creates the handle to a resource";
let result = i32Ty;
let arguments = [i8Ty, i32Ty, i32Ty, i1Ty];
let stages = [
Stages<DXIL1_0, [all_stages]>,
Stages<DXIL1_6, [removed]
];
let attributes = [Attributes<DXIL1_0, [ReadOnly]>];
}
Sample
- 具有基于 DXIL 版本的有效重载类型、阶段和属性的操作。
def Sample : DXILOp<60, sample> {
let Doc = "Samples a texture";
let LLVMIntrinsic = int_dx_sample;
let result = resRetF32Ty;
let arguments = [handleTy, handleTy, floatTy, floatTy, floatTy, floatTy,
i32Ty, i32Ty, i32Ty, floatTy];
let overloads = [Overloads<DXIL1_0, [halfTy, floatTy, i16Ty, i32Ty]>];
let stages = [
Stages<DXIL1_0, [library, pixel]>,
Stages<DXIL1_6, [library, pixel, amplification, compute, mesh]>
];
let attributes = [Attributes<DXIL1_0, [ReadOnly]>];
}
总结¶
本文档概述了 DXIL.td
中 DXIL Ops 的可读且可维护的 TableGen 规范的设计,旨在作为 TableGen 后端(如 DXILEmitter
)的单一参考源,这些后端生成 DXIL 后端 Pass 中使用的 C++ 表示形式。