summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLei Zhang <antiagainst@google.com>2019-12-23 13:04:34 -0800
committerA. Unique TensorFlower <gardener@tensorflow.org>2019-12-23 16:15:52 -0800
commita5d5d2912506322b224eff0428de796a5ef7c1a4 (patch)
treef5293e4550fc214f9c8897ad4c3ca8aa263c4ad4
parentab46543cebbd8845262a50291f296e453d110a14 (diff)
downloadbcm5719-llvm-a5d5d2912506322b224eff0428de796a5ef7c1a4.tar.gz
bcm5719-llvm-a5d5d2912506322b224eff0428de796a5ef7c1a4.zip
Update SPIR-V.md
This CL updates SPIR-V.md to reflect recent developments in the SPIR-V dialect and its conversions. Along the way, also updates the doc for define_inst.sh. PiperOrigin-RevId: 286933546
-rw-r--r--mlir/g3doc/Dialects/SPIR-V.md651
-rwxr-xr-xmlir/utils/spirv/define_inst.sh26
2 files changed, 612 insertions, 65 deletions
diff --git a/mlir/g3doc/Dialects/SPIR-V.md b/mlir/g3doc/Dialects/SPIR-V.md
index b753435c3c4..1d72e5449d3 100644
--- a/mlir/g3doc/Dialects/SPIR-V.md
+++ b/mlir/g3doc/Dialects/SPIR-V.md
@@ -1,47 +1,101 @@
# SPIR-V Dialect
-This document defines the SPIR-V dialect in MLIR.
-
-[SPIR-V][SPIR-V] 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.
+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
+means we can still have straightforward serailization and deserailization for
+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.
-## Design Principles
+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.
-SPIR-V defines a stable binary format for hardware driver consumption.
-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 driver consumption
-but not necessarily compiler transformations.
+### Dialect scopes
-The purpose of the SPIR-V dialect is to serve as the "proxy" of the binary
-format and to facilitate transformations. Therefore, it should
-
-* Stay as the same semantic level and try to be a mechanical 1:1 mapping;
-* But deviate representationally if possible with MLIR mechanisms.
-* Be straightforward to serialize into and deserialize from the SPIR-V binary
- format.
+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.
## Conventions
-The SPIR-V dialect has the following conventions:
+The SPIR-V dialect adopts the following conventions for IR:
* The prefix for all SPIR-V types and operations are `spv.`.
-* Ops that directly mirror instructions in the binary format have `CamelCase`
+* 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`
names that are the same as the instruction opnames (without the `Op`
- prefix). For example, `spv.FMul` is a direct mirror of `OpFMul`. They will
- be serialized into and deserialized from one instruction.
+ 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.
* Ops with `snake_case` names are those that have different representation
- from corresponding instructions (or concepts) in the binary format. These
+ from corresponding instructions (or concepts) in the specification. These
ops are mostly for defining the SPIR-V structure. For example, `spv.module`
- and `spv.constant`. They may correspond to zero or more instructions during
+ and `spv.constant`. They may correspond to one or more instructions during
(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.
+(TODO: consider merging the last two cases and adopting `spv.mlir.` prefix for
+them.)
+
## Module
A SPIR-V module is defined via the `spv.module` op, which has one region that
@@ -49,27 +103,77 @@ contains one block. Model-level instructions, including function definitions,
are all placed inside the block. Functions are defined using the builtin `func`
op.
-Compared to the binary format, we adjust how certain module-level SPIR-V
-instructions are represented in the SPIR-V dialect. Notably,
+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
* 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
- execution environment. It's easier to probe them if on the module op
- itself.
+ execution environment. It's easier to probe them if on the module op itself.
* Annotations/decoration instructions are "folded" into the instructions they
decorate and represented as attributes on those ops. This eliminates
potential forward references of SSA values, improves IR readability, and
- makes querying the annotations more direct.
+ makes querying the annotations more direct. More discussions can be found in
+ the [`Decorations`](#decorations) section.
+
+#### Model types with MLIR custom types
+
* Types are represented using MLIR standard types and SPIR-V dialect specific
- types. There are no type declaration ops in the SPIR-V dialect.
+ 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
+
* 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
- isolated and explicit capturing.
+ isolated and explicit capturing. Constants are cheap to duplicate given
+ attributes are uniqued in `MLIRContext`.
+
+#### Adopt symbol-based global variables and specialization constant
+
* 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
@@ -79,15 +183,90 @@ instructions are represented in the SPIR-V dialect. Notably,
reference, too. `spv._reference_of` is needed to turn the symbol into a SSA
value for use in a function block.
+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.
+
## Types
-The SPIR-V dialect reuses standard integer, float, and vector types and defines
-the following dialect-specific types:
+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:
```
spirv-type ::= array-type
+ | image-type
| pointer-type
| runtime-array-type
+ | struct-type
```
### Array type
@@ -134,7 +313,7 @@ image-type ::= `!spv.image<` element-type `,` dim `,` depth-info `,`
For example,
-```
+```mlir
!spv.image<f32, 1D, NoDepth, NonArrayed, SingleSampled, SamplerUnknown, Unknown>
!spv.image<f32, Cube, IsDepth, Arrayed, MultiSampled, NeedSampler, Rgba32f>
```
@@ -186,7 +365,7 @@ struct-type ::= `!spv.struct<` spirv-type (`[` struct-member-decoration `]`)?
For Example,
-```
+```mlir
!spv.struct<f32>
!spv.struct<f32 [0]>
!spv.struct<f32, !spv.image<f32, 1D, NoDepth, NonArrayed, SingleSampled, SamplerUnknown, Unknown>>
@@ -195,16 +374,115 @@ For Example,
## Function
-A SPIR-V function is defined using the builtin `func` op. `spv.module` verifies
-that the functions inside it comply with SPIR-V requirements: at most one
-result, no nested functions, and so on.
+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.)
## Operations
+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
+```
+
Operation documentation is written in each op's Op Definition Spec using
TableGen. A markdown version of the doc can be generated using `mlir-tblgen
-gen-doc`.
+### 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)
+%2 = "spv.GLSL.Sqrt(%cst) : (f32) -> (f32)
+```
+
## Control Flow
SPIR-V binary format uses merge instructions (`OpSelectionMerge` and
@@ -447,44 +725,315 @@ func @foo() -> () {
}
```
+## 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.
+
## Serialization and deserialization
+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.
+
The serialization library provides two entry points, `mlir::spirv::serialize()`
and `mlir::spirv::deserialize()`, for converting a MLIR SPIR-V module to binary
-format and back.
+format and back. The [Code organization](#code-organization) explains more about
+this.
-The purpose of this library is to enable importing SPIR-V binary modules to run
-transformations on them and exporting SPIR-V modules to be consumed by execution
-environments. The focus is transformations, which inevitably means changes to
-the binary module; so it 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][SPIRV-Tools] project.
+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.
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.
+* Types are serialized into `OpType*` instructions in the SPIR-V binary module
+ section for types, constants, and global variables.
* `spv.constant`s are unified and placed in the SPIR-V binary module section
for types, constants, and global variables.
+* 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.
* `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.
+* Block arguments are materialized as `OpPhi` instructions at the beginning of
+ the corresponding blocks.
Similarly, a few transformations are performed during deserialization:
-* Instructions for execution environment requirements will be placed as
- attributes on `spv.module`.
+* 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.
* `OpConstant*` instructions are materialized as `spv.constant` at each use
site.
+* `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.
* `OpPhi` instructions are converted to block arguments.
* Structured control flow are placed inside `spv.selection` and `spv.loop`.
-[SPIR-V]: https://www.khronos.org/registry/spir-v/
+## Conversions
+
+(TODO: expand this section)
+
+## 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.
+* [SPIRVOps.h][MlirSPirvOps] defines all SPIR-V operations.
+* [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.
+
+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
+```
+
+### Add a new conversion
+
+(TODO: add details for this section)
+
+[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
+[GlslStd450]: https://www.khronos.org/registry/spir-v/specs/1.0/GLSL.std.450.html
[ArrayType]: https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html#OpTypeArray
[ImageType]: https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html#OpTypeImage
[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
[StructType]: https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html#Structure
-[SPIRV-Tools]: https://github.com/KhronosGroup/SPIRV-Tools
+[SpirvTools]: https://github.com/KhronosGroup/SPIRV-Tools
[Rationale]: https://github.com/tensorflow/mlir/blob/master/g3doc/Rationale.md#block-arguments-vs-phi-nodes
+[ODS]: https://github.com/tensorflow/mlir/blob/master/g3doc/OpDefinitions.md
+[GreedyPatternRewriter]: https://github.com/tensorflow/mlir/blob/master/lib/Transforms/Utils/GreedyPatternRewriteDriver.cpp
+[MlirSpirvHeaders]: https://github.com/tensorflow/mlir/tree/master/include/mlir/Dialect/SPIRV
+[MlirSpirvLibs]: https://github.com/tensorflow/mlir/tree/master/lib/Dialect/SPIRV
+[MlirSpirvTests]: https://github.com/tensorflow/mlir/tree/master/test/Dialect/SPIRV
+[MlirSpirvUnittests]: https://github.com/tensorflow/mlir/tree/master/unittests/Dialect/SPIRV
+[MlirGpuToSpirvHeaders]: https://github.com/tensorflow/mlir/tree/master/include/mlir/Conversion/GPUToSPIRV
+[MlirGpuToSpirvLibs]: https://github.com/tensorflow/mlir/tree/master/lib/Conversion/GPUToSPIRV
+[MlirStdToSpirvHeaders]: https://github.com/tensorflow/mlir/tree/master/include/mlir/Conversion/StandardToSPIRV
+[MlirStdToSpirvLibs]: https://github.com/tensorflow/mlir/tree/master/lib/Conversion/StandardToSPIRV
+[MlirSpirvDialect]: https://github.com/tensorflow/mlir/blob/master/include/mlir/Dialect/SPIRV/SPIRVDialect.h
+[MlirSpirvTypes]: https://github.com/tensorflow/mlir/blob/master/include/mlir/Dialect/SPIRV/SPIRVTypes.h
+[MlirSpirvOps]: https://github.com/tensorflow/mlir/blob/master/include/mlir/Dialect/SPIRV/SPIRVOps.h
+[MlirSpirvSerialization]: https://github.com/tensorflow/mlir/blob/master/include/mlir/Dialect/SPIRV/Serialization.h
+[MlirSpirvBase]: https://github.com/tensorflow/mlir/blob/master/include/mlir/Dialect/SPIRV/SPIRVBase.td
+[MlirSpirvPasses]: https://github.com/tensorflow/mlir/blob/master/include/mlir/Dialect/SPIRV/Passes.h
+[MlirSpirvLowering]: https://github.com/tensorflow/mlir/blob/master/include/mlir/Dialect/SPIRV/SPIRVLowering.h
+[MlirSpirvAbi]: https://github.com/tensorflow/mlir/blob/master/include/mlir/Dialect/SPIRV/SPIRVLowering.td
+[GitHubDialectTracking]: https://github.com/tensorflow/mlir/issues/302
+[GitHubLoweringTracking]: https://github.com/tensorflow/mlir/issues/303
+[GenSpirvUtilsPy]: https://github.com/tensorflow/mlir/blob/master/utils/spirv/gen_spirv_dialect.py
diff --git a/mlir/utils/spirv/define_inst.sh b/mlir/utils/spirv/define_inst.sh
index 322c67e8da8..db58813c7b9 100755
--- a/mlir/utils/spirv/define_inst.sh
+++ b/mlir/utils/spirv/define_inst.sh
@@ -6,32 +6,30 @@
# Script for defining a new op using SPIR-V spec from the Internet.
#
# Run as:
-# ./define_inst.sh <filename> <inst_category> (<opname>)*
+# ./define_inst.sh <filename> <baseclass> (<opname>)*
# <filename> is required, which is the file name of MLIR SPIR-V op definitions
# spec.
-# <inst_category> is required. It can be one of
-# (Op|ArithmeticOp|LogicalOp|ControlFlowOp|StructureOp). Based on the
-# inst_category the file SPIRV<inst_category>s.td is updated with the
-# instruction definition. If <opname> is missing, this script updates existing
-# ones in SPIRV<inst_category>s.td
+# <baseclass> is required. It will be the direct base class the newly defined
+# op will drive from.
+# If <opname> is missing, this script updates existing ones in <filename>.
# For example:
-# ./define_inst.sh SPIRVArithmeticOps.td ArithmeticOp OpIAdd
+# ./define_inst.sh SPIRVArithmeticOps.td ArithmeticBianryOp OpIAdd
# ./define_inst.sh SPIRVLogicalOps.td LogicalOp OpFOrdEqual
set -e
file_name=$1
-inst_category=$2
+baseclass=$2
-case $inst_category in
- Op | ArithmeticOp | LogicalOp | CastOp | ControlFlowOp | StructureOp | AtomicUpdateOp | AtomicUpdateWithValueOp)
+case $baseclass in
+ Op | ArithmeticBinaryOp | ArithmeticUnaryOp | LogicalBinaryOp | LogicalUnaryOp | CastOp | ControlFlowOp | StructureOp | AtomicUpdateOp | AtomicUpdateWithValueOp)
;;
*)
- echo "Usage : " $0 "<filename> <inst_category> (<opname>)*"
+ echo "Usage : " $0 "<filename> <baseclass> (<opname>)*"
echo "<filename> is the file name of MLIR SPIR-V op definitions spec"
- echo "<inst_category> must be one of " \
- "(Op|ArithmeticOp|LogicalOp|CastOp|ControlFlowOp|StructureOp|AtomicUpdateOp)"
+ echo "<baseclass> must be one of " \
+ "(Op|ArithmeticBinaryOp|ArithmeticUnaryOp|LogicalBinaryOp|LogicalUnaryOp|CastOp|ControlFlowOp|StructureOp|AtomicUpdateOp)"
exit 1;
;;
esac
@@ -45,7 +43,7 @@ current_dir="$(dirname "$current_file")"
python3 ${current_dir}/gen_spirv_dialect.py \
--op-td-path \
${current_dir}/../../include/mlir/Dialect/SPIRV/${file_name} \
- --inst-category $inst_category --new-inst "$@"
+ --inst-category $baseclass --new-inst "$@"
${current_dir}/define_opcodes.sh "$@"
OpenPOWER on IntegriCloud