Skip to main content

rucc_regalloc/
live.rs

1//! Where every value in a machine function is live.
2//!
3//! Design: `spec/10-backend.md` section 10.4.
4//!
5//! A register can be given to two values at once exactly when the two are never both wanted, so
6//! this is the question every allocator asks first and the one both of ours will read the answer
7//! to from here. It is asked of the machine IR while it is still in SSA form, which is what makes
8//! the answer cheap: a value is written once, so its live range is one interval from where it is
9//! written to the last place it is read, and there is no need to ask which of several definitions
10//! a use is reading from.
11//!
12//! # What the answer is
13//!
14//! One interval per virtual register, with no holes in it. A value that is dead in the middle of
15//! its range is treated as live there, which costs a register the allocator could have handed out
16//! and never claims one is free when it is not. Holes are what the backtracking allocator will
17//! want and it will want a different structure to hold them in, since a range it can split is a
18//! range with a list of pieces rather than two numbers.
19//!
20//! Which is why the live-in and live-out sets the fixpoint computes are kept rather than thrown
21//! away once the intervals are built. An interval is generous by design and that is fine for
22//! deciding two values cannot share a register, since being generous there loses a register rather
23//! than losing a value. It is not fine wherever the answer decides something instead of describing
24//! it, and `Live::anywhere_in` is the question to ask there: whether a value is live in a given
25//! block, which the sets answer exactly and the interval only approximates.
26//!
27//! Physical registers in the operands are not in the answer. Nothing writes one before allocation
28//! except an instruction that must, and what a call destroys is a separate question that the ABI
29//! lowering asks, so a pass that reads this is reading about the values the allocator places.
30//!
31//! # How it is computed
32//!
33//! Which values arrive live in each block and which leave live is a fixpoint over the blocks, run
34//! backwards because liveness flows backwards, and it is a fixpoint rather than one pass because
35//! a loop carries a value from the end of a block round to a block in front of it. The intervals
36//! then come from one walk over the instructions. A block a value is live through contributes the
37//! whole of that block, which is what makes the interval cover the loop rather than stopping at
38//! the last instruction that mentions it.
39
40use rucc_mir::{Block, Func, Reg, Role};
41
42use crate::order::{Order, Point};
43
44/// The stretch of the function a value is live over.
45///
46/// Both ends are included: a value written at a point and read at a later one is live at both,
47/// and one written and never read is live where it was written, because the register it was
48/// written to is not free at the instant it was written to. A value written early is written
49/// before the instruction reads its operands and is still written when the instruction is done,
50/// so even one nothing reads covers the whole of the instruction that wrote it.
51#[derive(Debug, Clone, Copy, PartialEq, Eq)]
52pub struct Range {
53    /// Where the value is written.
54    pub start: Point,
55    /// The last place it is read, or where it is written if nothing reads it.
56    pub end: Point,
57}
58
59impl Range {
60    /// Whether the value is live at that point.
61    #[must_use]
62    pub fn covers(self, point: Point) -> bool {
63        self.start <= point && point <= self.end
64    }
65
66    /// Whether two values are both live anywhere, which is what stops them sharing a register.
67    #[must_use]
68    pub fn overlaps(self, other: Self) -> bool {
69        self.start <= other.end && other.start <= self.end
70    }
71
72    /// The smallest range covering both, which is how a range grows as more of the function is
73    /// read.
74    fn with(self, point: Point) -> Self {
75        Self { start: self.start.min(point), end: self.end.max(point) }
76    }
77}
78
79/// What is live where.
80#[derive(Debug, Clone)]
81pub struct Live {
82    live_in: Rows,
83    live_out: Rows,
84    defined: Rows,
85    ranges: Vec<Option<Range>>,
86}
87
88impl Live {
89    /// Works it out for a function laid out in that order.
90    #[must_use]
91    pub fn of(func: &Func, order: &Order) -> Self {
92        let vregs = func.vregs();
93        let (used, defined) = exposed(func, order);
94        let (live_in, live_out) = flow(func, order, &used, &defined);
95        let ranges = measure(func, order, &live_in, &live_out, vregs);
96        Self { live_in, live_out, defined, ranges }
97    }
98
99    /// Where a virtual register is live, or `None` for one this function never mentions and for
100    /// a physical register.
101    #[must_use]
102    pub fn range(&self, reg: Reg) -> Option<Range> {
103        self.ranges.get(usize::try_from(reg.number()?).ok()?).copied().flatten()
104    }
105
106    /// Every virtual register that arrives in a block already holding a value.
107    ///
108    /// The block's own parameters are not among them. A parameter is written where it arrives,
109    /// which makes it a value the block defines rather than one it inherits.
110    pub fn live_in(&self, block: Block) -> impl Iterator<Item = Reg> + '_ {
111        self.live_in.iter(block.index())
112    }
113
114    /// Every virtual register that is still wanted after a block, which is what its successors
115    /// and the arguments its terminator carries between them ask for.
116    pub fn live_out(&self, block: Block) -> impl Iterator<Item = Reg> + '_ {
117        self.live_out.iter(block.index())
118    }
119
120    /// Whether a value is live anywhere in a block.
121    ///
122    /// An interval has no holes in it, so a value live in two blocks is treated as live in every
123    /// block laid out between them, whether or not it reaches them. This answers the question the
124    /// interval cannot: a value is live somewhere in a block when it arrives live, or leaves live,
125    /// or is written there, and in no other block.
126    ///
127    /// Which matters wherever the answer decides something rather than describes it. The order the
128    /// blocks are in here is the one the function came in, and `crate::layout` puts them in a
129    /// different one afterwards, so a block between two others in this order is not between them in
130    /// the code. A call in such a block would otherwise take every register it destroys away from
131    /// every value laid out around it, including values whose loop the call is nowhere near.
132    /// tamnd/rucc#982.
133    #[must_use]
134    pub fn anywhere_in(&self, reg: Reg, block: Block) -> bool {
135        let row = block.index();
136        self.live_in.contains(row, reg)
137            || self.live_out.contains(row, reg)
138            || self.defined.contains(row, reg)
139    }
140}
141
142/// The intervals, from the blocks and from the instructions in them.
143fn measure(
144    func: &Func,
145    order: &Order,
146    live_in: &Rows,
147    live_out: &Rows,
148    vregs: usize,
149) -> Vec<Option<Range>> {
150    let mut ranges: Vec<Option<Range>> = vec![None; vregs];
151    let mut extend = |reg: Reg, point: Point| {
152        let Some(number) = reg.number().and_then(|number| usize::try_from(number).ok()) else {
153            return;
154        };
155        let Some(slot) = ranges.get_mut(number) else { return };
156        *slot = Some(match *slot {
157            Some(range) => range.with(point),
158            None => Range { start: point, end: point },
159        });
160    };
161
162    for &block in order.blocks() {
163        // A block a value arrives in and leaves is one it is live through, whether or not
164        // anything in it says the value's name.
165        for reg in live_in.iter(block.index()) {
166            extend(reg, order.start(block));
167        }
168        for reg in live_out.iter(block.index()) {
169            extend(reg, order.end(block));
170        }
171        for param in &func[block].params {
172            extend(param.reg, order.start(block));
173        }
174        for inst in func.insts(block) {
175            for operand in &func[func[inst].operands] {
176                match operand.role {
177                    Role::Use => extend(operand.reg, order.early(inst)),
178                    Role::Def => extend(operand.reg, order.late(inst)),
179                    // A register written early is taken from before the operands are read, which
180                    // is the whole of what makes it different from a plain definition, and it is
181                    // still taken when the instruction is done. Both ends have to be said. Saying
182                    // only the first would leave a value nothing reads live at a point in front of
183                    // everything else the instruction writes, and the register it went to would
184                    // look free to them.
185                    Role::EarlyDef => {
186                        extend(operand.reg, order.early(inst));
187                        extend(operand.reg, order.late(inst));
188                    }
189                }
190            }
191        }
192        for call in &func[block].succs {
193            for &arg in &call.args {
194                extend(arg, order.end(block));
195            }
196        }
197    }
198    ranges
199}
200
201/// What each block reads before writing, and what it writes.
202///
203/// The first is read backwards, because a value a block writes and then reads is one it does not
204/// want from anybody, while one it reads and then writes is.
205fn exposed(func: &Func, order: &Order) -> (Rows, Rows) {
206    let mut used = Rows::new(func.block_count(), func.vregs());
207    let mut defined = Rows::new(func.block_count(), func.vregs());
208    for &block in order.blocks() {
209        let row = block.index();
210        for call in &func[block].succs {
211            for &arg in &call.args {
212                used.insert(row, arg);
213            }
214        }
215        let insts: Vec<_> = func.insts(block).collect();
216        for &inst in insts.iter().rev() {
217            let operands = &func[func[inst].operands];
218            for operand in operands.iter().filter(|operand| operand.role.is_def()) {
219                used.remove(row, operand.reg);
220                defined.insert(row, operand.reg);
221            }
222            for operand in operands.iter().filter(|operand| !operand.role.is_def()) {
223                used.insert(row, operand.reg);
224            }
225        }
226        for param in &func[block].params {
227            used.remove(row, param.reg);
228            defined.insert(row, param.reg);
229        }
230    }
231    (used, defined)
232}
233
234/// The fixpoint: what arrives live in each block, and what leaves live.
235fn flow(func: &Func, order: &Order, used: &Rows, defined: &Rows) -> (Rows, Rows) {
236    let mut live_in = Rows::new(func.block_count(), func.vregs());
237    let mut live_out = Rows::new(func.block_count(), func.vregs());
238    let width = live_in.width;
239    let mut next = vec![0u64; width];
240    let mut changed = true;
241    while changed {
242        changed = false;
243        for &block in order.blocks().iter().rev() {
244            let row = block.index();
245            for call in &func[block].succs {
246                let successor = call.block.index();
247                for (word, &incoming) in
248                    live_out.row_mut(row).iter_mut().zip(live_in.row(successor))
249                {
250                    *word |= incoming;
251                }
252            }
253            for (index, word) in next.iter_mut().enumerate() {
254                *word =
255                    used.row(row)[index] | (live_out.row(row)[index] & !defined.row(row)[index]);
256            }
257            if live_in.row(row) != next.as_slice() {
258                live_in.row_mut(row).copy_from_slice(&next);
259                changed = true;
260            }
261        }
262    }
263    (live_in, live_out)
264}
265
266/// A set of virtual registers for each block.
267#[derive(Debug, Clone)]
268struct Rows {
269    words: Vec<u64>,
270    /// How many words one row is, which is at least one so that a row is a slice rather than
271    /// nothing.
272    width: usize,
273}
274
275impl Rows {
276    fn new(rows: usize, columns: usize) -> Self {
277        let width = columns.div_ceil(64).max(1);
278        Self { words: vec![0; rows * width], width }
279    }
280
281    fn row(&self, row: usize) -> &[u64] {
282        &self.words[row * self.width..(row + 1) * self.width]
283    }
284
285    fn row_mut(&mut self, row: usize) -> &mut [u64] {
286        &mut self.words[row * self.width..(row + 1) * self.width]
287    }
288
289    /// The column a register is, or nothing for a physical one, which this does not track.
290    fn column(&self, reg: Reg) -> Option<usize> {
291        let number = usize::try_from(reg.number()?).ok()?;
292        (number < self.width * 64).then_some(number)
293    }
294
295    fn insert(&mut self, row: usize, reg: Reg) {
296        if let Some(column) = self.column(reg) {
297            self.row_mut(row)[column / 64] |= 1 << (column % 64);
298        }
299    }
300
301    fn contains(&self, row: usize, reg: Reg) -> bool {
302        self.column(reg)
303            .is_some_and(|column| self.row(row)[column / 64] & (1 << (column % 64)) != 0)
304    }
305
306    fn remove(&mut self, row: usize, reg: Reg) {
307        if let Some(column) = self.column(reg) {
308            self.row_mut(row)[column / 64] &= !(1 << (column % 64));
309        }
310    }
311
312    fn iter(&self, row: usize) -> impl Iterator<Item = Reg> + '_ {
313        self.row(row).iter().enumerate().flat_map(|(word, &bits)| {
314            (0..64).filter(move |bit| bits & (1 << bit) != 0).map(move |bit| {
315                Reg::virtual_reg(u32::try_from(word * 64 + bit).expect("a register number"))
316            })
317        })
318    }
319}
320
321#[cfg(test)]
322mod tests {
323    use rucc_base::Interner;
324    use rucc_mir::{BlockCall, Opcode, Operand};
325    use rucc_target::x86_64::GPR;
326
327    use super::*;
328
329    /// The registers live in or out of a block, in order, which is what an assertion reads.
330    fn regs(of: impl Iterator<Item = Reg>) -> Vec<u32> {
331        of.filter_map(Reg::number).collect()
332    }
333
334    #[test]
335    fn a_value_is_live_from_where_it_is_written_to_where_it_is_last_read() {
336        let mut names = Interner::new();
337        let mut func = Func::new(names.intern("f"));
338        let opcode = Opcode::new(names.intern("x64.nop"));
339        let block = func.create_block();
340        let value = func.new_vreg(GPR);
341        let other = func.new_vreg(GPR);
342        let write = func.build(block, opcode).def(value, GPR).finish();
343        let idle = func.build(block, opcode).def(other, GPR).finish();
344        let read = func.build(block, opcode).uses(value, GPR).finish();
345
346        let order = Order::of(&func);
347        let live = Live::of(&func, &order);
348        let range = live.range(value).expect("the value is live somewhere");
349        assert_eq!(range, Range { start: order.late(write), end: order.early(read) });
350        assert!(range.covers(order.early(idle)));
351        // A value nothing reads is live where it was written and nowhere else, because the
352        // register it went to was not free at that instant either.
353        assert_eq!(
354            live.range(other),
355            Some(Range { start: order.late(idle), end: order.late(idle) })
356        );
357        assert!(!range.overlaps(Range { start: order.late(read), end: order.late(read) }));
358        assert_eq!(regs(live.live_in(block)), Vec::<u32>::new());
359    }
360
361    #[test]
362    fn a_value_read_in_another_block_is_live_between_them() {
363        let mut names = Interner::new();
364        let mut func = Func::new(names.intern("f"));
365        let opcode = Opcode::new(names.intern("x64.nop"));
366        let head = func.create_block();
367        let middle = func.create_block();
368        let tail = func.create_block();
369        let value = func.new_vreg(GPR);
370        func.build(head, opcode).def(value, GPR).finish();
371        *func.succs_mut(head) = vec![BlockCall::to(middle)];
372        *func.succs_mut(middle) = vec![BlockCall::to(tail)];
373        let read = func.build(tail, opcode).uses(value, GPR).finish();
374
375        let order = Order::of(&func);
376        let live = Live::of(&func, &order);
377        // The block in between never mentions it and it is live all the way through, which is
378        // the whole reason this is a fixpoint over the blocks and not a walk over the code.
379        assert_eq!(regs(live.live_in(middle)), vec![0]);
380        assert_eq!(regs(live.live_out(middle)), vec![0]);
381        assert!(live.range(value).expect("live somewhere").covers(order.start(middle)));
382        assert_eq!(live.range(value).expect("live somewhere").end, order.early(read));
383    }
384
385    #[test]
386    fn a_range_covers_a_block_the_value_never_reaches_and_being_live_there_does_not() {
387        let mut names = Interner::new();
388        let mut func = Func::new(names.intern("f"));
389        let opcode = Opcode::new(names.intern("x64.nop"));
390        let entry = func.create_block();
391        let arm = func.create_block();
392        let tail = func.create_block();
393        let value = func.new_vreg(GPR);
394        func.build(entry, opcode).def(value, GPR).finish();
395        *func.succs_mut(entry) = vec![BlockCall::to(arm), BlockCall::to(tail)];
396        let idle = func.build(arm, opcode).finish();
397        func.build(tail, opcode).uses(value, GPR).finish();
398
399        let order = Order::of(&func);
400        let live = Live::of(&func, &order);
401        // The arm is written between the two blocks the value is live in, so the range covers it
402        // and the value is nowhere near it. Both are true and they answer different questions.
403        assert!(live.range(value).expect("live somewhere").covers(order.early(idle)));
404        assert!(live.anywhere_in(value, entry));
405        assert!(live.anywhere_in(value, tail));
406        assert!(!live.anywhere_in(value, arm));
407    }
408
409    #[test]
410    fn a_value_carried_round_a_loop_is_live_round_all_of_it() {
411        let mut names = Interner::new();
412        let mut func = Func::new(names.intern("f"));
413        let opcode = Opcode::new(names.intern("x64.nop"));
414        let header = func.create_block();
415        let body = func.create_block();
416        let carried = func.append_param(header, GPR);
417        let next = func.new_vreg(GPR);
418        *func.succs_mut(header) = vec![BlockCall::to(body)];
419        func.build(body, opcode).def(next, GPR).uses(carried, GPR).finish();
420        *func.succs_mut(body) = vec![BlockCall::with(header, vec![next])];
421
422        let order = Order::of(&func);
423        let live = Live::of(&func, &order);
424        // The parameter arrives in the header, so the header does not want it from anybody, and
425        // the body does.
426        assert_eq!(regs(live.live_in(header)), Vec::<u32>::new());
427        assert_eq!(regs(live.live_in(body)), vec![carried.number().expect("virtual")]);
428        let range = live.range(next).expect("live somewhere");
429        assert_eq!(range.end, order.end(body));
430    }
431
432    #[test]
433    fn two_values_that_are_never_both_wanted_do_not_overlap() {
434        let mut names = Interner::new();
435        let mut func = Func::new(names.intern("f"));
436        let opcode = Opcode::new(names.intern("x64.nop"));
437        let block = func.create_block();
438        let first = func.new_vreg(GPR);
439        let second = func.new_vreg(GPR);
440        let write = func.build(block, opcode).def(first, GPR).finish();
441        func.build(block, opcode).def(second, GPR).uses(first, GPR).finish();
442
443        let order = Order::of(&func);
444        let live = Live::of(&func, &order);
445        let first = live.range(first).expect("live somewhere");
446        let second = live.range(second).expect("live somewhere");
447        // The second instruction reads the first value and writes its own, and it reads before
448        // it writes, so the two can be the same register. That is what a two address instruction
449        // needs to be true and it is a fact about the points rather than about the opcode.
450        assert!(!first.overlaps(second));
451        assert!(first.start > order.start(block));
452        assert_eq!(first.start, order.late(write));
453    }
454
455    #[test]
456    fn an_operand_written_early_is_wanted_where_the_operands_are_read() {
457        let mut names = Interner::new();
458        let mut func = Func::new(names.intern("f"));
459        let opcode = Opcode::new(names.intern("x64.nop"));
460        let block = func.create_block();
461        let source = func.new_vreg(GPR);
462        let early = func.new_vreg(GPR);
463        func.build(block, opcode).def(source, GPR).finish();
464        func.build(block, opcode)
465            .operand(Operand::write_early(early, GPR))
466            .operand(Operand::read(source, GPR))
467            .finish();
468
469        let order = Order::of(&func);
470        let live = Live::of(&func, &order);
471        let source = live.range(source).expect("live somewhere");
472        let early = live.range(early).expect("live somewhere");
473        // This is the difference between a division and an addition. The register the answer is
474        // going to is destroyed before the divisor is read, so the divisor may not be in it.
475        assert!(source.overlaps(early));
476    }
477
478    #[test]
479    fn a_register_a_memory_operand_names_is_read_like_any_other() {
480        use rucc_mir::Mem;
481
482        let mut names = Interner::new();
483        let mut func = Func::new(names.intern("f"));
484        let opcode = Opcode::new(names.intern("x64.nop"));
485        let block = func.create_block();
486        let address = func.new_vreg(GPR);
487        let write = func.build(block, opcode).def(address, GPR).finish();
488        let load = func.build(block, opcode).mem(Mem::at(Operand::read(address, GPR))).finish();
489
490        let order = Order::of(&func);
491        let live = Live::of(&func, &order);
492        assert_eq!(
493            live.range(address),
494            Some(Range { start: order.late(write), end: order.early(load) })
495        );
496    }
497}