[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