Skip to main content

rucc_opt/
scev.rs

1//! Scalar evolution: how a value changes across the iterations of a loop, and how many
2//! iterations there are.
3//!
4//! Design: `spec/optimizer/07-loops-and-scev.md` sections 7.4 through 7.7. This is the second
5//! half of document 07 and it answers the last two of the four questions section 7.6 says loop
6//! analysis exists for. The first two are in [`crate::loops`].
7//!
8//! # Chains of recurrences, and how much of one
9//!
10//! GCC writes how a value changes as a chain of recurrences, `{base, +, step}`, meaning a value
11//! that is `base` on the first iteration and `step` more on each one after. The representation is
12//! good because it is closed under the operations anyone wants: adding two chrecs of the same
13//! loop adds componentwise, multiplying by something invariant scales both parts, and evaluating
14//! one at a given iteration is arithmetic rather than a special case. That closure is why
15//! `j = 2 * i + 3` is as easy as `i = i + 1`, and pattern matching the second would run out of
16//! road on the first.
17//!
18//! Section 7.4 says what rucc builds and it is a subset: affine chrecs only. A value is
19//! invariant, or `{base, +, step}` with both parts invariant, or unknown. Addition, subtraction,
20//! multiplication by an invariant, shifting by a constant, and extension where the extension
21//! provably does not wrap. Nothing polynomial and nothing mutually recursive. That covers every
22//! induction variable a C programmer writes and every array subscript document 31 could use, and
23//! what it leaves out of GCC's four thousand lines is the part serving Fortran and the polyhedral
24//! framework.
25//!
26//! The one extension past affine is pointer chrecs, because C loops walk pointers and `p = p + 1`
27//! is `i = i + 1` with a scale. A `ptr_add` is addition with the byte offset as the step, which
28//! is the difference between analysing half of real C loops and analysing nearly all of them.
29//!
30//! # Trip counts, and the part that is uncomfortable
31//!
32//! Given an exit that compares an affine chrec against something invariant, solving for the
33//! iteration at which the comparison first fails is arithmetic. What makes it hard is that the
34//! answer is almost always conditional: on the loop being entered at all, and on the induction
35//! variable not wrapping before it gets there. Section 7.5 says a trip count returned without its
36//! assumptions is a miscompilation generator, and that the temptation to return one is strong
37//! because the assumptions are usually true.
38//!
39//! So [`Bound`] carries them and there is no way to read the count without seeing them.
40//! [`Bound::parts`] hands back both, and [`Bound::proven`] hands back the count only when there
41//! is nothing left to prove. A caller that means to emit a runtime check reads the assumptions
42//! and emits it, and a caller that forgets cannot get at the number.
43//!
44//! [`Bound`] and [`Estimate`] are different types on purpose. A bound is used for correctness, an
45//! estimate is used to decide whether a transformation is worth doing, and section 7.5 calls
46//! conflating them a category error that costs correctness. GCC keeps them apart as
47//! `max_loop_iterations` and `estimate_numbers_of_iterations` and the names do not stop anyone.
48//! Different structs do.
49
50use std::collections::HashMap;
51
52use rucc_ir::{Block, Def, Extra, Flags, Func, Imm, Inst, IntPred, Opcode, Type, Value};
53
54use crate::cfg::Cfg;
55use crate::loops::{LoopId, Loops};
56
57/// How deep the search for a step walks back through arithmetic.
58///
59/// The chain from a header parameter to the value fed back to it is two or three instructions in
60/// anything a person writes, and the walk terminates on its own because SSA has no cycles except
61/// through block parameters. The limit is here so a generated function with a thousand additions
62/// in the increment costs a bounded amount rather than a stack.
63const STEP_LIMIT: u32 = 16;
64
65/// How many times a loop is assumed to run when nothing better is known.
66///
67/// GCC's `--param avg-loop-niter`, whose default is the same number. It is a guess and it is only
68/// ever used through [`Estimate`], which is only ever used to decide whether something is worth
69/// doing.
70const ASSUMED_ITERATIONS: u64 = 10;
71
72/// A value that does not change inside the loop, read as `scale * value + offset`.
73///
74/// The `value` is a value defined outside the loop, or `None` when the expression is a plain
75/// number. Keeping the shape rather than a bare [`Value`] is what lets `j = 2 * i + 3` come out
76/// as `{3, +, 2}` instead of unknown: the base and the step of that chrec are expressions nothing
77/// in the function computes, so a representation that could only name existing values would have
78/// to give up.
79///
80/// Arithmetic on two of these is refused when both are symbolic and the symbols differ, because
81/// `x + y` is not of this shape. That is the boundary of the subset and it is where the answer
82/// becomes unknown rather than wrong.
83#[derive(Clone, Copy, Debug, PartialEq, Eq)]
84pub struct Invariant {
85    /// What it is built on, or `None` for a plain number.
86    pub value: Option<Value>,
87    /// How many of it.
88    pub scale: i128,
89    /// What is added to it.
90    pub offset: i128,
91}
92
93impl Invariant {
94    /// A plain number.
95    #[must_use]
96    pub fn number(offset: i128) -> Self {
97        Self { value: None, scale: 0, offset }
98    }
99
100    /// One of a value.
101    #[must_use]
102    pub fn of(value: Value) -> Self {
103        Self { value: Some(value), scale: 1, offset: 0 }
104    }
105
106    /// The number this is, when it is one.
107    #[must_use]
108    pub fn as_number(self) -> Option<i128> {
109        (self.value.is_none() || self.scale == 0).then_some(self.offset)
110    }
111
112    /// Whether this is the number zero.
113    #[must_use]
114    pub fn is_zero(self) -> bool {
115        self.as_number() == Some(0)
116    }
117
118    /// The symbol both expressions are built on, when they agree on one or one has none.
119    fn shared(self, other: Self) -> Option<Option<Value>> {
120        match (self.as_number().is_some(), other.as_number().is_some()) {
121            (true, _) => Some(other.value),
122            (_, true) => Some(self.value),
123            _ => (self.value == other.value).then_some(self.value),
124        }
125    }
126
127    /// The two added, when the sum is of this shape.
128    #[must_use]
129    pub fn plus(self, other: Self) -> Option<Self> {
130        let value = self.shared(other)?;
131        Some(Self {
132            value,
133            scale: self.scale.checked_add(other.scale)?,
134            offset: self.offset.checked_add(other.offset)?,
135        })
136    }
137
138    /// The second subtracted from the first, when the difference is of this shape.
139    #[must_use]
140    pub fn minus(self, other: Self) -> Option<Self> {
141        self.plus(other.negated()?)
142    }
143
144    /// This with its sign flipped.
145    #[must_use]
146    pub fn negated(self) -> Option<Self> {
147        Some(Self {
148            value: self.value,
149            scale: self.scale.checked_neg()?,
150            offset: self.offset.checked_neg()?,
151        })
152    }
153
154    /// The two multiplied, which needs one of them to be a plain number.
155    #[must_use]
156    pub fn times(self, other: Self) -> Option<Self> {
157        let (symbol, by) = match (self.as_number(), other.as_number()) {
158            (Some(by), _) => (other, by),
159            (_, Some(by)) => (self, by),
160            _ => return None,
161        };
162        Some(Self {
163            value: symbol.value,
164            scale: symbol.scale.checked_mul(by)?,
165            offset: symbol.offset.checked_mul(by)?,
166        })
167    }
168}
169
170/// How a value changes from one iteration of a loop to the next.
171#[derive(Clone, Copy, Debug, PartialEq, Eq)]
172pub enum Evolution {
173    /// The same on every iteration.
174    Invariant(Invariant),
175    /// `{base, +, step}`: `base` the first time round and `step` more each time after.
176    Affine(Chrec),
177    /// Not something this analysis describes. Never a claim that the value does not evolve.
178    Unknown,
179}
180
181impl Evolution {
182    /// The chrec, when this is one.
183    #[must_use]
184    pub fn chrec(self) -> Option<Chrec> {
185        match self {
186            Self::Affine(chrec) => Some(chrec),
187            _ => None,
188        }
189    }
190
191    /// The invariant expression, when this is one.
192    #[must_use]
193    pub fn invariant(self) -> Option<Invariant> {
194        match self {
195            Self::Invariant(inv) => Some(inv),
196            _ => None,
197        }
198    }
199}
200
201/// An affine chain of recurrences, `{base, +, step}`, evolving in a named type.
202///
203/// The type is not decoration. `{0, +, 1}` in `unsigned char` is not the sequence `0, 1, 2, ...`,
204/// it is that sequence modulo two hundred and fifty six, and section 7.7 says this is where a
205/// naive implementation is wrong constantly and in ways that pass every test written by someone
206/// thinking in `int`. Every operation here checks the type and every one that cannot stay right
207/// in it answers unknown.
208#[derive(Clone, Copy, Debug, PartialEq, Eq)]
209pub struct Chrec {
210    /// What the value is on the first iteration.
211    pub base: Invariant,
212    /// What is added each time round.
213    pub step: Invariant,
214    /// The type it evolves in, which is what says when it wraps.
215    pub ty: Type,
216    /// What the instruction that increments it promised. `nsw` means the sequence does not wrap
217    /// when read as signed and `nuw` means it does not when read as unsigned, and both come from
218    /// the increment rather than from anything this analysis proved.
219    pub flags: Flags,
220}
221
222impl Chrec {
223    /// Whether the sequence is known not to wrap under the reading this predicate takes.
224    #[must_use]
225    pub fn does_not_wrap(self, signed: bool) -> bool {
226        self.flags.contains(if signed { Flags::NSW } else { Flags::NUW })
227    }
228}
229
230/// Something that has to be true for a trip count to be the right answer.
231///
232/// Section 7.5 asks for exactly this: not a trip count but a trip count plus a predicate under
233/// which it holds, so the consumer either proves the predicate, emits a runtime check for it, or
234/// gives up. These are the predicates.
235#[derive(Clone, Copy, Debug, PartialEq, Eq)]
236pub enum Assumption {
237    /// The counter starts on the near side of its limit, so the distance between them is a
238    /// number that is not negative.
239    ///
240    /// For a loop ending on an ordering this is the loop being entered at all.
241    /// `for (i = 0; i < n; i++)` with `n` of zero runs no times and the distance is zero, but `n`
242    /// of minus one also runs no times and the distance is minus one, so a count taken from the
243    /// distance has to be told which case it is in. For a loop ending on `!=` it is the limit
244    /// being somewhere the counter is heading, because one stepping away from its limit never
245    /// arrives.
246    ///
247    /// Only ever present on a symbolic count. When the distance is a number the sign of it is
248    /// there to be read, so this is settled rather than assumed.
249    Approaching,
250    /// The induction variable does not wrap in its own type before the exit is taken.
251    ///
252    /// Present whenever the increment did not carry the matching `nsw` or `nuw` flag. With the
253    /// flag there is nothing to assume, because the flag is the promise.
254    NoWrap(Chrec),
255    /// Signed overflow is undefined here, which is what makes `for (int i = 0; i <= n; i++)`
256    /// finite.
257    ///
258    /// GCC infers loop bounds from this in `infer_loop_bounds_from_signedness`, and it is the
259    /// single most common source of a report that the compiler broke a working program. It is
260    /// recorded rather than assumed silently so that `-fwrapv` can withdraw the count and so that
261    /// a dump can name it.
262    StrictOverflow,
263}
264
265impl Assumption {
266    /// What it says, in a line, for a dump to print.
267    ///
268    /// Section 7.5 asks that every inference of this kind be dumpable and say what it rests on,
269    /// because a user who has been bitten by one deserves a command that tells them which line
270    /// the compiler used against them. This is the sentence that command prints.
271    #[must_use]
272    pub fn describe(&self) -> String {
273        match self {
274            Self::Approaching => "the counter starts on the near side of its limit".to_string(),
275            Self::NoWrap(chrec) => {
276                format!("the induction variable does not wrap in i{}", chrec.ty.bits())
277            }
278            Self::StrictOverflow => {
279                "signed overflow is undefined, so -fwrapv withdraws this count".to_string()
280            }
281        }
282    }
283}
284
285/// How many iterations, as a number or as an expression.
286#[derive(Clone, Copy, Debug, PartialEq, Eq)]
287pub enum Count {
288    /// Exactly this many.
289    Exact(u128),
290    /// This many, worked out from something the loop does not change.
291    Symbolic(Invariant),
292}
293
294/// How many times a loop runs at most, and what that rests on.
295///
296/// For correctness. A pass that deletes an iteration, peels one off, or decides a memory access
297/// is in bounds needs one of these. The count cannot be read without the assumptions, which is
298/// section 7.7's defence against a caller proving two of three and forgetting the third.
299#[derive(Clone, Debug, PartialEq, Eq)]
300pub struct Bound {
301    count: Count,
302    assumptions: Vec<Assumption>,
303}
304
305impl Bound {
306    /// The count and everything it rests on, together, because they cannot be asked for apart.
307    #[must_use]
308    pub fn parts(&self) -> (Count, &[Assumption]) {
309        (self.count, &self.assumptions)
310    }
311
312    /// What has to be proved before the count means anything.
313    #[must_use]
314    pub fn assumptions(&self) -> &[Assumption] {
315        &self.assumptions
316    }
317
318    /// The count, for a caller with nothing left to prove.
319    ///
320    /// `None` does not mean the count is unknown. It means there are assumptions and this is not
321    /// the accessor for reading a count that has them.
322    #[must_use]
323    pub fn proven(&self) -> Option<Count> {
324        self.assumptions.is_empty().then_some(self.count)
325    }
326}
327
328/// How many times a loop probably runs.
329///
330/// For cost decisions and never for correctness. A pass asking whether unrolling pays for itself
331/// wants one of these, and it is fine for the answer to be a guess, because being wrong makes the
332/// code slower rather than wrong. Nothing here can be turned into a [`Bound`].
333#[derive(Clone, Copy, Debug, PartialEq, Eq)]
334pub struct Estimate {
335    iterations: u64,
336    guessed: bool,
337}
338
339impl Estimate {
340    /// The number to do arithmetic with.
341    #[must_use]
342    pub fn iterations(self) -> u64 {
343        self.iterations
344    }
345
346    /// Whether nothing was known and this is the default.
347    #[must_use]
348    pub fn is_guess(self) -> bool {
349        self.guessed
350    }
351}
352
353/// The analysis, which works out an answer when asked and remembers it.
354///
355/// Demand driven and memoized, per section 7.8, because the cost of scalar evolution is a
356/// function of how many distinct values get asked about rather than of the size of the function.
357/// The cache holds one loop's worth of answers per loop and the whole thing is thrown away when
358/// anything about the loops changes, which per document 04.4 is any pass that touches one.
359#[derive(Debug)]
360pub struct Scev<'a> {
361    func: &'a Func,
362    cfg: &'a Cfg,
363    loops: &'a Loops,
364    known: HashMap<(LoopId, Value), Evolution>,
365}
366
367impl<'a> Scev<'a> {
368    /// A fresh analysis over these loops, knowing nothing yet.
369    #[must_use]
370    pub fn new(func: &'a Func, cfg: &'a Cfg, loops: &'a Loops) -> Self {
371        Self { func, cfg, loops, known: HashMap::new() }
372    }
373
374    /// How this value changes across the iterations of this loop.
375    pub fn evolution(&mut self, id: LoopId, value: Value) -> Evolution {
376        if let Some(&known) = self.known.get(&(id, value)) {
377            return known;
378        }
379        // Unknown while the answer is being worked out, so the cycle from a header parameter back
380        // to itself terminates instead of asking the same question forever. Anything that reaches
381        // the parameter again gets unknown and the shape it was matching fails, which is the
382        // right answer for a value defined in terms of itself through arithmetic this does not
383        // describe.
384        self.known.insert((id, value), Evolution::Unknown);
385        let found = self.compute(id, value);
386        self.known.insert((id, value), found);
387        found
388    }
389
390    /// How many times this loop runs at most, and what that rests on.
391    ///
392    /// Any one exit gives a valid upper bound, because a loop cannot run more times than the
393    /// first exit that fires, so this takes the first exit it can solve rather than the smallest.
394    /// That is `max_loop_iterations` and not `estimate_numbers_of_iterations`, which is why the
395    /// answer is a [`Bound`].
396    pub fn bound(&mut self, id: LoopId) -> Option<Bound> {
397        let exits: Vec<Block> = self.loops.exits(id).iter().map(|exit| exit.from).collect();
398        exits.into_iter().find_map(|from| self.bound_at(id, from))
399    }
400
401    /// How many times this loop probably runs.
402    pub fn estimate(&mut self, id: LoopId) -> Estimate {
403        match self.bound(id).map(|bound| bound.count) {
404            Some(Count::Exact(exact)) => {
405                Estimate { iterations: u64::try_from(exact).unwrap_or(u64::MAX), guessed: false }
406            }
407            _ => Estimate { iterations: ASSUMED_ITERATIONS, guessed: true },
408        }
409    }
410
411    /// The evolution of a value nothing is known about yet.
412    fn compute(&mut self, id: LoopId, value: Value) -> Evolution {
413        if let Some(invariant) = self.invariant(id, value) {
414            return Evolution::Invariant(invariant);
415        }
416        match self.func[value].def {
417            Def::Param { block, index } if block == self.loops.header(id) => {
418                self.at_header(id, value, index as usize)
419            }
420            // A parameter of a block inside the loop that is not the header takes a different
421            // value depending on which way control came, and describing that is a job for the
422            // value range work of document 10 rather than for a chrec.
423            Def::Param { .. } => Evolution::Unknown,
424            Def::Result { inst, .. } => self.at_inst(id, inst, value),
425        }
426    }
427
428    /// The value as an expression that does not change inside the loop, if it is one.
429    fn invariant(&self, id: LoopId, value: Value) -> Option<Invariant> {
430        if let Some((imm, ty)) = constant(self.func, value) {
431            return Some(Invariant::number(imm.signed(ty)));
432        }
433        // A constant is invariant wherever it sits, which is why it is asked about first. Anything
434        // else has to be defined outside the loop.
435        self.loops.is_invariant(self.func, id, value).then(|| Invariant::of(value))
436    }
437
438    /// The evolution of a parameter of the loop header, which is where an induction variable is.
439    ///
440    /// The parameter takes one value on the way in and another on the way round, which is what
441    /// other IRs spell as a phi node. If the way round is the parameter plus something invariant,
442    /// the parameter is an affine chrec and that something is its step.
443    fn at_header(&mut self, id: LoopId, value: Value, index: usize) -> Evolution {
444        let (func, cfg, loops) = (self.func, self.cfg, self.loops);
445        let header = loops.header(id);
446        // Section 7.3 wants exactly one latch and the canonicalizer makes one. Two of them means
447        // two ways round with two different increments, and picking one would be a guess.
448        let [latch] = loops.latches(id) else { return Evolution::Unknown };
449        let mut entering = None;
450        let mut around = None;
451        for &pred in cfg.predecessors(header) {
452            let Some(arg) = argument(func, pred, header, index) else { return Evolution::Unknown };
453            let slot = if pred == *latch { &mut around } else { &mut entering };
454            if slot.replace(arg).is_some_and(|old| old != arg) {
455                return Evolution::Unknown;
456            }
457        }
458        let (Some(entering), Some(around)) = (entering, around) else { return Evolution::Unknown };
459        let Some(base) = self.invariant(id, entering) else { return Evolution::Unknown };
460        let Some((step, flags)) = self.step(id, around, value, 0) else {
461            return Evolution::Unknown;
462        };
463        affine(base, step, func[value].ty, flags)
464    }
465
466    /// What is added to `of` to get `value`, and what the additions promised.
467    ///
468    /// Written as its own walk rather than as the general combination below, because at the point
469    /// this runs the parameter's own evolution is not known yet and the general walk would ask
470    /// for it and get unknown.
471    fn step(&self, id: LoopId, value: Value, of: Value, depth: u32) -> Option<(Invariant, Flags)> {
472        if value == of {
473            // Nothing added yet, and nothing has had a chance to overflow either.
474            return Some((Invariant::number(0), Flags::NSW.union(Flags::NUW)));
475        }
476        if depth >= STEP_LIMIT {
477            return None;
478        }
479        let Def::Result { inst, .. } = self.func[value].def else { return None };
480        let data = &self.func[inst];
481        let args = &self.func[data.args];
482        let (&lhs, &rhs) = (args.first()?, args.get(1)?);
483        let combine = |carried: (Invariant, Flags), other: Invariant, subtract: bool| {
484            let (delta, flags) = carried;
485            let moved = if subtract { delta.minus(other)? } else { delta.plus(other)? };
486            Some((moved, flags.intersection(data.flags)))
487        };
488        match data.opcode {
489            Opcode::Add => {
490                if let Some(carried) = self.step(id, lhs, of, depth + 1) {
491                    return combine(carried, self.invariant(id, rhs)?, false);
492                }
493                combine(self.step(id, rhs, of, depth + 1)?, self.invariant(id, lhs)?, false)
494            }
495            Opcode::Sub => {
496                combine(self.step(id, lhs, of, depth + 1)?, self.invariant(id, rhs)?, true)
497            }
498            // A pointer walks by bytes, and only the pointer side can be the one carrying the
499            // induction variable. The offset is the step, which is the element size the front end
500            // already multiplied in.
501            Opcode::PtrAdd => {
502                combine(self.step(id, lhs, of, depth + 1)?, self.invariant(id, rhs)?, false)
503            }
504            _ => None,
505        }
506    }
507
508    /// The evolution of an instruction's result, from the evolutions of its operands.
509    fn at_inst(&mut self, id: LoopId, inst: Inst, value: Value) -> Evolution {
510        let func = self.func;
511        let data = &func[inst];
512        let (opcode, flags) = (data.opcode, data.flags);
513        let args = &func[data.args];
514        let ty = func[value].ty;
515        let Some(&lhs) = args.first() else { return Evolution::Unknown };
516        match opcode {
517            Opcode::Add | Opcode::PtrAdd => {
518                let Some(&rhs) = args.get(1) else { return Evolution::Unknown };
519                let (left, right) = (self.evolution(id, lhs), self.evolution(id, rhs));
520                combine(left, right, ty, flags, false)
521            }
522            Opcode::Sub => {
523                let Some(&rhs) = args.get(1) else { return Evolution::Unknown };
524                let (left, right) = (self.evolution(id, lhs), self.evolution(id, rhs));
525                combine(left, right, ty, flags, true)
526            }
527            Opcode::Mul => {
528                let Some(&rhs) = args.get(1) else { return Evolution::Unknown };
529                let (left, right) = (self.evolution(id, lhs), self.evolution(id, rhs));
530                scale(left, right, ty, flags)
531            }
532            // A shift by a constant is a multiplication by a power of two, and only by a constant:
533            // a variable count is invariant in the loop and still not a number this can multiply
534            // by. A count at or above the width is poison rather than a shift to zero, so the
535            // range is checked here rather than assumed.
536            Opcode::Shl => {
537                let Some(&rhs) = args.get(1) else { return Evolution::Unknown };
538                let Some((count, count_ty)) = constant(func, rhs) else {
539                    return Evolution::Unknown;
540                };
541                let count = count.unsigned();
542                if count >= u128::from(ty.bits()) || !count_ty.is_int() {
543                    return Evolution::Unknown;
544                }
545                let by = Evolution::Invariant(Invariant::number(1i128 << count));
546                scale(self.evolution(id, lhs), by, ty, flags)
547            }
548            Opcode::SExt | Opcode::ZExt => self.extend(id, opcode, lhs, ty),
549            // A truncation is a wrap by construction, so a chrec through one describes a sequence
550            // that restarts, and this does not have a representation for that.
551            _ => Evolution::Unknown,
552        }
553    }
554
555    /// A chrec widened, which needs the sequence not to wrap at the narrow width.
556    ///
557    /// Section 7.4 allows extension only where the extension provably does not wrap, and the
558    /// proof here is the flag the increment carries. `nsw` on the increment is the promise that
559    /// the signed sequence does not wrap, which is exactly what makes the wide sequence the same
560    /// numbers as the narrow one.
561    ///
562    /// Both parts have to be plain numbers. A symbolic base or step is a value of the narrow type
563    /// and the widened chrec would need it widened too, which is an expression nothing computes
564    /// and which [`Invariant`] has no room to describe. Saying so is the honest answer, the case
565    /// that matters most is a counter from a constant by a constant, and lifting the restriction
566    /// is work for whoever needs a symbolic one.
567    fn extend(&mut self, id: LoopId, opcode: Opcode, from: Value, to: Type) -> Evolution {
568        let narrow = self.func[from].ty;
569        let signed = opcode == Opcode::SExt;
570        match self.evolution(id, from) {
571            Evolution::Invariant(inv) => match inv.as_number() {
572                // A number read at the narrow width means the same thing at the wide one under
573                // sign extension, and under zero extension once it is not negative.
574                Some(number) if signed || number >= 0 => Evolution::Invariant(inv),
575                _ => Evolution::Unknown,
576            },
577            Evolution::Affine(chrec) if chrec.ty == narrow && chrec.does_not_wrap(signed) => {
578                let (Some(base), Some(step)) = (chrec.base.as_number(), chrec.step.as_number())
579                else {
580                    return Evolution::Unknown;
581                };
582                Evolution::Affine(Chrec {
583                    base: Invariant::number(base),
584                    step: Invariant::number(step),
585                    ty: to,
586                    flags: chrec.flags,
587                })
588            }
589            _ => Evolution::Unknown,
590        }
591    }
592
593    /// The trip count from the exit leaving this block, if this exit can be solved.
594    fn bound_at(&mut self, id: LoopId, from: Block) -> Option<Bound> {
595        let func = self.func;
596        let term = func.terminator(from)?;
597        if func[term].opcode != Opcode::BrIf {
598            return None;
599        }
600        let args = &func[func[term].args];
601        let &cond = args.first()?;
602        let calls = &func[func.target_list(term)];
603        let (&taken, &not_taken) = (calls.first()?, calls.get(1)?);
604        // Which arm keeps going. If both stay in or both leave, the branch is not the test that
605        // ends the loop and there is nothing here to solve.
606        let stays = match (
607            self.loops.contains(id, taken.block),
608            self.loops.contains(id, not_taken.block),
609        ) {
610            (true, false) => true,
611            (false, true) => false,
612            _ => return None,
613        };
614
615        let Def::Result { inst, .. } = func[cond].def else { return None };
616        if func[inst].opcode != Opcode::ICmp {
617            return None;
618        }
619        let Extra::IntPred(pred) = func[inst].extra else { return None };
620        // The loop keeps going while the test says so, so an exit taken when the test is true is
621        // an exit whose continuing condition is the opposite one.
622        let pred = if stays { pred } else { invert(pred) };
623        let operands = &func[func[inst].args];
624        let (&lhs, &rhs) = (operands.first()?, operands.get(1)?);
625
626        // One side evolves and the other does not. Swapping puts the one that evolves on the left
627        // and turns the predicate round with it, so only one direction has to be solved.
628        let (chrec, limit, pred) = match (self.evolution(id, lhs), self.evolution(id, rhs)) {
629            (Evolution::Affine(chrec), other) => (chrec, other.invariant()?, pred),
630            (other, Evolution::Affine(chrec)) => (chrec, other.invariant()?, swap(pred)),
631            _ => return None,
632        };
633        solve(chrec, limit, pred)
634    }
635}
636
637/// Two evolutions added, or subtracted when asked.
638fn combine(left: Evolution, right: Evolution, ty: Type, flags: Flags, subtract: bool) -> Evolution {
639    let apply = |a: Invariant, b: Invariant| if subtract { a.minus(b) } else { a.plus(b) };
640    match (left, right) {
641        (Evolution::Invariant(a), Evolution::Invariant(b)) => {
642            apply(a, b).map_or(Evolution::Unknown, Evolution::Invariant)
643        }
644        (Evolution::Affine(chrec), Evolution::Invariant(b)) => {
645            // Adding something that does not move only moves the base.
646            let Some(base) = apply(chrec.base, b) else { return Evolution::Unknown };
647            affine(base, chrec.step, ty, flags.intersection(chrec.flags))
648        }
649        (Evolution::Invariant(a), Evolution::Affine(chrec)) => {
650            let (Some(base), Some(step)) = (
651                apply(a, chrec.base),
652                if subtract { chrec.step.negated() } else { Some(chrec.step) },
653            ) else {
654                return Evolution::Unknown;
655            };
656            affine(base, step, ty, flags.intersection(chrec.flags))
657        }
658        (Evolution::Affine(a), Evolution::Affine(b)) => {
659            // Two chrecs of the same loop add componentwise, which is the closure property that
660            // makes the representation worth having. Of different types they do not, because the
661            // two sequences wrap at different widths.
662            if a.ty != b.ty {
663                return Evolution::Unknown;
664            }
665            let (Some(base), Some(step)) = (apply(a.base, b.base), apply(a.step, b.step)) else {
666                return Evolution::Unknown;
667            };
668            affine(base, step, ty, flags.intersection(a.flags).intersection(b.flags))
669        }
670        _ => Evolution::Unknown,
671    }
672}
673
674/// One evolution multiplied by another, which needs one of them to stand still.
675fn scale(left: Evolution, right: Evolution, ty: Type, flags: Flags) -> Evolution {
676    let (chrec, by) = match (left, right) {
677        (Evolution::Invariant(a), Evolution::Invariant(b)) => {
678            return a.times(b).map_or(Evolution::Unknown, Evolution::Invariant);
679        }
680        (Evolution::Affine(chrec), Evolution::Invariant(by))
681        | (Evolution::Invariant(by), Evolution::Affine(chrec)) => (chrec, by),
682        // Two chrecs multiplied give a quadratic, which is a chain of recurrences with a second
683        // step and is outside the subset section 7.4 chose.
684        _ => return Evolution::Unknown,
685    };
686    let (Some(base), Some(step)) = (chrec.base.times(by), chrec.step.times(by)) else {
687        return Evolution::Unknown;
688    };
689    affine(base, step, ty, flags.intersection(chrec.flags))
690}
691
692/// A chrec, or invariant when the step turns out to be nothing.
693///
694/// A step of zero is a valid affine chrec describing a value that does not move, and section 7.7
695/// warns that code dividing by the step to get a trip count divides by zero. Reporting it as
696/// invariant here means the shape is right for every reader rather than only for the careful
697/// ones, and the trip count solver still checks, because a step can also come out zero from a
698/// header parameter incremented by an invariant that happens to be zero.
699fn affine(base: Invariant, step: Invariant, ty: Type, flags: Flags) -> Evolution {
700    if step.is_zero() {
701        return Evolution::Invariant(base);
702    }
703    Evolution::Affine(Chrec { base, step, ty, flags })
704}
705
706/// The iteration at which `chrec pred limit` first fails, with what that rests on.
707fn solve(chrec: Chrec, limit: Invariant, pred: IntPred) -> Option<Bound> {
708    // Section 7.7's first way of being wrong. A step of zero is a loop that never leaves through
709    // this exit, and dividing the distance by it is a crash rather than an answer.
710    let step = chrec.step.as_number()?;
711    if step == 0 {
712        return None;
713    }
714    let signed = matches!(pred, IntPred::Slt | IntPred::Sle | IntPred::Sgt | IntPred::Sge);
715
716    let mut assumptions = Vec::new();
717    if !chrec.does_not_wrap(signed) {
718        assumptions.push(Assumption::NoWrap(chrec));
719    }
720    if signed {
721        assumptions.push(Assumption::StrictOverflow);
722    }
723
724    // A test that does not read its operands as signed does not read the constants in them that
725    // way either, and every constant reaching here was read as signed on the way in.
726    let (base, limit) = if signed {
727        (chrec.base, limit)
728    } else {
729        (as_unsigned(chrec.base, chrec.ty)?, as_unsigned(limit, chrec.ty)?)
730    };
731
732    // The distance the counter has to travel, always counting up. A loop going down is the same
733    // problem with the ends swapped, which is why the step is used by size below and its sign is
734    // spent here.
735    let apart = step.unsigned_abs();
736    match (pred, step > 0) {
737        (IntPred::Slt | IntPred::Ult, true) => {
738            ordered(limit.minus(base)?, apart, false, assumptions)
739        }
740        (IntPred::Sle | IntPred::Ule, true) => {
741            ordered(limit.minus(base)?, apart, true, assumptions)
742        }
743        (IntPred::Sgt | IntPred::Ugt, false) => {
744            ordered(base.minus(limit)?, apart, false, assumptions)
745        }
746        (IntPred::Sge | IntPred::Uge, false) => {
747            ordered(base.minus(limit)?, apart, true, assumptions)
748        }
749        (IntPred::Ne, _) => {
750            let distance = if step > 0 { limit.minus(base)? } else { base.minus(limit)? };
751            landing(distance, apart, assumptions)
752        }
753        // Either the counter steps away from the limit, in which case the loop is endless rather
754        // than long, or the test is one this does not solve. Silence is the answer to both.
755        _ => None,
756    }
757}
758
759/// The same expression, read the way a test without a sign reads it.
760///
761/// Constants arrive here as the number their bits are when the sign bit is taken seriously,
762/// because that is the only reading available before anybody knows what will be done with them.
763/// An unsigned test disagrees about half of them. `for (unsigned char i = 0; i < 200; i++)` holds
764/// its limit as minus fifty six, and a distance worked out from that is negative, which reads as
765/// a loop that runs no times rather than one that runs two hundred.
766///
767/// The step is not put through this, because a step is a difference rather than a value and its
768/// signed reading is the one that says which way the counter goes.
769fn as_unsigned(inv: Invariant, ty: Type) -> Option<Invariant> {
770    match inv.as_number() {
771        Some(number) if number >= 0 => Some(inv),
772        Some(number) => {
773            // Only an integer constant was read as signed in the first place. A pointer never
774            // was, so a negative number sitting in one is an expression this cannot reinterpret.
775            let bits = ty.is_int().then(|| ty.bits()).filter(|&bits| bits < 127)?;
776            Some(Invariant::number(number & ((1i128 << bits) - 1)))
777        }
778        // A symbolic operand is whatever it is at run time, and the subtraction below cancels it
779        // rather than reading it, so long as nothing signed has been folded in beside it.
780        None => (inv.scale == 1 && inv.offset == 0).then_some(inv),
781    }
782}
783
784/// The count for an exit tested with an ordering, where overshooting the limit still ends it.
785fn ordered(
786    distance: Invariant,
787    step: u128,
788    inclusive: bool,
789    mut assumptions: Vec<Assumption>,
790) -> Option<Bound> {
791    match distance.as_number() {
792        Some(exact) => {
793            if exact < 0 {
794                // The counter starts past the limit, so the test fails the first time it runs.
795                // That is a count of zero and it rests on nothing at all, not even on the counter
796                // behaving, because the counter never moves.
797                return Some(Bound { count: Count::Exact(0), assumptions: Vec::new() });
798            }
799            // Rounding up, because a step that overshoots still took the iteration that overshot.
800            let count = (exact.unsigned_abs() + u128::from(inclusive)).div_ceil(step);
801            Some(Bound { count: Count::Exact(count), assumptions })
802        }
803        // Symbolic, and only for a step of one, because dividing an expression by anything else
804        // needs a representation for a division and there is not one here.
805        None if step == 1 => {
806            assumptions.push(Assumption::Approaching);
807            let count = distance.plus(Invariant::number(i128::from(inclusive)))?;
808            Some(Bound { count: Count::Symbolic(count), assumptions })
809        }
810        None => None,
811    }
812}
813
814/// The count for an exit tested with `!=`, where the counter has to land on the limit exactly.
815///
816/// This is a different problem from the one above and not a special case of it. An ordering test
817/// ends the loop the moment the counter is past the limit, so a step that overshoots still stops.
818/// `!=` only ends the loop on the one iteration where the counter is the limit, so a counter that
819/// steps over the limit, or that starts on the far side of it, keeps going until it wraps. Both
820/// of those are endless loops rather than short ones, and answering zero for either was the bug
821/// this function exists to not have.
822fn landing(distance: Invariant, step: u128, mut assumptions: Vec<Assumption>) -> Option<Bound> {
823    match distance.as_number() {
824        Some(exact) => {
825            let travel = u128::try_from(exact).ok()?;
826            // Checked outright rather than assumed, which is why nothing here needs an assumption
827            // about the step dividing anything.
828            (travel % step == 0).then(|| Bound { count: Count::Exact(travel / step), assumptions })
829        }
830        // A step of one lands on everything ahead of it, so the only thing left to establish is
831        // that the limit is ahead. `while (p != end)` is this case, and a step of anything else
832        // would need the division a symbolic distance has no room for.
833        None if step == 1 => {
834            assumptions.push(Assumption::Approaching);
835            Some(Bound { count: Count::Symbolic(distance), assumptions })
836        }
837        None => None,
838    }
839}
840
841/// The predicate that is true exactly when this one is not.
842fn invert(pred: IntPred) -> IntPred {
843    match pred {
844        IntPred::Eq => IntPred::Ne,
845        IntPred::Ne => IntPred::Eq,
846        IntPred::Slt => IntPred::Sge,
847        IntPred::Sle => IntPred::Sgt,
848        IntPred::Sgt => IntPred::Sle,
849        IntPred::Sge => IntPred::Slt,
850        IntPred::Ult => IntPred::Uge,
851        IntPred::Ule => IntPred::Ugt,
852        IntPred::Ugt => IntPred::Ule,
853        IntPred::Uge => IntPred::Ult,
854    }
855}
856
857/// The predicate that says the same thing with the operands the other way round.
858fn swap(pred: IntPred) -> IntPred {
859    match pred {
860        IntPred::Eq => IntPred::Eq,
861        IntPred::Ne => IntPred::Ne,
862        IntPred::Slt => IntPred::Sgt,
863        IntPred::Sle => IntPred::Sge,
864        IntPred::Sgt => IntPred::Slt,
865        IntPred::Sge => IntPred::Sle,
866        IntPred::Ult => IntPred::Ugt,
867        IntPred::Ule => IntPred::Uge,
868        IntPred::Ugt => IntPred::Ult,
869        IntPred::Uge => IntPred::Ule,
870    }
871}
872
873/// The constant a value is, if it is one.
874fn constant(func: &Func, value: Value) -> Option<(Imm, Type)> {
875    let Def::Result { inst, .. } = func[value].def else { return None };
876    if func[inst].opcode != Opcode::IConst {
877        return None;
878    }
879    let Extra::Imm(at) = func[inst].extra else { return None };
880    let ty = func[value].ty;
881    ty.is_int().then(|| (func[at], ty))
882}
883
884/// What this predecessor passes to the block's parameter at this position.
885///
886/// `None` when the predecessor branches to the block more than once with different arguments,
887/// which a `br_if` with both arms on the same block can do and which means the parameter takes a
888/// value that depends on the test rather than on the edge.
889fn argument(func: &Func, pred: Block, block: Block, index: usize) -> Option<Value> {
890    let term = func.terminator(pred)?;
891    let mut found = None;
892    for call in func.successors(term) {
893        if call.block != block {
894            continue;
895        }
896        let arg = *func[call.args].get(index)?;
897        if found.replace(arg).is_some_and(|old| old != arg) {
898            return None;
899        }
900    }
901    found
902}
903
904#[cfg(test)]
905mod tests {
906    use rucc_base::Interner;
907    use rucc_ir::{Builder, Flags, Func, IntPred, Opcode, Signature, Type, Value};
908
909    use crate::cfg::Cfg;
910    use crate::dom::Dominators;
911    use crate::loops::{LoopId, Loops};
912    use crate::scev::{Assumption, Bound, Count, Evolution, Invariant, Scev};
913
914    /// A loop counting in `ty` from `from` by `step` while the counter is below `to`.
915    ///
916    /// ```text
917    /// entry:  jump header(from)
918    /// header(i): test = icmp pred i, to ; br_if test, body, exit
919    /// body:   next = add i, step ; jump header(next)
920    /// exit:   ret
921    /// ```
922    ///
923    /// The counter is the header's only parameter, which is what the tests ask about.
924    struct Counted {
925        func: Func,
926        counter: Value,
927        next: Value,
928    }
929
930    fn counted(ty: Type, from: i128, to: i128, step: i128, pred: IntPred, flags: Flags) -> Counted {
931        let (it, ()) = counted_with(ty, from, to, step, pred, flags, |_, _| ());
932        it
933    }
934
935    /// The same loop, with `extra` run in the body on the counter before the counter steps.
936    ///
937    /// The builder appends, and the body's `jump` back to the header has to stay the last
938    /// instruction in it or the block has no terminator and the loop stops being one. So anything
939    /// a test wants derived from the counter goes in here rather than being tacked on afterwards.
940    fn counted_with<T>(
941        ty: Type,
942        from: i128,
943        to: i128,
944        step: i128,
945        pred: IntPred,
946        flags: Flags,
947        extra: impl FnOnce(&mut Builder<'_>, Value) -> T,
948    ) -> (Counted, T) {
949        let mut names = Interner::new();
950        let mut func = Func::new(names.intern("f"), Signature::new());
951        let entry = func.create_block();
952        let header = func.create_block();
953        let body = func.create_block();
954        let exit = func.create_block();
955        let counter = func.append_param(header, ty);
956
957        let mut build = Builder::new(&mut func, entry);
958        let start = build.iconst(ty, from);
959        build.jump(header, &[start]);
960
961        let mut build = Builder::new(&mut func, header);
962        let limit = build.iconst(ty, to);
963        let test = build.icmp(pred, counter, limit);
964        build.br_if(test, body, &[], exit, &[]);
965
966        let mut build = Builder::new(&mut func, body);
967        let derived = extra(&mut build, counter);
968        let by = build.iconst(ty, step);
969        let next = build.binary(Opcode::Add, counter, by, flags);
970        build.jump(header, &[next]);
971
972        let mut build = Builder::new(&mut func, exit);
973        build.ret(&[]);
974
975        (Counted { func, counter, next }, derived)
976    }
977
978    /// The analysis over a function, along with the one loop it has.
979    fn analyse(func: &Func) -> (Cfg, Loops) {
980        let cfg = Cfg::new(func);
981        let doms = Dominators::new(&cfg);
982        let loops = Loops::new(&cfg, &doms);
983        (cfg, loops)
984    }
985
986    /// The chrec of a value in the one loop of a function.
987    fn evolution(func: &Func, value: Value) -> Evolution {
988        let (cfg, loops) = analyse(func);
989        let id = loops.roots()[0];
990        Scev::new(func, &cfg, &loops).evolution(id, value)
991    }
992
993    /// The trip count of the one loop of a function.
994    fn bound(func: &Func) -> Option<Bound> {
995        let (cfg, loops) = analyse(func);
996        let id: LoopId = loops.roots()[0];
997        Scev::new(func, &cfg, &loops).bound(id)
998    }
999
1000    #[test]
1001    fn a_counter_from_zero_by_one_is_the_chrec_everyone_expects() {
1002        let it = counted(Type::int(32), 0, 100, 1, IntPred::Slt, Flags::NSW);
1003        let chrec = evolution(&it.func, it.counter).chrec().expect("the counter evolves");
1004        assert_eq!(chrec.base, Invariant::number(0));
1005        assert_eq!(chrec.step, Invariant::number(1));
1006        assert_eq!(chrec.ty, Type::int(32));
1007        assert!(chrec.does_not_wrap(true));
1008    }
1009
1010    #[test]
1011    fn the_value_fed_back_is_the_chrec_one_step_along() {
1012        let it = counted(Type::int(32), 5, 100, 3, IntPred::Slt, Flags::NSW);
1013        let chrec = evolution(&it.func, it.next).chrec().expect("the increment evolves");
1014        assert_eq!(chrec.base, Invariant::number(8));
1015        assert_eq!(chrec.step, Invariant::number(3));
1016    }
1017
1018    #[test]
1019    fn a_multiple_of_the_counter_plus_a_number_is_a_chrec_of_its_own() {
1020        // `j = 2 * i + 3` where `i = {0, +, 1}`, which is the shape section 7.4 says pattern
1021        // matching runs out of road on and chains of recurrences do not.
1022        let (it, shifted) =
1023            counted_with(Type::int(32), 0, 100, 1, IntPred::Slt, Flags::NSW, |build, counter| {
1024                let two = build.iconst(Type::int(32), 2);
1025                let three = build.iconst(Type::int(32), 3);
1026                let doubled = build.binary(Opcode::Mul, counter, two, Flags::NSW);
1027                build.binary(Opcode::Add, doubled, three, Flags::NSW)
1028            });
1029
1030        let chrec = evolution(&it.func, shifted).chrec().expect("it evolves");
1031        assert_eq!(chrec.base, Invariant::number(3));
1032        assert_eq!(chrec.step, Invariant::number(2));
1033    }
1034
1035    #[test]
1036    fn a_shift_by_a_constant_scales_the_chrec_and_a_shift_past_the_width_does_not() {
1037        let (it, (scaled, poison)) =
1038            counted_with(Type::int(32), 1, 100, 1, IntPred::Slt, Flags::NSW, |build, counter| {
1039                let three = build.iconst(Type::int(32), 3);
1040                let wide = build.iconst(Type::int(32), 32);
1041                (
1042                    build.binary(Opcode::Shl, counter, three, Flags::NSW),
1043                    build.binary(Opcode::Shl, counter, wide, Flags::NSW),
1044                )
1045            });
1046
1047        let chrec = evolution(&it.func, scaled).chrec().expect("it evolves");
1048        assert_eq!(chrec.base, Invariant::number(8));
1049        assert_eq!(chrec.step, Invariant::number(8));
1050        // A count at the width is poison rather than a shift to zero, so there is no sequence to
1051        // describe.
1052        assert_eq!(evolution(&it.func, poison), Evolution::Unknown);
1053    }
1054
1055    #[test]
1056    fn a_pointer_walked_by_the_element_size_is_a_chrec_in_bytes() {
1057        // What `for (p = a; p != end; p++)` lowers to on an array of four byte elements. Section
1058        // 7.4 calls this the one deliberate extension past affine and the difference between
1059        // analysing half of real C loops and nearly all of them.
1060        let mut names = Interner::new();
1061        let mut func = Func::new(names.intern("f"), Signature::new());
1062        let entry = func.create_block();
1063        let header = func.create_block();
1064        let body = func.create_block();
1065        let exit = func.create_block();
1066        let start = func.append_param(entry, Type::PTR);
1067        let cursor = func.append_param(header, Type::PTR);
1068
1069        let mut build = Builder::new(&mut func, entry);
1070        build.jump(header, &[start]);
1071        let mut build = Builder::new(&mut func, header);
1072        let done = build.icmp(IntPred::Eq, cursor, start);
1073        build.br_if(done, exit, &[], body, &[]);
1074        let mut build = Builder::new(&mut func, body);
1075        let four = build.iconst(Type::int(64), 4);
1076        let next = build.binary(Opcode::PtrAdd, cursor, four, Flags::NONE);
1077        build.jump(header, &[next]);
1078        let mut build = Builder::new(&mut func, exit);
1079        build.ret(&[]);
1080
1081        let chrec = evolution(&func, cursor).chrec().expect("the cursor evolves");
1082        assert_eq!(chrec.base, Invariant::of(start));
1083        assert_eq!(chrec.step, Invariant::number(4));
1084        assert_eq!(chrec.ty, Type::PTR);
1085    }
1086
1087    #[test]
1088    fn a_counter_in_unsigned_char_wraps_and_does_not_widen_without_a_promise() {
1089        // Section 7.7's second way of being wrong. `{0, +, 1}` in `unsigned char` is not
1090        // `0, 1, 2, ...`, it is that modulo two hundred and fifty six, and widening it is only
1091        // the same sequence if it does not get that far.
1092        let (it, wide) =
1093            counted_with(Type::int(8), 0, 100, 1, IntPred::Ult, Flags::NONE, |build, counter| {
1094                build.unary(Opcode::ZExt, counter, Type::int(32))
1095            });
1096        let chrec = evolution(&it.func, it.counter).chrec().expect("the counter evolves");
1097        assert_eq!(chrec.ty, Type::int(8));
1098        assert!(!chrec.does_not_wrap(false));
1099        assert_eq!(evolution(&it.func, wide), Evolution::Unknown);
1100    }
1101
1102    #[test]
1103    fn a_counter_in_short_widens_when_the_increment_promised_it_would_not_wrap() {
1104        let (it, (wide, zero_extended)) =
1105            counted_with(Type::int(16), 0, 100, 1, IntPred::Slt, Flags::NSW, |build, counter| {
1106                (
1107                    build.unary(Opcode::SExt, counter, Type::int(32)),
1108                    build.unary(Opcode::ZExt, counter, Type::int(32)),
1109                )
1110            });
1111
1112        let chrec = evolution(&it.func, wide).chrec().expect("it widens");
1113        assert_eq!(chrec.ty, Type::int(32));
1114        assert_eq!(chrec.base, Invariant::number(0));
1115        assert_eq!(chrec.step, Invariant::number(1));
1116        // `nsw` is a promise about the signed reading and says nothing about the unsigned one.
1117        assert_eq!(evolution(&it.func, zero_extended), Evolution::Unknown);
1118    }
1119
1120    #[test]
1121    fn a_step_of_zero_is_invariant_and_has_no_trip_count() {
1122        // Section 7.7's first way of being wrong. `i += k` with `k` of zero is a valid affine
1123        // chrec of a loop that never leaves through this exit, and code dividing the distance by
1124        // the step divides by zero.
1125        let it = counted(Type::int(32), 0, 100, 0, IntPred::Slt, Flags::NSW);
1126        assert!(matches!(evolution(&it.func, it.counter), Evolution::Invariant(_)));
1127        assert_eq!(bound(&it.func), None);
1128    }
1129
1130    #[test]
1131    fn a_counted_loop_has_the_count_anyone_would_work_out_by_hand() {
1132        let it = counted(Type::int(32), 0, 100, 1, IntPred::Slt, Flags::NSW);
1133        let found = bound(&it.func).expect("it is counted");
1134        let (count, assumptions) = found.parts();
1135        assert_eq!(count, Count::Exact(100));
1136        // The distance is a number and it is not negative, so being entered is not in question.
1137        // Signed overflow being undefined still is, which is what `-fwrapv` would withdraw.
1138        assert_eq!(assumptions, [Assumption::StrictOverflow]);
1139        assert_eq!(found.proven(), None);
1140    }
1141
1142    #[test]
1143    fn a_step_that_overshoots_still_takes_the_iteration_that_overshot() {
1144        // Zero, three, six, nine, and the test fails at twelve, so four iterations rather than
1145        // three and a third. Rounding the other way is an off by one in every unroller.
1146        let it = counted(Type::int(32), 0, 10, 3, IntPred::Slt, Flags::NSW);
1147        let (count, _) = bound(&it.func).expect("it is counted").parts();
1148        assert_eq!(count, Count::Exact(4));
1149    }
1150
1151    #[test]
1152    fn an_inclusive_test_runs_one_more_time() {
1153        let it = counted(Type::int(32), 0, 10, 1, IntPred::Sle, Flags::NSW);
1154        let (count, _) = bound(&it.func).expect("it is counted").parts();
1155        assert_eq!(count, Count::Exact(11));
1156    }
1157
1158    #[test]
1159    fn a_loop_whose_test_fails_first_time_runs_no_times_and_rests_on_nothing() {
1160        let it = counted(Type::int(32), 10, 0, 1, IntPred::Slt, Flags::NSW);
1161        let found = bound(&it.func).expect("it is counted");
1162        assert_eq!(found.proven(), Some(Count::Exact(0)));
1163        assert!(found.assumptions().is_empty());
1164    }
1165
1166    #[test]
1167    fn counting_down_is_the_same_problem_with_the_ends_swapped() {
1168        let it = counted(Type::int(32), 10, 0, -1, IntPred::Sgt, Flags::NSW);
1169        let (count, _) = bound(&it.func).expect("it is counted").parts();
1170        assert_eq!(count, Count::Exact(10));
1171    }
1172
1173    #[test]
1174    fn an_unsigned_test_does_not_drag_in_the_signed_overflow_assumption() {
1175        let it = counted(Type::int(32), 0, 100, 1, IntPred::Ult, Flags::NUW);
1176        let found = bound(&it.func).expect("it is counted");
1177        assert_eq!(found.proven(), Some(Count::Exact(100)));
1178    }
1179
1180    #[test]
1181    fn a_test_against_something_the_loop_does_not_change_gives_a_symbolic_count() {
1182        // `for (i = 0; i < n; i++)`, where the answer is `n` and is only `n` if the loop is
1183        // entered, because `n` of minus one runs no times and the distance is minus one.
1184        let mut names = Interner::new();
1185        let mut func = Func::new(names.intern("f"), Signature::new());
1186        let entry = func.create_block();
1187        let header = func.create_block();
1188        let body = func.create_block();
1189        let exit = func.create_block();
1190        let limit = func.append_param(entry, Type::int(32));
1191        let counter = func.append_param(header, Type::int(32));
1192
1193        let mut build = Builder::new(&mut func, entry);
1194        let zero = build.iconst(Type::int(32), 0);
1195        build.jump(header, &[zero]);
1196        let mut build = Builder::new(&mut func, header);
1197        let test = build.icmp(IntPred::Slt, counter, limit);
1198        build.br_if(test, body, &[], exit, &[]);
1199        let mut build = Builder::new(&mut func, body);
1200        let one = build.iconst(Type::int(32), 1);
1201        let next = build.binary(Opcode::Add, counter, one, Flags::NSW);
1202        build.jump(header, &[next]);
1203        let mut build = Builder::new(&mut func, exit);
1204        build.ret(&[]);
1205
1206        let found = bound(&func).expect("it is counted");
1207        let (count, assumptions) = found.parts();
1208        assert_eq!(count, Count::Symbolic(Invariant::of(limit)));
1209        assert!(assumptions.contains(&Assumption::Approaching), "{assumptions:?}");
1210        assert!(assumptions.contains(&Assumption::StrictOverflow), "{assumptions:?}");
1211        assert_eq!(found.proven(), None);
1212    }
1213
1214    #[test]
1215    fn a_counter_without_a_no_wrap_promise_carries_the_assumption_instead() {
1216        let it = counted(Type::int(32), 0, 100, 1, IntPred::Ult, Flags::NONE);
1217        let found = bound(&it.func).expect("it is counted");
1218        let (_, assumptions) = found.parts();
1219        assert!(assumptions.iter().any(|a| matches!(a, Assumption::NoWrap(_))), "{assumptions:?}");
1220    }
1221
1222    #[test]
1223    fn a_test_that_ends_the_loop_when_it_succeeds_is_read_the_other_way_round() {
1224        // `for (i = 0; ; i++) if (i >= 100) break;`, which is the same loop with the arms of the
1225        // branch swapped. The test that keeps the loop going is the opposite of the one written.
1226        let mut names = Interner::new();
1227        let mut func = Func::new(names.intern("f"), Signature::new());
1228        let entry = func.create_block();
1229        let header = func.create_block();
1230        let body = func.create_block();
1231        let exit = func.create_block();
1232        let counter = func.append_param(header, Type::int(32));
1233
1234        let mut build = Builder::new(&mut func, entry);
1235        let zero = build.iconst(Type::int(32), 0);
1236        build.jump(header, &[zero]);
1237        let mut build = Builder::new(&mut func, header);
1238        let limit = build.iconst(Type::int(32), 100);
1239        let done = build.icmp(IntPred::Sge, counter, limit);
1240        build.br_if(done, exit, &[], body, &[]);
1241        let mut build = Builder::new(&mut func, body);
1242        let one = build.iconst(Type::int(32), 1);
1243        let next = build.binary(Opcode::Add, counter, one, Flags::NSW);
1244        build.jump(header, &[next]);
1245        let mut build = Builder::new(&mut func, exit);
1246        build.ret(&[]);
1247
1248        let (count, _) = bound(&func).expect("it is counted").parts();
1249        assert_eq!(count, Count::Exact(100));
1250    }
1251
1252    #[test]
1253    fn an_unsigned_limit_past_the_middle_of_its_type_is_not_a_negative_one() {
1254        // `for (unsigned char i = 0; i < 200; i++)`. Two hundred does not fit in a signed byte
1255        // and the constant is held as minus fifty six, so a distance taken at face value is
1256        // negative and reads as a loop that runs no times.
1257        let it = counted(Type::int(8), 0, 200, 1, IntPred::Ult, Flags::NUW);
1258        let found = bound(&it.func).expect("it is counted");
1259        assert_eq!(found.proven(), Some(Count::Exact(200)));
1260    }
1261
1262    #[test]
1263    fn a_walk_that_lands_on_a_not_equal_limit_exactly_is_counted() {
1264        // `while (i != 10)` counting by one, which is `while (p != end)` over an array once the
1265        // element size has been divided out. `!=` says nothing about how its operands are read,
1266        // so the promise it wants is the unsigned one and an `nsw` on its own is not enough.
1267        let it = counted(Type::int(32), 0, 10, 1, IntPred::Ne, Flags::NSW.union(Flags::NUW));
1268        let found = bound(&it.func).expect("it lands on its limit");
1269        // The step divides the distance and both are numbers, so it was checked rather than
1270        // assumed and there is nothing left over.
1271        assert_eq!(found.proven(), Some(Count::Exact(10)));
1272    }
1273
1274    #[test]
1275    fn a_counter_stepping_away_from_a_not_equal_limit_is_not_a_loop_that_runs_no_times() {
1276        // The distance is negative and an ordering test would read that as the loop never being
1277        // entered. `!=` reads it as the counter never arriving, which is an endless loop, and
1278        // answering zero for it was a real bug that the property test in `tests/scev.rs` found.
1279        let it = counted(Type::int(32), 48, 15, 1, IntPred::Ne, Flags::NSW);
1280        assert_eq!(bound(&it.func), None);
1281    }
1282
1283    #[test]
1284    fn a_counter_stepping_over_a_not_equal_limit_never_arrives_either() {
1285        // Zero, three, six, nine, twelve, and ten is never one of them. An ordering test would
1286        // have stopped at twelve.
1287        let it = counted(Type::int(32), 0, 10, 3, IntPred::Ne, Flags::NSW);
1288        assert_eq!(bound(&it.func), None);
1289    }
1290
1291    #[test]
1292    fn an_estimate_is_the_count_when_there_is_one_and_a_guess_when_there_is_not() {
1293        let counted_loop = counted(Type::int(32), 0, 7, 1, IntPred::Slt, Flags::NSW);
1294        let (cfg, loops) = analyse(&counted_loop.func);
1295        let id = loops.roots()[0];
1296        let estimate = Scev::new(&counted_loop.func, &cfg, &loops).estimate(id);
1297        assert_eq!(estimate.iterations(), 7);
1298        assert!(!estimate.is_guess());
1299
1300        // A loop this cannot count still has to answer, because the caller is deciding whether
1301        // something is worth doing rather than whether it is legal.
1302        let uncounted = counted(Type::int(32), 0, 100, 0, IntPred::Slt, Flags::NSW);
1303        let (cfg, loops) = analyse(&uncounted.func);
1304        let id = loops.roots()[0];
1305        let estimate = Scev::new(&uncounted.func, &cfg, &loops).estimate(id);
1306        assert!(estimate.is_guess());
1307        assert_eq!(estimate.iterations(), super::ASSUMED_ITERATIONS);
1308    }
1309
1310    #[test]
1311    fn a_value_the_loop_does_not_touch_is_invariant_rather_than_unknown() {
1312        let it = counted(Type::int(32), 0, 100, 1, IntPred::Slt, Flags::NSW);
1313        let (cfg, loops) = analyse(&it.func);
1314        let id = loops.roots()[0];
1315        let mut scev = Scev::new(&it.func, &cfg, &loops);
1316        // The counter's start is an `iconst` in the entry block, which is both.
1317        assert_eq!(
1318            scev.evolution(id, it.counter).chrec().expect("it evolves").base,
1319            Invariant::number(0)
1320        );
1321    }
1322
1323    #[test]
1324    fn every_assumption_says_what_it_is_in_a_line() {
1325        let it = counted(Type::int(8), 0, 100, 1, IntPred::Ult, Flags::NONE);
1326        let found = bound(&it.func).expect("it is counted");
1327        for assumption in found.assumptions() {
1328            let line = assumption.describe();
1329            assert!(!line.is_empty());
1330            assert!(!line.contains('\n'), "an assumption is one line: {line}");
1331        }
1332    }
1333}