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