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        for piece in area.pieces() {
161            for point in piece.start..=piece.end {
162                if let Some(count) = row.get_mut(at(point)) {
163                    *count = count.saturating_sub(1);
164                }
165            }
166        }
167    }
168}
169
170fn row(table: &[Vec<u32>], class: RegClass) -> &[u32] {
171    table.get(usize::from(class.number())).map_or(&[], Vec::as_slice)
172}
173
174fn read(table: &[Vec<u32>], class: RegClass, point: Point) -> u32 {
175    row(table, class).get(at(point)).copied().unwrap_or(0)
176}
177
178fn at(point: Point) -> usize {
179    usize::try_from(point).expect("a point")
180}
181
182fn index(reg: Reg) -> usize {
183    usize::try_from(reg.number().expect("a virtual register")).expect("a register number")
184}
185
186#[cfg(test)]
187mod tests {
188    use rucc_base::Interner;
189    use rucc_mir::{Opcode, Operand};
190    use rucc_target::x86_64::{GPR, RAX, SYSV};
191
192    use super::*;
193
194    fn narrow(count: usize) -> Env {
195        Env::new().with(GPR, &SYSV.int_order[..count], &SYSV.int_order[count..count + 1])
196    }
197
198    fn of(func: &Func, env: &Env) -> (Order, Pressure) {
199        let order = Order::of(func);
200        let live = Live::of(func, &order);
201        let pressure = Pressure::of(func, &order, &live, env);
202        (order, pressure)
203    }
204
205    #[test]
206    fn every_value_live_at_a_point_is_counted_there() {
207        let mut names = Interner::new();
208        let mut func = Func::new(names.intern("f"));
209        let opcode = Opcode::new(names.intern("x64.nop"));
210        let block = func.create_block();
211        let values: Vec<Reg> = (0..3).map(|_| func.new_vreg(GPR)).collect();
212        let defs: Vec<_> = values
213            .iter()
214            .map(|&value| func.build(block, opcode).def(value, GPR).finish())
215            .collect();
216        let reads = func.build(block, opcode);
217        let reads = values.iter().fold(reads, |build, &value| build.uses(value, GPR));
218        let last = reads.finish();
219
220        let (order, pressure) = of(&func, &narrow(2));
221        assert_eq!(pressure.wanted(GPR, order.early(last)), 3);
222        assert_eq!(pressure.room(GPR, order.early(last)), 2);
223        assert_eq!(pressure.excess(GPR, order.early(last)), 1);
224        assert_eq!(pressure.peak(GPR), 3);
225        // From where the third is written to where all three are read.
226        let over = [order.late(defs[2]), order.early(last)];
227        assert_eq!(pressure.over(GPR).collect::<Vec<_>>(), over);
228    }
229
230    #[test]
231    fn a_register_an_instruction_writes_outright_is_not_room_where_it_writes_it() {
232        let mut names = Interner::new();
233        let mut func = Func::new(names.intern("f"));
234        let opcode = Opcode::new(names.intern("x64.nop"));
235        let block = func.create_block();
236        let value = func.new_vreg(GPR);
237        func.build(block, opcode).def(value, GPR).finish();
238        let call =
239            func.build(block, opcode).operand(Operand::write(Reg::physical(RAX), GPR)).finish();
240        func.build(block, opcode).uses(value, GPR).finish();
241
242        let (order, pressure) = of(&func, &narrow(2));
243        assert_eq!(pressure.room(GPR, order.early(call)), 2);
244        assert_eq!(pressure.room(GPR, order.late(call)), 1);
245        assert_eq!(pressure.wanted(GPR, order.late(call)), 1);
246        assert_eq!(pressure.over(GPR).count(), 0);
247    }
248
249    #[test]
250    fn a_value_sent_to_memory_is_taken_off_every_point_it_was_live_at() {
251        let mut names = Interner::new();
252        let mut func = Func::new(names.intern("f"));
253        let opcode = Opcode::new(names.intern("x64.nop"));
254        let block = func.create_block();
255        let first = func.new_vreg(GPR);
256        let second = func.new_vreg(GPR);
257        func.build(block, opcode).def(first, GPR).finish();
258        func.build(block, opcode).def(second, GPR).finish();
259        let last = func.build(block, opcode).uses(first, GPR).uses(second, GPR).finish();
260
261        let order = Order::of(&func);
262        let live = Live::of(&func, &order);
263        let mut pressure = Pressure::of(&func, &order, &live, &narrow(1));
264        assert_eq!(pressure.excess(GPR, order.early(last)), 1);
265        pressure.lift(GPR, live.area(first).expect("live"));
266        assert_eq!(pressure.over(GPR).count(), 0);
267        assert_eq!(pressure.wanted(GPR, order.early(last)), 1);
268    }
269}