MLIR 22.0.0git
DataLayoutInterfaces.cpp
Go to the documentation of this file.
1//===- DataLayoutInterfaces.cpp - Data Layout Interface Implementation ----===//
2//
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
6//
7//===----------------------------------------------------------------------===//
8
11#include "mlir/IR/BuiltinOps.h"
13#include "mlir/IR/Operation.h"
14
15#include "llvm/ADT/TypeSwitch.h"
16#include "llvm/Support/MathExtras.h"
17
18using namespace mlir;
19
20//===----------------------------------------------------------------------===//
21// Default implementations
22//===----------------------------------------------------------------------===//
23
24/// Reports that the given type is missing the data layout information and
25/// exits.
26[[noreturn]] static void reportMissingDataLayout(Type type) {
27 std::string message;
28 llvm::raw_string_ostream os(message);
29 os << "neither the scoping op nor the type class provide data layout "
30 "information for "
31 << type;
32 llvm::report_fatal_error(Twine(message));
33}
34
35/// Returns the bitwidth of the index type if specified in the param list.
36/// Assumes 64-bit index otherwise.
38 if (params.empty())
39 return 64;
40 auto attr = cast<IntegerAttr>(params.front().getValue());
41 return attr.getValue().getZExtValue();
42}
43
44llvm::TypeSize
47 llvm::TypeSize bits = getDefaultTypeSizeInBits(type, dataLayout, params);
48 return divideCeil(bits, 8);
49}
50
51llvm::TypeSize
54 if (type.isIntOrFloat())
55 return llvm::TypeSize::getFixed(type.getIntOrFloatBitWidth());
56
57 if (auto ctype = dyn_cast<ComplexType>(type)) {
58 Type et = ctype.getElementType();
59 uint64_t innerAlignment =
60 getDefaultPreferredAlignment(et, dataLayout, params) * 8;
61 llvm::TypeSize innerSize = getDefaultTypeSizeInBits(et, dataLayout, params);
62
63 // Include padding required to align the imaginary value in the complex
64 // type.
65 return llvm::alignTo(innerSize, innerAlignment) + innerSize;
66 }
67
68 // Index is an integer of some bitwidth.
69 if (isa<IndexType>(type))
70 return dataLayout.getTypeSizeInBits(
71 IntegerType::get(type.getContext(), getIndexBitwidth(params)));
72
73 // Sizes of vector types are rounded up to those of types with closest
74 // power-of-two number of elements in the innermost dimension. We also assume
75 // there is no bit-packing at the moment element sizes are taken in bytes and
76 // multiplied with 8 bits.
77 // TODO: make this extensible.
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;
82 return llvm::TypeSize::get(baseSize, vecType.isScalable());
83 }
84
85 if (auto typeInterface = dyn_cast<DataLayoutTypeInterface>(type))
86 return typeInterface.getTypeSizeInBits(dataLayout, params);
87
89}
90
91static DataLayoutEntryInterface
92findEntryForIntegerType(IntegerType intType,
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));
99 }
100 auto iter = sortedParams.lower_bound(intType.getWidth());
101 if (iter == sortedParams.end())
102 iter = std::prev(iter);
103
104 return iter->second;
105}
106
107constexpr const static uint64_t kDefaultBitsInByte = 8u;
108
109static uint64_t extractABIAlignment(DataLayoutEntryInterface entry) {
110 auto values =
111 cast<DenseIntElementsAttr>(entry.getValue()).getValues<uint64_t>();
112 return static_cast<uint64_t>(*values.begin()) / kDefaultBitsInByte;
113}
114
115static uint64_t
116getIntegerTypeABIAlignment(IntegerType intType,
118 constexpr uint64_t kDefaultSmallIntAlignment = 4u;
119 constexpr unsigned kSmallIntSize = 64;
120 if (params.empty()) {
121 return intType.getWidth() < kSmallIntSize
122 ? llvm::PowerOf2Ceil(
123 llvm::divideCeil(intType.getWidth(), kDefaultBitsInByte))
124 : kDefaultSmallIntAlignment;
125 }
126
127 return extractABIAlignment(findEntryForIntegerType(intType, params));
128}
129
130static uint64_t
131getFloatTypeABIAlignment(FloatType fltType, const DataLayout &dataLayout,
133 assert(params.size() <= 1 && "at most one data layout entry is expected for "
134 "the singleton floating-point type");
135 if (params.empty())
136 return llvm::PowerOf2Ceil(dataLayout.getTypeSize(fltType).getFixedValue());
137 return extractABIAlignment(params[0]);
138}
139
141 Type type, const DataLayout &dataLayout,
143 // Natural alignment is the closest power-of-two number above. For scalable
144 // vectors, aligning them to the same as the base vector is sufficient.
145 if (isa<VectorType>(type))
146 return llvm::PowerOf2Ceil(dataLayout.getTypeSize(type).getKnownMinValue());
147
148 if (auto fltType = dyn_cast<FloatType>(type))
149 return getFloatTypeABIAlignment(fltType, dataLayout, params);
150
151 // Index is an integer of some bitwidth.
152 if (isa<IndexType>(type))
153 return dataLayout.getTypeABIAlignment(
154 IntegerType::get(type.getContext(), getIndexBitwidth(params)));
155
156 if (auto intType = dyn_cast<IntegerType>(type))
157 return getIntegerTypeABIAlignment(intType, params);
158
159 if (auto ctype = dyn_cast<ComplexType>(type))
160 return getDefaultABIAlignment(ctype.getElementType(), dataLayout, params);
161
162 if (auto typeInterface = dyn_cast<DataLayoutTypeInterface>(type))
163 return typeInterface.getABIAlignment(dataLayout, params);
164
166}
167
168static uint64_t extractPreferredAlignment(DataLayoutEntryInterface entry) {
169 auto values =
170 cast<DenseIntElementsAttr>(entry.getValue()).getValues<uint64_t>();
171 return *std::next(values.begin(), values.size() - 1) / kDefaultBitsInByte;
172}
173
174static uint64_t
176 const DataLayout &dataLayout,
178 if (params.empty())
179 return llvm::PowerOf2Ceil(dataLayout.getTypeSize(intType).getFixedValue());
180
181 return extractPreferredAlignment(findEntryForIntegerType(intType, params));
182}
183
184static uint64_t
185getFloatTypePreferredAlignment(FloatType fltType, const DataLayout &dataLayout,
187 assert(params.size() <= 1 && "at most one data layout entry is expected for "
188 "the singleton floating-point type");
189 if (params.empty())
190 return dataLayout.getTypeABIAlignment(fltType);
191 return extractPreferredAlignment(params[0]);
192}
193
195 Type type, const DataLayout &dataLayout,
197 // Preferred alignment is same as natural for floats and vectors.
198 if (isa<VectorType>(type))
199 return dataLayout.getTypeABIAlignment(type);
200
201 if (auto fltType = dyn_cast<FloatType>(type))
202 return getFloatTypePreferredAlignment(fltType, dataLayout, params);
203
204 // Preferred alignment is the closest power-of-two number above for integers
205 // (ABI alignment may be smaller).
206 if (auto intType = dyn_cast<IntegerType>(type))
207 return getIntegerTypePreferredAlignment(intType, dataLayout, params);
208
209 if (isa<IndexType>(type)) {
210 return dataLayout.getTypePreferredAlignment(
211 IntegerType::get(type.getContext(), getIndexBitwidth(params)));
212 }
213
214 if (auto ctype = dyn_cast<ComplexType>(type))
215 return getDefaultPreferredAlignment(ctype.getElementType(), dataLayout,
216 params);
217
218 if (auto typeInterface = dyn_cast<DataLayoutTypeInterface>(type))
219 return typeInterface.getPreferredAlignment(dataLayout, params);
220
222}
223
225 Type type, const DataLayout &dataLayout,
227 if (isa<IndexType>(type))
228 return getIndexBitwidth(params);
229
230 if (auto typeInterface = dyn_cast<DataLayoutTypeInterface>(type))
231 if (std::optional<uint64_t> indexBitwidth =
232 typeInterface.getIndexBitwidth(dataLayout, params))
233 return *indexBitwidth;
234
235 // Return std::nullopt for all other types, which are assumed to be non
236 // pointer-like types.
237 return std::nullopt;
238}
239
240// Returns the endianness if specified in the given entry. If the entry is empty
241// the default endianness represented by an empty attribute is returned.
242Attribute mlir::detail::getDefaultEndianness(DataLayoutEntryInterface entry) {
243 if (entry == DataLayoutEntryInterface())
244 return Attribute();
245
246 return entry.getValue();
247}
248
249// Returns the default memory space if specified in the given entry. If the
250// entry is empty the default memory space represented by an empty attribute is
251// returned.
252Attribute mlir::detail::getDefaultMemorySpace(DataLayoutEntryInterface entry) {
253 if (!entry)
254 return Attribute();
255
256 return entry.getValue();
257}
258
259// Returns the memory space used for alloca operations if specified in the
260// given entry. If the entry is empty the default memory space represented by
261// an empty attribute is returned.
263mlir::detail::getDefaultAllocaMemorySpace(DataLayoutEntryInterface entry) {
264 if (entry == DataLayoutEntryInterface()) {
265 return Attribute();
266 }
267
268 return entry.getValue();
269}
270
271// Returns the mangling mode if specified in the given entry.
272// If the entry is empty, an empty attribute is returned.
273Attribute mlir::detail::getDefaultManglingMode(DataLayoutEntryInterface entry) {
274 if (entry == DataLayoutEntryInterface())
275 return Attribute();
276
277 return entry.getValue();
278}
279
280// Returns the memory space used for the program memory space. if
281// specified in the given entry. If the entry is empty the default
282// memory space represented by an empty attribute is returned.
284mlir::detail::getDefaultProgramMemorySpace(DataLayoutEntryInterface entry) {
285 if (entry == DataLayoutEntryInterface()) {
286 return Attribute();
287 }
288
289 return entry.getValue();
290}
291
292// Returns the memory space used for global the global memory space. if
293// specified in the given entry. If the entry is empty the default memory
294// space represented by an empty attribute is returned.
296mlir::detail::getDefaultGlobalMemorySpace(DataLayoutEntryInterface entry) {
297 if (entry == DataLayoutEntryInterface()) {
298 return Attribute();
299 }
300
301 return entry.getValue();
302}
303
304// Returns the stack alignment if specified in the given entry. If the entry is
305// empty the default alignment zero is returned.
306uint64_t
307mlir::detail::getDefaultStackAlignment(DataLayoutEntryInterface entry) {
308 if (entry == DataLayoutEntryInterface())
309 return 0;
310
311 auto value = cast<IntegerAttr>(entry.getValue());
312 return value.getValue().getZExtValue();
313}
314
315// Returns the function pointer alignment if specified in the given entry. If
316// the entry is empty the default alignment zero is returned.
318 DataLayoutEntryInterface entry) {
319 if (entry == DataLayoutEntryInterface())
320 return Attribute();
321 return entry.getValue();
322}
323
324// Returns the legal int widths if specified in the given entry. If the entry is
325// empty the default legal int widths represented by an empty attribute is
326// returned.
328mlir::detail::getDefaultLegalIntWidths(DataLayoutEntryInterface entry) {
329 if (entry == DataLayoutEntryInterface())
330 return Attribute();
331 return entry.getValue();
332}
333
334std::optional<Attribute>
335mlir::detail::getDevicePropertyValue(DataLayoutEntryInterface entry) {
336 if (entry == DataLayoutEntryInterface())
337 return std::nullopt;
338
339 return entry.getValue();
340}
341
344 TypeID typeID) {
345 return llvm::filter_to_vector<4>(
346 entries, [typeID](DataLayoutEntryInterface entry) {
347 auto type = llvm::dyn_cast_if_present<Type>(entry.getKey());
348 return type && type.getTypeID() == typeID;
349 });
350}
351
352DataLayoutEntryInterface
354 StringAttr id) {
355 const auto *it = llvm::find_if(entries, [id](DataLayoutEntryInterface entry) {
356 if (auto attr = dyn_cast<StringAttr>(entry.getKey()))
357 return attr == id;
358 return false;
359 });
360 return it == entries.end() ? DataLayoutEntryInterface() : *it;
361}
362
363static DataLayoutSpecInterface getSpec(Operation *operation) {
365 .Case<ModuleOp, DataLayoutOpInterface>(
366 [&](auto op) { return op.getDataLayoutSpec(); })
367 .DefaultUnreachable("expected an op with data layout spec");
368}
369
370static TargetSystemSpecInterface getTargetSystemSpec(Operation *operation) {
371 if (operation) {
372 ModuleOp moduleOp = dyn_cast<ModuleOp>(operation);
373 if (!moduleOp)
374 moduleOp = operation->getParentOfType<ModuleOp>();
375 return moduleOp.getTargetSystemSpec();
376 }
377 return TargetSystemSpecInterface();
378}
379
380/// Populates `opsWithLayout` with the list of proper ancestors of `leaf` that
381/// are either modules or implement the `DataLayoutOpInterface`.
382static void
385 SmallVectorImpl<Location> *opLocations = nullptr) {
386 if (!leaf)
387 return;
388
389 for (Operation *parent = leaf->getParentOp(); parent != nullptr;
390 parent = parent->getParentOp()) {
392 .Case<ModuleOp>([&](ModuleOp op) {
393 // Skip top-level module op unless it has a layout. Top-level module
394 // without layout is most likely the one implicitly added by the
395 // parser and it doesn't have location. Top-level null specification
396 // would have had the same effect as not having a specification at all
397 // (using type defaults).
398 if (!op->getParentOp() && !op.getDataLayoutSpec())
399 return;
400 specs.push_back(op.getDataLayoutSpec());
401 if (opLocations)
402 opLocations->push_back(op.getLoc());
403 })
404 .Case<DataLayoutOpInterface>([&](DataLayoutOpInterface op) {
405 specs.push_back(op.getDataLayoutSpec());
406 if (opLocations)
407 opLocations->push_back(op.getLoc());
408 });
409 }
410}
411
412/// Returns a layout spec that is a combination of the layout specs attached
413/// to the given operation and all its ancestors.
414static DataLayoutSpecInterface getCombinedDataLayout(Operation *leaf) {
415 if (!leaf)
416 return {};
417
418 assert((isa<ModuleOp, DataLayoutOpInterface>(leaf)) &&
419 "expected an op with data layout spec");
420
422 collectParentLayouts(leaf, specs);
423
424 // Fast track if there are no ancestors.
425 if (specs.empty())
426 return getSpec(leaf);
427
428 // Create the list of non-null specs (null/missing specs can be safely
429 // ignored) from the outermost to the innermost.
430 auto nonNullSpecs = llvm::filter_to_vector<2>(
431 llvm::reverse(specs),
432 [](DataLayoutSpecInterface iface) { return iface != nullptr; });
433
434 // Combine the specs using the innermost as anchor.
435 if (DataLayoutSpecInterface current = getSpec(leaf))
436 return current.combineWith(nonNullSpecs);
437 if (nonNullSpecs.empty())
438 return {};
439 return nonNullSpecs.back().combineWith(
440 llvm::ArrayRef(nonNullSpecs).drop_back());
441}
442
444 DataLayoutSpecInterface spec = getSpec(op);
445 // The layout specification may be missing and it's fine.
446 if (!spec)
447 return success();
448
449 if (failed(spec.verifySpec(op->getLoc())))
450 return failure();
451 if (!getCombinedDataLayout(op)) {
453 op->emitError()
454 << "data layout does not combine with layouts of enclosing ops";
456 SmallVector<Location> opLocations;
457 collectParentLayouts(op, specs, &opLocations);
458 for (Location loc : opLocations)
459 diag.attachNote(loc) << "enclosing op with data layout";
460 return diag;
461 }
462 return success();
463}
464
465llvm::TypeSize mlir::detail::divideCeil(llvm::TypeSize numerator,
466 uint64_t denominator) {
467 uint64_t divided =
468 llvm::divideCeil(numerator.getKnownMinValue(), denominator);
469 return llvm::TypeSize::get(divided, numerator.isScalable());
470}
471
472//===----------------------------------------------------------------------===//
473// DataLayout
474//===----------------------------------------------------------------------===//
475
476template <typename OpTy>
477void checkMissingLayout(DataLayoutSpecInterface originalLayout, OpTy op) {
478 if (!originalLayout) {
479 assert((!op || !op.getDataLayoutSpec()) &&
480 "could not compute layout information for an op (failed to "
481 "combine attributes?)");
482 }
483}
484
486
487mlir::DataLayout::DataLayout(DataLayoutOpInterface op)
488 : originalLayout(getCombinedDataLayout(op)),
489 originalTargetSystemDesc(getTargetSystemSpec(op)), scope(op),
490 allocaMemorySpace(std::nullopt), programMemorySpace(std::nullopt),
491 globalMemorySpace(std::nullopt), stackAlignment(std::nullopt) {
492#if LLVM_ENABLE_ABI_BREAKING_CHECKS
493 checkMissingLayout(originalLayout, op);
494 collectParentLayouts(op, layoutStack);
495#endif
496}
497
499 : originalLayout(getCombinedDataLayout(op)),
500 originalTargetSystemDesc(getTargetSystemSpec(op)), scope(op),
501 allocaMemorySpace(std::nullopt), programMemorySpace(std::nullopt),
502 globalMemorySpace(std::nullopt), stackAlignment(std::nullopt) {
503#if LLVM_ENABLE_ABI_BREAKING_CHECKS
504 checkMissingLayout(originalLayout, op);
505 collectParentLayouts(op, layoutStack);
506#endif
507}
508
510 // Search the closest parent either being a module operation or implementing
511 // the data layout interface.
512 while (op) {
513 if (auto module = dyn_cast<ModuleOp>(op))
514 return DataLayout(module);
515 if (auto iface = dyn_cast<DataLayoutOpInterface>(op))
516 return DataLayout(iface);
517 op = op->getParentOp();
518 }
519 return DataLayout();
520}
521
522void mlir::DataLayout::checkValid() const {
523#if LLVM_ENABLE_ABI_BREAKING_CHECKS
525 collectParentLayouts(scope, specs);
526 assert(specs.size() == layoutStack.size() &&
527 "data layout object used, but no longer valid due to the change in "
528 "number of nested layouts");
529 for (auto pair : llvm::zip(specs, layoutStack)) {
530 Attribute newLayout = std::get<0>(pair);
531 Attribute origLayout = std::get<1>(pair);
532 assert(newLayout == origLayout &&
533 "data layout object used, but no longer valid "
534 "due to the change in layout attributes");
535 }
536#endif
537 assert(((!scope && !this->originalLayout) ||
538 (scope && this->originalLayout == getCombinedDataLayout(scope))) &&
539 "data layout object used, but no longer valid due to the change in "
540 "layout spec");
541}
542
543/// Looks up the value for the given type key in the given cache. If there is no
544/// such value in the cache, compute it using the given callback and put it in
545/// the cache before returning.
546template <typename T>
548 function_ref<T(Type)> compute) {
549 auto it = cache.find(t);
550 if (it != cache.end())
551 return it->second;
552
553 auto result = cache.try_emplace(t, compute(t));
554 return result.first->second;
555}
556
557llvm::TypeSize mlir::DataLayout::getTypeSize(Type t) const {
558 checkValid();
559 return cachedLookup<llvm::TypeSize>(t, sizes, [&](Type ty) {
561 if (originalLayout)
562 list = originalLayout.getSpecForType(ty.getTypeID());
563 if (auto iface = dyn_cast_or_null<DataLayoutOpInterface>(scope))
564 return iface.getTypeSize(ty, *this, list);
565 return detail::getDefaultTypeSize(ty, *this, list);
566 });
567}
568
570 checkValid();
571 return cachedLookup<llvm::TypeSize>(t, bitsizes, [&](Type ty) {
573 if (originalLayout)
574 list = originalLayout.getSpecForType(ty.getTypeID());
575 if (auto iface = dyn_cast_or_null<DataLayoutOpInterface>(scope))
576 return iface.getTypeSizeInBits(ty, *this, list);
577 return detail::getDefaultTypeSizeInBits(ty, *this, list);
578 });
579}
580
582 checkValid();
583 return cachedLookup<uint64_t>(t, abiAlignments, [&](Type ty) {
585 if (originalLayout)
586 list = originalLayout.getSpecForType(ty.getTypeID());
587 if (auto iface = dyn_cast_or_null<DataLayoutOpInterface>(scope))
588 return iface.getTypeABIAlignment(ty, *this, list);
589 return detail::getDefaultABIAlignment(ty, *this, list);
590 });
591}
592
594 checkValid();
595 return cachedLookup<uint64_t>(t, preferredAlignments, [&](Type ty) {
597 if (originalLayout)
598 list = originalLayout.getSpecForType(ty.getTypeID());
599 if (auto iface = dyn_cast_or_null<DataLayoutOpInterface>(scope))
600 return iface.getTypePreferredAlignment(ty, *this, list);
601 return detail::getDefaultPreferredAlignment(ty, *this, list);
602 });
603}
604
605std::optional<uint64_t> mlir::DataLayout::getTypeIndexBitwidth(Type t) const {
606 checkValid();
607 return cachedLookup<std::optional<uint64_t>>(t, indexBitwidths, [&](Type ty) {
609 if (originalLayout)
610 list = originalLayout.getSpecForType(ty.getTypeID());
611 if (auto iface = dyn_cast_or_null<DataLayoutOpInterface>(scope))
612 return iface.getIndexBitwidth(ty, *this, list);
613 return detail::getDefaultIndexBitwidth(ty, *this, list);
614 });
615}
616
618 checkValid();
619 if (endianness)
620 return *endianness;
621 DataLayoutEntryInterface entry;
622 if (originalLayout)
623 entry = originalLayout.getSpecForIdentifier(
624 originalLayout.getEndiannessIdentifier(originalLayout.getContext()));
625
626 if (auto iface = dyn_cast_or_null<DataLayoutOpInterface>(scope))
627 endianness = iface.getEndianness(entry);
628 else
629 endianness = detail::getDefaultEndianness(entry);
630 return *endianness;
631}
632
634 checkValid();
635 if (defaultMemorySpace)
636 return *defaultMemorySpace;
637 DataLayoutEntryInterface entry;
638 if (originalLayout)
639 entry = originalLayout.getSpecForIdentifier(
640 originalLayout.getDefaultMemorySpaceIdentifier(
641 originalLayout.getContext()));
642 if (auto iface = dyn_cast_or_null<DataLayoutOpInterface>(scope))
643 defaultMemorySpace = iface.getDefaultMemorySpace(entry);
644 else
645 defaultMemorySpace = detail::getDefaultMemorySpace(entry);
646 return *defaultMemorySpace;
647}
648
650 checkValid();
651 if (allocaMemorySpace)
652 return *allocaMemorySpace;
653 DataLayoutEntryInterface entry;
654 if (originalLayout)
655 entry = originalLayout.getSpecForIdentifier(
656 originalLayout.getAllocaMemorySpaceIdentifier(
657 originalLayout.getContext()));
658 if (auto iface = dyn_cast_or_null<DataLayoutOpInterface>(scope))
659 allocaMemorySpace = iface.getAllocaMemorySpace(entry);
660 else
661 allocaMemorySpace = detail::getDefaultAllocaMemorySpace(entry);
662 return *allocaMemorySpace;
663}
664
666 checkValid();
667 if (manglingMode)
668 return *manglingMode;
669 DataLayoutEntryInterface entry;
670 if (originalLayout)
671 entry = originalLayout.getSpecForIdentifier(
672 originalLayout.getManglingModeIdentifier(originalLayout.getContext()));
673
674 if (auto iface = dyn_cast_or_null<DataLayoutOpInterface>(scope))
675 manglingMode = iface.getManglingMode(entry);
676 else
677 manglingMode = detail::getDefaultManglingMode(entry);
678 return *manglingMode;
679}
680
682 checkValid();
683 if (programMemorySpace)
684 return *programMemorySpace;
685 DataLayoutEntryInterface entry;
686 if (originalLayout)
687 entry = originalLayout.getSpecForIdentifier(
688 originalLayout.getProgramMemorySpaceIdentifier(
689 originalLayout.getContext()));
690 if (auto iface = dyn_cast_or_null<DataLayoutOpInterface>(scope))
691 programMemorySpace = iface.getProgramMemorySpace(entry);
692 else
693 programMemorySpace = detail::getDefaultProgramMemorySpace(entry);
694 return *programMemorySpace;
695}
696
698 checkValid();
699 if (globalMemorySpace)
700 return *globalMemorySpace;
701 DataLayoutEntryInterface entry;
702 if (originalLayout)
703 entry = originalLayout.getSpecForIdentifier(
704 originalLayout.getGlobalMemorySpaceIdentifier(
705 originalLayout.getContext()));
706 if (auto iface = dyn_cast_or_null<DataLayoutOpInterface>(scope))
707 globalMemorySpace = iface.getGlobalMemorySpace(entry);
708 else
709 globalMemorySpace = detail::getDefaultGlobalMemorySpace(entry);
710 return *globalMemorySpace;
711}
712
714 checkValid();
715 if (stackAlignment)
716 return *stackAlignment;
717 DataLayoutEntryInterface entry;
718 if (originalLayout)
719 entry = originalLayout.getSpecForIdentifier(
720 originalLayout.getStackAlignmentIdentifier(
721 originalLayout.getContext()));
722 if (auto iface = dyn_cast_or_null<DataLayoutOpInterface>(scope))
723 stackAlignment = iface.getStackAlignment(entry);
724 else
725 stackAlignment = detail::getDefaultStackAlignment(entry);
726 return *stackAlignment;
727}
728
730 checkValid();
731 if (functionPointerAlignment)
732 return *functionPointerAlignment;
733 DataLayoutEntryInterface entry;
734 if (originalLayout)
735 entry = originalLayout.getSpecForIdentifier(
736 originalLayout.getFunctionPointerAlignmentIdentifier(
737 originalLayout.getContext()));
738 if (auto iface = dyn_cast_or_null<DataLayoutOpInterface>(scope))
739 functionPointerAlignment = iface.getFunctionPointerAlignment(entry);
740 else
741 functionPointerAlignment =
743 return *functionPointerAlignment;
744}
745
747 checkValid();
748 if (legalIntWidths)
749 return *legalIntWidths;
750 DataLayoutEntryInterface entry;
751 if (originalLayout)
752 entry = originalLayout.getSpecForIdentifier(
753 originalLayout.getLegalIntWidthsIdentifier(
754 originalLayout.getContext()));
755 if (auto iface = dyn_cast_or_null<DataLayoutOpInterface>(scope))
756 legalIntWidths = iface.getLegalIntWidths(entry);
757 else
758 legalIntWidths = detail::getDefaultLegalIntWidths(entry);
759 return *legalIntWidths;
760}
761
763 TargetSystemSpecInterface::DeviceID deviceID,
764 StringAttr propertyName) const {
765 checkValid();
766 DataLayoutEntryInterface entry;
767 if (originalTargetSystemDesc) {
768 if (std::optional<TargetDeviceSpecInterface> device =
769 originalTargetSystemDesc.getDeviceSpecForDeviceID(deviceID))
770 entry = device->getSpecForIdentifier(propertyName);
771 }
772 // Currently I am not caching the results because we do not return
773 // default values of these properties. Instead if the property is
774 // missing, we return std::nullopt so that the users can resort to
775 // the default value however they want.
776 if (auto iface = dyn_cast_or_null<DataLayoutOpInterface>(scope))
777 return iface.getDevicePropertyValue(entry);
778 else
779 return detail::getDevicePropertyValue(entry);
780}
781
782//===----------------------------------------------------------------------===//
783// DataLayoutSpecInterface
784//===----------------------------------------------------------------------===//
785
786void DataLayoutSpecInterface::bucketEntriesByType(
787 llvm::MapVector<TypeID, DataLayoutEntryList> &types,
788 llvm::MapVector<StringAttr, DataLayoutEntryInterface> &ids) {
789 for (DataLayoutEntryInterface entry : getEntries()) {
790 if (auto type = llvm::dyn_cast_if_present<Type>(entry.getKey()))
791 types[type.getTypeID()].push_back(entry);
792 else
793 ids[llvm::cast<StringAttr>(entry.getKey())] = entry;
794 }
795}
796
797LogicalResult mlir::detail::verifyDataLayoutSpec(DataLayoutSpecInterface spec,
798 Location loc) {
799 // First, verify individual entries.
800 for (DataLayoutEntryInterface entry : spec.getEntries())
801 if (failed(entry.verifyEntry(loc)))
802 return failure();
803
804 // Second, dispatch verifications of entry groups to types or dialects they
805 // are associated with.
806 llvm::MapVector<TypeID, DataLayoutEntryList> types;
807 llvm::MapVector<StringAttr, DataLayoutEntryInterface> ids;
808 spec.bucketEntriesByType(types, ids);
809
810 for (const auto &kvp : types) {
811 auto sampleType = cast<Type>(kvp.second.front().getKey());
812 if (isa<IndexType>(sampleType)) {
813 assert(kvp.second.size() == 1 &&
814 "expected one data layout entry for non-parametric 'index' type");
815 if (!isa<IntegerAttr>(kvp.second.front().getValue()))
816 return emitError(loc)
817 << "expected integer attribute in the data layout entry for "
818 << sampleType;
819 continue;
820 }
821
822 if (sampleType.isIntOrFloat()) {
823 for (DataLayoutEntryInterface entry : kvp.second) {
824 auto value = dyn_cast<DenseIntElementsAttr>(entry.getValue());
825 if (!value || !value.getElementType().isSignlessInteger(64)) {
826 emitError(loc) << "expected a dense i64 elements attribute in the "
827 "data layout entry "
828 << entry;
829 return failure();
830 }
831
832 auto elements = llvm::to_vector<2>(value.getValues<uint64_t>());
833 unsigned numElements = elements.size();
834 if (numElements < 1 || numElements > 2) {
835 emitError(loc) << "expected 1 or 2 elements in the data layout entry "
836 << entry;
837 return failure();
838 }
839
840 uint64_t abi = elements[0];
841 uint64_t preferred = numElements == 2 ? elements[1] : abi;
842 if (preferred < abi) {
843 emitError(loc)
844 << "preferred alignment is expected to be greater than or equal "
845 "to the abi alignment in data layout entry "
846 << entry;
847 return failure();
848 }
849 }
850 continue;
851 }
852
853 if (isa<BuiltinDialect>(&sampleType.getDialect()))
854 return emitError(loc) << "unexpected data layout for a built-in type";
855
856 auto dlType = dyn_cast<DataLayoutTypeInterface>(sampleType);
857 if (!dlType)
858 return emitError(loc)
859 << "data layout specified for a type that does not support it";
860 if (failed(dlType.verifyEntries(kvp.second, loc)))
861 return failure();
862 }
863
864 for (const auto &kvp : ids) {
865 StringAttr identifier = cast<StringAttr>(kvp.second.getKey());
866 Dialect *dialect = identifier.getReferencedDialect();
867
868 // Ignore attributes that belong to an unknown dialect, the dialect may
869 // actually implement the relevant interface but we don't know about that.
870 if (!dialect)
871 continue;
872
873 const auto *iface = dyn_cast<DataLayoutDialectInterface>(dialect);
874 if (!iface) {
875 return emitError(loc)
876 << "the '" << dialect->getNamespace()
877 << "' dialect does not support identifier data layout entries";
878 }
879 if (failed(iface->verifyEntry(kvp.second, loc)))
880 return failure();
881 }
882
883 return success();
884}
885
886LogicalResult
887mlir::detail::verifyTargetSystemSpec(TargetSystemSpecInterface spec,
888 Location loc) {
891 for (const auto &entry : spec.getEntries()) {
892 auto targetDeviceSpec =
893 dyn_cast<TargetDeviceSpecInterface>(entry.getValue());
894
895 if (!targetDeviceSpec)
896 return failure();
897
898 // First, verify individual target device desc specs.
899 if (failed(targetDeviceSpec.verifyEntry(loc)))
900 return failure();
901
902 // Check that device IDs are unique across all entries.
903 auto deviceID =
904 llvm::dyn_cast<TargetSystemSpecInterface::DeviceID>(entry.getKey());
905 if (!deviceID)
906 return failure();
907
908 if (!deviceIDs.insert(deviceID).second) {
909 return failure();
910 }
911
912 // collect all the keys used by all the target device specs.
913 for (DataLayoutEntryInterface entry : targetDeviceSpec.getEntries()) {
914 if (auto type = llvm::dyn_cast_if_present<Type>(entry.getKey())) {
915 // targetDeviceSpec does not support Type as a key.
916 return failure();
917 } else {
918 deviceDescKeys[cast<StringAttr>(entry.getKey())] = entry;
919 }
920 }
921 }
922
923 for (const auto &[keyName, keyVal] : deviceDescKeys) {
924 Dialect *dialect = keyName.getReferencedDialect();
925
926 // Ignore attributes that belong to an unknown dialect, the dialect may
927 // actually implement the relevant interface but we don't know about that.
928 if (!dialect)
929 return failure();
930
931 const auto *iface = dyn_cast<DataLayoutDialectInterface>(dialect);
932 if (!iface) {
933 return emitError(loc)
934 << "the '" << dialect->getNamespace()
935 << "' dialect does not support identifier data layout entries";
936 }
937 if (failed(iface->verifyEntry(keyVal, loc)))
938 return failure();
939 }
940
941 return success();
942}
943
944#include "mlir/Interfaces/DataLayoutAttrInterface.cpp.inc"
945#include "mlir/Interfaces/DataLayoutOpInterface.cpp.inc"
946#include "mlir/Interfaces/DataLayoutTypeInterface.cpp.inc"
return success()
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.
Definition Attributes.h:25
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.
Attribute getManglingMode() const
Returns the mangling mode.
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 getFunctionPointerAlignment() const
Returns function pointer alignment.
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 getDefaultMemorySpace() const
Returns the default memory space used for memory operations.
Attribute getEndianness() const
Returns the specified endianness.
Attribute getLegalIntWidths() const
Returns the legal int widths.
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...
Definition Dialect.h:38
StringRef getNamespace() const
Definition Dialect.h:54
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...
Definition Location.h:76
Operation is the basic unit of execution within MLIR.
Definition Operation.h:88
Location getLoc()
The source location the operation was defined or derived from.
Definition Operation.h:223
Operation * getParentOp()
Returns the closest surrounding operation that contains this operation or nullptr if this is a top-le...
Definition Operation.h:234
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'.
Definition Operation.h:238
This class provides an efficient unique identifier for a specific C++ type.
Definition TypeID.h:107
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition Types.h:74
MLIRContext * getContext() const
Return the MLIRContext in which this type was uniqued.
Definition Types.cpp:35
bool isIntOrFloat() const
Return true if this is an integer (of any signedness) or a float type.
Definition Types.cpp:116
TypeID getTypeID()
Return a unique identifier for the concrete type.
Definition Types.h:101
unsigned getIntOrFloatBitWidth() const
Return the bit width of an integer or a float type, assert failure on other types.
Definition Types.cpp:122
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.
Attribute getDefaultManglingMode(DataLayoutEntryInterface entry)
Default handler for mangling mode request.
uint64_t getDefaultABIAlignment(Type type, const DataLayout &dataLayout, ArrayRef< DataLayoutEntryInterface > params)
Default handler for the required alignment request.
Attribute getDefaultLegalIntWidths(DataLayoutEntryInterface entry)
Default handler for the legal int widths 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.
Attribute getDefaultMemorySpace(DataLayoutEntryInterface entry)
Default handler for the default 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.
Attribute getDefaultFunctionPointerAlignment(DataLayoutEntryInterface entry)
Default handler for the function pointer alignment request.
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.
llvm::SmallVector< DataLayoutEntryInterface, 4 > DataLayoutEntryList
llvm::DenseSet< ValueT, ValueInfoT > DenseSet
Definition LLVM.h:128
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.
llvm::DenseMap< KeyT, ValueT, KeyInfoT, BucketT > DenseMap
Definition LLVM.h:126
llvm::ArrayRef< DataLayoutEntryInterface > DataLayoutEntryListRef
llvm::function_ref< Fn > function_ref
Definition LLVM.h:152