Skip to main content

rucc_opt/range/
query.rs

1//! Asking what a value is at a point, and answering it by walking backwards from there.
2//!
3//! Design: `spec/optimizer/10-value-ranges.md` sections 10.1, 10.3 and 10.6. The representation
4//! is [`super::Range`] and the arithmetic over it is [`super::ops`]. This is the part that reads
5//! a function.
6//!
7//! # On demand, and why that is the whole design
8//!
9//! The textbook version of this analysis is a forward propagation: start every value at empty,
10//! iterate over the control flow graph to a fixed point, keep a range per value. Section 10.1
11//! says what is wrong with it, and it is not the running time. It is that the range such a pass
12//! stores is the range at the definition, and the question anyone actually has is the range at a
13//! use, which is narrower by every branch in between. A pass that answers the first question
14//! precisely and the second one not at all has computed the wrong thing carefully.
15//!
16//! So [`Ranges::at`] takes a value and a block and walks backwards. The definition of the value
17//! gives a first answer, the branches that dominate the block narrow it, and nothing is computed
18//! for a value nobody asked about. Section 10.1 measured the ratio the other way round and rucc
19//! has fewer consumers than GCC does, so the ratio here is worse.
20//!
21//! # Inverting the condition, which is where the precision is
22//!
23//! `if (x < 10)` tells you about `x` and that is easy. `if (x + 3 < 10)` tells you about `x + 3`,
24//! and the fact worth having is that `x` is at most six. GCC calls the machinery that gets from
25//! one to the other GORI, and it is the inverse half of the table in [`super::ops`] applied along
26//! the chain from the condition back to the value being asked about.
27//!
28//! [`Ranges::at`] does that walk. It is bounded, because the chain can be as long as the function
29//! and because a walk that is not bounded is a compile time bug waiting for the right input.
30//! [`Options::logical_depth`] is how deep it goes, and it is GCC's `ranger-logical-depth`, whose
31//! default is the same six.
32//!
33//! # The oracle, which knows things intervals cannot say
34//!
35//! `a < b` is not a fact about the range of either. If both are `[0, 100]` the intervals say
36//! nothing, and yet a branch may have proved it. Section 10.3 says to keep this and to keep it
37//! small, so [`Ranges::relation`] answers from what was recorded on the dominating edges plus one
38//! step of composition, and it is keyed by block because `a < b` holds on one edge and not on the
39//! other one out of the same branch. Section 10.7 lists a relation recorded without its block as
40//! a way to be wrong, and it is the one that would show up as a miscompilation rather than as a
41//! missed optimization.
42//!
43//! # The cache is bounded on purpose
44//!
45//! A cache holding a range per value per block is quadratic in function size, and section 10.6
46//! points out that the input which makes that hurt is not hypothetical: generated parsers have
47//! tens of thousands of blocks and it is why GCC has `vrp-sparse-threshold` at all. So the cache
48//! here holds one range per value at its definition and at most [`Options::refinements`]
49//! block-specific answers beside it. Past that, a query for a new block gets the definition
50//! range, which is correct and less precise, and [`Counts::fallbacks`] says how often that
51//! happened. The bound is a parameter rather than a constant because the right number is an
52//! empirical question and section 10.6 says GCC's numbers are a record of bug reports.
53//!
54//! # How this is wrong
55//!
56//! A value carried around a loop is not pinned down by the walk. The walk assumes the range of the
57//! type for a value it is already in the middle of computing, which is what makes it terminate, so
58//! what comes back for a loop counter is one step of the recurrence applied to everything rather
59//! than the interval a fixed point would reach. That is sound, because every operation here
60//! over-approximates and the assumption it started from does too, and it is loose.
61//!
62//! `Ranges::counter` is what makes up the difference, and it is document 07's scalar evolution
63//! rather than a widening operator, which is what this paragraph used to say the honest answer
64//! would be. What it recovers is the end of a counter that the exit test does not say anything
65//! about, which is the end it started from. The gap left is a counter whose start is a value rather
66//! than a number.
67//!
68//! Ranges derived from an overflow flag are ranges derived from undefined behaviour, and section
69//! 10.7 says those have to be visible. [`Counts::assumed`] counts them, which is less than that
70//! section asks for: it wants `-fdump-ranges` to mark them and name the line, and the dump is not
71//! here yet.
72//!
73//! Precision loss is the failure mode with no symptom. [`Counts::losses`] breaks the queries that
74//! came back knowing nothing down by the opcode that lost it, which is how the table in
75//! [`super::ops`] grows by evidence rather than by guesswork.
76
77use std::collections::{BTreeMap, HashMap, HashSet};
78
79use rucc_ir::{Block, Def, Extra, Func, Inst, IntPred, Opcode, Value};
80
81use super::ops::{self, Truth, Undo};
82use super::{PAIRS, Range};
83use crate::cfg::Cfg;
84use crate::dom::Dominators;
85use crate::loops::Loops;
86use crate::scev::Scev;
87
88/// How many relations one block's chain of dominating edges keeps.
89///
90/// The oracle is a list rather than a matrix, so the cost of a query is the length of this and
91/// the cost of holding one is a small vector per block. Sixteen is more relations than any block
92/// in real C is dominated by, and a block that is dominated by more than sixteen keeps the ones
93/// nearest to it, which are the ones a query is most likely to be about.
94const RELATIONS: usize = 16;
95
96/// How many cases a switch default edge will exclude before it stops trying.
97///
98/// Excluding one value from a range costs an interval and there are [`PAIRS`] of them, so the
99/// fourth exclusion cannot be represented and the fifth is wasted work. This is not a limit on
100/// how many cases a switch may have.
101const EXCLUSIONS: usize = PAIRS + 1;
102
103/// The limits, all three of which exist because the thing they bound is otherwise unbounded.
104#[derive(Clone, Copy, Debug, PartialEq, Eq)]
105pub struct Options {
106    /// How deep into a condition the edge calculation looks, and how far back along the chain
107    /// from a condition to a value the inversion walks.
108    ///
109    /// GCC's `ranger-logical-depth`, whose default at `gcc/params.opt:998` is also six.
110    pub logical_depth: u32,
111    /// How many dominating edges one query walks before it stops narrowing.
112    ///
113    /// GCC's `ranger-recompute-depth` at `gcc/params.opt:1003` bounds a related walk with the
114    /// same default of five. The two are not the same walk, so the number is borrowed and the
115    /// meaning is not.
116    pub recompute_depth: u32,
117    /// How many block-specific answers the cache keeps for one value.
118    ///
119    /// Section 10.6's one threshold. A query past it gets the range at the definition.
120    pub refinements: usize,
121    /// How many definitions one set of queries works out before it stops narrowing.
122    ///
123    /// The other three bound one walk each and none of them bounds what a function's worth of
124    /// questions adds up to. What makes that a real number rather than a theoretical one is the
125    /// cycle rule: a range worked out while a cycle was open was worked out under an assumption,
126    /// so it is not cached, so the next question about it does the whole cycle again. Eight blocks
127    /// that dispatch to each other through a computed goto are eight values in one cycle and every
128    /// question about any of them walks all eight, which multiplies rather than adds.
129    ///
130    /// Past this every answer is the whole of the type. That is what a range knowing nothing is,
131    /// so what a program over the limit loses is code quality and not correctness, and
132    /// [`Counts::exhausted`] is how it is found out about rather than guessed at.
133    pub budget: u64,
134}
135
136impl Default for Options {
137    fn default() -> Self {
138        Self { logical_depth: 6, recompute_depth: 5, refinements: 8, budget: 4096 }
139    }
140}
141
142/// What the queries did, which is the only way to find out that this is not working.
143///
144/// A range that came back knowing nothing produces correct code that is slower, with no test
145/// failing and no warning printed. Section 10.7 says the defence is a counter and section 10.8
146/// says `-ftime-report` prints it.
147#[derive(Clone, Debug, Default, PartialEq, Eq)]
148pub struct Counts {
149    queries: u64,
150    hits: u64,
151    fallbacks: u64,
152    full: u64,
153    assumed: u64,
154    exhausted: u64,
155    counters: u64,
156    lost: BTreeMap<Opcode, u64>,
157}
158
159impl Counts {
160    /// How many times a range was asked for.
161    #[must_use]
162    pub const fn queries(&self) -> u64 {
163        self.queries
164    }
165
166    /// How many of those the cache answered.
167    #[must_use]
168    pub const fn hits(&self) -> u64 {
169        self.hits
170    }
171
172    /// How many were answered with the range at the definition because the cache was full.
173    #[must_use]
174    pub const fn fallbacks(&self) -> u64 {
175        self.fallbacks
176    }
177
178    /// How many came back knowing nothing at all.
179    #[must_use]
180    pub const fn full(&self) -> u64 {
181        self.full
182    }
183
184    /// How many were narrowed by what a loop counter's own recurrence says.
185    ///
186    /// These are a subset of the ones [`Counts::assumed`] counts, because every one of them rests
187    /// on the `nsw` the increment carries.
188    #[must_use]
189    pub const fn counters(&self) -> u64 {
190        self.counters
191    }
192
193    /// How many came back knowing nothing because the budget was spent.
194    ///
195    /// These are the ones section 10.7 is really about. A range that lost the information at an
196    /// opcode is a gap in the transfer functions and shows up in [`Counts::losses`]. A range that
197    /// never got worked out at all shows up nowhere else, and a function whose count here is not
198    /// zero is a function every pass downstream is optimizing blind.
199    #[must_use]
200    pub const fn exhausted(&self) -> u64 {
201        self.exhausted
202    }
203
204    /// How many ranges were narrower because an instruction promised not to overflow.
205    ///
206    /// These are the ranges section 10.7 calls correct and surprising: they are true only
207    /// because the program would be undefined otherwise.
208    #[must_use]
209    pub const fn assumed(&self) -> u64 {
210        self.assumed
211    }
212
213    /// Which opcodes lost the information, most often first.
214    #[must_use]
215    pub fn losses(&self) -> Vec<(Opcode, u64)> {
216        let mut losses: Vec<(Opcode, u64)> = self.lost.iter().map(|(&op, &n)| (op, n)).collect();
217        losses.sort_by_key(|&(opcode, count)| (std::cmp::Reverse(count), opcode));
218        losses
219    }
220}
221
222/// One relation between two values, as it was recorded on an edge.
223///
224/// The pair is ordered as it was written, so `a < b` and `b > a` are the same fact stored one
225/// way, and reading it the other way round is [`IntPred::swapped`].
226#[derive(Clone, Copy, Debug, PartialEq, Eq)]
227struct Relation {
228    left: Value,
229    pred: IntPred,
230    right: Value,
231}
232
233/// What the cache holds for one value.
234#[derive(Clone, Debug, Default)]
235struct Entry {
236    at_def: Option<Range>,
237    refined: HashMap<Block, Range>,
238}
239
240/// The range analysis of one function.
241///
242/// Queries take `&mut self` because a query fills the cache and moves the counters, which is the
243/// design and not an accident: an analysis that answered without recording what it was asked
244/// could not report the losses in section 10.7.
245#[derive(Debug)]
246pub struct Ranges<'a> {
247    func: &'a Func,
248    cfg: &'a Cfg,
249    dom: &'a Dominators,
250    options: Options,
251    cache: HashMap<Value, Entry>,
252    relations: HashMap<Block, Vec<Relation>>,
253    counts: Counts,
254    /// The values whose definition range is being computed right now.
255    ///
256    /// Re-entering one is a cycle, which in SSA means a loop-carried value, and the answer there
257    /// is the range of the type.
258    active: HashSet<Value>,
259    /// How many times that has happened, so that an answer which leaned on a cycle is not cached
260    /// and the next query gets the same answer rather than a worse one.
261    cycles: u64,
262    /// How much of [`Options::budget`] has gone.
263    spent: u64,
264    /// The loop tree, built the first time a header parameter is asked about.
265    ///
266    /// A function with no loop in it never builds one, which is most of the functions in a C
267    /// program, and a function with one builds it once however many counters it has.
268    loops: Option<Loops>,
269    /// The loop tree the caller already had, which is read instead of building one.
270    given: Option<&'a Loops>,
271}
272
273impl<'a> Ranges<'a> {
274    /// The analysis of this function, with the limits at their defaults.
275    #[must_use]
276    pub fn new(func: &'a Func, cfg: &'a Cfg, dom: &'a Dominators) -> Self {
277        Self::with(func, cfg, dom, Options::default())
278    }
279
280    /// The same, with the limits the command line asked for.
281    #[must_use]
282    pub fn with(func: &'a Func, cfg: &'a Cfg, dom: &'a Dominators, options: Options) -> Self {
283        Self {
284            func,
285            cfg,
286            dom,
287            options,
288            cache: HashMap::new(),
289            relations: HashMap::new(),
290            counts: Counts::default(),
291            active: HashSet::new(),
292            cycles: 0,
293            spent: 0,
294            loops: None,
295            given: None,
296        }
297    }
298
299    /// The same analysis, reading the loop tree the caller already has rather than building its
300    /// own the first time a counter is asked about.
301    ///
302    /// It has to be the tree of the same graph and dominators this was handed. What it is for is
303    /// a caller that makes a fresh analysis per loop, because it changes the function between
304    /// loops, and so would otherwise build a tree of the whole function once per loop. licm is
305    /// that caller, and on jtckdint's main, with thousands of loops, those trees were a tenth of
306    /// the `-O2` build.
307    #[must_use]
308    pub fn knowing(mut self, loops: &'a Loops) -> Self {
309        self.given = Some(loops);
310        self
311    }
312
313    /// What the queries have done so far.
314    #[must_use]
315    pub const fn counts(&self) -> &Counts {
316        &self.counts
317    }
318
319    /// What this value can be where it is defined.
320    pub fn of(&mut self, value: Value) -> Range {
321        self.counts.queries += 1;
322        self.at_def(value)
323    }
324
325    /// What this value can be on entry to this block.
326    ///
327    /// The block has to be one the definition reaches, which for a use is the block the use is
328    /// in. Asking about a block the definition does not dominate is not wrong, it just gets an
329    /// answer that ignored the branches it could not see.
330    pub fn at(&mut self, value: Value, block: Block) -> Range {
331        self.counts.queries += 1;
332        self.refined(value, block)
333    }
334
335    /// What this value can be at this instruction.
336    ///
337    /// The same as [`Ranges::at`] on the block holding it. Ranges within a block do not change
338    /// in rucc's IR, because there is nothing between two instructions that could narrow one:
339    /// the branches are all at the ends of blocks.
340    pub fn at_inst(&mut self, value: Value, inst: Inst) -> Range {
341        match self.func.block_of(inst) {
342            Some(block) => self.at(value, block),
343            None => self.of(value),
344        }
345    }
346
347    /// Whether this comparison is settled where it stands.
348    ///
349    /// The ranges answer first, because they answer more often. The oracle answers the cases
350    /// they cannot, which are the ones where the two values are related without either being
351    /// pinned down, and section 10.3 says that is most of what removes a repeated bounds check.
352    pub fn compare(&mut self, pred: IntPred, a: Value, b: Value, block: Block) -> Truth {
353        let (left, right) = (self.at(a, block), self.at(b, block));
354        if left.width() != right.width() {
355            return Truth::Either;
356        }
357        match ops::compare(pred, left, right) {
358            Truth::Either => (),
359            settled => return settled,
360        }
361        match self.relation(a, b, block) {
362            Some(known) if implies(known, pred) => Truth::Always,
363            Some(known) if excludes(known, pred) => Truth::Never,
364            _ => Truth::Either,
365        }
366    }
367
368    /// What is known to hold between these two values in this block, if anything.
369    ///
370    /// What was recorded on a dominating edge, read in the order asked, plus one step through an
371    /// intermediate value. Not the transitive closure: section 10.3 says computing that is where
372    /// the cost of a relational oracle goes and that one step pays for most of it.
373    pub fn relation(&mut self, a: Value, b: Value, block: Block) -> Option<IntPred> {
374        let facts = self.facts(block).clone();
375        if let Some(direct) = read(&facts, a, b) {
376            return Some(direct);
377        }
378        for step in &facts {
379            for middle in [step.left, step.right] {
380                if middle == a || middle == b {
381                    continue;
382                }
383                let composed = read(&facts, a, middle)
384                    .zip(read(&facts, middle, b))
385                    .and_then(|(first, second)| compose(first, second));
386                if composed.is_some() {
387                    return composed;
388                }
389            }
390        }
391        None
392    }
393
394    /// The range at the definition, cached, with the cycle guard around it.
395    fn at_def(&mut self, value: Value) -> Range {
396        let ty = self.func[value].ty;
397        if !ty.is_int() || !ty.is_scalar() {
398            return Range::of(ty);
399        }
400        if let Some(cached) = self.cache.get(&value).and_then(|entry| entry.at_def) {
401            self.counts.hits += 1;
402            return cached;
403        }
404        if !self.active.insert(value) {
405            self.cycles += 1;
406            return Range::of(ty);
407        }
408        // Spent here rather than at the query, because a query the cache answers costs nothing
409        // and this is where the work is. The guard has to put the value back before it leaves or
410        // the cycle set grows a member nothing removes.
411        if self.spent >= self.options.budget {
412            self.active.remove(&value);
413            self.counts.exhausted += 1;
414            return Range::of(ty);
415        }
416        self.spent += 1;
417        let before = self.cycles;
418        let range = self.compute(value);
419        self.active.remove(&value);
420        if self.cycles == before {
421            self.cache.entry(value).or_default().at_def = Some(range);
422        }
423        range
424    }
425
426    /// The range at the definition, worked out.
427    fn compute(&mut self, value: Value) -> Range {
428        let ty = self.func[value].ty;
429        match self.func[value].def {
430            Def::Param { block, index } => self.of_param(value, block, index),
431            Def::Result { inst, .. } => {
432                let range = self.of_inst(value, inst);
433                if range.is_full() {
434                    self.counts.full += 1;
435                    *self.counts.lost.entry(self.func[inst].opcode).or_default() += 1;
436                }
437                debug_assert_eq!(range.width(), ty.bits(), "a range of the wrong width");
438                range
439            }
440        }
441    }
442
443    /// The range of a block parameter, which is what every predecessor can pass to it.
444    ///
445    /// The union over the ways in, narrowed by what a loop counter's own recurrence says. The two
446    /// are worked out separately and met, because they are strong in opposite directions: the
447    /// union reads the exit test, which pins the end the loop stops at, and [`Ranges::counter`]
448    /// reads the entry value, which pins the end it starts from.
449    fn of_param(&mut self, value: Value, block: Block, index: u32) -> Range {
450        let ty = self.func[value].ty;
451        if self.cfg.entry() == Some(block) {
452            return Range::of(ty);
453        }
454        let preds: Vec<Block> = self.cfg.predecessors(block).to_vec();
455        if preds.is_empty() {
456            return Range::of(ty);
457        }
458        let mut range = Range::empty(ty.bits());
459        for pred in preds {
460            let Some(arg) = argument(self.func, pred, block, index as usize) else {
461                range = Range::of(ty);
462                break;
463            };
464            let incoming = self.refined(arg, pred);
465            let edge = self.edge_fact(pred, block, arg).unwrap_or_else(|| Range::of(ty));
466            range = range.union(incoming.intersect(edge));
467            if range.is_full() {
468                break;
469            }
470        }
471        match self.counter(value, block) {
472            Some(walked) => range.intersect(walked),
473            None => range,
474        }
475    }
476
477    /// Where a loop counter cannot have got to, read off the recurrence it walks.
478    ///
479    /// The gap the module comment names, closed the way it says to close it. A value carried round
480    /// a loop is a cycle in SSA, the walk assumes the range of the type when it re-enters one, and
481    /// what comes back for a counter is one step of the recurrence applied to everything. The exit
482    /// test still says something, so the end the loop stops at comes out tight and the end it
483    /// started from comes out as whatever the type allows. `i` in `for (i = 0; i < 200; i++)` was
484    /// coming back as `[-2147483647, 199]`, which is the wrong half of the answer.
485    ///
486    /// Document 07's scalar evolution already knows the shape, so this asks it rather than guessing
487    /// with a widening operator. `{base, +, step}` with a constant `base` and a constant `step`
488    /// that does not wrap when read as signed is a sequence that only moves one way, so `base` is
489    /// the end it never passes: the low end when it counts up and the high end when it counts down.
490    /// Nothing is claimed about the other end, which is the union's to say.
491    ///
492    /// # What it rests on
493    ///
494    /// The `nsw` on the increment, which is a promise the program made rather than anything proved
495    /// here, so [`Counts::assumed`] counts these with the rest of the ranges that would be wrong in
496    /// a program that is already undefined. Without it the sequence may wrap and a counter that
497    /// wraps has been everywhere.
498    ///
499    /// # What is not here
500    ///
501    /// A base that is not a number. `for (i = lo; i < hi; i++)` has one, and what it wants is this
502    /// asking for the range of `lo` where the loop is entered, which is a query inside a query and
503    /// worth measuring before it is written.
504    fn counter(&mut self, value: Value, block: Block) -> Option<Range> {
505        let ty = self.func[value].ty;
506        if !ty.is_int() || !ty.is_scalar() {
507            return None;
508        }
509        let loops = match self.given {
510            Some(loops) => loops,
511            None => &*self.loops.get_or_insert_with(|| Loops::new(self.cfg, self.dom)),
512        };
513        let id = loops.innermost(block)?;
514        if loops.header(id) != block {
515            return None;
516        }
517        // A fresh analysis per counter rather than one held on this. Scalar evolution is thrown
518        // away whenever anything about the loops changes and this does not know when that is, and
519        // the answer here is cached by the caller, so what a second one costs is the walk back
520        // along one chain of arithmetic.
521        let chrec = Scev::new(self.func, self.cfg, loops).evolution(id, value).chrec()?;
522        if chrec.ty != ty || !chrec.does_not_wrap(true) {
523            return None;
524        }
525        let base = chrec.base.as_number()?;
526        let step = chrec.step.as_number()?;
527        let (least, most) = Range::of(ty).signed_bounds()?;
528        let (lo, hi) = if step < 0 { (least, base) } else { (base, most) };
529        let walked = Range::signed_between(lo, hi, ty.bits());
530        if walked.is_full() {
531            return None;
532        }
533        self.counts.counters += 1;
534        self.counts.assumed += 1;
535        Some(walked)
536    }
537
538    /// The range of an instruction's result, which is the table in [`super::ops`] applied to the
539    /// ranges of its operands where they stand.
540    fn of_inst(&mut self, value: Value, inst: Inst) -> Range {
541        let ty = self.func[value].ty;
542        let width = ty.bits();
543        let data = self.func[inst];
544        let block = self.func.block_of(inst);
545        let args: Vec<Value> = self.func[data.args].to_vec();
546        let flags = data.flags;
547        let operand = |this: &mut Self, index: usize| match (args.get(index), block) {
548            (Some(&arg), Some(block)) => this.refined(arg, block),
549            (Some(&arg), None) => this.at_def(arg),
550            (None, _) => Range::of(ty),
551        };
552        match data.opcode {
553            Opcode::IConst => {
554                let Extra::Imm(at) = data.extra else { return Range::of(ty) };
555                Range::exactly(self.func[at].unsigned(), width)
556            }
557            Opcode::Add | Opcode::Sub | Opcode::Mul => {
558                let (a, b) = (operand(self, 0), operand(self, 1));
559                if a.width() != b.width() {
560                    return Range::of(ty);
561                }
562                let apply = |flags| match data.opcode {
563                    Opcode::Add => ops::add(a, b, flags),
564                    Opcode::Sub => ops::sub(a, b, flags),
565                    _ => ops::mul(a, b, flags),
566                };
567                self.assuming(apply, flags)
568            }
569            Opcode::And | Opcode::Or | Opcode::Xor => {
570                let (a, b) = (operand(self, 0), operand(self, 1));
571                if a.width() != b.width() {
572                    return Range::of(ty);
573                }
574                match data.opcode {
575                    Opcode::And => ops::and(a, b),
576                    Opcode::Or => ops::or(a, b),
577                    _ => ops::xor(a, b),
578                }
579            }
580            Opcode::Shl | Opcode::LShr | Opcode::AShr => {
581                let (a, count) = (operand(self, 0), operand(self, 1));
582                if a.width() != count.width() {
583                    return Range::of(ty);
584                }
585                let apply = |flags| match data.opcode {
586                    Opcode::Shl => ops::shl(a, count, flags),
587                    Opcode::LShr => ops::lshr(a, count, flags),
588                    _ => ops::ashr(a, count, flags),
589                };
590                self.assuming(apply, flags)
591            }
592            Opcode::Trunc => ops::trunc(operand(self, 0), width),
593            Opcode::ZExt => ops::zext(operand(self, 0), width),
594            Opcode::SExt => ops::sext(operand(self, 0), width),
595            Opcode::ICmp => {
596                let Extra::IntPred(pred) = data.extra else { return Range::of(ty) };
597                let (a, b) = (operand(self, 0), operand(self, 1));
598                if a.width() != b.width() {
599                    return Range::of(ty);
600                }
601                match ops::compare(pred, a, b) {
602                    Truth::Always => Range::exactly(1, width),
603                    Truth::Never => Range::exactly(0, width),
604                    Truth::Either => Range::of(ty),
605                }
606            }
607            // A bit count cannot exceed the width of what it counts, which is worth saying
608            // because the value it produces is almost always used to index or to shift.
609            Opcode::Ctlz | Opcode::Cttz | Opcode::Ctpop => {
610                let counted = args.first().map_or(width, |&arg| self.func[arg].ty.bits());
611                Range::between(0, u128::from(counted), width)
612            }
613            _ => Range::of(ty),
614        }
615    }
616
617    /// The operation under the flags it carries, and the count of how much they bought.
618    ///
619    /// Section 10.7 says the flag has to be an input to the operation rather than a check
620    /// somewhere upstream. It also says a range that is only true because the program would
621    /// otherwise be undefined has to be visible, and the difference between the two answers here
622    /// is exactly that range.
623    fn assuming(
624        &mut self,
625        apply: impl Fn(rucc_ir::Flags) -> Range,
626        flags: rucc_ir::Flags,
627    ) -> Range {
628        let range = apply(flags);
629        if !flags.is_empty() && range != apply(rucc_ir::Flags::NONE) {
630            self.counts.assumed += 1;
631        }
632        range
633    }
634
635    /// The range at the definition, narrowed by the branches that dominate this block.
636    fn refined(&mut self, value: Value, block: Block) -> Range {
637        let ty = self.func[value].ty;
638        if !ty.is_int() || !ty.is_scalar() {
639            return Range::of(ty);
640        }
641        if let Some(&cached) = self.cache.get(&value).and_then(|e| e.refined.get(&block)) {
642            self.counts.hits += 1;
643            return cached;
644        }
645        let full = self
646            .cache
647            .get(&value)
648            .is_some_and(|entry| entry.refined.len() >= self.options.refinements);
649        if full {
650            self.counts.fallbacks += 1;
651            return self.at_def(value);
652        }
653        let before = self.cycles;
654        let range = self.walk(value, block);
655        if self.cycles == before {
656            let entry = self.cache.entry(value).or_default();
657            if entry.refined.len() < self.options.refinements {
658                entry.refined.insert(block, range);
659            }
660        }
661        range
662    }
663
664    /// The walk itself, up the dominator tree from the block to the definition.
665    ///
666    /// It stops at the definition because an edge above that cannot say anything about a value
667    /// that does not exist yet, and because whatever it says about the operands is already in
668    /// the answer: they were asked for where the instruction stands.
669    fn walk(&mut self, value: Value, block: Block) -> Range {
670        let mut range = self.at_def(value);
671        let stop = defining_block(self.func, value);
672        let mut cursor = block;
673        let mut steps = 0;
674        while steps < self.options.recompute_depth && Some(cursor) != stop {
675            let Some(parent) = self.dom.immediate_dominator(cursor) else { break };
676            if self.cfg.predecessors(cursor) == [parent] {
677                if let Some(fact) = self.edge_fact(parent, cursor, value) {
678                    range = range.intersect(fact);
679                }
680            }
681            cursor = parent;
682            steps += 1;
683        }
684        range
685    }
686
687    /// What taking the edge from one block to another says about a value, if anything.
688    fn edge_fact(&mut self, from: Block, to: Block, value: Value) -> Option<Range> {
689        let term = self.func.terminator(from)?;
690        let depth = self.options.logical_depth;
691        match self.func[term].opcode {
692            Opcode::BrIf => {
693                let calls: Vec<_> = self.func.successors(term).collect();
694                let (then, other) = (calls.first()?, calls.get(1)?);
695                if then.block == other.block {
696                    return None;
697                }
698                let taken = then.block == to;
699                let cond = *self.func[self.func[term].args].first()?;
700                self.condition_fact(cond, taken, value, from, depth)
701            }
702            Opcode::Switch => self.switch_fact(term, to, value, from, depth),
703            _ => None,
704        }
705    }
706
707    /// What a switch edge says about the value it switched on, carried back to the value asked
708    /// about.
709    fn switch_fact(
710        &mut self,
711        term: Inst,
712        to: Block,
713        value: Value,
714        block: Block,
715        depth: u32,
716    ) -> Option<Range> {
717        if depth == 0 {
718            return None;
719        }
720        let Extra::Switch(info) = self.func[term].extra else { return None };
721        let info = self.func[info];
722        let calls: Vec<_> = self.func[info.targets].to_vec();
723        let cases: Vec<_> = self.func[info.cases].to_vec();
724        let subject = *self.func[self.func[term].args].first()?;
725        let width = self.func[subject].ty.bits();
726        let default = calls.first()?.block;
727        let hits: Vec<usize> = (1..calls.len()).filter(|&index| calls[index].block == to).collect();
728        let known = if default == to {
729            // The default edge means none of the cases matched, which is a fact only while the
730            // exclusions still fit. It is also not a fact at all if a case goes to the same
731            // block, since then the edge does not say which of the two ways it came.
732            if !hits.is_empty() {
733                return None;
734            }
735            let mut range = Range::full(width);
736            for &case in cases.iter().take(EXCLUSIONS) {
737                range = range.intersect(Range::other_than(case.unsigned(), width));
738            }
739            range
740        } else {
741            let pairs: Vec<(u128, u128)> = hits
742                .iter()
743                .filter_map(|&index| cases.get(index - 1))
744                .map(|case| (case.unsigned(), case.unsigned()))
745                .collect();
746            if pairs.is_empty() {
747                return None;
748            }
749            Range::from_pairs(&pairs, width)
750        };
751        self.carry_back(subject, known, value, block, depth - 1)
752    }
753
754    /// What a condition being true, or being false, says about a value.
755    fn condition_fact(
756        &mut self,
757        cond: Value,
758        taken: bool,
759        value: Value,
760        block: Block,
761        depth: u32,
762    ) -> Option<Range> {
763        if depth == 0 {
764            return None;
765        }
766        if cond == value {
767            let width = self.func[value].ty.bits();
768            return Some(Range::exactly(u128::from(taken), width));
769        }
770        let Def::Result { inst, .. } = self.func[cond].def else { return None };
771        let data = self.func[inst];
772        let args: Vec<Value> = self.func[data.args].to_vec();
773        match data.opcode {
774            Opcode::ICmp => {
775                let Extra::IntPred(pred) = data.extra else { return None };
776                let pred = if taken { pred } else { pred.inverse() };
777                let (&left, &right) = (args.first()?, args.get(1)?);
778                let (a, b) = (self.refined(left, block), self.refined(right, block));
779                if a.width() != b.width() {
780                    return None;
781                }
782                let want = ops::narrow_for(pred, a, b);
783                if let Some(found) = self.carry_back(left, want, value, block, depth - 1) {
784                    return Some(found);
785                }
786                let want = ops::narrow_for(pred.swapped(), b, a);
787                self.carry_back(right, want, value, block, depth - 1)
788            }
789            // Both arms of an `and` hold on the edge where it is true, and both fail on the edge
790            // where an `or` is false. The other two edges say nothing, because either arm could
791            // be the one that decided it. This is the whole of what section 10.1's logical depth
792            // is counting.
793            Opcode::And | Opcode::Or => {
794                let holds = data.opcode == Opcode::And;
795                if taken != holds {
796                    return None;
797                }
798                let (&left, &right) = (args.first()?, args.get(1)?);
799                let a = self.condition_fact(left, taken, value, block, depth - 1);
800                let b = self.condition_fact(right, taken, value, block, depth - 1);
801                match (a, b) {
802                    (Some(a), Some(b)) => Some(a.intersect(b)),
803                    (found, None) | (None, found) => found,
804                }
805            }
806            // `xor c, 1` on a one bit value is `not c`, which is how the front end writes a
807            // negated condition.
808            Opcode::Xor => {
809                let (&left, &right) = (args.first()?, args.get(1)?);
810                let (cond, other) = match self.constant(right) {
811                    Some(_) => (left, right),
812                    None => (right, left),
813                };
814                let one = self.constant(other)? == 1 && self.func[other].ty.bits() == 1;
815                if !one {
816                    return None;
817                }
818                self.condition_fact(cond, !taken, value, block, depth - 1)
819            }
820            _ => None,
821        }
822    }
823
824    /// Given that `subject` is in `known`, what that says about `value`.
825    ///
826    /// The inverse half of the table, walked back along the chain from the subject of a
827    /// condition to the value being asked about. Every step is sound on its own because
828    /// [`ops::backward`] answers with every operand that could have produced a result in range,
829    /// so a chain of them over-approximates and never loses a value that the program can reach.
830    fn carry_back(
831        &mut self,
832        subject: Value,
833        known: Range,
834        value: Value,
835        block: Block,
836        depth: u32,
837    ) -> Option<Range> {
838        if subject == value {
839            return Some(known);
840        }
841        if depth == 0 || known.is_full() {
842            return None;
843        }
844        let Def::Result { inst, .. } = self.func[subject].def else { return None };
845        let data = self.func[inst];
846        let args: Vec<Value> = self.func[data.args].to_vec();
847        let (&left, right) = (args.first()?, args.get(1).copied());
848        let steps: Vec<(Value, Undo, Option<Value>)> = match data.opcode {
849            // Addition is the same undo both ways round, since either operand is the result less
850            // the other one. Subtraction is not, and section 10.4's inverse for its right operand
851            // is the one that looks like the others and is not.
852            Opcode::Add => vec![(left, Undo::AddLeft, right), (right?, Undo::AddLeft, Some(left))],
853            Opcode::Sub => vec![(left, Undo::SubLeft, right), (right?, Undo::SubRight, Some(left))],
854            Opcode::Xor => vec![(left, Undo::Xor, right), (right?, Undo::Xor, Some(left))],
855            Opcode::ZExt => vec![(left, Undo::Zext(self.func[left].ty.bits()), None)],
856            Opcode::SExt => vec![(left, Undo::Sext(self.func[left].ty.bits()), None)],
857            _ => return None,
858        };
859        for (operand, undo, other) in steps {
860            let other = match other {
861                Some(other) => self.refined(other, block),
862                None => Range::full(known.width()),
863            };
864            if other.width() != known.width() {
865                continue;
866            }
867            let back = ops::backward(undo, known, other);
868            if let Some(found) = self.carry_back(operand, back, value, block, depth - 1) {
869                return Some(found);
870            }
871        }
872        None
873    }
874
875    /// The relations that hold in a block, which are its own edge's and its dominator's.
876    fn facts(&mut self, block: Block) -> &Vec<Relation> {
877        if !self.relations.contains_key(&block) {
878            let mut facts = match self.dom.immediate_dominator(block) {
879                Some(parent) => self.facts(parent).clone(),
880                None => Vec::new(),
881            };
882            if let Some(own) = self.own_relation(block) {
883                facts.push(own);
884                if facts.len() > RELATIONS {
885                    facts.remove(0);
886                }
887            }
888            self.relations.insert(block, facts);
889        }
890        &self.relations[&block]
891    }
892
893    /// The relation the one edge into this block recorded, if it recorded one.
894    fn own_relation(&mut self, block: Block) -> Option<Relation> {
895        let [from] = *self.cfg.predecessors(block) else { return None };
896        let term = self.func.terminator(from)?;
897        if self.func[term].opcode != Opcode::BrIf {
898            return None;
899        }
900        let calls: Vec<_> = self.func.successors(term).collect();
901        let (then, other) = (calls.first()?, calls.get(1)?);
902        if then.block == other.block {
903            return None;
904        }
905        let taken = then.block == block;
906        let cond = *self.func[self.func[term].args].first()?;
907        let Def::Result { inst, .. } = self.func[cond].def else { return None };
908        if self.func[inst].opcode != Opcode::ICmp {
909            return None;
910        }
911        let Extra::IntPred(pred) = self.func[inst].extra else { return None };
912        let args = &self.func[self.func[inst].args];
913        let (&left, &right) = (args.first()?, args.get(1)?);
914        let pred = if taken { pred } else { pred.inverse() };
915        Some(Relation { left, pred, right })
916    }
917
918    /// The constant a value is, if it is one.
919    fn constant(&self, value: Value) -> Option<u128> {
920        let Def::Result { inst, .. } = self.func[value].def else { return None };
921        if self.func[inst].opcode != Opcode::IConst {
922            return None;
923        }
924        let Extra::Imm(at) = self.func[inst].extra else { return None };
925        Some(self.func[at].unsigned())
926    }
927}
928
929/// The block a value is defined in.
930fn defining_block(func: &Func, value: Value) -> Option<Block> {
931    match func[value].def {
932        Def::Param { block, .. } => Some(block),
933        Def::Result { inst, .. } => func.block_of(inst),
934    }
935}
936
937/// What this predecessor passes to the block's parameter at this position.
938///
939/// `None` when the predecessor branches to the block more than once with different arguments,
940/// which a `br_if` with both arms on the same block can do and which means the parameter takes a
941/// value that depends on the test rather than on the edge.
942fn argument(func: &Func, pred: Block, block: Block, index: usize) -> Option<Value> {
943    let term = func.terminator(pred)?;
944    let mut found = None;
945    for call in func.successors(term) {
946        if call.block != block {
947            continue;
948        }
949        let arg = *func[call.args].get(index)?;
950        if found.replace(arg).is_some_and(|old| old != arg) {
951            return None;
952        }
953    }
954    found
955}
956
957/// The recorded relation between these two values, read in the order asked.
958fn read(facts: &[Relation], a: Value, b: Value) -> Option<IntPred> {
959    facts.iter().rev().find_map(|fact| {
960        if fact.left == a && fact.right == b {
961            Some(fact.pred)
962        } else if fact.left == b && fact.right == a {
963            Some(fact.pred.swapped())
964        } else {
965            None
966        }
967    })
968}
969
970/// Which of less, equal and greater a predicate allows.
971const fn outcomes(pred: IntPred) -> u8 {
972    match pred {
973        IntPred::Eq => 0b010,
974        IntPred::Ne => 0b101,
975        IntPred::Slt | IntPred::Ult => 0b001,
976        IntPred::Sle | IntPred::Ule => 0b011,
977        IntPred::Sgt | IntPred::Ugt => 0b100,
978        IntPred::Sge | IntPred::Uge => 0b110,
979    }
980}
981
982/// Whether two predicates are reading their operands the same way.
983///
984/// Equality reads them as neither signed nor unsigned, so it composes with both. Nothing else
985/// crosses: `a <s b` says nothing about `a <u b`, and a compiler that assumed otherwise would be
986/// wrong on exactly the inputs where it matters.
987const fn comparable(a: IntPred, b: IntPred) -> bool {
988    ordering_free(a) || ordering_free(b) || a.is_signed() == b.is_signed()
989}
990
991/// Whether a predicate reads its operands as neither signed nor unsigned.
992const fn ordering_free(pred: IntPred) -> bool {
993    matches!(pred, IntPred::Eq | IntPred::Ne)
994}
995
996/// Whether what is known forces this predicate to hold.
997fn implies(known: IntPred, pred: IntPred) -> bool {
998    comparable(known, pred) && outcomes(known) & !outcomes(pred) == 0
999}
1000
1001/// Whether what is known forces this predicate to fail.
1002fn excludes(known: IntPred, pred: IntPred) -> bool {
1003    comparable(known, pred) && outcomes(known) & outcomes(pred) == 0
1004}
1005
1006/// The relation that follows from two, when one does.
1007///
1008/// One step, not a closure. `a < m` and `m <= b` gives `a < b`, and anything mixing a less with a
1009/// greater gives nothing, which is right: it is the case where the two facts say the values are
1010/// on opposite sides of the middle one and nothing follows about them.
1011fn compose(first: IntPred, second: IntPred) -> Option<IntPred> {
1012    if !comparable(first, second) {
1013        return None;
1014    }
1015    let strict = |pred| matches!(pred, IntPred::Slt | IntPred::Ult | IntPred::Sgt | IntPred::Ugt);
1016    let direction = |pred| outcomes(pred) & 0b101;
1017    match (first, second) {
1018        (IntPred::Eq, other) | (other, IntPred::Eq) => Some(other),
1019        // Not equal is not a direction, so nothing follows through it: `a != m` and `m != b`
1020        // leaves `a` and `b` free to be the same value.
1021        (IntPred::Ne, _) | (_, IntPred::Ne) => None,
1022        // Two orderings compose when they point the same way, and the result is strict when
1023        // either step is.
1024        _ if direction(first) != direction(second) => None,
1025        _ if strict(first) => Some(first),
1026        _ => Some(second),
1027    }
1028}
1029
1030#[cfg(test)]
1031mod tests {
1032    use rucc_base::Interner;
1033    use rucc_ir::{Block, Builder, Flags, Func, IntPred, Opcode, Signature, Type, Value};
1034
1035    use super::{Options, Ranges};
1036    use crate::cfg::Cfg;
1037    use crate::dom::Dominators;
1038    use crate::range::Range;
1039    use crate::range::ops::{self, Truth};
1040
1041    const I32: Type = Type::int(32);
1042
1043    /// A function taking this many integer parameters, with this many blocks, the entry first.
1044    ///
1045    /// The parameters are the point. A test about what a branch proves needs a value that
1046    /// nothing is known about, and a constant passed into a block would be narrowed to itself
1047    /// before the branch got a chance to say anything.
1048    fn shape(params: usize, blocks: usize) -> (Func, Vec<Value>, Vec<Block>) {
1049        let mut names = Interner::new();
1050        let types = vec![I32; params];
1051        let mut func = Func::new(names.intern("f"), Signature::new().with_params(&types));
1052        let blocks: Vec<Block> = (0..blocks).map(|_| func.create_block()).collect();
1053        let args = types.iter().map(|&ty| func.append_param(blocks[0], ty)).collect();
1054        (func, args, blocks)
1055    }
1056
1057    /// The analysis of a finished function, kept together because the parts borrow each other.
1058    struct Asked {
1059        cfg: Cfg,
1060        dom: Dominators,
1061        func: Func,
1062    }
1063
1064    impl Asked {
1065        fn new(func: Func) -> Self {
1066            let cfg = Cfg::new(&func);
1067            let dom = Dominators::new(&cfg);
1068            Asked { cfg, dom, func }
1069        }
1070
1071        fn ranges(&self) -> Ranges<'_> {
1072            Ranges::new(&self.func, &self.cfg, &self.dom)
1073        }
1074
1075        fn with(&self, options: Options) -> Ranges<'_> {
1076            Ranges::with(&self.func, &self.cfg, &self.dom, options)
1077        }
1078    }
1079
1080    /// The signed bounds of a range, which is what most of these tests are asking about.
1081    fn bounds(range: Range) -> Option<(i128, i128)> {
1082        range.signed_bounds()
1083    }
1084
1085    #[test]
1086    fn a_constant_is_itself() {
1087        let (mut func, _, blocks) = shape(0, 1);
1088        let mut build = Builder::new(&mut func, blocks[0]);
1089        let seven = build.iconst(I32, 7);
1090        build.ret(&[]);
1091        let asked = Asked::new(func);
1092        assert_eq!(asked.ranges().of(seven).singleton(), Some(7));
1093    }
1094
1095    #[test]
1096    fn arithmetic_on_constants_is_the_arithmetic() {
1097        let (mut func, _, blocks) = shape(0, 1);
1098        let mut build = Builder::new(&mut func, blocks[0]);
1099        let a = build.iconst(I32, 7);
1100        let b = build.iconst(I32, 5);
1101        let sum = build.binary(Opcode::Add, a, b, Flags::NONE);
1102        build.ret(&[]);
1103        let asked = Asked::new(func);
1104        assert_eq!(asked.ranges().of(sum).singleton(), Some(12));
1105    }
1106
1107    #[test]
1108    fn a_value_nothing_is_known_about_is_the_whole_of_its_type_and_says_which_opcode_lost_it() {
1109        let (mut func, args, blocks) = shape(1, 1);
1110        let mut build = Builder::new(&mut func, blocks[0]);
1111        let counted = build.unary(Opcode::Ctlz, args[0], I32);
1112        let squared = build.binary(Opcode::Mul, args[0], args[0], Flags::NONE);
1113        build.ret(&[]);
1114        let asked = Asked::new(func);
1115        let mut ranges = asked.ranges();
1116        assert!(ranges.of(args[0]).is_full(), "a parameter is anything");
1117        // The count of leading zeroes is bounded by the width even though its operand is not.
1118        assert_eq!(bounds(ranges.of(counted)), Some((0, 32)));
1119        assert!(ranges.of(squared).is_full());
1120        assert_eq!(ranges.counts().losses(), vec![(Opcode::Mul, 1)]);
1121    }
1122
1123    /// `if (x < bound)` on a parameter, with the two arms in blocks one and two.
1124    fn guarded(pred: IntPred, bound: i128) -> (Func, Value, Block, Block) {
1125        let (mut func, args, blocks) = shape(1, 3);
1126        let mut build = Builder::new(&mut func, blocks[0]);
1127        let limit = build.iconst(I32, bound);
1128        let test = build.icmp(pred, args[0], limit);
1129        build.br_if(test, blocks[1], &[], blocks[2], &[]);
1130        Builder::new(&mut func, blocks[1]).ret(&[]);
1131        Builder::new(&mut func, blocks[2]).ret(&[]);
1132        (func, args[0], blocks[1], blocks[2])
1133    }
1134
1135    #[test]
1136    fn a_branch_narrows_the_value_it_tested_on_both_of_its_edges() {
1137        let (func, x, then, otherwise) = guarded(IntPred::Slt, 10);
1138        let asked = Asked::new(func);
1139        let mut ranges = asked.ranges();
1140        assert_eq!(bounds(ranges.at(x, then)), Some((i128::from(i32::MIN), 9)));
1141        assert_eq!(bounds(ranges.at(x, otherwise)), Some((10, i128::from(i32::MAX))));
1142    }
1143
1144    #[test]
1145    fn the_range_at_the_definition_is_not_the_range_at_the_use() {
1146        let (func, x, then, _) = guarded(IntPred::Ult, 64);
1147        let asked = Asked::new(func);
1148        let mut ranges = asked.ranges();
1149        assert!(ranges.of(x).is_full(), "nothing is known where it is defined");
1150        assert_eq!(ranges.at(x, then).unsigned_bounds(), Some((0, 63)));
1151    }
1152
1153    #[test]
1154    fn a_null_check_is_the_fact_a_single_interval_cannot_hold() {
1155        let (func, x, _, otherwise) = guarded(IntPred::Eq, 0);
1156        let asked = Asked::new(func);
1157        let mut ranges = asked.ranges();
1158        let range = ranges.at(x, otherwise);
1159        assert!(range.nonzero(), "the else edge of an equality with zero proves it");
1160        // One interval, because this reasons about bit patterns rather than signed numbers.
1161        // The same fact in GCC's signed domain is two, which is why section 10.2 insists on
1162        // there being more than one and why the count here is worth writing down.
1163        assert_eq!(range.pairs().len(), 1);
1164    }
1165
1166    /// `if (x + offset < bound)`, which is section 10.1's example of what the inversion is for.
1167    fn through_arithmetic(offset: i128, bound: i128) -> (Func, Value, Block) {
1168        let (mut func, args, blocks) = shape(1, 3);
1169        let mut build = Builder::new(&mut func, blocks[0]);
1170        let by = build.iconst(I32, offset);
1171        let shifted = build.binary(Opcode::Add, args[0], by, Flags::NSW);
1172        let limit = build.iconst(I32, bound);
1173        let test = build.icmp(IntPred::Slt, shifted, limit);
1174        build.br_if(test, blocks[1], &[], blocks[2], &[]);
1175        Builder::new(&mut func, blocks[1]).ret(&[]);
1176        Builder::new(&mut func, blocks[2]).ret(&[]);
1177        (func, args[0], blocks[1])
1178    }
1179
1180    #[test]
1181    fn the_condition_is_inverted_back_to_the_value_it_was_computed_from() {
1182        let (func, x, then) = through_arithmetic(3, 10);
1183        let asked = Asked::new(func);
1184        let mut ranges = asked.ranges();
1185        let (_, high) = bounds(ranges.at(x, then)).expect("not empty");
1186        assert!(high <= 6, "x + 3 < 10 makes x at most six, and this said {high}");
1187    }
1188
1189    #[test]
1190    fn the_inversion_stops_where_it_is_told_to() {
1191        let (func, x, then) = through_arithmetic(3, 10);
1192        let asked = Asked::new(func);
1193        let options = Options { logical_depth: 1, ..Options::default() };
1194        let mut ranges = asked.with(options);
1195        assert!(ranges.at(x, then).is_full(), "one step cannot reach past the comparison");
1196    }
1197
1198    /// `for (counter = start; counter < 100; counter += step)`, with the step's flags as given.
1199    ///
1200    /// The counter is the header parameter and the four blocks are the preheader, the header, the
1201    /// body and the exit, which is the shape the loop finder wants and the shape scalar evolution
1202    /// reads a chrec off.
1203    fn counting(start: i128, step: i128, flags: Flags) -> (Func, Value, Vec<Block>) {
1204        let (mut func, _, blocks) = shape(0, 4);
1205        let counter = func.append_param(blocks[1], I32);
1206        let mut build = Builder::new(&mut func, blocks[0]);
1207        let first = build.iconst(I32, start);
1208        build.jump(blocks[1], &[first]);
1209        let mut build = Builder::new(&mut func, blocks[1]);
1210        let limit = build.iconst(I32, 100);
1211        let test = build.icmp(IntPred::Slt, counter, limit);
1212        build.br_if(test, blocks[2], &[], blocks[3], &[]);
1213        let mut build = Builder::new(&mut func, blocks[2]);
1214        let by = build.iconst(I32, step);
1215        let next = build.binary(Opcode::Add, counter, by, flags);
1216        build.jump(blocks[1], &[next]);
1217        Builder::new(&mut func, blocks[3]).ret(&[]);
1218        (func, counter, blocks)
1219    }
1220
1221    #[test]
1222    fn a_counter_is_pinned_at_the_end_it_started_from_and_the_branch_says_the_other() {
1223        let (func, counter, blocks) = counting(0, 1, Flags::NSW);
1224        let asked = Asked::new(func);
1225        let mut ranges = asked.ranges();
1226        // The union over the ways in gives the high end, because the exit test pins it, and the
1227        // recurrence gives the low end, because a sequence that starts at zero and only ever adds
1228        // to itself never goes below zero. Neither half says both.
1229        let at_def = ranges.of(counter);
1230        assert!(at_def.contains(0) && at_def.contains(50) && at_def.contains(100));
1231        assert_eq!(bounds(at_def), Some((0, 100)));
1232        assert_eq!(ranges.counts().counters(), 1, "one counter, read once");
1233        // The branch still says what a consumer inside the loop wanted.
1234        let (_, inside) = bounds(ranges.at(counter, blocks[2])).expect("not empty");
1235        assert_eq!(inside, 99);
1236        let (after, _) = bounds(ranges.at(counter, blocks[3])).expect("not empty");
1237        assert_eq!(after, 100);
1238    }
1239
1240    #[test]
1241    fn a_counter_that_walks_down_is_pinned_at_the_top() {
1242        // The exit test is the same one, so it says nothing at all about a counter walking away
1243        // from it, and the whole of what is known is where the walk began.
1244        let (func, counter, _) = counting(50, -1, Flags::NSW);
1245        let asked = Asked::new(func);
1246        let mut ranges = asked.ranges();
1247        assert_eq!(bounds(ranges.of(counter)), Some((i128::from(i32::MIN), 50)));
1248    }
1249
1250    #[test]
1251    fn a_counter_that_may_wrap_is_not_pinned_down() {
1252        // Without the `nsw` the increment promises nothing, and a counter that wraps has been
1253        // everywhere, so nothing is read off the recurrence and what comes back is what the module
1254        // comment describes: one step applied to everything, narrowed by the exit test.
1255        let (func, counter, _) = counting(0, 1, Flags::NONE);
1256        let asked = Asked::new(func);
1257        let mut ranges = asked.ranges();
1258        let at_def = ranges.of(counter);
1259        assert!(at_def.contains(u128::from(u32::MAX)), "minus one is still in it");
1260        assert_eq!(ranges.counts().counters(), 0, "nothing was read off the recurrence");
1261    }
1262
1263    #[test]
1264    fn a_block_parameter_is_everything_its_predecessors_pass_to_it() {
1265        let (mut func, args, blocks) = shape(1, 4);
1266        let merged = func.append_param(blocks[3], I32);
1267        let mut build = Builder::new(&mut func, blocks[0]);
1268        let zero = build.iconst(I32, 0);
1269        let cond = build.icmp(IntPred::Slt, args[0], zero);
1270        build.br_if(cond, blocks[1], &[], blocks[2], &[]);
1271        let mut build = Builder::new(&mut func, blocks[1]);
1272        let five = build.iconst(I32, 5);
1273        build.jump(blocks[3], &[five]);
1274        let mut build = Builder::new(&mut func, blocks[2]);
1275        let nine = build.iconst(I32, 9);
1276        build.jump(blocks[3], &[nine]);
1277        Builder::new(&mut func, blocks[3]).ret(&[]);
1278        let asked = Asked::new(func);
1279        let mut ranges = asked.ranges();
1280        let range = ranges.of(merged);
1281        assert!(range.contains(5) && range.contains(9), "both arms are in it");
1282        assert!(!range.contains(7), "and nothing between them is");
1283    }
1284
1285    #[test]
1286    fn a_switch_edge_pins_its_cases_and_the_default_excludes_them() {
1287        let (mut func, args, blocks) = shape(1, 3);
1288        let mut build = Builder::new(&mut func, blocks[0]);
1289        build.switch(args[0], blocks[2], &[(4, blocks[1]), (7, blocks[1])]);
1290        Builder::new(&mut func, blocks[1]).ret(&[]);
1291        Builder::new(&mut func, blocks[2]).ret(&[]);
1292        let asked = Asked::new(func);
1293        let mut ranges = asked.ranges();
1294        assert_eq!(ranges.at(args[0], blocks[1]).list(4), Some(vec![4, 7]), "the two cases");
1295        let fell_through = ranges.at(args[0], blocks[2]);
1296        assert!(!fell_through.contains(4) && !fell_through.contains(7));
1297        assert!(fell_through.contains(5), "and everything else is still possible");
1298    }
1299
1300    #[test]
1301    fn both_arms_of_an_and_hold_where_it_is_true() {
1302        let (mut func, args, blocks) = shape(1, 3);
1303        let mut build = Builder::new(&mut func, blocks[0]);
1304        let low = build.iconst(I32, 10);
1305        let high = build.iconst(I32, 20);
1306        let above = build.icmp(IntPred::Sgt, args[0], low);
1307        let below = build.icmp(IntPred::Slt, args[0], high);
1308        let both = build.binary(Opcode::And, above, below, Flags::NONE);
1309        build.br_if(both, blocks[1], &[], blocks[2], &[]);
1310        Builder::new(&mut func, blocks[1]).ret(&[]);
1311        Builder::new(&mut func, blocks[2]).ret(&[]);
1312        let asked = Asked::new(func);
1313        let mut ranges = asked.ranges();
1314        assert_eq!(bounds(ranges.at(args[0], blocks[1])), Some((11, 19)));
1315        assert!(ranges.at(args[0], blocks[2]).is_full(), "the false edge says nothing");
1316    }
1317
1318    #[test]
1319    fn a_comparison_the_ranges_settle_is_settled() {
1320        let (func, x, then, _) = guarded(IntPred::Slt, 10);
1321        let mut asked = Asked::new(func);
1322        let ten = {
1323            let mut build = Builder::new(&mut asked.func, then);
1324            build.iconst(I32, 10)
1325        };
1326        let asked = Asked::new(asked.func);
1327        let mut ranges = asked.ranges();
1328        assert_eq!(ranges.compare(IntPred::Slt, x, ten, then), Truth::Always);
1329        assert_eq!(ranges.compare(IntPred::Sgt, x, ten, then), Truth::Never);
1330    }
1331
1332    /// `if (a < b)`, with nothing known about either, which is what the oracle is for.
1333    ///
1334    /// Blocks one and two are the arms and block three is where they meet again.
1335    fn related() -> (Func, Value, Value, Vec<Block>) {
1336        let (mut func, args, blocks) = shape(2, 4);
1337        let mut build = Builder::new(&mut func, blocks[0]);
1338        let test = build.icmp(IntPred::Slt, args[0], args[1]);
1339        build.br_if(test, blocks[1], &[], blocks[2], &[]);
1340        Builder::new(&mut func, blocks[1]).jump(blocks[3], &[]);
1341        Builder::new(&mut func, blocks[2]).jump(blocks[3], &[]);
1342        Builder::new(&mut func, blocks[3]).ret(&[]);
1343        (func, args[0], args[1], blocks)
1344    }
1345
1346    #[test]
1347    fn a_relation_the_intervals_cannot_see_is_still_known() {
1348        let (func, a, b, blocks) = related();
1349        let asked = Asked::new(func);
1350        let mut ranges = asked.ranges();
1351        // The intervals do learn something from `a < b`, which is that neither is at the end of
1352        // the type it could not be at. What they cannot do is settle the comparison, and that is
1353        // what the oracle is here for.
1354        let (left, right) = (ranges.at(a, blocks[1]), ranges.at(b, blocks[1]));
1355        assert_eq!(ops::compare(IntPred::Slt, left, right), Truth::Either);
1356        assert_eq!(ranges.relation(a, b, blocks[1]), Some(IntPred::Slt));
1357        assert_eq!(ranges.compare(IntPred::Slt, a, b, blocks[1]), Truth::Always);
1358        assert_eq!(ranges.compare(IntPred::Sge, a, b, blocks[1]), Truth::Never);
1359        assert_eq!(ranges.compare(IntPred::Ne, a, b, blocks[1]), Truth::Always);
1360        assert_eq!(ranges.compare(IntPred::Ult, a, b, blocks[1]), Truth::Either);
1361    }
1362
1363    #[test]
1364    fn a_relation_belongs_to_the_block_the_edge_led_to() {
1365        let (func, a, b, blocks) = related();
1366        let asked = Asked::new(func);
1367        let mut ranges = asked.ranges();
1368        assert_eq!(ranges.relation(a, b, blocks[1]), Some(IntPred::Slt));
1369        assert_eq!(ranges.relation(a, b, blocks[2]), Some(IntPred::Sge), "the other edge");
1370        assert_eq!(ranges.relation(a, b, blocks[3]), None, "where they meet, neither holds");
1371        assert_eq!(ranges.compare(IntPred::Slt, a, b, blocks[3]), Truth::Either);
1372    }
1373
1374    #[test]
1375    fn one_step_of_composition_is_taken() {
1376        let (mut func, args, blocks) = shape(3, 4);
1377        let [a, b, c] = [args[0], args[1], args[2]];
1378        let mut build = Builder::new(&mut func, blocks[0]);
1379        let first = build.icmp(IntPred::Slt, a, b);
1380        build.br_if(first, blocks[1], &[], blocks[3], &[]);
1381        let mut build = Builder::new(&mut func, blocks[1]);
1382        let second = build.icmp(IntPred::Sle, b, c);
1383        build.br_if(second, blocks[2], &[], blocks[3], &[]);
1384        Builder::new(&mut func, blocks[2]).ret(&[]);
1385        Builder::new(&mut func, blocks[3]).ret(&[]);
1386        let asked = Asked::new(func);
1387        let mut ranges = asked.ranges();
1388        assert_eq!(ranges.relation(a, c, blocks[2]), Some(IntPred::Slt), "a < b and b <= c");
1389        assert_eq!(ranges.compare(IntPred::Slt, a, c, blocks[2]), Truth::Always);
1390    }
1391
1392    #[test]
1393    fn the_cache_gives_up_rather_than_growing_without_a_bound() {
1394        let (func, x, then, otherwise) = guarded(IntPred::Slt, 10);
1395        let asked = Asked::new(func);
1396        let options = Options { refinements: 1, ..Options::default() };
1397        let mut ranges = asked.with(options);
1398        assert_eq!(bounds(ranges.at(x, then)), Some((i128::from(i32::MIN), 9)));
1399        assert!(ranges.at(x, otherwise).is_full(), "past the bound it is the definition range");
1400        assert_eq!(ranges.counts().fallbacks(), 1);
1401    }
1402
1403    #[test]
1404    fn asking_twice_asks_the_cache_the_second_time() {
1405        let (func, x, then, _) = guarded(IntPred::Slt, 10);
1406        let asked = Asked::new(func);
1407        let mut ranges = asked.ranges();
1408        let first = ranges.at(x, then);
1409        let hits = ranges.counts().hits();
1410        let second = ranges.at(x, then);
1411        assert_eq!(first, second);
1412        assert!(ranges.counts().hits() > hits, "the second query hit the cache");
1413        assert_eq!(ranges.counts().queries(), 2);
1414    }
1415
1416    #[test]
1417    fn a_range_that_is_only_true_because_overflow_is_undefined_is_counted() {
1418        let (mut func, args, blocks) = shape(1, 1);
1419        let mut build = Builder::new(&mut func, blocks[0]);
1420        let big = build.iconst(I32, i128::from(i32::MAX) - 4);
1421        let counted = build.unary(Opcode::Ctlz, args[0], I32);
1422        let sum = build.binary(Opcode::Add, counted, big, Flags::NSW);
1423        build.ret(&[]);
1424        let asked = Asked::new(func);
1425        let mut ranges = asked.ranges();
1426        assert!(!ranges.of(sum).is_full(), "the promise not to overflow bounds the sum");
1427        assert_eq!(ranges.counts().assumed(), 1);
1428    }
1429
1430    #[test]
1431    fn a_query_about_something_that_is_not_an_integer_answers_without_pretending() {
1432        let (mut func, _, blocks) = shape(0, 1);
1433        let mut build = Builder::new(&mut func, blocks[0]);
1434        let mem = build.mem_entry();
1435        build.ret(&[]);
1436        let asked = Asked::new(func);
1437        let mut ranges = asked.ranges();
1438        assert!(ranges.of(mem).is_full());
1439        assert_eq!(ranges.counts().full(), 0, "a memory value is not a lost integer");
1440    }
1441}