Skip to main content

rucc_regalloc/
order.rs

1//! The linear order the allocator works over, and the positions in it.
2//!
3//! Design: `spec/10-backend.md` section 10.4.
4//!
5//! A live range is an interval, and an interval needs the program laid out in a line. The line
6//! is the function's own block order, because `spec/10-backend.md` section 10.3 says the `-O0`
7//! path does no block layout beyond preserving the order it was given, and because that is the
8//! order the encoder will write the blocks out in. An allocator that worked over some other
9//! order would be allocating for a program nobody emits.
10//!
11//! # Where the points are
12//!
13//! Every instruction has two of them. An operand is read at the first and written at the second,
14//! which is what makes a two address instruction possible at all: the register a value is read
15//! from is free by the time the result is written, so the two can be the same register, and an
16//! operand written early is written at the first point instead, where it collides with every
17//! operand read there. That is the whole meaning of an early definition and it is the reason the
18//! points come in pairs rather than one to an instruction.
19//!
20//! Every block has two more. The parameters arrive at the point in front of its first
21//! instruction, and the arguments its terminator carries away are read at the point after its
22//! last, which is where the moves that write the successor's parameters will go. Neither is an
23//! instruction, and both are places where something is live.
24
25use rucc_mir::{Block, Func, Inst};
26
27/// A place in the function, counted along the line the blocks make.
28pub type Point = u32;
29
30/// The blocks in the order they are emitted in, and the point every part of them is at.
31#[derive(Debug, Clone)]
32pub struct Order {
33    blocks: Vec<Block>,
34    /// The point a block's parameters arrive at, by block index.
35    start: Vec<Point>,
36    /// The point a block's outgoing arguments are read at, by block index.
37    end: Vec<Point>,
38    /// The point an instruction reads at, by instruction index. It writes at the one after.
39    early: Vec<Point>,
40    points: Point,
41}
42
43impl Order {
44    /// Lays a function out.
45    #[must_use]
46    pub fn of(func: &Func) -> Self {
47        let mut order = Self {
48            blocks: Vec::with_capacity(func.block_count()),
49            start: vec![0; func.block_count()],
50            end: vec![0; func.block_count()],
51            early: vec![0; func.inst_count()],
52            points: 0,
53        };
54        let mut point = 0;
55        for block in func.blocks() {
56            order.blocks.push(block);
57            order.start[block.index()] = point;
58            point += 1;
59            for inst in func.insts(block) {
60                order.early[inst.index()] = point;
61                point += 2;
62            }
63            order.end[block.index()] = point;
64            point += 1;
65        }
66        order.points = point;
67        order
68    }
69
70    /// The blocks, in the order they are emitted in.
71    #[must_use]
72    pub fn blocks(&self) -> &[Block] {
73        &self.blocks
74    }
75
76    /// How many points the function has, which is what a table indexed by point is sized
77    /// against.
78    #[must_use]
79    pub fn points(&self) -> Point {
80        self.points
81    }
82
83    /// Where a block's parameters arrive.
84    #[must_use]
85    pub fn start(&self, block: Block) -> Point {
86        self.start[block.index()]
87    }
88
89    /// Where a block's outgoing arguments are read, which is after everything in it.
90    #[must_use]
91    pub fn end(&self, block: Block) -> Point {
92        self.end[block.index()]
93    }
94
95    /// Where a block's parameters arrive and where its outgoing arguments are read, or `None` for a
96    /// block the function did not have when it was laid out.
97    #[must_use]
98    pub fn bounds(&self, block: Block) -> Option<(Point, Point)> {
99        // A block that was laid out ends after it starts, since its parameters and its outgoing
100        // arguments are two points, and one that was not has both left at zero.
101        let (start, end) = (*self.start.get(block.index())?, *self.end.get(block.index())?);
102        (end > start).then_some((start, end))
103    }
104
105    /// Where an instruction reads its operands, and where it writes the ones it writes early.
106    #[must_use]
107    pub fn early(&self, inst: Inst) -> Point {
108        self.early[inst.index()]
109    }
110
111    /// Where an instruction writes its results.
112    #[must_use]
113    pub fn late(&self, inst: Inst) -> Point {
114        self.early[inst.index()] + 1
115    }
116}
117
118#[cfg(test)]
119mod tests {
120    use rucc_base::Interner;
121    use rucc_mir::{BlockCall, Opcode};
122
123    use super::*;
124
125    /// A function of two blocks, the first with two instructions in it and the second with one.
126    fn func() -> (Func, Vec<Block>, Vec<Inst>) {
127        let mut names = Interner::new();
128        let mut func = Func::new(names.intern("f"));
129        let opcode = Opcode::new(names.intern("x64.nop"));
130        let head = func.create_block();
131        let tail = func.create_block();
132        let first = func.build(head, opcode).finish();
133        let second = func.build(head, opcode).finish();
134        *func.succs_mut(head) = vec![BlockCall::to(tail)];
135        let third = func.build(tail, opcode).finish();
136        (func, vec![head, tail], vec![first, second, third])
137    }
138
139    #[test]
140    fn the_order_is_the_one_the_function_is_written_in() {
141        let (func, blocks, _) = func();
142        assert_eq!(Order::of(&func).blocks(), blocks);
143    }
144
145    #[test]
146    fn an_instruction_reads_before_it_writes() {
147        let (func, _, insts) = func();
148        let order = Order::of(&func);
149        for &inst in &insts {
150            assert!(order.early(inst) < order.late(inst));
151        }
152    }
153
154    #[test]
155    fn nothing_in_a_function_shares_a_point_with_anything_else() {
156        let (func, blocks, insts) = func();
157        let order = Order::of(&func);
158        let mut points: Vec<Point> = Vec::new();
159        for &block in &blocks {
160            points.push(order.start(block));
161            points.push(order.end(block));
162        }
163        for &inst in &insts {
164            points.push(order.early(inst));
165            points.push(order.late(inst));
166        }
167        points.sort_unstable();
168        let count = points.len();
169        points.dedup();
170        assert_eq!(points.len(), count);
171        assert_eq!(order.points(), Point::try_from(count).expect("a small function"));
172    }
173
174    #[test]
175    fn a_block_holds_everything_in_it() {
176        let (func, blocks, insts) = func();
177        let order = Order::of(&func);
178        for &block in &blocks {
179            for inst in func.insts(block) {
180                assert!(order.start(block) < order.early(inst));
181                assert!(order.late(inst) < order.end(block));
182            }
183        }
184        // And one block ends before the next one starts.
185        assert!(order.end(blocks[0]) < order.start(blocks[1]));
186        assert!(order.late(insts[1]) < order.early(insts[2]));
187    }
188}