| ============================================================== |
| 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. |