MLIR  14.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(os.str()));
33 }
34 
35 /// Returns the bitwidth of the index type if specified in the param list.
36 /// Assumes 64-bit index otherwise.
37 static unsigned getIndexBitwidth(DataLayoutEntryListRef params) {
38  if (params.empty())
39  return 64;
40  auto attr = params.front().getValue().cast<IntegerAttr>();
41  return attr.getValue().getZExtValue();
42 }
43 
44 unsigned
47  unsigned bits = getDefaultTypeSizeInBits(type, dataLayout, params);
48  return llvm::divideCeil(bits, 8);
49 }
50 
52  const DataLayout &dataLayout,
53  DataLayoutEntryListRef params) {
54  if (type.isa<IntegerType, FloatType>())
55  return type.getIntOrFloatBitWidth();
56 
57  if (auto ctype = type.dyn_cast<ComplexType>()) {
58  auto et = ctype.getElementType();
59  auto innerAlignment =
60  getDefaultPreferredAlignment(et, dataLayout, params) * 8;
61  auto 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 (type.isa<IndexType>())
70  return dataLayout.getTypeSizeInBits(
71  IntegerType::get(type.getContext(), getIndexBitwidth(params)));
72 
73  // Sizes of vector types are rounded up to those of types with closest
74  // power-of-two number of elements in the innermost dimension. We also assume
75  // there is no bit-packing at the moment element sizes are taken in bytes and
76  // multiplied with 8 bits.
77  // TODO: make this extensible.
78  if (auto vecType = type.dyn_cast<VectorType>())
79  return vecType.getNumElements() / vecType.getShape().back() *
80  llvm::PowerOf2Ceil(vecType.getShape().back()) *
81  dataLayout.getTypeSize(vecType.getElementType()) * 8;
82 
83  if (auto typeInterface = type.dyn_cast<DataLayoutTypeInterface>())
84  return typeInterface.getTypeSizeInBits(dataLayout, params);
85 
87 }
88 
90  Type type, const DataLayout &dataLayout,
92  // Natural alignment is the closest power-of-two number above.
93  if (type.isa<FloatType, VectorType>())
94  return llvm::PowerOf2Ceil(dataLayout.getTypeSize(type));
95 
96  // Index is an integer of some bitwidth.
97  if (type.isa<IndexType>())
98  return dataLayout.getTypeABIAlignment(
99  IntegerType::get(type.getContext(), getIndexBitwidth(params)));
100 
101  if (auto intType = type.dyn_cast<IntegerType>()) {
102  return intType.getWidth() < 64
103  ? llvm::PowerOf2Ceil(llvm::divideCeil(intType.getWidth(), 8))
104  : 4;
105  }
106 
107  if (auto ctype = type.dyn_cast<ComplexType>())
108  return getDefaultABIAlignment(ctype.getElementType(), dataLayout, params);
109 
110  if (auto typeInterface = type.dyn_cast<DataLayoutTypeInterface>())
111  return typeInterface.getABIAlignment(dataLayout, params);
112 
114 }
115 
117  Type type, const DataLayout &dataLayout,
119  // Preferred alignment is same as natural for floats and vectors.
120  if (type.isa<FloatType, VectorType>())
121  return dataLayout.getTypeABIAlignment(type);
122 
123  // Preferred alignment is the cloest power-of-two number above for integers
124  // (ABI alignment may be smaller).
125  if (type.isa<IntegerType, IndexType>())
126  return llvm::PowerOf2Ceil(dataLayout.getTypeSize(type));
127 
128  if (auto ctype = type.dyn_cast<ComplexType>())
129  return getDefaultPreferredAlignment(ctype.getElementType(), dataLayout,
130  params);
131 
132  if (auto typeInterface = type.dyn_cast<DataLayoutTypeInterface>())
133  return typeInterface.getPreferredAlignment(dataLayout, params);
134 
136 }
137 
140  TypeID typeID) {
141  return llvm::to_vector<4>(llvm::make_filter_range(
142  entries, [typeID](DataLayoutEntryInterface entry) {
143  auto type = entry.getKey().dyn_cast<Type>();
144  return type && type.getTypeID() == typeID;
145  }));
146 }
147 
148 DataLayoutEntryInterface
150  StringAttr id) {
151  const auto *it = llvm::find_if(entries, [id](DataLayoutEntryInterface entry) {
152  if (!entry.getKey().is<StringAttr>())
153  return false;
154  return entry.getKey().get<StringAttr>() == id;
155  });
156  return it == entries.end() ? DataLayoutEntryInterface() : *it;
157 }
158 
159 static DataLayoutSpecInterface getSpec(Operation *operation) {
161  .Case<ModuleOp, DataLayoutOpInterface>(
162  [&](auto op) { return op.getDataLayoutSpec(); })
163  .Default([](Operation *) {
164  llvm_unreachable("expected an op with data layout spec");
165  return DataLayoutSpecInterface();
166  });
167 }
168 
169 /// Populates `opsWithLayout` with the list of proper ancestors of `leaf` that
170 /// are either modules or implement the `DataLayoutOpInterface`.
171 static void
174  SmallVectorImpl<Location> *opLocations = nullptr) {
175  if (!leaf)
176  return;
177 
178  for (Operation *parent = leaf->getParentOp(); parent != nullptr;
179  parent = parent->getParentOp()) {
181  .Case<ModuleOp>([&](ModuleOp op) {
182  // Skip top-level module op unless it has a layout. Top-level module
183  // without layout is most likely the one implicitly added by the
184  // parser and it doesn't have location. Top-level null specification
185  // would have had the same effect as not having a specification at all
186  // (using type defaults).
187  if (!op->getParentOp() && !op.getDataLayoutSpec())
188  return;
189  specs.push_back(op.getDataLayoutSpec());
190  if (opLocations)
191  opLocations->push_back(op.getLoc());
192  })
193  .Case<DataLayoutOpInterface>([&](DataLayoutOpInterface op) {
194  specs.push_back(op.getDataLayoutSpec());
195  if (opLocations)
196  opLocations->push_back(op.getLoc());
197  });
198  }
199 }
200 
201 /// Returns a layout spec that is a combination of the layout specs attached
202 /// to the given operation and all its ancestors.
203 static DataLayoutSpecInterface getCombinedDataLayout(Operation *leaf) {
204  if (!leaf)
205  return {};
206 
207  assert((isa<ModuleOp, DataLayoutOpInterface>(leaf)) &&
208  "expected an op with data layout spec");
209 
212  collectParentLayouts(leaf, specs);
213 
214  // Fast track if there are no ancestors.
215  if (specs.empty())
216  return getSpec(leaf);
217 
218  // Create the list of non-null specs (null/missing specs can be safely
219  // ignored) from the outermost to the innermost.
220  auto nonNullSpecs = llvm::to_vector<2>(llvm::make_filter_range(
221  llvm::reverse(specs),
222  [](DataLayoutSpecInterface iface) { return iface != nullptr; }));
223 
224  // Combine the specs using the innermost as anchor.
225  if (DataLayoutSpecInterface current = getSpec(leaf))
226  return current.combineWith(nonNullSpecs);
227  if (nonNullSpecs.empty())
228  return {};
229  return nonNullSpecs.back().combineWith(
230  llvm::makeArrayRef(nonNullSpecs).drop_back());
231 }
232 
234  DataLayoutSpecInterface spec = getSpec(op);
235  // The layout specification may be missing and it's fine.
236  if (!spec)
237  return success();
238 
239  if (failed(spec.verifySpec(op->getLoc())))
240  return failure();
241  if (!getCombinedDataLayout(op)) {
243  op->emitError()
244  << "data layout does not combine with layouts of enclosing ops";
246  SmallVector<Location> opLocations;
247  collectParentLayouts(op, specs, &opLocations);
248  for (Location loc : opLocations)
249  diag.attachNote(loc) << "enclosing op with data layout";
250  return diag;
251  }
252  return success();
253 }
254 
255 //===----------------------------------------------------------------------===//
256 // DataLayout
257 //===----------------------------------------------------------------------===//
258 
259 template <typename OpTy>
260 void checkMissingLayout(DataLayoutSpecInterface originalLayout, OpTy op) {
261  if (!originalLayout) {
262  assert((!op || !op.getDataLayoutSpec()) &&
263  "could not compute layout information for an op (failed to "
264  "combine attributes?)");
265  }
266 }
267 
269 
270 mlir::DataLayout::DataLayout(DataLayoutOpInterface op)
271  : originalLayout(getCombinedDataLayout(op)), scope(op) {
272 #if LLVM_ENABLE_ABI_BREAKING_CHECKS
273  checkMissingLayout(originalLayout, op);
274  collectParentLayouts(op, layoutStack);
275 #endif
276 }
277 
279  : originalLayout(getCombinedDataLayout(op)), scope(op) {
280 #if LLVM_ENABLE_ABI_BREAKING_CHECKS
281  checkMissingLayout(originalLayout, op);
282  collectParentLayouts(op, layoutStack);
283 #endif
284 }
285 
287  // Search the closest parent either being a module operation or implementing
288  // the data layout interface.
289  while (op) {
290  if (auto module = dyn_cast<ModuleOp>(op))
291  return DataLayout(module);
292  if (auto iface = dyn_cast<DataLayoutOpInterface>(op))
293  return DataLayout(iface);
294  op = op->getParentOp();
295  }
296  return DataLayout();
297 }
298 
299 void mlir::DataLayout::checkValid() const {
300 #if LLVM_ENABLE_ABI_BREAKING_CHECKS
302  collectParentLayouts(scope, specs);
303  assert(specs.size() == layoutStack.size() &&
304  "data layout object used, but no longer valid due to the change in "
305  "number of nested layouts");
306  for (auto pair : llvm::zip(specs, layoutStack)) {
307  Attribute newLayout = std::get<0>(pair);
308  Attribute origLayout = std::get<1>(pair);
309  assert(newLayout == origLayout &&
310  "data layout object used, but no longer valid "
311  "due to the change in layout attributes");
312  }
313 #endif
314  assert(((!scope && !this->originalLayout) ||
315  (scope && this->originalLayout == getCombinedDataLayout(scope))) &&
316  "data layout object used, but no longer valid due to the change in "
317  "layout spec");
318 }
319 
320 /// Looks up the value for the given type key in the given cache. If there is no
321 /// such value in the cache, compute it using the given callback and put it in
322 /// the cache before returning.
323 static unsigned cachedLookup(Type t, DenseMap<Type, unsigned> &cache,
324  function_ref<unsigned(Type)> compute) {
325  auto it = cache.find(t);
326  if (it != cache.end())
327  return it->second;
328 
329  auto result = cache.try_emplace(t, compute(t));
330  return result.first->second;
331 }
332 
334  checkValid();
335  return cachedLookup(t, sizes, [&](Type ty) {
337  if (originalLayout)
338  list = originalLayout.getSpecForType(ty.getTypeID());
339  if (auto iface = dyn_cast_or_null<DataLayoutOpInterface>(scope))
340  return iface.getTypeSize(ty, *this, list);
341  return detail::getDefaultTypeSize(ty, *this, list);
342  });
343 }
344 
346  checkValid();
347  return cachedLookup(t, bitsizes, [&](Type ty) {
349  if (originalLayout)
350  list = originalLayout.getSpecForType(ty.getTypeID());
351  if (auto iface = dyn_cast_or_null<DataLayoutOpInterface>(scope))
352  return iface.getTypeSizeInBits(ty, *this, list);
353  return detail::getDefaultTypeSizeInBits(ty, *this, list);
354  });
355 }
356 
358  checkValid();
359  return cachedLookup(t, abiAlignments, [&](Type ty) {
361  if (originalLayout)
362  list = originalLayout.getSpecForType(ty.getTypeID());
363  if (auto iface = dyn_cast_or_null<DataLayoutOpInterface>(scope))
364  return iface.getTypeABIAlignment(ty, *this, list);
365  return detail::getDefaultABIAlignment(ty, *this, list);
366  });
367 }
368 
370  checkValid();
371  return cachedLookup(t, preferredAlignments, [&](Type ty) {
373  if (originalLayout)
374  list = originalLayout.getSpecForType(ty.getTypeID());
375  if (auto iface = dyn_cast_or_null<DataLayoutOpInterface>(scope))
376  return iface.getTypePreferredAlignment(ty, *this, list);
377  return detail::getDefaultPreferredAlignment(ty, *this, list);
378  });
379 }
380 
381 //===----------------------------------------------------------------------===//
382 // DataLayoutSpecInterface
383 //===----------------------------------------------------------------------===//
384 
385 void DataLayoutSpecInterface::bucketEntriesByType(
388  for (DataLayoutEntryInterface entry : getEntries()) {
389  if (auto type = entry.getKey().dyn_cast<Type>())
390  types[type.getTypeID()].push_back(entry);
391  else
392  ids[entry.getKey().get<StringAttr>()] = entry;
393  }
394 }
395 
397  Location loc) {
398  // First, verify individual entries.
399  for (DataLayoutEntryInterface entry : spec.getEntries())
400  if (failed(entry.verifyEntry(loc)))
401  return failure();
402 
403  // Second, dispatch verifications of entry groups to types or dialects they
404  // are are associated with.
407  spec.bucketEntriesByType(types, ids);
408 
409  for (const auto &kvp : types) {
410  auto sampleType = kvp.second.front().getKey().get<Type>();
411  if (sampleType.isa<IndexType>()) {
412  assert(kvp.second.size() == 1 &&
413  "expected one data layout entry for non-parametric 'index' type");
414  if (!kvp.second.front().getValue().isa<IntegerAttr>())
415  return emitError(loc)
416  << "expected integer attribute in the data layout entry for "
417  << sampleType;
418  continue;
419  }
420 
421  if (isa<BuiltinDialect>(&sampleType.getDialect()))
422  return emitError(loc) << "unexpected data layout for a built-in type";
423 
424  auto dlType = sampleType.dyn_cast<DataLayoutTypeInterface>();
425  if (!dlType)
426  return emitError(loc)
427  << "data layout specified for a type that does not support it";
428  if (failed(dlType.verifyEntries(kvp.second, loc)))
429  return failure();
430  }
431 
432  for (const auto &kvp : ids) {
433  StringAttr identifier = kvp.second.getKey().get<StringAttr>();
434  Dialect *dialect = identifier.getReferencedDialect();
435 
436  // Ignore attributes that belong to an unknown dialect, the dialect may
437  // actually implement the relevant interface but we don't know about that.
438  if (!dialect)
439  continue;
440 
441  const auto *iface =
443  if (!iface) {
444  return emitError(loc)
445  << "the '" << dialect->getNamespace()
446  << "' dialect does not support identifier data layout entries";
447  }
448  if (failed(iface->verifyEntry(kvp.second, loc)))
449  return failure();
450  }
451 
452  return success();
453 }
454 
455 #include "mlir/Interfaces/DataLayoutAttrInterface.cpp.inc"
456 #include "mlir/Interfaces/DataLayoutOpInterface.cpp.inc"
457 #include "mlir/Interfaces/DataLayoutTypeInterface.cpp.inc"
Include the generated interface declarations.
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 std::string diag(llvm::Value &v)
static void reportMissingDataLayout(Type type)
Reports that the given type is missing the data layout information and exits.
Operation is a basic unit of execution within MLIR.
Definition: Operation.h:28
This class represents a diagnostic that is inflight and set to be reported.
Definition: Diagnostics.h:301
static DataLayoutSpecInterface getSpec(Operation *operation)
bool failed(LogicalResult result)
Utility function that returns true if the provided LogicalResult corresponds to a failure value...
Definition: LogicalResult.h:72
This class provides an efficient unique identifier for a specific C++ type.
Definition: TypeID.h:52
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition: Location.h:48
unsigned getTypeSize(Type t) const
Returns the size of the given type in the current scope.
LogicalResult success(bool isSuccess=true)
Utility function to generate a LogicalResult.
Definition: LogicalResult.h:56
static unsigned getIndexBitwidth(DataLayoutEntryListRef params)
Returns the bitwidth of the index type if specified in the param list.
This class represents an efficient way to signal success or failure.
Definition: LogicalResult.h:26
LogicalResult failure(bool isFailure=true)
Utility function to generate a LogicalResult.
Definition: LogicalResult.h:62
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...
DataLayoutEntryInterface filterEntryForIdentifier(DataLayoutEntryListRef entries, StringAttr id)
Given a list of data layout entries, returns the entry that has the given identifier as key...
U dyn_cast() const
Definition: Types.h:244
Diagnostic & attachNote(Optional< Location > noteLoc=llvm::None)
Attaches a note to this diagnostic.
Definition: Diagnostics.h:335
const DialectInterface * getRegisteredInterface(TypeID interfaceID)
Lookup an interface for the given ID if one is registered, otherwise nullptr.
Definition: Dialect.h:161
Attributes are known-constant values of operations.
Definition: Attributes.h:24
Operation * getParentOp()
Returns the closest surrounding operation that contains this operation or nullptr if this is a top-le...
Definition: Operation.h:117
unsigned getTypeABIAlignment(Type t) const
Returns the required alignment of the given type in the current scope.
Dialects are groups of MLIR operations, types and attributes, as well as behavior associated with the...
Definition: Dialect.h:42
Location getLoc()
The source location the operation was defined or derived from.
Definition: Operation.h:106
static DataLayout closest(Operation *op)
Returns the layout of the closest parent operation carrying layout info.
unsigned getDefaultPreferredAlignment(Type type, const DataLayout &dataLayout, ArrayRef< DataLayoutEntryInterface > params)
Default handler for the preferred alignemnt request.
static DataLayoutSpecInterface getCombinedDataLayout(Operation *leaf)
Returns a layout spec that is a combination of the layout specs attached to the given operation and a...
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition: Types.h:72
MLIRContext * getContext() const
Return the MLIRContext in which this type was uniqued.
Definition: Types.cpp:19
unsigned getTypeSizeInBits(Type t) const
Returns the size in bits of the given type in the current scope.
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.
unsigned getDefaultTypeSize(Type type, const DataLayout &dataLayout, DataLayoutEntryListRef params)
Default handler for the type size request.
LogicalResult verifyDataLayoutOp(Operation *op)
Verifies that the operation implementing the data layout interface, or a module operation, is valid.
TypeID getTypeID()
Return a unique identifier for the concrete type.
Definition: Types.h:108
unsigned getTypePreferredAlignment(Type t) const
Returns the preferred of the given type in the current scope.
unsigned getIntOrFloatBitWidth() const
Return the bit width of an integer or a float type, assert failure on other types.
Definition: Types.cpp:91
LogicalResult verifyDataLayoutSpec(DataLayoutSpecInterface spec, Location loc)
Verifies that a data layout spec is valid.
unsigned getDefaultTypeSizeInBits(Type type, const DataLayout &dataLayout, DataLayoutEntryListRef params)
Default handler for the type size in bits request.
bool isa() const
Definition: Types.h:234
void checkMissingLayout(DataLayoutSpecInterface originalLayout, OpTy op)
An interface to be implemented by dialects that can have identifiers in the data layout specification...
InFlightDiagnostic emitError(const Twine &message={})
Emit an error about fatal conditions with this operation, reporting up to any diagnostic handlers tha...
Definition: Operation.cpp:231
static unsigned cachedLookup(Type t, DenseMap< Type, unsigned > &cache, function_ref< unsigned(Type)> compute)
Looks up the value for the given type key in the given cache.
The main mechanism for performing data layout queries.
unsigned getDefaultABIAlignment(Type type, const DataLayout &dataLayout, ArrayRef< DataLayoutEntryInterface > params)
Default handler for the required alignemnt request.