[llvm] 78247bf - [SROA] Adjust struct layout requirements for struct to vector canonicalization (#201967)

via llvm-commits llvm-commits at lists.llvm.org
Thu Jun 25 09:08:15 PDT 2026


Author: Yonah Goldberg
Date: 2026-06-25T09:08:10-07:00
New Revision: 78247bf69011b35f04e0905b3cea56eab7d7831c

URL: https://github.com/llvm/llvm-project/commit/78247bf69011b35f04e0905b3cea56eab7d7831c
DIFF: https://github.com/llvm/llvm-project/commit/78247bf69011b35f04e0905b3cea56eab7d7831c.diff

LOG: [SROA] Adjust struct layout requirements for struct to vector canonicalization (#201967)

Address a bug pointed out by @bjope (thank you!)

- To perform struct to vector canonicalization, it is not enough that
the struct layout size is the same as the vector layout size, because
structs and vectors may have padding in different locations! Previously
we would promote `{ i5, i5 }` as `<i5, i5>`, which is a miscompile!

- I also relaxed another requirement. Previously we made sure that the
struct layout size is equal to the vector allocation size. This
prevented promoting `{ i32, i32, i32 }` as `<i32, i32, i32>` because the
struct layout size is 3 x i32 but the vector allocation size is 4 x i32.
So instead we should compare to the vector store size, which is 3 x i32.

Added: 
    

Modified: 
    llvm/lib/Transforms/Scalar/SROA.cpp
    llvm/test/Transforms/SROA/struct-to-vector-fp-store-only-tail.ll
    llvm/test/Transforms/SROA/struct-to-vector.ll

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Transforms/Scalar/SROA.cpp b/llvm/lib/Transforms/Scalar/SROA.cpp
index 40f99e4341d47..14a1b093ea49a 100644
--- a/llvm/lib/Transforms/Scalar/SROA.cpp
+++ b/llvm/lib/Transforms/Scalar/SROA.cpp
@@ -5306,9 +5306,10 @@ bool SROA::presplitLoadsAndStores(AllocaInst &AI, AllocaSlices &AS) {
 
 /// Try to canonicalize a homogeneous struct partition to a vector type.
 ///
-/// We can do this if all the elements of the struct are the same and tightly
-/// packed. This can sometimes eliminate allocas because structs cannot get
-/// promoted to LLVM values, but vectors can.
+/// We can do this if all the elements of the struct are the same and the
+/// corresponding vector has the same byte-level layout. This can sometimes
+/// eliminate allocas because structs cannot get promoted to LLVM values, but
+/// vectors can.
 ///
 /// We only apply this transformation when all users of the partition are memory
 /// intrinsics. Otherwise, if there is a load or store of some other type to the
@@ -5341,9 +5342,18 @@ static FixedVectorType *tryCanonicalizeStructToVector(StructType *STy,
       !IsIntegralPointerTy)
     return nullptr;
 
+  // Ensure the struct is tightly packed so that the bit-layout is the same as
+  // the corresponding vector. For example, this prevents a miscompile for
+  // { i5, i5 }, which has padding after each i5 field, whereas <i5, i5> has
+  // tightly packed elements and trailing padding.
+  if (DL.getTypeSizeInBits(EltTy) != DL.getTypeAllocSizeInBits(EltTy))
+    return nullptr;
+
   auto *VTy = FixedVectorType::get(EltTy, NumElts);
   TypeSize StructSize = DL.getStructLayout(STy)->getSizeInBytes();
-  TypeSize VectorSize = DL.getTypeAllocSize(VTy);
+  TypeSize VectorSize = DL.getTypeStoreSize(VTy);
+  // After ruling out per-element padding, make sure a vector load/store
+  // covers the same number of bytes as the struct layout.
   if (StructSize != VectorSize)
     return nullptr;
 

diff  --git a/llvm/test/Transforms/SROA/struct-to-vector-fp-store-only-tail.ll b/llvm/test/Transforms/SROA/struct-to-vector-fp-store-only-tail.ll
index d1b7dcab99612..aa5e7b10a089b 100644
--- a/llvm/test/Transforms/SROA/struct-to-vector-fp-store-only-tail.ll
+++ b/llvm/test/Transforms/SROA/struct-to-vector-fp-store-only-tail.ll
@@ -31,13 +31,10 @@ define ptr @store_only_fp_tail() {
 ; NO-CANON-NEXT:    ret ptr null
 ;
 ; CANON-LABEL: define ptr @store_only_fp_tail() {
-; CANON-NEXT:    [[DOTSROA_4:%.*]] = alloca { float, float, float, float, float, float, float, float, float, float, float }, align 8
-; CANON-NEXT:    [[DOTSROA_2:%.*]] = alloca { float, float, float, float, float, float, float, float, float, float, float }, align 8
-; CANON-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[DOTSROA_4]], ptr align 8 [[DOTSROA_2]], i64 44, i1 false)
 ; CANON-NEXT:    [[DST_SROA_0_0_VEC_INSERT:%.*]] = insertelement <4 x float> {{.*}}, float 0.000000e+00, i32 0
 ; CANON-NEXT:    store <4 x float> [[DST_SROA_0_0_VEC_INSERT]], ptr null, align 1
 ; CANON-NEXT:    store float 0.000000e+00, ptr getelementptr inbounds (i8, ptr null, i64 16), align 1
-; CANON-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr align 1 getelementptr inbounds (i8, ptr null, i64 20), ptr align 8 [[DOTSROA_4]], i64 44, i1 false)
+; CANON-NEXT:    store <11 x float> {{.*}}, ptr getelementptr inbounds (i8, ptr null, i64 20), align 1
 ; CANON-NEXT:    ret ptr null
 ;
   %dst = alloca %class.aiMatrix4x4t, align 4

diff  --git a/llvm/test/Transforms/SROA/struct-to-vector.ll b/llvm/test/Transforms/SROA/struct-to-vector.ll
index ad7d3df129d6c..b7fb3cc97d66a 100644
--- a/llvm/test/Transforms/SROA/struct-to-vector.ll
+++ b/llvm/test/Transforms/SROA/struct-to-vector.ll
@@ -387,3 +387,34 @@ cond.end.ptr:
   call void @llvm.lifetime.end.p0(ptr %temp)
   ret void
 }
+
+%struct.i5x2 = type { i5, i5 }
+define void @struct_i5x2_memcpy_into_alloca(ptr %c) {
+; CHECK-LABEL: define void @struct_i5x2_memcpy_into_alloca(
+; CHECK-SAME: ptr [[C:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[E:%.*]] = alloca [[STRUCT_I5X2:%.*]], align 1
+; CHECK-NEXT:    call void @llvm.memcpy.p0.p0.i32(ptr align 1 [[E]], ptr align 1 [[C]], i32 2, i1 true)
+; CHECK-NEXT:    ret void
+;
+entry:
+  %e = alloca %struct.i5x2, align 1
+  call void @llvm.memcpy.p0.p0.i32(ptr align 1 %e, ptr align 1 %c, i32 2, i1 true)
+  ret void
+}
+
+%struct.i32x3 = type { i32, i32, i32 }
+define void @struct_i32x3_memcpy_into_alloca(ptr %c) {
+; CHECK-LABEL: define void @struct_i32x3_memcpy_into_alloca(
+; CHECK-SAME: ptr [[C:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[E_SROA_0:%.*]] = alloca <3 x i32>, align 16
+; CHECK-NEXT:    [[E_SROA_0_0_COPYLOAD:%.*]] = load volatile <3 x i32>, ptr [[C]], align 4
+; CHECK-NEXT:    store volatile <3 x i32> [[E_SROA_0_0_COPYLOAD]], ptr [[E_SROA_0]], align 16
+; CHECK-NEXT:    ret void
+;
+entry:
+  %e = alloca %struct.i32x3, align 4
+  call void @llvm.memcpy.p0.p0.i32(ptr align 4 %e, ptr align 4 %c, i32 12, i1 true)
+  ret void
+}


        


More information about the llvm-commits mailing list