compiler/
lib/
examples/
std/
arch/
char/
collections/
lang/
alloc/
ast/
gen/
bitset/
regalloc/
bitset.rad
4.6 KiB
data.rad
8.6 KiB
labels.rad
2.4 KiB
regalloc.rad
2.4 KiB
types.rad
594 B
il/
module/
parser/
resolver/
scanner/
alloc.rad
7.1 KiB
ast.rad
26.7 KiB
gen.rad
513 B
il.rad
20.3 KiB
lower.rad
315.6 KiB
module.rad
14.9 KiB
package.rad
1.3 KiB
parser.rad
91.2 KiB
resolver.rad
452.9 KiB
scanner.rad
17.9 KiB
sexpr.rad
6.7 KiB
strings.rad
2.2 KiB
types.rad
1.6 KiB
sys/
arch.rad
68 B
char.rad
855 B
collections.rad
39 B
fmt.rad
8.3 KiB
intrinsics.rad
467 B
io.rad
1.7 KiB
lang.rad
276 B
mem.rad
2.3 KiB
sys.rad
179 B
testing.rad
2.4 KiB
tests.rad
15.7 KiB
vec.rad
3.2 KiB
std.rad
281 B
scripts/
seed/
sublime/
test/
vim/
.gitignore
336 B
.gitsigners
112 B
CONTRIBUTING
2.1 KiB
LICENSE
1.1 KiB
Makefile
5.4 KiB
README
2.5 KiB
STYLE
2.5 KiB
std.lib
1.5 KiB
std.lib.test
662 B
lib/std/lang/gen/regalloc.rad
raw
| 1 | //! Register allocator. |
| 2 | //! |
| 3 | //! Coordinates the two phases of register allocation: |
| 4 | //! 1. `spill`: Determine spill slots and register class constraints. |
| 5 | //! 2. `assign`: Map SSA values to physical registers. |
| 6 | //! |
| 7 | //! Spill pass focuses on *what* to spill and call-clobber policy. |
| 8 | //! Assign pass focuses on *where* to put values. |
| 9 | //! Neither pass can fail if the other did its job correctly. |
| 10 | //! |
| 11 | //! [`il`] -> [`liveness`] -> [`spill`] -> [`assign`] -> [`AllocResult`]. |
| 12 | //! |
| 13 | //! Note that the IL is not modified. The allocator produces a mapping that |
| 14 | //! instruction selection uses to emit physical registers. Spilled values are |
| 15 | //! handled by [`isel`] inserting loads/stores. |
| 16 | |
| 17 | export mod liveness; |
| 18 | export mod spill; |
| 19 | export mod assign; |
| 20 | |
| 21 | use std::lang::il; |
| 22 | use std::lang::alloc; |
| 23 | |
| 24 | /// Target configuration for register allocation. |
| 25 | export record TargetConfig: Copy { |
| 26 | /// List of allocatable physical registers. |
| 27 | /// Order determines allocation preference. |
| 28 | allocatable: *[super::Reg], |
| 29 | /// Function argument registers. |
| 30 | argRegs: *[super::Reg], |
| 31 | /// Callee-saved registers. |
| 32 | calleeSaved: *[super::Reg], |
| 33 | /// Size of a spill slot in bytes. |
| 34 | slotSize: u32, |
| 35 | } |
| 36 | |
| 37 | /// Complete register allocation result. |
| 38 | export record AllocResult: 'scratch + Copy { |
| 39 | /// SSA register to physical register mapping. |
| 40 | assignments: &'scratch [?super::Reg], |
| 41 | /// Spill slot information. |
| 42 | spill: spill::SpillInfo 'scratch, |
| 43 | /// Bitmask of used callee-saved registers. |
| 44 | usedCalleeSaved: u32, |
| 45 | } |
| 46 | |
| 47 | /// Run register allocation on a function. |
| 48 | /// |
| 49 | /// Returns a mapping from SSA registers to physical registers, plus |
| 50 | /// spill information. |
| 51 | export unsafe fn allocate 'scratch ( |
| 52 | func: &il::Fn, |
| 53 | config: &TargetConfig, |
| 54 | storage: &Session 'scratch |
| 55 | ) -> AllocResult 'scratch throws (alloc::AllocError) { |
| 56 | // Phase 1: Liveness analysis. |
| 57 | let live = try liveness::analyze(func, storage); |
| 58 | // Phase 2: Spill analysis (determine which values need stack slots). |
| 59 | let spillInfo = try spill::analyze(func, &live, config.allocatable.len, config.calleeSaved.len, config.slotSize, storage); |
| 60 | // Phase 3: Register assignment (map SSA registers to physical registers). |
| 61 | let assignInfo = try assign::assign(func, &live, &spillInfo, config, storage); |
| 62 | |
| 63 | return AllocResult 'scratch { |
| 64 | assignments: assignInfo.assignments, |
| 65 | spill: spillInfo, |
| 66 | usedCalleeSaved: assignInfo.usedCalleeSaved, |
| 67 | }; |
| 68 | } |