compiler: Borrow instruction tables for liveness queries

989da8bfe2b41499682bcca41ca64ad7c46b1f80a384934368f9e6697b399e60
Alexis Sellier committed ago 1 parent 1671d5e0
lib/std/lang/gen/regalloc/liveness.rad +20 -17
109 109
            set b -= 1;
110 110
            let block = &blocks[b];
111 111
112 112
            // Add successor live-in sets directly to this block's live-out set.
113 113
            unsafe {
114 -
                addSuccessorLiveIn(block, liveIn, words, &mut liveOut[b * words..(b + 1) * words]);
114 +
                addSuccessorLiveIn(block.instrs, liveIn, words, &mut liveOut[b * words..(b + 1) * words]);
115 115
            }
116 116
117 117
            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]) {
118 118
                set changed = true;
119 119
            }
198 198
        set *max = count;
199 199
    }
200 200
}
201 201
202 202
/// Add successor live-in sets to the block's live-out set.
203 -
unsafe fn addSuccessorLiveIn(block: &il::Block, liveIn: &[u32], words: u32, liveOut: &mut [u32]) {
204 -
    if block.instrs.len == 0 {
203 +
fn addSuccessorLiveIn(instructions: &[il::Instr], liveIn: &[u32], words: u32, liveOut: &mut [u32]) {
204 +
    if instructions.len == 0 {
205 205
        return;
206 206
    }
207 -
    let term = &block.instrs[block.instrs.len - 1];
207 +
    let term = &instructions[instructions.len - 1];
208 208
    if let case il::Instr::Switch { cases, .. } = term {
209 -
        mergeSuccessorLiveIn(term, *cases, liveIn, words, liveOut);
209 +
        unsafe {
210 +
            mergeSuccessorLiveIn(term, *cases, liveIn, words, liveOut);
211 +
        }
210 212
    } else {
211 213
        mergeSuccessorLiveIn(term, &[], liveIn, words, liveOut);
212 214
    }
213 215
}
214 216
241 243
    let block = &func.blocks[blockIdx];
242 244
243 245
    if bitset::contains(liveOutRow(info, blockIdx), reg.n) {
244 246
        return true;
245 247
    }
246 -
    for i in (instrIdx + 1)..block.instrs.len {
247 -
        if instrUsesReg(block.instrs[i], reg) {
248 -
            return true;
249 -
        }
250 -
    }
251 -
    return false;
248 +
    return usesRegisterAfter(block.instrs, instrIdx, reg);
252 249
}
253 250
254 -
/// Check if an instruction uses a specific register.
255 -
unsafe fn instrUsesReg(instr: il::Instr, reg: il::Reg) -> bool {
256 -
    let mut registers = il::registers(&instr);
257 -
    while let source = il::nextReg(&mut registers, &instr) {
258 -
        if source.n == reg.n {
259 -
            return true;
251 +
/// Check if an instruction after the given index uses a specific register.
252 +
fn usesRegisterAfter(instructions: &[il::Instr], instrIdx: u32, reg: il::Reg) -> bool {
253 +
    for i in (instrIdx + 1)..instructions.len {
254 +
        let instr = &instructions[i];
255 +
        let mut registers = il::registers(instr);
256 +
        // Argument groups contain raw views into the function's IL storage.
257 +
        unsafe {
258 +
            while let source = il::nextReg(&mut registers, instr) {
259 +
                if source.n == reg.n {
260 +
                    return true;
261 +
                }
262 +
            }
260 263
        }
261 264
    }
262 265
    return false;
263 266
}
264 267
lib/std/lang/gen/regalloc/liveness/tests.rad +33 -0
66 66
            try check(super::liveOutRow(&live, index), false, false, index <> 1 and index < 4);
67 67
        }
68 68
    }
69 69
}
70 70
71 +
/// Later-use queries include outgoing values and exclude the current instruction.
72 +
@test unsafe fn testLaterUses() throws (testing::TestError) {
73 +
    let outgoing = il::Reg { n: 0 };
74 +
    let argument = il::Reg { n: 1 };
75 +
    let result = il::Reg { n: 33 };
76 +
    let unused = il::Reg { n: 66 };
77 +
    let params = [il::Param { value: argument, type: il::Type::W64 }];
78 +
    let args = [il::Val::Imm(0), il::Val::Reg(argument), il::Val::Reg(argument)];
79 +
    let mut entry = [
80 +
        il::Instr::Copy { dst: outgoing, val: il::Val::Imm(1) },
81 +
        il::Instr::Call { retTy: il::Type::W64, dst: result,
82 +
            func: il::Val::FnAddr("callee"), args: &args[..] },
83 +
        il::Instr::Copy { dst: unused, val: il::Val::Reg(result) },
84 +
        il::Instr::Jmp { target: 1, args: &mut [] },
85 +
    ];
86 +
    let mut exit = [il::Instr::Ret { val: il::Val::Reg(outgoing) }];
87 +
    let blocks = [block("entry", &mut entry[..]), block("exit", &mut exit[..])];
88 +
    let function = il::Fn { name: "later", params: &params[..], returnType: il::Type::W64,
89 +
        isExtern: false, isLeaf: false, blocks: &blocks[..] };
90 +
    static DATA: [u8; 8192] = [0; 8192];
91 +
    let mut arena = alloc::new(&mut DATA[..]);
92 +
    use arena as analysis in {
93 +
        let live = try! super::analyze(&function, &analysis);
94 +
        for index in 0..entry.len {
95 +
            assert super::hasLaterUse(&live, &function, 0, index, outgoing);
96 +
            assert super::hasLaterUse(&live, &function, 0, index, argument) == (index == 0);
97 +
            assert super::hasLaterUse(&live, &function, 0, index, result) == (index < 2);
98 +
            assert not super::hasLaterUse(&live, &function, 0, index, unused);
99 +
        }
100 +
        assert not super::hasLaterUse(&live, &function, 1, 0, outgoing);
101 +
    }
102 +
}
103 +
71 104
/// Local uses precede definitions, and block parameters define their registers.
72 105
@test unsafe fn testLocalDefinitionOrder() throws (testing::TestError) {
73 106
    let parameter = il::Reg { n: 33 };
74 107
    let temporary = il::Reg { n: 66 };
75 108
    let external = il::Reg { n: 0 };