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 an instruction reads its operands, and where it writes the ones it writes early.
96    #[must_use]
97    pub fn early(&self, inst: Inst) -> Point {
98        self.early[inst.index()]
99    }
100
101    /// Where an instruction writes its results.
102    #[must_use]
103    pub fn late(&self, inst: Inst) -> Point {
104        self.early[inst.index()] + 1
105    }
106}
107
108#[cfg(test)]
109mod tests {
110    use rucc_base::Interner;
111    use rucc_mir::{BlockCall, Opcode};
112
113    use super::*;
114
115    /// A function of two blocks, the first with two instructions in it and the second with one.
116    fn func() -> (Func, Vec<Block>, Vec<Inst>) {
117        let mut names = Interner::new();
118        let mut func = Func::new(names.intern("f"));
119        let opcode = Opcode::new(names.intern("x64.nop"));
120        let head = func.create_block();
121        let tail = func.create_block();
122        let first = func.build(head, opcode).finish();
123        let second = func.build(head, opcode).finish();
124        *func.succs_mut(head) = vec![BlockCall::to(tail)];
125        let third = func.build(tail, opcode).finish();
126        (func, vec![head, tail], vec![first, second, third])
127    }
128
129    #[test]
130    fn the_order_is_the_one_the_function_is_written_in() {
131        let (func, blocks, _) = func();
132        assert_eq!(Order::of(&func).blocks(), blocks);
133    }
134
135    #[test]
136    fn an_instruction_reads_before_it_writes() {
137        let (func, _, insts) = func();
138        let order = Order::of(&func);
139        for &inst in &insts {
140            assert!(order.early(inst) < order.late(inst));
141        }
142    }
143
144    #[test]
145    fn nothing_in_a_function_shares_a_point_with_anything_else() {
146        let (func, blocks, insts) = func();
147        let order = Order::of(&func);
148        let mut points: Vec<Point> = Vec::new();
149        for &block in &blocks {
150            points.push(order.start(block));
151            points.push(order.end(block));
152        }
153        for &inst in &insts {
154            points.push(order.early(inst));
155            points.push(order.late(inst));
156        }
157        points.sort_unstable();
158        let count = points.len();
159        points.dedup();
160        assert_eq!(points.len(), count);
161        assert_eq!(order.points(), Point::try_from(count).expect("a small function"));
162    }
163
164    #[test]
165    fn a_block_holds_everything_in_it() {
166        let (func, blocks, insts) = func();
167        let order = Order::of(&func);
168        for &block in &blocks {
169            for inst in func.insts(block) {
170                assert!(order.start(block) < order.early(inst));
171                assert!(order.late(inst) < order.end(block));
172            }
173        }
174        // And one block ends before the next one starts.
175        assert!(order.end(blocks[0]) < order.start(blocks[1]));
176        assert!(order.late(insts[1]) < order.early(insts[2]));
177    }
178}