MLIR 24.0.0git
SPIRVOps.cpp
Go to the documentation of this file.
1//===- SPIRVOps.cpp - MLIR SPIR-V operations ------------------------------===//
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 defines the operations in the SPIR-V dialect.
10//
11//===----------------------------------------------------------------------===//
12
14
15#include "SPIRVOpUtils.h"
16#include "SPIRVParsingUtils.h"
17
24#include "mlir/IR/Builders.h"
28#include "mlir/IR/Operation.h"
31#include "llvm/ADT/APFloat.h"
32#include "llvm/ADT/APInt.h"
33#include "llvm/ADT/ArrayRef.h"
34#include "llvm/ADT/STLExtras.h"
35#include "llvm/ADT/StringExtras.h"
36#include "llvm/ADT/TypeSwitch.h"
37#include "llvm/Support/InterleavedRange.h"
38#include <cassert>
39#include <numeric>
40#include <optional>
41
42using namespace mlir;
43using namespace mlir::spirv::AttrNames;
44
45//===----------------------------------------------------------------------===//
46// Common utility functions
47//===----------------------------------------------------------------------===//
48
49LogicalResult spirv::extractValueFromConstOp(Operation *op, int32_t &value) {
50 auto constOp = dyn_cast_or_null<spirv::ConstantOp>(op);
51 if (!constOp) {
52 return failure();
53 }
54 auto valueAttr = constOp.getValue();
55 auto integerValueAttr = dyn_cast<IntegerAttr>(valueAttr);
56 if (!integerValueAttr) {
57 return failure();
58 }
59
60 if (integerValueAttr.getType().isSignlessInteger())
61 value = integerValueAttr.getInt();
62 else
63 value = integerValueAttr.getSInt();
64
65 return success();
66}
67
68LogicalResult
70 spirv::MemorySemantics memorySemantics) {
71 // According to the SPIR-V specification:
72 // "Despite being a mask and allowing multiple bits to be combined, it is
73 // invalid for more than one of these four bits to be set: Acquire, Release,
74 // AcquireRelease, or SequentiallyConsistent. Requesting both Acquire and
75 // Release semantics is done by setting the AcquireRelease bit, not by setting
76 // two bits."
77 auto atMostOneInSet = spirv::MemorySemantics::Acquire |
78 spirv::MemorySemantics::Release |
79 spirv::MemorySemantics::AcquireRelease |
80 spirv::MemorySemantics::SequentiallyConsistent;
81
82 auto bitCount =
83 llvm::popcount(static_cast<uint32_t>(memorySemantics & atMostOneInSet));
84 if (bitCount > 1) {
85 return op->emitError(
86 "expected at most one of these four memory constraints "
87 "to be set: `Acquire`, `Release`,"
88 "`AcquireRelease` or `SequentiallyConsistent`");
89 }
90 return success();
91}
92
94 Type pointeeType) {
95 // From SPV_KHR_physical_storage_buffer:
96 // > If an OpVariable's pointee type is a pointer (or array of pointers) in
97 // > PhysicalStorageBuffer storage class, then the variable must be decorated
98 // > with exactly one of AliasedPointer or RestrictPointer.
99 auto pointeePtrType = dyn_cast<spirv::PointerType>(pointeeType);
100 if (!pointeePtrType) {
101 if (auto pointeeArrayType = dyn_cast<spirv::ArrayType>(pointeeType)) {
102 pointeePtrType =
103 dyn_cast<spirv::PointerType>(pointeeArrayType.getElementType());
104 }
105 }
106
107 if (!pointeePtrType || pointeePtrType.getStorageClass() !=
108 spirv::StorageClass::PhysicalStorageBuffer)
109 return success();
110
111 auto getDecorationAttr = [op](spirv::Decoration decoration) {
112 return op->getDiscardableAttr(spirv::getDecorationString(decoration));
113 };
114
115 bool hasAliasedPtr =
116 getDecorationAttr(spirv::Decoration::AliasedPointer) != nullptr;
117 bool hasRestrictPtr =
118 getDecorationAttr(spirv::Decoration::RestrictPointer) != nullptr;
119
120 if (!hasAliasedPtr && !hasRestrictPtr)
121 return op->emitOpError()
122 << " with physical buffer pointer must be decorated "
123 "either 'AliasedPointer' or 'RestrictPointer'";
124
125 if (hasAliasedPtr && hasRestrictPtr)
126 return op->emitOpError()
127 << " with physical buffer pointer must have exactly one "
128 "aliasing decoration";
129
130 return success();
131}
132
134 SmallVectorImpl<StringRef> &elidedAttrs) {
135 NamedAttrList attrs(op->getDiscardableAttrDictionary().getValue());
136 op->getName().populateInherentAttrs(op, attrs);
137
138 // Print optional descriptor binding
139 auto descriptorSetName = llvm::convertToSnakeFromCamelCase(
140 stringifyDecoration(spirv::Decoration::DescriptorSet));
141 auto bindingName = llvm::convertToSnakeFromCamelCase(
142 stringifyDecoration(spirv::Decoration::Binding));
143 auto descriptorSet =
144 dyn_cast_or_null<IntegerAttr>(attrs.get(descriptorSetName));
145 auto binding = dyn_cast_or_null<IntegerAttr>(attrs.get(bindingName));
146 if (descriptorSet && binding) {
147 elidedAttrs.push_back(descriptorSetName);
148 elidedAttrs.push_back(bindingName);
149 printer << " bind(" << descriptorSet.getInt() << ", " << binding.getInt()
150 << ")";
151 }
152
153 // Print BuiltIn attribute if present
154 auto builtInName = llvm::convertToSnakeFromCamelCase(
155 stringifyDecoration(spirv::Decoration::BuiltIn));
156 if (auto builtin = dyn_cast_or_null<StringAttr>(attrs.get(builtInName))) {
157 printer << " " << builtInName << "(\"" << builtin.getValue() << "\")";
158 elidedAttrs.push_back(builtInName);
159 }
160
161 printer.printOptionalAttrDict(attrs, elidedAttrs);
162}
163
167 Type type;
168 // If the operand list is in-between parentheses, then we have a generic form.
169 // (see the fallback in `printOneResultOp`).
170 SMLoc loc = parser.getCurrentLocation();
171 if (!parser.parseOptionalLParen()) {
172 if (parser.parseOperandList(ops) || parser.parseRParen() ||
173 parser.parseOptionalAttrDict(result.attributes) ||
174 parser.parseColon() || parser.parseType(type))
175 return failure();
176 auto fnType = dyn_cast<FunctionType>(type);
177 if (!fnType) {
178 parser.emitError(loc, "expected function type");
179 return failure();
180 }
181 if (parser.resolveOperands(ops, fnType.getInputs(), loc, result.operands))
182 return failure();
183 result.addTypes(fnType.getResults());
184 return success();
185 }
186 return failure(parser.parseOperandList(ops) ||
187 parser.parseOptionalAttrDict(result.attributes) ||
188 parser.parseColonType(type) ||
189 parser.resolveOperands(ops, type, result.operands) ||
190 parser.addTypeToList(type, result.types));
191}
192
194 assert(op->getNumResults() == 1 && "op should have one result");
195
196 // If not all the operand and result types are the same, just use the
197 // generic assembly form to avoid omitting information in printing.
198 auto resultType = op->getResult(0).getType();
199 if (llvm::any_of(op->getOperandTypes(),
200 [&](Type type) { return type != resultType; })) {
201 p.printGenericOp(op, /*printOpName=*/false);
202 return;
203 }
204
205 p << ' ';
206 p.printOperands(op->getOperands());
208 // Now we can output only one type for all operands and the result.
209 p << " : " << resultType;
210}
211
212template <typename BlockReadWriteOpTy>
213static LogicalResult verifyBlockReadWritePtrAndValTypes(BlockReadWriteOpTy op,
214 Value ptr, Value val) {
215 auto valType = val.getType();
216 if (auto valVecTy = dyn_cast<VectorType>(valType))
217 valType = valVecTy.getElementType();
218
219 if (valType != cast<spirv::PointerType>(ptr.getType()).getPointeeType()) {
220 return op.emitOpError("mismatch in result type and pointer type");
221 }
222 return success();
223}
224
225/// Walks the given type hierarchy with the given indices, potentially down
226/// to component granularity, to select an element type. Returns null type and
227/// emits errors with the given loc on failure.
228static Type
230 function_ref<InFlightDiagnostic(StringRef)> emitErrorFn) {
231 if (indices.empty()) {
232 emitErrorFn("expected at least one index for spirv.CompositeExtract");
233 return nullptr;
234 }
235
236 for (auto index : indices) {
237 if (auto cType = dyn_cast<spirv::CompositeType>(type)) {
238 if (cType.hasCompileTimeKnownNumElements() &&
239 (index < 0 ||
240 static_cast<uint64_t>(index) >= cType.getNumElements())) {
241 emitErrorFn("index ") << index << " out of bounds for " << type;
242 return nullptr;
243 }
244 type = cType.getElementType(index);
245 } else {
246 emitErrorFn("cannot extract from non-composite type ")
247 << type << " with index " << index;
248 return nullptr;
249 }
250 }
251 return type;
252}
253
254static Type
256 function_ref<InFlightDiagnostic(StringRef)> emitErrorFn) {
257 auto indicesArrayAttr = dyn_cast<ArrayAttr>(indices);
258 if (!indicesArrayAttr) {
259 emitErrorFn("expected a 32-bit integer array attribute for 'indices'");
260 return nullptr;
261 }
262 if (indicesArrayAttr.empty()) {
263 emitErrorFn("expected at least one index for spirv.CompositeExtract");
264 return nullptr;
265 }
266
267 SmallVector<int32_t, 2> indexVals;
268 for (auto indexAttr : indicesArrayAttr) {
269 auto indexIntAttr = dyn_cast<IntegerAttr>(indexAttr);
270 if (!indexIntAttr) {
271 emitErrorFn("expected an 32-bit integer for index, but found '")
272 << indexAttr << "'";
273 return nullptr;
274 }
275 indexVals.push_back(indexIntAttr.getInt());
276 }
277 return getElementType(type, indexVals, emitErrorFn);
278}
279
281 auto errorFn = [&](StringRef err) -> InFlightDiagnostic {
282 return ::mlir::emitError(loc, err);
283 };
284 return getElementType(type, indices, errorFn);
285}
286
288 SMLoc loc) {
289 auto errorFn = [&](StringRef err) -> InFlightDiagnostic {
290 return parser.emitError(loc, err);
291 };
292 return getElementType(type, indices, errorFn);
293}
294
295template <typename ExtendedBinaryOp>
296static LogicalResult verifyArithmeticExtendedBinaryOp(ExtendedBinaryOp op) {
297 auto resultType = cast<spirv::StructType>(op.getType());
298 if (resultType.getNumElements() != 2)
299 return op.emitOpError("expected result struct type containing two members");
300
301 if (!llvm::all_equal({op.getOperand1().getType(), op.getOperand2().getType(),
302 resultType.getElementType(0),
303 resultType.getElementType(1)}))
304 return op.emitOpError(
305 "expected all operand types and struct member types are the same");
306
307 return success();
308}
309
313 if (parser.parseOptionalAttrDict(result.attributes) ||
314 parser.parseOperandList(operands) || parser.parseColon())
315 return failure();
316
317 Type resultType;
318 SMLoc loc = parser.getCurrentLocation();
319 if (parser.parseType(resultType))
320 return failure();
321
322 auto structType = dyn_cast<spirv::StructType>(resultType);
323 if (!structType || structType.getNumElements() != 2)
324 return parser.emitError(loc, "expected spirv.struct type with two members");
325
326 SmallVector<Type, 2> operandTypes(2, structType.getElementType(0));
327 if (parser.resolveOperands(operands, operandTypes, loc, result.operands))
328 return failure();
329
330 result.addTypes(resultType);
331 return success();
332}
333
335 OpAsmPrinter &printer) {
336 printer << ' ';
337 printer.printOptionalAttrDict(op->getDiscardableAttrDictionary().getValue());
338 printer.printOperands(op->getOperands());
339 printer << " : " << op->getResultTypes().front();
340}
341
342static LogicalResult verifyShiftOp(Operation *op) {
343 if (op->getOperand(0).getType() != op->getResult(0).getType()) {
344 return op->emitError("expected the same type for the first operand and "
345 "result, but provided ")
346 << op->getOperand(0).getType() << " and "
347 << op->getResult(0).getType();
348 }
349 return success();
350}
351
352//===----------------------------------------------------------------------===//
353// spirv.mlir.addressof
354//===----------------------------------------------------------------------===//
355
356void spirv::AddressOfOp::build(OpBuilder &builder, OperationState &state,
357 spirv::GlobalVariableOp var) {
358 build(builder, state, var.getType(), SymbolRefAttr::get(var));
359}
360
361LogicalResult
362spirv::AddressOfOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
363 auto varOp = symbolTable.lookupNearestSymbolFrom<spirv::GlobalVariableOp>(
364 *this, getVariableAttr());
365 if (!varOp) {
366 return emitOpError("expected spirv.GlobalVariable symbol");
367 }
368 if (getPointer().getType() != varOp.getType()) {
369 return emitOpError(
370 "result type mismatch with the referenced global variable's type");
371 }
372 return success();
373}
374
375//===----------------------------------------------------------------------===//
376// spirv.CompositeConstruct
377//===----------------------------------------------------------------------===//
378
379LogicalResult spirv::CompositeConstructOp::verify() {
380 operand_range constituents = this->getConstituents();
381
382 // There are 4 cases with varying verification rules:
383 // 1. Cooperative Matrices (1 constituent)
384 // 2. Structs (1 constituent for each member)
385 // 3. Arrays (1 constituent for each array element)
386 // 4. Vectors (1 constituent (sub-)element for each vector element)
387
388 auto coopElementType = llvm::TypeSwitch<Type, Type>(getType())
389 .Case([](spirv::CooperativeMatrixType coopType) {
390 return coopType.getElementType();
391 })
392 .Default(nullptr);
393
394 // Case 1. -- matrices.
395 if (coopElementType) {
396 if (constituents.size() != 1)
397 return emitOpError("has incorrect number of operands: expected ")
398 << "1, but provided " << constituents.size();
399 if (coopElementType != constituents.front().getType())
400 return emitOpError("operand type mismatch: expected operand type ")
401 << coopElementType << ", but provided "
402 << constituents.front().getType();
403 return success();
404 }
405
406 // Case 2./3./4. -- number of constituents matches the number of elements.
407 auto cType = cast<spirv::CompositeType>(getType());
408 if (constituents.size() == cType.getNumElements()) {
409 for (auto index : llvm::seq<uint32_t>(0, constituents.size())) {
410 if (constituents[index].getType() != cType.getElementType(index)) {
411 return emitOpError("operand type mismatch: expected operand type ")
412 << cType.getElementType(index) << ", but provided "
413 << constituents[index].getType();
414 }
415 }
416 return success();
417 }
418
419 // Case 4. -- check that all constituents add up tp the expected vector type.
420 auto resultType = dyn_cast<VectorType>(cType);
421 if (!resultType)
422 return emitOpError(
423 "expected to return a vector or cooperative matrix when the number of "
424 "constituents is less than what the result needs");
425
426 if (constituents.size() < 2)
427 return emitOpError("has incorrect number of operands: expected at least "
428 "2 for a vector result, but provided ")
429 << constituents.size();
430
432 for (Value component : constituents) {
433 if (!isa<VectorType>(component.getType()) &&
434 !component.getType().isIntOrFloat())
435 return emitOpError("operand type mismatch: expected operand to have "
436 "a scalar or vector type, but provided ")
437 << component.getType();
438
439 Type elementType = component.getType();
440 if (auto vectorType = dyn_cast<VectorType>(component.getType())) {
441 sizes.push_back(vectorType.getNumElements());
442 elementType = vectorType.getElementType();
443 } else {
444 sizes.push_back(1);
445 }
446
447 if (elementType != resultType.getElementType())
448 return emitOpError("operand element type mismatch: expected to be ")
449 << resultType.getElementType() << ", but provided " << elementType;
450 }
451 unsigned totalCount = llvm::sum_of(sizes);
452 if (totalCount != cType.getNumElements())
453 return emitOpError("has incorrect number of operands: expected ")
454 << cType.getNumElements() << ", but provided " << totalCount;
455 return success();
456}
457
458//===----------------------------------------------------------------------===//
459// spirv.CompositeExtractOp
460//===----------------------------------------------------------------------===//
461
462void spirv::CompositeExtractOp::build(OpBuilder &builder, OperationState &state,
463 Value composite,
465 auto indexAttr = builder.getI32ArrayAttr(indices);
466 auto elementType =
467 getElementType(composite.getType(), indexAttr, state.location);
468 if (!elementType) {
469 return;
470 }
471 build(builder, state, elementType, composite, indexAttr);
472}
473
474ParseResult spirv::CompositeExtractOp::parse(OpAsmParser &parser,
476 OpAsmParser::UnresolvedOperand compositeInfo;
477 Attribute indicesAttr;
478 StringRef indicesAttrName =
479 spirv::CompositeExtractOp::getIndicesAttrName(result.name);
480 Type compositeType;
481 SMLoc attrLocation;
482
483 if (parser.parseOperand(compositeInfo) ||
484 parser.getCurrentLocation(&attrLocation) ||
485 parser.parseAttribute(indicesAttr, indicesAttrName, result.attributes) ||
486 parser.parseColonType(compositeType) ||
487 parser.resolveOperand(compositeInfo, compositeType, result.operands)) {
488 return failure();
489 }
490
491 Type resultType =
492 getElementType(compositeType, indicesAttr, parser, attrLocation);
493 if (!resultType) {
494 return failure();
495 }
496 result.addTypes(resultType);
497 return success();
498}
499
500void spirv::CompositeExtractOp::print(OpAsmPrinter &printer) {
501 printer << ' ' << getComposite() << getIndices() << " : "
502 << getComposite().getType();
503}
504
505LogicalResult spirv::CompositeExtractOp::verify() {
506 auto indicesArrayAttr = dyn_cast<ArrayAttr>(getIndices());
507 auto resultType =
508 getElementType(getComposite().getType(), indicesArrayAttr, getLoc());
509 if (!resultType)
510 return failure();
511
512 if (resultType != getType()) {
513 return emitOpError("invalid result type: expected ")
514 << resultType << " but provided " << getType();
515 }
516
517 return success();
518}
519
520//===----------------------------------------------------------------------===//
521// spirv.CompositeInsert
522//===----------------------------------------------------------------------===//
523
524void spirv::CompositeInsertOp::build(OpBuilder &builder, OperationState &state,
525 Value object, Value composite,
527 auto indexAttr = builder.getI32ArrayAttr(indices);
528 build(builder, state, composite.getType(), object, composite, indexAttr);
529}
530
531ParseResult spirv::CompositeInsertOp::parse(OpAsmParser &parser,
534 Type objectType, compositeType;
535 Attribute indicesAttr;
536 StringRef indicesAttrName =
537 spirv::CompositeInsertOp::getIndicesAttrName(result.name);
538 auto loc = parser.getCurrentLocation();
539
540 return failure(
541 parser.parseOperandList(operands, 2) ||
542 parser.parseAttribute(indicesAttr, indicesAttrName, result.attributes) ||
543 parser.parseColonType(objectType) ||
544 parser.parseKeywordType("into", compositeType) ||
545 parser.resolveOperands(operands, {objectType, compositeType}, loc,
546 result.operands) ||
547 parser.addTypesToList(compositeType, result.types));
548}
549
550LogicalResult spirv::CompositeInsertOp::verify() {
551 auto indicesArrayAttr = dyn_cast<ArrayAttr>(getIndices());
552 auto objectType =
553 getElementType(getComposite().getType(), indicesArrayAttr, getLoc());
554 if (!objectType)
555 return failure();
556
557 if (objectType != getObject().getType()) {
558 return emitOpError("object operand type should be ")
559 << objectType << ", but found " << getObject().getType();
560 }
561
562 if (getComposite().getType() != getType()) {
563 return emitOpError("result type should be the same as "
564 "the composite type, but found ")
565 << getComposite().getType() << " vs " << getType();
566 }
567
568 return success();
569}
570
571void spirv::CompositeInsertOp::print(OpAsmPrinter &printer) {
572 printer << " " << getObject() << ", " << getComposite() << getIndices()
573 << " : " << getObject().getType() << " into "
574 << getComposite().getType();
575}
576
577//===----------------------------------------------------------------------===//
578// spirv.Constant
579//===----------------------------------------------------------------------===//
580
581ParseResult spirv::ConstantOp::parse(OpAsmParser &parser,
583 Attribute value;
584 StringRef valueAttrName = spirv::ConstantOp::getValueAttrName(result.name);
585 if (parser.parseAttribute(value, valueAttrName, result.attributes))
586 return failure();
587
588 Type type = NoneType::get(parser.getContext());
589 if (auto typedAttr = dyn_cast<TypedAttr>(value))
590 type = typedAttr.getType();
591 if (isa<NoneType, TensorType>(type)) {
592 if (parser.parseColonType(type))
593 return failure();
594 }
595
596 if (isa<TensorArmType>(type)) {
597 if (parser.parseOptionalColon().succeeded())
598 if (parser.parseType(type))
599 return failure();
600 }
601
602 return parser.addTypeToList(type, result.types);
603}
604
605void spirv::ConstantOp::print(OpAsmPrinter &printer) {
606 printer << ' ' << getValue();
607 if (isa<spirv::ArrayType, spirv::StructType>(getType()) &&
608 !isa<spirv::NullAttr>(getValue()))
609 printer << " : " << getType();
610}
611
612static LogicalResult verifyConstantType(spirv::ConstantOp op, Attribute value,
613 Type opType) {
614 if (isa<spirv::CooperativeMatrixType>(opType)) {
615 auto denseAttr = dyn_cast<DenseElementsAttr>(value);
616 if (!denseAttr || !denseAttr.isSplat())
617 return op.emitOpError("expected a splat dense attribute for cooperative "
618 "matrix constant, but found ")
619 << denseAttr;
620 }
621 if (isa<spirv::NullAttr>(value) &&
622 !isa<spirv::ArrayType, spirv::PointerType, spirv::StructType>(opType))
623 return op.emitOpError("must have spirv.ptr, spirv.array or spirv.struct "
624 "result type for null value");
625 if (isa<IntegerAttr, FloatAttr, spirv::NullAttr>(value)) {
626 auto valueType = cast<TypedAttr>(value).getType();
627 if (valueType != opType)
628 return op.emitOpError("result type (")
629 << opType << ") does not match value type (" << valueType << ")";
630 return success();
631 }
632 if (isa<DenseTypedElementsAttr, SparseElementsAttr>(value)) {
633 auto valueType = cast<TypedAttr>(value).getType();
634 if (valueType == opType)
635 return success();
636 auto arrayType = dyn_cast<spirv::ArrayType>(opType);
637 auto shapedType = dyn_cast<ShapedType>(valueType);
638 if (!arrayType)
639 return op.emitOpError("result or element type (")
640 << opType << ") does not match value type (" << valueType
641 << "), must be the same or spirv.array";
642
643 int numElements = arrayType.getNumElements();
644 auto opElemType = arrayType.getElementType();
645 while (auto t = dyn_cast<spirv::ArrayType>(opElemType)) {
646 numElements *= t.getNumElements();
647 opElemType = t.getElementType();
648 }
649 if (!opElemType.isIntOrFloat())
650 return op.emitOpError("only support nested array result type");
651
652 auto valueElemType = shapedType.getElementType();
653 if (valueElemType != opElemType) {
654 return op.emitOpError("result element type (")
655 << opElemType << ") does not match value element type ("
656 << valueElemType << ")";
657 }
658
659 if (numElements != shapedType.getNumElements()) {
660 return op.emitOpError("result number of elements (")
661 << numElements << ") does not match value number of elements ("
662 << shapedType.getNumElements() << ")";
663 }
664 return success();
665 }
666 if (auto arrayAttr = dyn_cast<ArrayAttr>(value)) {
667 if (auto structType = dyn_cast<spirv::StructType>(opType)) {
668 // Identified (possibly recursive) structs are not supported as constants.
669 if (structType.isIdentified())
670 return op.emitOpError(
671 "cannot have an identified struct as a constant type");
672 if (arrayAttr.size() != structType.getNumElements())
673 return op.emitOpError("number of constituents (")
674 << arrayAttr.size()
675 << ") does not match number of struct members ("
676 << structType.getNumElements() << ")";
677 for (auto [idx, element] : llvm::enumerate(arrayAttr.getValue())) {
678 if (failed(verifyConstantType(op, element,
679 structType.getElementType(idx))))
680 return failure();
681 }
682 return success();
683 }
684 auto arrayType = dyn_cast<spirv::ArrayType>(opType);
685 if (!arrayType)
686 return op.emitOpError(
687 "must have spirv.array or spirv.struct result type for array value");
688 Type elemType = arrayType.getElementType();
689 for (Attribute element : arrayAttr.getValue()) {
690 // Verify array elements recursively.
691 if (failed(verifyConstantType(op, element, elemType)))
692 return failure();
693 }
694 return success();
695 }
696 return op.emitOpError("cannot have attribute: ") << value;
697}
698
699LogicalResult spirv::ConstantOp::verify() {
700 // ODS already generates checks to make sure the result type is valid. We just
701 // need to additionally check that the value's attribute type is consistent
702 // with the result type.
703 return verifyConstantType(*this, getValueAttr(), getType());
704}
705
706bool spirv::ConstantOp::isBuildableWith(Type type) {
707 // Must be valid SPIR-V type first.
708 if (!isa<spirv::SPIRVType>(type))
709 return false;
710
711 if (isa<SPIRVDialect>(type.getDialect())) {
712 if (auto structType = dyn_cast<spirv::StructType>(type))
713 return !structType.isIdentified();
714 return isa<spirv::ArrayType>(type);
715 }
716
717 return true;
718}
719
720spirv::ConstantOp spirv::ConstantOp::getZero(Type type, Location loc,
721 OpBuilder &builder) {
722 if (auto intType = dyn_cast<IntegerType>(type)) {
723 unsigned width = intType.getWidth();
724 if (width == 1)
725 return spirv::ConstantOp::create(builder, loc, type,
726 builder.getBoolAttr(false));
727 return spirv::ConstantOp::create(
728 builder, loc, type, builder.getIntegerAttr(type, APInt(width, 0)));
729 }
730 if (auto floatType = dyn_cast<FloatType>(type)) {
731 return spirv::ConstantOp::create(builder, loc, type,
732 builder.getFloatAttr(floatType, 0.0));
733 }
734 if (auto vectorType = dyn_cast<VectorType>(type)) {
735 Type elemType = vectorType.getElementType();
736 if (isa<IntegerType>(elemType)) {
737 return spirv::ConstantOp::create(
738 builder, loc, type,
739 DenseElementsAttr::get(vectorType,
740 IntegerAttr::get(elemType, 0).getValue()));
741 }
742 if (isa<FloatType>(elemType)) {
743 return spirv::ConstantOp::create(
744 builder, loc, type,
745 DenseFPElementsAttr::get(vectorType,
746 FloatAttr::get(elemType, 0.0).getValue()));
747 }
748 }
749
750 llvm_unreachable("unimplemented types for ConstantOp::getZero()");
751}
752
753spirv::ConstantOp spirv::ConstantOp::getOne(Type type, Location loc,
754 OpBuilder &builder) {
755 if (auto intType = dyn_cast<IntegerType>(type)) {
756 unsigned width = intType.getWidth();
757 if (width == 1)
758 return spirv::ConstantOp::create(builder, loc, type,
759 builder.getBoolAttr(true));
760 return spirv::ConstantOp::create(
761 builder, loc, type, builder.getIntegerAttr(type, APInt(width, 1)));
762 }
763 if (auto floatType = dyn_cast<FloatType>(type)) {
764 return spirv::ConstantOp::create(builder, loc, type,
765 builder.getFloatAttr(floatType, 1.0));
766 }
767 if (auto vectorType = dyn_cast<VectorType>(type)) {
768 Type elemType = vectorType.getElementType();
769 if (isa<IntegerType>(elemType)) {
770 return spirv::ConstantOp::create(
771 builder, loc, type,
772 DenseElementsAttr::get(vectorType,
773 IntegerAttr::get(elemType, 1).getValue()));
774 }
775 if (isa<FloatType>(elemType)) {
776 return spirv::ConstantOp::create(
777 builder, loc, type,
778 DenseFPElementsAttr::get(vectorType,
779 FloatAttr::get(elemType, 1.0).getValue()));
780 }
781 }
782
783 llvm_unreachable("unimplemented types for ConstantOp::getOne()");
784}
785
786void mlir::spirv::ConstantOp::getAsmResultNames(
787 llvm::function_ref<void(mlir::Value, llvm::StringRef)> setNameFn) {
788 Type type = getType();
789
790 SmallString<32> specialNameBuffer;
791 llvm::raw_svector_ostream specialName(specialNameBuffer);
792 specialName << "cst";
793
794 IntegerType intTy = dyn_cast<IntegerType>(type);
795
796 if (IntegerAttr intCst = dyn_cast<IntegerAttr>(getValue())) {
797 assert(intTy);
798
799 if (intTy.getWidth() == 1) {
800 return setNameFn(getResult(), (intCst.getInt() ? "true" : "false"));
801 }
802
803 if (intTy.isSignless()) {
804 specialName << intCst.getInt();
805 } else if (intTy.isUnsigned()) {
806 specialName << intCst.getUInt();
807 } else {
808 specialName << intCst.getSInt();
809 }
810 }
811
812 if (intTy || isa<FloatType>(type)) {
813 specialName << '_' << type;
814 }
815
816 if (auto vecType = dyn_cast<VectorType>(type)) {
817 specialName << "_vec_";
818 specialName << vecType.getDimSize(0);
819
820 Type elementType = vecType.getElementType();
821
822 if (isa<IntegerType>(elementType) || isa<FloatType>(elementType)) {
823 specialName << "x" << elementType;
824 }
825 }
826
827 setNameFn(getResult(), specialName.str());
828}
829
830void mlir::spirv::AddressOfOp::getAsmResultNames(
831 llvm::function_ref<void(mlir::Value, llvm::StringRef)> setNameFn) {
832 SmallString<32> specialNameBuffer;
833 llvm::raw_svector_ostream specialName(specialNameBuffer);
834 specialName << getVariable() << "_addr";
835 setNameFn(getResult(), specialName.str());
836}
837
838//===----------------------------------------------------------------------===//
839// spirv.EXTConstantCompositeReplicate
840//===----------------------------------------------------------------------===//
841
842// Returns type of attribute. In case of a TypedAttr this will simply return
843// the type. But for an ArrayAttr which is untyped and can be multidimensional
844// it creates the ArrayType recursively.
846 if (auto typedAttr = dyn_cast<TypedAttr>(attr)) {
847 return typedAttr.getType();
848 }
849
850 if (auto arrayAttr = dyn_cast<ArrayAttr>(attr)) {
851 return spirv::ArrayType::get(getValueType(arrayAttr[0]), arrayAttr.size());
852 }
853
854 return nullptr;
855}
856
857LogicalResult spirv::EXTConstantCompositeReplicateOp::verify() {
858 Type valueType = getValueType(getValue());
859 if (!valueType)
860 return emitError("unknown value attribute type");
861
862 auto compositeType = dyn_cast<spirv::CompositeType>(getType());
863 if (!compositeType)
864 return emitError("result type is not a composite type");
865
866 Type compositeElementType = compositeType.getElementType(0);
867
868 SmallVector<Type, 3> possibleTypes = {compositeElementType};
869 while (auto type = dyn_cast<spirv::CompositeType>(compositeElementType)) {
870 compositeElementType = type.getElementType(0);
871 possibleTypes.push_back(compositeElementType);
872 }
873
874 if (!is_contained(possibleTypes, valueType)) {
875 return emitError("expected value attribute type ")
876 << interleaved(possibleTypes, " or ") << ", but got: " << valueType;
877 }
878
879 return success();
880}
881
882//===----------------------------------------------------------------------===//
883// spirv.ControlBarrierOp
884//===----------------------------------------------------------------------===//
885
886LogicalResult spirv::ControlBarrierOp::verify() {
887 return verifyMemorySemantics(getOperation(), getMemorySemantics());
888}
889
890//===----------------------------------------------------------------------===//
891// spirv.EntryPoint
892//===----------------------------------------------------------------------===//
893
894void spirv::EntryPointOp::build(OpBuilder &builder, OperationState &state,
895 spirv::ExecutionModel executionModel,
896 spirv::FuncOp function,
897 ArrayRef<Attribute> interfaceVars) {
898 build(builder, state,
899 spirv::ExecutionModelAttr::get(builder.getContext(), executionModel),
900 SymbolRefAttr::get(function), builder.getArrayAttr(interfaceVars));
901}
902
903ParseResult spirv::EntryPointOp::parse(OpAsmParser &parser,
905 spirv::ExecutionModel execModel;
906 SmallVector<Attribute, 4> interfaceVars;
907
909 if (parseEnumStrAttr<spirv::ExecutionModelAttr>(execModel, parser, result) ||
910 parser.parseAttribute(fn, Type(), kFnNameAttrName, result.attributes)) {
911 return failure();
912 }
913
914 if (!parser.parseOptionalComma()) {
915 // Parse the interface variables
916 if (parser.parseCommaSeparatedList([&]() -> ParseResult {
917 // The name of the interface variable attribute isnt important
918 FlatSymbolRefAttr var;
919 NamedAttrList attrs;
920 if (parser.parseAttribute(var, Type(), "var_symbol", attrs))
921 return failure();
922 interfaceVars.push_back(var);
923 return success();
924 }))
925 return failure();
926 }
927 result.addAttribute(spirv::EntryPointOp::getInterfaceAttrName(result.name),
928 parser.getBuilder().getArrayAttr(interfaceVars));
929 return success();
930}
931
932void spirv::EntryPointOp::print(OpAsmPrinter &printer) {
933 printer << " \"" << stringifyExecutionModel(getExecutionModel()) << "\" ";
934 printer.printSymbolName(getFn());
935 auto interfaceVars = getInterface().getValue();
936 if (!interfaceVars.empty())
937 printer << ", " << llvm::interleaved(interfaceVars);
938}
939
940LogicalResult spirv::EntryPointOp::verify() {
941 // Checks for fn and interface symbol reference are done in spirv::ModuleOp
942 // verification.
943 return success();
944}
945
946//===----------------------------------------------------------------------===//
947// spirv.ExecutionMode / spirv.ExecutionModeId
948//===----------------------------------------------------------------------===//
949
950namespace {
951// Describes the extra operands a SPIR-V ExecutionMode expects: whether they
952// are <id> operands (only valid on spirv.ExecutionModeId) or literal integers
953// (only valid on spirv.ExecutionMode), and how many of them are required.
954struct ExecutionModeOperandSchema {
955 bool isIdOperand;
956 unsigned numOperands;
957};
958
959ExecutionModeOperandSchema
960getExecutionModeOperandSchema(spirv::ExecutionMode mode) {
961 switch (mode) {
962 case spirv::ExecutionMode::Invocations:
963 case spirv::ExecutionMode::OutputVertices:
964 case spirv::ExecutionMode::VecTypeHint:
965 case spirv::ExecutionMode::SubgroupSize:
966 case spirv::ExecutionMode::SubgroupsPerWorkgroup:
967 case spirv::ExecutionMode::DenormPreserve:
968 case spirv::ExecutionMode::DenormFlushToZero:
969 case spirv::ExecutionMode::SignedZeroInfNanPreserve:
970 case spirv::ExecutionMode::RoundingModeRTE:
971 case spirv::ExecutionMode::RoundingModeRTZ:
972 case spirv::ExecutionMode::OutputPrimitivesEXT:
973 case spirv::ExecutionMode::SharedLocalMemorySizeINTEL:
974 case spirv::ExecutionMode::RoundingModeRTPINTEL:
975 case spirv::ExecutionMode::RoundingModeRTNINTEL:
976 case spirv::ExecutionMode::FloatingPointModeALTINTEL:
977 case spirv::ExecutionMode::FloatingPointModeIEEEINTEL:
978 case spirv::ExecutionMode::MaxWorkDimINTEL:
979 case spirv::ExecutionMode::NumSIMDWorkitemsINTEL:
980 case spirv::ExecutionMode::SchedulerTargetFmaxMhzINTEL:
981 case spirv::ExecutionMode::StreamingInterfaceINTEL:
982 case spirv::ExecutionMode::NamedBarrierCountINTEL:
983 return {/*isIdOperand=*/false, /*numOperands=*/1};
984 case spirv::ExecutionMode::LocalSize:
985 case spirv::ExecutionMode::LocalSizeHint:
986 case spirv::ExecutionMode::MaxWorkgroupSizeINTEL:
987 return {/*isIdOperand=*/false, /*numOperands=*/3};
988 case spirv::ExecutionMode::SubgroupsPerWorkgroupId:
989 return {/*isIdOperand=*/true, /*numOperands=*/1};
990 case spirv::ExecutionMode::LocalSizeId:
991 case spirv::ExecutionMode::LocalSizeHintId:
992 return {/*isIdOperand=*/true, /*numOperands=*/3};
993 default:
994 return {/*isIdOperand=*/false, /*numOperands=*/0};
995 }
996}
997} // namespace
998
999//===----------------------------------------------------------------------===//
1000// spirv.ExecutionMode
1001//===----------------------------------------------------------------------===//
1002
1003void spirv::ExecutionModeOp::build(OpBuilder &builder, OperationState &state,
1004 spirv::FuncOp function,
1005 spirv::ExecutionMode executionMode,
1006 ArrayRef<int32_t> params) {
1007 build(builder, state, SymbolRefAttr::get(function),
1008 spirv::ExecutionModeAttr::get(builder.getContext(), executionMode),
1009 builder.getI32ArrayAttr(params));
1010}
1011
1012ParseResult spirv::ExecutionModeOp::parse(OpAsmParser &parser,
1014 spirv::ExecutionMode execMode;
1015 Attribute fn;
1016 if (parser.parseAttribute(fn, kFnNameAttrName, result.attributes) ||
1018 return failure();
1019 }
1020
1022 Type i32Type = parser.getBuilder().getIntegerType(32);
1023 while (!parser.parseOptionalComma()) {
1024 NamedAttrList attr;
1025 Attribute value;
1026 if (parser.parseAttribute(value, i32Type, "value", attr)) {
1027 return failure();
1028 }
1029 values.push_back(cast<IntegerAttr>(value).getInt());
1030 }
1031 StringRef valuesAttrName =
1032 spirv::ExecutionModeOp::getValuesAttrName(result.name);
1033 result.addAttribute(valuesAttrName,
1034 parser.getBuilder().getI32ArrayAttr(values));
1035 return success();
1036}
1037
1038void spirv::ExecutionModeOp::print(OpAsmPrinter &printer) {
1039 printer << " ";
1040 printer.printSymbolName(getFn());
1041 printer << " \"" << stringifyExecutionMode(getExecutionMode()) << "\"";
1042 ArrayAttr values = this->getValues();
1043 if (!values.empty())
1044 printer << ", " << llvm::interleaved(values.getAsValueRange<IntegerAttr>());
1045}
1046
1047LogicalResult spirv::ExecutionModeOp::verify() {
1048 ExecutionModeOperandSchema schema =
1049 getExecutionModeOperandSchema(getExecutionMode());
1050
1051 if (schema.isIdOperand)
1052 return emitOpError("expected ExecutionMode that takes extra operands "
1053 "that are not <id> operands, got: ")
1054 << stringifyExecutionMode(getExecutionMode());
1055
1056 if (getValues().size() != schema.numOperands)
1057 return emitOpError("expected ")
1058 << schema.numOperands << " value operand(s), got "
1059 << getValues().size();
1060
1061 return success();
1062}
1063
1064//===----------------------------------------------------------------------===//
1065// spirv.ExecutionModeId
1066//===----------------------------------------------------------------------===//
1067
1068ParseResult spirv::ExecutionModeIdOp::parse(OpAsmParser &parser,
1070 ExecutionMode execMode;
1071 if (Attribute fn;
1072 parser.parseAttribute(fn, kFnNameAttrName, result.attributes) ||
1073 parseEnumStrAttr<ExecutionModeAttr>(execMode, parser, result)) {
1074 return failure();
1075 }
1076
1078 if (parser.parseCommaSeparatedList([&]() -> ParseResult {
1079 FlatSymbolRefAttr attr;
1080 if (parser.parseAttribute(attr))
1081 return failure();
1082 values.push_back(attr);
1083 return success();
1084 })) {
1085 return failure();
1086 }
1087
1088 StringRef valuesAttrName = getValuesAttrName(result.name);
1089 ArrayAttr valuesAttr = parser.getBuilder().getArrayAttr(values);
1090 result.addAttribute(valuesAttrName, valuesAttr);
1091 return success();
1092}
1093
1094void spirv::ExecutionModeIdOp::print(OpAsmPrinter &printer) {
1095 printer << " ";
1096 printer.printSymbolName(getFn());
1097 printer << " \"" << stringifyExecutionMode(getExecutionMode()) << "\" ";
1098
1099 llvm::interleaveComma(
1100 getValues().getAsValueRange<FlatSymbolRefAttr>(), printer,
1101 [&](StringRef value) { printer.printSymbolName(value); });
1102}
1103
1104LogicalResult spirv::ExecutionModeIdOp::verify() {
1105 ExecutionModeOperandSchema schema =
1106 getExecutionModeOperandSchema(getExecutionMode());
1107
1108 if (!schema.isIdOperand)
1109 return emitOpError("expected ExecutionMode that takes extra operands that "
1110 "are <id> operands, got: ")
1111 << stringifyExecutionMode(getExecutionMode());
1112
1113 if (getValues().size() != schema.numOperands)
1114 return emitOpError("expected ")
1115 << schema.numOperands << " value operand(s), got "
1116 << getValues().size();
1117
1118 if (!llvm::all_of(getValues(), llvm::IsaPred<FlatSymbolRefAttr>))
1119 return emitOpError("expected value operands to be symbol reference");
1120
1121 return success();
1122}
1123
1124LogicalResult
1125spirv::ExecutionModeIdOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1126 for (auto valueSymbol : getValues().getAsRange<FlatSymbolRefAttr>()) {
1127 if (!symbolTable.lookupNearestSymbolFrom(*this, valueSymbol))
1128 return emitOpError("cannot find symbol referenced by value operand: ")
1129 << valueSymbol.getValue();
1130 }
1131 return success();
1132}
1133
1134//===----------------------------------------------------------------------===//
1135// spirv.func
1136//===----------------------------------------------------------------------===//
1137
1138ParseResult spirv::FuncOp::parse(OpAsmParser &parser, OperationState &result) {
1140 SmallVector<DictionaryAttr> resultAttrs;
1141 SmallVector<Type> resultTypes;
1142 auto &builder = parser.getBuilder();
1143
1145
1146 // Parse the name as a symbol.
1147 StringAttr nameAttr;
1148 if (parser.parseSymbolName(nameAttr, getSymNameAttrName(result.name),
1149 result.attributes))
1150 return failure();
1151
1152 // Parse the function signature.
1153 bool isVariadic = false;
1155 parser, /*allowVariadic=*/false, entryArgs, isVariadic, resultTypes,
1156 resultAttrs))
1157 return failure();
1158
1159 SmallVector<Type> argTypes;
1160 for (auto &arg : entryArgs)
1161 argTypes.push_back(arg.type);
1162 auto fnType = builder.getFunctionType(argTypes, resultTypes);
1163 result.addAttribute(getFunctionTypeAttrName(result.name),
1164 TypeAttr::get(fnType));
1165
1166 // Parse the optional function control keyword.
1167 spirv::FunctionControl fnControl;
1169 return failure();
1170
1171 // If additional attributes are present, parse them.
1172 if (parser.parseOptionalAttrDictWithKeyword(result.attributes))
1173 return failure();
1174
1175 // Add the attributes to the function arguments.
1176 assert(resultAttrs.size() == resultTypes.size());
1178 builder, result, entryArgs, resultAttrs, getArgAttrsAttrName(result.name),
1179 getResAttrsAttrName(result.name));
1180
1181 // Parse the optional function body.
1182 auto *body = result.addRegion();
1183 OptionalParseResult parseResult =
1184 parser.parseOptionalRegion(*body, entryArgs);
1185 return failure(parseResult.has_value() && failed(*parseResult));
1186}
1187
1188void spirv::FuncOp::print(OpAsmPrinter &printer) {
1189 // Print function name, signature, and control.
1190 printer << ' ';
1191 if (StringAttr visibility = getSymVisibilityAttr())
1192 printer << visibility.getValue() << ' ';
1193 printer.printSymbolName(getSymName());
1194 auto fnType = getFunctionType();
1196 printer, *this, fnType.getInputs(),
1197 /*isVariadic=*/false, fnType.getResults());
1198 printer << " \"" << spirv::stringifyFunctionControl(getFunctionControl())
1199 << "\"";
1201 printer, *this,
1202 {spirv::attributeName<spirv::FunctionControl>(),
1203 getFunctionTypeAttrName(), getArgAttrsAttrName(), getResAttrsAttrName(),
1204 getFunctionControlAttrName(), getSymVisibilityAttrName()});
1205
1206 // Print the body if this is not an external function.
1207 Region &body = this->getBody();
1208 if (!body.empty()) {
1209 printer << ' ';
1210 printer.printRegion(body, /*printEntryBlockArgs=*/false,
1211 /*printBlockTerminators=*/true);
1212 }
1213}
1214
1215LogicalResult spirv::FuncOp::verifyType() {
1216 FunctionType fnType = getFunctionType();
1217 if (fnType.getNumResults() > 1)
1218 return emitOpError("cannot have more than one result");
1219
1220 auto hasDecorationAttr = [&](spirv::Decoration decoration,
1221 unsigned argIndex) {
1222 auto func = cast<FunctionOpInterface>(getOperation());
1223 for (auto argAttr : cast<FunctionOpInterface>(func).getArgAttrs(argIndex)) {
1224 if (argAttr.getName() != spirv::DecorationAttr::name)
1225 continue;
1226 if (auto decAttr = dyn_cast<spirv::DecorationAttr>(argAttr.getValue()))
1227 return decAttr.getValue() == decoration;
1228 }
1229 return false;
1230 };
1231
1232 for (unsigned i = 0, e = this->getNumArguments(); i != e; ++i) {
1233 Type param = fnType.getInputs()[i];
1234 auto inputPtrType = dyn_cast<spirv::PointerType>(param);
1235 if (!inputPtrType)
1236 continue;
1237
1238 auto pointeePtrType =
1239 dyn_cast<spirv::PointerType>(inputPtrType.getPointeeType());
1240 if (pointeePtrType) {
1241 // SPIR-V spec, from SPV_KHR_physical_storage_buffer:
1242 // > If an OpFunctionParameter is a pointer (or contains a pointer)
1243 // > and the type it points to is a pointer in the PhysicalStorageBuffer
1244 // > storage class, the function parameter must be decorated with exactly
1245 // > one of AliasedPointer or RestrictPointer.
1246 if (pointeePtrType.getStorageClass() !=
1247 spirv::StorageClass::PhysicalStorageBuffer)
1248 continue;
1249
1250 bool hasAliasedPtr =
1251 hasDecorationAttr(spirv::Decoration::AliasedPointer, i);
1252 bool hasRestrictPtr =
1253 hasDecorationAttr(spirv::Decoration::RestrictPointer, i);
1254 if (!hasAliasedPtr && !hasRestrictPtr)
1255 return emitOpError()
1256 << "with a pointer points to a physical buffer pointer must "
1257 "be decorated either 'AliasedPointer' or 'RestrictPointer'";
1258 continue;
1259 }
1260 // SPIR-V spec, from SPV_KHR_physical_storage_buffer:
1261 // > If an OpFunctionParameter is a pointer (or contains a pointer) in
1262 // > the PhysicalStorageBuffer storage class, the function parameter must
1263 // > be decorated with exactly one of Aliased or Restrict.
1264 if (auto pointeeArrayType =
1265 dyn_cast<spirv::ArrayType>(inputPtrType.getPointeeType())) {
1266 pointeePtrType =
1267 dyn_cast<spirv::PointerType>(pointeeArrayType.getElementType());
1268 } else {
1269 pointeePtrType = inputPtrType;
1270 }
1271
1272 if (!pointeePtrType || pointeePtrType.getStorageClass() !=
1273 spirv::StorageClass::PhysicalStorageBuffer)
1274 continue;
1275
1276 bool hasAliased = hasDecorationAttr(spirv::Decoration::Aliased, i);
1277 bool hasRestrict = hasDecorationAttr(spirv::Decoration::Restrict, i);
1278 if (!hasAliased && !hasRestrict)
1279 return emitOpError() << "with physical buffer pointer must be decorated "
1280 "either 'Aliased' or 'Restrict'";
1281 }
1282
1283 return success();
1284}
1285
1286LogicalResult spirv::FuncOp::verifyBody() {
1287 FunctionType fnType = getFunctionType();
1288 if (!isExternal()) {
1289 Block &entryBlock = front();
1290
1291 unsigned numArguments = this->getNumArguments();
1292 if (entryBlock.getNumArguments() != numArguments)
1293 return emitOpError("entry block must have ")
1294 << numArguments << " arguments to match function signature";
1295
1296 for (auto [index, fnArgType, blockArgType] :
1297 llvm::enumerate(getArgumentTypes(), entryBlock.getArgumentTypes())) {
1298 if (blockArgType != fnArgType) {
1299 return emitOpError("type of entry block argument #")
1300 << index << '(' << blockArgType
1301 << ") must match the type of the corresponding argument in "
1302 << "function signature(" << fnArgType << ')';
1303 }
1304 }
1305 }
1306
1307 auto walkResult = walk([fnType](Operation *op) -> WalkResult {
1308 if (auto retOp = dyn_cast<spirv::ReturnOp>(op)) {
1309 if (fnType.getNumResults() != 0)
1310 return retOp.emitOpError("cannot be used in functions returning value");
1311 } else if (auto retOp = dyn_cast<spirv::ReturnValueOp>(op)) {
1312 if (fnType.getNumResults() != 1)
1313 return retOp.emitOpError(
1314 "returns 1 value but enclosing function requires ")
1315 << fnType.getNumResults() << " results";
1316
1317 auto retOperandType = retOp.getValue().getType();
1318 auto fnResultType = fnType.getResult(0);
1319 if (retOperandType != fnResultType)
1320 return retOp.emitOpError(" return value's type (")
1321 << retOperandType << ") mismatch with function's result type ("
1322 << fnResultType << ")";
1323 }
1324 return WalkResult::advance();
1325 });
1326
1327 // TODO: verify other bits like linkage type.
1328
1329 return failure(walkResult.wasInterrupted());
1330}
1331
1332void spirv::FuncOp::build(OpBuilder &builder, OperationState &state,
1333 StringRef name, FunctionType type,
1334 spirv::FunctionControl control,
1336 state.addAttribute(getSymNameAttrName(state.name),
1337 builder.getStringAttr(name));
1338 state.addAttribute(getFunctionTypeAttrName(state.name), TypeAttr::get(type));
1339 state.addAttribute(spirv::attributeName<spirv::FunctionControl>(),
1340 builder.getAttr<spirv::FunctionControlAttr>(control));
1341 state.attributes.append(attrs.begin(), attrs.end());
1342 state.addRegion();
1343}
1344
1345//===----------------------------------------------------------------------===//
1346// spirv.GLFClampOp
1347//===----------------------------------------------------------------------===//
1348
1349ParseResult spirv::GLFClampOp::parse(OpAsmParser &parser,
1352}
1353void spirv::GLFClampOp::print(OpAsmPrinter &p) { printOneResultOp(*this, p); }
1354
1355//===----------------------------------------------------------------------===//
1356// spirv.GLUClampOp
1357//===----------------------------------------------------------------------===//
1358
1359ParseResult spirv::GLUClampOp::parse(OpAsmParser &parser,
1362}
1363void spirv::GLUClampOp::print(OpAsmPrinter &p) { printOneResultOp(*this, p); }
1364
1365//===----------------------------------------------------------------------===//
1366// spirv.GLSClampOp
1367//===----------------------------------------------------------------------===//
1368
1369ParseResult spirv::GLSClampOp::parse(OpAsmParser &parser,
1372}
1373void spirv::GLSClampOp::print(OpAsmPrinter &p) { printOneResultOp(*this, p); }
1374
1375//===----------------------------------------------------------------------===//
1376// spirv.GLNClampOp
1377//===----------------------------------------------------------------------===//
1378
1379ParseResult spirv::GLNClampOp::parse(OpAsmParser &parser,
1382}
1383void spirv::GLNClampOp::print(OpAsmPrinter &p) { printOneResultOp(*this, p); }
1384
1385//===----------------------------------------------------------------------===//
1386// spirv.GLSmoothStepOp
1387//===----------------------------------------------------------------------===//
1388
1389ParseResult spirv::GLSmoothStepOp::parse(OpAsmParser &parser,
1392}
1393void spirv::GLSmoothStepOp::print(OpAsmPrinter &p) {
1394 printOneResultOp(*this, p);
1395}
1396
1397//===----------------------------------------------------------------------===//
1398// spirv.GLFmaOp
1399//===----------------------------------------------------------------------===//
1400
1401ParseResult spirv::GLFmaOp::parse(OpAsmParser &parser, OperationState &result) {
1403}
1404void spirv::GLFmaOp::print(OpAsmPrinter &p) { printOneResultOp(*this, p); }
1405
1406//===----------------------------------------------------------------------===//
1407// spirv.GlobalVariable
1408//===----------------------------------------------------------------------===//
1409
1410void spirv::GlobalVariableOp::build(OpBuilder &builder, OperationState &state,
1411 Type type, StringRef name,
1412 unsigned descriptorSet, unsigned binding) {
1413 build(builder, state, TypeAttr::get(type), builder.getStringAttr(name));
1414 state.addAttribute(
1415 spirv::SPIRVDialect::getAttributeName(spirv::Decoration::DescriptorSet),
1416 builder.getI32IntegerAttr(descriptorSet));
1417 state.addAttribute(
1418 spirv::SPIRVDialect::getAttributeName(spirv::Decoration::Binding),
1419 builder.getI32IntegerAttr(binding));
1420}
1421
1422void spirv::GlobalVariableOp::build(OpBuilder &builder, OperationState &state,
1423 Type type, StringRef name,
1424 spirv::BuiltIn builtin) {
1425 build(builder, state, TypeAttr::get(type), builder.getStringAttr(name));
1426 state.addAttribute(
1427 spirv::SPIRVDialect::getAttributeName(spirv::Decoration::BuiltIn),
1428 builder.getStringAttr(spirv::stringifyBuiltIn(builtin)));
1429}
1430
1431ParseResult spirv::GlobalVariableOp::parse(OpAsmParser &parser,
1434
1435 // Parse variable name.
1436 StringAttr nameAttr;
1437 StringRef initializerAttrName =
1438 spirv::GlobalVariableOp::getInitializerAttrName(result.name);
1439 if (parser.parseSymbolName(nameAttr, getSymNameAttrName(result.name),
1440 result.attributes)) {
1441 return failure();
1442 }
1443
1444 // Parse optional initializer
1445 if (succeeded(parser.parseOptionalKeyword(initializerAttrName))) {
1446 FlatSymbolRefAttr initSymbol;
1447 if (parser.parseLParen() ||
1448 parser.parseAttribute(initSymbol, Type(), initializerAttrName,
1449 result.attributes) ||
1450 parser.parseRParen())
1451 return failure();
1452 }
1453
1454 if (parseVariableDecorations(parser, result)) {
1455 return failure();
1456 }
1457
1458 Type type;
1459 StringRef typeAttrName =
1460 spirv::GlobalVariableOp::getTypeAttrName(result.name);
1461 auto loc = parser.getCurrentLocation();
1462 if (parser.parseColonType(type)) {
1463 return failure();
1464 }
1465 if (!isa<spirv::PointerType>(type)) {
1466 return parser.emitError(loc, "expected spirv.ptr type");
1467 }
1468 result.addAttribute(typeAttrName, TypeAttr::get(type));
1469
1470 return success();
1471}
1472
1473void spirv::GlobalVariableOp::print(OpAsmPrinter &printer) {
1474 SmallVector<StringRef, 4> elidedAttrs{
1475 spirv::attributeName<spirv::StorageClass>()};
1476
1477 // Print variable name.
1478 printer << ' ';
1479 if (StringAttr visibility = getSymVisibilityAttr())
1480 printer << visibility.getValue() << ' ';
1481 printer.printSymbolName(getSymName());
1482 elidedAttrs.push_back(getSymNameAttrName());
1483 elidedAttrs.push_back(getSymVisibilityAttrName());
1484
1485 StringRef initializerAttrName = this->getInitializerAttrName();
1486 // Print optional initializer
1487 if (auto initializer = this->getInitializer()) {
1488 printer << " " << initializerAttrName << '(';
1489 printer.printSymbolName(*initializer);
1490 printer << ')';
1491 elidedAttrs.push_back(initializerAttrName);
1492 }
1493
1494 StringRef typeAttrName = this->getTypeAttrName();
1495 elidedAttrs.push_back(typeAttrName);
1496 spirv::printVariableDecorations(*this, printer, elidedAttrs);
1497 printer << " : " << getType();
1498}
1499
1500LogicalResult spirv::GlobalVariableOp::verify() {
1501 if (!isa<spirv::PointerType>(getType()))
1502 return emitOpError("result must be of a !spv.ptr type");
1503
1504 // SPIR-V spec: "Storage Class is the Storage Class of the memory holding the
1505 // object. It cannot be Generic. It must be the same as the Storage Class
1506 // operand of the Result Type."
1507 // Also, Function storage class is reserved by spirv.Variable.
1508 auto storageClass = this->storageClass();
1509 if (storageClass == spirv::StorageClass::Generic ||
1510 storageClass == spirv::StorageClass::Function) {
1511 return emitOpError("storage class cannot be '")
1512 << stringifyStorageClass(storageClass) << "'";
1513 }
1514
1515 // SPIR-V spec: "A module-scope OpVariable with an Initializer operand must
1516 // not be decorated with the Import Linkage Type."
1517 if (std::optional<spirv::LinkageAttributesAttr> linkage =
1518 getLinkageAttributes()) {
1519 if (linkage->getLinkageType().getValue() == spirv::LinkageType::Import &&
1520 getInitializer()) {
1521 return emitOpError(
1522 "with Import linkage type must not have an initializer");
1523 }
1524 }
1525
1526 Type pointeeType = cast<spirv::PointerType>(getType()).getPointeeType();
1527 return verifyPhysicalStorageBufferDecorations(getOperation(), pointeeType);
1528}
1529
1530LogicalResult
1531spirv::GlobalVariableOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1532 if (FlatSymbolRefAttr init = getInitializerAttr()) {
1533 Operation *initOp = symbolTable.lookupNearestSymbolFrom(*this, init);
1534 // TODO: Currently only variable initialization with specialization
1535 // constants is supported. There could be normal constants in the module
1536 // scope as well.
1537 //
1538 // In the current setup we also cannot initialize one global variable with
1539 // another. The problem is that if we try to initialize pointer of type X
1540 // with another pointer type, the validator fails because it expects the
1541 // variable to be initialized to be type X, not pointer to X. Now
1542 // `spirv.GlobalVariable` only allows pointer type, so in the current design
1543 // we cannot initialize one `spirv.GlobalVariable` with another.
1544 if (!initOp ||
1545 !isa<spirv::SpecConstantOp, spirv::SpecConstantCompositeOp>(initOp)) {
1546 return emitOpError("initializer must be result of a "
1547 "spirv.SpecConstant or "
1548 "spirv.SpecConstantCompositeOp op");
1549 }
1550 }
1551
1552 return success();
1553}
1554
1555//===----------------------------------------------------------------------===//
1556// spirv.INTEL.SubgroupBlockRead
1557//===----------------------------------------------------------------------===//
1558
1559LogicalResult spirv::INTELSubgroupBlockReadOp::verify() {
1560 if (failed(verifyBlockReadWritePtrAndValTypes(*this, getPtr(), getValue())))
1561 return failure();
1562
1563 return success();
1564}
1565
1566//===----------------------------------------------------------------------===//
1567// spirv.INTEL.SubgroupBlockWrite
1568//===----------------------------------------------------------------------===//
1569
1570ParseResult spirv::INTELSubgroupBlockWriteOp::parse(OpAsmParser &parser,
1572 // Parse the storage class specification
1573 spirv::StorageClass storageClass;
1575 auto loc = parser.getCurrentLocation();
1576 Type elementType;
1577 if (parseEnumStrAttr(storageClass, parser) ||
1578 parser.parseOperandList(operandInfo, 2) || parser.parseColon() ||
1579 parser.parseType(elementType)) {
1580 return failure();
1581 }
1582
1583 auto ptrType = spirv::PointerType::get(elementType, storageClass);
1584 if (auto valVecTy = dyn_cast<VectorType>(elementType))
1585 ptrType = spirv::PointerType::get(valVecTy.getElementType(), storageClass);
1586
1587 if (parser.resolveOperands(operandInfo, {ptrType, elementType}, loc,
1588 result.operands)) {
1589 return failure();
1590 }
1591 return success();
1592}
1593
1594void spirv::INTELSubgroupBlockWriteOp::print(OpAsmPrinter &printer) {
1595 StringRef sc = stringifyStorageClass(
1596 cast<spirv::PointerType>(getPtr().getType()).getStorageClass());
1597 printer << " \"" << sc << "\" " << getPtr() << ", " << getValue() << " : "
1598 << getValue().getType();
1599}
1600
1601LogicalResult spirv::INTELSubgroupBlockWriteOp::verify() {
1602 if (failed(verifyBlockReadWritePtrAndValTypes(*this, getPtr(), getValue())))
1603 return failure();
1604
1605 return success();
1606}
1607
1608//===----------------------------------------------------------------------===//
1609// spirv.IAddCarryOp
1610//===----------------------------------------------------------------------===//
1611
1612LogicalResult spirv::IAddCarryOp::verify() {
1613 return ::verifyArithmeticExtendedBinaryOp(*this);
1614}
1615
1616ParseResult spirv::IAddCarryOp::parse(OpAsmParser &parser,
1618 return ::parseArithmeticExtendedBinaryOp(parser, result);
1619}
1620
1621void spirv::IAddCarryOp::print(OpAsmPrinter &printer) {
1622 ::printArithmeticExtendedBinaryOp(*this, printer);
1623}
1624
1625//===----------------------------------------------------------------------===//
1626// spirv.ISubBorrowOp
1627//===----------------------------------------------------------------------===//
1628
1629LogicalResult spirv::ISubBorrowOp::verify() {
1630 return ::verifyArithmeticExtendedBinaryOp(*this);
1631}
1632
1633ParseResult spirv::ISubBorrowOp::parse(OpAsmParser &parser,
1635 return ::parseArithmeticExtendedBinaryOp(parser, result);
1636}
1637
1638void spirv::ISubBorrowOp::print(OpAsmPrinter &printer) {
1639 ::printArithmeticExtendedBinaryOp(*this, printer);
1640}
1641
1642//===----------------------------------------------------------------------===//
1643// spirv.SMulExtended
1644//===----------------------------------------------------------------------===//
1645
1646LogicalResult spirv::SMulExtendedOp::verify() {
1647 return ::verifyArithmeticExtendedBinaryOp(*this);
1648}
1649
1650ParseResult spirv::SMulExtendedOp::parse(OpAsmParser &parser,
1652 return ::parseArithmeticExtendedBinaryOp(parser, result);
1653}
1654
1655void spirv::SMulExtendedOp::print(OpAsmPrinter &printer) {
1656 ::printArithmeticExtendedBinaryOp(*this, printer);
1657}
1658
1659//===----------------------------------------------------------------------===//
1660// spirv.UMulExtended
1661//===----------------------------------------------------------------------===//
1662
1663LogicalResult spirv::UMulExtendedOp::verify() {
1664 return ::verifyArithmeticExtendedBinaryOp(*this);
1665}
1666
1667ParseResult spirv::UMulExtendedOp::parse(OpAsmParser &parser,
1669 return ::parseArithmeticExtendedBinaryOp(parser, result);
1670}
1671
1672void spirv::UMulExtendedOp::print(OpAsmPrinter &printer) {
1673 ::printArithmeticExtendedBinaryOp(*this, printer);
1674}
1675
1676//===----------------------------------------------------------------------===//
1677// spirv.MemoryBarrierOp
1678//===----------------------------------------------------------------------===//
1679
1680LogicalResult spirv::MemoryBarrierOp::verify() {
1681 return verifyMemorySemantics(getOperation(), getMemorySemantics());
1682}
1683
1684//===----------------------------------------------------------------------===//
1685// spirv.MemoryNamedBarrierOp
1686//===----------------------------------------------------------------------===//
1687
1688LogicalResult spirv::MemoryNamedBarrierOp::verify() {
1689 return verifyMemorySemantics(getOperation(), getMemorySemantics());
1690}
1691
1692//===----------------------------------------------------------------------===//
1693// spirv.module
1694//===----------------------------------------------------------------------===//
1695
1696void spirv::ModuleOp::build(OpBuilder &builder, OperationState &state,
1697 std::optional<StringRef> name) {
1698 OpBuilder::InsertionGuard guard(builder);
1699 builder.createBlock(state.addRegion());
1700 if (name) {
1701 state.attributes.append(getSymNameAttrName(state.name),
1702 builder.getStringAttr(*name));
1703 }
1704}
1705
1706void spirv::ModuleOp::build(OpBuilder &builder, OperationState &state,
1707 spirv::AddressingModel addressingModel,
1708 spirv::MemoryModel memoryModel,
1709 std::optional<VerCapExtAttr> vceTriple,
1710 std::optional<StringRef> name) {
1711 state.addAttribute(
1712 "addressing_model",
1713 builder.getAttr<spirv::AddressingModelAttr>(addressingModel));
1714 state.addAttribute("memory_model",
1715 builder.getAttr<spirv::MemoryModelAttr>(memoryModel));
1716 OpBuilder::InsertionGuard guard(builder);
1717 builder.createBlock(state.addRegion());
1718 if (vceTriple)
1719 state.addAttribute(getVCETripleAttrName(), *vceTriple);
1720 if (name)
1721 state.addAttribute(getSymNameAttrName(state.name),
1722 builder.getStringAttr(*name));
1723}
1724
1725ParseResult spirv::ModuleOp::parse(OpAsmParser &parser,
1727 Region *body = result.addRegion();
1728
1730
1731 // If the name is present, parse it.
1732 StringAttr nameAttr;
1734 nameAttr, getSymNameAttrName(result.name), result.attributes);
1735
1736 // Parse attributes
1737 spirv::AddressingModel addrModel;
1738 spirv::MemoryModel memoryModel;
1740 result) ||
1742 result))
1743 return failure();
1744
1745 if (succeeded(parser.parseOptionalKeyword("requires"))) {
1746 spirv::VerCapExtAttr vceTriple;
1747 if (parser.parseAttribute(vceTriple,
1748 spirv::ModuleOp::getVCETripleAttrName(),
1749 result.attributes))
1750 return failure();
1751 }
1752
1753 if (parser.parseOptionalAttrDictWithKeyword(result.attributes) ||
1754 parser.parseRegion(*body, /*arguments=*/{}))
1755 return failure();
1756
1757 // Make sure we have at least one block.
1758 if (body->empty())
1759 body->push_back(new Block());
1760
1761 return success();
1762}
1763
1764void spirv::ModuleOp::print(OpAsmPrinter &printer) {
1765 if (StringAttr visibility = getSymVisibilityAttr())
1766 printer << ' ' << visibility.getValue();
1767 if (std::optional<StringRef> name = getName()) {
1768 printer << ' ';
1769 printer.printSymbolName(*name);
1770 }
1771
1772 SmallVector<StringRef, 2> elidedAttrs;
1773
1774 printer << " " << spirv::stringifyAddressingModel(getAddressingModel()) << " "
1775 << spirv::stringifyMemoryModel(getMemoryModel());
1776 auto addressingModelAttrName = spirv::attributeName<spirv::AddressingModel>();
1777 auto memoryModelAttrName = spirv::attributeName<spirv::MemoryModel>();
1778 elidedAttrs.assign({addressingModelAttrName, memoryModelAttrName,
1779 getSymNameAttrName(), getSymVisibilityAttrName()});
1780
1781 if (std::optional<spirv::VerCapExtAttr> triple = getVceTriple()) {
1782 printer << " requires " << *triple;
1783 elidedAttrs.push_back(spirv::ModuleOp::getVCETripleAttrName());
1784 }
1785
1787 (*this)->getDiscardableAttrDictionary().getValue(), elidedAttrs);
1788 printer << ' ';
1789 printer.printRegion(getRegion());
1790}
1791
1792LogicalResult spirv::ModuleOp::verifyRegions() {
1793 Dialect *dialect = (*this)->getDialect();
1795 entryPoints;
1796 mlir::SymbolTable table(*this);
1797
1798 for (auto &op : *getBody()) {
1799 if (op.getDialect() != dialect)
1800 return op.emitError("'spirv.module' can only contain spirv.* ops");
1801
1802 // For EntryPoint op, check that the function and execution model is not
1803 // duplicated in EntryPointOps. Also verify that the interface specified
1804 // comes from globalVariables here to make this check cheaper.
1805 if (auto entryPointOp = dyn_cast<spirv::EntryPointOp>(op)) {
1806 auto funcOp = table.lookup<spirv::FuncOp>(entryPointOp.getFn());
1807 if (!funcOp) {
1808 return entryPointOp.emitError("function '")
1809 << entryPointOp.getFn() << "' not found in 'spirv.module'";
1810 }
1811 if (auto interface = entryPointOp.getInterface()) {
1812 for (Attribute varRef : interface) {
1813 auto varSymRef = dyn_cast<FlatSymbolRefAttr>(varRef);
1814 if (!varSymRef) {
1815 return entryPointOp.emitError(
1816 "expected symbol reference for interface "
1817 "specification instead of '")
1818 << varRef;
1819 }
1820 auto variableOp =
1821 table.lookup<spirv::GlobalVariableOp>(varSymRef.getValue());
1822 if (!variableOp) {
1823 return entryPointOp.emitError("expected spirv.GlobalVariable "
1824 "symbol reference instead of'")
1825 << varSymRef << "'";
1826 }
1827 }
1828 }
1829
1830 auto key = std::pair<spirv::FuncOp, spirv::ExecutionModel>(
1831 funcOp, entryPointOp.getExecutionModel());
1832 if (!entryPoints.try_emplace(key, entryPointOp).second)
1833 return entryPointOp.emitError("duplicate of a previous EntryPointOp");
1834 } else if (auto funcOp = dyn_cast<spirv::FuncOp>(op)) {
1835 // If the function is external and does not have 'Import'
1836 // linkage_attributes(LinkageAttributes), throw an error. 'Import'
1837 // LinkageAttributes is used to import external functions.
1838 auto linkageAttr = funcOp.getLinkageAttributes();
1839 auto hasImportLinkage =
1840 linkageAttr && (linkageAttr.value().getLinkageType().getValue() ==
1841 spirv::LinkageType::Import);
1842 if (funcOp.isExternal() && !hasImportLinkage)
1843 return op.emitError(
1844 "'spirv.module' cannot contain external functions "
1845 "without 'Import' linkage_attributes (LinkageAttributes)");
1846
1847 // TODO: move this check to spirv.func.
1848 for (auto &block : funcOp)
1849 for (auto &op : block) {
1850 if (op.getDialect() != dialect)
1851 return op.emitError(
1852 "functions in 'spirv.module' can only contain spirv.* ops");
1853 }
1854 }
1855 }
1856
1857 return success();
1858}
1859
1860//===----------------------------------------------------------------------===//
1861// spirv.mlir.referenceof
1862//===----------------------------------------------------------------------===//
1863
1864LogicalResult
1865spirv::ReferenceOfOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1866 Operation *specConstSym =
1867 symbolTable.lookupNearestSymbolFrom(*this, getSpecConstAttr());
1868 Type constType;
1869
1870 auto specConstOp = dyn_cast_or_null<spirv::SpecConstantOp>(specConstSym);
1871 if (specConstOp)
1872 constType = specConstOp.getDefaultValue().getType();
1873
1874 auto specConstCompositeOp =
1875 dyn_cast_or_null<spirv::SpecConstantCompositeOp>(specConstSym);
1876 if (specConstCompositeOp)
1877 constType = specConstCompositeOp.getType();
1878
1879 if (!specConstOp && !specConstCompositeOp)
1880 return emitOpError(
1881 "expected spirv.SpecConstant or spirv.SpecConstantComposite symbol");
1882
1883 if (getReference().getType() != constType)
1884 return emitOpError("result type mismatch with the referenced "
1885 "specialization constant's type");
1886
1887 return success();
1888}
1889
1890//===----------------------------------------------------------------------===//
1891// spirv.SpecConstant
1892//===----------------------------------------------------------------------===//
1893
1894ParseResult spirv::SpecConstantOp::parse(OpAsmParser &parser,
1897
1898 StringAttr nameAttr;
1899 Attribute valueAttr;
1900 StringRef defaultValueAttrName =
1901 spirv::SpecConstantOp::getDefaultValueAttrName(result.name);
1902
1903 if (parser.parseSymbolName(nameAttr, getSymNameAttrName(result.name),
1904 result.attributes))
1905 return failure();
1906
1907 // Parse optional spec_id.
1908 if (succeeded(parser.parseOptionalKeyword(kSpecIdAttrName))) {
1909 IntegerAttr specIdAttr;
1910 if (parser.parseLParen() ||
1911 parser.parseAttribute(specIdAttr, kSpecIdAttrName, result.attributes) ||
1912 parser.parseRParen())
1913 return failure();
1914 }
1915
1916 if (parser.parseEqual() ||
1917 parser.parseAttribute(valueAttr, defaultValueAttrName, result.attributes))
1918 return failure();
1919
1920 return success();
1921}
1922
1923void spirv::SpecConstantOp::print(OpAsmPrinter &printer) {
1924 printer << ' ';
1925 if (StringAttr visibility = getSymVisibilityAttr())
1926 printer << visibility.getValue() << ' ';
1927 printer.printSymbolName(getSymName());
1928 if (auto specID =
1929 (*this)->getDiscardableAttrOfType<IntegerAttr>(kSpecIdAttrName))
1930 printer << ' ' << kSpecIdAttrName << '(' << specID.getInt() << ')';
1931 printer << " = " << getDefaultValue();
1932}
1933
1934LogicalResult spirv::SpecConstantOp::verify() {
1935 if (auto specID =
1936 (*this)->getDiscardableAttrOfType<IntegerAttr>(kSpecIdAttrName))
1937 if (specID.getValue().isNegative())
1938 return emitOpError("SpecId cannot be negative");
1939
1940 auto value = getDefaultValue();
1941 if (isa<IntegerAttr, FloatAttr>(value)) {
1942 // Make sure bitwidth is allowed.
1943 if (!isa<spirv::SPIRVType>(value.getType()))
1944 return emitOpError("default value bitwidth disallowed");
1945 return success();
1946 }
1947 return emitOpError(
1948 "default value can only be a bool, integer, or float scalar");
1949}
1950
1951//===----------------------------------------------------------------------===//
1952// spirv.VectorShuffle
1953//===----------------------------------------------------------------------===//
1954
1955LogicalResult spirv::VectorShuffleOp::verify() {
1956 VectorType resultType = cast<VectorType>(getType());
1957
1958 size_t numResultElements = resultType.getNumElements();
1959 if (numResultElements != getComponents().size())
1960 return emitOpError("result type element count (")
1961 << numResultElements
1962 << ") mismatch with the number of component selectors ("
1963 << getComponents().size() << ")";
1964
1965 size_t totalSrcElements =
1966 cast<VectorType>(getVector1().getType()).getNumElements() +
1967 cast<VectorType>(getVector2().getType()).getNumElements();
1968
1969 for (const auto &selector : getComponents().getAsValueRange<IntegerAttr>()) {
1970 uint32_t index = selector.getZExtValue();
1971 if (index >= totalSrcElements &&
1972 index != std::numeric_limits<uint32_t>().max())
1973 return emitOpError("component selector ")
1974 << index << " out of range: expected to be in [0, "
1975 << totalSrcElements << ") or 0xffffffff";
1976 }
1977 return success();
1978}
1979
1980//===----------------------------------------------------------------------===//
1981// spirv.SpecConstantComposite
1982//===----------------------------------------------------------------------===//
1983
1984ParseResult spirv::SpecConstantCompositeOp::parse(OpAsmParser &parser,
1986
1988
1989 StringAttr compositeName;
1990 if (parser.parseSymbolName(compositeName, getSymNameAttrName(result.name),
1991 result.attributes))
1992 return failure();
1993
1994 if (parser.parseLParen())
1995 return failure();
1996
1997 SmallVector<Attribute, 4> constituents;
1998
1999 do {
2000 // The name of the constituent attribute isn't important
2001 const char *attrName = "spec_const";
2002 FlatSymbolRefAttr specConstRef;
2003 NamedAttrList attrs;
2004
2005 if (parser.parseAttribute(specConstRef, Type(), attrName, attrs))
2006 return failure();
2007
2008 constituents.push_back(specConstRef);
2009 } while (!parser.parseOptionalComma());
2010
2011 if (parser.parseRParen())
2012 return failure();
2013
2014 StringAttr compositeSpecConstituentsName =
2015 spirv::SpecConstantCompositeOp::getConstituentsAttrName(result.name);
2016 result.addAttribute(compositeSpecConstituentsName,
2017 parser.getBuilder().getArrayAttr(constituents));
2018
2019 Type type;
2020 if (parser.parseColonType(type))
2021 return failure();
2022
2023 StringAttr typeAttrName =
2024 spirv::SpecConstantCompositeOp::getTypeAttrName(result.name);
2025 result.addAttribute(typeAttrName, TypeAttr::get(type));
2026
2027 return success();
2028}
2029
2030void spirv::SpecConstantCompositeOp::print(OpAsmPrinter &printer) {
2031 printer << ' ';
2032 if (StringAttr visibility = getSymVisibilityAttr())
2033 printer << visibility.getValue() << ' ';
2034 printer.printSymbolName(getSymName());
2035 printer << " (" << llvm::interleaved(this->getConstituents().getValue())
2036 << ") : " << getType();
2037}
2038
2039LogicalResult spirv::SpecConstantCompositeOp::verify() {
2040 auto cType = dyn_cast<spirv::CompositeType>(getType());
2041 auto constituents = this->getConstituents().getValue();
2042
2043 if (!cType)
2044 return emitError("result type must be a composite type, but provided ")
2045 << getType();
2046
2047 if (isa<spirv::CooperativeMatrixType>(cType))
2048 return emitError("unsupported composite type ") << cType;
2049 if (constituents.size() != cType.getNumElements())
2050 return emitError("has incorrect number of operands: expected ")
2051 << cType.getNumElements() << ", but provided "
2052 << constituents.size();
2053
2054 return success();
2055}
2056
2057LogicalResult spirv::SpecConstantCompositeOp::verifySymbolUses(
2058 SymbolTableCollection &symbolTable) {
2059 auto cType = cast<spirv::CompositeType>(getType());
2060 ArrayRef<Attribute> constituents = getConstituents().getValue();
2061
2062 for (auto index : llvm::seq<uint32_t>(0, constituents.size())) {
2063 auto constituent = cast<FlatSymbolRefAttr>(constituents[index]);
2064
2065 Operation *constituentOp =
2066 symbolTable.lookupNearestSymbolFrom(*this, constituent);
2067
2068 if (!constituentOp)
2069 return emitError("unknown constituent symbol ") << constituent.getAttr();
2070
2071 Type constituentType;
2072 if (auto specConstOp = dyn_cast<spirv::SpecConstantOp>(constituentOp)) {
2073 constituentType = specConstOp.getDefaultValue().getType();
2074 } else if (auto specConstCompositeOp =
2075 dyn_cast<spirv::SpecConstantCompositeOp>(constituentOp)) {
2076 constituentType = specConstCompositeOp.getType();
2077 } else {
2078 return emitError("unsupported constituent ")
2079 << constituent.getAttr()
2080 << ": must reference a spirv.SpecConstant or "
2081 "spirv.SpecConstantComposite";
2082 }
2083
2084 if (constituentType != cType.getElementType(index))
2085 return emitError("has incorrect types of operands: expected ")
2086 << cType.getElementType(index) << ", but provided "
2087 << constituentType;
2088 }
2089
2090 return success();
2091}
2092
2093//===----------------------------------------------------------------------===//
2094// spirv.EXTSpecConstantCompositeReplicateOp
2095//===----------------------------------------------------------------------===//
2096
2097ParseResult
2098spirv::EXTSpecConstantCompositeReplicateOp::parse(OpAsmParser &parser,
2101
2102 StringAttr compositeName;
2103 FlatSymbolRefAttr specConstRef;
2104 const char *attrName = "spec_const";
2105 NamedAttrList attrs;
2106 Type type;
2107
2108 if (parser.parseSymbolName(compositeName, getSymNameAttrName(result.name),
2109 result.attributes) ||
2110 parser.parseLParen() ||
2111 parser.parseAttribute(specConstRef, Type(), attrName, attrs) ||
2112 parser.parseRParen() || parser.parseColonType(type))
2113 return failure();
2114
2115 StringAttr compositeSpecConstituentName =
2116 spirv::EXTSpecConstantCompositeReplicateOp::getConstituentAttrName(
2117 result.name);
2118 result.addAttribute(compositeSpecConstituentName, specConstRef);
2119
2120 StringAttr typeAttrName =
2121 spirv::EXTSpecConstantCompositeReplicateOp::getTypeAttrName(result.name);
2122 result.addAttribute(typeAttrName, TypeAttr::get(type));
2123
2124 return success();
2125}
2126
2127void spirv::EXTSpecConstantCompositeReplicateOp::print(OpAsmPrinter &printer) {
2128 printer << ' ';
2129 if (StringAttr visibility = getSymVisibilityAttr())
2130 printer << visibility.getValue() << ' ';
2131 printer.printSymbolName(getSymName());
2132 printer << " (" << this->getConstituent() << ") : " << getType();
2133}
2134
2135LogicalResult spirv::EXTSpecConstantCompositeReplicateOp::verify() {
2136 if (!isa<spirv::CompositeType>(getType()))
2137 return emitError("result type must be a composite type, but provided ")
2138 << getType();
2139
2140 return success();
2141}
2142
2143LogicalResult spirv::EXTSpecConstantCompositeReplicateOp::verifySymbolUses(
2144 SymbolTableCollection &symbolTable) {
2145 auto compositeType = cast<spirv::CompositeType>(getType());
2146 Operation *constituentOp =
2147 symbolTable.lookupNearestSymbolFrom(*this, getConstituent());
2148 if (!constituentOp)
2149 return emitError(
2150 "splat spec constant reference defining constituent not found");
2151
2152 auto constituentSpecConstOp = dyn_cast<spirv::SpecConstantOp>(constituentOp);
2153 if (!constituentSpecConstOp)
2154 return emitError("constituent is not a spec constant");
2155
2156 Type constituentType = constituentSpecConstOp.getDefaultValue().getType();
2157 Type compositeElementType = compositeType.getElementType(0);
2158 if (constituentType != compositeElementType)
2159 return emitError("constituent has incorrect type: expected ")
2160 << compositeElementType << ", but provided " << constituentType;
2161
2162 return success();
2163}
2164
2165//===----------------------------------------------------------------------===//
2166// spirv.SpecConstantOperation
2167//===----------------------------------------------------------------------===//
2168
2169ParseResult spirv::SpecConstantOperationOp::parse(OpAsmParser &parser,
2171 Region *body = result.addRegion();
2172
2173 if (parser.parseKeyword("wraps"))
2174 return failure();
2175
2176 body->push_back(new Block);
2177 Block &block = body->back();
2178 Operation *wrappedOp = parser.parseGenericOperation(&block, block.begin());
2179
2180 if (!wrappedOp)
2181 return failure();
2182
2183 OpBuilder builder(parser.getContext());
2184 builder.setInsertionPointToEnd(&block);
2185 spirv::YieldOp::create(builder, wrappedOp->getLoc(), wrappedOp->getResult(0));
2186 result.location = wrappedOp->getLoc();
2187
2188 result.addTypes(wrappedOp->getResult(0).getType());
2189
2190 if (parser.parseOptionalAttrDict(result.attributes))
2191 return failure();
2192
2193 return success();
2194}
2195
2196void spirv::SpecConstantOperationOp::print(OpAsmPrinter &printer) {
2197 printer << " wraps ";
2198 printer.printGenericOp(&getBody().front().front());
2199}
2200
2201LogicalResult spirv::SpecConstantOperationOp::verifyRegions() {
2202 Block &block = getRegion().getBlocks().front();
2203
2204 if (block.getOperations().size() != 2)
2205 return emitOpError("expected exactly 2 nested ops");
2206
2207 Operation &enclosedOp = block.getOperations().front();
2208
2210 return emitOpError("invalid enclosed op");
2211
2212 for (auto operand : enclosedOp.getOperands())
2213 if (!isa_and_present<spirv::ConstantOp, spirv::ReferenceOfOp,
2214 spirv::SpecConstantOperationOp>(
2215 operand.getDefiningOp()))
2216 return emitOpError(
2217 "invalid operand, must be defined by a constant operation");
2218
2219 return success();
2220}
2221
2222//===----------------------------------------------------------------------===//
2223// spirv.GL.FrexpStruct
2224//===----------------------------------------------------------------------===//
2225
2226LogicalResult spirv::GLFrexpStructOp::verify() {
2227 spirv::StructType structTy =
2228 dyn_cast<spirv::StructType>(getResult().getType());
2229
2230 if (structTy.getNumElements() != 2)
2231 return emitError("result type must be a struct type with two memebers");
2232
2233 Type significandTy = structTy.getElementType(0);
2234 Type exponentTy = structTy.getElementType(1);
2235 VectorType exponentVecTy = dyn_cast<VectorType>(exponentTy);
2236 IntegerType exponentIntTy = dyn_cast<IntegerType>(exponentTy);
2237
2238 Type operandTy = getOperand().getType();
2239 VectorType operandVecTy = dyn_cast<VectorType>(operandTy);
2240 FloatType operandFTy = dyn_cast<FloatType>(operandTy);
2241
2242 if (significandTy != operandTy)
2243 return emitError("member zero of the resulting struct type must be the "
2244 "same type as the operand");
2245
2246 if (exponentVecTy) {
2247 IntegerType componentIntTy =
2248 dyn_cast<IntegerType>(exponentVecTy.getElementType());
2249 if (!componentIntTy || componentIntTy.getWidth() != 32)
2250 return emitError("member one of the resulting struct type must"
2251 "be a scalar or vector of 32 bit integer type");
2252 } else if (!exponentIntTy || exponentIntTy.getWidth() != 32) {
2253 return emitError("member one of the resulting struct type "
2254 "must be a scalar or vector of 32 bit integer type");
2255 }
2256
2257 // Check that the two member types have the same number of components
2258 if (operandVecTy && exponentVecTy &&
2259 (exponentVecTy.getNumElements() == operandVecTy.getNumElements()))
2260 return success();
2261
2262 if (operandFTy && exponentIntTy)
2263 return success();
2264
2265 return emitError("member one of the resulting struct type must have the same "
2266 "number of components as the operand type");
2267}
2268
2269//===----------------------------------------------------------------------===//
2270// spirv.GL.Ldexp
2271//===----------------------------------------------------------------------===//
2272
2273static LogicalResult verifyFloatIntegerBuiltin(Operation *op, Type floatType,
2274 Type integerType) {
2275 if (isa<FloatType>(floatType) != isa<IntegerType>(integerType))
2276 return op->emitOpError("operands must both be scalars or vectors");
2277
2278 auto getNumElements = [](Type type) -> unsigned {
2279 if (auto vectorType = dyn_cast<VectorType>(type))
2280 return vectorType.getNumElements();
2281 return 1;
2282 };
2283
2284 if (getNumElements(floatType) != getNumElements(integerType))
2285 return op->emitOpError("operands must have the same number of elements");
2286
2287 return success();
2288}
2289
2290LogicalResult spirv::GLLdexpOp::verify() {
2291 return verifyFloatIntegerBuiltin(getOperation(), getX().getType(),
2292 getExp().getType());
2293}
2294
2295//===----------------------------------------------------------------------===//
2296// spirv.CL.ldexp
2297//===----------------------------------------------------------------------===//
2298
2299LogicalResult spirv::CLLdexpOp::verify() {
2300 return verifyFloatIntegerBuiltin(getOperation(), getX().getType(),
2301 getExp().getType());
2302}
2303
2304//===----------------------------------------------------------------------===//
2305// spirv.CL.pown
2306//===----------------------------------------------------------------------===//
2307
2308LogicalResult spirv::CLPownOp::verify() {
2309 return verifyFloatIntegerBuiltin(getOperation(), getX().getType(),
2310 getY().getType());
2311}
2312
2313//===----------------------------------------------------------------------===//
2314// spirv.CL.rootn
2315//===----------------------------------------------------------------------===//
2316
2317LogicalResult spirv::CLRootnOp::verify() {
2318 return verifyFloatIntegerBuiltin(getOperation(), getX().getType(),
2319 getN().getType());
2320}
2321
2322//===----------------------------------------------------------------------===//
2323// spirv.CL.sincos
2324//===----------------------------------------------------------------------===//
2325
2326LogicalResult spirv::CLSincosOp::verify() {
2327 auto storageClass =
2328 cast<spirv::PointerType>(getCosval().getType()).getStorageClass();
2329 if (!llvm::is_contained(
2330 {spirv::StorageClass::CrossWorkgroup, spirv::StorageClass::Workgroup,
2331 spirv::StorageClass::Function, spirv::StorageClass::Generic},
2332 storageClass))
2333 return emitOpError("cosval must point to CrossWorkgroup, Workgroup, "
2334 "Function, or Generic storage");
2335 return success();
2336}
2337
2338//===----------------------------------------------------------------------===//
2339// spirv.ShiftLeftLogicalOp
2340//===----------------------------------------------------------------------===//
2341
2342LogicalResult spirv::ShiftLeftLogicalOp::verify() {
2343 return verifyShiftOp(*this);
2344}
2345
2346//===----------------------------------------------------------------------===//
2347// spirv.ShiftRightArithmeticOp
2348//===----------------------------------------------------------------------===//
2349
2350LogicalResult spirv::ShiftRightArithmeticOp::verify() {
2351 return verifyShiftOp(*this);
2352}
2353
2354//===----------------------------------------------------------------------===//
2355// spirv.ShiftRightLogicalOp
2356//===----------------------------------------------------------------------===//
2357
2358LogicalResult spirv::ShiftRightLogicalOp::verify() {
2359 return verifyShiftOp(*this);
2360}
2361
2362//===----------------------------------------------------------------------===//
2363// spirv.VectorTimesScalarOp
2364//===----------------------------------------------------------------------===//
2365
2366LogicalResult spirv::VectorTimesScalarOp::verify() {
2367 if (getVector().getType() != getType())
2368 return emitOpError("vector operand and result type mismatch");
2369 auto scalarType = cast<VectorType>(getType()).getElementType();
2370 if (getScalar().getType() != scalarType)
2371 return emitOpError("scalar operand and result element type match");
2372 return success();
2373}
static Value getPointer(Location loc, Value value, ConversionPatternRewriter &rewriter)
return success()
static int64_t getNumElements(Type t)
Compute the total number of elements in the given type, also taking into account nested types.
ArrayAttr()
static Value max(ImplicitLocOpBuilder &builder, Value value, Value bound)
static ParseResult parseArithmeticExtendedBinaryOp(OpAsmParser &parser, OperationState &result)
Definition SPIRVOps.cpp:310
static Type getValueType(Attribute attr)
Definition SPIRVOps.cpp:845
static LogicalResult verifyConstantType(spirv::ConstantOp op, Attribute value, Type opType)
Definition SPIRVOps.cpp:612
static ParseResult parseOneResultSameOperandTypeOp(OpAsmParser &parser, OperationState &result)
Definition SPIRVOps.cpp:164
static LogicalResult verifyArithmeticExtendedBinaryOp(ExtendedBinaryOp op)
Definition SPIRVOps.cpp:296
static LogicalResult verifyFloatIntegerBuiltin(Operation *op, Type floatType, Type integerType)
static LogicalResult verifyShiftOp(Operation *op)
Definition SPIRVOps.cpp:342
static LogicalResult verifyBlockReadWritePtrAndValTypes(BlockReadWriteOpTy op, Value ptr, Value val)
Definition SPIRVOps.cpp:213
static Type getElementType(Type type, ArrayRef< int32_t > indices, function_ref< InFlightDiagnostic(StringRef)> emitErrorFn)
Walks the given type hierarchy with the given indices, potentially down to component granularity,...
Definition SPIRVOps.cpp:229
static void printOneResultOp(Operation *op, OpAsmPrinter &p)
Definition SPIRVOps.cpp:193
static void printArithmeticExtendedBinaryOp(Operation *op, OpAsmPrinter &printer)
Definition SPIRVOps.cpp:334
ParseResult parseSymbolName(StringAttr &result)
Parse an -identifier and store it (without the '@' symbol) in a string attribute.
virtual ParseResult parseOptionalSymbolName(StringAttr &result)=0
Parse an optional -identifier and store it (without the '@' symbol) in a string attribute.
virtual Builder & getBuilder() const =0
Return a builder which provides useful access to MLIRContext, global objects like types and attribute...
virtual ParseResult parseCommaSeparatedList(Delimiter delimiter, function_ref< ParseResult()> parseElementFn, StringRef contextMessage=StringRef())=0
Parse a list of comma-separated items with an optional delimiter.
virtual ParseResult parseOptionalAttrDict(NamedAttrList &result)=0
Parse a named dictionary into 'result' if it is present.
virtual ParseResult parseOptionalKeyword(StringRef keyword)=0
Parse the given keyword if present.
MLIRContext * getContext() const
virtual ParseResult parseRParen()=0
Parse a ) token.
virtual InFlightDiagnostic emitError(SMLoc loc, const Twine &message={})=0
Emit a diagnostic at the specified location and return failure.
virtual ParseResult parseOptionalColon()=0
Parse a : token if present.
ParseResult addTypeToList(Type type, SmallVectorImpl< Type > &result)
Add the specified type to the end of the specified type list and return success.
virtual ParseResult parseEqual()=0
Parse a = token.
virtual ParseResult parseOptionalAttrDictWithKeyword(NamedAttrList &result)=0
Parse a named dictionary into 'result' if the attributes keyword is present.
virtual ParseResult parseColonType(Type &result)=0
Parse a colon followed by a type.
virtual SMLoc getCurrentLocation()=0
Get the location of the next token and store it into the argument.
virtual ParseResult parseOptionalComma()=0
Parse a , token if present.
virtual ParseResult parseColon()=0
Parse a : token.
ParseResult addTypesToList(ArrayRef< Type > types, SmallVectorImpl< Type > &result)
Add the specified types to the end of the specified type list and return success.
virtual ParseResult parseLParen()=0
Parse a ( token.
virtual ParseResult parseType(Type &result)=0
Parse a type.
virtual ParseResult parseOptionalLParen()=0
Parse a ( token if present.
ParseResult parseKeywordType(const char *keyword, Type &result)
Parse a keyword followed by a type.
ParseResult parseKeyword(StringRef keyword)
Parse a given keyword.
virtual ParseResult parseAttribute(Attribute &result, Type type={})=0
Parse an arbitrary attribute of a given type and return it in result.
virtual void printSymbolName(StringRef symbolRef)
Print the given string as a symbol reference, i.e.
Attributes are known-constant values of operations.
Definition Attributes.h:25
Block represents an ordered list of Operations.
Definition Block.h:34
ValueTypeRange< BlockArgListType > getArgumentTypes()
Return a range containing the types of the arguments for this block.
Definition Block.cpp:154
unsigned getNumArguments()
Definition Block.h:153
OpListType & getOperations()
Definition Block.h:162
Operation & front()
Definition Block.h:178
iterator begin()
Definition Block.h:168
IntegerAttr getI32IntegerAttr(int32_t value)
Definition Builders.cpp:208
IntegerAttr getIntegerAttr(Type type, int64_t value)
Definition Builders.cpp:237
ArrayAttr getI32ArrayAttr(ArrayRef< int32_t > values)
Definition Builders.cpp:285
FloatAttr getFloatAttr(Type type, double value)
Definition Builders.cpp:263
FunctionType getFunctionType(TypeRange inputs, TypeRange results)
Definition Builders.cpp:84
IntegerType getIntegerType(unsigned width)
Definition Builders.cpp:75
BoolAttr getBoolAttr(bool value)
Definition Builders.cpp:108
StringAttr getStringAttr(const Twine &bytes)
Definition Builders.cpp:271
ArrayAttr getArrayAttr(ArrayRef< Attribute > value)
Definition Builders.cpp:275
MLIRContext * getContext() const
Definition Builders.h:56
Attr getAttr(Args &&...args)
Get or construct an instance of the attribute Attr with provided arguments.
Definition Builders.h:101
static DenseElementsAttr get(ShapedType type, ArrayRef< Attribute > values)
Constructs a dense elements attribute from an array of element values.
static DenseFPElementsAttr get(const ShapedType &type, Arg &&arg)
Get an instance of a DenseFPElementsAttr with the given arguments.
Dialects are groups of MLIR operations, types and attributes, as well as behavior associated with the...
Definition Dialect.h:38
A symbol reference with a reference path containing a single element.
This class represents a diagnostic that is inflight and set to be reported.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition Location.h:76
NamedAttrList is array of NamedAttributes that tracks whether it is sorted and does some basic work t...
Attribute get(StringAttr name) const
Return the specified attribute if present, null otherwise.
void append(StringRef name, Attribute attr)
Add an attribute with the specified name.
The OpAsmParser has methods for interacting with the asm parser: parsing things from it,...
virtual ParseResult parseRegion(Region &region, ArrayRef< Argument > arguments={}, bool enableNameShadowing=false)=0
Parses a region.
virtual ParseResult resolveOperand(const UnresolvedOperand &operand, Type type, SmallVectorImpl< Value > &result)=0
Resolve an operand to an SSA value, emitting an error on failure.
virtual OptionalParseResult parseOptionalRegion(Region &region, ArrayRef< Argument > arguments={}, bool enableNameShadowing=false)=0
Parses a region if present.
virtual Operation * parseGenericOperation(Block *insertBlock, Block::iterator insertPt)=0
Parse an operation in its generic form.
ParseResult resolveOperands(Operands &&operands, Type type, SmallVectorImpl< Value > &result)
Resolve a list of operands to SSA values, emitting an error on failure, or appending the results to t...
virtual ParseResult parseOperand(UnresolvedOperand &result, bool allowResultNumber=true)=0
Parse a single SSA value operand name along with a result number if allowResultNumber is true.
virtual ParseResult parseOperandList(SmallVectorImpl< UnresolvedOperand > &result, Delimiter delimiter=Delimiter::None, bool allowResultNumber=true, int requiredOperandCount=-1)=0
Parse zero or more SSA comma-separated operand references with a specified surrounding delimiter,...
This is a pure-virtual base class that exposes the asmprinter hooks necessary to implement a custom p...
virtual void printOptionalAttrDictWithKeyword(ArrayRef< NamedAttribute > attrs, ArrayRef< StringRef > elidedAttrs={})=0
If the specified operation has attributes, print out an attribute dictionary prefixed with 'attribute...
void printOperands(OperandRange operands)
Print a comma separated range of operation operands out of line to avoid instantiating the range iter...
virtual void printOptionalAttrDict(ArrayRef< NamedAttribute > attrs, ArrayRef< StringRef > elidedAttrs={})=0
If the specified operation has attributes, print out an attribute dictionary with their values.
virtual void printGenericOp(Operation *op, bool printOpName=true)=0
Print the entire operation with the default generic assembly form.
virtual void printRegion(Region &blocks, bool printEntryBlockArgs=true, bool printBlockTerminators=true, bool printEmptyBlock=false)=0
Prints a region.
RAII guard to reset the insertion point of the builder when destroyed.
Definition Builders.h:351
This class helps build Operations.
Definition Builders.h:210
Block * createBlock(Region *parent, Region::iterator insertPt={}, TypeRange argTypes={}, ArrayRef< Location > locs={})
Add new block with 'argTypes' arguments and set the insertion point to the end of it.
Definition Builders.cpp:439
void setInsertionPointToEnd(Block *block)
Sets the insertion point to the end of the specified block.
Definition Builders.h:439
A trait to mark ops that can be enclosed/wrapped in a SpecConstantOperation op.
type_range getType() const
void populateInherentAttrs(Operation *op, NamedAttrList &attrs) const
Append the inherent attributes stored in the properties of op to attrs.
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
Attribute getDiscardableAttr(StringRef name)
Access a discardable attribute by name, returns a null Attribute if the discardable attribute does no...
Definition Operation.h:487
Dialect * getDialect()
Return the dialect this operation is associated with, or nullptr if the associated dialect is not loa...
Definition Operation.h:237
Value getOperand(unsigned idx)
Definition Operation.h:377
bool hasTrait()
Returns true if the operation was registered with a particular trait, e.g.
Definition Operation.h:803
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
Definition Operation.h:434
Location getLoc()
The source location the operation was defined or derived from.
Definition Operation.h:240
InFlightDiagnostic emitError(const Twine &message={})
Emit an error about fatal conditions with this operation, reporting up to any diagnostic handlers tha...
OperationName getName()
The name of an operation is the key identifier for it.
Definition Operation.h:115
DictionaryAttr getDiscardableAttrDictionary()
Return all of the discardable attributes on this operation as a DictionaryAttr.
Definition Operation.h:555
operand_type_range getOperandTypes()
Definition Operation.h:424
result_type_range getResultTypes()
Definition Operation.h:455
operand_range getOperands()
Returns an iterator on the underlying Value's.
Definition Operation.h:405
InFlightDiagnostic emitOpError(const Twine &message={})
Emit an error with the op name prefixed, like "'dim' op " which is convenient for verifiers.
unsigned getNumResults()
Return the number of results held by this operation.
Definition Operation.h:431
This class implements Optional functionality for ParseResult.
bool has_value() const
Returns true if we contain a valid ParseResult value.
This class contains a list of basic blocks and a link to the parent operation it is attached to.
Definition Region.h:26
void push_back(Block *block)
Definition Region.h:61
Block & back()
Definition Region.h:64
bool empty()
Definition Region.h:60
This class represents a collection of SymbolTables.
virtual Operation * lookupNearestSymbolFrom(Operation *from, StringAttr symbol)
Returns the operation registered with the given symbol name within the closest parent operation of,...
This class allows for representing and managing the symbol table used by operations with the 'SymbolT...
Definition SymbolTable.h:25
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition Types.h:74
Dialect & getDialect() const
Get the dialect this type is registered to.
Definition Types.h:107
Type front()
Return first type in the range.
Definition TypeRange.h:164
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition Value.h:96
Type getType() const
Return the type of this value.
Definition Value.h:105
A utility result that is used to signal how to proceed with an ongoing walk:
Definition WalkResult.h:29
static WalkResult advance()
Definition WalkResult.h:47
static ArrayType get(Type elementType, unsigned elementCount)
static PointerType get(Type pointeeType, StorageClass storageClass)
SPIR-V struct type.
Definition SPIRVTypes.h:274
unsigned getNumElements() const
Type getElementType(unsigned) const
An attribute that specifies the SPIR-V (version, capabilities, extensions) triple.
void addArgAndResultAttrs(Builder &builder, OperationState &result, ArrayRef< DictionaryAttr > argAttrs, ArrayRef< DictionaryAttr > resultAttrs, StringAttr argAttrsName, StringAttr resAttrsName)
Adds argument and result attributes, provided as argAttrs and resultAttrs arguments,...
void walk(Operation *op, function_ref< void(Region *)> callback, WalkOrder order)
Walk all of the regions, blocks, or operations nested under (and including) the given operation.
Definition Visitors.h:102
ArrayRef< NamedAttribute > getArgAttrs(FunctionOpInterface op, unsigned index)
Return all of the attributes for the argument at 'index'.
ParseResult parseFunctionSignatureWithArguments(OpAsmParser &parser, bool allowVariadic, SmallVectorImpl< OpAsmParser::Argument > &arguments, bool &isVariadic, SmallVectorImpl< Type > &resultTypes, SmallVectorImpl< DictionaryAttr > &resultAttrs)
Parses a function signature using parser.
void printFunctionAttributes(OpAsmPrinter &p, Operation *op, ArrayRef< StringRef > elided={})
Prints the list of function prefixed with the "attributes" keyword.
void printFunctionSignature(OpAsmPrinter &p, FunctionOpInterface op, ArrayRef< Type > argTypes, bool isVariadic, ArrayRef< Type > resultTypes)
Prints the signature of the function-like operation op.
ParseResult parseOptionalVisibilityKeyword(OpAsmParser &parser, NamedAttrList &attrs)
Parse an optional visibility attribute keyword (i.e., public, private, or nested) without quotes in a...
Operation::operand_range getIndices(Operation *op)
Get the indices that the given load/store operation is operating on.
Definition Utils.cpp:18
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition Remarks.h:853
uint64_t getN(LevelType lt)
Definition Enums.h:442
constexpr char kFnNameAttrName[]
constexpr char kSpecIdAttrName[]
LogicalResult verifyMemorySemantics(Operation *op, spirv::MemorySemantics memorySemantics)
Definition SPIRVOps.cpp:69
ParseResult parseEnumStrAttr(EnumClass &value, OpAsmParser &parser, StringRef attrName=spirv::attributeName< EnumClass >())
Parses the next string attribute in parser as an enumerant of the given EnumClass.
ParseResult parseEnumKeywordAttr(EnumClass &value, ParserType &parser, StringRef attrName=spirv::attributeName< EnumClass >())
Parses the next keyword in parser as an enumerant of the given EnumClass.
void printVariableDecorations(Operation *op, OpAsmPrinter &printer, SmallVectorImpl< StringRef > &elidedAttrs)
Definition SPIRVOps.cpp:133
LogicalResult verifyPhysicalStorageBufferDecorations(Operation *op, Type pointeeType)
Verifies the SPV_KHR_physical_storage_buffer rule that a variable whose pointee is a pointer (or arra...
Definition SPIRVOps.cpp:93
AddressingModel getAddressingModel(TargetEnvAttr targetAttr, bool use64bitAddress)
Returns addressing model selected based on target environment.
FailureOr< ExecutionModel > getExecutionModel(TargetEnvAttr targetAttr)
Returns execution model selected based on target environment.
FailureOr< MemoryModel > getMemoryModel(TargetEnvAttr targetAttr)
Returns memory model selected based on target environment.
LogicalResult extractValueFromConstOp(Operation *op, int32_t &value)
Definition SPIRVOps.cpp:49
std::string getDecorationString(Decoration decoration)
Converts a SPIR-V Decoration enum value to its snake_case string representation for use in MLIR attri...
ParseResult parseVariableDecorations(OpAsmParser &parser, OperationState &state)
Include the generated interface declarations.
Type getType(OpFoldResult ofr)
Returns the int type of the integer in ofr.
Definition Utils.cpp:311
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.
llvm::DenseMap< KeyT, ValueT, KeyInfoT, BucketT > DenseMap
Definition LLVM.h:123
llvm::function_ref< Fn > function_ref
Definition LLVM.h:155
This is the representation of an operand reference.
This represents an operation in an abstracted form, suitable for use with the builder APIs.
void addAttribute(StringRef name, Attribute attr)
Add an attribute with the specified name.
Region * addRegion()
Create a region that should be attached to the operation.