[llvm] [AArch64] Optimise materialisation of large stack offset calculations (PR #201856)
Nathan Corbyn via llvm-commits
llvm-commits at lists.llvm.org
Fri Jun 12 06:49:48 PDT 2026
https://github.com/cofibrant updated https://github.com/llvm/llvm-project/pull/201856
>From b04089a82f1df3ce2aebcf397ac056fca3276061 Mon Sep 17 00:00:00 2001
From: Nathan Corbyn <n_corbyn at apple.com>
Date: Fri, 5 Jun 2026 15:39:44 +0100
Subject: [PATCH 1/3] [AArch64](TestOnly) Add test for forcing large stack
offset calculation
---
.../CodeGen/AArch64/large-stack-offset-calcs.mir | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
create mode 100644 llvm/test/CodeGen/AArch64/large-stack-offset-calcs.mir
diff --git a/llvm/test/CodeGen/AArch64/large-stack-offset-calcs.mir b/llvm/test/CodeGen/AArch64/large-stack-offset-calcs.mir
new file mode 100644
index 0000000000000..9edee4122de3d
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/large-stack-offset-calcs.mir
@@ -0,0 +1,16 @@
+# RUN: llc -mtriple=arm64-apple-ios -run-pass=prologepilog %s -o - | FileCheck %s
+---
+name: large_stack_offset_calc
+tracksRegLiveness: true
+stack:
+ - { id: 0, size: 32768, alignment: 8 }
+body: |
+ ; CHECK-LABEL: name: large_stack_offset_calc
+ ; CHECK: $[[BASE:x[0-9]+]] = ADDXri $sp, 2, 12
+ ; CHECK-NEXT: $[[BASE]] = ADDXri $[[BASE]], 2544, 0
+ ; CHECK-NEXT: STRXui $x0, killed $[[BASE]], 4095
+ bb.0:
+ liveins: $x0
+ STRXui $x0, %stack.0, 5437 :: (store (s64))
+ RET_ReallyLR
+...
>From 46075daa2f2e827c8a9556bf7f6701aaffaf0adc Mon Sep 17 00:00:00 2001
From: Nathan Corbyn <n_corbyn at apple.com>
Date: Fri, 5 Jun 2026 15:41:32 +0100
Subject: [PATCH 2/3] [AArch64] Optimise materialisation of large stack offset
calculations
---
llvm/lib/Target/AArch64/AArch64InstrInfo.cpp | 22 +++++++++++++++++--
...ramelayout-scavengingslot-stack-hazard.mir | 4 ++--
.../AArch64/framelayout-scavengingslot.mir | 4 ++--
llvm/test/CodeGen/AArch64/irg_sp_tagp.ll | 4 ++--
.../AArch64/large-stack-offset-calcs.mir | 5 ++---
llvm/test/CodeGen/AArch64/stack-guard-sve.ll | 10 ++++-----
.../CodeGen/AArch64/swiftself-scavenger.ll | 6 ++---
7 files changed, 35 insertions(+), 20 deletions(-)
diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp b/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp
index 57ea2d2f2f992..aa32410012e85 100644
--- a/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp
+++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp
@@ -7282,8 +7282,26 @@ int llvm::isAArch64FrameOffsetLegal(const MachineInstr &MI,
if (MinOff <= NewOffset && NewOffset <= MaxOff)
Offset = Remainder;
else {
- NewOffset = NewOffset < 0 ? MinOff : MaxOff;
- Offset = Offset - (NewOffset * Scale);
+ // Try to minimise the number of instructions required to materialise the
+ // offset calculation. Specifically, for fixed offsets, if masking out the
+ // low 12 bits leaves a legal add immediate, we can realise the offset
+ // calculation with a single add instruction. Whenever this is possible,
+ // prefer this split.
+ const TargetLowering *TLI = MI.getMF()->getSubtarget().getTargetLowering();
+ int64_t HighPart = Offset & ~0xFFF;
+ int64_t LowPart = Offset & 0xFFF;
+ int64_t LowScaled = LowPart / Scale;
+ if (!IsMulVL && NewOffset >= 0 && LowPart % Scale == 0 &&
+ MinOff <= LowScaled && LowScaled <= MaxOff &&
+ TLI->isLegalAddImmediate(HighPart)) {
+ NewOffset = LowScaled;
+ Offset = HighPart;
+ } else {
+ // Default to a greedy split: take the memop immediate to be maximum /
+ // minimum expressible offset and materialise the remainder.
+ NewOffset = NewOffset < 0 ? MinOff : MaxOff;
+ Offset = Offset - (NewOffset * Scale);
+ }
}
if (EmittableOffset)
diff --git a/llvm/test/CodeGen/AArch64/framelayout-scavengingslot-stack-hazard.mir b/llvm/test/CodeGen/AArch64/framelayout-scavengingslot-stack-hazard.mir
index 52ac36f801854..5a75f540c60f6 100644
--- a/llvm/test/CodeGen/AArch64/framelayout-scavengingslot-stack-hazard.mir
+++ b/llvm/test/CodeGen/AArch64/framelayout-scavengingslot-stack-hazard.mir
@@ -77,8 +77,8 @@ name: stack_hazard_streaming_compat_emergency_spill_slot
# CHECK-LABEL: name: stack_hazard_streaming_compat_emergency_spill_slot
# CHECK: bb.0:
# CHECK: STRXui killed $[[SCRATCH:x[0-9]+]], $x19, 0
-# CHECK-NEXT: $[[SCRATCH]] = ADDXri $x19, 1056, 0
-# CHECK-NEXT: STRDui $d0, killed $[[SCRATCH]], 4095
+# CHECK-NEXT: $[[SCRATCH]] = ADDXri $x19, 8, 12
+# CHECK-NEXT: STRDui $d0, killed $[[SCRATCH]], 131
# CHECK-NEXT: $[[SCRATCH]] = LDRXui $x19, 0
# CHECK: bb.1:
tracksRegLiveness: true
diff --git a/llvm/test/CodeGen/AArch64/framelayout-scavengingslot.mir b/llvm/test/CodeGen/AArch64/framelayout-scavengingslot.mir
index 390582969d026..17ae97ba58077 100644
--- a/llvm/test/CodeGen/AArch64/framelayout-scavengingslot.mir
+++ b/llvm/test/CodeGen/AArch64/framelayout-scavengingslot.mir
@@ -6,8 +6,8 @@ name: LateScavengingSlotRealignment
# CHECK-LABEL: name: LateScavengingSlotRealignment
# CHECK: bb.0:
# CHECK: STRXui killed $[[SCRATCH:x[0-9]+]], $sp, 0
-# CHECK-NEXT: $[[SCRATCH]] = ADDXri $sp, 40, 0
-# CHECK-NEXT: STRXui $x0, killed $[[SCRATCH]], 4095
+# CHECK-NEXT: $[[SCRATCH]] = ADDXri $sp, 8, 12
+# CHECK-NEXT: STRXui $x0, killed $[[SCRATCH]], 4
# CHECK-NEXT: $[[SCRATCH]] = LDRXui $sp, 0
# CHECK: bb.1:
tracksRegLiveness: true
diff --git a/llvm/test/CodeGen/AArch64/irg_sp_tagp.ll b/llvm/test/CodeGen/AArch64/irg_sp_tagp.ll
index 4fa96c771a330..b6d32c59a1f19 100644
--- a/llvm/test/CodeGen/AArch64/irg_sp_tagp.ll
+++ b/llvm/test/CodeGen/AArch64/irg_sp_tagp.ll
@@ -37,8 +37,8 @@ define dso_local void @huge_allocas() {
entry:
; CHECK-LABEL: huge_allocas:
; CHECK: irg x1, sp{{$}}
-; CHECK: add [[TMP:x[0-9]+]], x1, #3088
-; CHECK: addg x0, [[TMP]], #1008, #1
+; CHECK: add [[TMP:x[0-9]+]], x1, #1, lsl #12
+; CHECK: addg x0, [[TMP]], #0, #1
; CHECK: bl use2
%a = alloca i8, i64 4096, align 16
%b = alloca i8, i64 4096, align 16
diff --git a/llvm/test/CodeGen/AArch64/large-stack-offset-calcs.mir b/llvm/test/CodeGen/AArch64/large-stack-offset-calcs.mir
index 9edee4122de3d..bae954a7ad3d8 100644
--- a/llvm/test/CodeGen/AArch64/large-stack-offset-calcs.mir
+++ b/llvm/test/CodeGen/AArch64/large-stack-offset-calcs.mir
@@ -6,9 +6,8 @@ stack:
- { id: 0, size: 32768, alignment: 8 }
body: |
; CHECK-LABEL: name: large_stack_offset_calc
- ; CHECK: $[[BASE:x[0-9]+]] = ADDXri $sp, 2, 12
- ; CHECK-NEXT: $[[BASE]] = ADDXri $[[BASE]], 2544, 0
- ; CHECK-NEXT: STRXui $x0, killed $[[BASE]], 4095
+ ; CHECK: $[[BASE:x[0-9]+]] = ADDXri $sp, 10, 12
+ ; CHECK-NEXT: STRXui $x0, killed $[[BASE]], 317
bb.0:
liveins: $x0
STRXui $x0, %stack.0, 5437 :: (store (s64))
diff --git a/llvm/test/CodeGen/AArch64/stack-guard-sve.ll b/llvm/test/CodeGen/AArch64/stack-guard-sve.ll
index e719e6d9d25b2..644ddc9ee4bc2 100644
--- a/llvm/test/CodeGen/AArch64/stack-guard-sve.ll
+++ b/llvm/test/CodeGen/AArch64/stack-guard-sve.ll
@@ -151,10 +151,9 @@ entry:
; CHECK: addvl sp, sp, #-2
; Stack guard is placed below the SVE stack area (and above all fixed-width objects)
-; CHECK-DAG: add [[STACK_GUARD_SPILL_PART_LOC:x[0-9]+]], sp, #8, lsl #12
-; CHECK-DAG: add [[STACK_GUARD_SPILL_PART_LOC]], [[STACK_GUARD_SPILL_PART_LOC]], #16
+; CHECK-DAG: add [[STACK_GUARD_SPILL_PART_LOC:x[0-9]+]], sp, #16, lsl #12
; CHECK-DAG: ldr [[STACK_GUARD:x[0-9]+]], [{{x[0-9]+}}, :lo12:__stack_chk_guard]
-; CHECK-DAG: str [[STACK_GUARD]], [[[STACK_GUARD_SPILL_PART_LOC]], #32760]
+; CHECK-DAG: str [[STACK_GUARD]], [[[STACK_GUARD_SPILL_PART_LOC]], #8]
; char_arr is below the stack guard
; CHECK-DAG: add [[CHAR_ARR_LOC:x[0-9]+]], sp, #16, lsl #12
@@ -206,9 +205,8 @@ entry:
; CHECK-DAG: str [[STACK_GUARD]], [[[STACK_GUARD_POS]]]
; char_arr is below the SVE stack area
-; CHECK-DAG: add [[CHAR_ARR:x[0-9]+]], sp, #15, lsl #12 // =61440
-; CHECK-DAG: add [[CHAR_ARR]], [[CHAR_ARR]], #9
-; CHECK-DAG: strb wzr, [[[CHAR_ARR]], #4095]
+; CHECK-DAG: add [[CHAR_ARR:x[0-9]+]], sp, #16, lsl #12 // =65536
+; CHECK-DAG: strb wzr, [[[CHAR_ARR]], #8]
; large1 is accessed via a virtual base register
; CHECK-DAG: add [[LARGE1:x[0-9]+]], sp, #8, lsl #12
diff --git a/llvm/test/CodeGen/AArch64/swiftself-scavenger.ll b/llvm/test/CodeGen/AArch64/swiftself-scavenger.ll
index 030593986be4a..a19cc12150924 100644
--- a/llvm/test/CodeGen/AArch64/swiftself-scavenger.ll
+++ b/llvm/test/CodeGen/AArch64/swiftself-scavenger.ll
@@ -3,8 +3,8 @@
; CSR spill for the values used by the swiftself parameter.
; CHECK-LABEL: func:
; CHECK: str [[REG:x[0-9]+]], [sp]
-; CHECK: add [[REG]], sp, #248
-; CHECK: str xzr, [{{\s*}}[[REG]], #32760]
+; CHECK: add [[REG]], sp, #8, lsl #12
+; CHECK: str xzr, [{{\s*}}[[REG]], #240]
; CHECK: ldr [[REG]], [sp]
target triple = "arm64-apple-ios"
@@ -75,7 +75,7 @@ bb:
store volatile i64 %v23, ptr @ptr64, align 8
store volatile i64 %v24, ptr @ptr64, align 8
store volatile i64 %v25, ptr @ptr64, align 8
-
+
; use swiftself parameter late so it stays alive throughout the function.
store volatile ptr %arg, ptr @ptr8
ret void
>From 5855c6bdd2904e3a6abc4a0cf979469c444107da Mon Sep 17 00:00:00 2001
From: Nathan Corbyn <n_corbyn at apple.com>
Date: Fri, 12 Jun 2026 14:49:22 +0100
Subject: [PATCH 3/3] Remove appeal to `TLI`
---
llvm/lib/Target/AArch64/AArch64InstrInfo.cpp | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp b/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp
index aa32410012e85..5a2732e7f8945 100644
--- a/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp
+++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp
@@ -7287,13 +7287,12 @@ int llvm::isAArch64FrameOffsetLegal(const MachineInstr &MI,
// low 12 bits leaves a legal add immediate, we can realise the offset
// calculation with a single add instruction. Whenever this is possible,
// prefer this split.
- const TargetLowering *TLI = MI.getMF()->getSubtarget().getTargetLowering();
int64_t HighPart = Offset & ~0xFFF;
int64_t LowPart = Offset & 0xFFF;
int64_t LowScaled = LowPart / Scale;
if (!IsMulVL && NewOffset >= 0 && LowPart % Scale == 0 &&
MinOff <= LowScaled && LowScaled <= MaxOff &&
- TLI->isLegalAddImmediate(HighPart)) {
+ AArch64_AM::isLegalArithImmed(HighPart)) {
NewOffset = LowScaled;
Offset = HighPart;
} else {
More information about the llvm-commits
mailing list