| //===- DataLayoutImporter.cpp - LLVM to MLIR data layout conversion -------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "mlir/Target/LLVMIR/DataLayoutImporter.h" | |
| #include "mlir/Dialect/DLTI/DLTI.h" | |
| #include "mlir/IR/Builders.h" | |
| #include "mlir/IR/BuiltinAttributes.h" | |
| #include "mlir/IR/BuiltinTypes.h" | |
| #include "mlir/Interfaces/DataLayoutInterfaces.h" | |
| #include "mlir/Target/LLVMIR/Import.h" | |
| #include "llvm/IR/DataLayout.h" | |
| using namespace mlir; | |
| using namespace mlir::LLVM; | |
| using namespace mlir::LLVM::detail; | |
| /// The default data layout used during the translation. | |
| static constexpr StringRef kDefaultDataLayout = | |
| "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-" | |
| "f16:16:16-f64:64:64-f128:128:128"; | |
| FloatType mlir::LLVM::detail::getFloatType(MLIRContext *context, | |
| unsigned width) { | |
| switch (width) { | |
| case 16: | |
| return Float16Type::get(context); | |
| case 32: | |
| return Float32Type::get(context); | |
| case 64: | |
| return Float64Type::get(context); | |
| case 80: | |
| return Float80Type::get(context); | |
| case 128: | |
| return Float128Type::get(context); | |
| default: | |
| return {}; | |
| } | |
| } | |
| FailureOr<StringRef> | |
| DataLayoutImporter::tryToParseAlphaPrefix(StringRef &token) const { | |
| if (token.empty()) | |
| return failure(); | |
| StringRef prefix = token.take_while(isalpha); | |
| if (prefix.empty()) | |
| return failure(); | |
| token.consume_front(prefix); | |
| return prefix; | |
| } | |
| FailureOr<uint64_t> DataLayoutImporter::tryToParseInt(StringRef &token) const { | |
| uint64_t parameter; | |
| if (token.consumeInteger(/*Radix=*/10, parameter)) | |
| return failure(); | |
| return parameter; | |
| } | |
| template <class T> | |
| static FailureOr<SmallVector<T>> tryToParseIntListImpl(StringRef token) { | |
| SmallVector<StringRef> tokens; | |
| token.consume_front(":"); | |
| token.split(tokens, ':'); | |
| // Parse an integer list. | |
| SmallVector<T> results(tokens.size()); | |
| for (auto [result, token] : llvm::zip(results, tokens)) | |
| if (token.getAsInteger(/*Radix=*/10, result)) | |
| return failure(); | |
| return results; | |
| } | |
| FailureOr<SmallVector<uint64_t>> | |
| DataLayoutImporter::tryToParseIntList(StringRef token) const { | |
| return tryToParseIntListImpl<uint64_t>(token); | |
| } | |
| FailureOr<DenseIntElementsAttr> | |
| DataLayoutImporter::tryToParseAlignment(StringRef token) const { | |
| FailureOr<SmallVector<uint64_t>> alignment = tryToParseIntList(token); | |
| if (failed(alignment)) | |
| return failure(); | |
| if (alignment->empty() || alignment->size() > 2) | |
| return failure(); | |
| // Alignment specifications (such as 32 or 32:64) are of the | |
| // form <abi>[:<pref>], where abi specifies the minimal alignment and pref the | |
| // optional preferred alignment. The preferred alignment is set to the minimal | |
| // alignment if not available. | |
| uint64_t minimal = (*alignment)[0]; | |
| uint64_t preferred = alignment->size() == 1 ? minimal : (*alignment)[1]; | |
| return DenseIntElementsAttr::get( | |
| VectorType::get({2}, IntegerType::get(context, 64)), | |
| {minimal, preferred}); | |
| } | |
| FailureOr<DenseIntElementsAttr> | |
| DataLayoutImporter::tryToParsePointerAlignment(StringRef token) const { | |
| FailureOr<SmallVector<uint64_t>> alignment = tryToParseIntList(token); | |
| if (failed(alignment)) | |
| return failure(); | |
| if (alignment->size() < 2 || alignment->size() > 4) | |
| return failure(); | |
| // Pointer alignment specifications (such as 64:32:64:32 or 32:32) are of | |
| // the form <size>:<abi>[:<pref>][:<idx>], where size is the pointer size, abi | |
| // specifies the minimal alignment, pref the optional preferred alignment, and | |
| // idx the optional index computation bit width. The preferred alignment is | |
| // set to the minimal alignment if not available and the index computation | |
| // width is set to the pointer size if not available. | |
| uint64_t size = (*alignment)[0]; | |
| uint64_t minimal = (*alignment)[1]; | |
| uint64_t preferred = alignment->size() < 3 ? minimal : (*alignment)[2]; | |
| uint64_t idx = alignment->size() < 4 ? size : (*alignment)[3]; | |
| return DenseIntElementsAttr::get<uint64_t>( | |
| VectorType::get({4}, IntegerType::get(context, 64)), | |
| {size, minimal, preferred, idx}); | |
| } | |
| LogicalResult DataLayoutImporter::tryToEmplaceAlignmentEntry(Type type, | |
| StringRef token) { | |
| auto key = TypeAttr::get(type); | |
| if (typeEntries.count(key)) | |
| return success(); | |
| FailureOr<DenseIntElementsAttr> params = tryToParseAlignment(token); | |
| if (failed(params)) | |
| return failure(); | |
| typeEntries.try_emplace(key, DataLayoutEntryAttr::get(type, *params)); | |
| return success(); | |
| } | |
| LogicalResult | |
| DataLayoutImporter::tryToEmplacePointerAlignmentEntry(LLVMPointerType type, | |
| StringRef token) { | |
| auto key = TypeAttr::get(type); | |
| if (typeEntries.count(key)) | |
| return success(); | |
| FailureOr<DenseIntElementsAttr> params = tryToParsePointerAlignment(token); | |
| if (failed(params)) | |
| return failure(); | |
| typeEntries.try_emplace(key, DataLayoutEntryAttr::get(type, *params)); | |
| return success(); | |
| } | |
| LogicalResult | |
| DataLayoutImporter::tryToEmplaceEndiannessEntry(StringRef endianness, | |
| StringRef token) { | |
| auto key = StringAttr::get(context, DLTIDialect::kDataLayoutEndiannessKey); | |
| if (keyEntries.count(key)) | |
| return success(); | |
| if (!token.empty()) | |
| return failure(); | |
| keyEntries.try_emplace( | |
| key, DataLayoutEntryAttr::get(key, StringAttr::get(context, endianness))); | |
| return success(); | |
| } | |
| LogicalResult DataLayoutImporter::tryToEmplaceManglingModeEntry( | |
| StringRef token, llvm::StringLiteral manglingKey) { | |
| auto key = StringAttr::get(context, manglingKey); | |
| if (keyEntries.count(key)) | |
| return success(); | |
| token.consume_front(":"); | |
| if (token.empty()) | |
| return failure(); | |
| keyEntries.try_emplace( | |
| key, DataLayoutEntryAttr::get(key, StringAttr::get(context, token))); | |
| return success(); | |
| } | |
| LogicalResult | |
| DataLayoutImporter::tryToEmplaceAddrSpaceEntry(StringRef token, | |
| llvm::StringLiteral spaceKey) { | |
| auto key = StringAttr::get(context, spaceKey); | |
| if (keyEntries.count(key)) | |
| return success(); | |
| FailureOr<uint64_t> space = tryToParseInt(token); | |
| if (failed(space)) | |
| return failure(); | |
| // Only store the address space if it has a non-default value. | |
| if (*space == 0) | |
| return success(); | |
| OpBuilder builder(context); | |
| keyEntries.try_emplace( | |
| key, | |
| DataLayoutEntryAttr::get( | |
| key, builder.getIntegerAttr( | |
| builder.getIntegerType(64, /*isSigned=*/false), *space))); | |
| return success(); | |
| } | |
| LogicalResult | |
| DataLayoutImporter::tryToEmplaceStackAlignmentEntry(StringRef token) { | |
| auto key = | |
| StringAttr::get(context, DLTIDialect::kDataLayoutStackAlignmentKey); | |
| if (keyEntries.count(key)) | |
| return success(); | |
| FailureOr<uint64_t> alignment = tryToParseInt(token); | |
| if (failed(alignment)) | |
| return failure(); | |
| // Stack alignment shouldn't be zero. | |
| if (*alignment == 0) | |
| return failure(); | |
| OpBuilder builder(context); | |
| keyEntries.try_emplace(key, DataLayoutEntryAttr::get( | |
| key, builder.getI64IntegerAttr(*alignment))); | |
| return success(); | |
| } | |
| LogicalResult DataLayoutImporter::tryToEmplaceFunctionPointerAlignmentEntry( | |
| StringRef fnPtrString, StringRef token) { | |
| auto key = StringAttr::get( | |
| context, DLTIDialect::kDataLayoutFunctionPointerAlignmentKey); | |
| if (keyEntries.count(key)) | |
| return success(); | |
| // The data layout entry for "F<type><abi>". <abi> is the aligment value, | |
| // preceded by one of the two possible <types>: | |
| // "i": The alignment of function pointers is independent of the alignment of | |
| // functions, and is a multiple of <abi>. | |
| // "n": The alignment of function pointers is a multiple of the explicit | |
| // alignment specified on the function, and is a multiple of <abi>. | |
| bool functionDependent = false; | |
| if (fnPtrString == "n") | |
| functionDependent = true; | |
| else if (fnPtrString != "i") | |
| return failure(); | |
| FailureOr<uint64_t> alignment = tryToParseInt(token); | |
| if (failed(alignment)) | |
| return failure(); | |
| keyEntries.try_emplace( | |
| key, DataLayoutEntryAttr::get( | |
| key, FunctionPointerAlignmentAttr::get( | |
| key.getContext(), *alignment, functionDependent))); | |
| return success(); | |
| } | |
| LogicalResult | |
| DataLayoutImporter::tryToEmplaceLegalIntWidthsEntry(StringRef token) { | |
| auto key = | |
| StringAttr::get(context, DLTIDialect::kDataLayoutLegalIntWidthsKey); | |
| if (keyEntries.count(key)) | |
| return success(); | |
| FailureOr<SmallVector<int32_t>> intWidths = | |
| tryToParseIntListImpl<int32_t>(token); | |
| if (failed(intWidths) || intWidths->empty()) | |
| return failure(); | |
| OpBuilder builder(context); | |
| keyEntries.try_emplace( | |
| key, | |
| DataLayoutEntryAttr::get(key, builder.getDenseI32ArrayAttr(*intWidths))); | |
| return success(); | |
| } | |
| DataLayoutSpecInterface DataLayoutImporter::dataLayoutSpecFromDataLayoutStr() { | |
| if (!dataLayoutStr.empty()) | |
| dataLayoutStr += "-"; | |
| dataLayoutStr += kDefaultDataLayout; | |
| // Split the data layout string into tokens separated by a dash. | |
| SmallVector<StringRef> tokens; | |
| StringRef(dataLayoutStr).split(tokens, '-'); | |
| for (StringRef token : tokens) { | |
| lastToken = token; | |
| FailureOr<StringRef> prefix = tryToParseAlphaPrefix(token); | |
| if (failed(prefix)) | |
| return {}; | |
| // Parse the endianness. | |
| if (*prefix == "e") { | |
| if (failed(tryToEmplaceEndiannessEntry( | |
| DLTIDialect::kDataLayoutEndiannessLittle, token))) | |
| return {}; | |
| continue; | |
| } | |
| if (*prefix == "E") { | |
| if (failed(tryToEmplaceEndiannessEntry( | |
| DLTIDialect::kDataLayoutEndiannessBig, token))) | |
| return {}; | |
| continue; | |
| } | |
| // Parse the program address space. | |
| if (*prefix == "P") { | |
| if (failed(tryToEmplaceAddrSpaceEntry( | |
| token, DLTIDialect::kDataLayoutProgramMemorySpaceKey))) | |
| return {}; | |
| continue; | |
| } | |
| // Parse the mangling mode. | |
| if (*prefix == "m") { | |
| if (failed(tryToEmplaceManglingModeEntry( | |
| token, DLTIDialect::kDataLayoutManglingModeKey))) | |
| return {}; | |
| continue; | |
| } | |
| // Parse the global address space. | |
| if (*prefix == "G") { | |
| if (failed(tryToEmplaceAddrSpaceEntry( | |
| token, DLTIDialect::kDataLayoutGlobalMemorySpaceKey))) | |
| return {}; | |
| continue; | |
| } | |
| // Parse the alloca address space. | |
| if (*prefix == "A") { | |
| if (failed(tryToEmplaceAddrSpaceEntry( | |
| token, DLTIDialect::kDataLayoutAllocaMemorySpaceKey))) | |
| return {}; | |
| continue; | |
| } | |
| // Parse the stack alignment. | |
| if (*prefix == "S") { | |
| if (failed(tryToEmplaceStackAlignmentEntry(token))) | |
| return {}; | |
| continue; | |
| } | |
| // Parse integer alignment specifications. | |
| if (*prefix == "i") { | |
| FailureOr<uint64_t> width = tryToParseInt(token); | |
| if (failed(width)) | |
| return {}; | |
| Type type = IntegerType::get(context, *width); | |
| if (failed(tryToEmplaceAlignmentEntry(type, token))) | |
| return {}; | |
| continue; | |
| } | |
| // Parse float alignment specifications. | |
| if (*prefix == "f") { | |
| FailureOr<uint64_t> width = tryToParseInt(token); | |
| if (failed(width)) | |
| return {}; | |
| Type type = getFloatType(context, *width); | |
| if (failed(tryToEmplaceAlignmentEntry(type, token))) | |
| return {}; | |
| continue; | |
| } | |
| // Parse pointer alignment specifications. | |
| if (*prefix == "p") { | |
| FailureOr<uint64_t> space = | |
| token.starts_with(":") ? 0 : tryToParseInt(token); | |
| if (failed(space)) | |
| return {}; | |
| auto type = LLVMPointerType::get(context, *space); | |
| if (failed(tryToEmplacePointerAlignmentEntry(type, token))) | |
| return {}; | |
| continue; | |
| } | |
| // Parse native integer widths specifications. | |
| if (*prefix == "n") { | |
| if (failed(tryToEmplaceLegalIntWidthsEntry(token))) | |
| return {}; | |
| continue; | |
| } | |
| // Parse function pointer alignment specifications. | |
| // Note that prefix here is "Fn" or "Fi", not a single character. | |
| if (prefix->starts_with("F")) { | |
| StringRef nextPrefix = prefix->drop_front(1); | |
| if (failed(tryToEmplaceFunctionPointerAlignmentEntry(nextPrefix, token))) | |
| return {}; | |
| continue; | |
| } | |
| // Store all tokens that have not been handled. | |
| unhandledTokens.push_back(lastToken); | |
| } | |
| // Assemble all entries to a data layout specification. | |
| SmallVector<DataLayoutEntryInterface> entries; | |
| entries.reserve(typeEntries.size() + keyEntries.size()); | |
| for (const auto &it : typeEntries) | |
| entries.push_back(it.second); | |
| for (const auto &it : keyEntries) | |
| entries.push_back(it.second); | |
| return DataLayoutSpecAttr::get(context, entries); | |
| } | |
| DataLayoutSpecInterface | |
| mlir::translateDataLayout(const llvm::DataLayout &dataLayout, | |
| MLIRContext *context) { | |
| return DataLayoutImporter(context, dataLayout.getStringRepresentation()) | |
| .getDataLayoutSpec(); | |
| } |