Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Facts and proofs

For any expression, Context::facts returns what bitwright knows about its value without knowing the symbols: known bits, an unsigned strided interval (the values lo, lo + stride, …, hi) and a signed range, combined so that each part tightens the others. Facts are always sound: every value the expression can take lies within them.

#![allow(unused)]
fn main() {
use bitwright::{Context, ParseOptions, Width};

let mut cx = Context::new();
let e = cx.parse("(x << 4) | 3", &ParseOptions::width(Width::W8))?;
let f = cx.facts(e)?;
// The low four bits are known: 0011.
assert_eq!(f.known().known().to_u64(), Some(0x0f));
assert_eq!(f.known().known_one().to_u64(), Some(0x03));
assert_eq!(f.urange().lo().to_u64(), Some(3));
Ok::<(), bitwright::Error>(())
}

The stride carries what arithmetic keeps. (x & 7) * 3 + 1 takes the values 1, 4, 7, …, 22, so it is never 5, although 5 lies between its bounds; and known bits and the interval tighten each other both ways (an odd value between 4 and 10 is between 5 and 9, and a stride of 4 fixes the two low bits):

#![allow(unused)]
fn main() {
use bitwright::{BitVec, Context, ParseOptions, Width};

let mut cx = Context::new();
let o = ParseOptions::width(Width::W32);
let e = cx.parse("(x & 7) * 3 + 1", &o)?;
let u = cx.facts(e)?.urange();
assert_eq!((u.lo().to_u64(), u.hi().to_u64(), u.stride()), (Some(1), Some(22), 3));
let q = cx.parse("(x & 7) * 3 + 1 == 5", &o)?;
assert_eq!(cx.exact(q)?, Some(BitVec::from_bool(false)));
Ok::<(), bitwright::Error>(())
}

Facts are computed on demand, iteratively (no recursion, so deep expressions are fine), and cached per node for the life of the context. Each query is bounded by a work cap (ContextConfig::fact_work): when the cap is reached the answer is the sound “anything”, and the next query continues where this one stopped.

Proofs

Context::prove answers a question with Truth::True, Truth::False or Truth::Unknown. Unknown means the facts do not decide it, never that it is false:

#![allow(unused)]
fn main() {
use bitwright::{CmpOpExt, Context, ParseOptions, Query, Truth, Width};

let mut cx = Context::new();
let o = ParseOptions::width(Width::W16);
let small = cx.parse("zext<16>(trunc<8>(x))", &o)?;
let limit = cx.parse("256", &o)?;
assert_eq!(cx.prove(Query::Cmp(CmpOpExt::Ult, small, limit))?, Truth::True);
let odd = cx.parse("x | 1", &o)?;
assert_eq!(cx.prove(Query::IsZero(odd))?, Truth::False);
let y = cx.parse("y", &o)?;
assert_eq!(cx.prove(Query::IsZero(y))?, Truth::Unknown); // y may or may not be zero
Ok::<(), bitwright::Error>(())
}

Query::Injective and Query::Bijective ask whether an expression is an invertible function of one of its subexpressions; see Invertibility.

Assumptions

Facts about a path (after a branch, for example) are given as Assumptions. They never change the context’s own facts; queries under assumptions use a separate overlay. The next chapter, Constraints, covers predicates, propagation, and what results rely on.

#![allow(unused)]
fn main() {
use bitwright::{
    Assumptions, BitVec, CmpOpExt, Context, Facts, KnownBits, ParseOptions, Query, Truth, Width,
};

let mut cx = Context::new();
let o = ParseOptions::width(Width::W8);
let x = cx.parse("x", &o)?;
let odd = cx.parse("x & 1", &o)?;
let zero = cx.parse("0", &o)?;
// On this path, x is known to have its low bit set.
let kb = KnownBits::new(BitVec::zero(Width::W8), BitVec::one(Width::W8)).unwrap();
let mut a = Assumptions::new();
a.assume(&mut cx, x, Facts::from_known(kb))?;
assert_eq!(cx.prove_with(Query::Cmp(CmpOpExt::Ne, odd, zero), &a)?, Truth::True);
assert_eq!(cx.prove(Query::Cmp(CmpOpExt::Ne, odd, zero))?, Truth::Unknown);
Ok::<(), bitwright::Error>(())
}

What holds of a symbol everywhere, not just on one path, is declared instead: Context::declare_known(x, kb) makes the known bits part of the symbol’s meaning. The context’s own facts then start from them, and no run pays for assumptions. The book’s Inside a compiler shows it.

Rule guards (the next chapters) are answered by these same facts, which is why a guard can only ever say “proven”, never “disproven”.