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