Skip to content

[DirectX][DXIL] Design document for TableGen Spec of DXIL Operations #85170

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
May 29, 2024
Merged
182 changes: 182 additions & 0 deletions llvm/docs/DirectX/DXILOpTableGenDesign.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
==============================================================
Specification of DXIL Operations using TableGen Representation
==============================================================
.. contents::
:local:

.. 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/130877392c263888ef06bab768856d3dab1f1c9a/docs/DXIL.rst#L1978>`_:

#. Using LLVM instructions
#. 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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't render correctly - I think you need to indent 3 spaces to match the start of "Using LLVM External..."

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The rendering still seems off. Have you considered merging the two lists together?

#. Using LLVM instructions (standard LLVM intrinsics, e.g., llvm.sin.*)
#. Using LLVM External functions, also called HLSL intrinsics. These are defined in llvm/include/llvm/IR/IntrinsicsDirectX.td (e.g., llvm.dx.*).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The rendering still seems off...

Thanks! Fixed.


Following is the complete list of properties of DXIL Ops with the corresponding field name
as used in ``hctdb.py``, if one exists. A DXIL Op is represented by a set of associated properties

1. Name of operation (``dxil_op``)
2. The generic or HLSL-specific intrinsic that maps to the operation (``llvm_name``).
3. Unique Integer ID (``dxil_opid``)
4. 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>``. The DXIL validator checks for any
deviation from this for each of the DXIL Op call.

5. List of valid overload types for the operation (``oload_types``).
6. Required minimum Shader Model version with support for the operation. (``shader_model``).
7. Required minimum DXIL version with support for the operation.
8. Minimum shader model required with translation by linker (``shader_model_translated``)
9. List of shader stages the operation is applicable to (``shader_stages``); empty if applicable to all stages.
10. Memory access attributes of the operation (``fn_attr``).
11. 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``).

12. A string that documents the operation (``doc``)

Motivation
==========

DXIL backend passes depend on the knowledge of various properties of DXIL Operations.
For example, ``DXILLowering`` pass will need information such as the DXIL operation an
LLVM intrinsic is to be lowered to, along with valid overload and parameter types etc.
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 for DXIL backend implementation in ``llvm-project`` repo -
analogous to ``hctdb.py`` for ``DirectXShadeCompiler`` repo. 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.

Design
======

1. Each DXIL Operation is represented as a TableGen record. The name of each of the records
signifies operation name.
2. The LLVM Intrinsic that maps to the operation is represented using ``Intrinsic::*``.
3. The unique operation id is represented by an integer.
4. DXIL Operation Class is represented as follows

.. code-block::

// Abstraction of DXIL Operation class.
// It encapsulates an associated function signature viz.,
// returnTy(param1Ty, param2Ty, ...) represented as a list of LLVMTypes.
// DXIL Ops that belong to a DXILOpClass record the signature of that DXILOpClass

class DXILOpClass<list<LLVMType> OpSig> {
list<LLVMType> OpSignature = OpSig;
}

Concrete operation classes, such as ``unary`` are defined inheriting from ``DXILOpClass``.
5. Valid overload types are represented as a list of ``LLVMType`` s.
6. Concrete records of Shader Model version and DXIL versions and are defined
by inheriting from the class

.. code-block::

// Abstract class to represent major and minor version values
class Version<int major, int minor> {
int Major = major;
int Minor = minor;
}

7. Shader stages for which the operation is applicable are represented as a list of
concrete records that inherit from the Class

.. code-block::

class ShaderStage;


8. All remaining properties of the operation are represented as a list as of concrete records
that inherit from the class

.. code-block::

class OpAttributes;

- memory access - ``ReadNone``, ``ReadNone``
- ``IsDerivative``, ``IsGradient``, ``IsFeedback``, ``IsWave``, ``NeedsUniformInputs``, ``IsBarrier``
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As we discussed this morning I'm unsure these properties are required.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As we discussed this morning I'm unsure these properties are required.

Updated per discussion.


9. A documentation string for the operation.


A DXIL Operation is represented by the following TableGne class by encapsulating the various
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
A DXIL Operation is represented by the following TableGne class by encapsulating the various
A DXIL Operation is represented by the following TableGen class by encapsulating the various

TableGen representations of its properties described above.

.. code-block::

// Abstraction DXIL Operation
class DXILOpPropertiesBase {
int OpCode = 0; // Opcode of DXIL Operation
DXILOpClass OpClass = UnknownOpClass; // Class of DXIL Operation.
Intrinsic LLVMIntrinsic = ?; // LLVM Intrinsic DXIL Operation maps to
list<LLVMType> OpOverloadTypes = ?; // Valid overload type
// of DXIL Operation
Version SMVer = ?; // Min Shader Model version
Version SMVerLinker = ?; // Min Shader Model required for linking
Version DXILVer = ?; // Min DXIL version
list<ShaderStage> ShaderStages = ?; // List of applicable shader stages
list<OpAttributes> OpAttribs = ?; // Operation attributes
string Doc = ""; // A short description of the operation
}


The following convenience class is used to demonstrate the definitions of a couple of
operations:

.. code-block::

class DXILOpProperties<int opCode,
Intrinsic intrinsic,
list<LLVMType> overloadTypes,
string doc> : DXILOpPropertiesBase {
int OpCode = opCode;
Intrinsic LLVMIntrinsic = intrinsic;
list<LLVMType> OpOverloadTypes = overloadTypes;
string Doc = doc;
}

Additionally, following definition of ``unary`` class is also used:

.. code-block::

def unary : DXILOpClass<[llvm_any_ty, LLVMMatchType<0>]>;

Following is the definition of ``Sin`` and ``Cos``

.. code-block::

let OpClass = unary in {
def Cos : DXILOpProperties<12, int_cos, [llvm_half_ty, llvm_float_ty],
"Returns cosine(theta) for theta in radians.">;
def Sin : DXILOpProperties<13, int_sin, [llvm_half_ty, llvm_float_ty],
"Returns sine(theta) for theta in radians.">;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm curious what the representation looks like for a more exotic DXIL Op. Like maybe ddx? How do we represent that ddx is available in SM 2.0+ for Pixel shaders and SM 6.6+ for Compute, Mesh and Amplification?

How do we handle RawBufferLoad where the double and i64 overloads are only valid for SM 6.3+?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm curious what the representation looks like for a more exotic DXIL Op. Like maybe ddx? How do we represent that ddx is available in SM 2.0+ for Pixel shaders and SM 6.6+ for Compute, Mesh and Amplification?

How do we handle RawBufferLoad where the double and i64 overloads are only valid for SM 6.3+?

Thanks for flagging these. I've noted this and plan to update the design spec in subsequent PRs, if needed, as I make progress with implementation and learn about the specific requirements to represent DXIL ops that may not be sufficiently represented using the current specification mechanism.

}

Summary
=======

This note discusses the design of TableGen specification of DXIL Ops in ``DXIL.td``
that is intended to serve as a single source of reference for TableGen
backends (such as ``DXILEmitter`` - specific to DXIL backend), have an accurate
and rich specification, be readable and maintainable.