Skip to content

Commit aa9b978

Browse files
committed
Update following feedback.
1 parent b45fbab commit aa9b978

File tree

1 file changed

+133
-66
lines changed

1 file changed

+133
-66
lines changed

llvm/docs/DirectX/DXILOpTableGenDesign.rst

Lines changed: 133 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,10 @@ DXIL Operations are represented in one of the following `two ways
1919
#. Using LLVM instructions
2020
#. Using LLVM External functions. These are represented in LLVM IR as follows:
2121

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.*``)
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.*``)
2424

25-
These are collectively referred to as `LLVM Intrinsics` in this note.
25+
These are collectively referred to as `LLVM Intrinsics` in this note.
2626

2727
DXIL Ops, as currently represented in ``hctdb.py`` have the following attributes
2828

@@ -61,23 +61,25 @@ Motivation
6161
``DXILLowering`` pass needs to lower the LLVM intrinsics. TableGen file -
6262
``llvm/lib/Target/DirectX/DXIL.td`` - is used to specify the properties of DXIL
6363
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
64+
This purpose is served by ``utils/hct/hctdb.py`` in ``DirectXShaderCompiler`` repo.
65+
Analogously, ``DXIL.td`` is planned to be the single source of reference
6666
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
67+
implementation in ``llvm-project`` repo. It needs to have a rich representation
6868
abilities that TableGen backends (such as ``DXILEmitter``) can rely on. Additionally,
69-
the DXIL Op specification should be easy to read and comprehend.
69+
the DXIL Op specification should be declarative - as much as possible - making it
70+
easy to read and comprehend.
7071

7172
Design
7273
======
7374

74-
Distilling the essential attributes of DXIL Op from the above (as a start), following
75+
Distilling the essential attributes of DXIL Op from the above, following
7576
attributes form the core of its specification.
7677

7778
#. ``dxil_opid`` or ``OpCode``
7879
#. ``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.
7980
#. ``ops`` - list of operands encapsulating the index and valid (fixed or overload) types
8081
#. ``oload_types`` - Valid overload types of the DXIL op
82+
#. Rest of the attributes represented using ``is_*`` booleans
8183

8284
Each of the LLVM intrinsics maps to an external function represented by a call to an
8385
external function of the form ``dx.op.<class-name>.<overload-type>`` as noted above.
@@ -94,61 +96,54 @@ Following is a basic TableGen class structure to encapsulate the mapping of LLVM
9496
string Doc = ""; // A short description of the operation
9597
list<LLVMType> OpTypes = ?; // Valid types of DXIL Operation in the
9698
// format [returnTy, param1ty, ...]
99+
list<DXILAttribute> OpAttributes = ? // List of various attributes including
100+
// bool fields above
97101
}
98102
99103
Various options considered to represent this mapping - keeping the goals of rich
100-
representation and readability stated above - are discussed in the remainder
104+
representation and declarative readability stated above - are discussed in the remainder
101105
of the note. The basic difference between these options is the way return and
102106
parameter types are represented for DXIL Ops with valid overload types.
103107
Valid overload types for several DXIL Ops would be over-specified using LLVM's
104108
``llvm_any*_ty`` types. For example, ``half`` and ``float`` are only valid for
105109
DXIL ``Sin`` and would be overspecified using ``llvm_anyfloat_ty``. The options
106110
listed below address the need to model such overload types specific types
107111
precisely for correct code generation. They each provide specifications with
108-
varying levels in (a) ease of readablility and maintainability and
112+
varying levels in (a) ease of readability and maintainability and
109113
(b) of compactness / richness.
110114

111115
Option 1 : Specify ``OpType`` as a list of valid fixed types.
112116
-------------------------------------------------------------
113117

114118
``OpTypes`` for ``Sin`` may be specified as
115-
``[[llvm_i16, llvm_i32], [llvm_i16, llvm_i32]]``. Repeating such lists for each
119+
``[[llvm_i16, llvm_i32], [llvm_i16, llvm_i32]]`` denoting the valid overloads of
120+
its return and parameter types. Repeating such lists for each
116121
of the DXIL Ops - not all of which are unary - reduces readability and increases
117122
the proclivity for errors in specification and maintenance. Even if one can
118123
consider usage of TableGen definitions to create shorthand concrete record
119-
defs for these, above stated problems are barely mitigated.
124+
defs for these, above stated problems are barely mitigated. Additionally, such
125+
a specification of types duplicates that of LLVM intrinsic it maps to. The
126+
lists would be more verbose in case of overload types as an overload type is
127+
expanded to the supported fixed types, as shown in the above example of `Sin`.
120128

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
129+
Option 2a : Specify ``OpTypes`` as an override of list valid fixed types
135130
------------------------------------------------------------------------
136131
[**Current strawman implementation**]
137132

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.
133+
This option inherits the valid types of the LLVM Intrinsic being lowered as
134+
valid for the DXIL Op, by default. This will reduce the need to specify a
135+
``OpTypes`` list for those DXIL Ops with the same valid types as the LLVM
136+
Intrinsic. In cases where valid types of DXIL Op differ from those of LLVM
137+
Intrinsic (such as ``Sin``), an optional list that overrides the default
138+
inheritance should be specified. This improves the readability by eliminating
139+
specification of lists of ``OpType`` lists, when not needed. A relatively small
140+
set of precise overload types that are specific to DXIL Ops are defined to
141+
further improve readability. Such types (e.g., ``llvm_halforfloat_ty``) are
142+
defined using standard LLVM MVT kinds (viz., ``MVT::Other``).
143+
144+
For example, following is the specification of ``Sin`` where the default type
145+
inheritance from LLVM intrinsic ``int_sin`` is overridden via explicit
146+
specification of valid overload types that are more precise.
152147

153148
.. code-block::
154149
@@ -157,51 +152,123 @@ types that are more precise.
157152
[llvm_halforfloat_ty, LLVMMatchType<0>]>;
158153
159154
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.
155+
intrinsic ``int_dx_thread_id`` (defined in ``llvm/include/llvm/IR/IntrinsicsDirectX.td``)
156+
are valid for ``dx.op.threadId.*`` and need not be overridden.
157+
158+
.. code-block::
159+
160+
def ThreadId : DXILOpMapping<93, threadId, int_dx_thread_id,
161+
"Reads the thread ID">;
162+
163+
164+
However, consider the specification of the LLVM intrinsic ``int_dx_barrier`` (
165+
which would be defined in ``llvm/include/llvm/IR/IntrinsicsDirectX.td``)
166+
which returns ``void`` and has one parameter of type ``int32``
167+
168+
.. code-block::
169+
170+
def int_dx_barrier : Intrinsic<[], [llvm_i32_ty], [IntrNoDuplicate, IntrWillReturn]>;
171+
172+
Per design, it appears that the DXIL Operation ``Barrier`` can inherit the types from
173+
``int_dx_barrier``. However, the ``void`` return type information would be lost since
174+
``OpTypes`` is the concatenated list of return and parameter types of ``int_dx_barrier`` -
175+
viz., would be ``[llvm_i32_ty]``. As a result, types need to overridden
176+
in the specification of ``Barrier`` Op as follows
177+
178+
.. code-block::
179+
180+
def Barrier : DXILOpMapping<80, barrier, int_dx_barrier,
181+
"Inserts a memory barrier in the shader",
182+
[llvm_void_ty, llvm_i32_ty]>;
183+
184+
Such an override is avoidable by aligning the type specification mechanisms of DXIL Op
185+
and LLVM Intrinsic. This change is proposed in
186+
`PR 86311 <https://github.com/llvm/llvm-project/pull/86311>`_. The core change is to
187+
replace ``OpTypes`` with separate lists for return and parameter types as follows
188+
189+
.. code-block::
190+
191+
list<LLVMType> OpRetTypes = ?; // Valid return types of DXIL Operation
192+
list<LLVMType> OpParamTypes = ?; // Valid parameter types of DXIL Operation
193+
194+
Following are the updated specifications of the above examples that adapt
195+
the changes in proposed
196+
`PR 86311 <https://github.com/llvm/llvm-project/pull/86311>`_
162197

163198
.. code-block::
164199
200+
def Sin : DXILOpMapping<13, unary, int_sin,
201+
"Returns sine(theta) for theta in radians.",
202+
[llvm_halforfloat_ty], [LLVMMatchType<0>]>;
165203
def ThreadId : DXILOpMapping<93, threadId, int_dx_thread_id,
166204
"Reads the thread ID">;
205+
def Barrier : DXILOpMapping<80, barrier, int_dx_barrier,
206+
"Inserts a memory barrier in the shader">;
167207
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.
208+
The specification option in this section provides better readability, compactness
209+
and expressive type information while eliminating duplication. It does not
210+
completely eliminate the mechanism of using lists as return and parameter types,
211+
but DXIL Ops that need such lists would be lesser.
173212

174-
Option 3b : Specify ``OpTypes`` as an exclusion list of valid fixed types
213+
Option 2b : Specify ``OpTypes`` as an exclusion list of valid fixed types
175214
-------------------------------------------------------------------------
176215

177-
Another variant of the Option 3a is to specify an exclusion list. An
216+
Another variant of the Option 2a is to specify an exclusion list. An
178217
exclusion list instead of an override list provides a list of fixed types
179218
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.
219+
type list of LLVM Intrinsic. The benefits and downsides of this are the same
220+
as those of specifying an override list as in Option 2a.
221+
222+
Option 3 : Specify a function to validate accepted overload types
223+
-----------------------------------------------------------------
224+
225+
Specify a validation function to verify/generate the accepted set of overload
226+
types for DXIL Ops as a field of ``class DXILOpMappingBase``. While lowering an
227+
intrinsic, the function associated with the DXIL Op being lowered to is invoked
228+
for type validation. Such validation functions can either take the form of
229+
affirming the validity of a given concrete type for a DXIL Op or
230+
generating a list of valid concrete types for a DXIL Op. Following is a
231+
trivial example
232+
233+
.. code-block::
234+
235+
bool isValidType(unsigned Type* Ty, dxil::OpCode Op) {
236+
auto *Prop = getOpcodeproperty(Op);
237+
// Convert Ty to ParameterKind and compare with valid type
238+
return (getParameterKind(Ty) == getOpcodeParameterKind(*Prop));
239+
}
240+
241+
Such a specification can provide relief from the need to specify and maintain
242+
long lists of OpTypes (as in Option 1). However, having such set of functions
243+
splits the generation of valid types in ``*.inc`` file by the ``DXILEmitter``
244+
and validation being done at compile-time that appears to be a rather awkward
245+
consumption of the content of a DXIL Op record. Using functions to specify
246+
valid overload types lacks the clear expressiveness and declarative readability
247+
of an explicit specification. In addition, validation functions add to the
248+
maintenance overhead while not necessarily making the specification more readable.
182249

183-
Option 4 : Specify accepted overload types as ``Attr`` records
184-
---------------------------------------------------------------
250+
Option 4 : Specify accepted overload types as attribute records
251+
----------------------------------------------------------------
185252

186253
LLVM's TableGen infrastructure defines a base ``class Attr``
187254
(``llvm/include/llvm/IR/Attributes.td``) with an associated
188255
``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
256+
records of a class ``DXILAttribute``, similar to ``Attr``. This can provide the
257+
necessary declarative means for better readability and the expressiveness of
191258
specification. Additionally, the other properties of a DXIL Op (such as the
192-
``bool is_*``) can also be uniformly represented as ``Attr`` records.
259+
``bool is_*``) can also be uniformly represented as ``DXILAttribute`` records.
193260

194261
Summary
195262
=======
196263

197264
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 **Option 4** to improve readability and maintainablity while
206-
leveraging constructs in LLVM TableGen infrastructure for a potentially rich
207-
specification.
265+
a Tablegen representation of DXIL Ops in ``DXIL.td``. ``DXIL.td`` is intended to
266+
serve as a single source of reference for TableGen backends (such as ``DXILEmitter``
267+
- specific to DXIL backend), have an accurate and rich specification, be
268+
declarative as much as possible for readability and maintainability. The current
269+
implementation employs Option 2a. It is in place, primarily, to facilitate continued
270+
lowering of new LLVM intrinsics for HLSL functions being added in the front end. It
271+
serves to uncover any additional considerations necessary for an improved design of
272+
``DXIL.td``. The current plan is to explore the design outlined in **Option 4** to
273+
improve readability and maintainability while leveraging constructs in LLVM TableGen
274+
infrastructure for a potentially rich specification.

0 commit comments

Comments
 (0)