1use rucc_cost::heuristics::{MAX_PREDICTED_ITERATIONS, PROFILE_SUM_TOLERANCE_PERCENT};
47use rucc_ir::{Block, Func};
48
49use crate::cfg::Cfg;
50use crate::loops::{LoopId, Loops};
51use crate::predict::{Callees, Predictions};
52use crate::profile::{Frequency, Probability, Quality};
53
54#[derive(Debug, Clone, PartialEq, Eq)]
60pub struct Frequencies {
61 told: Predictions,
62 of: Vec<Frequency>,
63 reliable: Vec<bool>,
64 capped: Vec<bool>,
65 cyclic: Vec<Probability>,
66 entry: Frequency,
67}
68
69impl Frequencies {
70 #[must_use]
76 pub fn of(func: &Func, cfg: &Cfg, loops: &Loops, callees: &Callees) -> Self {
77 let told = Predictions::of(func, cfg, loops, callees);
78 let width = cfg.capacity();
79 let cyclic = cyclic_probabilities(cfg, loops, &told);
80 let mut of = vec![Frequency::NEVER; width];
81 let mut reliable = vec![true; width];
82 let mut capped = vec![false; width];
83
84 let Some(entry) = cfg.entry() else {
85 return Self { told, of, reliable, capped, cyclic, entry: Frequency::UNKNOWN };
86 };
87 of[entry.index()] = Frequency::ENTRY;
88
89 for block in cfg.reverse_postorder() {
90 if block != entry {
91 let mut total = Frequency::NEVER;
92 let mut sound = !loops.is_irreducible(block);
93 for &pred in cfg.predecessors(block) {
94 if !forward(cfg, pred, block) {
95 continue;
96 }
97 total = total.plus(of[pred.index()].along(edge(&told, cfg, pred, block)));
98 sound = sound && reliable[pred.index()];
99 }
100 of[block.index()] = total;
101 reliable[block.index()] = sound;
102 }
103 let Some(id) = heads(loops, block) else { continue };
104 let again = cyclic[id.index()];
105 of[block.index()] = of[block.index()].repeated_while(again, MAX_PREDICTED_ITERATIONS);
106 capped[block.index()] = is_capped(again);
107 }
108
109 let entry = of[entry.index()];
110 Self { told, of, reliable, capped, cyclic, entry }
111 }
112
113 #[must_use]
115 pub fn told(&self) -> &Predictions {
116 &self.told
117 }
118
119 #[must_use]
123 pub fn taken(&self, block: Block, index: usize) -> Probability {
124 self.told.taken(block, index)
125 }
126
127 #[must_use]
129 pub fn get(&self, block: Block) -> Frequency {
130 self.of.get(block.index()).copied().unwrap_or(Frequency::UNKNOWN)
131 }
132
133 #[must_use]
135 pub fn entry(&self) -> Frequency {
136 self.entry
137 }
138
139 #[must_use]
145 pub fn is_reliable(&self, block: Block) -> bool {
146 self.reliable.get(block.index()).copied().unwrap_or(false)
147 }
148
149 #[must_use]
154 pub fn is_capped(&self, block: Block) -> bool {
155 self.capped.get(block.index()).copied().unwrap_or(false)
156 }
157
158 #[must_use]
160 pub fn is_hot(&self, block: Block) -> bool {
161 self.get(block).is_hot_in_function(self.entry)
162 }
163
164 #[must_use]
166 pub fn cyclic(&self, id: LoopId) -> Probability {
167 self.cyclic.get(id.index()).copied().unwrap_or_else(Probability::never)
168 }
169
170 #[must_use]
176 pub fn iterations(&self, id: LoopId) -> u32 {
177 let once = Frequency::ENTRY.repeated_while(self.cyclic(id), MAX_PREDICTED_ITERATIONS);
178 let count = once.raw() / u64::from(Probability::SCALE);
179 u32::try_from(count).unwrap_or(MAX_PREDICTED_ITERATIONS)
180 }
181
182 #[must_use]
184 pub fn hottest(&self, func: &Func) -> Option<Block> {
185 func.blocks().max_by_key(|&block| self.get(block).raw())
186 }
187
188 #[must_use]
206 pub fn problems(&self, func: &Func, cfg: &Cfg) -> Vec<String> {
207 let mut problems = Vec::new();
208 let entry = cfg.entry();
209 for block in func.blocks() {
210 let out: u32 = self.told.edges(block).iter().map(|edge| edge.parts()).sum();
211 if !self.told.edges(block).is_empty() && out != Probability::SCALE {
212 problems.push(format!(
213 "the edges out of {block:?} are taken {out} parts in {} of the time",
214 Probability::SCALE
215 ));
216 }
217 if Some(block) == entry || self.is_capped(block) || !self.is_reliable(block) {
218 continue;
219 }
220 let mut arriving = Frequency::NEVER;
221 let mut edges = 0;
222 for &pred in cfg.predecessors(block) {
223 arriving = arriving.plus(self.get(pred).along(edge(&self.told, cfg, pred, block)));
224 edges += 1;
225 }
226 let here = self.get(block);
227 let apart = here.raw().abs_diff(arriving.raw());
228 let allowed = here.raw() / 100 * u64::from(PROFILE_SUM_TOLERANCE_PERCENT) + edges;
231 if apart > allowed {
232 problems.push(format!(
233 "{block:?} runs at {here} and the paths into it add up to {arriving}"
234 ));
235 }
236 }
237 problems
238 }
239}
240
241fn cyclic_probabilities(cfg: &Cfg, loops: &Loops, told: &Predictions) -> Vec<Probability> {
247 let mut cyclic = vec![Probability::never(); loops.count()];
248 let mut relative = vec![Frequency::NEVER; cfg.capacity()];
249 let order: Vec<LoopId> = loops.all().collect();
250
251 for &id in order.iter().rev() {
252 let header = loops.header(id);
253 let mut inside: Vec<Block> = loops.blocks(id).to_vec();
254 inside.sort_by_key(|&block| cfg.rank(block));
255 for &block in &inside {
256 relative[block.index()] = Frequency::NEVER;
257 }
258 relative[header.index()] = Frequency::ENTRY;
259
260 for &block in &inside {
261 if block != header {
262 let mut total = Frequency::NEVER;
263 for &pred in cfg.predecessors(block) {
264 if !loops.contains(id, pred) || !forward(cfg, pred, block) {
267 continue;
268 }
269 total = total.plus(relative[pred.index()].along(edge(told, cfg, pred, block)));
270 }
271 relative[block.index()] = total;
272 }
273 let Some(inner) = heads(loops, block) else { continue };
274 if inner == id {
275 continue;
276 }
277 let again = cyclic[inner.index()];
278 relative[block.index()] =
279 relative[block.index()].repeated_while(again, MAX_PREDICTED_ITERATIONS);
280 }
281
282 let mut round = Frequency::NEVER;
283 for &latch in loops.latches(id) {
284 round = round.plus(relative[latch.index()].along(edge(told, cfg, latch, header)));
285 }
286 let parts = u32::try_from(round.raw()).unwrap_or(Probability::SCALE);
287 cyclic[id.index()] = Probability::new(parts, round.quality().min(Quality::Guessed));
288 }
289 cyclic
290}
291
292fn heads(loops: &Loops, block: Block) -> Option<LoopId> {
294 let id = loops.innermost(block)?;
295 (loops.header(id) == block).then_some(id)
296}
297
298fn forward(cfg: &Cfg, from: Block, to: Block) -> bool {
304 match (cfg.rank(from), cfg.rank(to)) {
305 (Some(from), Some(to)) => from < to,
306 _ => false,
307 }
308}
309
310fn edge(told: &Predictions, cfg: &Cfg, from: Block, to: Block) -> Probability {
312 match cfg.successors(from).iter().position(|&block| block == to) {
313 Some(at) => told.taken(from, at),
314 None => Probability::never(),
315 }
316}
317
318fn is_capped(again: Probability) -> bool {
320 let stop = Probability::SCALE - again.parts().min(Probability::SCALE);
321 stop <= Probability::SCALE.div_ceil(MAX_PREDICTED_ITERATIONS)
322}
323
324#[cfg(test)]
325mod tests {
326 use rucc_base::Interner;
327 use rucc_ir::{Block, Builder, Func, Signature, Type};
328
329 use super::Frequencies;
330 use crate::cfg::Cfg;
331 use crate::dom::Dominators;
332 use crate::loops::Loops;
333 use crate::predict::Callees;
334 use crate::profile::{Frequency, Probability, Quality};
335
336 const ONE: u64 = Probability::SCALE as u64;
338
339 fn frequencies(func: &Func) -> (Frequencies, Cfg, Loops) {
341 let cfg = Cfg::new(func);
342 let doms = Dominators::new(&cfg);
343 let loops = Loops::new(&cfg, &doms);
344 let of = Frequencies::of(func, &cfg, &loops, &Callees::nothing());
345 assert!(of.problems(func, &cfg).is_empty(), "{:?}", of.problems(func, &cfg));
346 (of, cfg, loops)
347 }
348
349 fn blank(blocks: usize) -> (Interner, Func, Vec<Block>) {
351 let mut names = Interner::new();
352 let mut func = Func::new(names.intern("f"), Signature::new());
353 let list = (0..blocks).map(|_| func.create_block()).collect();
354 (names, func, list)
355 }
356
357 fn ret(func: &mut Func, block: Block) {
359 let mut build = Builder::new(func, block);
360 let zero = build.iconst(Type::int(32), 0);
361 build.ret(&[zero]);
362 }
363
364 fn line() -> (Func, Vec<Block>) {
366 let (_, mut func, at) = blank(3);
367 Builder::new(&mut func, at[0]).jump(at[1], &[]);
368 Builder::new(&mut func, at[1]).jump(at[2], &[]);
369 ret(&mut func, at[2]);
370 (func, at)
371 }
372
373 fn fork() -> (Func, Vec<Block>) {
375 let (_, mut func, at) = blank(4);
376 let mut build = Builder::new(&mut func, at[0]);
377 let cond = build.iconst(Type::int(1), 1);
378 build.br_if(cond, at[1], &[], at[2], &[]);
379 Builder::new(&mut func, at[1]).jump(at[3], &[]);
380 Builder::new(&mut func, at[2]).jump(at[3], &[]);
381 ret(&mut func, at[3]);
382 (func, at)
383 }
384
385 fn loop_shape() -> (Func, Vec<Block>) {
387 let (_, mut func, at) = blank(4);
388 Builder::new(&mut func, at[0]).jump(at[1], &[]);
389 let mut build = Builder::new(&mut func, at[1]);
390 let cond = build.iconst(Type::int(1), 1);
391 build.br_if(cond, at[2], &[], at[3], &[]);
392 Builder::new(&mut func, at[2]).jump(at[1], &[]);
393 ret(&mut func, at[3]);
394 (func, at)
395 }
396
397 fn nest() -> (Func, Vec<Block>) {
400 let (_, mut func, at) = blank(6);
401 Builder::new(&mut func, at[0]).jump(at[1], &[]);
402 for (test, stay, leave) in [(at[1], at[2], at[5]), (at[2], at[3], at[4])] {
403 let mut build = Builder::new(&mut func, test);
404 let cond = build.iconst(Type::int(1), 1);
405 build.br_if(cond, stay, &[], leave, &[]);
406 }
407 Builder::new(&mut func, at[3]).jump(at[2], &[]);
408 Builder::new(&mut func, at[4]).jump(at[1], &[]);
409 ret(&mut func, at[5]);
410 (func, at)
411 }
412
413 #[test]
414 fn a_straight_line_runs_once_and_that_is_not_a_guess() {
415 let (func, at) = line();
416 let (of, ..) = frequencies(&func);
417 for block in at {
418 assert_eq!(of.get(block).raw(), ONE, "{block:?}");
419 assert_eq!(of.get(block).quality(), Quality::Precise);
420 }
421 }
422
423 #[test]
424 fn the_arms_of_a_branch_nobody_predicted_run_half_the_time_each() {
425 let (func, at) = fork();
426 let (of, ..) = frequencies(&func);
427 assert_eq!(of.get(at[1]).raw(), ONE / 2);
428 assert_eq!(of.get(at[2]).raw(), ONE / 2);
429 assert_eq!(of.get(at[3]).raw(), ONE);
431 assert_eq!(of.get(at[3]).quality(), Quality::Guessed);
433 }
434
435 #[test]
436 fn a_loop_body_runs_as_many_times_as_the_series_says() {
437 let (func, at) = loop_shape();
438 let (of, _, loops) = frequencies(&func);
439 let id = loops.all().next().expect("a loop");
440 assert_eq!(of.cyclic(id), Probability::percent(89, Quality::Guessed));
442 assert_eq!(of.taken(at[1], 0), of.cyclic(id));
444 assert_eq!(of.get(at[1]).raw(), ONE * ONE / 1_100);
445 assert_eq!(of.iterations(id), 9);
446 assert_eq!(of.get(at[2]).raw(), of.get(at[1]).along(of.cyclic(id)).raw());
448 assert!(of.get(at[3]).raw().abs_diff(ONE) < ONE / 100, "{}", of.get(at[3]));
450 }
451
452 #[test]
453 fn a_loop_inside_a_loop_multiplies() {
454 let (func, at) = nest();
455 let (of, _, loops) = frequencies(&func);
456 let mut all = loops.all();
457 let outer = all.next().expect("the outer loop");
458 let inner = all.next().expect("the inner loop");
459 assert_eq!(loops.header(outer), at[1]);
460 assert_eq!(loops.header(inner), at[2]);
461 assert_eq!(of.iterations(outer), 9);
463 assert_eq!(of.iterations(inner), 9);
464 let round = u64::from(of.iterations(outer) * of.iterations(inner));
465 assert!(of.get(at[3]).raw() > round * ONE * 3 / 4, "{}", of.get(at[3]));
466 assert!(of.get(at[5]).raw().abs_diff(ONE) < ONE / 100, "{}", of.get(at[5]));
468 }
469
470 #[test]
471 fn a_loop_nothing_predicts_an_exit_for_gets_the_cap_rather_than_a_division_by_zero() {
472 let (_, mut func, at) = blank(2);
473 Builder::new(&mut func, at[0]).jump(at[1], &[]);
474 Builder::new(&mut func, at[1]).jump(at[1], &[]);
475 let (of, _, loops) = frequencies(&func);
476 let id = loops.all().next().expect("a loop");
477 assert_eq!(of.cyclic(id).parts(), Probability::SCALE);
478 assert!(of.is_capped(at[1]));
479 assert_eq!(of.iterations(id), 100);
480 assert_eq!(of.get(at[1]).raw(), ONE * 100);
481 }
482
483 #[test]
484 fn a_frequency_in_an_irreducible_region_says_it_does_not_mean_anything() {
485 let (_, mut func, at) = blank(3);
486 let mut build = Builder::new(&mut func, at[0]);
487 let cond = build.iconst(Type::int(1), 1);
488 build.br_if(cond, at[1], &[], at[2], &[]);
489 Builder::new(&mut func, at[1]).jump(at[2], &[]);
490 Builder::new(&mut func, at[2]).jump(at[1], &[]);
491 let (of, ..) = frequencies(&func);
492 assert!(of.is_reliable(at[0]));
493 assert!(!of.is_reliable(at[1]), "a two entry cycle has no header and no series");
494 assert!(!of.is_reliable(at[2]));
495 }
496
497 #[test]
498 fn a_block_nothing_reaches_never_runs_and_is_not_hot() {
499 let (_, mut func, at) = blank(3);
500 Builder::new(&mut func, at[0]).jump(at[1], &[]);
501 ret(&mut func, at[1]);
502 ret(&mut func, at[2]);
503 let (of, ..) = frequencies(&func);
504 assert_eq!(of.get(at[2]), Frequency::NEVER);
505 assert!(!of.is_hot(at[2]));
506 assert!(of.is_hot(at[0]));
507 }
508
509 #[test]
510 fn the_hottest_block_of_a_loop_is_the_one_in_it() {
511 let (func, at) = loop_shape();
512 let (of, ..) = frequencies(&func);
513 assert_eq!(of.hottest(&func), Some(at[1]));
514 assert!(of.is_hot(at[2]));
515 assert_eq!(of.entry(), Frequency::ENTRY);
516 }
517
518 #[test]
519 fn what_arrives_at_a_block_adds_up_to_the_block_which_is_the_check_section_11_5_asks_for() {
520 for (func, _) in [line(), fork(), loop_shape(), nest()] {
522 let cfg = Cfg::new(&func);
523 let doms = Dominators::new(&cfg);
524 let loops = Loops::new(&cfg, &doms);
525 let mut of = Frequencies::of(&func, &cfg, &loops, &Callees::nothing());
526 assert!(of.problems(&func, &cfg).is_empty());
527 let last = func.blocks().last().expect("a block");
530 of.of[last.index()] = Frequency::times(7, Quality::Precise);
531 let complaints = of.problems(&func, &cfg);
532 assert_eq!(complaints.len(), 1, "{complaints:?}");
533 assert!(complaints[0].contains("add up to"), "{}", complaints[0]);
534 }
535 }
536}