[Mlir-commits] [mlir] 664cc93 - [mlir] Implement `DataLayoutTypeInterface` for `LLVMStructType`
Markus Böck
llvmlistbot at llvm.org
Mon Dec 13 06:09:29 PST 2021
Author: Markus Böck
Date: 2021-12-13T15:09:16+01:00
New Revision: 664cc9312cf5e670a58f841c3ac6e7e6b930ba0a
URL: https://github.com/llvm/llvm-project/commit/664cc9312cf5e670a58f841c3ac6e7e6b930ba0a
DIFF: https://github.com/llvm/llvm-project/commit/664cc9312cf5e670a58f841c3ac6e7e6b930ba0a.diff
LOG: [mlir] Implement `DataLayoutTypeInterface` for `LLVMStructType`
Using this implementation of the interface it is possible to query the size, ABI alignment as well as the preferred alignment of a struct. It should yield the same results as LLVMs `llvm::DataLayout` on an equivalent `llvm::StructType`, including for packed structs.
Additionally it is also possible to increase the ABI and preferred alignment using a data layout entry with the type `llvm.struct<()>, which serves the same functionality as the `a:` component in LLVMs data layout string.
Differential Revision: https://reviews.llvm.org/D115600
Added:
Modified:
mlir/include/mlir/Dialect/LLVMIR/LLVMTypes.h
mlir/lib/Dialect/LLVMIR/IR/LLVMTypes.cpp
mlir/test/Dialect/LLVMIR/layout.mlir
Removed:
################################################################################
diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMTypes.h b/mlir/include/mlir/Dialect/LLVMIR/LLVMTypes.h
index 6abede4b8c557..399542317953b 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/LLVMTypes.h
+++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMTypes.h
@@ -226,8 +226,9 @@ class LLVMPointerType : public Type::TypeBase<LLVMPointerType, Type,
///
/// Note that the packedness of the struct takes place in uniquing of literal
/// structs, but does not in uniquing of identified structs.
-class LLVMStructType : public Type::TypeBase<LLVMStructType, Type,
- detail::LLVMStructTypeStorage> {
+class LLVMStructType
+ : public Type::TypeBase<LLVMStructType, Type, detail::LLVMStructTypeStorage,
+ DataLayoutTypeInterface::Trait> {
public:
/// Inherit base constructors.
using Base::Base;
@@ -282,10 +283,10 @@ class LLVMStructType : public Type::TypeBase<LLVMStructType, Type,
LogicalResult setBody(ArrayRef<Type> types, bool isPacked);
/// Checks if a struct is packed.
- bool isPacked();
+ bool isPacked() const;
/// Checks if a struct is identified.
- bool isIdentified();
+ bool isIdentified() const;
/// Checks if a struct is opaque.
bool isOpaque();
@@ -297,13 +298,30 @@ class LLVMStructType : public Type::TypeBase<LLVMStructType, Type,
StringRef getName();
/// Returns the list of element types contained in a non-opaque struct.
- ArrayRef<Type> getBody();
+ ArrayRef<Type> getBody() const;
/// Verifies that the type about to be constructed is well-formed.
static LogicalResult verify(function_ref<InFlightDiagnostic()> emitError,
StringRef, bool);
static LogicalResult verify(function_ref<InFlightDiagnostic()> emitError,
ArrayRef<Type> types, bool);
+
+ /// Hooks for DataLayoutTypeInterface. Should not be called directly. Obtain a
+ /// DataLayout instance and query it instead.
+ unsigned getTypeSizeInBits(const DataLayout &dataLayout,
+ DataLayoutEntryListRef params) const;
+
+ unsigned getABIAlignment(const DataLayout &dataLayout,
+ DataLayoutEntryListRef params) const;
+
+ unsigned getPreferredAlignment(const DataLayout &dataLayout,
+ DataLayoutEntryListRef params) const;
+
+ bool areCompatible(DataLayoutEntryListRef oldLayout,
+ DataLayoutEntryListRef newLayout) const;
+
+ LogicalResult verifyEntries(DataLayoutEntryListRef entries,
+ Location loc) const;
};
//===----------------------------------------------------------------------===//
diff --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMTypes.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMTypes.cpp
index 1ce9e4481e9c0..6751825fdfe69 100644
--- a/mlir/lib/Dialect/LLVMIR/IR/LLVMTypes.cpp
+++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMTypes.cpp
@@ -361,15 +361,15 @@ LogicalResult LLVMStructType::setBody(ArrayRef<Type> types, bool isPacked) {
return Base::mutate(types, isPacked);
}
-bool LLVMStructType::isPacked() { return getImpl()->isPacked(); }
-bool LLVMStructType::isIdentified() { return getImpl()->isIdentified(); }
+bool LLVMStructType::isPacked() const { return getImpl()->isPacked(); }
+bool LLVMStructType::isIdentified() const { return getImpl()->isIdentified(); }
bool LLVMStructType::isOpaque() {
return getImpl()->isIdentified() &&
(getImpl()->isOpaque() || !getImpl()->isInitialized());
}
bool LLVMStructType::isInitialized() { return getImpl()->isInitialized(); }
StringRef LLVMStructType::getName() { return getImpl()->getIdentifier(); }
-ArrayRef<Type> LLVMStructType::getBody() {
+ArrayRef<Type> LLVMStructType::getBody() const {
return isIdentified() ? getImpl()->getIdentifiedStructBody()
: getImpl()->getTypeList();
}
@@ -389,6 +389,147 @@ LLVMStructType::verify(function_ref<InFlightDiagnostic()> emitError,
return success();
}
+unsigned
+LLVMStructType::getTypeSizeInBits(const DataLayout &dataLayout,
+ DataLayoutEntryListRef params) const {
+ unsigned structSize = 0;
+ unsigned structAlignment = 1;
+ for (Type element : getBody()) {
+ unsigned elementAlignment =
+ isPacked() ? 1 : dataLayout.getTypeABIAlignment(element);
+ // Add padding to the struct size to align it to the abi alignment of the
+ // element type before than adding the size of the element
+ structSize = llvm::alignTo(structSize, elementAlignment);
+ structSize += dataLayout.getTypeSize(element);
+
+ // The alignment requirement of a struct is equal to the strictest alignment
+ // requirement of its elements.
+ structAlignment = std::max(elementAlignment, structAlignment);
+ }
+ // At the end, add padding to the struct to satisfy its own alignment
+ // requirement. Otherwise structs inside of arrays would be misaligned.
+ structSize = llvm::alignTo(structSize, structAlignment);
+ return structSize * kBitsInByte;
+}
+
+namespace {
+enum class StructDLEntryPos { Abi = 0, Preferred = 1 };
+}
+
+static Optional<unsigned>
+getStructDataLayoutEntry(DataLayoutEntryListRef params, LLVMStructType type,
+ StructDLEntryPos pos) {
+ auto currentEntry = llvm::find_if(params, [](DataLayoutEntryInterface entry) {
+ return entry.isTypeEntry();
+ });
+ if (currentEntry == params.end())
+ return llvm::None;
+
+ auto attr = currentEntry->getValue().cast<DenseIntElementsAttr>();
+ if (pos == StructDLEntryPos::Preferred &&
+ attr.size() <= static_cast<unsigned>(StructDLEntryPos::Preferred))
+ // If no preferred was specified, fall back to abi alignment
+ pos = StructDLEntryPos::Abi;
+
+ return attr.getValues<unsigned>()[static_cast<unsigned>(pos)];
+}
+
+static unsigned calculateStructAlignment(const DataLayout &dataLayout,
+ DataLayoutEntryListRef params,
+ LLVMStructType type,
+ StructDLEntryPos pos) {
+ // Packed structs always have an abi alignment of 1
+ if (pos == StructDLEntryPos::Abi && type.isPacked()) {
+ return 1;
+ }
+
+ // The alignment requirement of a struct is equal to the strictest alignment
+ // requirement of its elements.
+ unsigned structAlignment = 1;
+ for (Type iter : type.getBody()) {
+ structAlignment =
+ std::max(dataLayout.getTypeABIAlignment(iter), structAlignment);
+ }
+
+ // Entries are only allowed to be stricter than the required alignment
+ if (Optional<unsigned> entryResult =
+ getStructDataLayoutEntry(params, type, pos))
+ return std::max(*entryResult / kBitsInByte, structAlignment);
+
+ return structAlignment;
+}
+
+unsigned LLVMStructType::getABIAlignment(const DataLayout &dataLayout,
+ DataLayoutEntryListRef params) const {
+ return calculateStructAlignment(dataLayout, params, *this,
+ StructDLEntryPos::Abi);
+}
+
+unsigned
+LLVMStructType::getPreferredAlignment(const DataLayout &dataLayout,
+ DataLayoutEntryListRef params) const {
+ return calculateStructAlignment(dataLayout, params, *this,
+ StructDLEntryPos::Preferred);
+}
+
+static unsigned extractStructSpecValue(Attribute attr, StructDLEntryPos pos) {
+ return attr.cast<DenseIntElementsAttr>()
+ .getValues<unsigned>()[static_cast<unsigned>(pos)];
+}
+
+bool LLVMStructType::areCompatible(DataLayoutEntryListRef oldLayout,
+ DataLayoutEntryListRef newLayout) const {
+ for (DataLayoutEntryInterface newEntry : newLayout) {
+ if (!newEntry.isTypeEntry())
+ continue;
+
+ auto previousEntry =
+ llvm::find_if(oldLayout, [](DataLayoutEntryInterface entry) {
+ return entry.isTypeEntry();
+ });
+ if (previousEntry == oldLayout.end())
+ continue;
+
+ unsigned abi = extractStructSpecValue(previousEntry->getValue(),
+ StructDLEntryPos::Abi);
+ unsigned newAbi =
+ extractStructSpecValue(newEntry.getValue(), StructDLEntryPos::Abi);
+ if (abi < newAbi || abi % newAbi != 0)
+ return false;
+ }
+ return true;
+}
+
+LogicalResult LLVMStructType::verifyEntries(DataLayoutEntryListRef entries,
+ Location loc) const {
+ for (DataLayoutEntryInterface entry : entries) {
+ if (!entry.isTypeEntry())
+ continue;
+
+ auto key = entry.getKey().get<Type>().cast<LLVMStructType>();
+ auto values = entry.getValue().dyn_cast<DenseIntElementsAttr>();
+ if (!values || (values.size() != 2 && values.size() != 1)) {
+ return emitError(loc)
+ << "expected layout attribute for " << entry.getKey().get<Type>()
+ << " to be a dense integer elements attribute of 1 or 2 elements";
+ }
+
+ if (key.isIdentified() || !key.getBody().empty()) {
+ return emitError(loc) << "unexpected layout attribute for struct " << key;
+ }
+
+ if (values.size() == 1)
+ continue;
+
+ if (extractStructSpecValue(values, StructDLEntryPos::Abi) >
+ extractStructSpecValue(values, StructDLEntryPos::Preferred)) {
+ return emitError(loc) << "preferred alignment is expected to be at least "
+ "as large as ABI alignment";
+ }
+ }
+ return mlir::success();
+}
+
//===----------------------------------------------------------------------===//
// Vector types.
//===----------------------------------------------------------------------===//
diff --git a/mlir/test/Dialect/LLVMIR/layout.mlir b/mlir/test/Dialect/LLVMIR/layout.mlir
index 245fee0cd2863..a64724dfaa719 100644
--- a/mlir/test/Dialect/LLVMIR/layout.mlir
+++ b/mlir/test/Dialect/LLVMIR/layout.mlir
@@ -111,3 +111,136 @@ module attributes { dlti.dl_spec = #dlti.dl_spec<
return
}
}
+
+// -----
+
+module {
+ // CHECK: @no_spec
+ func @no_spec() {
+ // simple case
+ // CHECK: alignment = 4
+ // CHECK: bitsize = 32
+ // CHECK: preferred = 4
+ // CHECK: size = 4
+ "test.data_layout_query"() : () -> !llvm.struct<(i32)>
+
+ // padding inbetween
+ // CHECK: alignment = 8
+ // CHECK: bitsize = 128
+ // CHECK: preferred = 8
+ // CHECK: size = 16
+ "test.data_layout_query"() : () -> !llvm.struct<(i32, f64)>
+
+ // padding at end of struct
+ // CHECK: alignment = 8
+ // CHECK: bitsize = 128
+ // CHECK: preferred = 8
+ // CHECK: size = 16
+ "test.data_layout_query"() : () -> !llvm.struct<(f64, i32)>
+
+ // packed
+ // CHECK: alignment = 1
+ // CHECK: bitsize = 96
+ // CHECK: preferred = 8
+ // CHECK: size = 12
+ "test.data_layout_query"() : () -> !llvm.struct<packed (f64, i32)>
+
+ // empty
+ // CHECK: alignment = 1
+ // CHECK: bitsize = 0
+ // CHECK: preferred = 1
+ // CHECK: size = 0
+ "test.data_layout_query"() : () -> !llvm.struct<()>
+ return
+ }
+}
+
+// -----
+
+module attributes { dlti.dl_spec = #dlti.dl_spec<
+ #dlti.dl_entry<!llvm.struct<()>, dense<[32, 32]> : vector<2xi32>>
+>} {
+ // CHECK: @spec
+ func @spec() {
+ // Strict alignment is applied
+ // CHECK: alignment = 4
+ // CHECK: bitsize = 16
+ // CHECK: preferred = 4
+ // CHECK: size = 2
+ "test.data_layout_query"() : () -> !llvm.struct<(i16)>
+
+ // No impact on structs that have stricter requirements
+ // CHECK: alignment = 8
+ // CHECK: bitsize = 128
+ // CHECK: preferred = 8
+ // CHECK: size = 16
+ "test.data_layout_query"() : () -> !llvm.struct<(i32, f64)>
+
+ // Only the preferred alignment of structs is affected
+ // CHECK: alignment = 1
+ // CHECK: bitsize = 32
+ // CHECK: preferred = 4
+ // CHECK: size = 4
+ "test.data_layout_query"() : () -> !llvm.struct<packed (i16, i16)>
+
+ // empty
+ // CHECK: alignment = 4
+ // CHECK: bitsize = 0
+ // CHECK: preferred = 4
+ // CHECK: size = 0
+ "test.data_layout_query"() : () -> !llvm.struct<()>
+ return
+ }
+}
+
+// -----
+
+module attributes { dlti.dl_spec = #dlti.dl_spec<
+ #dlti.dl_entry<!llvm.struct<()>, dense<[32]> : vector<1xi32>>
+>} {
+ // CHECK: @spec_without_preferred
+ func @spec_without_preferred() {
+ // abi alignment is applied to both preferred and abi
+ // CHECK: alignment = 4
+ // CHECK: bitsize = 16
+ // CHECK: preferred = 4
+ // CHECK: size = 2
+ "test.data_layout_query"() : () -> !llvm.struct<(i16)>
+ return
+ }
+}
+
+// -----
+
+// expected-error at below {{unexpected layout attribute for struct '!llvm.struct<(i8)>'}}
+module attributes { dlti.dl_spec = #dlti.dl_spec<
+ #dlti.dl_entry<!llvm.struct<(i8)>, dense<[64, 64]> : vector<2xi32>>
+>} {
+ func @struct() {
+ return
+ }
+}
+
+// -----
+
+// expected-error at below {{expected layout attribute for '!llvm.struct<()>' to be a dense integer elements attribute of 1 or 2 elements}}
+module attributes { dlti.dl_spec = #dlti.dl_spec<
+ #dlti.dl_entry<!llvm.struct<()>, dense<[64, 64, 64]> : vector<3xi32>>
+>} {
+ func @struct() {
+ return
+ }
+}
+
+// -----
+
+// expected-error at below {{preferred alignment is expected to be at least as large as ABI alignment}}
+module attributes { dlti.dl_spec = #dlti.dl_spec<
+ #dlti.dl_entry<!llvm.struct<()>, dense<[64, 32]> : vector<2xi32>>
+>} {
+ func @struct() {
+ return
+ }
+}
+
+// -----
More information about the Mlir-commits
mailing list