[Mlir-commits] [mlir] [mlir][LLVM] Add LLVMStructType::getElementOffset() (PR #234171)
Ivan Kosarev
llvmlistbot at llvm.org
Wed Oct 21 04:56:41 PDT 2026
https://github.com/kosarev created https://github.com/llvm/llvm-project/pull/234171
Since bb6f7fcb46cd LLVMStructType::getTypeSizeInBits() uses allocation sizes, but there are a few places where we still compute field offsets based on store sizes.
This change factors out the computations into a single method, a counterpart of LLVM's StructLayout::getElementOffset(), so that all now agree in how they do that.
Also simplify the surrounding code a bit while there.
>From dd6ebf2e3cd64729d96f9a8bc0044df11a669d00 Mon Sep 17 00:00:00 2001
From: Ivan Kosarev <mail at ivankosarev.com>
Date: Wed, 21 Oct 2026 12:52:11 +0100
Subject: [PATCH] [mlir][LLVM] Add LLVMStructType::getElementOffset()
Since bb6f7fcb46cd LLVMStructType::getTypeSizeInBits() uses
allocation sizes, but there are a few places where we still
compute field offsets based on store sizes.
This change factors out the computations into a single method,
a counterpart of LLVM's StructLayout::getElementOffset(), so
that all now agree in how they do that.
Also simplify the surrounding code a bit while there.
---
mlir/include/mlir/Dialect/LLVMIR/LLVMTypes.td | 4 ++
mlir/lib/Dialect/LLVMIR/IR/LLVMMemorySlot.cpp | 58 +++++--------------
mlir/lib/Dialect/LLVMIR/IR/LLVMTypes.cpp | 54 ++++++++++++-----
.../unittests/Dialect/LLVMIR/LLVMTypeTest.cpp | 33 +++++++++++
4 files changed, 91 insertions(+), 58 deletions(-)
diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMTypes.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMTypes.td
index 080f78e080d63a..e31323651ceda1 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/LLVMTypes.td
+++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMTypes.td
@@ -258,6 +258,10 @@ def LLVMStructType : LLVMType<"LLVMStruct", "struct", [
/// Checks if a struct is packed.
bool isPacked() const;
+ /// Returns the byte offset of the element at the given index.
+ uint64_t getElementOffset(const DataLayout &dataLayout,
+ unsigned index) const;
+
/// Checks if a struct is identified.
bool isIdentified() const;
diff --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMMemorySlot.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMMemorySlot.cpp
index b76dc8647b91b0..a67dea264c3a89 100644
--- a/mlir/lib/Dialect/LLVMIR/IR/LLVMMemorySlot.cpp
+++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMMemorySlot.cpp
@@ -726,20 +726,8 @@ static std::optional<uint64_t> gepToByteOffset(const DataLayout &dataLayout,
return false;
})
.Case([&](LLVM::LLVMStructType structType) {
- ArrayRef<Type> body = structType.getBody();
- assert(index < body.size() && "expected valid struct indexing");
- for (uint32_t i : llvm::seq(index)) {
- if (!structType.isPacked())
- offset = llvm::alignTo(
- offset, dataLayout.getTypeABIAlignment(body[i]));
- offset += dataLayout.getTypeSize(body[i]);
- }
-
- // Align for the current type as well.
- if (!structType.isPacked())
- offset = llvm::alignTo(
- offset, dataLayout.getTypeABIAlignment(body[index]));
- currentType = body[index];
+ offset += structType.getElementOffset(dataLayout, index);
+ currentType = structType.getBody()[index];
return false;
})
.Default([&](Type type) {
@@ -800,31 +788,22 @@ getSubslotAccessInfo(const DestructurableMemorySlot &slot,
})
.Case([&](LLVM::LLVMStructType structType)
-> std::optional<SubslotAccessInfo> {
- uint64_t distanceToStart = 0;
// Walk over the elements of the struct to find in which of
// them the offset is.
for (auto [index, elem] : llvm::enumerate(structType.getBody())) {
- uint64_t elemSize = dataLayout.getTypeSize(elem);
- if (!structType.isPacked()) {
- distanceToStart = llvm::alignTo(
- distanceToStart, dataLayout.getTypeABIAlignment(elem));
- // If the offset is in padding, cancel the rewrite.
- if (offset < distanceToStart)
- return {};
- }
+ uint64_t elemOffset = structType.getElementOffset(dataLayout, index);
+ // If the offset is in padding, cancel the rewrite.
+ if (*offset < elemOffset)
+ return {};
- if (offset < distanceToStart + elemSize) {
+ if (*offset < elemOffset + dataLayout.getTypeSize(elem)) {
if (isOutOfBoundsGEPIndex(index))
return {};
// The offset is within this element, stop iterating the
// struct and return the index.
return SubslotAccessInfo{static_cast<uint32_t>(index),
- *offset - distanceToStart};
+ *offset - elemOffset};
}
-
- // The offset is not within this element, continue walking
- // over the struct.
- distanceToStart += elemSize;
}
return {};
@@ -1145,35 +1124,28 @@ memsetRewire(MemsetIntr op, const DestructurableMemorySlot &slot,
IntegerAttr memsetLenAttr = createMemsetLenAttr(op);
- bool packed = false;
- if (auto structType = dyn_cast<LLVM::LLVMStructType>(slot.valueType))
- packed = structType.isPacked();
+ auto structType = dyn_cast<LLVM::LLVMStructType>(slot.valueType);
Type i32 = IntegerType::get(op.getContext(), 32);
uint64_t memsetLen = memsetLenAttr.getValue().getZExtValue();
- uint64_t covered = 0;
for (size_t i = 0; i < types->size(); i++) {
// Create indices on the fly to get elements in the right order.
Attribute index = IntegerAttr::get(i32, i);
Type elemType = types->at(index);
uint64_t typeSize = dataLayout.getTypeSize(elemType);
-
- if (!packed)
- covered =
- llvm::alignTo(covered, dataLayout.getTypeABIAlignment(elemType));
-
- if (covered >= memsetLen)
+ uint64_t offset =
+ structType
+ ? structType.getElementOffset(dataLayout, i)
+ : i * dataLayout.getTypeAllocSize(elemType);
+ if (offset >= memsetLen)
break;
// If this subslot is used, apply a new memset to it.
- // Otherwise, only compute its offset within the original memset.
if (subslots.contains(index)) {
- uint64_t newMemsetSize = std::min(memsetLen - covered, typeSize);
+ uint64_t newMemsetSize = std::min(memsetLen - offset, typeSize);
createMemsetIntr(builder, op, memsetLenAttr, newMemsetSize, subslots,
index);
}
-
- covered += typeSize;
}
return DeletionKind::Delete;
diff --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMTypes.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMTypes.cpp
index 7ee1ae5afd062b..2cc2db897b7f4f 100644
--- a/mlir/lib/Dialect/LLVMIR/IR/LLVMTypes.cpp
+++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMTypes.cpp
@@ -550,27 +550,51 @@ LLVMStructType::verifyInvariants(function_ref<InFlightDiagnostic()> emitError,
return success();
}
+/// Returns the byte offset of the element of the specified structure at the
+/// given index and the strictest alignment requirement of the elements up to
+/// the index. For the index past the last element it thus returns the size and
+/// alignment of the whole structure.
+static uint64_t getOffsetAndAlignment(const DataLayout &dataLayout,
+ LLVMStructType structType, unsigned index,
+ uint64_t &structAlignment) {
+ uint64_t offset = 0;
+ structAlignment = 1;
+ for (auto [i, element] : llvm::enumerate(structType.getBody())) {
+ // Align the element unless the structure is packed.
+ uint64_t elementAlignment = dataLayout.getTypeABIAlignment(element);
+ if (!structType.isPacked()) {
+ offset = llvm::alignTo(offset, elementAlignment);
+
+ // The structure alignment is the strictest alignment of its
+ // elements.
+ structAlignment = std::max(structAlignment, elementAlignment);
+ }
+ if (i == index)
+ return offset;
+
+ // Each element gets its allocation size, even in packed structures.
+ offset += dataLayout.getTypeAllocSize(element);
+ }
+ return offset;
+}
+
+uint64_t LLVMStructType::getElementOffset(const DataLayout &dataLayout,
+ unsigned index) const {
+ assert(index < getBody().size() && "expected a valid struct element index");
+ uint64_t structAlignment;
+ return getOffsetAndAlignment(dataLayout, *this, index, structAlignment);
+}
+
llvm::TypeSize
LLVMStructType::getTypeSizeInBits(const DataLayout &dataLayout,
DataLayoutEntryListRef params) const {
- auto structSize = llvm::TypeSize::getFixed(0);
- uint64_t structAlignment = 1;
- for (Type element : getBody()) {
- uint64_t elementAlignment =
- isPacked() ? 1 : dataLayout.getTypeABIAlignment(element);
- // Add padding to align the element unless the struct is packed.
- structSize = llvm::alignTo(structSize, elementAlignment);
- // Elements occupy their allocation size, even in packed structs.
- structSize += dataLayout.getTypeAllocSize(element);
-
- // The alignment requirement of a struct is equal to the strictest alignment
- // requirement of its elements.
- structAlignment = std::max(elementAlignment, structAlignment);
- }
+ uint64_t structAlignment;
+ uint64_t structSize = getOffsetAndAlignment(
+ dataLayout, *this, getBody().size(), 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;
+ return llvm::TypeSize::getFixed(structSize * kBitsInByte);
}
namespace {
diff --git a/mlir/unittests/Dialect/LLVMIR/LLVMTypeTest.cpp b/mlir/unittests/Dialect/LLVMIR/LLVMTypeTest.cpp
index df19caec693976..72d8867d232aad 100644
--- a/mlir/unittests/Dialect/LLVMIR/LLVMTypeTest.cpp
+++ b/mlir/unittests/Dialect/LLVMIR/LLVMTypeTest.cpp
@@ -27,3 +27,36 @@ TEST_F(LLVMIRDeathTest, StructTypeGetVerifiesElementTypes) {
"invalid LLVM structure element type");
}
#endif
+
+TEST_F(LLVMIRTest, StructElementOffsets) {
+ DataLayout dataLayout;
+ Type i8 = IntegerType::get(&context, 8);
+ Type i32 = IntegerType::get(&context, 32);
+ Type f64 = Float64Type::get(&context);
+ Type f80 = Float80Type::get(&context);
+
+ // Check ABI alignment for unpacked structures.
+ auto unpacked = LLVMStructType::getLiteral(&context, {i8, f64, i32});
+ EXPECT_EQ(unpacked.getElementOffset(dataLayout, 0), 0u);
+ EXPECT_EQ(unpacked.getElementOffset(dataLayout, 1), 8u);
+ EXPECT_EQ(unpacked.getElementOffset(dataLayout, 2), 16u);
+ EXPECT_EQ(dataLayout.getTypeSize(unpacked), 24u);
+
+ // Check no alignment for packed structures.
+ auto packed = LLVMStructType::getLiteral(&context, {i8, f64, i32},
+ /*isPacked=*/true);
+ EXPECT_EQ(packed.getElementOffset(dataLayout, 0), 0u);
+ EXPECT_EQ(packed.getElementOffset(dataLayout, 1), 1u);
+ EXPECT_EQ(packed.getElementOffset(dataLayout, 2), 9u);
+ EXPECT_EQ(dataLayout.getTypeSize(packed), 13u);
+
+ // Check that fields get their allocation size, even in packed
+ // structures.
+ auto alloc = LLVMStructType::getLiteral(&context, {f80, i8});
+ EXPECT_EQ(alloc.getElementOffset(dataLayout, 1), 16u);
+ EXPECT_EQ(dataLayout.getTypeSize(alloc), 32u);
+ auto packedAlloc =
+ LLVMStructType::getLiteral(&context, {f80, i8}, /*isPacked=*/true);
+ EXPECT_EQ(packedAlloc.getElementOffset(dataLayout, 1), 16u);
+ EXPECT_EQ(dataLayout.getTypeSize(packedAlloc), 17u);
+}
More information about the Mlir-commits
mailing list