//===-- 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, 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 ``` }]; 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, 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. ``` 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, MaxVersion, 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 `` 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. ``` execution-model ::= "Vertex" | "TesellationControl" | 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":$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 `` 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 `` operands. ``` execution-mode ::= "Invocations" | "SpacingEqual" | 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":$params)>]; } // ----- def SPIRV_ExecutionModeIdOp : SPIRV_Op<"ExecutionModeId", []> { let summary = [{ Declare an execution mode for an entry point, using s as Extra Operands. }]; let description = [{ Entry Point must be the Entry Point 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 operands. Otherwise, use OpExecutionMode. ``` execution-mode ::= "Invocations" | "SpacingEqual" | 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, MaxVersion, 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":$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`. ``` 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, { spirv.decoration = #spirv.decoration }) -> () "None" { ... } spirv.func @restrict_pointer(%arg0: !spirv.ptr, { spirv.decoration = #spirv.decoration }) -> () "None" { ... } spirv.func @aliased_pointee(%arg0: !spirv.ptr, Generic> { spirv.decoration = #spirv.decoration }) -> () "None" { ... } spirv.func @restrict_pointee(%arg0: !spirv.ptr, Generic> { spirv.decoration = #spirv.decoration }) -> () "None" { ... } ``` }]; let arguments = (ins TypeAttrOf:$function_type, OptionalAttr:$arg_attrs, OptionalAttr:$res_attrs, StrAttr:$sym_name, SPIRV_FunctionControlAttr:$function_control, OptionalAttr:$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", "{}">:$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 getArgumentTypes() { return getFunctionType().getInputs(); } /// Returns the result types of this function. ArrayRef 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. ``` 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 spirv.GlobalVariable @var1 initializer(@var0) : !spirv.ptr spirv.GlobalVariable @var2 bind(1, 2) : !spirv.ptr spirv.GlobalVariable @var3 built_in("GlobalInvocationId") : !spirv.ptr, Input> ``` }]; let arguments = (ins TypeAttr:$type, StrAttr:$sym_name, OptionalAttr:$initializer, OptionalAttr:$location, OptionalAttr:$binding, OptionalAttr:$descriptor_set, OptionalAttr:$built_in, OptionalAttr:$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":$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. ``` 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 attributes { some_additional_attr = ... } { spirv.func @do_nothing() -> () { spirv.Return } } ``` }]; let arguments = (ins SPIRV_AddressingModelAttr:$addressing_model, SPIRV_MemoryModelAttr:$memory_model, OptionalAttr:$vce_triple, OptionalAttr:$sym_name ); let results = (outs); let regions = (region AnyRegion); let builders = [ OpBuilder<(ins CArg<"std::optional", "std::nullopt">:$name)>, OpBuilder<(ins "spirv::AddressingModel":$addressing_model, "spirv::MemoryModel":$memory_model, CArg<"std::optional", "std::nullopt">:$vce_triple, CArg<"std::optional", "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 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. ``` 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 ``` 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, MaxVersion, 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