compiler: Check borrowed successor liveness merging

9ab18d59dedd0217cee358c0e8b5188ec580fb46273fa221ac1e23fe505c4706
Alexis Sellier committed ago 1 parent 09c34aea
lib/std/lang/gen/regalloc/liveness.rad +16 -8
107 107
        while b > 0 {
108 108
            set b -= 1;
109 109
            let block = &func.blocks[b];
110 110
111 111
            // Add successor live-in sets directly to this block's live-out set.
112 -
            addSuccessorLiveIn(func, block, liveIn, words, &mut liveOut[b * words..(b + 1) * words]);
112 +
            addSuccessorLiveIn(block, liveIn, words, &mut liveOut[b * words..(b + 1) * words]);
113 113
114 114
            if computeAndUpdateLiveIn(&mut liveIn[b * words..(b + 1) * words], &liveOut[b * words..(b + 1) * words], &defs[b * words..(b + 1) * words], &uses[b * words..(b + 1) * words]) {
115 115
                set changed = true;
116 116
            }
117 117
        }
172 172
        set *max = count;
173 173
    }
174 174
}
175 175
176 176
/// Add successor live-in sets to the block's live-out set.
177 -
unsafe fn addSuccessorLiveIn(func: &il::Fn, block: &il::Block, liveIn: &[u32], words: u32, liveOut: &mut [u32]) {
177 +
unsafe fn addSuccessorLiveIn(block: &il::Block, liveIn: &[u32], words: u32, liveOut: &mut [u32]) {
178 178
    if block.instrs.len == 0 {
179 179
        return;
180 180
    }
181 -
    let term = block.instrs[block.instrs.len - 1];
181 +
    let term = &block.instrs[block.instrs.len - 1];
182 +
    if let case il::Instr::Switch { cases, .. } = term {
183 +
        mergeSuccessorLiveIn(term, *cases, liveIn, words, liveOut);
184 +
    } else {
185 +
        mergeSuccessorLiveIn(term, &[], liveIn, words, liveOut);
186 +
    }
187 +
}
182 188
189 +
/// Merge live-in rows selected by a terminator and its borrowed switch cases.
190 +
fn mergeSuccessorLiveIn(term: &il::Instr, cases: &[il::SwitchCase], liveIn: &[u32], words: u32, liveOut: &mut [u32]) {
183 191
    match term {
184 192
        case il::Instr::Jmp { target, .. } =>
185 -
            unionBlockLiveIn(target, liveIn, words, liveOut),
193 +
            unionBlockLiveIn(*target, liveIn, words, liveOut),
186 194
        case il::Instr::Br { thenTarget, elseTarget, .. } => {
187 -
            unionBlockLiveIn(thenTarget, liveIn, words, liveOut);
188 -
            unionBlockLiveIn(elseTarget, liveIn, words, liveOut);
195 +
            unionBlockLiveIn(*thenTarget, liveIn, words, liveOut);
196 +
            unionBlockLiveIn(*elseTarget, liveIn, words, liveOut);
189 197
        },
190 -
        case il::Instr::Switch { defaultTarget, cases, .. } => {
191 -
            unionBlockLiveIn(defaultTarget, liveIn, words, liveOut);
198 +
        case il::Instr::Switch { defaultTarget, .. } => {
199 +
            unionBlockLiveIn(*defaultTarget, liveIn, words, liveOut);
192 200
            for c in cases {
193 201
                unionBlockLiveIn(c.target, liveIn, words, liveOut);
194 202
            }
195 203
        },
196 204
        else => {},
lib/std/lang/gen/regalloc/liveness/tests.rad +29 -0
11 11
unsafe fn block(name: *[u8], instructions: *unsafe mut [il::Instr]) -> il::Block {
12 12
    return il::Block { label: name, params: &[], instrs: instructions,
13 13
        locs: &[], preds: &[], loopDepth: 0 };
14 14
}
15 15
16 +
/// Switch successors merge distinct words and tolerate repeated destinations.
17 +
@test unsafe fn testSwitchSuccessorLiveness() throws (testing::TestError) {
18 +
    for count in [0 as u32, 1, 3] {
19 +
        let mut cases = [
20 +
            il::SwitchCase { value: 0, target: 1, args: &mut [] },
21 +
            il::SwitchCase { value: 1, target: 1, args: &mut [] },
22 +
            il::SwitchCase { value: 2, target: 2, args: &mut [] },
23 +
        ];
24 +
        let mut entry = [il::Instr::Switch { val: il::Val::Imm(0),
25 +
            defaultTarget: 3, defaultArgs: &mut [], cases: &mut cases[..count] }];
26 +
        let mut first = [il::Instr::Ret { val: il::Val::Reg(il::Reg { n: 0 }) }];
27 +
        let mut second = [il::Instr::Ret { val: il::Val::Reg(il::Reg { n: 33 }) }];
28 +
        let mut fallback = [il::Instr::Ret { val: il::Val::Reg(il::Reg { n: 66 }) }];
29 +
        let blocks = [block("entry", &mut entry[..]), block("first", &mut first[..]),
30 +
            block("second", &mut second[..]), block("default", &mut fallback[..]),
31 +
            block("empty", &mut [])];
32 +
        let function = il::Fn { name: "switch", params: &[], returnType: il::Type::W64,
33 +
            isExtern: false, isLeaf: true, blocks: &blocks[..] };
34 +
        static DATA: [u8; 8192] = [0; 8192];
35 +
        let mut arena = alloc::new(&mut DATA[..]);
36 +
        use arena as analysis in {
37 +
            let live = try! super::analyze(&function, &analysis);
38 +
            try check(super::liveOutRow(&live, 0), count > 0, count == 3, true);
39 +
            try check(super::liveInRow(&live, 0), count > 0, count == 3, true);
40 +
            try check(super::liveOutRow(&live, 4), false, false, false);
41 +
        }
42 +
    }
43 +
}
44 +
16 45
/// Liveness sizes its rows from register operands and definitions.
17 46
@test unsafe fn testLargeRegisterExtent() throws (testing::TestError) {
18 47
    for index in [8191 as u32, 8192, 8193, 16384] {
19 48
        let source = il::Reg { n: index };
20 49
        let destination = il::Reg { n: index + 1 };