1use super::dist::{DefaultWeight, Dist, Item, SELECTOR_BASE, Span};
39use super::func::{self, Func, FuncError};
40use super::guard::{self, GuardValue};
41use super::ir::{
42 ArrayId, ArrayNode, Assignment, BinOp, ConstraintEvalError, Expr, LoopId, ReduceOp, Ty, UnOp,
43 VarId, eval, mask,
44};
45use super::solve::{SolveError, Solver};
46use num_bigint::BigUint;
47use num_traits::{One, Zero};
48use std::collections::HashMap;
49
50pub const SIZE_TIERS: [u64; 2] = [1024, 65536];
52
53pub const MAX_SIZE_RETRIES: usize = 256;
56
57#[derive(Clone, Debug)]
59pub struct ArrayState {
60 pub elem_ty: Ty,
62 pub active: bool,
65 pub cur: Vec<Option<BigUint>>,
67 pub domain: Option<Vec<BigUint>>,
69}
70
71#[derive(Clone, Debug, PartialEq, Eq)]
73pub enum ExpandError {
74 IndexOutOfRange {
76 array: ArrayId,
78 index: i64,
80 size: usize,
82 },
83 LeafIndexOutOfRange {
86 index: i64,
88 size: usize,
90 },
91 NonConstantIndex,
94 GuardError,
96 StateUnknown(u32),
98 Func(FuncError),
100 DistRandomBound,
104 Malformed(&'static str),
106}
107
108#[derive(Clone, Debug, Default)]
110pub struct Expanded {
111 pub conjuncts: Vec<Expr>,
114 pub elem_vars: Vec<(ArrayId, usize)>,
116 pub elem_tys: Vec<Ty>,
118 pub extra_states: Vec<Option<BigUint>>,
121 pub extra_dists: Vec<Dist>,
126}
127
128struct Expander<'a> {
129 arrays: &'a [ArrayState],
130 sizes: &'a [usize],
131 elem_base: Vec<u32>,
133 asg: Assignment,
134 synth: HashMap<(ArrayId, usize), u32>,
135 env: Vec<(LoopId, Expr)>,
136 funcs: &'a [Func],
137 dists: &'a [Dist],
139 extra_dists: Vec<Dist>,
140 instanced: Vec<InstancedDist>,
145}
146
147type InstancedDist = (usize, Vec<(LoopId, Expr)>, usize);
149
150fn konst(t: Ty, v: u64) -> Expr {
151 Expr::constant(t, v)
152}
153
154fn has_vars(e: &Expr) -> bool {
155 let mut any = false;
156 e.for_each_var(&mut |_, _| any = true);
157 any
158}
159
160impl Expander<'_> {
161 fn array(&self, a: ArrayId) -> Result<&ArrayState, ExpandError> {
162 self.arrays.get(a as usize).ok_or(ExpandError::Malformed(
163 "an array id outside the program's table",
164 ))
165 }
166
167 fn size_of(&self, a: ArrayId) -> Result<usize, ExpandError> {
168 self.sizes
169 .get(a as usize)
170 .copied()
171 .ok_or(ExpandError::Malformed("no size for an array"))
172 }
173
174 fn const_value(&self, e: &Expr) -> Result<BigUint, ExpandError> {
176 if has_vars(e) {
177 return Err(ExpandError::NonConstantIndex);
178 }
179 eval(e, &self.asg).map_err(|err| match err {
180 ConstraintEvalError::StateUnknown(s) => ExpandError::StateUnknown(s),
181 _ => ExpandError::NonConstantIndex,
182 })
183 }
184
185 fn index_value(&mut self, index: &Expr) -> Result<i64, ExpandError> {
188 let idx = self.expand(index)?;
189 let v = self.const_value(&idx)?;
190 let t = idx.ty();
191 Ok(if t.signed && v.bit(u64::from(t.width) - 1) {
192 -i64::try_from((BigUint::one() << t.width) - &v).unwrap_or(i64::MAX)
193 } else {
194 i64::try_from(&v).unwrap_or(i64::MAX)
195 })
196 }
197
198 fn element(&mut self, a: ArrayId, idx: usize, ty: Ty) -> Result<Expr, ExpandError> {
201 let arr = self.array(a)?;
202 if arr.elem_ty != ty {
203 return Err(ExpandError::Malformed("an element read at the wrong type"));
204 }
205 let (active, cur) = (arr.active, arr.cur.get(idx).cloned());
206 if active {
207 let id = self.elem_base[a as usize] + u32::try_from(idx).expect("an element index");
208 return Ok(Expr::Var(id, ty));
209 }
210 let Some(cur) = cur else {
211 return Err(ExpandError::Malformed("an element of a stale array view"));
212 };
213 if let Some(&id) = self.synth.get(&(a, idx)) {
214 return Ok(Expr::State(id, ty));
215 }
216 let id = u32::try_from(self.asg.states.len()).expect("a state id");
217 self.asg.states.push(cur);
218 self.synth.insert((a, idx), id);
219 Ok(Expr::State(id, ty))
220 }
221
222 fn lookup(&self, id: LoopId) -> Result<Expr, ExpandError> {
223 self.env
224 .iter()
225 .rev()
226 .find(|(l, _)| *l == id)
227 .map(|(_, e)| e.clone())
228 .ok_or(ExpandError::Malformed("a loop variable outside its loop"))
229 }
230
231 fn expand(&mut self, e: &Expr) -> Result<Expr, ExpandError> {
232 match e {
233 Expr::Guard(p, then, otherwise, t) => {
234 let p = self.expand(p)?;
235 match guard::value(&p, &self.asg) {
236 GuardValue::True => self.expand(then),
237 GuardValue::False => self.expand(otherwise),
238 GuardValue::Error => Err(ExpandError::GuardError),
239 GuardValue::Random => Ok(Expr::Guard(
240 Box::new(p),
241 Box::new(self.expand(then)?),
242 Box::new(self.expand(otherwise)?),
243 *t,
244 )),
245 }
246 }
247 Expr::Ite(c, x, y, t) => {
248 let c = self.expand(c)?;
249 if !has_vars(&c)
250 && let Ok(v) = eval(&c, &self.asg)
251 {
252 return if v.is_zero() {
253 self.expand(y)
254 } else {
255 self.expand(x)
256 };
257 }
258 Ok(Expr::Ite(
259 Box::new(c),
260 Box::new(self.expand(x)?),
261 Box::new(self.expand(y)?),
262 *t,
263 ))
264 }
265 Expr::Binary(op @ (BinOp::LogAnd | BinOp::LogOr | BinOp::LogImpl), l, r, t) => {
266 let l = self.expand(l)?;
267 if !has_vars(&l)
268 && let Ok(v) = eval(&l, &self.asg)
269 {
270 let l_true = !v.is_zero();
271 let decided = match op {
272 BinOp::LogAnd | BinOp::LogImpl => !l_true,
273 _ => l_true,
274 };
275 if decided {
276 return Ok(konst(*t, u64::from(*op != BinOp::LogAnd)));
277 }
278 }
279 Ok(Expr::Binary(
280 *op,
281 Box::new(l),
282 Box::new(self.expand(r)?),
283 *t,
284 ))
285 }
286 Expr::Array(n) => self.array_node(n),
287 Expr::Var(v, t) if *v >= SELECTOR_BASE => {
288 let j = (*v - SELECTOR_BASE) as usize;
289 match self.dists.get(j) {
290 Some(d) if d.subject.is_some() => {
291 let k = match self
292 .instanced
293 .iter()
294 .find(|(tj, env, _)| *tj == j && *env == self.env)
295 {
296 Some(&(_, _, k)) => k,
297 None => {
298 let inst = self.instantiate(d)?;
299 self.extra_dists.push(inst);
300 let k = self.extra_dists.len() - 1;
301 self.instanced.push((j, self.env.clone(), k));
302 k
303 }
304 };
305 Ok(Expr::Var(SELECTOR_BASE + (self.dists.len() + k) as u32, *t))
306 }
307 _ => Ok(e.clone()),
308 }
309 }
310 other => other.try_map_children(&mut |c| self.expand(c)),
311 }
312 }
313
314 fn instantiate(&mut self, d: &Dist) -> Result<Dist, ExpandError> {
319 let Some(subject) = &d.subject else {
320 return Err(ExpandError::Malformed("an instance of a plain dist"));
321 };
322 let subject = self.expand(subject)?;
323 let mut vars: Vec<VarId> = Vec::new();
324 subject.for_each_var(&mut |v, _| vars.push(v));
325 vars.sort_unstable();
326 vars.dedup();
327 let plain_var = match &subject {
328 Expr::Var(v, _) => Some(*v),
329 _ => None,
330 };
331 let fixed = |this: &mut Self, e: &Expr| -> Result<Expr, ExpandError> {
332 let x = this.expand(e)?;
333 if has_vars(&x) {
334 return Err(ExpandError::DistRandomBound);
335 }
336 Ok(x)
337 };
338 let mut items = Vec::with_capacity(d.items.len());
339 for it in &d.items {
340 let span = match &it.span {
341 Span::Point(v) => Span::Point(fixed(self, v)?),
342 Span::Range { lo, hi } => Span::Range {
343 lo: lo.as_ref().map(|b| fixed(self, b)).transpose()?,
344 hi: hi.as_ref().map(|b| fixed(self, b)).transpose()?,
345 },
346 };
347 items.push(Item {
348 member: konst(Ty::BIT, 1),
349 ty: it.ty,
350 span,
351 weight: fixed(self, &it.weight)?,
352 per_range: it.per_range,
353 });
354 }
355 let default = match &d.default {
356 Some(w) => Some(DefaultWeight {
357 weight: fixed(self, &w.weight)?,
358 per_range: w.per_range,
359 }),
360 None => None,
361 };
362 Ok(Dist {
363 subject_ty: d.subject_ty,
364 plain_var,
365 vars,
366 items,
367 default,
368 subject: None,
369 })
370 }
371
372 fn array_node(&mut self, n: &ArrayNode) -> Result<Expr, ExpandError> {
373 match n {
374 ArrayNode::Call { func, args, ty } => {
375 let args = args
376 .iter()
377 .map(|a| self.expand(a))
378 .collect::<Result<Vec<_>, _>>()?;
379 let call = Expr::Array(ArrayNode::Call {
380 func: *func,
381 args,
382 ty: *ty,
383 });
384 func::fold_ground_calls(&call, self.funcs, &self.asg.states).map_err(|e| match e {
387 FuncError::Eval(ConstraintEvalError::StateUnknown(s)) => {
388 ExpandError::StateUnknown(s)
389 }
390 other => ExpandError::Func(other),
391 })
392 }
393 ArrayNode::Size(a) => Ok(konst(Ty::signed(32), self.size_of(*a)? as u64)),
394 ArrayNode::Loop(id, t) => {
395 let v = self.lookup(*id)?;
396 if v.ty() != *t {
397 return Err(ExpandError::Malformed(
398 "a loop variable bound at another type",
399 ));
400 }
401 Ok(v)
402 }
403 ArrayNode::Pick {
404 index,
405 low,
406 elems,
407 ty,
408 } => {
409 let i = self.index_value(index)?;
410 let pos = i
411 .checked_sub(*low)
412 .and_then(|p| usize::try_from(p).ok())
413 .filter(|&p| p < elems.len());
414 let Some(pos) = pos else {
415 return Err(ExpandError::LeafIndexOutOfRange {
416 index: i,
417 size: elems.len(),
418 });
419 };
420 let x = self.expand(&elems[pos])?;
421 if x.ty() != *ty {
422 return Err(ExpandError::Malformed("a pick element of another type"));
423 }
424 Ok(x)
425 }
426 ArrayNode::Elem { array, index, ty } => {
427 let i = self.index_value(index)?;
428 let size = self.size_of(*array)?;
429 if i < 0 || i as usize >= size {
430 return Err(ExpandError::IndexOutOfRange {
431 array: *array,
432 index: i,
433 size,
434 });
435 }
436 self.element(*array, i as usize, *ty)
437 }
438 ArrayNode::Distinct { scalars, arrays } => {
439 let mut members = Vec::new();
440 for s in scalars {
441 members.push(self.expand(s)?);
442 }
443 for a in arrays {
444 let size = self.size_of(*a)?;
445 let elem_ty = self.array(*a)?.elem_ty;
446 for i in 0..size {
447 members.push(self.element(*a, i, elem_ty)?);
448 }
449 }
450 if members.iter().any(|m| m.ty() != members[0].ty()) {
451 return Err(ExpandError::Malformed(
452 "the members of a unique group are of different types",
453 ));
454 }
455 Ok(if members.len() < 2 {
456 konst(Ty::BIT, 1)
457 } else {
458 Expr::AllDistinct(members)
459 })
460 }
461 ArrayNode::Foreach { array, var, body } => {
462 let size = self.size_of(*array)?;
463 let mut acc: Option<Expr> = None;
464 for i in 0..size {
465 self.env.push((*var, konst(Ty::signed(32), i as u64)));
466 let b = self.expand(body);
467 self.env.pop();
468 let b = b?;
469 acc = Some(match acc {
470 None => b,
471 Some(a) => Expr::Binary(BinOp::LogAnd, Box::new(a), Box::new(b), Ty::BIT),
472 });
473 }
474 Ok(acc.unwrap_or_else(|| konst(Ty::BIT, 1)))
475 }
476 ArrayNode::Reduce {
477 op,
478 array,
479 var,
480 index_var,
481 body,
482 } => {
483 let size = self.size_of(*array)?;
484 let elem_ty = self.array(*array)?.elem_ty;
485 let ty = body.ty();
486 let mut acc: Option<Expr> = None;
487 for i in 0..size {
488 let item = self.element(*array, i, elem_ty)?;
489 self.env.push((*var, item));
490 self.env.push((*index_var, konst(Ty::signed(32), i as u64)));
491 let b = self.expand(body);
492 self.env.pop();
493 self.env.pop();
494 let b = b?;
495 acc = Some(match acc {
496 None => b,
497 Some(a) => {
498 let bop = match op {
499 ReduceOp::Sum => BinOp::Add,
500 ReduceOp::Product => BinOp::Mul,
501 ReduceOp::And => BinOp::And,
502 ReduceOp::Or => BinOp::Or,
503 ReduceOp::Xor => BinOp::Xor,
504 };
505 Expr::Binary(bop, Box::new(a), Box::new(b), ty)
506 }
507 });
508 }
509 Ok(acc.unwrap_or_else(|| {
510 Expr::Const(
511 ty,
512 match op {
513 ReduceOp::Product => BigUint::one(),
514 ReduceOp::And => mask(ty.width),
515 ReduceOp::Sum | ReduceOp::Or | ReduceOp::Xor => BigUint::zero(),
516 },
517 )
518 }))
519 }
520 }
521 }
522}
523
524fn domain_constraint(v: Expr, domain: &[BigUint]) -> Expr {
526 let t = v.ty();
527 let mut acc: Option<Expr> = None;
528 for d in domain {
529 let eq = Expr::Binary(
530 BinOp::Eq,
531 Box::new(v.clone()),
532 Box::new(Expr::Const(t, d.clone() & mask(t.width))),
533 Ty::BIT,
534 );
535 acc = Some(match acc {
536 None => eq,
537 Some(a) => Expr::Binary(BinOp::LogOr, Box::new(a), Box::new(eq), Ty::BIT),
538 });
539 }
540 acc.unwrap_or_else(|| konst(Ty::BIT, 0))
541}
542
543pub fn expand(
549 conjuncts: &[Expr],
550 arrays: &[ArrayState],
551 sizes: &[usize],
552 n_vars0: u32,
553 states: &[Option<BigUint>],
554) -> Result<Expanded, ExpandError> {
555 expand_with(conjuncts, arrays, sizes, n_vars0, states, &[], &[])
556}
557
558pub fn expand_with(
563 conjuncts: &[Expr],
564 arrays: &[ArrayState],
565 sizes: &[usize],
566 n_vars0: u32,
567 states: &[Option<BigUint>],
568 funcs: &[Func],
569 dists: &[Dist],
570) -> Result<Expanded, ExpandError> {
571 if arrays.len() != sizes.len() {
572 return Err(ExpandError::Malformed("one size per array"));
573 }
574 let mut out = Expanded::default();
575 let mut elem_base = vec![0u32; arrays.len()];
576 let mut next = n_vars0;
577 for (a, arr) in arrays.iter().enumerate() {
578 if !arr.active {
579 continue;
580 }
581 elem_base[a] = next;
582 for i in 0..sizes[a] {
583 out.elem_vars.push((a as ArrayId, i));
584 out.elem_tys.push(arr.elem_ty);
585 next += 1;
586 if let Some(dom) = &arr.domain {
587 let id = elem_base[a] + i as u32;
588 out.conjuncts
589 .push(domain_constraint(Expr::Var(id, arr.elem_ty), dom));
590 }
591 }
592 }
593 let mut ex = Expander {
594 arrays,
595 sizes,
596 elem_base,
597 asg: Assignment {
598 vars: Vec::new(),
599 states: states.to_vec(),
600 },
601 synth: HashMap::new(),
602 env: Vec::new(),
603 funcs,
604 dists,
605 extra_dists: Vec::new(),
606 instanced: Vec::new(),
607 };
608 for c in conjuncts {
609 let e = ex.expand(c)?;
610 out.conjuncts.push(e);
611 }
612 out.extra_dists = std::mem::take(&mut ex.extra_dists);
613 out.extra_states = ex.asg.states.split_off(states.len());
614 Ok(out)
615}
616
617#[derive(Clone, Debug, PartialEq, Eq)]
619pub enum SizeError {
620 Unsatisfiable,
622 TooLarge,
624 GuardError,
626 Solve(SolveError),
628}
629
630#[derive(Clone, Debug, PartialEq, Eq)]
632pub struct SizeSolution {
633 pub sizes: Vec<usize>,
635 pub sized: Vec<ArrayId>,
637 pub tuple: Vec<usize>,
639}
640
641fn size_eligible(c: &Expr) -> bool {
644 !c.any_node(&mut |n| match n {
645 Expr::Array(
646 ArrayNode::Elem { .. }
647 | ArrayNode::Loop(..)
648 | ArrayNode::Foreach { .. }
649 | ArrayNode::Reduce { .. }
650 | ArrayNode::Distinct { .. }
651 | ArrayNode::Pick { .. }
653 | ArrayNode::Call { .. },
656 ) => true,
657 Expr::Var(v, _) => *v >= super::dist::SELECTOR_BASE,
658 _ => false,
659 })
660}
661
662fn replace_sizes(e: &Expr, f: &mut impl FnMut(ArrayId) -> Expr) -> Expr {
663 match e {
664 Expr::Array(ArrayNode::Size(a)) => f(*a),
665 other => other
666 .try_map_children::<()>(&mut |c| Ok(replace_sizes(c, f)))
667 .expect("infallible"),
668 }
669}
670
671fn find(parent: &mut [usize], x: usize) -> usize {
672 let mut r = x;
673 while parent[r] != r {
674 r = parent[r];
675 }
676 let mut c = x;
677 while parent[c] != r {
678 let n = parent[c];
679 parent[c] = r;
680 c = n;
681 }
682 r
683}
684
685pub fn solve_sizes(
691 conjuncts: &[Expr],
692 arrays: &[ArrayState],
693 var_tys: &[Ty],
694 states: &[Option<BigUint>],
695 blocked: &[Vec<usize>],
696 seed: &mut u64,
697) -> Result<SizeSolution, SizeError> {
698 let current: Vec<usize> = arrays.iter().map(|a| a.cur.len()).collect();
699 let n_fields = var_tys.len() as u32;
700 let mut sized: Vec<ArrayId> = Vec::new();
702 let mut eligible: Vec<&Expr> = Vec::new();
703 for c in conjuncts {
704 if !size_eligible(c) {
705 continue;
706 }
707 c.any_node(&mut |n| {
708 if let Expr::Array(ArrayNode::Size(a)) = n
709 && arrays.get(*a as usize).is_some_and(|x| x.active)
710 && !sized.contains(a)
711 {
712 sized.push(*a);
713 }
714 false
715 });
716 eligible.push(c);
717 }
718 if sized.is_empty() {
719 return Ok(SizeSolution {
720 sizes: current,
721 sized,
722 tuple: Vec::new(),
723 });
724 }
725 sized.sort_unstable();
726 let size_var = |a: ArrayId| -> u32 {
727 n_fields + u32::try_from(sized.iter().position(|x| *x == a).expect("sized")).unwrap()
728 };
729 let total = n_fields as usize + sized.len();
731 let subbed: Vec<Expr> = eligible
732 .iter()
733 .map(|c| {
734 replace_sizes(c, &mut |a| {
735 if arrays[a as usize].active {
736 Expr::Var(size_var(a), Ty::signed(32))
737 } else {
738 konst(Ty::signed(32), current[a as usize] as u64)
739 }
740 })
741 })
742 .collect();
743 let mut parent: Vec<usize> = (0..total).collect();
744 let vars_of: Vec<Vec<VarId>> = subbed
745 .iter()
746 .map(|c| {
747 let mut v = Vec::new();
748 c.for_each_var(&mut |x, _| v.push(x));
749 v
750 })
751 .collect();
752 for vs in &vars_of {
753 for w in vs.windows(2) {
754 let (a, b) = (
755 find(&mut parent, w[0] as usize),
756 find(&mut parent, w[1] as usize),
757 );
758 parent[a] = b;
759 }
760 }
761 let size_roots: Vec<usize> = (0..sized.len())
762 .map(|k| find(&mut parent, n_fields as usize + k))
763 .collect();
764 let mut keep: Vec<Expr> = Vec::new();
765 let mut used = vec![false; total];
766 for k in 0..sized.len() {
767 used[n_fields as usize + k] = true;
768 }
769 for (c, vs) in subbed.iter().zip(&vars_of) {
770 let Some(&v0) = vs.first() else { continue };
771 let root = find(&mut parent, v0 as usize);
772 if size_roots.contains(&root) {
773 for &v in vs {
774 used[v as usize] = true;
775 }
776 keep.push(c.clone());
777 }
778 }
779 let mut new_id = vec![0u32; total];
781 let mut tys: Vec<Ty> = Vec::new();
782 for (v, u) in used.iter().enumerate() {
783 if *u {
784 new_id[v] = tys.len() as u32;
785 tys.push(if v < n_fields as usize {
786 var_tys[v]
787 } else {
788 Ty::signed(32)
789 });
790 }
791 }
792 let mut keep: Vec<Expr> = keep
793 .iter()
794 .map(|c| c.substitute_vars(&mut |v, t| Expr::Var(new_id[v as usize], t)))
795 .collect();
796 let svar = |k: usize| Expr::Var(new_id[n_fields as usize + k], Ty::signed(32));
797 for tup in blocked {
799 let mut same: Option<Expr> = None;
800 for (k, &sz) in tup.iter().enumerate() {
801 let eq = Expr::Binary(
802 BinOp::Eq,
803 Box::new(svar(k)),
804 Box::new(konst(Ty::signed(32), sz as u64)),
805 Ty::BIT,
806 );
807 same = Some(match same {
808 None => eq,
809 Some(a) => Expr::Binary(BinOp::LogAnd, Box::new(a), Box::new(eq), Ty::BIT),
810 });
811 }
812 if let Some(s) = same {
813 keep.push(Expr::Unary(UnOp::LogNot, Box::new(s), Ty::BIT));
814 }
815 }
816 let assignment = Assignment {
817 vars: Vec::new(),
818 states: states.to_vec(),
819 };
820 let order: Vec<VarId> = (0..tys.len() as u32).collect();
821 let tiers: [u64; 3] = [SIZE_TIERS[0], SIZE_TIERS[1], i32::MAX as u64];
822 for (ti, bound) in tiers.iter().enumerate() {
823 let mut cs = keep.clone();
824 for k in 0..sized.len() {
825 cs.push(Expr::Binary(
826 BinOp::Ge,
827 Box::new(svar(k)),
828 Box::new(konst(Ty::signed(32), 0)),
829 Ty::BIT,
830 ));
831 cs.push(Expr::Binary(
832 BinOp::Le,
833 Box::new(svar(k)),
834 Box::new(konst(Ty::signed(32), *bound)),
835 Ty::BIT,
836 ));
837 }
838 let mut resolved = Vec::with_capacity(cs.len());
839 for c in &cs {
840 resolved.push(guard::resolve(c, &assignment).map_err(|_| SizeError::GuardError)?);
841 }
842 let solver = match Solver::new(&tys, states, &resolved) {
843 Ok(s) => s,
844 Err(SolveError::Unsatisfiable) => continue,
845 Err(e) => return Err(SizeError::Solve(e)),
846 };
847 let values = match solver.sample(&order, seed) {
848 Ok(v) => v,
849 Err(SolveError::Unsatisfiable) => continue,
850 Err(e) => return Err(SizeError::Solve(e)),
851 };
852 if ti == 2 {
853 return Err(SizeError::TooLarge);
854 }
855 let mut sizes = current;
856 let mut tuple = Vec::with_capacity(sized.len());
857 for (k, &a) in sized.iter().enumerate() {
858 let v = &values[new_id[n_fields as usize + k] as usize];
859 let n = usize::try_from(v).expect("a bounded size");
860 sizes[a as usize] = n;
861 tuple.push(n);
862 }
863 return Ok(SizeSolution {
864 sizes,
865 sized,
866 tuple,
867 });
868 }
869 Err(SizeError::Unsatisfiable)
870}
871
872#[cfg(test)]
873mod tests {
874 use super::*;
875
876 fn s32() -> Ty {
877 Ty::signed(32)
878 }
879 fn u(w: u32) -> Ty {
880 Ty::unsigned(w)
881 }
882 fn size(a: ArrayId) -> Expr {
883 Expr::Array(ArrayNode::Size(a))
884 }
885 fn elem(a: ArrayId, i: Expr, t: Ty) -> Expr {
886 Expr::Array(ArrayNode::Elem {
887 array: a,
888 index: Box::new(i),
889 ty: t,
890 })
891 }
892 fn lp(id: LoopId) -> Expr {
893 Expr::Array(ArrayNode::Loop(id, s32()))
894 }
895 fn bin(op: BinOp, a: Expr, b: Expr, t: Ty) -> Expr {
896 Expr::Binary(op, Box::new(a), Box::new(b), t)
897 }
898 fn arr(active: bool, cur: &[u64]) -> ArrayState {
899 ArrayState {
900 elem_ty: s32(),
901 active,
902 cur: cur.iter().map(|v| Some(BigUint::from(*v))).collect(),
903 domain: None,
904 }
905 }
906 fn ev(e: &Expr, vars: &[u64], states: &[Option<BigUint>]) -> bool {
907 let a = Assignment {
908 vars: vars.iter().map(|v| BigUint::from(*v)).collect(),
909 states: states.to_vec(),
910 };
911 !eval(e, &a).expect("evaluates").is_zero()
912 }
913
914 #[test]
917 fn foreach_unrolls_over_the_size() {
918 let body = bin(
919 BinOp::Gt,
920 elem(0, lp(0), s32()),
921 bin(BinOp::Mul, konst(s32(), 2), lp(0), s32()),
922 Ty::BIT,
923 );
924 let c = Expr::Array(ArrayNode::Foreach {
925 array: 0,
926 var: 0,
927 body: Box::new(body),
928 });
929 let x = expand(&[c], &[arr(true, &[])], &[3], 0, &[]).expect("expands");
930 assert_eq!(x.elem_vars, vec![(0, 0), (0, 1), (0, 2)]);
931 assert_eq!(x.conjuncts.len(), 1);
932 assert!(ev(&x.conjuncts[0], &[1, 3, 5], &[]));
933 assert!(!ev(&x.conjuncts[0], &[1, 2, 5], &[]));
934 assert!(!ev(&x.conjuncts[0], &[0, 3, 5], &[]));
935 }
936
937 #[test]
940 fn a_constant_guard_keeps_the_last_neighbour_unread() {
941 let pred = bin(
942 BinOp::Lt,
943 lp(0),
944 bin(BinOp::Sub, size(0), konst(s32(), 1), s32()),
945 Ty::BIT,
946 );
947 let then = bin(
948 BinOp::Lt,
949 elem(0, lp(0), s32()),
950 elem(0, bin(BinOp::Add, lp(0), konst(s32(), 1), s32()), s32()),
951 Ty::BIT,
952 );
953 let body = Expr::Guard(
954 Box::new(pred),
955 Box::new(then),
956 Box::new(konst(Ty::BIT, 1)),
957 Ty::BIT,
958 );
959 let c = Expr::Array(ArrayNode::Foreach {
960 array: 0,
961 var: 0,
962 body: Box::new(body),
963 });
964 let x = expand(&[c], &[arr(true, &[])], &[4], 0, &[]).expect("expands");
965 assert_eq!(x.elem_vars.len(), 4);
966 assert!(ev(&x.conjuncts[0], &[1, 2, 3, 4], &[]));
967 assert!(!ev(&x.conjuncts[0], &[1, 3, 2, 4], &[]));
968 }
969
970 #[test]
971 fn an_unguarded_out_of_range_index_is_a_loud_error() {
972 let c = bin(
973 BinOp::Eq,
974 elem(0, konst(s32(), 5), s32()),
975 konst(s32(), 0),
976 Ty::BIT,
977 );
978 assert_eq!(
979 expand(&[c], &[arr(true, &[])], &[2], 0, &[]).unwrap_err(),
980 ExpandError::IndexOutOfRange {
981 array: 0,
982 index: 5,
983 size: 2
984 }
985 );
986 }
987
988 #[test]
991 fn a_pick_selects_the_element_its_index_names() {
992 let pick = |idx: Expr| {
993 Expr::Array(ArrayNode::Pick {
994 index: Box::new(idx),
995 low: 2,
996 elems: vec![Expr::Var(0, u(8)), Expr::Var(1, u(8)), Expr::Var(2, u(8))],
997 ty: u(8),
998 })
999 };
1000 let states = [Some(BigUint::from(3u32))];
1001 let c = bin(
1002 BinOp::Eq,
1003 pick(Expr::State(0, s32())),
1004 konst(u(8), 9),
1005 Ty::BIT,
1006 );
1007 let x = expand(&[c], &[], &[], 3, &states).expect("expands");
1008 assert_eq!(x.conjuncts.len(), 1);
1009 assert!(ev(&x.conjuncts[0], &[0, 9, 0], &states));
1010 assert!(!ev(&x.conjuncts[0], &[9, 0, 9], &states));
1011 }
1012
1013 #[test]
1017 fn an_out_of_range_pick_is_an_error_only_when_live() {
1018 let pick = Expr::Array(ArrayNode::Pick {
1019 index: Box::new(Expr::State(0, s32())),
1020 low: 0,
1021 elems: vec![Expr::Var(0, u(8)), Expr::Var(1, u(8))],
1022 ty: u(8),
1023 });
1024 let body = bin(BinOp::Eq, pick, konst(u(8), 1), Ty::BIT);
1025 let guarded = Expr::Guard(
1026 Box::new(bin(
1027 BinOp::Lt,
1028 Expr::State(0, s32()),
1029 konst(s32(), 2),
1030 Ty::BIT,
1031 )),
1032 Box::new(body.clone()),
1033 Box::new(konst(Ty::BIT, 1)),
1034 Ty::BIT,
1035 );
1036 let out = [Some(BigUint::from(7u32))];
1037 let x = expand(&[guarded], &[], &[], 2, &out).expect("the guard drops the pick");
1038 assert!(ev(&x.conjuncts[0], &[], &out));
1039 assert_eq!(
1040 expand(std::slice::from_ref(&body), &[], &[], 2, &out).unwrap_err(),
1041 ExpandError::LeafIndexOutOfRange { index: 7, size: 2 }
1042 );
1043 let neg = [Some((BigUint::from(1u32) << 32) - 1u32)];
1045 assert_eq!(
1046 expand(&[body], &[], &[], 2, &neg).unwrap_err(),
1047 ExpandError::LeafIndexOutOfRange { index: -1, size: 2 }
1048 );
1049 }
1050
1051 #[test]
1054 fn an_inactive_array_is_state() {
1055 let c = bin(
1056 BinOp::Eq,
1057 elem(0, konst(s32(), 1), s32()),
1058 konst(s32(), 7),
1059 Ty::BIT,
1060 );
1061 let x = expand(&[c], &[arr(false, &[3, 7])], &[2], 0, &[]).expect("expands");
1062 assert!(x.elem_vars.is_empty());
1063 assert_eq!(x.extra_states, vec![Some(BigUint::from(7u32))]);
1064 assert!(ev(&x.conjuncts[0], &[], &x.extra_states));
1065 }
1066
1067 #[test]
1068 fn reductions_fold_and_empty_ones_are_the_identity() {
1069 let sum = |op| {
1070 Expr::Array(ArrayNode::Reduce {
1071 op,
1072 array: 0,
1073 var: 0,
1074 index_var: 1,
1075 body: Box::new(Expr::Array(ArrayNode::Loop(0, s32()))),
1076 })
1077 };
1078 let c = bin(BinOp::Eq, sum(ReduceOp::Sum), konst(s32(), 6), Ty::BIT);
1079 let x = expand(&[c], &[arr(true, &[])], &[3], 0, &[]).expect("expands");
1080 assert!(ev(&x.conjuncts[0], &[1, 2, 3], &[]));
1081 assert!(!ev(&x.conjuncts[0], &[1, 2, 4], &[]));
1082 for (op, want) in [
1083 (ReduceOp::Sum, 0u64),
1084 (ReduceOp::Product, 1),
1085 (ReduceOp::Or, 0),
1086 (ReduceOp::Xor, 0),
1087 (ReduceOp::And, 0xFFFF_FFFF),
1088 ] {
1089 let c = bin(BinOp::Eq, sum(op), konst(s32(), want), Ty::BIT);
1090 let x = expand(&[c], &[arr(true, &[])], &[0], 0, &[]).expect("expands");
1091 assert!(ev(&x.conjuncts[0], &[], &[]), "{op:?}");
1092 }
1093 }
1094
1095 #[test]
1097 fn a_size_constraint_resizes_the_array() {
1098 let c = bin(
1099 BinOp::Eq,
1100 size(0),
1101 Expr::Resize(Box::new(Expr::Var(0, u(8))), s32()),
1102 Ty::BIT,
1103 );
1104 let bound = bin(BinOp::Lt, Expr::Var(0, u(8)), konst(u(8), 5), Ty::BIT);
1105 let mut seen = std::collections::BTreeSet::new();
1106 let mut seed = 1u64;
1107 for _ in 0..200 {
1108 let s = solve_sizes(
1109 &[c.clone(), bound.clone()],
1110 &[arr(true, &[9, 9])],
1111 &[u(8)],
1112 &[],
1113 &[],
1114 &mut seed,
1115 )
1116 .expect("solves");
1117 assert_eq!(s.sized, vec![0]);
1118 assert!(s.sizes[0] < 5);
1119 seen.insert(s.sizes[0]);
1120 }
1121 assert_eq!(seen.len(), 5, "every size 0..5 is reachable: {seen:?}");
1122 }
1123
1124 #[test]
1125 fn an_unconstrained_size_keeps_the_array() {
1126 let mut seed = 1u64;
1127 let s = solve_sizes(&[], &[arr(true, &[1, 2, 3])], &[], &[], &[], &mut seed).unwrap();
1128 assert_eq!(s.sizes, vec![3]);
1129 assert!(s.sized.is_empty());
1130 }
1131
1132 #[test]
1133 fn an_impossible_size_is_unsatisfiable_and_a_huge_one_is_too_large() {
1134 let mut seed = 1u64;
1135 let lt = |n: u64| bin(BinOp::Lt, size(0), konst(s32(), n), Ty::BIT);
1136 let gt = |n: u64| bin(BinOp::Gt, size(0), konst(s32(), n), Ty::BIT);
1137 let a = [arr(true, &[])];
1138 assert_eq!(
1139 solve_sizes(&[lt(3), gt(5)], &a, &[], &[], &[], &mut seed).unwrap_err(),
1140 SizeError::Unsatisfiable
1141 );
1142 assert_eq!(
1143 solve_sizes(&[gt(10_000_000)], &a, &[], &[], &[], &mut seed).unwrap_err(),
1144 SizeError::TooLarge
1145 );
1146 let s = solve_sizes(&[gt(2000)], &a, &[], &[], &[], &mut seed).unwrap();
1148 assert!(s.sizes[0] > 2000);
1149 }
1150
1151 #[test]
1152 fn blocked_tuples_are_excluded() {
1153 let c = bin(BinOp::Le, size(0), konst(s32(), 1), Ty::BIT);
1154 let a = [arr(true, &[])];
1155 let mut seed = 3u64;
1156 let s = solve_sizes(
1157 std::slice::from_ref(&c),
1158 &a,
1159 &[],
1160 &[],
1161 &[vec![0]],
1162 &mut seed,
1163 )
1164 .unwrap();
1165 assert_eq!(s.sizes, vec![1]);
1166 assert_eq!(
1167 solve_sizes(&[c], &a, &[], &[], &[vec![0], vec![1]], &mut seed).unwrap_err(),
1168 SizeError::Unsatisfiable
1169 );
1170 }
1171
1172 #[test]
1175 fn the_size_problem_holds_only_connected_size_constraints() {
1176 let elem_c = bin(
1177 BinOp::Eq,
1178 elem(0, konst(s32(), 0), s32()),
1179 konst(s32(), 1),
1180 Ty::BIT,
1181 );
1182 let unrelated = bin(BinOp::Eq, Expr::Var(0, u(8)), konst(u(8), 200), Ty::BIT);
1183 let c = bin(BinOp::Eq, size(0), konst(s32(), 2), Ty::BIT);
1184 let mut seed = 1u64;
1185 let s = solve_sizes(
1186 &[elem_c, unrelated, c],
1187 &[arr(true, &[])],
1188 &[u(8)],
1189 &[],
1190 &[],
1191 &mut seed,
1192 )
1193 .unwrap();
1194 assert_eq!(s.sizes, vec![2]);
1195 }
1196}