MLIR 23.0.0git
SPIRVDialect.cpp
Go to the documentation of this file.
1//===- LLVMDialect.cpp - MLIR SPIR-V dialect ------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM
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 SPIR-V dialect in MLIR.
10//
11//===----------------------------------------------------------------------===//
12
14
15#include "SPIRVParsingUtils.h"
16
22#include "mlir/IR/Builders.h"
25#include "mlir/IR/MLIRContext.h"
26#include "mlir/Parser/Parser.h"
28#include "llvm/ADT/Sequence.h"
29#include "llvm/ADT/StringExtras.h"
30#include "llvm/ADT/TypeSwitch.h"
31
32using namespace mlir;
33using namespace mlir::spirv;
34
35#include "mlir/Dialect/SPIRV/IR/SPIRVOpsDialect.cpp.inc"
36
37//===----------------------------------------------------------------------===//
38// InlinerInterface
39//===----------------------------------------------------------------------===//
40
41/// Returns true if the given region contains spirv.Return or spirv.ReturnValue
42/// ops.
43static inline bool containsReturn(Region &region) {
44 return llvm::any_of(region, [](Block &block) {
45 Operation *terminator = block.getTerminator();
46 return isa<spirv::ReturnOp, spirv::ReturnValueOp>(terminator);
47 });
48}
49
50namespace {
51/// This class defines the interface for inlining within the SPIR-V dialect.
52struct SPIRVInlinerInterface : public DialectInlinerInterface {
53 using DialectInlinerInterface::DialectInlinerInterface;
54
55 /// All call operations within SPIRV can be inlined.
56 bool isLegalToInline(Operation *call, Operation *callable,
57 bool wouldBeCloned) const final {
58 return true;
59 }
60
61 /// Returns true if the given region 'src' can be inlined into the region
62 /// 'dest' that is attached to an operation registered to the current dialect.
63 bool isLegalToInline(Region *dest, Region *src, bool wouldBeCloned,
64 IRMapping &) const final {
65 // Return true here when inlining into spirv.func, spirv.mlir.selection, and
66 // spirv.mlir.loop operations.
67 auto *op = dest->getParentOp();
68 return isa<spirv::FuncOp, spirv::SelectionOp, spirv::LoopOp>(op);
69 }
70
71 /// Returns true if the given operation 'op', that is registered to this
72 /// dialect, can be inlined into the region 'dest' that is attached to an
73 /// operation registered to the current dialect.
74 bool isLegalToInline(Operation *op, Region *dest, bool wouldBeCloned,
75 IRMapping &) const final {
76 // TODO: Enable inlining structured control flows with return.
77 if ((isa<spirv::SelectionOp, spirv::LoopOp>(op)) &&
78 containsReturn(op->getRegion(0)))
79 return false;
80 // TODO: we need to filter OpKill here to avoid inlining it to
81 // a loop continue construct:
82 // https://github.com/KhronosGroup/SPIRV-Headers/issues/86
83 // For now, we just disallow inlining OpKill anywhere in the code,
84 // but this restriction should be relaxed, as pointed above.
85 if (isa<spirv::KillOp>(op))
86 return false;
87
88 return true;
89 }
90
91 /// Handle the given inlined terminator by replacing it with a new operation
92 /// as necessary.
93 void handleTerminator(Operation *op, Block *newDest) const final {
94 if (auto returnOp = dyn_cast<spirv::ReturnOp>(op)) {
95 auto builder = OpBuilder(op);
96 spirv::BranchOp::create(builder, op->getLoc(), newDest);
97 op->erase();
98 } else if (auto retValOp = dyn_cast<spirv::ReturnValueOp>(op)) {
99 auto builder = OpBuilder(op);
100 spirv::BranchOp::create(builder, retValOp->getLoc(), newDest,
101 retValOp->getOperands());
102 op->erase();
103 }
104 }
105
106 /// Handle the given inlined terminator by replacing it with a new operation
107 /// as necessary.
108 void handleTerminator(Operation *op, ValueRange valuesToRepl) const final {
109 // Only spirv.ReturnValue needs to be handled here.
110 auto retValOp = dyn_cast<spirv::ReturnValueOp>(op);
111 if (!retValOp)
112 return;
113
114 // Replace the values directly with the return operands.
115 assert(valuesToRepl.size() == 1 &&
116 "spirv.ReturnValue expected to only handle one result");
117 valuesToRepl.front().replaceAllUsesWith(retValOp.getValue());
118 }
119};
120} // namespace
121
122//===----------------------------------------------------------------------===//
123// SPIR-V Dialect
124//===----------------------------------------------------------------------===//
125
126void SPIRVDialect::initialize() {
127 registerAttributes();
128 registerTypes();
129
130 // Add SPIR-V ops.
131 addOperations<
132#define GET_OP_LIST
133#include "mlir/Dialect/SPIRV/IR/SPIRVOps.cpp.inc"
134 >();
135
136 addInterfaces<SPIRVInlinerInterface>();
137
138 // Allow unknown operations because SPIR-V is extensible.
139 allowUnknownOperations();
140 declarePromisedInterface<gpu::TargetAttrInterface, TargetEnvAttr>();
141}
142
143std::string SPIRVDialect::getAttributeName(Decoration decoration) {
144 return getDecorationString(decoration);
145}
146
147//===----------------------------------------------------------------------===//
148// Type Parsing
149//===----------------------------------------------------------------------===//
150
151// Forward declarations.
152template <typename ValTy>
153static std::optional<ValTy> parseAndVerify(SPIRVDialect const &dialect,
154 DialectAsmParser &parser);
155template <>
156std::optional<Type> parseAndVerify<Type>(SPIRVDialect const &dialect,
157 DialectAsmParser &parser);
158
159template <>
160std::optional<unsigned> parseAndVerify<unsigned>(SPIRVDialect const &dialect,
161 DialectAsmParser &parser);
162
163static Type parseAndVerifyType(SPIRVDialect const &dialect,
164 DialectAsmParser &parser) {
165 Type type;
166 SMLoc typeLoc = parser.getCurrentLocation();
167 if (parser.parseType(type))
168 return Type();
169
170 // Allow SPIR-V dialect types.
171 if (&type.getDialect() == &dialect)
172 return type;
173
174 // Check other allowed types.
175 if (auto t = dyn_cast<FloatType>(type)) {
176 if (!ScalarType::isValid(t)) {
177 parser.emitError(typeLoc,
178 "only 8/16/32/64-bit float type allowed but found ")
179 << type;
180 return Type();
181 }
182 } else if (auto t = dyn_cast<IntegerType>(type)) {
183 if (!ScalarType::isValid(t)) {
184 parser.emitError(typeLoc,
185 "only 1/8/16/32/64-bit integer type allowed but found ")
186 << type;
187 return Type();
188 }
189 } else if (auto t = dyn_cast<VectorType>(type)) {
190 if (t.getRank() != 1) {
191 parser.emitError(typeLoc, "only 1-D vector allowed but found ") << t;
192 return Type();
193 }
194 if (t.getNumElements() < 2) {
195 parser.emitError(typeLoc, "SPIR-V does not allow one-element vectors");
196 return Type();
197 }
198 if (t.getNumElements() > 4) {
199 parser.emitError(
200 typeLoc, "vector length has to be less than or equal to 4 but found ")
201 << t.getNumElements();
202 return Type();
203 }
204 if (!isa<ScalarType>(t.getElementType())) {
205 parser.emitError(
206 typeLoc,
207 "vector element type must be a SPIR-V scalar type but found ")
208 << t.getElementType();
209 return Type();
210 }
211 } else if (auto t = dyn_cast<TensorArmType>(type)) {
212 if (!isa<ScalarType>(t.getElementType())) {
213 parser.emitError(
214 typeLoc, "only scalar element type allowed in tensor type but found ")
215 << t.getElementType();
216 return Type();
217 }
218 } else {
219 parser.emitError(typeLoc, "cannot use ")
220 << type << " to compose SPIR-V types";
221 return Type();
222 }
223
224 return type;
225}
226
227static Type parseAndVerifyMatrixType(SPIRVDialect const &dialect,
228 DialectAsmParser &parser) {
229 Type type;
230 SMLoc typeLoc = parser.getCurrentLocation();
231 if (parser.parseType(type))
232 return Type();
233
234 if (auto t = dyn_cast<VectorType>(type)) {
235 if (t.getRank() != 1) {
236 parser.emitError(typeLoc, "only 1-D vector allowed but found ") << t;
237 return Type();
238 }
239 if (t.getNumElements() > 4 || t.getNumElements() < 2) {
240 parser.emitError(typeLoc,
241 "matrix columns size has to be less than or equal "
242 "to 4 and greater than or equal 2, but found ")
243 << t.getNumElements();
244 return Type();
245 }
246
247 if (!isa<FloatType>(t.getElementType())) {
248 parser.emitError(typeLoc, "matrix columns' elements must be of "
249 "Float type, got ")
250 << t.getElementType();
251 return Type();
252 }
253 } else {
254 parser.emitError(typeLoc, "matrix must be composed using vector "
255 "type, got ")
256 << type;
257 return Type();
258 }
259
260 return type;
261}
262
263static Type parseAndVerifySampledImageType(SPIRVDialect const &dialect,
264 DialectAsmParser &parser) {
265 Type type;
266 SMLoc typeLoc = parser.getCurrentLocation();
267 if (parser.parseType(type))
268 return Type();
269
270 auto imageType = dyn_cast<ImageType>(type);
271 if (!imageType) {
272 parser.emitError(typeLoc,
273 "sampled image must be composed using image type, got ")
274 << type;
275 return Type();
276 }
277
278 if (llvm::is_contained({Dim::SubpassData, Dim::Buffer}, imageType.getDim())) {
279 parser.emitError(
280 typeLoc, "sampled image Dim must not be SubpassData or Buffer, got ")
281 << stringifyDim(imageType.getDim());
282 return Type();
283 }
284
285 return type;
286}
287
288/// Parses an optional `, stride = N` assembly segment. If no parsing failure
289/// occurs, writes `N` to `stride` if existing and writes 0 to `stride` if
290/// missing.
291static LogicalResult parseOptionalArrayStride(const SPIRVDialect &dialect,
292 DialectAsmParser &parser,
293 unsigned &stride) {
294 if (failed(parser.parseOptionalComma())) {
295 stride = 0;
296 return success();
297 }
298
299 if (parser.parseKeyword("stride") || parser.parseEqual())
300 return failure();
301
302 SMLoc strideLoc = parser.getCurrentLocation();
303 std::optional<unsigned> optStride = parseAndVerify<unsigned>(dialect, parser);
304 if (!optStride)
305 return failure();
306
307 if (!(stride = *optStride)) {
308 parser.emitError(strideLoc, "ArrayStride must be greater than zero");
309 return failure();
310 }
311 return success();
312}
313
314// element-type ::= integer-type
315// | floating-point-type
316// | vector-type
317// | spirv-type
318//
319// array-type ::= `!spirv.array` `<` integer-literal `x` element-type
320// (`,` `stride` `=` integer-literal)? `>`
321static Type parseArrayType(SPIRVDialect const &dialect,
322 DialectAsmParser &parser) {
323 if (parser.parseLess())
324 return Type();
325
326 SmallVector<int64_t, 1> countDims;
327 SMLoc countLoc = parser.getCurrentLocation();
328 if (parser.parseDimensionList(countDims, /*allowDynamic=*/false))
329 return Type();
330 if (countDims.size() != 1) {
331 parser.emitError(countLoc,
332 "expected single integer for array element count");
333 return Type();
334 }
335
336 // According to the SPIR-V spec:
337 // "Length is the number of elements in the array. It must be at least 1."
338 int64_t count = countDims[0];
339 if (count == 0) {
340 parser.emitError(countLoc, "expected array length greater than 0");
341 return Type();
342 }
343
344 Type elementType = parseAndVerifyType(dialect, parser);
345 if (!elementType)
346 return Type();
347
348 unsigned stride = 0;
349 if (failed(parseOptionalArrayStride(dialect, parser, stride)))
350 return Type();
351
352 if (parser.parseGreater())
353 return Type();
354 return ArrayType::get(elementType, count, stride);
355}
356
357// cooperative-matrix-type ::=
358// `!spirv.coopmatrix` `<` rows `x` columns `x` element-type `,`
359// scope `,` use `>`
360static Type parseCooperativeMatrixType(SPIRVDialect const &dialect,
361 DialectAsmParser &parser) {
362 if (parser.parseLess())
363 return {};
364
366 SMLoc countLoc = parser.getCurrentLocation();
367 if (parser.parseDimensionList(dims, /*allowDynamic=*/false))
368 return {};
369
370 if (dims.size() != 2) {
371 parser.emitError(countLoc, "expected row and column count");
372 return {};
373 }
374
375 auto elementTy = parseAndVerifyType(dialect, parser);
376 if (!elementTy)
377 return {};
378
379 Scope scope;
380 if (parser.parseComma() ||
381 spirv::parseEnumKeywordAttr(scope, parser, "scope <id>"))
382 return {};
383
384 CooperativeMatrixUseKHR use;
385 if (parser.parseComma() ||
386 spirv::parseEnumKeywordAttr(use, parser, "use <id>"))
387 return {};
388
389 if (parser.parseGreater())
390 return {};
391
392 return CooperativeMatrixType::get(elementTy, dims[0], dims[1], scope, use);
393}
394
395// tensor-arm-type ::=
396// `!spirv.arm.tensor` `<` dim0 `x` dim1 `x` ... `x` dimN `x` element-type`>`
397static Type parseTensorArmType(SPIRVDialect const &dialect,
398 DialectAsmParser &parser) {
399 if (parser.parseLess())
400 return {};
401
402 bool unranked = false;
404 SMLoc countLoc = parser.getCurrentLocation();
405
406 if (parser.parseOptionalStar().succeeded()) {
407 unranked = true;
408 if (parser.parseXInDimensionList())
409 return {};
410 } else if (parser.parseDimensionList(dims, /*allowDynamic=*/true)) {
411 return {};
412 }
413
414 if (!unranked && dims.empty()) {
415 parser.emitError(countLoc, "arm.tensors do not support rank zero");
416 return {};
417 }
418
419 if (llvm::is_contained(dims, 0)) {
420 parser.emitError(countLoc, "arm.tensors do not support zero dimensions");
421 return {};
422 }
423
424 if (llvm::any_of(dims, [](int64_t dim) { return dim < 0; }) &&
425 llvm::any_of(dims, [](int64_t dim) { return dim > 0; })) {
426 parser.emitError(countLoc, "arm.tensor shape dimensions must be either "
427 "fully dynamic or completed shaped");
428 return {};
429 }
430
431 auto elementTy = parseAndVerifyType(dialect, parser);
432 if (!elementTy)
433 return {};
434
435 if (parser.parseGreater())
436 return {};
437
438 return TensorArmType::get(dims, elementTy);
439}
440
441// TODO: Reorder methods to be utilities first and parse*Type
442// methods in alphabetical order
443//
444// storage-class ::= `UniformConstant`
445// | `Uniform`
446// | `Workgroup`
447// | <and other storage classes...>
448//
449// pointer-type ::= `!spirv.ptr<` element-type `,` storage-class `>`
450static Type parsePointerType(SPIRVDialect const &dialect,
451 DialectAsmParser &parser) {
452 if (parser.parseLess())
453 return Type();
454
455 auto pointeeType = parseAndVerifyType(dialect, parser);
456 if (!pointeeType)
457 return Type();
458
459 StringRef storageClassSpec;
460 SMLoc storageClassLoc = parser.getCurrentLocation();
461 if (parser.parseComma() || parser.parseKeyword(&storageClassSpec))
462 return Type();
463
464 auto storageClass = symbolizeStorageClass(storageClassSpec);
465 if (!storageClass) {
466 parser.emitError(storageClassLoc, "unknown storage class: ")
467 << storageClassSpec;
468 return Type();
469 }
470 if (parser.parseGreater())
471 return Type();
472 return PointerType::get(pointeeType, *storageClass);
473}
474
475// runtime-array-type ::= `!spirv.rtarray` `<` element-type
476// (`,` `stride` `=` integer-literal)? `>`
477static Type parseRuntimeArrayType(SPIRVDialect const &dialect,
478 DialectAsmParser &parser) {
479 if (parser.parseLess())
480 return Type();
481
482 Type elementType = parseAndVerifyType(dialect, parser);
483 if (!elementType)
484 return Type();
485
486 unsigned stride = 0;
487 if (failed(parseOptionalArrayStride(dialect, parser, stride)))
488 return Type();
489
490 if (parser.parseGreater())
491 return Type();
492 return RuntimeArrayType::get(elementType, stride);
493}
494
495// matrix-type ::= `!spirv.matrix` `<` integer-literal `x` element-type `>`
496static Type parseMatrixType(SPIRVDialect const &dialect,
497 DialectAsmParser &parser) {
498 if (parser.parseLess())
499 return Type();
500
501 SmallVector<int64_t, 1> countDims;
502 SMLoc countLoc = parser.getCurrentLocation();
503 if (parser.parseDimensionList(countDims, /*allowDynamic=*/false))
504 return Type();
505 if (countDims.size() != 1) {
506 parser.emitError(countLoc, "expected single unsigned "
507 "integer for number of columns");
508 return Type();
509 }
510
511 int64_t columnCount = countDims[0];
512 // According to the specification, Matrices can have 2, 3, or 4 columns
513 if (columnCount < 2 || columnCount > 4) {
514 parser.emitError(countLoc, "matrix is expected to have 2, 3, or 4 "
515 "columns");
516 return Type();
517 }
518
519 Type columnType = parseAndVerifyMatrixType(dialect, parser);
520 if (!columnType)
521 return Type();
522
523 if (parser.parseGreater())
524 return Type();
525
526 return MatrixType::get(columnType, columnCount);
527}
528
529// Specialize this function to parse each of the parameters that define an
530// ImageType. By default it assumes this is an enum type.
531template <typename ValTy>
532static std::optional<ValTy> parseAndVerify(SPIRVDialect const &dialect,
533 DialectAsmParser &parser) {
534 StringRef enumSpec;
535 SMLoc enumLoc = parser.getCurrentLocation();
536 if (parser.parseKeyword(&enumSpec)) {
537 return std::nullopt;
538 }
539
540 auto val = spirv::symbolizeEnum<ValTy>(enumSpec);
541 if (!val)
542 parser.emitError(enumLoc, "unknown attribute: '") << enumSpec << "'";
543 return val;
544}
545
546template <>
547std::optional<Type> parseAndVerify<Type>(SPIRVDialect const &dialect,
548 DialectAsmParser &parser) {
549 // TODO: Further verify that the element type can be sampled
550 auto ty = parseAndVerifyType(dialect, parser);
551 if (!ty)
552 return std::nullopt;
553 return ty;
554}
555
556template <typename IntTy>
557static std::optional<IntTy> parseAndVerifyInteger(SPIRVDialect const &dialect,
558 DialectAsmParser &parser) {
559 IntTy offsetVal = std::numeric_limits<IntTy>::max();
560 if (parser.parseInteger(offsetVal))
561 return std::nullopt;
562 return offsetVal;
563}
564
565template <>
566std::optional<unsigned> parseAndVerify<unsigned>(SPIRVDialect const &dialect,
567 DialectAsmParser &parser) {
568 return parseAndVerifyInteger<unsigned>(dialect, parser);
569}
570
571namespace {
572// Functor object to parse a comma separated list of specs. The function
573// parseAndVerify does the actual parsing and verification of individual
574// elements. This is a functor since parsing the last element of the list
575// (termination condition) needs partial specialization.
576template <typename ParseType, typename... Args>
577struct ParseCommaSeparatedList {
578 std::optional<std::tuple<ParseType, Args...>>
579 operator()(SPIRVDialect const &dialect, DialectAsmParser &parser) const {
580 auto parseVal = parseAndVerify<ParseType>(dialect, parser);
581 if (!parseVal)
582 return std::nullopt;
583
584 auto numArgs = std::tuple_size<std::tuple<Args...>>::value;
585 if (numArgs != 0 && failed(parser.parseComma()))
586 return std::nullopt;
587 auto remainingValues = ParseCommaSeparatedList<Args...>{}(dialect, parser);
588 if (!remainingValues)
589 return std::nullopt;
590 return std::tuple_cat(std::tuple<ParseType>(parseVal.value()),
591 remainingValues.value());
592 }
593};
594
595// Partial specialization of the function to parse a comma separated list of
596// specs to parse the last element of the list.
597template <typename ParseType>
598struct ParseCommaSeparatedList<ParseType> {
599 std::optional<std::tuple<ParseType>>
600 operator()(SPIRVDialect const &dialect, DialectAsmParser &parser) const {
601 if (auto value = parseAndVerify<ParseType>(dialect, parser))
602 return std::tuple<ParseType>(*value);
603 return std::nullopt;
604 }
605};
606} // namespace
607
608// dim ::= `1D` | `2D` | `3D` | `Cube` | <and other SPIR-V Dim specifiers...>
609//
610// depth-info ::= `NoDepth` | `IsDepth` | `DepthUnknown`
611//
612// arrayed-info ::= `NonArrayed` | `Arrayed`
613//
614// sampling-info ::= `SingleSampled` | `MultiSampled`
615//
616// sampler-use-info ::= `SamplerUnknown` | `NeedSampler` | `NoSampler`
617//
618// format ::= `Unknown` | `Rgba32f` | <and other SPIR-V Image formats...>
619//
620// image-type ::= `!spirv.image<` element-type `,` dim `,` depth-info `,`
621// arrayed-info `,` sampling-info `,`
622// sampler-use-info `,` format `>`
623static Type parseImageType(SPIRVDialect const &dialect,
624 DialectAsmParser &parser) {
625 if (parser.parseLess())
626 return Type();
627
628 auto value =
629 ParseCommaSeparatedList<Type, Dim, ImageDepthInfo, ImageArrayedInfo,
630 ImageSamplingInfo, ImageSamplerUseInfo,
631 ImageFormat>{}(dialect, parser);
632 if (!value)
633 return Type();
634
635 if (parser.parseGreater())
636 return Type();
637 return ImageType::get(*value);
638}
639
640// sampledImage-type :: = `!spirv.sampledImage<` image-type `>`
641static Type parseSampledImageType(SPIRVDialect const &dialect,
642 DialectAsmParser &parser) {
643 if (parser.parseLess())
644 return Type();
645
646 Type parsedType = parseAndVerifySampledImageType(dialect, parser);
647 if (!parsedType)
648 return Type();
649
650 if (parser.parseGreater())
651 return Type();
652 return SampledImageType::get(parsedType);
653}
654
655// Parse decorations associated with a member.
657 SPIRVDialect const &dialect, DialectAsmParser &parser,
658 ArrayRef<Type> memberTypes,
661
662 // Check if the first element is offset.
663 SMLoc offsetLoc = parser.getCurrentLocation();
664 StructType::OffsetInfo offset = 0;
665 OptionalParseResult offsetParseResult = parser.parseOptionalInteger(offset);
666 if (offsetParseResult.has_value()) {
667 if (failed(*offsetParseResult))
668 return failure();
669
670 if (offsetInfo.size() != memberTypes.size() - 1) {
671 return parser.emitError(offsetLoc,
672 "offset specification must be given for "
673 "all members");
674 }
675 offsetInfo.push_back(offset);
676 }
677
678 // Check for no spirv::Decorations.
679 if (succeeded(parser.parseOptionalRSquare()))
680 return success();
681
682 // If there was an offset, make sure to parse the comma.
683 if (offsetParseResult.has_value() && parser.parseComma())
684 return failure();
685
686 // Check for spirv::Decorations.
687 auto parseDecorations = [&]() {
688 auto memberDecoration = parseAndVerify<spirv::Decoration>(dialect, parser);
689 if (!memberDecoration)
690 return failure();
691
692 // Parse member decoration value if it exists.
693 if (succeeded(parser.parseOptionalEqual())) {
694 Attribute memberDecorationValue;
695 if (failed(parser.parseAttribute(memberDecorationValue)))
696 return failure();
697
698 memberDecorationInfo.emplace_back(
699 static_cast<uint32_t>(memberTypes.size() - 1),
700 memberDecoration.value(), memberDecorationValue);
701 } else {
702 memberDecorationInfo.emplace_back(
703 static_cast<uint32_t>(memberTypes.size() - 1),
704 memberDecoration.value(), UnitAttr::get(dialect.getContext()));
705 }
706 return success();
707 };
708 if (failed(parser.parseCommaSeparatedList(parseDecorations)) ||
709 failed(parser.parseRSquare()))
710 return failure();
711
712 return success();
713}
714
715// struct-member-decoration ::= integer-literal? spirv-decoration*
716// struct-type ::=
717// `!spirv.struct<` (id `,`)?
718// `(`
719// (spirv-type (`[` struct-member-decoration `]`)?)*
720// `)`
721// (`,` struct-decoration)?
722// `>`
723static Type parseStructType(SPIRVDialect const &dialect,
724 DialectAsmParser &parser) {
725 // TODO: This function is quite lengthy. Break it down into smaller chunks.
726
727 if (parser.parseLess())
728 return Type();
729
730 StringRef identifier;
731 FailureOr<DialectAsmParser::CyclicParseReset> cyclicParse;
732
733 // Check if this is an identified struct type.
734 if (succeeded(parser.parseOptionalKeyword(&identifier))) {
735 // Check if this is a possible recursive reference.
736 auto structType =
737 StructType::getIdentified(dialect.getContext(), identifier);
738 cyclicParse = parser.tryStartCyclicParse(structType);
739 if (succeeded(parser.parseOptionalGreater())) {
740 if (succeeded(cyclicParse)) {
741 parser.emitError(
742 parser.getNameLoc(),
743 "recursive struct reference not nested in struct definition");
744
745 return Type();
746 }
747
748 return structType;
749 }
750
751 if (failed(parser.parseComma()))
752 return Type();
753
754 if (failed(cyclicParse)) {
755 parser.emitError(parser.getNameLoc(),
756 "identifier already used for an enclosing struct");
757 return Type();
758 }
759 }
760
761 if (failed(parser.parseLParen()))
762 return Type();
763
764 if (succeeded(parser.parseOptionalRParen()) &&
765 succeeded(parser.parseOptionalGreater())) {
766 return StructType::getEmpty(dialect.getContext(), identifier);
767 }
768
769 StructType idStructTy;
770
771 if (!identifier.empty())
772 idStructTy = StructType::getIdentified(dialect.getContext(), identifier);
773
774 SmallVector<Type, 4> memberTypes;
777
778 do {
779 Type memberType;
780 if (parser.parseType(memberType))
781 return Type();
782 if (!isa<SPIRVType>(memberType)) {
783 parser.emitError(parser.getNameLoc(),
784 "member type must be a valid SPIR-V type");
785 return Type();
786 }
787 memberTypes.push_back(memberType);
788
789 if (succeeded(parser.parseOptionalLSquare()))
790 if (parseStructMemberDecorations(dialect, parser, memberTypes, offsetInfo,
791 memberDecorationInfo))
792 return Type();
793 } while (succeeded(parser.parseOptionalComma()));
794
795 if (!offsetInfo.empty() && memberTypes.size() != offsetInfo.size()) {
796 parser.emitError(parser.getNameLoc(),
797 "offset specification must be given for all members");
798 return Type();
799 }
800
801 if (failed(parser.parseRParen()))
802 return Type();
803
805
806 auto parseStructDecoration = [&]() {
807 std::optional<spirv::Decoration> decoration =
808 parseAndVerify<spirv::Decoration>(dialect, parser);
809 if (!decoration)
810 return failure();
811
812 // Parse decoration value if it exists.
813 if (succeeded(parser.parseOptionalEqual())) {
814 Attribute decorationValue;
815 if (failed(parser.parseAttribute(decorationValue)))
816 return failure();
817
818 structDecorationInfo.emplace_back(decoration.value(), decorationValue);
819 } else {
820 structDecorationInfo.emplace_back(decoration.value(),
821 UnitAttr::get(dialect.getContext()));
822 }
823 return success();
824 };
825
826 while (succeeded(parser.parseOptionalComma()))
827 if (failed(parseStructDecoration()))
828 return Type();
829
830 if (failed(parser.parseGreater()))
831 return Type();
832
833 if (!identifier.empty()) {
834 if (failed(idStructTy.trySetBody(memberTypes, offsetInfo,
835 memberDecorationInfo,
836 structDecorationInfo)))
837 return Type();
838 return idStructTy;
839 }
840
841 return StructType::get(memberTypes, offsetInfo, memberDecorationInfo,
842 structDecorationInfo);
843}
844
845// spirv-type ::= array-type
846// | element-type
847// | image-type
848// | pointer-type
849// | runtime-array-type
850// | sampled-image-type
851// | struct-type
852Type SPIRVDialect::parseType(DialectAsmParser &parser) const {
853 StringRef keyword;
854 if (parser.parseKeyword(&keyword))
855 return Type();
856
857 if (keyword == "array")
858 return parseArrayType(*this, parser);
859 if (keyword == "coopmatrix")
860 return parseCooperativeMatrixType(*this, parser);
861 if (keyword == "image")
862 return parseImageType(*this, parser);
863 if (keyword == "ptr")
864 return parsePointerType(*this, parser);
865 if (keyword == "rtarray")
866 return parseRuntimeArrayType(*this, parser);
867 if (keyword == "sampled_image")
868 return parseSampledImageType(*this, parser);
869 if (keyword == "sampler")
871 if (keyword == "named_barrier")
873 if (keyword == "struct")
874 return parseStructType(*this, parser);
875 if (keyword == "matrix")
876 return parseMatrixType(*this, parser);
877 if (keyword == "arm.tensor")
878 return parseTensorArmType(*this, parser);
879 parser.emitError(parser.getNameLoc(), "unknown SPIR-V type: ") << keyword;
880 return Type();
881}
882
883//===----------------------------------------------------------------------===//
884// Type Printing
885//===----------------------------------------------------------------------===//
886
887static void print(ArrayType type, DialectAsmPrinter &os) {
888 os << "array<" << type.getNumElements() << " x " << type.getElementType();
889 if (unsigned stride = type.getArrayStride())
890 os << ", stride=" << stride;
891 os << ">";
892}
893
895 os << "rtarray<" << type.getElementType();
896 if (unsigned stride = type.getArrayStride())
897 os << ", stride=" << stride;
898 os << ">";
899}
900
901static void print(PointerType type, DialectAsmPrinter &os) {
902 os << "ptr<" << type.getPointeeType() << ", "
903 << stringifyStorageClass(type.getStorageClass()) << ">";
904}
905
906static void print(ImageType type, DialectAsmPrinter &os) {
907 os << "image<" << type.getElementType() << ", " << stringifyDim(type.getDim())
908 << ", " << stringifyImageDepthInfo(type.getDepthInfo()) << ", "
909 << stringifyImageArrayedInfo(type.getArrayedInfo()) << ", "
910 << stringifyImageSamplingInfo(type.getSamplingInfo()) << ", "
911 << stringifyImageSamplerUseInfo(type.getSamplerUseInfo()) << ", "
912 << stringifyImageFormat(type.getImageFormat()) << ">";
913}
914
916 os << "sampled_image<" << type.getImageType() << ">";
917}
918
919static void print(SamplerType type, DialectAsmPrinter &os) { os << "sampler"; }
920
922 os << "named_barrier";
923}
924
925static void print(StructType type, DialectAsmPrinter &os) {
926 FailureOr<AsmPrinter::CyclicPrintReset> cyclicPrint;
927
928 os << "struct<";
929
930 if (type.isIdentified()) {
931 os << type.getIdentifier();
932
933 cyclicPrint = os.tryStartCyclicPrint(type);
934 if (failed(cyclicPrint)) {
935 os << ">";
936 return;
937 }
938
939 os << ", ";
940 }
941
942 os << "(";
943
944 auto printMember = [&](unsigned i) {
945 os << type.getElementType(i);
947 type.getMemberDecorations(i, decorations);
948 if (type.hasOffset() || !decorations.empty()) {
949 os << " [";
950 if (type.hasOffset()) {
951 os << type.getMemberOffset(i);
952 if (!decorations.empty())
953 os << ", ";
954 }
955 auto eachFn = [&os](spirv::StructType::MemberDecorationInfo decoration) {
956 os << stringifyDecoration(decoration.decoration);
957 if (decoration.hasValue()) {
958 os << "=";
959 os.printAttributeWithoutType(decoration.decorationValue);
960 }
961 };
962 llvm::interleaveComma(decorations, os, eachFn);
963 os << "]";
964 }
965 };
966 llvm::interleaveComma(llvm::seq<unsigned>(0, type.getNumElements()), os,
967 printMember);
968 os << ")";
969
971 type.getStructDecorations(decorations);
972 if (!decorations.empty()) {
973 os << ", ";
974 auto eachFn = [&os](spirv::StructType::StructDecorationInfo decoration) {
975 os << stringifyDecoration(decoration.decoration);
976 if (decoration.hasValue()) {
977 os << "=";
978 os.printAttributeWithoutType(decoration.decorationValue);
979 }
980 };
981 llvm::interleaveComma(decorations, os, eachFn);
982 }
983
984 os << ">";
985}
986
988 os << "coopmatrix<" << type.getRows() << "x" << type.getColumns() << "x"
989 << type.getElementType() << ", " << type.getScope() << ", "
990 << type.getUse() << ">";
991}
992
993static void print(MatrixType type, DialectAsmPrinter &os) {
994 os << "matrix<" << type.getNumColumns() << " x " << type.getColumnType();
995 os << ">";
996}
997
998static void print(TensorArmType type, DialectAsmPrinter &os) {
999 os << "arm.tensor<";
1000
1001 llvm::interleave(
1002 type.getShape(), os,
1003 [&](int64_t dim) {
1004 if (ShapedType::isDynamic(dim))
1005 os << '?';
1006 else
1007 os << dim;
1008 },
1009 "x");
1010 if (!type.hasRank()) {
1011 os << "*";
1012 }
1013 os << "x" << type.getElementType() << ">";
1014}
1015
1016void SPIRVDialect::printType(Type type, DialectAsmPrinter &os) const {
1017 TypeSwitch<Type>(type)
1021 [&](auto type) { print(type, os); })
1022 .DefaultUnreachable("Unhandled SPIR-V type");
1023}
1024
1025//===----------------------------------------------------------------------===//
1026// Constant
1027//===----------------------------------------------------------------------===//
1028
1029Operation *SPIRVDialect::materializeConstant(OpBuilder &builder,
1030 Attribute value, Type type,
1031 Location loc) {
1032 if (auto poison = dyn_cast<ub::PoisonAttr>(value))
1033 return ub::PoisonOp::create(builder, loc, type, poison);
1034
1035 if (!spirv::ConstantOp::isBuildableWith(type))
1036 return nullptr;
1037
1038 return spirv::ConstantOp::create(builder, loc, type, value);
1039}
1040
1041//===----------------------------------------------------------------------===//
1042// Shader Interface ABI
1043//===----------------------------------------------------------------------===//
1044
1045LogicalResult SPIRVDialect::verifyOperationAttribute(Operation *op,
1046 NamedAttribute attribute) {
1047 StringRef symbol = attribute.getName().strref();
1048 Attribute attr = attribute.getValue();
1049
1050 if (symbol == spirv::getEntryPointABIAttrName()) {
1051 if (!isa<spirv::EntryPointABIAttr>(attr)) {
1052 return op->emitError("'")
1053 << symbol << "' attribute must be an entry point ABI attribute";
1054 }
1055 } else if (symbol == spirv::getTargetEnvAttrName()) {
1056 if (!isa<spirv::TargetEnvAttr>(attr))
1057 return op->emitError("'") << symbol << "' must be a spirv::TargetEnvAttr";
1058 } else if (symbol == spirv::getLoopControlAttrName()) {
1059 if (!isa<spirv::LoopControlAttr>(attr))
1060 return op->emitError("'")
1061 << symbol << "' must be a spirv::LoopControlAttr";
1062 } else if (symbol == spirv::getSelectionControlAttrName()) {
1063 if (!isa<spirv::SelectionControlAttr>(attr))
1064 return op->emitError("'")
1065 << symbol << "' must be a spirv::SelectionControlAttr";
1066 } else {
1067 return op->emitError("found unsupported '")
1068 << symbol << "' attribute on operation";
1069 }
1070
1071 return success();
1072}
1073
1074/// Verifies the given SPIR-V `attribute` attached to a value of the given
1075/// `valueType` is valid.
1076static LogicalResult verifyRegionAttribute(Location loc, Type valueType,
1077 NamedAttribute attribute) {
1078 StringRef symbol = attribute.getName().strref();
1079 Attribute attr = attribute.getValue();
1080
1081 if (symbol == spirv::getInterfaceVarABIAttrName()) {
1082 auto varABIAttr = dyn_cast<spirv::InterfaceVarABIAttr>(attr);
1083 if (!varABIAttr)
1084 return emitError(loc, "'")
1085 << symbol << "' must be a spirv::InterfaceVarABIAttr";
1086
1087 if (varABIAttr.getStorageClass() && !valueType.isIntOrIndexOrFloat())
1088 return emitError(loc, "'") << symbol
1089 << "' attribute cannot specify storage class "
1090 "when attaching to a non-scalar value";
1091 return success();
1092 }
1093 if (symbol == spirv::DecorationAttr::name) {
1094 if (!isa<spirv::DecorationAttr>(attr))
1095 return emitError(loc, "'")
1096 << symbol << "' must be a spirv::DecorationAttr";
1097 return success();
1098 }
1099
1100 return emitError(loc, "found unsupported '")
1101 << symbol << "' attribute on region argument";
1102}
1103
1104LogicalResult SPIRVDialect::verifyRegionArgAttribute(Operation *op,
1105 unsigned regionIndex,
1106 unsigned argIndex,
1107 NamedAttribute attribute) {
1108 auto funcOp = dyn_cast<FunctionOpInterface>(op);
1109 if (!funcOp)
1110 return success();
1111 Type argType = funcOp.getArgumentTypes()[argIndex];
1112
1113 return verifyRegionAttribute(op->getLoc(), argType, attribute);
1114}
1115
1116LogicalResult SPIRVDialect::verifyRegionResultAttribute(
1117 Operation *op, unsigned /*regionIndex*/, unsigned resultIndex,
1118 NamedAttribute attribute) {
1119 if (auto graphOp = dyn_cast<spirv::GraphARMOp>(op))
1120 return verifyRegionAttribute(
1121 op->getLoc(), graphOp.getResultTypes()[resultIndex], attribute);
1122 return op->emitError(
1123 "cannot attach SPIR-V attributes to region result which is "
1124 "not part of a spirv::GraphARMOp type");
1125}
return success()
static bool isLegalToInline(InlinerInterface &interface, Region *src, Region *insertRegion, bool shouldCloneInlinedRegion, IRMapping &valueMapping)
Utility to check that all of the operations within 'src' can be inlined.
b getContext())
std::optional< unsigned > parseAndVerify< unsigned >(SPIRVDialect const &dialect, DialectAsmParser &parser)
static std::optional< IntTy > parseAndVerifyInteger(SPIRVDialect const &dialect, DialectAsmParser &parser)
static LogicalResult parseOptionalArrayStride(const SPIRVDialect &dialect, DialectAsmParser &parser, unsigned &stride)
Parses an optional , stride = N assembly segment.
static LogicalResult verifyRegionAttribute(Location loc, Type valueType, NamedAttribute attribute)
Verifies the given SPIR-V attribute attached to a value of the given valueType is valid.
static Type parseTensorArmType(SPIRVDialect const &dialect, DialectAsmParser &parser)
static void print(ArrayType type, DialectAsmPrinter &os)
static Type parseSampledImageType(SPIRVDialect const &dialect, DialectAsmParser &parser)
static Type parseAndVerifyType(SPIRVDialect const &dialect, DialectAsmParser &parser)
static ParseResult parseStructMemberDecorations(SPIRVDialect const &dialect, DialectAsmParser &parser, ArrayRef< Type > memberTypes, SmallVectorImpl< StructType::OffsetInfo > &offsetInfo, SmallVectorImpl< StructType::MemberDecorationInfo > &memberDecorationInfo)
static Type parseAndVerifySampledImageType(SPIRVDialect const &dialect, DialectAsmParser &parser)
static Type parseCooperativeMatrixType(SPIRVDialect const &dialect, DialectAsmParser &parser)
std::optional< Type > parseAndVerify< Type >(SPIRVDialect const &dialect, DialectAsmParser &parser)
static Type parseAndVerifyMatrixType(SPIRVDialect const &dialect, DialectAsmParser &parser)
static Type parseArrayType(SPIRVDialect const &dialect, DialectAsmParser &parser)
static bool containsReturn(Region &region)
Returns true if the given region contains spirv.Return or spirv.ReturnValue ops.
static Type parseStructType(SPIRVDialect const &dialect, DialectAsmParser &parser)
static Type parseRuntimeArrayType(SPIRVDialect const &dialect, DialectAsmParser &parser)
static Type parseMatrixType(SPIRVDialect const &dialect, DialectAsmParser &parser)
static Type parseImageType(SPIRVDialect const &dialect, DialectAsmParser &parser)
static std::optional< ValTy > parseAndVerify(SPIRVDialect const &dialect, DialectAsmParser &parser)
static Type parsePointerType(SPIRVDialect const &dialect, DialectAsmParser &parser)
virtual OptionalParseResult parseOptionalInteger(APInt &result)=0
Parse an optional integer value from the stream.
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 parseOptionalEqual()=0
Parse a = token if present.
virtual ParseResult parseOptionalKeyword(StringRef keyword)=0
Parse the given keyword if present.
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 parseRSquare()=0
Parse a ] token.
ParseResult parseInteger(IntT &result)
Parse an integer value from the stream.
virtual ParseResult parseOptionalRParen()=0
Parse a ) token if present.
virtual ParseResult parseLess()=0
Parse a '<' token.
virtual ParseResult parseDimensionList(SmallVectorImpl< int64_t > &dimensions, bool allowDynamic=true, bool withTrailingX=true)=0
Parse a dimension list of a tensor or memref type.
virtual ParseResult parseOptionalGreater()=0
Parse a '>' token if present.
virtual ParseResult parseEqual()=0
Parse a = token.
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 SMLoc getNameLoc() const =0
Return the location of the original name token.
virtual ParseResult parseOptionalStar()=0
Parse a '*' token if present.
FailureOr< CyclicParseReset > tryStartCyclicParse(AttrOrTypeT attrOrType)
Attempts to start a cyclic parsing region for attrOrType.
virtual ParseResult parseOptionalRSquare()=0
Parse a ] token if present.
virtual ParseResult parseGreater()=0
Parse a '>' token.
virtual ParseResult parseLParen()=0
Parse a ( token.
virtual ParseResult parseType(Type &result)=0
Parse a type.
virtual ParseResult parseComma()=0
Parse a , token.
ParseResult parseKeyword(StringRef keyword)
Parse a given keyword.
virtual ParseResult parseOptionalLSquare()=0
Parse a [ token if present.
virtual ParseResult parseAttribute(Attribute &result, Type type={})=0
Parse an arbitrary attribute of a given type and return it in result.
virtual ParseResult parseXInDimensionList()=0
Parse an 'x' token in a dimension list, handling the case where the x is juxtaposed with an element t...
virtual void printAttributeWithoutType(Attribute attr)
Print the given attribute without its type.
FailureOr< CyclicPrintReset > tryStartCyclicPrint(AttrOrTypeT attrOrType)
Attempts to start a cyclic printing region for attrOrType.
Attributes are known-constant values of operations.
Definition Attributes.h:25
Block represents an ordered list of Operations.
Definition Block.h:33
Operation * getTerminator()
Get the terminator operation of this block.
Definition Block.cpp:249
The DialectAsmParser has methods for interacting with the asm parser when parsing attributes and type...
This is a pure-virtual base class that exposes the asmprinter hooks necessary to implement a custom p...
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition Location.h:76
NamedAttribute represents a combination of a name and an Attribute value.
Definition Attributes.h:164
StringAttr getName() const
Return the name of the attribute.
Attribute getValue() const
Return the value of the attribute.
Definition Attributes.h:179
This class helps build Operations.
Definition Builders.h:209
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
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...
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
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
bool isIntOrIndexOrFloat() const
Return true if this is an integer (of any signedness), index, or float type.
Definition Types.cpp:122
Type getElementType() const
unsigned getArrayStride() const
Returns the array stride in bytes.
unsigned getNumElements() const
static ArrayType get(Type elementType, unsigned elementCount)
Scope getScope() const
Returns the scope of the matrix.
uint32_t getRows() const
Returns the number of rows of the matrix.
uint32_t getColumns() const
Returns the number of columns of the matrix.
static CooperativeMatrixType get(Type elementType, uint32_t rows, uint32_t columns, Scope scope, CooperativeMatrixUseKHR use)
CooperativeMatrixUseKHR getUse() const
Returns the use parameter of the cooperative matrix.
static ImageType get(Type elementType, Dim dim, ImageDepthInfo depth=ImageDepthInfo::DepthUnknown, ImageArrayedInfo arrayed=ImageArrayedInfo::NonArrayed, ImageSamplingInfo samplingInfo=ImageSamplingInfo::SingleSampled, ImageSamplerUseInfo samplerUse=ImageSamplerUseInfo::SamplerUnknown, ImageFormat format=ImageFormat::Unknown)
Definition SPIRVTypes.h:148
ImageDepthInfo getDepthInfo() const
ImageArrayedInfo getArrayedInfo() const
ImageFormat getImageFormat() const
ImageSamplerUseInfo getSamplerUseInfo() const
Type getElementType() const
ImageSamplingInfo getSamplingInfo() const
static MatrixType get(Type columnType, uint32_t columnCount)
unsigned getNumColumns() const
Returns the number of columns.
static NamedBarrierType get(MLIRContext *context)
StorageClass getStorageClass() const
static PointerType get(Type pointeeType, StorageClass storageClass)
unsigned getArrayStride() const
Returns the array stride in bytes.
static RuntimeArrayType get(Type elementType)
static SampledImageType get(Type imageType)
static SamplerType get(MLIRContext *context)
static bool isValid(FloatType)
Returns true if the given float type is valid for the SPIR-V dialect.
SPIR-V struct type.
Definition SPIRVTypes.h:274
void getStructDecorations(SmallVectorImpl< StructType::StructDecorationInfo > &structDecorations) const
void getMemberDecorations(SmallVectorImpl< StructType::MemberDecorationInfo > &memberDecorations) const
static StructType getIdentified(MLIRContext *context, StringRef identifier)
Construct an identified StructType.
bool isIdentified() const
Returns true if the StructType is identified.
StringRef getIdentifier() const
For literal structs, return an empty string.
static StructType getEmpty(MLIRContext *context, StringRef identifier="")
Construct a (possibly identified) StructType with no members.
unsigned getNumElements() const
Type getElementType(unsigned) const
LogicalResult trySetBody(ArrayRef< Type > memberTypes, ArrayRef< OffsetInfo > offsetInfo={}, ArrayRef< MemberDecorationInfo > memberDecorations={}, ArrayRef< StructDecorationInfo > structDecorations={})
Sets the contents of an incomplete identified StructType.
static StructType get(ArrayRef< Type > memberTypes, ArrayRef< OffsetInfo > offsetInfo={}, ArrayRef< MemberDecorationInfo > memberDecorations={}, ArrayRef< StructDecorationInfo > structDecorations={})
Construct a literal StructType with at least one member.
uint64_t getMemberOffset(unsigned) const
SPIR-V TensorARM Type.
Definition SPIRVTypes.h:509
static TensorArmType get(ArrayRef< int64_t > shape, Type elementType)
ArrayRef< int64_t > getShape() const
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition Remarks.h:717
StringRef getInterfaceVarABIAttrName()
Returns the attribute name for specifying argument ABI information.
StringRef getLoopControlAttrName()
Returns the attribute name for specifying loop control.
ParseResult parseEnumKeywordAttr(EnumClass &value, ParserType &parser, StringRef attrName=spirv::attributeName< EnumClass >())
Parses the next keyword in parser as an enumerant of the given EnumClass.
StringRef getTargetEnvAttrName()
Returns the attribute name for specifying SPIR-V target environment.
std::string getDecorationString(Decoration decoration)
Converts a SPIR-V Decoration enum value to its snake_case string representation for use in MLIR attri...
StringRef getSelectionControlAttrName()
Returns the attribute name for specifying selection control.
StringRef getEntryPointABIAttrName()
Returns the attribute name for specifying entry point information.
Include the generated interface declarations.
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.
llvm::TypeSwitch< T, ResultT > TypeSwitch
Definition LLVM.h:139