compiler: Check reverse block pressure traversal
a000c3ac545ea1e2807fcef5de886bf1886471708b54be259e87a28e94078ad8
1 parent
5d3e0414
lib/std/lang/gen/regalloc/liveness/tests.rad
+41 -0
| 186 | 186 | assert info.slots[0] == 0; |
|
| 187 | 187 | } |
|
| 188 | 188 | } |
|
| 189 | 189 | } |
|
| 190 | 190 | ||
| 191 | + | /// Cross-call pressure excludes the result and spills equal-cost values in order. |
|
| 192 | + | @test unsafe fn testCrossCallPressure() throws (testing::TestError) { |
|
| 193 | + | let first = il::Reg { n: 0 }; |
|
| 194 | + | let second = il::Reg { n: 33 }; |
|
| 195 | + | let result = il::Reg { n: 66 }; |
|
| 196 | + | let sum = il::Reg { n: 67 }; |
|
| 197 | + | let total = il::Reg { n: 68 }; |
|
| 198 | + | let params = [il::Param { value: first, type: il::Type::W64 }, |
|
| 199 | + | il::Param { value: second, type: il::Type::W64 }]; |
|
| 200 | + | let args = [il::Val::Reg(first), il::Val::Reg(second)]; |
|
| 201 | + | let mut instructions = [ |
|
| 202 | + | il::Instr::Call { retTy: il::Type::W64, dst: result, |
|
| 203 | + | func: il::Val::FnAddr("callee"), args: &args[..] }, |
|
| 204 | + | il::Instr::BinOp { op: il::BinOp::Add, typ: il::Type::W64, dst: sum, |
|
| 205 | + | a: il::Val::Reg(first), b: il::Val::Reg(second) }, |
|
| 206 | + | il::Instr::BinOp { op: il::BinOp::Add, typ: il::Type::W64, dst: total, |
|
| 207 | + | a: il::Val::Reg(sum), b: il::Val::Reg(result) }, |
|
| 208 | + | il::Instr::Ret { val: il::Val::Reg(total) }, |
|
| 209 | + | ]; |
|
| 210 | + | let blocks = [block("entry", &mut instructions[..]), block("empty", &mut [])]; |
|
| 211 | + | let function = il::Fn { name: "crossing", params: ¶ms[..], returnType: il::Type::W64, |
|
| 212 | + | isExtern: false, isLeaf: false, blocks: &blocks[..] }; |
|
| 213 | + | for capacity in [0 as u32, 1, 2] { |
|
| 214 | + | static DATA: [u8; 8192] = [0; 8192]; |
|
| 215 | + | let mut arena = alloc::new(&mut DATA[..]); |
|
| 216 | + | use arena as analysis in { |
|
| 217 | + | let live = try! super::analyze(&function, &analysis); |
|
| 218 | + | let info = try! spill::analyze(&function, &live, 8, capacity, 8, &analysis); |
|
| 219 | + | assert spill::isSpilled(&info, first) == (capacity < 2); |
|
| 220 | + | assert spill::isSpilled(&info, second) == (capacity == 0); |
|
| 221 | + | assert not spill::isSpilled(&info, result); |
|
| 222 | + | assert not spill::isSpilled(&info, sum); |
|
| 223 | + | assert not spill::isSpilled(&info, total); |
|
| 224 | + | assert bitset::contains(info.calleeClass, first.n) == (capacity == 2); |
|
| 225 | + | assert bitset::contains(info.calleeClass, second.n) == (capacity > 0); |
|
| 226 | + | assert not bitset::contains(info.calleeClass, result.n); |
|
| 227 | + | assert info.frameSize == ((2 - capacity) * 8) as i32; |
|
| 228 | + | } |
|
| 229 | + | } |
|
| 230 | + | } |
|
| 231 | + | ||
| 191 | 232 | /// Function and block parameter extents receive the same overflow checks. |
|
| 192 | 233 | @test unsafe fn testParameterRegisterExtent() throws (testing::TestError) { |
|
| 193 | 234 | for index in [16384 as u32, 0xffffffff] { |
|
| 194 | 235 | for isFunction in [false, true] { |
|
| 195 | 236 | let parameters = [il::Param { value: il::Reg { n: index }, type: il::Type::W64 }]; |
lib/std/lang/gen/regalloc/spill.rad
+43 -27
| 109 | 109 | let block = &func.blocks[b]; |
|
| 110 | 110 | ||
| 111 | 111 | // Start with live-out set. |
|
| 112 | 112 | bitset::copy(scratch, liveness::liveOutRow(live, b)); |
|
| 113 | 113 | ||
| 114 | - | // Walk instructions backwards. |
|
| 115 | - | let mut i = block.instrs.len; |
|
| 116 | - | while i > 0 { |
|
| 117 | - | set i -= 1; |
|
| 118 | - | let instr = block.instrs[i]; |
|
| 114 | + | try limitBlockPressure(block.instrs, scratch, spilled, costs, calleeClass, numRegs, numCalleeSaved); |
|
| 115 | + | } |
|
| 119 | 116 | ||
| 120 | - | // Limit register pressure before processing this instruction. |
|
| 121 | - | try limitPressure(scratch, spilled, costs, numRegs); |
|
| 117 | + | let frameSize = try finishSpills(slots, spilled, calleeClass, numCalleeSaved, slotSize); |
|
| 118 | + | return SpillInfo 'scratch { slots: &slots[..], frameSize, calleeClass: &calleeClass[..], maxReg }; |
|
| 119 | + | } |
|
| 122 | 120 | ||
| 123 | - | // Enforce cross-call pressure at call sites. |
|
| 124 | - | if il::isCall(instr) { |
|
| 125 | - | try limitCrossCallPressure( |
|
| 126 | - | scratch, spilled, costs, |
|
| 127 | - | calleeClass, numCalleeSaved, il::instrDst(instr) |
|
| 128 | - | ); |
|
| 129 | - | } |
|
| 130 | - | // Remove definition from live set. |
|
| 131 | - | if let dst = il::instrDst(instr) { |
|
| 132 | - | bitset::clear(scratch, dst.n); |
|
| 133 | - | } |
|
| 134 | - | // Add uses to live set. |
|
| 135 | - | let mut registers = il::registers(&instr); |
|
| 136 | - | while let reg = il::nextReg(&mut registers, &instr) { |
|
| 137 | - | bitset::put(scratch, reg.n); |
|
| 121 | + | /// Limit register pressure from the block's live-out set to its entry. |
|
| 122 | + | fn limitBlockPressure( |
|
| 123 | + | instructions: &[il::Instr], |
|
| 124 | + | live: &mut [u32], |
|
| 125 | + | spilled: &mut [u32], |
|
| 126 | + | costs: &[SpillCost], |
|
| 127 | + | calleeClass: &mut [u32], |
|
| 128 | + | numRegs: u32, |
|
| 129 | + | numCalleeSaved: u32, |
|
| 130 | + | ) throws (alloc::AllocError) { |
|
| 131 | + | // Walk instructions backwards. |
|
| 132 | + | let mut i = instructions.len; |
|
| 133 | + | while i > 0 { |
|
| 134 | + | set i -= 1; |
|
| 135 | + | let instr = &instructions[i]; |
|
| 136 | + | ||
| 137 | + | // Limit register pressure before processing this instruction. |
|
| 138 | + | try limitPressure(live, spilled, costs, numRegs); |
|
| 139 | + | ||
| 140 | + | // Enforce cross-call pressure at call sites. |
|
| 141 | + | if il::isCall(*instr) { |
|
| 142 | + | try limitCrossCallPressure( |
|
| 143 | + | live, spilled, costs, |
|
| 144 | + | calleeClass, numCalleeSaved, il::instrDst(*instr) |
|
| 145 | + | ); |
|
| 146 | + | } |
|
| 147 | + | // Remove definition from live set. |
|
| 148 | + | if let dst = il::instrDst(*instr) { |
|
| 149 | + | bitset::clear(live, dst.n); |
|
| 150 | + | } |
|
| 151 | + | // Add uses to live set. |
|
| 152 | + | let mut registers = il::registers(instr); |
|
| 153 | + | // Argument groups contain raw views into the function's IL storage. |
|
| 154 | + | unsafe { |
|
| 155 | + | while let reg = il::nextReg(&mut registers, instr) { |
|
| 156 | + | bitset::put(live, reg.n); |
|
| 138 | 157 | } |
|
| 139 | 158 | } |
|
| 140 | - | // Also limit pressure at block entry. |
|
| 141 | - | try limitPressure(scratch, spilled, costs, numRegs); |
|
| 142 | 159 | } |
|
| 143 | - | ||
| 144 | - | let frameSize = try finishSpills(slots, spilled, calleeClass, numCalleeSaved, slotSize); |
|
| 145 | - | return SpillInfo 'scratch { slots: &slots[..], frameSize, calleeClass: &calleeClass[..], maxReg }; |
|
| 160 | + | // Also limit pressure at block entry. |
|
| 161 | + | try limitPressure(live, spilled, costs, numRegs); |
|
| 146 | 162 | } |
|
| 147 | 163 | ||
| 148 | 164 | /// Enforce the global register-class limit and assign bounded stack offsets. |
|
| 149 | 165 | fn finishSpills( |
|
| 150 | 166 | slots: &mut [i32], |