register spilling
Picture a workbench with room for only a handful of tools at a time. If a job needs more tools than fit, you have to set some down on a shelf behind you and fetch them back when you need them again. A processor's registers are that workbench, and the stack is the shelf. Register spilling is what the compiler does when there are more simultaneously-live values than registers: it 'spills' some values out to memory and reloads them later.
Spilling is the fallback inside register allocation. When the allocator cannot fit all the values that are live at once into the available registers (the program has high register pressure), it picks some values to spill: it stores them to a slot on the stack, frees their registers for other values, and inserts a load to bring each spilled value back just before its next use. Each spilled value therefore costs two extra memory operations (a store and a reload) it would not have needed if it had stayed in a register. The allocator tries to spill values that are used least or whose live ranges are longest, to minimize the added memory traffic.
It matters because spills are pure overhead — work the program does only because the chip ran out of registers — and a hot loop full of spills is markedly slower than one that fits in registers. Reading disassembly, the telltale sign is unexpected mov instructions to and from stack offsets like [rsp-8] surrounding your real computation. A caveat for honesty: spilling is not a bug and is often unavoidable; the compiler is doing the right thing given a fixed register count. The lesson for a systems programmer is more subtle — pushing too many live values at once (huge functions, over-aggressive inlining, too many local variables alive together) raises register pressure and forces spills, so sometimes simpler code with shorter live ranges runs faster than 'optimized' code that overwhelms the register file.
// when more values are live than there are registers, the allocator emits: mov [rsp-16], rbx ; spill: park a live value on the stack ... ; reuse rbx meanwhile mov rbx, [rsp-16] ; reload before the value is needed again // these two memory ops exist only because registers ran out
Spill code is the store/reload pair the allocator inserts when a value cannot stay in a register the whole time.
Spills are not a compiler bug but pure overhead from limited registers; the practical lever is register pressure — fewer simultaneously-live values (smaller functions, shorter live ranges) means fewer spills, so 'more optimization' that lengthens live ranges can backfire.