Expand description
The frame: what a function’s stack looks like while it runs.
Design: spec/10-backend.md section 10.7.
This is worked out after register allocation and not before, because the largest area in most frames is the spill slots and nothing knows how many of those there are until the allocator has finished running out of registers. It is worked out from the rewritten function rather than from the assignment alone, because the rewrite is what decides which scratch registers a reload uses, and a scratch register a call preserves is one the prologue has to save.
§What is in one
Section 10.7 lists the areas and this is the order they are in, from the stack pointer upward, which is the order of increasing address on every machine here.
incoming stack arguments the caller wrote these and they are above everything
return address the call instruction pushed it, on a machine that does
saved frame pointer when the function keeps one
saved general purpose regs pushed, one word each
saved vector registers stored rather than pushed, since no machine here pushes one
stack protector canary when the function has one, above everything a local reaches
locals what an alloca becomes, widest alignment first
spill slots one for every value the allocator ran out of registers for
outgoing argument area at the bottom, because a call reads its stack arguments from
the stack pointer upwardEvery offset reported here is from the stack pointer as it stands in the body of the function,
which is after the prologue and before the epilogue. That is the one base register always
available. A frame pointer is a second way to reach the same bytes and the prologue is what
knows the distance between the two, so nothing here reports an offset from it. There are two
exceptions and Frame::incoming is one of them, because the bytes it reports are the caller’s
rather than this function’s, which is the one part of the picture a realigned frame loses sight
of. It says which register it counted from. The other is a frame that grows, which is the next
section and where the stack pointer stops being a base register at all.
§Where the alignment comes from
A call has to leave the stack pointer on a multiple of the convention’s alignment, so a
function’s own frame is what puts it back: the call that reached this function pushed a return
address and left the stack pointer one word off, and the prologue’s pushes either fix that or
make it worse depending on how many there are. The size the prologue subtracts is therefore not
the size of the areas. It is whatever brings the stack pointer back to a multiple of the
alignment given the pushes in front of it, which is the arithmetic in Frame::of.
§The red zone
A leaf function may use the bytes below the stack pointer without moving it, which is what
red_zone on a convention says and what makes a small leaf function’s prologue and epilogue
empty. Then the offsets are negative, which is why they are signed, and the areas are in the
same order as ever, below the line rather than above it. Anything that calls, or is too big for
the zone, or wants more alignment than the stack pointer has for free, moves the stack pointer.
§Realignment
A local wanting more alignment than a call leaves the stack pointer with cannot be placed by
arithmetic, because nothing in the frame knows what the caller’s stack pointer was a multiple
of. The prologue has to force it, and forcing it destroys the only record of where the caller’s
stack was, so a realigned frame needs a frame pointer and the distance from the body’s stack
pointer to the incoming arguments stops being a constant. Frame::realign is where that is
reported and it is why Frame::incoming answers from the frame pointer in such a frame and
from the stack pointer in every other one.
§Growing
A variable length array is bytes the function takes off the stack pointer where the declaration
stands, so in a function that has one the stack pointer is in a different place in the middle of
the body than it was at the top of it. Every other offset in the frame was a distance from the
stack pointer, and a distance from a register that moves is not a distance, so in a frame like
this they are all distances from the frame pointer instead. That is what Layout::grows says
and Frame::grows reports, and it is why such a frame keeps a frame pointer whatever the
flags asked for, the same way a realigned one does and for a version of the same reason.
Three other things follow from it. The red zone is gone, because the zone is the bytes below the
stack pointer and the first thing an array like this does is move the stack pointer down over
them. The frame asks for the convention’s alignment even when nothing in it wanted that much, so
that the stack pointer is on a multiple of it when the body starts and stays on one as each
array rounds its own size up. And the bytes the array hands out start above the outgoing
argument area rather than at the stack pointer, because that area stays at the bottom of the
frame wherever the bottom has moved to, which is what Frame::below is for.
Realigning and growing together is the one combination that is not here. After the prologue has forced an alignment the distance from the frame pointer to the body’s stack pointer is already not a constant, so there is no register left for the rest of the frame to be counted from, and what fixes that is a second pointer held for the purpose. The lowering refuses that pair rather than this guessing at it.
Structs§
- Frame
- What a function’s stack looks like while it runs.
- Incoming
- Where the arguments the caller passed on the stack are, and which register reaches them.
- Layout
- Everything about a function’s frame that does not come out of its allocation.
- Local
- A piece of memory the function needs for its own use, which is what an
allocabecomes. - Save
- One register the prologue puts away in the frame, and where in the frame it goes.
Functions§
- widths
- How many bytes each of an allocation’s spill slots takes on the stack.