[Mlir-commits] [mlir] [mlir][LLVM] Add LLVMStructType::getElementOffset() (PR #234171)

llvmlistbot at llvm.org llvmlistbot at llvm.org
Wed Oct 21 04:57:23 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-mlir

Author: Ivan Kosarev (kosarev)

<details>
<summary>Changes</summary>

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.

---
Full diff: https://github.com/llvm/llvm-project/pull/234171.diff


4 Files Affected:

- (modified) mlir/include/mlir/Dialect/LLVMIR/LLVMTypes.td (+4) 
- (modified) mlir/lib/Dialect/LLVMIR/IR/LLVMMemorySlot.cpp (+15-43) 
- (modified) mlir/lib/Dialect/LLVMIR/IR/LLVMTypes.cpp (+39-15) 
- (modified) mlir/unittests/Dialect/LLVMIR/LLVMTypeTest.cpp (+33) 


``````````diff
diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMTypes.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMTypes.td
index 080f78e080d63..e31323651ceda 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 b76dc8647b91b..a67dea264c3a8 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 7ee1ae5afd062..2cc2db897b7f4 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 df19caec69397..72d8867d232aa 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);
+}

``````````

</details>


https://github.com/llvm/llvm-project/pull/234171


More information about the Mlir-commits mailing list