1 //===- DLTI.cpp - Data Layout And Target Info MLIR Dialect Implementation -===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "mlir/Dialect/DLTI/DLTI.h"
10 #include "mlir/IR/Builders.h"
11 #include "mlir/IR/BuiltinAttributes.h"
12 #include "mlir/IR/BuiltinDialect.h"
13 #include "mlir/IR/BuiltinOps.h"
14 #include "mlir/IR/BuiltinTypes.h"
15 #include "mlir/IR/Dialect.h"
16 #include "mlir/IR/DialectImplementation.h"
17 #include "llvm/ADT/TypeSwitch.h"
19 #include "llvm/ADT/TypeSwitch.h"
20 #include "llvm/Support/Debug.h"
21 #include "llvm/Support/MathExtras.h"
25 #include "mlir/Dialect/DLTI/DLTIDialect.cpp.inc"
27 #define GET_ATTRDEF_CLASSES
28 #include "mlir/Dialect/DLTI/DLTIAttrs.cpp.inc"
30 #define DEBUG_TYPE "dlti"
32 //===----------------------------------------------------------------------===//
33 // Common parsing utility functions.
34 //===----------------------------------------------------------------------===//
36 /// Parse an entry which can either be of the form `key = value` or a
37 /// #dlti.dl_entry attribute. When `tryType=true` the key can be a type,
38 /// otherwise only quoted strings are allowed. The grammar is as follows:
39 /// entry ::= ((type | quoted-string) `=` attr) | dl-entry-attr
40 static ParseResult
parseKeyValuePair(AsmParser
&parser
,
41 DataLayoutEntryInterface
&entry
,
42 bool tryType
= false) {
47 OptionalParseResult parsedType
= parser
.parseOptionalType(type
);
48 if (parsedType
.has_value()) {
49 if (failed(parsedType
.value()))
50 return parser
.emitError(parser
.getCurrentLocation())
51 << "error while parsing type DLTI key";
53 if (failed(parser
.parseEqual()) || failed(parser
.parseAttribute(value
)))
56 entry
= DataLayoutEntryAttr::get(type
, value
);
57 return ParseResult::success();
62 OptionalParseResult parsedStr
= parser
.parseOptionalString(&ident
);
63 if (parsedStr
.has_value() && succeeded(parsedStr
.value())) {
64 if (failed(parser
.parseEqual()) || failed(parser
.parseAttribute(value
)))
65 return failure(); // Assume that an error has already been emitted.
67 entry
= DataLayoutEntryAttr::get(
68 StringAttr::get(parser
.getContext(), ident
), value
);
69 return ParseResult::success();
72 OptionalParseResult parsedEntry
= parser
.parseAttribute(entry
);
73 if (parsedEntry
.has_value()) {
74 if (succeeded(parsedEntry
.value()))
75 return parsedEntry
.value();
76 return failure(); // Assume that an error has already been emitted.
78 return parser
.emitError(parser
.getCurrentLocation())
79 << "failed to parse DLTI entry";
82 /// Construct a requested attribute by parsing list of entries occurring within
83 /// a pair of `<` and `>`, optionally allow types as keys and an empty list.
84 /// The grammar is as follows:
85 /// bracketed-entry-list ::=`<` entry-list `>`
86 /// entry-list ::= | entry | entry `,` entry-list
87 /// entry ::= ((type | quoted-string) `=` attr) | dl-entry-attr
89 static Attribute
parseAngleBracketedEntries(AsmParser
&parser
, Type ty
,
91 bool allowEmpty
= false) {
92 SmallVector
<DataLayoutEntryInterface
> entries
;
93 if (failed(parser
.parseCommaSeparatedList(
94 AsmParser::Delimiter::LessGreater
, [&]() {
95 return parseKeyValuePair(parser
, entries
.emplace_back(), tryType
);
99 if (entries
.empty() && !allowEmpty
) {
100 parser
.emitError(parser
.getNameLoc()) << "no DLTI entries provided";
104 return Attr::getChecked([&] { return parser
.emitError(parser
.getNameLoc()); },
105 parser
.getContext(), ArrayRef(entries
));
108 //===----------------------------------------------------------------------===//
109 // Common printing utility functions.
110 //===----------------------------------------------------------------------===//
112 /// Convert pointer-union keys to strings.
113 static std::string
keyToStr(DataLayoutEntryKey key
) {
115 TypeSwitch
<DataLayoutEntryKey
>(key
)
116 .Case
<StringAttr
, Type
>( // The only two kinds of key we know of.
117 [&](auto key
) { llvm::raw_string_ostream(buf
) << key
; });
121 /// Pretty-print entries, each in `key = value` format, separated by commas.
123 static void printAngleBracketedEntries(AsmPrinter
&os
, T
&&entries
) {
125 llvm::interleaveComma(std::forward
<T
>(entries
), os
, [&](auto entry
) {
126 os
<< keyToStr(entry
.getKey()) << " = " << entry
.getValue();
131 //===----------------------------------------------------------------------===//
132 // Common verifying utility functions.
133 //===----------------------------------------------------------------------===//
135 /// Verify entries, with the option to disallow types as keys.
136 static LogicalResult
verifyEntries(function_ref
<InFlightDiagnostic()> emitError
,
137 ArrayRef
<DataLayoutEntryInterface
> entries
,
138 bool allowTypes
= true) {
139 DenseSet
<DataLayoutEntryKey
> keys
;
140 for (DataLayoutEntryInterface entry
: entries
) {
142 return emitError() << "contained invalid DLTI entry";
143 DataLayoutEntryKey key
= entry
.getKey();
145 return emitError() << "contained invalid DLTI key";
146 if (!allowTypes
&& dyn_cast
<Type
>(key
))
147 return emitError() << "type as DLTI key is not allowed";
148 if (auto strKey
= dyn_cast
<StringAttr
>(key
))
149 if (strKey
.getValue().empty())
150 return emitError() << "empty string as DLTI key is not allowed";
151 if (!keys
.insert(key
).second
)
152 return emitError() << "repeated DLTI key: " << keyToStr(key
);
153 if (!entry
.getValue())
154 return emitError() << "value associated to DLTI key " << keyToStr(key
)
160 //===----------------------------------------------------------------------===//
161 // DataLayoutEntryAttr
162 //===----------------------------------------------------------------------===//
165 class DataLayoutEntryAttrStorage
: public AttributeStorage
{
167 using KeyTy
= std::pair
<DataLayoutEntryKey
, Attribute
>;
169 DataLayoutEntryAttrStorage(DataLayoutEntryKey entryKey
, Attribute value
)
170 : entryKey(entryKey
), value(value
) {}
172 static DataLayoutEntryAttrStorage
*
173 construct(AttributeStorageAllocator
&allocator
, const KeyTy
&key
) {
174 return new (allocator
.allocate
<DataLayoutEntryAttrStorage
>())
175 DataLayoutEntryAttrStorage(key
.first
, key
.second
);
178 bool operator==(const KeyTy
&other
) const {
179 return other
.first
== entryKey
&& other
.second
== value
;
182 DataLayoutEntryKey entryKey
;
185 } // namespace detail
188 DataLayoutEntryAttr
DataLayoutEntryAttr::get(StringAttr key
, Attribute value
) {
189 return Base::get(key
.getContext(), key
, value
);
192 DataLayoutEntryAttr
DataLayoutEntryAttr::get(Type key
, Attribute value
) {
193 return Base::get(key
.getContext(), key
, value
);
196 DataLayoutEntryKey
DataLayoutEntryAttr::getKey() const {
197 return getImpl()->entryKey
;
200 Attribute
DataLayoutEntryAttr::getValue() const { return getImpl()->value
; }
202 /// Parses an attribute with syntax:
203 /// dl-entry-attr ::= `#dlti.` `dl_entry` `<` (type | quoted-string) `,`
205 Attribute
DataLayoutEntryAttr::parse(AsmParser
&parser
, Type type
) {
206 if (failed(parser
.parseLess()))
209 Type typeKey
= nullptr;
210 std::string identifier
;
211 SMLoc idLoc
= parser
.getCurrentLocation();
212 OptionalParseResult parsedType
= parser
.parseOptionalType(typeKey
);
213 if (parsedType
.has_value() && failed(parsedType
.value()))
215 if (!parsedType
.has_value()) {
216 OptionalParseResult parsedString
= parser
.parseOptionalString(&identifier
);
217 if (!parsedString
.has_value() || failed(parsedString
.value())) {
218 parser
.emitError(idLoc
) << "expected a type or a quoted string";
224 if (failed(parser
.parseComma()) || failed(parser
.parseAttribute(value
)) ||
225 failed(parser
.parseGreater()))
228 return typeKey
? get(typeKey
, value
)
229 : get(parser
.getBuilder().getStringAttr(identifier
), value
);
232 void DataLayoutEntryAttr::print(AsmPrinter
&printer
) const {
233 printer
<< "<" << keyToStr(getKey()) << ", " << getValue() << ">";
236 //===----------------------------------------------------------------------===//
238 //===----------------------------------------------------------------------===//
240 /// Parses an attribute with syntax:
241 /// map-attr ::= `#dlti.` `map` `<` entry-list `>`
242 /// entry-list ::= entry | entry `,` entry-list
243 /// entry ::= ((type | quoted-string) `=` attr) | dl-entry-attr
244 Attribute
MapAttr::parse(AsmParser
&parser
, Type type
) {
245 return parseAngleBracketedEntries
<MapAttr
>(parser
, type
, /*tryType=*/true,
246 /*allowEmpty=*/true);
249 void MapAttr::print(AsmPrinter
&printer
) const {
250 printAngleBracketedEntries(printer
, getEntries());
253 LogicalResult
MapAttr::verify(function_ref
<InFlightDiagnostic()> emitError
,
254 ArrayRef
<DataLayoutEntryInterface
> entries
) {
255 return verifyEntries(emitError
, entries
);
258 //===----------------------------------------------------------------------===//
259 // DataLayoutSpecAttr
260 //===----------------------------------------------------------------------===//
263 DataLayoutSpecAttr::verify(function_ref
<InFlightDiagnostic()> emitError
,
264 ArrayRef
<DataLayoutEntryInterface
> entries
) {
265 return verifyEntries(emitError
, entries
);
268 /// Given a list of old and a list of new entries, overwrites old entries with
269 /// new ones if they have matching keys, appends new entries to the old entry
272 overwriteDuplicateEntries(SmallVectorImpl
<DataLayoutEntryInterface
> &oldEntries
,
273 ArrayRef
<DataLayoutEntryInterface
> newEntries
) {
274 unsigned oldEntriesSize
= oldEntries
.size();
275 for (DataLayoutEntryInterface entry
: newEntries
) {
276 // We expect a small (dozens) number of entries, so it is practically
277 // cheaper to iterate over the list linearly rather than to create an
278 // auxiliary hashmap to avoid duplication. Also note that we never need to
279 // check for duplicate keys the values that were added from `newEntries`.
280 bool replaced
= false;
281 for (unsigned i
= 0; i
< oldEntriesSize
; ++i
) {
282 if (oldEntries
[i
].getKey() == entry
.getKey()) {
283 oldEntries
[i
] = entry
;
289 oldEntries
.push_back(entry
);
293 /// Combines a data layout spec into the given lists of entries organized by
294 /// type class and identifier, overwriting them if necessary. Fails to combine
295 /// if the two entries with identical keys are not compatible.
297 combineOneSpec(DataLayoutSpecInterface spec
,
298 DenseMap
<TypeID
, DataLayoutEntryList
> &entriesForType
,
299 DenseMap
<StringAttr
, DataLayoutEntryInterface
> &entriesForID
) {
300 // A missing spec should be fine.
304 DenseMap
<TypeID
, DataLayoutEntryList
> newEntriesForType
;
305 DenseMap
<StringAttr
, DataLayoutEntryInterface
> newEntriesForID
;
306 spec
.bucketEntriesByType(newEntriesForType
, newEntriesForID
);
308 // Try overwriting the old entries with the new ones.
309 for (auto &kvp
: newEntriesForType
) {
310 if (!entriesForType
.count(kvp
.first
)) {
311 entriesForType
[kvp
.first
] = std::move(kvp
.second
);
315 Type typeSample
= kvp
.second
.front().getKey().get
<Type
>();
316 assert(&typeSample
.getDialect() !=
317 typeSample
.getContext()->getLoadedDialect
<BuiltinDialect
>() &&
318 "unexpected data layout entry for built-in type");
320 auto interface
= cast
<DataLayoutTypeInterface
>(typeSample
);
321 if (!interface
.areCompatible(entriesForType
.lookup(kvp
.first
), kvp
.second
))
324 overwriteDuplicateEntries(entriesForType
[kvp
.first
], kvp
.second
);
327 for (const auto &kvp
: newEntriesForID
) {
328 StringAttr id
= kvp
.second
.getKey().get
<StringAttr
>();
329 Dialect
*dialect
= id
.getReferencedDialect();
330 if (!entriesForID
.count(id
)) {
331 entriesForID
[id
] = kvp
.second
;
335 // Attempt to combine the enties using the dialect interface. If the
336 // dialect is not loaded for some reason, use the default combinator
337 // that conservatively accepts identical entries only.
339 dialect
? cast
<DataLayoutDialectInterface
>(dialect
)->combine(
340 entriesForID
[id
], kvp
.second
)
341 : DataLayoutDialectInterface::defaultCombine(entriesForID
[id
],
343 if (!entriesForID
[id
])
351 DataLayoutSpecAttr::combineWith(ArrayRef
<DataLayoutSpecInterface
> specs
) const {
352 // Only combine with attributes of the same kind.
353 // TODO: reconsider this when the need arises.
354 if (any_of(specs
, [](DataLayoutSpecInterface spec
) {
355 return !llvm::isa
<DataLayoutSpecAttr
>(spec
);
359 // Combine all specs in order, with `this` being the last one.
360 DenseMap
<TypeID
, DataLayoutEntryList
> entriesForType
;
361 DenseMap
<StringAttr
, DataLayoutEntryInterface
> entriesForID
;
362 for (DataLayoutSpecInterface spec
: specs
)
363 if (failed(combineOneSpec(spec
, entriesForType
, entriesForID
)))
365 if (failed(combineOneSpec(*this, entriesForType
, entriesForID
)))
368 // Rebuild the linear list of entries.
369 SmallVector
<DataLayoutEntryInterface
> entries
;
370 llvm::append_range(entries
, llvm::make_second_range(entriesForID
));
371 for (const auto &kvp
: entriesForType
)
372 llvm::append_range(entries
, kvp
.getSecond());
374 return DataLayoutSpecAttr::get(getContext(), entries
);
378 DataLayoutSpecAttr::getEndiannessIdentifier(MLIRContext
*context
) const {
379 return Builder(context
).getStringAttr(DLTIDialect::kDataLayoutEndiannessKey
);
383 DataLayoutSpecAttr::getAllocaMemorySpaceIdentifier(MLIRContext
*context
) const {
384 return Builder(context
).getStringAttr(
385 DLTIDialect::kDataLayoutAllocaMemorySpaceKey
);
388 StringAttr
DataLayoutSpecAttr::getProgramMemorySpaceIdentifier(
389 MLIRContext
*context
) const {
390 return Builder(context
).getStringAttr(
391 DLTIDialect::kDataLayoutProgramMemorySpaceKey
);
395 DataLayoutSpecAttr::getGlobalMemorySpaceIdentifier(MLIRContext
*context
) const {
396 return Builder(context
).getStringAttr(
397 DLTIDialect::kDataLayoutGlobalMemorySpaceKey
);
401 DataLayoutSpecAttr::getStackAlignmentIdentifier(MLIRContext
*context
) const {
402 return Builder(context
).getStringAttr(
403 DLTIDialect::kDataLayoutStackAlignmentKey
);
406 /// Parses an attribute with syntax:
407 /// dl-spec-attr ::= `#dlti.` `dl_spec` `<` entry-list `>`
408 /// entry-list ::= | entry | entry `,` entry-list
409 /// entry ::= ((type | quoted-string) = attr) | dl-entry-attr
410 Attribute
DataLayoutSpecAttr::parse(AsmParser
&parser
, Type type
) {
411 return parseAngleBracketedEntries
<DataLayoutSpecAttr
>(parser
, type
,
413 /*allowEmpty=*/true);
416 void DataLayoutSpecAttr::print(AsmPrinter
&printer
) const {
417 printAngleBracketedEntries(printer
, getEntries());
420 //===----------------------------------------------------------------------===//
421 // TargetDeviceSpecAttr
422 //===----------------------------------------------------------------------===//
425 TargetDeviceSpecAttr::verify(function_ref
<InFlightDiagnostic()> emitError
,
426 ArrayRef
<DataLayoutEntryInterface
> entries
) {
427 return verifyEntries(emitError
, entries
, /*allowTypes=*/false);
430 /// Parses an attribute with syntax:
431 /// dev-spec-attr ::= `#dlti.` `target_device_spec` `<` entry-list `>`
432 /// entry-list ::= entry | entry `,` entry-list
433 /// entry ::= (quoted-string `=` attr) | dl-entry-attr
434 Attribute
TargetDeviceSpecAttr::parse(AsmParser
&parser
, Type type
) {
435 return parseAngleBracketedEntries
<TargetDeviceSpecAttr
>(parser
, type
);
438 void TargetDeviceSpecAttr::print(AsmPrinter
&printer
) const {
439 printAngleBracketedEntries(printer
, getEntries());
442 //===----------------------------------------------------------------------===//
443 // TargetSystemSpecAttr
444 //===----------------------------------------------------------------------===//
447 TargetSystemSpecAttr::verify(function_ref
<InFlightDiagnostic()> emitError
,
448 ArrayRef
<DataLayoutEntryInterface
> entries
) {
449 DenseSet
<TargetSystemSpecInterface::DeviceID
> deviceIds
;
451 for (const auto &entry
: entries
) {
453 llvm::dyn_cast
<TargetSystemSpecInterface::DeviceID
>(entry
.getKey());
455 return emitError() << "non-string key of DLTI system spec";
457 if (auto targetDeviceSpec
=
458 llvm::dyn_cast
<TargetDeviceSpecInterface
>(entry
.getValue())) {
459 if (failed(TargetDeviceSpecAttr::verify(emitError
,
460 targetDeviceSpec
.getEntries())))
461 return failure(); // Assume sub-verifier outputted error message.
463 return emitError() << "value associated with key " << deviceId
464 << " is not a DLTI device spec";
467 // Check that device IDs are unique across all entries.
468 if (!deviceIds
.insert(deviceId
).second
)
469 return emitError() << "repeated device ID in dlti.target_system_spec: "
476 /// Parses an attribute with syntax:
477 /// sys-spec-attr ::= `#dlti.` `target_system_spec` `<` entry-list `>`
478 /// entry-list ::= entry | entry `,` entry-list
479 /// entry ::= (quoted-string `=` dev-spec-attr) | dl-entry-attr
480 Attribute
TargetSystemSpecAttr::parse(AsmParser
&parser
, Type type
) {
481 return parseAngleBracketedEntries
<TargetSystemSpecAttr
>(parser
, type
);
484 void TargetSystemSpecAttr::print(AsmPrinter
&printer
) const {
485 printAngleBracketedEntries(printer
, getEntries());
488 //===----------------------------------------------------------------------===//
490 //===----------------------------------------------------------------------===//
492 /// Retrieve the first `DLTIQueryInterface`-implementing attribute that is
493 /// attached to `op` or such an attr on as close as possible an ancestor. The
494 /// op the attribute is attached to is returned as well.
495 static std::pair
<DLTIQueryInterface
, Operation
*>
496 getClosestQueryable(Operation
*op
) {
497 DLTIQueryInterface queryable
= {};
499 // Search op and its ancestors for the first attached DLTIQueryInterface attr.
501 for (NamedAttribute attr
: op
->getAttrs())
502 if ((queryable
= dyn_cast
<DLTIQueryInterface
>(attr
.getValue())))
504 } while (!queryable
&& (op
= op
->getParentOp()));
506 return std::pair(queryable
, op
);
510 dlti::query(Operation
*op
, ArrayRef
<DataLayoutEntryKey
> keys
, bool emitError
) {
513 auto diag
= op
->emitError() << "target op of failed DLTI query";
514 diag
.attachNote(op
->getLoc()) << "no keys provided to attempt query with";
519 auto [queryable
, queryOp
] = getClosestQueryable(op
);
520 Operation
*reportOp
= (queryOp
? queryOp
: op
);
524 auto diag
= op
->emitError() << "target op of failed DLTI query";
525 diag
.attachNote(reportOp
->getLoc())
526 << "no DLTI-queryable attrs on target op or any of its ancestors";
531 Attribute currentAttr
= queryable
;
532 for (auto &&[idx
, key
] : llvm::enumerate(keys
)) {
533 if (auto map
= dyn_cast
<DLTIQueryInterface
>(currentAttr
)) {
534 auto maybeAttr
= map
.query(key
);
535 if (failed(maybeAttr
)) {
537 auto diag
= op
->emitError() << "target op of failed DLTI query";
538 diag
.attachNote(reportOp
->getLoc())
539 << "key " << keyToStr(key
)
540 << " has no DLTI-mapping per attr: " << map
;
544 currentAttr
= *maybeAttr
;
547 std::string commaSeparatedKeys
;
549 keys
.take_front(idx
), // All prior keys.
550 [&](auto key
) { commaSeparatedKeys
+= keyToStr(key
); },
551 [&]() { commaSeparatedKeys
+= ","; });
553 auto diag
= op
->emitError() << "target op of failed DLTI query";
554 diag
.attachNote(reportOp
->getLoc())
555 << "got non-DLTI-queryable attribute upon looking up keys ["
556 << commaSeparatedKeys
<< "] at op";
565 constexpr const StringLiteral
mlir::DLTIDialect::kDataLayoutAttrName
;
566 constexpr const StringLiteral
mlir::DLTIDialect::kDataLayoutEndiannessKey
;
567 constexpr const StringLiteral
mlir::DLTIDialect::kDataLayoutEndiannessBig
;
568 constexpr const StringLiteral
mlir::DLTIDialect::kDataLayoutEndiannessLittle
;
571 class TargetDataLayoutInterface
: public DataLayoutDialectInterface
{
573 using DataLayoutDialectInterface::DataLayoutDialectInterface
;
575 LogicalResult
verifyEntry(DataLayoutEntryInterface entry
,
576 Location loc
) const final
{
577 StringRef entryName
= entry
.getKey().get
<StringAttr
>().strref();
578 if (entryName
== DLTIDialect::kDataLayoutEndiannessKey
) {
579 auto value
= dyn_cast
<StringAttr
>(entry
.getValue());
581 (value
.getValue() == DLTIDialect::kDataLayoutEndiannessBig
||
582 value
.getValue() == DLTIDialect::kDataLayoutEndiannessLittle
))
584 return emitError(loc
) << "'" << entryName
585 << "' data layout entry is expected to be either '"
586 << DLTIDialect::kDataLayoutEndiannessBig
<< "' or '"
587 << DLTIDialect::kDataLayoutEndiannessLittle
<< "'";
589 if (entryName
== DLTIDialect::kDataLayoutAllocaMemorySpaceKey
||
590 entryName
== DLTIDialect::kDataLayoutProgramMemorySpaceKey
||
591 entryName
== DLTIDialect::kDataLayoutGlobalMemorySpaceKey
||
592 entryName
== DLTIDialect::kDataLayoutStackAlignmentKey
)
594 return emitError(loc
) << "unknown data layout entry name: " << entryName
;
599 void DLTIDialect::initialize() {
601 #define GET_ATTRDEF_LIST
602 #include "mlir/Dialect/DLTI/DLTIAttrs.cpp.inc"
604 addInterfaces
<TargetDataLayoutInterface
>();
607 LogicalResult
DLTIDialect::verifyOperationAttribute(Operation
*op
,
608 NamedAttribute attr
) {
609 if (attr
.getName() == DLTIDialect::kDataLayoutAttrName
) {
610 if (!llvm::isa
<DataLayoutSpecAttr
>(attr
.getValue())) {
611 return op
->emitError() << "'" << DLTIDialect::kDataLayoutAttrName
612 << "' is expected to be a #dlti.dl_spec attribute";
614 if (isa
<ModuleOp
>(op
))
615 return detail::verifyDataLayoutOp(op
);
619 if (attr
.getName() == DLTIDialect::kTargetSystemDescAttrName
) {
620 if (!llvm::isa
<TargetSystemSpecAttr
>(attr
.getValue())) {
621 return op
->emitError()
622 << "'" << DLTIDialect::kTargetSystemDescAttrName
623 << "' is expected to be a #dlti.target_system_spec attribute";
628 if (attr
.getName() == DLTIDialect::kMapAttrName
) {
629 if (!llvm::isa
<MapAttr
>(attr
.getValue())) {
630 return op
->emitError() << "'" << DLTIDialect::kMapAttrName
631 << "' is expected to be a #dlti.map attribute";
636 return op
->emitError() << "attribute '" << attr
.getName().getValue()
637 << "' not supported by dialect";