compiler: Check register extent table traversal
891b61aee05c8e01cc48bdc1b0f28014b852564b7db40695b4c60c582658b928
1 parent
bf50fe77
lib/std/lang/gen/regalloc/liveness.rad
+22 -15
| 60 | 60 | return LiveInfo 'scratch { liveIn: frozen, liveOut: frozen, defs: frozen, uses: frozen, words: 0, blockCount: 0, maxReg: 0 }; |
|
| 61 | 61 | } |
|
| 62 | 62 | ||
| 63 | 63 | // Find max register number. |
|
| 64 | 64 | let mut maxReg: u32 = 0; |
|
| 65 | - | for p in func.params { |
|
| 66 | - | try updateMaxReg(p.value, &mut maxReg); |
|
| 67 | - | } |
|
| 65 | + | try updateExtent(func.params, &[], &mut maxReg); |
|
| 68 | 66 | for b in 0..blockCount { |
|
| 69 | 67 | let block = &func.blocks[b]; |
|
| 70 | - | for p in block.params { |
|
| 71 | - | try updateMaxReg(p.value, &mut maxReg); |
|
| 72 | - | } |
|
| 73 | - | for instr in block.instrs { |
|
| 74 | - | let mut registers = il::registers(&instr); |
|
| 75 | - | while let reg = il::nextReg(&mut registers, &instr) { |
|
| 76 | - | try updateMaxReg(reg, &mut maxReg); |
|
| 77 | - | } |
|
| 78 | - | if let dst = il::instrDst(instr) { |
|
| 79 | - | try updateMaxReg(dst, &mut maxReg); |
|
| 80 | - | } |
|
| 81 | - | } |
|
| 68 | + | try updateExtent(block.params, block.instrs, &mut maxReg); |
|
| 82 | 69 | } |
|
| 83 | 70 | // Allocate one contiguous word matrix for each set class. |
|
| 84 | 71 | let words = bitset::wordsFor(maxReg); |
|
| 85 | 72 | let count64 = blockCount as u64 * words as u64; |
|
| 86 | 73 | if count64 > 0xFFFFFFFF { |
| 165 | 152 | if not bitset::contains(defs, reg.n) { |
|
| 166 | 153 | bitset::put(uses, reg.n); |
|
| 167 | 154 | } |
|
| 168 | 155 | } |
|
| 169 | 156 | ||
| 157 | + | /// Include all parameter, source, and destination registers in the entry count. |
|
| 158 | + | fn updateExtent(params: &[il::Param], instructions: &[il::Instr], max: &mut u32) throws (alloc::AllocError) { |
|
| 159 | + | for p in params { |
|
| 160 | + | try updateMaxReg(p.value, max); |
|
| 161 | + | } |
|
| 162 | + | for i in 0..instructions.len { |
|
| 163 | + | let instr = &instructions[i]; |
|
| 164 | + | let mut registers = il::registers(instr); |
|
| 165 | + | // Argument groups contain raw views into the function's IL storage. |
|
| 166 | + | unsafe { |
|
| 167 | + | while let reg = il::nextReg(&mut registers, instr) { |
|
| 168 | + | try updateMaxReg(reg, max); |
|
| 169 | + | } |
|
| 170 | + | } |
|
| 171 | + | if let dst = il::instrDst(*instr) { |
|
| 172 | + | try updateMaxReg(dst, max); |
|
| 173 | + | } |
|
| 174 | + | } |
|
| 175 | + | } |
|
| 176 | + | ||
| 170 | 177 | /// Retain the larger of the current entry count and the register index plus one. |
|
| 171 | 178 | fn updateMaxReg(reg: il::Reg, max: &mut u32) throws (alloc::AllocError) { |
|
| 172 | 179 | if reg.n > MAX_REGISTER_INDEX { |
|
| 173 | 180 | throw alloc::AllocError::OutOfMemory; |
|
| 174 | 181 | } |
lib/std/lang/gen/regalloc/liveness/tests.rad
+36 -0
| 95 | 95 | assert not bitset::contains(super::liveInRow(&live, 0), destination.n); |
|
| 96 | 96 | } |
|
| 97 | 97 | } |
|
| 98 | 98 | } |
|
| 99 | 99 | ||
| 100 | + | /// Nested switch arguments contribute to the extent and reject index overflow. |
|
| 101 | + | @test unsafe fn testSwitchArgumentExtent() throws (testing::TestError) { |
|
| 102 | + | for index in [8192 as u32, 16384, 0xffffffff] { |
|
| 103 | + | for inDefault in [false, true] { |
|
| 104 | + | let source = il::Reg { n: index }; |
|
| 105 | + | let mut caseArgs = [il::Val::Imm(0)]; |
|
| 106 | + | let mut defaultArgs = [il::Val::Imm(0)]; |
|
| 107 | + | if inDefault { |
|
| 108 | + | set defaultArgs[0] = il::Val::Reg(source); |
|
| 109 | + | } else { |
|
| 110 | + | set caseArgs[0] = il::Val::Reg(source); |
|
| 111 | + | } |
|
| 112 | + | let mut cases = [il::SwitchCase { value: 1, target: 1, args: &mut caseArgs[..] }]; |
|
| 113 | + | let mut entry = [il::Instr::Switch { val: il::Val::Imm(0), |
|
| 114 | + | defaultTarget: 1, defaultArgs: &mut defaultArgs[..], cases: &mut cases[..] }]; |
|
| 115 | + | let params = [il::Param { value: il::Reg { n: 0 }, type: il::Type::W64 }]; |
|
| 116 | + | let mut exit = [il::Instr::Ret { val: il::Val::Reg(il::Reg { n: 0 }) }]; |
|
| 117 | + | let blocks = [block("entry", &mut entry[..]), il::Block { label: "exit", |
|
| 118 | + | params: ¶ms[..], instrs: &mut exit[..], locs: &[], preds: &[], loopDepth: 0 }]; |
|
| 119 | + | let function = il::Fn { name: "argument", params: &[], returnType: il::Type::W64, |
|
| 120 | + | isExtern: false, isLeaf: true, blocks: &blocks[..] }; |
|
| 121 | + | static DATA: [u8; 32768] = [0; 32768]; |
|
| 122 | + | let mut arena = alloc::new(&mut DATA[..]); |
|
| 123 | + | use arena as analysis in { |
|
| 124 | + | let live = try super::analyze(&function, &analysis) catch { |
|
| 125 | + | assert index == 0xffffffff; |
|
| 126 | + | continue; |
|
| 127 | + | }; |
|
| 128 | + | assert index <> 0xffffffff; |
|
| 129 | + | assert live.maxReg == index + 1; |
|
| 130 | + | assert bitset::contains(super::liveInRow(&live, 0), index); |
|
| 131 | + | } |
|
| 132 | + | } |
|
| 133 | + | } |
|
| 134 | + | } |
|
| 135 | + | ||
| 100 | 136 | /// An unrepresentable register extent fails before any bitset access. |
|
| 101 | 137 | @test unsafe fn testRegisterExtentOverflow() throws (testing::TestError) { |
|
| 102 | 138 | for instruction in [ |
|
| 103 | 139 | il::Instr::Ret { val: il::Val::Reg(il::Reg { n: 0xffffffff }) }, |
|
| 104 | 140 | il::Instr::Copy { dst: il::Reg { n: 0xffffffff }, val: il::Val::Imm(0) }, |