[llvm] [AArch64] Restrict SVE index to profitable BUILD_VECTOR sequences (PR #215250)

David Sherwood via llvm-commits llvm-commits at lists.llvm.org
Mon Aug 10 04:35:21 PDT 2026


https://github.com/david-arm created https://github.com/llvm/llvm-project/pull/215250

We were previously arbitrarily using the SVE index instruction for all BUILD_VECTOR arithmetic sequences even when it's not profitable to do so. According to the software optimisation guides all variants of index that take a scalar register as input are slower than just loading a literal. Furthermore, by taking this shortcut so early during LowerBUILD_VECTOR we miss out on more profitable opportunities as seen by the changes in sve-fixed-length-build-vector.ll.

>From 653f0e2b89ab58e1f66bce5b2e0bc150c749aa14 Mon Sep 17 00:00:00 2001
From: David Sherwood <david.sherwood at arm.com>
Date: Mon, 10 Aug 2026 11:32:36 +0000
Subject: [PATCH] [AArch64] Restrict SVE index to profitable BUILD_VECTOR
 sequences

We were previously arbitrarily using the SVE index instruction
for all BUILD_VECTOR arithmetic sequences even when it's not
profitable to do so. According to the software optimisation
guides all variants of index that take a scalar register as
input are slower than just loading a literal. Furthermore, by
taking this shortcut so early during LowerBUILD_VECTOR we miss
out on more profitable opportunities as seen by the changes in
sve-fixed-length-build-vector.ll.
---
 .../lib/Target/AArch64/AArch64ISelLowering.cpp | 15 +++++++++++++--
 .../AArch64/sve-fixed-length-build-vector.ll   | 18 ++++++------------
 .../AArch64/sve-index-const-step-vector.ll     | 16 ++++++----------
 3 files changed, 25 insertions(+), 24 deletions(-)

diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
index 2ac6c5fbc471a..cc00c1003d05f 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
@@ -16888,8 +16888,19 @@ SDValue AArch64TargetLowering::LowerBUILD_VECTOR(SDValue Op,
                                                  SelectionDAG &DAG) const {
   EVT VT = Op.getValueType();
 
-  bool OverrideNEON = !Subtarget->isNeonAvailable() ||
-                      cast<BuildVectorSDNode>(Op)->isArithmeticSequence();
+  bool OverrideNEON = false;
+  if (!Subtarget->isNeonAvailable())
+    OverrideNEON = true;
+  else if (DAG.getSubtarget<AArch64Subtarget>()
+               .isSVEorStreamingSVEAvailable()) {
+    if (auto Seq = cast<BuildVectorSDNode>(Op)->isArithmeticSequence()) {
+      // Only attempt to use the SVE index instruction if both operands are
+      // immediate, otherwise it's better to load a literal.
+      if (Seq->first.sge(-16) && Seq->first.slt(16) && Seq->second.sge(-16) &&
+          Seq->second.slt(16))
+        OverrideNEON = true;
+    }
+  }
   if (useSVEForFixedLengthVectorVT(VT, OverrideNEON))
     return LowerFixedLengthBuildVectorToSVE(Op, DAG);
 
diff --git a/llvm/test/CodeGen/AArch64/sve-fixed-length-build-vector.ll b/llvm/test/CodeGen/AArch64/sve-fixed-length-build-vector.ll
index 96056713857cb..d47575f56dfb8 100644
--- a/llvm/test/CodeGen/AArch64/sve-fixed-length-build-vector.ll
+++ b/llvm/test/CodeGen/AArch64/sve-fixed-length-build-vector.ll
@@ -196,9 +196,8 @@ define <8 x i8> @zip2_const_seq_with_variable(<8 x i8> %x) #0 {
 define void @build_vector_mod_inverse_v4i8(ptr %a) #0 {
 ; VBITS_GE_256-LABEL: build_vector_mod_inverse_v4i8:
 ; VBITS_GE_256:       // %bb.0:
-; VBITS_GE_256-NEXT:    mov w8, #85 // =0x55
+; VBITS_GE_256-NEXT:    movi d0, #0xff000000ff0000
 ; VBITS_GE_256-NEXT:    ptrue p0.h, vl4
-; VBITS_GE_256-NEXT:    index z0.h, #0, w8
 ; VBITS_GE_256-NEXT:    st1b { z0.h }, p0, [x0]
 ; VBITS_GE_256-NEXT:    ret
   store <4 x i8> <i8 0, i8 poison, i8 poison, i8 255>, ptr %a
@@ -211,9 +210,7 @@ define void @build_vector_mod_inverse_v4i8(ptr %a) #0 {
 define void @build_vector_mod_inverse_v8i8_0xAA(ptr %a) #0 {
 ; VBITS_GE_256-LABEL: build_vector_mod_inverse_v8i8_0xAA:
 ; VBITS_GE_256:       // %bb.0:
-; VBITS_GE_256-NEXT:    mov w8, #170 // =0xaa
-; VBITS_GE_256-NEXT:    index z0.b, #0, w8
-; VBITS_GE_256-NEXT:    add z0.b, z0.b, #86 // =0x56
+; VBITS_GE_256-NEXT:    movi v0.4h, #254
 ; VBITS_GE_256-NEXT:    str d0, [x0]
 ; VBITS_GE_256-NEXT:    ret
   store <8 x i8> <i8 poison, i8 0, i8 poison, i8 poison, i8 254, i8 poison, i8 poison, i8 poison>, ptr %a
@@ -239,9 +236,8 @@ define void @build_vector_mod_inverse_v8i8_neg1(ptr %a) #0 {
 define void @build_vector_mod_inverse_v7i8(ptr %a) #0 {
 ; VBITS_GE_256-LABEL: build_vector_mod_inverse_v7i8:
 ; VBITS_GE_256:       // %bb.0:
-; VBITS_GE_256-NEXT:    mov w8, #85 // =0x55
-; VBITS_GE_256-NEXT:    index z0.b, #0, w8
-; VBITS_GE_256-NEXT:    add z0.b, z0.b, #85 // =0x55
+; VBITS_GE_256-NEXT:    adrp x8, .LCPI18_0
+; VBITS_GE_256-NEXT:    ldr d0, [x8, :lo12:.LCPI18_0]
 ; VBITS_GE_256-NEXT:    mov h1, v0.h[2]
 ; VBITS_GE_256-NEXT:    str s0, [x0]
 ; VBITS_GE_256-NEXT:    str h1, [x0, #4]
@@ -256,8 +252,7 @@ define void @build_vector_mod_inverse_v7i8(ptr %a) #0 {
 define void @build_vector_mod_inverse_i16(ptr %a) #0 {
 ; VBITS_GE_256-LABEL: build_vector_mod_inverse_i16:
 ; VBITS_GE_256:       // %bb.0:
-; VBITS_GE_256-NEXT:    mov w8, #21845 // =0x5555
-; VBITS_GE_256-NEXT:    index z0.h, #0, w8
+; VBITS_GE_256-NEXT:    movi d0, #0xffff0000ffff0000
 ; VBITS_GE_256-NEXT:    str d0, [x0]
 ; VBITS_GE_256-NEXT:    ret
   store <4 x i16> <i16 0, i16 poison, i16 poison, i16 -1>, ptr %a
@@ -270,8 +265,7 @@ define void @build_vector_mod_inverse_i16(ptr %a) #0 {
 define void @build_vector_mod_inverse_i32(ptr %a) #0 {
 ; VBITS_GE_256-LABEL: build_vector_mod_inverse_i32:
 ; VBITS_GE_256:       // %bb.0:
-; VBITS_GE_256-NEXT:    mov w8, #1431655765 // =0x55555555
-; VBITS_GE_256-NEXT:    index z0.s, #1, w8
+; VBITS_GE_256-NEXT:    mov z0.d, #1 // =0x1
 ; VBITS_GE_256-NEXT:    str q0, [x0]
 ; VBITS_GE_256-NEXT:    ret
   store <4 x i32> <i32 1, i32 poison, i32 poison, i32 0>, ptr %a
diff --git a/llvm/test/CodeGen/AArch64/sve-index-const-step-vector.ll b/llvm/test/CodeGen/AArch64/sve-index-const-step-vector.ll
index cf2ae02c14b18..3557760fb00eb 100644
--- a/llvm/test/CodeGen/AArch64/sve-index-const-step-vector.ll
+++ b/llvm/test/CodeGen/AArch64/sve-index-const-step-vector.ll
@@ -94,9 +94,8 @@ define <4 x i32> @v4i32_neg_immediates() #0 {
 define <4 x i32> @v4i32_out_range_start() #0 {
 ; CHECK-LABEL: v4i32_out_range_start:
 ; CHECK:       // %bb.0:
-; CHECK-NEXT:    mov w8, #16 // =0x10
-; CHECK-NEXT:    index z0.s, w8, #1
-; CHECK-NEXT:    // kill: def $q0 killed $q0 killed $z0
+; CHECK-NEXT:    adrp x8, .LCPI9_0
+; CHECK-NEXT:    ldr q0, [x8, :lo12:.LCPI9_0]
 ; CHECK-NEXT:    ret
   ret <4 x i32> <i32 16, i32 17, i32 18, i32 19>
 }
@@ -105,9 +104,8 @@ define <4 x i32> @v4i32_out_range_start() #0 {
 define <4 x i32> @v4i32_out_range_step() #0 {
 ; CHECK-LABEL: v4i32_out_range_step:
 ; CHECK:       // %bb.0:
-; CHECK-NEXT:    mov w8, #16 // =0x10
-; CHECK-NEXT:    index z0.s, #0, w8
-; CHECK-NEXT:    // kill: def $q0 killed $q0 killed $z0
+; CHECK-NEXT:    adrp x8, .LCPI10_0
+; CHECK-NEXT:    ldr q0, [x8, :lo12:.LCPI10_0]
 ; CHECK-NEXT:    ret
   ret <4 x i32> <i32 0, i32 16, i32 32, i32 48>
 }
@@ -116,10 +114,8 @@ define <4 x i32> @v4i32_out_range_step() #0 {
 define <4 x i32> @v4i32_out_range_start_step() #0 {
 ; CHECK-LABEL: v4i32_out_range_start_step:
 ; CHECK:       // %bb.0:
-; CHECK-NEXT:    mov w8, #16 // =0x10
-; CHECK-NEXT:    index z0.s, #0, w8
-; CHECK-NEXT:    add z0.s, z0.s, #16 // =0x10
-; CHECK-NEXT:    // kill: def $q0 killed $q0 killed $z0
+; CHECK-NEXT:    adrp x8, .LCPI11_0
+; CHECK-NEXT:    ldr q0, [x8, :lo12:.LCPI11_0]
 ; CHECK-NEXT:    ret
   ret <4 x i32> <i32 16, i32 32, i32 48, i32 64>
 }



More information about the llvm-commits mailing list