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