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