• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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