1use core::fmt;
38use num_bigint::{BigInt, BigUint};
39use num_traits::{One, Zero};
40
41pub type VarId = u32;
43pub type StateId = u32;
45
46#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
48pub struct Ty {
49 pub width: u32,
51 pub signed: bool,
53}
54
55impl Ty {
56 pub const fn unsigned(width: u32) -> Ty {
58 Ty {
59 width,
60 signed: false,
61 }
62 }
63 pub const fn signed(width: u32) -> Ty {
65 Ty {
66 width,
67 signed: true,
68 }
69 }
70 pub const BIT: Ty = Ty::unsigned(1);
72}
73
74impl fmt::Display for Ty {
75 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76 write!(f, "{}[{}]", if self.signed { "s" } else { "u" }, self.width)
77 }
78}
79
80#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
82pub enum UnOp {
83 BitNot,
85 Neg,
87 LogNot,
89 RedAnd,
91 RedOr,
93 RedXor,
95 RedNand,
97 RedNor,
99 RedXnor,
101}
102
103#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
105pub enum BinOp {
106 Add,
108 Sub,
110 Mul,
112 Div,
114 Mod,
116 And,
118 Or,
120 Xor,
122 Xnor,
124 Shl,
126 Shr,
128 Ashr,
130 Eq,
132 Ne,
134 Lt,
136 Le,
138 Gt,
140 Ge,
142 LogAnd,
144 LogOr,
146 LogImpl,
148 LogEquiv,
150}
151
152impl BinOp {
153 fn is_compare(self) -> bool {
154 matches!(
155 self,
156 BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge
157 )
158 }
159 fn is_logical(self) -> bool {
160 matches!(
161 self,
162 BinOp::LogAnd | BinOp::LogOr | BinOp::LogImpl | BinOp::LogEquiv
163 )
164 }
165 fn is_shift(self) -> bool {
166 matches!(self, BinOp::Shl | BinOp::Shr | BinOp::Ashr)
167 }
168}
169
170#[derive(Clone, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
172pub enum Expr {
173 Const(Ty, BigUint),
175 Var(VarId, Ty),
177 State(StateId, Ty),
179 Unary(UnOp, Box<Expr>, Ty),
181 Binary(BinOp, Box<Expr>, Box<Expr>, Ty),
183 Ite(Box<Expr>, Box<Expr>, Box<Expr>, Ty),
185 Guard(Box<Expr>, Box<Expr>, Box<Expr>, Ty),
193 Resize(Box<Expr>, Ty),
196 Concat(Vec<Expr>, Ty),
198 Slice(Box<Expr>, u32, Ty),
201 AllDistinct(Vec<Expr>),
207 Array(ArrayNode),
216}
217
218pub type ArrayId = u32;
220pub type LoopId = u32;
222
223#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
225pub enum ReduceOp {
226 Sum,
228 Product,
230 And,
232 Or,
234 Xor,
236}
237
238#[derive(Clone, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
240pub enum ArrayNode {
241 Size(ArrayId),
243 Elem {
246 array: ArrayId,
248 index: Box<Expr>,
250 ty: Ty,
252 },
253 Loop(LoopId, Ty),
255 Foreach {
258 array: ArrayId,
260 var: LoopId,
262 body: Box<Expr>,
264 },
265 Reduce {
269 op: ReduceOp,
271 array: ArrayId,
273 var: LoopId,
275 index_var: LoopId,
277 body: Box<Expr>,
279 },
280 Call {
286 func: FuncId,
288 args: Vec<Expr>,
291 ty: Ty,
293 },
294 Pick {
302 index: Box<Expr>,
304 low: i64,
306 elems: Vec<Expr>,
308 ty: Ty,
310 },
311 Distinct {
318 scalars: Vec<Expr>,
320 arrays: Vec<ArrayId>,
322 },
323}
324
325pub type FuncId = u32;
327
328#[derive(Clone, Debug, PartialEq, Eq)]
330pub enum IrError {
331 IllTyped {
333 what: String,
335 },
336}
337
338impl fmt::Display for IrError {
339 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
340 match self {
341 IrError::IllTyped { what } => write!(f, "ill-typed constraint IR: {what}"),
342 }
343 }
344}
345
346impl std::error::Error for IrError {}
347
348#[derive(Clone, Debug, PartialEq, Eq)]
350pub enum ConstraintEvalError {
351 StateUnknown(StateId),
354 DivideByZero,
356 UnboundVar(VarId),
358 UnboundState(StateId),
360 Unexpanded,
363}
364
365impl fmt::Display for ConstraintEvalError {
366 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
367 match self {
368 ConstraintEvalError::StateUnknown(s) => write!(
369 f,
370 "state variable #{s} has an X/Z bit; a 4-state value in a constraint is an \
371 error (IEEE 1800-2023 18.3)"
372 ),
373 ConstraintEvalError::DivideByZero => f.write_str("division by zero in a constraint"),
374 ConstraintEvalError::UnboundVar(v) => write!(f, "no value for random variable #{v}"),
375 ConstraintEvalError::UnboundState(s) => {
376 write!(f, "no value for state variable #{s}")
377 }
378 ConstraintEvalError::Unexpanded => {
379 f.write_str("an array-level constraint node was not expanded before evaluation")
380 }
381 }
382 }
383}
384
385impl std::error::Error for ConstraintEvalError {}
386
387#[derive(Clone, Debug, Default, PartialEq, Eq)]
390pub struct Assignment {
391 pub vars: Vec<BigUint>,
393 pub states: Vec<Option<BigUint>>,
395}
396
397pub fn mask(w: u32) -> BigUint {
399 (BigUint::one() << w) - BigUint::one()
400}
401
402fn to_signed(v: &BigUint, w: u32) -> BigInt {
403 if v.bit(u64::from(w) - 1) {
404 BigInt::from(v.clone()) - (BigInt::one() << w)
405 } else {
406 BigInt::from(v.clone())
407 }
408}
409
410fn from_signed(i: &BigInt, w: u32) -> BigUint {
411 let m = BigInt::one() << w;
412 let r = ((i % &m) + &m) % &m;
413 r.to_biguint().expect("reduced into [0, 2^w)")
414}
415
416fn bit(b: bool) -> BigUint {
417 if b { BigUint::one() } else { BigUint::zero() }
418}
419
420impl Expr {
421 pub fn constant(ty: Ty, val: impl Into<BigUint>) -> Expr {
423 Expr::Const(ty, val.into() & mask(ty.width))
424 }
425
426 pub fn ty(&self) -> Ty {
428 match self {
429 Expr::Const(t, _)
430 | Expr::Var(_, t)
431 | Expr::State(_, t)
432 | Expr::Unary(_, _, t)
433 | Expr::Binary(_, _, _, t)
434 | Expr::Ite(_, _, _, t)
435 | Expr::Guard(_, _, _, t)
436 | Expr::Resize(_, t)
437 | Expr::Concat(_, t)
438 | Expr::Slice(_, _, t) => *t,
439 Expr::AllDistinct(_) => Ty::BIT,
440 Expr::Array(n) => n.ty(),
441 }
442 }
443
444 pub fn validate(&self) -> Result<(), IrError> {
446 let bad = |what: String| Err(IrError::IllTyped { what });
447 match self {
448 Expr::Const(t, v) => {
449 if t.width == 0 {
450 return bad("zero-width constant".into());
451 }
452 if v.bits() > u64::from(t.width) {
453 return bad(format!("constant {v} does not fit {t}"));
454 }
455 Ok(())
456 }
457 Expr::Var(_, t) | Expr::State(_, t) => {
458 if t.width == 0 {
459 return bad("zero-width variable".into());
460 }
461 Ok(())
462 }
463 Expr::Unary(op, a, t) => {
464 a.validate()?;
465 let at = a.ty();
466 let want = match op {
467 UnOp::BitNot | UnOp::Neg => at,
468 _ => Ty::BIT,
469 };
470 if *t != want {
471 return bad(format!("{op:?} on {at} must produce {want}, has {t}"));
472 }
473 Ok(())
474 }
475 Expr::Binary(op, l, r, t) => {
476 l.validate()?;
477 r.validate()?;
478 let (lt, rt) = (l.ty(), r.ty());
479 if op.is_logical() {
480 if *t != Ty::BIT {
481 return bad(format!("{op:?} must produce u[1], has {t}"));
482 }
483 } else if op.is_compare() {
484 if lt != rt {
485 return bad(format!("{op:?} operands differ: {lt} vs {rt}"));
486 }
487 if *t != Ty::BIT {
488 return bad(format!("{op:?} must produce u[1], has {t}"));
489 }
490 } else if op.is_shift() {
491 if *t != lt {
492 return bad(format!("{op:?} result {t} must equal left operand {lt}"));
493 }
494 } else if lt != rt || *t != lt {
495 return bad(format!("{op:?} operands/result differ: {lt}, {rt}, {t}"));
496 }
497 Ok(())
498 }
499 Expr::Ite(c, a, b, t) | Expr::Guard(c, a, b, t) => {
500 c.validate()?;
501 a.validate()?;
502 b.validate()?;
503 if a.ty() != *t || b.ty() != *t {
504 return bad(format!(
505 "ternary arms {} / {} must both equal result {t}",
506 a.ty(),
507 b.ty()
508 ));
509 }
510 Ok(())
511 }
512 Expr::Resize(a, t) => {
513 a.validate()?;
514 if t.width == 0 {
515 return bad("resize to zero width".into());
516 }
517 Ok(())
518 }
519 Expr::Concat(parts, t) => {
520 if parts.is_empty() {
521 return bad("empty concatenation".into());
522 }
523 let mut w = 0u32;
524 for p in parts {
525 p.validate()?;
526 w += p.ty().width;
527 }
528 if *t != Ty::unsigned(w) {
529 return bad(format!("concatenation of {w} bits has type {t}"));
530 }
531 Ok(())
532 }
533 Expr::Slice(a, lo, t) => {
534 a.validate()?;
535 if t.signed || t.width == 0 || lo + t.width > a.ty().width {
536 return bad(format!(
537 "slice [{}+:{}] of {} (result {t})",
538 lo,
539 t.width,
540 a.ty()
541 ));
542 }
543 Ok(())
544 }
545 Expr::AllDistinct(members) => {
546 let Some(first) = members.first() else {
547 return bad("empty uniqueness group".into());
548 };
549 let t = first.ty();
550 for m in members {
551 m.validate()?;
552 if m.ty() != t {
553 return bad(format!(
554 "uniqueness members differ in type: {t} vs {}",
555 m.ty()
556 ));
557 }
558 }
559 Ok(())
560 }
561 Expr::Array(n) => n.validate(),
562 }
563 }
564
565 pub fn for_each_var(&self, f: &mut impl FnMut(VarId, Ty)) {
567 match self {
568 Expr::Var(v, t) => f(*v, *t),
569 Expr::Const(..) | Expr::State(..) => {}
570 Expr::Unary(_, a, _) | Expr::Resize(a, _) | Expr::Slice(a, _, _) => {
571 a.for_each_var(f);
572 }
573 Expr::Binary(_, a, b, _) => {
574 a.for_each_var(f);
575 b.for_each_var(f);
576 }
577 Expr::Ite(c, a, b, _) | Expr::Guard(c, a, b, _) => {
578 c.for_each_var(f);
579 a.for_each_var(f);
580 b.for_each_var(f);
581 }
582 Expr::Concat(parts, _) | Expr::AllDistinct(parts) => {
583 parts.iter().for_each(|p| p.for_each_var(f));
584 }
585 Expr::Array(n) => n.for_each_child(&mut |c| c.for_each_var(f)),
586 }
587 }
588
589 pub fn for_each_state(&self, f: &mut impl FnMut(StateId)) {
591 match self {
592 Expr::State(s, _) => f(*s),
593 Expr::Const(..) | Expr::Var(..) => {}
594 Expr::Unary(_, a, _) | Expr::Resize(a, _) | Expr::Slice(a, _, _) => {
595 a.for_each_state(f);
596 }
597 Expr::Binary(_, a, b, _) => {
598 a.for_each_state(f);
599 b.for_each_state(f);
600 }
601 Expr::Ite(c, a, b, _) | Expr::Guard(c, a, b, _) => {
602 c.for_each_state(f);
603 a.for_each_state(f);
604 b.for_each_state(f);
605 }
606 Expr::Concat(parts, _) | Expr::AllDistinct(parts) => {
607 parts.iter().for_each(|p| p.for_each_state(f));
608 }
609 Expr::Array(n) => n.for_each_child(&mut |c| c.for_each_state(f)),
610 }
611 }
612
613 pub fn substitute_vars(&self, f: &mut impl FnMut(VarId, Ty) -> Expr) -> Expr {
624 match self {
625 Expr::Var(v, t) => {
626 let r = f(*v, *t);
627 debug_assert_eq!(r.ty(), *t, "substitute_vars: replacement type differs");
628 r
629 }
630 Expr::Const(..) | Expr::State(..) => self.clone(),
631 Expr::Unary(op, a, t) => Expr::Unary(*op, Box::new(a.substitute_vars(f)), *t),
632 Expr::Resize(a, t) => Expr::Resize(Box::new(a.substitute_vars(f)), *t),
633 Expr::Slice(a, lo, t) => Expr::Slice(Box::new(a.substitute_vars(f)), *lo, *t),
634 Expr::Binary(op, a, b, t) => {
635 let a = Box::new(a.substitute_vars(f));
636 let b = Box::new(b.substitute_vars(f));
637 Expr::Binary(*op, a, b, *t)
638 }
639 Expr::Ite(c, a, b, t) => {
640 let c = Box::new(c.substitute_vars(f));
641 let a = Box::new(a.substitute_vars(f));
642 let b = Box::new(b.substitute_vars(f));
643 Expr::Ite(c, a, b, *t)
644 }
645 Expr::Guard(c, a, b, t) => {
646 let c = Box::new(c.substitute_vars(f));
647 let a = Box::new(a.substitute_vars(f));
648 let b = Box::new(b.substitute_vars(f));
649 Expr::Guard(c, a, b, *t)
650 }
651 Expr::Concat(parts, t) => {
652 Expr::Concat(parts.iter().map(|p| p.substitute_vars(f)).collect(), *t)
653 }
654 Expr::AllDistinct(parts) => {
655 Expr::AllDistinct(parts.iter().map(|p| p.substitute_vars(f)).collect())
656 }
657 Expr::Array(n) => Expr::Array(n.map_children(&mut |c| c.substitute_vars(f))),
658 }
659 }
660}
661
662impl Expr {
663 pub fn try_map_children<E>(
666 &self,
667 f: &mut impl FnMut(&Expr) -> Result<Expr, E>,
668 ) -> Result<Expr, E> {
669 Ok(match self {
670 Expr::Const(..) | Expr::Var(..) | Expr::State(..) => self.clone(),
671 Expr::Unary(op, a, t) => Expr::Unary(*op, Box::new(f(a)?), *t),
672 Expr::Binary(op, a, b, t) => Expr::Binary(*op, Box::new(f(a)?), Box::new(f(b)?), *t),
673 Expr::Ite(c, a, b, t) => {
674 Expr::Ite(Box::new(f(c)?), Box::new(f(a)?), Box::new(f(b)?), *t)
675 }
676 Expr::Guard(c, a, b, t) => {
677 Expr::Guard(Box::new(f(c)?), Box::new(f(a)?), Box::new(f(b)?), *t)
678 }
679 Expr::Resize(a, t) => Expr::Resize(Box::new(f(a)?), *t),
680 Expr::Concat(parts, t) => {
681 Expr::Concat(parts.iter().map(&mut *f).collect::<Result<_, _>>()?, *t)
682 }
683 Expr::Slice(a, lo, t) => Expr::Slice(Box::new(f(a)?), *lo, *t),
684 Expr::AllDistinct(parts) => {
685 Expr::AllDistinct(parts.iter().map(&mut *f).collect::<Result<_, _>>()?)
686 }
687 Expr::Array(n) => {
688 let mut err = None;
689 let r = n.map_children(&mut |c| match f(c) {
690 Ok(x) => x,
691 Err(e) => {
692 err.get_or_insert(e);
693 c.clone()
694 }
695 });
696 if let Some(e) = err {
697 return Err(e);
698 }
699 Expr::Array(r)
700 }
701 })
702 }
703
704 pub fn any_node(&self, pred: &mut impl FnMut(&Expr) -> bool) -> bool {
706 if pred(self) {
707 return true;
708 }
709 let mut found = false;
710 let mut visit = |c: &Expr| {
711 if !found && c.any_node(pred) {
712 found = true;
713 }
714 };
715 match self {
716 Expr::Const(..) | Expr::Var(..) | Expr::State(..) => {}
717 Expr::Unary(_, a, _) | Expr::Resize(a, _) | Expr::Slice(a, _, _) => visit(a),
718 Expr::Binary(_, a, b, _) => {
719 visit(a);
720 visit(b);
721 }
722 Expr::Ite(c, a, b, _) | Expr::Guard(c, a, b, _) => {
723 visit(c);
724 visit(a);
725 visit(b);
726 }
727 Expr::Concat(parts, _) | Expr::AllDistinct(parts) => parts.iter().for_each(visit),
728 Expr::Array(n) => n.for_each_child(&mut visit),
729 }
730 found
731 }
732}
733
734impl ArrayNode {
735 pub fn ty(&self) -> Ty {
737 match self {
738 ArrayNode::Size(_) => Ty::signed(32),
739 ArrayNode::Elem { ty, .. } | ArrayNode::Loop(_, ty) => *ty,
740 ArrayNode::Foreach { .. } | ArrayNode::Distinct { .. } => Ty::BIT,
741 ArrayNode::Reduce { body, .. } => body.ty(),
742 ArrayNode::Call { ty, .. } | ArrayNode::Pick { ty, .. } => *ty,
743 }
744 }
745
746 fn validate(&self) -> Result<(), IrError> {
747 let bad = |what: String| Err(IrError::IllTyped { what });
748 match self {
749 ArrayNode::Size(_) => Ok(()),
750 ArrayNode::Loop(_, t) => {
751 if t.width == 0 {
752 return bad("zero-width loop variable".into());
753 }
754 Ok(())
755 }
756 ArrayNode::Elem { index, ty, .. } => {
757 index.validate()?;
758 if ty.width == 0 {
759 return bad("zero-width array element".into());
760 }
761 Ok(())
762 }
763 ArrayNode::Foreach { body, .. } => body.validate(),
764 ArrayNode::Distinct { scalars, arrays } => {
765 if scalars.is_empty() && arrays.is_empty() {
766 return bad("a distinct group with no members".into());
767 }
768 scalars.iter().try_for_each(Expr::validate)
769 }
770 ArrayNode::Reduce { op, body, .. } => {
771 body.validate()?;
772 if matches!(op, ReduceOp::And | ReduceOp::Or | ReduceOp::Xor)
773 && body.ty().width == 0
774 {
775 return bad("zero-width reduction".into());
776 }
777 Ok(())
778 }
779 ArrayNode::Call { args, ty, .. } => {
780 if ty.width == 0 {
781 return bad("zero-width function result".into());
782 }
783 args.iter().try_for_each(Expr::validate)
784 }
785 ArrayNode::Pick {
786 index, elems, ty, ..
787 } => {
788 index.validate()?;
789 if ty.width == 0 {
790 return bad("zero-width array element".into());
791 }
792 if elems.is_empty() {
793 return bad("a pick over no elements".into());
794 }
795 if elems.iter().any(|e| e.ty() != *ty) {
796 return bad("a pick element of another type".into());
797 }
798 elems.iter().try_for_each(Expr::validate)
799 }
800 }
801 }
802
803 pub fn for_each_child(&self, f: &mut impl FnMut(&Expr)) {
805 match self {
806 ArrayNode::Size(_) | ArrayNode::Loop(..) => {}
807 ArrayNode::Pick { index, elems, .. } => {
808 f(index);
809 elems.iter().for_each(f);
810 }
811 ArrayNode::Call { args, .. } => args.iter().for_each(f),
812 ArrayNode::Distinct { scalars, .. } => scalars.iter().for_each(f),
813 ArrayNode::Elem { index, .. } => f(index),
814 ArrayNode::Foreach { body, .. } | ArrayNode::Reduce { body, .. } => f(body),
815 }
816 }
817
818 pub fn map_children(&self, f: &mut impl FnMut(&Expr) -> Expr) -> ArrayNode {
820 match self {
821 ArrayNode::Size(_) | ArrayNode::Loop(..) => self.clone(),
822 ArrayNode::Pick {
823 index,
824 low,
825 elems,
826 ty,
827 } => ArrayNode::Pick {
828 index: Box::new(f(index)),
829 low: *low,
830 elems: elems.iter().map(&mut *f).collect(),
831 ty: *ty,
832 },
833 ArrayNode::Call { func, args, ty } => ArrayNode::Call {
834 func: *func,
835 args: args.iter().map(&mut *f).collect(),
836 ty: *ty,
837 },
838 ArrayNode::Distinct { scalars, arrays } => ArrayNode::Distinct {
839 scalars: scalars.iter().map(&mut *f).collect(),
840 arrays: arrays.clone(),
841 },
842 ArrayNode::Elem { array, index, ty } => ArrayNode::Elem {
843 array: *array,
844 index: Box::new(f(index)),
845 ty: *ty,
846 },
847 ArrayNode::Foreach { array, var, body } => ArrayNode::Foreach {
848 array: *array,
849 var: *var,
850 body: Box::new(f(body)),
851 },
852 ArrayNode::Reduce {
853 op,
854 array,
855 var,
856 index_var,
857 body,
858 } => ArrayNode::Reduce {
859 op: *op,
860 array: *array,
861 var: *var,
862 index_var: *index_var,
863 body: Box::new(f(body)),
864 },
865 }
866 }
867}
868
869pub fn eval(e: &Expr, a: &Assignment) -> Result<BigUint, ConstraintEvalError> {
871 match e {
872 Expr::Const(_, v) => Ok(v.clone()),
873 Expr::Var(id, t) => a
874 .vars
875 .get(*id as usize)
876 .map(|v| v & mask(t.width))
877 .ok_or(ConstraintEvalError::UnboundVar(*id)),
878 Expr::State(id, t) => match a.states.get(*id as usize) {
879 None => Err(ConstraintEvalError::UnboundState(*id)),
880 Some(None) => Err(ConstraintEvalError::StateUnknown(*id)),
881 Some(Some(v)) => Ok(v & mask(t.width)),
882 },
883 Expr::Unary(op, x, t) => {
884 let v = eval(x, a)?;
885 let w = x.ty().width;
886 let r = match op {
887 UnOp::BitNot => mask(w) ^ v,
888 UnOp::Neg => ((BigUint::one() << w) - v) & mask(w),
889 UnOp::LogNot => bit(v.is_zero()),
890 UnOp::RedAnd => bit(v == mask(w)),
891 UnOp::RedNand => bit(v != mask(w)),
892 UnOp::RedOr => bit(!v.is_zero()),
893 UnOp::RedNor => bit(v.is_zero()),
894 UnOp::RedXor => bit(popcount(&v) % 2 == 1),
895 UnOp::RedXnor => bit(popcount(&v).is_multiple_of(2)),
896 };
897 Ok(r & mask(t.width))
898 }
899 Expr::Binary(op @ (BinOp::LogAnd | BinOp::LogOr | BinOp::LogImpl), l, r, _) => {
900 let l_true = !eval(l, a)?.is_zero();
905 let decided = match op {
906 BinOp::LogAnd | BinOp::LogImpl => !l_true,
907 _ => l_true,
908 };
909 if decided {
910 return Ok(bit(*op != BinOp::LogAnd));
911 }
912 Ok(bit(!eval(r, a)?.is_zero()))
913 }
914 Expr::Binary(op, l, r, t) => {
915 let lv = eval(l, a)?;
916 let rv = eval(r, a)?;
917 let lt = l.ty();
918 let w = t.width;
919 let m = mask(w);
920 Ok(match op {
921 BinOp::Add => (lv + rv) & m,
922 BinOp::Sub => ((BigUint::one() << w) + lv - rv) & m,
923 BinOp::Mul => (lv * rv) & m,
924 BinOp::Div | BinOp::Mod => {
925 if rv.is_zero() {
926 return Err(ConstraintEvalError::DivideByZero);
927 }
928 if lt.signed {
929 let (x, y) = (to_signed(&lv, w), to_signed(&rv, w));
930 from_signed(&if *op == BinOp::Div { x / y } else { x % y }, w)
931 } else if *op == BinOp::Div {
932 lv / rv
933 } else {
934 lv % rv
935 }
936 }
937 BinOp::And => lv & rv,
938 BinOp::Or => lv | rv,
939 BinOp::Xor => lv ^ rv,
940 BinOp::Xnor => (lv ^ rv) ^ m,
941 BinOp::Shl | BinOp::Shr | BinOp::Ashr => {
942 let amt = if rv < BigUint::from(w) {
945 u32::try_from(&rv).expect("below w")
946 } else {
947 w
948 };
949 match op {
950 BinOp::Shl => (lv << amt) & m,
951 BinOp::Shr => lv >> amt,
952 _ => {
953 if lt.signed && lv.bit(u64::from(w) - 1) {
954 let x = to_signed(&lv, w) >> amt;
955 from_signed(&x, w)
956 } else {
957 lv >> amt
958 }
959 }
960 }
961 }
962 BinOp::Eq => bit(lv == rv),
963 BinOp::Ne => bit(lv != rv),
964 BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge => {
965 let ord = if lt.signed {
966 to_signed(&lv, lt.width).cmp(&to_signed(&rv, lt.width))
967 } else {
968 lv.cmp(&rv)
969 };
970 bit(match op {
971 BinOp::Lt => ord.is_lt(),
972 BinOp::Le => ord.is_le(),
973 BinOp::Gt => ord.is_gt(),
974 _ => ord.is_ge(),
975 })
976 }
977 BinOp::LogAnd => bit(!lv.is_zero() && !rv.is_zero()),
978 BinOp::LogOr => bit(!lv.is_zero() || !rv.is_zero()),
979 BinOp::LogImpl => bit(lv.is_zero() || !rv.is_zero()),
980 BinOp::LogEquiv => bit(lv.is_zero() == rv.is_zero()),
981 })
982 }
983 Expr::Ite(c, x, y, _) | Expr::Guard(c, x, y, _) => {
984 if eval(c, a)?.is_zero() {
985 eval(y, a)
986 } else {
987 eval(x, a)
988 }
989 }
990 Expr::Resize(x, t) => {
991 let v = eval(x, a)?;
992 let from = x.ty();
993 Ok(if t.width <= from.width {
994 v & mask(t.width)
995 } else if from.signed {
996 from_signed(&to_signed(&v, from.width), t.width)
997 } else {
998 v
999 })
1000 }
1001 Expr::Concat(parts, _) => {
1002 let mut acc = BigUint::zero();
1003 for p in parts {
1004 let v = eval(p, a)?;
1005 acc = (acc << p.ty().width) | v;
1006 }
1007 Ok(acc)
1008 }
1009 Expr::Slice(x, lo, t) => Ok((eval(x, a)? >> *lo) & mask(t.width)),
1010 Expr::AllDistinct(members) => {
1011 let vals = members
1014 .iter()
1015 .map(|m| eval(m, a))
1016 .collect::<Result<Vec<_>, _>>()?;
1017 Ok(bit(vals
1018 .iter()
1019 .enumerate()
1020 .all(|(i, v)| vals[i + 1..].iter().all(|w| w != v))))
1021 }
1022 Expr::Array(_) => Err(ConstraintEvalError::Unexpanded),
1023 }
1024}
1025
1026pub fn popcount(v: &BigUint) -> u64 {
1028 v.iter_u64_digits().map(|d| u64::from(d.count_ones())).sum()
1029}
1030
1031#[cfg(test)]
1032mod tests {
1033 use super::*;
1034
1035 fn u(w: u32) -> Ty {
1036 Ty::unsigned(w)
1037 }
1038 fn s(w: u32) -> Ty {
1039 Ty::signed(w)
1040 }
1041 fn c(t: Ty, v: i64) -> Expr {
1042 Expr::Const(t, from_signed(&BigInt::from(v), t.width))
1044 }
1045 fn var(id: u32, t: Ty) -> Expr {
1046 Expr::Var(id, t)
1047 }
1048 fn bin(op: BinOp, l: Expr, r: Expr) -> Expr {
1049 let t = if op.is_compare() || op.is_logical() {
1050 Ty::BIT
1051 } else {
1052 l.ty()
1053 };
1054 Expr::Binary(op, Box::new(l), Box::new(r), t)
1055 }
1056 fn asg(vars: &[u64]) -> Assignment {
1057 Assignment {
1058 vars: vars.iter().map(|v| BigUint::from(*v)).collect(),
1059 states: vec![],
1060 }
1061 }
1062 fn ev(e: &Expr, vars: &[u64]) -> u64 {
1063 e.validate().expect("well typed");
1064 u64::try_from(eval(e, &asg(vars)).expect("evaluates")).expect("fits u64")
1065 }
1066
1067 #[test]
1072 fn array_nodes_validate_and_refuse_direct_evaluation() {
1073 let size = Expr::Array(ArrayNode::Size(0));
1074 assert_eq!(size.ty(), s(32));
1075 let elem = Expr::Array(ArrayNode::Elem {
1076 array: 0,
1077 index: Box::new(c(s(32), 1)),
1078 ty: u(8),
1079 });
1080 assert_eq!(elem.ty(), u(8));
1081 let each = Expr::Array(ArrayNode::Foreach {
1082 array: 0,
1083 var: 0,
1084 body: Box::new(bin(BinOp::Gt, elem.clone(), c(u(8), 0))),
1085 });
1086 assert_eq!(each.ty(), Ty::BIT);
1087 let sum = Expr::Array(ArrayNode::Reduce {
1088 op: ReduceOp::Sum,
1089 array: 0,
1090 var: 0,
1091 index_var: 1,
1092 body: Box::new(Expr::Array(ArrayNode::Loop(0, u(8)))),
1093 });
1094 assert_eq!(sum.ty(), u(8), "a reduction folds at the body's type");
1095 for e in [&size, &elem, &each, &sum] {
1096 e.validate().expect("well typed");
1097 assert_eq!(
1098 eval(e, &asg(&[])),
1099 Err(ConstraintEvalError::Unexpanded),
1100 "{e:?}"
1101 );
1102 }
1103 let bad = Expr::Array(ArrayNode::Elem {
1105 array: 0,
1106 index: Box::new(Expr::Const(u(0), BigUint::default())),
1107 ty: u(8),
1108 });
1109 assert!(bad.validate().is_err());
1110 let zero = Expr::Array(ArrayNode::Elem {
1111 array: 0,
1112 index: Box::new(c(s(32), 0)),
1113 ty: u(0),
1114 });
1115 assert!(zero.validate().is_err());
1116 }
1117
1118 #[test]
1119 fn wraparound_arithmetic_is_modular() {
1120 let a = var(0, u(8));
1121 let b = var(1, u(8));
1122 assert_eq!(ev(&bin(BinOp::Add, a.clone(), b.clone()), &[200, 100]), 44);
1123 assert_eq!(ev(&bin(BinOp::Sub, a.clone(), b.clone()), &[3, 5]), 254);
1124 assert_eq!(ev(&bin(BinOp::Mul, a, b), &[16, 17]), 16);
1125 }
1126
1127 #[test]
1128 fn neg_and_not() {
1129 let a = var(0, u(4));
1130 let neg = Expr::Unary(UnOp::Neg, Box::new(a.clone()), u(4));
1131 assert_eq!(ev(&neg, &[1]), 15);
1132 assert_eq!(ev(&neg, &[0]), 0);
1133 let not = Expr::Unary(UnOp::BitNot, Box::new(a), u(4));
1134 assert_eq!(ev(¬, &[0b1010]), 0b0101);
1135 }
1136
1137 #[test]
1138 fn signed_compare_differs_from_unsigned() {
1139 let lt_s = bin(BinOp::Lt, var(0, s(8)), c(s(8), 0));
1141 let lt_u = bin(BinOp::Lt, var(0, u(8)), c(u(8), 0));
1142 assert_eq!(ev(<_s, &[0xFF]), 1);
1143 assert_eq!(ev(<_u, &[0xFF]), 0);
1144 let ge_s = bin(BinOp::Ge, var(0, s(8)), c(s(8), -2));
1145 assert_eq!(ev(&ge_s, &[0xFF]), 1);
1146 assert_eq!(ev(&ge_s, &[0xFD]), 0);
1147 }
1148
1149 #[test]
1150 fn signed_division_truncates_toward_zero() {
1151 let q = bin(BinOp::Div, var(0, s(8)), var(1, s(8)));
1152 let r = bin(BinOp::Mod, var(0, s(8)), var(1, s(8)));
1153 assert_eq!(ev(&q, &[0xF9, 2]), 0xFD);
1155 assert_eq!(ev(&r, &[0xF9, 2]), 0xFF);
1156 let uq = bin(BinOp::Div, var(0, u(8)), var(1, u(8)));
1158 assert_eq!(ev(&uq, &[0xF9, 2]), 124);
1159 }
1160
1161 #[test]
1162 fn divide_by_zero_is_a_typed_error() {
1163 let q = bin(BinOp::Div, var(0, u(8)), var(1, u(8)));
1164 assert_eq!(
1165 eval(&q, &asg(&[1, 0])),
1166 Err(ConstraintEvalError::DivideByZero)
1167 );
1168 }
1169
1170 #[test]
1171 fn shifts() {
1172 let a = var(0, u(8));
1173 let sh = |op, n: u64| bin(op, a.clone(), c(u(4), n as i64));
1174 assert_eq!(ev(&sh(BinOp::Shl, 3), &[0b1011_0001]), 0b1000_1000);
1175 assert_eq!(ev(&sh(BinOp::Shr, 3), &[0b1011_0001]), 0b0001_0110);
1176 assert_eq!(ev(&sh(BinOp::Shl, 9), &[0xFF]), 0);
1178 assert_eq!(ev(&sh(BinOp::Shr, 12), &[0xFF]), 0);
1179 assert_eq!(ev(&sh(BinOp::Ashr, 2), &[0x80]), 0x20);
1181 let sa = bin(BinOp::Ashr, var(0, s(8)), c(u(4), 2));
1183 assert_eq!(ev(&sa, &[0x80]), 0xE0);
1184 let sa_big = bin(BinOp::Ashr, var(0, s(8)), c(u(4), 12));
1185 assert_eq!(ev(&sa_big, &[0x80]), 0xFF);
1186 assert_eq!(ev(&sa_big, &[0x40]), 0x00);
1187 }
1188
1189 #[test]
1190 fn reductions_and_logical_ops() {
1191 let a = var(0, u(4));
1192 let red = |op| Expr::Unary(op, Box::new(a.clone()), Ty::BIT);
1193 assert_eq!(ev(&red(UnOp::RedAnd), &[0b1111]), 1);
1194 assert_eq!(ev(&red(UnOp::RedAnd), &[0b1110]), 0);
1195 assert_eq!(ev(&red(UnOp::RedNand), &[0b1110]), 1);
1196 assert_eq!(ev(&red(UnOp::RedOr), &[0]), 0);
1197 assert_eq!(ev(&red(UnOp::RedNor), &[0]), 1);
1198 assert_eq!(ev(&red(UnOp::RedXor), &[0b0111]), 1);
1199 assert_eq!(ev(&red(UnOp::RedXnor), &[0b0111]), 0);
1200 assert_eq!(ev(&red(UnOp::LogNot), &[0]), 1);
1201 let imp = bin(BinOp::LogImpl, var(0, u(4)), var(1, u(4)));
1203 assert_eq!(ev(&imp, &[8, 0]), 0);
1204 assert_eq!(ev(&imp, &[0, 0]), 1);
1205 assert_eq!(ev(&imp, &[8, 3]), 1);
1206 let eqv = bin(BinOp::LogEquiv, var(0, u(4)), var(1, u(4)));
1207 assert_eq!(ev(&eqv, &[8, 1]), 1);
1208 assert_eq!(ev(&eqv, &[8, 0]), 0);
1209 }
1210
1211 #[test]
1212 fn resize_extends_by_the_operand_signedness() {
1213 let zs = Expr::Resize(Box::new(var(0, s(4))), s(8));
1215 let zu = Expr::Resize(Box::new(var(0, u(4))), s(8));
1216 assert_eq!(ev(&zs, &[0b1000]), 0xF8);
1217 assert_eq!(ev(&zu, &[0b1000]), 0x08);
1218 let tr = Expr::Resize(Box::new(var(0, u(8))), u(4));
1219 assert_eq!(ev(&tr, &[0xAB]), 0xB);
1220 }
1221
1222 #[test]
1223 fn concat_and_slice() {
1224 let cat = Expr::Concat(vec![var(0, u(4)), var(1, u(4))], u(8));
1225 assert_eq!(ev(&cat, &[0xA, 0x5]), 0xA5);
1226 let sl = Expr::Slice(Box::new(var(0, u(8))), 2, u(3));
1227 assert_eq!(ev(&sl, &[0b1011_0100]), 0b101);
1228 }
1229
1230 #[test]
1231 fn all_distinct_is_every_pair_not_just_neighbours() {
1232 let e = Expr::AllDistinct(vec![var(0, u(2)), var(1, u(2)), var(2, u(2))]);
1233 assert_eq!(e.ty(), Ty::BIT);
1234 for a in 0..4u64 {
1235 for b in 0..4u64 {
1236 for d in 0..4u64 {
1237 let want = u64::from(a != b && b != d && a != d);
1238 assert_eq!(ev(&e, &[a, b, d]), want, "{a} {b} {d}");
1239 }
1240 }
1241 }
1242 let twice = Expr::AllDistinct(vec![var(0, u(2)), var(0, u(2))]);
1244 assert_eq!(ev(&twice, &[1]), 0);
1245 assert_eq!(ev(&Expr::AllDistinct(vec![var(0, u(2))]), &[1]), 1);
1247 }
1248
1249 #[test]
1250 fn all_distinct_reads_every_member_and_state() {
1251 let e = Expr::AllDistinct(vec![var(0, u(4)), Expr::State(0, u(4)), var(1, u(4))]);
1252 let st = |v: Option<u8>| Assignment {
1253 vars: vec![BigUint::from(1u8), BigUint::from(2u8)],
1254 states: vec![v.map(BigUint::from)],
1255 };
1256 assert_eq!(eval(&e, &st(Some(3))), Ok(BigUint::one()));
1257 assert_eq!(eval(&e, &st(Some(2))), Ok(BigUint::zero()));
1258 let dup = Expr::AllDistinct(vec![var(0, u(4)), var(0, u(4)), Expr::State(0, u(4))]);
1261 assert_eq!(
1262 eval(&dup, &st(None)),
1263 Err(ConstraintEvalError::StateUnknown(0))
1264 );
1265 let mut vars = vec![];
1266 e.for_each_var(&mut |v, _| vars.push(v));
1267 assert_eq!(vars, [0, 1]);
1268 let mut states = vec![];
1269 e.for_each_state(&mut |s| states.push(s));
1270 assert_eq!(states, [0]);
1271 }
1272
1273 #[test]
1274 fn all_distinct_substitution_keeps_member_order() {
1275 let e = Expr::AllDistinct(vec![var(0, u(4)), var(1, u(4)), var(2, u(4))]);
1276 let sub = e.substitute_vars(&mut |v, t| match v {
1277 1 => Expr::constant(t, 9u8),
1278 other => Expr::Var(other + 10, t),
1279 });
1280 assert_eq!(
1281 sub,
1282 Expr::AllDistinct(vec![var(10, u(4)), c(u(4), 9), var(12, u(4))])
1283 );
1284 }
1285
1286 #[test]
1287 fn all_distinct_validation_demands_one_member_type() {
1288 assert!(
1289 Expr::AllDistinct(vec![var(0, u(4)), var(1, u(4))])
1290 .validate()
1291 .is_ok()
1292 );
1293 assert!(Expr::AllDistinct(vec![]).validate().is_err());
1295 assert!(
1297 Expr::AllDistinct(vec![var(0, u(4)), var(1, u(8))])
1298 .validate()
1299 .is_err()
1300 );
1301 assert!(
1302 Expr::AllDistinct(vec![var(0, u(4)), var(1, s(4))])
1303 .validate()
1304 .is_err()
1305 );
1306 let bad_member = Expr::Const(u(4), BigUint::from(16u8));
1308 assert!(
1309 Expr::AllDistinct(vec![var(0, u(4)), bad_member])
1310 .validate()
1311 .is_err()
1312 );
1313 }
1314
1315 #[test]
1316 fn ite_reads_truthiness() {
1317 let e = Expr::Ite(
1318 Box::new(var(0, u(4))),
1319 Box::new(c(u(8), 7)),
1320 Box::new(c(u(8), 9)),
1321 u(8),
1322 );
1323 assert_eq!(ev(&e, &[0]), 9);
1324 assert_eq!(ev(&e, &[2]), 7);
1325 }
1326
1327 #[test]
1328 fn state_with_unknown_bits_is_an_error_never_zero() {
1329 let e = bin(BinOp::Add, Expr::State(0, u(8)), c(u(8), 1));
1330 let ok = Assignment {
1331 vars: vec![],
1332 states: vec![Some(BigUint::from(4u8))],
1333 };
1334 assert_eq!(eval(&e, &ok), Ok(BigUint::from(5u8)));
1335 let xz = Assignment {
1336 vars: vec![],
1337 states: vec![None],
1338 };
1339 assert_eq!(eval(&e, &xz), Err(ConstraintEvalError::StateUnknown(0)));
1340 let none = Assignment::default();
1341 assert_eq!(eval(&e, &none), Err(ConstraintEvalError::UnboundState(0)));
1342 }
1343
1344 #[test]
1345 fn wide_values_beyond_64_bits() {
1346 let w = 100;
1347 let a = Expr::Var(0, u(w));
1348 let e = bin(BinOp::Add, a, Expr::constant(u(w), 1u8));
1349 let all_ones = Assignment {
1350 vars: vec![mask(w)],
1351 states: vec![],
1352 };
1353 assert_eq!(eval(&e, &all_ones), Ok(BigUint::zero()));
1355 let half = Assignment {
1356 vars: vec![BigUint::one() << 99],
1357 states: vec![],
1358 };
1359 assert_eq!(eval(&e, &half), Ok((BigUint::one() << 99) + BigUint::one()));
1360 }
1361
1362 #[test]
1363 fn validation_rejects_ill_typed_trees() {
1364 let bad = Expr::Binary(
1366 BinOp::Add,
1367 Box::new(var(0, u(8))),
1368 Box::new(var(1, u(4))),
1369 u(8),
1370 );
1371 assert!(matches!(bad.validate(), Err(IrError::IllTyped { .. })));
1372 let bad = Expr::Binary(
1374 BinOp::Lt,
1375 Box::new(var(0, u(8))),
1376 Box::new(var(1, u(8))),
1377 u(8),
1378 );
1379 assert!(bad.validate().is_err());
1380 let bad = Expr::Slice(Box::new(var(0, u(8))), 6, u(4));
1382 assert!(bad.validate().is_err());
1383 let bad = Expr::Const(u(4), BigUint::from(16u8));
1385 assert!(bad.validate().is_err());
1386 let bad = Expr::Concat(vec![var(0, u(4)), var(1, u(4))], u(9));
1388 assert!(bad.validate().is_err());
1389 let bad = Expr::Binary(
1391 BinOp::Lt,
1392 Box::new(var(0, s(8))),
1393 Box::new(var(1, u(8))),
1394 Ty::BIT,
1395 );
1396 assert!(bad.validate().is_err());
1397 }
1398
1399 #[test]
1400 fn for_each_var_visits_every_read() {
1401 let e = bin(
1402 BinOp::Add,
1403 Expr::Concat(vec![var(3, u(4)), var(1, u(4))], u(8)),
1404 Expr::Ite(
1405 Box::new(var(2, u(1))),
1406 Box::new(Expr::Const(u(8), BigUint::one())),
1407 Box::new(Expr::State(0, u(8))),
1408 u(8),
1409 ),
1410 );
1411 let mut seen = vec![];
1412 e.for_each_var(&mut |v, _| seen.push(v));
1413 seen.sort_unstable();
1414 assert_eq!(seen, [1, 2, 3]);
1415 }
1416
1417 #[test]
1418 fn substitute_vars_replaces_and_preserves_evaluation() {
1419 let e = bin(
1423 BinOp::Add,
1424 var(0, u(8)),
1425 Expr::Concat(vec![var(1, u(4)), var(2, u(4))], u(8)),
1426 );
1427 let sub = e.substitute_vars(&mut |v, t| match v {
1428 0 => Expr::Var(1, t),
1429 1 => Expr::constant(t, 5u8),
1430 _ => Expr::Var(0, t),
1431 });
1432 sub.validate().expect("still well-typed");
1433 let mut seen = vec![];
1434 sub.for_each_var(&mut |v, _| seen.push(v));
1435 seen.sort_unstable();
1436 assert_eq!(seen, [0, 1], "x1 is gone, the others renumbered");
1437 let full = Assignment {
1438 vars: vec![BigUint::from(10u8), BigUint::from(5u8), BigUint::from(3u8)],
1439 states: vec![],
1440 };
1441 let part = Assignment {
1442 vars: vec![BigUint::from(3u8), BigUint::from(10u8)],
1443 states: vec![],
1444 };
1445 assert_eq!(eval(&e, &full), eval(&sub, &part));
1446 assert_eq!(eval(&sub, &part), Ok(BigUint::from(10u8 + 0x53)));
1447 }
1448}