Commit edd2e55
Add a configurable spill-everything register allocator
Summary:
Adds `SpillAllocator`, a deliberately naive register allocator that spills every
virtual register to its own stack slot, and makes the register allocator
selectable from the command line / environment. It's meant for testing and
isolating the rest of the JIT pipeline from the optimizing linear scan
allocator.
Configuration:
- New `-X cinderx-jit-reg-alloc=<linear-scan|spill>` option (and
`CINDERX_JIT_REG_ALLOC` env var), parsed in `pyjit.cpp` into a new
`RegAllocKind` config enum. The default is `linear-scan`. Any other value
raises a `ValueError` during CinderX initialization.
- `gen_asm.cpp` selects the allocator through the `RegisterAllocator` interface
based on `getConfig().reg_alloc`.
SpillAllocator (`cinderx/Jit/lir/spill_alloc.{cpp,h}`, new `:spill-alloc`
library):
- Gives every vreg a home stack slot it keeps for the whole function, so a
value defined in one block and used in another is just re-read from the same
slot -- no live intervals, interval splitting, or cross-block location
bookkeeping.
- Rewrites each instruction to refer only to physical locations, loading
operands that must be in registers (per `getInputPhyRegUse` /
`getOutputPhyRegUse`) into caller-save scratch registers around the
instruction and leaving everything else in its slot.
- Lowers phis to copies on the incoming edges (phi-output slots are unique, so
no critical-edge splitting is needed) and strips the `kReturn` /
`kBranchToYieldExit` pseudo-terminators for PostRegAllocRewrite.
- Handles `kBind` (store the bound register into the slot), calls (operands
stay in slots; PostRegAllocRewrite applies the calling convention), and
read-modify-write `kInc`/`kDec`.
- Because nothing is ever live in a register across an instruction boundary,
caller-save registers are always free at calls and no callee-saved register
is ever clobbered.
Generators and coroutines are supported. A generator runs with the frame
pointer swapped between the machine stack and a heap-allocated generator frame,
so a value produced on one side of a `Move` into the frame-pointer register but
read on the other is stale once spilled. `handleFramePointerSwitch` carries
those values across the switch in a register: the frame-setup call's result on
the forward switch (stack -> heap), and the return-value exit phi on the
reverse switch (heap -> stack, including the extra deferred-refcount step on
free-threaded builds).
Reviewed By: DinoV
Differential Revision: D112574130
fbshipit-source-id: 06c4c72bd9ebc3480dcd67533cd13071e190d9401 parent aeb4fed commit edd2e55
5 files changed
Lines changed: 704 additions & 4 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
33 | 33 | | |
34 | 34 | | |
35 | 35 | | |
| 36 | + | |
| 37 | + | |
36 | 38 | | |
37 | 39 | | |
38 | 40 | | |
| |||
845 | 847 | | |
846 | 848 | | |
847 | 849 | | |
848 | | - | |
| 850 | + | |
| 851 | + | |
| 852 | + | |
| 853 | + | |
| 854 | + | |
| 855 | + | |
| 856 | + | |
| 857 | + | |
| 858 | + | |
| 859 | + | |
| 860 | + | |
849 | 861 | | |
850 | 862 | | |
851 | 863 | | |
852 | 864 | | |
853 | | - | |
| 865 | + | |
854 | 866 | | |
855 | | - | |
856 | | - | |
| 867 | + | |
| 868 | + | |
857 | 869 | | |
858 | 870 | | |
859 | 871 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
115 | 115 | | |
116 | 116 | | |
117 | 117 | | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
118 | 128 | | |
119 | 129 | | |
120 | 130 | | |
| |||
221 | 231 | | |
222 | 232 | | |
223 | 233 | | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
224 | 237 | | |
225 | 238 | | |
226 | 239 | | |
| |||
0 commit comments