[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