[AArch64] CSR allocation prevents shrink-wrapping of cold call path
- Dominant language
- LLVM
- Stars
- 40.5k
- Forks
- 18.7k
- PR merge metrics
- PR metrics pending
Description
https://godbolt.org/z/3qYzTnEMK
### LLVM
Clang emits the frame record and an x19 save at function entry:
```asm
Perl_pp_gv:
stp x29, x30, [sp, #-32]!
str x19, [sp, #16]
mov x29, sp
adrp x19, PL_stack_sp
...
b.gt .LBB0_2
mov w1, #1
bl Perl_stack_grow
.LBB0_2:
...
str x0, [x19, :lo12:PL_stack_sp]
ldr x19, [sp, #16]
ldp x29, x30, [sp], #32
ret
```
The important part is that PL_stack_sp's address is kept in the callee-saved register x19:
```asm
adrp x19, PL_stack_sp
...
str x0, [x19, :lo12:PL_stack_sp]
```
This creates a CSR use in the entry block. Since LLVM shrink-wrapping runs after register
allocation, the save point must dominate this x19 use, so the prologue cannot be sunk into the
cold block containing the only call.
As a result, the hot path pays for:
```asm
stp x29, x30, [sp, #-32]!
str x19, [sp, #16]
mov x29, sp
...
ldr x19, [sp, #16]
ldp x29, x30, [sp], #32
ret
```
even when Perl_stack_grow is not called.
### GCC output
GCC keeps the hot path frame-free. It uses a caller-saved register for the PL_stack_sp address:
```asm
Perl_pp_gv:
adrp x2, PL_stack_sp
...
ble .L14
...
str x3, [x2, #:lo12:PL_stack_sp]
ret
```
The frame record is sunk into the cold block containing the call:
```asm
.L14:
mov w1, 1
stp x29, x30, [sp, -16]!
mov x29, sp
bl Perl_stack_grow
...
adrp x2, PL_stack_sp
...
ldp x29, x30, [sp], 16
str x3, [x2, #:lo12:PL_stack_sp]
ret
```
So GCC chooses caller-saved registers on the hot path and rematerializes the PL_stack_sp address after the call on the cold path, instead of keeping it live in a CSR across the call.
### Expected behavior
LLVM should ideally generate code closer to GCC here:
- avoid assigning the PL_stack_sp address/base to a callee-saved register when caller-saved registers are available on the hot path;
- prefer rematerializing the global address after the cold call over keeping it live in a CSR across the call;
- allow the hot path to remain frame-free;
- allow shrink-wrapping to sink the frame record and LR save into the cold block containing the only call.
Contributor guide
Research direction
Start with the Godbolt example and compare the LLVM and GCC AArch64 output shown in the issue. Trace LLVM's register allocation and post-allocation shrink-wrapping decisions around the x19 callee-saved register use; done means the hot path avoids the unnecessary frame and the cold call path can receive the sunk frame and LR save.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- perl
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100