15 #include "llvm/ADT/TypeSwitch.h"
16 #include "llvm/Support/MathExtras.h"
28 llvm::raw_string_ostream os(message);
29 os <<
"neither the scoping op nor the type class provide data layout "
32 llvm::report_fatal_error(Twine(message));
40 auto attr = cast<IntegerAttr>(params.front().getValue());
41 return attr.getValue().getZExtValue();
54 if (isa<IntegerType, FloatType>(type))
57 if (
auto ctype = dyn_cast<ComplexType>(type)) {
58 Type et = ctype.getElementType();
59 uint64_t innerAlignment =
65 return llvm::alignTo(innerSize, innerAlignment) + innerSize;
69 if (isa<IndexType>(type))
78 if (
auto vecType = dyn_cast<VectorType>(type)) {
79 uint64_t baseSize = vecType.getNumElements() / vecType.getShape().back() *
80 llvm::PowerOf2Ceil(vecType.getShape().back()) *
81 dataLayout.
getTypeSize(vecType.getElementType()) * 8;
85 if (
auto typeInterface = dyn_cast<DataLayoutTypeInterface>(type))
86 return typeInterface.getTypeSizeInBits(dataLayout, params);
91 static DataLayoutEntryInterface
94 assert(!params.empty() &&
"expected non-empty parameter list");
95 std::map<unsigned, DataLayoutEntryInterface> sortedParams;
96 for (DataLayoutEntryInterface entry : params) {
97 sortedParams.insert(std::make_pair(
98 cast<Type>(entry.getKey()).getIntOrFloatBitWidth(), entry));
100 auto iter = sortedParams.lower_bound(intType.getWidth());
101 if (iter == sortedParams.end())
102 iter = std::prev(iter);
111 cast<DenseIntElementsAttr>(entry.getValue()).getValues<uint64_t>();
118 constexpr uint64_t kDefaultSmallIntAlignment = 4u;
119 constexpr
unsigned kSmallIntSize = 64;
120 if (params.empty()) {
121 return intType.getWidth() < kSmallIntSize
122 ? llvm::PowerOf2Ceil(
124 : kDefaultSmallIntAlignment;
133 assert(params.size() <= 1 &&
"at most one data layout entry is expected for "
134 "the singleton floating-point type");
136 return llvm::PowerOf2Ceil(dataLayout.
getTypeSize(fltType).getFixedValue());
145 if (isa<VectorType>(type))
146 return llvm::PowerOf2Ceil(dataLayout.
getTypeSize(type).getKnownMinValue());
148 if (
auto fltType = dyn_cast<FloatType>(type))
152 if (isa<IndexType>(type))
156 if (
auto intType = dyn_cast<IntegerType>(type))
159 if (
auto ctype = dyn_cast<ComplexType>(type))
162 if (
auto typeInterface = dyn_cast<DataLayoutTypeInterface>(type))
163 return typeInterface.getABIAlignment(dataLayout, params);
170 cast<DenseIntElementsAttr>(entry.getValue()).getValues<uint64_t>();
179 return llvm::PowerOf2Ceil(dataLayout.
getTypeSize(intType).getFixedValue());
187 assert(params.size() <= 1 &&
"at most one data layout entry is expected for "
188 "the singleton floating-point type");
198 if (isa<VectorType>(type))
201 if (
auto fltType = dyn_cast<FloatType>(type))
206 if (
auto intType = dyn_cast<IntegerType>(type))
209 if (isa<IndexType>(type)) {
214 if (
auto ctype = dyn_cast<ComplexType>(type))
218 if (
auto typeInterface = dyn_cast<DataLayoutTypeInterface>(type))
219 return typeInterface.getPreferredAlignment(dataLayout, params);
227 if (isa<IndexType>(type))
230 if (
auto typeInterface = dyn_cast<DataLayoutTypeInterface>(type))
231 if (std::optional<uint64_t> indexBitwidth =
232 typeInterface.getIndexBitwidth(dataLayout, params))
233 return *indexBitwidth;
243 if (entry == DataLayoutEntryInterface())
246 return entry.getValue();
254 if (entry == DataLayoutEntryInterface()) {
258 return entry.getValue();
266 if (entry == DataLayoutEntryInterface()) {
270 return entry.getValue();
278 if (entry == DataLayoutEntryInterface()) {
282 return entry.getValue();
289 if (entry == DataLayoutEntryInterface())
292 auto value = cast<IntegerAttr>(entry.getValue());
293 return value.getValue().getZExtValue();
296 std::optional<Attribute>
298 if (entry == DataLayoutEntryInterface())
301 return entry.getValue();
307 return llvm::filter_to_vector<4>(
308 entries, [typeID](DataLayoutEntryInterface entry) {
309 auto type = llvm::dyn_cast_if_present<Type>(entry.getKey());
310 return type && type.getTypeID() == typeID;
314 DataLayoutEntryInterface
317 const auto *it = llvm::find_if(entries, [
id](DataLayoutEntryInterface entry) {
318 if (
auto attr = dyn_cast<StringAttr>(entry.getKey()))
322 return it == entries.end() ? DataLayoutEntryInterface() : *it;
327 .Case<ModuleOp, DataLayoutOpInterface>(
328 [&](
auto op) {
return op.getDataLayoutSpec(); })
330 llvm_unreachable(
"expected an op with data layout spec");
331 return DataLayoutSpecInterface();
337 ModuleOp moduleOp = dyn_cast<ModuleOp>(operation);
340 return moduleOp.getTargetSystemSpec();
342 return TargetSystemSpecInterface();
357 .Case<ModuleOp>([&](ModuleOp op) {
363 if (!op->getParentOp() && !op.getDataLayoutSpec())
365 specs.push_back(op.getDataLayoutSpec());
367 opLocations->push_back(op.getLoc());
369 .Case<DataLayoutOpInterface>([&](DataLayoutOpInterface op) {
370 specs.push_back(op.getDataLayoutSpec());
372 opLocations->push_back(op.getLoc());
383 assert((isa<ModuleOp, DataLayoutOpInterface>(leaf)) &&
384 "expected an op with data layout spec");
396 auto nonNullSpecs = llvm::filter_to_vector<2>(
397 llvm::reverse(specs),
398 [](DataLayoutSpecInterface iface) {
return iface !=
nullptr; });
401 if (DataLayoutSpecInterface current =
getSpec(leaf))
402 return current.combineWith(nonNullSpecs);
403 if (nonNullSpecs.empty())
405 return nonNullSpecs.back().combineWith(
410 DataLayoutSpecInterface spec =
getSpec(op);
415 if (failed(spec.verifySpec(op->
getLoc())))
420 <<
"data layout does not combine with layouts of enclosing ops";
425 diag.attachNote(loc) <<
"enclosing op with data layout";
432 uint64_t denominator) {
442 template <
typename OpTy>
444 if (!originalLayout) {
445 assert((!op || !op.getDataLayoutSpec()) &&
446 "could not compute layout information for an op (failed to "
447 "combine attributes?)");
456 allocaMemorySpace(std::nullopt), programMemorySpace(std::nullopt),
457 globalMemorySpace(std::nullopt), stackAlignment(std::nullopt) {
458 #if LLVM_ENABLE_ABI_BREAKING_CHECKS
467 allocaMemorySpace(std::nullopt), programMemorySpace(std::nullopt),
468 globalMemorySpace(std::nullopt), stackAlignment(std::nullopt) {
469 #if LLVM_ENABLE_ABI_BREAKING_CHECKS
479 if (
auto module = dyn_cast<ModuleOp>(op))
481 if (
auto iface = dyn_cast<DataLayoutOpInterface>(op))
488 void mlir::DataLayout::checkValid()
const {
489 #if LLVM_ENABLE_ABI_BREAKING_CHECKS
492 assert(specs.size() == layoutStack.size() &&
493 "data layout object used, but no longer valid due to the change in "
494 "number of nested layouts");
495 for (
auto pair : llvm::zip(specs, layoutStack)) {
497 Attribute origLayout = std::get<1>(pair);
498 assert(newLayout == origLayout &&
499 "data layout object used, but no longer valid "
500 "due to the change in layout attributes");
503 assert(((!scope && !this->originalLayout) ||
505 "data layout object used, but no longer valid due to the change in "
512 template <
typename T>
515 auto it = cache.find(t);
516 if (it != cache.end())
519 auto result = cache.try_emplace(t, compute(t));
520 return result.first->second;
525 return cachedLookup<llvm::TypeSize>(t, sizes, [&](
Type ty) {
528 list = originalLayout.getSpecForType(ty.
getTypeID());
529 if (
auto iface = dyn_cast_or_null<DataLayoutOpInterface>(scope))
530 return iface.getTypeSize(ty, *
this, list);
537 return cachedLookup<llvm::TypeSize>(t, bitsizes, [&](
Type ty) {
540 list = originalLayout.getSpecForType(ty.
getTypeID());
541 if (
auto iface = dyn_cast_or_null<DataLayoutOpInterface>(scope))
542 return iface.getTypeSizeInBits(ty, *
this, list);
549 return cachedLookup<uint64_t>(t, abiAlignments, [&](
Type ty) {
552 list = originalLayout.getSpecForType(ty.
getTypeID());
553 if (
auto iface = dyn_cast_or_null<DataLayoutOpInterface>(scope))
554 return iface.getTypeABIAlignment(ty, *
this, list);
561 return cachedLookup<uint64_t>(t, preferredAlignments, [&](
Type ty) {
564 list = originalLayout.getSpecForType(ty.
getTypeID());
565 if (
auto iface = dyn_cast_or_null<DataLayoutOpInterface>(scope))
566 return iface.getTypePreferredAlignment(ty, *
this, list);
573 return cachedLookup<std::optional<uint64_t>>(t, indexBitwidths, [&](
Type ty) {
576 list = originalLayout.getSpecForType(ty.getTypeID());
577 if (
auto iface = dyn_cast_or_null<DataLayoutOpInterface>(scope))
578 return iface.getIndexBitwidth(ty, *
this, list);
587 DataLayoutEntryInterface entry;
589 entry = originalLayout.getSpecForIdentifier(
590 originalLayout.getEndiannessIdentifier(originalLayout.getContext()));
592 if (
auto iface = dyn_cast_or_null<DataLayoutOpInterface>(scope))
593 endianness = iface.getEndianness(entry);
601 if (allocaMemorySpace)
602 return *allocaMemorySpace;
603 DataLayoutEntryInterface entry;
605 entry = originalLayout.getSpecForIdentifier(
606 originalLayout.getAllocaMemorySpaceIdentifier(
607 originalLayout.getContext()));
608 if (
auto iface = dyn_cast_or_null<DataLayoutOpInterface>(scope))
609 allocaMemorySpace = iface.getAllocaMemorySpace(entry);
612 return *allocaMemorySpace;
617 if (programMemorySpace)
618 return *programMemorySpace;
619 DataLayoutEntryInterface entry;
621 entry = originalLayout.getSpecForIdentifier(
622 originalLayout.getProgramMemorySpaceIdentifier(
623 originalLayout.getContext()));
624 if (
auto iface = dyn_cast_or_null<DataLayoutOpInterface>(scope))
625 programMemorySpace = iface.getProgramMemorySpace(entry);
628 return *programMemorySpace;
633 if (globalMemorySpace)
634 return *globalMemorySpace;
635 DataLayoutEntryInterface entry;
637 entry = originalLayout.getSpecForIdentifier(
638 originalLayout.getGlobalMemorySpaceIdentifier(
639 originalLayout.getContext()));
640 if (
auto iface = dyn_cast_or_null<DataLayoutOpInterface>(scope))
641 globalMemorySpace = iface.getGlobalMemorySpace(entry);
644 return *globalMemorySpace;
650 return *stackAlignment;
651 DataLayoutEntryInterface entry;
653 entry = originalLayout.getSpecForIdentifier(
654 originalLayout.getStackAlignmentIdentifier(
655 originalLayout.getContext()));
656 if (
auto iface = dyn_cast_or_null<DataLayoutOpInterface>(scope))
657 stackAlignment = iface.getStackAlignment(entry);
660 return *stackAlignment;
664 TargetSystemSpecInterface::DeviceID deviceID,
665 StringAttr propertyName)
const {
667 DataLayoutEntryInterface entry;
668 if (originalTargetSystemDesc) {
669 if (std::optional<TargetDeviceSpecInterface> device =
670 originalTargetSystemDesc.getDeviceSpecForDeviceID(deviceID))
671 entry = device->getSpecForIdentifier(propertyName);
677 if (
auto iface = dyn_cast_or_null<DataLayoutOpInterface>(scope))
678 return iface.getDevicePropertyValue(entry);
687 void DataLayoutSpecInterface::bucketEntriesByType(
690 for (DataLayoutEntryInterface entry : getEntries()) {
691 if (
auto type = llvm::dyn_cast_if_present<Type>(entry.getKey()))
692 types[type.getTypeID()].push_back(entry);
694 ids[llvm::cast<StringAttr>(entry.getKey())] = entry;
701 for (DataLayoutEntryInterface entry : spec.getEntries())
702 if (failed(entry.verifyEntry(loc)))
709 spec.bucketEntriesByType(types, ids);
711 for (
const auto &kvp : types) {
712 auto sampleType = cast<Type>(kvp.second.front().getKey());
713 if (isa<IndexType>(sampleType)) {
714 assert(kvp.second.size() == 1 &&
715 "expected one data layout entry for non-parametric 'index' type");
716 if (!isa<IntegerAttr>(kvp.second.front().getValue()))
718 <<
"expected integer attribute in the data layout entry for "
723 if (isa<IntegerType, FloatType>(sampleType)) {
724 for (DataLayoutEntryInterface entry : kvp.second) {
725 auto value = dyn_cast<DenseIntElementsAttr>(entry.getValue());
726 if (!value || !value.getElementType().isSignlessInteger(64)) {
727 emitError(loc) <<
"expected a dense i64 elements attribute in the "
733 auto elements = llvm::to_vector<2>(value.getValues<uint64_t>());
734 unsigned numElements = elements.size();
735 if (numElements < 1 || numElements > 2) {
736 emitError(loc) <<
"expected 1 or 2 elements in the data layout entry "
741 uint64_t abi = elements[0];
742 uint64_t preferred = numElements == 2 ? elements[1] : abi;
743 if (preferred < abi) {
745 <<
"preferred alignment is expected to be greater than or equal "
746 "to the abi alignment in data layout entry "
754 if (isa<BuiltinDialect>(&sampleType.getDialect()))
755 return emitError(loc) <<
"unexpected data layout for a built-in type";
757 auto dlType = dyn_cast<DataLayoutTypeInterface>(sampleType);
760 <<
"data layout specified for a type that does not support it";
761 if (failed(dlType.verifyEntries(kvp.second, loc)))
765 for (
const auto &kvp : ids) {
766 StringAttr identifier = cast<StringAttr>(kvp.second.getKey());
767 Dialect *dialect = identifier.getReferencedDialect();
774 const auto *iface = dyn_cast<DataLayoutDialectInterface>(dialect);
778 <<
"' dialect does not support identifier data layout entries";
780 if (failed(iface->verifyEntry(kvp.second, loc)))
792 for (
const auto &entry : spec.getEntries()) {
793 auto targetDeviceSpec =
794 dyn_cast<TargetDeviceSpecInterface>(entry.getValue());
796 if (!targetDeviceSpec)
800 if (failed(targetDeviceSpec.verifyEntry(loc)))
805 llvm::dyn_cast<TargetSystemSpecInterface::DeviceID>(entry.getKey());
809 if (!deviceIDs.insert(deviceID).second) {
814 for (DataLayoutEntryInterface entry : targetDeviceSpec.getEntries()) {
815 if (
auto type = llvm::dyn_cast_if_present<Type>(entry.getKey())) {
819 deviceDescKeys[cast<StringAttr>(entry.getKey())] = entry;
824 for (
const auto &[keyName, keyVal] : deviceDescKeys) {
825 Dialect *dialect = keyName.getReferencedDialect();
832 const auto *iface = dyn_cast<DataLayoutDialectInterface>(dialect);
836 <<
"' dialect does not support identifier data layout entries";
838 if (failed(iface->verifyEntry(keyVal, loc)))
845 #include "mlir/Interfaces/DataLayoutAttrInterface.cpp.inc"
846 #include "mlir/Interfaces/DataLayoutOpInterface.cpp.inc"
847 #include "mlir/Interfaces/DataLayoutTypeInterface.cpp.inc"
static uint64_t getIntegerTypePreferredAlignment(IntegerType intType, const DataLayout &dataLayout, ArrayRef< DataLayoutEntryInterface > params)
static DataLayoutSpecInterface getCombinedDataLayout(Operation *leaf)
Returns a layout spec that is a combination of the layout specs attached to the given operation and a...
static TargetSystemSpecInterface getTargetSystemSpec(Operation *operation)
constexpr static const uint64_t kDefaultBitsInByte
static uint64_t getIntegerTypeABIAlignment(IntegerType intType, ArrayRef< DataLayoutEntryInterface > params)
static T cachedLookup(Type t, DenseMap< Type, T > &cache, function_ref< T(Type)> compute)
Looks up the value for the given type key in the given cache.
static uint64_t extractPreferredAlignment(DataLayoutEntryInterface entry)
static void collectParentLayouts(Operation *leaf, SmallVectorImpl< DataLayoutSpecInterface > &specs, SmallVectorImpl< Location > *opLocations=nullptr)
Populates opsWithLayout with the list of proper ancestors of leaf that are either modules or implemen...
static uint64_t getFloatTypePreferredAlignment(FloatType fltType, const DataLayout &dataLayout, ArrayRef< DataLayoutEntryInterface > params)
static uint64_t getFloatTypeABIAlignment(FloatType fltType, const DataLayout &dataLayout, ArrayRef< DataLayoutEntryInterface > params)
static DataLayoutSpecInterface getSpec(Operation *operation)
static uint64_t getIndexBitwidth(DataLayoutEntryListRef params)
Returns the bitwidth of the index type if specified in the param list.
static uint64_t extractABIAlignment(DataLayoutEntryInterface entry)
static DataLayoutEntryInterface findEntryForIntegerType(IntegerType intType, ArrayRef< DataLayoutEntryInterface > params)
void checkMissingLayout(DataLayoutSpecInterface originalLayout, OpTy op)
static void reportMissingDataLayout(Type type)
Reports that the given type is missing the data layout information and exits.
static std::string diag(const llvm::Value &value)
Attributes are known-constant values of operations.
The main mechanism for performing data layout queries.
Attribute getAllocaMemorySpace() const
Returns the memory space used for AllocaOps.
static DataLayout closest(Operation *op)
Returns the layout of the closest parent operation carrying layout info.
std::optional< uint64_t > getTypeIndexBitwidth(Type t) const
Returns the bitwidth that should be used when performing index computations for the given pointer-lik...
llvm::TypeSize getTypeSize(Type t) const
Returns the size of the given type in the current scope.
uint64_t getStackAlignment() const
Returns the natural alignment of the stack in bits.
Attribute getProgramMemorySpace() const
Returns the memory space used for program memory operations.
uint64_t getTypePreferredAlignment(Type t) const
Returns the preferred of the given type in the current scope.
Attribute getGlobalMemorySpace() const
Returns the memory space used for global operations.
uint64_t getTypeABIAlignment(Type t) const
Returns the required alignment of the given type in the current scope.
llvm::TypeSize getTypeSizeInBits(Type t) const
Returns the size in bits of the given type in the current scope.
Attribute getEndianness() const
Returns the specified endianness.
std::optional< Attribute > getDevicePropertyValue(TargetSystemSpecInterface::DeviceID, StringAttr propertyName) const
Returns the value of the specified property if the property is defined for the given device ID,...
Dialects are groups of MLIR operations, types and attributes, as well as behavior associated with the...
StringRef getNamespace() const
This class represents a diagnostic that is inflight and set to be reported.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Operation is the basic unit of execution within MLIR.
Location getLoc()
The source location the operation was defined or derived from.
Operation * getParentOp()
Returns the closest surrounding operation that contains this operation or nullptr if this is a top-le...
InFlightDiagnostic emitError(const Twine &message={})
Emit an error about fatal conditions with this operation, reporting up to any diagnostic handlers tha...
OpTy getParentOfType()
Return the closest surrounding parent operation that is of type 'OpTy'.
This class provides an efficient unique identifier for a specific C++ type.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
MLIRContext * getContext() const
Return the MLIRContext in which this type was uniqued.
TypeID getTypeID()
Return a unique identifier for the concrete type.
unsigned getIntOrFloatBitWidth() const
Return the bit width of an integer or a float type, assert failure on other types.
Attribute getDefaultAllocaMemorySpace(DataLayoutEntryInterface entry)
Default handler for alloca memory space request.
Attribute getDefaultProgramMemorySpace(DataLayoutEntryInterface entry)
Default handler for program memory space request.
Attribute getDefaultEndianness(DataLayoutEntryInterface entry)
Default handler for endianness request.
std::optional< uint64_t > getDefaultIndexBitwidth(Type type, const DataLayout &dataLayout, ArrayRef< DataLayoutEntryInterface > params)
Default handler for the index bitwidth request.
DataLayoutEntryList filterEntriesForType(DataLayoutEntryListRef entries, TypeID typeID)
Given a list of data layout entries, returns a new list containing the entries with keys having the g...
LogicalResult verifyTargetSystemSpec(TargetSystemSpecInterface spec, Location loc)
Verifies that a target system desc spec is valid.
std::optional< Attribute > getDevicePropertyValue(DataLayoutEntryInterface entry)
Returns the value of the property from the specified DataLayoutEntry.
uint64_t getDefaultABIAlignment(Type type, const DataLayout &dataLayout, ArrayRef< DataLayoutEntryInterface > params)
Default handler for the required alignment request.
llvm::TypeSize getDefaultTypeSize(Type type, const DataLayout &dataLayout, DataLayoutEntryListRef params)
Default handler for the type size request.
llvm::TypeSize getDefaultTypeSizeInBits(Type type, const DataLayout &dataLayout, DataLayoutEntryListRef params)
Default handler for the type size in bits request.
uint64_t getDefaultPreferredAlignment(Type type, const DataLayout &dataLayout, ArrayRef< DataLayoutEntryInterface > params)
Default handler for the preferred alignment request.
llvm::TypeSize divideCeil(llvm::TypeSize numerator, uint64_t denominator)
Divides the known min value of the numerator by the denominator and rounds the result up to the next ...
uint64_t getDefaultStackAlignment(DataLayoutEntryInterface entry)
Default handler for the stack alignment request.
Attribute getDefaultGlobalMemorySpace(DataLayoutEntryInterface entry)
Default handler for global memory space request.
LogicalResult verifyDataLayoutOp(Operation *op)
Verifies that the operation implementing the data layout interface, or a module operation,...
LogicalResult verifyDataLayoutSpec(DataLayoutSpecInterface spec, Location loc)
Verifies that a data layout spec is valid.
DataLayoutEntryInterface filterEntryForIdentifier(DataLayoutEntryListRef entries, StringAttr id)
Given a list of data layout entries, returns the entry that has the given identifier as key,...
Include the generated interface declarations.
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.
auto get(MLIRContext *context, Ts &&...params)
Helper method that injects context only if needed, this helps unify some of the attribute constructio...