aarch64-pg
4.2 · the stack and the frame pointer

the stack and the frame pointer

The stack is the memory a function uses for its own working storage: saved registers, local variables, and the bookkeeping that lets control return to the caller. It grows downward, from higher addresses toward lower ones, and the stack pointer sp points at the lowest in-use byte.

The frame pointer fp, the register x29, marks a fixed point in the current function's slice of the stack, its frame. Because sp can move during a function, code reads and writes locals at steady offsets from fp instead, so each local keeps one stable address for the life of the call.

the prologue and epilogue

A function that calls other functions opens with a prologue and closes with an epilogue. The prologue saves the caller's frame pointer and the return address, then claims space for the new frame; the epilogue restores them and hands control back.

The pair instructions do this in one step. stp fp, lr, [sp, alloc]! stores fp and lr and moves sp down by alloc in one pre-indexed access, and ldp fp, lr, [sp], dealloc restores them and lifts sp back afterward. Between the two, mov fp, sp anchors the frame.

// the frame skeleton: prologue, one local by fp offset, epiloguedefine(fp, x29)define(lr, x30)define(n_r, w9)n_s = 16alloc = -(16 + 16) & -16dealloc = -alloc        .datafmt:    .string "n = %d\n"        .text        .balign 4        .global mainmain:        stp     fp, lr, [sp, alloc]!        // save fp+lr and open the frame        mov     fp, sp        mov     w0, 5        str     w0, [fp, n_s]               // store a local at fp+16        ldr     n_r, [fp, n_s]              // read it back by its offset        ldr     x0, =fmt        mov     w1, n_r        bl      printf        mov     w0, 0        ldp     fp, lr, [sp], dealloc       // restore fp+lr and close the frame        ret
Open in playground

warning

Two rules keep a frame correct. First, sp must stay 16-byte aligned at every point where another function might run, which is why the frame size is rounded with & -16. Second, a function that calls anything must save fp and lr on entry and restore them before it returns: lr holds the address to return to, and a call overwrites it, so losing it strands the function with no way back.

locals in the frame

Local variables live at fixed offsets above fp. Storing one is str w0, [fp, 16]; reading it back is ldr w1, [fp, 16]. The offsets are yours to choose, as long as they stay inside the space the prologue reserved.

The reserved size comes from the alignment formula alloc = -(16 + locals) & -16, where locals is the number of bytes the function needs. The & -16 rounds the frame up to a multiple of sixteen so the stack keeps its alignment, and the leading sixteen bytes hold the saved fp and lr pair. The two editors below allocate frames, fill locals with fixed values, and read them back by offset.

loading editor...
figure 4.2.1runnable — step it and watch the registersOpen in playground
loading editor...
figure 4.2.2runnable — step it and watch the registersOpen in playground