blob: 41b82389e719f1c8ef4e8580b25d6891d4c5da43 [file] [log] [blame]
//===-- SPIRVOps.td - MLIR SPIR-V Op Definitions Spec ------*- tablegen -*-===//
//
// Copyright 2019 The MLIR Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// =============================================================================
//
// This file contains ops for defining the SPIR-V structure: module, function,
// and module-level operations. The representational form of these ops deviate
// from the SPIR-V binary format in order to utilize MLIR mechanisms.
//
//===----------------------------------------------------------------------===//
#ifdef SPIRV_STRUCTURE_OPS
#else
#define SPIRV_STRUCTURE_OPS
#ifdef SPIRV_BASE
#else
include "mlir/SPIRV/SPIRVBase.td"
#endif // SPIRV_BASE
def SPV_ModuleOp : SPV_Op<"module", []> {
let summary = "The top-level op that defines a SPIR-V module";
let description = [{
This op defines a SPIR-V module using a MLIR region. The region contains
one block. Module-level operations, including functions definitions,
are all placed in this block.
Using an op with a region to define a SPIR-V module enables "embedding"
SPIR-V modules in other dialects in a clean manner: this op guarantees
the validaty and serializability of a SPIR-V module and thus serves as
a clear-cut boundary.
This op takes no operands and generates no results. This op should not
implicitly capture values from the enclosing environment.
This op has only one region, which only contains one block. The block
must be terminated via the `spv._module_end` op.
### Custom assembly form
``` {.ebnf}
addressing-model ::= `"Logical"` | `"Physical32"` | `"Physical64"`
memory-model ::= `"Simple"` | `"GLSL450"` | `"OpenCL"` | `"VulkanKHR"`
spv-module-op ::= `spv.module` addressing-model memory-model
region
(`attributes` attribute-dict)?
```
For example:
```
spv.module "Logical" "VulkanKHR" { }
spv.module "Logical" "VulkanKHR" {
func @do_nothing() -> () {
spv.Return
}
} attributes {
capability = ["Shader"],
extension = ["SPV_KHR_16bit_storage"]
}
```
}];
let arguments = (ins
OptionalAttr<StrArrayAttr>:$capabilities,
OptionalAttr<StrArrayAttr>:$extensions,
OptionalAttr<StrArrayAttr>:$extended_instruction_sets,
SPV_AddressingModelAttr:$addressing_model,
SPV_MemoryModelAttr:$memory_model
);
let results = (outs);
let regions = (region SizedRegion<1>:$body);
let builders = [OpBuilder<"Builder *, OperationState *state">];
}
def SPV_ModuleEndOp : SPV_Op<"_module_end", [Terminator]> {
let summary = "The pseudo op that ends a SPIR-V module";
let description = [{
This op terminates the only block inside a `spv.module`'s only region.
This op does not have a corresponding SPIR-V instruction and thus will
not be serialized into the binary format; it is used solely to satisfy
the structual requirement that an block must be ended with a terminator.
}];
let arguments = (ins);
let results = (outs);
let parser = [{ return parseNoIOOp(parser, result); }];
let printer = [{ printNoIOOp(getOperation(), p); }];
let verifier = [{ return verifyModuleOnly(*this); }];
}
def SPV_ConstantOp : SPV_Op<"constant", [NoSideEffect]> {
let summary = "The op that declares a SPIR-V constant";
let description = [{
This op declares a SPIR-V constant. SPIR-V has multiple constant
instructions covering different constant types:
* `OpConstantTrue` and `OpConstantFalse` for boolean constants
* `OpConstant` for scalar constants
* `OpConstantComposite` for composite constants
* `OpConstantNull` for null constants
* ...
Having such a plethora of constant instructions renders IR transformations
more tedious. Therefore, we use a single `spv.constant` op to represent
them all. Note that conversion between those SPIR-V constant instructions
and this op is purely mechanical; so it can be scoped to the binary
(de)serialzation process.
### Custom assembly form
``` {.ebnf}
spv-constant-op ::= ssa-id `=` `spv.constant` attribute-value
(`:` spirv-type)?
```
For example:
```
%0 = spv.constant true
%1 = spv.constant dense<vector<2xf32>, [2, 3]>
%2 = spv.constant [dense<vector<2xf32>, 3.0>] : !spv.array<1xvector<2xf32>>
```
}];
let arguments = (ins
AnyAttr:$value
);
let results = (outs
SPV_Type:$constant
);
}
#endif // SPIRV_STRUCTURE_OPS