frame_slots.pl
Conservative stack-slot allocation for the Fil-C backend: temp_slots/6 assigns each temporary a frame slot, and temporaries whose live intervals are disjoint may share one.
A temporary's interval is closed and covers every write (even of a dead result), every use by an instruction and every block boundary where it is live in or out. One lowered instruction, including all its helper calls, is thus indivisible. Two temporaries share a slot only when their intervals are strictly disjoint, so a result never overwrites its operands.
| Pool | Holds |
|---|---|
pointer | p temporaries: each slot is a payload slot and its lower, allocated together |
address | derived p temporaries (lower_roots.pl): a payload slot only; the lower is their root's |
scalar | w and l temporaries, and s and d ones when the pools are shared; they never use the pointer pool. With pools own, never reused |
float | s and d temporaries when the pools are own |
wide | x temporaries: 16-byte slots for the 10 bytes of a long double |
A variadic function's hidden va_snapshot parameter is kept live for the whole invocation. Functions that call setjmp must not use this allocator (docs/setjmp.md).
Aliases maps each temp name to physical storage(Pool, Index). The pools are shared: s and d temporaries use scalar slots.
As temp_slots/7, with Pools shared, own, own(Groups) or own(Groups, Ranges). Own gives s and d temporaries a float pool of their own, so no scalar slot holds both an integer and a float temporary (register allocation, docs/perf.md item 3, takes only w/l homes), and reuses no scalar slot: each w/l temporary has its own home, so a home is one live range (docs/perf.md 3d). Own(Groups), with Groups a list of Temporary-Key pairs, pools each key's w/l temporaries among themselves, as shared pools them; key frame first. (A split choice, own's float pool with shared scalar slots, served the retired function, operands and cells modes.) Own(Groups, Ranges) is own(Groups) that binds Ranges to the live ranges, or takes them when Ranges is bound (temp_ranges/6).
As temp_slots/9, with Derived mapping each derived pointer D to its lower root R (lower_roots:derived_lowers/3): D takes an address slot, storage(address, I), with no lower of its own, and R's pointer slot, address and lower together, stays R's until D's live range ends too, so R's lowers[] entry is never reused while D is live (docs/lowers.md).
each temporary's live range, wide ones apart. They do not depend on the scalar groups, so own(Groups, Ranges) lowers again with the ranges of an earlier lowering (temps mode, docs/perf.md 3d).
block_ranges/8 leaves them out.each derived pointer's range moves to the address pool, and each root's pointer range grows to the hull of its own and its derived pointers' ranges. A derived pointer is live only inside its range, so no other temporary shares the root's slot, and so writes its lowers[] entry, while it is live.
all when every scalar temporary is pooled (shared), else an assoc from temporary to its group (own(Pairs); own has no groups, so every scalar home is its own).
Grouped lists Key-Ranges for each group but frame, in key order; Owns are the scalar ranges of temporaries in no group.
Wide slots are a pool of their own, reused as scalar slots are.
First maps each temporary to the point of its first touch in Touches, leaving out the live sets tagged Skip (in or out).
the touch groups of Blocks from point P0: live-in and phis at the block's point, each instruction's uses and definitions at its own, and the jump's uses and live-out at the exit. PrevOut is the live-out set of the block before; the live sets keep only what can move a bound (above): the live-in set less PrevOut, the live-out set less the next block's live-in set.
An own scalar temporary gets a new slot that nothing reuses: only pooled temporaries are entered in the reuse list.
One end point per physical slot, not an interference graph. Reuse only strictly disjoint intervals: results cannot overwrite their operands.