-
Notifications
You must be signed in to change notification settings - Fork 11.6k
/
SPIRVStructureOps.td
562 lines (416 loc) · 16.7 KB
/
SPIRVStructureOps.td
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
//===-- 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 SPIRV_STRUCTURE_OPS
#define SPIRV_STRUCTURE_OPS
include "mlir/Dialect/SPIRV/SPIRVBase.td"
include "mlir/IR/SymbolInterfaces.td"
include "mlir/Interfaces/CallInterfaces.td"
include "mlir/Interfaces/SideEffectInterfaces.td"
// -----
def SPV_AddressOfOp : SPV_Op<"_address_of", [InFunctionScope, NoSideEffect]> {
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.
<!-- End of AutoGen section -->
```
spv-address-of-op ::= ssa-id `=` `spv._address_of` symbol-ref-id
`:` spirv-pointer-type
```
#### Example:
```mlir
%0 = spv._address_of @global_var : !spv.ptr<f32, Input>
```
}];
let arguments = (ins
FlatSymbolRefAttr:$variable
);
let results = (outs
SPV_AnyPtr:$pointer
);
let hasOpcode = 0;
let autogenSerialization = 0;
let builders = [OpBuilder<[{spirv::GlobalVariableOp var}]>];
let assemblyFormat = "$variable attr-dict `:` type($pointer)";
}
// -----
def SPV_ConstantOp : SPV_Op<"constant", [ConstantLike, NoSideEffect]> {
let summary = "The op that declares a SPIR-V normal 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 `spv.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.
<!-- End of AutoGen section -->
```
spv-constant-op ::= ssa-id `=` `spv.constant` attribute-value
(`:` spirv-type)?
```
#### Example:
```mlir
%0 = spv.constant true
%1 = spv.constant dense<[2, 3]> : vector<2xf32>
%2 = spv.constant [dense<3.0> : vector<2xf32>] : !spv.array<1xvector<2xf32>>
```
TODO: support constant structs
}];
let arguments = (ins
AnyAttr:$value
);
let results = (outs
SPV_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 SPV_EntryPointOp : SPV_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 <id> 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 `spv.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
`spv.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.
<!-- End of AutoGen section -->
```
execution-model ::= "Vertex" | "TesellationControl" |
<and other SPIR-V execution models...>
entry-point-op ::= ssa-id `=` `spv.EntryPoint` execution-model
symbol-reference (`, ` symbol-reference)*
```
#### Example:
```mlir
spv.EntryPoint "GLCompute" @foo
spv.EntryPoint "Kernel" @foo, @var1, @var2
```
}];
let arguments = (ins
SPV_ExecutionModelAttr:$execution_model,
FlatSymbolRefAttr:$fn,
SymbolRefArrayAttr:$interface
);
let results = (outs);
let autogenSerialization = 0;
let builders = [OpBuilder<[{spirv::ExecutionModel executionModel,
spirv::FuncOp function,
ArrayRef<Attribute> interfaceVars}]>];
}
// -----
def SPV_FuncOp : SPV_Op<"func", [
AutomaticAllocationScope, DeclareOpInterfaceMethods<CallableOpInterface>,
FunctionLike, InModuleScope, IsolatedFromAbove, Symbol
]> {
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.
<!-- End of AutoGen section -->
```
spv-function-control ::= "None" | "Inline" | "DontInline" | ...
spv-function-op ::= `spv.func` function-signature
spv-function-control region
```
#### Example:
```mlir
spv.func @foo() -> () "None" { ... }
spv.func @bar() -> () "Inline|Pure" { ... }
```
}];
let arguments = (ins
TypeAttr:$type,
StrAttr:$sym_name,
SPV_FunctionControlAttr:$function_control
);
let results = (outs);
let regions = (region AnyRegion:$body);
let verifier = [{ return success(); }];
let builders = [OpBuilder<[{
StringRef name, FunctionType type,
spirv::FunctionControl control = spirv::FunctionControl::None,
ArrayRef<NamedAttribute> attrs = {}
}]>];
let hasOpcode = 0;
let autogenSerialization = 0;
let extraClassDeclaration = [{
private:
// This trait needs access to the hooks defined below.
friend class OpTrait::FunctionLike<FuncOp>;
/// Returns the number of arguments. Hook for OpTrait::FunctionLike.
unsigned getNumFuncArguments() { return getType().getNumInputs(); }
/// Returns the number of results. Hook for OpTrait::FunctionLike.
unsigned getNumFuncResults() { return getType().getNumResults(); }
/// Hook for OpTrait::FunctionLike, called after verifying that the 'type'
/// attribute is present and checks if it holds a function type. Ensures
/// getType, getNumFuncArguments, and getNumFuncResults can be called safely
LogicalResult verifyType();
/// Hook for OpTrait::FunctionLike, called after verifying the function
/// type and the presence of the (potentially empty) function body.
/// Ensures SPIR-V specific semantics.
LogicalResult verifyBody();
}];
}
// -----
def SPV_GlobalVariableOp : SPV_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
`spv.globalVariable` operation in module scope. Initializer must
have the same type as the type of the defined symbol.
<!-- End of AutoGen section -->
```
variable-op ::= `spv.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
spv.globalVariable @var0 : !spv.ptr<f32, Input> @var0
spv.globalVariable @var1 initializer(@var0) : !spv.ptr<f32, Output>
spv.globalVariable @var2 bind(1, 2) : !spv.ptr<f32, Uniform>
spv.globalVariable @var3 built_in("GlobalInvocationId") : !spv.ptr<vector<3xi32>, Input>
```
}];
let arguments = (ins
TypeAttr:$type,
StrAttr:$sym_name,
OptionalAttr<FlatSymbolRefAttr>:$initializer
);
let results = (outs);
let builders = [
OpBuilder<
"TypeAttr type, ArrayRef<NamedAttribute> namedAttrs", [{
$_state.addAttribute("type", type);
$_state.addAttributes(namedAttrs);
}]>,
OpBuilder<[{Type type, StringRef name, unsigned descriptorSet,
unsigned binding}]>,
OpBuilder<[{Type type, StringRef name, spirv::BuiltIn builtin}]>
];
let hasOpcode = 0;
let autogenSerialization = 0;
let extraClassDeclaration = [{
::mlir::spirv::StorageClass storageClass() {
return this->type().cast<::mlir::spirv::PointerType>().getStorageClass();
}
}];
}
// -----
def SPV_ModuleOp : SPV_Op<"module",
[IsolatedFromAbove,
SingleBlockImplicitTerminator<"ModuleEndOp">,
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
must be terminated via the `spv._module_end` op.
<!-- End of AutoGen section -->
```
addressing-model ::= `Logical` | `Physical32` | `Physical64` | ...
memory-model ::= `Simple` | `GLSL450` | `OpenCL` | `Vulkan` | ...
spv-module-op ::= `spv.module` addressing-model memory-model
(requires spirv-vce-attribute)?
(`attributes` attribute-dict)?
region
```
#### Example:
```mlir
spv.module Logical GLSL450 {}
spv.module Logical Vulkan
requires #spv.vce<v1.0, [Shader], [SPV_KHR_vulkan_memory_model]>
attributes { some_additional_attr = ... } {
spv.func @do_nothing() -> () {
spv.Return
}
}
```
}];
let arguments = (ins
SPV_AddressingModelAttr:$addressing_model,
SPV_MemoryModelAttr:$memory_model,
OptionalAttr<SPV_VerCapExtAttr>:$vce_triple,
OptionalAttr<StrAttr>:$sym_name
);
let results = (outs);
let regions = (region SizedRegion<1>:$body);
let builders = [
OpBuilder<[{Optional<StringRef> name = llvm::None}]>,
OpBuilder<[{spirv::AddressingModel addressing_model,
spirv::MemoryModel memory_model,
Optional<StringRef> name = llvm::None}]>
];
// 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; }
Optional<StringRef> getName() { return sym_name(); }
static StringRef getVCETripleAttrName() { return "vce_triple"; }
Block& getBlock() {
return this->getOperation()->getRegion(0).front();
}
}];
}
// -----
def SPV_ModuleEndOp : SPV_Op<"_module_end", [InModuleScope, Terminator]> {
let summary = "The pseudo op that ends a SPIR-V module";
let description = [{
This op terminates the only block inside a `spv.module`'s only region.
This op does not have a corresponding SPIR-V instruction and thus will
not be serialized into the binary format; it is used solely to satisfy
the structual requirement that an block must be ended with a terminator.
}];
let arguments = (ins);
let results = (outs);
let assemblyFormat = "attr-dict";
let verifier = [{ return success(); }];
let hasOpcode = 0;
let autogenSerialization = 0;
}
// -----
def SPV_ReferenceOfOp : SPV_Op<"_reference_of", [NoSideEffect]> {
let summary = "Reference a specialization constant.";
let description = [{
Specialization constant 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.
<!-- End of AutoGen section -->
```
spv-reference-of-op ::= ssa-id `=` `spv._reference_of` symbol-ref-id
`:` spirv-scalar-type
```
#### Example:
```mlir
%0 = spv._reference_of @spec_const : f32
```
}];
let arguments = (ins
FlatSymbolRefAttr:$spec_const
);
let results = (outs
SPV_Type:$reference
);
let hasOpcode = 0;
let autogenSerialization = 0;
let assemblyFormat = "$spec_const attr-dict `:` type($reference)";
}
// -----
def SPV_SpecConstantOp : SPV_Op<"specConstant", [InModuleScope, Symbol]> {
let summary = "The op that declares a SPIR-V 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 `spv.constant`, this op represents all of the above cases.
`OpSpecConstantComposite` and `OpSpecConstantOp` are modelled with
separate ops.
<!-- End of AutoGen section -->
```
spv-spec-constant-op ::= `spv.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
spv.specConstant @spec_const1 = true
spv.specConstant @spec_const2 spec_id(5) = 42 : i32
```
TODO: support composite spec constants with another op
}];
let arguments = (ins
StrAttr:$sym_name,
AnyAttr:$default_value
);
let results = (outs);
let hasOpcode = 0;
let autogenSerialization = 0;
}
// -----
#endif // SPIRV_STRUCTURE_OPS