Skip to main content

bitwright/facts/
mod.rs

1//! Bit-level facts: known bits, unsigned and signed ranges, and tri-state proofs.
2//!
3//! Facts about an expression are sound over-approximations of the set of values it can take.
4//! They are computed lazily, iteratively (no recursion), cached per node for the life of the
5//! context, and bounded by a per-query work cap: when the cap is reached the answer is `top`
6//! (always sound), the nodes whose transfer completed stay cached, and the next query
7//! continues from there. Fact queries never create expression nodes.
8
9mod backward;
10mod constraint;
11pub(crate) mod known;
12mod range;
13#[cfg(test)]
14mod tests;
15mod transfer;
16
17use core::fmt;
18use std::sync::Arc;
19
20pub(crate) use constraint::Env;
21pub use constraint::{Assumptions, ConstraintId, Reliance};
22pub use known::KnownBits;
23pub use range::{SRange, URange};
24
25use crate::error::Error;
26use crate::expr::{Context, Expr, OpCode};
27use crate::hash::IdMap;
28use crate::ops::{CmpOp, CmpOpExt, UnOp};
29use crate::{BitVec, Width};
30use range::{sle, slt, ule, ult};
31pub(crate) use transfer::decide as decide_cmp;
32use transfer::{TOp, decide, transfer};
33
34/// What is known about the values of a `W`-bit expression: known bits, and an unsigned and a
35/// signed interval. All three are kept consistent with each other (a reduced product).
36#[derive(Copy, Clone, PartialEq, Eq, Hash)]
37pub struct Facts {
38    pub(crate) known: KnownBits,
39    pub(crate) urange: URange,
40    pub(crate) srange: SRange,
41}
42
43impl fmt::Debug for Facts {
44    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45        write!(
46            f,
47            "Facts({:?}, u[{}, {}], s[{}, {}])",
48            self.known,
49            self.urange.lo(),
50            self.urange.hi(),
51            self.srange.lo(),
52            self.srange.hi()
53        )
54    }
55}
56
57impl Facts {
58    /// Nothing known.
59    pub fn top(w: Width) -> Facts {
60        Facts {
61            known: KnownBits::unknown(w),
62            urange: URange::full(w),
63            srange: SRange::full(w),
64        }
65    }
66
67    /// Exactly one value.
68    pub fn constant(v: &BitVec) -> Facts {
69        Facts {
70            known: KnownBits::constant(v),
71            urange: URange::constant(v),
72            srange: SRange::constant(v),
73        }
74    }
75
76    /// Facts from their three components, reduced against each other. `None` if the widths
77    /// differ or no value satisfies all three.
78    pub fn new(known: KnownBits, urange: URange, srange: SRange) -> Option<Facts> {
79        if known.width() != urange.width() || known.width() != srange.width() {
80            return None;
81        }
82        Facts::reduce(known, urange, srange)
83    }
84
85    /// Known bits.
86    pub fn known(&self) -> KnownBits {
87        self.known
88    }
89
90    /// The unsigned interval.
91    pub fn urange(&self) -> URange {
92        self.urange
93    }
94
95    /// The signed interval.
96    pub fn srange(&self) -> SRange {
97        self.srange
98    }
99
100    /// The facts implied by known bits alone.
101    pub fn from_known(k: KnownBits) -> Facts {
102        let w = k.width();
103        Facts::reduce(k, URange::full(w), SRange::full(w)).unwrap_or_else(|| Facts::top(w))
104    }
105
106    /// The width.
107    pub fn width(&self) -> Width {
108        self.known.width()
109    }
110
111    /// The value, if the facts pin exactly one.
112    pub fn as_constant(&self) -> Option<BitVec> {
113        self.known.as_constant()
114    }
115
116    /// Whether `v` is consistent with every component.
117    pub fn contains(&self, v: &BitVec) -> bool {
118        self.known.contains(v) && self.urange.contains(v) && self.srange.contains(v)
119    }
120
121    /// Facts true of both (the intersection); `None` if contradictory or of different widths.
122    pub fn meet(&self, o: &Facts) -> Option<Facts> {
123        if self.width() != o.width() {
124            return None;
125        }
126        // Facts are always reduced, so when one refines the other it is the meet.
127        if self.refines(o) {
128            return Some(*self);
129        }
130        if o.refines(self) {
131            return Some(*o);
132        }
133        Facts::reduce(
134            self.known.meet(&o.known)?,
135            self.urange.meet(&o.urange)?,
136            self.srange.meet(&o.srange)?,
137        )
138    }
139
140    /// Whether every value `self` allows, `o` allows too, component by component.
141    fn refines(&self, o: &Facts) -> bool {
142        let (k, ok) = (&self.known, &o.known);
143        let bits = |a: &BitVec, b: &BitVec| known::bv_and(a, &known::bv_not(b)).is_zero();
144        bits(&ok.known_zero(), &k.known_zero())
145            && bits(&ok.known_one(), &k.known_one())
146            && ule(&o.urange.lo(), &self.urange.lo())
147            && ule(&self.urange.hi(), &o.urange.hi())
148            && sle(&o.srange.lo(), &self.srange.lo())
149            && sle(&self.srange.hi(), &o.srange.hi())
150    }
151
152    /// Facts true of either (the hull); `None` if the widths differ.
153    pub fn join(&self, o: &Facts) -> Option<Facts> {
154        (self.width() == o.width()).then(|| self.hull(o))
155    }
156
157    /// `join` for operands known to have the same width.
158    pub(crate) fn hull(&self, o: &Facts) -> Facts {
159        let w = self.width();
160        Facts::reduce(
161            self.known.hull(&o.known),
162            self.urange.hull(&o.urange),
163            self.srange.hull(&o.srange),
164        )
165        .unwrap_or_else(|| Facts::top(w))
166    }
167
168    pub(crate) fn meet_known(&self, k: &KnownBits) -> Option<Facts> {
169        Facts::reduce(self.known.meet(k)?, self.urange, self.srange)
170    }
171
172    pub(crate) fn meet_urange(&self, u: &URange) -> Option<Facts> {
173        Facts::reduce(self.known, self.urange.meet(u)?, self.srange)
174    }
175
176    /// The reduced product: tightens each component with the others. `None` if the
177    /// components are contradictory (no value satisfies all three).
178    pub(crate) fn reduce(mut k: KnownBits, mut u: URange, mut s: SRange) -> Option<Facts> {
179        let w = k.width();
180        for round in 0..2 {
181            let before = (k, u, s);
182            // Known bits bound both ranges.
183            u = u.meet(&URange::new(k.umin(), k.umax())?)?;
184            s = s.meet(&SRange::new(k.smin(), k.smax())?)?;
185            // An unsigned range fixes its bounds' common high bits.
186            k = k.meet(&prefix_bits(&u.lo(), &u.hi()))?;
187            // A signed range on one side of zero orders like an unsigned one.
188            let zero = BitVec::zero(w);
189            let non_negative = sle(&zero, &s.lo());
190            let negative = slt(&s.hi(), &zero);
191            if non_negative || negative {
192                k = k.meet(&prefix_bits(&s.lo(), &s.hi()))?;
193                u = u.meet(&URange::new(s.lo(), s.hi())?)?;
194            }
195            // An unsigned range on one side of the sign boundary is also a signed range.
196            let smax = BitVec::smax(w);
197            if ule(&u.hi(), &smax) || ult(&smax, &u.lo()) {
198                s = s.meet(&SRange::new(u.lo(), u.hi())?)?;
199            }
200            // A round that changed nothing is a fixed point; the second round only helps when
201            // the first tightened something.
202            if round == 0 && before == (k, u, s) {
203                break;
204            }
205        }
206        Some(Facts {
207            known: k,
208            urange: u,
209            srange: s,
210        })
211    }
212}
213
214/// Transfer functions over facts, for hosts composing their own (for example the known bits of
215/// an extension operation): the facts of an operator's result for every operand value inside
216/// the operands' facts. Sound; as precise as the context's own.
217impl Facts {
218    /// Facts of `op(a)`.
219    pub fn apply_un(op: UnOp, a: &Facts) -> Result<Facts, crate::WidthError> {
220        BitVec::apply_un(op, &BitVec::zero(a.width()))?;
221        Ok(transfer(&TOp::Un(op), &[a]))
222    }
223
224    /// Facts of `op(a, b)`.
225    pub fn apply_bin(op: crate::BinOp, a: &Facts, b: &Facts) -> Result<Facts, crate::WidthError> {
226        same_width(a, b)?;
227        Ok(transfer(&TOp::Bin(op), &[a, b]))
228    }
229
230    /// Facts of the 1-bit comparison `op(a, b)`.
231    pub fn apply_cmp(
232        op: impl Into<CmpOpExt>,
233        a: &Facts,
234        b: &Facts,
235    ) -> Result<Facts, crate::WidthError> {
236        same_width(a, b)?;
237        let (op, swap) = op.into().canonical();
238        let (a, b) = if swap { (b, a) } else { (a, b) };
239        Ok(transfer(&TOp::Cmp(op), &[a, b]))
240    }
241
242    /// Facts of the zero extension to `to` bits (`to` at least the width).
243    pub fn zext(&self, to: Width) -> Result<Facts, crate::WidthError> {
244        if to == self.width() {
245            return Ok(*self);
246        }
247        crate::BitVec::zero(self.width()).zext(to)?;
248        Ok(transfer(&TOp::Zext(to), &[self]))
249    }
250
251    /// Facts of the sign extension to `to` bits (`to` at least the width).
252    pub fn sext(&self, to: Width) -> Result<Facts, crate::WidthError> {
253        if to == self.width() {
254            return Ok(*self);
255        }
256        crate::BitVec::zero(self.width()).sext(to)?;
257        Ok(transfer(&TOp::Sext(to), &[self]))
258    }
259
260    /// Facts of bits `[lo, lo + len)`.
261    pub fn extract(&self, lo: u16, len: Width) -> Result<Facts, crate::WidthError> {
262        crate::BitVec::zero(self.width()).extract(lo, len)?;
263        Ok(transfer(&TOp::Extract { lo, width: len }, &[self]))
264    }
265
266    /// Facts of `hi ยท 2^L + lo`.
267    pub fn concat(hi: &Facts, lo: &Facts) -> Result<Facts, crate::WidthError> {
268        crate::BitVec::concat(&BitVec::zero(hi.width()), &BitVec::zero(lo.width()))?;
269        Ok(transfer(&TOp::Concat, &[hi, lo]))
270    }
271
272    /// Facts of `c ? t : f` (`c` of 1 bit).
273    pub fn select(c: &Facts, t: &Facts, f: &Facts) -> Result<Facts, crate::WidthError> {
274        crate::BitVec::select(
275            &BitVec::zero(c.width()),
276            &BitVec::zero(t.width()),
277            &BitVec::zero(f.width()),
278        )?;
279        Ok(transfer(&TOp::Select, &[c, t, f]))
280    }
281}
282
283/// The same transfers on known bits alone (through [`Facts`]: the result may be sharper than
284/// a bits-only transfer, never less sound).
285impl KnownBits {
286    /// Known bits of `op(a)`.
287    pub fn apply_un(op: UnOp, a: &KnownBits) -> Result<KnownBits, crate::WidthError> {
288        Ok(Facts::apply_un(op, &Facts::from_known(*a))?.known)
289    }
290
291    /// Known bits of `op(a, b)`.
292    pub fn apply_bin(
293        op: crate::BinOp,
294        a: &KnownBits,
295        b: &KnownBits,
296    ) -> Result<KnownBits, crate::WidthError> {
297        Ok(Facts::apply_bin(op, &Facts::from_known(*a), &Facts::from_known(*b))?.known)
298    }
299}
300
301fn same_width(a: &Facts, b: &Facts) -> Result<(), crate::WidthError> {
302    if a.width() == b.width() {
303        Ok(())
304    } else {
305        Err(crate::WidthError::Mismatch {
306            left: a.width().bits(),
307            right: b.width().bits(),
308        })
309    }
310}
311
312/// Known bits shared by every value in `[lo, hi]` (unsigned): the common high bits.
313fn prefix_bits(lo: &BitVec, hi: &BitVec) -> KnownBits {
314    let w = lo.width();
315    let diff = known::bv_xor(lo, hi);
316    let p = known::leading_zeros(&diff);
317    let mask = known::high_mask(w, p);
318    KnownBits::from_masks(
319        known::bv_and(&known::bv_not(lo), &mask),
320        known::bv_and(lo, &mask),
321    )
322}
323
324/// A three-valued answer. `Unknown` is always a correct answer.
325#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
326pub enum Truth {
327    /// Proved true.
328    True,
329    /// Proved false.
330    False,
331    /// Not decided.
332    Unknown,
333}
334
335impl Truth {
336    fn from_opt(b: Option<bool>) -> Truth {
337        match b {
338            Some(true) => Truth::True,
339            Some(false) => Truth::False,
340            None => Truth::Unknown,
341        }
342    }
343}
344
345impl core::ops::Not for Truth {
346    type Output = Truth;
347    /// The logical negation (`Unknown` stays `Unknown`).
348    fn not(self) -> Truth {
349        match self {
350            Truth::True => Truth::False,
351            Truth::False => Truth::True,
352            Truth::Unknown => Truth::Unknown,
353        }
354    }
355}
356
357/// A question about expression values, answered by [`Context::prove`].
358#[derive(Copy, Clone, Debug)]
359#[non_exhaustive]
360pub enum Query<'q> {
361    /// The value is zero.
362    IsZero(Expr),
363    /// The value is not zero.
364    IsNonZero(Expr),
365    /// Bit `bit` has value `value`.
366    Bit {
367        /// The expression.
368        e: Expr,
369        /// The bit index.
370        bit: u16,
371        /// The value asked about.
372        value: bool,
373    },
374    /// The two values are equal.
375    Eq(Expr, Expr),
376    /// The comparison holds.
377    Cmp(CmpOpExt, Expr, Expr),
378    /// The value is in `[lo, hi]` (unsigned).
379    InURange {
380        /// The expression.
381        e: Expr,
382        /// Lower bound.
383        lo: &'q BitVec,
384        /// Upper bound.
385        hi: &'q BitVec,
386    },
387    /// The value is in `[lo, hi]` (signed).
388    InSRange {
389        /// The expression.
390        e: Expr,
391        /// Lower bound.
392        lo: &'q BitVec,
393        /// Upper bound.
394        hi: &'q BitVec,
395    },
396    /// The value is a multiple of `2^log2`.
397    Aligned {
398        /// The expression.
399        e: Expr,
400        /// Alignment exponent.
401        log2: u16,
402    },
403    /// `e & mask == e`.
404    MaskRedundant {
405        /// The expression.
406        e: Expr,
407        /// The mask.
408        mask: &'q BitVec,
409    },
410    /// The unsigned value fits in `bits` bits.
411    FitsUnsigned {
412        /// The expression.
413        e: Expr,
414        /// Number of bits.
415        bits: u16,
416    },
417    /// The signed value fits in `bits` bits.
418    FitsSigned {
419        /// The expression.
420        e: Expr,
421        /// Number of bits.
422        bits: u16,
423    },
424    /// The facts pin the value to one constant.
425    IsConstant(Expr),
426}
427
428/// An answer from [`Context::prove_under`]: the truth value, and the constraints it relies on
429/// (none for `Unknown`).
430#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
431#[non_exhaustive]
432pub struct Proof {
433    /// The answer.
434    pub truth: Truth,
435    /// The constraints the answer relies on.
436    pub relies_on: Reliance,
437}
438
439/// A capped base-fact computation in progress: the post-order of the uncached part below
440/// `root`, and how far it got. A later query for the same root resumes here.
441#[derive(Clone, Debug)]
442struct Pending {
443    root: u32,
444    order: Vec<u32>,
445    pos: usize,
446}
447
448/// The per-context fact cache: base facts per node, one overlay for the most recent
449/// assumption set (keyed by an exact copy of it), and the resumable capped computation.
450#[derive(Clone, Debug, Default)]
451pub(crate) struct FactCache {
452    base: IdMap<u32, Facts>,
453    overlay: IdMap<u32, (Facts, Reliance)>,
454    overlay_key: Option<Assumptions>,
455    pending: Option<Pending>,
456    /// Transfer functions run so far (never reset; the engine charges differences).
457    pub(crate) work: u64,
458    /// Queries answered imprecisely because a work cap was reached (never reset).
459    pub(crate) capped: u64,
460    /// Nodes the overlay walks visited (never reset; for tests of its cost).
461    pub(crate) walked: u64,
462}
463
464impl FactCache {
465    /// Stops keying the overlay by `a` (if it is), so `a` can change without a copy.
466    pub(crate) fn forget(&mut self, a: &Assumptions) {
467        if self
468            .overlay_key
469            .as_ref()
470            .is_some_and(|k| Arc::ptr_eq(&k.store, &a.store))
471        {
472            self.overlay_key = None;
473            self.overlay.clear();
474        }
475    }
476
477    pub(crate) fn clear(&mut self) {
478        self.base.clear();
479        self.overlay.clear();
480        self.overlay_key = None;
481        self.pending = None;
482    }
483}
484
485impl Context {
486    pub(crate) fn top_of(&self, i: u32) -> TOp {
487        let n = self.node(i);
488        let w = self.width_of(i);
489        match n.op {
490            OpCode::Const => TOp::Const(self.const_val(i).unwrap_or(BitVec::zero(w))),
491            OpCode::Sym => TOp::Top(w),
492            OpCode::Zext => TOp::Zext(w),
493            OpCode::Sext => TOp::Sext(w),
494            OpCode::Extract => TOp::Extract {
495                lo: n.b as u16,
496                width: w,
497            },
498            OpCode::Concat => TOp::Concat,
499            OpCode::Select => TOp::Select,
500            op => {
501                if let Some(u) = op.as_un() {
502                    TOp::Un(u)
503                } else if let Some(b) = op.as_bin() {
504                    TOp::Bin(b)
505                } else if let Some(c) = op.as_cmp() {
506                    TOp::Cmp(c)
507                } else {
508                    TOp::Top(w)
509                }
510            }
511        }
512    }
513
514    /// Runs the transfer function of node `i` over facts of its children taken from `get`.
515    fn transfer_node(&self, i: u32, get: impl Fn(u32) -> Facts) -> Facts {
516        let op = self.top_of(i);
517        let n = self.node(i);
518        let mut kids = [Facts::top(Width::W1); 3];
519        let mut k = 0;
520        for c in n.children() {
521            kids[k] = get(c);
522            k += 1;
523        }
524        let refs: [&Facts; 3] = [&kids[0], &kids[1], &kids[2]];
525        self.transfer_at(i, &op, &refs[..k])
526    }
527
528    /// The facts of node `i` (whose operator is `op`) from its operands' facts: the transfer
529    /// function, or for an extension node its operation's known bits (exact when every
530    /// argument is).
531    pub(crate) fn transfer_at(&self, i: u32, op: &TOp, args: &[&Facts]) -> Facts {
532        let n = self.node(i);
533        if n.op == OpCode::Select {
534            let f = transfer(op, args);
535            return self
536                .min_max_bound(i, args)
537                .and_then(|g| f.meet(&g))
538                .unwrap_or(f);
539        }
540        if n.op == OpCode::Concat {
541            let f = transfer(op, args);
542            return self
543                .wide_product_bound(i)
544                .and_then(|g| f.meet(&g))
545                .unwrap_or(f);
546        }
547        let Some((_, k)) = n.op.as_ext() else {
548            return transfer(op, args);
549        };
550        let w = self.width_of(i);
551        let Some(o) = self.registry.as_deref().and_then(|r| r.op_at(n.aux)) else {
552            return Facts::top(w);
553        };
554        // Exact when every argument is.
555        let vals: Option<Vec<BitVec>> = args.iter().map(|f| f.as_constant()).collect();
556        if let Some(vals) = vals {
557            return match crate::ext::run_eval(o, &vals) {
558                Ok(out) if out.get(k).is_some_and(|v| v.width() == w) => Facts::constant(&out[k]),
559                _ => Facts::top(w),
560            };
561        }
562        let widths: Vec<Width> = args.iter().map(|f| f.width()).collect();
563        let Ok(sig) = o.signature(&widths) else {
564            return Facts::top(w);
565        };
566        let kb: Vec<KnownBits> = args.iter().map(|f| f.known).collect();
567        match crate::ext::run_known_bits(o, &sig, &kb) {
568            Ok(out) => out
569                .get(k)
570                .filter(|kb| kb.width() == w)
571                .map_or(Facts::top(w), |kb| Facts::from_known(*kb)),
572            Err(_) => Facts::top(w),
573        }
574    }
575
576    /// A select choosing between the two operands of its own ordered comparison is a minimum or
577    /// maximum (`select(p <u q, p, q)` is `umin(p, q)`): the range of the choice is then bounded
578    /// on both sides, which the join of the arms alone does not give.
579    fn min_max_bound(&self, i: u32, args: &[&Facts]) -> Option<Facts> {
580        let n = self.node(i);
581        let c = self.node(n.a);
582        let op = c.op.as_cmp()?;
583        let min = if (n.b, n.c) == (c.a, c.b) {
584            true
585        } else if (n.b, n.c) == (c.b, c.a) {
586            false
587        } else {
588            return None;
589        };
590        let (t, f) = (args[1], args[2]);
591        let w = t.width();
592        let pick = |x: BitVec, y: BitVec, less: fn(&BitVec, &BitVec) -> bool, low: bool| {
593            if less(&x, &y) == low { x } else { y }
594        };
595        match op {
596            CmpOp::Ult | CmpOp::Ule => {
597                let (a, b) = (t.urange, f.urange);
598                let lo = pick(a.lo(), b.lo(), ult, min);
599                let hi = pick(a.hi(), b.hi(), ult, min);
600                Facts::new(KnownBits::unknown(w), URange::new(lo, hi)?, SRange::full(w))
601            }
602            CmpOp::Slt | CmpOp::Sle => {
603                let (a, b) = (t.srange, f.srange);
604                let lo = pick(a.lo(), b.lo(), slt, min);
605                let hi = pick(a.hi(), b.hi(), slt, min);
606                Facts::new(KnownBits::unknown(w), URange::full(w), SRange::new(lo, hi)?)
607            }
608            _ => None,
609        }
610    }
611
612    /// `concat(umulhi(a, b), a * b)` is the full unsigned product of `a` and `b` (and with
613    /// `smulhi`, the signed one): at twice the width it cannot overflow, so its range follows
614    /// from the operands' ranges, which the two halves' facts alone lose.
615    fn wide_product_bound(&self, i: u32) -> Option<Facts> {
616        let n = self.node(i);
617        let (hi, lo) = (self.node(n.a), self.node(n.b));
618        if lo.op != OpCode::Mul || (hi.a, hi.b) != (lo.a, lo.b) {
619            return None;
620        }
621        let signed = match hi.op {
622            OpCode::UMulHi => false,
623            OpCode::SMulHi => true,
624            _ => return None,
625        };
626        let w2 = self.width_of(i);
627        let fa = self.facts.base.get(&lo.a)?;
628        let fb = self.facts.base.get(&lo.b)?;
629        let mul = |x: &BitVec, y: &BitVec| BitVec::bin_unchecked(crate::BinOp::Mul, x, y);
630        if signed {
631            let ext = |v: BitVec| v.sext(w2).ok();
632            let (a, b) = (fa.srange, fb.srange);
633            let corners = [
634                mul(&ext(a.lo())?, &ext(b.lo())?),
635                mul(&ext(a.lo())?, &ext(b.hi())?),
636                mul(&ext(a.hi())?, &ext(b.lo())?),
637                mul(&ext(a.hi())?, &ext(b.hi())?),
638            ];
639            let lo = corners
640                .iter()
641                .copied()
642                .reduce(|x, y| if slt(&y, &x) { y } else { x })?;
643            let hi = corners
644                .iter()
645                .copied()
646                .reduce(|x, y| if slt(&x, &y) { y } else { x })?;
647            Facts::new(
648                KnownBits::unknown(w2),
649                URange::full(w2),
650                SRange::new(lo, hi)?,
651            )
652        } else {
653            let ext = |v: BitVec| v.zext(w2).ok();
654            let (a, b) = (fa.urange, fb.urange);
655            let lo = mul(&ext(a.lo())?, &ext(b.lo())?);
656            let hi = mul(&ext(a.hi())?, &ext(b.hi())?);
657            Facts::new(
658                KnownBits::unknown(w2),
659                URange::new(lo, hi)?,
660                SRange::full(w2),
661            )
662        }
663    }
664
665    /// Post-order of the part of the DAG below `root` whose facts are not cached.
666    fn uncached_order(&mut self, root: u32) -> Vec<u32> {
667        self.marks.begin(self.len());
668        let mut order = Vec::new();
669        let mut stack: Vec<(u32, u8)> = vec![(root, 0)];
670        self.marks.test_and_set(root);
671        while let Some(top) = stack.last_mut() {
672            let (i, k) = *top;
673            let n = self.node(i);
674            if (k as usize) < n.op.arity() {
675                top.1 += 1;
676                let c = [n.a, n.b, n.c][k as usize];
677                if !self.facts.base.contains_key(&c) && !self.marks.test_and_set(c) {
678                    stack.push((c, 0));
679                }
680            } else {
681                stack.pop();
682                order.push(i);
683            }
684        }
685        order
686    }
687
688    /// Computes base facts for `root` and every uncached node below it, running at most
689    /// `fact_work` transfers. Returns `None` if the cap was reached first; the next call for
690    /// the same root resumes where this one stopped, so total work stays linear.
691    fn compute_facts(&mut self, root: u32) -> Option<Facts> {
692        let cap = self.config().fact_work.max(1);
693        self.compute_facts_cap(root, cap)
694    }
695
696    /// [`Self::compute_facts`] with at most `cap` transfers (and at most the context's cap).
697    fn compute_facts_cap(&mut self, root: u32, cap: u32) -> Option<Facts> {
698        if let Some(f) = self.facts.base.get(&root) {
699            return Some(*f);
700        }
701        let cap = cap.min(self.config().fact_work.max(1));
702        if cap == 0 {
703            self.facts.capped += 1;
704            return None;
705        }
706        let cap = cap as usize;
707        let mut pending = match self.facts.pending.take() {
708            Some(p) if p.root == root => p,
709            _ => Pending {
710                root,
711                order: self.uncached_order(root),
712                pos: 0,
713            },
714        };
715        let mut done = 0usize;
716        while pending.pos < pending.order.len() {
717            let i = pending.order[pending.pos];
718            if !self.facts.base.contains_key(&i) {
719                if done >= cap {
720                    self.facts.pending = Some(pending);
721                    self.facts.capped += 1;
722                    return None;
723                }
724                let f = self.transfer_node(i, |c| self.facts.base[&c]);
725                self.facts.base.insert(i, f);
726                done += 1;
727                self.facts.work += 1;
728            }
729            pending.pos += 1;
730        }
731        self.facts.base.get(&root).copied()
732    }
733
734    /// Facts about `e`. If the per-query work cap (`ContextConfig::fact_work`) is reached,
735    /// the answer is [`Facts::top`] (sound, just imprecise); see [`Context::try_facts`].
736    pub fn facts(&mut self, e: Expr) -> Result<Facts, Error> {
737        let i = self.id(e)?;
738        Ok(self
739            .compute_facts(i)
740            .unwrap_or_else(|| Facts::top(self.width_of(i))))
741    }
742
743    /// Facts about `e`, or `None` if the per-query work cap was reached first. Progress is
744    /// kept: repeating the call for the same expression continues where it stopped.
745    pub fn try_facts(&mut self, e: Expr) -> Result<Option<Facts>, Error> {
746        let i = self.id(e)?;
747        Ok(self.compute_facts(i))
748    }
749
750    /// The value of `e` if its facts pin exactly one.
751    pub fn exact(&mut self, e: Expr) -> Result<Option<BitVec>, Error> {
752        if let Some(v) = self.as_const(e)? {
753            return Ok(Some(v));
754        }
755        Ok(self.facts(e)?.as_constant())
756    }
757
758    /// The values `e`'s facts allow, if there are at most `limit` of them (and at most 2^20
759    /// candidates to examine). Every value `e` can take is included; some included values may
760    /// be impossible, since facts over-approximate.
761    pub fn enumerate_values(&mut self, e: Expr, limit: u32) -> Result<Option<Vec<BitVec>>, Error> {
762        let f = self.facts(e)?;
763        let limit = u64::from(limit.min(1 << 20));
764        // From the unsigned range when it is narrow.
765        let span = BitVec::bin_unchecked(crate::BinOp::Sub, &f.urange.hi(), &f.urange.lo());
766        if span.to_u64().is_some_and(|d| d < limit) {
767            let mut out = Vec::new();
768            let mut v = f.urange.lo();
769            let one = BitVec::one(f.width());
770            loop {
771                if f.contains(&v) {
772                    out.push(v);
773                }
774                if v == f.urange.hi() {
775                    break;
776                }
777                v = BitVec::bin_unchecked(crate::BinOp::Add, &v, &one);
778            }
779            return Ok(Some(out));
780        }
781        // From the known bits otherwise.
782        Ok(f.known
783            .enumerate(20)
784            .map(|vs| vs.into_iter().filter(|v| f.contains(v)).collect::<Vec<_>>())
785            .filter(|vs| vs.len() as u64 <= limit))
786    }
787
788    /// Facts about `e` under `assumptions`, or `None` if the assumptions are infeasible for
789    /// the part of the expression that `e` depends on. Never pollutes the base cache.
790    pub fn facts_with(
791        &mut self,
792        e: Expr,
793        assumptions: &Assumptions,
794    ) -> Result<Option<Facts>, Error> {
795        let cap = self.config().fact_work;
796        self.facts_with_cap(e, assumptions, cap)
797    }
798
799    /// Base facts with at most `cap` transfers; `None` if the cap was reached first.
800    pub(crate) fn try_facts_cap(&mut self, e: Expr, cap: u32) -> Result<Option<Facts>, Error> {
801        let i = self.id(e)?;
802        Ok(self.compute_facts_cap(i, cap))
803    }
804
805    /// Facts about `e` under `assumptions`, with the constraints they rely on; `None` if the
806    /// assumptions are infeasible. Never pollutes the base cache.
807    pub fn facts_under(
808        &mut self,
809        e: Expr,
810        assumptions: &Assumptions,
811    ) -> Result<Option<(Facts, Reliance)>, Error> {
812        let cap = self.config().fact_work;
813        Ok(self.facts_under_cap(e, assumptions, cap)?.ok())
814    }
815
816    /// [`Context::facts_with`] with at most `cap` transfers per query (and at most the
817    /// context's cap); a capped query answers `top`, counted in `capped`.
818    pub(crate) fn facts_with_cap(
819        &mut self,
820        e: Expr,
821        assumptions: &Assumptions,
822        cap: u32,
823    ) -> Result<Option<Facts>, Error> {
824        Ok(self
825            .facts_under_cap(e, assumptions, cap)?
826            .ok()
827            .map(|(f, _)| f))
828    }
829
830    /// Facts about `e` under `assumptions` and the constraints they rely on, with at most `cap`
831    /// transfers; `Err` with the conflicting constraints if the assumptions are infeasible.
832    pub(crate) fn facts_under_cap(
833        &mut self,
834        e: Expr,
835        assumptions: &Assumptions,
836        cap: u32,
837    ) -> Result<Result<(Facts, Reliance), Reliance>, Error> {
838        let root = self.id(e)?;
839        self.overlay_for(assumptions)?;
840        let mut overlay = core::mem::take(&mut self.facts.overlay);
841        let r = self.overlay_facts(&assumptions.store.env, &mut overlay, root, cap);
842        self.facts.overlay = overlay;
843        Ok(r)
844    }
845
846    /// Makes the overlay follow `assumptions` (dropping it if they changed).
847    fn overlay_for(&mut self, assumptions: &Assumptions) -> Result<(), Error> {
848        if self.facts.overlay_key.as_ref() != Some(assumptions) {
849            assumptions.check_context(self)?;
850            self.facts.overlay.clear();
851            self.facts.overlay_key = Some(assumptions.clone());
852        }
853        Ok(())
854    }
855
856    /// Whether `op(x, y)` follows from (or is refuted by) an assumed ordering of the nodes `x`
857    /// and `y`, and what it relies on.
858    pub(crate) fn assumed_order(
859        &self,
860        assumptions: &Assumptions,
861        op: CmpOp,
862        x: u32,
863        y: u32,
864    ) -> Option<(bool, Reliance)> {
865        let env = &assumptions.store.env;
866        if env.infeasible.is_some() || x == y {
867            return None;
868        }
869        let (m, r) = env.relation(x, y)?;
870        constraint::worlds::decide(m, op).map(|v| (v, r))
871    }
872
873    /// Facts of node `root` under `env`, cached in `overlay`, with at most `cap` transfers (a
874    /// capped query answers `top`, counted in `capped`). `Err` with the constraints involved if
875    /// the assumptions are infeasible.
876    pub(crate) fn overlay_facts(
877        &mut self,
878        env: &Env,
879        overlay: &mut IdMap<u32, (Facts, Reliance)>,
880        root: u32,
881        cap: u32,
882    ) -> Result<(Facts, Reliance), Reliance> {
883        if let Some(rel) = env.infeasible {
884            return Err(rel);
885        }
886        let cap = cap.min(self.config().fact_work.max(1));
887        if let Some(f) = overlay.get(&root) {
888            return Ok(*f);
889        }
890        let base = |cx: &mut Context, i: u32| {
891            let f = cx
892                .compute_facts_cap(i, cap)
893                .unwrap_or_else(|| Facts::top(cx.width_of(i)));
894            (f, Reliance::NONE)
895        };
896        // Only nodes at or above the lowest assumed index can depend on an assumption
897        // (children always have lower indices), so the walk stops below it.
898        if env.is_empty() || root < env.min {
899            return Ok(base(self, root));
900        }
901        let min = env.min;
902        // Post-order of the part not in the overlay yet. Every node the walk finishes goes into
903        // the overlay (those that depend on no assumption with their base facts and no
904        // reliance), so a later query stops at it instead of walking below it again.
905        self.marks.begin(self.len());
906        let mut order = Vec::new();
907        let mut stack: Vec<(u32, u8)> = vec![(root, 0)];
908        self.marks.test_and_set(root);
909        while let Some(top) = stack.last_mut() {
910            let (i, k) = *top;
911            let n = self.node(i);
912            if (k as usize) < n.op.arity() {
913                top.1 += 1;
914                let c = [n.a, n.b, n.c][k as usize];
915                if c >= min && !overlay.contains_key(&c) && !self.marks.test_and_set(c) {
916                    self.facts.walked += 1;
917                    stack.push((c, 0));
918                }
919            } else {
920                stack.pop();
921                order.push(i);
922            }
923        }
924        let cap = cap as usize;
925        let mut work = 0usize;
926        for &i in &order {
927            let n = self.node(i);
928            let ordering = n.op.as_cmp().and_then(|_| env.relation(n.a, n.b));
929            // Operands below `min` depend on no assumption: base facts.
930            let kid = |cx: &mut Context, overlay: &IdMap<u32, (Facts, Reliance)>, c: u32| {
931                overlay.get(&c).copied().unwrap_or_else(|| base(cx, c))
932            };
933            let mut kids = [(Facts::top(Width::W1), Reliance::NONE); 3];
934            for (k, c) in n.children().enumerate() {
935                kids[k] = kid(self, overlay, c);
936            }
937            let tainted = env.assumed.contains_key(&i)
938                || ordering.is_some()
939                || kids[..n.op.arity()].iter().any(|(_, r)| !r.is_none());
940            if !tainted {
941                let f = base(self, i);
942                overlay.insert(i, f);
943                continue;
944            }
945            if work >= cap {
946                self.facts.capped += 1;
947                return Ok((Facts::top(self.width_of(root)), Reliance::NONE));
948            }
949            work += 1;
950            self.facts.work += 1;
951            let mut rel = Reliance::NONE;
952            for (_, r) in &kids[..n.op.arity()] {
953                rel |= *r;
954            }
955            let refs: [&Facts; 3] = [&kids[0].0, &kids[1].0, &kids[2].0];
956            let mut f = self.transfer_at(i, &self.top_of(i), &refs[..n.op.arity()]);
957            if let (Some(op), Some((m, r))) = (n.op.as_cmp(), ordering)
958                && let Some(v) = constraint::worlds::decide(m, op)
959            {
960                let v = BitVec::from_bool(v);
961                if !f.contains(&v) {
962                    return Err(rel | r);
963                }
964                // The ordering alone decides it: what the operands' facts rely on is not needed.
965                f = Facts::constant(&v);
966                rel = r;
967            }
968            if let Some((a, r)) = env.assumed.get(&i) {
969                rel |= *r;
970                f = f.meet(a).ok_or(rel)?;
971            }
972            overlay.insert(i, (f, rel));
973        }
974        Ok(overlay
975            .get(&root)
976            .copied()
977            .unwrap_or_else(|| base(self, root)))
978    }
979
980    /// Answers a question from facts. Every `True`/`False` is proved; `Unknown` otherwise.
981    pub fn prove(&mut self, q: Query<'_>) -> Result<Truth, Error> {
982        Ok(self.prove_inner(q, None)?.truth)
983    }
984
985    /// Answers a question from facts under assumptions. Infeasible assumptions prove
986    /// anything: every well-formed question is answered `True`.
987    pub fn prove_with(&mut self, q: Query<'_>, assumptions: &Assumptions) -> Result<Truth, Error> {
988        Ok(self.prove_under(q, assumptions)?.truth)
989    }
990
991    /// [`Context::prove_with`], also reporting the constraints the answer relies on.
992    pub fn prove_under(&mut self, q: Query<'_>, assumptions: &Assumptions) -> Result<Proof, Error> {
993        self.prove_inner(q, Some(assumptions))
994    }
995
996    /// Checks a query's arguments (handles, widths, bit indices) before anything else.
997    fn validate_query(&self, q: &Query<'_>) -> Result<Vec<Expr>, Error> {
998        let same = |a: Width, b: Width| -> Result<(), Error> {
999            if a != b {
1000                return Err(crate::WidthError::Mismatch {
1001                    left: a.bits(),
1002                    right: b.bits(),
1003                }
1004                .into());
1005            }
1006            Ok(())
1007        };
1008        Ok(match *q {
1009            Query::IsZero(e) | Query::IsNonZero(e) | Query::IsConstant(e) => {
1010                self.width(e)?;
1011                vec![e]
1012            }
1013            Query::Aligned { e, .. }
1014            | Query::FitsUnsigned { e, .. }
1015            | Query::FitsSigned { e, .. } => {
1016                self.width(e)?;
1017                vec![e]
1018            }
1019            Query::Bit { e, bit, .. } => {
1020                let w = self.width(e)?;
1021                if bit >= w.bits() {
1022                    return Err(crate::WidthError::ExtractRange {
1023                        width: w.bits(),
1024                        lo: bit,
1025                        len: 1,
1026                    }
1027                    .into());
1028                }
1029                vec![e]
1030            }
1031            Query::Eq(x, y) | Query::Cmp(_, x, y) => {
1032                same(self.width(x)?, self.width(y)?)?;
1033                vec![x, y]
1034            }
1035            Query::InURange { e, lo, hi } | Query::InSRange { e, lo, hi } => {
1036                let w = self.width(e)?;
1037                same(w, lo.width())?;
1038                same(w, hi.width())?;
1039                vec![e]
1040            }
1041            Query::MaskRedundant { e, mask } => {
1042                same(self.width(e)?, mask.width())?;
1043                vec![e]
1044            }
1045        })
1046    }
1047
1048    fn prove_inner(&mut self, q: Query<'_>, a: Option<&Assumptions>) -> Result<Proof, Error> {
1049        let exprs = self.validate_query(&q)?;
1050        let proof = |truth: Truth, relies_on: Reliance| Proof {
1051            truth,
1052            relies_on: if truth == Truth::Unknown {
1053                Reliance::NONE
1054            } else {
1055                relies_on
1056            },
1057        };
1058        // An assumed ordering decides a comparison of two expressions directly.
1059        if let Some(a) = a
1060            && let Query::Eq(x, y) | Query::Cmp(_, x, y) = q
1061            && x != y
1062        {
1063            let (op, swap) = match q {
1064                Query::Cmp(op, ..) => op.canonical(),
1065                _ => (CmpOp::Eq, false),
1066            };
1067            let (x, y) = if swap { (y, x) } else { (x, y) };
1068            a.check_context(self)?;
1069            let (x, y) = (self.id(x)?, self.id(y)?);
1070            if let Some((v, r)) = self.assumed_order(a, op, x, y) {
1071                return Ok(proof(Truth::from_opt(Some(v)), r));
1072            }
1073        }
1074        // Facts of every involved expression (under the assumptions, if any).
1075        let mut fs = Vec::with_capacity(2);
1076        let mut rel = Reliance::NONE;
1077        for e in exprs {
1078            let f = match a {
1079                None => self.facts(e)?,
1080                Some(a) => {
1081                    let cap = self.config().fact_work;
1082                    match self.facts_under_cap(e, a, cap)? {
1083                        Ok((f, r)) => {
1084                            rel |= r;
1085                            f
1086                        }
1087                        // Infeasible: anything holds.
1088                        Err(r) => return Ok(proof(Truth::True, r)),
1089                    }
1090                }
1091            };
1092            fs.push(f);
1093        }
1094        let f = fs[0];
1095        let w = f.width();
1096        let truth = match q {
1097            Query::IsZero(_) => {
1098                Truth::from_opt(decide(CmpOp::Eq, &f, &Facts::constant(&BitVec::zero(w))))
1099            }
1100            Query::IsNonZero(_) => {
1101                !Truth::from_opt(decide(CmpOp::Eq, &f, &Facts::constant(&BitVec::zero(w))))
1102            }
1103            Query::IsConstant(_) => {
1104                if f.as_constant().is_some() {
1105                    Truth::True
1106                } else {
1107                    Truth::Unknown
1108                }
1109            }
1110            Query::Bit { bit, value, .. } => Truth::from_opt(f.known.bit(bit).map(|b| b == value)),
1111            Query::Eq(x, y) => {
1112                if x == y {
1113                    Truth::True
1114                } else {
1115                    Truth::from_opt(decide(CmpOp::Eq, &fs[0], &fs[1]))
1116                }
1117            }
1118            Query::Cmp(op, x, y) => {
1119                let (op, swap) = op.canonical();
1120                if x == y {
1121                    Truth::from_opt(Some(matches!(op, CmpOp::Eq | CmpOp::Ule | CmpOp::Sle)))
1122                } else {
1123                    let (fx, fy) = if swap {
1124                        (&fs[1], &fs[0])
1125                    } else {
1126                        (&fs[0], &fs[1])
1127                    };
1128                    Truth::from_opt(decide(op, fx, fy))
1129                }
1130            }
1131            Query::InURange { lo, hi, .. } => {
1132                if ule(lo, &f.urange.lo()) && ule(&f.urange.hi(), hi) {
1133                    Truth::True
1134                } else if ult(hi, &f.urange.lo()) || ult(&f.urange.hi(), lo) {
1135                    Truth::False
1136                } else {
1137                    Truth::Unknown
1138                }
1139            }
1140            Query::InSRange { lo, hi, .. } => {
1141                if sle(lo, &f.srange.lo()) && sle(&f.srange.hi(), hi) {
1142                    Truth::True
1143                } else if slt(hi, &f.srange.lo()) || slt(&f.srange.hi(), lo) {
1144                    Truth::False
1145                } else {
1146                    Truth::Unknown
1147                }
1148            }
1149            Query::Aligned { log2, .. } => {
1150                let low = known::low_mask(w, u32::from(log2));
1151                if known::bv_and(&f.known.maybe_one(), &low).is_zero() {
1152                    Truth::True
1153                } else if !known::bv_and(&f.known.known_one(), &low).is_zero() {
1154                    Truth::False
1155                } else {
1156                    Truth::Unknown
1157                }
1158            }
1159            Query::MaskRedundant { mask, .. } => {
1160                let outside = known::bv_not(mask);
1161                if known::bv_and(&f.known.maybe_one(), &outside).is_zero() {
1162                    Truth::True
1163                } else if !known::bv_and(&f.known.known_one(), &outside).is_zero() {
1164                    Truth::False
1165                } else {
1166                    Truth::Unknown
1167                }
1168            }
1169            Query::FitsUnsigned { bits, .. } => {
1170                if u32::from(bits) >= u32::from(w.bits()) {
1171                    Truth::True
1172                } else {
1173                    let limit = known::low_mask(w, u32::from(bits));
1174                    if ule(&f.urange.hi(), &limit) {
1175                        Truth::True
1176                    } else if ult(&limit, &f.urange.lo()) {
1177                        Truth::False
1178                    } else {
1179                        Truth::Unknown
1180                    }
1181                }
1182            }
1183            Query::FitsSigned { bits, .. } => {
1184                if bits == 0 {
1185                    Truth::False
1186                } else if u32::from(bits) >= u32::from(w.bits()) {
1187                    Truth::True
1188                } else {
1189                    // [-(2^(bits-1)), 2^(bits-1) - 1] at width W.
1190                    let hi = known::low_mask(w, u32::from(bits) - 1);
1191                    let lo = known::bv_not(&hi);
1192                    if sle(&lo, &f.srange.lo()) && sle(&f.srange.hi(), &hi) {
1193                        Truth::True
1194                    } else if slt(&f.srange.hi(), &lo) || slt(&hi, &f.srange.lo()) {
1195                        Truth::False
1196                    } else {
1197                        Truth::Unknown
1198                    }
1199                }
1200            }
1201        };
1202        Ok(proof(truth, rel))
1203    }
1204}