diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMTypes.h b/mlir/include/mlir/Dialect/LLVMIR/LLVMTypes.h --- a/mlir/include/mlir/Dialect/LLVMIR/LLVMTypes.h +++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMTypes.h @@ -15,6 +15,7 @@ #define MLIR_DIALECT_LLVMIR_LLVMTYPES_H_ #include "mlir/IR/Types.h" +#include "mlir/Interfaces/DataLayoutInterfaces.h" namespace llvm { class ElementCount; @@ -148,7 +149,8 @@ /// object in memory. It is parameterized by the element type and the address /// space. class LLVMPointerType : public Type::TypeBase { + detail::LLVMPointerTypeStorage, + DataLayoutTypeInterface::Trait> { public: /// Inherit base constructors. using Base::Base; @@ -166,14 +168,27 @@ unsigned addressSpace = 0); /// Returns the pointed-to type. - Type getElementType(); + Type getElementType() const; /// Returns the address space of the pointer. - unsigned getAddressSpace(); + unsigned getAddressSpace() const; /// Verifies that the type about to be constructed is well-formed. static LogicalResult verify(function_ref emitError, Type pointee, unsigned); + + /// 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/CMakeLists.txt b/mlir/lib/Dialect/LLVMIR/CMakeLists.txt --- a/mlir/lib/Dialect/LLVMIR/CMakeLists.txt +++ b/mlir/lib/Dialect/LLVMIR/CMakeLists.txt @@ -24,6 +24,7 @@ LINK_LIBS PUBLIC MLIRCallInterfaces MLIRControlFlowInterfaces + MLIRDataLayoutInterfaces MLIRIR MLIRSideEffectInterfaces MLIRSupport diff --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMTypes.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMTypes.cpp --- a/mlir/lib/Dialect/LLVMIR/IR/LLVMTypes.cpp +++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMTypes.cpp @@ -136,9 +136,11 @@ addressSpace); } -Type LLVMPointerType::getElementType() { return getImpl()->pointeeType; } +Type LLVMPointerType::getElementType() const { return getImpl()->pointeeType; } -unsigned LLVMPointerType::getAddressSpace() { return getImpl()->addressSpace; } +unsigned LLVMPointerType::getAddressSpace() const { + return getImpl()->addressSpace; +} LogicalResult LLVMPointerType::verify(function_ref emitError, @@ -148,6 +150,149 @@ return success(); } +namespace { +/// The positions of different values in the data layout entry. +enum class DLEntryPos { Size = 0, Abi = 1, Preferred = 2, Address = 3 }; +} // namespace + +constexpr const static unsigned kDefaultPointerSizeBits = 64; +constexpr const static unsigned kDefaultPointerAlignment = 8; +constexpr const static unsigned kBitsInByte = 8; + +/// Returns the value that corresponds to named position `pos` from the +/// attribute `attr` assuming it's a dense integer elements attribute. +static unsigned extractPointerSpecValue(Attribute attr, DLEntryPos pos) { + return attr.cast().getValue( + static_cast(pos)); +} + +/// Returns the part of the data layout entry that corresponds to `pos` for the +/// given `type` by interpreting the list of entries `params`. For the pointer +/// type in the default address space, returns the default value if the entries +/// do not provide a custom one, for other address spaces returns None. +static Optional +getPointerDataLayoutEntry(DataLayoutEntryListRef params, LLVMPointerType type, + DLEntryPos pos) { + // First, look for the entry for the pointer in the current address space. + Attribute currentEntry; + for (DataLayoutEntryInterface entry : params) { + if (!entry.isTypeEntry()) + continue; + if (entry.getKey().get().cast().getAddressSpace() == + type.getAddressSpace()) { + currentEntry = entry.getValue(); + break; + } + } + if (currentEntry) { + return extractPointerSpecValue(currentEntry, pos) / + (pos == DLEntryPos::Size ? 1 : kBitsInByte); + } + + // If not found, and this is the pointer to the default memory space, assume + // 64-bit pointers. + if (type.getAddressSpace() == 0) { + return pos == DLEntryPos::Size ? kDefaultPointerSizeBits + : kDefaultPointerAlignment; + } + + return llvm::None; +} + +unsigned +LLVMPointerType::getTypeSizeInBits(const DataLayout &dataLayout, + DataLayoutEntryListRef params) const { + if (Optional size = + getPointerDataLayoutEntry(params, *this, DLEntryPos::Size)) + return *size; + + // For other memory spaces, use the size of the pointer to the default memory + // space. + return dataLayout.getTypeSizeInBits(get(getElementType())); +} + +unsigned LLVMPointerType::getABIAlignment(const DataLayout &dataLayout, + DataLayoutEntryListRef params) const { + if (Optional alignment = + getPointerDataLayoutEntry(params, *this, DLEntryPos::Abi)) + return *alignment; + + return dataLayout.getTypeABIAlignment(get(getElementType())); +} + +unsigned +LLVMPointerType::getPreferredAlignment(const DataLayout &dataLayout, + DataLayoutEntryListRef params) const { + if (Optional alignment = + getPointerDataLayoutEntry(params, *this, DLEntryPos::Preferred)) + return *alignment; + + return dataLayout.getTypePreferredAlignment(get(getElementType())); +} + +bool LLVMPointerType::areCompatible(DataLayoutEntryListRef oldLayout, + DataLayoutEntryListRef newLayout) const { + for (DataLayoutEntryInterface newEntry : newLayout) { + if (!newEntry.isTypeEntry()) + continue; + unsigned size = kDefaultPointerSizeBits; + unsigned abi = kDefaultPointerAlignment; + auto newType = newEntry.getKey().get().cast(); + auto it = llvm::find_if(oldLayout, [&](DataLayoutEntryInterface entry) { + if (auto type = entry.getKey().dyn_cast()) { + return type.cast().getAddressSpace() == + newType.getAddressSpace(); + } + return false; + }); + if (it == oldLayout.end()) { + llvm::find_if(oldLayout, [&](DataLayoutEntryInterface entry) { + if (auto type = entry.getKey().dyn_cast()) { + return type.cast().getAddressSpace() == 0; + } + return false; + }); + } + if (it != oldLayout.end()) { + size = extractPointerSpecValue(*it, DLEntryPos::Size); + abi = extractPointerSpecValue(*it, DLEntryPos::Abi); + } + + Attribute newSpec = newEntry.getValue().cast(); + unsigned newSize = extractPointerSpecValue(newSpec, DLEntryPos::Size); + unsigned newAbi = extractPointerSpecValue(newSpec, DLEntryPos::Abi); + if (size != newSize || abi < newAbi || abi % newAbi != 0) + return false; + } + return true; +} + +LogicalResult LLVMPointerType::verifyEntries(DataLayoutEntryListRef entries, + Location loc) const { + for (DataLayoutEntryInterface entry : entries) { + if (!entry.isTypeEntry()) + continue; + auto key = entry.getKey().get().cast(); + auto values = entry.getValue().dyn_cast(); + if (!values || (values.size() != 3 && values.size() != 4)) { + return emitError(loc) + << "expected layout attribute for " << entry.getKey().get() + << " to be a dense integer elements attribute with 3 or 4 " + "elements"; + } + if (!key.getElementType().isInteger(8)) { + return emitError(loc) << "unexpected layout attribute for pointer to " + << key.getElementType(); + } + if (extractPointerSpecValue(values, DLEntryPos::Abi) > + extractPointerSpecValue(values, DLEntryPos::Preferred)) { + return emitError(loc) << "preferred alignment is expected to be at least " + "as large as ABI alignment"; + } + } + return success(); +} + //===----------------------------------------------------------------------===// // Struct type. //===----------------------------------------------------------------------===// diff --git a/mlir/test/Dialect/LLVMIR/layout.mlir b/mlir/test/Dialect/LLVMIR/layout.mlir new file mode 100644 --- /dev/null +++ b/mlir/test/Dialect/LLVMIR/layout.mlir @@ -0,0 +1,113 @@ +// RUN: mlir-opt --test-data-layout-query --split-input-file --verify-diagnostics %s | FileCheck %s + +module { + // CHECK: @no_spec + func @no_spec() { + // CHECK: alignment = 8 + // CHECK: bitsize = 64 + // CHECK: preferred = 8 + // CHECK: size = 8 + "test.data_layout_query"() : () -> !llvm.ptr + // CHECK: alignment = 8 + // CHECK: bitsize = 64 + // CHECK: preferred = 8 + // CHECK: size = 8 + "test.data_layout_query"() : () -> !llvm.ptr + // CHECK: alignment = 8 + // CHECK: bitsize = 64 + // CHECK: preferred = 8 + // CHECK: size = 8 + "test.data_layout_query"() : () -> !llvm.ptr + // CHECK: alignment = 8 + // CHECK: bitsize = 64 + // CHECK: preferred = 8 + // CHECK: size = 8 + "test.data_layout_query"() : () -> !llvm.ptr> + // CHECK: alignment = 8 + // CHECK: bitsize = 64 + // CHECK: preferred = 8 + // CHECK: size = 8 + "test.data_layout_query"() : () -> !llvm.ptr + // CHECK: alignment = 8 + // CHECK: bitsize = 64 + // CHECK: preferred = 8 + // CHECK: size = 8 + "test.data_layout_query"() : () -> !llvm.ptr + return + } +} + +// ----- + +module attributes { dlti.dl_spec = #dlti.dl_spec< + #dlti.dl_entry, dense<[32, 32, 64]> : vector<3xi32>>, + #dlti.dl_entry, dense<[64, 64, 64]> : vector<3xi32>> +>} { + // CHECK: @spec + func @spec() { + // CHECK: alignment = 4 + // CHECK: bitsize = 32 + // CHECK: preferred = 8 + // CHECK: size = 4 + "test.data_layout_query"() : () -> !llvm.ptr + // CHECK: alignment = 4 + // CHECK: bitsize = 32 + // CHECK: preferred = 8 + // CHECK: size = 4 + "test.data_layout_query"() : () -> !llvm.ptr + // CHECK: alignment = 4 + // CHECK: bitsize = 32 + // CHECK: preferred = 8 + // CHECK: size = 4 + "test.data_layout_query"() : () -> !llvm.ptr + // CHECK: alignment = 4 + // CHECK: bitsize = 32 + // CHECK: preferred = 8 + // CHECK: size = 4 + "test.data_layout_query"() : () -> !llvm.ptr> + // CHECK: alignment = 4 + // CHECK: bitsize = 32 + // CHECK: preferred = 8 + // CHECK: size = 4 + "test.data_layout_query"() : () -> !llvm.ptr + // CHECK: alignment = 8 + // CHECK: bitsize = 64 + // CHECK: preferred = 8 + // CHECK: size = 8 + "test.data_layout_query"() : () -> !llvm.ptr + return + } +} + +// ----- + +// expected-error@below {{unexpected layout attribute for pointer to 'i32'}} +module attributes { dlti.dl_spec = #dlti.dl_spec< + #dlti.dl_entry, dense<[64, 64, 64]> : vector<3xi32>> +>} { + func @pointer() { + return + } +} + +// ----- + +// expected-error@below {{expected layout attribute for '!llvm.ptr' to be a dense integer elements attribute with 3 or 4 elements}} +module attributes { dlti.dl_spec = #dlti.dl_spec< + #dlti.dl_entry, dense<[64.0, 64.0, 64.0]> : vector<3xf32>> +>} { + func @pointer() { + return + } +} + +// ----- + +// expected-error@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, dense<[64, 64, 32]> : vector<3xi32>> +>} { + func @pointer() { + return + } +}