stack_layout/ #
Stack frame layout: determines which IR values get stack slots and where.
Why this exists #
Every backend needs to assign stack slots for local variables (allocas) and SSA temporaries that don't fit in registers. A naive approach gives every value its own 8-byte slot, but this wastes hundreds of bytes per function on kernel/PostgreSQL code where macro expansion creates thousands of short-lived intermediates.
This module implements a three-tier allocation scheme that typically reduces stack usage by 40-60% compared to naive allocation.
Three-tier allocation #
-
Tier 1 (Permanent): Allocas get dedicated, non-shared slots. They're addressable memory -- the program may take their address.
-
Tier 2 (Liveness-packed): Multi-block SSA temporaries share slots based on live interval overlap. Uses greedy interval coloring with a min-heap.
-
Tier 3 (Block-local): Single-block values use intra-block greedy slot reuse. Values whose lifetimes don't overlap within a block share a slot. Different blocks' pools overlap since only one block executes at a time.
Module layout #
| File | Purpose |
|---|---|
analysis.rs |
Value use-block maps, used-value collection, dead param alloca detection |
alloca_coalescing.rs |
Escape analysis: determines which allocas can share slots |
copy_coalescing.rs |
Copy alias map and immediately-consumed value optimization |
slot_assignment.rs |
The core phases: instruction classification, tier assignment, slot packing |
inline_asm.rs |
Inline assembly clobber register scanning |
regalloc_helpers.rs |
Register allocator integration and callee-saved register filtering |
Key design decisions #
-
Escape analysis for allocas: An alloca whose address never escapes (isn't stored, passed to calls, or used in pointer arithmetic beyond GEP) can be demoted to Tier 3 block-local sharing. This is the biggest win for kernel code where many allocas are single-block temporaries.
-
Copy coalescing: When a
Copyinstruction is the sole use of its source, the destination shares the source's stack slot. This eliminates redundant slots created by phi elimination. -
Immediately-consumed optimization: When value V is defined at instruction I and consumed at instruction I+1 as the first operand, the accumulator register cache keeps it alive without needing a stack slot at all.
-
Architecture independence: The
assign_slotclosure abstracts over stack growth direction (x86 grows down, ARM/RISC-V grow up). All tier logic is shared across backends.
Pipeline (7 phases) #
- Build context: Use-block maps, def-block tracking, copy aliases, alloca coalescability, immediately-consumed analysis
- Classify instructions: Sort each value into Tier 1/2/3
- Tier 3 assignment: Block-local greedy slot reuse
- Tier 2 assignment: Liveness-based interval packing
- Finalize deferred: Convert block-local offsets to absolute offsets
- Copy alias resolution: Propagate slots from roots to aliases
- Wide value propagation: 32-bit target multi-word copy tracking