[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