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
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(¤t, 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] { |