Skip to content

Commit 8876fd6

Browse files
committed
[DirectX][DXIL] Design document for TableGen Spec of DXIL Operations
Add a design document that outlines the choices considered and current implementation.
1 parent 92a09c0 commit 8876fd6

File tree

1 file changed

+207
-0
lines changed

1 file changed

+207
-0
lines changed
Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
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

Comments
 (0)