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