| ============================================================== | |
| Specification of DXIL Operations using TableGen Representation | |
| ============================================================== | |
| .. contents:: | |
| :local: | |
| .. toctree | |
| :hidden | |
| Introduction | |
| ============ | |
| `DirectXShaderCompiler <https://github.com/microsoft/DirectXShaderCompiler>`_ | |
| encapsulates, among other information, various DXIL Operations in | |
| `hctdb.py <https://github.com/microsoft/DirectXShaderCompiler/blob/main/utils/hct/hctdb.py>`_. | |
| DXIL Operations are represented in one of the following `two ways | |
| <https://github.com/microsoft/DirectXShaderCompiler/blob/main/docs/DXIL.rst#operations>`_: | |
| #. Using LLVM instructions. | |
| #. Using LLVM External functions. These are represented in LLVM IR as follows: | |
| * "Standard" LLVM intrinsics (e.g., ``llvm.sin.*``) and | |
| * HLSL intrinsics (defined as LLVM intrinsics in ``llvm/include/llvm/IR/IntrinsicsDirectX.td``, e.g., ``llvm.dx.*``) | |
| These are collectively referred to as `LLVM Intrinsics` in this note. | |
| Following is the complete list of properties of DXIL Ops with the corresponding field name | |
| as used in ``hctdb.py``. A DXIL Op is represented by a set of associated properties. These | |
| are consumed in DXIL backend passes as well as in other usage scenarios such as validation, | |
| DXIL reader, etc. | |
| A. Properties consumed in DXIL backend passes | |
| 1. Name of operation (``dxil_op``) | |
| 2. A string that documents the operation (``doc``) - This is not strictly necessary but is included | |
| for readability and documentation of the operation. | |
| 3. The generic or HLSL-specific intrinsic that maps to the operation (``llvm_name``). | |
| 4. Unique Integer ID (``dxil_opid``) | |
| 5. Operation Class signifying the name and function signature of the operation (``dxil_class``). | |
| This string is an integral part of the DXIL Op function name and is constructed in | |
| the format ``dx.op.<class-name>.<overload-type>``. Each DXIL Op call target function name | |
| is required to conform to this format per existing contract with the driver. | |
| 6. List of valid overload types for the operation (``oload_types``). | |
| 7. Required DXIL Version with support for the operation. | |
| 8. Required minimum Shader Model (``shader_model``). | |
| 9. Minimum shader model required with translation by linker (``shader_model_translated``) | |
| 10. List of shader stages applicable to (``shader_stages``), empty, if applicable to all stages. | |
| 11. Memory access attributes of the operation (``fn_attr``). | |
| 12. Boolean attributes of operation to indicate if it | |
| * is some kind of a derivative (``is_derivative``) | |
| * requires gradient calculation (``is_gradient``) | |
| * is a sampler feedback (``is_feedback``) | |
| * requires in-wave, cross-lane functionality (``is_wave``) | |
| * requires that all of its inputs are uniform across the wave (``requires_uniform_inputs``). | |
| * is a barrier operation (``is_barrier``). | |
| Motivation | |
| ========== | |
| DXIL backend passes depend on various properties of DXIL Operations. For example, ``DXILOpLowering`` | |
| pass will need information such as the DXIL operation an LLVM intrinsic is to be lowered to, | |
| along with valid overload and argument types etc. The TableGen file - | |
| ``llvm/lib/Target/DirectX/DXIL.td`` - is used to represent DXIL Operations | |
| by specifying their properties listed above. ``DXIL.td`` is designed to be the single source | |
| of reference of DXIL Operations primarily for the implementation of passes in DXIL backend in | |
| ``llvm-project`` repo - analogous to ``hctdb.py`` for ``DirectXShadeCompiler`` repo. However, | |
| the current design does not intend to encapsulate various validation rules, present in ``hctdb.py``, | |
| but do not pertain to DXIL Operations. It needs to have a rich representation capabilities that | |
| TableGen backends (such as ``DXILEmitter``) can rely on. Additionally, the DXIL Op specification | |
| should be easy to read and comprehend. | |
| This note provides the design of the specification DXIL Ops as TableGen class ``DXILOp`` | |
| by specifying its properties identified above. | |
| DXIL Operation Specification | |
| ============================ | |
| The DXIL Operation is represented using the TableGen class ``DXILOp``. The DXIL operation | |
| properties are specified as fields of the ``DXILOp`` class as described below. | |
| 1. Each DXIL Operation is represented as a TableGen record. The name of each of the records | |
| signifies operation name. | |
| 2. A documentation string for the operation. | |
| 3. The LLVM Intrinsic that maps to the operation is represented as ``Intrinsic`` defined in | |
| `Intrinsics.td <https://github.com/llvm/llvm-project/blob/main/llvm/include/llvm/IR/Intrinsics.td>`_. | |
| 4. The unique operation id is represented by an integer. | |
| 5. DXIL Operation Class is represented as follows | |
| .. code-block:: | |
| // Abstraction of DXIL Operation class. | |
| class DXILOpClass; | |
| Concrete operation records, such as ``unary`` are defined by inheriting from ``DXILOpClass``. | |
| 6. A set of type names are defined that represent return and argument types, | |
| which all inherit from ``DXILOpParamType``. These represent simple types | |
| like ``int32Ty``, DXIL types like ``dx.types.Handle``, and a special | |
| ``overloadTy`` which can be any type allowed by ``Overloads``, described | |
| below. | |
| 7. Operation return type is represented as a ``DXILOpParamType``, and arguments | |
| are represented as a list of the same. An operation with no return value | |
| shall specify ``VoidTy`` as its return. | |
| 8. Valid operation overload types predicated on DXIL version are specified as | |
| a list of ``Overloads`` records. Representation of ``Overloads`` | |
| class is described in a later section. | |
| 9. Valid shader stages predicated on DXIL version are specified as a list of | |
| ``Stages`` records. Representation of ``Stages`` class is | |
| described in a later section. | |
| 10. Various attributes of the DXIL Operation are represented as a ``list`` of | |
| ``Attributes`` class records. Representation of ``Attributes`` | |
| class is described in a later section. | |
| Types specific to DXIL | |
| ---------------------- | |
| Type notation used in this document viz., ``<size>Ty`` corresponds to TableGen records for | |
| LLVM types ``llvm_<size>_ty``. Apart from ``overloadTy`` described above, ``resRetF32Ty`` is | |
| used to denote resource return type and ``handleTy`` is used to denote handle type. | |
| Specification of DXIL Operation | |
| ================================ | |
| A DXIL Operation is represented by the following TableGen class that encapsulates the various | |
| TableGen representations of its properties described above. | |
| .. code-block:: | |
| // 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 = []; | |
| } | |
| Version Specification | |
| ===================== | |
| DXIL version is used to specify various version-dependent operation properties in | |
| place of Shader Model version. | |
| A ``Version`` class encapsulating ``Major`` and ``Minor`` version number is defined | |
| as follows: | |
| .. code-block:: | |
| // Abstract class to represent major and minor version values | |
| class Version<int major, int minor> { | |
| int Major = major; | |
| int Minor = minor; | |
| } | |
| Concrete representations of valid DXIL versions are defined as follows: | |
| .. code-block:: | |
| // Definition of DXIL Version 1.0 - 1.8 | |
| foreach i = 0...8 in { | |
| def DXIL1_#i : Version<1, i>; | |
| } | |
| Shader Stage Specification | |
| ========================== | |
| Various shader stages such as ``compute``, ``pixel``, ``vertex``, etc., are represented | |
| as follows | |
| .. code-block:: | |
| // Shader stages | |
| class DXILShaderStage; | |
| def compute : DXILShaderStage; | |
| def pixel : DXILShaderStage; | |
| def vertex : DXILShaderStage; | |
| ... | |
| Shader Attribute Specification | |
| ============================== | |
| Various operation memory access and boolean attributes such as ``ReadNone``, | |
| ``IsWave`` etc., are represented as follows | |
| .. code-block:: | |
| class DXILAttribute; | |
| def ReadOnly : DXILOpAttributes; | |
| def ReadNone : DXILOpAttributes; | |
| def IsWave : DXILOpAttributes; | |
| ... | |
| Versioned Property Specification | |
| ================================ | |
| DXIL Operation properties such as valid overload types, shader stages and | |
| attributes are predicated on DXIL version. These are represented as list of | |
| versioned properties. | |
| Overload Type Specification | |
| --------------------------- | |
| ``overloads`` field of ``class DXILOp`` is used to represent valid operation | |
| overloads predicated on DXIL version as list of records of the following class | |
| .. code-block:: | |
| class Overloads<Version minver, list<DXILOpParamType> ols> { | |
| Version dxil_version = minver; | |
| list<DXILOpParamType> overload_types = ols; | |
| } | |
| Following is an example specification of valid overload types for ``DXIL1_0`` and | |
| ``DXIL1_2``. | |
| .. code-block:: | |
| overloads = [ | |
| Overloads<DXIL1_0, [halfTy, floatTy]>, | |
| Overloads<DXIL1_2, [halfTy, floatTy, doubleTy]> | |
| ]; | |
| An empty list signifies that the operation supports no overload types. | |
| Stages Specification | |
| -------------------- | |
| ``stages`` field of ``class DXILOp`` is used to represent valid operation | |
| stages predicated on DXIL version as list of records of the following class | |
| .. code-block:: | |
| class Stages<Version minver, list<DXILShaderStage> sts> { | |
| Version dxil_version = minver; | |
| list<DXILShaderStage> shader_stages = sts; | |
| } | |
| Following is an example specification of valid stages for ``DXIL1_0``, | |
| ``DXIL1_2``, ``DXIL1_4`` and ``DXIL1_6``. | |
| .. code-block:: | |
| stages = [ | |
| Stages<DXIL1_0, [compute, pixel]>, | |
| Stages<DXIL1_2, [compute, pixel, mesh]>, | |
| Stages<DXIL1_4, [all_stages]>, | |
| Stages<DXIL1_6, [removed]> | |
| ]; | |
| The following two pseudo stage records in addition to standard shader stages | |
| are defined. | |
| 1. ``all_stages`` signifies that the operation is valid for all stages in the | |
| specified DXIL version and later. | |
| 2. ``removed`` signifies removal of support for the operation in the specified | |
| DXIL version and later. | |
| A non-empty list of supported stages is required to be specified. If an operation | |
| is supported in all DXIL versions and all stages it is required to be specified as | |
| .. code-block:: | |
| stages = [Stages<DXIL1_0, [all_stages]>]; | |
| Attribute Specification | |
| ----------------------- | |
| ``attributes`` field of ``class DXILOp`` is used to represent valid operation | |
| attributes predicated on DXIL version as list of records of the following class | |
| .. code-block:: | |
| class Attributes<MinVersion minver, list<DXILAttribute> attrs> { | |
| MinVersion dxil_version = ver; | |
| list<DXILAttribute> attributes = attrs; | |
| } | |
| Following is an example specification of valid attributes for ``DXIL1_0``. | |
| .. code-block:: | |
| attributes = [Attributes<DXIL1_0, [ReadNone]]; | |
| A null list of ``attributes`` signifies no operation attributes. | |
| Interpretation of Multiple Versioned Properties | |
| ----------------------------------------------- | |
| Each of the versioned properties states that the specified overload type, stage or | |
| attribute records are valid for the predicated DXIL version. Only | |
| the properties corresponding to latest minimal DXIL version are applicable. | |
| Note as in the above example, any overload types, stages or attributes, | |
| that remain valid in a later DXIL version need to be specified in full. | |
| For example, consider the following specification of valid overload types: | |
| .. code-block:: | |
| overloads = [ | |
| Overloads<DXIL1_0, [halfTy, floatTy]>, | |
| Overloads<DXIL1_2, [halfTy, floatTy, doubleTy]> | |
| ]; | |
| It specifies that the overload types ``halfTy`` and ``floatTy`` are valid for DXIL | |
| version 1.0 and later. It also specifies that ``doubleTy`` is additionally supported | |
| in DXIL version 1.2 and later. | |
| This provides the flexibility to specify properties independent of other | |
| versioned specifications in the list. | |
| DXIL Operation Specification Examples | |
| ===================================== | |
| Following examples illustrate the specification of some of the DXIL Ops. | |
| ``Sin`` operation - an operation valid in all DXIL versions and all stages | |
| and has valid overload types predicated on DXIL version. | |
| .. code-block:: | |
| 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`` - an operation with no arguments, no | |
| overload types, and valid stages and attributes predicated by DXIL Version. | |
| .. code-block:: | |
| 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`` - an operation with ``void`` return type, valid overload types | |
| predicated by DXIL Version and valid in all DXIL versions and stages. | |
| .. code-block:: | |
| 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`` - an operation with no overload types and stages predicated | |
| by DXIL Version. | |
| .. code-block:: | |
| 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`` - an operation with no overload types, no associated ``LLVMIntrinsic`` | |
| and stages predicated by DXIL Version. | |
| .. code-block:: | |
| 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`` - an operation with valid overload types, stages and attributes | |
| predicated by DXIL version. | |
| .. code-block:: | |
| 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]>]; | |
| } | |
| Summary | |
| ======= | |
| This note sketches the design of a readable and maintainable TableGen specification of | |
| DXIL Ops in ``DXIL.td`` intended to serve as a single source of reference for TableGen | |
| backends (such as ``DXILEmitter``) that generate C++ representations used in DXIL | |
| backend passes. |