[llvm] 57ad789 - [RISCV][SLP] Use common alignment when checking constant-stride loads (#222520)
via llvm-commits
llvm-commits at lists.llvm.org
Fri Sep 11 12:10:35 PDT 2026
Author: Kiva
Date: 2026-09-12T03:10:30+08:00
New Revision: 57ad7898dc17e228c3ce1d34a53626080131f384
URL: https://github.com/llvm/llvm-project/commit/57ad7898dc17e228c3ce1d34a53626080131f384
DIFF: https://github.com/llvm/llvm-project/commit/57ad7898dc17e228c3ce1d34a53626080131f384.diff
LOG: [RISCV][SLP] Use common alignment when checking constant-stride loads (#222520)
Fix SLP's constant-stride load legality check, which can widen naturally
aligned 32-bit loads into misaligned 64-bit RVV accesses and cause
`SIGBUS`.
In `BoUpSLP::canVectorizeLoads`, the check uses the first sorted load's
alignment, which may be stronger than that of later widened groups. Pass
`CommonAlignment` to `analyzeConstantStrideCandidate` instead, matching
the existing cost model and code generation. This is conservative: it
fixes the legality check without deriving a more precise alignment for
each widened group's starting address, so it may reject some otherwise
legal widening.
### Reproducer
The following reproduces the failure on the tested [SpacemiT K1 / Banana
Pi F3](https://docs.banana-pi.org/en/BPI-F3/BananaPi_BPI-F3) running
RV64 Linux, compiled with `-O3 -march=rv64gcv -mabi=lp64d
-mstrict-align`:
```c
struct Row {
unsigned x, y, padding[5];
};
_Static_assert(sizeof(unsigned) == 4, "requires 32-bit unsigned");
_Static_assert(sizeof(struct Row) == 28, "requires a 28-byte row");
__attribute__((noinline))
void gather_fields(const struct Row *p, unsigned *restrict out) {
p = __builtin_assume_aligned(p, 16);
out[0] = p[0].x + 3;
out[1] = p[0].y + 3;
out[2] = p[1].x + 3;
out[3] = p[1].y + 3;
}
int main(void) {
_Alignas(16) struct Row rows[2] = {
{5, 9, {0}}, {11, 17, {0}}};
unsigned out[4];
gather_fields(rows, out);
return out[0] != 8 || out[1] != 12 ||
out[2] != 14 || out[3] != 20;
}
```
The alignment assumption is satisfied, and the four loads at byte
offsets `0`, `4`, `28`, and `32` are naturally aligned and in bounds.
SLP widens the adjacent pairs into:
```llvm
%v = call <2 x i64> @llvm.experimental.vp.strided.load.v2i64.p0.i64(
ptr align 4 %p, i64 28, <2 x i1> splat (i1 true), i32 2)
```
On RV64 this lowers to:
```asm
li a2, 28
vsetivli zero, 2, e64, m1, ta, ma
vlse64.v v8, (a0), a2
```
whose second active lane accesses `base + 28`, which is not eight-byte
aligned. The legality check incorrectly uses alignment 16, although the
emitted intrinsic already carries `align 4`.
On the tested machine, the example raises `SIGBUS` before the fix and
returns zero afterward. The fixed compiler uses 32-bit vector loads and
retains vectorized arithmetic.
Added:
Modified:
llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
llvm/test/Transforms/SLPVectorizer/RISCV/strided-load-common-alignment.ll
Removed:
################################################################################
diff --git a/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp b/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
index 488bfb36b6ccc..2cb633be89c3c 100644
--- a/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
+++ b/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
@@ -6196,11 +6196,10 @@ BoUpSLP::LoadsState BoUpSLP::canVectorizeLoads(
},
SLPReVec))
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
index 81203dbb5a047..0e34894be821c 100644
--- a/llvm/test/Transforms/SLPVectorizer/RISCV/strided-load-common-alignment.ll
+++ b/llvm/test/Transforms/SLPVectorizer/RISCV/strided-load-common-alignment.ll
@@ -5,13 +5,16 @@
;
; 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.
-; FIXME: Do not form an i64 strided load in the RV64 strict-alignment case.
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: [[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)
-; RV64-NEXT: [[TMP5:%.*]] = bitcast <2 x i64> [[TMP1]] to <4 x i32>
+; 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
@@ -19,13 +22,13 @@ define void @gather_fields(ptr %base, ptr %out) {
; 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: [[Q2:%.*]] = getelementptr inbounds i32, ptr [[OUT]], i64 2
; RV32-NEXT: [[TMP1:%.*]] = load <2 x i32>, ptr [[BASE]], align 16
-; RV32-NEXT: [[TMP3:%.*]] = add <2 x i32> [[TMP1]], splat (i32 3)
; RV32-NEXT: [[TMP2:%.*]] = load <2 x i32>, ptr [[P2]], align 4
-; RV32-NEXT: [[TMP4:%.*]] = add <2 x i32> [[TMP2]], splat (i32 3)
-; RV32-NEXT: store <2 x i32> [[TMP3]], ptr [[OUT]], align 4
-; RV32-NEXT: store <2 x i32> [[TMP4]], ptr [[Q2]], 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(
More information about the llvm-commits
mailing list