[llvm] [LoadStoreVectorizer] Propagate alignment through contiguous chain (PR #145733)

Drew Kersnar via llvm-commits llvm-commits at lists.llvm.org
Thu Jul 3 11:51:53 PDT 2025


================
@@ -0,0 +1,450 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -passes=load-store-vectorizer -S < %s | FileCheck %s
+
+; The IR has the first float3 labeled with align 16, and that 16 should
+; be propagated such that the second set of 4 values
+; can also be vectorized together.
----------------
dakersnar wrote:

I think this is nice to have for a variety of edge cases, but the specific motivator for this change is based on InstCombine's handling of nested structs. You are correct that if loads/stores are unpacked from aligned loads/stores of aggregates, alignment will be propagated to the unpacked elements. However, with nested structs, InstCombine unpacks one layer at a time, losing alignment context in between passes over the worklist.

Before IC:
```
; RUN: opt < %s -passes=instcombine -S | FileCheck %s
%struct.S1 = type { %struct.float3, %struct.float3, i32, i32 }
%struct.float3 = type { float, float, float }

define void @_Z7init_s1P2S1S0_(ptr noundef %v, ptr noundef %vout) {
  %v.addr = alloca ptr, align 8
  %vout.addr = alloca ptr, align 8
  store ptr %v, ptr %v.addr, align 8
  store ptr %vout, ptr %vout.addr, align 8
  %tmp = load ptr, ptr %vout.addr, align 8
  %tmp1 = load ptr, ptr %v.addr, align 8
  %1 = load %struct.S1, ptr %tmp1, align 16
  store %struct.S1 %1, ptr %tmp, align 16
  ret void
}
```
After IC:
```
define void @_Z7init_s1P2S1S0_(ptr noundef %v, ptr noundef %vout) {
  %.unpack.unpack = load float, ptr %v, align 16
  %.unpack.elt7 = getelementptr inbounds nuw i8, ptr %v, i64 4
  %.unpack.unpack8 = load float, ptr %.unpack.elt7, align 4
  %.unpack.elt9 = getelementptr inbounds nuw i8, ptr %v, i64 8
  %.unpack.unpack10 = load float, ptr %.unpack.elt9, align 8
  %.elt1 = getelementptr inbounds nuw i8, ptr %v, i64 12
  %.unpack2.unpack = load float, ptr %.elt1, align 4
  %.unpack2.elt12 = getelementptr inbounds nuw i8, ptr %v, i64 16
  %.unpack2.unpack13 = load float, ptr %.unpack2.elt12, align 4 ; <----------- this should be align 16
  %.unpack2.elt14 = getelementptr inbounds nuw i8, ptr %v, i64 20
  %.unpack2.unpack15 = load float, ptr %.unpack2.elt14, align 4
  %.elt3 = getelementptr inbounds nuw i8, ptr %v, i64 24
  %.unpack4 = load i32, ptr %.elt3, align 8
  %.elt5 = getelementptr inbounds nuw i8, ptr %v, i64 28
  %.unpack6 = load i32, ptr %.elt5, align 4
  store float %.unpack.unpack, ptr %vout, align 16
  %vout.repack23 = getelementptr inbounds nuw i8, ptr %vout, i64 4
  store float %.unpack.unpack8, ptr %vout.repack23, align 4
  %vout.repack25 = getelementptr inbounds nuw i8, ptr %vout, i64 8
  store float %.unpack.unpack10, ptr %vout.repack25, align 8
  %vout.repack17 = getelementptr inbounds nuw i8, ptr %vout, i64 12
  store float %.unpack2.unpack, ptr %vout.repack17, align 4
  %vout.repack17.repack27 = getelementptr inbounds nuw i8, ptr %vout, i64 16
  store float %.unpack2.unpack13, ptr %vout.repack17.repack27, align 4
  %vout.repack17.repack29 = getelementptr inbounds nuw i8, ptr %vout, i64 20
  store float %.unpack2.unpack15, ptr %vout.repack17.repack29, align 4
  %vout.repack19 = getelementptr inbounds nuw i8, ptr %vout, i64 24
  store i32 %.unpack4, ptr %vout.repack19, align 8
  %vout.repack21 = getelementptr inbounds nuw i8, ptr %vout, i64 28
  store i32 %.unpack6, ptr %vout.repack21, align 4
  ret void
}
```
To visualize what's happening under the hood, InstCombine is unpacking the load in stages like this:
%1 = load %struct.S1, ptr %tmp1, align 16
->
load struct.float3 align 16
load struct.float3 align 4
load i32 align 8
load i32 align 4
->
load float align 16
load float align 4
load float align 8
load float align 4
load float align 4
load float align 4
load i32 align 8
load i32 align 4

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


More information about the llvm-commits mailing list