1//===-- SPIRVOps.td - MLIR SPIR-V Op Definitions Spec ------*- tablegen -*-===// 2// 3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4// See https://llvm.org/LICENSE.txt for license information. 5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6// 7//===----------------------------------------------------------------------===// 8// 9// This is the main operation definition specification file for SPIR-V 10// operations. 11// 12//===----------------------------------------------------------------------===// 13 14// Note that for each op in this file and the included files for specific op 15// categories, we use a tool to automatically generate certain sections in its 16// definition: basic structure, summary, description. So modifications to these 17// sections will not be respected. Modifications to op traits, arguments, 18// results, and sections after the results are retained. Besides, ops must be 19// separated via the '// -----' marker. 20 21#ifndef SPIRV_OPS 22#define SPIRV_OPS 23 24include "mlir/Dialect/SPIRV/SPIRVBase.td" 25include "mlir/Dialect/SPIRV/SPIRVArithmeticOps.td" 26include "mlir/Dialect/SPIRV/SPIRVAtomicOps.td" 27include "mlir/Dialect/SPIRV/SPIRVBitOps.td" 28include "mlir/Dialect/SPIRV/SPIRVCastOps.td" 29include "mlir/Dialect/SPIRV/SPIRVCompositeOps.td" 30include "mlir/Dialect/SPIRV/SPIRVControlFlowOps.td" 31include "mlir/Dialect/SPIRV/SPIRVCooperativeMatrixOps.td" 32include "mlir/Dialect/SPIRV/SPIRVGLSLOps.td" 33include "mlir/Dialect/SPIRV/SPIRVGroupOps.td" 34include "mlir/Dialect/SPIRV/SPIRVLogicalOps.td" 35include "mlir/Dialect/SPIRV/SPIRVMatrixOps.td" 36include "mlir/Dialect/SPIRV/SPIRVNonUniformOps.td" 37include "mlir/Dialect/SPIRV/SPIRVOCLOps.td" 38include "mlir/Dialect/SPIRV/SPIRVStructureOps.td" 39include "mlir/Interfaces/SideEffectInterfaces.td" 40 41// ----- 42 43def SPV_AccessChainOp : SPV_Op<"AccessChain", [NoSideEffect]> { 44 let summary = [{ 45 Create a pointer into a composite object that can be used with OpLoad 46 and OpStore. 47 }]; 48 49 let description = [{ 50 Result Type must be an OpTypePointer. Its Type operand must be the type 51 reached by walking the Base’s type hierarchy down to the last provided 52 index in Indexes, and its Storage Class operand must be the same as the 53 Storage Class of Base. 54 55 Base must be a pointer, pointing to the base of a composite object. 56 57 Indexes walk the type hierarchy to the desired depth, potentially down 58 to scalar granularity. The first index in Indexes will select the top- 59 level member/element/component/element of the base composite. All 60 composite constituents use zero-based numbering, as described by their 61 OpType… instruction. The second index will apply similarly to that 62 result, and so on. Once any non-composite type is reached, there must be 63 no remaining (unused) indexes. 64 65 Each index in Indexes 66 67 - must be a scalar integer type, 68 69 - is treated as a signed count, and 70 71 - must be an OpConstant when indexing into a structure. 72 73 <!-- End of AutoGen section --> 74 ``` 75 access-chain-op ::= ssa-id `=` `spv.AccessChain` ssa-use 76 `[` ssa-use (',' ssa-use)* `]` 77 `:` pointer-type 78 ``` 79 80 #### Example: 81 82 ```mlir 83 %0 = "spv.constant"() { value = 1: i32} : () -> i32 84 %1 = spv.Variable : !spv.ptr<!spv.struct<f32, !spv.array<4xf32>>, Function> 85 %2 = spv.AccessChain %1[%0] : !spv.ptr<!spv.struct<f32, !spv.array<4xf32>>, Function> 86 %3 = spv.Load "Function" %2 ["Volatile"] : !spv.array<4xf32> 87 ``` 88 }]; 89 90 let arguments = (ins 91 SPV_AnyPtr:$base_ptr, 92 Variadic<SPV_Integer>:$indices 93 ); 94 95 let results = (outs 96 SPV_AnyPtr:$component_ptr 97 ); 98 99 let builders = [OpBuilderDAG<(ins "Value":$basePtr, "ValueRange":$indices)>]; 100 101 let hasCanonicalizer = 1; 102} 103 104// ----- 105 106def SPV_ControlBarrierOp : SPV_Op<"ControlBarrier", []> { 107 let summary = [{ 108 Wait for other invocations of this module to reach the current point of 109 execution. 110 }]; 111 112 let description = [{ 113 All invocations of this module within Execution scope must reach this 114 point of execution before any invocation will proceed beyond it. 115 116 When Execution is Workgroup or larger, behavior is undefined if this 117 instruction is used in control flow that is non-uniform within 118 Execution. When Execution is Subgroup or Invocation, the behavior of 119 this instruction in non-uniform control flow is defined by the client 120 API. 121 122 If Semantics is not None, this instruction also serves as an 123 OpMemoryBarrier instruction, and must also perform and adhere to the 124 description and semantics of an OpMemoryBarrier instruction with the 125 same Memory and Semantics operands. This allows atomically specifying 126 both a control barrier and a memory barrier (that is, without needing 127 two instructions). If Semantics is None, Memory is ignored. 128 129 Before version 1.3, it is only valid to use this instruction with 130 TessellationControl, GLCompute, or Kernel execution models. There is no 131 such restriction starting with version 1.3. 132 133 When used with the TessellationControl execution model, it also 134 implicitly synchronizes the Output Storage Class: Writes to Output 135 variables performed by any invocation executed prior to a 136 OpControlBarrier will be visible to any other invocation after return 137 from that OpControlBarrier. 138 139 <!-- End of AutoGen section --> 140 141 ``` 142 scope ::= `"CrossDevice"` | `"Device"` | `"Workgroup"` | ... 143 144 memory-semantics ::= `"None"` | `"Acquire"` | "Release"` | ... 145 146 control-barrier-op ::= `spv.ControlBarrier` scope, scope, memory-semantics 147 ``` 148 149 #### Example: 150 151 ```mlir 152 spv.ControlBarrier "Workgroup", "Device", "Acquire|UniformMemory" 153 154 ``` 155 }]; 156 157 let arguments = (ins 158 SPV_ScopeAttr:$execution_scope, 159 SPV_ScopeAttr:$memory_scope, 160 SPV_MemorySemanticsAttr:$memory_semantics 161 ); 162 163 let results = (outs); 164 165 let verifier = [{ return verifyMemorySemantics(*this); }]; 166 167 let autogenSerialization = 0; 168 169 let assemblyFormat = [{ 170 $execution_scope `,` $memory_scope `,` $memory_semantics attr-dict 171 }]; 172} 173 174// ----- 175 176def SPV_CopyMemoryOp : SPV_Op<"CopyMemory", []> { 177 let summary = [{ 178 Copy from the memory pointed to by Source to the memory pointed to by 179 Target. Both operands must be non-void pointers and having the same <id> 180 Type operand in their OpTypePointer type declaration. Matching Storage 181 Class is not required. The amount of memory copied is the size of the 182 type pointed to. The copied type must have a fixed size; i.e., it cannot 183 be, nor include, any OpTypeRuntimeArray types. 184 }]; 185 186 let description = [{ 187 If present, any Memory Operands must begin with a memory operand 188 literal. If not present, it is the same as specifying the memory operand 189 None. Before version 1.4, at most one memory operands mask can be 190 provided. Starting with version 1.4 two masks can be provided, as 191 described in Memory Operands. If no masks or only one mask is present, 192 it applies to both Source and Target. If two masks are present, the 193 first applies to Target and cannot include MakePointerVisible, and the 194 second applies to Source and cannot include MakePointerAvailable. 195 196 <!-- End of AutoGen section --> 197 198 ``` 199 copy-memory-op ::= `spv.CopyMemory ` storage-class ssa-use 200 storage-class ssa-use 201 (`[` memory-access `]` (`, [` memory-access `]`)?)? 202 ` : ` spirv-element-type 203 ``` 204 205 #### Example: 206 207 ```mlir 208 %0 = spv.Variable : !spv.ptr<f32, Function> 209 %1 = spv.Variable : !spv.ptr<f32, Function> 210 spv.CopyMemory "Function" %0, "Function" %1 : f32 211 ``` 212 }]; 213 214 let arguments = (ins 215 SPV_AnyPtr:$target, 216 SPV_AnyPtr:$source, 217 OptionalAttr<SPV_MemoryAccessAttr>:$memory_access, 218 OptionalAttr<I32Attr>:$alignment, 219 OptionalAttr<SPV_MemoryAccessAttr>:$source_memory_access, 220 OptionalAttr<I32Attr>:$source_alignment 221 ); 222 223 let results = (outs); 224 225 let verifier = [{ return verifyCopyMemory(*this); }]; 226 227 let autogenSerialization = 0; 228} 229 230// ----- 231 232def SPV_ExecutionModeOp : SPV_Op<"ExecutionMode", [InModuleScope]> { 233 let summary = "Declare an execution mode for an entry point."; 234 235 let description = [{ 236 Entry Point must be the Entry Point <id> operand of an OpEntryPoint 237 instruction. 238 239 Mode is the execution mode. See Execution Mode. 240 241 This instruction is only valid when the Mode operand is an execution 242 mode that takes no Extra Operands, or takes Extra Operands that are not 243 <id> operands. 244 245 <!-- End of AutoGen section --> 246 247 ``` 248 execution-mode ::= "Invocations" | "SpacingEqual" | 249 <and other SPIR-V execution modes...> 250 251 execution-mode-op ::= `spv.ExecutionMode ` ssa-use execution-mode 252 (integer-literal (`, ` integer-literal)* )? 253 ``` 254 255 #### Example: 256 257 ```mlir 258 spv.ExecutionMode @foo "ContractionOff" 259 spv.ExecutionMode @bar "LocalSizeHint", 3, 4, 5 260 ``` 261 }]; 262 263 let arguments = (ins 264 FlatSymbolRefAttr:$fn, 265 SPV_ExecutionModeAttr:$execution_mode, 266 I32ArrayAttr:$values 267 ); 268 269 let results = (outs); 270 271 let verifier = [{ return success(); }]; 272 273 let autogenSerialization = 0; 274 275 let builders = [ 276 OpBuilderDAG<(ins "spirv::FuncOp":$function, 277 "spirv::ExecutionMode":$executionMode, "ArrayRef<int32_t>":$params)>]; 278} 279 280// ----- 281 282def SPV_LoadOp : SPV_Op<"Load", []> { 283 let summary = "Load through a pointer."; 284 285 let description = [{ 286 Result Type is the type of the loaded object. It must be a type with 287 fixed size; i.e., it cannot be, nor include, any OpTypeRuntimeArray 288 types. 289 290 Pointer is the pointer to load through. Its type must be an 291 OpTypePointer whose Type operand is the same as Result Type. 292 293 If present, any Memory Operands must begin with a memory operand 294 literal. If not present, it is the same as specifying the memory operand 295 None. 296 297 <!-- End of AutoGen section --> 298 299 ``` 300 memory-access ::= `"None"` | `"Volatile"` | `"Aligned", ` integer-literal 301 | `"NonTemporal"` 302 303 load-op ::= ssa-id ` = spv.Load ` storage-class ssa-use 304 (`[` memory-access `]`)? ` : ` spirv-element-type 305 ``` 306 307 #### Example: 308 309 ```mlir 310 %0 = spv.Variable : !spv.ptr<f32, Function> 311 %1 = spv.Load "Function" %0 : f32 312 %2 = spv.Load "Function" %0 ["Volatile"] : f32 313 %3 = spv.Load "Function" %0 ["Aligned", 4] : f32 314 ``` 315 }]; 316 317 let arguments = (ins 318 SPV_AnyPtr:$ptr, 319 OptionalAttr<SPV_MemoryAccessAttr>:$memory_access, 320 OptionalAttr<I32Attr>:$alignment 321 ); 322 323 let results = (outs 324 SPV_Type:$value 325 ); 326 327 let builders = [ 328 OpBuilderDAG<(ins "Value":$basePtr, 329 CArg<"IntegerAttr", "{}">:$memory_access, 330 CArg<"IntegerAttr", "{}">:$alignment)> 331 ]; 332} 333 334// ----- 335 336def SPV_MemoryBarrierOp : SPV_Op<"MemoryBarrier", []> { 337 let summary = "Control the order that memory accesses are observed."; 338 339 let description = [{ 340 Ensures that memory accesses issued before this instruction will be 341 observed before memory accesses issued after this instruction. This 342 control is ensured only for memory accesses issued by this invocation 343 and observed by another invocation executing within Memory scope. If the 344 Vulkan memory model is declared, this ordering only applies to memory 345 accesses that use the NonPrivatePointer memory operand or 346 NonPrivateTexel image operand. 347 348 Semantics declares what kind of memory is being controlled and what kind 349 of control to apply. 350 351 To execute both a memory barrier and a control barrier, see 352 OpControlBarrier. 353 354 <!-- End of AutoGen section --> 355 356 ``` 357 scope ::= `"CrossDevice"` | `"Device"` | `"Workgroup"` | ... 358 359 memory-semantics ::= `"None"` | `"Acquire"` | `"Release"` | ... 360 361 memory-barrier-op ::= `spv.MemoryBarrier` scope, memory-semantics 362 ``` 363 364 #### Example: 365 366 ```mlir 367 spv.MemoryBarrier "Device", "Acquire|UniformMemory" 368 369 ``` 370 }]; 371 372 let arguments = (ins 373 SPV_ScopeAttr:$memory_scope, 374 SPV_MemorySemanticsAttr:$memory_semantics 375 ); 376 377 let results = (outs); 378 379 let verifier = [{ return verifyMemorySemantics(*this); }]; 380 381 let autogenSerialization = 0; 382 383 let assemblyFormat = "$memory_scope `,` $memory_semantics attr-dict"; 384} 385 386// ----- 387 388def SPV_StoreOp : SPV_Op<"Store", []> { 389 let summary = "Store through a pointer."; 390 391 let description = [{ 392 Pointer is the pointer to store through. Its type must be an 393 OpTypePointer whose Type operand is the same as the type of Object. 394 395 Object is the object to store. 396 397 If present, any Memory Operands must begin with a memory operand 398 literal. If not present, it is the same as specifying the memory operand 399 None. 400 401 <!-- End of AutoGen section --> 402 403 ``` 404 store-op ::= `spv.Store ` storage-class ssa-use `, ` ssa-use `, ` 405 (`[` memory-access `]`)? `:` spirv-element-type 406 ``` 407 408 #### Example: 409 410 ```mlir 411 %0 = spv.Variable : !spv.ptr<f32, Function> 412 %1 = spv.FMul ... : f32 413 spv.Store "Function" %0, %1 : f32 414 spv.Store "Function" %0, %1 ["Volatile"] : f32 415 spv.Store "Function" %0, %1 ["Aligned", 4] : f32 416 ``` 417 }]; 418 419 let arguments = (ins 420 SPV_AnyPtr:$ptr, 421 SPV_Type:$value, 422 OptionalAttr<SPV_MemoryAccessAttr>:$memory_access, 423 OptionalAttr<I32Attr>:$alignment 424 ); 425 426 let results = (outs); 427 428 let builders = [ 429 OpBuilderDAG<(ins "Value":$ptr, "Value":$value, 430 CArg<"ArrayRef<NamedAttribute>", "{}">:$namedAttrs), 431 [{ 432 $_state.addOperands(ptr); 433 $_state.addOperands(value); 434 $_state.addAttributes(namedAttrs); 435 }]> 436 ]; 437} 438 439// ----- 440 441def SPV_UndefOp : SPV_Op<"undef", []> { 442 let summary = "Make an intermediate object whose value is undefined."; 443 444 let description = [{ 445 Result Type is the type of object to make. 446 447 Each consumption of Result <id> yields an arbitrary, possibly different 448 bit pattern or abstract value resulting in possibly different concrete, 449 abstract, or opaque values. 450 451 <!-- End of AutoGen section --> 452 453 ``` 454 undef-op ::= `spv.undef` `:` spirv-type 455 ``` 456 457 #### Example: 458 459 ```mlir 460 %0 = spv.undef : f32 461 %1 = spv.undef : !spv.struct<!spv.array<4 x vector<4xi32>>> 462 ``` 463 }]; 464 465 let arguments = (ins); 466 467 let results = (outs 468 SPV_Type:$result 469 ); 470 471 let verifier = [{ return success(); }]; 472 473 let hasOpcode = 0; 474 let autogenSerialization = 0; 475 476 let assemblyFormat = "attr-dict `:` type($result)"; 477} 478 479// ----- 480 481def SPV_VariableOp : SPV_Op<"Variable", []> { 482 let summary = [{ 483 Allocate an object in memory, resulting in a pointer to it, which can be 484 used with OpLoad and OpStore. 485 }]; 486 487 let description = [{ 488 Result Type must be an OpTypePointer. Its Type operand is the type of 489 object in memory. 490 491 Storage Class is the Storage Class of the memory holding the object. 492 Since the op is used to model function-level variables, the storage class 493 must be the `Function` Storage Class. 494 495 Initializer is optional. If Initializer is present, it will be the 496 initial value of the variable’s memory content. Initializer must be an 497 <id> from a constant instruction or a global (module scope) OpVariable 498 instruction. Initializer must have the same type as the type pointed to 499 by Result Type. 500 501 <!-- End of AutoGen section --> 502 503 ``` 504 variable-op ::= ssa-id `=` `spv.Variable` (`init(` ssa-use `)`)? 505 attribute-dict? `:` spirv-pointer-type 506 ``` 507 508 where `init` specifies initializer. 509 510 #### Example: 511 512 ```mlir 513 %0 = spv.constant ... 514 515 %1 = spv.Variable : !spv.ptr<f32, Function> 516 %2 = spv.Variable init(%0): !spv.ptr<f32, Function> 517 ``` 518 }]; 519 520 let arguments = (ins 521 SPV_StorageClassAttr:$storage_class, 522 Optional<AnyType>:$initializer 523 ); 524 525 let results = (outs 526 SPV_AnyPtr:$pointer 527 ); 528} 529 530// ----- 531 532#endif // SPIRV_OPS 533