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