compiler: Check block-entry register assignment
972c0b107bf4c4063a1c065e6aaddf1449f91138ebbac9a1643e1f4d57d309a5
1 parent
ccb91cef
lib/std/lang/gen/regalloc/assign.rad
+42 -29
| 102 | 102 | while let reg = il::nextReg(&mut registers, &instr) { |
|
| 103 | 103 | recordLastUse(reg, &mut lastUse[..], i); |
|
| 104 | 104 | } |
|
| 105 | 105 | } |
|
| 106 | 106 | ||
| 107 | - | // Reset for new block. |
|
| 108 | - | set current.n = 0; |
|
| 109 | - | bitset::clearAll(usedRegs); |
|
| 110 | - | ||
| 111 | - | // Mark all live-in values' registers as used. |
|
| 112 | - | // This ensures we don't reuse registers for values that flow in |
|
| 113 | - | // from predecessors, even at merge points with multiple predecessors. |
|
| 114 | - | // Values that are live-in but have no assignment yet (e.g. callee-saved |
|
| 115 | - | // function parameters not used before a phi block) are allocated now to |
|
| 116 | - | // prevent conflicts with block parameters. |
|
| 117 | - | let mut liveInIter = bitset::iter(liveness::liveInRow(live, b)); |
|
| 118 | - | while let ssaReg = bitset::iterNext(&mut liveInIter, liveness::liveInRow(live, b)) { |
|
| 119 | - | let reg = il::Reg { n: ssaReg }; |
|
| 120 | - | if not spill::isSpilled(spillInfo, reg) { |
|
| 121 | - | if let phys = assignments[ssaReg] { |
|
| 122 | - | bitset::put(usedRegs, *phys as u32); |
|
| 123 | - | rmapSet(&mut current, ssaReg, phys); |
|
| 124 | - | } else { |
|
| 125 | - | set assignments[ssaReg] = rallocReg(&mut current, usedRegs, ssaReg, allocatable, config.calleeSaved, spillInfo); |
|
| 126 | - | } |
|
| 127 | - | } |
|
| 128 | - | } |
|
| 129 | - | ||
| 130 | - | // Allocate block parameters. |
|
| 131 | - | for p in block.params { |
|
| 132 | - | if p.value.n < maxReg and not spill::isSpilled(spillInfo, p.value) { |
|
| 133 | - | set assignments[p.value.n] = rallocReg(&mut current, usedRegs, p.value.n, allocatable, config.calleeSaved, spillInfo); |
|
| 134 | - | } |
|
| 135 | - | } |
|
| 107 | + | enterBlock(&mut current, usedRegs, liveness::liveInRow(live, b), block.params, assignments, config, spillInfo); |
|
| 136 | 108 | ||
| 137 | 109 | // Process each instruction. |
|
| 138 | 110 | for instr, i in block.instrs { |
|
| 139 | 111 | let currentRef: 'step = &mut current, usedRef = &mut usedRegs[..], |
|
| 140 | 112 | lastUseRef = &lastUse[..], liveRef = &*live, |
| 182 | 154 | assignments: &assignments[..], |
|
| 183 | 155 | usedCalleeSaved, |
|
| 184 | 156 | }; |
|
| 185 | 157 | } |
|
| 186 | 158 | ||
| 159 | + | /// Reserve live-in registers before assigning the block's parameter registers. |
|
| 160 | + | fn enterBlock 'scratch ( |
|
| 161 | + | current: &mut RegMap 'scratch, |
|
| 162 | + | usedRegs: &mut [u32], |
|
| 163 | + | liveIn: &[u32], |
|
| 164 | + | params: &[il::Param], |
|
| 165 | + | assignments: &mut [?gen::Reg], |
|
| 166 | + | config: &super::TargetConfig, |
|
| 167 | + | spillInfo: &spill::SpillInfo 'scratch, |
|
| 168 | + | ) { |
|
| 169 | + | // Reset for new block. |
|
| 170 | + | set current.n = 0; |
|
| 171 | + | bitset::clearAll(usedRegs); |
|
| 172 | + | ||
| 173 | + | // Mark all live-in values' registers as used. |
|
| 174 | + | // This ensures we don't reuse registers for values that flow in |
|
| 175 | + | // from predecessors, even at merge points with multiple predecessors. |
|
| 176 | + | // Values that are live-in but have no assignment yet (e.g. callee-saved |
|
| 177 | + | // function parameters not used before a phi block) are allocated now to |
|
| 178 | + | // prevent conflicts with block parameters. |
|
| 179 | + | let mut liveInIter = bitset::iter(liveIn); |
|
| 180 | + | while let ssaReg = bitset::iterNext(&mut liveInIter, liveIn) { |
|
| 181 | + | let reg = il::Reg { n: ssaReg }; |
|
| 182 | + | if not spill::isSpilled(spillInfo, reg) { |
|
| 183 | + | if let phys = assignments[ssaReg] { |
|
| 184 | + | bitset::put(usedRegs, *phys as u32); |
|
| 185 | + | rmapSet(current, ssaReg, phys); |
|
| 186 | + | } else { |
|
| 187 | + | set assignments[ssaReg] = rallocReg(current, usedRegs, ssaReg, config.allocatable, config.calleeSaved, spillInfo); |
|
| 188 | + | } |
|
| 189 | + | } |
|
| 190 | + | } |
|
| 191 | + | ||
| 192 | + | // Allocate block parameters. |
|
| 193 | + | for p in params { |
|
| 194 | + | if p.value.n < assignments.len and not spill::isSpilled(spillInfo, p.value) { |
|
| 195 | + | set assignments[p.value.n] = rallocReg(current, usedRegs, p.value.n, config.allocatable, config.calleeSaved, spillInfo); |
|
| 196 | + | } |
|
| 197 | + | } |
|
| 198 | + | } |
|
| 199 | + | ||
| 187 | 200 | /// Create an empty register map. |
|
| 188 | 201 | fn createRegMap 'scratch (storage: &Session 'scratch) -> RegMap 'scratch throws (alloc::AllocError) { |
|
| 189 | 202 | let virtRegs = try storage.fill(0 as u32, MAX_ACTIVE); |
|
| 190 | 203 | let physRegs = try storage.fill(gen::Reg(0), MAX_ACTIVE); |
|
| 191 | 204 | return RegMap 'scratch { virtRegs, physRegs, n: 0 }; |
test/tests/regalloc.block.entry.rad
added
+29 -0
| 1 | + | //! returns: 0 |
|
| 2 | + | ||
| 3 | + | /// Compute a full-width value across a call boundary. |
|
| 4 | + | fn step(value: u64) -> u64 { |
|
| 5 | + | return value * 3 + 1; |
|
| 6 | + | } |
|
| 7 | + | ||
| 8 | + | /// Keep both input parameters live beside a merged block parameter. |
|
| 9 | + | fn combine(first: u64, second: u64, selected: bool) -> u64 { |
|
| 10 | + | let mut value: u64 = 0; |
|
| 11 | + | if selected { |
|
| 12 | + | set value = step(first) + second; |
|
| 13 | + | } else { |
|
| 14 | + | set value = step(second) + first; |
|
| 15 | + | } |
|
| 16 | + | let result = step(value); |
|
| 17 | + | return result + first + second; |
|
| 18 | + | } |
|
| 19 | + | ||
| 20 | + | /// Check both predecessor paths and repeated block-entry register allocation. |
|
| 21 | + | @default fn main() -> u32 { |
|
| 22 | + | for index in 0..32 { |
|
| 23 | + | let first = 0x100000000 + index as u64; |
|
| 24 | + | let second = 0x200000000 + 2 * index as u64; |
|
| 25 | + | assert combine(first, second, true) == 10 * first + 4 * second + 4; |
|
| 26 | + | assert combine(first, second, false) == 4 * first + 10 * second + 4; |
|
| 27 | + | } |
|
| 28 | + | return 0; |
|
| 29 | + | } |