331 lines
11 KiB
C++
331 lines
11 KiB
C++
|
//===- 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 "DataLayoutImporter.h"
|
||
|
#include "mlir/Dialect/DLTI/DLTI.h"
|
||
|
#include "mlir/Dialect/LLVMIR/LLVMDialect.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 FloatType::getF16(context);
|
||
|
case 32:
|
||
|
return FloatType::getF32(context);
|
||
|
case 64:
|
||
|
return FloatType::getF64(context);
|
||
|
case 80:
|
||
|
return FloatType::getF80(context);
|
||
|
case 128:
|
||
|
return FloatType::getF128(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;
|
||
|
}
|
||
|
|
||
|
FailureOr<SmallVector<uint64_t>>
|
||
|
DataLayoutImporter::tryToParseIntList(StringRef token) const {
|
||
|
SmallVector<StringRef> tokens;
|
||
|
token.consume_front(":");
|
||
|
token.split(tokens, ':');
|
||
|
|
||
|
// Parse an integer list.
|
||
|
SmallVector<uint64_t> results(tokens.size());
|
||
|
for (auto [result, token] : llvm::zip(results, tokens))
|
||
|
if (token.getAsInteger(/*Radix=*/10, result))
|
||
|
return failure();
|
||
|
return results;
|
||
|
}
|
||
|
|
||
|
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::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();
|
||
|
|
||
|
// Only store the stack alignment if it has a non-default value.
|
||
|
if (*alignment == 0)
|
||
|
return success();
|
||
|
OpBuilder builder(context);
|
||
|
keyEntries.try_emplace(key, DataLayoutEntryAttr::get(
|
||
|
key, builder.getI64IntegerAttr(*alignment)));
|
||
|
return success();
|
||
|
}
|
||
|
|
||
|
void DataLayoutImporter::translateDataLayout(
|
||
|
const llvm::DataLayout &llvmDataLayout) {
|
||
|
dataLayout = {};
|
||
|
|
||
|
// Transform the data layout to its string representation and append the
|
||
|
// default data layout string specified in the language reference
|
||
|
// (https://llvm.org/docs/LangRef.html#data-layout). The translation then
|
||
|
// parses the string and ignores the default value if a specific kind occurs
|
||
|
// in both strings. Additionally, the following default values exist:
|
||
|
// - non-default address space pointer specifications default to the default
|
||
|
// address space pointer specification
|
||
|
// - the alloca address space defaults to the default address space.
|
||
|
layoutStr = llvmDataLayout.getStringRepresentation();
|
||
|
if (!layoutStr.empty())
|
||
|
layoutStr += "-";
|
||
|
layoutStr += kDefaultDataLayout;
|
||
|
StringRef layout(layoutStr);
|
||
|
|
||
|
// Split the data layout string into tokens separated by a dash.
|
||
|
SmallVector<StringRef> tokens;
|
||
|
layout.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 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;
|
||
|
}
|
||
|
|
||
|
// 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);
|
||
|
dataLayout = DataLayoutSpecAttr::get(context, entries);
|
||
|
}
|
||
|
|
||
|
DataLayoutSpecInterface
|
||
|
mlir::translateDataLayout(const llvm::DataLayout &dataLayout,
|
||
|
MLIRContext *context) {
|
||
|
return DataLayoutImporter(context, dataLayout).getDataLayout();
|
||
|
}
|