| # Specification of DXIL Operations using TableGen Representation | |
| % 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): | |
| 1. Using LLVM instructions. | |
| 2. 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. | |
| 1. Properties consumed in DXIL backend passes | |
| 01. Name of operation (`dxil_op`) | |
| 02. A string that documents the operation (`doc`) - This is not strictly necessary but is included | |
| for readability and documentation of the operation. | |
| 03. The generic or HLSL-specific intrinsic that maps to the operation (`llvm_name`). | |
| 04. Unique Integer ID (`dxil_opid`) | |
| 05. 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. | |
| 06. List of valid overload types for the operation (`oload_types`). | |
| 07. Required DXIL Version with support for the operation. | |
| 08. Required minimum Shader Model (`shader_model`). | |
| 09. 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. | |
| 01. Each DXIL Operation is represented as a TableGen record. The name of each of the records | |
| signifies operation name. | |
| 02. A documentation string for the operation. | |
| 03. 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). | |
| 04. The unique operation id is represented by an integer. | |
| 05. DXIL Operation Class is represented as follows | |
| ``` | |
| // Abstraction of DXIL Operation class. | |
| class DXILOpClass; | |
| ``` | |
| Concrete operation records, such as `unary` are defined by inheriting from `DXILOpClass`. | |
| 06. 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. | |
| 07. 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. | |
| 08. 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. | |
| 09. 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. | |
| ``` | |
| // 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: | |
| ``` | |
| // 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: | |
| ``` | |
| // 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 | |
| ``` | |
| // 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 | |
| ``` | |
| 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 | |
| ``` | |
| 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`. | |
| ``` | |
| 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 | |
| ``` | |
| 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`. | |
| ``` | |
| 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 | |
| ``` | |
| 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 | |
| ``` | |
| 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`. | |
| ``` | |
| 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: | |
| ``` | |
| 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. | |
| ``` | |
| 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. | |
| ``` | |
| 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. | |
| ``` | |
| 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. | |
| ``` | |
| 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. | |
| ``` | |
| 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. | |
| ``` | |
| 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. | |