lib/std/lang/gen/regalloc/liveness.rad 8.1 KiB raw
1
//! Backward dataflow liveness analysis.
2
//!
3
//! Computes live-in and live-out sets for each basic block. A register is
4
//! "live" at a program point if its value may be used on some path from that
5
//! point to program exit.
6
//!
7
//! Uses iterative dataflow analysis:
8
//!
9
//!   REPEAT UNTIL changed == false
10
//!     changed = false
11
//!     FOR EACH BLOCK b IN POST-ORDER DO
12
//!       newOut = UNION(liveIn[succ] FOR EACH SUCCESSOR OF b)
13
//!       newIn = uses[b] | (newOut - defs[b])
14
//!       IF newIn != liveIn[b] OR newOut != liveOut[b] THEN
15
//!         changed = true
16
//!         liveIn[b] = newIn
17
//!         liveOut[b] = newOut
18
//!
19
//! Usage:
20
//!
21
//!   let live = liveness::analyze(func, ...);
22
//!   if liveness::isLastUse(&live, func, blockIdx, instrIdx, reg) {
23
//!       // `reg` last use is at this instruction.
24
//!   }
25
26
use std::mem;
27
use std::lang::il;
28
use std::lang::alloc;
29
use std::lang::gen::bitset;
30
31
/// Maximum number of SSA registers supported.
32
// TODO: Why so many?
33
pub const MAX_SSA_REGS: u32 = 8192;
34
35
/// Liveness information for a function.
36
pub record LiveInfo {
37
    /// Per-block live-in sets (indexed by block index).
38
    liveIn: *mut [bitset::Bitset],
39
    /// Per-block live-out sets (indexed by block index).
40
    liveOut: *mut [bitset::Bitset],
41
    /// Per-block defs sets (registers defined in block).
42
    defs: *mut [bitset::Bitset],
43
    /// Per-block uses sets (registers used before defined in block).
44
    uses: *mut [bitset::Bitset],
45
    /// Number of blocks.
46
    blockCount: u32,
47
    /// Maximum register number used.
48
    maxReg: u32,
49
}
50
51
/// Context for collecting defs and uses during block analysis.
52
record DefsUses {
53
    defs: *bitset::Bitset,
54
    uses: *mut bitset::Bitset,
55
}
56
57
/// Context for searching for a specific register in an instruction.
58
record FindCtx {
59
    target: u32,
60
    found: bool,
61
}
62
63
/// Compute liveness information for a function.
64
pub fn analyze(func: *il::Fn, arena: *mut alloc::Arena) -> LiveInfo throws (alloc::AllocError) {
65
    let blockCount = func.blocks.len;
66
    if blockCount == 0 {
67
        return LiveInfo {
68
            liveIn: &mut [],
69
            liveOut: &mut [],
70
            defs: &mut [],
71
            uses: &mut [],
72
            blockCount: 0,
73
            maxReg: 0,
74
        };
75
    }
76
77
    // Find max register number.
78
    let mut maxReg: u32 = 0;
79
    for p in func.params {
80
        maxReg = maxRegNum(p.value.n, maxReg);
81
    }
82
    for b in 0..blockCount {
83
        let block = &func.blocks[b];
84
        for p in block.params {
85
            maxReg = maxRegNum(p.value.n, maxReg);
86
        }
87
        for i in 0..block.instrs.len {
88
            il::forEachReg(block.instrs[i], maxRegCallback, &mut maxReg as *mut opaque);
89
            if let dst = il::instrDst(block.instrs[i]) {
90
                maxReg = maxRegNum(dst.n, maxReg);
91
            }
92
        }
93
    }
94
    if maxReg > MAX_SSA_REGS {
95
        panic "analyze: maximum SSA registers exceeded";
96
    }
97
    // Allocate per-block bitsets.
98
    let liveIn = try alloc::allocSlice(arena, @sizeOf(bitset::Bitset), @alignOf(bitset::Bitset), blockCount) as *mut [bitset::Bitset];
99
    let liveOut = try alloc::allocSlice(arena, @sizeOf(bitset::Bitset), @alignOf(bitset::Bitset), blockCount) as *mut [bitset::Bitset];
100
    let defs = try alloc::allocSlice(arena, @sizeOf(bitset::Bitset), @alignOf(bitset::Bitset), blockCount) as *mut [bitset::Bitset];
101
    let uses = try alloc::allocSlice(arena, @sizeOf(bitset::Bitset), @alignOf(bitset::Bitset), blockCount) as *mut [bitset::Bitset];
102
103
    for b in 0..blockCount {
104
        liveIn[b] = try bitset::allocate(arena, maxReg);
105
        liveOut[b] = try bitset::allocate(arena, maxReg);
106
        defs[b] = try bitset::allocate(arena, maxReg);
107
        uses[b] = try bitset::allocate(arena, maxReg);
108
    }
109
110
    // Compute local defs and uses for each block.
111
    for b in 0..blockCount {
112
        computeLocalDefsUses(&func.blocks[b], &mut defs[b], &mut uses[b]);
113
    }
114
    // Iterative dataflow analysis.
115
    let mut changed = true;
116
    let mut scratch = try bitset::allocate(arena, maxReg);
117
118
    while changed {
119
        changed = false;
120
121
        // Process blocks in reverse order (approximates post-order).
122
        let mut b = blockCount;
123
        while b > 0 {
124
            b -= 1;
125
            let block = &func.blocks[b];
126
127
            // Compute new `liveOut` as union of successor `liveIn` sets.
128
            bitset::clearAll(&mut scratch);
129
            addSuccessorLiveIn(func, block, liveIn, &mut scratch);
130
131
            if not bitset::eq(&liveOut[b], &scratch) {
132
                bitset::copy(&mut liveOut[b], &scratch);
133
                changed = true;
134
            }
135
            // Compute new liveIn = uses | (liveOut - defs)
136
            bitset::copy(&mut scratch, &liveOut[b]);
137
            bitset::subtract(&mut scratch, &defs[b]);
138
            bitset::union_(&mut scratch, &uses[b]);
139
140
            if not bitset::eq(&liveIn[b], &scratch) {
141
                bitset::copy(&mut liveIn[b], &scratch);
142
                changed = true;
143
            }
144
        }
145
    }
146
    return LiveInfo { liveIn, liveOut, defs, uses, blockCount, maxReg };
147
}
148
149
/// Compute local defs and uses for a single block.
150
fn computeLocalDefsUses(block: *il::Block, defs: *mut bitset::Bitset, uses: *mut bitset::Bitset) {
151
    for p in block.params {
152
        bitset::set(defs, p.value.n);
153
    }
154
    for i in 0..block.instrs.len {
155
        let instr = block.instrs[i];
156
        let mut ctx = DefsUses { defs, uses };
157
        il::forEachReg(instr, addUseCallback, &mut ctx as *mut opaque);
158
159
        if let dst = il::instrDst(instr) {
160
            bitset::set(defs, dst.n);
161
        }
162
    }
163
}
164
165
/// Callback for [`il::forEachReg`]: adds register to uses if not already defined.
166
fn addUseCallback(reg: il::Reg, ctx: *mut opaque) {
167
    let c = ctx as *mut DefsUses;
168
    if not bitset::contains(c.defs, reg.n) {
169
        bitset::set(c.uses, reg.n);
170
    }
171
}
172
173
/// Callback for [`il::forEachReg`]: updates max register number.
174
fn maxRegCallback(reg: il::Reg, ctx: *mut opaque) {
175
    let max = ctx as *mut u32;
176
    *max = maxRegNum(reg.n, *max);
177
}
178
179
/// Return the larger of n+1 and current.
180
fn maxRegNum(n: u32, current: u32) -> u32 {
181
    if n + 1 > current {
182
        return n + 1;
183
    }
184
    return current;
185
}
186
187
/// Add successor "live in" sets to the scratch bitset.
188
fn addSuccessorLiveIn(func: *il::Fn, block: *il::Block, liveIn: *[bitset::Bitset], scratch: *mut bitset::Bitset) {
189
    if block.instrs.len == 0 {
190
        return;
191
    }
192
    let term = block.instrs[block.instrs.len - 1];
193
194
    match term {
195
        case il::Instr::Jmp { target, .. } =>
196
            unionBlockLiveIn(target, liveIn, scratch),
197
        case il::Instr::Br { thenTarget, elseTarget, .. } => {
198
            unionBlockLiveIn(thenTarget, liveIn, scratch);
199
            unionBlockLiveIn(elseTarget, liveIn, scratch);
200
        },
201
        case il::Instr::Switch { defaultTarget, cases, .. } => {
202
            unionBlockLiveIn(defaultTarget, liveIn, scratch);
203
            for c in cases {
204
                unionBlockLiveIn(c.target, liveIn, scratch);
205
            }
206
        },
207
        else => {},
208
    }
209
}
210
211
/// Union a target block's "live in" set into scratch.
212
fn unionBlockLiveIn(target: u32, liveIn: *[bitset::Bitset], scratch: *mut bitset::Bitset) {
213
    bitset::union_(scratch, &liveIn[target]);
214
}
215
216
/// Check if this is the last use of a register at this instruction.
217
pub fn isLastUse(info: *LiveInfo, func: *il::Fn, blockIdx: u32, instrIdx: u32, reg: il::Reg) -> bool {
218
    let block = &func.blocks[blockIdx];
219
    let instr = block.instrs[instrIdx];
220
221
    if not instrUsesReg(instr, reg) {
222
        return false;
223
    }
224
    if bitset::contains(&info.liveOut[blockIdx], reg.n) {
225
        return false;
226
    }
227
    for i in (instrIdx + 1)..block.instrs.len {
228
        if instrUsesReg(block.instrs[i], reg) {
229
            return false;
230
        }
231
    }
232
    return true;
233
}
234
235
/// Check if an instruction uses a specific register.
236
fn instrUsesReg(instr: il::Instr, reg: il::Reg) -> bool {
237
    let mut ctx = FindCtx { target: reg.n, found: false };
238
    il::forEachReg(instr, findRegCallback, &mut ctx as *mut opaque);
239
    return ctx.found;
240
}
241
242
/// Callback for [`il::forEachReg`]: sets found if register matches target.
243
fn findRegCallback(reg: il::Reg, ctx: *mut opaque) {
244
    let c = ctx as *mut FindCtx;
245
    if reg.n == c.target {
246
        c.found = true;
247
    }
248
}