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 
9 #include "mlir/Interfaces/DataLayoutInterfaces.h"
10 #include "mlir/IR/BuiltinDialect.h"
11 #include "mlir/IR/BuiltinTypes.h"
12 #include "mlir/IR/Operation.h"
13 
14 using namespace mlir;
15 
16 //===----------------------------------------------------------------------===//
17 // Default implementations
18 //===----------------------------------------------------------------------===//
19 
20 /// Reports that the given type is missing the data layout information and
21 /// exits.
22 static LLVM_ATTRIBUTE_NORETURN void reportMissingDataLayout(Type type) {
23   std::string message;
24   llvm::raw_string_ostream os(message);
25   os << "neither the scoping op nor the type class provide data layout "
26         "information for "
27      << type;
28   llvm::report_fatal_error(os.str());
29 }
30 
31 unsigned
32 mlir::detail::getDefaultTypeSize(Type type, const DataLayout &dataLayout,
33                                  ArrayRef<DataLayoutEntryInterface> params) {
34   if (type.isa<IntegerType, FloatType>())
35     return llvm::divideCeil(type.getIntOrFloatBitWidth(), 8);
36 
37   // Sizes of vector types are rounded up to those of types with closest
38   // power-of-two number of elements.
39   // TODO: make this extensible.
40   if (auto vecType = type.dyn_cast<VectorType>())
41     return llvm::PowerOf2Ceil(vecType.getNumElements()) *
42            dataLayout.getTypeSize(vecType.getElementType());
43 
44   if (auto typeInterface = type.dyn_cast<DataLayoutTypeInterface>())
45     return typeInterface.getTypeSize(dataLayout, params);
46 
47   reportMissingDataLayout(type);
48 }
49 
50 unsigned mlir::detail::getDefaultABIAlignment(
51     Type type, const DataLayout &dataLayout,
52     ArrayRef<DataLayoutEntryInterface> params) {
53   // Natural alignment is the closest power-of-two number above.
54   if (type.isa<FloatType, VectorType>())
55     return llvm::PowerOf2Ceil(dataLayout.getTypeSize(type));
56 
57   if (auto intType = type.dyn_cast<IntegerType>()) {
58     return intType.getWidth() < 64
59                ? llvm::PowerOf2Ceil(llvm::divideCeil(intType.getWidth(), 8))
60                : 4;
61   }
62 
63   if (auto typeInterface = type.dyn_cast<DataLayoutTypeInterface>())
64     return typeInterface.getABIAlignment(dataLayout, params);
65 
66   reportMissingDataLayout(type);
67 }
68 
69 unsigned mlir::detail::getDefaultPreferredAlignment(
70     Type type, const DataLayout &dataLayout,
71     ArrayRef<DataLayoutEntryInterface> params) {
72   // Preferred alignment is same as natural for floats and vectors.
73   if (type.isa<FloatType, VectorType>())
74     return dataLayout.getTypeABIAlignment(type);
75 
76   // Preferred alignment is the cloest power-of-two number above for integers
77   // (ABI alignment may be smaller).
78   if (auto intType = type.dyn_cast<IntegerType>())
79     return llvm::PowerOf2Ceil(dataLayout.getTypeSize(type));
80 
81   if (auto typeInterface = type.dyn_cast<DataLayoutTypeInterface>())
82     return typeInterface.getPreferredAlignment(dataLayout, params);
83 
84   reportMissingDataLayout(type);
85 }
86 
87 DataLayoutEntryList
88 mlir::detail::filterEntriesForType(DataLayoutEntryListRef entries,
89                                    TypeID typeID) {
90   return llvm::to_vector<4>(llvm::make_filter_range(
91       entries, [typeID](DataLayoutEntryInterface entry) {
92         auto type = entry.getKey().dyn_cast<Type>();
93         return type && type.getTypeID() == typeID;
94       }));
95 }
96 
97 DataLayoutEntryInterface
98 mlir::detail::filterEntryForIdentifier(DataLayoutEntryListRef entries,
99                                        Identifier id) {
100   const auto *it = llvm::find_if(entries, [id](DataLayoutEntryInterface entry) {
101     if (!entry.getKey().is<Identifier>())
102       return false;
103     return entry.getKey().get<Identifier>() == id;
104   });
105   return it == entries.end() ? DataLayoutEntryInterface() : *it;
106 }
107 
108 /// Populates `opsWithLayout` with the list of proper ancestors of `leaf` that
109 /// implement the `DataLayoutOpInterface`.
110 static void findProperAscendantsWithLayout(
111     Operation *leaf, SmallVectorImpl<DataLayoutOpInterface> &opsWithLayout) {
112   if (!leaf)
113     return;
114 
115   while (auto opLayout = leaf->getParentOfType<DataLayoutOpInterface>()) {
116     opsWithLayout.push_back(opLayout);
117     leaf = opLayout;
118   }
119 }
120 
121 /// Returns a layout spec that is a combination of the layout specs attached
122 /// to the given operation and all its ancestors.
123 static DataLayoutSpecInterface
124 getCombinedDataLayout(DataLayoutOpInterface leaf) {
125   if (!leaf)
126     return {};
127 
128   SmallVector<DataLayoutOpInterface> opsWithLayout;
129   findProperAscendantsWithLayout(leaf, opsWithLayout);
130 
131   // Fast track if there are no ancestors.
132   if (opsWithLayout.empty())
133     return leaf.getDataLayoutSpec();
134 
135   // Create the list of non-null specs (null/missing specs can be safely
136   // ignored) from the outermost to the innermost.
137   SmallVector<DataLayoutSpecInterface> specs;
138   specs.reserve(opsWithLayout.size());
139   for (DataLayoutOpInterface op : llvm::reverse(opsWithLayout))
140     if (DataLayoutSpecInterface current = op.getDataLayoutSpec())
141       specs.push_back(current);
142 
143   // Combine the specs using the innermost as anchor.
144   if (DataLayoutSpecInterface current = leaf.getDataLayoutSpec())
145     return current.combineWith(specs);
146   if (specs.empty())
147     return {};
148   return specs.back().combineWith(llvm::makeArrayRef(specs).drop_back());
149 }
150 
151 LogicalResult mlir::detail::verifyDataLayoutOp(DataLayoutOpInterface op) {
152   DataLayoutSpecInterface spec = op.getDataLayoutSpec();
153   // The layout specification may be missing and it's fine.
154   if (!spec)
155     return success();
156 
157   if (failed(spec.verifySpec(op.getLoc())))
158     return failure();
159   if (!getCombinedDataLayout(op)) {
160     InFlightDiagnostic diag =
161         op.emitError()
162         << "data layout is not a refinement of the layouts in enclosing ops";
163     SmallVector<DataLayoutOpInterface> opsWithLayout;
164     findProperAscendantsWithLayout(op, opsWithLayout);
165     for (DataLayoutOpInterface parent : opsWithLayout)
166       diag.attachNote(parent.getLoc()) << "enclosing op with data layout";
167     return diag;
168   }
169   return success();
170 }
171 
172 //===----------------------------------------------------------------------===//
173 // DataLayout
174 //===----------------------------------------------------------------------===//
175 
176 mlir::DataLayout::DataLayout(DataLayoutOpInterface op)
177     : originalLayout(getCombinedDataLayout(op)), scope(op) {
178   if (!originalLayout) {
179     assert((!op || !op.getDataLayoutSpec()) &&
180            "could not compute layout information for an op (failed to "
181            "combine attributes?)");
182   }
183 
184 #ifndef NDEBUG
185   SmallVector<DataLayoutOpInterface> opsWithLayout;
186   findProperAscendantsWithLayout(op, opsWithLayout);
187   layoutStack = llvm::to_vector<2>(
188       llvm::map_range(opsWithLayout, [](DataLayoutOpInterface iface) {
189         return iface.getDataLayoutSpec();
190       }));
191 #endif
192 }
193 
194 void mlir::DataLayout::checkValid() const {
195 #ifndef NDEBUG
196   SmallVector<DataLayoutOpInterface> opsWithLayout;
197   findProperAscendantsWithLayout(scope, opsWithLayout);
198   assert(opsWithLayout.size() == layoutStack.size() &&
199          "data layout object used, but no longer valid due to the change in "
200          "number of nested layouts");
201   for (auto pair : llvm::zip(opsWithLayout, layoutStack)) {
202     Attribute newLayout = std::get<0>(pair).getDataLayoutSpec();
203     Attribute origLayout = std::get<1>(pair);
204     assert(newLayout == origLayout &&
205            "data layout object used, but no longer valid "
206            "due to the change in layout attributes");
207   }
208 #endif
209   assert(((!scope && !this->originalLayout) ||
210           (scope && this->originalLayout == getCombinedDataLayout(scope))) &&
211          "data layout object used, but no longer valid due to the change in "
212          "layout spec");
213 }
214 
215 /// Looks up the value for the given type key in the given cache. If there is no
216 /// such value in the cache, compute it using the given callback and put it in
217 /// the cache before returning.
218 static unsigned cachedLookup(Type t, DenseMap<Type, unsigned> &cache,
219                              function_ref<unsigned(Type)> compute) {
220   auto it = cache.find(t);
221   if (it != cache.end())
222     return it->second;
223 
224   auto result = cache.try_emplace(t, compute(t));
225   return result.first->second;
226 }
227 
228 unsigned mlir::DataLayout::getTypeSize(Type t) const {
229   checkValid();
230   return cachedLookup(t, sizes, [&](Type ty) {
231     return (scope && originalLayout)
232                ? scope.getTypeSize(
233                      ty, *this, originalLayout.getSpecForType(ty.getTypeID()))
234                : detail::getDefaultTypeSize(ty, *this, {});
235   });
236 }
237 
238 unsigned mlir::DataLayout::getTypeABIAlignment(Type t) const {
239   checkValid();
240   return cachedLookup(t, abiAlignments, [&](Type ty) {
241     return (scope && originalLayout)
242                ? scope.getTypeABIAlignment(
243                      ty, *this, originalLayout.getSpecForType(ty.getTypeID()))
244                : detail::getDefaultABIAlignment(ty, *this, {});
245   });
246 }
247 
248 unsigned mlir::DataLayout::getTypePreferredAlignment(Type t) const {
249   checkValid();
250   return cachedLookup(t, preferredAlignments, [&](Type ty) {
251     return (scope && originalLayout)
252                ? scope.getTypePreferredAlignment(
253                      ty, *this, originalLayout.getSpecForType(ty.getTypeID()))
254                : detail::getDefaultPreferredAlignment(ty, *this, {});
255   });
256 }
257 
258 //===----------------------------------------------------------------------===//
259 // DataLayoutSpecInterface
260 //===----------------------------------------------------------------------===//
261 
262 void DataLayoutSpecInterface::bucketEntriesByType(
263     DenseMap<TypeID, DataLayoutEntryList> &types,
264     DenseMap<Identifier, DataLayoutEntryInterface> &ids) {
265   for (DataLayoutEntryInterface entry : getEntries()) {
266     if (auto type = entry.getKey().dyn_cast<Type>())
267       types[type.getTypeID()].push_back(entry);
268     else
269       ids[entry.getKey().get<Identifier>()] = entry;
270   }
271 }
272 
273 LogicalResult mlir::detail::verifyDataLayoutSpec(DataLayoutSpecInterface spec,
274                                                  Location loc) {
275   // First, verify individual entries.
276   for (DataLayoutEntryInterface entry : spec.getEntries())
277     if (failed(entry.verifyEntry(loc)))
278       return failure();
279 
280   // Second, dispatch verifications of entry groups to types or dialects they
281   // are are associated with.
282   DenseMap<TypeID, DataLayoutEntryList> types;
283   DenseMap<Identifier, DataLayoutEntryInterface> ids;
284   spec.bucketEntriesByType(types, ids);
285 
286   for (const auto &kvp : types) {
287     auto sampleType = kvp.second.front().getKey().get<Type>();
288     if (isa<BuiltinDialect>(&sampleType.getDialect()))
289       return emitError(loc) << "unexpected data layout for a built-in type";
290 
291     auto dlType = sampleType.dyn_cast<DataLayoutTypeInterface>();
292     if (!dlType)
293       return emitError(loc)
294              << "data layout specified for a type that does not support it";
295     if (failed(dlType.verifyEntries(kvp.second, loc)))
296       return failure();
297   }
298 
299   for (const auto &kvp : ids) {
300     Identifier identifier = kvp.second.getKey().get<Identifier>();
301     Dialect *dialect = identifier.getDialect();
302 
303     // Ignore attributes that belong to an unknown dialect, the dialect may
304     // actually implement the relevant interface but we don't know about that.
305     if (!dialect)
306       continue;
307 
308     const auto *iface =
309         dialect->getRegisteredInterface<DataLayoutDialectInterface>();
310     if (failed(iface->verifyEntry(kvp.second, loc)))
311       return failure();
312   }
313 
314   return success();
315 }
316 
317 #include "mlir/Interfaces/DataLayoutAttrInterface.cpp.inc"
318 #include "mlir/Interfaces/DataLayoutOpInterface.cpp.inc"
319 #include "mlir/Interfaces/DataLayoutTypeInterface.cpp.inc"
320