1//===-- SPIRVStructureOps.td - MLIR SPIR-V Structure Ops ---*- 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 file contains ops for defining the SPIR-V structure: module, function, 10// and module-level operations. The representational form of these ops deviate 11// from the SPIR-V binary format in order to utilize MLIR mechanisms. 12// 13//===----------------------------------------------------------------------===// 14 15#ifndef SPIRV_STRUCTURE_OPS 16#define SPIRV_STRUCTURE_OPS 17 18include "mlir/Dialect/SPIRV/SPIRVBase.td" 19include "mlir/IR/SymbolInterfaces.td" 20include "mlir/Interfaces/CallInterfaces.td" 21include "mlir/Interfaces/SideEffectInterfaces.td" 22 23// ----- 24 25def SPV_AddressOfOp : SPV_Op<"mlir.addressof", [InFunctionScope, NoSideEffect]> { 26 let summary = "Get the address of a global variable."; 27 28 let description = [{ 29 Variables in module scope are defined using symbol names. This op generates 30 an SSA value that can be used to refer to the symbol within function scope 31 for use in ops that expect an SSA value. This operation has no corresponding 32 SPIR-V instruction; it's merely used for modelling purpose in the SPIR-V 33 dialect. Since variables in module scope in SPIR-V dialect are of pointer 34 type, this op returns a pointer type as well, and the type is the same as 35 the variable referenced. 36 37 <!-- End of AutoGen section --> 38 39 ``` 40 spv-address-of-op ::= ssa-id `=` `spv.mlir.addressof` symbol-ref-id 41 `:` spirv-pointer-type 42 ``` 43 44 #### Example: 45 46 ```mlir 47 %0 = spv.mlir.addressof @global_var : !spv.ptr<f32, Input> 48 ``` 49 }]; 50 51 let arguments = (ins 52 FlatSymbolRefAttr:$variable 53 ); 54 55 let results = (outs 56 SPV_AnyPtr:$pointer 57 ); 58 59 let hasOpcode = 0; 60 61 let autogenSerialization = 0; 62 63 let builders = [OpBuilderDAG<(ins "spirv::GlobalVariableOp":$var)>]; 64 65 let assemblyFormat = "$variable attr-dict `:` type($pointer)"; 66} 67 68// ----- 69 70def SPV_ConstantOp : SPV_Op<"constant", [ConstantLike, NoSideEffect]> { 71 let summary = "The op that declares a SPIR-V normal constant"; 72 73 let description = [{ 74 This op declares a SPIR-V normal constant. SPIR-V has multiple constant 75 instructions covering different constant types: 76 77 * `OpConstantTrue` and `OpConstantFalse` for boolean constants 78 * `OpConstant` for scalar constants 79 * `OpConstantComposite` for composite constants 80 * `OpConstantNull` for null constants 81 * ... 82 83 Having such a plethora of constant instructions renders IR transformations 84 more tedious. Therefore, we use a single `spv.constant` op to represent 85 them all. Note that conversion between those SPIR-V constant instructions 86 and this op is purely mechanical; so it can be scoped to the binary 87 (de)serialization process. 88 89 <!-- End of AutoGen section --> 90 91 ``` 92 spv-constant-op ::= ssa-id `=` `spv.constant` attribute-value 93 (`:` spirv-type)? 94 ``` 95 96 #### Example: 97 98 ```mlir 99 %0 = spv.constant true 100 %1 = spv.constant dense<[2, 3]> : vector<2xf32> 101 %2 = spv.constant [dense<3.0> : vector<2xf32>] : !spv.array<1xvector<2xf32>> 102 ``` 103 104 TODO: support constant structs 105 }]; 106 107 let arguments = (ins 108 AnyAttr:$value 109 ); 110 111 let results = (outs 112 SPV_Type:$constant 113 ); 114 115 let hasFolder = 1; 116 117 let extraClassDeclaration = [{ 118 // Returns true if a constant can be built for the given `type`. 119 static bool isBuildableWith(Type type); 120 121 // Creates a constant zero/one of the given `type` at the current insertion 122 // point of `builder` and returns it. 123 static spirv::ConstantOp getZero(Type type, Location loc, 124 OpBuilder &builder); 125 static spirv::ConstantOp getOne(Type type, Location loc, 126 OpBuilder &builder); 127 }]; 128 129 let hasOpcode = 0; 130 131 let autogenSerialization = 0; 132} 133 134// ----- 135 136def SPV_EntryPointOp : SPV_Op<"EntryPoint", [InModuleScope]> { 137 let summary = [{ 138 Declare an entry point, its execution model, and its interface. 139 }]; 140 141 let description = [{ 142 Execution Model is the execution model for the entry point and its 143 static call tree. See Execution Model. 144 145 Entry Point must be the Result <id> of an OpFunction instruction. 146 147 Name is a name string for the entry point. A module cannot have two 148 OpEntryPoint instructions with the same Execution Model and the same 149 Name string. 150 151 Interface is a list of symbol references to `spv.globalVariable` 152 operations. These declare the set of global variables from a 153 module that form the interface of this entry point. The set of 154 Interface symbols must be equal to or a superset of the 155 `spv.globalVariable`s referenced by the entry point’s static call 156 tree, within the interface’s storage classes. Before version 1.4, 157 the interface’s storage classes are limited to the Input and 158 Output storage classes. Starting with version 1.4, the interface’s 159 storage classes are all storage classes used in declaring all 160 global variables referenced by the entry point’s call tree. 161 162 <!-- End of AutoGen section --> 163 164 ``` 165 execution-model ::= "Vertex" | "TesellationControl" | 166 <and other SPIR-V execution models...> 167 168 entry-point-op ::= ssa-id `=` `spv.EntryPoint` execution-model 169 symbol-reference (`, ` symbol-reference)* 170 ``` 171 172 #### Example: 173 174 ```mlir 175 spv.EntryPoint "GLCompute" @foo 176 spv.EntryPoint "Kernel" @foo, @var1, @var2 177 178 ``` 179 }]; 180 181 let arguments = (ins 182 SPV_ExecutionModelAttr:$execution_model, 183 FlatSymbolRefAttr:$fn, 184 SymbolRefArrayAttr:$interface 185 ); 186 187 let results = (outs); 188 189 let autogenSerialization = 0; 190 191 let builders = [ 192 OpBuilderDAG<(ins "spirv::ExecutionModel":$executionModel, 193 "spirv::FuncOp":$function, "ArrayRef<Attribute>":$interfaceVars)>]; 194} 195 196// ----- 197 198def SPV_FuncOp : SPV_Op<"func", [ 199 AutomaticAllocationScope, DeclareOpInterfaceMethods<CallableOpInterface>, 200 FunctionLike, InModuleScope, IsolatedFromAbove, Symbol 201 ]> { 202 let summary = "Declare or define a function"; 203 204 let description = [{ 205 This op declares or defines a SPIR-V function using one region, which 206 contains one or more blocks. 207 208 Different from the SPIR-V binary format, this op is not allowed to 209 implicitly capture global values, and all external references must use 210 function arguments or symbol references. This op itself defines a symbol 211 that is unique in the enclosing module op. 212 213 This op itself takes no operands and generates no results. Its region 214 can take zero or more arguments and return zero or one values. 215 216 <!-- End of AutoGen section --> 217 218 ``` 219 spv-function-control ::= "None" | "Inline" | "DontInline" | ... 220 spv-function-op ::= `spv.func` function-signature 221 spv-function-control region 222 ``` 223 224 #### Example: 225 226 ```mlir 227 spv.func @foo() -> () "None" { ... } 228 spv.func @bar() -> () "Inline|Pure" { ... } 229 ``` 230 }]; 231 232 let arguments = (ins 233 TypeAttr:$type, 234 StrAttr:$sym_name, 235 SPV_FunctionControlAttr:$function_control 236 ); 237 238 let results = (outs); 239 240 let regions = (region AnyRegion:$body); 241 242 let verifier = [{ return success(); }]; 243 244 let builders = [ 245 OpBuilderDAG<(ins "StringRef":$name, "FunctionType":$type, 246 CArg<"spirv::FunctionControl", "spirv::FunctionControl::None">:$control, 247 CArg<"ArrayRef<NamedAttribute>", "{}">:$attrs)>]; 248 249 let hasOpcode = 0; 250 251 let autogenSerialization = 0; 252 253 let extraClassDeclaration = [{ 254 private: 255 // This trait needs access to the hooks defined below. 256 friend class OpTrait::FunctionLike<FuncOp>; 257 258 /// Returns the number of arguments. Hook for OpTrait::FunctionLike. 259 unsigned getNumFuncArguments() { return getType().getNumInputs(); } 260 261 /// Returns the number of results. Hook for OpTrait::FunctionLike. 262 unsigned getNumFuncResults() { return getType().getNumResults(); } 263 264 /// Hook for OpTrait::FunctionLike, called after verifying that the 'type' 265 /// attribute is present and checks if it holds a function type. Ensures 266 /// getType, getNumFuncArguments, and getNumFuncResults can be called safely 267 LogicalResult verifyType(); 268 269 /// Hook for OpTrait::FunctionLike, called after verifying the function 270 /// type and the presence of the (potentially empty) function body. 271 /// Ensures SPIR-V specific semantics. 272 LogicalResult verifyBody(); 273 }]; 274} 275 276// ----- 277 278def SPV_GlobalVariableOp : SPV_Op<"globalVariable", [InModuleScope, Symbol]> { 279 let summary = [{ 280 Allocate an object in memory at module scope. The object is 281 referenced using a symbol name. 282 }]; 283 284 let description = [{ 285 The variable type must be an OpTypePointer. Its type operand is the type of 286 object in memory. 287 288 Storage Class is the Storage Class of the memory holding the object. It 289 cannot be Generic. It must be the same as the Storage Class operand of 290 the variable types. Only those storage classes that are valid at module 291 scope (like Input, Output, StorageBuffer, etc.) are valid. 292 293 Initializer is optional. If Initializer is present, it will be 294 the initial value of the variable’s memory content. Initializer 295 must be an symbol defined from a constant instruction or other 296 `spv.globalVariable` operation in module scope. Initializer must 297 have the same type as the type of the defined symbol. 298 299 <!-- End of AutoGen section --> 300 301 ``` 302 variable-op ::= `spv.globalVariable` spirv-type symbol-ref-id 303 (`initializer(` symbol-ref-id `)`)? 304 (`bind(` integer-literal, integer-literal `)`)? 305 (`built_in(` string-literal `)`)? 306 attribute-dict? 307 ``` 308 309 where `initializer` specifies initializer and `bind` specifies the 310 descriptor set and binding number. `built_in` specifies SPIR-V 311 BuiltIn decoration associated with the op. 312 313 #### Example: 314 315 ```mlir 316 spv.globalVariable @var0 : !spv.ptr<f32, Input> @var0 317 spv.globalVariable @var1 initializer(@var0) : !spv.ptr<f32, Output> 318 spv.globalVariable @var2 bind(1, 2) : !spv.ptr<f32, Uniform> 319 spv.globalVariable @var3 built_in("GlobalInvocationId") : !spv.ptr<vector<3xi32>, Input> 320 ``` 321 }]; 322 323 let arguments = (ins 324 TypeAttr:$type, 325 StrAttr:$sym_name, 326 OptionalAttr<FlatSymbolRefAttr>:$initializer 327 ); 328 329 let results = (outs); 330 331 let builders = [ 332 OpBuilderDAG<(ins "TypeAttr":$type, "ArrayRef<NamedAttribute>":$namedAttrs), 333 [{ 334 $_state.addAttribute("type", type); 335 $_state.addAttributes(namedAttrs); 336 }]>, 337 OpBuilderDAG<(ins "Type":$type, "StringRef":$name, 338 "unsigned":$descriptorSet, "unsigned":$binding)>, 339 OpBuilderDAG<(ins "Type":$type, "StringRef":$name, 340 "spirv::BuiltIn":$builtin)> 341 ]; 342 343 let hasOpcode = 0; 344 345 let autogenSerialization = 0; 346 347 let extraClassDeclaration = [{ 348 ::mlir::spirv::StorageClass storageClass() { 349 return this->type().cast<::mlir::spirv::PointerType>().getStorageClass(); 350 } 351 }]; 352} 353 354// ----- 355 356def SPV_ModuleOp : SPV_Op<"module", 357 [IsolatedFromAbove, 358 SingleBlockImplicitTerminator<"ModuleEndOp">, 359 SymbolTable, Symbol]> { 360 let summary = "The top-level op that defines a SPIR-V module"; 361 362 let description = [{ 363 This op defines a SPIR-V module using a MLIR region. The region contains 364 one block. Module-level operations, including functions definitions, 365 are all placed in this block. 366 367 Using an op with a region to define a SPIR-V module enables "embedding" 368 SPIR-V modules in other dialects in a clean manner: this op guarantees 369 the validity and serializability of a SPIR-V module and thus serves as 370 a clear-cut boundary. 371 372 This op takes no operands and generates no results. This op should not 373 implicitly capture values from the enclosing environment. 374 375 This op has only one region, which only contains one block. The block 376 must be terminated via the `spv.mlir.endmodule` op. 377 378 <!-- End of AutoGen section --> 379 380 ``` 381 addressing-model ::= `Logical` | `Physical32` | `Physical64` | ... 382 memory-model ::= `Simple` | `GLSL450` | `OpenCL` | `Vulkan` | ... 383 spv-module-op ::= `spv.module` addressing-model memory-model 384 (requires spirv-vce-attribute)? 385 (`attributes` attribute-dict)? 386 region 387 ``` 388 389 #### Example: 390 391 ```mlir 392 spv.module Logical GLSL450 {} 393 394 spv.module Logical Vulkan 395 requires #spv.vce<v1.0, [Shader], [SPV_KHR_vulkan_memory_model]> 396 attributes { some_additional_attr = ... } { 397 spv.func @do_nothing() -> () { 398 spv.Return 399 } 400 } 401 ``` 402 }]; 403 404 let arguments = (ins 405 SPV_AddressingModelAttr:$addressing_model, 406 SPV_MemoryModelAttr:$memory_model, 407 OptionalAttr<SPV_VerCapExtAttr>:$vce_triple, 408 OptionalAttr<StrAttr>:$sym_name 409 ); 410 411 let results = (outs); 412 413 let regions = (region SizedRegion<1>:$body); 414 415 let builders = [ 416 OpBuilderDAG<(ins CArg<"Optional<StringRef>", "llvm::None">:$name)>, 417 OpBuilderDAG<(ins "spirv::AddressingModel":$addressing_model, 418 "spirv::MemoryModel":$memory_model, 419 CArg<"Optional<StringRef>", "llvm::None">:$name)> 420 ]; 421 422 // We need to ensure the block inside the region is properly terminated; 423 // the auto-generated builders do not guarantee that. 424 let skipDefaultBuilders = 1; 425 426 let hasOpcode = 0; 427 428 let autogenSerialization = 0; 429 430 let extraClassDeclaration = [{ 431 432 bool isOptionalSymbol() { return true; } 433 434 Optional<StringRef> getName() { return sym_name(); } 435 436 static StringRef getVCETripleAttrName() { return "vce_triple"; } 437 438 Block& getBlock() { 439 return this->getOperation()->getRegion(0).front(); 440 } 441 }]; 442} 443 444// ----- 445 446def SPV_ModuleEndOp : SPV_Op<"mlir.endmodule", [InModuleScope, Terminator]> { 447 let summary = "The pseudo op that ends a SPIR-V module"; 448 449 let description = [{ 450 This op terminates the only block inside a `spv.module`'s only region. 451 This op does not have a corresponding SPIR-V instruction and thus will 452 not be serialized into the binary format; it is used solely to satisfy 453 the structual requirement that an block must be ended with a terminator. 454 }]; 455 456 let arguments = (ins); 457 458 let results = (outs); 459 460 let assemblyFormat = "attr-dict"; 461 462 let verifier = [{ return success(); }]; 463 464 let hasOpcode = 0; 465 466 let autogenSerialization = 0; 467} 468 469// ----- 470 471def SPV_ReferenceOfOp : SPV_Op<"mlir.referenceof", [NoSideEffect]> { 472 let summary = "Reference a specialization constant."; 473 474 let description = [{ 475 Specialization constants in module scope are defined using symbol names. 476 This op generates an SSA value that can be used to refer to the symbol 477 within function scope for use in ops that expect an SSA value. 478 This operation has no corresponding SPIR-V instruction; it's merely used 479 for modelling purpose in the SPIR-V dialect. This op's return type is 480 the same as the specialization constant. 481 482 <!-- End of AutoGen section --> 483 484 ``` 485 spv-reference-of-op ::= ssa-id `=` `spv.mlir.referenceof` symbol-ref-id 486 `:` spirv-scalar-type 487 ``` 488 489 #### Example: 490 491 ```mlir 492 %0 = spv.mlir.referenceof @spec_const : f32 493 ``` 494 495 TODO Add support for composite specialization constants. 496 }]; 497 498 let arguments = (ins 499 FlatSymbolRefAttr:$spec_const 500 ); 501 502 let results = (outs 503 SPV_Type:$reference 504 ); 505 506 let hasOpcode = 0; 507 508 let autogenSerialization = 0; 509 510 let assemblyFormat = "$spec_const attr-dict `:` type($reference)"; 511} 512 513// ----- 514 515def SPV_SpecConstantOp : SPV_Op<"specConstant", [InModuleScope, Symbol]> { 516 let summary = "The op that declares a SPIR-V specialization constant"; 517 518 let description = [{ 519 This op declares a SPIR-V scalar specialization constant. SPIR-V has 520 multiple constant instructions covering different scalar types: 521 522 * `OpSpecConstantTrue` and `OpSpecConstantFalse` for boolean constants 523 * `OpSpecConstant` for scalar constants 524 525 Similar as `spv.constant`, this op represents all of the above cases. 526 `OpSpecConstantComposite` and `OpSpecConstantOp` are modelled with 527 separate ops. 528 529 <!-- End of AutoGen section --> 530 531 ``` 532 spv-spec-constant-op ::= `spv.specConstant` symbol-ref-id 533 `spec_id(` integer `)` 534 `=` attribute-value (`:` spirv-type)? 535 ``` 536 537 where `spec_id` specifies the SPIR-V SpecId decoration associated with 538 the op. 539 540 #### Example: 541 542 ```mlir 543 spv.specConstant @spec_const1 = true 544 spv.specConstant @spec_const2 spec_id(5) = 42 : i32 545 ``` 546 }]; 547 548 let arguments = (ins 549 StrAttr:$sym_name, 550 AnyAttr:$default_value 551 ); 552 553 let results = (outs); 554 555 let hasOpcode = 0; 556 557 let autogenSerialization = 0; 558} 559 560def SPV_SpecConstantCompositeOp : SPV_Op<"specConstantComposite", [InModuleScope, Symbol]> { 561 let summary = "Declare a new composite specialization constant."; 562 563 let description = [{ 564 This op declares a SPIR-V composite specialization constant. This covers 565 the `OpSpecConstantComposite` SPIR-V instruction. Scalar constants are 566 covered by `spv.specConstant`. 567 568 A constituent of a spec constant composite can be: 569 - A symbol referring of another spec constant. 570 - The SSA ID of a non-specialization constant (i.e. defined through 571 `spv.specConstant`). 572 - The SSA ID of a `spv.undef`. 573 574 ``` 575 spv-spec-constant-composite-op ::= `spv.specConstantComposite` symbol-ref-id ` (` 576 symbol-ref-id (`, ` symbol-ref-id)* 577 `) :` composite-type 578 ``` 579 580 where `composite-type` is some non-scalar type that can be represented in the `spv` 581 dialect: `spv.struct`, `spv.array`, or `vector`. 582 583 #### Example: 584 585 ```mlir 586 spv.specConstant @sc1 = 1 : i32 587 spv.specConstant @sc2 = 2.5 : f32 588 spv.specConstant @sc3 = 3.5 : f32 589 spv.specConstantComposite @scc (@sc1, @sc2, @sc3) : !spv.struct<i32, f32, f32> 590 ``` 591 592 TODO Add support for constituents that are: 593 - regular constants. 594 - undef. 595 - spec constant composite. 596 }]; 597 598 let arguments = (ins 599 TypeAttr:$type, 600 StrAttr:$sym_name, 601 SymbolRefArrayAttr:$constituents 602 ); 603 604 let results = (outs); 605 606 let hasOpcode = 0; 607 608 let autogenSerialization = 0; 609} 610 611def SPV_YieldOp : SPV_Op<"mlir.yield", [NoSideEffect, Terminator]> { 612 let summary = "Yields the result computed in `spv.SpecConstantOperation`'s" 613 "region back to the parent op."; 614 615 let description = [{ 616 This op is a special terminator whose only purpose is to terminate 617 an `spv.SpecConstantOperation`'s enclosed region. It accepts a 618 single operand produced by the preceeding (and only other) instruction 619 in its parent block (see SPV_SpecConstantOperation for further 620 details). This op has no corresponding SPIR-V instruction. 621 622 ``` 623 spv.mlir.yield ::= `spv.mlir.yield` ssa-id : spirv-type 624 ``` 625 626 #### Example: 627 ```mlir 628 %0 = ... (some op supported by SPIR-V OpSpecConstantOp) 629 spv.mlir.yield %0 630 ``` 631 }]; 632 633 let arguments = (ins AnyType:$operand); 634 635 let results = (outs); 636 637 let hasOpcode = 0; 638 639 let autogenSerialization = 0; 640 641 let assemblyFormat = "attr-dict $operand `:` type($operand)"; 642} 643 644def SPV_SpecConstantOperationOp : SPV_Op<"SpecConstantOperation", [ 645 InFunctionScope, NoSideEffect, 646 IsolatedFromAbove]> { 647 let summary = "Declare a new specialization constant that results from doing an operation."; 648 649 let description = [{ 650 This op declares a SPIR-V specialization constant that results from 651 doing an operation on other constants (specialization or otherwise). 652 653 In the `spv` dialect, this op is modelled as follows: 654 655 ``` 656 spv-spec-constant-operation-op ::= `"spv.SpecConstantOperation"` 657 `(`ssa-id (`, ` ssa-id)`)` 658 `({` 659 ssa-id = spirv-op 660 `spv.mlir.yield` ssa-id 661 `})` `:` function-type 662 ``` 663 664 In particular, an `spv.SpecConstantOperation` contains exactly one 665 region. In turn, that region, contains exactly 2 instructions: 666 - One of SPIR-V's instructions that are allowed within an 667 OpSpecConstantOp. 668 - An `spv.mlir.yield` instruction as the terminator. 669 670 The following SPIR-V instructions are valid: 671 - OpSConvert, 672 - OpUConvert, 673 - OpFConvert, 674 - OpSNegate, 675 - OpNot, 676 - OpIAdd, 677 - OpISub, 678 - OpIMul, 679 - OpUDiv, 680 - OpSDiv, 681 - OpUMod, 682 - OpSRem, 683 - OpSMod 684 - OpShiftRightLogical, 685 - OpShiftRightArithmetic, 686 - OpShiftLeftLogical 687 - OpBitwiseOr, 688 - OpBitwiseXor, 689 - OpBitwiseAnd 690 - OpVectorShuffle, 691 - OpCompositeExtract, 692 - OpCompositeInsert 693 - OpLogicalOr, 694 - OpLogicalAnd, 695 - OpLogicalNot, 696 - OpLogicalEqual, 697 - OpLogicalNotEqual 698 - OpSelect 699 - OpIEqual, 700 - OpINotEqual 701 - OpULessThan, 702 - OpSLessThan 703 - OpUGreaterThan, 704 - OpSGreaterThan 705 - OpULessThanEqual, 706 - OpSLessThanEqual 707 - OpUGreaterThanEqual, 708 - OpSGreaterThanEqual 709 710 TODO Add capability-specific ops when supported. 711 712 #### Example: 713 ```mlir 714 %0 = spv.constant 1: i32 715 716 %1 = "spv.SpecConstantOperation"(%0) ({ 717 %ret = spv.IAdd %0, %0 : i32 718 spv.mlir.yield %ret : i32 719 }) : (i32) -> i32 720 ``` 721 }]; 722 723 let arguments = (ins Variadic<AnyType>:$operands); 724 725 let results = (outs AnyType:$results); 726 727 let regions = (region SizedRegion<1>:$body); 728 729 let hasOpcode = 0; 730 731 let autogenSerialization = 0; 732} 733 734// ----- 735 736#endif // SPIRV_STRUCTURE_OPS 737