| 1 | //===- DataLayoutImporter.cpp - LLVM to MLIR data layout conversion -------===// |
| 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 "DataLayoutImporter.h" |
| 10 | #include "mlir/Dialect/DLTI/DLTI.h" |
| 11 | #include "mlir/IR/Builders.h" |
| 12 | #include "mlir/IR/BuiltinAttributes.h" |
| 13 | #include "mlir/IR/BuiltinTypes.h" |
| 14 | #include "mlir/Interfaces/DataLayoutInterfaces.h" |
| 15 | #include "mlir/Target/LLVMIR/Import.h" |
| 16 | #include "llvm/IR/DataLayout.h" |
| 17 | |
| 18 | using namespace mlir; |
| 19 | using namespace mlir::LLVM; |
| 20 | using namespace mlir::LLVM::detail; |
| 21 | |
| 22 | /// The default data layout used during the translation. |
| 23 | static constexpr StringRef kDefaultDataLayout = |
| 24 | "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-" |
| 25 | "f16:16:16-f64:64:64-f128:128:128" ; |
| 26 | |
| 27 | FloatType mlir::LLVM::detail::getFloatType(MLIRContext *context, |
| 28 | unsigned width) { |
| 29 | switch (width) { |
| 30 | case 16: |
| 31 | return Float16Type::get(context); |
| 32 | case 32: |
| 33 | return Float32Type::get(context); |
| 34 | case 64: |
| 35 | return Float64Type::get(context); |
| 36 | case 80: |
| 37 | return Float80Type::get(context); |
| 38 | case 128: |
| 39 | return Float128Type::get(context); |
| 40 | default: |
| 41 | return {}; |
| 42 | } |
| 43 | } |
| 44 | |
| 45 | FailureOr<StringRef> |
| 46 | DataLayoutImporter::tryToParseAlphaPrefix(StringRef &token) const { |
| 47 | if (token.empty()) |
| 48 | return failure(); |
| 49 | |
| 50 | StringRef prefix = token.take_while(F: isalpha); |
| 51 | if (prefix.empty()) |
| 52 | return failure(); |
| 53 | |
| 54 | token.consume_front(Prefix: prefix); |
| 55 | return prefix; |
| 56 | } |
| 57 | |
| 58 | FailureOr<uint64_t> DataLayoutImporter::tryToParseInt(StringRef &token) const { |
| 59 | uint64_t parameter; |
| 60 | if (token.consumeInteger(/*Radix=*/10, Result&: parameter)) |
| 61 | return failure(); |
| 62 | return parameter; |
| 63 | } |
| 64 | |
| 65 | template <class T> |
| 66 | static FailureOr<SmallVector<T>> tryToParseIntListImpl(StringRef token) { |
| 67 | SmallVector<StringRef> tokens; |
| 68 | token.consume_front(Prefix: ":" ); |
| 69 | token.split(A&: tokens, Separator: ':'); |
| 70 | |
| 71 | // Parse an integer list. |
| 72 | SmallVector<T> results(tokens.size()); |
| 73 | for (auto [result, token] : llvm::zip(results, tokens)) |
| 74 | if (token.getAsInteger(/*Radix=*/10, result)) |
| 75 | return failure(); |
| 76 | return results; |
| 77 | } |
| 78 | |
| 79 | FailureOr<SmallVector<uint64_t>> |
| 80 | DataLayoutImporter::tryToParseIntList(StringRef token) const { |
| 81 | return tryToParseIntListImpl<uint64_t>(token); |
| 82 | } |
| 83 | |
| 84 | FailureOr<DenseIntElementsAttr> |
| 85 | DataLayoutImporter::tryToParseAlignment(StringRef token) const { |
| 86 | FailureOr<SmallVector<uint64_t>> alignment = tryToParseIntList(token); |
| 87 | if (failed(Result: alignment)) |
| 88 | return failure(); |
| 89 | if (alignment->empty() || alignment->size() > 2) |
| 90 | return failure(); |
| 91 | |
| 92 | // Alignment specifications (such as 32 or 32:64) are of the |
| 93 | // form <abi>[:<pref>], where abi specifies the minimal alignment and pref the |
| 94 | // optional preferred alignment. The preferred alignment is set to the minimal |
| 95 | // alignment if not available. |
| 96 | uint64_t minimal = (*alignment)[0]; |
| 97 | uint64_t preferred = alignment->size() == 1 ? minimal : (*alignment)[1]; |
| 98 | return DenseIntElementsAttr::get( |
| 99 | type: VectorType::get(shape: {2}, elementType: IntegerType::get(context, width: 64)), |
| 100 | list: {minimal, preferred}); |
| 101 | } |
| 102 | |
| 103 | FailureOr<DenseIntElementsAttr> |
| 104 | DataLayoutImporter::tryToParsePointerAlignment(StringRef token) const { |
| 105 | FailureOr<SmallVector<uint64_t>> alignment = tryToParseIntList(token); |
| 106 | if (failed(Result: alignment)) |
| 107 | return failure(); |
| 108 | if (alignment->size() < 2 || alignment->size() > 4) |
| 109 | return failure(); |
| 110 | |
| 111 | // Pointer alignment specifications (such as 64:32:64:32 or 32:32) are of |
| 112 | // the form <size>:<abi>[:<pref>][:<idx>], where size is the pointer size, abi |
| 113 | // specifies the minimal alignment, pref the optional preferred alignment, and |
| 114 | // idx the optional index computation bit width. The preferred alignment is |
| 115 | // set to the minimal alignment if not available and the index computation |
| 116 | // width is set to the pointer size if not available. |
| 117 | uint64_t size = (*alignment)[0]; |
| 118 | uint64_t minimal = (*alignment)[1]; |
| 119 | uint64_t preferred = alignment->size() < 3 ? minimal : (*alignment)[2]; |
| 120 | uint64_t idx = alignment->size() < 4 ? size : (*alignment)[3]; |
| 121 | return DenseIntElementsAttr::get<uint64_t>( |
| 122 | type: VectorType::get(shape: {4}, elementType: IntegerType::get(context, width: 64)), |
| 123 | list: {size, minimal, preferred, idx}); |
| 124 | } |
| 125 | |
| 126 | LogicalResult DataLayoutImporter::tryToEmplaceAlignmentEntry(Type type, |
| 127 | StringRef token) { |
| 128 | auto key = TypeAttr::get(type); |
| 129 | if (typeEntries.count(Key: key)) |
| 130 | return success(); |
| 131 | |
| 132 | FailureOr<DenseIntElementsAttr> params = tryToParseAlignment(token); |
| 133 | if (failed(Result: params)) |
| 134 | return failure(); |
| 135 | |
| 136 | typeEntries.try_emplace(Key: key, Args: DataLayoutEntryAttr::get(key: type, value: *params)); |
| 137 | return success(); |
| 138 | } |
| 139 | |
| 140 | LogicalResult |
| 141 | DataLayoutImporter::tryToEmplacePointerAlignmentEntry(LLVMPointerType type, |
| 142 | StringRef token) { |
| 143 | auto key = TypeAttr::get(type); |
| 144 | if (typeEntries.count(Key: key)) |
| 145 | return success(); |
| 146 | |
| 147 | FailureOr<DenseIntElementsAttr> params = tryToParsePointerAlignment(token); |
| 148 | if (failed(Result: params)) |
| 149 | return failure(); |
| 150 | |
| 151 | typeEntries.try_emplace(Key: key, Args: DataLayoutEntryAttr::get(key: type, value: *params)); |
| 152 | return success(); |
| 153 | } |
| 154 | |
| 155 | LogicalResult |
| 156 | DataLayoutImporter::tryToEmplaceEndiannessEntry(StringRef endianness, |
| 157 | StringRef token) { |
| 158 | auto key = StringAttr::get(context, bytes: DLTIDialect::kDataLayoutEndiannessKey); |
| 159 | if (keyEntries.count(Key: key)) |
| 160 | return success(); |
| 161 | |
| 162 | if (!token.empty()) |
| 163 | return failure(); |
| 164 | |
| 165 | keyEntries.try_emplace( |
| 166 | Key: key, Args: DataLayoutEntryAttr::get(key, value: StringAttr::get(context, bytes: endianness))); |
| 167 | return success(); |
| 168 | } |
| 169 | |
| 170 | LogicalResult DataLayoutImporter::tryToEmplaceManglingModeEntry( |
| 171 | StringRef token, llvm::StringLiteral manglingKey) { |
| 172 | auto key = StringAttr::get(context, bytes: manglingKey); |
| 173 | if (keyEntries.count(Key: key)) |
| 174 | return success(); |
| 175 | |
| 176 | token.consume_front(Prefix: ":" ); |
| 177 | if (token.empty()) |
| 178 | return failure(); |
| 179 | |
| 180 | keyEntries.try_emplace( |
| 181 | Key: key, Args: DataLayoutEntryAttr::get(key, value: StringAttr::get(context, bytes: token))); |
| 182 | return success(); |
| 183 | } |
| 184 | |
| 185 | LogicalResult |
| 186 | DataLayoutImporter::tryToEmplaceAddrSpaceEntry(StringRef token, |
| 187 | llvm::StringLiteral spaceKey) { |
| 188 | auto key = StringAttr::get(context, bytes: spaceKey); |
| 189 | if (keyEntries.count(Key: key)) |
| 190 | return success(); |
| 191 | |
| 192 | FailureOr<uint64_t> space = tryToParseInt(token); |
| 193 | if (failed(Result: space)) |
| 194 | return failure(); |
| 195 | |
| 196 | // Only store the address space if it has a non-default value. |
| 197 | if (*space == 0) |
| 198 | return success(); |
| 199 | OpBuilder builder(context); |
| 200 | keyEntries.try_emplace( |
| 201 | Key: key, |
| 202 | Args: DataLayoutEntryAttr::get( |
| 203 | key, value: builder.getIntegerAttr( |
| 204 | type: builder.getIntegerType(width: 64, /*isSigned=*/false), value: *space))); |
| 205 | return success(); |
| 206 | } |
| 207 | |
| 208 | LogicalResult |
| 209 | DataLayoutImporter::tryToEmplaceStackAlignmentEntry(StringRef token) { |
| 210 | auto key = |
| 211 | StringAttr::get(context, bytes: DLTIDialect::kDataLayoutStackAlignmentKey); |
| 212 | if (keyEntries.count(Key: key)) |
| 213 | return success(); |
| 214 | |
| 215 | FailureOr<uint64_t> alignment = tryToParseInt(token); |
| 216 | if (failed(Result: alignment)) |
| 217 | return failure(); |
| 218 | |
| 219 | // Stack alignment shouldn't be zero. |
| 220 | if (*alignment == 0) |
| 221 | return failure(); |
| 222 | OpBuilder builder(context); |
| 223 | keyEntries.try_emplace(Key: key, Args: DataLayoutEntryAttr::get( |
| 224 | key, value: builder.getI64IntegerAttr(value: *alignment))); |
| 225 | return success(); |
| 226 | } |
| 227 | |
| 228 | LogicalResult DataLayoutImporter::tryToEmplaceFunctionPointerAlignmentEntry( |
| 229 | StringRef fnPtrString, StringRef token) { |
| 230 | auto key = StringAttr::get( |
| 231 | context, bytes: DLTIDialect::kDataLayoutFunctionPointerAlignmentKey); |
| 232 | if (keyEntries.count(Key: key)) |
| 233 | return success(); |
| 234 | |
| 235 | // The data layout entry for "F<type><abi>". <abi> is the aligment value, |
| 236 | // preceded by one of the two possible <types>: |
| 237 | // "i": The alignment of function pointers is independent of the alignment of |
| 238 | // functions, and is a multiple of <abi>. |
| 239 | // "n": The alignment of function pointers is a multiple of the explicit |
| 240 | // alignment specified on the function, and is a multiple of <abi>. |
| 241 | bool functionDependent = false; |
| 242 | if (fnPtrString == "n" ) |
| 243 | functionDependent = true; |
| 244 | else if (fnPtrString != "i" ) |
| 245 | return failure(); |
| 246 | |
| 247 | FailureOr<uint64_t> alignment = tryToParseInt(token); |
| 248 | if (failed(Result: alignment)) |
| 249 | return failure(); |
| 250 | |
| 251 | keyEntries.try_emplace( |
| 252 | Key: key, Args: DataLayoutEntryAttr::get( |
| 253 | key, value: FunctionPointerAlignmentAttr::get( |
| 254 | context: key.getContext(), alignment: *alignment, function_dependent: functionDependent))); |
| 255 | return success(); |
| 256 | } |
| 257 | |
| 258 | LogicalResult |
| 259 | DataLayoutImporter::tryToEmplaceLegalIntWidthsEntry(StringRef token) { |
| 260 | auto key = |
| 261 | StringAttr::get(context, bytes: DLTIDialect::kDataLayoutLegalIntWidthsKey); |
| 262 | if (keyEntries.count(Key: key)) |
| 263 | return success(); |
| 264 | |
| 265 | FailureOr<SmallVector<int32_t>> intWidths = |
| 266 | tryToParseIntListImpl<int32_t>(token); |
| 267 | if (failed(Result: intWidths) || intWidths->empty()) |
| 268 | return failure(); |
| 269 | |
| 270 | OpBuilder builder(context); |
| 271 | keyEntries.try_emplace( |
| 272 | Key: key, |
| 273 | Args: DataLayoutEntryAttr::get(key, value: builder.getDenseI32ArrayAttr(values: *intWidths))); |
| 274 | return success(); |
| 275 | } |
| 276 | |
| 277 | void DataLayoutImporter::translateDataLayout( |
| 278 | const llvm::DataLayout &llvmDataLayout) { |
| 279 | dataLayout = {}; |
| 280 | |
| 281 | // Transform the data layout to its string representation and append the |
| 282 | // default data layout string specified in the language reference |
| 283 | // (https://llvm.org/docs/LangRef.html#data-layout). The translation then |
| 284 | // parses the string and ignores the default value if a specific kind occurs |
| 285 | // in both strings. Additionally, the following default values exist: |
| 286 | // - non-default address space pointer specifications default to the default |
| 287 | // address space pointer specification |
| 288 | // - the alloca address space defaults to the default address space. |
| 289 | layoutStr = llvmDataLayout.getStringRepresentation(); |
| 290 | if (!layoutStr.empty()) |
| 291 | layoutStr += "-" ; |
| 292 | layoutStr += kDefaultDataLayout; |
| 293 | StringRef layout(layoutStr); |
| 294 | |
| 295 | // Split the data layout string into tokens separated by a dash. |
| 296 | SmallVector<StringRef> tokens; |
| 297 | layout.split(A&: tokens, Separator: '-'); |
| 298 | |
| 299 | for (StringRef token : tokens) { |
| 300 | lastToken = token; |
| 301 | FailureOr<StringRef> prefix = tryToParseAlphaPrefix(token); |
| 302 | if (failed(Result: prefix)) |
| 303 | return; |
| 304 | |
| 305 | // Parse the endianness. |
| 306 | if (*prefix == "e" ) { |
| 307 | if (failed(Result: tryToEmplaceEndiannessEntry( |
| 308 | endianness: DLTIDialect::kDataLayoutEndiannessLittle, token))) |
| 309 | return; |
| 310 | continue; |
| 311 | } |
| 312 | if (*prefix == "E" ) { |
| 313 | if (failed(Result: tryToEmplaceEndiannessEntry( |
| 314 | endianness: DLTIDialect::kDataLayoutEndiannessBig, token))) |
| 315 | return; |
| 316 | continue; |
| 317 | } |
| 318 | // Parse the program address space. |
| 319 | if (*prefix == "P" ) { |
| 320 | if (failed(Result: tryToEmplaceAddrSpaceEntry( |
| 321 | token, spaceKey: DLTIDialect::kDataLayoutProgramMemorySpaceKey))) |
| 322 | return; |
| 323 | continue; |
| 324 | } |
| 325 | // Parse the mangling mode. |
| 326 | if (*prefix == "m" ) { |
| 327 | if (failed(Result: tryToEmplaceManglingModeEntry( |
| 328 | token, manglingKey: DLTIDialect::kDataLayoutManglingModeKey))) |
| 329 | return; |
| 330 | continue; |
| 331 | } |
| 332 | // Parse the global address space. |
| 333 | if (*prefix == "G" ) { |
| 334 | if (failed(Result: tryToEmplaceAddrSpaceEntry( |
| 335 | token, spaceKey: DLTIDialect::kDataLayoutGlobalMemorySpaceKey))) |
| 336 | return; |
| 337 | continue; |
| 338 | } |
| 339 | // Parse the alloca address space. |
| 340 | if (*prefix == "A" ) { |
| 341 | if (failed(Result: tryToEmplaceAddrSpaceEntry( |
| 342 | token, spaceKey: DLTIDialect::kDataLayoutAllocaMemorySpaceKey))) |
| 343 | return; |
| 344 | continue; |
| 345 | } |
| 346 | // Parse the stack alignment. |
| 347 | if (*prefix == "S" ) { |
| 348 | if (failed(Result: tryToEmplaceStackAlignmentEntry(token))) |
| 349 | return; |
| 350 | continue; |
| 351 | } |
| 352 | // Parse integer alignment specifications. |
| 353 | if (*prefix == "i" ) { |
| 354 | FailureOr<uint64_t> width = tryToParseInt(token); |
| 355 | if (failed(Result: width)) |
| 356 | return; |
| 357 | |
| 358 | Type type = IntegerType::get(context, width: *width); |
| 359 | if (failed(Result: tryToEmplaceAlignmentEntry(type, token))) |
| 360 | return; |
| 361 | continue; |
| 362 | } |
| 363 | // Parse float alignment specifications. |
| 364 | if (*prefix == "f" ) { |
| 365 | FailureOr<uint64_t> width = tryToParseInt(token); |
| 366 | if (failed(Result: width)) |
| 367 | return; |
| 368 | |
| 369 | Type type = getFloatType(context, width: *width); |
| 370 | if (failed(Result: tryToEmplaceAlignmentEntry(type, token))) |
| 371 | return; |
| 372 | continue; |
| 373 | } |
| 374 | // Parse pointer alignment specifications. |
| 375 | if (*prefix == "p" ) { |
| 376 | FailureOr<uint64_t> space = |
| 377 | token.starts_with(Prefix: ":" ) ? 0 : tryToParseInt(token); |
| 378 | if (failed(Result: space)) |
| 379 | return; |
| 380 | |
| 381 | auto type = LLVMPointerType::get(context, addressSpace: *space); |
| 382 | if (failed(Result: tryToEmplacePointerAlignmentEntry(type, token))) |
| 383 | return; |
| 384 | continue; |
| 385 | } |
| 386 | // Parse native integer widths specifications. |
| 387 | if (*prefix == "n" ) { |
| 388 | if (failed(Result: tryToEmplaceLegalIntWidthsEntry(token))) |
| 389 | return; |
| 390 | continue; |
| 391 | } |
| 392 | // Parse function pointer alignment specifications. |
| 393 | // Note that prefix here is "Fn" or "Fi", not a single character. |
| 394 | if (prefix->starts_with(Prefix: "F" )) { |
| 395 | StringRef nextPrefix = prefix->drop_front(N: 1); |
| 396 | if (failed(Result: tryToEmplaceFunctionPointerAlignmentEntry(fnPtrString: nextPrefix, token))) |
| 397 | return; |
| 398 | continue; |
| 399 | } |
| 400 | |
| 401 | // Store all tokens that have not been handled. |
| 402 | unhandledTokens.push_back(Elt: lastToken); |
| 403 | } |
| 404 | |
| 405 | // Assemble all entries to a data layout specification. |
| 406 | SmallVector<DataLayoutEntryInterface> entries; |
| 407 | entries.reserve(N: typeEntries.size() + keyEntries.size()); |
| 408 | for (const auto &it : typeEntries) |
| 409 | entries.push_back(Elt: it.second); |
| 410 | for (const auto &it : keyEntries) |
| 411 | entries.push_back(Elt: it.second); |
| 412 | dataLayout = DataLayoutSpecAttr::get(context, entries); |
| 413 | } |
| 414 | |
| 415 | DataLayoutSpecInterface |
| 416 | mlir::translateDataLayout(const llvm::DataLayout &dataLayout, |
| 417 | MLIRContext *context) { |
| 418 | return DataLayoutImporter(context, dataLayout).getDataLayout(); |
| 419 | } |
| 420 | |