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