1use super::{State, TransitionSystem};
7use crate::expr::*;
8use rustc_hash::FxHashSet;
9
10pub type UseCountInt = u16;
11
12pub fn count_expr_uses(ctx: &Context, sys: &TransitionSystem) -> Vec<UseCountInt> {
13 internal_count_expr_uses(ctx, sys, false)
14}
15
16fn internal_count_expr_uses(
17 ctx: &Context,
18 sys: &TransitionSystem,
19 ignore_init: bool,
20) -> Vec<UseCountInt> {
21 let mut use_count: DenseExprMetaData<UseCountInt> = DenseExprMetaData::default();
22 let states = sys.state_map();
23 let mut todo = sys.get_assert_assume_output_exprs();
25 for expr in todo.iter() {
27 use_count[*expr] = 1;
28 }
29
30 while let Some(expr) = todo.pop() {
31 if let Some(state) = states.get(&expr) {
32 if let Some(init) = state.init {
34 if !ignore_init {
35 let count = use_count[init];
36 if count == 0 {
37 use_count[init] = 1;
38 todo.push(init);
39 }
40 }
41 }
42 if let Some(next) = state.next {
43 let count = use_count[next];
44 if count == 0 {
45 use_count[next] = 1;
46 todo.push(next);
47 }
48 }
49 }
50
51 count_uses(ctx, expr, &mut use_count, &mut todo);
52 }
53
54 use_count.into_vec()
55}
56
57pub fn cone_of_influence(ctx: &Context, sys: &TransitionSystem, root: ExprRef) -> Vec<ExprRef> {
59 cone_of_influence_impl(ctx, sys, root, true, true)
61}
62
63pub fn cone_of_influence_init(
65 ctx: &Context,
66 sys: &TransitionSystem,
67 root: ExprRef,
68) -> Vec<ExprRef> {
69 cone_of_influence_impl(ctx, sys, root, false, true)
70}
71
72pub fn cone_of_influence_comb(
74 ctx: &Context,
75 sys: &TransitionSystem,
76 root: ExprRef,
77) -> Vec<ExprRef> {
78 cone_of_influence_impl(ctx, sys, root, false, false)
79}
80
81fn cone_of_influence_impl(
83 ctx: &Context,
84 sys: &TransitionSystem,
85 root: ExprRef,
86 follow_next: bool,
87 follow_init: bool,
88) -> Vec<ExprRef> {
89 let mut out = vec![];
90 let mut todo = vec![root];
91 let mut visited = DenseExprSet::default();
92 let states = sys.state_map();
93 let inputs = sys.input_set();
94
95 while let Some(expr_ref) = todo.pop() {
96 if visited.contains(&expr_ref) {
97 continue;
98 }
99
100 let expr = &ctx[expr_ref];
102 expr.for_each_child(|c| {
103 if !visited.contains(c) {
104 todo.push(*c);
105 }
106 });
107
108 if let Some(state) = states.get(&expr_ref) {
110 if follow_init {
111 if let Some(c) = state.init {
112 if !visited.contains(&c) {
113 todo.push(c);
114 }
115 }
116 }
117 if follow_next {
118 if let Some(c) = state.next {
119 if !visited.contains(&c) {
120 todo.push(c);
121 }
122 }
123 }
124 }
125
126 if expr.is_symbol() && (states.contains_key(&expr_ref) || inputs.contains(&expr_ref)) {
128 out.push(expr_ref);
129 }
130 visited.insert(expr_ref);
131 }
132
133 out
134}
135
136fn simple_count_expr_uses(ctx: &Context, roots: Vec<ExprRef>) -> impl ExprMap<UseCountInt> {
138 let mut use_count = SparseExprMap::default();
139 let mut todo = roots;
140
141 for expr in todo.iter() {
143 use_count[*expr] = 1;
144 }
145
146 while let Some(expr) = todo.pop() {
147 count_uses(ctx, expr, &mut use_count, &mut todo);
148 }
149
150 use_count
151}
152
153#[inline]
154fn count_uses(
155 ctx: &Context,
156 expr: ExprRef,
157 use_count: &mut impl ExprMap<UseCountInt>,
158 todo: &mut Vec<ExprRef>,
159) {
160 ctx[expr].for_each_child(|child| {
161 let count = use_count[*child];
162 let is_first_use = count == 0;
163 use_count[*child] = count.saturating_add(1);
164 if is_first_use {
165 todo.push(*child);
166 }
167 });
168}
169
170#[derive(Debug, Clone)]
171pub struct RootInfo {
172 pub expr: ExprRef,
173 pub name: Option<StringRef>,
174 pub uses: Uses,
175 pub kind: SerializeSignalKind,
176}
177
178impl RootInfo {
179 fn new(expr: ExprRef, name: Option<StringRef>, uses: Uses, kind: SerializeSignalKind) -> Self {
180 Self {
181 expr,
182 name,
183 uses,
184 kind,
185 }
186 }
187}
188
189#[derive(Debug, Clone, Copy, PartialEq, Eq)]
190pub enum SerializeSignalKind {
191 BadState,
192 Constraint,
193 Output,
194 Input,
195 StateInit,
196 StateNext,
197 None,
198}
199
200#[derive(Debug, Clone, Copy, Default, Eq, PartialEq)]
202pub struct Uses {
203 pub next: UseCountInt,
204 pub init: UseCountInt,
205 pub other: UseCountInt,
206}
207
208impl Uses {
209 #[inline]
210 pub fn is_used(&self) -> bool {
211 self.total() > 0
212 }
213 #[inline]
214 pub fn total(&self) -> UseCountInt {
215 self.next
216 .saturating_add(self.init)
217 .saturating_add(self.other)
218 }
219}
220
221fn determine_simples_uses(
223 ctx: &Context,
224 sys: &TransitionSystem,
225 include_output: bool,
226) -> impl ExprMap<Uses> {
227 let init = simple_count_expr_uses(ctx, sys.get_init_exprs());
228 let next = simple_count_expr_uses(ctx, sys.get_next_exprs());
229 let other = if include_output {
230 simple_count_expr_uses(ctx, sys.get_assert_assume_output_exprs())
231 } else {
232 simple_count_expr_uses(ctx, sys.get_assert_assume_exprs())
233 };
234
235 let mut all_expr = FxHashSet::from_iter(other.non_default_value_keys());
237 all_expr.extend(init.non_default_value_keys());
238 all_expr.extend(next.non_default_value_keys());
239
240 let mut out = SparseExprMap::default();
242 for expr in all_expr.into_iter() {
243 out[expr] = Uses {
244 next: next[expr],
245 init: init[expr],
246 other: other[expr],
247 };
248 }
249 out
250}
251
252#[derive(Debug, Default, Clone)]
255pub struct SerializeMeta {
256 pub signal_order: Vec<RootInfo>,
257}
258
259pub fn analyze_for_serialization(
260 ctx: &Context,
261 sys: &TransitionSystem,
262 include_outputs: bool,
263) -> SerializeMeta {
264 let uses = determine_simples_uses(ctx, sys, include_outputs);
265
266 let mut signal_order = Vec::new();
268 for &input in sys.inputs.iter() {
269 signal_order.push(RootInfo::new(
270 input,
271 Some(ctx[input].get_symbol_name_ref().unwrap()),
272 uses[input],
273 SerializeSignalKind::Input,
274 ));
275 }
276
277 let mut visited = DenseExprSet::default();
279
280 let mut todo: Vec<RootInfo> = vec![];
282 if include_outputs {
283 todo.extend(sys.outputs.iter().map(|o| {
284 RootInfo::new(
285 o.expr,
286 Some(o.name),
287 uses[o.expr],
288 SerializeSignalKind::Output,
289 )
290 }));
291 }
292 todo.extend(sys.constraints.iter().map(|&e| {
293 RootInfo::new(
294 e,
295 find_name(ctx, sys, e),
296 uses[e],
297 SerializeSignalKind::Constraint,
298 )
299 }));
300 todo.extend(sys.bad_states.iter().map(|&e| {
301 RootInfo::new(
302 e,
303 find_name(ctx, sys, e),
304 uses[e],
305 SerializeSignalKind::BadState,
306 )
307 }));
308
309 todo.extend(sys.get_init_exprs().iter().map(|&e| {
311 RootInfo::new(
312 e,
313 find_name(ctx, sys, e),
314 uses[e],
315 SerializeSignalKind::StateInit,
316 )
317 }));
318 todo.extend(sys.get_next_exprs().iter().map(|&e| {
319 RootInfo::new(
320 e,
321 find_name(ctx, sys, e),
322 uses[e],
323 SerializeSignalKind::StateNext,
324 )
325 }));
326
327 todo.reverse();
329
330 while let Some(info) = todo.pop() {
332 if visited.contains(&info.expr) {
333 continue;
334 }
335
336 let expr = &ctx[info.expr];
337
338 let mut all_done = true;
340 let mut num_children = 0;
341 expr.for_each_child(|c| {
342 if !visited.contains(c) {
343 if all_done {
344 todo.push(info.clone()); }
346 all_done = false;
347 let child_info = RootInfo::new(*c, None, uses[*c], SerializeSignalKind::None);
349 todo.push(child_info);
350 }
351 num_children += 1;
352 });
353
354 if !all_done {
355 continue;
356 }
357
358 let is_output_like = matches!(
360 info.kind,
361 SerializeSignalKind::Output
362 | SerializeSignalKind::Constraint
363 | SerializeSignalKind::BadState
364 );
365 let is_input = info.kind == SerializeSignalKind::Input;
366 if num_children > 0 || is_output_like || is_input {
367 let used_multiple_times = info.uses.total() > 1;
368 if is_output_like || used_multiple_times {
369 signal_order.push(info.clone());
370 }
371 }
372 visited.insert(info.expr);
373 }
374
375 SerializeMeta { signal_order }
376}
377
378#[inline]
379fn find_name(ctx: &Context, sys: &TransitionSystem, e: ExprRef) -> Option<StringRef> {
380 ctx[e].get_symbol_name_ref().or_else(|| sys.names[e])
381}
382
383impl ForEachChild<ExprRef> for State {
384 fn for_each_child(&self, mut visitor: impl FnMut(&ExprRef)) {
385 if let Some(next) = &self.next {
386 visitor(next);
387 }
388 if let Some(init) = &self.init {
389 visitor(init);
390 }
391 }
392
393 fn num_children(&self) -> usize {
394 self.next.is_some() as usize + self.init.is_some() as usize
395 }
396}
397
398#[cfg(test)]
399mod tests {
400 use super::*;
401 use crate::btor2;
402
403 fn format_symbol_list(ctx: &Context, symbols: &[ExprRef]) -> String {
404 let v: Vec<_> = symbols
405 .iter()
406 .map(|s| ctx.get_symbol_name(*s).unwrap())
407 .collect();
408 v.join(", ")
409 }
410
411 #[test]
412 fn test_cone_of_influence() {
413 let (ctx, sys) = btor2::parse_file("../inputs/unittest/delay.btor").unwrap();
414 let reg0 = sys.get_state_by_name(&ctx, "reg0").unwrap().symbol;
415 let reg1 = sys.get_state_by_name(&ctx, "reg1").unwrap().symbol;
416 let cone0 = cone_of_influence(&ctx, &sys, reg0);
417 let cone1 = cone_of_influence(&ctx, &sys, reg1);
418 insta::assert_snapshot!(format_symbol_list(&ctx, &cone0));
419 insta::assert_snapshot!(format_symbol_list(&ctx, &cone1));
420 let cone2 = cone_of_influence_init(&ctx, &sys, reg0);
421 assert_eq!(cone2, [reg0], "reg0 is initialized to zero. {:?}", cone2);
422 let cone3 = cone_of_influence_init(&ctx, &sys, reg1);
423 assert_eq!(cone3, [reg1], "reg1 is initialized to zero. {:?}", cone3);
424 }
425}