|
| 1 | +============================================================== |
| 2 | +Specification of DXIL Operations using TableGen Representation |
| 3 | +============================================================== |
| 4 | +.. contents:: |
| 5 | + :local: |
| 6 | + |
| 7 | +.. toctree |
| 8 | + :hidden |
| 9 | +
|
| 10 | +Introduction |
| 11 | +============ |
| 12 | + |
| 13 | +`DirectXShaderCompiler <https://github.com/microsoft/DirectXShaderCompiler>`_ |
| 14 | +encapsulates, among other information, various DXIL Operations in |
| 15 | +`hctdb.py <https://github.com/microsoft/DirectXShaderCompiler/blob/main/utils/hct/hctdb.py>`_. |
| 16 | +DXIL Operations are represented in one of the following `two ways |
| 17 | +<https://github.com/microsoft/DirectXShaderCompiler/blob/130877392c263888ef06bab768856d3dab1f1c9a/docs/DXIL.rst#L1978>`_: |
| 18 | + |
| 19 | +#. Using LLVM instructions |
| 20 | +#. Using LLVM External functions. These are represented in LLVM IR as follows: |
| 21 | + |
| 22 | + * "Standard" LLVM intrinsics (e.g., ``llvm.sin.*``) and |
| 23 | + * HLSL intrinsics (defined as LLVM intrinsics in ``llvm/include/llvm/IR/IntrinsicsDirectX.td``, e.g., ``llvm.dx.*``) |
| 24 | + |
| 25 | + These are collectively referred to as `LLVM Intrinsics` in this note. |
| 26 | + |
| 27 | +DXIL Ops, as currently represented in ``hctdb.py`` have the following attributes |
| 28 | + |
| 29 | +#. ``name`` - A short, unique name |
| 30 | +#. ``llvm_id`` - ID of LLVM instruction. This is just an arbitrary, yet fixed, number that indicates LLVM's ``CallInst`` for all LLVM intrinsics |
| 31 | +#. ``llvm_name`` - String name of LLVM instruction type |
| 32 | +#. ``is_dxil_op`` - A bool indicating whether this is a call into a built-in DXIL function |
| 33 | +#. ``dxil_op`` - String name of DXIL operation |
| 34 | +#. ``dxil_opid`` - ID of DXIL operation |
| 35 | +#. ``dxil_class`` - String name of the opcode class |
| 36 | +#. ``category`` - String classification for this instruction |
| 37 | +#. ``doc`` - String documentation description of this instruction |
| 38 | +#. ``remarks`` - String long-form remarks on this instruction |
| 39 | +#. ``ops`` - List of operands that this instruction takes |
| 40 | +#. ``is_allowed`` - Bool indicating whether this instruction is allowed in a DXIL program |
| 41 | +#. ``oload_types`` - String denoting overload types if applicable (e.g., "hf", "iwl") |
| 42 | +#. ``fn_attr`` - Attribute shorthand strings: rn=does not access memory,ro=only reads from memory, |
| 43 | +#. ``is_deriv`` - Bool indicating whether this is some kind of derivative |
| 44 | +#. ``is_gradient`` - Bool indicating whether this requires a gradient calculation |
| 45 | +#. ``is_feedback`` - Bool indicating whether this is a sampler feedback op |
| 46 | +#. ``is_wave`` - Bool indicating whether this requires in-wave, cross-lane functionality |
| 47 | +#. ``requires_uniform_inputs`` - Bool indicating whether this operation requires that all of its inputs are uniform across the wave |
| 48 | +#. ``is_barrier`` - Bool indicating whether this is a barrier operation |
| 49 | +#. ``shader_stages`` - shader stages to which this applies, empty for all. |
| 50 | +#. ``shader_model`` - minimum shader model required (e.g., 6, 0) |
| 51 | +#. ``inst_helper_prefix`` - None |
| 52 | +#. ``fully_qualified_name_prefix`` - Constant string ``"hlsl::OP::OpCode"`` |
| 53 | +#. ``is_dxil_op`` - Bool that evaluates (dxil_op != "") indicating whether this is a DXIL operation |
| 54 | +#. ``is_reserved`` - Bool that evaluates (dxil_class == "Reserved") |
| 55 | +#. ``shader_model_translated`` - minimum shader model required with translation by linker |
| 56 | +#. ``props`` - extra properties |
| 57 | + |
| 58 | +Motivation |
| 59 | +========== |
| 60 | + |
| 61 | +``DXILLowering`` pass needs to lower the LLVM intrinsics. TableGen file - |
| 62 | +``llvm/lib/Target/DirectX/DXIL.td`` - is used to specify the properties of DXIL |
| 63 | +Ops including the mapping of each of them to LLVM intrinsics they correspond to, if any. |
| 64 | +``utils/hct/hctdb.py`` serves this purpose in ``DirectXShaderCompier`` repo. |
| 65 | +Anologously, ``DXIL.td`` is planned to be the single source of reference |
| 66 | +for the properties and LLVM intrinsic mapping of DXIL Ops for DXIL backend |
| 67 | +implemetation in ``llvm-project`` repo. It needs to have a rich representation |
| 68 | +abilities that TableGen backends (such as ``DXILEmitter``) can rely on. Additionally, |
| 69 | +the DXIL Op specification should be easy to read and comprehend. |
| 70 | + |
| 71 | +Design |
| 72 | +====== |
| 73 | + |
| 74 | +Distilling the essential attributes of DXIL Op from the above (as a start), following |
| 75 | +attributes form the core of its specification. |
| 76 | + |
| 77 | +#. ``dxil_opid`` or ``OpCode`` |
| 78 | +#. ``dxil_class`` or ``OpClass`` - this string is an integral part of the DXIL Op function name and is constructed in the format ``dx.op.<class-name>.<overload-type>``. The DXIL validator checks for any deviation from this for each of the DXIL Op call. |
| 79 | +#. ``ops`` - list of operands encapsulating the index and valid (fixed or overload) types |
| 80 | +#. ``oload_types`` - Valid overload types of the DXIL op |
| 81 | + |
| 82 | +Each of the LLVM intrinsics maps to an external function represented by a call to an |
| 83 | +external function of the form ``dx.op.<class-name>.<overload-type>`` as noted above. |
| 84 | + |
| 85 | +Following is a basic TableGen class structure to encapsulate the mapping of LLVM Intrinsics to DXIL Ops. |
| 86 | + |
| 87 | +.. code-block:: |
| 88 | +
|
| 89 | + // Abstraction of DXIL Operation to LLVM Intrinsic mapping |
| 90 | + class DXILOpMappingBase { |
| 91 | + int OpCode = 0; // Opcode of DXIL Operation |
| 92 | + DXILOpClass OpClass = UnknownOpClass;// Class of DXIL Operation. |
| 93 | + Intrinsic LLVMIntrinsic = ?; // LLVM Intrinsic DXIL Operation maps to |
| 94 | + string Doc = ""; // A short description of the operation |
| 95 | + list<LLVMType> OpTypes = ?; // Valid types of DXIL Operation in the |
| 96 | + // format [returnTy, param1ty, ...] |
| 97 | + } |
| 98 | +
|
| 99 | +Various options considered to represent this mapping - keeping the goals of rich |
| 100 | +representation and readability stated above - are discussed in the remainder |
| 101 | +of the note. The basic difference between these options is the way return and |
| 102 | +parameter types are represented for DXIL Ops with valid overload types. |
| 103 | +Valid overload types for several DXIL Ops would be over-specified using LLVM's |
| 104 | +``llvm_any*_ty`` types. For example, ``half`` and ``float`` are only valid for |
| 105 | +DXIL ``Sin`` and would be overspecified using ``llvm_anyfloat_ty``. The options |
| 106 | +listed below address the need to model such overload types specific types |
| 107 | +precisely for correct code generation. They each provide specifications with |
| 108 | +varying levels in (a) ease of readablility and maintainability and |
| 109 | +(b) of compactness / richness. |
| 110 | + |
| 111 | +Option 1 : Specify ``OpType`` as a list of valid fixed types. |
| 112 | +------------------------------------------------------------- |
| 113 | + |
| 114 | +``OpTypes`` for ``Sin`` may be specified as |
| 115 | +``[[llvm_i16, llvm_i32], [llvm_i16, llvm_i32]]``. Repeating such lists for each |
| 116 | +of the DXIL Ops - not all of which are unary - reduces readability and increases |
| 117 | +the proclivity for errors in specification and maintenance. Even if one can |
| 118 | +consider usage of TableGen definitions to create shorthand concrete record |
| 119 | +defs for these, above stated problems are barely mitigated. |
| 120 | + |
| 121 | +Option 2 : Specify a function to validate accepted overload types |
| 122 | +----------------------------------------------------------------- |
| 123 | + |
| 124 | +Specify a validation function to verify/generate the accepted set of overload |
| 125 | +types for DXIL Ops as a field of ``class DXILOpMappingBase``. This function is |
| 126 | +expected to be invoked by the TableGen backends to generate relevant ``*.inc`` |
| 127 | +files with accurate content. Such a specification can provide relief from the |
| 128 | +need to specify and maintain long lists of OpTypes. However, having such set |
| 129 | +of functions fits rather awkwardly with the TableGen API usage of being able |
| 130 | +to directly work with the content of a record. Further, these validation |
| 131 | +functions add to the maintenace overlead while not not necessarily making |
| 132 | +the specification more readable. |
| 133 | + |
| 134 | +Option 3a : Specify ``OpTypes`` as an override of list valid fixed types |
| 135 | +------------------------------------------------------------------------ |
| 136 | +[**Current strawman implementation**] |
| 137 | + |
| 138 | +Inherit the valid types of the LLVM Intrinsic being lowered as valid for the |
| 139 | +DXIL Op, by default. This will reduce the need to specify a ``OpTypes`` list |
| 140 | +for those DXIL Ops with the same valid types as the LLVM Intrinsic. In cases |
| 141 | +where it is not the case (such as ``Sin``), an optional list that overrides |
| 142 | +the default inheritence should be specified. This improves the readability |
| 143 | +by eliminating specification of ``OpType`` lists, when not needed. A |
| 144 | +relatively small set of precise overload types that are specific to DXIL Ops |
| 145 | +are defined to further improve readability. Such types |
| 146 | +(e.g., ``llvm_halforfloat_ty``) are defined using standard LLVM MVT |
| 147 | +kinds (viz., ``MVT::Other``). |
| 148 | + |
| 149 | +For example, following is the specification of ``Sin`` where the default |
| 150 | +type inheritence is overridden via explicit specification of valid overload |
| 151 | +types that are more precise. |
| 152 | + |
| 153 | +.. code-block:: |
| 154 | +
|
| 155 | + def Sin : DXILOpMapping<13, unary, int_sin, |
| 156 | + "Returns sine(theta) for theta in radians.", |
| 157 | + [llvm_halforfloat_ty, LLVMMatchType<0>]>; |
| 158 | +
|
| 159 | +Following is the specification of ``ThreadId`` where the types of the LLVM |
| 160 | +intrinsic (``int_dx_thread_id``) are valid for ``dx.op.threadId.*`` and |
| 161 | +need not be overridden. |
| 162 | + |
| 163 | +.. code-block:: |
| 164 | +
|
| 165 | + def ThreadId : DXILOpMapping<93, threadId, int_dx_thread_id, |
| 166 | + "Reads the thread ID">; |
| 167 | +
|
| 168 | +The backends can consume such specification without needing to execute |
| 169 | +additional validation function or code. Such specification provides better |
| 170 | +readability and the necessary type information. It does not completely |
| 171 | +eliminate the mechanism of using lists as ``OpTypes``, but DXIL Ops that |
| 172 | +need ``OpTypes`` lists could be lesser. |
| 173 | + |
| 174 | +Option 3b : Specify ``OpTypes`` as an exclusion list of valid fixed types |
| 175 | +------------------------------------------------------------------------- |
| 176 | + |
| 177 | +Another variant of the Option 3a is to specify an exclusion list. An |
| 178 | +exclusion list instead of an override list provides a list of fixed types |
| 179 | +not valid for an DXIL Op and thus need to be excluded from a valid overload |
| 180 | +type lsit of LLVM Intrinsic. The benefits and downsides of this are the same |
| 181 | +as those of specifying an override list as in Option 3a. |
| 182 | + |
| 183 | +Option 4 : Specify accepted overload types as ``Attr`` records |
| 184 | +--------------------------------------------------------------- |
| 185 | + |
| 186 | +LLVM's TableGen infrastructure defines a base ``class Attr`` |
| 187 | +(``llvm/include/llvm/IR/Attributes.td``) with an associated |
| 188 | +``AttrProperty``. Valid overload types of a DXIL Op can be represented as |
| 189 | +``Attr`` records, distinguished via ``AttrProperty`` to model precise types |
| 190 | +as needed. This can provide the necessary readability and the richness of |
| 191 | +specification. Additionally, the other properties of a DXIL Op (such as the |
| 192 | +``bool is_*``) can also be uniformly represented as ``Attr`` records. |
| 193 | + |
| 194 | +Summary |
| 195 | +======= |
| 196 | + |
| 197 | +This note discusses various design options that have been explored to implement |
| 198 | +a representation of DXIL Ops in ``DXIL.td``. ``DXIL.td`` is intended to serve as |
| 199 | +a single source of reference for TableGen backends (such as ``DXILEmitter`` - |
| 200 | +specific to DXIL backend), have an accurate and rich specification, be readable |
| 201 | +and maintainable. The current implementation employs Option 3a. It is in place, |
| 202 | +primarily, to facilitate lowering of new LLVM intrinsics for HLSL functions |
| 203 | +being added in the front end. It serves to expose any additional considerations |
| 204 | +necessary for an improved design of ``DXIL.td``. The current plan is to design |
| 205 | +and implement **Opton 4** to improve readability and maintainablity while |
| 206 | +leveraging constructs in LLVM TableGen infrastructure for a potentially rich |
| 207 | +specification. |
0 commit comments