compiler: Check spill class and stack-slot finalization
ccb91cef1699b4d7ab09b1ef73f7d2c6d43973b68c0037537d6ec2e8e188a368
1 parent
9ab18d59
lib/std/lang/gen/regalloc/liveness/tests.rad
+34 -0
| 97 | 97 | try testing::expect(bitset::contains(words, 0) == first); |
|
| 98 | 98 | try testing::expect(bitset::contains(words, 33) == second); |
|
| 99 | 99 | try testing::expect(bitset::contains(words, 66) == third); |
|
| 100 | 100 | } |
|
| 101 | 101 | ||
| 102 | + | /// Multiple spill slots preserve register order and reject total-frame overflow. |
|
| 103 | + | @test unsafe fn testMultipleSpillOffsets() throws (testing::TestError) { |
|
| 104 | + | for size in [0 as u32, 8, 0x2aaaaaaa, 0x2aaaaaab] { |
|
| 105 | + | let first = il::Reg { n: 0 }; |
|
| 106 | + | let second = il::Reg { n: 33 }; |
|
| 107 | + | let result = il::Reg { n: 66 }; |
|
| 108 | + | let mut instructions = [ |
|
| 109 | + | il::Instr::Copy { dst: first, val: il::Val::Imm(1) }, |
|
| 110 | + | il::Instr::Copy { dst: second, val: il::Val::Imm(2) }, |
|
| 111 | + | il::Instr::BinOp { op: il::BinOp::Add, typ: il::Type::W64, |
|
| 112 | + | dst: result, a: il::Val::Reg(first), b: il::Val::Reg(second) }, |
|
| 113 | + | il::Instr::Ret { val: il::Val::Reg(result) }, |
|
| 114 | + | ]; |
|
| 115 | + | let blocks = [block("entry", &mut instructions[..])]; |
|
| 116 | + | let function = il::Fn { name: "offsets", params: &[], returnType: il::Type::W64, |
|
| 117 | + | isExtern: false, isLeaf: true, blocks: &blocks[..] }; |
|
| 118 | + | static DATA: [u8; 16384] = [0; 16384]; |
|
| 119 | + | let mut arena = alloc::new(&mut DATA[..]); |
|
| 120 | + | use arena as analysis in { |
|
| 121 | + | let live = try! super::analyze(&function, &analysis); |
|
| 122 | + | let info = try spill::analyze(&function, &live, 0, 0, size, &analysis) catch { |
|
| 123 | + | assert size == 0x2aaaaaab; |
|
| 124 | + | continue; |
|
| 125 | + | }; |
|
| 126 | + | assert size <= 0x2aaaaaaa; |
|
| 127 | + | assert info.slots[0] == 0; |
|
| 128 | + | assert info.slots[33] == size as i32; |
|
| 129 | + | assert info.slots[66] == (size * 2) as i32; |
|
| 130 | + | assert info.slots[1] == -1; |
|
| 131 | + | assert info.frameSize == (size * 3) as i32; |
|
| 132 | + | } |
|
| 133 | + | } |
|
| 134 | + | } |
|
| 135 | + | ||
| 102 | 136 | /// Spill frame sizes must fit their signed byte-offset representation. |
|
| 103 | 137 | @test unsafe fn testSpillFrameExtent() throws (testing::TestError) { |
|
| 104 | 138 | for size in [0x7fffffff as u32, 0x80000000, 0xffffffff] { |
|
| 105 | 139 | let value = il::Reg { n: 0 }; |
|
| 106 | 140 | let mut instructions = [ |
lib/std/lang/gen/regalloc/spill.rad
+13 -1
| 139 | 139 | } |
|
| 140 | 140 | // Also limit pressure at block entry. |
|
| 141 | 141 | try limitPressure(scratch, spilled, costs, numRegs); |
|
| 142 | 142 | } |
|
| 143 | 143 | ||
| 144 | + | let frameSize = try finishSpills(slots, spilled, calleeClass, numCalleeSaved, slotSize); |
|
| 145 | + | return SpillInfo 'scratch { slots: &slots[..], frameSize, calleeClass: &calleeClass[..], maxReg }; |
|
| 146 | + | } |
|
| 147 | + | ||
| 148 | + | /// Enforce the global register-class limit and assign bounded stack offsets. |
|
| 149 | + | fn finishSpills( |
|
| 150 | + | slots: &mut [i32], |
|
| 151 | + | spilled: &mut [u32], |
|
| 152 | + | calleeClass: &mut [u32], |
|
| 153 | + | numCalleeSaved: u32, |
|
| 154 | + | slotSize: u32, |
|
| 155 | + | ) -> i32 throws (alloc::AllocError) { |
|
| 144 | 156 | // Phase 3: Enforce global callee-class limit. |
|
| 145 | 157 | // The per-call-site limit may leave the callee-class set larger than |
|
| 146 | 158 | // `numCalleeSaved` when different call sites keep different subsets. |
|
| 147 | 159 | // Spill excess values to guarantee the assignment phase always finds |
|
| 148 | 160 | // a callee-saved register for cross-call values. |
| 168 | 180 | throw alloc::AllocError::OutOfMemory; |
|
| 169 | 181 | } |
|
| 170 | 182 | set slots[n] = frameSize; |
|
| 171 | 183 | set frameSize += slotSize as i32; |
|
| 172 | 184 | } |
|
| 173 | - | return SpillInfo 'scratch { slots: &slots[..], frameSize, calleeClass: &calleeClass[..], maxReg }; |
|
| 185 | + | return frameSize; |
|
| 174 | 186 | } |
|
| 175 | 187 | ||
| 176 | 188 | /// Calculate spill costs for all registers, weighted by loop depth. |
|
| 177 | 189 | unsafe fn fillCosts(func: &il::Fn, costs: &mut [SpillCost]) { |
|
| 178 | 190 | for b in 0..func.blocks.len { |