[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