compiler: Check spill allocation and analysis pipeline
39698da53b5fe7a47668b294244240c0b67055de07830a061c6a79cbc5a4252e
1 parent
4a63458b
lib/std/lang/gen/regalloc/liveness/tests.rad
+22 -0
| 27 | 27 | assert live.blockCount == count; |
|
| 28 | 28 | assert live.maxReg == 0; |
|
| 29 | 29 | assert live.words == 0; |
|
| 30 | 30 | assert live.liveIn.len == 0 and live.liveOut.len == 0; |
|
| 31 | 31 | assert live.defs.len == 0 and live.uses.len == 0; |
|
| 32 | + | let spills = try! spill::analyze(&function, &live, 0, 0, 8, &analysis); |
|
| 33 | + | assert spills.maxReg == 0; |
|
| 34 | + | assert spills.frameSize == 0; |
|
| 35 | + | assert spills.slots.len == 0 and spills.calleeClass.len == 0; |
|
| 32 | 36 | } |
|
| 33 | 37 | } |
|
| 34 | 38 | } |
|
| 35 | 39 | ||
| 36 | 40 | /// Insufficient matrix storage reports an allocation error. |
| 48 | 52 | }; |
|
| 49 | 53 | } |
|
| 50 | 54 | assert failed; |
|
| 51 | 55 | } |
|
| 52 | 56 | ||
| 57 | + | /// Spill storage exhaustion reports an error after liveness succeeds. |
|
| 58 | + | @test unsafe fn testSpillStorageExhaustion() throws (testing::TestError) { |
|
| 59 | + | let mut instructions = [il::Instr::Ret { val: il::Val::Reg(il::Reg { n: 66 }) }]; |
|
| 60 | + | let blocks = [block("entry", &mut instructions[..])]; |
|
| 61 | + | let function = il::Fn { name: "spillStorage", params: &[], returnType: il::Type::W64, |
|
| 62 | + | isExtern: false, isLeaf: true, blocks: &blocks[..] }; |
|
| 63 | + | static DATA: [u8; 128] = [0; 128]; |
|
| 64 | + | let mut arena = alloc::new(&mut DATA[..]); |
|
| 65 | + | let mut failed = false; |
|
| 66 | + | use arena as analysis in { |
|
| 67 | + | let live = try! super::analyze(&function, &analysis); |
|
| 68 | + | try spill::analyze(&function, &live, 0, 0, 8, &analysis) catch { |
|
| 69 | + | set failed = true; |
|
| 70 | + | }; |
|
| 71 | + | } |
|
| 72 | + | assert failed; |
|
| 73 | + | } |
|
| 74 | + | ||
| 53 | 75 | /// Switch successors merge distinct words and tolerate repeated destinations. |
|
| 54 | 76 | @test unsafe fn testSwitchSuccessorLiveness() throws (testing::TestError) { |
|
| 55 | 77 | for count in [0 as u32, 1, 3] { |
|
| 56 | 78 | let mut cases = [ |
|
| 57 | 79 | il::SwitchCase { value: 0, target: 1, args: &mut [] }, |
lib/std/lang/gen/regalloc/spill.rad
+22 -6
| 80 | 80 | live: &liveness::LiveInfo 'scratch, |
|
| 81 | 81 | numRegs: u32, |
|
| 82 | 82 | numCalleeSaved: u32, |
|
| 83 | 83 | slotSize: u32, |
|
| 84 | 84 | storage: &Session 'scratch |
|
| 85 | + | ) -> SpillInfo 'scratch throws (alloc::AllocError) { |
|
| 86 | + | return try analyzeBlocks(func.blocks, live, numRegs, numCalleeSaved, slotSize, storage); |
|
| 87 | + | } |
|
| 88 | + | ||
| 89 | + | /// Allocate spill storage and analyze pressure over a borrowed block table. |
|
| 90 | + | fn analyzeBlocks 'scratch ( |
|
| 91 | + | blocks: &[il::Block], |
|
| 92 | + | live: &liveness::LiveInfo 'scratch, |
|
| 93 | + | numRegs: u32, |
|
| 94 | + | numCalleeSaved: u32, |
|
| 95 | + | slotSize: u32, |
|
| 96 | + | storage: &Session 'scratch, |
|
| 85 | 97 | ) -> SpillInfo 'scratch throws (alloc::AllocError) { |
|
| 86 | 98 | let maxReg = live.maxReg; |
|
| 87 | 99 | if maxReg == 0 { |
|
| 88 | 100 | let calleeClass = try bitset::allocate(storage, 0); |
|
| 89 | 101 | return SpillInfo 'scratch { |
| 96 | 108 | // Allocate spill slots array. |
|
| 97 | 109 | let slots = try storage.fill(-1 as i32, maxReg); |
|
| 98 | 110 | // Allocate cost array. |
|
| 99 | 111 | let costs = try storage.fill(SpillCost { defs: 0, uses: 0 }, maxReg); |
|
| 100 | 112 | // Phase 1: Calculate spill costs. |
|
| 101 | - | for b in 0..func.blocks.len { |
|
| 102 | - | let block = &func.blocks[b]; |
|
| 103 | - | fillCosts(block.params, block.instrs, block.loopDepth, costs); |
|
| 113 | + | for b in 0..blocks.len { |
|
| 114 | + | let block = &blocks[b]; |
|
| 115 | + | unsafe { |
|
| 116 | + | fillCosts(block.params, block.instrs, block.loopDepth, costs); |
|
| 117 | + | } |
|
| 104 | 118 | } |
|
| 105 | 119 | ||
| 106 | 120 | // Phase 2: Find values that exceed register pressure. |
|
| 107 | 121 | let spilled = try bitset::allocate(storage, maxReg); |
|
| 108 | 122 | let calleeClass = try bitset::allocate(storage, maxReg); |
|
| 109 | 123 | let scratch = try bitset::allocate(storage, maxReg); |
|
| 110 | 124 | ||
| 111 | - | for b in 0..func.blocks.len { |
|
| 112 | - | let block = &func.blocks[b]; |
|
| 125 | + | for b in 0..blocks.len { |
|
| 126 | + | let block = &blocks[b]; |
|
| 113 | 127 | ||
| 114 | 128 | // Start with live-out set. |
|
| 115 | 129 | bitset::copy(scratch, liveness::liveOutRow(live, b)); |
|
| 116 | 130 | ||
| 117 | - | try limitBlockPressure(block.instrs, scratch, spilled, costs, calleeClass, numRegs, numCalleeSaved); |
|
| 131 | + | unsafe { |
|
| 132 | + | try limitBlockPressure(block.instrs, scratch, spilled, costs, calleeClass, numRegs, numCalleeSaved); |
|
| 133 | + | } |
|
| 118 | 134 | } |
|
| 119 | 135 | ||
| 120 | 136 | let frameSize = try finishSpills(slots, spilled, calleeClass, numCalleeSaved, slotSize); |
|
| 121 | 137 | return SpillInfo 'scratch { slots: &slots[..], frameSize, calleeClass: &calleeClass[..], maxReg }; |
|
| 122 | 138 | } |