Skip to main content

rucc_opt/
predict.rs

1//! Static branch prediction: which way a branch goes, when there is no profile that says.
2//!
3//! Design: section 11.2 of `spec/optimizer/11-profile-and-frequency.md`.
4//!
5//! # Ten predictors and not fifty five
6//!
7//! GCC has fifty five, in `gcc/predict.def`, each naming a syntactic situation and the rate at
8//! which the guess turned out right when somebody measured it. Ten of those are for Fortran, and
9//! a long tail of the rest sit below sixty five percent. Section 11.2 keeps ten: the ones that
10//! survive both cuts. A predictor at fifty nine percent moves a probability nine points off even,
11//! and nothing downstream of a frequency decides differently over nine points, so it costs a
12//! branch of code here and buys nothing.
13//!
14//! The numbers themselves are Ball and Larus's and Wu and Larus's, from the middle of the 1990s,
15//! and they have held up because they are facts about how people write programs rather than about
16//! any machine. They live in [`rucc_cost::heuristics`] with the document that argued for them, the
17//! way section 40.12 says every threshold has to.
18//!
19//! # First match
20//!
21//! The predictors are ordered and the first one that applies decides. GCC computes both this and a
22//! Dempster-Shafer combination of every predictor that applies, and uses first match by default;
23//! this does the part GCC uses. The order is the order section 11.2 lists them in and it is the
24//! part of this file most worth getting right, because it is where the predictors disagree that
25//! the order is doing anything at all. `__builtin_expect` is first because a user who wrote it
26//! meant it, and the `cold` attribute is near the top for the same reason.
27//!
28//! # What a prediction is worth
29//!
30//! Every probability out of here is [`Quality::Guessed`], with one exception: a block with one
31//! way out takes it, and that is [`Quality::Precise`] because it is not a guess. So a function
32//! with no branches in it gets precise frequencies, which is the right answer and comes out of the
33//! arithmetic rather than out of a special case.
34//!
35//! # Where the noreturn predictor gets its answer
36//!
37//! Two places, and only the first needs a call graph. The IR says it directly: the front end emits
38//! [`Opcode::Unreachable`] after a call to a `noreturn` function, so a block from which no `return`
39//! is reachable is a block control does not come back from, and that is a walk backwards from the
40//! returns. The other place is the callee's own attributes, which are per function and not at the
41//! call site, so a caller that has the module hands them over in [`Callees`]. A function pass that
42//! has only its function passes [`Callees::nothing`] and keeps the first answer, which is most of
43//! what the predictor was for: C error handling is `if (x) { report(); abort(); }` and it is the
44//! `abort` that shows up as unreachable.
45
46use std::collections::HashMap;
47
48use rucc_base::Symbol;
49use rucc_cost::heuristics::{
50    PREDICT_CALL_NOT_TAKEN, PREDICT_COLD_CALL, PREDICT_CONTINUE_TAKEN, PREDICT_EXPECT,
51    PREDICT_LOOP_EXIT_NOT_TAKEN, PREDICT_LOOP_GUARD_TAKEN, PREDICT_NEGATIVE_RETURN,
52    PREDICT_NEVER_RETURNS, PREDICT_NULL_RETURN, PREDICT_POINTER_NOT_NULL, PREDICT_RETURN_BLOCKS,
53};
54use rucc_ir::{AttrSet, Attrs, Block, Def, Extra, Func, Inst, IntPred, Module, Opcode, Value};
55
56use crate::cfg::Cfg;
57use crate::fold::constant;
58use crate::loops::Loops;
59use crate::profile::{Probability, Quality};
60
61/// Which predictor decided a branch.
62///
63/// In the order they are asked, which is section 11.2's order, so a comparison between two of
64/// these says which one wins where both apply.
65#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
66pub enum Predictor {
67    /// `__builtin_expect` named an arm.
68    Expect,
69    /// One arm does not come back.
70    NeverReturns,
71    /// One arm calls a function the user marked `cold`.
72    ColdCall,
73    /// One arm leaves the loop and the other stays in it.
74    LoopExit,
75    /// The branch decides whether to run a loop at all.
76    LoopGuard,
77    /// The condition compares a pointer against null.
78    PointerNotNull,
79    /// One arm returns a negative constant.
80    NegativeReturn,
81    /// One arm returns a null pointer.
82    NullReturn,
83    /// One arm contains a call and the other does not.
84    CallNotTaken,
85    /// One arm goes back to the top of the loop.
86    Continue,
87    /// Nothing applied, so the arms are even.
88    Nothing,
89}
90
91impl Predictor {
92    /// How it reads in a dump.
93    #[must_use]
94    pub const fn as_str(self) -> &'static str {
95        match self {
96            Self::Expect => "__builtin_expect",
97            Self::NeverReturns => "the arm that does not come back",
98            Self::ColdCall => "the arm that calls a cold function",
99            Self::LoopExit => "the loop exit",
100            Self::LoopGuard => "the loop guard",
101            Self::PointerNotNull => "the pointer is not null",
102            Self::NegativeReturn => "the arm that returns a negative number",
103            Self::NullReturn => "the arm that returns null",
104            Self::CallNotTaken => "the arm that calls something",
105            Self::Continue => "the continue",
106            Self::Nothing => "nothing, so even",
107        }
108    }
109
110    /// The rate at which it was measured right, in percent, and fifty for no prediction at all.
111    #[must_use]
112    pub const fn hit_rate(self) -> u32 {
113        match self {
114            Self::Expect => PREDICT_EXPECT,
115            Self::NeverReturns => PREDICT_NEVER_RETURNS,
116            Self::ColdCall => PREDICT_COLD_CALL,
117            Self::LoopExit => PREDICT_LOOP_EXIT_NOT_TAKEN,
118            Self::LoopGuard => PREDICT_LOOP_GUARD_TAKEN,
119            Self::PointerNotNull => PREDICT_POINTER_NOT_NULL,
120            Self::NegativeReturn => PREDICT_NEGATIVE_RETURN,
121            Self::NullReturn => PREDICT_NULL_RETURN,
122            Self::CallNotTaken => PREDICT_CALL_NOT_TAKEN,
123            Self::Continue => PREDICT_CONTINUE_TAKEN,
124            // Even, which is the absence of a prediction rather than one, and not a number
125            // anybody would tune.
126            Self::Nothing => 50,
127        }
128    }
129
130    /// The ten, in the order they are asked.
131    pub const ORDER: [Self; 10] = [
132        Self::Expect,
133        Self::NeverReturns,
134        Self::ColdCall,
135        Self::LoopExit,
136        Self::LoopGuard,
137        Self::PointerNotNull,
138        Self::NegativeReturn,
139        Self::NullReturn,
140        Self::CallNotTaken,
141        Self::Continue,
142    ];
143}
144
145impl std::fmt::Display for Predictor {
146    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
147        f.write_str(self.as_str())
148    }
149}
150
151/// What the predictors know about the functions this one calls.
152///
153/// A call site carries the callee's name and the signature it is called with, and not the callee's
154/// attributes, because the attributes belong to the callee and there is one of it and many call
155/// sites. So whoever has the module builds this once and hands it over. A caller that does not
156/// have one passes [`Callees::nothing`], which answers no to everything and costs the two
157/// predictors that read it.
158#[derive(Debug, Clone, Default)]
159pub struct Callees {
160    known: HashMap<Symbol, AttrSet>,
161}
162
163impl Callees {
164    /// Nothing known about anything.
165    #[must_use]
166    pub fn nothing() -> Self {
167        Self::default()
168    }
169
170    /// Every function in the module, by name, with what it promises.
171    ///
172    /// Declarations count and are most of the value: `abort` is declared and not defined, and it
173    /// is the one the predictor most wants to know about.
174    #[must_use]
175    pub fn of_module(module: &Module) -> Self {
176        let mut known = HashMap::new();
177        for id in module.funcs() {
178            let func = &module[id];
179            known.insert(func.name, func.attrs.set);
180        }
181        Self { known }
182    }
183
184    /// Records what one function promises, for a caller assembling this by hand.
185    pub fn record(&mut self, name: Symbol, attrs: Attrs) {
186        self.known.insert(name, attrs.set);
187    }
188
189    /// Whether control does not come back from a call to it.
190    #[must_use]
191    pub fn never_returns(&self, name: Symbol) -> bool {
192        self.known.get(&name).is_some_and(|set| set.contains(AttrSet::NORETURN))
193    }
194
195    /// Whether the user said it is rarely called.
196    #[must_use]
197    pub fn is_cold(&self, name: Symbol) -> bool {
198        self.known.get(&name).is_some_and(|set| set.contains(AttrSet::COLD))
199    }
200}
201
202/// How likely each edge out of each block is.
203///
204/// Indexed the way the graph is: [`Predictions::edges`] gives one probability for each block in
205/// [`Cfg::successors`], in that order. They sum to exactly [`Probability::SCALE`] for every block
206/// that has any, which is what the frequency computation in section 11.3 needs and what the test
207/// at the bottom of this file checks on every shape it builds.
208#[derive(Debug, Clone, PartialEq, Eq)]
209pub struct Predictions {
210    edges: Vec<Vec<Probability>>,
211    by: Vec<Predictor>,
212}
213
214impl Predictions {
215    /// Predicts every branch in the function.
216    ///
217    /// Linear in the blocks and the edges, except for the two return value predictors, which walk
218    /// forward from an arm over blocks with one way out and stop after
219    /// [`PREDICT_RETURN_BLOCKS`] of them.
220    #[must_use]
221    pub fn of(func: &Func, cfg: &Cfg, loops: &Loops, callees: &Callees) -> Self {
222        let width = cfg.capacity();
223        let mut edges: Vec<Vec<Probability>> = vec![Vec::new(); width];
224        let mut by = vec![Predictor::Nothing; width];
225        let returns = returning(func, cfg);
226
227        for block in func.blocks() {
228            let Some(term) = func.terminator(block) else { continue };
229            let succs = cfg.successors(block);
230            if succs.len() == 2 && func[term].opcode == Opcode::BrIf {
231                let (taken, who) = branch(func, cfg, loops, callees, &returns, block);
232                edges[block.index()] = vec![taken, taken.complement()];
233                by[block.index()] = who;
234                continue;
235            }
236            let (parts, who) = share(func, cfg, callees, &returns, block, term);
237            edges[block.index()] = parts;
238            by[block.index()] = who;
239        }
240
241        Self { edges, by }
242    }
243
244    /// The probability of each edge out of this block, in [`Cfg::successors`] order.
245    #[must_use]
246    pub fn edges(&self, block: Block) -> &[Probability] {
247        self.edges.get(block.index()).map_or(&[], Vec::as_slice)
248    }
249
250    /// The probability of the edge at that position among this block's successors.
251    ///
252    /// Zero for an edge that is not there, because the chance of taking an edge that does not
253    /// exist is not a guess.
254    #[must_use]
255    pub fn taken(&self, block: Block, index: usize) -> Probability {
256        self.edges(block).get(index).copied().unwrap_or_else(Probability::never)
257    }
258
259    /// Which predictor decided this block's branch.
260    #[must_use]
261    pub fn by(&self, block: Block) -> Predictor {
262        self.by.get(block.index()).copied().unwrap_or(Predictor::Nothing)
263    }
264}
265
266/// The probability of the first arm, given which arm the predictor thinks is taken.
267fn toward(first: bool, percent: u32) -> Probability {
268    let likely = Probability::percent(percent, Quality::Guessed);
269    if first { likely } else { likely.complement() }
270}
271
272/// Predicts a two armed branch, first match, in section 11.2's order.
273///
274/// The answer is the probability of the first successor, which for a `br_if` is the arm taken when
275/// the condition is one. The second gets the complement, so the two sum to certainty exactly.
276fn branch(
277    func: &Func,
278    cfg: &Cfg,
279    loops: &Loops,
280    callees: &Callees,
281    returns: &[bool],
282    block: Block,
283) -> (Probability, Predictor) {
284    let succs = cfg.successors(block);
285    let (first, second) = (succs[0], succs[1]);
286    let term = func.terminator(block).expect("a block with successors has a terminator");
287    let cond = *func[func[term].args].first().expect("a br_if has a condition");
288
289    if let Some(taken) = claimed(func, term) {
290        return (taken, Predictor::Expect);
291    }
292
293    let gone = |at: Block| never_comes_back(func, callees, returns, at);
294    if gone(first) != gone(second) {
295        return (toward(!gone(first), PREDICT_NEVER_RETURNS), Predictor::NeverReturns);
296    }
297
298    let cold = |at: Block| calls_named(func, at, |name| callees.is_cold(name));
299    if cold(first) != cold(second) {
300        return (toward(!cold(first), PREDICT_COLD_CALL), Predictor::ColdCall);
301    }
302
303    let leaves = |at: Block| match loops.innermost(block) {
304        Some(id) => !loops.contains(id, at),
305        None => false,
306    };
307    if leaves(first) != leaves(second) {
308        return (toward(!leaves(first), PREDICT_LOOP_EXIT_NOT_TAKEN), Predictor::LoopExit);
309    }
310
311    let enters = |at: Block| enters_loop(cfg, loops, block, at);
312    if enters(first) != enters(second) {
313        return (toward(enters(first), PREDICT_LOOP_GUARD_TAKEN), Predictor::LoopGuard);
314    }
315
316    if let Some(taken) = pointer_null(func, cond) {
317        return (taken, Predictor::PointerNotNull);
318    }
319
320    let gives = |at: Block| returns_constant(func, cfg, at);
321    let negative = |at: Block| matches!(gives(at), Some(Returned::Negative));
322    if negative(first) != negative(second) {
323        return (toward(!negative(first), PREDICT_NEGATIVE_RETURN), Predictor::NegativeReturn);
324    }
325    let null = |at: Block| matches!(gives(at), Some(Returned::Null));
326    if null(first) != null(second) {
327        return (toward(!null(first), PREDICT_NULL_RETURN), Predictor::NullReturn);
328    }
329
330    let calls = |at: Block| has_call(func, at);
331    if calls(first) != calls(second) {
332        return (toward(!calls(first), PREDICT_CALL_NOT_TAKEN), Predictor::CallNotTaken);
333    }
334
335    let again = |at: Block| goes_round_again(loops, block, at);
336    if again(first) != again(second) {
337        return (toward(again(first), PREDICT_CONTINUE_TAKEN), Predictor::Continue);
338    }
339
340    (Probability::even(), Predictor::Nothing)
341}
342
343/// Splits a block's outgoing probability when it is not a two armed branch.
344///
345/// A jump takes its one edge, and that is a certainty rather than a guess. A `switch` and an
346/// `indirect_br` split evenly, weighted by how many edges name each successor, because a block two
347/// labels lead to is reached two ways. The one prediction that still applies is the noreturn one:
348/// a `switch` arm that aborts is as unlikely here as it is on a branch, and the arms that come back
349/// share what is left.
350fn share(
351    func: &Func,
352    cfg: &Cfg,
353    callees: &Callees,
354    returns: &[bool],
355    block: Block,
356    term: Inst,
357) -> (Vec<Probability>, Predictor) {
358    let succs = cfg.successors(block);
359    if succs.is_empty() {
360        return (Vec::new(), Predictor::Nothing);
361    }
362    if succs.len() == 1 {
363        return (vec![Probability::always()], Predictor::Nothing);
364    }
365    if let Some(split) = hinted(func, succs, term) {
366        return (split, Predictor::Expect);
367    }
368
369    let mut weight = vec![0u64; succs.len()];
370    for call in func.successors(term) {
371        if let Some(at) = succs.iter().position(|&block| block == call.block) {
372            weight[at] += 1;
373        }
374    }
375    let gone: Vec<bool> =
376        succs.iter().map(|&at| never_comes_back(func, callees, returns, at)).collect();
377
378    let total = |side: bool| -> u64 {
379        weight.iter().zip(&gone).filter(|&(_, &away)| away == side).map(|(w, _)| *w).sum()
380    };
381    let whole = u64::from(Probability::SCALE);
382    let mut parts = vec![0u32; succs.len()];
383    let who = if total(true) == 0 || total(false) == 0 {
384        // Every arm comes back or none of them does, and either way there is nothing true of one
385        // of them that is not true of all of them. The side with the weight takes everything.
386        hand_out(whole, &weight, &gone, total(false) == 0, &mut parts);
387        Predictor::Nothing
388    } else {
389        let budget = u64::from(
390            Probability::percent(PREDICT_NEVER_RETURNS, Quality::Guessed).complement().parts(),
391        );
392        hand_out(budget, &weight, &gone, true, &mut parts);
393        hand_out(whole - budget, &weight, &gone, false, &mut parts);
394        Predictor::NeverReturns
395    };
396
397    let split = parts.into_iter().map(|parts| Probability::new(parts, Quality::Guessed)).collect();
398    (split, who)
399}
400
401/// Divides a budget between the successors on one side of a question, in proportion to how many
402/// edges lead to each.
403///
404/// What the division leaves over goes to the first of them, so the parts add up to the budget
405/// exactly. A budget of nothing is a group that gets nothing and is not an error: a switch whose
406/// every arm aborts has no arm to give the other side's share to.
407fn hand_out(budget: u64, weight: &[u64], gone: &[bool], side: bool, parts: &mut [u32]) {
408    let total: u64 =
409        weight.iter().zip(gone).filter(|&(_, &away)| away == side).map(|(w, _)| *w).sum();
410    if total == 0 || budget == 0 {
411        return;
412    }
413    let mut spent = 0;
414    let mut first = None;
415    for (at, &w) in weight.iter().enumerate() {
416        if gone[at] != side {
417            continue;
418        }
419        let share = budget * w / total;
420        parts[at] = u32::try_from(share).unwrap_or(Probability::SCALE);
421        spent += share;
422        if first.is_none() {
423            first = Some(at);
424        }
425    }
426    if let Some(at) = first {
427        parts[at] += u32::try_from(budget - spent).unwrap_or(0);
428    }
429}
430
431/// What the branch itself says about how often its first arm is taken, if anything does.
432///
433/// This is where `__builtin_expect` arrives. The front end writes an [`Opcode::Expect`] holding the
434/// value and what the program says it will be, and `crate::expect` moves the claim onto the arms of
435/// the branch and takes the instruction away, so by the time anything predicts anything the hint is
436/// a number on the edge rather than a node to chase the condition back to. A profile will write the
437/// same number in the same place, which is the point of it being there.
438///
439/// The quality is [`Quality::Guessed`] because a hint is what somebody expected and not what
440/// anybody measured. Section 11.2 says the same thing about the number itself, which is ninety
441/// percent rather than certainty: the other arm still has to run correctly.
442fn claimed(func: &Func, term: Inst) -> Option<Probability> {
443    let at = func.target_list(term).iter().next()?;
444    let parts = func[at].hint.taken()?;
445    Some(Probability::new(parts, Quality::Guessed))
446}
447
448/// What the arms of a `switch` say about themselves, when every one of them says something and the
449/// hints add up to certainty.
450///
451/// Summed by where each arm goes, the way the even split weights a block two labels lead to. All of
452/// them or none, because a hint on some arms says nothing about how the others divide what is left,
453/// and that is a guess the even split already makes better.
454fn hinted(func: &Func, succs: &[Block], term: Inst) -> Option<Vec<Probability>> {
455    let mut parts = vec![0u32; succs.len()];
456    for call in func.successors(term) {
457        let taken = call.hint.taken()?;
458        let at = succs.iter().position(|&block| block == call.block)?;
459        parts[at] += taken;
460    }
461    if parts.iter().sum::<u32>() != Probability::SCALE {
462        return None;
463    }
464    Some(parts.into_iter().map(|parts| Probability::new(parts, Quality::Guessed)).collect())
465}
466
467/// The prediction a comparison of a pointer against null makes, if that is what the condition is.
468fn pointer_null(func: &Func, cond: Value) -> Option<Probability> {
469    let Def::Result { inst, .. } = func[cond].def else { return None };
470    let data = &func[inst];
471    if data.opcode != Opcode::ICmp {
472        return None;
473    }
474    let Extra::IntPred(pred) = data.extra else { return None };
475    let args = &func[data.args];
476    let lhs = *args.first()?;
477    let rhs = *args.get(1)?;
478    // Exactly one side null. Both sides null is a comparison of two constants, which simplify-cfg
479    // answers properly rather than guessing at.
480    if is_null(func, lhs) == is_null(func, rhs) {
481        return None;
482    }
483    match pred {
484        IntPred::Eq => Some(toward(false, PREDICT_POINTER_NOT_NULL)),
485        IntPred::Ne => Some(toward(true, PREDICT_POINTER_NOT_NULL)),
486        _ => None,
487    }
488}
489
490/// Whether this value is a null pointer.
491///
492/// Which is `int_to_ptr` of a zero, because that is what `crates/rucc-lower/src/body.rs` writes for
493/// one: `iconst` produces an integer and never a pointer, so a pointer constant is always a
494/// conversion of an integer one.
495fn is_null(func: &Func, value: Value) -> bool {
496    if !func[value].ty.is_ptr() {
497        return false;
498    }
499    let Def::Result { inst, .. } = func[value].def else { return false };
500    if func[inst].opcode != Opcode::IntToPtr {
501        return false;
502    }
503    let Some(&arg) = func[func[inst].args].first() else { return false };
504    match constant(func, arg) {
505        Some((value, ty)) => value.signed(ty) == 0,
506        None => false,
507    }
508}
509
510/// What the two return value predictors found at the end of an arm.
511#[derive(Debug, Clone, Copy, PartialEq, Eq)]
512enum Returned {
513    /// A negative constant, which in C means the call failed.
514    Negative,
515    /// A null pointer.
516    Null,
517    /// A constant that is neither.
518    Other,
519}
520
521/// What this arm returns, if it goes straight to a `return` of a constant.
522///
523/// Forward over blocks with one way out, stopping after [`PREDICT_RETURN_BLOCKS`] of them. GCC
524/// propagates the prediction backwards from the return over every path that reaches it, which
525/// needs the paths. This finds `if (bad) return -1;` and the two or three statements somebody put
526/// in front of the return, which is the shape the predictor was measured on.
527fn returns_constant(func: &Func, cfg: &Cfg, start: Block) -> Option<Returned> {
528    let mut at = start;
529    for _ in 0..PREDICT_RETURN_BLOCKS {
530        let term = func.terminator(at)?;
531        if func[term].opcode == Opcode::Return {
532            let &value = func[func[term].args].first()?;
533            if is_null(func, value) {
534                return Some(Returned::Null);
535            }
536            let (value, ty) = constant(func, value)?;
537            return Some(if value.signed(ty) < 0 { Returned::Negative } else { Returned::Other });
538        }
539        match cfg.successors(at) {
540            [only] => at = *only,
541            _ => return None,
542        }
543    }
544    None
545}
546
547/// Whether control comes back from this block at all.
548///
549/// Two questions in one, because they have the same answer and the same consequence: whether a
550/// `return` is reachable from here, and whether the block calls something the callee's own
551/// attributes say does not come back.
552fn never_comes_back(func: &Func, callees: &Callees, returns: &[bool], block: Block) -> bool {
553    !returns[block.index()] || calls_named(func, block, |name| callees.never_returns(name))
554}
555
556/// Whether this block holds a direct call to a function the predicate accepts.
557///
558/// A call through a pointer is never one, because there is no name to ask about.
559fn calls_named(func: &Func, block: Block, mut ok: impl FnMut(Symbol) -> bool) -> bool {
560    func.insts(block).any(|inst| {
561        let data = &func[inst];
562        if !matches!(data.opcode, Opcode::Call | Opcode::TailCall) {
563            return false;
564        }
565        let Extra::Call(at) = data.extra else { return false };
566        match func[at].callee {
567            Some(name) => ok(name),
568            None => false,
569        }
570    })
571}
572
573/// Whether this block calls anything at all, by name or through a pointer.
574fn has_call(func: &Func, block: Block) -> bool {
575    func.insts(block).any(|inst| {
576        matches!(func[inst].opcode, Opcode::Call | Opcode::TailCall | Opcode::CallIndirect)
577    })
578}
579
580/// Whether taking this edge runs a loop the branch is outside of.
581///
582/// The header itself, or the one block in front of it, because a guard the front end wrote usually
583/// branches to a preheader rather than to the header.
584fn enters_loop(cfg: &Cfg, loops: &Loops, from: Block, at: Block) -> bool {
585    if heads_a_loop(loops, from, at) {
586        return true;
587    }
588    match cfg.successors(at) {
589        [only] => heads_a_loop(loops, from, *only),
590        _ => false,
591    }
592}
593
594/// Whether this block is the header of a loop the other block is not in.
595fn heads_a_loop(loops: &Loops, from: Block, at: Block) -> bool {
596    let Some(id) = loops.innermost(at) else { return false };
597    loops.header(id) == at && !loops.contains(id, from)
598}
599
600/// Whether this edge is a `continue`, which is a jump back to the top from inside the body.
601fn goes_round_again(loops: &Loops, from: Block, at: Block) -> bool {
602    match loops.innermost(from) {
603        Some(id) => loops.header(id) == at,
604        None => false,
605    }
606}
607
608/// Which blocks a `return` is reachable from.
609///
610/// Backwards from every block that ends in one. What this answers is the noreturn question without
611/// a call graph: a block from which no return is reachable either aborts or spins forever, and in
612/// C the first is nearly always what it is. A block with no terminator is a function under
613/// construction and counts as not returning, which costs nothing because a pass asking this has a
614/// function the verifier has already accepted.
615fn returning(func: &Func, cfg: &Cfg) -> Vec<bool> {
616    let mut yes = vec![false; cfg.capacity()];
617    let mut stack = Vec::new();
618    for block in func.blocks() {
619        let Some(term) = func.terminator(block) else { continue };
620        if matches!(func[term].opcode, Opcode::Return | Opcode::TailCall) {
621            yes[block.index()] = true;
622            stack.push(block);
623        }
624    }
625    while let Some(block) = stack.pop() {
626        for &pred in cfg.predecessors(block) {
627            if !yes[pred.index()] {
628                yes[pred.index()] = true;
629                stack.push(pred);
630            }
631        }
632    }
633    yes
634}
635
636#[cfg(test)]
637mod tests {
638    use rucc_base::Interner;
639    use rucc_ir::{
640        AttrSet, Attrs, Block, BlockCall, Builder, Func, Hint, IntPred, Opcode, Signature, Type,
641    };
642
643    use super::{Callees, Predictions, Predictor};
644    use crate::cfg::Cfg;
645    use crate::dom::Dominators;
646    use crate::loops::Loops;
647    use crate::profile::{Probability, Quality};
648
649    /// The three analyses a prediction is read against.
650    fn shape(func: &Func) -> (Cfg, Loops) {
651        let cfg = Cfg::new(func);
652        let doms = Dominators::new(&cfg);
653        let loops = Loops::new(&cfg, &doms);
654        (cfg, loops)
655    }
656
657    /// Predicts with nothing known about any callee, which is what a function pass has.
658    fn predict(func: &Func) -> (Predictions, Cfg) {
659        let (cfg, loops) = shape(func);
660        let seen = Predictions::of(func, &cfg, &loops, &Callees::nothing());
661        (seen, cfg)
662    }
663
664    /// A function with `n` blocks and a name to call things by.
665    fn blank(blocks: usize) -> (Interner, Func, Vec<Block>) {
666        let mut names = Interner::new();
667        let mut func = Func::new(names.intern("f"), Signature::new());
668        let list = (0..blocks).map(|_| func.create_block()).collect();
669        (names, func, list)
670    }
671
672    #[test]
673    fn a_block_with_one_way_out_takes_it_and_that_is_not_a_guess() {
674        let (_, mut func, at) = blank(2);
675        Builder::new(&mut func, at[0]).jump(at[1], &[]);
676        let mut build = Builder::new(&mut func, at[1]);
677        let zero = build.iconst(Type::int(32), 0);
678        build.ret(&[zero]);
679
680        let (seen, _) = predict(&func);
681        assert_eq!(seen.edges(at[0]).len(), 1);
682        assert_eq!(seen.taken(at[0], 0), Probability::always());
683        assert_eq!(seen.taken(at[0], 0).quality(), Quality::Precise);
684        // The block that returns has no edges at all, and an edge that is not there is not taken.
685        assert!(seen.edges(at[1]).is_empty());
686        assert_eq!(seen.taken(at[1], 0), Probability::never());
687    }
688
689    #[test]
690    fn the_arm_that_does_not_come_back_is_the_one_not_taken() {
691        // `if (x) abort();` as the front end leaves it, which is a branch to a block ending in
692        // `unreachable`. No call graph is needed to see it.
693        let (_, mut func, at) = blank(3);
694        let mut build = Builder::new(&mut func, at[0]);
695        let cond = build.iconst(Type::int(1), 1);
696        build.br_if(cond, at[1], &[], at[2], &[]);
697        Builder::new(&mut func, at[1]).unreachable();
698        let mut build = Builder::new(&mut func, at[2]);
699        let zero = build.iconst(Type::int(32), 0);
700        build.ret(&[zero]);
701
702        let (seen, _) = predict(&func);
703        assert_eq!(seen.by(at[0]), Predictor::NeverReturns);
704        assert_eq!(seen.taken(at[0], 0), Probability::percent(99, Quality::Guessed).complement());
705        assert_eq!(seen.taken(at[0], 1), Probability::percent(99, Quality::Guessed));
706    }
707
708    #[test]
709    fn the_arm_that_calls_a_noreturn_function_is_the_one_not_taken() {
710        // The same prediction from the other direction: control does come back from the block as
711        // far as the graph is concerned, and it is the callee's attributes that say otherwise.
712        let (mut names, mut func, at) = blank(4);
713        let abort = names.intern("abort");
714        let sig = func.add_signature(Signature::new());
715        let mut build = Builder::new(&mut func, at[0]);
716        let cond = build.iconst(Type::int(1), 1);
717        build.br_if(cond, at[1], &[], at[2], &[]);
718        let mut build = Builder::new(&mut func, at[1]);
719        build.call(abort, sig, &[]);
720        build.jump(at[3], &[]);
721        Builder::new(&mut func, at[2]).jump(at[3], &[]);
722        let mut build = Builder::new(&mut func, at[3]);
723        let zero = build.iconst(Type::int(32), 0);
724        build.ret(&[zero]);
725
726        let mut callees = Callees::nothing();
727        callees.record(abort, Attrs { set: AttrSet::NORETURN, ..Attrs::NONE });
728        let (cfg, loops) = shape(&func);
729
730        let told = Predictions::of(&func, &cfg, &loops, &callees);
731        assert_eq!(told.by(at[0]), Predictor::NeverReturns);
732        assert_eq!(told.taken(at[0], 0), Probability::percent(99, Quality::Guessed).complement());
733
734        // And with nothing known about the callee the two arms are the same shape, so the call
735        // predictor is what is left to say anything about them.
736        let (guessed, _) = predict(&func);
737        assert_eq!(guessed.by(at[0]), Predictor::CallNotTaken);
738    }
739
740    #[test]
741    fn the_arm_that_calls_a_cold_function_is_the_one_not_taken() {
742        let (mut names, mut func, at) = blank(4);
743        let report = names.intern("report");
744        let sig = func.add_signature(Signature::new());
745        let mut build = Builder::new(&mut func, at[0]);
746        let cond = build.iconst(Type::int(1), 1);
747        build.br_if(cond, at[1], &[], at[2], &[]);
748        let mut build = Builder::new(&mut func, at[1]);
749        build.call(report, sig, &[]);
750        build.jump(at[3], &[]);
751        Builder::new(&mut func, at[2]).jump(at[3], &[]);
752        let mut build = Builder::new(&mut func, at[3]);
753        let zero = build.iconst(Type::int(32), 0);
754        build.ret(&[zero]);
755
756        let mut callees = Callees::nothing();
757        callees.record(report, Attrs { set: AttrSet::COLD, ..Attrs::NONE });
758        let (cfg, loops) = shape(&func);
759        let told = Predictions::of(&func, &cfg, &loops, &callees);
760
761        // The call predictor would also have fired here, at sixty seven percent. First match is
762        // what makes the user's own statement win over the guess, which is what section 11.2
763        // asks for when it says the attribute is honoured rather than blended.
764        assert_eq!(told.by(at[0]), Predictor::ColdCall);
765        assert_eq!(told.taken(at[0], 0), Probability::percent(99, Quality::Guessed).complement());
766    }
767
768    /// A loop: entry, header, body, exit, with the header testing and the body going round.
769    fn loop_shape() -> (Func, Vec<Block>) {
770        let (_, mut func, at) = blank(4);
771        Builder::new(&mut func, at[0]).jump(at[1], &[]);
772        let mut build = Builder::new(&mut func, at[1]);
773        let cond = build.iconst(Type::int(1), 1);
774        build.br_if(cond, at[2], &[], at[3], &[]);
775        Builder::new(&mut func, at[2]).jump(at[1], &[]);
776        let mut build = Builder::new(&mut func, at[3]);
777        let zero = build.iconst(Type::int(32), 0);
778        build.ret(&[zero]);
779        (func, at)
780    }
781
782    #[test]
783    fn a_loop_exit_is_the_edge_not_taken() {
784        let (func, at) = loop_shape();
785        let (seen, _) = predict(&func);
786        assert_eq!(seen.by(at[1]), Predictor::LoopExit);
787        // Staying in the loop, which is the first arm here.
788        assert_eq!(seen.taken(at[1], 0), Probability::percent(89, Quality::Guessed));
789        assert_eq!(seen.taken(at[1], 1), Probability::percent(89, Quality::Guessed).complement());
790    }
791
792    #[test]
793    fn a_loop_guard_is_taken_more_often_than_not() {
794        // `if (n) { while (...) ... }`, where the guard branches to the preheader rather than to
795        // the header, which is the shape the front end produces.
796        let (_, mut func, at) = blank(6);
797        let mut build = Builder::new(&mut func, at[0]);
798        let cond = build.iconst(Type::int(1), 1);
799        build.br_if(cond, at[1], &[], at[2], &[]);
800        Builder::new(&mut func, at[1]).jump(at[3], &[]);
801        Builder::new(&mut func, at[2]).jump(at[5], &[]);
802        let mut build = Builder::new(&mut func, at[3]);
803        let test = build.iconst(Type::int(1), 1);
804        build.br_if(test, at[4], &[], at[5], &[]);
805        Builder::new(&mut func, at[4]).jump(at[3], &[]);
806        let mut build = Builder::new(&mut func, at[5]);
807        let zero = build.iconst(Type::int(32), 0);
808        build.ret(&[zero]);
809
810        let (seen, _) = predict(&func);
811        assert_eq!(seen.by(at[0]), Predictor::LoopGuard);
812        assert_eq!(seen.taken(at[0], 0), Probability::percent(73, Quality::Guessed));
813    }
814
815    #[test]
816    fn a_continue_goes_round_again_more_often_than_it_falls_through() {
817        let (_, mut func, at) = blank(5);
818        Builder::new(&mut func, at[0]).jump(at[1], &[]);
819        let mut build = Builder::new(&mut func, at[1]);
820        let cond = build.iconst(Type::int(1), 1);
821        build.br_if(cond, at[2], &[], at[3], &[]);
822        let mut build = Builder::new(&mut func, at[2]);
823        let again = build.iconst(Type::int(1), 1);
824        build.br_if(again, at[1], &[], at[4], &[]);
825        Builder::new(&mut func, at[4]).jump(at[1], &[]);
826        let mut build = Builder::new(&mut func, at[3]);
827        let zero = build.iconst(Type::int(32), 0);
828        build.ret(&[zero]);
829
830        let (seen, _) = predict(&func);
831        assert_eq!(seen.by(at[2]), Predictor::Continue);
832        assert_eq!(seen.taken(at[2], 0), Probability::percent(67, Quality::Guessed));
833    }
834
835    #[test]
836    fn a_pointer_tested_against_null_is_predicted_not_null() {
837        let (_, mut func, at) = blank(3);
838        let mut build = Builder::new(&mut func, at[0]);
839        let seven = build.iconst(Type::int(64), 7);
840        let some = build.unary(Opcode::IntToPtr, seven, Type::PTR);
841        let zero = build.iconst(Type::int(64), 0);
842        let null = build.unary(Opcode::IntToPtr, zero, Type::PTR);
843        let cond = build.icmp(IntPred::Eq, some, null);
844        build.br_if(cond, at[1], &[], at[2], &[]);
845        for block in [at[1], at[2]] {
846            let mut build = Builder::new(&mut func, block);
847            let zero = build.iconst(Type::int(32), 0);
848            build.ret(&[zero]);
849        }
850
851        let (seen, _) = predict(&func);
852        assert_eq!(seen.by(at[0]), Predictor::PointerNotNull);
853        // The arm taken when the pointer is null, which is the thirty percent of the time.
854        assert_eq!(seen.taken(at[0], 0), Probability::percent(70, Quality::Guessed).complement());
855    }
856
857    #[test]
858    fn an_arm_that_returns_a_negative_number_is_the_one_not_taken() {
859        let (_, mut func, at) = blank(3);
860        let mut build = Builder::new(&mut func, at[0]);
861        let cond = build.iconst(Type::int(1), 1);
862        build.br_if(cond, at[1], &[], at[2], &[]);
863        let mut build = Builder::new(&mut func, at[1]);
864        let bad = build.iconst(Type::int(32), -1);
865        build.ret(&[bad]);
866        let mut build = Builder::new(&mut func, at[2]);
867        let good = build.iconst(Type::int(32), 0);
868        build.ret(&[good]);
869
870        let (seen, _) = predict(&func);
871        assert_eq!(seen.by(at[0]), Predictor::NegativeReturn);
872        assert_eq!(seen.taken(at[0], 0), Probability::percent(98, Quality::Guessed).complement());
873    }
874
875    #[test]
876    fn an_arm_that_returns_null_is_the_one_not_taken_and_by_a_smaller_margin() {
877        let (_, mut func, at) = blank(3);
878        let mut build = Builder::new(&mut func, at[0]);
879        let cond = build.iconst(Type::int(1), 1);
880        build.br_if(cond, at[1], &[], at[2], &[]);
881        let mut build = Builder::new(&mut func, at[1]);
882        let zero = build.iconst(Type::int(64), 0);
883        let null = build.unary(Opcode::IntToPtr, zero, Type::PTR);
884        build.ret(&[null]);
885        let mut build = Builder::new(&mut func, at[2]);
886        let seven = build.iconst(Type::int(64), 7);
887        let some = build.unary(Opcode::IntToPtr, seven, Type::PTR);
888        build.ret(&[some]);
889
890        let (seen, _) = predict(&func);
891        assert_eq!(seen.by(at[0]), Predictor::NullReturn);
892        assert_eq!(seen.taken(at[0], 0), Probability::percent(71, Quality::Guessed).complement());
893        // The end of a list is an ordinary answer and a negative return is a failure, which is
894        // why one of these predictors is at seventy one and the other at ninety eight.
895        assert!(Predictor::NullReturn.hit_rate() < Predictor::NegativeReturn.hit_rate());
896    }
897
898    #[test]
899    fn nothing_to_go_on_is_an_even_split_that_says_it_is_a_guess() {
900        let (_, mut func, at) = blank(3);
901        let mut build = Builder::new(&mut func, at[0]);
902        let cond = build.iconst(Type::int(1), 1);
903        build.br_if(cond, at[1], &[], at[2], &[]);
904        for block in [at[1], at[2]] {
905            let mut build = Builder::new(&mut func, block);
906            let zero = build.iconst(Type::int(32), 0);
907            build.ret(&[zero]);
908        }
909
910        let (seen, _) = predict(&func);
911        assert_eq!(seen.by(at[0]), Predictor::Nothing);
912        assert_eq!(seen.taken(at[0], 0), Probability::even());
913        assert_eq!(seen.taken(at[0], 0).quality(), Quality::Guessed);
914        assert!(!seen.taken(at[0], 0).is_predictable());
915    }
916
917    /// Writes a hint onto the arms of a branch, which is what `crate::expect` does to a
918    /// `__builtin_expect` before anything gets here.
919    fn hinted(func: &mut Func, block: Block, parts: u32) {
920        let term = func.terminator(block).expect("a branch");
921        let hint = Hint::parts(parts);
922        for (at, hint) in func.target_list(term).iter().zip([hint, hint.complement()]) {
923            let call = func[at];
924            func.set_block_call(at, BlockCall { hint, ..call });
925        }
926    }
927
928    #[test]
929    fn a_builtin_expect_wins_over_every_predictor_after_it() {
930        // The arm the user named is also the arm that aborts, and the user wins. This is the one
931        // test that says what first match is for: without it the noreturn predictor would answer,
932        // and it would answer the other way round.
933        let (_, mut func, at) = blank(3);
934        let mut build = Builder::new(&mut func, at[0]);
935        let cond = build.iconst(Type::int(1), 1);
936        build.br_if(cond, at[1], &[], at[2], &[]);
937        Builder::new(&mut func, at[1]).unreachable();
938        let mut build = Builder::new(&mut func, at[2]);
939        let zero = build.iconst(Type::int(32), 0);
940        build.ret(&[zero]);
941        hinted(&mut func, at[0], 9_000);
942
943        let (seen, _) = predict(&func);
944        assert_eq!(seen.by(at[0]), Predictor::Expect);
945        assert_eq!(seen.taken(at[0], 0), Probability::percent(90, Quality::Guessed));
946    }
947
948    #[test]
949    fn a_builtin_expect_of_zero_names_the_other_arm() {
950        let (_, mut func, at) = blank(3);
951        let mut build = Builder::new(&mut func, at[0]);
952        let cond = build.iconst(Type::int(1), 1);
953        build.br_if(cond, at[1], &[], at[2], &[]);
954        for block in [at[1], at[2]] {
955            let mut build = Builder::new(&mut func, block);
956            let zero = build.iconst(Type::int(32), 0);
957            build.ret(&[zero]);
958        }
959        hinted(&mut func, at[0], 1_000);
960
961        let (seen, _) = predict(&func);
962        assert_eq!(seen.by(at[0]), Predictor::Expect);
963        assert_eq!(seen.taken(at[0], 0), Probability::percent(90, Quality::Guessed).complement());
964    }
965
966    /// A switch on four values, where the first case aborts and the last two share a block.
967    fn switch_shape() -> (Func, Vec<Block>) {
968        let (_, mut func, at) = blank(5);
969        let mut build = Builder::new(&mut func, at[0]);
970        let value = build.iconst(Type::int(32), 0);
971        build.switch(value, at[1], &[(0, at[2]), (1, at[3]), (2, at[4]), (3, at[4])]);
972        Builder::new(&mut func, at[2]).unreachable();
973        for block in [at[1], at[3], at[4]] {
974            let mut build = Builder::new(&mut func, block);
975            let zero = build.iconst(Type::int(32), 0);
976            build.ret(&[zero]);
977        }
978        (func, at)
979    }
980
981    /// Writes these hints onto the arms of the switch in `switch_shape`, the default first.
982    fn hint_switch(func: &mut Func, block: Block, parts: &[Option<u32>]) {
983        let term = func.terminator(block).expect("the switch");
984        for (at, &parts) in func.target_list(term).iter().zip(parts) {
985            let call = func[at];
986            let hint = parts.map_or(Hint::NONE, Hint::parts);
987            func.set_block_call(at, BlockCall { hint, ..call });
988        }
989    }
990
991    #[test]
992    fn a_switch_whose_arms_all_carry_a_hint_is_split_the_way_they_say() {
993        let (mut func, at) = switch_shape();
994        let parts = [Some(1_000), Some(1_000), Some(6_000), Some(1_000), Some(1_000)];
995        hint_switch(&mut func, at[0], &parts);
996        let (seen, cfg) = predict(&func);
997        let succs = cfg.successors(at[0]);
998        let edge = |block: Block| succs.iter().position(|&one| one == block).expect("an edge");
999
1000        assert_eq!(seen.by(at[0]), Predictor::Expect);
1001        assert_eq!(seen.taken(at[0], edge(at[3])).parts(), 6_000);
1002        // Two cases lead to the same block, and it gets both their hints.
1003        assert_eq!(seen.taken(at[0], edge(at[4])).parts(), 2_000);
1004        // The arm that aborts gets what its hint says, as a branch the program hinted does.
1005        assert_eq!(seen.taken(at[0], edge(at[2])).parts(), 1_000);
1006    }
1007
1008    #[test]
1009    fn a_switch_with_a_hint_on_only_some_arms_is_split_as_if_it_had_none() {
1010        let (mut func, at) = switch_shape();
1011        hint_switch(&mut func, at[0], &[None, None, Some(9_000), None, None]);
1012        let (seen, _) = predict(&func);
1013        assert_eq!(seen.by(at[0]), Predictor::NeverReturns);
1014    }
1015
1016    #[test]
1017    fn a_switch_arm_that_aborts_leaves_the_rest_to_share_what_is_left() {
1018        let (func, at) = switch_shape();
1019        let (seen, cfg) = predict(&func);
1020        let succs = cfg.successors(at[0]);
1021        let aborts = succs.iter().position(|&block| block == at[2]).expect("the arm is an edge");
1022        let shared = succs.iter().position(|&block| block == at[4]).expect("the arm is an edge");
1023        let alone = succs.iter().position(|&block| block == at[3]).expect("the arm is an edge");
1024
1025        assert_eq!(seen.by(at[0]), Predictor::NeverReturns);
1026        // One percent between the arms that do not come back, of which there is one.
1027        assert_eq!(
1028            seen.taken(at[0], aborts),
1029            Probability::percent(99, Quality::Guessed).complement()
1030        );
1031        // Two cases lead to the same block, so it is reached two ways and gets twice the share.
1032        assert_eq!(seen.taken(at[0], shared).parts(), 2 * seen.taken(at[0], alone).parts());
1033    }
1034
1035    #[test]
1036    fn the_edges_out_of_every_block_add_up_to_certainty() {
1037        // What the frequency computation in section 11.3 needs, and the one property of this file
1038        // that a caller is entitled to assume without reading it.
1039        let (guarded, _) = {
1040            let (_, mut func, at) = blank(3);
1041            let mut build = Builder::new(&mut func, at[0]);
1042            let cond = build.iconst(Type::int(1), 1);
1043            build.br_if(cond, at[1], &[], at[2], &[]);
1044            for block in [at[1], at[2]] {
1045                let mut build = Builder::new(&mut func, block);
1046                let zero = build.iconst(Type::int(32), 0);
1047                build.ret(&[zero]);
1048            }
1049            (func, at)
1050        };
1051        let (looped, _) = loop_shape();
1052        let (switched, _) = switch_shape();
1053
1054        for func in [guarded, looped, switched] {
1055            let (seen, cfg) = predict(&func);
1056            for block in func.blocks() {
1057                let edges = seen.edges(block);
1058                if edges.is_empty() {
1059                    continue;
1060                }
1061                assert_eq!(edges.len(), cfg.successors(block).len());
1062                let total: u32 = edges.iter().map(|edge| edge.parts()).sum();
1063                assert_eq!(total, Probability::SCALE, "block {block:?} does not add up");
1064            }
1065        }
1066    }
1067
1068    #[test]
1069    fn the_ten_are_the_ten_the_document_named_and_they_are_asked_in_its_order() {
1070        assert_eq!(Predictor::ORDER.len(), 10);
1071        assert!(!Predictor::ORDER.contains(&Predictor::Nothing));
1072        let mut sorted = Predictor::ORDER;
1073        sorted.sort_unstable();
1074        assert_eq!(sorted, Predictor::ORDER, "the enum order is the order they are asked in");
1075        for one in Predictor::ORDER {
1076            assert!(one.hit_rate() > Predictor::Nothing.hit_rate(), "{one} predicts nothing");
1077            assert!(!one.as_str().is_empty());
1078        }
1079    }
1080}