lib/std/lang/gen/regalloc.rad 2.4 KiB 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 flow;
19
export mod spill;
20
export mod assign;
21
22
use std::lang::il::published;
23
use std::lang::alloc;
24
25
/// Target configuration for register allocation.
26
export record TargetConfig: Copy {
27
    /// List of allocatable physical registers.
28
    /// Order determines allocation preference.
29
    allocatable: *[super::Reg],
30
    /// Function argument registers.
31
    argRegs: *[super::Reg],
32
    /// Callee-saved registers.
33
    calleeSaved: *[super::Reg],
34
    /// Size of a spill slot in bytes.
35
    slotSize: u32,
36
}
37
38
/// Complete register allocation result.
39
export record AllocResult: 'scratch + Copy {
40
    /// SSA register to physical register mapping.
41
    assignments: &'scratch [?super::Reg],
42
    /// Spill slot information.
43
    spill: spill::SpillInfo 'scratch,
44
    /// Bitmask of used callee-saved registers.
45
    usedCalleeSaved: u32,
46
}
47
48
/// Run register allocation on a function.
49
///
50
/// Returns a mapping from SSA registers to physical registers, plus
51
/// spill information.
52
export fn allocate 'input 'scratch (
53
    func: &published::Function 'input,
54
    config: &TargetConfig,
55
    storage: &Session 'scratch
56
) -> AllocResult 'scratch throws (alloc::AllocError) {
57
    // Phase 1: Liveness analysis.
58
    let live = try liveness::analyze(func, storage);
59
    // Phase 2: Spill analysis (determine which values need stack slots).
60
    let spillInfo = try spill::analyze(func, &live, config.allocatable.len, config.calleeSaved.len, config.slotSize, storage);
61
    // Phase 3: Register assignment (map SSA registers to physical registers).
62
    let assignInfo = try assign::assign(func, &live, &spillInfo, config, storage);
63
64
    return AllocResult 'scratch {
65
        assignments: assignInfo.assignments,
66
        spill: spillInfo,
67
        usedCalleeSaved: assignInfo.usedCalleeSaved,
68
    };
69
}