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