MLIR 23.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 checkValid();
581 return cachedLookup<uint64_t>(t, abiAlignments, [&](Type ty) {
583 if (originalLayout)
584 list = originalLayout.getSpecForType(ty.getTypeID());
585 if (auto iface = dyn_cast_or_null<DataLayoutOpInterface>(scope))
586 return iface.getTypeABIAlignment(ty, *this, list);
587 return detail::getDefaultABIAlignment(ty, *this, list);
588 });
589}
590
592 checkValid();
593 return cachedLookup<uint64_t>(t, preferredAlignments, [&](Type ty) {
595 if (originalLayout)
596 list = originalLayout.getSpecForType(ty.getTypeID());
597 if (auto iface = dyn_cast_or_null<DataLayoutOpInterface>(scope))
598 return iface.getTypePreferredAlignment(ty, *this, list);
599 return detail::getDefaultPreferredAlignment(ty, *this, list);
600 });
601}
602
603std::optional<uint64_t> mlir::DataLayout::getTypeIndexBitwidth(Type t) const {
604 checkValid();
605 return cachedLookup<std::optional<uint64_t>>(t, indexBitwidths, [&](Type ty) {
607 if (originalLayout)
608 list = originalLayout.getSpecForType(ty.getTypeID());
609 if (auto iface = dyn_cast_or_null<DataLayoutOpInterface>(scope))
610 return iface.getIndexBitwidth(ty, *this, list);
611 return detail::getDefaultIndexBitwidth(ty, *this, list);
612 });
613}
614
616 checkValid();
617 if (endianness)
618 return *endianness;
619 DataLayoutEntryInterface entry;
620 if (originalLayout)
621 entry = originalLayout.getSpecForIdentifier(
622 originalLayout.getEndiannessIdentifier(originalLayout.getContext()));
623
624 if (auto iface = dyn_cast_or_null<DataLayoutOpInterface>(scope))
625 endianness = iface.getEndianness(entry);
626 else
627 endianness = detail::getDefaultEndianness(entry);
628 return *endianness;
629}
630
632 checkValid();
633 if (defaultMemorySpace)
634 return *defaultMemorySpace;
635 DataLayoutEntryInterface entry;
636 if (originalLayout)
637 entry = originalLayout.getSpecForIdentifier(
638 originalLayout.getDefaultMemorySpaceIdentifier(
639 originalLayout.getContext()));
640 if (auto iface = dyn_cast_or_null<DataLayoutOpInterface>(scope))
641 defaultMemorySpace = iface.getDefaultMemorySpace(entry);
642 else
643 defaultMemorySpace = detail::getDefaultMemorySpace(entry);
644 return *defaultMemorySpace;
645}
646
648 checkValid();
649 if (allocaMemorySpace)
650 return *allocaMemorySpace;
651 DataLayoutEntryInterface entry;
652 if (originalLayout)
653 entry = originalLayout.getSpecForIdentifier(
654 originalLayout.getAllocaMemorySpaceIdentifier(
655 originalLayout.getContext()));
656 if (auto iface = dyn_cast_or_null<DataLayoutOpInterface>(scope))
657 allocaMemorySpace = iface.getAllocaMemorySpace(entry);
658 else
659 allocaMemorySpace = detail::getDefaultAllocaMemorySpace(entry);
660 return *allocaMemorySpace;
661}
662
664 checkValid();
665 if (manglingMode)
666 return *manglingMode;
667 DataLayoutEntryInterface entry;
668 if (originalLayout)
669 entry = originalLayout.getSpecForIdentifier(
670 originalLayout.getManglingModeIdentifier(originalLayout.getContext()));
671
672 if (auto iface = dyn_cast_or_null<DataLayoutOpInterface>(scope))
673 manglingMode = iface.getManglingMode(entry);
674 else
675 manglingMode = detail::getDefaultManglingMode(entry);
676 return *manglingMode;
677}
678
680 checkValid();
681 if (programMemorySpace)
682 return *programMemorySpace;
683 DataLayoutEntryInterface entry;
684 if (originalLayout)
685 entry = originalLayout.getSpecForIdentifier(
686 originalLayout.getProgramMemorySpaceIdentifier(
687 originalLayout.getContext()));
688 if (auto iface = dyn_cast_or_null<DataLayoutOpInterface>(scope))
689 programMemorySpace = iface.getProgramMemorySpace(entry);
690 else
691 programMemorySpace = detail::getDefaultProgramMemorySpace(entry);
692 return *programMemorySpace;
693}
694
696 checkValid();
697 if (globalMemorySpace)
698 return *globalMemorySpace;
699 DataLayoutEntryInterface entry;
700 if (originalLayout)
701 entry = originalLayout.getSpecForIdentifier(
702 originalLayout.getGlobalMemorySpaceIdentifier(
703 originalLayout.getContext()));
704 if (auto iface = dyn_cast_or_null<DataLayoutOpInterface>(scope))
705 globalMemorySpace = iface.getGlobalMemorySpace(entry);
706 else
707 globalMemorySpace = detail::getDefaultGlobalMemorySpace(entry);
708 return *globalMemorySpace;
709}
710
712 checkValid();
713 if (stackAlignment)
714 return *stackAlignment;
715 DataLayoutEntryInterface entry;
716 if (originalLayout)
717 entry = originalLayout.getSpecForIdentifier(
718 originalLayout.getStackAlignmentIdentifier(
719 originalLayout.getContext()));
720 if (auto iface = dyn_cast_or_null<DataLayoutOpInterface>(scope))
721 stackAlignment = iface.getStackAlignment(entry);
722 else
723 stackAlignment = detail::getDefaultStackAlignment(entry);
724 return *stackAlignment;
725}
726
728 checkValid();
729 if (functionPointerAlignment)
730 return *functionPointerAlignment;
731 DataLayoutEntryInterface entry;
732 if (originalLayout)
733 entry = originalLayout.getSpecForIdentifier(
734 originalLayout.getFunctionPointerAlignmentIdentifier(
735 originalLayout.getContext()));
736 if (auto iface = dyn_cast_or_null<DataLayoutOpInterface>(scope))
737 functionPointerAlignment = iface.getFunctionPointerAlignment(entry);
738 else
739 functionPointerAlignment =
741 return *functionPointerAlignment;
742}
743
745 checkValid();
746 if (legalIntWidths)
747 return *legalIntWidths;
748 DataLayoutEntryInterface entry;
749 if (originalLayout)
750 entry = originalLayout.getSpecForIdentifier(
751 originalLayout.getLegalIntWidthsIdentifier(
752 originalLayout.getContext()));
753 if (auto iface = dyn_cast_or_null<DataLayoutOpInterface>(scope))
754 legalIntWidths = iface.getLegalIntWidths(entry);
755 else
756 legalIntWidths = detail::getDefaultLegalIntWidths(entry);
757 return *legalIntWidths;
758}
759
761 TargetSystemSpecInterface::DeviceID deviceID,
762 StringAttr propertyName) const {
763 checkValid();
764 DataLayoutEntryInterface entry;
765 if (originalTargetSystemDesc) {
766 if (std::optional<TargetDeviceSpecInterface> device =
767 originalTargetSystemDesc.getDeviceSpecForDeviceID(deviceID))
768 entry = device->getSpecForIdentifier(propertyName);
769 }
770 // Currently I am not caching the results because we do not return
771 // default values of these properties. Instead if the property is
772 // missing, we return std::nullopt so that the users can resort to
773 // the default value however they want.
774 if (auto iface = dyn_cast_or_null<DataLayoutOpInterface>(scope))
775 return iface.getDevicePropertyValue(entry);
776 else
777 return detail::getDevicePropertyValue(entry);
778}
779
780//===----------------------------------------------------------------------===//
781// DataLayoutSpecInterface
782//===----------------------------------------------------------------------===//
783
784void DataLayoutSpecInterface::bucketEntriesByType(
785 llvm::MapVector<TypeID, DataLayoutEntryList> &types,
786 llvm::MapVector<StringAttr, DataLayoutEntryInterface> &ids) {
787 for (DataLayoutEntryInterface entry : getEntries()) {
788 if (auto type = llvm::dyn_cast_if_present<Type>(entry.getKey()))
789 types[type.getTypeID()].push_back(entry);
790 else
791 ids[llvm::cast<StringAttr>(entry.getKey())] = entry;
792 }
793}
794
795LogicalResult mlir::detail::verifyDataLayoutSpec(DataLayoutSpecInterface spec,
796 Location loc) {
797 // First, verify individual entries.
798 for (DataLayoutEntryInterface entry : spec.getEntries())
799 if (failed(entry.verifyEntry(loc)))
800 return failure();
801
802 // Second, dispatch verifications of entry groups to types or dialects they
803 // are associated with.
804 llvm::MapVector<TypeID, DataLayoutEntryList> types;
805 llvm::MapVector<StringAttr, DataLayoutEntryInterface> ids;
806 spec.bucketEntriesByType(types, ids);
807
808 for (const auto &kvp : types) {
809 auto sampleType = cast<Type>(kvp.second.front().getKey());
810 if (isa<IndexType>(sampleType)) {
811 assert(kvp.second.size() == 1 &&
812 "expected one data layout entry for non-parametric 'index' type");
813 if (!isa<IntegerAttr>(kvp.second.front().getValue()))
814 return emitError(loc)
815 << "expected integer attribute in the data layout entry for "
816 << sampleType;
817 continue;
818 }
819
820 if (sampleType.isIntOrFloat()) {
821 for (DataLayoutEntryInterface entry : kvp.second) {
822 auto value = dyn_cast<DenseIntElementsAttr>(entry.getValue());
823 if (!value || !value.getElementType().isSignlessInteger(64)) {
824 emitError(loc) << "expected a dense i64 elements attribute in the "
825 "data layout entry "
826 << entry;
827 return failure();
828 }
829
830 auto elements = llvm::to_vector<2>(value.getValues<uint64_t>());
831 unsigned numElements = elements.size();
832 if (numElements < 1 || numElements > 2) {
833 emitError(loc) << "expected 1 or 2 elements in the data layout entry "
834 << entry;
835 return failure();
836 }
837
838 uint64_t abi = elements[0];
839 uint64_t preferred = numElements == 2 ? elements[1] : abi;
840 if (preferred < abi) {
841 emitError(loc)
842 << "preferred alignment is expected to be greater than or equal "
843 "to the abi alignment in data layout entry "
844 << entry;
845 return failure();
846 }
847 }
848 continue;
849 }
850
851 if (isa<BuiltinDialect>(&sampleType.getDialect()))
852 return emitError(loc) << "unexpected data layout for a built-in type";
853
854 auto dlType = dyn_cast<DataLayoutTypeInterface>(sampleType);
855 if (!dlType)
856 return emitError(loc)
857 << "data layout specified for a type that does not support it";
858 if (failed(dlType.verifyEntries(kvp.second, loc)))
859 return failure();
860 }
861
862 for (const auto &kvp : ids) {
863 StringAttr identifier = cast<StringAttr>(kvp.second.getKey());
864 Dialect *dialect = identifier.getReferencedDialect();
865
866 // Ignore attributes that belong to an unknown dialect, the dialect may
867 // actually implement the relevant interface but we don't know about that.
868 if (!dialect)
869 continue;
870
871 const auto *iface = dyn_cast<DataLayoutDialectInterface>(dialect);
872 if (!iface) {
873 return emitError(loc)
874 << "the '" << dialect->getNamespace()
875 << "' dialect does not support identifier data layout entries";
876 }
877 if (failed(iface->verifyEntry(kvp.second, loc)))
878 return failure();
879 }
880
881 return success();
882}
883
884LogicalResult
885mlir::detail::verifyTargetSystemSpec(TargetSystemSpecInterface spec,
886 Location loc) {
889 for (const auto &entry : spec.getEntries()) {
890 auto targetDeviceSpec =
891 dyn_cast<TargetDeviceSpecInterface>(entry.getValue());
892
893 if (!targetDeviceSpec)
894 return failure();
895
896 // First, verify individual target device desc specs.
897 if (failed(targetDeviceSpec.verifyEntry(loc)))
898 return failure();
899
900 // Check that device IDs are unique across all entries.
901 auto deviceID =
902 llvm::dyn_cast<TargetSystemSpecInterface::DeviceID>(entry.getKey());
903 if (!deviceID)
904 return failure();
905
906 if (!deviceIDs.insert(deviceID).second) {
907 return failure();
908 }
909
910 // collect all the keys used by all the target device specs.
911 for (DataLayoutEntryInterface entry : targetDeviceSpec.getEntries()) {
912 if (auto type = llvm::dyn_cast_if_present<Type>(entry.getKey())) {
913 // targetDeviceSpec does not support Type as a key.
914 return failure();
915 } else {
916 deviceDescKeys[cast<StringAttr>(entry.getKey())] = entry;
917 }
918 }
919 }
920
921 for (const auto &[keyName, keyVal] : deviceDescKeys) {
922 Dialect *dialect = keyName.getReferencedDialect();
923
924 // Ignore attributes that belong to an unknown dialect, the dialect may
925 // actually implement the relevant interface but we don't know about that.
926 if (!dialect)
927 return failure();
928
929 const auto *iface = dyn_cast<DataLayoutDialectInterface>(dialect);
930 if (!iface) {
931 return emitError(loc)
932 << "the '" << dialect->getNamespace()
933 << "' dialect does not support identifier data layout entries";
934 }
935 if (failed(iface->verifyEntry(keyVal, loc)))
936 return failure();
937 }
938
939 return success();
940}
941
942#include "mlir/Interfaces/DataLayoutAttrInterface.cpp.inc"
943#include "mlir/Interfaces/DataLayoutOpInterface.cpp.inc"
944#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: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:120
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.
llvm::DenseMap< KeyT, ValueT, KeyInfoT, BucketT > DenseMap
Definition LLVM.h:118
llvm::ArrayRef< DataLayoutEntryInterface > DataLayoutEntryListRef
llvm::function_ref< Fn > function_ref
Definition LLVM.h:144