[llvm] [AArch64] Use SVE for load-extend-broadcast on V3AE (PR #221689)
Jack Styles via llvm-commits
llvm-commits at lists.llvm.org
Mon Sep 7 02:35:39 PDT 2026
https://github.com/Stylie777 created https://github.com/llvm/llvm-project/pull/221689
On Neoverse-V3AE, it is faster to use SVE ld1r instructions to load, extend and broadcast an integer value to a vector in all cases other than 64bit vectors where no type promotion occurs.
To acheive this, introduce a tuning option, `PreferSVEVectors` for use when combining DUP instructions to create a Splat Vector to the scalable alternative, and then use the already implemented V3AE scheduling model pattern for splat vectors to end up generate the ld1r instruction.
If used in a loop, the predicate will be hoisted outside of the loop.
>From b9ae9f11f17222b2bbe1ff29b585e580f9c38d67 Mon Sep 17 00:00:00 2001
From: Jack Styles <jack.styles at arm.com>
Date: Fri, 4 Sep 2026 11:39:59 +0100
Subject: [PATCH] [AArch64] Use SVE for load-extend-broadcast on V3AE
On Neoverse-V3AE, it is faster to use SVE ld1r instructions to
load, extend and broadcast an integer value to a vector in all
cases other than 64bit vectors where no type promotion occurs.
To acheive this, introduce a tuning option, `PreferSVEVectors`
for use when combining DUP instructions to create a Splat Vector to
the scalable alternative, and then use the already implemented V3AE
scheduling model pattern for splat vectors to end up generate the
ld1r instruction.
If used in a loop, the predicate will be hoisted outside of the loop.
---
llvm/lib/Target/AArch64/AArch64Features.td | 4 +
.../Target/AArch64/AArch64ISelLowering.cpp | 17 +-
llvm/lib/Target/AArch64/AArch64Processors.td | 3 +-
.../CodeGen/AArch64/dup-ext-load-combine.ll | 332 ++++++++++++++----
4 files changed, 287 insertions(+), 69 deletions(-)
diff --git a/llvm/lib/Target/AArch64/AArch64Features.td b/llvm/lib/Target/AArch64/AArch64Features.td
index 6eea064b257f6..cd03583c7f4e5 100644
--- a/llvm/lib/Target/AArch64/AArch64Features.td
+++ b/llvm/lib/Target/AArch64/AArch64Features.td
@@ -1022,6 +1022,10 @@ def FeatureLimited64bitVectorMulBandwidth : SubtargetFeature<
"Has limited 64bit vector multiply bandwidth compared to scalar multiply",
[], InlineIgnore>;
+def FeaturePreferSVEVectors : SubtargetFeature <
+ "prefer vectors to be SVE", "PreferSVEVectors", "true",
+ "Prefer Vectors to use SVE", [], InlineIgnore>;
+
//===----------------------------------------------------------------------===//
// Architectures.
//
diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
index 22198cd122fc7..568d7b581370d 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
@@ -30117,7 +30117,8 @@ static SDValue performSelectCombine(SDNode *N,
}
static SDValue performDUPCombine(SDNode *N,
- TargetLowering::DAGCombinerInfo &DCI) {
+ TargetLowering::DAGCombinerInfo &DCI,
+ const AArch64Subtarget *SubTarget) {
EVT VT = N->getValueType(0);
SDLoc DL(N);
// If "v2i32 DUP(x)" and "v4i32 DUP(x)" both exist, use an extract from the
@@ -30141,6 +30142,18 @@ static SDValue performDUPCombine(SDNode *N,
// v4i32 = SCALAR_TO_VECTOR (i32 (zextloadi8 addr)) ; Matches to ldr b0
// v4i32 = DUPLANE32 (v4i32), 0
if (auto *LD = dyn_cast<LoadSDNode>(Op)) {
+ if (SubTarget->preferSVEVectors() &&
+ SubTarget->isSVEorStreamingSVEAvailable() && Op->hasOneUse() &&
+ VT.getScalarType().isInteger()) {
+ EVT ScalaleVT = getContainerForFixedLengthVector(DCI.DAG, VT);
+ SDValue SplatNode =
+ DCI.DAG.getNode(ISD::SPLAT_VECTOR, DL, ScalaleVT, Op);
+ // Using SVE on 64bit Vectors with the same scalar type is not
+ // profitable
+ if (!(VT.is64BitVector() && SplatNode.getValueType().getScalarType() ==
+ LD->getMemoryVT().getScalarType()))
+ return convertFromScalableVector(DCI.DAG, VT, SplatNode);
+ }
ISD::LoadExtType ExtType = LD->getExtensionType();
EVT MemVT = LD->getMemoryVT();
EVT ElemVT = VT.getVectorElementType();
@@ -31626,7 +31639,7 @@ SDValue AArch64TargetLowering::PerformDAGCombine(SDNode *N,
case AArch64ISD::DUPLANE16:
case AArch64ISD::DUPLANE32:
case AArch64ISD::DUPLANE64:
- return performDUPCombine(N, DCI);
+ return performDUPCombine(N, DCI, Subtarget);
case AArch64ISD::DUPLANE128:
return performDupLane128Combine(N, DAG);
case AArch64ISD::NVCAST:
diff --git a/llvm/lib/Target/AArch64/AArch64Processors.td b/llvm/lib/Target/AArch64/AArch64Processors.td
index f996522ba4d09..5b1411501a63e 100644
--- a/llvm/lib/Target/AArch64/AArch64Processors.td
+++ b/llvm/lib/Target/AArch64/AArch64Processors.td
@@ -618,7 +618,8 @@ def TuneNeoverseV3AE : SubtargetFeature<"neoversev3AE", "ARMProcFamily", "Neover
FeatureUseFixedOverScalableIfEqualCost,
FeatureAvoidLDAPUR,
FeaturePredictableSelectIsExpensive,
- FeatureLimited64bitVectorMulBandwidth]>;
+ FeatureLimited64bitVectorMulBandwidth,
+ FeaturePreferSVEVectors]>;
def TuneSaphira : SubtargetFeature<"saphira", "ARMProcFamily", "Saphira",
"Qualcomm Saphira processors", [
diff --git a/llvm/test/CodeGen/AArch64/dup-ext-load-combine.ll b/llvm/test/CodeGen/AArch64/dup-ext-load-combine.ll
index cf529343c6728..cb41f89574cea 100644
--- a/llvm/test/CodeGen/AArch64/dup-ext-load-combine.ll
+++ b/llvm/test/CodeGen/AArch64/dup-ext-load-combine.ll
@@ -1,15 +1,24 @@
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
-; RUN: llc -mtriple=aarch64-none-linux-gnu < %s | FileCheck %s
+; RUN: llc -mtriple=aarch64-none-linux-gnu < %s | FileCheck %s --check-prefixes=CHECK,CHECK-BASE
+; RUN: llc -mtriple=aarch64-none-linux-gnu -mcpu=neoverse-v3ae -mattr=-sve < %s | FileCheck %s --check-prefixes=CHECK,CHECK-BASE
+; RUN: llc -mtriple=aarch64-none-linux-gnu -mcpu=neoverse-v3ae < %s | FileCheck %s --check-prefixes=CHECK,CHECK-V3AE
; Test optimization of DUP with extended narrow loads
; This should avoid GPR->SIMD transfers by loading directly into vector registers
define <4 x i16> @test_dup_zextload_i8_v4i16(ptr %p) {
-; CHECK-LABEL: test_dup_zextload_i8_v4i16:
-; CHECK: // %bb.0:
-; CHECK-NEXT: ldr b0, [x0]
-; CHECK-NEXT: dup v0.4h, v0.h[0]
-; CHECK-NEXT: ret
+; CHECK-BASE-LABEL: test_dup_zextload_i8_v4i16:
+; CHECK-BASE: // %bb.0:
+; CHECK-BASE-NEXT: ldr b0, [x0]
+; CHECK-BASE-NEXT: dup v0.4h, v0.h[0]
+; CHECK-BASE-NEXT: ret
+;
+; CHECK-V3AE-LABEL: test_dup_zextload_i8_v4i16:
+; CHECK-V3AE: // %bb.0:
+; CHECK-V3AE-NEXT: ptrue p0.h
+; CHECK-V3AE-NEXT: ld1rb { z0.h }, p0/z, [x0]
+; CHECK-V3AE-NEXT: // kill: def $d0 killed $d0 killed $z0
+; CHECK-V3AE-NEXT: ret
%load = load i8, ptr %p, align 1
%ext = zext i8 %load to i16
%vec = insertelement <4 x i16> poison, i16 %ext, i32 0
@@ -18,11 +27,18 @@ define <4 x i16> @test_dup_zextload_i8_v4i16(ptr %p) {
}
define <8 x i16> @test_dup_zextload_i8_v8i16(ptr %p) {
-; CHECK-LABEL: test_dup_zextload_i8_v8i16:
-; CHECK: // %bb.0:
-; CHECK-NEXT: ldr b0, [x0]
-; CHECK-NEXT: dup v0.8h, v0.h[0]
-; CHECK-NEXT: ret
+; CHECK-BASE-LABEL: test_dup_zextload_i8_v8i16:
+; CHECK-BASE: // %bb.0:
+; CHECK-BASE-NEXT: ldr b0, [x0]
+; CHECK-BASE-NEXT: dup v0.8h, v0.h[0]
+; CHECK-BASE-NEXT: ret
+;
+; CHECK-V3AE-LABEL: test_dup_zextload_i8_v8i16:
+; CHECK-V3AE: // %bb.0:
+; CHECK-V3AE-NEXT: ptrue p0.h
+; CHECK-V3AE-NEXT: ld1rb { z0.h }, p0/z, [x0]
+; CHECK-V3AE-NEXT: // kill: def $q0 killed $q0 killed $z0
+; CHECK-V3AE-NEXT: ret
%load = load i8, ptr %p, align 1
%ext = zext i8 %load to i16
%vec = insertelement <8 x i16> poison, i16 %ext, i32 0
@@ -31,11 +47,18 @@ define <8 x i16> @test_dup_zextload_i8_v8i16(ptr %p) {
}
define <2 x i32> @test_dup_zextload_i8_v2i32(ptr %p) {
-; CHECK-LABEL: test_dup_zextload_i8_v2i32:
-; CHECK: // %bb.0:
-; CHECK-NEXT: ldr b0, [x0]
-; CHECK-NEXT: dup v0.2s, v0.s[0]
-; CHECK-NEXT: ret
+; CHECK-BASE-LABEL: test_dup_zextload_i8_v2i32:
+; CHECK-BASE: // %bb.0:
+; CHECK-BASE-NEXT: ldr b0, [x0]
+; CHECK-BASE-NEXT: dup v0.2s, v0.s[0]
+; CHECK-BASE-NEXT: ret
+;
+; CHECK-V3AE-LABEL: test_dup_zextload_i8_v2i32:
+; CHECK-V3AE: // %bb.0:
+; CHECK-V3AE-NEXT: ptrue p0.s
+; CHECK-V3AE-NEXT: ld1rb { z0.s }, p0/z, [x0]
+; CHECK-V3AE-NEXT: // kill: def $d0 killed $d0 killed $z0
+; CHECK-V3AE-NEXT: ret
%load = load i8, ptr %p, align 1
%ext = zext i8 %load to i32
%vec = insertelement <2 x i32> poison, i32 %ext, i32 0
@@ -44,11 +67,18 @@ define <2 x i32> @test_dup_zextload_i8_v2i32(ptr %p) {
}
define <4 x i32> @test_dup_zextload_i8_v4i32(ptr %p) {
-; CHECK-LABEL: test_dup_zextload_i8_v4i32:
-; CHECK: // %bb.0:
-; CHECK-NEXT: ldr b0, [x0]
-; CHECK-NEXT: dup v0.4s, v0.s[0]
-; CHECK-NEXT: ret
+; CHECK-BASE-LABEL: test_dup_zextload_i8_v4i32:
+; CHECK-BASE: // %bb.0:
+; CHECK-BASE-NEXT: ldr b0, [x0]
+; CHECK-BASE-NEXT: dup v0.4s, v0.s[0]
+; CHECK-BASE-NEXT: ret
+;
+; CHECK-V3AE-LABEL: test_dup_zextload_i8_v4i32:
+; CHECK-V3AE: // %bb.0:
+; CHECK-V3AE-NEXT: ptrue p0.s
+; CHECK-V3AE-NEXT: ld1rb { z0.s }, p0/z, [x0]
+; CHECK-V3AE-NEXT: // kill: def $q0 killed $q0 killed $z0
+; CHECK-V3AE-NEXT: ret
%load = load i8, ptr %p, align 1
%ext = zext i8 %load to i32
%vec = insertelement <4 x i32> poison, i32 %ext, i32 0
@@ -57,11 +87,18 @@ define <4 x i32> @test_dup_zextload_i8_v4i32(ptr %p) {
}
define <4 x i32> @test_dup_zextload_i8_v4i32_offset(ptr %p) {
-; CHECK-LABEL: test_dup_zextload_i8_v4i32_offset:
-; CHECK: // %bb.0:
-; CHECK-NEXT: ldr b0, [x0, #4]
-; CHECK-NEXT: dup v0.4s, v0.s[0]
-; CHECK-NEXT: ret
+; CHECK-BASE-LABEL: test_dup_zextload_i8_v4i32_offset:
+; CHECK-BASE: // %bb.0:
+; CHECK-BASE-NEXT: ldr b0, [x0, #4]
+; CHECK-BASE-NEXT: dup v0.4s, v0.s[0]
+; CHECK-BASE-NEXT: ret
+;
+; CHECK-V3AE-LABEL: test_dup_zextload_i8_v4i32_offset:
+; CHECK-V3AE: // %bb.0:
+; CHECK-V3AE-NEXT: ptrue p0.s
+; CHECK-V3AE-NEXT: ld1rb { z0.s }, p0/z, [x0, #4]
+; CHECK-V3AE-NEXT: // kill: def $q0 killed $q0 killed $z0
+; CHECK-V3AE-NEXT: ret
%addr = getelementptr inbounds i8, ptr %p, i64 4
%load = load i8, ptr %addr, align 1
%ext = zext i8 %load to i32
@@ -71,11 +108,19 @@ define <4 x i32> @test_dup_zextload_i8_v4i32_offset(ptr %p) {
}
define <4 x i32> @test_dup_zextload_i8_v4i32_reg_offset(ptr %p, i64 %offset) {
-; CHECK-LABEL: test_dup_zextload_i8_v4i32_reg_offset:
-; CHECK: // %bb.0:
-; CHECK-NEXT: ldr b0, [x0, x1]
-; CHECK-NEXT: dup v0.4s, v0.s[0]
-; CHECK-NEXT: ret
+; CHECK-BASE-LABEL: test_dup_zextload_i8_v4i32_reg_offset:
+; CHECK-BASE: // %bb.0:
+; CHECK-BASE-NEXT: ldr b0, [x0, x1]
+; CHECK-BASE-NEXT: dup v0.4s, v0.s[0]
+; CHECK-BASE-NEXT: ret
+;
+; CHECK-V3AE-LABEL: test_dup_zextload_i8_v4i32_reg_offset:
+; CHECK-V3AE: // %bb.0:
+; CHECK-V3AE-NEXT: ptrue p0.s
+; CHECK-V3AE-NEXT: add x8, x0, x1
+; CHECK-V3AE-NEXT: ld1rb { z0.s }, p0/z, [x8]
+; CHECK-V3AE-NEXT: // kill: def $q0 killed $q0 killed $z0
+; CHECK-V3AE-NEXT: ret
%addr = getelementptr inbounds i8, ptr %p, i64 %offset
%load = load i8, ptr %addr, align 1
%ext = zext i8 %load to i32
@@ -85,11 +130,18 @@ define <4 x i32> @test_dup_zextload_i8_v4i32_reg_offset(ptr %p, i64 %offset) {
}
define <2 x i64> @test_dup_zextload_i8_v2i64(ptr %p) {
-; CHECK-LABEL: test_dup_zextload_i8_v2i64:
-; CHECK: // %bb.0:
-; CHECK-NEXT: ldr b0, [x0]
-; CHECK-NEXT: dup v0.2d, v0.d[0]
-; CHECK-NEXT: ret
+; CHECK-BASE-LABEL: test_dup_zextload_i8_v2i64:
+; CHECK-BASE: // %bb.0:
+; CHECK-BASE-NEXT: ldr b0, [x0]
+; CHECK-BASE-NEXT: dup v0.2d, v0.d[0]
+; CHECK-BASE-NEXT: ret
+;
+; CHECK-V3AE-LABEL: test_dup_zextload_i8_v2i64:
+; CHECK-V3AE: // %bb.0:
+; CHECK-V3AE-NEXT: ptrue p0.d
+; CHECK-V3AE-NEXT: ld1rb { z0.d }, p0/z, [x0]
+; CHECK-V3AE-NEXT: // kill: def $q0 killed $q0 killed $z0
+; CHECK-V3AE-NEXT: ret
%load = load i8, ptr %p, align 1
%ext = zext i8 %load to i64
%vec = insertelement <2 x i64> poison, i64 %ext, i32 0
@@ -98,11 +150,18 @@ define <2 x i64> @test_dup_zextload_i8_v2i64(ptr %p) {
}
define <2 x i32> @test_dup_zextload_i16_v2i32(ptr %p) {
-; CHECK-LABEL: test_dup_zextload_i16_v2i32:
-; CHECK: // %bb.0:
-; CHECK-NEXT: ldr h0, [x0]
-; CHECK-NEXT: dup v0.2s, v0.s[0]
-; CHECK-NEXT: ret
+; CHECK-BASE-LABEL: test_dup_zextload_i16_v2i32:
+; CHECK-BASE: // %bb.0:
+; CHECK-BASE-NEXT: ldr h0, [x0]
+; CHECK-BASE-NEXT: dup v0.2s, v0.s[0]
+; CHECK-BASE-NEXT: ret
+;
+; CHECK-V3AE-LABEL: test_dup_zextload_i16_v2i32:
+; CHECK-V3AE: // %bb.0:
+; CHECK-V3AE-NEXT: ptrue p0.s
+; CHECK-V3AE-NEXT: ld1rh { z0.s }, p0/z, [x0]
+; CHECK-V3AE-NEXT: // kill: def $d0 killed $d0 killed $z0
+; CHECK-V3AE-NEXT: ret
%load = load i16, ptr %p, align 1
%ext = zext i16 %load to i32
%vec = insertelement <2 x i32> poison, i32 %ext, i32 0
@@ -111,11 +170,18 @@ define <2 x i32> @test_dup_zextload_i16_v2i32(ptr %p) {
}
define <4 x i32> @test_dup_zextload_i16_v4i32(ptr %p) {
-; CHECK-LABEL: test_dup_zextload_i16_v4i32:
-; CHECK: // %bb.0:
-; CHECK-NEXT: ldr h0, [x0]
-; CHECK-NEXT: dup v0.4s, v0.s[0]
-; CHECK-NEXT: ret
+; CHECK-BASE-LABEL: test_dup_zextload_i16_v4i32:
+; CHECK-BASE: // %bb.0:
+; CHECK-BASE-NEXT: ldr h0, [x0]
+; CHECK-BASE-NEXT: dup v0.4s, v0.s[0]
+; CHECK-BASE-NEXT: ret
+;
+; CHECK-V3AE-LABEL: test_dup_zextload_i16_v4i32:
+; CHECK-V3AE: // %bb.0:
+; CHECK-V3AE-NEXT: ptrue p0.s
+; CHECK-V3AE-NEXT: ld1rh { z0.s }, p0/z, [x0]
+; CHECK-V3AE-NEXT: // kill: def $q0 killed $q0 killed $z0
+; CHECK-V3AE-NEXT: ret
%load = load i16, ptr %p, align 1
%ext = zext i16 %load to i32
%vec = insertelement <4 x i32> poison, i32 %ext, i32 0
@@ -124,11 +190,18 @@ define <4 x i32> @test_dup_zextload_i16_v4i32(ptr %p) {
}
define <4 x i32> @test_dup_zextload_i16_v4i32_offset(ptr %p) {
-; CHECK-LABEL: test_dup_zextload_i16_v4i32_offset:
-; CHECK: // %bb.0:
-; CHECK-NEXT: ldr h0, [x0, #8]
-; CHECK-NEXT: dup v0.4s, v0.s[0]
-; CHECK-NEXT: ret
+; CHECK-BASE-LABEL: test_dup_zextload_i16_v4i32_offset:
+; CHECK-BASE: // %bb.0:
+; CHECK-BASE-NEXT: ldr h0, [x0, #8]
+; CHECK-BASE-NEXT: dup v0.4s, v0.s[0]
+; CHECK-BASE-NEXT: ret
+;
+; CHECK-V3AE-LABEL: test_dup_zextload_i16_v4i32_offset:
+; CHECK-V3AE: // %bb.0:
+; CHECK-V3AE-NEXT: ptrue p0.s
+; CHECK-V3AE-NEXT: ld1rh { z0.s }, p0/z, [x0, #8]
+; CHECK-V3AE-NEXT: // kill: def $q0 killed $q0 killed $z0
+; CHECK-V3AE-NEXT: ret
%addr = getelementptr inbounds i16, ptr %p, i64 4
%load = load i16, ptr %addr, align 1
%ext = zext i16 %load to i32
@@ -138,11 +211,19 @@ define <4 x i32> @test_dup_zextload_i16_v4i32_offset(ptr %p) {
}
define <4 x i32> @test_dup_zextload_i16_v4i32_reg_offset(ptr %p, i64 %offset) {
-; CHECK-LABEL: test_dup_zextload_i16_v4i32_reg_offset:
-; CHECK: // %bb.0:
-; CHECK-NEXT: ldr h0, [x0, x1, lsl #1]
-; CHECK-NEXT: dup v0.4s, v0.s[0]
-; CHECK-NEXT: ret
+; CHECK-BASE-LABEL: test_dup_zextload_i16_v4i32_reg_offset:
+; CHECK-BASE: // %bb.0:
+; CHECK-BASE-NEXT: ldr h0, [x0, x1, lsl #1]
+; CHECK-BASE-NEXT: dup v0.4s, v0.s[0]
+; CHECK-BASE-NEXT: ret
+;
+; CHECK-V3AE-LABEL: test_dup_zextload_i16_v4i32_reg_offset:
+; CHECK-V3AE: // %bb.0:
+; CHECK-V3AE-NEXT: ptrue p0.s
+; CHECK-V3AE-NEXT: add x8, x0, x1, lsl #1
+; CHECK-V3AE-NEXT: ld1rh { z0.s }, p0/z, [x8]
+; CHECK-V3AE-NEXT: // kill: def $q0 killed $q0 killed $z0
+; CHECK-V3AE-NEXT: ret
%addr = getelementptr inbounds i16, ptr %p, i64 %offset
%load = load i16, ptr %addr, align 1
%ext = zext i16 %load to i32
@@ -152,11 +233,18 @@ define <4 x i32> @test_dup_zextload_i16_v4i32_reg_offset(ptr %p, i64 %offset) {
}
define <2 x i64> @test_dup_zextload_i16_v2i64(ptr %p) {
-; CHECK-LABEL: test_dup_zextload_i16_v2i64:
-; CHECK: // %bb.0:
-; CHECK-NEXT: ldr h0, [x0]
-; CHECK-NEXT: dup v0.2d, v0.d[0]
-; CHECK-NEXT: ret
+; CHECK-BASE-LABEL: test_dup_zextload_i16_v2i64:
+; CHECK-BASE: // %bb.0:
+; CHECK-BASE-NEXT: ldr h0, [x0]
+; CHECK-BASE-NEXT: dup v0.2d, v0.d[0]
+; CHECK-BASE-NEXT: ret
+;
+; CHECK-V3AE-LABEL: test_dup_zextload_i16_v2i64:
+; CHECK-V3AE: // %bb.0:
+; CHECK-V3AE-NEXT: ptrue p0.d
+; CHECK-V3AE-NEXT: ld1rh { z0.d }, p0/z, [x0]
+; CHECK-V3AE-NEXT: // kill: def $q0 killed $q0 killed $z0
+; CHECK-V3AE-NEXT: ret
%load = load i16, ptr %p, align 1
%ext = zext i16 %load to i64
%vec = insertelement <2 x i64> poison, i64 %ext, i32 0
@@ -165,14 +253,126 @@ define <2 x i64> @test_dup_zextload_i16_v2i64(ptr %p) {
}
define <2 x i64> @test_dup_zextload_i32_v2i64(ptr %p) {
-; CHECK-LABEL: test_dup_zextload_i32_v2i64:
-; CHECK: // %bb.0:
-; CHECK-NEXT: ldr s0, [x0]
-; CHECK-NEXT: dup v0.2d, v0.d[0]
-; CHECK-NEXT: ret
+; CHECK-BASE-LABEL: test_dup_zextload_i32_v2i64:
+; CHECK-BASE: // %bb.0:
+; CHECK-BASE-NEXT: ldr s0, [x0]
+; CHECK-BASE-NEXT: dup v0.2d, v0.d[0]
+; CHECK-BASE-NEXT: ret
+;
+; CHECK-V3AE-LABEL: test_dup_zextload_i32_v2i64:
+; CHECK-V3AE: // %bb.0:
+; CHECK-V3AE-NEXT: ptrue p0.d
+; CHECK-V3AE-NEXT: ld1rw { z0.d }, p0/z, [x0]
+; CHECK-V3AE-NEXT: // kill: def $q0 killed $q0 killed $z0
+; CHECK-V3AE-NEXT: ret
%load = load i32, ptr %p, align 1
%ext = zext i32 %load to i64
%vec = insertelement <2 x i64> poison, i64 %ext, i32 0
%dup = shufflevector <2 x i64> %vec, <2 x i64> poison, <2 x i32> zeroinitializer
ret <2 x i64> %dup
}
+
+define <16 x i8> @test_dup_load_i8_v16i8(ptr %p) {
+; CHECK-BASE-LABEL: test_dup_load_i8_v16i8:
+; CHECK-BASE: // %bb.0:
+; CHECK-BASE-NEXT: ld1r { v0.16b }, [x0]
+; CHECK-BASE-NEXT: ret
+;
+; CHECK-V3AE-LABEL: test_dup_load_i8_v16i8:
+; CHECK-V3AE: // %bb.0:
+; CHECK-V3AE-NEXT: ptrue p0.b
+; CHECK-V3AE-NEXT: ld1rb { z0.b }, p0/z, [x0]
+; CHECK-V3AE-NEXT: // kill: def $q0 killed $q0 killed $z0
+; CHECK-V3AE-NEXT: ret
+ %load = load i8, ptr %p, align 1
+ %vec = insertelement <16 x i8> poison, i8 %load, i64 0
+ %dup = shufflevector <16 x i8> %vec, <16 x i8> poison, <16 x i32> zeroinitializer
+ ret <16 x i8> %dup
+}
+
+define <8 x i8> @test_dup_load_i8_v8i8(ptr %p) {
+; CHECK-LABEL: test_dup_load_i8_v8i8:
+; CHECK: // %bb.0:
+; CHECK-NEXT: ld1r { v0.8b }, [x0]
+; CHECK-NEXT: ret
+ %load = load i8, ptr %p, align 1
+ %vec = insertelement <8 x i8> poison, i8 %load, i64 0
+ %dup = shufflevector <8 x i8> %vec, <8 x i8> poison, <8 x i32> zeroinitializer
+ ret <8 x i8> %dup
+}
+
+define <8 x i16> @test_dup_load_i16_v8i16(ptr %p) {
+; CHECK-BASE-LABEL: test_dup_load_i16_v8i16:
+; CHECK-BASE: // %bb.0:
+; CHECK-BASE-NEXT: ld1r { v0.8h }, [x0]
+; CHECK-BASE-NEXT: ret
+;
+; CHECK-V3AE-LABEL: test_dup_load_i16_v8i16:
+; CHECK-V3AE: // %bb.0:
+; CHECK-V3AE-NEXT: ptrue p0.h
+; CHECK-V3AE-NEXT: ld1rh { z0.h }, p0/z, [x0]
+; CHECK-V3AE-NEXT: // kill: def $q0 killed $q0 killed $z0
+; CHECK-V3AE-NEXT: ret
+ %load = load i16, ptr %p, align 1
+ %vec = insertelement <8 x i16> poison, i16 %load, i64 0
+ %dup = shufflevector <8 x i16> %vec, <8 x i16> poison, <8 x i32> zeroinitializer
+ ret <8 x i16> %dup
+}
+
+define <4 x i16> @test_dup_load_i16_v4i16(ptr %p) {
+; CHECK-LABEL: test_dup_load_i16_v4i16:
+; CHECK: // %bb.0:
+; CHECK-NEXT: ld1r { v0.4h }, [x0]
+; CHECK-NEXT: ret
+ %load = load i16, ptr %p, align 1
+ %vec = insertelement <4 x i16> poison, i16 %load, i64 0
+ %dup = shufflevector <4 x i16> %vec, <4 x i16> poison, <4 x i32> zeroinitializer
+ ret <4 x i16> %dup
+}
+
+define <4 x i32> @test_dup_load_i32_v4i32(ptr %p) {
+; CHECK-BASE-LABEL: test_dup_load_i32_v4i32:
+; CHECK-BASE: // %bb.0:
+; CHECK-BASE-NEXT: ld1r { v0.4s }, [x0]
+; CHECK-BASE-NEXT: ret
+;
+; CHECK-V3AE-LABEL: test_dup_load_i32_v4i32:
+; CHECK-V3AE: // %bb.0:
+; CHECK-V3AE-NEXT: ptrue p0.s
+; CHECK-V3AE-NEXT: ld1rw { z0.s }, p0/z, [x0]
+; CHECK-V3AE-NEXT: // kill: def $q0 killed $q0 killed $z0
+; CHECK-V3AE-NEXT: ret
+ %load = load i32, ptr %p, align 1
+ %vec = insertelement <4 x i32> poison, i32 %load, i64 0
+ %dup = shufflevector <4 x i32> %vec, <4 x i32> poison, <4 x i32> zeroinitializer
+ ret <4 x i32> %dup
+}
+
+define <2 x i32> @test_dup_load_i32_v2i32(ptr %p) {
+; CHECK-LABEL: test_dup_load_i32_v2i32:
+; CHECK: // %bb.0:
+; CHECK-NEXT: ld1r { v0.2s }, [x0]
+; CHECK-NEXT: ret
+ %load = load i32, ptr %p, align 1
+ %vec = insertelement <2 x i32> poison, i32 %load, i64 0
+ %dup = shufflevector <2 x i32> %vec, <2 x i32> poison, <2 x i32> zeroinitializer
+ ret <2 x i32> %dup
+}
+
+define <2 x i64> @test_dup_load_i64_v2i64(ptr %p) {
+; CHECK-BASE-LABEL: test_dup_load_i64_v2i64:
+; CHECK-BASE: // %bb.0:
+; CHECK-BASE-NEXT: ld1r { v0.2d }, [x0]
+; CHECK-BASE-NEXT: ret
+;
+; CHECK-V3AE-LABEL: test_dup_load_i64_v2i64:
+; CHECK-V3AE: // %bb.0:
+; CHECK-V3AE-NEXT: ptrue p0.d
+; CHECK-V3AE-NEXT: ld1rd { z0.d }, p0/z, [x0]
+; CHECK-V3AE-NEXT: // kill: def $q0 killed $q0 killed $z0
+; CHECK-V3AE-NEXT: ret
+ %load = load i64, ptr %p, align 1
+ %vec = insertelement <2 x i64> poison, i64 %load, i64 0
+ %dup = shufflevector <2 x i64> %vec, <2 x i64> poison, <2 x i32> zeroinitializer
+ ret <2 x i64> %dup
+}
More information about the llvm-commits
mailing list