regalloc: Rebuild mappings from live-in values

02871810dfd097842ea4e97035446d914c6d5e0c449f4797f5a1377a509c8fee
Live-in values and global assignments provide the register mapping
for each block. Use them directly to avoid duplicate snapshots and
per-block storage.

Assisted-by: Codex:gpt-6-astra
Alexis Sellier committed ago 1 parent 6d57411c
lib/std/lang/gen/regalloc/assign.rad +1 -28
82 82
            if not bitset::contains(&spillInfo.calleeClass, param.value.n) {
83 83
                set assignments[param.value.n] = config.argRegs[i];
84 84
            }
85 85
        }
86 86
    }
87 -
    let blkEnd = try alloc::allocSlice(arena, @sizeOf(RegMap), @alignOf(RegMap), blockCount) as *mut [RegMap];
88 -
    for i in 0..blockCount {
89 -
        set blkEnd[i] = try createRegMap(arena);
90 -
    }
91 87
    // Allocate used registers bitset (32 physical registers per 32-bit word).
92 88
    let usedRegsBits = try alloc::allocSlice(arena, @sizeOf(u32), @alignOf(u32), 1) as *mut [u32];
93 89
    let mut usedRegs = bitset::init(usedRegsBits);
94 90
95 91
    // Current register mapping.
101 97
102 98
        // Reset for new block.
103 99
        set current.n = 0;
104 100
        bitset::clearAll(&mut usedRegs);
105 101
106 -
        // Initialize from predecessor if single predecessor.
107 -
        // Only copy values that are live-in to this block.
108 -
        if block.preds.len == 1 {
109 -
            let pred = &blkEnd[block.preds[0]];
110 -
            for k in 0..pred.n {
111 -
                if bitset::contains(&live.liveIn[b], pred.virtRegs[k]) {
112 -
                    set current.virtRegs[current.n] = pred.virtRegs[k];
113 -
                    set current.physRegs[current.n] = pred.physRegs[k];
114 -
                    set current.n += 1;
115 -
                    bitset::put(&mut usedRegs, *pred.physRegs[k] as u32);
116 -
                }
117 -
            }
118 -
        }
119 -
120 102
        // Mark all live-in values' registers as used.
121 103
        // This ensures we don't reuse registers for values that flow in
122 104
        // from predecessors, even at merge points with multiple predecessors.
123 105
        // Values that are live-in but have no assignment yet (e.g. callee-saved
124 106
        // function parameters not used before a phi block) are allocated now to
127 109
        while let ssaReg = bitset::iterNext(&mut liveInIter) {
128 110
            let reg = il::Reg { n: ssaReg };
129 111
            if not spill::isSpilled(spillInfo, reg) {
130 112
                if let phys = assignments[ssaReg] {
131 113
                    bitset::put(&mut usedRegs, *phys as u32);
132 -
                    // Also track in current mapping for block-end state.
133 -
                    if rmapFind(&current, ssaReg) == nil {
134 -
                        rmapSet(&mut current, ssaReg, phys);
135 -
                    }
114 +
                    rmapSet(&mut current, ssaReg, phys);
136 115
                } else {
137 116
                    set assignments[ssaReg] = rallocReg(&mut current, &mut usedRegs, ssaReg, allocatable, config.calleeSaved, spillInfo);
138 117
                }
139 118
            }
140 119
        }
167 146
                if dst.n < maxReg and not spill::isSpilled(spillInfo, dst) {
168 147
                    set assignments[dst.n] = rallocReg(&mut current, &mut usedRegs, dst.n, allocatable, config.calleeSaved, spillInfo);
169 148
                }
170 149
            }
171 150
        }
172 -
        // Save block-end state.
173 -
        set blkEnd[b].n = current.n;
174 -
        for ri in 0..current.n {
175 -
            set blkEnd[b].virtRegs[ri] = current.virtRegs[ri];
176 -
            set blkEnd[b].physRegs[ri] = current.physRegs[ri];
177 -
        }
178 151
    }
179 152
    // Compute bitmask of used callee-saved registers.
180 153
    let mut usedCalleeSaved: u32 = 0;
181 154
    for i in 0..maxReg {
182 155
        if let phys = assignments[i] {