[llvm] [RISCV][SLP] Use common alignment when checking constant-stride loads (PR #222520)

via llvm-commits llvm-commits at lists.llvm.org
Fri Sep 11 00:15:55 PDT 2026


https://github.com/imkiva updated https://github.com/llvm/llvm-project/pull/222520

>From 65fe07e2d16b91a374db5e0a6aec78c4c0a550f7 Mon Sep 17 00:00:00 2001
From: imkiva <zengtao at iscas.ac.cn>
Date: Wed, 9 Sep 2026 12:11:18 +0800
Subject: [PATCH 1/3] [RISCV][SLP] Use common alignment when checking
 constant-stride loads

---
 .../Transforms/Vectorize/SLPVectorizer.cpp    |  9 ++-
 .../RISCV/strided-load-common-alignment.ll    | 60 +++++++++++++++++++
 2 files changed, 64 insertions(+), 5 deletions(-)
 create mode 100644 llvm/test/Transforms/SLPVectorizer/RISCV/strided-load-common-alignment.ll

diff --git a/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp b/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
index 108b8a52d54bc..0f54e773b8486 100644
--- a/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
+++ b/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
@@ -6796,11 +6796,10 @@ BoUpSLP::LoadsState BoUpSLP::canVectorizeLoads(
                                    cast<Instruction>(V), UserIgnoreList);
                              }))
       return LoadsState::CompressVectorize;
-    Align Alignment =
-        cast<LoadInst>(Order.empty() ? VL.front() : VL[Order.front()])
-            ->getAlign();
-    if (analyzeConstantStrideCandidate(PointerOps, ScalarTy, Alignment, Order,
-                                       Diff, Ptr0, SPtrInfo))
+    // Widened strided loads must be legal for every group, not just the first
+    // pointer, which may have a stronger alignment than the remaining loads.
+    if (analyzeConstantStrideCandidate(PointerOps, ScalarTy, CommonAlignment,
+                                       Order, Diff, Ptr0, SPtrInfo))
       return LoadsState::StridedVectorize;
   }
   if (!IsMaskedGatherLegal())
diff --git a/llvm/test/Transforms/SLPVectorizer/RISCV/strided-load-common-alignment.ll b/llvm/test/Transforms/SLPVectorizer/RISCV/strided-load-common-alignment.ll
new file mode 100644
index 0000000000000..165e0d940085e
--- /dev/null
+++ b/llvm/test/Transforms/SLPVectorizer/RISCV/strided-load-common-alignment.ll
@@ -0,0 +1,60 @@
+; RUN: opt -S -passes=slp-vectorizer -mtriple=riscv64 -mattr=+v,-unaligned-vector-mem < %s | FileCheck %s
+; RUN: opt -S -passes=slp-vectorizer -mtriple=riscv32 -mattr=+v,-unaligned-vector-mem < %s | FileCheck %s
+; RUN: opt -S -passes=slp-vectorizer -mtriple=riscv64 -mattr=+v,+unaligned-vector-mem < %s | FileCheck %s --check-prefix=UNALIGNED
+;
+; The first load's alignment does not describe every group. Widening each
+; adjacent pair to i64 would leave the group at byte offset 28 misaligned.
+
+define void @gather_fields(ptr align 16 %base, ptr noalias %out) {
+; CHECK-LABEL: define void @gather_fields(
+; CHECK-NOT: call <2 x i64> @llvm.experimental.vp.strided.load
+; CHECK: ret void
+; UNALIGNED-LABEL: define void @gather_fields(
+; UNALIGNED: call <2 x i64> @llvm.experimental.vp.strided.load
+; UNALIGNED: ret void
+  %p1 = getelementptr inbounds i32, ptr %base, i64 1
+  %p2 = getelementptr inbounds i32, ptr %base, i64 7
+  %p3 = getelementptr inbounds i32, ptr %base, i64 8
+  %a = load i32, ptr %base, align 16
+  %b = load i32, ptr %p1, align 4
+  %c = load i32, ptr %p2, align 4
+  %d = load i32, ptr %p3, align 16
+  %v0 = add i32 %a, 3
+  %v1 = add i32 %b, 3
+  %v2 = add i32 %c, 3
+  %v3 = add i32 %d, 3
+  %q1 = getelementptr inbounds i32, ptr %out, i64 1
+  %q2 = getelementptr inbounds i32, ptr %out, i64 2
+  %q3 = getelementptr inbounds i32, ptr %out, i64 3
+  store i32 %v0, ptr %out, align 4
+  store i32 %v1, ptr %q1, align 4
+  store i32 %v2, ptr %q2, align 4
+  store i32 %v3, ptr %q3, align 4
+  ret void
+}
+
+; Naturally aligned strided loads remain legal.
+define void @aligned_values(ptr %base, ptr noalias %out) {
+; CHECK-LABEL: define void @aligned_values(
+; CHECK: call <4 x i64> @llvm.experimental.vp.strided.load
+; CHECK: ret void
+  %p1 = getelementptr inbounds i64, ptr %base, i64 8
+  %p2 = getelementptr inbounds i64, ptr %base, i64 16
+  %p3 = getelementptr inbounds i64, ptr %base, i64 24
+  %a = load i64, ptr %base, align 16
+  %b = load i64, ptr %p1, align 8
+  %c = load i64, ptr %p2, align 8
+  %d = load i64, ptr %p3, align 8
+  %v0 = add i64 %a, 3
+  %v1 = add i64 %b, 3
+  %v2 = add i64 %c, 3
+  %v3 = add i64 %d, 3
+  %q1 = getelementptr inbounds i64, ptr %out, i64 1
+  %q2 = getelementptr inbounds i64, ptr %out, i64 2
+  %q3 = getelementptr inbounds i64, ptr %out, i64 3
+  store i64 %v0, ptr %out, align 8
+  store i64 %v1, ptr %q1, align 8
+  store i64 %v2, ptr %q2, align 8
+  store i64 %v3, ptr %q3, align 8
+  ret void
+}

>From 3d91496de5ae8de036c07015bb9670748e8e4462 Mon Sep 17 00:00:00 2001
From: imkiva <zengtao at iscas.ac.cn>
Date: Thu, 10 Sep 2026 13:57:27 +0800
Subject: [PATCH 2/3] update test

---
 .../RISCV/strided-load-common-alignment.ll      | 17 ++++++++++-------
 1 file changed, 10 insertions(+), 7 deletions(-)

diff --git a/llvm/test/Transforms/SLPVectorizer/RISCV/strided-load-common-alignment.ll b/llvm/test/Transforms/SLPVectorizer/RISCV/strided-load-common-alignment.ll
index 165e0d940085e..4ec15de2532fd 100644
--- a/llvm/test/Transforms/SLPVectorizer/RISCV/strided-load-common-alignment.ll
+++ b/llvm/test/Transforms/SLPVectorizer/RISCV/strided-load-common-alignment.ll
@@ -1,6 +1,6 @@
-; RUN: opt -S -passes=slp-vectorizer -mtriple=riscv64 -mattr=+v,-unaligned-vector-mem < %s | FileCheck %s
-; RUN: opt -S -passes=slp-vectorizer -mtriple=riscv32 -mattr=+v,-unaligned-vector-mem < %s | FileCheck %s
-; RUN: opt -S -passes=slp-vectorizer -mtriple=riscv64 -mattr=+v,+unaligned-vector-mem < %s | FileCheck %s --check-prefix=UNALIGNED
+; RUN: opt -S -passes=slp-vectorizer -mtriple=riscv64 -mattr=+v,-unaligned-vector-mem < %s | FileCheck %s --check-prefixes=CHECK,RV64
+; RUN: opt -S -passes=slp-vectorizer -mtriple=riscv32 -mattr=+v,-unaligned-vector-mem < %s | FileCheck %s --check-prefixes=CHECK,RV32
+; RUN: opt -S -passes=slp-vectorizer -mtriple=riscv64 -mattr=+v,+unaligned-vector-mem < %s | FileCheck %s --check-prefixes=UNALIGNED,RV64
 ;
 ; The first load's alignment does not describe every group. Widening each
 ; adjacent pair to i64 would leave the group at byte offset 28 misaligned.
@@ -10,7 +10,7 @@ define void @gather_fields(ptr align 16 %base, ptr noalias %out) {
 ; CHECK-NOT: call <2 x i64> @llvm.experimental.vp.strided.load
 ; CHECK: ret void
 ; UNALIGNED-LABEL: define void @gather_fields(
-; UNALIGNED: call <2 x i64> @llvm.experimental.vp.strided.load
+; UNALIGNED: call <2 x i64> @llvm.experimental.vp.strided.load.v2i64.p0.i64(ptr align 4 %base, i64 28, <2 x i1> splat (i1 true), i32 2)
 ; UNALIGNED: ret void
   %p1 = getelementptr inbounds i32, ptr %base, i64 1
   %p2 = getelementptr inbounds i32, ptr %base, i64 7
@@ -35,9 +35,12 @@ define void @gather_fields(ptr align 16 %base, ptr noalias %out) {
 
 ; Naturally aligned strided loads remain legal.
 define void @aligned_values(ptr %base, ptr noalias %out) {
-; CHECK-LABEL: define void @aligned_values(
-; CHECK: call <4 x i64> @llvm.experimental.vp.strided.load
-; CHECK: ret void
+; RV64-LABEL: define void @aligned_values(
+; RV64: call <4 x i64> @llvm.experimental.vp.strided.load.v4i64.p0.i64(ptr align 8 %base, i64 64, <4 x i1> splat (i1 true), i32 4)
+; RV64: ret void
+; RV32-LABEL: define void @aligned_values(
+; RV32: call <4 x i64> @llvm.experimental.vp.strided.load.v4i64.p0.i32(ptr align 8 %base, i32 64, <4 x i1> splat (i1 true), i32 4)
+; RV32: ret void
   %p1 = getelementptr inbounds i64, ptr %base, i64 8
   %p2 = getelementptr inbounds i64, ptr %base, i64 16
   %p3 = getelementptr inbounds i64, ptr %base, i64 24

>From 9d639eec08124c5a36dd726e6ccd2a1a192148e7 Mon Sep 17 00:00:00 2001
From: imkiva <zengtao at iscas.ac.cn>
Date: Fri, 11 Sep 2026 03:52:46 +0800
Subject: [PATCH 3/3] update test

---
 .../RISCV/strided-load-common-alignment.ll    | 70 +++++++++++++++----
 1 file changed, 56 insertions(+), 14 deletions(-)

diff --git a/llvm/test/Transforms/SLPVectorizer/RISCV/strided-load-common-alignment.ll b/llvm/test/Transforms/SLPVectorizer/RISCV/strided-load-common-alignment.ll
index 4ec15de2532fd..0e34894be821c 100644
--- a/llvm/test/Transforms/SLPVectorizer/RISCV/strided-load-common-alignment.ll
+++ b/llvm/test/Transforms/SLPVectorizer/RISCV/strided-load-common-alignment.ll
@@ -1,17 +1,44 @@
-; RUN: opt -S -passes=slp-vectorizer -mtriple=riscv64 -mattr=+v,-unaligned-vector-mem < %s | FileCheck %s --check-prefixes=CHECK,RV64
-; RUN: opt -S -passes=slp-vectorizer -mtriple=riscv32 -mattr=+v,-unaligned-vector-mem < %s | FileCheck %s --check-prefixes=CHECK,RV32
-; RUN: opt -S -passes=slp-vectorizer -mtriple=riscv64 -mattr=+v,+unaligned-vector-mem < %s | FileCheck %s --check-prefixes=UNALIGNED,RV64
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -S -passes=slp-vectorizer -mtriple=riscv64 -mattr=+v,-unaligned-vector-mem < %s | FileCheck %s --check-prefix=RV64
+; RUN: opt -S -passes=slp-vectorizer -mtriple=riscv32 -mattr=+v,-unaligned-vector-mem < %s | FileCheck %s --check-prefix=RV32
+; RUN: opt -S -passes=slp-vectorizer -mtriple=riscv64 -mattr=+v,+unaligned-vector-mem < %s | FileCheck %s --check-prefix=UNALIGNED
 ;
 ; The first load's alignment does not describe every group. Widening each
 ; adjacent pair to i64 would leave the group at byte offset 28 misaligned.
 
-define void @gather_fields(ptr align 16 %base, ptr noalias %out) {
-; CHECK-LABEL: define void @gather_fields(
-; CHECK-NOT: call <2 x i64> @llvm.experimental.vp.strided.load
-; CHECK: ret void
+define void @gather_fields(ptr %base, ptr %out) {
+; RV64-LABEL: define void @gather_fields(
+; RV64-SAME: ptr [[BASE:%.*]], ptr [[OUT:%.*]]) #[[ATTR0:[0-9]+]] {
+; RV64-NEXT:    [[P2:%.*]] = getelementptr inbounds i32, ptr [[BASE]], i64 7
+; RV64-NEXT:    [[TMP1:%.*]] = load <2 x i32>, ptr [[BASE]], align 16
+; RV64-NEXT:    [[TMP2:%.*]] = load <2 x i32>, ptr [[P2]], align 4
+; RV64-NEXT:    [[TMP3:%.*]] = shufflevector <2 x i32> [[TMP1]], <2 x i32> poison, <4 x i32> <i32 0, i32 1, i32 poison, i32 poison>
+; RV64-NEXT:    [[TMP4:%.*]] = shufflevector <2 x i32> [[TMP2]], <2 x i32> poison, <4 x i32> <i32 0, i32 1, i32 poison, i32 poison>
+; RV64-NEXT:    [[TMP5:%.*]] = shufflevector <2 x i32> [[TMP1]], <2 x i32> [[TMP2]], <4 x i32> <i32 0, i32 1, i32 2, i32 3>
+; RV64-NEXT:    [[TMP6:%.*]] = add <4 x i32> [[TMP5]], splat (i32 3)
+; RV64-NEXT:    store <4 x i32> [[TMP6]], ptr [[OUT]], align 4
+; RV64-NEXT:    ret void
+;
+; RV32-LABEL: define void @gather_fields(
+; RV32-SAME: ptr [[BASE:%.*]], ptr [[OUT:%.*]]) #[[ATTR0:[0-9]+]] {
+; RV32-NEXT:    [[P2:%.*]] = getelementptr inbounds i32, ptr [[BASE]], i64 7
+; RV32-NEXT:    [[TMP1:%.*]] = load <2 x i32>, ptr [[BASE]], align 16
+; RV32-NEXT:    [[TMP2:%.*]] = load <2 x i32>, ptr [[P2]], align 4
+; RV32-NEXT:    [[TMP3:%.*]] = shufflevector <2 x i32> [[TMP1]], <2 x i32> poison, <4 x i32> <i32 0, i32 1, i32 poison, i32 poison>
+; RV32-NEXT:    [[TMP4:%.*]] = shufflevector <2 x i32> [[TMP2]], <2 x i32> poison, <4 x i32> <i32 0, i32 1, i32 poison, i32 poison>
+; RV32-NEXT:    [[TMP5:%.*]] = shufflevector <2 x i32> [[TMP1]], <2 x i32> [[TMP2]], <4 x i32> <i32 0, i32 1, i32 2, i32 3>
+; RV32-NEXT:    [[TMP6:%.*]] = add <4 x i32> [[TMP5]], splat (i32 3)
+; RV32-NEXT:    store <4 x i32> [[TMP6]], ptr [[OUT]], align 4
+; RV32-NEXT:    ret void
+;
 ; UNALIGNED-LABEL: define void @gather_fields(
-; UNALIGNED: call <2 x i64> @llvm.experimental.vp.strided.load.v2i64.p0.i64(ptr align 4 %base, i64 28, <2 x i1> splat (i1 true), i32 2)
-; UNALIGNED: ret void
+; UNALIGNED-SAME: ptr [[BASE:%.*]], ptr [[OUT:%.*]]) #[[ATTR0:[0-9]+]] {
+; UNALIGNED-NEXT:    [[TMP1:%.*]] = call <2 x i64> @llvm.experimental.vp.strided.load.v2i64.p0.i64(ptr align 4 [[BASE]], i64 28, <2 x i1> splat (i1 true), i32 2)
+; UNALIGNED-NEXT:    [[TMP2:%.*]] = bitcast <2 x i64> [[TMP1]] to <4 x i32>
+; UNALIGNED-NEXT:    [[TMP3:%.*]] = add <4 x i32> [[TMP2]], splat (i32 3)
+; UNALIGNED-NEXT:    store <4 x i32> [[TMP3]], ptr [[OUT]], align 4
+; UNALIGNED-NEXT:    ret void
+;
   %p1 = getelementptr inbounds i32, ptr %base, i64 1
   %p2 = getelementptr inbounds i32, ptr %base, i64 7
   %p3 = getelementptr inbounds i32, ptr %base, i64 8
@@ -34,13 +61,28 @@ define void @gather_fields(ptr align 16 %base, ptr noalias %out) {
 }
 
 ; Naturally aligned strided loads remain legal.
-define void @aligned_values(ptr %base, ptr noalias %out) {
+define void @aligned_values(ptr %base, ptr %out) {
 ; RV64-LABEL: define void @aligned_values(
-; RV64: call <4 x i64> @llvm.experimental.vp.strided.load.v4i64.p0.i64(ptr align 8 %base, i64 64, <4 x i1> splat (i1 true), i32 4)
-; RV64: ret void
+; RV64-SAME: ptr [[BASE:%.*]], ptr [[OUT:%.*]]) #[[ATTR0]] {
+; RV64-NEXT:    [[TMP1:%.*]] = call <4 x i64> @llvm.experimental.vp.strided.load.v4i64.p0.i64(ptr align 8 [[BASE]], i64 64, <4 x i1> splat (i1 true), i32 4)
+; RV64-NEXT:    [[TMP2:%.*]] = add <4 x i64> [[TMP1]], splat (i64 3)
+; RV64-NEXT:    store <4 x i64> [[TMP2]], ptr [[OUT]], align 8
+; RV64-NEXT:    ret void
+;
 ; RV32-LABEL: define void @aligned_values(
-; RV32: call <4 x i64> @llvm.experimental.vp.strided.load.v4i64.p0.i32(ptr align 8 %base, i32 64, <4 x i1> splat (i1 true), i32 4)
-; RV32: ret void
+; RV32-SAME: ptr [[BASE:%.*]], ptr [[OUT:%.*]]) #[[ATTR0]] {
+; RV32-NEXT:    [[TMP1:%.*]] = call <4 x i64> @llvm.experimental.vp.strided.load.v4i64.p0.i32(ptr align 8 [[BASE]], i32 64, <4 x i1> splat (i1 true), i32 4)
+; RV32-NEXT:    [[TMP2:%.*]] = add <4 x i64> [[TMP1]], splat (i64 3)
+; RV32-NEXT:    store <4 x i64> [[TMP2]], ptr [[OUT]], align 8
+; RV32-NEXT:    ret void
+;
+; UNALIGNED-LABEL: define void @aligned_values(
+; UNALIGNED-SAME: ptr [[BASE:%.*]], ptr [[OUT:%.*]]) #[[ATTR0]] {
+; UNALIGNED-NEXT:    [[TMP1:%.*]] = call <4 x i64> @llvm.experimental.vp.strided.load.v4i64.p0.i64(ptr align 8 [[BASE]], i64 64, <4 x i1> splat (i1 true), i32 4)
+; UNALIGNED-NEXT:    [[TMP2:%.*]] = add <4 x i64> [[TMP1]], splat (i64 3)
+; UNALIGNED-NEXT:    store <4 x i64> [[TMP2]], ptr [[OUT]], align 8
+; UNALIGNED-NEXT:    ret void
+;
   %p1 = getelementptr inbounds i64, ptr %base, i64 8
   %p2 = getelementptr inbounds i64, ptr %base, i64 16
   %p3 = getelementptr inbounds i64, ptr %base, i64 24



More information about the llvm-commits mailing list