964 lines
29 KiB
TableGen
964 lines
29 KiB
TableGen
//===-- SPIRVStructureOps.td - MLIR SPIR-V Structure Ops ---*- tablegen -*-===//
|
||
//
|
||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||
// See https://llvm.org/LICENSE.txt for license information.
|
||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||
//
|
||
//===----------------------------------------------------------------------===//
|
||
//
|
||
// 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.
|
||
//
|
||
//===----------------------------------------------------------------------===//
|
||
|
||
#ifndef MLIR_DIALECT_SPIRV_IR_STRUCTURE_OPS
|
||
#define MLIR_DIALECT_SPIRV_IR_STRUCTURE_OPS
|
||
|
||
include "mlir/Dialect/SPIRV/IR/SPIRVAttributes.td"
|
||
include "mlir/Dialect/SPIRV/IR/SPIRVBase.td"
|
||
include "mlir/IR/BuiltinAttributeInterfaces.td"
|
||
include "mlir/Interfaces/FunctionInterfaces.td"
|
||
include "mlir/IR/OpAsmInterface.td"
|
||
include "mlir/IR/SymbolInterfaces.td"
|
||
include "mlir/Interfaces/CallInterfaces.td"
|
||
include "mlir/Interfaces/SideEffectInterfaces.td"
|
||
|
||
// -----
|
||
|
||
def SPIRV_AddressOfOp : SPIRV_Op<"mlir.addressof",
|
||
[DeclareOpInterfaceMethods<OpAsmOpInterface, ["getAsmResultNames"]>,
|
||
InFunctionScope, Pure]> {
|
||
let summary = "Get the address of a global variable.";
|
||
|
||
let description = [{
|
||
Variables in module scope are defined using symbol names. This op generates
|
||
an SSA value that can be used to refer to the symbol within function scope
|
||
for use in ops that expect an SSA value. This operation has no corresponding
|
||
SPIR-V instruction; it's merely used for modelling purpose in the SPIR-V
|
||
dialect. Since variables in module scope in SPIR-V dialect are of pointer
|
||
type, this op returns a pointer type as well, and the type is the same as
|
||
the variable referenced.
|
||
|
||
#### Example:
|
||
|
||
```mlir
|
||
%0 = spirv.mlir.addressof @global_var : !spirv.ptr<f32, Input>
|
||
```
|
||
}];
|
||
|
||
let arguments = (ins
|
||
FlatSymbolRefAttr:$variable
|
||
);
|
||
|
||
let results = (outs
|
||
SPIRV_AnyPtr:$pointer
|
||
);
|
||
|
||
let hasOpcode = 0;
|
||
|
||
let autogenSerialization = 0;
|
||
|
||
let builders = [OpBuilder<(ins "spirv::GlobalVariableOp":$var)>];
|
||
|
||
let assemblyFormat = "$variable attr-dict `:` type($pointer)";
|
||
}
|
||
|
||
// -----
|
||
|
||
def SPIRV_ConstantOp : SPIRV_Op<"Constant",
|
||
[ConstantLike,
|
||
DeclareOpInterfaceMethods<OpAsmOpInterface, ["getAsmResultNames"]>,
|
||
Pure]> {
|
||
let summary = [{
|
||
Declare a new integer-type or floating-point-type scalar constant.
|
||
}];
|
||
|
||
let description = [{
|
||
This op declares a SPIR-V normal 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 `spirv.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)serialization process.
|
||
|
||
<!-- End of AutoGen section -->
|
||
|
||
```
|
||
spirv.Constant-op ::= ssa-id `=` `spirv.Constant` attribute-value
|
||
(`:` spirv-type)?
|
||
```
|
||
|
||
#### Example:
|
||
|
||
```mlir
|
||
%0 = spirv.Constant true
|
||
%1 = spirv.Constant dense<[2.0, 3.0]> : vector<2xf32>
|
||
%2 = spirv.Constant [dense<3.0> : vector<2xf32>] : !spirv.array<1xvector<2xf32>>
|
||
```
|
||
|
||
TODO: support constant structs
|
||
}];
|
||
|
||
let arguments = (ins
|
||
AnyAttr:$value
|
||
);
|
||
|
||
let results = (outs
|
||
SPIRV_Type:$constant
|
||
);
|
||
|
||
let hasFolder = 1;
|
||
|
||
let extraClassDeclaration = [{
|
||
// Returns true if a constant can be built for the given `type`.
|
||
static bool isBuildableWith(Type type);
|
||
|
||
// Creates a constant zero/one of the given `type` at the current insertion
|
||
// point of `builder` and returns it.
|
||
static spirv::ConstantOp getZero(Type type, Location loc,
|
||
OpBuilder &builder);
|
||
static spirv::ConstantOp getOne(Type type, Location loc,
|
||
OpBuilder &builder);
|
||
}];
|
||
|
||
let hasOpcode = 0;
|
||
|
||
let autogenSerialization = 0;
|
||
}
|
||
|
||
|
||
// -----
|
||
|
||
def SPIRV_EXTConstantCompositeReplicateOp : SPIRV_ExtVendorOp<"ConstantCompositeReplicate", [Pure]> {
|
||
let summary = [{
|
||
Declare a new replicated composite constant op.
|
||
}];
|
||
|
||
let description = [{
|
||
Represents a splat composite constant i.e., all elements of composite constant
|
||
have the same value.
|
||
|
||
#### Example:
|
||
|
||
```mlir
|
||
%0 = spirv.EXT.ConstantCompositeReplicate [1 : i32] : vector<2xi32>
|
||
%1 = spirv.EXT.ConstantCompositeReplicate [1 : i32] : !spirv.array<2 x vector<2xi32>>
|
||
%2 = spirv.EXT.ConstantCompositeReplicate [dense<[1, 2]> : vector<2xi32>] : !spirv.array<2 x vector<2xi32>>
|
||
```
|
||
}];
|
||
|
||
let availability = [
|
||
MinVersion<SPIRV_V_1_0>,
|
||
MaxVersion<SPIRV_V_1_6>,
|
||
Extension<[SPV_EXT_replicated_composites]>,
|
||
Capability<[SPIRV_C_ReplicatedCompositesEXT]>
|
||
];
|
||
|
||
let arguments = (ins
|
||
AnyAttr:$value
|
||
);
|
||
|
||
let results = (outs
|
||
SPIRV_Composite:$replicated_constant
|
||
);
|
||
|
||
let autogenSerialization = 0;
|
||
|
||
let assemblyFormat = "` ` `[` $value `]` `:` type($replicated_constant) attr-dict";
|
||
}
|
||
|
||
// -----
|
||
|
||
def SPIRV_EntryPointOp : SPIRV_Op<"EntryPoint", [InModuleScope]> {
|
||
let summary = [{
|
||
Declare an entry point, its execution model, and its interface.
|
||
}];
|
||
|
||
let description = [{
|
||
Execution Model is the execution model for the entry point and its
|
||
static call tree. See Execution Model.
|
||
|
||
Entry Point must be the Result `<id>` of an OpFunction instruction.
|
||
|
||
Name is a name string for the entry point. A module cannot have two
|
||
OpEntryPoint instructions with the same Execution Model and the same
|
||
Name string.
|
||
|
||
Interface is a list of symbol references to `spirv.GlobalVariable`
|
||
operations. These declare the set of global variables from a
|
||
module that form the interface of this entry point. The set of
|
||
Interface symbols must be equal to or a superset of the
|
||
`spirv.GlobalVariable`s referenced by the entry point’s static call
|
||
tree, within the interface’s storage classes. Before version 1.4,
|
||
the interface’s storage classes are limited to the Input and
|
||
Output storage classes. Starting with version 1.4, the interface’s
|
||
storage classes are all storage classes used in declaring all
|
||
global variables referenced by the entry point’s call tree.
|
||
|
||
<!-- End of AutoGen section -->
|
||
|
||
```
|
||
execution-model ::= "Vertex" | "TesellationControl" |
|
||
<and other SPIR-V execution models...>
|
||
|
||
entry-point-op ::= ssa-id `=` `spirv.EntryPoint` execution-model
|
||
symbol-reference (`, ` symbol-reference)*
|
||
```
|
||
|
||
#### Example:
|
||
|
||
```mlir
|
||
spirv.EntryPoint "GLCompute" @foo
|
||
spirv.EntryPoint "Kernel" @foo, @var1, @var2
|
||
|
||
```
|
||
}];
|
||
|
||
let arguments = (ins
|
||
SPIRV_ExecutionModelAttr:$execution_model,
|
||
FlatSymbolRefAttr:$fn,
|
||
SymbolRefArrayAttr:$interface
|
||
);
|
||
|
||
let results = (outs);
|
||
|
||
let autogenSerialization = 0;
|
||
|
||
let builders = [
|
||
OpBuilder<(ins "spirv::ExecutionModel":$executionModel,
|
||
"spirv::FuncOp":$function, "ArrayRef<Attribute>":$interfaceVars)>];
|
||
}
|
||
|
||
// -----
|
||
|
||
def SPIRV_ExecutionModeOp : SPIRV_Op<"ExecutionMode", [InModuleScope]> {
|
||
let summary = "Declare an execution mode for an entry point.";
|
||
|
||
let description = [{
|
||
Entry Point must be the Entry Point `<id>` operand of an OpEntryPoint
|
||
instruction.
|
||
|
||
Mode is the execution mode. See Execution Mode.
|
||
|
||
This instruction is only valid when the Mode operand is an execution
|
||
mode that takes no Extra Operands, or takes Extra Operands that are not
|
||
`<id>` operands.
|
||
|
||
<!-- End of AutoGen section -->
|
||
|
||
```
|
||
execution-mode ::= "Invocations" | "SpacingEqual" |
|
||
<and other SPIR-V execution modes...>
|
||
|
||
execution-mode-op ::= `spirv.ExecutionMode ` ssa-use execution-mode
|
||
(integer-literal (`, ` integer-literal)* )?
|
||
```
|
||
|
||
#### Example:
|
||
|
||
```mlir
|
||
spirv.ExecutionMode @foo "ContractionOff"
|
||
spirv.ExecutionMode @bar "LocalSizeHint", 3, 4, 5
|
||
```
|
||
}];
|
||
|
||
let arguments = (ins
|
||
FlatSymbolRefAttr:$fn,
|
||
SPIRV_ExecutionModeAttr:$execution_mode,
|
||
I32ArrayAttr:$values
|
||
);
|
||
|
||
let results = (outs);
|
||
|
||
let hasVerifier = 0;
|
||
|
||
let autogenSerialization = 0;
|
||
|
||
let builders = [
|
||
OpBuilder<(ins "spirv::FuncOp":$function,
|
||
"spirv::ExecutionMode":$executionMode, "ArrayRef<int32_t>":$params)>];
|
||
}
|
||
|
||
// -----
|
||
|
||
def SPIRV_ExecutionModeIdOp : SPIRV_Op<"ExecutionModeId", []> {
|
||
let summary = [{
|
||
Declare an execution mode for an entry point, using <id>s as Extra
|
||
Operands.
|
||
}];
|
||
|
||
let description = [{
|
||
Entry Point must be the Entry Point <id> operand of an OpEntryPoint
|
||
instruction.
|
||
|
||
Mode is the execution mode. See Execution Mode.
|
||
|
||
This instruction is only valid if the Mode operand is an execution mode
|
||
that takes Extra Operands that are <id> operands. Otherwise, use
|
||
OpExecutionMode.
|
||
|
||
<!-- End of AutoGen section -->
|
||
|
||
```
|
||
execution-mode ::= "Invocations" | "SpacingEqual" |
|
||
<and other SPIR-V execution modes...>
|
||
execution-mode-id-op ::= `spirv.ExecutionMode ` ssa-use execution-mode
|
||
symbol-reference (`, ` symbol-reference)*
|
||
```
|
||
|
||
#### Example:
|
||
|
||
```mlir
|
||
spirv.ExecutionModeId @foo "LocalSizeId" @var0, @var1, @var2
|
||
spirv.ExecutionModeId @bar "LocalSizeHintId" @x, @y, @z
|
||
```
|
||
}];
|
||
|
||
let availability = [
|
||
MinVersion<SPIRV_V_1_2>,
|
||
MaxVersion<SPIRV_V_1_6>,
|
||
Extension<[]>,
|
||
Capability<[]>
|
||
];
|
||
|
||
let arguments = (ins
|
||
FlatSymbolRefAttr:$fn,
|
||
SPIRV_ExecutionModeAttr:$execution_mode,
|
||
SymbolRefArrayAttr:$values
|
||
);
|
||
|
||
let results = (outs);
|
||
|
||
let hasVerifier = 1;
|
||
|
||
let autogenSerialization = 0;
|
||
|
||
let builders = [OpBuilder<(ins "spirv::FuncOp":$function,
|
||
"spirv::ExecutionMode":$executionMode,
|
||
"ArrayRef<Attribute>":$params),
|
||
[{
|
||
build($_builder, $_state,
|
||
SymbolRefAttr::get(function),
|
||
ExecutionModeAttr::get($_builder.getContext(), executionMode),
|
||
$_builder.getArrayAttr(params));
|
||
}]>];
|
||
}
|
||
|
||
// -----
|
||
|
||
def SPIRV_FuncOp : SPIRV_Op<"func", [
|
||
AutomaticAllocationScope, FunctionOpInterface,
|
||
InModuleScope, IsolatedFromAbove
|
||
]> {
|
||
let summary = "Declare or define a function";
|
||
|
||
let description = [{
|
||
This op declares or defines a SPIR-V function using one region, which
|
||
contains one or more blocks.
|
||
|
||
Different from the SPIR-V binary format, this op is not allowed to
|
||
implicitly capture global values, and all external references must use
|
||
function arguments or symbol references. This op itself defines a symbol
|
||
that is unique in the enclosing module op.
|
||
|
||
This op itself takes no operands and generates no results. Its region
|
||
can take zero or more arguments and return zero or one values.
|
||
|
||
From `SPV_KHR_physical_storage_buffer`:
|
||
If a parameter of function is
|
||
- a pointer (or contains a pointer) in the PhysicalStorageBuffer storage
|
||
class, the function parameter must be decorated with exactly one of
|
||
`Aliased` or `Restrict`.
|
||
- a pointer (or contains a pointer) and the type it points to is a pointer
|
||
in the PhysicalStorageBuffer storage class, the function parameter must
|
||
be decorated with exactly one of `AliasedPointer` or `RestrictPointer`.
|
||
|
||
<!-- End of AutoGen section -->
|
||
|
||
```
|
||
spv-function-control ::= "None" | "Inline" | "DontInline" | ...
|
||
spv-function-op ::= `spirv.func` function-signature
|
||
spv-function-control region
|
||
```
|
||
|
||
#### Example:
|
||
|
||
```mlir
|
||
spirv.func @foo() -> () "None" { ... }
|
||
spirv.func @bar() -> () "Inline|Pure" { ... }
|
||
|
||
spirv.func @aliased_pointer(%arg0: !spirv.ptr<i32, PhysicalStorageBuffer>,
|
||
{ spirv.decoration = #spirv.decoration<Aliased> }) -> () "None" { ... }
|
||
|
||
spirv.func @restrict_pointer(%arg0: !spirv.ptr<i32, PhysicalStorageBuffer>,
|
||
{ spirv.decoration = #spirv.decoration<Restrict> }) -> () "None" { ... }
|
||
|
||
spirv.func @aliased_pointee(%arg0: !spirv.ptr<!spirv.ptr<i32,
|
||
PhysicalStorageBuffer>, Generic> { spirv.decoration =
|
||
#spirv.decoration<AliasedPointer> }) -> () "None" { ... }
|
||
|
||
spirv.func @restrict_pointee(%arg0: !spirv.ptr<!spirv.ptr<i32,
|
||
PhysicalStorageBuffer>, Generic> { spirv.decoration =
|
||
#spirv.decoration<RestrictPointer> }) -> () "None" { ... }
|
||
```
|
||
}];
|
||
|
||
let arguments = (ins
|
||
TypeAttrOf<FunctionType>:$function_type,
|
||
OptionalAttr<DictArrayAttr>:$arg_attrs,
|
||
OptionalAttr<DictArrayAttr>:$res_attrs,
|
||
StrAttr:$sym_name,
|
||
SPIRV_FunctionControlAttr:$function_control,
|
||
OptionalAttr<SPIRV_LinkageAttributesAttr>:$linkage_attributes
|
||
);
|
||
|
||
let results = (outs);
|
||
|
||
let regions = (region AnyRegion:$body);
|
||
|
||
let hasVerifier = 0;
|
||
|
||
let builders = [
|
||
OpBuilder<(ins "StringRef":$name, "FunctionType":$type,
|
||
CArg<"spirv::FunctionControl", "spirv::FunctionControl::None">:$control,
|
||
CArg<"ArrayRef<NamedAttribute>", "{}">:$attrs)>];
|
||
|
||
let hasOpcode = 0;
|
||
|
||
let autogenSerialization = 0;
|
||
|
||
let extraClassDeclaration = [{
|
||
/// Hook for FunctionOpInterface, called after verifying that the 'type'
|
||
/// attribute is present and checks if it holds a function type. Ensures
|
||
/// getType, getNumArguments, and getNumResults can be called safely
|
||
LogicalResult verifyType();
|
||
|
||
/// Hook for FunctionOpInterface, called after verifying the function
|
||
/// type and the presence of the (potentially empty) function body.
|
||
/// Ensures SPIR-V specific semantics.
|
||
LogicalResult verifyBody();
|
||
|
||
//===------------------------------------------------------------------===//
|
||
// FunctionOpInterface Methods
|
||
//===------------------------------------------------------------------===//
|
||
|
||
/// Returns the argument types of this function.
|
||
ArrayRef<Type> getArgumentTypes() { return getFunctionType().getInputs(); }
|
||
|
||
/// Returns the result types of this function.
|
||
ArrayRef<Type> getResultTypes() { return getFunctionType().getResults(); }
|
||
|
||
// CallableOpInterface
|
||
Region *getCallableRegion() { return isExternal() ? nullptr : &getBody(); }
|
||
}];
|
||
}
|
||
|
||
// -----
|
||
|
||
def SPIRV_GlobalVariableOp : SPIRV_Op<"GlobalVariable", [InModuleScope, Symbol]> {
|
||
let summary = [{
|
||
Allocate an object in memory at module scope. The object is
|
||
referenced using a symbol name.
|
||
}];
|
||
|
||
let description = [{
|
||
The variable type must be an OpTypePointer. Its type operand is the type of
|
||
object in memory.
|
||
|
||
Storage Class is the Storage Class of the memory holding the object. It
|
||
cannot be Generic. It must be the same as the Storage Class operand of
|
||
the variable types. Only those storage classes that are valid at module
|
||
scope (like Input, Output, StorageBuffer, etc.) are valid.
|
||
|
||
Initializer is optional. If Initializer is present, it will be
|
||
the initial value of the variable’s memory content. Initializer
|
||
must be an symbol defined from a constant instruction or other
|
||
`spirv.GlobalVariable` operation in module scope. Initializer must
|
||
have the same type as the type of the defined symbol.
|
||
|
||
<!-- End of AutoGen section -->
|
||
|
||
```
|
||
variable-op ::= `spirv.GlobalVariable` spirv-type symbol-ref-id
|
||
(`initializer(` symbol-ref-id `)`)?
|
||
(`bind(` integer-literal, integer-literal `)`)?
|
||
(`built_in(` string-literal `)`)?
|
||
attribute-dict?
|
||
```
|
||
|
||
where `initializer` specifies initializer and `bind` specifies the
|
||
descriptor set and binding number. `built_in` specifies SPIR-V
|
||
BuiltIn decoration associated with the op.
|
||
|
||
#### Example:
|
||
|
||
```mlir
|
||
spirv.GlobalVariable @var0 : !spirv.ptr<f32, Input>
|
||
spirv.GlobalVariable @var1 initializer(@var0) : !spirv.ptr<f32, Output>
|
||
spirv.GlobalVariable @var2 bind(1, 2) : !spirv.ptr<f32, Uniform>
|
||
spirv.GlobalVariable @var3 built_in("GlobalInvocationId") : !spirv.ptr<vector<3xi32>, Input>
|
||
```
|
||
}];
|
||
|
||
let arguments = (ins
|
||
TypeAttr:$type,
|
||
StrAttr:$sym_name,
|
||
OptionalAttr<FlatSymbolRefAttr>:$initializer,
|
||
OptionalAttr<I32Attr>:$location,
|
||
OptionalAttr<I32Attr>:$binding,
|
||
OptionalAttr<I32Attr>:$descriptor_set,
|
||
OptionalAttr<StrAttr>:$built_in,
|
||
OptionalAttr<SPIRV_LinkageAttributesAttr>:$linkage_attributes
|
||
);
|
||
|
||
let results = (outs);
|
||
|
||
let builders = [
|
||
OpBuilder<(ins "TypeAttr":$type,
|
||
"StringAttr":$sym_name,
|
||
CArg<"FlatSymbolRefAttr", "nullptr">:$initializer),
|
||
[{
|
||
$_state.addAttribute("type", type);
|
||
$_state.addAttribute(getSymNameAttrName($_state.name), sym_name);
|
||
if (initializer)
|
||
$_state.addAttribute(getInitializerAttrName($_state.name), initializer);
|
||
}]>,
|
||
OpBuilder<(ins "TypeAttr":$type, "ArrayRef<NamedAttribute>":$namedAttrs),
|
||
[{
|
||
$_state.addAttribute("type", type);
|
||
$_state.addAttributes(namedAttrs);
|
||
}]>,
|
||
OpBuilder<(ins "Type":$type, "StringRef":$name,
|
||
"unsigned":$descriptorSet, "unsigned":$binding)>,
|
||
OpBuilder<(ins "Type":$type, "StringRef":$name,
|
||
"spirv::BuiltIn":$builtin)>,
|
||
OpBuilder<(ins "Type":$type,
|
||
"StringRef":$sym_name,
|
||
CArg<"FlatSymbolRefAttr", "{}">:$initializer),
|
||
[{
|
||
$_state.addAttribute("type", TypeAttr::get(type));
|
||
$_state.addAttribute(getSymNameAttrName($_state.name), $_builder.getStringAttr(sym_name));
|
||
if (initializer)
|
||
$_state.addAttribute(getInitializerAttrName($_state.name), initializer);
|
||
}]>
|
||
];
|
||
|
||
let hasOpcode = 0;
|
||
|
||
let autogenSerialization = 0;
|
||
|
||
let extraClassDeclaration = [{
|
||
::mlir::spirv::StorageClass storageClass() {
|
||
return ::llvm::cast<::mlir::spirv::PointerType>(this->getType()).getStorageClass();
|
||
}
|
||
}];
|
||
}
|
||
|
||
// -----
|
||
|
||
def SPIRV_ModuleOp : SPIRV_Op<"module",
|
||
[IsolatedFromAbove, NoRegionArguments, NoTerminator,
|
||
SingleBlock, SymbolTable, Symbol]> {
|
||
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 validity 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
|
||
has no terminator.
|
||
|
||
<!-- End of AutoGen section -->
|
||
|
||
```
|
||
addressing-model ::= `Logical` | `Physical32` | `Physical64` | ...
|
||
memory-model ::= `Simple` | `GLSL450` | `OpenCL` | `Vulkan` | ...
|
||
spv-module-op ::= `spirv.module` addressing-model memory-model
|
||
(requires spirv-vce-attribute)?
|
||
(`attributes` attribute-dict)?
|
||
region
|
||
```
|
||
|
||
#### Example:
|
||
|
||
```mlir
|
||
spirv.module Logical GLSL450 {}
|
||
|
||
spirv.module Logical Vulkan
|
||
requires #spirv.vce<v1.0, [Shader], [SPV_KHR_vulkan_memory_model]>
|
||
attributes { some_additional_attr = ... } {
|
||
spirv.func @do_nothing() -> () {
|
||
spirv.Return
|
||
}
|
||
}
|
||
```
|
||
}];
|
||
|
||
let arguments = (ins
|
||
SPIRV_AddressingModelAttr:$addressing_model,
|
||
SPIRV_MemoryModelAttr:$memory_model,
|
||
OptionalAttr<SPIRV_VerCapExtAttr>:$vce_triple,
|
||
OptionalAttr<StrAttr>:$sym_name
|
||
);
|
||
|
||
let results = (outs);
|
||
|
||
let regions = (region AnyRegion);
|
||
|
||
let builders = [
|
||
OpBuilder<(ins CArg<"std::optional<StringRef>", "std::nullopt">:$name)>,
|
||
OpBuilder<(ins "spirv::AddressingModel":$addressing_model,
|
||
"spirv::MemoryModel":$memory_model,
|
||
CArg<"std::optional<spirv::VerCapExtAttr>", "std::nullopt">:$vce_triple,
|
||
CArg<"std::optional<StringRef>", "std::nullopt">:$name)>
|
||
];
|
||
|
||
// We need to ensure the block inside the region is properly terminated;
|
||
// the auto-generated builders do not guarantee that.
|
||
let skipDefaultBuilders = 1;
|
||
|
||
let hasOpcode = 0;
|
||
|
||
let autogenSerialization = 0;
|
||
|
||
let extraClassDeclaration = [{
|
||
|
||
bool isOptionalSymbol() { return true; }
|
||
|
||
std::optional<StringRef> getName() { return getSymName(); }
|
||
|
||
static StringRef getVCETripleAttrName() { return "vce_triple"; }
|
||
}];
|
||
|
||
let hasVerifier = 0;
|
||
let hasRegionVerifier = 1;
|
||
}
|
||
|
||
// -----
|
||
|
||
def SPIRV_ReferenceOfOp : SPIRV_Op<"mlir.referenceof", [Pure]> {
|
||
let summary = "Reference a specialization constant.";
|
||
|
||
let description = [{
|
||
Specialization constants in module scope are defined using symbol names.
|
||
This op generates an SSA value that can be used to refer to the symbol
|
||
within function scope for use in ops that expect an SSA value.
|
||
This operation has no corresponding SPIR-V instruction; it's merely used
|
||
for modelling purpose in the SPIR-V dialect. This op's return type is
|
||
the same as the specialization constant.
|
||
|
||
#### Example:
|
||
|
||
```mlir
|
||
%0 = spirv.mlir.referenceof @spec_const : f32
|
||
```
|
||
|
||
TODO Add support for composite specialization constants.
|
||
}];
|
||
|
||
let arguments = (ins
|
||
FlatSymbolRefAttr:$spec_const
|
||
);
|
||
|
||
let results = (outs
|
||
SPIRV_Type:$reference
|
||
);
|
||
|
||
let hasOpcode = 0;
|
||
|
||
let autogenSerialization = 0;
|
||
|
||
let assemblyFormat = "$spec_const attr-dict `:` type($reference)";
|
||
}
|
||
|
||
// -----
|
||
|
||
def SPIRV_SpecConstantOp : SPIRV_Op<"SpecConstant", [InModuleScope, Symbol]> {
|
||
let summary = [{
|
||
Declare a new integer-type or floating-point-type scalar specialization
|
||
constant.
|
||
}];
|
||
|
||
let description = [{
|
||
This op declares a SPIR-V scalar specialization constant. SPIR-V has
|
||
multiple constant instructions covering different scalar types:
|
||
|
||
* `OpSpecConstantTrue` and `OpSpecConstantFalse` for boolean constants
|
||
* `OpSpecConstant` for scalar constants
|
||
|
||
Similar as `spirv.Constant`, this op represents all of the above cases.
|
||
`OpSpecConstantComposite` and `OpSpecConstantOp` are modelled with
|
||
separate ops.
|
||
|
||
<!-- End of AutoGen section -->
|
||
|
||
```
|
||
spv-spec-constant-op ::= `spirv.SpecConstant` symbol-ref-id
|
||
`spec_id(` integer `)`
|
||
`=` attribute-value (`:` spirv-type)?
|
||
```
|
||
|
||
where `spec_id` specifies the SPIR-V SpecId decoration associated with
|
||
the op.
|
||
|
||
#### Example:
|
||
|
||
```mlir
|
||
spirv.SpecConstant @spec_const1 = true
|
||
spirv.SpecConstant @spec_const2 spec_id(5) = 42 : i32
|
||
```
|
||
}];
|
||
|
||
let arguments = (ins
|
||
StrAttr:$sym_name,
|
||
TypedAttrInterface:$default_value
|
||
);
|
||
|
||
let results = (outs);
|
||
|
||
let hasOpcode = 0;
|
||
|
||
let autogenSerialization = 0;
|
||
}
|
||
|
||
// -----
|
||
|
||
def SPIRV_SpecConstantCompositeOp : SPIRV_Op<"SpecConstantComposite", [
|
||
InModuleScope, Symbol]> {
|
||
let summary = "Declare a new composite specialization constant.";
|
||
|
||
let description = [{
|
||
This op declares a SPIR-V composite specialization constant. This covers
|
||
the `OpSpecConstantComposite` SPIR-V instruction. Scalar constants are
|
||
covered by `spirv.SpecConstant`.
|
||
|
||
A constituent of a spec constant composite can be:
|
||
- A symbol referring of another spec constant.
|
||
- The SSA ID of a non-specialization constant (i.e. defined through
|
||
`spirv.SpecConstant`).
|
||
- The SSA ID of a `spirv.Undef`.
|
||
|
||
```
|
||
spv-spec-constant-composite-op ::= `spirv.SpecConstantComposite` symbol-ref-id ` (`
|
||
symbol-ref-id (`, ` symbol-ref-id)*
|
||
`) :` composite-type
|
||
```
|
||
|
||
where `composite-type` is some non-scalar type that can be represented in the `spv`
|
||
dialect: `spirv.struct`, `spirv.array`, or `vector`.
|
||
|
||
#### Example:
|
||
|
||
```mlir
|
||
spirv.SpecConstant @sc1 = 1 : i32
|
||
spirv.SpecConstant @sc2 = 2.5 : f32
|
||
spirv.SpecConstant @sc3 = 3.5 : f32
|
||
spirv.SpecConstantComposite @scc (@sc1, @sc2, @sc3) : !spirv.struct<i32, f32, f32>
|
||
```
|
||
|
||
TODO Add support for constituents that are:
|
||
- regular constants.
|
||
- undef.
|
||
- spec constant composite.
|
||
}];
|
||
|
||
let arguments = (ins
|
||
TypeAttr:$type,
|
||
StrAttr:$sym_name,
|
||
SymbolRefArrayAttr:$constituents
|
||
);
|
||
|
||
let results = (outs);
|
||
|
||
let hasOpcode = 0;
|
||
|
||
let autogenSerialization = 0;
|
||
}
|
||
|
||
// -----
|
||
|
||
def SPIRV_EXTSpecConstantCompositeReplicateOp : SPIRV_ExtVendorOp<"SpecConstantCompositeReplicate", [InModuleScope, Symbol]> {
|
||
let summary = "Declare a new replicated composite specialization constant op.";
|
||
|
||
let description = [{
|
||
Represents a splat spec composite constant i.e., all elements of spec composite
|
||
constant have the same value. The splat value must come from a symbol reference
|
||
of spec constant instruction.
|
||
|
||
#### Example:
|
||
|
||
```mlir
|
||
spirv.SpecConstant @sc_i32_1 = 1 : i32
|
||
spirv.EXT.SpecConstantCompositeReplicate @scc_splat_array_of_i32 (@sc_i32_1) : !spirv.array<3 x i32>
|
||
spirv.EXT.SpecConstantCompositeReplicate @scc_splat_struct_of_i32 (@sc_i32_1) : !spirv.struct<(i32, i32, i32)>
|
||
```
|
||
}];
|
||
|
||
let availability = [
|
||
MinVersion<SPIRV_V_1_0>,
|
||
MaxVersion<SPIRV_V_1_6>,
|
||
Extension<[SPV_EXT_replicated_composites]>,
|
||
Capability<[SPIRV_C_ReplicatedCompositesEXT]>
|
||
];
|
||
|
||
let arguments = (ins
|
||
TypeAttr:$type,
|
||
StrAttr:$sym_name,
|
||
SymbolRefAttr:$constituent
|
||
);
|
||
|
||
let results = (outs);
|
||
|
||
let autogenSerialization = 0;
|
||
}
|
||
|
||
// -----
|
||
|
||
def SPIRV_SpecConstantOperationOp : SPIRV_Op<"SpecConstantOperation", [
|
||
Pure, InFunctionScope,
|
||
SingleBlockImplicitTerminator<"YieldOp">]> {
|
||
let summary = [{
|
||
Declare a new specialization constant that results from doing an operation.
|
||
}];
|
||
|
||
let description = [{
|
||
This op declares a SPIR-V specialization constant that results from
|
||
doing an operation on other constants (specialization or otherwise).
|
||
|
||
In the `spv` dialect, this op is modelled as follows:
|
||
|
||
```
|
||
spv-spec-constant-operation-op ::= `spirv.SpecConstantOperation` `wraps`
|
||
generic-spirv-op `:` function-type
|
||
```
|
||
|
||
In particular, an `spirv.SpecConstantOperation` contains exactly one
|
||
region. In turn, that region, contains exactly 2 instructions:
|
||
- One of SPIR-V's instructions that are allowed within an
|
||
OpSpecConstantOp.
|
||
- An `spirv.mlir.yield` instruction as the terminator.
|
||
|
||
The following SPIR-V instructions are valid:
|
||
- OpSConvert,
|
||
- OpUConvert,
|
||
- OpFConvert,
|
||
- OpSNegate,
|
||
- OpNot,
|
||
- OpIAdd,
|
||
- OpISub,
|
||
- OpIMul,
|
||
- OpUDiv,
|
||
- OpSDiv,
|
||
- OpUMod,
|
||
- OpSRem,
|
||
- OpSMod
|
||
- OpShiftRightLogical,
|
||
- OpShiftRightArithmetic,
|
||
- OpShiftLeftLogical
|
||
- OpBitwiseOr,
|
||
- OpBitwiseXor,
|
||
- OpBitwiseAnd
|
||
- OpVectorShuffle,
|
||
- OpCompositeExtract,
|
||
- OpCompositeInsert
|
||
- OpLogicalOr,
|
||
- OpLogicalAnd,
|
||
- OpLogicalNot,
|
||
- OpLogicalEqual,
|
||
- OpLogicalNotEqual
|
||
- OpSelect
|
||
- OpIEqual,
|
||
- OpINotEqual
|
||
- OpULessThan,
|
||
- OpSLessThan
|
||
- OpUGreaterThan,
|
||
- OpSGreaterThan
|
||
- OpULessThanEqual,
|
||
- OpSLessThanEqual
|
||
- OpUGreaterThanEqual,
|
||
- OpSGreaterThanEqual
|
||
|
||
TODO Add capability-specific ops when supported.
|
||
|
||
#### Example:
|
||
```mlir
|
||
%0 = spirv.Constant 1: i32
|
||
%1 = spirv.Constant 1: i32
|
||
|
||
%2 = spirv.SpecConstantOperation wraps "spirv.IAdd"(%0, %1) : (i32, i32) -> i32
|
||
```
|
||
}];
|
||
|
||
let arguments = (ins);
|
||
|
||
let results = (outs AnyType:$result);
|
||
|
||
let regions = (region SizedRegion<1>:$body);
|
||
|
||
let hasOpcode = 0;
|
||
|
||
let autogenSerialization = 0;
|
||
|
||
let hasVerifier = 0;
|
||
let hasRegionVerifier = 1;
|
||
}
|
||
|
||
// -----
|
||
|
||
def SPIRV_YieldOp : SPIRV_Op<"mlir.yield", [
|
||
HasParent<"SpecConstantOperationOp">, Pure, Terminator]> {
|
||
let summary = [{
|
||
Yields the result computed in `spirv.SpecConstantOperation`'s
|
||
region back to the parent op.
|
||
}];
|
||
|
||
let description = [{
|
||
This op is a special terminator whose only purpose is to terminate
|
||
an `spirv.SpecConstantOperation`'s enclosed region. It accepts a
|
||
single operand produced by the preceeding (and only other) instruction
|
||
in its parent block (see SPIRV_SpecConstantOperation for further
|
||
details). This op has no corresponding SPIR-V instruction.
|
||
|
||
#### Example:
|
||
|
||
```mlir
|
||
%0 = ... (some op supported by SPIR-V OpSpecConstantOp)
|
||
spirv.mlir.yield %0
|
||
```
|
||
}];
|
||
|
||
let arguments = (ins AnyType:$operand);
|
||
|
||
let results = (outs);
|
||
|
||
let hasOpcode = 0;
|
||
|
||
let autogenSerialization = 0;
|
||
|
||
let assemblyFormat = "attr-dict $operand `:` type($operand)";
|
||
|
||
let hasVerifier = 0;
|
||
}
|
||
|
||
// -----
|
||
|
||
#endif // MLIR_DIALECT_SPIRV_IR_STRUCTURE_OPS
|