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 
10 #include "mlir/IR/BuiltinDialect.h"
11 #include "mlir/IR/BuiltinOps.h"
12 #include "mlir/IR/BuiltinTypes.h"
13 #include "mlir/IR/Operation.h"
14 
15 #include "llvm/ADT/TypeSwitch.h"
16 #include "llvm/Support/MathExtras.h"
17 
18 using 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.
37 static uint64_t getIndexBitwidth(DataLayoutEntryListRef params) {
38  if (params.empty())
39  return 64;
40  auto attr = cast<IntegerAttr>(params.front().getValue());
41  return attr.getValue().getZExtValue();
42 }
43 
44 llvm::TypeSize
47  llvm::TypeSize bits = getDefaultTypeSizeInBits(type, dataLayout, params);
48  return divideCeil(bits, 8);
49 }
50 
51 llvm::TypeSize
53  DataLayoutEntryListRef params) {
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(
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 
91 static DataLayoutEntryInterface
92 findEntryForIntegerType(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 
107 constexpr const static uint64_t kDefaultBitsInByte = 8u;
108 
109 static 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 
115 static uint64_t
116 getIntegerTypeABIAlignment(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 
130 static uint64_t
131 getFloatTypeABIAlignment(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 
168 static 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 
174 static 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 
184 static uint64_t
185 getFloatTypePreferredAlignment(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.
242 Attribute 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.
252 Attribute 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.
262 Attribute
263 mlir::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.
273 Attribute 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.
283 Attribute
284 mlir::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.
295 Attribute
296 mlir::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.
306 uint64_t
307 mlir::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.
327 Attribute
328 mlir::detail::getDefaultLegalIntWidths(DataLayoutEntryInterface entry) {
329  if (entry == DataLayoutEntryInterface())
330  return Attribute();
331  return entry.getValue();
332 }
333 
334 std::optional<Attribute>
335 mlir::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 
352 DataLayoutEntryInterface
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 
363 static 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 
370 static 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`.
382 static 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.
414 static 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 
465 llvm::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 
476 template <typename OpTy>
477 void 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 
487 mlir::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 
522 void 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.
546 template <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 
557 llvm::TypeSize mlir::DataLayout::getTypeSize(Type t) const {
558  checkValid();
559  return cachedLookup<llvm::TypeSize>(t, sizes, [&](Type ty) {
560  DataLayoutEntryList list;
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 
569 llvm::TypeSize mlir::DataLayout::getTypeSizeInBits(Type t) const {
570  checkValid();
571  return cachedLookup<llvm::TypeSize>(t, bitsizes, [&](Type ty) {
572  DataLayoutEntryList list;
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) {
584  DataLayoutEntryList list;
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) {
596  DataLayoutEntryList list;
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 
605 std::optional<uint64_t> mlir::DataLayout::getTypeIndexBitwidth(Type t) const {
606  checkValid();
607  return cachedLookup<std::optional<uint64_t>>(t, indexBitwidths, [&](Type ty) {
608  DataLayoutEntryList list;
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 
786 void 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 
797 LogicalResult 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 
886 LogicalResult
887 mlir::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"
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.
Definition: Diagnostics.h:314
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...
Definition: Operation.cpp:268
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,...
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition: Remarks.h:491
Include the generated interface declarations.
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.
auto get(MLIRContext *context, Ts &&...params)
Helper method that injects context only if needed, this helps unify some of the attribute constructio...