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}