| ; 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 %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-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 |
| %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 %out) { |
| ; RV64-LABEL: define void @aligned_values( |
| ; 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-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 |
| %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 |
| } |