2019-06-18 02:42:37 +08:00
|
|
|
|
# SPIR-V Dialect
|
|
|
|
|
|
2019-12-24 05:04:34 +08:00
|
|
|
|
This document describes the design of the SPIR-V dialect in MLIR. It lists
|
|
|
|
|
various design choices we made for modeling different SPIR-V mechanisms, and
|
|
|
|
|
their rationale.
|
|
|
|
|
|
|
|
|
|
This document also explains in a high-level manner how different components are
|
|
|
|
|
organized and implemented in the code and gives steps to follow for extending
|
|
|
|
|
them.
|
|
|
|
|
|
|
|
|
|
This document assumes familiarity with SPIR-V. [SPIR-V][Spirv] is the Khronos
|
|
|
|
|
Group’s binary intermediate language for representing graphics shaders and
|
|
|
|
|
compute kernels. It is adopted by multiple Khronos Group’s APIs, including
|
|
|
|
|
Vulkan and OpenCL. It is fully defined in a
|
|
|
|
|
[human-readable specification][SpirvSpec]; the syntax of various SPIR-V
|
|
|
|
|
instructions are encoded in a [machine-readable grammar][SpirvGrammar].
|
|
|
|
|
|
|
|
|
|
## Design Guidelines
|
|
|
|
|
|
|
|
|
|
SPIR-V is a binary intermediate language that serves dual purpose: on one side,
|
|
|
|
|
it is an intermediate language to represent graphics shaders and compute kernels
|
|
|
|
|
for high-level languages to target; on the other side, it defines a stable
|
|
|
|
|
binary format for hardware driver consumption. As a result, SPIR-V has design
|
|
|
|
|
principles pertain to not only intermediate language, but also binary format.
|
|
|
|
|
For example, regularity is one of the design goals of SPIR-V. All concepts are
|
|
|
|
|
represented as SPIR-V instructions, including declaring extensions and
|
|
|
|
|
capabilities, defining types and constants, defining functions, attaching
|
|
|
|
|
additional properties to computation results, etc. This way favors binary
|
|
|
|
|
encoding and decoding for driver consumption but not necessarily compiler
|
|
|
|
|
transformations.
|
|
|
|
|
|
|
|
|
|
### Dialect design principles
|
|
|
|
|
|
|
|
|
|
The main objective of the SPIR-V dialect is to be a proper intermediate
|
|
|
|
|
representation (IR) to facilitate compiler transformations. While we still aim
|
|
|
|
|
to support serializing to and deserializing from the binary format for various
|
|
|
|
|
good reasons, the binary format and its concerns play less a role in the design
|
|
|
|
|
of the SPIR-V dialect: when there is a trade-off to be made between favoring IR
|
|
|
|
|
and supporting binary format, we lean towards the former.
|
|
|
|
|
|
|
|
|
|
On the IR aspect, the SPIR-V dialect aims to model SPIR-V at the same semantic
|
|
|
|
|
level. It is not intended to be a higher level or lower level abstraction than
|
|
|
|
|
the SPIR-V specification. Those abstractions are easily outside the domain of
|
|
|
|
|
SPIR-V and should be modeled with other proper dialects so they can be shared
|
|
|
|
|
among various compilation paths. Because of the dual purpose of SPIR-V, SPIR-V
|
|
|
|
|
dialect staying at the same semantic level as the SPIR-V specification also
|
2020-01-10 12:36:34 +08:00
|
|
|
|
means we can still have straightforward serialization and deserialization for
|
2019-12-24 05:04:34 +08:00
|
|
|
|
the majority of functionalities.
|
|
|
|
|
|
|
|
|
|
To summarize, the SPIR-V dialect follows the following design principles:
|
|
|
|
|
|
|
|
|
|
* Stay as the same semantic level as the SPIR-V specification by having
|
|
|
|
|
one-to-one mapping for most concepts and entities.
|
|
|
|
|
* Adopt SPIR-V specification's syntax if possible, but deviate intentionally
|
|
|
|
|
to utilize MLIR mechanisms if it results in better representation and
|
|
|
|
|
benefits transformation.
|
|
|
|
|
* Be straightforward to serialize into and deserialize from the SPIR-V binary
|
|
|
|
|
format.
|
2019-06-18 02:42:37 +08:00
|
|
|
|
|
2019-12-24 05:04:34 +08:00
|
|
|
|
SPIR-V is designed to be consumed by hardware drivers, so its representation is
|
|
|
|
|
quite clear, yet verbose for some cases. Allowing representational deviation
|
|
|
|
|
gives us the flexibility to reduce the verbosity by using MLIR mechanisms.
|
2019-06-18 05:47:22 +08:00
|
|
|
|
|
2019-12-24 05:04:34 +08:00
|
|
|
|
### Dialect scopes
|
2019-06-18 02:42:37 +08:00
|
|
|
|
|
2019-12-24 05:04:34 +08:00
|
|
|
|
SPIR-V supports multiple execution environments, specified by client APIs.
|
|
|
|
|
Notable adopters include Vulkan and OpenCL. It follows that the SPIR-V dialect
|
|
|
|
|
should support multiple execution environments if to be a proper proxy of SPIR-V
|
|
|
|
|
in MLIR systems. The SPIR-V dialect is designed with these considerations: it
|
|
|
|
|
has proper support for versions, extensions, and capabilities and is as
|
|
|
|
|
extensible as SPIR-V specification.
|
2019-06-18 02:42:37 +08:00
|
|
|
|
|
2019-06-18 05:47:22 +08:00
|
|
|
|
## Conventions
|
|
|
|
|
|
2019-12-24 05:04:34 +08:00
|
|
|
|
The SPIR-V dialect adopts the following conventions for IR:
|
2019-06-18 05:47:22 +08:00
|
|
|
|
|
2019-09-06 03:45:08 +08:00
|
|
|
|
* The prefix for all SPIR-V types and operations are `spv.`.
|
2019-12-24 05:04:34 +08:00
|
|
|
|
* All instructions in an extended instruction set are further qualified with
|
|
|
|
|
the extended instruction set's prefix. For example, all operations in the
|
|
|
|
|
GLSL extended instruction set is has the prefix of `spv.GLSL.`.
|
|
|
|
|
* Ops that directly mirror instructions in the specification have `CamelCase`
|
2019-09-06 03:45:08 +08:00
|
|
|
|
names that are the same as the instruction opnames (without the `Op`
|
2019-12-24 05:04:34 +08:00
|
|
|
|
prefix). For example, `spv.FMul` is a direct mirror of `OpFMul` in the
|
|
|
|
|
specification. Such an op will be serialized into and deserialized from one
|
|
|
|
|
SPIR-V instruction.
|
2019-09-06 03:45:08 +08:00
|
|
|
|
* Ops with `snake_case` names are those that have different representation
|
2019-12-24 05:04:34 +08:00
|
|
|
|
from corresponding instructions (or concepts) in the specification. These
|
2019-09-06 03:45:08 +08:00
|
|
|
|
ops are mostly for defining the SPIR-V structure. For example, `spv.module`
|
2019-12-24 05:04:34 +08:00
|
|
|
|
and `spv.constant`. They may correspond to one or more instructions during
|
2019-09-06 03:45:08 +08:00
|
|
|
|
(de)serialization.
|
|
|
|
|
* Ops with `_snake_case` names are those that have no corresponding
|
|
|
|
|
instructions (or concepts) in the binary format. They are introduced to
|
|
|
|
|
satisfy MLIR structural requirements. For example, `spv._module_end` and
|
|
|
|
|
`spv._merge`. They maps to no instructions during (de)serialization.
|
2019-06-18 05:47:22 +08:00
|
|
|
|
|
2019-12-24 05:04:34 +08:00
|
|
|
|
(TODO: consider merging the last two cases and adopting `spv.mlir.` prefix for
|
|
|
|
|
them.)
|
|
|
|
|
|
2019-06-18 05:47:22 +08:00
|
|
|
|
## Module
|
|
|
|
|
|
|
|
|
|
A SPIR-V module is defined via the `spv.module` op, which has one region that
|
|
|
|
|
contains one block. Model-level instructions, including function definitions,
|
2019-09-06 03:45:08 +08:00
|
|
|
|
are all placed inside the block. Functions are defined using the builtin `func`
|
2019-06-22 05:51:58 +08:00
|
|
|
|
op.
|
2019-06-18 05:47:22 +08:00
|
|
|
|
|
2019-12-24 05:04:34 +08:00
|
|
|
|
We choose to model a SPIR-V module with a dedicated `spv.module` op based on the
|
|
|
|
|
following considerations:
|
|
|
|
|
|
|
|
|
|
* It maps cleanly to a SPIR-V module in the specification.
|
|
|
|
|
* We can enforce SPIR-V specific verification that is suitable to be performed
|
|
|
|
|
at the module-level.
|
|
|
|
|
* We can attach additional model-level attributes.
|
|
|
|
|
* We can control custom assembly form.
|
|
|
|
|
|
|
|
|
|
The `spv.module` op's region cannot capture SSA values from outside, neither
|
|
|
|
|
implicitly nor explicitly. The `spv.module` op's region is closed as to what ops
|
|
|
|
|
can appear inside: apart from the builtin `func` op, it can only contain ops
|
|
|
|
|
from the SPIR-V dialect. The `spv.module` op's verifier enforces this rule. This
|
|
|
|
|
meaningfully guarantees that a `spv.module` can be the entry point and boundary
|
|
|
|
|
for serialization.
|
|
|
|
|
|
|
|
|
|
### Module-level operations
|
|
|
|
|
|
|
|
|
|
SPIR-V binary format defines the following [sections][SpirvLogicalLayout]:
|
|
|
|
|
|
|
|
|
|
1. Capabilities required by the module.
|
|
|
|
|
1. Extensions required by the module.
|
|
|
|
|
1. Extended instructions sets required by the module.
|
|
|
|
|
1. Addressing and memory model specification.
|
|
|
|
|
1. Entry point specifications.
|
|
|
|
|
1. Execution mode declarations.
|
|
|
|
|
1. Debug instructions.
|
|
|
|
|
1. Annotation/decoration instructions.
|
|
|
|
|
1. Type, constant, global variables.
|
|
|
|
|
1. Function declarations.
|
|
|
|
|
1. Function definitions.
|
|
|
|
|
|
|
|
|
|
Basically, a SPIR-V binary module contains multiple module-level instructions
|
|
|
|
|
followed by a list of functions. Those module-level instructions are essential
|
|
|
|
|
and they can generate result ids referenced by functions, notably, declaring
|
|
|
|
|
resource variables to interact with the execution environment.
|
|
|
|
|
|
|
|
|
|
Compared to the binary format, we adjust how these module-level SPIR-V
|
|
|
|
|
instructions are represented in the SPIR-V dialect:
|
|
|
|
|
|
|
|
|
|
#### Use MLIR attributes for metadata
|
2019-06-18 05:47:22 +08:00
|
|
|
|
|
2019-09-06 03:45:08 +08:00
|
|
|
|
* Requirements for capabilities, extensions, extended instruction sets,
|
|
|
|
|
addressing model, and memory model is conveyed using `spv.module`
|
|
|
|
|
attributes. This is considered better because these information are for the
|
2019-12-24 05:04:34 +08:00
|
|
|
|
execution environment. It's easier to probe them if on the module op itself.
|
2019-09-26 02:57:13 +08:00
|
|
|
|
* Annotations/decoration instructions are "folded" into the instructions they
|
|
|
|
|
decorate and represented as attributes on those ops. This eliminates
|
2019-09-06 03:45:08 +08:00
|
|
|
|
potential forward references of SSA values, improves IR readability, and
|
2019-12-24 05:04:34 +08:00
|
|
|
|
makes querying the annotations more direct. More discussions can be found in
|
|
|
|
|
the [`Decorations`](#decorations) section.
|
|
|
|
|
|
|
|
|
|
#### Model types with MLIR custom types
|
|
|
|
|
|
2019-09-06 03:45:08 +08:00
|
|
|
|
* Types are represented using MLIR standard types and SPIR-V dialect specific
|
2019-12-24 05:04:34 +08:00
|
|
|
|
types. There are no type declaration ops in the SPIR-V dialect. More
|
|
|
|
|
discussions can be found in the [Types](#types) section later.
|
|
|
|
|
|
|
|
|
|
#### Unify and localize constants
|
|
|
|
|
|
2019-09-06 03:45:08 +08:00
|
|
|
|
* Various normal constant instructions are represented by the same
|
|
|
|
|
`spv.constant` op. Those instructions are just for constants of different
|
|
|
|
|
types; using one op to represent them reduces IR verbosity and makes
|
|
|
|
|
transformations less tedious.
|
|
|
|
|
* Normal constants are not placed in `spv.module`'s region; they are localized
|
|
|
|
|
into functions. This is to make functions in the SPIR-V dialect to be
|
2019-12-24 05:04:34 +08:00
|
|
|
|
isolated and explicit capturing. Constants are cheap to duplicate given
|
|
|
|
|
attributes are uniqued in `MLIRContext`.
|
|
|
|
|
|
|
|
|
|
#### Adopt symbol-based global variables and specialization constant
|
|
|
|
|
|
2019-09-06 03:45:08 +08:00
|
|
|
|
* Global variables are defined with the `spv.globalVariable` op. They do not
|
|
|
|
|
generate SSA values. Instead they have symbols and should be referenced via
|
|
|
|
|
symbols. To use a global variables in a function block, `spv._address_of` is
|
|
|
|
|
needed to turn the symbol into a SSA value.
|
|
|
|
|
* Specialization constants are defined with the `spv.specConstant` op. Similar
|
|
|
|
|
to global variables, they do not generate SSA values and have symbols for
|
|
|
|
|
reference, too. `spv._reference_of` is needed to turn the symbol into a SSA
|
|
|
|
|
value for use in a function block.
|
2019-06-18 02:42:37 +08:00
|
|
|
|
|
2019-12-24 05:04:34 +08:00
|
|
|
|
The above choices enables functions in the SPIR-V dialect to be isolated and
|
|
|
|
|
explicit capturing.
|
|
|
|
|
|
|
|
|
|
#### Disallow implicit capturing in functions
|
|
|
|
|
|
|
|
|
|
* In SPIR-V specification, functions support implicit capturing: they can
|
|
|
|
|
reference SSA values defined in modules. In the SPIR-V dialect functions are
|
|
|
|
|
defined with `func` op, which disallows implicit capturing. This is more
|
|
|
|
|
friendly to compiler analyses and transformations. More discussions can be
|
|
|
|
|
found in the [Function](#function) section later.
|
|
|
|
|
|
|
|
|
|
### Model entry points and execution models as normal ops
|
|
|
|
|
|
|
|
|
|
* A SPIR-V module can have multiple entry points. And these entry points refer
|
|
|
|
|
to the function and interface variables. It’s not suitable to model them as
|
|
|
|
|
`spv.module` op attributes. We can model them as normal ops of using symbol
|
|
|
|
|
references.
|
|
|
|
|
* Similarly for execution modes, which are coupled with entry points, we can
|
|
|
|
|
model them as normal ops in `spv.module`'s region.
|
|
|
|
|
|
|
|
|
|
## Decorations
|
|
|
|
|
|
|
|
|
|
Annotations/decorations provide additional information on result ids. In SPIR-V,
|
|
|
|
|
all instructions can generate result ids, including value-computing and
|
|
|
|
|
type-defining ones.
|
|
|
|
|
|
|
|
|
|
For decorations on value result ids, we can just have a corresponding attribute
|
|
|
|
|
attached to the operation generating the SSA value. For example, for the
|
|
|
|
|
following SPIR-V:
|
|
|
|
|
|
|
|
|
|
```spirv
|
|
|
|
|
OpDecorate %v1 RelaxedPrecision
|
|
|
|
|
OpDecorate %v2 NoContraction
|
|
|
|
|
...
|
|
|
|
|
%v1 = OpFMul %float %0 %0
|
|
|
|
|
%v2 = OpFMul %float %1 %1
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
We can represent them in the SPIR-V dialect as:
|
|
|
|
|
|
|
|
|
|
```mlir
|
|
|
|
|
%v1 = "spv.FMul"(%0, %0) {RelaxedPrecision: unit} : (f32, f32) -> (f32)
|
|
|
|
|
%v2 = "spv.FMul"(%1, %1) {NoContraction: unit} : (f32, f32) -> (f32)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
This approach benefits transformations. Essentially those decorations are just
|
|
|
|
|
additional properties of the result ids (and thus their defining instructions).
|
|
|
|
|
In SPIR-V binary format, they are just represented as instructions. Literally
|
|
|
|
|
following SPIR-V binary format means we need to through def-use chains to find
|
|
|
|
|
the decoration instructions and query information from them.
|
|
|
|
|
|
|
|
|
|
For decorations on type result ids, notice that practically, only result ids
|
|
|
|
|
generated from composite types (e.g., `OpTypeArray`, `OpTypeStruct`) need to be
|
|
|
|
|
decorated for memory layouting purpose (e.g., `ArrayStride`, `Offset`, etc.);
|
|
|
|
|
scalar/vector types are required to be uniqued in SPIR-V. Therefore, we can just
|
|
|
|
|
encode them directly in the dialect-specific type.
|
|
|
|
|
|
2019-06-18 02:42:37 +08:00
|
|
|
|
## Types
|
|
|
|
|
|
2019-12-24 05:04:34 +08:00
|
|
|
|
Theoretically we can define all SPIR-V types using MLIR extensible type system,
|
|
|
|
|
but other than representational purity, it does not buy us more. Instead, we
|
|
|
|
|
need to maintain the code and invest in pretty printing them. So we prefer to
|
|
|
|
|
use builtin/standard types if possible.
|
|
|
|
|
|
|
|
|
|
The SPIR-V dialect reuses standard integer, float, and vector types:
|
|
|
|
|
|
|
|
|
|
Specification | Dialect
|
|
|
|
|
:----------------------------------: | :-------------------------------:
|
|
|
|
|
`OpTypeBool` | `i1`
|
|
|
|
|
`OpTypeInt <bitwidth>` | `i<bitwidth>`
|
|
|
|
|
`OpTypeFloat <bitwidth>` | `f<bitwidth>`
|
|
|
|
|
`OpTypeVector <scalar-type> <count>` | `vector<<count> x <scalar-type>>`
|
|
|
|
|
|
|
|
|
|
Similarly, `mlir::NoneType` can be used for SPIR-V `OpTypeVoid`; builtin
|
|
|
|
|
function types can be used for SPIR-V `OpTypeFunction` types.
|
|
|
|
|
|
|
|
|
|
The SPIR-V dialect and defines the following dialect-specific types:
|
2019-06-18 02:42:37 +08:00
|
|
|
|
|
2019-12-10 19:00:29 +08:00
|
|
|
|
```
|
2019-06-18 02:42:37 +08:00
|
|
|
|
spirv-type ::= array-type
|
2019-12-24 05:04:34 +08:00
|
|
|
|
| image-type
|
2019-06-18 02:42:37 +08:00
|
|
|
|
| pointer-type
|
|
|
|
|
| runtime-array-type
|
2019-12-24 05:04:34 +08:00
|
|
|
|
| struct-type
|
2019-06-18 02:42:37 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Array type
|
|
|
|
|
|
|
|
|
|
This corresponds to SPIR-V [array type][ArrayType]. Its syntax is
|
|
|
|
|
|
2019-12-10 19:00:29 +08:00
|
|
|
|
```
|
2019-06-18 02:42:37 +08:00
|
|
|
|
element-type ::= integer-type
|
|
|
|
|
| floating-point-type
|
|
|
|
|
| vector-type
|
|
|
|
|
| spirv-type
|
|
|
|
|
|
|
|
|
|
array-type ::= `!spv.array<` integer-literal `x` element-type `>`
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
For example,
|
|
|
|
|
|
2019-12-19 02:48:02 +08:00
|
|
|
|
```mlir
|
2019-06-18 02:42:37 +08:00
|
|
|
|
!spv.array<4 x i32>
|
|
|
|
|
!spv.array<16 x vector<4 x f32>>
|
|
|
|
|
```
|
|
|
|
|
|
2019-06-20 07:09:57 +08:00
|
|
|
|
### Image type
|
|
|
|
|
|
2019-06-22 05:51:58 +08:00
|
|
|
|
This corresponds to SPIR-V [image type][ImageType]. Its syntax is
|
2019-06-20 07:09:57 +08:00
|
|
|
|
|
2019-12-10 19:00:29 +08:00
|
|
|
|
```
|
2019-06-20 07:09:57 +08:00
|
|
|
|
dim ::= `1D` | `2D` | `3D` | `Cube` | <and other SPIR-V Dim specifiers...>
|
|
|
|
|
|
|
|
|
|
depth-info ::= `NoDepth` | `IsDepth` | `DepthUnknown`
|
|
|
|
|
|
|
|
|
|
arrayed-info ::= `NonArrayed` | `Arrayed`
|
|
|
|
|
|
|
|
|
|
sampling-info ::= `SingleSampled` | `MultiSampled`
|
|
|
|
|
|
|
|
|
|
sampler-use-info ::= `SamplerUnknown` | `NeedSampler` | `NoSampler`
|
|
|
|
|
|
|
|
|
|
format ::= `Unknown` | `Rgba32f` | <and other SPIR-V Image Formats...>
|
|
|
|
|
|
|
|
|
|
image-type ::= `!spv.image<` element-type `,` dim `,` depth-info `,`
|
|
|
|
|
arrayed-info `,` sampling-info `,`
|
|
|
|
|
sampler-use-info `,` format `>`
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
For example,
|
|
|
|
|
|
2019-12-24 05:04:34 +08:00
|
|
|
|
```mlir
|
2019-06-20 07:09:57 +08:00
|
|
|
|
!spv.image<f32, 1D, NoDepth, NonArrayed, SingleSampled, SamplerUnknown, Unknown>
|
|
|
|
|
!spv.image<f32, Cube, IsDepth, Arrayed, MultiSampled, NeedSampler, Rgba32f>
|
|
|
|
|
```
|
|
|
|
|
|
2019-06-18 02:42:37 +08:00
|
|
|
|
### Pointer type
|
|
|
|
|
|
|
|
|
|
This corresponds to SPIR-V [pointer type][PointerType]. Its syntax is
|
|
|
|
|
|
2019-12-10 19:00:29 +08:00
|
|
|
|
```
|
2019-06-18 02:42:37 +08:00
|
|
|
|
storage-class ::= `UniformConstant`
|
|
|
|
|
| `Uniform`
|
|
|
|
|
| `Workgroup`
|
|
|
|
|
| <and other storage classes...>
|
|
|
|
|
|
|
|
|
|
pointer-type ::= `!spv.ptr<` element-type `,` storage-class `>`
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
For example,
|
|
|
|
|
|
2019-12-19 02:48:02 +08:00
|
|
|
|
```mlir
|
2019-06-18 02:42:37 +08:00
|
|
|
|
!spv.ptr<i32, Function>
|
|
|
|
|
!spv.ptr<vector<4 x f32>, Uniform>
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Runtime array type
|
|
|
|
|
|
|
|
|
|
This corresponds to SPIR-V [runtime array type][RuntimeArrayType]. Its syntax is
|
|
|
|
|
|
2019-12-10 19:00:29 +08:00
|
|
|
|
```
|
2019-06-18 02:42:37 +08:00
|
|
|
|
runtime-array-type ::= `!spv.rtarray<` element-type `>`
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
For example,
|
|
|
|
|
|
2019-12-19 02:48:02 +08:00
|
|
|
|
```mlir
|
2019-06-18 02:42:37 +08:00
|
|
|
|
!spv.rtarray<i32>
|
|
|
|
|
!spv.rtarray<vector<4 x f32>>
|
|
|
|
|
```
|
|
|
|
|
|
2019-07-03 03:30:34 +08:00
|
|
|
|
### Struct type
|
|
|
|
|
|
|
|
|
|
This corresponds to SPIR-V [struct type][StructType]. Its syntax is
|
|
|
|
|
|
2019-12-10 19:00:29 +08:00
|
|
|
|
```
|
2019-09-20 05:49:29 +08:00
|
|
|
|
struct-member-decoration ::= integer-literal? spirv-decoration*
|
|
|
|
|
struct-type ::= `!spv.struct<` spirv-type (`[` struct-member-decoration `]`)?
|
|
|
|
|
(`, ` spirv-type (`[` struct-member-decoration `]`)?
|
2019-07-03 03:30:34 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
For Example,
|
|
|
|
|
|
2019-12-24 05:04:34 +08:00
|
|
|
|
```mlir
|
2019-07-03 03:30:34 +08:00
|
|
|
|
!spv.struct<f32>
|
|
|
|
|
!spv.struct<f32 [0]>
|
|
|
|
|
!spv.struct<f32, !spv.image<f32, 1D, NoDepth, NonArrayed, SingleSampled, SamplerUnknown, Unknown>>
|
|
|
|
|
!spv.struct<f32 [0], i32 [4]>
|
|
|
|
|
```
|
|
|
|
|
|
2019-09-06 03:45:08 +08:00
|
|
|
|
## Function
|
|
|
|
|
|
2019-12-24 05:04:34 +08:00
|
|
|
|
In SPIR-V, a function construct consists of multiple instructions involving
|
|
|
|
|
`OpFunction`, `OpFunctionParameter`, `OpLabel`, `OpFunctionEnd`.
|
|
|
|
|
|
|
|
|
|
```spirv
|
|
|
|
|
// int f(int v) { return v; }
|
|
|
|
|
%1 = OpTypeInt 32 0
|
|
|
|
|
%2 = OpTypeFunction %1 %1
|
|
|
|
|
%3 = OpFunction %1 %2
|
|
|
|
|
%4 = OpFunctionParameter %1
|
|
|
|
|
%5 = OpLabel
|
|
|
|
|
%6 = OpReturnValue %4
|
|
|
|
|
OpFunctionEnd
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
This construct is very clear yet quite verbose. It is intended for driver
|
|
|
|
|
consumption. There is little benefit to literally replicate this construct in
|
|
|
|
|
the SPIR-V dialect. Instead, we reuse the builtin `func` op to express functions
|
|
|
|
|
more concisely:
|
|
|
|
|
|
|
|
|
|
```mlir
|
|
|
|
|
func @f(%arg: i32) -> i32 {
|
|
|
|
|
"spv.ReturnValue"(%arg) : (i32) -> (i32)
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
A SPIR-V function can have at most one result. It cannot contain nested
|
|
|
|
|
functions or non-SPIR-V operations. `spv.module` verifies these requirements.
|
|
|
|
|
|
|
|
|
|
A major difference between the SPIR-V dialect and the SPIR-V specification for
|
|
|
|
|
functions is that the former are isolated and require explicit capturing, while
|
|
|
|
|
the latter allow implicit capturing. In SPIR-V specification, functions can
|
|
|
|
|
refer to SSA values (generated by constants, global variables, etc.) defined in
|
|
|
|
|
modules. The SPIR-V dialect adjusted how constants and global variables are
|
|
|
|
|
modeled to enable isolated functions. Isolated functions are more friendly to
|
|
|
|
|
compiler analyses and transformations. This also enables the SPIR-V dialect to
|
|
|
|
|
better utilize core infrastructure: many functionalities in the core
|
|
|
|
|
infrastructure requires ops to be isolated, e.g., the
|
|
|
|
|
[greedy pattern rewriter][GreedyPatternRewriter] can only act on ops isolated
|
|
|
|
|
from above.
|
|
|
|
|
|
|
|
|
|
(TODO: create a dedicated `spv.fn` op for SPIR-V functions.)
|
2019-09-06 03:45:08 +08:00
|
|
|
|
|
2019-10-29 06:58:11 +08:00
|
|
|
|
## Operations
|
|
|
|
|
|
2019-12-24 05:04:34 +08:00
|
|
|
|
In SPIR-V, instruction is a generalized concept; a SPIR-V module is just a
|
|
|
|
|
sequence of instructions. Declaring types, expressing computations, annotating
|
|
|
|
|
result ids, expressing control flows and others are all in the form of
|
|
|
|
|
instructions.
|
|
|
|
|
|
|
|
|
|
We only discuss instructions expressing computations here, which can be
|
|
|
|
|
represented via SPIR-V dialect ops. Module-level instructions for declarations
|
|
|
|
|
and definitions are represented differently in the SPIR-V dialect as explained
|
|
|
|
|
earlier in the [Module-level operations](#module-level-operations) section.
|
|
|
|
|
|
|
|
|
|
An instruction computes zero or one result from zero or more operands. The
|
|
|
|
|
result is a new result id. An operand can be a result id generated by a previous
|
|
|
|
|
instruction, an immediate value, or a case of an enum type. We can model result
|
|
|
|
|
id operands and results with MLIR SSA values; for immediate value and enum
|
|
|
|
|
cases, we can model them with MLIR attributes.
|
|
|
|
|
|
|
|
|
|
For example,
|
|
|
|
|
|
|
|
|
|
```spirv
|
|
|
|
|
%i32 = OpTypeInt 32 0
|
|
|
|
|
%c42 = OpConstant %i32 42
|
|
|
|
|
...
|
|
|
|
|
%3 = OpVariable %i32 Function 42
|
|
|
|
|
%4 = OpIAdd %i32 %c42 %c42
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
can be represented in the dialect as
|
|
|
|
|
|
|
|
|
|
```mlir
|
|
|
|
|
%0 = "spv.constant"() { value = 42 : i32 } : () -> i32
|
|
|
|
|
%1 = "spv.Variable"(%0) { storage_class = "Function" } : (i32) -> !spv.ptr<i32, Function>
|
|
|
|
|
%2 = "spv.IAdd"(%0, %0) : (i32, i32) -> i32
|
|
|
|
|
```
|
|
|
|
|
|
2019-10-29 06:58:11 +08:00
|
|
|
|
Operation documentation is written in each op's Op Definition Spec using
|
2019-12-27 21:34:26 +08:00
|
|
|
|
TableGen. A markdown version of the doc can be found at
|
|
|
|
|
[mlir.llvm.org][LlvmMlirSpirvDoc] or generated using `mlir-tblgen -gen-doc`.
|
2019-10-29 06:58:11 +08:00
|
|
|
|
|
2019-12-24 05:04:34 +08:00
|
|
|
|
### Ops from extended instruction sets
|
|
|
|
|
|
|
|
|
|
Analogically extended instruction set is a mechanism to import SPIR-V
|
|
|
|
|
instructions within another namespace. [`GLSL.std.450`][GlslStd450] is an
|
|
|
|
|
extended instruction set that provides common mathematical routines that should
|
|
|
|
|
be supported. Instead of modeling `OpExtInstImport` as a separate op and use a
|
|
|
|
|
single op to model `OpExtInst` for all extended instructions, we model each
|
|
|
|
|
SPIR-V instruction in an extended instruction set as a separate op with the
|
|
|
|
|
proper name prefix. For example, for
|
|
|
|
|
|
|
|
|
|
```spirv
|
|
|
|
|
%glsl = OpExtInstImport "GLSL.std.450"
|
|
|
|
|
|
|
|
|
|
%f32 = OpTypeFloat 32
|
|
|
|
|
%cst = OpConstant %f32 ...
|
|
|
|
|
|
|
|
|
|
%1 = OpExtInst %f32 %glsl 28 %cst
|
|
|
|
|
%2 = OpExtInst %f32 %glsl 31 %cst
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
we can have
|
|
|
|
|
|
|
|
|
|
```mlir
|
|
|
|
|
%1 = "spv.GLSL.Log"(%cst) : (f32) -> (f32)
|
2020-01-02 11:39:28 +08:00
|
|
|
|
%2 = "spv.GLSL.Sqrt"(%cst) : (f32) -> (f32)
|
2019-12-24 05:04:34 +08:00
|
|
|
|
```
|
|
|
|
|
|
2019-09-06 03:45:08 +08:00
|
|
|
|
## Control Flow
|
|
|
|
|
|
|
|
|
|
SPIR-V binary format uses merge instructions (`OpSelectionMerge` and
|
|
|
|
|
`OpLoopMerge`) to declare structured control flow. They explicitly declare a
|
|
|
|
|
header block before the control flow diverges and a merge block where control
|
|
|
|
|
flow subsequently converges. These blocks delimit constructs that must nest, and
|
|
|
|
|
can only be entered and exited in structured ways.
|
|
|
|
|
|
|
|
|
|
In the SPIR-V dialect, we use regions to mark the boundary of a structured
|
|
|
|
|
control flow construct. With this approach, it's easier to discover all blocks
|
|
|
|
|
belonging to a structured control flow construct. It is also more idiomatic to
|
|
|
|
|
MLIR system.
|
|
|
|
|
|
2019-10-03 02:00:50 +08:00
|
|
|
|
We introduce a `spv.selection` and `spv.loop` op for structured selections and
|
|
|
|
|
loops, respectively. The merge targets are the next ops following them. Inside
|
|
|
|
|
their regions, a special terminator, `spv._merge` is introduced for branching to
|
|
|
|
|
the merge target.
|
|
|
|
|
|
|
|
|
|
### Selection
|
|
|
|
|
|
|
|
|
|
`spv.selection` defines a selection construct. It contains one region. The
|
|
|
|
|
region should contain at least two blocks: one selection header block and one
|
|
|
|
|
merge block.
|
|
|
|
|
|
|
|
|
|
* The selection header block should be the first block. It should contain the
|
|
|
|
|
`spv.BranchConditional` or `spv.Switch` op.
|
|
|
|
|
* The merge block should be the last block. The merge block should only
|
|
|
|
|
contain a `spv._merge` op. Any block can branch to the merge block for early
|
|
|
|
|
exit.
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
+--------------+
|
|
|
|
|
| header block | (may have multiple outgoing branches)
|
|
|
|
|
+--------------+
|
|
|
|
|
/ | \
|
|
|
|
|
...
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+---------+ +---------+ +---------+
|
|
|
|
|
| case #0 | | case #1 | | case #2 | ... (may have branches between each other)
|
|
|
|
|
+---------+ +---------+ +---------+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
...
|
|
|
|
|
\ | /
|
|
|
|
|
v
|
|
|
|
|
+-------------+
|
|
|
|
|
| merge block | (may have multiple incoming branches)
|
|
|
|
|
+-------------+
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
For example, for the given function
|
|
|
|
|
|
|
|
|
|
```c++
|
|
|
|
|
void loop(bool cond) {
|
|
|
|
|
int x = 0;
|
|
|
|
|
if (cond) {
|
|
|
|
|
x = 1;
|
|
|
|
|
} else {
|
|
|
|
|
x = 2;
|
|
|
|
|
}
|
|
|
|
|
// ...
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
It will be represented as
|
|
|
|
|
|
|
|
|
|
```mlir
|
|
|
|
|
func @selection(%cond: i1) -> () {
|
|
|
|
|
%zero = spv.constant 0: i32
|
|
|
|
|
%one = spv.constant 1: i32
|
|
|
|
|
%two = spv.constant 2: i32
|
|
|
|
|
%x = spv.Variable init(%zero) : !spv.ptr<i32, Function>
|
|
|
|
|
|
|
|
|
|
spv.selection {
|
|
|
|
|
spv.BranchConditional %cond, ^then, ^else
|
|
|
|
|
|
|
|
|
|
^then:
|
|
|
|
|
spv.Store "Function" %x, %one : i32
|
|
|
|
|
spv.Branch ^merge
|
|
|
|
|
|
|
|
|
|
^else:
|
|
|
|
|
spv.Store "Function" %x, %two : i32
|
|
|
|
|
spv.Branch ^merge
|
|
|
|
|
|
|
|
|
|
^merge:
|
|
|
|
|
spv._merge
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ...
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
```
|
2019-09-06 03:45:08 +08:00
|
|
|
|
|
|
|
|
|
### Loop
|
|
|
|
|
|
2019-10-03 02:00:50 +08:00
|
|
|
|
`spv.loop` defines a loop construct. It contains one region. The region should
|
|
|
|
|
contain at least four blocks: one entry block, one loop header block, one loop
|
|
|
|
|
continue block, one merge block.
|
2019-09-06 03:45:08 +08:00
|
|
|
|
|
|
|
|
|
* The entry block should be the first block and it should jump to the loop
|
|
|
|
|
header block, which is the second block.
|
|
|
|
|
* The merge block should be the last block. The merge block should only
|
|
|
|
|
contain a `spv._merge` op. Any block except the entry block can branch to
|
|
|
|
|
the merge block for early exit.
|
|
|
|
|
* The continue block should be the second to last block and it should have a
|
|
|
|
|
branch to the loop header block.
|
|
|
|
|
* The loop continue block should be the only block, except the entry block,
|
|
|
|
|
branching to the loop header block.
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
+-------------+
|
|
|
|
|
| entry block | (one outgoing branch)
|
|
|
|
|
+-------------+
|
|
|
|
|
|
|
|
|
|
|
v
|
|
|
|
|
+-------------+ (two incoming branches)
|
|
|
|
|
| loop header | <-----+ (may have one or two outgoing branches)
|
|
|
|
|
+-------------+ |
|
|
|
|
|
|
|
|
|
|
|
... |
|
|
|
|
|
\ | / |
|
|
|
|
|
v |
|
|
|
|
|
+---------------+ | (may have multiple incoming branches)
|
|
|
|
|
| loop continue | -----+ (may have one or two outgoing branches)
|
|
|
|
|
+---------------+
|
|
|
|
|
|
|
|
|
|
...
|
|
|
|
|
\ | /
|
|
|
|
|
v
|
2019-09-26 02:57:13 +08:00
|
|
|
|
+-------------+ (may have multiple incoming branches)
|
2019-09-06 03:45:08 +08:00
|
|
|
|
| merge block |
|
|
|
|
|
+-------------+
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
The reason to have another entry block instead of directly using the loop header
|
|
|
|
|
block as the entry block is to satisfy region's requirement: entry block of
|
|
|
|
|
region may not have predecessors. We have a merge block so that branch ops can
|
|
|
|
|
reference it as successors. The loop continue block here corresponds to
|
|
|
|
|
"continue construct" using SPIR-V spec's term; it does not mean the "continue
|
|
|
|
|
block" as defined in the SPIR-V spec, which is "a block containing a branch to
|
|
|
|
|
an OpLoopMerge instruction’s Continue Target."
|
|
|
|
|
|
|
|
|
|
For example, for the given function
|
|
|
|
|
|
|
|
|
|
```c++
|
|
|
|
|
void loop(int count) {
|
|
|
|
|
for (int i = 0; i < count; ++i) {
|
|
|
|
|
// ...
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
It will be represented as
|
|
|
|
|
|
|
|
|
|
```mlir
|
|
|
|
|
func @loop(%count : i32) -> () {
|
|
|
|
|
%zero = spv.constant 0: i32
|
|
|
|
|
%one = spv.constant 1: i32
|
|
|
|
|
%var = spv.Variable init(%zero) : !spv.ptr<i32, Function>
|
|
|
|
|
|
|
|
|
|
spv.loop {
|
|
|
|
|
spv.Branch ^header
|
|
|
|
|
|
|
|
|
|
^header:
|
|
|
|
|
%val0 = spv.Load "Function" %var : i32
|
|
|
|
|
%cmp = spv.SLessThan %val0, %count : i32
|
|
|
|
|
spv.BranchConditional %cmp, ^body, ^merge
|
|
|
|
|
|
|
|
|
|
^body:
|
|
|
|
|
// ...
|
|
|
|
|
spv.Branch ^continue
|
|
|
|
|
|
|
|
|
|
^continue:
|
|
|
|
|
%val1 = spv.Load "Function" %var : i32
|
|
|
|
|
%add = spv.IAdd %val1, %one : i32
|
|
|
|
|
spv.Store "Function" %var, %add : i32
|
|
|
|
|
spv.Branch ^header
|
|
|
|
|
|
|
|
|
|
^merge:
|
|
|
|
|
spv._merge
|
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2019-10-29 06:58:11 +08:00
|
|
|
|
### Block argument for Phi
|
|
|
|
|
|
|
|
|
|
There are no direct Phi operations in the SPIR-V dialect; SPIR-V `OpPhi`
|
|
|
|
|
instructions are modelled as block arguments in the SPIR-V dialect. (See the
|
|
|
|
|
[Rationale][Rationale] doc for "Block Arguments vs Phi nodes".) Each block
|
|
|
|
|
argument corresponds to one `OpPhi` instruction in the SPIR-V binary format. For
|
|
|
|
|
example, for the following SPIR-V function `foo`:
|
|
|
|
|
|
|
|
|
|
```spirv
|
|
|
|
|
%foo = OpFunction %void None ...
|
|
|
|
|
%entry = OpLabel
|
|
|
|
|
%var = OpVariable %_ptr_Function_int Function
|
|
|
|
|
OpSelectionMerge %merge None
|
|
|
|
|
OpBranchConditional %true %true %false
|
|
|
|
|
%true = OpLabel
|
|
|
|
|
OpBranch %phi
|
|
|
|
|
%false = OpLabel
|
|
|
|
|
OpBranch %phi
|
|
|
|
|
%phi = OpLabel
|
|
|
|
|
%val = OpPhi %int %int_1 %false %int_0 %true
|
|
|
|
|
OpStore %var %val
|
|
|
|
|
OpReturn
|
|
|
|
|
%merge = OpLabel
|
|
|
|
|
OpReturn
|
|
|
|
|
OpFunctionEnd
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
It will be represented as:
|
|
|
|
|
|
|
|
|
|
```mlir
|
|
|
|
|
func @foo() -> () {
|
|
|
|
|
%var = spv.Variable : !spv.ptr<i32, Function>
|
|
|
|
|
|
|
|
|
|
spv.selection {
|
|
|
|
|
%true = spv.constant true
|
|
|
|
|
spv.BranchConditional %true, ^true, ^false
|
|
|
|
|
|
|
|
|
|
^true:
|
|
|
|
|
%zero = spv.constant 0 : i32
|
|
|
|
|
spv.Branch ^phi(%zero: i32)
|
|
|
|
|
|
|
|
|
|
^false:
|
|
|
|
|
%one = spv.constant 1 : i32
|
|
|
|
|
spv.Branch ^phi(%one: i32)
|
|
|
|
|
|
|
|
|
|
^phi(%arg: i32):
|
|
|
|
|
spv.Store "Function" %var, %arg : i32
|
|
|
|
|
spv.Return
|
|
|
|
|
|
|
|
|
|
^merge:
|
|
|
|
|
spv._merge
|
|
|
|
|
}
|
|
|
|
|
spv.Return
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2020-01-15 07:23:25 +08:00
|
|
|
|
## Target environment
|
|
|
|
|
|
|
|
|
|
SPIR-V aims to support multiple execution environments as specified by client
|
|
|
|
|
APIs. These execution environments affect the availability of certain SPIR-V
|
|
|
|
|
features. For example, a [Vulkan 1.1][VulkanSpirv] implementation must support
|
|
|
|
|
the 1.0, 1.1, 1.2, and 1.3 versions of SPIR-V and the 1.0 version of the SPIR-V
|
|
|
|
|
extended instructions for GLSL. Further Vulkan extensions may enable more SPIR-V
|
|
|
|
|
instructions.
|
|
|
|
|
|
|
|
|
|
SPIR-V compilation should also take into consideration of the execution
|
|
|
|
|
environment, so we generate SPIR-V modules valid for the target environment.
|
|
|
|
|
This is conveyed by the `spv.target_env` attribute. It is a triple of
|
|
|
|
|
|
|
|
|
|
* `version`: a 32-bit integer indicating the target SPIR-V version.
|
|
|
|
|
* `extensions`: a string array attribute containing allowed extensions.
|
|
|
|
|
* `capabilities`: a 32-bit integer array attribute containing allowed
|
|
|
|
|
capabilities.
|
|
|
|
|
|
|
|
|
|
Dialect conversion framework will utilize the information in `spv.target_env`
|
|
|
|
|
to properly filter out patterns and ops not available in the target execution
|
|
|
|
|
environment.
|
|
|
|
|
|
2019-12-24 05:04:34 +08:00
|
|
|
|
## Shader interface (ABI)
|
|
|
|
|
|
|
|
|
|
SPIR-V itself is just expressing computation happening on GPU device. SPIR-V
|
|
|
|
|
programs themselves are not enough for running workloads on GPU; a companion
|
|
|
|
|
host application is needed to manage the resources referenced by SPIR-V programs
|
|
|
|
|
and dispatch the workload. For the Vulkan execution environment, the host
|
|
|
|
|
application will be written using Vulkan API. Unlike CUDA, the SPIR-V program
|
|
|
|
|
and the Vulkan application are typically authored with different front-end
|
|
|
|
|
languages, which isolates these two worlds. Yet they still need to match
|
|
|
|
|
_interfaces_: the variables declared in a SPIR-V program for referencing
|
|
|
|
|
resources need to match with the actual resources managed by the application
|
|
|
|
|
regarding their parameters.
|
|
|
|
|
|
|
|
|
|
Still using Vulkan as an example execution environment, there are two primary
|
|
|
|
|
resource types in Vulkan: buffers and images. They are used to back various uses
|
|
|
|
|
that may differ regarding the classes of operations (load, store, atomic) to be
|
|
|
|
|
performed. These uses are differentiated via descriptor types. (For example,
|
|
|
|
|
uniform storage buffer descriptors can only support load operations while
|
|
|
|
|
storage buffer descriptors can support load, store, and atomic operations.)
|
|
|
|
|
Vulkan uses a binding model for resources. Resources are associated with
|
|
|
|
|
descriptors and descriptors are further grouped into sets. Each descriptor thus
|
|
|
|
|
has a set number and a binding number. Descriptors in the application
|
|
|
|
|
corresponds to variables in the SPIR-V program. Their parameters must match,
|
|
|
|
|
including but not limited to set and binding numbers.
|
|
|
|
|
|
|
|
|
|
Apart from buffers and images, there is other data that is set up by Vulkan and
|
|
|
|
|
referenced inside the SPIR-V program, for example, push constants. They also
|
|
|
|
|
have parameters that require matching between the two worlds.
|
|
|
|
|
|
|
|
|
|
The interface requirements are external information to the SPIR-V compilation
|
|
|
|
|
path in MLIR. Besides, each Vulkan application may want to handle resources
|
|
|
|
|
differently. To avoid duplication and to share common utilities, a SPIR-V shader
|
|
|
|
|
interface specification needs to be defined to provide the external requirements
|
|
|
|
|
to and guide the SPIR-V compilation path.
|
|
|
|
|
|
|
|
|
|
### Shader interface attributes
|
|
|
|
|
|
|
|
|
|
The SPIR-V dialect defines [a few attributes][MlirSpirvAbi] for specifying these
|
|
|
|
|
interfaces:
|
|
|
|
|
|
|
|
|
|
* `spv.entry_point_abi` is a struct attribute that should be attached to the
|
|
|
|
|
entry function. It contains:
|
|
|
|
|
* `local_size` for specifying the local work group size for the dispatch.
|
|
|
|
|
* `spv.interface_var_abi` is a struct attribute that should be attached to
|
|
|
|
|
each operand and result of the entry function. It contains:
|
|
|
|
|
* `descriptor_set` for specifying the descriptor set number for the
|
|
|
|
|
corresponding resource variable.
|
|
|
|
|
* `binding` for specifying the binding number for the corresponding
|
|
|
|
|
resource variable.
|
|
|
|
|
* `storage_class` for specifying the storage class for the corresponding
|
|
|
|
|
resource variable.
|
|
|
|
|
|
|
|
|
|
The SPIR-V dialect provides a [`LowerABIAttributesPass`][MlirSpirvPasses] for
|
|
|
|
|
consuming these attributes and create SPIR-V module complying with the
|
|
|
|
|
interface.
|
|
|
|
|
|
2019-10-29 06:58:11 +08:00
|
|
|
|
## Serialization and deserialization
|
2019-06-22 05:51:58 +08:00
|
|
|
|
|
2019-12-24 05:04:34 +08:00
|
|
|
|
Although the main objective of the SPIR-V dialect is to act as a proper IR for
|
|
|
|
|
compiler transformations, being able to serialize to and deserialize from the
|
|
|
|
|
binary format is still very valuable for many good reasons. Serialization
|
|
|
|
|
enables the artifacts of SPIR-V compilation to be consumed by a execution
|
|
|
|
|
environment; deserialization allows us to import SPIR-V binary modules and run
|
|
|
|
|
transformations on them. So serialization and deserialization is supported from
|
|
|
|
|
the very beginning of the development of the SPIR-V dialect.
|
|
|
|
|
|
2019-06-22 05:51:58 +08:00
|
|
|
|
The serialization library provides two entry points, `mlir::spirv::serialize()`
|
|
|
|
|
and `mlir::spirv::deserialize()`, for converting a MLIR SPIR-V module to binary
|
2019-12-24 05:04:34 +08:00
|
|
|
|
format and back. The [Code organization](#code-organization) explains more about
|
|
|
|
|
this.
|
2019-06-22 05:51:58 +08:00
|
|
|
|
|
2019-12-24 05:04:34 +08:00
|
|
|
|
Given that the focus is transformations, which inevitably means changes to the
|
|
|
|
|
binary module; so serialization is not designed to be a general tool for
|
|
|
|
|
investigating the SPIR-V binary module and does not guarantee roundtrip
|
|
|
|
|
equivalence (at least for now). For the latter, please use the
|
|
|
|
|
assembler/disassembler in the [SPIRV-Tools][SpirvTools] project.
|
2019-06-22 05:51:58 +08:00
|
|
|
|
|
2019-10-29 06:58:11 +08:00
|
|
|
|
A few transformations are performed in the process of serialization because of
|
|
|
|
|
the representational differences between SPIR-V dialect and binary format:
|
|
|
|
|
|
|
|
|
|
* Attributes on `spv.module` are emitted as their corresponding SPIR-V
|
|
|
|
|
instructions.
|
2019-12-24 05:04:34 +08:00
|
|
|
|
* Types are serialized into `OpType*` instructions in the SPIR-V binary module
|
|
|
|
|
section for types, constants, and global variables.
|
2019-10-29 06:58:11 +08:00
|
|
|
|
* `spv.constant`s are unified and placed in the SPIR-V binary module section
|
|
|
|
|
for types, constants, and global variables.
|
2019-12-24 05:04:34 +08:00
|
|
|
|
* Attributes on ops, if not part of the op's binary encoding, are emitted as
|
|
|
|
|
`OpDecorate*` instructions in the SPIR-V binary module section for
|
|
|
|
|
decorations.
|
2019-10-29 06:58:11 +08:00
|
|
|
|
* `spv.selection`s and `spv.loop`s are emitted as basic blocks with `Op*Merge`
|
|
|
|
|
instructions in the header block as required by the binary format.
|
2019-12-24 05:04:34 +08:00
|
|
|
|
* Block arguments are materialized as `OpPhi` instructions at the beginning of
|
|
|
|
|
the corresponding blocks.
|
2019-10-29 06:58:11 +08:00
|
|
|
|
|
|
|
|
|
Similarly, a few transformations are performed during deserialization:
|
|
|
|
|
|
2019-12-24 05:04:34 +08:00
|
|
|
|
* Instructions for execution environment requirements (extensions,
|
|
|
|
|
capabilities, extended instruction sets, etc.) will be placed as attributes
|
|
|
|
|
on `spv.module`.
|
|
|
|
|
* `OpType*` instructions will be converted into proper `mlir::Type`s.
|
2019-10-29 06:58:11 +08:00
|
|
|
|
* `OpConstant*` instructions are materialized as `spv.constant` at each use
|
|
|
|
|
site.
|
2019-12-24 05:04:34 +08:00
|
|
|
|
* `OpVariable` instructions will be converted to `spv.globalVariable` ops if
|
|
|
|
|
in module-level; otherwise they will be converted into `spv.Variable` ops.
|
|
|
|
|
* Every use of a module-level `OpVariable` instruction will materialize a
|
|
|
|
|
`spv._address_of` op to turn the symbol of the corresponding
|
|
|
|
|
`spv.globalVariable` into an SSA value.
|
|
|
|
|
* Every use of a `OpSpecConstant` instruction will materialize a
|
|
|
|
|
`spv._reference_of` op to turn the symbol of the corresponding
|
|
|
|
|
`spv.specConstant` into an SSA value.
|
2019-10-29 06:58:11 +08:00
|
|
|
|
* `OpPhi` instructions are converted to block arguments.
|
|
|
|
|
* Structured control flow are placed inside `spv.selection` and `spv.loop`.
|
|
|
|
|
|
2019-12-24 05:04:34 +08:00
|
|
|
|
## Conversions
|
|
|
|
|
|
2020-01-03 07:17:48 +08:00
|
|
|
|
One of the main features of MLIR is the ability to progressively lower from
|
|
|
|
|
dialects that capture programmer abstraction into dialects that are closer to a
|
|
|
|
|
machine representation, like SPIR-V dialect. This progressive lowering through
|
|
|
|
|
multiple dialects is enabled through the use of the
|
|
|
|
|
[DialectConversion][MlirDialectConversion] framework in MLIR. To simplify
|
|
|
|
|
targeting SPIR-V dialect using the Dialect Conversion framework, two utility
|
|
|
|
|
classes are provided.
|
|
|
|
|
|
|
|
|
|
(**Note** : While SPIR-V has some [validation rules][SpirvShaderValidation],
|
|
|
|
|
additional rules are imposed by [Vulkan execution environment][VulkanSpirv]. The
|
|
|
|
|
lowering described below implements both these requirements.)
|
|
|
|
|
|
2020-01-15 07:23:25 +08:00
|
|
|
|
### `SPIRVConversionTarget`
|
|
|
|
|
|
|
|
|
|
The `mlir::spirv::SPIRVConversionTarget` class derives from the
|
|
|
|
|
`mlir::ConversionTarget` class and serves as a utility to define a conversion
|
|
|
|
|
target satisfying a given [`spv.target_env`](#target-environment). It registers
|
|
|
|
|
proper hooks to check the dynamic legality of SPIR-V ops. Users can further
|
|
|
|
|
register other legality constraints into the returned `SPIRVConversionTarget`.
|
2020-01-03 07:17:48 +08:00
|
|
|
|
|
2020-01-15 07:23:25 +08:00
|
|
|
|
### `SPIRVTypeConverter`
|
2020-01-03 07:17:48 +08:00
|
|
|
|
|
2020-01-15 07:23:25 +08:00
|
|
|
|
The `mlir::SPIRVTypeConverter` derives from `mlir::TypeConverter` and provides
|
|
|
|
|
type conversion for standard types to SPIR-V types:
|
2020-01-03 07:17:48 +08:00
|
|
|
|
|
|
|
|
|
* [Standard Integer][MlirIntegerType] -> Standard Integer
|
|
|
|
|
* [Standard Float][MlirFloatType] -> Standard Float
|
|
|
|
|
* [Vector Type][MlirVectorType] -> Vector Type
|
|
|
|
|
* [Memref Type][MlirMemrefType] with static shape and stride -> `spv.array`
|
|
|
|
|
with number of elements obtained from the layout specification of the
|
|
|
|
|
`memref`, and same element type.
|
|
|
|
|
|
|
|
|
|
(TODO: Generate the conversion matrix from comments automatically)
|
|
|
|
|
|
|
|
|
|
[Index Type][MlirIndexType] need special handling since they are not directly
|
|
|
|
|
supported in SPIR-V. Currently the `index` type is converted to `i32`.
|
|
|
|
|
|
|
|
|
|
(TODO: Allow for configuring the integer width to use for `index` types in the
|
|
|
|
|
SPIR-V dialect)
|
|
|
|
|
|
2020-01-15 07:23:25 +08:00
|
|
|
|
### `SPIRVOpLowering`
|
2020-01-03 07:17:48 +08:00
|
|
|
|
|
2020-01-15 07:23:25 +08:00
|
|
|
|
`mlir::SPIRVOpLowering` is a base class that can be used to define the patterns
|
|
|
|
|
used for implementing the lowering. For now this only provides derived classes
|
|
|
|
|
access to an instance of `mlir::SPIRVTypeLowering` class.
|
2020-01-03 07:17:48 +08:00
|
|
|
|
|
|
|
|
|
### Utility functions for lowering
|
|
|
|
|
|
|
|
|
|
#### Setting shader interface
|
|
|
|
|
|
|
|
|
|
The method `mlir::spirv::setABIAttrs` allows setting the [shader interface
|
|
|
|
|
attributes](#shader-interface-abi) for a function that is to be an entry
|
|
|
|
|
point function within the `spv.module` on lowering. A later pass
|
|
|
|
|
`mlir::spirv::LowerABIAttributesPass` uses this information to lower the entry
|
|
|
|
|
point function and its ABI consistent with the Vulkan validation
|
|
|
|
|
rules. Specifically,
|
|
|
|
|
|
|
|
|
|
* Creates `spv.globalVariable`s for the arguments, and replaces all uses of
|
|
|
|
|
the argument with this variable. The SSA value used for replacement is
|
|
|
|
|
obtained using the `spv._address_of` operation.
|
|
|
|
|
* Adds the `spv.EntryPoint` and `spv.ExecutionMode` operations into the
|
|
|
|
|
`spv.module` for the entry function.
|
|
|
|
|
|
|
|
|
|
#### Setting layout for shader interface variables
|
|
|
|
|
|
|
|
|
|
SPIR-V validation rules for shaders require composite objects to be explicitly
|
|
|
|
|
laid out. If a `spv.globalVariable` is not explicitly laid out, the utility
|
|
|
|
|
method `mlir::spirv::decorateType` implements a layout consistent with
|
|
|
|
|
the [Vulkan shader requirements][VulkanShaderInterface].
|
|
|
|
|
|
|
|
|
|
#### Creating builtin variables
|
|
|
|
|
|
|
|
|
|
In SPIR-V dialect, builtins are represented using `spv.globalVariable`s, with
|
|
|
|
|
`spv._address_of` used to get a handle to the builtin as an SSA value. The
|
|
|
|
|
method `mlir::spirv::getBuiltinVariableValue` creates a `spv.globalVariable` for
|
|
|
|
|
the builtin in the current `spv.module` if it does not exist already, and
|
|
|
|
|
returns an SSA value generated from an `spv._address_of` operation.
|
|
|
|
|
|
|
|
|
|
### Current conversions to SPIR-V
|
|
|
|
|
|
|
|
|
|
Using the above infrastructure, conversion are implemented from
|
|
|
|
|
|
|
|
|
|
* [Standard Dialect][MlirStandardDialect] : Only arithmetic and logical
|
|
|
|
|
operations conversions are implemented.
|
|
|
|
|
* [GPU Dialect][MlirGpuDialect] : A module with the attribute
|
|
|
|
|
`gpu.kernel_module` is converted to a `spv.module`. A function within this
|
|
|
|
|
module with the attribute `gpu.kernel` is lowered as an entry function.
|
2019-12-24 05:04:34 +08:00
|
|
|
|
|
|
|
|
|
## Code organization
|
|
|
|
|
|
|
|
|
|
We aim to provide multiple libraries with clear dependencies for SPIR-V related
|
|
|
|
|
functionalities in MLIR so developers can just choose the needed components
|
|
|
|
|
without pulling in the whole world.
|
|
|
|
|
|
|
|
|
|
### The dialect
|
|
|
|
|
|
|
|
|
|
The code for the SPIR-V dialect resides in a few places:
|
|
|
|
|
|
|
|
|
|
* Public headers are placed in [include/mlir/Dialect/SPIRV][MlirSpirvHeaders].
|
|
|
|
|
* Libraries are placed in [lib/Dialect/SPIRV][MlirSpirvLibs].
|
|
|
|
|
* IR tests are placed in [test/Dialect/SPIRV][MlirSpirvTests].
|
|
|
|
|
* Unit tests are placed in [unittests/Dialect/SPIRV][MlirSpirvUnittests].
|
|
|
|
|
|
|
|
|
|
The whole SPIR-V dialect is exposed via multiple headers for better
|
|
|
|
|
organization:
|
|
|
|
|
|
|
|
|
|
* [SPIRVDialect.h][MlirSpirvDialect] defines the SPIR-V dialect.
|
|
|
|
|
* [SPIRVTypes.h][MlirSpirvTypes] defines all SPIR-V specific types.
|
2019-12-28 04:28:45 +08:00
|
|
|
|
* [SPIRVOps.h][MlirSPirvOpsH] defines all SPIR-V operations.
|
2019-12-24 05:04:34 +08:00
|
|
|
|
* [Serialization.h][MlirSpirvSerialization] defines the entry points for
|
|
|
|
|
serialization and deserialization.
|
|
|
|
|
|
|
|
|
|
The dialect itself, including all types and ops, is in the `MLIRSPIRV` library.
|
|
|
|
|
Serialization functionalities are in the `MLIRSPIRVSerialization` library.
|
|
|
|
|
|
|
|
|
|
### Op definitions
|
|
|
|
|
|
|
|
|
|
We use [Op Definition Spec][ODS] to define all SPIR-V ops. They are written in
|
|
|
|
|
TableGen syntax and placed in various `*Ops.td` files in the header directory.
|
|
|
|
|
Those `*Ops.td` files are organized according to the instruction categories used
|
|
|
|
|
in the SPIR-V specification, for example, an op belonging to the "Atomics
|
|
|
|
|
Instructions" section is put in the `SPIRVAtomicOps.td` file.
|
|
|
|
|
|
|
|
|
|
`SPIRVOps.td` serves as the master op definition file that includes all files
|
|
|
|
|
for specific categories.
|
|
|
|
|
|
|
|
|
|
`SPIRVBase.td` defines common classes and utilities used by various op
|
|
|
|
|
definitions. It contains the TableGen SPIR-V dialect definition, SPIR-V
|
|
|
|
|
versions, known extensions, various SPIR-V enums, TableGen SPIR-V types, and
|
|
|
|
|
base op classes, etc.
|
|
|
|
|
|
|
|
|
|
Many of the contents in `SPIRVBase.td`, e.g., the opcodes and various enums, and
|
|
|
|
|
all `*Ops.td` files can be automatically updated via a Python script, which
|
|
|
|
|
queries the SPIR-V specification and grammar. This greatly reduces the burden of
|
|
|
|
|
supporting new ops and keeping updated with the SPIR-V spec. More details on
|
|
|
|
|
this automated development can be found in the
|
|
|
|
|
[Automated development flow](#automated-development-flow) section.
|
|
|
|
|
|
|
|
|
|
### Dialect conversions
|
|
|
|
|
|
|
|
|
|
The code for conversions from other dialects to the SPIR-V dialect also resides
|
|
|
|
|
in a few places:
|
|
|
|
|
|
|
|
|
|
* From GPU dialect: headers are at
|
|
|
|
|
[include/mlir/Conversion/GPUTOSPIRV][MlirGpuToSpirvHeaders]; libraries are
|
|
|
|
|
at [lib/Conversion/GPUToSPIRV][MlirGpuToSpirvLibs].
|
|
|
|
|
* From standard dialect: headers are at
|
|
|
|
|
[include/mlir/Conversion/StandardTOSPIRV][MlirStdToSpirvHeaders]; libraries
|
|
|
|
|
are at [lib/Conversion/StandardToSPIRV][MlirStdToSpirvLibs].
|
|
|
|
|
|
|
|
|
|
These dialect to dialect conversions have their dedicated libraries,
|
|
|
|
|
`MLIRGPUToSPIRVTransforms` and `MLIRStandardToSPIRVTransforms`, respectively.
|
|
|
|
|
|
|
|
|
|
There are also common utilities when targeting SPIR-V from any dialect:
|
|
|
|
|
|
|
|
|
|
* [include/mlir/Dialect/SPIRV/Passes.h][MlirSpirvPasses] contains SPIR-V
|
|
|
|
|
specific analyses and transformations.
|
|
|
|
|
* [include/mlir/Dialect/SPIRV/SPIRVLowering.h][MlirSpirvLowering] contains
|
|
|
|
|
type converters and other utility functions.
|
|
|
|
|
|
|
|
|
|
These common utilities are implemented in the `MLIRSPIRVTransforms` library.
|
|
|
|
|
|
|
|
|
|
## Contribution
|
|
|
|
|
|
|
|
|
|
All kinds of contributions are highly appreciated! :) We have GitHub issues for
|
|
|
|
|
tracking the [dialect][GitHubDialectTracking] and
|
|
|
|
|
[lowering][GitHubLoweringTracking] development. You can find todo tasks there.
|
|
|
|
|
The [Code organization](#code-organization) section gives an overview of how
|
|
|
|
|
SPIR-V related functionalities are implemented in MLIR. This section gives more
|
|
|
|
|
concrete steps on how to contribute.
|
|
|
|
|
|
|
|
|
|
### Automated development flow
|
|
|
|
|
|
|
|
|
|
One of the goals of SPIR-V dialect development is to leverage both the SPIR-V
|
|
|
|
|
[human-readable specification][SpirvSpec] and
|
|
|
|
|
[machine-readable grammar][SpirvGrammar] to auto-generate as much contents as
|
|
|
|
|
possible. Specifically, the following tasks can be automated (partially or
|
|
|
|
|
fully):
|
|
|
|
|
|
|
|
|
|
* Adding support for a new operation.
|
|
|
|
|
* Adding support for a new SPIR-V enum.
|
|
|
|
|
* Serialization and deserialization of a new operation.
|
|
|
|
|
|
|
|
|
|
We achieve this using the Python script
|
|
|
|
|
[`gen_spirv_dialect.py`][GenSpirvUtilsPy]. It fetches the human-readable
|
|
|
|
|
specification and machine-readable grammar directly from the Internet and
|
|
|
|
|
updates various SPIR-V `*.td` files in place. The script gives us an automated
|
|
|
|
|
flow for adding support for new ops or enums.
|
|
|
|
|
|
|
|
|
|
Afterwards, we have SPIR-V specific `mlir-tblgen` backends for reading the Op
|
|
|
|
|
Definition Spec and generate various components, including (de)serialization
|
|
|
|
|
logic for ops. Together with standard `mlir-tblgen` backends, we auto-generate
|
|
|
|
|
all op classes, enum classes, etc.
|
|
|
|
|
|
|
|
|
|
In the following subsections, we list the detailed steps to follow for common
|
|
|
|
|
tasks.
|
|
|
|
|
|
|
|
|
|
### Add a new op
|
|
|
|
|
|
|
|
|
|
To add a new op, invoke the `define_inst.sh` script wrapper in utils/spirv.
|
|
|
|
|
`define_inst.sh` requires a few parameters:
|
|
|
|
|
|
|
|
|
|
```sh
|
|
|
|
|
./define_inst.sh <filename> <base-class-name> <opname>
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
For example, to define the op for `OpIAdd`, invoke
|
|
|
|
|
|
|
|
|
|
```sh
|
|
|
|
|
./define_inst.sh SPIRVArithmeticOps.td ArithmeticBinaryOp OpIAdd
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
where `SPIRVArithmeticOps.td` is the filename for hosting the new op and
|
|
|
|
|
`ArithmeticBinaryOp` is the direct base class the newly defined op will derive
|
|
|
|
|
from.
|
|
|
|
|
|
|
|
|
|
Similarly, to define the op for `OpAtomicAnd`,
|
|
|
|
|
|
|
|
|
|
```sh
|
|
|
|
|
./define_inst.sh SPIRVAtomicOps.td AtomicUpdateWithValueOp OpAtomicAnd
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Note that the generated SPIR-V op definition is just a best-effort template; it
|
|
|
|
|
is still expected to be updated to have more accurate traits, arguments, and
|
|
|
|
|
results.
|
|
|
|
|
|
2019-12-28 04:28:45 +08:00
|
|
|
|
It is also expected that a custom assembly form is defined for the new op,
|
|
|
|
|
which will require providing the parser and printer. The EBNF form of the
|
|
|
|
|
custom assembly should be described in the op's description and the parser
|
|
|
|
|
and printer should be placed in [`SPIRVOps.cpp`][MlirSpirvOpsCpp] with the
|
|
|
|
|
following signatures:
|
|
|
|
|
|
|
|
|
|
```c++
|
|
|
|
|
static ParseResult parse<spirv-op-symbol>Op(OpAsmParser &parser,
|
|
|
|
|
OperationState &state);
|
|
|
|
|
static void print(spirv::<spirv-op-symbol>Op op, OpAsmPrinter &printer);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
See any existing op as an example.
|
|
|
|
|
|
|
|
|
|
Verification should be provided for the new op to cover all the rules described
|
|
|
|
|
in the SPIR-V specification. Choosing the proper ODS types and attribute kinds,
|
|
|
|
|
which can be found in [`SPIRVBase.td`][MlirSpirvBase], can help here. Still
|
|
|
|
|
sometimes we need to manually write additional verification logic in
|
|
|
|
|
[`SPIRVOps.cpp`][MlirSpirvOpsCpp] in a function with the following signature:
|
|
|
|
|
|
|
|
|
|
```c++
|
|
|
|
|
static LogicalResult verify(spirv::<spirv-op-symbol>Op op);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
See any such function in [`SPIRVOps.cpp`][MlirSpirvOpsCpp] as an example.
|
|
|
|
|
|
|
|
|
|
If no additional verification is needed, one need to add the following to
|
|
|
|
|
the op's Op Definition Spec:
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
let verifier = [{ return success(); }];
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
To suppress the requirement of the above C++ verification function.
|
|
|
|
|
|
|
|
|
|
Tests for the op's custom assembly form and verification should be added to
|
|
|
|
|
the proper file in test/Dialect/SPIRV/.
|
|
|
|
|
|
2019-12-24 05:04:34 +08:00
|
|
|
|
The generated op will automatically gain the logic for (de)serialization.
|
|
|
|
|
However, tests still need to be coupled with the change to make sure no
|
|
|
|
|
surprises. Serialization tests live in test/Dialect/SPIRV/Serialization.
|
|
|
|
|
|
|
|
|
|
### Add a new enum
|
|
|
|
|
|
|
|
|
|
To add a new enum, invoke the `define_enum.sh` script wrapper in utils/spirv.
|
|
|
|
|
`define_enum.sh` expects the following parameters:
|
|
|
|
|
|
|
|
|
|
```sh
|
|
|
|
|
./define_enum.sh <enum-class-name>
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
For example, to add the definition for SPIR-V storage class in to
|
|
|
|
|
`SPIRVBase.td`:
|
|
|
|
|
|
|
|
|
|
```sh
|
|
|
|
|
./define_enum.sh StorageClass
|
|
|
|
|
```
|
|
|
|
|
|
2019-12-28 04:28:45 +08:00
|
|
|
|
### Add a new custom type
|
|
|
|
|
|
|
|
|
|
SPIR-V specific types are defined in [`SPIRVTypes.h`][MlirSpirvTypes]. See
|
|
|
|
|
examples there and the [tutorial][CustomTypeAttrTutorial] for defining new
|
|
|
|
|
custom types.
|
|
|
|
|
|
2019-12-24 05:04:34 +08:00
|
|
|
|
### Add a new conversion
|
|
|
|
|
|
2020-01-03 07:17:48 +08:00
|
|
|
|
To add conversion for a type update the `mlir::spirv::SPIRVTypeConverter` to
|
|
|
|
|
return the converted type (must be a valid SPIR-V type). See [Type
|
|
|
|
|
Conversion][MlirDialectConversionTypeConversion] for more details.
|
|
|
|
|
|
|
|
|
|
To lower an operation into SPIR-V dialect, implement a [conversion
|
|
|
|
|
pattern][MlirDialectConversionRewritePattern]. If the conversion requires type
|
|
|
|
|
conversion as well, the pattern must inherit from the
|
|
|
|
|
`mlir::spirv::SPIRVOpLowering` class to get access to
|
|
|
|
|
`mlir::spirv::SPIRVTypeConverter`. If the operation has a region, [signature
|
|
|
|
|
conversion][MlirDialectConversionSignatureConversion] might be needed as well.
|
|
|
|
|
|
|
|
|
|
**Note**: The current validation rules of `spv.module` require that all
|
|
|
|
|
operations contained within its region are valid operations in the SPIR-V
|
|
|
|
|
dialect.
|
2019-12-24 05:04:34 +08:00
|
|
|
|
|
|
|
|
|
[Spirv]: https://www.khronos.org/registry/spir-v/
|
|
|
|
|
[SpirvSpec]: https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html
|
|
|
|
|
[SpirvLogicalLayout]: https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html#_a_id_logicallayout_a_logical_layout_of_a_module
|
|
|
|
|
[SpirvGrammar]: https://raw.githubusercontent.com/KhronosGroup/SPIRV-Headers/master/include/spirv/unified1/spirv.core.grammar.json
|
2020-01-03 07:17:48 +08:00
|
|
|
|
[SpirvShaderValidation]: https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html#_a_id_shadervalidation_a_validation_rules_for_shader_a_href_capability_capabilities_a
|
2019-12-24 05:04:34 +08:00
|
|
|
|
[GlslStd450]: https://www.khronos.org/registry/spir-v/specs/1.0/GLSL.std.450.html
|
2019-06-18 02:42:37 +08:00
|
|
|
|
[ArrayType]: https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html#OpTypeArray
|
2019-07-03 03:30:34 +08:00
|
|
|
|
[ImageType]: https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html#OpTypeImage
|
2019-06-18 02:42:37 +08:00
|
|
|
|
[PointerType]: https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html#OpTypePointer
|
|
|
|
|
[RuntimeArrayType]: https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html#OpTypeRuntimeArray
|
2020-01-03 07:17:48 +08:00
|
|
|
|
[MlirDialectConversion]: ../DialectConversion.md
|
2019-07-03 03:30:34 +08:00
|
|
|
|
[StructType]: https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html#Structure
|
2019-12-24 05:04:34 +08:00
|
|
|
|
[SpirvTools]: https://github.com/KhronosGroup/SPIRV-Tools
|
2020-01-03 07:17:48 +08:00
|
|
|
|
[Rationale]: ../Rationale/#block-arguments-vs-phi-nodes
|
|
|
|
|
[ODS]: ../OpDefinitions/
|
2019-12-27 21:34:26 +08:00
|
|
|
|
[GreedyPatternRewriter]: https://github.com/llvm/llvm-project/blob/master/mlir/lib/Transforms/Utils/GreedyPatternRewriteDriver.cpp
|
2020-01-03 07:17:48 +08:00
|
|
|
|
[MlirDialectConversionTypeConversion]: ../DialectConversion.md#type-converter
|
|
|
|
|
[MlirDialectConversionRewritePattern]: ../DialectConversion.md#conversion-patterns
|
|
|
|
|
[MlirDialectConversionSignatureConversion]: ../DialectConversion.md#region-signature-conversion
|
|
|
|
|
[MlirIntegerType]: ../LangRef.md#integer-type
|
|
|
|
|
[MlirFloatType]: ../LangRef.md#floating-point-types
|
|
|
|
|
[MlirVectorType]: ../LangRef.md#vector-type
|
|
|
|
|
[MlirMemrefType]: ../LangRef.md#memref-type
|
|
|
|
|
[MlirIndexType]: ../LangRef.md#index-type
|
|
|
|
|
[MlirGpuDialect]: ../Dialects/GPU.md
|
|
|
|
|
[MlirStandardDialect]: ../Dialects/Standard.md
|
2020-01-02 11:39:28 +08:00
|
|
|
|
[MlirSpirvHeaders]: https://github.com/llvm/llvm-project/tree/master/mlir/include/mlir/Dialect/SPIRV
|
|
|
|
|
[MlirSpirvLibs]: https://github.com/llvm/llvm-project/tree/master/mlir/lib/Dialect/SPIRV
|
|
|
|
|
[MlirSpirvTests]: https://github.com/llvm/llvm-project/tree/master/mlir/test/Dialect/SPIRV
|
|
|
|
|
[MlirSpirvUnittests]: https://github.com/llvm/llvm-project/tree/master/mlir/unittests/Dialect/SPIRV
|
|
|
|
|
[MlirGpuToSpirvHeaders]: https://github.com/llvm/llvm-project/tree/master/mlir/include/mlir/Conversion/GPUToSPIRV
|
|
|
|
|
[MlirGpuToSpirvLibs]: https://github.com/llvm/llvm-project/tree/master/mlir/lib/Conversion/GPUToSPIRV
|
|
|
|
|
[MlirStdToSpirvHeaders]: https://github.com/llvm/llvm-project/tree/master/mlir/include/mlir/Conversion/StandardToSPIRV
|
|
|
|
|
[MlirStdToSpirvLibs]: https://github.com/llvm/llvm-project/tree/master/mlir/lib/Conversion/StandardToSPIRV
|
2019-12-27 21:34:26 +08:00
|
|
|
|
[MlirSpirvDialect]: https://github.com/llvm/llvm-project/blob/master/mlir/include/mlir/Dialect/SPIRV/SPIRVDialect.h
|
|
|
|
|
[MlirSpirvTypes]: https://github.com/llvm/llvm-project/blob/master/mlir/include/mlir/Dialect/SPIRV/SPIRVTypes.h
|
2019-12-28 04:28:45 +08:00
|
|
|
|
[MlirSpirvOpsH]: https://github.com/llvm/llvm-project/blob/master/mlir/include/mlir/Dialect/SPIRV/SPIRVOps.h
|
2019-12-27 21:34:26 +08:00
|
|
|
|
[MlirSpirvSerialization]: https://github.com/llvm/llvm-project/blob/master/mlir/include/mlir/Dialect/SPIRV/Serialization.h
|
|
|
|
|
[MlirSpirvBase]: https://github.com/llvm/llvm-project/blob/master/mlir/include/mlir/Dialect/SPIRV/SPIRVBase.td
|
|
|
|
|
[MlirSpirvPasses]: https://github.com/llvm/llvm-project/blob/master/mlir/include/mlir/Dialect/SPIRV/Passes.h
|
|
|
|
|
[MlirSpirvLowering]: https://github.com/llvm/llvm-project/blob/master/mlir/include/mlir/Dialect/SPIRV/SPIRVLowering.h
|
|
|
|
|
[MlirSpirvAbi]: https://github.com/llvm/llvm-project/blob/master/mlir/include/mlir/Dialect/SPIRV/SPIRVLowering.td
|
2019-12-28 04:28:45 +08:00
|
|
|
|
[MlirSpirvOpsCpp]: https://github.com/llvm/llvm-project/blob/master/mlir/lib/Dialect/SPIRV/SPIRVOps.cpp
|
2019-12-24 05:04:34 +08:00
|
|
|
|
[GitHubDialectTracking]: https://github.com/tensorflow/mlir/issues/302
|
|
|
|
|
[GitHubLoweringTracking]: https://github.com/tensorflow/mlir/issues/303
|
2019-12-27 21:34:26 +08:00
|
|
|
|
[GenSpirvUtilsPy]: https://github.com/llvm/llvm-project/blob/master/mlir/utils/spirv/gen_spirv_dialect.py
|
2020-01-03 07:17:48 +08:00
|
|
|
|
[LlvmMlirSpirvDoc]: ../Dialects/SPIRVOps/
|
|
|
|
|
[CustomTypeAttrTutorial]: ../DefiningAttributesAndTypes/
|
|
|
|
|
[VulkanSpirv]: https://renderdoc.org/vkspec_chunked/chap40.html#spirvenv
|
|
|
|
|
[VulkanShaderInterface]: https://renderdoc.org/vkspec_chunked/chap14.html#interfaces-resources
|