mod build;
pub(crate) use build::count_mod;
pub use build::traps;
mod eval;
mod fp;
mod node;
mod symbols;
#[cfg(test)]
mod tests;
use core::cmp::Ordering;
use core::fmt;
use core::num::NonZeroU32;
use std::sync::atomic::{AtomicU32, Ordering as AtomicOrdering};
use hashbrown::HashTable;
use crate::error::Error;
use crate::hash::{IdMap, combine};
use crate::ops::{BinOp, CmpOp, UnOp};
use crate::value::wide::nlimbs;
use crate::{BitVec, Width};
pub use eval::{Env, FnEnv, Substitution};
pub(crate) use node::{AUX_POOLED, Node, OpCode};
pub(crate) use symbols::{SymEntry, SymbolTable};
pub use symbols::{SymbolId, SymbolKey};
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub struct Expr {
index: u32,
tag: NonZeroU32,
}
impl Expr {
#[inline]
pub fn index(self) -> u32 {
self.index
}
#[inline]
pub fn to_bits(self) -> u64 {
u64::from(self.tag.get()) << 32 | u64::from(self.index)
}
#[inline]
pub fn from_bits(bits: u64) -> Option<Expr> {
Some(Expr {
index: bits as u32,
tag: NonZeroU32::new((bits >> 32) as u32)?,
})
}
}
impl fmt::Debug for Expr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "e{}", self.index)
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub struct ContextConfig {
pub max_nodes: u32,
pub hash_seed: u64,
pub fact_work: u32,
}
impl Default for ContextConfig {
fn default() -> Self {
ContextConfig {
max_nodes: 1 << 24,
hash_seed: 0,
fact_work: 1 << 20,
}
}
}
impl ContextConfig {
pub fn with_max_nodes(mut self, n: u32) -> Self {
self.max_nodes = n;
self
}
pub fn with_fact_work(mut self, n: u32) -> Self {
self.fact_work = n;
self
}
pub fn with_hash_seed(mut self, seed: u64) -> Self {
self.hash_seed = seed;
self
}
}
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
#[non_exhaustive]
pub struct ArenaCounters {
pub caller_nodes: u64,
pub engine_nodes: u64,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct Mark {
tag: NonZeroU32,
caller_nodes: u64,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum Bounded {
Exact(u32),
AtLeast(u32),
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
#[non_exhaustive]
pub enum View {
Const(BitVec),
Sym(SymbolId),
Un(UnOp, Expr),
Bin(BinOp, Expr, Expr),
Cmp(CmpOp, Expr, Expr),
Zext(Expr),
Sext(Expr),
Extract {
lo: u16,
src: Expr,
},
Concat {
hi: Expr,
lo: Expr,
},
Select {
cond: Expr,
then: Expr,
els: Expr,
},
Fp {
op: crate::fp::FpOp,
format: crate::fp::FpFormat,
args: crate::fp::FpArgs,
},
Ext {
op: crate::ext::ExtId,
output: u8,
args: crate::ext::ExtArgs,
},
}
#[derive(Copy, Clone, Debug)]
pub(crate) struct Meta {
pub(crate) shash: u64,
pub(crate) height: u32,
pub(crate) tree: u32,
}
#[derive(Clone, Debug, Default)]
pub(crate) struct Marks {
stamp: Vec<u32>,
cur: u32,
}
impl Marks {
pub(crate) fn begin(&mut self, len: usize) {
if self.stamp.len() < len {
self.stamp.resize(len, 0);
}
self.cur = self.cur.wrapping_add(1);
if self.cur == 0 {
self.stamp.fill(0);
self.cur = 1;
}
}
#[inline]
pub(crate) fn test_and_set(&mut self, i: u32) -> bool {
let s = &mut self.stamp[i as usize];
let was = *s == self.cur;
*s = self.cur;
was
}
}
static TAGS: AtomicU32 = AtomicU32::new(0);
fn fresh_tag() -> NonZeroU32 {
loop {
let t = TAGS.fetch_add(1, AtomicOrdering::Relaxed).wrapping_add(1);
if let Some(nz) = NonZeroU32::new(t) {
return nz;
}
}
}
const RETIRED: usize = 8;
pub struct Context {
pub(crate) nodes: Vec<Node>,
pub(crate) meta: Vec<Meta>,
interner: HashTable<u32>,
pub(crate) wide_consts: Vec<u64>,
pub(crate) symbols: SymbolTable,
config: ContextConfig,
generation: u32,
pub(crate) declaration_revision: u64,
tag: NonZeroU32,
retired: [u32; RETIRED],
counters: ArenaCounters,
engine_mode: bool,
engine_allowance: Option<u64>,
engine_refused: bool,
pub(crate) marks: Marks,
dag_cache: IdMap<u32, u32>,
eval_plan: Option<Box<eval::Plan>>,
pub(crate) facts: crate::facts::FactCache,
pub(crate) memo: crate::engine::Memo,
#[cfg(feature = "eqsat")]
pub(crate) eqsat_memo: crate::eqsat::Memo,
pub(crate) registry: Option<std::sync::Arc<crate::ext::Registry>>,
}
impl fmt::Debug for Context {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Context")
.field("nodes", &self.nodes.len())
.field("symbols", &self.symbols.entries.len())
.field("generation", &self.generation)
.finish_non_exhaustive()
}
}
impl Default for Context {
fn default() -> Self {
Self::new()
}
}
impl Context {
pub fn new() -> Self {
Self::with_config(ContextConfig::default())
}
pub fn with_config(config: ContextConfig) -> Self {
Context {
nodes: Vec::new(),
meta: Vec::new(),
interner: HashTable::new(),
wide_consts: Vec::new(),
symbols: SymbolTable::default(),
config,
generation: 0,
declaration_revision: 0,
tag: fresh_tag(),
retired: [0; RETIRED],
counters: ArenaCounters::default(),
engine_mode: false,
engine_allowance: None,
engine_refused: false,
marks: Marks::default(),
dag_cache: IdMap::default(),
eval_plan: None,
facts: crate::facts::FactCache::default(),
memo: crate::engine::Memo::default(),
#[cfg(feature = "eqsat")]
eqsat_memo: crate::eqsat::Memo::default(),
registry: None,
}
}
pub fn with_registry(
config: ContextConfig,
registry: std::sync::Arc<crate::ext::Registry>,
) -> Self {
let mut cx = Self::with_config(config);
cx.registry = Some(registry);
cx
}
pub fn registry(&self) -> Option<&crate::ext::Registry> {
self.registry.as_deref()
}
pub fn extend_registry(
&mut self,
registry: std::sync::Arc<crate::ext::Registry>,
) -> Result<(), Error> {
if let Some(old) = self.registry.as_deref() {
let kept = old.ids().all(|id| {
registry.op(id).is_some_and(|o| {
old.op(id).is_some_and(|p| {
p.name() == o.name()
&& p.revision() == o.revision()
&& p.traits() == o.traits()
})
})
});
if !kept {
return Err(Error::Unsupported(
"the new registry does not extend the current one".into(),
));
}
}
self.registry = Some(registry);
Ok(())
}
pub fn config(&self) -> &ContextConfig {
&self.config
}
pub fn set_max_nodes(&mut self, n: u32) {
self.config.max_nodes = n;
}
pub fn len(&self) -> usize {
self.nodes.len()
}
pub fn is_empty(&self) -> bool {
self.nodes.is_empty()
}
pub fn clear(&mut self) {
self.nodes.clear();
self.meta.clear();
self.interner.clear();
self.wide_consts.clear();
self.symbols.clear();
self.declaration_revision = 0;
self.dag_cache.clear();
if let Some(plan) = &mut self.eval_plan {
plan.clear();
}
self.facts.clear();
self.memo.clear();
#[cfg(feature = "eqsat")]
self.eqsat_memo.clear();
self.counters = ArenaCounters::default();
self.generation = self.generation.wrapping_add(1);
self.retired.rotate_right(1);
self.retired[0] = self.tag.get();
self.tag = fresh_tag();
}
pub fn reserve(&mut self, additional: usize) {
self.nodes.reserve(additional);
self.meta.reserve(additional);
let seed = self.config.hash_seed;
let meta = &self.meta;
self.interner
.reserve(additional, |&i| combine(seed, meta[i as usize].shash));
}
pub fn counters(&self) -> ArenaCounters {
self.counters
}
pub fn mark(&self) -> Mark {
Mark {
tag: self.tag,
caller_nodes: self.counters.caller_nodes,
}
}
pub fn caller_growth_since(&self, mark: Mark) -> u64 {
if mark.tag != self.tag {
return self.counters.caller_nodes;
}
self.counters.caller_nodes.saturating_sub(mark.caller_nodes)
}
#[inline]
pub(crate) fn id(&self, e: Expr) -> Result<u32, Error> {
if e.tag == self.tag && (e.index as usize) < self.nodes.len() {
return Ok(e.index);
}
if self.retired.contains(&e.tag.get()) {
Err(Error::StaleExpr)
} else {
Err(Error::ForeignExpr)
}
}
#[inline]
pub(crate) fn handle(&self, index: u32) -> Expr {
Expr {
index,
tag: self.tag,
}
}
pub(crate) fn ids(&self, es: &[Expr]) -> Result<Vec<u32>, Error> {
es.iter().map(|&e| self.id(e)).collect()
}
#[inline]
pub(crate) fn node(&self, i: u32) -> Node {
self.nodes[i as usize]
}
#[inline]
pub(crate) fn wid(&self, i: u32) -> u16 {
self.nodes[i as usize].width
}
#[inline]
pub(crate) fn width_of(&self, i: u32) -> Width {
Width::new(self.nodes[i as usize].width).unwrap_or(Width::W1)
}
#[inline]
pub(crate) fn is_const(&self, i: u32) -> bool {
self.nodes[i as usize].op == OpCode::Const
}
pub(crate) fn const_val(&self, i: u32) -> Option<BitVec> {
let n = self.nodes[i as usize];
if n.op != OpCode::Const {
return None;
}
let width = Width::new(n.width).ok()?;
if n.aux & AUX_POOLED != 0 {
let len = nlimbs(n.width);
let off = n.a as usize;
Some(BitVec::wrapping_from_limbs(
width,
&self.wide_consts[off..off + len],
))
} else {
Some(BitVec::wrapping_from_u64(
width,
u64::from(n.a) | (u64::from(n.b) << 32),
))
}
}
fn table_hash(&self, shash: u64) -> u64 {
combine(self.config.hash_seed, shash)
}
fn structural_hash(&self, n: &Node) -> u64 {
let mut h = combine(u64::from(n.op as u8), u64::from(n.width));
if n.op.as_ext().is_some() {
let op = self.registry.as_deref().map_or(0, |r| r.hash_at(n.aux));
h = combine(h, op);
}
if n.op.is_fp() {
h = combine(h, u64::from(n.aux));
if n.op == OpCode::FConvert {
h = combine(h, u64::from(n.b));
}
}
match n.op {
OpCode::Extract => {
h = combine(h, u64::from(n.b));
h = combine(h, self.meta[n.a as usize].shash);
}
_ => {
for c in n.children() {
h = combine(h, self.meta[c as usize].shash);
}
}
}
h
}
fn const_hash(v: &BitVec) -> u64 {
let mut h = combine(u64::from(OpCode::Const as u8), u64::from(v.width().bits()));
for &l in v.limbs() {
h = combine(h, l);
}
h
}
fn push(&mut self, n: Node, shash: u64) -> Result<u32, Error> {
self.room()?;
let idx = self.nodes.len() as u32;
let (mut height, mut tree) = (0u32, 1u32);
for c in n.children() {
let m = self.meta[c as usize];
height = height.max(m.height);
tree = tree.saturating_add(m.tree);
}
let height = if n.op.arity() == 0 {
0
} else {
height.saturating_add(1)
};
self.nodes.push(n);
self.meta.push(Meta {
shash,
height,
tree,
});
let seed = self.config.hash_seed;
let meta = &self.meta;
self.interner
.insert_unique(combine(seed, shash), idx, |&i| {
combine(seed, meta[i as usize].shash)
});
if self.engine_mode {
self.counters.engine_nodes += 1;
if let Some(a) = &mut self.engine_allowance {
*a -= 1;
}
} else {
self.counters.caller_nodes += 1;
}
Ok(idx)
}
pub(crate) fn mk(&mut self, n: Node) -> Result<u32, Error> {
let shash = self.structural_hash(&n);
let nodes = &self.nodes;
if let Some(&i) = self
.interner
.find(self.table_hash(shash), |&i| nodes[i as usize] == n)
{
return Ok(i);
}
self.push(n, shash)
}
pub(crate) fn mk_const_u64(&mut self, w: u16, v: u64) -> Result<u32, Error> {
debug_assert!(
w <= 64 && (w == 64 || v >> w == 0),
"a canonical small constant"
);
let shash = combine(combine(u64::from(OpCode::Const as u8), u64::from(w)), v);
let nodes = &self.nodes;
let found = self.interner.find(self.table_hash(shash), |&i| {
let n = &nodes[i as usize];
n.op == OpCode::Const && n.width == w && u64::from(n.a) | (u64::from(n.b) << 32) == v
});
if let Some(&i) = found {
return Ok(i);
}
self.room()?;
self.push(
Node::new(OpCode::Const, w, v as u32, (v >> 32) as u32, 0),
shash,
)
}
pub(crate) fn mk_const(&mut self, v: &BitVec) -> Result<u32, Error> {
let w = v.width().bits();
if w <= 64 {
return self.mk_const_u64(w, v.limbs()[0]);
}
let shash = Self::const_hash(v);
let (nodes, wide) = (&self.nodes, &self.wide_consts);
let limbs = v.limbs();
let inline = w <= 64;
let lo = limbs[0];
let found = self.interner.find(self.table_hash(shash), |&i| {
let n = &nodes[i as usize];
n.op == OpCode::Const
&& n.width == w
&& if inline {
u64::from(n.a) | (u64::from(n.b) << 32) == lo
} else {
let off = n.a as usize;
wide[off..off + limbs.len()] == *limbs
}
});
if let Some(&i) = found {
return Ok(i);
}
self.room()?;
let node = if inline {
Node::new(OpCode::Const, w, lo as u32, (lo >> 32) as u32, 0)
} else {
let off = self.wide_consts.len() as u32;
self.wide_consts.extend_from_slice(limbs);
let mut n = Node::new(OpCode::Const, w, off, 0, 0);
n.aux = AUX_POOLED;
n
};
self.push(node, shash)
}
fn room(&mut self) -> Result<(), Error> {
let limit = self.config.max_nodes;
if self.nodes.len() >= limit as usize {
return Err(Error::ArenaFull { limit });
}
if self.engine_mode && self.engine_allowance == Some(0) {
self.engine_refused = true;
return Err(Error::ArenaFull { limit });
}
Ok(())
}
pub(crate) fn as_engine_limited<T>(
&mut self,
allowance: u64,
f: impl FnOnce(&mut Self) -> T,
) -> (T, bool) {
let prev = (
self.engine_allowance.replace(allowance),
core::mem::replace(&mut self.engine_refused, false),
);
let r = self.as_engine(f);
let refused = self.engine_refused;
(self.engine_allowance, self.engine_refused) = prev;
(r, refused)
}
pub(crate) fn as_engine<T>(&mut self, f: impl FnOnce(&mut Self) -> T) -> T {
let prev = core::mem::replace(&mut self.engine_mode, true);
let r = f(self);
self.engine_mode = prev;
r
}
pub(crate) fn order(&self, x: u32, y: u32) -> Ordering {
if x == y {
return Ordering::Equal;
}
let (nx, ny) = (self.node(x), self.node(y));
let class = |op: OpCode| match op {
OpCode::Const => 2u8,
OpCode::Sym => 1,
_ => 0,
};
class(nx.op)
.cmp(&class(ny.op))
.then_with(|| match (nx.op, ny.op) {
(OpCode::Const, OpCode::Const) => {
let (a, b) = (self.const_val(x), self.const_val(y));
match (a, b) {
(Some(a), Some(b)) => {
let (la, lb) = (a.limbs(), b.limbs());
la.len()
.cmp(&lb.len())
.then_with(|| la.iter().rev().cmp(lb.iter().rev()))
}
_ => Ordering::Equal,
}
}
(OpCode::Sym, OpCode::Sym) => {
let ka = &self.symbols.entries[nx.a as usize].key;
let kb = &self.symbols.entries[ny.a as usize].key;
ka.cmp(kb)
}
_ => (nx.op as u8).cmp(&(ny.op as u8)).then(
self.meta[x as usize]
.shash
.cmp(&self.meta[y as usize].shash),
),
})
.then(nx.width.cmp(&ny.width))
.then(x.cmp(&y))
}
pub fn width(&self, e: Expr) -> Result<Width, Error> {
Ok(self.width_of(self.id(e)?))
}
pub fn view(&self, e: Expr) -> Result<View, Error> {
let i = self.id(e)?;
Ok(self.view_of(i))
}
pub(crate) fn view_of(&self, i: u32) -> View {
let n = self.node(i);
let h = |j: u32| self.handle(j);
match n.op {
OpCode::Const => View::Const(self.const_val(i).unwrap_or(BitVec::zero(Width::W1))),
OpCode::Sym => View::Sym(SymbolId(n.a)),
OpCode::Zext => View::Zext(h(n.a)),
OpCode::Sext => View::Sext(h(n.a)),
OpCode::Extract => View::Extract {
lo: n.b as u16,
src: h(n.a),
},
OpCode::Concat => View::Concat {
hi: h(n.a),
lo: h(n.b),
},
OpCode::Select => View::Select {
cond: h(n.a),
then: h(n.b),
els: h(n.c),
},
op if op.as_ext().is_some() => {
let (arity, k) = op.as_ext().unwrap_or((1, 0));
View::Ext {
op: self
.registry
.as_deref()
.and_then(|r| r.id_at(n.aux))
.unwrap_or_else(crate::ext::ExtId::dangling),
output: k as u8,
args: crate::ext::ExtArgs::new(&[h(n.a), h(n.b), h(n.c)][..arity]),
}
}
op => {
if let Some(u) = op.as_un() {
View::Un(u, h(n.a))
} else if let Some(b) = op.as_bin() {
View::Bin(b, h(n.a), h(n.b))
} else if let Some(c) = op.as_cmp() {
View::Cmp(c, h(n.a), h(n.b))
} else if let Some(d) = self.fp_desc(i) {
let arity = d.kind().arity();
View::Fp {
op: d.op,
format: d.format,
args: crate::fp::FpArgs::new(&[h(n.a), h(n.b), h(n.c)][..arity]),
}
} else {
unreachable!("every opcode is covered")
}
}
}
}
pub(crate) fn fp_desc(&self, i: u32) -> Option<crate::fp::node::Desc> {
let n = self.node(i);
crate::fp::node::Kind::of(n.op)?;
crate::fp::node::Desc::decode(&n, self.wid(n.a))
}
pub fn children(&self, e: Expr) -> Result<impl Iterator<Item = Expr> + '_, Error> {
let i = self.id(e)?;
Ok(self.node(i).children().map(|c| self.handle(c)))
}
pub fn as_const(&self, e: Expr) -> Result<Option<BitVec>, Error> {
Ok(self.const_val(self.id(e)?))
}
pub fn is_leaf(&self, e: Expr) -> Result<bool, Error> {
Ok(self.node(self.id(e)?).op.arity() == 0)
}
pub fn height(&self, e: Expr) -> Result<u32, Error> {
Ok(self.meta[self.id(e)? as usize].height)
}
pub fn tree_size(&self, e: Expr) -> Result<u32, Error> {
Ok(self.meta[self.id(e)? as usize].tree)
}
pub fn symbol(&mut self, key: impl Into<SymbolKey>, width: Width) -> Result<Expr, Error> {
let key = key.into();
if let Some(&id) = self.symbols.index.get(&key) {
let e = &self.symbols.entries[id as usize];
if e.width != width {
return Err(Error::SymbolWidthConflict {
key,
have: e.width,
want: width,
});
}
return Ok(self.handle(e.node));
}
self.new_symbol(key, width).map(|i| self.handle(i))
}
fn new_symbol(&mut self, key: SymbolKey, width: Width) -> Result<u32, Error> {
let id = self.symbols.entries.len() as u32;
let shash = combine(
combine(u64::from(OpCode::Sym as u8), u64::from(width.bits())),
key.structural_hash(),
);
let node = self.push(Node::new(OpCode::Sym, width.bits(), id, 0, 0), shash)?;
self.symbols.index.insert(key.clone(), id);
self.symbols.entries.push(SymEntry {
key,
width,
node,
known: None,
});
Ok(node)
}
pub fn fresh_symbol(&mut self, width: Width) -> Result<Expr, Error> {
loop {
let key = SymbolKey::Fresh(self.symbols.next_fresh);
self.symbols.next_fresh += 1;
if !self.symbols.index.contains_key(&key) {
return self.new_symbol(key, width).map(|i| self.handle(i));
}
}
}
pub fn find_symbol(&self, key: &SymbolKey) -> Option<Expr> {
let id = *self.symbols.index.get(key)?;
Some(self.handle(self.symbols.entries[id as usize].node))
}
pub fn symbol_id(&self, e: Expr) -> Result<Option<SymbolId>, Error> {
let n = self.node(self.id(e)?);
Ok((n.op == OpCode::Sym).then_some(SymbolId(n.a)))
}
pub fn symbol_key(&self, id: SymbolId) -> Option<&SymbolKey> {
self.symbols.entries.get(id.0 as usize).map(|e| &e.key)
}
pub fn symbol_width(&self, id: SymbolId) -> Option<Width> {
self.symbols.entries.get(id.0 as usize).map(|e| e.width)
}
pub fn symbol_count(&self) -> usize {
self.symbols.entries.len()
}
pub fn declare_known(&mut self, s: Expr, known: crate::KnownBits) -> Result<(), Error> {
let i = self.id(s)?;
let n = self.node(i);
if n.op != OpCode::Sym {
return Err(Error::Unsupported(
"known bits are declared for symbols only".into(),
));
}
if known.width().bits() != n.width {
return Err(crate::error::WidthError::Mismatch {
left: n.width,
right: known.width().bits(),
}
.into());
}
let known = (!known.known().is_zero()).then_some(known);
let entry = &mut self.symbols.entries[n.a as usize];
if entry.known == known {
return Ok(());
}
entry.known = known;
self.declaration_revision = self.declaration_revision.wrapping_add(1);
self.facts.clear();
self.memo.clear();
#[cfg(feature = "eqsat")]
self.eqsat_memo.clear();
Ok(())
}
pub fn declared_known(&self, s: Expr) -> Result<Option<crate::KnownBits>, Error> {
let i = self.id(s)?;
Ok(self.declared_at(i))
}
pub(crate) fn declared_at(&self, i: u32) -> Option<crate::KnownBits> {
let n = self.node(i);
(n.op == OpCode::Sym)
.then(|| self.symbols.entries[n.a as usize].known)
.flatten()
}
pub fn post_order(&mut self, roots: &[Expr]) -> Result<Vec<Expr>, Error> {
let ids = self.ids(roots)?;
let order = self.post_order_ids(&ids);
Ok(order.into_iter().map(|i| self.handle(i)).collect())
}
pub(crate) fn post_order_ids(&mut self, roots: &[u32]) -> Vec<u32> {
self.post_order_ids_pruned(roots, |_| false)
}
pub(crate) fn post_order_ids_pruned(
&mut self,
roots: &[u32],
skip: impl Fn(Expr) -> bool,
) -> Vec<u32> {
let mut out = Vec::new();
let mut stack: Vec<(u32, u8)> = Vec::new();
let mut begun = false;
for &r in roots {
if skip(self.handle(r)) {
continue;
}
if !begun {
self.marks.begin(self.nodes.len());
begun = true;
}
if self.marks.test_and_set(r) {
continue;
}
stack.push((r, 0));
while let Some(top) = stack.last_mut() {
let (i, k) = *top;
let n = self.nodes[i as usize];
if (k as usize) < n.op.arity() {
top.1 += 1;
let c = [n.a, n.b, n.c][k as usize];
if !self.marks.test_and_set(c) && !skip(self.handle(c)) {
stack.push((c, 0));
}
} else {
stack.pop();
out.push(i);
}
}
}
out
}
pub fn symbols_in(&mut self, roots: &[Expr]) -> Result<Vec<SymbolId>, Error> {
let ids = self.ids(roots)?;
let mut syms: Vec<SymbolId> = self
.post_order_ids(&ids)
.into_iter()
.filter_map(|i| {
let n = self.node(i);
(n.op == OpCode::Sym).then_some(SymbolId(n.a))
})
.collect();
syms.sort_by(|a, b| {
self.symbols.entries[a.0 as usize]
.key
.cmp(&self.symbols.entries[b.0 as usize].key)
});
Ok(syms)
}
pub fn dag_size(&mut self, roots: &[Expr], cap: u32) -> Result<Bounded, Error> {
let ids = self.ids(roots)?;
if let [r] = ids[..]
&& let Some(&n) = self.dag_cache.get(&r)
{
return Ok(if n <= cap {
Bounded::Exact(n)
} else {
Bounded::AtLeast(cap.saturating_add(1))
});
}
self.marks.begin(self.nodes.len());
let mut count: u32 = 0;
let mut stack: Vec<u32> = Vec::new();
for &r in &ids {
if !self.marks.test_and_set(r) {
stack.push(r);
}
}
while let Some(i) = stack.pop() {
count += 1;
if count > cap {
return Ok(Bounded::AtLeast(cap.saturating_add(1)));
}
for c in self.nodes[i as usize].children() {
if !self.marks.test_and_set(c) {
stack.push(c);
}
}
}
if let [r] = ids[..] {
self.dag_cache.insert(r, count);
}
Ok(Bounded::Exact(count))
}
}
#[derive(Clone, Debug)]
pub struct ExprMap<T> {
data: Vec<Option<T>>,
}
impl<T> Default for ExprMap<T> {
fn default() -> Self {
ExprMap { data: Vec::new() }
}
}
impl<T> ExprMap<T> {
pub fn new() -> Self {
Self::default()
}
pub fn insert(&mut self, e: Expr, v: T) -> Option<T> {
let i = e.index() as usize;
if self.data.len() <= i {
self.data.resize_with(i + 1, || None);
}
self.data[i].replace(v)
}
pub fn get(&self, e: Expr) -> Option<&T> {
self.data.get(e.index() as usize)?.as_ref()
}
pub fn remove(&mut self, e: Expr) -> Option<T> {
self.data.get_mut(e.index() as usize)?.take()
}
pub fn contains(&self, e: Expr) -> bool {
self.get(e).is_some()
}
pub fn clear(&mut self) {
self.data.clear();
}
}
#[derive(Clone, Debug, Default)]
pub struct ExprSet {
bits: Vec<u64>,
}
impl ExprSet {
pub fn new() -> Self {
Self::default()
}
pub fn insert(&mut self, e: Expr) -> bool {
let i = e.index() as usize;
if self.bits.len() <= i / 64 {
self.bits.resize(i / 64 + 1, 0);
}
let m = 1u64 << (i % 64);
let fresh = self.bits[i / 64] & m == 0;
self.bits[i / 64] |= m;
fresh
}
pub fn contains(&self, e: Expr) -> bool {
let i = e.index() as usize;
self.bits
.get(i / 64)
.is_some_and(|w| w & (1 << (i % 64)) != 0)
}
pub fn clear(&mut self) {
self.bits.clear();
}
}