MLIR 24.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 registerSPIRVDialectOperations(this);
131
132 addInterfaces<SPIRVInlinerInterface>();
133
134 // Allow unknown operations because SPIR-V is extensible.
135 allowUnknownOperations();
136 declarePromisedInterface<gpu::TargetAttrInterface, TargetEnvAttr>();
137}
138
139std::string SPIRVDialect::getAttributeName(Decoration decoration) {
140 return getDecorationString(decoration);
141}
142
143//===----------------------------------------------------------------------===//
144// Type Parsing
145//===----------------------------------------------------------------------===//
146
147// Forward declarations.
148template <typename ValTy>
149static std::optional<ValTy> parseAndVerify(SPIRVDialect const &dialect,
150 DialectAsmParser &parser);
151template <>
152std::optional<Type> parseAndVerify<Type>(SPIRVDialect const &dialect,
153 DialectAsmParser &parser);
154
155template <>
156std::optional<unsigned> parseAndVerify<unsigned>(SPIRVDialect const &dialect,
157 DialectAsmParser &parser);
158
159static Type parseAndVerifyType(SPIRVDialect const &dialect,
160 DialectAsmParser &parser) {
161 Type type;
162 SMLoc typeLoc = parser.getCurrentLocation();
163 if (parser.parseType(type))
164 return Type();
165
166 // Allow SPIR-V dialect types.
167 if (&type.getDialect() == &dialect)
168 return type;
169
170 // Check other allowed types.
171 if (auto t = dyn_cast<FloatType>(type)) {
172 if (!ScalarType::isValid(t)) {
173 parser.emitError(typeLoc,
174 "only 8/16/32/64-bit float type allowed but found ")
175 << type;
176 return Type();
177 }
178 } else if (auto t = dyn_cast<IntegerType>(type)) {
179 if (!ScalarType::isValid(t)) {
180 parser.emitError(typeLoc,
181 "only 1/8/16/32/64-bit integer type allowed but found ")
182 << type;
183 return Type();
184 }
185 } else if (auto t = dyn_cast<VectorType>(type)) {
186 if (t.getRank() != 1) {
187 parser.emitError(typeLoc, "only 1-D vector allowed but found ") << t;
188 return Type();
189 }
190 if (t.getNumElements() < 2) {
191 parser.emitError(typeLoc, "SPIR-V does not allow one-element vectors");
192 return Type();
193 }
194 if (t.getNumElements() > 4) {
195 parser.emitError(
196 typeLoc, "vector length has to be less than or equal to 4 but found ")
197 << t.getNumElements();
198 return Type();
199 }
200 if (!isa<ScalarType>(t.getElementType())) {
201 parser.emitError(
202 typeLoc,
203 "vector element type must be a SPIR-V scalar type but found ")
204 << t.getElementType();
205 return Type();
206 }
207 } else if (auto t = dyn_cast<TensorArmType>(type)) {
208 if (!isa<ScalarType>(t.getElementType())) {
209 parser.emitError(
210 typeLoc, "only scalar element type allowed in tensor type but found ")
211 << t.getElementType();
212 return Type();
213 }
214 } else {
215 parser.emitError(typeLoc, "cannot use ")
216 << type << " to compose SPIR-V types";
217 return Type();
218 }
219
220 return type;
221}
222
223static Type parseAndVerifyMatrixType(SPIRVDialect const &dialect,
224 DialectAsmParser &parser) {
225 Type type;
226 SMLoc typeLoc = parser.getCurrentLocation();
227 if (parser.parseType(type))
228 return Type();
229
230 if (auto t = dyn_cast<VectorType>(type)) {
231 if (t.getRank() != 1) {
232 parser.emitError(typeLoc, "only 1-D vector allowed but found ") << t;
233 return Type();
234 }
235 if (t.getNumElements() > 4 || t.getNumElements() < 2) {
236 parser.emitError(typeLoc,
237 "matrix columns size has to be less than or equal "
238 "to 4 and greater than or equal 2, but found ")
239 << t.getNumElements();
240 return Type();
241 }
242
243 if (!isa<FloatType>(t.getElementType())) {
244 parser.emitError(typeLoc, "matrix columns' elements must be of "
245 "Float type, got ")
246 << t.getElementType();
247 return Type();
248 }
249 } else {
250 parser.emitError(typeLoc, "matrix must be composed using vector "
251 "type, got ")
252 << type;
253 return Type();
254 }
255
256 return type;
257}
258
259static Type parseAndVerifySampledImageType(SPIRVDialect const &dialect,
260 DialectAsmParser &parser) {
261 Type type;
262 SMLoc typeLoc = parser.getCurrentLocation();
263 if (parser.parseType(type))
264 return Type();
265
266 auto imageType = dyn_cast<ImageType>(type);
267 if (!imageType) {
268 parser.emitError(typeLoc,
269 "sampled image must be composed using image type, got ")
270 << type;
271 return Type();
272 }
273
274 if (llvm::is_contained({Dim::SubpassData, Dim::Buffer}, imageType.getDim())) {
275 parser.emitError(
276 typeLoc, "sampled image Dim must not be SubpassData or Buffer, got ")
277 << stringifyDim(imageType.getDim());
278 return Type();
279 }
280
281 return type;
282}
283
284/// Parses an optional `, stride = N` assembly segment. If no parsing failure
285/// occurs, writes `N` to `stride` if existing and writes 0 to `stride` if
286/// missing.
287static LogicalResult parseOptionalArrayStride(const SPIRVDialect &dialect,
288 DialectAsmParser &parser,
289 unsigned &stride) {
290 if (failed(parser.parseOptionalComma())) {
291 stride = 0;
292 return success();
293 }
294
295 if (parser.parseKeyword("stride") || parser.parseEqual())
296 return failure();
297
298 SMLoc strideLoc = parser.getCurrentLocation();
299 std::optional<unsigned> optStride = parseAndVerify<unsigned>(dialect, parser);
300 if (!optStride)
301 return failure();
302
303 if (!(stride = *optStride)) {
304 parser.emitError(strideLoc, "ArrayStride must be greater than zero");
305 return failure();
306 }
307 return success();
308}
309
310// element-type ::= integer-type
311// | floating-point-type
312// | vector-type
313// | spirv-type
314//
315// array-type ::= `!spirv.array` `<` integer-literal `x` element-type
316// (`,` `stride` `=` integer-literal)? `>`
317static Type parseArrayType(SPIRVDialect const &dialect,
318 DialectAsmParser &parser) {
319 if (parser.parseLess())
320 return Type();
321
322 SmallVector<int64_t, 1> countDims;
323 SMLoc countLoc = parser.getCurrentLocation();
324 if (parser.parseDimensionList(countDims, /*allowDynamic=*/false))
325 return Type();
326 if (countDims.size() != 1) {
327 parser.emitError(countLoc,
328 "expected single integer for array element count");
329 return Type();
330 }
331
332 // According to the SPIR-V spec:
333 // "Length is the number of elements in the array. It must be at least 1."
334 int64_t count = countDims[0];
335 if (count == 0) {
336 parser.emitError(countLoc, "expected array length greater than 0");
337 return Type();
338 }
339
340 Type elementType = parseAndVerifyType(dialect, parser);
341 if (!elementType)
342 return Type();
343
344 unsigned stride = 0;
345 if (failed(parseOptionalArrayStride(dialect, parser, stride)))
346 return Type();
347
348 if (parser.parseGreater())
349 return Type();
350 return ArrayType::get(elementType, count, stride);
351}
352
353// cooperative-matrix-type ::=
354// `!spirv.coopmatrix` `<` rows `x` columns `x` element-type `,`
355// scope `,` use `>`
356static Type parseCooperativeMatrixType(SPIRVDialect const &dialect,
357 DialectAsmParser &parser) {
358 if (parser.parseLess())
359 return {};
360
362 SMLoc countLoc = parser.getCurrentLocation();
363 if (parser.parseDimensionList(dims, /*allowDynamic=*/false))
364 return {};
365
366 if (dims.size() != 2) {
367 parser.emitError(countLoc, "expected row and column count");
368 return {};
369 }
370
371 auto elementTy = parseAndVerifyType(dialect, parser);
372 if (!elementTy)
373 return {};
374
375 Scope scope;
376 if (parser.parseComma() ||
377 spirv::parseEnumKeywordAttr(scope, parser, "scope <id>"))
378 return {};
379
380 CooperativeMatrixUseKHR use;
381 if (parser.parseComma() ||
382 spirv::parseEnumKeywordAttr(use, parser, "use <id>"))
383 return {};
384
385 if (parser.parseGreater())
386 return {};
387
388 return CooperativeMatrixType::get(elementTy, dims[0], dims[1], scope, use);
389}
390
391// tensor-arm-type ::=
392// `!spirv.arm.tensor` `<` dim0 `x` dim1 `x` ... `x` dimN `x` element-type`>`
393static Type parseTensorArmType(SPIRVDialect const &dialect,
394 DialectAsmParser &parser) {
395 if (parser.parseLess())
396 return {};
397
398 bool unranked = false;
400 SMLoc countLoc = parser.getCurrentLocation();
401
402 if (parser.parseOptionalStar().succeeded()) {
403 unranked = true;
404 if (parser.parseXInDimensionList())
405 return {};
406 } else if (parser.parseDimensionList(dims, /*allowDynamic=*/true)) {
407 return {};
408 }
409
410 if (!unranked && dims.empty()) {
411 parser.emitError(countLoc, "arm.tensors do not support rank zero");
412 return {};
413 }
414
415 if (llvm::is_contained(dims, 0)) {
416 parser.emitError(countLoc, "arm.tensors do not support zero dimensions");
417 return {};
418 }
419
420 if (llvm::any_of(dims, [](int64_t dim) { return dim < 0; }) &&
421 llvm::any_of(dims, [](int64_t dim) { return dim > 0; })) {
422 parser.emitError(countLoc, "arm.tensor shape dimensions must be either "
423 "fully dynamic or completed shaped");
424 return {};
425 }
426
427 auto elementTy = parseAndVerifyType(dialect, parser);
428 if (!elementTy)
429 return {};
430
431 if (parser.parseGreater())
432 return {};
433
434 return TensorArmType::get(dims, elementTy);
435}
436
437// TODO: Reorder methods to be utilities first and parse*Type
438// methods in alphabetical order
439//
440// storage-class ::= `UniformConstant`
441// | `Uniform`
442// | `Workgroup`
443// | <and other storage classes...>
444//
445// pointer-type ::= `!spirv.ptr<` element-type `,` storage-class `>`
446static Type parsePointerType(SPIRVDialect const &dialect,
447 DialectAsmParser &parser) {
448 if (parser.parseLess())
449 return Type();
450
451 auto pointeeType = parseAndVerifyType(dialect, parser);
452 if (!pointeeType)
453 return Type();
454
455 StringRef storageClassSpec;
456 SMLoc storageClassLoc = parser.getCurrentLocation();
457 if (parser.parseComma() || parser.parseKeyword(&storageClassSpec))
458 return Type();
459
460 auto storageClass = symbolizeStorageClass(storageClassSpec);
461 if (!storageClass) {
462 parser.emitError(storageClassLoc, "unknown storage class: ")
463 << storageClassSpec;
464 return Type();
465 }
466 if (parser.parseGreater())
467 return Type();
468 return PointerType::get(pointeeType, *storageClass);
469}
470
471// runtime-array-type ::= `!spirv.rtarray` `<` element-type
472// (`,` `stride` `=` integer-literal)? `>`
473static Type parseRuntimeArrayType(SPIRVDialect const &dialect,
474 DialectAsmParser &parser) {
475 if (parser.parseLess())
476 return Type();
477
478 Type elementType = parseAndVerifyType(dialect, parser);
479 if (!elementType)
480 return Type();
481
482 unsigned stride = 0;
483 if (failed(parseOptionalArrayStride(dialect, parser, stride)))
484 return Type();
485
486 if (parser.parseGreater())
487 return Type();
488 return RuntimeArrayType::get(elementType, stride);
489}
490
491// matrix-type ::= `!spirv.matrix` `<` integer-literal `x` element-type `>`
492static Type parseMatrixType(SPIRVDialect const &dialect,
493 DialectAsmParser &parser) {
494 if (parser.parseLess())
495 return Type();
496
497 SmallVector<int64_t, 1> countDims;
498 SMLoc countLoc = parser.getCurrentLocation();
499 if (parser.parseDimensionList(countDims, /*allowDynamic=*/false))
500 return Type();
501 if (countDims.size() != 1) {
502 parser.emitError(countLoc, "expected single unsigned "
503 "integer for number of columns");
504 return Type();
505 }
506
507 int64_t columnCount = countDims[0];
508 // According to the specification, Matrices can have 2, 3, or 4 columns
509 if (columnCount < 2 || columnCount > 4) {
510 parser.emitError(countLoc, "matrix is expected to have 2, 3, or 4 "
511 "columns");
512 return Type();
513 }
514
515 Type columnType = parseAndVerifyMatrixType(dialect, parser);
516 if (!columnType)
517 return Type();
518
519 if (parser.parseGreater())
520 return Type();
521
522 return MatrixType::get(columnType, columnCount);
523}
524
525// Specialize this function to parse each of the parameters that define an
526// ImageType. By default it assumes this is an enum type.
527template <typename ValTy>
528static std::optional<ValTy> parseAndVerify(SPIRVDialect const &dialect,
529 DialectAsmParser &parser) {
530 StringRef enumSpec;
531 SMLoc enumLoc = parser.getCurrentLocation();
532 if (parser.parseKeyword(&enumSpec)) {
533 return std::nullopt;
534 }
535
536 auto val = spirv::symbolizeEnum<ValTy>(enumSpec);
537 if (!val)
538 parser.emitError(enumLoc, "unknown attribute: '") << enumSpec << "'";
539 return val;
540}
541
542template <>
543std::optional<Type> parseAndVerify<Type>(SPIRVDialect const &dialect,
544 DialectAsmParser &parser) {
545 // TODO: Further verify that the element type can be sampled
546 auto ty = parseAndVerifyType(dialect, parser);
547 if (!ty)
548 return std::nullopt;
549 return ty;
550}
551
552template <typename IntTy>
553static std::optional<IntTy> parseAndVerifyInteger(SPIRVDialect const &dialect,
554 DialectAsmParser &parser) {
555 IntTy offsetVal = std::numeric_limits<IntTy>::max();
556 if (parser.parseInteger(offsetVal))
557 return std::nullopt;
558 return offsetVal;
559}
560
561template <>
562std::optional<unsigned> parseAndVerify<unsigned>(SPIRVDialect const &dialect,
563 DialectAsmParser &parser) {
564 return parseAndVerifyInteger<unsigned>(dialect, parser);
565}
566
567namespace {
568// Functor object to parse a comma separated list of specs. The function
569// parseAndVerify does the actual parsing and verification of individual
570// elements. This is a functor since parsing the last element of the list
571// (termination condition) needs partial specialization.
572template <typename ParseType, typename... Args>
573struct ParseCommaSeparatedList {
574 std::optional<std::tuple<ParseType, Args...>>
575 operator()(SPIRVDialect const &dialect, DialectAsmParser &parser) const {
576 auto parseVal = parseAndVerify<ParseType>(dialect, parser);
577 if (!parseVal)
578 return std::nullopt;
579
580 auto numArgs = std::tuple_size<std::tuple<Args...>>::value;
581 if (numArgs != 0 && failed(parser.parseComma()))
582 return std::nullopt;
583 auto remainingValues = ParseCommaSeparatedList<Args...>{}(dialect, parser);
584 if (!remainingValues)
585 return std::nullopt;
586 return std::tuple_cat(std::tuple<ParseType>(parseVal.value()),
587 remainingValues.value());
588 }
589};
590
591// Partial specialization of the function to parse a comma separated list of
592// specs to parse the last element of the list.
593template <typename ParseType>
594struct ParseCommaSeparatedList<ParseType> {
595 std::optional<std::tuple<ParseType>>
596 operator()(SPIRVDialect const &dialect, DialectAsmParser &parser) const {
597 if (auto value = parseAndVerify<ParseType>(dialect, parser))
598 return std::tuple<ParseType>(*value);
599 return std::nullopt;
600 }
601};
602} // namespace
603
604// dim ::= `1D` | `2D` | `3D` | `Cube` | <and other SPIR-V Dim specifiers...>
605//
606// depth-info ::= `NoDepth` | `IsDepth` | `DepthUnknown`
607//
608// arrayed-info ::= `NonArrayed` | `Arrayed`
609//
610// sampling-info ::= `SingleSampled` | `MultiSampled`
611//
612// sampler-use-info ::= `SamplerUnknown` | `NeedSampler` | `NoSampler`
613//
614// format ::= `Unknown` | `Rgba32f` | <and other SPIR-V Image formats...>
615//
616// image-type ::= `!spirv.image<` element-type `,` dim `,` depth-info `,`
617// arrayed-info `,` sampling-info `,`
618// sampler-use-info `,` format `>`
619static Type parseImageType(SPIRVDialect const &dialect,
620 DialectAsmParser &parser) {
621 if (parser.parseLess())
622 return Type();
623
624 auto value =
625 ParseCommaSeparatedList<Type, Dim, ImageDepthInfo, ImageArrayedInfo,
626 ImageSamplingInfo, ImageSamplerUseInfo,
627 ImageFormat>{}(dialect, parser);
628 if (!value)
629 return Type();
630
631 if (parser.parseGreater())
632 return Type();
633 return ImageType::get(*value);
634}
635
636// sampledImage-type :: = `!spirv.sampledImage<` image-type `>`
637static Type parseSampledImageType(SPIRVDialect const &dialect,
638 DialectAsmParser &parser) {
639 if (parser.parseLess())
640 return Type();
641
642 Type parsedType = parseAndVerifySampledImageType(dialect, parser);
643 if (!parsedType)
644 return Type();
645
646 if (parser.parseGreater())
647 return Type();
648 return SampledImageType::get(parsedType);
649}
650
651// Parse decorations associated with a member.
653 SPIRVDialect const &dialect, DialectAsmParser &parser,
654 ArrayRef<Type> memberTypes,
657
658 // Check if the first element is offset.
659 SMLoc offsetLoc = parser.getCurrentLocation();
660 StructType::OffsetInfo offset = 0;
661 OptionalParseResult offsetParseResult = parser.parseOptionalInteger(offset);
662 if (offsetParseResult.has_value()) {
663 if (failed(*offsetParseResult))
664 return failure();
665
666 if (offsetInfo.size() != memberTypes.size() - 1) {
667 return parser.emitError(offsetLoc,
668 "offset specification must be given for "
669 "all members");
670 }
671 offsetInfo.push_back(offset);
672 }
673
674 // Check for no spirv::Decorations.
675 if (succeeded(parser.parseOptionalRSquare()))
676 return success();
677
678 // If there was an offset, make sure to parse the comma.
679 if (offsetParseResult.has_value() && parser.parseComma())
680 return failure();
681
682 // Check for spirv::Decorations.
683 auto parseDecorations = [&]() {
684 auto memberDecoration = parseAndVerify<spirv::Decoration>(dialect, parser);
685 if (!memberDecoration)
686 return failure();
687
688 // Parse member decoration value if it exists.
689 if (succeeded(parser.parseOptionalEqual())) {
690 Attribute memberDecorationValue;
691 if (failed(parser.parseAttribute(memberDecorationValue)))
692 return failure();
693
694 memberDecorationInfo.emplace_back(
695 static_cast<uint32_t>(memberTypes.size() - 1),
696 memberDecoration.value(), memberDecorationValue);
697 } else {
698 memberDecorationInfo.emplace_back(
699 static_cast<uint32_t>(memberTypes.size() - 1),
700 memberDecoration.value(), UnitAttr::get(dialect.getContext()));
701 }
702 return success();
703 };
704 if (failed(parser.parseCommaSeparatedList(parseDecorations)) ||
705 failed(parser.parseRSquare()))
706 return failure();
707
708 return success();
709}
710
711// struct-member-decoration ::= integer-literal? spirv-decoration*
712// struct-type ::=
713// `!spirv.struct<` (id `,`)?
714// `(`
715// (spirv-type (`[` struct-member-decoration `]`)?)*
716// `)`
717// (`,` struct-decoration)?
718// `>`
719static Type parseStructType(SPIRVDialect const &dialect,
720 DialectAsmParser &parser) {
721 // TODO: This function is quite lengthy. Break it down into smaller chunks.
722
723 if (parser.parseLess())
724 return Type();
725
726 StringRef identifier;
727 FailureOr<DialectAsmParser::CyclicParseReset> cyclicParse;
728
729 // Check if this is an identified struct type.
730 if (succeeded(parser.parseOptionalKeyword(&identifier))) {
731 // Check if this is a possible recursive reference.
732 auto structType =
733 StructType::getIdentified(dialect.getContext(), identifier);
734 cyclicParse = parser.tryStartCyclicParse(structType);
735 if (succeeded(parser.parseOptionalGreater())) {
736 if (succeeded(cyclicParse)) {
737 parser.emitError(
738 parser.getNameLoc(),
739 "recursive struct reference not nested in struct definition");
740
741 return Type();
742 }
743
744 return structType;
745 }
746
747 if (failed(parser.parseComma()))
748 return Type();
749
750 if (failed(cyclicParse)) {
751 parser.emitError(parser.getNameLoc(),
752 "identifier already used for an enclosing struct");
753 return Type();
754 }
755 }
756
757 if (failed(parser.parseLParen()))
758 return Type();
759
760 if (succeeded(parser.parseOptionalRParen()) &&
761 succeeded(parser.parseOptionalGreater())) {
762 return StructType::getEmpty(dialect.getContext(), identifier);
763 }
764
765 StructType idStructTy;
766
767 if (!identifier.empty())
768 idStructTy = StructType::getIdentified(dialect.getContext(), identifier);
769
770 SmallVector<Type, 4> memberTypes;
773
774 do {
775 Type memberType;
776 if (parser.parseType(memberType))
777 return Type();
778 if (!isa<SPIRVType>(memberType)) {
779 parser.emitError(parser.getNameLoc(),
780 "member type must be a valid SPIR-V type");
781 return Type();
782 }
783 memberTypes.push_back(memberType);
784
785 if (succeeded(parser.parseOptionalLSquare()))
786 if (parseStructMemberDecorations(dialect, parser, memberTypes, offsetInfo,
787 memberDecorationInfo))
788 return Type();
789 } while (succeeded(parser.parseOptionalComma()));
790
791 if (!offsetInfo.empty() && memberTypes.size() != offsetInfo.size()) {
792 parser.emitError(parser.getNameLoc(),
793 "offset specification must be given for all members");
794 return Type();
795 }
796
797 if (failed(parser.parseRParen()))
798 return Type();
799
801
802 auto parseStructDecoration = [&]() {
803 std::optional<spirv::Decoration> decoration =
804 parseAndVerify<spirv::Decoration>(dialect, parser);
805 if (!decoration)
806 return failure();
807
808 // Parse decoration value if it exists.
809 if (succeeded(parser.parseOptionalEqual())) {
810 Attribute decorationValue;
811 if (failed(parser.parseAttribute(decorationValue)))
812 return failure();
813
814 structDecorationInfo.emplace_back(decoration.value(), decorationValue);
815 } else {
816 structDecorationInfo.emplace_back(decoration.value(),
817 UnitAttr::get(dialect.getContext()));
818 }
819 return success();
820 };
821
822 while (succeeded(parser.parseOptionalComma()))
823 if (failed(parseStructDecoration()))
824 return Type();
825
826 if (failed(parser.parseGreater()))
827 return Type();
828
829 if (!identifier.empty()) {
830 if (failed(idStructTy.trySetBody(memberTypes, offsetInfo,
831 memberDecorationInfo,
832 structDecorationInfo)))
833 return Type();
834 return idStructTy;
835 }
836
837 return StructType::get(memberTypes, offsetInfo, memberDecorationInfo,
838 structDecorationInfo);
839}
840
841// spirv-type ::= array-type
842// | element-type
843// | image-type
844// | pointer-type
845// | runtime-array-type
846// | sampled-image-type
847// | struct-type
848Type SPIRVDialect::parseType(DialectAsmParser &parser) const {
849 StringRef keyword;
850 if (parser.parseKeyword(&keyword))
851 return Type();
852
853 if (keyword == "array")
854 return parseArrayType(*this, parser);
855 if (keyword == "coopmatrix")
856 return parseCooperativeMatrixType(*this, parser);
857 if (keyword == "image")
858 return parseImageType(*this, parser);
859 if (keyword == "ptr")
860 return parsePointerType(*this, parser);
861 if (keyword == "rtarray")
862 return parseRuntimeArrayType(*this, parser);
863 if (keyword == "sampled_image")
864 return parseSampledImageType(*this, parser);
865 if (keyword == "sampler")
867 if (keyword == "named_barrier")
869 if (keyword == "struct")
870 return parseStructType(*this, parser);
871 if (keyword == "matrix")
872 return parseMatrixType(*this, parser);
873 if (keyword == "arm.tensor")
874 return parseTensorArmType(*this, parser);
875 parser.emitError(parser.getNameLoc(), "unknown SPIR-V type: ") << keyword;
876 return Type();
877}
878
879//===----------------------------------------------------------------------===//
880// Type Printing
881//===----------------------------------------------------------------------===//
882
883static void print(ArrayType type, DialectAsmPrinter &os) {
884 os << "array<" << type.getNumElements() << " x " << type.getElementType();
885 if (unsigned stride = type.getArrayStride())
886 os << ", stride=" << stride;
887 os << ">";
888}
889
891 os << "rtarray<" << type.getElementType();
892 if (unsigned stride = type.getArrayStride())
893 os << ", stride=" << stride;
894 os << ">";
895}
896
897static void print(PointerType type, DialectAsmPrinter &os) {
898 os << "ptr<" << type.getPointeeType() << ", "
899 << stringifyStorageClass(type.getStorageClass()) << ">";
900}
901
902static void print(ImageType type, DialectAsmPrinter &os) {
903 os << "image<" << type.getElementType() << ", " << stringifyDim(type.getDim())
904 << ", " << stringifyImageDepthInfo(type.getDepthInfo()) << ", "
905 << stringifyImageArrayedInfo(type.getArrayedInfo()) << ", "
906 << stringifyImageSamplingInfo(type.getSamplingInfo()) << ", "
907 << stringifyImageSamplerUseInfo(type.getSamplerUseInfo()) << ", "
908 << stringifyImageFormat(type.getImageFormat()) << ">";
909}
910
912 os << "sampled_image<" << type.getImageType() << ">";
913}
914
915static void print(SamplerType type, DialectAsmPrinter &os) { os << "sampler"; }
916
918 os << "named_barrier";
919}
920
921static void print(StructType type, DialectAsmPrinter &os) {
922 FailureOr<AsmPrinter::CyclicPrintReset> cyclicPrint;
923
924 os << "struct<";
925
926 if (type.isIdentified()) {
927 os << type.getIdentifier();
928
929 cyclicPrint = os.tryStartCyclicPrint(type);
930 if (failed(cyclicPrint)) {
931 os << ">";
932 return;
933 }
934
935 os << ", ";
936 }
937
938 os << "(";
939
940 auto printMember = [&](unsigned i) {
941 os << type.getElementType(i);
943 type.getMemberDecorations(i, decorations);
944 if (type.hasOffset() || !decorations.empty()) {
945 os << " [";
946 if (type.hasOffset()) {
947 os << type.getMemberOffset(i);
948 if (!decorations.empty())
949 os << ", ";
950 }
951 auto eachFn = [&os](spirv::StructType::MemberDecorationInfo decoration) {
952 os << stringifyDecoration(decoration.decoration);
953 if (decoration.hasValue()) {
954 os << "=";
955 os.printAttributeWithoutType(decoration.decorationValue);
956 }
957 };
958 llvm::interleaveComma(decorations, os, eachFn);
959 os << "]";
960 }
961 };
962 llvm::interleaveComma(llvm::seq<unsigned>(0, type.getNumElements()), os,
963 printMember);
964 os << ")";
965
967 type.getStructDecorations(decorations);
968 if (!decorations.empty()) {
969 os << ", ";
970 auto eachFn = [&os](spirv::StructType::StructDecorationInfo decoration) {
971 os << stringifyDecoration(decoration.decoration);
972 if (decoration.hasValue()) {
973 os << "=";
974 os.printAttributeWithoutType(decoration.decorationValue);
975 }
976 };
977 llvm::interleaveComma(decorations, os, eachFn);
978 }
979
980 os << ">";
981}
982
984 os << "coopmatrix<" << type.getRows() << "x" << type.getColumns() << "x"
985 << type.getElementType() << ", " << type.getScope() << ", "
986 << type.getUse() << ">";
987}
988
989static void print(MatrixType type, DialectAsmPrinter &os) {
990 os << "matrix<" << type.getNumColumns() << " x " << type.getColumnType();
991 os << ">";
992}
993
994static void print(TensorArmType type, DialectAsmPrinter &os) {
995 os << "arm.tensor<";
996
997 llvm::interleave(
998 type.getShape(), os,
999 [&](int64_t dim) {
1000 if (ShapedType::isDynamic(dim))
1001 os << '?';
1002 else
1003 os << dim;
1004 },
1005 "x");
1006 if (!type.hasRank()) {
1007 os << "*";
1008 }
1009 os << "x" << type.getElementType() << ">";
1010}
1011
1012void SPIRVDialect::printType(Type type, DialectAsmPrinter &os) const {
1013 TypeSwitch<Type>(type)
1017 [&](auto type) { print(type, os); })
1018 .DefaultUnreachable("Unhandled SPIR-V type");
1019}
1020
1021//===----------------------------------------------------------------------===//
1022// Constant
1023//===----------------------------------------------------------------------===//
1024
1025Operation *SPIRVDialect::materializeConstant(OpBuilder &builder,
1026 Attribute value, Type type,
1027 Location loc) {
1028 if (auto poison = dyn_cast<ub::PoisonAttr>(value))
1029 return ub::PoisonOp::create(builder, loc, type, poison);
1030
1031 if (!spirv::ConstantOp::isBuildableWith(type))
1032 return nullptr;
1033
1034 return spirv::ConstantOp::create(builder, loc, type, value);
1035}
1036
1037//===----------------------------------------------------------------------===//
1038// Shader Interface ABI
1039//===----------------------------------------------------------------------===//
1040
1041LogicalResult SPIRVDialect::verifyOperationAttribute(Operation *op,
1042 NamedAttribute attribute) {
1043 StringRef symbol = attribute.getName().strref();
1044 Attribute attr = attribute.getValue();
1045
1046 if (symbol == spirv::getEntryPointABIAttrName()) {
1047 if (!isa<spirv::EntryPointABIAttr>(attr)) {
1048 return op->emitError("'")
1049 << symbol << "' attribute must be an entry point ABI attribute";
1050 }
1051 } else if (symbol == spirv::getTargetEnvAttrName()) {
1052 if (!isa<spirv::TargetEnvAttr>(attr))
1053 return op->emitError("'") << symbol << "' must be a spirv::TargetEnvAttr";
1054 } else if (symbol == spirv::getLoopControlAttrName()) {
1055 if (!isa<spirv::LoopControlAttr>(attr))
1056 return op->emitError("'")
1057 << symbol << "' must be a spirv::LoopControlAttr";
1058 } else if (symbol == spirv::getSelectionControlAttrName()) {
1059 if (!isa<spirv::SelectionControlAttr>(attr))
1060 return op->emitError("'")
1061 << symbol << "' must be a spirv::SelectionControlAttr";
1062 } else {
1063 return op->emitError("found unsupported '")
1064 << symbol << "' attribute on operation";
1065 }
1066
1067 return success();
1068}
1069
1070/// Verifies the given SPIR-V `attribute` attached to a value of the given
1071/// `valueType` is valid.
1072static LogicalResult verifyRegionAttribute(Location loc, Type valueType,
1073 NamedAttribute attribute) {
1074 StringRef symbol = attribute.getName().strref();
1075 Attribute attr = attribute.getValue();
1076
1077 if (symbol == spirv::getInterfaceVarABIAttrName()) {
1078 auto varABIAttr = dyn_cast<spirv::InterfaceVarABIAttr>(attr);
1079 if (!varABIAttr)
1080 return emitError(loc, "'")
1081 << symbol << "' must be a spirv::InterfaceVarABIAttr";
1082
1083 if (varABIAttr.getStorageClass() && !valueType.isIntOrIndexOrFloat())
1084 return emitError(loc, "'") << symbol
1085 << "' attribute cannot specify storage class "
1086 "when attaching to a non-scalar value";
1087 return success();
1088 }
1089 if (symbol == spirv::DecorationAttr::name) {
1090 if (!isa<spirv::DecorationAttr>(attr))
1091 return emitError(loc, "'")
1092 << symbol << "' must be a spirv::DecorationAttr";
1093 return success();
1094 }
1095
1096 return emitError(loc, "found unsupported '")
1097 << symbol << "' attribute on region argument";
1098}
1099
1100LogicalResult SPIRVDialect::verifyRegionArgAttribute(Operation *op,
1101 unsigned regionIndex,
1102 unsigned argIndex,
1103 NamedAttribute attribute) {
1104 auto funcOp = dyn_cast<FunctionOpInterface>(op);
1105 if (!funcOp)
1106 return success();
1107 Type argType = funcOp.getArgumentTypes()[argIndex];
1108
1109 return verifyRegionAttribute(op->getLoc(), argType, attribute);
1110}
1111
1112LogicalResult SPIRVDialect::verifyRegionResultAttribute(
1113 Operation *op, unsigned /*regionIndex*/, unsigned resultIndex,
1114 NamedAttribute attribute) {
1115 if (auto graphOp = dyn_cast<spirv::GraphARMOp>(op))
1116 return verifyRegionAttribute(
1117 op->getLoc(), graphOp.getResultTypes()[resultIndex], attribute);
1118 return op->emitError(
1119 "cannot attach SPIR-V attributes to region result which is "
1120 "not part of a spirv::GraphARMOp type");
1121}
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:210
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:732
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