Skip to main content

rucc_regalloc/
pressure.rs

1//! How many values want a register at each point of a function, and how many registers there are
2//! to put them in.
3//!
4//! Design: `spec/optimizer/39-register-allocation.md` section 39.7, and tamnd/rucc#1177.
5//!
6//! This is the question the spill phase in [`crate::spill`] asks before anything is placed. A
7//! point where more values are live than there are registers is a point where some of them have
8//! to be in memory whatever the assignment does, and knowing that up front means the choice of
9//! which ones can be made by what each costs, over the whole function, instead of by which value
10//! the assignment happened to meet last.
11//!
12//! # What is counted
13//!
14//! A value is counted at every point [`crate::live`] says it is live at, in its own class. A value
15//! an instruction can only read from memory is not counted, since it never wants a register. A
16//! physical register an operand names is not counted either, because it is not a value the
17//! allocator places.
18//!
19//! The room at a point is every register the environment hands out in the class, less the ones an
20//! instruction takes outright there. A call is where that shows: at the point a call writes its
21//! results it destroys every register the convention says it destroys, and only the ones it saves
22//! are left for what lives across it.
23//!
24//! # Why it may say less than the truth and never more
25//!
26//! Both halves are counted so that the difference is a floor. A register a fixed operand claims
27//! for one value is still counted as room, though nothing else may be in it there, and the answer
28//! of a two address instruction is counted from where it is written rather than from where its
29//! source is read. Each of those makes the room look larger or the values look fewer than they
30//! are, so a point this says is over is really over, and it is over by at least as much as this
31//! says. The spill phase leans on that: taking no more values off a point than this says have to
32//! go is never taking off one that could have stayed for want of room there.
33
34use rucc_mir::{Func, Reg};
35use rucc_target::RegClass;
36
37use crate::assign::{self, Env};
38use crate::live::{Area, Live};
39use crate::order::{Order, Point};
40
41/// How many values want a register at each point, and how many registers there are for them.
42#[derive(Debug, Clone, Default)]
43pub struct Pressure {
44    /// By class number, then by point.
45    wanted: Vec<Vec<u32>>,
46    /// By class number, then by point.
47    room: Vec<Vec<u32>>,
48}
49
50impl Pressure {
51    /// Counts the values and the registers at every point of a function.
52    ///
53    /// # Panics
54    ///
55    /// Panics on a function with more points than a table can index, which no machine has the
56    /// memory to have handed it.
57    #[must_use]
58    pub fn of(func: &Func, order: &Order, live: &Live, env: &Env) -> Self {
59        let points = usize::try_from(order.points()).expect("a point count");
60        let mut forced = vec![false; func.vregs()];
61        for reg in assign::forced(func) {
62            forced[index(reg)] = true;
63        }
64        // One more entry than there are points, so the end of a piece that reaches the last point
65        // has somewhere to be taken off at.
66        let mut steps: Vec<Vec<i64>> = Vec::new();
67        for (number, &forced) in forced.iter().enumerate() {
68            let reg = Reg::virtual_reg(u32::try_from(number).expect("a register number"));
69            let (Some(area), Some(class)) = (live.area(reg), func.class_of(reg)) else {
70                continue;
71            };
72            if forced {
73                continue;
74            }
75            let class = usize::from(class.number());
76            if steps.len() <= class {
77                steps.resize_with(class + 1, Vec::new);
78            }
79            let row = &mut steps[class];
80            if row.is_empty() {
81                *row = vec![0; points + 1];
82            }
83            for piece in area.pieces() {
84                row[at(piece.start)] += 1;
85                row[at(piece.end) + 1] -= 1;
86            }
87        }
88        let wanted = steps
89            .into_iter()
90            .map(|row| {
91                let mut count = 0i64;
92                let mut wanted: Vec<u32> = row
93                    .iter()
94                    .map(|step| {
95                        count += step;
96                        u32::try_from(count).expect("a count of values")
97                    })
98                    .collect();
99                wanted.truncate(points);
100                wanted
101            })
102            .collect();
103
104        let mut room: Vec<Vec<u32>> = env
105            .offered()
106            .map(|offered| {
107                let count = u32::try_from(offered.len()).expect("a count of registers");
108                if count == 0 { Vec::new() } else { vec![count; points] }
109            })
110            .collect();
111        // Sorted by class, register and point, so an instruction that names one register twice at
112        // one point is two entries next to each other, and it is one register taken.
113        let mut taken: Vec<_> = assign::blocked(func, order).taken().collect();
114        taken.dedup();
115        for (class, reg, point) in taken {
116            let Some(row) = room.get_mut(usize::from(class.number())) else { continue };
117            if row.is_empty() || !env.order(class).contains(&reg) {
118                continue;
119            }
120            row[at(point)] = row[at(point)].saturating_sub(1);
121        }
122        Self { wanted, room }
123    }
124
125    /// How many values of a class want a register at a point.
126    #[must_use]
127    pub fn wanted(&self, class: RegClass, point: Point) -> u32 {
128        read(&self.wanted, class, point)
129    }
130
131    /// How many registers of a class there are at a point.
132    #[must_use]
133    pub fn room(&self, class: RegClass, point: Point) -> u32 {
134        read(&self.room, class, point)
135    }
136
137    /// How many of the values of a class live at a point have to be in memory there, at the least.
138    #[must_use]
139    pub fn excess(&self, class: RegClass, point: Point) -> u32 {
140        self.wanted(class, point).saturating_sub(self.room(class, point))
141    }
142
143    /// The most values of a class that want a register at any one point.
144    #[must_use]
145    pub fn peak(&self, class: RegClass) -> u32 {
146        row(&self.wanted, class).iter().copied().max().unwrap_or(0)
147    }
148
149    /// The points where more values of a class want a register than there are registers, in order.
150    pub fn over(&self, class: RegClass) -> impl Iterator<Item = Point> + '_ {
151        let wanted = row(&self.wanted, class);
152        (0..wanted.len())
153            .filter_map(|point| u32::try_from(point).ok())
154            .filter(move |&point| self.excess(class, point) > 0)
155    }
156
157    /// Takes a value off every point it is live at, which is what sending it to memory does.
158    pub(crate) fn lift(&mut self, class: RegClass, area: Area<'_>) {
159        let Some(row) = self.wanted.get_mut(usize::from(class.number())) else { return };
160        // A slice per piece rather than a lookup per point, so the loop has no bounds to check and
161        // the compiler can take one off several counts at once.
162        for piece in area.pieces() {
163            let end = at(piece.end).saturating_add(1).min(row.len());
164            let start = at(piece.start).min(end);
165            for count in &mut row[start..end] {
166                *count = count.saturating_sub(1);
167            }
168        }
169    }
170}
171
172fn row(table: &[Vec<u32>], class: RegClass) -> &[u32] {
173    table.get(usize::from(class.number())).map_or(&[], Vec::as_slice)
174}
175
176fn read(table: &[Vec<u32>], class: RegClass, point: Point) -> u32 {
177    row(table, class).get(at(point)).copied().unwrap_or(0)
178}
179
180fn at(point: Point) -> usize {
181    usize::try_from(point).expect("a point")
182}
183
184fn index(reg: Reg) -> usize {
185    usize::try_from(reg.number().expect("a virtual register")).expect("a register number")
186}
187
188#[cfg(test)]
189mod tests {
190    use rucc_base::Interner;
191    use rucc_mir::{Opcode, Operand};
192    use rucc_target::x86_64::{GPR, RAX, SYSV};
193
194    use super::*;
195
196    fn narrow(count: usize) -> Env {
197        Env::new().with(GPR, &SYSV.int_order[..count], &SYSV.int_order[count..count + 1])
198    }
199
200    fn of(func: &Func, env: &Env) -> (Order, Pressure) {
201        let order = Order::of(func);
202        let live = Live::of(func, &order);
203        let pressure = Pressure::of(func, &order, &live, env);
204        (order, pressure)
205    }
206
207    #[test]
208    fn every_value_live_at_a_point_is_counted_there() {
209        let mut names = Interner::new();
210        let mut func = Func::new(names.intern("f"));
211        let opcode = Opcode::new(names.intern("x64.nop"));
212        let block = func.create_block();
213        let values: Vec<Reg> = (0..3).map(|_| func.new_vreg(GPR)).collect();
214        let defs: Vec<_> = values
215            .iter()
216            .map(|&value| func.build(block, opcode).def(value, GPR).finish())
217            .collect();
218        let reads = func.build(block, opcode);
219        let reads = values.iter().fold(reads, |build, &value| build.uses(value, GPR));
220        let last = reads.finish();
221
222        let (order, pressure) = of(&func, &narrow(2));
223        assert_eq!(pressure.wanted(GPR, order.early(last)), 3);
224        assert_eq!(pressure.room(GPR, order.early(last)), 2);
225        assert_eq!(pressure.excess(GPR, order.early(last)), 1);
226        assert_eq!(pressure.peak(GPR), 3);
227        // From where the third is written to where all three are read.
228        let over = [order.late(defs[2]), order.early(last)];
229        assert_eq!(pressure.over(GPR).collect::<Vec<_>>(), over);
230    }
231
232    #[test]
233    fn a_register_an_instruction_writes_outright_is_not_room_where_it_writes_it() {
234        let mut names = Interner::new();
235        let mut func = Func::new(names.intern("f"));
236        let opcode = Opcode::new(names.intern("x64.nop"));
237        let block = func.create_block();
238        let value = func.new_vreg(GPR);
239        func.build(block, opcode).def(value, GPR).finish();
240        let call =
241            func.build(block, opcode).operand(Operand::write(Reg::physical(RAX), GPR)).finish();
242        func.build(block, opcode).uses(value, GPR).finish();
243
244        let (order, pressure) = of(&func, &narrow(2));
245        assert_eq!(pressure.room(GPR, order.early(call)), 2);
246        assert_eq!(pressure.room(GPR, order.late(call)), 1);
247        assert_eq!(pressure.wanted(GPR, order.late(call)), 1);
248        assert_eq!(pressure.over(GPR).count(), 0);
249    }
250
251    #[test]
252    fn a_value_sent_to_memory_is_taken_off_every_point_it_was_live_at() {
253        let mut names = Interner::new();
254        let mut func = Func::new(names.intern("f"));
255        let opcode = Opcode::new(names.intern("x64.nop"));
256        let block = func.create_block();
257        let first = func.new_vreg(GPR);
258        let second = func.new_vreg(GPR);
259        func.build(block, opcode).def(first, GPR).finish();
260        func.build(block, opcode).def(second, GPR).finish();
261        let last = func.build(block, opcode).uses(first, GPR).uses(second, GPR).finish();
262
263        let order = Order::of(&func);
264        let live = Live::of(&func, &order);
265        let mut pressure = Pressure::of(&func, &order, &live, &narrow(1));
266        assert_eq!(pressure.excess(GPR, order.early(last)), 1);
267        pressure.lift(GPR, live.area(first).expect("live"));
268        assert_eq!(pressure.over(GPR).count(), 0);
269        assert_eq!(pressure.wanted(GPR, order.early(last)), 1);
270    }
271}