1use rucc_cost::heuristics::LOOP_RESERVED_REGS;
47use rucc_ir::{Block, Func, Type};
48
49use crate::cfg::Cfg;
50use crate::live::Liveness;
51use crate::loops::{LoopId, Loops};
52
53#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
55pub enum Class {
56 Integer,
58 Float,
60}
61
62impl Class {
63 pub const ALL: [Self; 2] = [Self::Integer, Self::Float];
65
66 pub const COUNT: usize = Self::ALL.len();
68
69 #[must_use]
71 pub const fn as_str(self) -> &'static str {
72 match self {
73 Self::Integer => "integer",
74 Self::Float => "float",
75 }
76 }
77
78 #[must_use]
80 pub const fn of(ty: Type) -> Option<Self> {
81 if ty.is_float() {
82 return Some(Self::Float);
83 }
84 if ty.is_vector() {
85 return Some(Self::Float);
87 }
88 if ty.is_int() || ty.is_ptr() || ty.is_cap() {
89 return Some(Self::Integer);
90 }
91 None
93 }
94
95 const fn index(self) -> usize {
96 match self {
97 Self::Integer => 0,
98 Self::Float => 1,
99 }
100 }
101}
102
103impl std::fmt::Display for Class {
104 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
105 f.write_str(self.as_str())
106 }
107}
108
109type PerClass = [u32; Class::COUNT];
111
112#[derive(Debug, Clone, PartialEq, Eq)]
114pub struct Pressure {
115 arriving: Vec<PerClass>,
116 most_in: Vec<PerClass>,
117 most: PerClass,
118}
119
120impl Pressure {
121 #[must_use]
123 pub fn of(func: &Func, cfg: &Cfg, live: &Liveness) -> Self {
124 let blocks = cfg.capacity();
125 let mut arriving = vec![[0; Class::COUNT]; blocks];
126 let mut most_in = vec![[0; Class::COUNT]; blocks];
127 let mut most = [0; Class::COUNT];
128
129 for block in cfg.reverse_postorder() {
130 let at = block.index();
131 for value in live.live_in(block) {
132 if let Some(class) = Class::of(func[value].ty) {
133 arriving[at][class.index()] += 1;
134 }
135 }
136 let mut here = [0; Class::COUNT];
139 for value in live.live_out(block) {
140 if let Some(class) = Class::of(func[value].ty) {
141 here[class.index()] += 1;
142 }
143 }
144 most_in[at] = here;
145 live.through(func, block, |_, at_inst| {
146 let mut counted = [0; Class::COUNT];
147 for value in at_inst.iter() {
148 if let Some(class) = Class::of(func[value].ty) {
149 counted[class.index()] += 1;
150 }
151 }
152 for class in Class::ALL {
153 let index = class.index();
154 most_in[at][index] = most_in[at][index].max(counted[index]);
155 }
156 });
157 for class in Class::ALL {
158 let index = class.index();
159 most[index] = most[index].max(most_in[at][index]);
160 }
161 }
162
163 Self { arriving, most_in, most }
164 }
165
166 #[must_use]
168 pub fn arriving_at(&self, block: Block, class: Class) -> u32 {
169 self.arriving[block.index()][class.index()]
170 }
171
172 #[must_use]
174 pub fn most_in_block(&self, block: Block, class: Class) -> u32 {
175 self.most_in[block.index()][class.index()]
176 }
177
178 #[must_use]
182 pub fn most_in_function(&self, class: Class) -> u32 {
183 self.most[class.index()]
184 }
185
186 #[must_use]
191 pub fn most_in_loop(&self, loops: &Loops, id: LoopId, class: Class) -> u32 {
192 loops.blocks(id).iter().map(|&block| self.most_in_block(block, class)).max().unwrap_or(0)
193 }
194
195 #[must_use]
202 pub fn is_tight(&self, loops: &Loops, id: LoopId, class: Class, allocatable: u32) -> bool {
203 self.most_in_loop(loops, id, class) >= allocatable.saturating_sub(LOOP_RESERVED_REGS)
204 }
205
206 #[must_use]
212 pub fn problems(&self, cfg: &Cfg) -> Vec<String> {
213 let mut problems = Vec::new();
214 for block in cfg.reverse_postorder() {
215 for class in Class::ALL {
216 let here = self.most_in_block(block, class);
217 let whole = self.most_in_function(class);
218 if here > whole {
219 problems.push(format!(
220 "block{} needs {here} {class} registers and the function claims {whole}",
221 block.index()
222 ));
223 }
224 if self.arriving_at(block, class) > here {
225 problems.push(format!(
226 "block{} has more {class} values arriving than it ever holds",
227 block.index()
228 ));
229 }
230 }
231 }
232 problems
233 }
234}
235
236#[cfg(test)]
237mod tests {
238 use rucc_base::Interner;
239 use rucc_ir::{Block, Builder, Flags, Float, Func, Opcode, Signature, Type};
240
241 use super::{Class, Pressure};
242 use crate::cfg::Cfg;
243 use crate::dom::Dominators;
244 use crate::live::Liveness;
245 use crate::loops::Loops;
246
247 const I32: Type = Type::int(32);
248
249 fn blank(count: usize) -> (Func, Vec<Block>) {
250 let mut names = Interner::new();
251 let mut func = Func::new(names.intern("f"), Signature::new());
252 let blocks: Vec<Block> = (0..count).map(|_| func.create_block()).collect();
253 (func, blocks)
254 }
255
256 fn pressure(func: &Func) -> (Cfg, Pressure) {
257 let cfg = Cfg::new(func);
258 let live = Liveness::of(func, &cfg);
259 let of = Pressure::of(func, &cfg, &live);
260 assert!(of.problems(&cfg).is_empty(), "{:?}", of.problems(&cfg));
261 (cfg, of)
262 }
263
264 #[test]
265 fn the_most_live_at_once_is_the_number_of_registers_the_block_needs() {
266 let (mut func, blocks) = blank(1);
268 let mut build = Builder::new(&mut func, blocks[0]);
269 let one = build.iconst(I32, 1);
270 let two = build.iconst(I32, 2);
271 let three = build.iconst(I32, 3);
272 let first = build.binary(Opcode::Add, one, two, Flags::NONE);
273 let second = build.binary(Opcode::Add, first, three, Flags::NONE);
274 build.ret(&[second]);
275
276 let (_, of) = pressure(&func);
277 assert_eq!(of.most_in_block(blocks[0], Class::Integer), 3);
278 assert_eq!(of.most_in_function(Class::Integer), 3);
279 assert_eq!(of.most_in_function(Class::Float), 0);
280 }
281
282 #[test]
283 fn the_two_classes_are_counted_apart_because_they_are_two_banks_of_registers() {
284 let (mut func, blocks) = blank(1);
285 let mut build = Builder::new(&mut func, blocks[0]);
286 let whole = build.iconst(I32, 1);
287 let fraction = build.fconst(Type::float(Float::F64), 0);
288 let other = build.fconst(Type::float(Float::F64), 1);
289 let sum = build.binary(Opcode::FAdd, fraction, other, Flags::NONE);
290 build.ret(&[whole, sum]);
291
292 let (_, of) = pressure(&func);
293 assert_eq!(of.most_in_function(Class::Integer), 1);
294 assert_eq!(of.most_in_function(Class::Float), 2);
295 }
296
297 #[test]
298 fn nothing_that_is_not_held_in_a_register_is_counted() {
299 let (mut func, blocks) = blank(1);
301 let mut build = Builder::new(&mut func, blocks[0]);
302 let mem = build.mem_entry();
303 build.ret(&[]);
304 let ty = func[mem].ty;
305
306 assert!(ty.is_mem());
307 assert_eq!(Class::of(ty), None);
308 assert_eq!(Class::of(Type::VOID), None);
309 assert_eq!(Class::of(Type::PTR), Some(Class::Integer));
310 assert_eq!(Class::of(Type::vector(I32, 4)), Some(Class::Float));
311
312 let (_, of) = pressure(&func);
313 assert_eq!(of.most_in_function(Class::Integer), 0);
314 }
315
316 #[test]
317 fn a_value_read_after_the_loop_costs_a_register_everywhere_inside_it() {
318 let (mut func, blocks) = blank(3);
320 let mut build = Builder::new(&mut func, blocks[0]);
321 let kept = build.iconst(I32, 7);
322 let cond = build.iconst(Type::I1, 1);
323 build.jump(blocks[1], &[]);
324 let mut build = Builder::new(&mut func, blocks[1]);
325 build.br_if(cond, blocks[1], &[], blocks[2], &[]);
326 let mut build = Builder::new(&mut func, blocks[2]);
327 build.ret(&[kept]);
328
329 let (cfg, of) = pressure(&func);
330 let doms = Dominators::new(&cfg);
331 let loops = Loops::new(&cfg, &doms);
332 let id = loops.innermost(blocks[1]).expect("block 1 is a loop");
333 assert_eq!(of.most_in_loop(&loops, id, Class::Integer), 2);
335 assert!(of.is_tight(&loops, id, Class::Integer, 4), "two of four, less a margin of two");
336 assert!(!of.is_tight(&loops, id, Class::Integer, 16), "there is room on a real machine");
337 }
338
339 #[test]
340 fn the_margin_is_what_stops_a_hoist_from_walking_up_to_the_edge() {
341 let (mut func, blocks) = blank(2);
342 let mut build = Builder::new(&mut func, blocks[0]);
343 let cond = build.iconst(Type::I1, 1);
344 build.jump(blocks[1], &[]);
345 let mut build = Builder::new(&mut func, blocks[1]);
346 build.br_if(cond, blocks[1], &[], blocks[0], &[]);
347
348 let (cfg, of) = pressure(&func);
349 let doms = Dominators::new(&cfg);
350 let loops = Loops::new(&cfg, &doms);
351 let id = loops.innermost(blocks[1]).expect("block 1 is a loop");
352 assert_eq!(of.most_in_loop(&loops, id, Class::Integer), 1);
353 assert!(of.is_tight(&loops, id, Class::Integer, 3));
356 assert!(!of.is_tight(&loops, id, Class::Integer, 4));
357 assert!(of.is_tight(&loops, id, Class::Integer, 1));
359 }
360
361 #[test]
362 fn a_block_control_never_reaches_needs_nothing() {
363 let (mut func, blocks) = blank(2);
364 let mut build = Builder::new(&mut func, blocks[0]);
365 let one = build.iconst(I32, 1);
366 build.ret(&[one]);
367 let mut build = Builder::new(&mut func, blocks[1]);
368 let two = build.iconst(I32, 2);
369 build.ret(&[two]);
370
371 let (cfg, of) = pressure(&func);
372 assert!(!cfg.reaches(blocks[1]));
373 assert_eq!(of.most_in_block(blocks[1], Class::Integer), 0);
374 assert_eq!(of.arriving_at(blocks[1], Class::Integer), 0);
375 }
376
377 #[test]
378 fn what_arrives_at_a_block_is_never_more_than_the_block_ever_holds() {
379 let (mut func, blocks) = blank(3);
380 let mut build = Builder::new(&mut func, blocks[0]);
381 let kept = build.iconst(I32, 7);
382 let cond = build.iconst(Type::I1, 1);
383 build.br_if(cond, blocks[1], &[], blocks[2], &[]);
384 let mut build = Builder::new(&mut func, blocks[1]);
385 build.ret(&[kept]);
386 let mut build = Builder::new(&mut func, blocks[2]);
387 build.ret(&[]);
388
389 let (cfg, of) = pressure(&func);
390 assert!(of.problems(&cfg).is_empty());
391 for block in cfg.reverse_postorder() {
392 for class in Class::ALL {
393 assert!(of.arriving_at(block, class) <= of.most_in_block(block, class));
394 assert!(of.most_in_block(block, class) <= of.most_in_function(class));
395 }
396 }
397 assert_eq!(of.arriving_at(blocks[1], Class::Integer), 1, "the value it returns");
398 assert_eq!(of.arriving_at(blocks[2], Class::Integer), 0, "this arm reads nothing");
399 }
400
401 #[test]
402 fn every_class_names_itself_and_there_are_only_the_two() {
403 assert_eq!(Class::ALL.len(), Class::COUNT);
404 for class in Class::ALL {
405 assert!(!class.as_str().is_empty());
406 assert_eq!(class.to_string(), class.as_str());
407 }
408 assert_ne!(Class::Integer, Class::Float);
409 }
410}