Skip to content

Commit

Permalink
count uses again to decide what to make a symbol
Browse files Browse the repository at this point in the history
  • Loading branch information
ekiwi committed Nov 26, 2024
1 parent 2e74b25 commit 74f63bc
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 78 deletions.
16 changes: 16 additions & 0 deletions src/expr/meta.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ where
fn iter<'a>(&'a self) -> impl Iterator<Item = (ExprRef, &'a T)>
where
T: 'a;

fn non_default_value_keys(&self) -> impl Iterator<Item = ExprRef>;
}

/// finds the fixed point value and updates values it discovers along the way
Expand Down Expand Up @@ -77,6 +79,14 @@ impl<T: Default + Clone + Debug + PartialEq> ExprMap<T> for SparseExprMap<T> {
{
self.inner.iter().map(|(k, v)| (*k, v))
}

#[inline]
fn non_default_value_keys(&self) -> impl Iterator<Item = ExprRef> {
self.inner
.iter()
.filter(|(_, v)| **v != T::default())
.map(|(k, _)| *k)
}
}

/// A dense hash map to store meta-data related to each expression
Expand Down Expand Up @@ -124,6 +134,12 @@ impl<T: Default + Clone + Debug + PartialEq> ExprMap<T> for DenseExprMetaData<T>
index: 0,
}
}

fn non_default_value_keys(&self) -> impl Iterator<Item = ExprRef> {
self.iter()
.filter(|(_, v)| **v != T::default())
.map(|(k, _)| k)
}
}

struct ExprMetaDataIter<'a, T> {
Expand Down
10 changes: 5 additions & 5 deletions src/mc/smt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -488,7 +488,7 @@ impl TransitionSystemEncoding for UnrollSmtEncoding {

if step == 0 {
// define signals that are used to calculate init expressions
self.define_signals(ctx, smt_ctx, 0, &|info: &SmtSignalInfo| info.uses.init)?;
self.define_signals(ctx, smt_ctx, 0, &|info: &SmtSignalInfo| info.uses.init > 0)?;
}

// declare/define initial states
Expand All @@ -514,11 +514,11 @@ impl TransitionSystemEncoding for UnrollSmtEncoding {
// define other signals including inputs
if step == 0 {
self.define_signals(ctx, smt_ctx, step, &|info: &SmtSignalInfo| {
(info.uses.other || info.is_input) && !info.uses.init
(info.uses.other > 0 || info.is_input) && !info.uses.init > 0
})?;
} else {
self.define_signals(ctx, smt_ctx, step, &|info: &SmtSignalInfo| {
info.uses.other || info.is_input
info.uses.other > 0 || info.is_input
})?;
}

Expand All @@ -532,7 +532,7 @@ impl TransitionSystemEncoding for UnrollSmtEncoding {

// define next state signals for previous state
self.define_signals(ctx, smt_ctx, prev_step, &|info: &SmtSignalInfo| {
info.uses.next && !info.uses.other && !info.is_input
info.uses.next > 0 && !info.uses.other > 0 && !info.is_input
})?;

// define next state
Expand All @@ -558,7 +558,7 @@ impl TransitionSystemEncoding for UnrollSmtEncoding {
// we always define all inputs, even if they are only used in the "next" expression
// since our witness extraction relies on them being available
self.define_signals(ctx, smt_ctx, next_step, &|info: &SmtSignalInfo| {
info.uses.other || info.is_input
info.uses.other > 0 || info.is_input
})?;

// update step count
Expand Down
153 changes: 80 additions & 73 deletions src/system/analysis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,8 @@
// author: Kevin Laeufer <[email protected]>

use super::{State, TransitionSystem};
use crate::expr::traversal::TraversalCmd;
use crate::expr::{
traversal, Context, DenseExprMetaData, DenseExprSet, ExprRef, ExprSet, ForEachChild,
};
use crate::expr::*;
use rustc_hash::FxHashSet;

pub type UseCountInt = u16;

Expand Down Expand Up @@ -135,27 +133,28 @@ fn cone_of_influence_impl(
out
}

/// Checks whether expressions are used. This version _does not_ follow any state symbols.
fn check_expr_use(ctx: &Context, roots: &[ExprRef]) -> impl ExprSet {
let mut is_used = DenseExprSet::default();
for &root in roots.iter() {
traversal::top_down(ctx, root, |_, e| {
if is_used.contains(&e) {
TraversalCmd::Stop
} else {
is_used.insert(e);
TraversalCmd::Continue
}
})
/// Counts how often expressions are used. This version _does not_ follow any state symbols.
fn simple_count_expr_uses(ctx: &Context, roots: Vec<ExprRef>) -> impl ExprMap<UseCountInt> {
let mut use_count = SparseExprMap::default();
let mut todo = roots;

// ensure that all roots start with count 1
for expr in todo.iter() {
use_count[*expr] = 1;
}

while let Some(expr) = todo.pop() {
count_uses(ctx, expr, &mut use_count, &mut todo);
}
is_used

use_count
}

#[inline]
fn count_uses(
ctx: &Context,
expr: ExprRef,
use_count: &mut DenseExprMetaData<UseCountInt>,
use_count: &mut impl ExprMap<UseCountInt>,
todo: &mut Vec<ExprRef>,
) {
ctx.get(expr).for_each_child(|child| {
Expand Down Expand Up @@ -187,30 +186,55 @@ pub enum SerializeSignalKind {
}

/// Indicates which context an expression is used in.
#[derive(Debug, Clone, Default)]
#[derive(Debug, Clone, Copy, Default, Eq, PartialEq)]
pub struct Uses {
pub next: bool,
pub init: bool,
pub other: bool,
pub next: UseCountInt,
pub init: UseCountInt,
pub other: UseCountInt,
}

impl Uses {
#[inline]
pub fn is_used(&self) -> bool {
self.init || self.next || self.other
self.total() > 0
}
#[inline]
pub fn total(&self) -> UseCountInt {
self.next
.saturating_add(self.init)
.saturating_add(self.other)
}
}

pub fn new(
e: ExprRef,
init_used: &impl ExprSet,
next_used: &impl ExprSet,
other_used: &impl ExprSet,
) -> Self {
Self {
next: next_used.contains(&e),
init: init_used.contains(&e),
other: other_used.contains(&e),
}
/// analyzes expression uses to determine in what contexts the expression is used.
fn determine_simples_uses(
ctx: &Context,
sys: &TransitionSystem,
include_output: bool,
) -> impl ExprMap<Uses> {
let init = simple_count_expr_uses(ctx, sys.get_init_exprs());
let next = simple_count_expr_uses(ctx, sys.get_next_exprs());
let other = if include_output {
simple_count_expr_uses(ctx, sys.get_assert_assume_output_exprs())
} else {
simple_count_expr_uses(ctx, sys.get_assert_assume_exprs())
};

// collect all expressions
let mut all_expr = FxHashSet::from_iter(other.non_default_value_keys());
all_expr.extend(init.non_default_value_keys());
all_expr.extend(next.non_default_value_keys());

// generate combined map
let mut out = SparseExprMap::default();
for expr in all_expr.into_iter() {
out[expr] = Uses {
next: next[expr],
init: init[expr],
other: other[expr],
};
}
out
}

/// Meta-data that helps with serialization, no matter if serializing to btor, our custom
Expand All @@ -225,26 +249,14 @@ pub fn analyze_for_serialization(
sys: &TransitionSystem,
include_outputs: bool,
) -> SerializeMeta {
let init_exprs = sys.get_init_exprs();
let next_exprs = sys.get_next_exprs();
let other_exprs = if include_outputs {
sys.get_assert_assume_output_exprs()
} else {
sys.get_assert_assume_exprs()
};

// count how often the signal is used in different situations
let init_used = check_expr_use(ctx, &init_exprs);
let next_used = check_expr_use(ctx, &next_exprs);
let mut other_used = check_expr_use(ctx, &other_exprs);
let uses = determine_simples_uses(ctx, sys, include_outputs);

// always add all inputs to the signal order
// always add all inputs first to the signal order
let mut signal_order = Vec::new();
for &input in sys.inputs.iter() {
let uses = Uses::new(input, &init_used, &next_used, &other_used);
signal_order.push(RootInfo {
expr: input,
uses,
uses: uses[input],
kind: SerializeSignalKind::Input,
});
}
Expand All @@ -253,17 +265,16 @@ pub fn analyze_for_serialization(
let mut visited = DenseExprSet::default();

// add all roots and give them a large other count
let mut todo: Vec<(ExprRef, SerializeSignalKind)> =
Vec::with_capacity(init_exprs.len() + next_exprs.len() + other_exprs.len());
let mut todo: Vec<(ExprRef, SerializeSignalKind)> = vec![];
todo.extend(
sys.constraints
sys.bad_states
.iter()
.map(|&e| (e, SerializeSignalKind::Constraint)),
.map(|&e| (e, SerializeSignalKind::BadState)),
);
todo.extend(
sys.bad_states
sys.constraints
.iter()
.map(|&e| (e, SerializeSignalKind::BadState)),
.map(|&e| (e, SerializeSignalKind::Constraint)),
);
if include_outputs {
todo.extend(
Expand All @@ -272,19 +283,15 @@ pub fn analyze_for_serialization(
.map(|o| (o.expr, SerializeSignalKind::Output)),
);
}
// ensure that non-state root expressions are always serialized
for (expr, _) in todo.iter() {
other_used.insert(*expr);
}

// add state root expressions
todo.extend(
init_exprs
sys.get_init_exprs()
.iter()
.map(|&e| (e, SerializeSignalKind::StateInit)),
);
todo.extend(
next_exprs
sys.get_next_exprs()
.iter()
.map(|&e| (e, SerializeSignalKind::StateNext)),
);
Expand Down Expand Up @@ -320,20 +327,20 @@ pub fn analyze_for_serialization(
}

// add to signal order if applicable
if num_children > 0
|| matches!(
kind,
SerializeSignalKind::Input
| SerializeSignalKind::Output
| SerializeSignalKind::Constraint
| SerializeSignalKind::BadState
)
{
let uses = Uses::new(expr_ref, &init_used, &next_used, &other_used);
if uses.other {
let is_output_like = matches!(
kind,
SerializeSignalKind::Output
| SerializeSignalKind::Constraint
| SerializeSignalKind::BadState
);
let is_input = kind == SerializeSignalKind::Input;
if num_children > 0 || is_output_like || is_input {
let expr_uses = uses[expr_ref];
let used_multiple_times = uses[expr_ref].total() > 1;
if is_output_like || used_multiple_times {
signal_order.push(RootInfo {
expr: expr_ref,
uses,
uses: expr_uses,
kind,
});
}
Expand Down

0 comments on commit 74f63bc

Please sign in to comment.