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 spirv::AddressOfOp::verify() {
362 auto varOp = dyn_cast_or_null<spirv::GlobalVariableOp>(
363 SymbolTable::lookupNearestSymbolFrom((*this)->getParentOp(),
364 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
427 for (Value component : constituents) {
428 if (!isa<VectorType>(component.getType()) &&
429 !component.getType().isIntOrFloat())
430 return emitOpError("operand type mismatch: expected operand to have "
431 "a scalar or vector type, but provided ")
432 << component.getType();
433
434 Type elementType = component.getType();
435 if (auto vectorType = dyn_cast<VectorType>(component.getType())) {
436 sizes.push_back(vectorType.getNumElements());
437 elementType = vectorType.getElementType();
438 } else {
439 sizes.push_back(1);
440 }
441
442 if (elementType != resultType.getElementType())
443 return emitOpError("operand element type mismatch: expected to be ")
444 << resultType.getElementType() << ", but provided " << elementType;
445 }
446 unsigned totalCount = llvm::sum_of(sizes);
447 if (totalCount != cType.getNumElements())
448 return emitOpError("has incorrect number of operands: expected ")
449 << cType.getNumElements() << ", but provided " << totalCount;
450 return success();
451}
452
453//===----------------------------------------------------------------------===//
454// spirv.CompositeExtractOp
455//===----------------------------------------------------------------------===//
456
457void spirv::CompositeExtractOp::build(OpBuilder &builder, OperationState &state,
458 Value composite,
460 auto indexAttr = builder.getI32ArrayAttr(indices);
461 auto elementType =
462 getElementType(composite.getType(), indexAttr, state.location);
463 if (!elementType) {
464 return;
465 }
466 build(builder, state, elementType, composite, indexAttr);
467}
468
469ParseResult spirv::CompositeExtractOp::parse(OpAsmParser &parser,
471 OpAsmParser::UnresolvedOperand compositeInfo;
472 Attribute indicesAttr;
473 StringRef indicesAttrName =
474 spirv::CompositeExtractOp::getIndicesAttrName(result.name);
475 Type compositeType;
476 SMLoc attrLocation;
477
478 if (parser.parseOperand(compositeInfo) ||
479 parser.getCurrentLocation(&attrLocation) ||
480 parser.parseAttribute(indicesAttr, indicesAttrName, result.attributes) ||
481 parser.parseColonType(compositeType) ||
482 parser.resolveOperand(compositeInfo, compositeType, result.operands)) {
483 return failure();
484 }
485
486 Type resultType =
487 getElementType(compositeType, indicesAttr, parser, attrLocation);
488 if (!resultType) {
489 return failure();
490 }
491 result.addTypes(resultType);
492 return success();
493}
494
495void spirv::CompositeExtractOp::print(OpAsmPrinter &printer) {
496 printer << ' ' << getComposite() << getIndices() << " : "
497 << getComposite().getType();
498}
499
500LogicalResult spirv::CompositeExtractOp::verify() {
501 auto indicesArrayAttr = dyn_cast<ArrayAttr>(getIndices());
502 auto resultType =
503 getElementType(getComposite().getType(), indicesArrayAttr, getLoc());
504 if (!resultType)
505 return failure();
506
507 if (resultType != getType()) {
508 return emitOpError("invalid result type: expected ")
509 << resultType << " but provided " << getType();
510 }
511
512 return success();
513}
514
515//===----------------------------------------------------------------------===//
516// spirv.CompositeInsert
517//===----------------------------------------------------------------------===//
518
519void spirv::CompositeInsertOp::build(OpBuilder &builder, OperationState &state,
520 Value object, Value composite,
522 auto indexAttr = builder.getI32ArrayAttr(indices);
523 build(builder, state, composite.getType(), object, composite, indexAttr);
524}
525
526ParseResult spirv::CompositeInsertOp::parse(OpAsmParser &parser,
529 Type objectType, compositeType;
530 Attribute indicesAttr;
531 StringRef indicesAttrName =
532 spirv::CompositeInsertOp::getIndicesAttrName(result.name);
533 auto loc = parser.getCurrentLocation();
534
535 return failure(
536 parser.parseOperandList(operands, 2) ||
537 parser.parseAttribute(indicesAttr, indicesAttrName, result.attributes) ||
538 parser.parseColonType(objectType) ||
539 parser.parseKeywordType("into", compositeType) ||
540 parser.resolveOperands(operands, {objectType, compositeType}, loc,
541 result.operands) ||
542 parser.addTypesToList(compositeType, result.types));
543}
544
545LogicalResult spirv::CompositeInsertOp::verify() {
546 auto indicesArrayAttr = dyn_cast<ArrayAttr>(getIndices());
547 auto objectType =
548 getElementType(getComposite().getType(), indicesArrayAttr, getLoc());
549 if (!objectType)
550 return failure();
551
552 if (objectType != getObject().getType()) {
553 return emitOpError("object operand type should be ")
554 << objectType << ", but found " << getObject().getType();
555 }
556
557 if (getComposite().getType() != getType()) {
558 return emitOpError("result type should be the same as "
559 "the composite type, but found ")
560 << getComposite().getType() << " vs " << getType();
561 }
562
563 return success();
564}
565
566void spirv::CompositeInsertOp::print(OpAsmPrinter &printer) {
567 printer << " " << getObject() << ", " << getComposite() << getIndices()
568 << " : " << getObject().getType() << " into "
569 << getComposite().getType();
570}
571
572//===----------------------------------------------------------------------===//
573// spirv.Constant
574//===----------------------------------------------------------------------===//
575
576ParseResult spirv::ConstantOp::parse(OpAsmParser &parser,
578 Attribute value;
579 StringRef valueAttrName = spirv::ConstantOp::getValueAttrName(result.name);
580 if (parser.parseAttribute(value, valueAttrName, result.attributes))
581 return failure();
582
583 Type type = NoneType::get(parser.getContext());
584 if (auto typedAttr = dyn_cast<TypedAttr>(value))
585 type = typedAttr.getType();
586 if (isa<NoneType, TensorType>(type)) {
587 if (parser.parseColonType(type))
588 return failure();
589 }
590
591 if (isa<TensorArmType>(type)) {
592 if (parser.parseOptionalColon().succeeded())
593 if (parser.parseType(type))
594 return failure();
595 }
596
597 return parser.addTypeToList(type, result.types);
598}
599
600void spirv::ConstantOp::print(OpAsmPrinter &printer) {
601 printer << ' ' << getValue();
602 if (isa<spirv::ArrayType, spirv::StructType>(getType()))
603 printer << " : " << getType();
604}
605
606static LogicalResult verifyConstantType(spirv::ConstantOp op, Attribute value,
607 Type opType) {
608 if (isa<spirv::CooperativeMatrixType>(opType)) {
609 auto denseAttr = dyn_cast<DenseElementsAttr>(value);
610 if (!denseAttr || !denseAttr.isSplat())
611 return op.emitOpError("expected a splat dense attribute for cooperative "
612 "matrix constant, but found ")
613 << denseAttr;
614 }
615 if (isa<IntegerAttr, FloatAttr>(value)) {
616 auto valueType = cast<TypedAttr>(value).getType();
617 if (valueType != opType)
618 return op.emitOpError("result type (")
619 << opType << ") does not match value type (" << valueType << ")";
620 return success();
621 }
622 if (isa<DenseTypedElementsAttr, SparseElementsAttr>(value)) {
623 auto valueType = cast<TypedAttr>(value).getType();
624 if (valueType == opType)
625 return success();
626 auto arrayType = dyn_cast<spirv::ArrayType>(opType);
627 auto shapedType = dyn_cast<ShapedType>(valueType);
628 if (!arrayType)
629 return op.emitOpError("result or element type (")
630 << opType << ") does not match value type (" << valueType
631 << "), must be the same or spirv.array";
632
633 int numElements = arrayType.getNumElements();
634 auto opElemType = arrayType.getElementType();
635 while (auto t = dyn_cast<spirv::ArrayType>(opElemType)) {
636 numElements *= t.getNumElements();
637 opElemType = t.getElementType();
638 }
639 if (!opElemType.isIntOrFloat())
640 return op.emitOpError("only support nested array result type");
641
642 auto valueElemType = shapedType.getElementType();
643 if (valueElemType != opElemType) {
644 return op.emitOpError("result element type (")
645 << opElemType << ") does not match value element type ("
646 << valueElemType << ")";
647 }
648
649 if (numElements != shapedType.getNumElements()) {
650 return op.emitOpError("result number of elements (")
651 << numElements << ") does not match value number of elements ("
652 << shapedType.getNumElements() << ")";
653 }
654 return success();
655 }
656 if (auto arrayAttr = dyn_cast<ArrayAttr>(value)) {
657 if (auto structType = dyn_cast<spirv::StructType>(opType)) {
658 // Identified (possibly recursive) structs are not supported as constants.
659 if (structType.isIdentified())
660 return op.emitOpError(
661 "cannot have an identified struct as a constant type");
662 if (arrayAttr.size() != structType.getNumElements())
663 return op.emitOpError("number of constituents (")
664 << arrayAttr.size()
665 << ") does not match number of struct members ("
666 << structType.getNumElements() << ")";
667 for (auto [idx, element] : llvm::enumerate(arrayAttr.getValue())) {
668 if (failed(verifyConstantType(op, element,
669 structType.getElementType(idx))))
670 return failure();
671 }
672 return success();
673 }
674 auto arrayType = dyn_cast<spirv::ArrayType>(opType);
675 if (!arrayType)
676 return op.emitOpError(
677 "must have spirv.array or spirv.struct result type for array value");
678 Type elemType = arrayType.getElementType();
679 for (Attribute element : arrayAttr.getValue()) {
680 // Verify array elements recursively.
681 if (failed(verifyConstantType(op, element, elemType)))
682 return failure();
683 }
684 return success();
685 }
686 return op.emitOpError("cannot have attribute: ") << value;
687}
688
689LogicalResult spirv::ConstantOp::verify() {
690 // ODS already generates checks to make sure the result type is valid. We just
691 // need to additionally check that the value's attribute type is consistent
692 // with the result type.
693 return verifyConstantType(*this, getValueAttr(), getType());
694}
695
696bool spirv::ConstantOp::isBuildableWith(Type type) {
697 // Must be valid SPIR-V type first.
698 if (!isa<spirv::SPIRVType>(type))
699 return false;
700
701 if (isa<SPIRVDialect>(type.getDialect())) {
702 if (auto structType = dyn_cast<spirv::StructType>(type))
703 return !structType.isIdentified();
704 return isa<spirv::ArrayType>(type);
705 }
706
707 return true;
708}
709
710spirv::ConstantOp spirv::ConstantOp::getZero(Type type, Location loc,
711 OpBuilder &builder) {
712 if (auto intType = dyn_cast<IntegerType>(type)) {
713 unsigned width = intType.getWidth();
714 if (width == 1)
715 return spirv::ConstantOp::create(builder, loc, type,
716 builder.getBoolAttr(false));
717 return spirv::ConstantOp::create(
718 builder, loc, type, builder.getIntegerAttr(type, APInt(width, 0)));
719 }
720 if (auto floatType = dyn_cast<FloatType>(type)) {
721 return spirv::ConstantOp::create(builder, loc, type,
722 builder.getFloatAttr(floatType, 0.0));
723 }
724 if (auto vectorType = dyn_cast<VectorType>(type)) {
725 Type elemType = vectorType.getElementType();
726 if (isa<IntegerType>(elemType)) {
727 return spirv::ConstantOp::create(
728 builder, loc, type,
729 DenseElementsAttr::get(vectorType,
730 IntegerAttr::get(elemType, 0).getValue()));
731 }
732 if (isa<FloatType>(elemType)) {
733 return spirv::ConstantOp::create(
734 builder, loc, type,
735 DenseFPElementsAttr::get(vectorType,
736 FloatAttr::get(elemType, 0.0).getValue()));
737 }
738 }
739
740 llvm_unreachable("unimplemented types for ConstantOp::getZero()");
741}
742
743spirv::ConstantOp spirv::ConstantOp::getOne(Type type, Location loc,
744 OpBuilder &builder) {
745 if (auto intType = dyn_cast<IntegerType>(type)) {
746 unsigned width = intType.getWidth();
747 if (width == 1)
748 return spirv::ConstantOp::create(builder, loc, type,
749 builder.getBoolAttr(true));
750 return spirv::ConstantOp::create(
751 builder, loc, type, builder.getIntegerAttr(type, APInt(width, 1)));
752 }
753 if (auto floatType = dyn_cast<FloatType>(type)) {
754 return spirv::ConstantOp::create(builder, loc, type,
755 builder.getFloatAttr(floatType, 1.0));
756 }
757 if (auto vectorType = dyn_cast<VectorType>(type)) {
758 Type elemType = vectorType.getElementType();
759 if (isa<IntegerType>(elemType)) {
760 return spirv::ConstantOp::create(
761 builder, loc, type,
762 DenseElementsAttr::get(vectorType,
763 IntegerAttr::get(elemType, 1).getValue()));
764 }
765 if (isa<FloatType>(elemType)) {
766 return spirv::ConstantOp::create(
767 builder, loc, type,
768 DenseFPElementsAttr::get(vectorType,
769 FloatAttr::get(elemType, 1.0).getValue()));
770 }
771 }
772
773 llvm_unreachable("unimplemented types for ConstantOp::getOne()");
774}
775
776void mlir::spirv::ConstantOp::getAsmResultNames(
777 llvm::function_ref<void(mlir::Value, llvm::StringRef)> setNameFn) {
778 Type type = getType();
779
780 SmallString<32> specialNameBuffer;
781 llvm::raw_svector_ostream specialName(specialNameBuffer);
782 specialName << "cst";
783
784 IntegerType intTy = dyn_cast<IntegerType>(type);
785
786 if (IntegerAttr intCst = dyn_cast<IntegerAttr>(getValue())) {
787 assert(intTy);
788
789 if (intTy.getWidth() == 1) {
790 return setNameFn(getResult(), (intCst.getInt() ? "true" : "false"));
791 }
792
793 if (intTy.isSignless()) {
794 specialName << intCst.getInt();
795 } else if (intTy.isUnsigned()) {
796 specialName << intCst.getUInt();
797 } else {
798 specialName << intCst.getSInt();
799 }
800 }
801
802 if (intTy || isa<FloatType>(type)) {
803 specialName << '_' << type;
804 }
805
806 if (auto vecType = dyn_cast<VectorType>(type)) {
807 specialName << "_vec_";
808 specialName << vecType.getDimSize(0);
809
810 Type elementType = vecType.getElementType();
811
812 if (isa<IntegerType>(elementType) || isa<FloatType>(elementType)) {
813 specialName << "x" << elementType;
814 }
815 }
816
817 setNameFn(getResult(), specialName.str());
818}
819
820void mlir::spirv::AddressOfOp::getAsmResultNames(
821 llvm::function_ref<void(mlir::Value, llvm::StringRef)> setNameFn) {
822 SmallString<32> specialNameBuffer;
823 llvm::raw_svector_ostream specialName(specialNameBuffer);
824 specialName << getVariable() << "_addr";
825 setNameFn(getResult(), specialName.str());
826}
827
828//===----------------------------------------------------------------------===//
829// spirv.EXTConstantCompositeReplicate
830//===----------------------------------------------------------------------===//
831
832// Returns type of attribute. In case of a TypedAttr this will simply return
833// the type. But for an ArrayAttr which is untyped and can be multidimensional
834// it creates the ArrayType recursively.
836 if (auto typedAttr = dyn_cast<TypedAttr>(attr)) {
837 return typedAttr.getType();
838 }
839
840 if (auto arrayAttr = dyn_cast<ArrayAttr>(attr)) {
841 return spirv::ArrayType::get(getValueType(arrayAttr[0]), arrayAttr.size());
842 }
843
844 return nullptr;
845}
846
847LogicalResult spirv::EXTConstantCompositeReplicateOp::verify() {
848 Type valueType = getValueType(getValue());
849 if (!valueType)
850 return emitError("unknown value attribute type");
851
852 auto compositeType = dyn_cast<spirv::CompositeType>(getType());
853 if (!compositeType)
854 return emitError("result type is not a composite type");
855
856 Type compositeElementType = compositeType.getElementType(0);
857
858 SmallVector<Type, 3> possibleTypes = {compositeElementType};
859 while (auto type = dyn_cast<spirv::CompositeType>(compositeElementType)) {
860 compositeElementType = type.getElementType(0);
861 possibleTypes.push_back(compositeElementType);
862 }
863
864 if (!is_contained(possibleTypes, valueType)) {
865 return emitError("expected value attribute type ")
866 << interleaved(possibleTypes, " or ") << ", but got: " << valueType;
867 }
868
869 return success();
870}
871
872//===----------------------------------------------------------------------===//
873// spirv.ControlBarrierOp
874//===----------------------------------------------------------------------===//
875
876LogicalResult spirv::ControlBarrierOp::verify() {
877 return verifyMemorySemantics(getOperation(), getMemorySemantics());
878}
879
880//===----------------------------------------------------------------------===//
881// spirv.EntryPoint
882//===----------------------------------------------------------------------===//
883
884void spirv::EntryPointOp::build(OpBuilder &builder, OperationState &state,
885 spirv::ExecutionModel executionModel,
886 spirv::FuncOp function,
887 ArrayRef<Attribute> interfaceVars) {
888 build(builder, state,
889 spirv::ExecutionModelAttr::get(builder.getContext(), executionModel),
890 SymbolRefAttr::get(function), builder.getArrayAttr(interfaceVars));
891}
892
893ParseResult spirv::EntryPointOp::parse(OpAsmParser &parser,
895 spirv::ExecutionModel execModel;
896 SmallVector<Attribute, 4> interfaceVars;
897
899 if (parseEnumStrAttr<spirv::ExecutionModelAttr>(execModel, parser, result) ||
900 parser.parseAttribute(fn, Type(), kFnNameAttrName, result.attributes)) {
901 return failure();
902 }
903
904 if (!parser.parseOptionalComma()) {
905 // Parse the interface variables
906 if (parser.parseCommaSeparatedList([&]() -> ParseResult {
907 // The name of the interface variable attribute isnt important
908 FlatSymbolRefAttr var;
909 NamedAttrList attrs;
910 if (parser.parseAttribute(var, Type(), "var_symbol", attrs))
911 return failure();
912 interfaceVars.push_back(var);
913 return success();
914 }))
915 return failure();
916 }
917 result.addAttribute(spirv::EntryPointOp::getInterfaceAttrName(result.name),
918 parser.getBuilder().getArrayAttr(interfaceVars));
919 return success();
920}
921
922void spirv::EntryPointOp::print(OpAsmPrinter &printer) {
923 printer << " \"" << stringifyExecutionModel(getExecutionModel()) << "\" ";
924 printer.printSymbolName(getFn());
925 auto interfaceVars = getInterface().getValue();
926 if (!interfaceVars.empty())
927 printer << ", " << llvm::interleaved(interfaceVars);
928}
929
930LogicalResult spirv::EntryPointOp::verify() {
931 // Checks for fn and interface symbol reference are done in spirv::ModuleOp
932 // verification.
933 return success();
934}
935
936//===----------------------------------------------------------------------===//
937// spirv.ExecutionMode / spirv.ExecutionModeId
938//===----------------------------------------------------------------------===//
939
940namespace {
941// Describes the extra operands a SPIR-V ExecutionMode expects: whether they
942// are <id> operands (only valid on spirv.ExecutionModeId) or literal integers
943// (only valid on spirv.ExecutionMode), and how many of them are required.
944struct ExecutionModeOperandSchema {
945 bool isIdOperand;
946 unsigned numOperands;
947};
948
949ExecutionModeOperandSchema
950getExecutionModeOperandSchema(spirv::ExecutionMode mode) {
951 switch (mode) {
952 case spirv::ExecutionMode::Invocations:
953 case spirv::ExecutionMode::OutputVertices:
954 case spirv::ExecutionMode::VecTypeHint:
955 case spirv::ExecutionMode::SubgroupSize:
956 case spirv::ExecutionMode::SubgroupsPerWorkgroup:
957 case spirv::ExecutionMode::DenormPreserve:
958 case spirv::ExecutionMode::DenormFlushToZero:
959 case spirv::ExecutionMode::SignedZeroInfNanPreserve:
960 case spirv::ExecutionMode::RoundingModeRTE:
961 case spirv::ExecutionMode::RoundingModeRTZ:
962 case spirv::ExecutionMode::OutputPrimitivesEXT:
963 case spirv::ExecutionMode::SharedLocalMemorySizeINTEL:
964 case spirv::ExecutionMode::RoundingModeRTPINTEL:
965 case spirv::ExecutionMode::RoundingModeRTNINTEL:
966 case spirv::ExecutionMode::FloatingPointModeALTINTEL:
967 case spirv::ExecutionMode::FloatingPointModeIEEEINTEL:
968 case spirv::ExecutionMode::MaxWorkDimINTEL:
969 case spirv::ExecutionMode::NumSIMDWorkitemsINTEL:
970 case spirv::ExecutionMode::SchedulerTargetFmaxMhzINTEL:
971 case spirv::ExecutionMode::StreamingInterfaceINTEL:
972 case spirv::ExecutionMode::NamedBarrierCountINTEL:
973 return {/*isIdOperand=*/false, /*numOperands=*/1};
974 case spirv::ExecutionMode::LocalSize:
975 case spirv::ExecutionMode::LocalSizeHint:
976 case spirv::ExecutionMode::MaxWorkgroupSizeINTEL:
977 return {/*isIdOperand=*/false, /*numOperands=*/3};
978 case spirv::ExecutionMode::SubgroupsPerWorkgroupId:
979 return {/*isIdOperand=*/true, /*numOperands=*/1};
980 case spirv::ExecutionMode::LocalSizeId:
981 case spirv::ExecutionMode::LocalSizeHintId:
982 return {/*isIdOperand=*/true, /*numOperands=*/3};
983 default:
984 return {/*isIdOperand=*/false, /*numOperands=*/0};
985 }
986}
987} // namespace
988
989//===----------------------------------------------------------------------===//
990// spirv.ExecutionMode
991//===----------------------------------------------------------------------===//
992
993void spirv::ExecutionModeOp::build(OpBuilder &builder, OperationState &state,
994 spirv::FuncOp function,
995 spirv::ExecutionMode executionMode,
996 ArrayRef<int32_t> params) {
997 build(builder, state, SymbolRefAttr::get(function),
998 spirv::ExecutionModeAttr::get(builder.getContext(), executionMode),
999 builder.getI32ArrayAttr(params));
1000}
1001
1002ParseResult spirv::ExecutionModeOp::parse(OpAsmParser &parser,
1004 spirv::ExecutionMode execMode;
1005 Attribute fn;
1006 if (parser.parseAttribute(fn, kFnNameAttrName, result.attributes) ||
1008 return failure();
1009 }
1010
1012 Type i32Type = parser.getBuilder().getIntegerType(32);
1013 while (!parser.parseOptionalComma()) {
1014 NamedAttrList attr;
1015 Attribute value;
1016 if (parser.parseAttribute(value, i32Type, "value", attr)) {
1017 return failure();
1018 }
1019 values.push_back(cast<IntegerAttr>(value).getInt());
1020 }
1021 StringRef valuesAttrName =
1022 spirv::ExecutionModeOp::getValuesAttrName(result.name);
1023 result.addAttribute(valuesAttrName,
1024 parser.getBuilder().getI32ArrayAttr(values));
1025 return success();
1026}
1027
1028void spirv::ExecutionModeOp::print(OpAsmPrinter &printer) {
1029 printer << " ";
1030 printer.printSymbolName(getFn());
1031 printer << " \"" << stringifyExecutionMode(getExecutionMode()) << "\"";
1032 ArrayAttr values = this->getValues();
1033 if (!values.empty())
1034 printer << ", " << llvm::interleaved(values.getAsValueRange<IntegerAttr>());
1035}
1036
1037LogicalResult spirv::ExecutionModeOp::verify() {
1038 ExecutionModeOperandSchema schema =
1039 getExecutionModeOperandSchema(getExecutionMode());
1040
1041 if (schema.isIdOperand)
1042 return emitOpError("expected ExecutionMode that takes extra operands "
1043 "that are not <id> operands, got: ")
1044 << stringifyExecutionMode(getExecutionMode());
1045
1046 if (getValues().size() != schema.numOperands)
1047 return emitOpError("expected ")
1048 << schema.numOperands << " value operand(s), got "
1049 << getValues().size();
1050
1051 return success();
1052}
1053
1054//===----------------------------------------------------------------------===//
1055// spirv.ExecutionModeId
1056//===----------------------------------------------------------------------===//
1057
1058ParseResult spirv::ExecutionModeIdOp::parse(OpAsmParser &parser,
1060 ExecutionMode execMode;
1061 if (Attribute fn;
1062 parser.parseAttribute(fn, kFnNameAttrName, result.attributes) ||
1063 parseEnumStrAttr<ExecutionModeAttr>(execMode, parser, result)) {
1064 return failure();
1065 }
1066
1068 if (parser.parseCommaSeparatedList([&]() -> ParseResult {
1069 FlatSymbolRefAttr attr;
1070 if (parser.parseAttribute(attr))
1071 return failure();
1072 values.push_back(attr);
1073 return success();
1074 })) {
1075 return failure();
1076 }
1077
1078 StringRef valuesAttrName = getValuesAttrName(result.name);
1079 ArrayAttr valuesAttr = parser.getBuilder().getArrayAttr(values);
1080 result.addAttribute(valuesAttrName, valuesAttr);
1081 return success();
1082}
1083
1084void spirv::ExecutionModeIdOp::print(OpAsmPrinter &printer) {
1085 printer << " ";
1086 printer.printSymbolName(getFn());
1087 printer << " \"" << stringifyExecutionMode(getExecutionMode()) << "\" ";
1088
1089 llvm::interleaveComma(
1090 getValues().getAsValueRange<FlatSymbolRefAttr>(), printer,
1091 [&](StringRef value) { printer.printSymbolName(value); });
1092}
1093
1094LogicalResult spirv::ExecutionModeIdOp::verify() {
1095 ExecutionModeOperandSchema schema =
1096 getExecutionModeOperandSchema(getExecutionMode());
1097
1098 if (!schema.isIdOperand)
1099 return emitOpError("expected ExecutionMode that takes extra operands that "
1100 "are <id> operands, got: ")
1101 << stringifyExecutionMode(getExecutionMode());
1102
1103 if (getValues().size() != schema.numOperands)
1104 return emitOpError("expected ")
1105 << schema.numOperands << " value operand(s), got "
1106 << getValues().size();
1107
1108 for (Attribute value : getValues()) {
1109 auto valueSymbol = dyn_cast<FlatSymbolRefAttr>(value);
1110 if (!valueSymbol)
1111 return emitOpError("expected value operands to be symbol reference");
1113 (*this)->getParentOp(), valueSymbol);
1114 if (!valueOp)
1115 return emitOpError("cannot find symbol referenced by value operand: ")
1116 << valueSymbol.getValue();
1117 }
1118
1119 return success();
1120}
1121
1122//===----------------------------------------------------------------------===//
1123// spirv.func
1124//===----------------------------------------------------------------------===//
1125
1126ParseResult spirv::FuncOp::parse(OpAsmParser &parser, OperationState &result) {
1128 SmallVector<DictionaryAttr> resultAttrs;
1129 SmallVector<Type> resultTypes;
1130 auto &builder = parser.getBuilder();
1131
1133
1134 // Parse the name as a symbol.
1135 StringAttr nameAttr;
1136 if (parser.parseSymbolName(nameAttr, getSymNameAttrName(result.name),
1137 result.attributes))
1138 return failure();
1139
1140 // Parse the function signature.
1141 bool isVariadic = false;
1143 parser, /*allowVariadic=*/false, entryArgs, isVariadic, resultTypes,
1144 resultAttrs))
1145 return failure();
1146
1147 SmallVector<Type> argTypes;
1148 for (auto &arg : entryArgs)
1149 argTypes.push_back(arg.type);
1150 auto fnType = builder.getFunctionType(argTypes, resultTypes);
1151 result.addAttribute(getFunctionTypeAttrName(result.name),
1152 TypeAttr::get(fnType));
1153
1154 // Parse the optional function control keyword.
1155 spirv::FunctionControl fnControl;
1157 return failure();
1158
1159 // If additional attributes are present, parse them.
1160 if (parser.parseOptionalAttrDictWithKeyword(result.attributes))
1161 return failure();
1162
1163 // Add the attributes to the function arguments.
1164 assert(resultAttrs.size() == resultTypes.size());
1166 builder, result, entryArgs, resultAttrs, getArgAttrsAttrName(result.name),
1167 getResAttrsAttrName(result.name));
1168
1169 // Parse the optional function body.
1170 auto *body = result.addRegion();
1171 OptionalParseResult parseResult =
1172 parser.parseOptionalRegion(*body, entryArgs);
1173 return failure(parseResult.has_value() && failed(*parseResult));
1174}
1175
1176void spirv::FuncOp::print(OpAsmPrinter &printer) {
1177 // Print function name, signature, and control.
1178 printer << ' ';
1179 if (StringAttr visibility = getSymVisibilityAttr())
1180 printer << visibility.getValue() << ' ';
1181 printer.printSymbolName(getSymName());
1182 auto fnType = getFunctionType();
1184 printer, *this, fnType.getInputs(),
1185 /*isVariadic=*/false, fnType.getResults());
1186 printer << " \"" << spirv::stringifyFunctionControl(getFunctionControl())
1187 << "\"";
1189 printer, *this,
1190 {spirv::attributeName<spirv::FunctionControl>(),
1191 getFunctionTypeAttrName(), getArgAttrsAttrName(), getResAttrsAttrName(),
1192 getFunctionControlAttrName(), getSymVisibilityAttrName()});
1193
1194 // Print the body if this is not an external function.
1195 Region &body = this->getBody();
1196 if (!body.empty()) {
1197 printer << ' ';
1198 printer.printRegion(body, /*printEntryBlockArgs=*/false,
1199 /*printBlockTerminators=*/true);
1200 }
1201}
1202
1203LogicalResult spirv::FuncOp::verifyType() {
1204 FunctionType fnType = getFunctionType();
1205 if (fnType.getNumResults() > 1)
1206 return emitOpError("cannot have more than one result");
1207
1208 auto hasDecorationAttr = [&](spirv::Decoration decoration,
1209 unsigned argIndex) {
1210 auto func = cast<FunctionOpInterface>(getOperation());
1211 for (auto argAttr : cast<FunctionOpInterface>(func).getArgAttrs(argIndex)) {
1212 if (argAttr.getName() != spirv::DecorationAttr::name)
1213 continue;
1214 if (auto decAttr = dyn_cast<spirv::DecorationAttr>(argAttr.getValue()))
1215 return decAttr.getValue() == decoration;
1216 }
1217 return false;
1218 };
1219
1220 for (unsigned i = 0, e = this->getNumArguments(); i != e; ++i) {
1221 Type param = fnType.getInputs()[i];
1222 auto inputPtrType = dyn_cast<spirv::PointerType>(param);
1223 if (!inputPtrType)
1224 continue;
1225
1226 auto pointeePtrType =
1227 dyn_cast<spirv::PointerType>(inputPtrType.getPointeeType());
1228 if (pointeePtrType) {
1229 // SPIR-V spec, from SPV_KHR_physical_storage_buffer:
1230 // > If an OpFunctionParameter is a pointer (or contains a pointer)
1231 // > and the type it points to is a pointer in the PhysicalStorageBuffer
1232 // > storage class, the function parameter must be decorated with exactly
1233 // > one of AliasedPointer or RestrictPointer.
1234 if (pointeePtrType.getStorageClass() !=
1235 spirv::StorageClass::PhysicalStorageBuffer)
1236 continue;
1237
1238 bool hasAliasedPtr =
1239 hasDecorationAttr(spirv::Decoration::AliasedPointer, i);
1240 bool hasRestrictPtr =
1241 hasDecorationAttr(spirv::Decoration::RestrictPointer, i);
1242 if (!hasAliasedPtr && !hasRestrictPtr)
1243 return emitOpError()
1244 << "with a pointer points to a physical buffer pointer must "
1245 "be decorated either 'AliasedPointer' or 'RestrictPointer'";
1246 continue;
1247 }
1248 // SPIR-V spec, from SPV_KHR_physical_storage_buffer:
1249 // > If an OpFunctionParameter is a pointer (or contains a pointer) in
1250 // > the PhysicalStorageBuffer storage class, the function parameter must
1251 // > be decorated with exactly one of Aliased or Restrict.
1252 if (auto pointeeArrayType =
1253 dyn_cast<spirv::ArrayType>(inputPtrType.getPointeeType())) {
1254 pointeePtrType =
1255 dyn_cast<spirv::PointerType>(pointeeArrayType.getElementType());
1256 } else {
1257 pointeePtrType = inputPtrType;
1258 }
1259
1260 if (!pointeePtrType || pointeePtrType.getStorageClass() !=
1261 spirv::StorageClass::PhysicalStorageBuffer)
1262 continue;
1263
1264 bool hasAliased = hasDecorationAttr(spirv::Decoration::Aliased, i);
1265 bool hasRestrict = hasDecorationAttr(spirv::Decoration::Restrict, i);
1266 if (!hasAliased && !hasRestrict)
1267 return emitOpError() << "with physical buffer pointer must be decorated "
1268 "either 'Aliased' or 'Restrict'";
1269 }
1270
1271 return success();
1272}
1273
1274LogicalResult spirv::FuncOp::verifyBody() {
1275 FunctionType fnType = getFunctionType();
1276 if (!isExternal()) {
1277 Block &entryBlock = front();
1278
1279 unsigned numArguments = this->getNumArguments();
1280 if (entryBlock.getNumArguments() != numArguments)
1281 return emitOpError("entry block must have ")
1282 << numArguments << " arguments to match function signature";
1283
1284 for (auto [index, fnArgType, blockArgType] :
1285 llvm::enumerate(getArgumentTypes(), entryBlock.getArgumentTypes())) {
1286 if (blockArgType != fnArgType) {
1287 return emitOpError("type of entry block argument #")
1288 << index << '(' << blockArgType
1289 << ") must match the type of the corresponding argument in "
1290 << "function signature(" << fnArgType << ')';
1291 }
1292 }
1293 }
1294
1295 auto walkResult = walk([fnType](Operation *op) -> WalkResult {
1296 if (auto retOp = dyn_cast<spirv::ReturnOp>(op)) {
1297 if (fnType.getNumResults() != 0)
1298 return retOp.emitOpError("cannot be used in functions returning value");
1299 } else if (auto retOp = dyn_cast<spirv::ReturnValueOp>(op)) {
1300 if (fnType.getNumResults() != 1)
1301 return retOp.emitOpError(
1302 "returns 1 value but enclosing function requires ")
1303 << fnType.getNumResults() << " results";
1304
1305 auto retOperandType = retOp.getValue().getType();
1306 auto fnResultType = fnType.getResult(0);
1307 if (retOperandType != fnResultType)
1308 return retOp.emitOpError(" return value's type (")
1309 << retOperandType << ") mismatch with function's result type ("
1310 << fnResultType << ")";
1311 }
1312 return WalkResult::advance();
1313 });
1314
1315 // TODO: verify other bits like linkage type.
1316
1317 return failure(walkResult.wasInterrupted());
1318}
1319
1320void spirv::FuncOp::build(OpBuilder &builder, OperationState &state,
1321 StringRef name, FunctionType type,
1322 spirv::FunctionControl control,
1324 state.addAttribute(getSymNameAttrName(state.name),
1325 builder.getStringAttr(name));
1326 state.addAttribute(getFunctionTypeAttrName(state.name), TypeAttr::get(type));
1327 state.addAttribute(spirv::attributeName<spirv::FunctionControl>(),
1328 builder.getAttr<spirv::FunctionControlAttr>(control));
1329 state.attributes.append(attrs.begin(), attrs.end());
1330 state.addRegion();
1331}
1332
1333//===----------------------------------------------------------------------===//
1334// spirv.GLFClampOp
1335//===----------------------------------------------------------------------===//
1336
1337ParseResult spirv::GLFClampOp::parse(OpAsmParser &parser,
1340}
1341void spirv::GLFClampOp::print(OpAsmPrinter &p) { printOneResultOp(*this, p); }
1342
1343//===----------------------------------------------------------------------===//
1344// spirv.GLUClampOp
1345//===----------------------------------------------------------------------===//
1346
1347ParseResult spirv::GLUClampOp::parse(OpAsmParser &parser,
1350}
1351void spirv::GLUClampOp::print(OpAsmPrinter &p) { printOneResultOp(*this, p); }
1352
1353//===----------------------------------------------------------------------===//
1354// spirv.GLSClampOp
1355//===----------------------------------------------------------------------===//
1356
1357ParseResult spirv::GLSClampOp::parse(OpAsmParser &parser,
1360}
1361void spirv::GLSClampOp::print(OpAsmPrinter &p) { printOneResultOp(*this, p); }
1362
1363//===----------------------------------------------------------------------===//
1364// spirv.GLNClampOp
1365//===----------------------------------------------------------------------===//
1366
1367ParseResult spirv::GLNClampOp::parse(OpAsmParser &parser,
1370}
1371void spirv::GLNClampOp::print(OpAsmPrinter &p) { printOneResultOp(*this, p); }
1372
1373//===----------------------------------------------------------------------===//
1374// spirv.GLSmoothStepOp
1375//===----------------------------------------------------------------------===//
1376
1377ParseResult spirv::GLSmoothStepOp::parse(OpAsmParser &parser,
1380}
1381void spirv::GLSmoothStepOp::print(OpAsmPrinter &p) {
1382 printOneResultOp(*this, p);
1383}
1384
1385//===----------------------------------------------------------------------===//
1386// spirv.GLFmaOp
1387//===----------------------------------------------------------------------===//
1388
1389ParseResult spirv::GLFmaOp::parse(OpAsmParser &parser, OperationState &result) {
1391}
1392void spirv::GLFmaOp::print(OpAsmPrinter &p) { printOneResultOp(*this, p); }
1393
1394//===----------------------------------------------------------------------===//
1395// spirv.GlobalVariable
1396//===----------------------------------------------------------------------===//
1397
1398void spirv::GlobalVariableOp::build(OpBuilder &builder, OperationState &state,
1399 Type type, StringRef name,
1400 unsigned descriptorSet, unsigned binding) {
1401 build(builder, state, TypeAttr::get(type), builder.getStringAttr(name));
1402 state.addAttribute(
1403 spirv::SPIRVDialect::getAttributeName(spirv::Decoration::DescriptorSet),
1404 builder.getI32IntegerAttr(descriptorSet));
1405 state.addAttribute(
1406 spirv::SPIRVDialect::getAttributeName(spirv::Decoration::Binding),
1407 builder.getI32IntegerAttr(binding));
1408}
1409
1410void spirv::GlobalVariableOp::build(OpBuilder &builder, OperationState &state,
1411 Type type, StringRef name,
1412 spirv::BuiltIn builtin) {
1413 build(builder, state, TypeAttr::get(type), builder.getStringAttr(name));
1414 state.addAttribute(
1415 spirv::SPIRVDialect::getAttributeName(spirv::Decoration::BuiltIn),
1416 builder.getStringAttr(spirv::stringifyBuiltIn(builtin)));
1417}
1418
1419ParseResult spirv::GlobalVariableOp::parse(OpAsmParser &parser,
1422
1423 // Parse variable name.
1424 StringAttr nameAttr;
1425 StringRef initializerAttrName =
1426 spirv::GlobalVariableOp::getInitializerAttrName(result.name);
1427 if (parser.parseSymbolName(nameAttr, getSymNameAttrName(result.name),
1428 result.attributes)) {
1429 return failure();
1430 }
1431
1432 // Parse optional initializer
1433 if (succeeded(parser.parseOptionalKeyword(initializerAttrName))) {
1434 FlatSymbolRefAttr initSymbol;
1435 if (parser.parseLParen() ||
1436 parser.parseAttribute(initSymbol, Type(), initializerAttrName,
1437 result.attributes) ||
1438 parser.parseRParen())
1439 return failure();
1440 }
1441
1442 if (parseVariableDecorations(parser, result)) {
1443 return failure();
1444 }
1445
1446 Type type;
1447 StringRef typeAttrName =
1448 spirv::GlobalVariableOp::getTypeAttrName(result.name);
1449 auto loc = parser.getCurrentLocation();
1450 if (parser.parseColonType(type)) {
1451 return failure();
1452 }
1453 if (!isa<spirv::PointerType>(type)) {
1454 return parser.emitError(loc, "expected spirv.ptr type");
1455 }
1456 result.addAttribute(typeAttrName, TypeAttr::get(type));
1457
1458 return success();
1459}
1460
1461void spirv::GlobalVariableOp::print(OpAsmPrinter &printer) {
1462 SmallVector<StringRef, 4> elidedAttrs{
1463 spirv::attributeName<spirv::StorageClass>()};
1464
1465 // Print variable name.
1466 printer << ' ';
1467 if (StringAttr visibility = getSymVisibilityAttr())
1468 printer << visibility.getValue() << ' ';
1469 printer.printSymbolName(getSymName());
1470 elidedAttrs.push_back(getSymNameAttrName());
1471 elidedAttrs.push_back(getSymVisibilityAttrName());
1472
1473 StringRef initializerAttrName = this->getInitializerAttrName();
1474 // Print optional initializer
1475 if (auto initializer = this->getInitializer()) {
1476 printer << " " << initializerAttrName << '(';
1477 printer.printSymbolName(*initializer);
1478 printer << ')';
1479 elidedAttrs.push_back(initializerAttrName);
1480 }
1481
1482 StringRef typeAttrName = this->getTypeAttrName();
1483 elidedAttrs.push_back(typeAttrName);
1484 spirv::printVariableDecorations(*this, printer, elidedAttrs);
1485 printer << " : " << getType();
1486}
1487
1488LogicalResult spirv::GlobalVariableOp::verify() {
1489 if (!isa<spirv::PointerType>(getType()))
1490 return emitOpError("result must be of a !spv.ptr type");
1491
1492 // SPIR-V spec: "Storage Class is the Storage Class of the memory holding the
1493 // object. It cannot be Generic. It must be the same as the Storage Class
1494 // operand of the Result Type."
1495 // Also, Function storage class is reserved by spirv.Variable.
1496 auto storageClass = this->storageClass();
1497 if (storageClass == spirv::StorageClass::Generic ||
1498 storageClass == spirv::StorageClass::Function) {
1499 return emitOpError("storage class cannot be '")
1500 << stringifyStorageClass(storageClass) << "'";
1501 }
1502
1503 // SPIR-V spec: "A module-scope OpVariable with an Initializer operand must
1504 // not be decorated with the Import Linkage Type."
1505 if (std::optional<spirv::LinkageAttributesAttr> linkage =
1506 getLinkageAttributes()) {
1507 if (linkage->getLinkageType().getValue() == spirv::LinkageType::Import &&
1508 getInitializer()) {
1509 return emitOpError(
1510 "with Import linkage type must not have an initializer");
1511 }
1512 }
1513
1514 if (FlatSymbolRefAttr init = getInitializerAttr()) {
1516 (*this)->getParentOp(), init.getAttr());
1517 // TODO: Currently only variable initialization with specialization
1518 // constants is supported. There could be normal constants in the module
1519 // scope as well.
1520 //
1521 // In the current setup we also cannot initialize one global variable with
1522 // another. The problem is that if we try to initialize pointer of type X
1523 // with another pointer type, the validator fails because it expects the
1524 // variable to be initialized to be type X, not pointer to X. Now
1525 // `spirv.GlobalVariable` only allows pointer type, so in the current design
1526 // we cannot initialize one `spirv.GlobalVariable` with another.
1527 if (!initOp ||
1528 !isa<spirv::SpecConstantOp, spirv::SpecConstantCompositeOp>(initOp)) {
1529 return emitOpError("initializer must be result of a "
1530 "spirv.SpecConstant or "
1531 "spirv.SpecConstantCompositeOp op");
1532 }
1533 }
1534
1535 Type pointeeType = cast<spirv::PointerType>(getType()).getPointeeType();
1536 if (failed(
1537 verifyPhysicalStorageBufferDecorations(getOperation(), pointeeType)))
1538 return failure();
1539
1540 return success();
1541}
1542
1543//===----------------------------------------------------------------------===//
1544// spirv.INTEL.SubgroupBlockRead
1545//===----------------------------------------------------------------------===//
1546
1547LogicalResult spirv::INTELSubgroupBlockReadOp::verify() {
1548 if (failed(verifyBlockReadWritePtrAndValTypes(*this, getPtr(), getValue())))
1549 return failure();
1550
1551 return success();
1552}
1553
1554//===----------------------------------------------------------------------===//
1555// spirv.INTEL.SubgroupBlockWrite
1556//===----------------------------------------------------------------------===//
1557
1558ParseResult spirv::INTELSubgroupBlockWriteOp::parse(OpAsmParser &parser,
1560 // Parse the storage class specification
1561 spirv::StorageClass storageClass;
1563 auto loc = parser.getCurrentLocation();
1564 Type elementType;
1565 if (parseEnumStrAttr(storageClass, parser) ||
1566 parser.parseOperandList(operandInfo, 2) || parser.parseColon() ||
1567 parser.parseType(elementType)) {
1568 return failure();
1569 }
1570
1571 auto ptrType = spirv::PointerType::get(elementType, storageClass);
1572 if (auto valVecTy = dyn_cast<VectorType>(elementType))
1573 ptrType = spirv::PointerType::get(valVecTy.getElementType(), storageClass);
1574
1575 if (parser.resolveOperands(operandInfo, {ptrType, elementType}, loc,
1576 result.operands)) {
1577 return failure();
1578 }
1579 return success();
1580}
1581
1582void spirv::INTELSubgroupBlockWriteOp::print(OpAsmPrinter &printer) {
1583 printer << " " << getPtr() << ", " << getValue() << " : "
1584 << getValue().getType();
1585}
1586
1587LogicalResult spirv::INTELSubgroupBlockWriteOp::verify() {
1588 if (failed(verifyBlockReadWritePtrAndValTypes(*this, getPtr(), getValue())))
1589 return failure();
1590
1591 return success();
1592}
1593
1594//===----------------------------------------------------------------------===//
1595// spirv.IAddCarryOp
1596//===----------------------------------------------------------------------===//
1597
1598LogicalResult spirv::IAddCarryOp::verify() {
1599 return ::verifyArithmeticExtendedBinaryOp(*this);
1600}
1601
1602ParseResult spirv::IAddCarryOp::parse(OpAsmParser &parser,
1604 return ::parseArithmeticExtendedBinaryOp(parser, result);
1605}
1606
1607void spirv::IAddCarryOp::print(OpAsmPrinter &printer) {
1608 ::printArithmeticExtendedBinaryOp(*this, printer);
1609}
1610
1611//===----------------------------------------------------------------------===//
1612// spirv.ISubBorrowOp
1613//===----------------------------------------------------------------------===//
1614
1615LogicalResult spirv::ISubBorrowOp::verify() {
1616 return ::verifyArithmeticExtendedBinaryOp(*this);
1617}
1618
1619ParseResult spirv::ISubBorrowOp::parse(OpAsmParser &parser,
1621 return ::parseArithmeticExtendedBinaryOp(parser, result);
1622}
1623
1624void spirv::ISubBorrowOp::print(OpAsmPrinter &printer) {
1625 ::printArithmeticExtendedBinaryOp(*this, printer);
1626}
1627
1628//===----------------------------------------------------------------------===//
1629// spirv.SMulExtended
1630//===----------------------------------------------------------------------===//
1631
1632LogicalResult spirv::SMulExtendedOp::verify() {
1633 return ::verifyArithmeticExtendedBinaryOp(*this);
1634}
1635
1636ParseResult spirv::SMulExtendedOp::parse(OpAsmParser &parser,
1638 return ::parseArithmeticExtendedBinaryOp(parser, result);
1639}
1640
1641void spirv::SMulExtendedOp::print(OpAsmPrinter &printer) {
1642 ::printArithmeticExtendedBinaryOp(*this, printer);
1643}
1644
1645//===----------------------------------------------------------------------===//
1646// spirv.UMulExtended
1647//===----------------------------------------------------------------------===//
1648
1649LogicalResult spirv::UMulExtendedOp::verify() {
1650 return ::verifyArithmeticExtendedBinaryOp(*this);
1651}
1652
1653ParseResult spirv::UMulExtendedOp::parse(OpAsmParser &parser,
1655 return ::parseArithmeticExtendedBinaryOp(parser, result);
1656}
1657
1658void spirv::UMulExtendedOp::print(OpAsmPrinter &printer) {
1659 ::printArithmeticExtendedBinaryOp(*this, printer);
1660}
1661
1662//===----------------------------------------------------------------------===//
1663// spirv.MemoryBarrierOp
1664//===----------------------------------------------------------------------===//
1665
1666LogicalResult spirv::MemoryBarrierOp::verify() {
1667 return verifyMemorySemantics(getOperation(), getMemorySemantics());
1668}
1669
1670//===----------------------------------------------------------------------===//
1671// spirv.MemoryNamedBarrierOp
1672//===----------------------------------------------------------------------===//
1673
1674LogicalResult spirv::MemoryNamedBarrierOp::verify() {
1675 return verifyMemorySemantics(getOperation(), getMemorySemantics());
1676}
1677
1678//===----------------------------------------------------------------------===//
1679// spirv.module
1680//===----------------------------------------------------------------------===//
1681
1682void spirv::ModuleOp::build(OpBuilder &builder, OperationState &state,
1683 std::optional<StringRef> name) {
1684 OpBuilder::InsertionGuard guard(builder);
1685 builder.createBlock(state.addRegion());
1686 if (name) {
1687 state.attributes.append(getSymNameAttrName(state.name),
1688 builder.getStringAttr(*name));
1689 }
1690}
1691
1692void spirv::ModuleOp::build(OpBuilder &builder, OperationState &state,
1693 spirv::AddressingModel addressingModel,
1694 spirv::MemoryModel memoryModel,
1695 std::optional<VerCapExtAttr> vceTriple,
1696 std::optional<StringRef> name) {
1697 state.addAttribute(
1698 "addressing_model",
1699 builder.getAttr<spirv::AddressingModelAttr>(addressingModel));
1700 state.addAttribute("memory_model",
1701 builder.getAttr<spirv::MemoryModelAttr>(memoryModel));
1702 OpBuilder::InsertionGuard guard(builder);
1703 builder.createBlock(state.addRegion());
1704 if (vceTriple)
1705 state.addAttribute(getVCETripleAttrName(), *vceTriple);
1706 if (name)
1707 state.addAttribute(getSymNameAttrName(state.name),
1708 builder.getStringAttr(*name));
1709}
1710
1711ParseResult spirv::ModuleOp::parse(OpAsmParser &parser,
1713 Region *body = result.addRegion();
1714
1716
1717 // If the name is present, parse it.
1718 StringAttr nameAttr;
1720 nameAttr, getSymNameAttrName(result.name), result.attributes);
1721
1722 // Parse attributes
1723 spirv::AddressingModel addrModel;
1724 spirv::MemoryModel memoryModel;
1726 result) ||
1728 result))
1729 return failure();
1730
1731 if (succeeded(parser.parseOptionalKeyword("requires"))) {
1732 spirv::VerCapExtAttr vceTriple;
1733 if (parser.parseAttribute(vceTriple,
1734 spirv::ModuleOp::getVCETripleAttrName(),
1735 result.attributes))
1736 return failure();
1737 }
1738
1739 if (parser.parseOptionalAttrDictWithKeyword(result.attributes) ||
1740 parser.parseRegion(*body, /*arguments=*/{}))
1741 return failure();
1742
1743 // Make sure we have at least one block.
1744 if (body->empty())
1745 body->push_back(new Block());
1746
1747 return success();
1748}
1749
1750void spirv::ModuleOp::print(OpAsmPrinter &printer) {
1751 if (StringAttr visibility = getSymVisibilityAttr())
1752 printer << ' ' << visibility.getValue();
1753 if (std::optional<StringRef> name = getName()) {
1754 printer << ' ';
1755 printer.printSymbolName(*name);
1756 }
1757
1758 SmallVector<StringRef, 2> elidedAttrs;
1759
1760 printer << " " << spirv::stringifyAddressingModel(getAddressingModel()) << " "
1761 << spirv::stringifyMemoryModel(getMemoryModel());
1762 auto addressingModelAttrName = spirv::attributeName<spirv::AddressingModel>();
1763 auto memoryModelAttrName = spirv::attributeName<spirv::MemoryModel>();
1764 elidedAttrs.assign({addressingModelAttrName, memoryModelAttrName,
1765 getSymNameAttrName(), getSymVisibilityAttrName()});
1766
1767 if (std::optional<spirv::VerCapExtAttr> triple = getVceTriple()) {
1768 printer << " requires " << *triple;
1769 elidedAttrs.push_back(spirv::ModuleOp::getVCETripleAttrName());
1770 }
1771
1773 (*this)->getDiscardableAttrDictionary().getValue(), elidedAttrs);
1774 printer << ' ';
1775 printer.printRegion(getRegion());
1776}
1777
1778LogicalResult spirv::ModuleOp::verifyRegions() {
1779 Dialect *dialect = (*this)->getDialect();
1781 entryPoints;
1782 mlir::SymbolTable table(*this);
1783
1784 for (auto &op : *getBody()) {
1785 if (op.getDialect() != dialect)
1786 return op.emitError("'spirv.module' can only contain spirv.* ops");
1787
1788 // For EntryPoint op, check that the function and execution model is not
1789 // duplicated in EntryPointOps. Also verify that the interface specified
1790 // comes from globalVariables here to make this check cheaper.
1791 if (auto entryPointOp = dyn_cast<spirv::EntryPointOp>(op)) {
1792 auto funcOp = table.lookup<spirv::FuncOp>(entryPointOp.getFn());
1793 if (!funcOp) {
1794 return entryPointOp.emitError("function '")
1795 << entryPointOp.getFn() << "' not found in 'spirv.module'";
1796 }
1797 if (auto interface = entryPointOp.getInterface()) {
1798 for (Attribute varRef : interface) {
1799 auto varSymRef = dyn_cast<FlatSymbolRefAttr>(varRef);
1800 if (!varSymRef) {
1801 return entryPointOp.emitError(
1802 "expected symbol reference for interface "
1803 "specification instead of '")
1804 << varRef;
1805 }
1806 auto variableOp =
1807 table.lookup<spirv::GlobalVariableOp>(varSymRef.getValue());
1808 if (!variableOp) {
1809 return entryPointOp.emitError("expected spirv.GlobalVariable "
1810 "symbol reference instead of'")
1811 << varSymRef << "'";
1812 }
1813 }
1814 }
1815
1816 auto key = std::pair<spirv::FuncOp, spirv::ExecutionModel>(
1817 funcOp, entryPointOp.getExecutionModel());
1818 if (!entryPoints.try_emplace(key, entryPointOp).second)
1819 return entryPointOp.emitError("duplicate of a previous EntryPointOp");
1820 } else if (auto funcOp = dyn_cast<spirv::FuncOp>(op)) {
1821 // If the function is external and does not have 'Import'
1822 // linkage_attributes(LinkageAttributes), throw an error. 'Import'
1823 // LinkageAttributes is used to import external functions.
1824 auto linkageAttr = funcOp.getLinkageAttributes();
1825 auto hasImportLinkage =
1826 linkageAttr && (linkageAttr.value().getLinkageType().getValue() ==
1827 spirv::LinkageType::Import);
1828 if (funcOp.isExternal() && !hasImportLinkage)
1829 return op.emitError(
1830 "'spirv.module' cannot contain external functions "
1831 "without 'Import' linkage_attributes (LinkageAttributes)");
1832
1833 // TODO: move this check to spirv.func.
1834 for (auto &block : funcOp)
1835 for (auto &op : block) {
1836 if (op.getDialect() != dialect)
1837 return op.emitError(
1838 "functions in 'spirv.module' can only contain spirv.* ops");
1839 }
1840 }
1841 }
1842
1843 return success();
1844}
1845
1846//===----------------------------------------------------------------------===//
1847// spirv.mlir.referenceof
1848//===----------------------------------------------------------------------===//
1849
1850LogicalResult spirv::ReferenceOfOp::verify() {
1851 auto *specConstSym = SymbolTable::lookupNearestSymbolFrom(
1852 (*this)->getParentOp(), getSpecConstAttr());
1853 Type constType;
1854
1855 auto specConstOp = dyn_cast_or_null<spirv::SpecConstantOp>(specConstSym);
1856 if (specConstOp)
1857 constType = specConstOp.getDefaultValue().getType();
1858
1859 auto specConstCompositeOp =
1860 dyn_cast_or_null<spirv::SpecConstantCompositeOp>(specConstSym);
1861 if (specConstCompositeOp)
1862 constType = specConstCompositeOp.getType();
1863
1864 if (!specConstOp && !specConstCompositeOp)
1865 return emitOpError(
1866 "expected spirv.SpecConstant or spirv.SpecConstantComposite symbol");
1867
1868 if (getReference().getType() != constType)
1869 return emitOpError("result type mismatch with the referenced "
1870 "specialization constant's type");
1871
1872 return success();
1873}
1874
1875//===----------------------------------------------------------------------===//
1876// spirv.SpecConstant
1877//===----------------------------------------------------------------------===//
1878
1879ParseResult spirv::SpecConstantOp::parse(OpAsmParser &parser,
1882
1883 StringAttr nameAttr;
1884 Attribute valueAttr;
1885 StringRef defaultValueAttrName =
1886 spirv::SpecConstantOp::getDefaultValueAttrName(result.name);
1887
1888 if (parser.parseSymbolName(nameAttr, getSymNameAttrName(result.name),
1889 result.attributes))
1890 return failure();
1891
1892 // Parse optional spec_id.
1893 if (succeeded(parser.parseOptionalKeyword(kSpecIdAttrName))) {
1894 IntegerAttr specIdAttr;
1895 if (parser.parseLParen() ||
1896 parser.parseAttribute(specIdAttr, kSpecIdAttrName, result.attributes) ||
1897 parser.parseRParen())
1898 return failure();
1899 }
1900
1901 if (parser.parseEqual() ||
1902 parser.parseAttribute(valueAttr, defaultValueAttrName, result.attributes))
1903 return failure();
1904
1905 return success();
1906}
1907
1908void spirv::SpecConstantOp::print(OpAsmPrinter &printer) {
1909 printer << ' ';
1910 if (StringAttr visibility = getSymVisibilityAttr())
1911 printer << visibility.getValue() << ' ';
1912 printer.printSymbolName(getSymName());
1913 if (auto specID =
1914 (*this)->getDiscardableAttrOfType<IntegerAttr>(kSpecIdAttrName))
1915 printer << ' ' << kSpecIdAttrName << '(' << specID.getInt() << ')';
1916 printer << " = " << getDefaultValue();
1917}
1918
1919LogicalResult spirv::SpecConstantOp::verify() {
1920 if (auto specID =
1921 (*this)->getDiscardableAttrOfType<IntegerAttr>(kSpecIdAttrName))
1922 if (specID.getValue().isNegative())
1923 return emitOpError("SpecId cannot be negative");
1924
1925 auto value = getDefaultValue();
1926 if (isa<IntegerAttr, FloatAttr>(value)) {
1927 // Make sure bitwidth is allowed.
1928 if (!isa<spirv::SPIRVType>(value.getType()))
1929 return emitOpError("default value bitwidth disallowed");
1930 return success();
1931 }
1932 return emitOpError(
1933 "default value can only be a bool, integer, or float scalar");
1934}
1935
1936//===----------------------------------------------------------------------===//
1937// spirv.VectorShuffle
1938//===----------------------------------------------------------------------===//
1939
1940LogicalResult spirv::VectorShuffleOp::verify() {
1941 VectorType resultType = cast<VectorType>(getType());
1942
1943 size_t numResultElements = resultType.getNumElements();
1944 if (numResultElements != getComponents().size())
1945 return emitOpError("result type element count (")
1946 << numResultElements
1947 << ") mismatch with the number of component selectors ("
1948 << getComponents().size() << ")";
1949
1950 size_t totalSrcElements =
1951 cast<VectorType>(getVector1().getType()).getNumElements() +
1952 cast<VectorType>(getVector2().getType()).getNumElements();
1953
1954 for (const auto &selector : getComponents().getAsValueRange<IntegerAttr>()) {
1955 uint32_t index = selector.getZExtValue();
1956 if (index >= totalSrcElements &&
1957 index != std::numeric_limits<uint32_t>().max())
1958 return emitOpError("component selector ")
1959 << index << " out of range: expected to be in [0, "
1960 << totalSrcElements << ") or 0xffffffff";
1961 }
1962 return success();
1963}
1964
1965//===----------------------------------------------------------------------===//
1966// spirv.SpecConstantComposite
1967//===----------------------------------------------------------------------===//
1968
1969ParseResult spirv::SpecConstantCompositeOp::parse(OpAsmParser &parser,
1971
1973
1974 StringAttr compositeName;
1975 if (parser.parseSymbolName(compositeName, getSymNameAttrName(result.name),
1976 result.attributes))
1977 return failure();
1978
1979 if (parser.parseLParen())
1980 return failure();
1981
1982 SmallVector<Attribute, 4> constituents;
1983
1984 do {
1985 // The name of the constituent attribute isn't important
1986 const char *attrName = "spec_const";
1987 FlatSymbolRefAttr specConstRef;
1988 NamedAttrList attrs;
1989
1990 if (parser.parseAttribute(specConstRef, Type(), attrName, attrs))
1991 return failure();
1992
1993 constituents.push_back(specConstRef);
1994 } while (!parser.parseOptionalComma());
1995
1996 if (parser.parseRParen())
1997 return failure();
1998
1999 StringAttr compositeSpecConstituentsName =
2000 spirv::SpecConstantCompositeOp::getConstituentsAttrName(result.name);
2001 result.addAttribute(compositeSpecConstituentsName,
2002 parser.getBuilder().getArrayAttr(constituents));
2003
2004 Type type;
2005 if (parser.parseColonType(type))
2006 return failure();
2007
2008 StringAttr typeAttrName =
2009 spirv::SpecConstantCompositeOp::getTypeAttrName(result.name);
2010 result.addAttribute(typeAttrName, TypeAttr::get(type));
2011
2012 return success();
2013}
2014
2015void spirv::SpecConstantCompositeOp::print(OpAsmPrinter &printer) {
2016 printer << ' ';
2017 if (StringAttr visibility = getSymVisibilityAttr())
2018 printer << visibility.getValue() << ' ';
2019 printer.printSymbolName(getSymName());
2020 printer << " (" << llvm::interleaved(this->getConstituents().getValue())
2021 << ") : " << getType();
2022}
2023
2024LogicalResult spirv::SpecConstantCompositeOp::verify() {
2025 auto cType = dyn_cast<spirv::CompositeType>(getType());
2026 auto constituents = this->getConstituents().getValue();
2027
2028 if (!cType)
2029 return emitError("result type must be a composite type, but provided ")
2030 << getType();
2031
2032 if (isa<spirv::CooperativeMatrixType>(cType))
2033 return emitError("unsupported composite type ") << cType;
2034 if (constituents.size() != cType.getNumElements())
2035 return emitError("has incorrect number of operands: expected ")
2036 << cType.getNumElements() << ", but provided "
2037 << constituents.size();
2038
2039 for (auto index : llvm::seq<uint32_t>(0, constituents.size())) {
2040 auto constituent = cast<FlatSymbolRefAttr>(constituents[index]);
2041
2043 (*this)->getParentOp(), constituent.getAttr());
2044
2045 if (!constituentOp)
2046 return emitError("unknown constituent symbol ") << constituent.getAttr();
2047
2048 Type constituentType;
2049 if (auto specConstOp = dyn_cast<spirv::SpecConstantOp>(constituentOp)) {
2050 constituentType = specConstOp.getDefaultValue().getType();
2051 } else if (auto specConstCompositeOp =
2052 dyn_cast<spirv::SpecConstantCompositeOp>(constituentOp)) {
2053 constituentType = specConstCompositeOp.getType();
2054 } else {
2055 return emitError("unsupported constituent ")
2056 << constituent.getAttr()
2057 << ": must reference a spirv.SpecConstant or "
2058 "spirv.SpecConstantComposite";
2059 }
2060
2061 if (constituentType != cType.getElementType(index))
2062 return emitError("has incorrect types of operands: expected ")
2063 << cType.getElementType(index) << ", but provided "
2064 << constituentType;
2065 }
2066
2067 return success();
2068}
2069
2070//===----------------------------------------------------------------------===//
2071// spirv.EXTSpecConstantCompositeReplicateOp
2072//===----------------------------------------------------------------------===//
2073
2074ParseResult
2075spirv::EXTSpecConstantCompositeReplicateOp::parse(OpAsmParser &parser,
2078
2079 StringAttr compositeName;
2080 FlatSymbolRefAttr specConstRef;
2081 const char *attrName = "spec_const";
2082 NamedAttrList attrs;
2083 Type type;
2084
2085 if (parser.parseSymbolName(compositeName, getSymNameAttrName(result.name),
2086 result.attributes) ||
2087 parser.parseLParen() ||
2088 parser.parseAttribute(specConstRef, Type(), attrName, attrs) ||
2089 parser.parseRParen() || parser.parseColonType(type))
2090 return failure();
2091
2092 StringAttr compositeSpecConstituentName =
2093 spirv::EXTSpecConstantCompositeReplicateOp::getConstituentAttrName(
2094 result.name);
2095 result.addAttribute(compositeSpecConstituentName, specConstRef);
2096
2097 StringAttr typeAttrName =
2098 spirv::EXTSpecConstantCompositeReplicateOp::getTypeAttrName(result.name);
2099 result.addAttribute(typeAttrName, TypeAttr::get(type));
2100
2101 return success();
2102}
2103
2104void spirv::EXTSpecConstantCompositeReplicateOp::print(OpAsmPrinter &printer) {
2105 printer << ' ';
2106 if (StringAttr visibility = getSymVisibilityAttr())
2107 printer << visibility.getValue() << ' ';
2108 printer.printSymbolName(getSymName());
2109 printer << " (" << this->getConstituent() << ") : " << getType();
2110}
2111
2112LogicalResult spirv::EXTSpecConstantCompositeReplicateOp::verify() {
2113 auto compositeType = dyn_cast<spirv::CompositeType>(getType());
2114 if (!compositeType)
2115 return emitError("result type must be a composite type, but provided ")
2116 << getType();
2117
2119 (*this)->getParentOp(), this->getConstituent());
2120 if (!constituentOp)
2121 return emitError(
2122 "splat spec constant reference defining constituent not found");
2123
2124 auto constituentSpecConstOp = dyn_cast<spirv::SpecConstantOp>(constituentOp);
2125 if (!constituentSpecConstOp)
2126 return emitError("constituent is not a spec constant");
2127
2128 Type constituentType = constituentSpecConstOp.getDefaultValue().getType();
2129 Type compositeElementType = compositeType.getElementType(0);
2130 if (constituentType != compositeElementType)
2131 return emitError("constituent has incorrect type: expected ")
2132 << compositeElementType << ", but provided " << constituentType;
2133
2134 return success();
2135}
2136
2137//===----------------------------------------------------------------------===//
2138// spirv.SpecConstantOperation
2139//===----------------------------------------------------------------------===//
2140
2141ParseResult spirv::SpecConstantOperationOp::parse(OpAsmParser &parser,
2143 Region *body = result.addRegion();
2144
2145 if (parser.parseKeyword("wraps"))
2146 return failure();
2147
2148 body->push_back(new Block);
2149 Block &block = body->back();
2150 Operation *wrappedOp = parser.parseGenericOperation(&block, block.begin());
2151
2152 if (!wrappedOp)
2153 return failure();
2154
2155 OpBuilder builder(parser.getContext());
2156 builder.setInsertionPointToEnd(&block);
2157 spirv::YieldOp::create(builder, wrappedOp->getLoc(), wrappedOp->getResult(0));
2158 result.location = wrappedOp->getLoc();
2159
2160 result.addTypes(wrappedOp->getResult(0).getType());
2161
2162 if (parser.parseOptionalAttrDict(result.attributes))
2163 return failure();
2164
2165 return success();
2166}
2167
2168void spirv::SpecConstantOperationOp::print(OpAsmPrinter &printer) {
2169 printer << " wraps ";
2170 printer.printGenericOp(&getBody().front().front());
2171}
2172
2173LogicalResult spirv::SpecConstantOperationOp::verifyRegions() {
2174 Block &block = getRegion().getBlocks().front();
2175
2176 if (block.getOperations().size() != 2)
2177 return emitOpError("expected exactly 2 nested ops");
2178
2179 Operation &enclosedOp = block.getOperations().front();
2180
2182 return emitOpError("invalid enclosed op");
2183
2184 for (auto operand : enclosedOp.getOperands())
2185 if (!isa_and_present<spirv::ConstantOp, spirv::ReferenceOfOp,
2186 spirv::SpecConstantOperationOp>(
2187 operand.getDefiningOp()))
2188 return emitOpError(
2189 "invalid operand, must be defined by a constant operation");
2190
2191 return success();
2192}
2193
2194//===----------------------------------------------------------------------===//
2195// spirv.GL.FrexpStruct
2196//===----------------------------------------------------------------------===//
2197
2198LogicalResult spirv::GLFrexpStructOp::verify() {
2199 spirv::StructType structTy =
2200 dyn_cast<spirv::StructType>(getResult().getType());
2201
2202 if (structTy.getNumElements() != 2)
2203 return emitError("result type must be a struct type with two memebers");
2204
2205 Type significandTy = structTy.getElementType(0);
2206 Type exponentTy = structTy.getElementType(1);
2207 VectorType exponentVecTy = dyn_cast<VectorType>(exponentTy);
2208 IntegerType exponentIntTy = dyn_cast<IntegerType>(exponentTy);
2209
2210 Type operandTy = getOperand().getType();
2211 VectorType operandVecTy = dyn_cast<VectorType>(operandTy);
2212 FloatType operandFTy = dyn_cast<FloatType>(operandTy);
2213
2214 if (significandTy != operandTy)
2215 return emitError("member zero of the resulting struct type must be the "
2216 "same type as the operand");
2217
2218 if (exponentVecTy) {
2219 IntegerType componentIntTy =
2220 dyn_cast<IntegerType>(exponentVecTy.getElementType());
2221 if (!componentIntTy || componentIntTy.getWidth() != 32)
2222 return emitError("member one of the resulting struct type must"
2223 "be a scalar or vector of 32 bit integer type");
2224 } else if (!exponentIntTy || exponentIntTy.getWidth() != 32) {
2225 return emitError("member one of the resulting struct type "
2226 "must be a scalar or vector of 32 bit integer type");
2227 }
2228
2229 // Check that the two member types have the same number of components
2230 if (operandVecTy && exponentVecTy &&
2231 (exponentVecTy.getNumElements() == operandVecTy.getNumElements()))
2232 return success();
2233
2234 if (operandFTy && exponentIntTy)
2235 return success();
2236
2237 return emitError("member one of the resulting struct type must have the same "
2238 "number of components as the operand type");
2239}
2240
2241//===----------------------------------------------------------------------===//
2242// spirv.GL.Ldexp
2243//===----------------------------------------------------------------------===//
2244
2245static LogicalResult verifyFloatIntegerBuiltin(Operation *op, Type floatType,
2246 Type integerType) {
2247 if (isa<FloatType>(floatType) != isa<IntegerType>(integerType))
2248 return op->emitOpError("operands must both be scalars or vectors");
2249
2250 auto getNumElements = [](Type type) -> unsigned {
2251 if (auto vectorType = dyn_cast<VectorType>(type))
2252 return vectorType.getNumElements();
2253 return 1;
2254 };
2255
2256 if (getNumElements(floatType) != getNumElements(integerType))
2257 return op->emitOpError("operands must have the same number of elements");
2258
2259 return success();
2260}
2261
2262LogicalResult spirv::GLLdexpOp::verify() {
2263 return verifyFloatIntegerBuiltin(getOperation(), getX().getType(),
2264 getExp().getType());
2265}
2266
2267//===----------------------------------------------------------------------===//
2268// spirv.CL.ldexp
2269//===----------------------------------------------------------------------===//
2270
2271LogicalResult spirv::CLLdexpOp::verify() {
2272 return verifyFloatIntegerBuiltin(getOperation(), getX().getType(),
2273 getExp().getType());
2274}
2275
2276//===----------------------------------------------------------------------===//
2277// spirv.CL.pown
2278//===----------------------------------------------------------------------===//
2279
2280LogicalResult spirv::CLPownOp::verify() {
2281 return verifyFloatIntegerBuiltin(getOperation(), getX().getType(),
2282 getY().getType());
2283}
2284
2285//===----------------------------------------------------------------------===//
2286// spirv.CL.rootn
2287//===----------------------------------------------------------------------===//
2288
2289LogicalResult spirv::CLRootnOp::verify() {
2290 return verifyFloatIntegerBuiltin(getOperation(), getX().getType(),
2291 getN().getType());
2292}
2293
2294//===----------------------------------------------------------------------===//
2295// spirv.ShiftLeftLogicalOp
2296//===----------------------------------------------------------------------===//
2297
2298LogicalResult spirv::ShiftLeftLogicalOp::verify() {
2299 return verifyShiftOp(*this);
2300}
2301
2302//===----------------------------------------------------------------------===//
2303// spirv.ShiftRightArithmeticOp
2304//===----------------------------------------------------------------------===//
2305
2306LogicalResult spirv::ShiftRightArithmeticOp::verify() {
2307 return verifyShiftOp(*this);
2308}
2309
2310//===----------------------------------------------------------------------===//
2311// spirv.ShiftRightLogicalOp
2312//===----------------------------------------------------------------------===//
2313
2314LogicalResult spirv::ShiftRightLogicalOp::verify() {
2315 return verifyShiftOp(*this);
2316}
2317
2318//===----------------------------------------------------------------------===//
2319// spirv.VectorTimesScalarOp
2320//===----------------------------------------------------------------------===//
2321
2322LogicalResult spirv::VectorTimesScalarOp::verify() {
2323 if (getVector().getType() != getType())
2324 return emitOpError("vector operand and result type mismatch");
2325 auto scalarType = cast<VectorType>(getType()).getElementType();
2326 if (getScalar().getType() != scalarType)
2327 return emitOpError("scalar operand and result element type match");
2328 return success();
2329}
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:835
static LogicalResult verifyConstantType(spirv::ConstantOp op, Attribute value, Type opType)
Definition SPIRVOps.cpp:606
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:33
ValueTypeRange< BlockArgListType > getArgumentTypes()
Return a range containing the types of the arguments for this block.
Definition Block.cpp:154
unsigned getNumArguments()
Definition Block.h:152
OpListType & getOperations()
Definition Block.h:161
Operation & front()
Definition Block.h:177
iterator begin()
Definition Block.h:167
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...
void printOperands(const ContainerType &container)
Print a comma separated list of operands.
virtual void printOptionalAttrDictWithKeyword(ArrayRef< NamedAttribute > attrs, ArrayRef< StringRef > elidedAttrs={})=0
If the specified operation has attributes, print out an attribute dictionary prefixed with 'attribute...
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:485
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:375
bool hasTrait()
Returns true if the operation was registered with a particular trait, e.g.
Definition Operation.h:801
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
Definition Operation.h:432
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:553
operand_type_range getOperandTypes()
Definition Operation.h:422
result_type_range getResultTypes()
Definition Operation.h:453
operand_range getOperands()
Returns an iterator on the underlying Value's.
Definition Operation.h:403
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:429
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 allows for representing and managing the symbol table used by operations with the 'SymbolT...
Definition SymbolTable.h:24
static Operation * lookupNearestSymbolFrom(Operation *from, StringAttr symbol)
Returns the operation registered with the given symbol name within the closest parent operation of,...
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:732
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:310
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.
llvm::DenseMap< KeyT, ValueT, KeyInfoT, BucketT > DenseMap
Definition LLVM.h:120
llvm::function_ref< Fn > function_ref
Definition LLVM.h:147
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.