//===- DLTI.cpp - Data Layout And Target Info MLIR Dialect Implementation -===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "mlir/Dialect/DLTI/DLTI.h" #include "mlir/IR/Builders.h" #include "mlir/IR/BuiltinDialect.h" #include "mlir/IR/BuiltinOps.h" #include "mlir/IR/Dialect.h" #include "mlir/IR/DialectImplementation.h" #include "llvm/ADT/TypeSwitch.h" using namespace mlir; #include "mlir/Dialect/DLTI/DLTIDialect.cpp.inc" //===----------------------------------------------------------------------===// // DataLayoutEntryAttr //===----------------------------------------------------------------------===// // constexpr const StringLiteral mlir::DataLayoutEntryAttr::kAttrKeyword; namespace mlir { namespace impl { class DataLayoutEntryStorage : public AttributeStorage { public: using KeyTy = std::pair; DataLayoutEntryStorage(DataLayoutEntryKey entryKey, Attribute value) : entryKey(entryKey), value(value) {} static DataLayoutEntryStorage *construct(AttributeStorageAllocator &allocator, const KeyTy &key) { return new (allocator.allocate()) DataLayoutEntryStorage(key.first, key.second); } bool operator==(const KeyTy &other) const { return other.first == entryKey && other.second == value; } DataLayoutEntryKey entryKey; Attribute value; }; } // namespace impl } // namespace mlir DataLayoutEntryAttr DataLayoutEntryAttr::get(StringAttr key, Attribute value) { return Base::get(key.getContext(), key, value); } DataLayoutEntryAttr DataLayoutEntryAttr::get(Type key, Attribute value) { return Base::get(key.getContext(), key, value); } DataLayoutEntryKey DataLayoutEntryAttr::getKey() const { return getImpl()->entryKey; } Attribute DataLayoutEntryAttr::getValue() const { return getImpl()->value; } /// Parses an attribute with syntax: /// attr ::= `#target.` `dl_entry` `<` (type | quoted-string) `,` attr `>` DataLayoutEntryAttr DataLayoutEntryAttr::parse(AsmParser &parser) { if (failed(parser.parseLess())) return {}; Type type = nullptr; std::string identifier; SMLoc idLoc = parser.getCurrentLocation(); OptionalParseResult parsedType = parser.parseOptionalType(type); if (parsedType.has_value() && failed(parsedType.value())) return {}; if (!parsedType.has_value()) { OptionalParseResult parsedString = parser.parseOptionalString(&identifier); if (!parsedString.has_value() || failed(parsedString.value())) { parser.emitError(idLoc) << "expected a type or a quoted string"; return {}; } } Attribute value; if (failed(parser.parseComma()) || failed(parser.parseAttribute(value)) || failed(parser.parseGreater())) return {}; return type ? get(type, value) : get(parser.getBuilder().getStringAttr(identifier), value); } void DataLayoutEntryAttr::print(AsmPrinter &os) const { os << DataLayoutEntryAttr::kAttrKeyword << "<"; if (auto type = llvm::dyn_cast_if_present(getKey())) os << type; else os << "\"" << getKey().get().strref() << "\""; os << ", " << getValue() << ">"; } //===----------------------------------------------------------------------===// // DataLayoutSpecAttr //===----------------------------------------------------------------------===// // constexpr const StringLiteral mlir::DataLayoutSpecAttr::kAttrKeyword; constexpr const StringLiteral mlir::DLTIDialect::kDataLayoutAllocaMemorySpaceKey; constexpr const StringLiteral mlir::DLTIDialect::kDataLayoutProgramMemorySpaceKey; constexpr const StringLiteral mlir::DLTIDialect::kDataLayoutGlobalMemorySpaceKey; constexpr const StringLiteral mlir::DLTIDialect::kDataLayoutStackAlignmentKey; namespace mlir { namespace impl { class DataLayoutSpecStorage : public AttributeStorage { public: using KeyTy = ArrayRef; DataLayoutSpecStorage(ArrayRef entries) : entries(entries) {} bool operator==(const KeyTy &key) const { return key == entries; } static DataLayoutSpecStorage *construct(AttributeStorageAllocator &allocator, const KeyTy &key) { return new (allocator.allocate()) DataLayoutSpecStorage(allocator.copyInto(key)); } ArrayRef entries; }; } // namespace impl } // namespace mlir DataLayoutSpecAttr DataLayoutSpecAttr::get(MLIRContext *ctx, ArrayRef entries) { return Base::get(ctx, entries); } DataLayoutSpecAttr DataLayoutSpecAttr::getChecked(function_ref emitError, MLIRContext *context, ArrayRef entries) { return Base::getChecked(emitError, context, entries); } LogicalResult DataLayoutSpecAttr::verify(function_ref emitError, ArrayRef entries) { DenseSet types; DenseSet ids; for (DataLayoutEntryInterface entry : entries) { if (auto type = llvm::dyn_cast_if_present(entry.getKey())) { if (!types.insert(type).second) return emitError() << "repeated layout entry key: " << type; } else { auto id = entry.getKey().get(); if (!ids.insert(id).second) return emitError() << "repeated layout entry key: " << id.getValue(); } } return success(); } /// Given a list of old and a list of new entries, overwrites old entries with /// new ones if they have matching keys, appends new entries to the old entry /// list otherwise. static void overwriteDuplicateEntries(SmallVectorImpl &oldEntries, ArrayRef newEntries) { unsigned oldEntriesSize = oldEntries.size(); for (DataLayoutEntryInterface entry : newEntries) { // We expect a small (dozens) number of entries, so it is practically // cheaper to iterate over the list linearly rather than to create an // auxiliary hashmap to avoid duplication. Also note that we never need to // check for duplicate keys the values that were added from `newEntries`. bool replaced = false; for (unsigned i = 0; i < oldEntriesSize; ++i) { if (oldEntries[i].getKey() == entry.getKey()) { oldEntries[i] = entry; replaced = true; break; } } if (!replaced) oldEntries.push_back(entry); } } /// Combines a data layout spec into the given lists of entries organized by /// type class and identifier, overwriting them if necessary. Fails to combine /// if the two entries with identical keys are not compatible. static LogicalResult combineOneSpec(DataLayoutSpecInterface spec, DenseMap &entriesForType, DenseMap &entriesForID) { // A missing spec should be fine. if (!spec) return success(); DenseMap newEntriesForType; DenseMap newEntriesForID; spec.bucketEntriesByType(newEntriesForType, newEntriesForID); // Try overwriting the old entries with the new ones. for (auto &kvp : newEntriesForType) { if (!entriesForType.count(kvp.first)) { entriesForType[kvp.first] = std::move(kvp.second); continue; } Type typeSample = kvp.second.front().getKey().get(); assert(&typeSample.getDialect() != typeSample.getContext()->getLoadedDialect() && "unexpected data layout entry for built-in type"); auto interface = llvm::cast(typeSample); if (!interface.areCompatible(entriesForType.lookup(kvp.first), kvp.second)) return failure(); overwriteDuplicateEntries(entriesForType[kvp.first], kvp.second); } for (const auto &kvp : newEntriesForID) { StringAttr id = kvp.second.getKey().get(); Dialect *dialect = id.getReferencedDialect(); if (!entriesForID.count(id)) { entriesForID[id] = kvp.second; continue; } // Attempt to combine the enties using the dialect interface. If the // dialect is not loaded for some reason, use the default combinator // that conservatively accepts identical entries only. entriesForID[id] = dialect ? cast(dialect)->combine( entriesForID[id], kvp.second) : DataLayoutDialectInterface::defaultCombine(entriesForID[id], kvp.second); if (!entriesForID[id]) return failure(); } return success(); } DataLayoutSpecAttr DataLayoutSpecAttr::combineWith(ArrayRef specs) const { // Only combine with attributes of the same kind. // TODO: reconsider this when the need arises. if (llvm::any_of(specs, [](DataLayoutSpecInterface spec) { return !llvm::isa(spec); })) return {}; // Combine all specs in order, with `this` being the last one. DenseMap entriesForType; DenseMap entriesForID; for (DataLayoutSpecInterface spec : specs) if (failed(combineOneSpec(spec, entriesForType, entriesForID))) return nullptr; if (failed(combineOneSpec(*this, entriesForType, entriesForID))) return nullptr; // Rebuild the linear list of entries. SmallVector entries; llvm::append_range(entries, llvm::make_second_range(entriesForID)); for (const auto &kvp : entriesForType) llvm::append_range(entries, kvp.getSecond()); return DataLayoutSpecAttr::get(getContext(), entries); } DataLayoutEntryListRef DataLayoutSpecAttr::getEntries() const { return getImpl()->entries; } StringAttr DataLayoutSpecAttr::getAllocaMemorySpaceIdentifier(MLIRContext *context) const { return Builder(context).getStringAttr( DLTIDialect::kDataLayoutAllocaMemorySpaceKey); } StringAttr DataLayoutSpecAttr::getProgramMemorySpaceIdentifier( MLIRContext *context) const { return Builder(context).getStringAttr( DLTIDialect::kDataLayoutProgramMemorySpaceKey); } StringAttr DataLayoutSpecAttr::getGlobalMemorySpaceIdentifier(MLIRContext *context) const { return Builder(context).getStringAttr( DLTIDialect::kDataLayoutGlobalMemorySpaceKey); } StringAttr DataLayoutSpecAttr::getStackAlignmentIdentifier(MLIRContext *context) const { return Builder(context).getStringAttr( DLTIDialect::kDataLayoutStackAlignmentKey); } /// Parses an attribute with syntax /// attr ::= `#target.` `dl_spec` `<` attr-list? `>` /// attr-list ::= attr /// | attr `,` attr-list DataLayoutSpecAttr DataLayoutSpecAttr::parse(AsmParser &parser) { if (failed(parser.parseLess())) return {}; // Empty spec. if (succeeded(parser.parseOptionalGreater())) return get(parser.getContext(), {}); SmallVector entries; if (parser.parseCommaSeparatedList( [&]() { return parser.parseAttribute(entries.emplace_back()); }) || parser.parseGreater()) return {}; return getChecked([&] { return parser.emitError(parser.getNameLoc()); }, parser.getContext(), entries); } void DataLayoutSpecAttr::print(AsmPrinter &os) const { os << DataLayoutSpecAttr::kAttrKeyword << "<"; llvm::interleaveComma(getEntries(), os); os << ">"; } //===----------------------------------------------------------------------===// // DLTIDialect //===----------------------------------------------------------------------===// constexpr const StringLiteral mlir::DLTIDialect::kDataLayoutAttrName; constexpr const StringLiteral mlir::DLTIDialect::kDataLayoutEndiannessKey; constexpr const StringLiteral mlir::DLTIDialect::kDataLayoutEndiannessBig; constexpr const StringLiteral mlir::DLTIDialect::kDataLayoutEndiannessLittle; namespace { class TargetDataLayoutInterface : public DataLayoutDialectInterface { public: using DataLayoutDialectInterface::DataLayoutDialectInterface; LogicalResult verifyEntry(DataLayoutEntryInterface entry, Location loc) const final { StringRef entryName = entry.getKey().get().strref(); if (entryName == DLTIDialect::kDataLayoutEndiannessKey) { auto value = llvm::dyn_cast(entry.getValue()); if (value && (value.getValue() == DLTIDialect::kDataLayoutEndiannessBig || value.getValue() == DLTIDialect::kDataLayoutEndiannessLittle)) return success(); return emitError(loc) << "'" << entryName << "' data layout entry is expected to be either '" << DLTIDialect::kDataLayoutEndiannessBig << "' or '" << DLTIDialect::kDataLayoutEndiannessLittle << "'"; } if (entryName == DLTIDialect::kDataLayoutAllocaMemorySpaceKey || entryName == DLTIDialect::kDataLayoutProgramMemorySpaceKey || entryName == DLTIDialect::kDataLayoutGlobalMemorySpaceKey || entryName == DLTIDialect::kDataLayoutStackAlignmentKey) return success(); return emitError(loc) << "unknown data layout entry name: " << entryName; } }; } // namespace void DLTIDialect::initialize() { addAttributes(); addInterfaces(); } Attribute DLTIDialect::parseAttribute(DialectAsmParser &parser, Type type) const { StringRef attrKind; if (parser.parseKeyword(&attrKind)) return {}; if (attrKind == DataLayoutEntryAttr::kAttrKeyword) return DataLayoutEntryAttr::parse(parser); if (attrKind == DataLayoutSpecAttr::kAttrKeyword) return DataLayoutSpecAttr::parse(parser); parser.emitError(parser.getNameLoc(), "unknown attrribute type: ") << attrKind; return {}; } void DLTIDialect::printAttribute(Attribute attr, DialectAsmPrinter &os) const { llvm::TypeSwitch(attr) .Case( [&](auto a) { a.print(os); }) .Default([](Attribute) { llvm_unreachable("unknown attribute kind"); }); } LogicalResult DLTIDialect::verifyOperationAttribute(Operation *op, NamedAttribute attr) { if (attr.getName() == DLTIDialect::kDataLayoutAttrName) { if (!llvm::isa(attr.getValue())) { return op->emitError() << "'" << DLTIDialect::kDataLayoutAttrName << "' is expected to be a #dlti.dl_spec attribute"; } if (isa(op)) return detail::verifyDataLayoutOp(op); return success(); } return op->emitError() << "attribute '" << attr.getName().getValue() << "' not supported by dialect"; }