lib/std/lang/gen/regalloc.rad 2.3 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 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: Copy {
39
    /// SSA register to physical register mapping.
40
    assignments: *unsafe [?super::Reg],
41
    /// Spill slot information.
42
    spill: spill::SpillInfo,
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(
52
    func: *unsafe il::Fn,
53
    config: &TargetConfig,
54
    arena: &mut alloc::Arena
55
) -> AllocResult throws (alloc::AllocError) {
56
    // Phase 1: Liveness analysis.
57
    let live = try liveness::analyze(func, arena);
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, arena);
60
    // Phase 3: Register assignment (map SSA registers to physical registers).
61
    let assignInfo = try assign::assign(func, &live, &spillInfo, config, arena);
62
63
    return AllocResult {
64
        assignments: assignInfo.assignments,
65
        spill: spillInfo,
66
        usedCalleeSaved: assignInfo.usedCalleeSaved,
67
    };
68
}