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