compiler: Check loop-weighted spill cost traversal
bf50fe77d32bcb945f576242f85356a746e69ee3ef8088aa13b4ff62896e52c2
1 parent
a000c3ac
lib/std/lang/gen/regalloc/liveness/tests.rad
+34 -0
| 186 | 186 | assert info.slots[0] == 0; |
|
| 187 | 187 | } |
|
| 188 | 188 | } |
|
| 189 | 189 | } |
|
| 190 | 190 | ||
| 191 | + | /// Loop-weighted uses retain hot values and cap large loop depths. |
|
| 192 | + | @test unsafe fn testLoopWeightedSpills() throws (testing::TestError) { |
|
| 193 | + | let first = il::Reg { n: 0 }; |
|
| 194 | + | let second = il::Reg { n: 33 }; |
|
| 195 | + | let temporary = il::Reg { n: 66 }; |
|
| 196 | + | let mut entry = [ |
|
| 197 | + | il::Instr::Copy { dst: first, val: il::Val::Imm(1) }, |
|
| 198 | + | il::Instr::Copy { dst: second, val: il::Val::Imm(2) }, |
|
| 199 | + | il::Instr::Jmp { target: 1, args: &mut [] }, |
|
| 200 | + | ]; |
|
| 201 | + | let mut body = [ |
|
| 202 | + | il::Instr::Copy { dst: temporary, val: il::Val::Reg(first) }, |
|
| 203 | + | il::Instr::Jmp { target: 2, args: &mut [] }, |
|
| 204 | + | ]; |
|
| 205 | + | let mut exit = [il::Instr::Ret { val: il::Val::Reg(second) }]; |
|
| 206 | + | for depth in [0 as u32, 1, 10, 11, 32, 0xffffffff] { |
|
| 207 | + | let mut weighted = block("body", &mut body[..]); |
|
| 208 | + | set weighted.loopDepth = depth; |
|
| 209 | + | let blocks = [block("entry", &mut entry[..]), weighted, block("exit", &mut exit[..])]; |
|
| 210 | + | let function = il::Fn { name: "weighted", params: &[], returnType: il::Type::W64, |
|
| 211 | + | isExtern: false, isLeaf: true, blocks: &blocks[..] }; |
|
| 212 | + | static DATA: [u8; 8192] = [0; 8192]; |
|
| 213 | + | let mut arena = alloc::new(&mut DATA[..]); |
|
| 214 | + | use arena as analysis in { |
|
| 215 | + | let live = try! super::analyze(&function, &analysis); |
|
| 216 | + | let info = try! spill::analyze(&function, &live, 1, 0, 8, &analysis); |
|
| 217 | + | assert spill::isSpilled(&info, first) == (depth == 0); |
|
| 218 | + | assert spill::isSpilled(&info, second) == (depth > 0); |
|
| 219 | + | assert not spill::isSpilled(&info, temporary); |
|
| 220 | + | assert info.frameSize == 8; |
|
| 221 | + | } |
|
| 222 | + | } |
|
| 223 | + | } |
|
| 224 | + | ||
| 191 | 225 | /// Cross-call pressure excludes the result and spills equal-cost values in order. |
|
| 192 | 226 | @test unsafe fn testCrossCallPressure() throws (testing::TestError) { |
|
| 193 | 227 | let first = il::Reg { n: 0 }; |
|
| 194 | 228 | let second = il::Reg { n: 33 }; |
|
| 195 | 229 | let result = il::Reg { n: 66 }; |
lib/std/lang/gen/regalloc/spill.rad
+27 -24
| 96 | 96 | // Allocate spill slots array. |
|
| 97 | 97 | let slots = try storage.fill(-1 as i32, maxReg); |
|
| 98 | 98 | // Allocate cost array. |
|
| 99 | 99 | let costs = try storage.fill(SpillCost { defs: 0, uses: 0 }, maxReg); |
|
| 100 | 100 | // Phase 1: Calculate spill costs. |
|
| 101 | - | fillCosts(func, costs); |
|
| 101 | + | for b in 0..func.blocks.len { |
|
| 102 | + | let block = &func.blocks[b]; |
|
| 103 | + | fillCosts(block.params, block.instrs, block.loopDepth, costs); |
|
| 104 | + | } |
|
| 102 | 105 | ||
| 103 | 106 | // Phase 2: Find values that exceed register pressure. |
|
| 104 | 107 | let spilled = try bitset::allocate(storage, maxReg); |
|
| 105 | 108 | let calleeClass = try bitset::allocate(storage, maxReg); |
|
| 106 | 109 | let scratch = try bitset::allocate(storage, maxReg); |
| 199 | 202 | set frameSize += slotSize as i32; |
|
| 200 | 203 | } |
|
| 201 | 204 | return frameSize; |
|
| 202 | 205 | } |
|
| 203 | 206 | ||
| 204 | - | /// Calculate spill costs for all registers, weighted by loop depth. |
|
| 205 | - | unsafe fn fillCosts(func: &il::Fn, costs: &mut [SpillCost]) { |
|
| 206 | - | for b in 0..func.blocks.len { |
|
| 207 | - | let block = &func.blocks[b]; |
|
| 208 | - | ||
| 209 | - | // Exponential weight for loop depth, capped to avoid overflow. |
|
| 210 | - | let depth = MAX_LOOP_WEIGHT if block.loopDepth > MAX_LOOP_WEIGHT else block.loopDepth; |
|
| 211 | - | let weight: u32 = 1 << depth; |
|
| 207 | + | /// Add one block's register spill costs, weighted by loop depth. |
|
| 208 | + | fn fillCosts(params: &[il::Param], instructions: &[il::Instr], loopDepth: u32, costs: &mut [SpillCost]) { |
|
| 209 | + | // Exponential weight for loop depth, capped to avoid overflow. |
|
| 210 | + | let depth = MAX_LOOP_WEIGHT if loopDepth > MAX_LOOP_WEIGHT else loopDepth; |
|
| 211 | + | let weight: u32 = 1 << depth; |
|
| 212 | 212 | ||
| 213 | - | // Count block parameter definitions. |
|
| 214 | - | for p in block.params { |
|
| 215 | - | if p.value.n < costs.len { |
|
| 216 | - | set costs[p.value.n].defs = costs[p.value.n].defs + weight; |
|
| 217 | - | } |
|
| 213 | + | // Count block parameter definitions. |
|
| 214 | + | for p in params { |
|
| 215 | + | if p.value.n < costs.len { |
|
| 216 | + | set costs[p.value.n].defs = costs[p.value.n].defs + weight; |
|
| 218 | 217 | } |
|
| 219 | - | // Count instruction defs and uses. |
|
| 220 | - | for instr in block.instrs { |
|
| 218 | + | } |
|
| 219 | + | // Count instruction defs and uses. |
|
| 220 | + | for i in 0..instructions.len { |
|
| 221 | + | let instr = &instructions[i]; |
|
| 221 | 222 | ||
| 222 | - | // Count definition. |
|
| 223 | - | if let dst = il::instrDst(instr) { |
|
| 224 | - | if dst.n < costs.len { |
|
| 225 | - | set costs[dst.n].defs = costs[dst.n].defs + weight; |
|
| 226 | - | } |
|
| 223 | + | // Count definition. |
|
| 224 | + | if let dst = il::instrDst(*instr) { |
|
| 225 | + | if dst.n < costs.len { |
|
| 226 | + | set costs[dst.n].defs = costs[dst.n].defs + weight; |
|
| 227 | 227 | } |
|
| 228 | - | // Count uses. |
|
| 229 | - | let mut registers = il::registers(&instr); |
|
| 230 | - | while let reg = il::nextReg(&mut registers, &instr) { |
|
| 228 | + | } |
|
| 229 | + | // Count uses. |
|
| 230 | + | let mut registers = il::registers(instr); |
|
| 231 | + | // Argument groups contain raw views into the function's IL storage. |
|
| 232 | + | unsafe { |
|
| 233 | + | while let reg = il::nextReg(&mut registers, instr) { |
|
| 231 | 234 | countRegUse(reg, &mut costs[..], weight); |
|
| 232 | 235 | } |
|
| 233 | 236 | } |
|
| 234 | 237 | } |
|
| 235 | 238 | } |