1use cranelift_isle as isle;
7use isle::sema::{Pattern, Rule, TermEnv, TypeEnv};
8
9use crate::solver::encoded_ops::popcnt::popcnt;
10use crate::type_inference::RuleSemantics;
11use crate::Config;
12use easy_smt::{Response, SExpr};
13use std::cmp::Ordering;
14use std::collections::HashMap;
15use veri_ir::{
16 BinaryOp, ConcreteTest, Counterexample, Expr, TermSignature, Terminal, Type, TypeContext,
17 UnaryOp, VerificationResult,
18};
19
20mod encoded_ops;
21
22use encoded_ops::cls;
23use encoded_ops::clz;
24use encoded_ops::rev;
25use encoded_ops::subs;
26
27use crate::MAX_WIDTH;
28
29pub struct SolverCtx {
30 smt: easy_smt::Context,
31 pub find_widths: bool,
32 tyctx: TypeContext,
33 pub bitwidth: usize,
34 var_map: HashMap<String, SExpr>,
35 width_vars: HashMap<u32, String>,
36 width_assumptions: Vec<SExpr>,
37 pub additional_decls: Vec<(String, SExpr)>,
38 pub additional_assumptions: Vec<SExpr>,
39 pub additional_assertions: Vec<SExpr>,
40 fresh_bits_idx: usize,
41 lhs_load_args: Option<Vec<SExpr>>,
42 rhs_load_args: Option<Vec<SExpr>>,
43 lhs_store_args: Option<Vec<SExpr>>,
44 rhs_store_args: Option<Vec<SExpr>>,
45 load_return: Option<SExpr>,
46 lhs_flag: bool,
47}
48
49pub struct RuleCtx<'a> {
50 rule_sem: &'a RuleSemantics,
51 rule: &'a Rule,
52 termenv: &'a TermEnv,
53 typeenv: &'a TypeEnv,
54 config: &'a Config,
55}
56
57impl SolverCtx {
58 pub fn new_fresh_bits(&mut self, width: usize) -> SExpr {
59 let name = format!("fresh{}", self.fresh_bits_idx);
60 self.fresh_bits_idx += 1;
61 self.additional_decls
62 .push((name.clone(), self.smt.bit_vec_sort(self.smt.numeral(width))));
63 self.smt.atom(name)
64 }
65
66 fn new_fresh_int(&mut self) -> SExpr {
67 let name = format!("fresh{}", self.fresh_bits_idx);
68 self.fresh_bits_idx += 1;
69 self.additional_decls
70 .push((name.clone(), self.smt.int_sort()));
71 self.smt.atom(name)
72 }
73
74 fn new_fresh_bool(&mut self) -> SExpr {
75 let name = format!("fresh{}", self.fresh_bits_idx);
76 self.fresh_bits_idx += 1;
77 self.additional_decls
78 .push((name.clone(), self.smt.bool_sort()));
79 self.smt.atom(name)
80 }
81
82 fn declare(&mut self, name: String, typ: SExpr) -> SExpr {
83 let atom = self.smt.atom(&name);
84 self.additional_decls.push((name, typ));
85 atom
86 }
87
88 fn assume(&mut self, expr: SExpr) {
89 self.additional_assumptions.push(expr);
90 }
91
92 fn assert(&mut self, expr: SExpr) {
93 self.additional_assertions.push(expr);
94 }
95
96 fn bv(&self, value: i128, width: usize) -> SExpr {
99 if value < 0 {
100 return self
101 .smt
102 .list(vec![self.smt.atom("bvneg"), self.bv(-value, width)]);
103 }
104 self.smt.list(vec![
105 self.smt.atoms().und,
106 self.smt.atom(format!("bv{}", value)),
107 self.smt.numeral(width),
108 ])
109 }
110
111 fn int2bv(&self, width: usize, value: SExpr) -> SExpr {
113 self.smt.list(vec![
114 self.smt.list(vec![
115 self.smt.atoms().und,
116 self.smt.atom("int2bv"),
117 self.smt.numeral(width),
118 ]),
119 value,
120 ])
121 }
122
123 fn bv2nat(&self, value: SExpr) -> SExpr {
125 self.smt.list(vec![self.smt.atom("bv2nat"), value])
126 }
127
128 fn zero_extend(&self, padding: usize, value: SExpr) -> SExpr {
131 if padding == 0 {
132 return value;
133 }
134 self.smt.list(vec![
135 self.smt.list(vec![
136 self.smt.atoms().und,
137 self.smt.atom("zero_extend"),
138 self.smt.numeral(padding),
139 ]),
140 value,
141 ])
142 }
143
144 fn sign_extend(&self, padding: usize, value: SExpr) -> SExpr {
147 self.smt.list(vec![
148 self.smt.list(vec![
149 self.smt.atoms().und,
150 self.smt.atom("sign_extend"),
151 self.smt.numeral(padding),
152 ]),
153 value,
154 ])
155 }
156
157 fn extend_concrete(
159 &mut self,
160 dest_width: usize,
161 source: SExpr,
162 source_width: usize,
163 op: &str,
164 ) -> SExpr {
165 if dest_width < source_width {
166 log::warn!(
167 "Unexpected extend widths for {}: dest {} source {} ",
168 self.smt.display(source),
169 dest_width,
170 source_width,
171 );
172 self.assert(self.smt.false_());
173 return self.bv(
174 0,
175 if self.find_widths {
176 self.bitwidth
177 } else {
178 dest_width
179 },
180 );
181 }
182
183 let delta = dest_width - source_width;
184 if !self.find_widths {
185 return self.smt.list(vec![
186 self.smt.list(vec![
187 self.smt.atoms().und,
188 self.smt.atom(op),
189 self.smt.numeral(delta),
190 ]),
191 source,
192 ]);
193 }
194
195 let extract = self
198 .smt
199 .extract(source_width.wrapping_sub(1).try_into().unwrap(), 0, source);
200
201 let extend = self.smt.list(vec![
203 self.smt.list(vec![
204 self.smt.atoms().und,
205 self.smt.atom(op),
206 self.smt.numeral(delta),
207 ]),
208 extract,
209 ]);
210
211 let mut unconstrained_bits = 0;
221 if dest_width < self.bitwidth {
222 unconstrained_bits = self
223 .bitwidth
224 .checked_sub(delta)
225 .unwrap()
226 .checked_sub(source_width)
227 .unwrap();
228 }
229
230 if unconstrained_bits == 0 {
232 extend
233 } else {
234 let padding = self.smt.extract(
235 self.bitwidth.checked_sub(1).unwrap().try_into().unwrap(),
236 self.bitwidth
237 .checked_sub(unconstrained_bits)
238 .unwrap()
239 .try_into()
240 .unwrap(),
241 source,
242 );
243 self.smt.concat(padding, extend)
244 }
245 }
246
247 fn extend_symbolic(
253 &mut self,
254 dest_width: SExpr,
255 source: SExpr,
256 source_width: SExpr,
257 op: &str,
258 ) -> SExpr {
259 if self.find_widths {
260 return source;
261 }
262 let shift = self.smt.sub(dest_width, source_width);
264
265 let mut some_match = vec![];
266 let mut ite_str = source;
267
268 let matching = self.smt.eq(self.smt.numeral(0), shift);
270 some_match.push(matching);
271 ite_str = self.smt.ite(matching, source, ite_str);
272
273 for possible_delta in 1..self.bitwidth + 1 {
275 for possible_source in 1..self.bitwidth + 1 {
277 if possible_source + possible_delta > self.bitwidth {
280 continue;
281 }
282
283 let matching = self.smt.and(
285 self.smt.eq(self.smt.numeral(possible_delta), shift),
286 self.smt.eq(self.smt.numeral(possible_source), source_width),
287 );
288 some_match.push(matching);
289 let extend = self.extend_concrete(
290 possible_source + possible_delta,
291 source,
292 possible_source,
293 op,
294 );
295 ite_str = self.smt.ite(matching, extend, ite_str);
296 }
297 }
298 let some_shift_matches = self.smt.or_many(some_match);
299 self.width_assumptions.push(some_shift_matches);
300 ite_str
301 }
302
303 fn encode_rotate(&self, op: &str, source: SExpr, amount: SExpr, width: usize) -> SExpr {
304 let width_as_bv = self.bv(width.try_into().unwrap(), width);
308 let wrapped_amount = self.smt.bvurem(amount, width_as_bv);
309 let wrapped_delta = self.smt.bvsub(width_as_bv, wrapped_amount);
310 match op {
311 "rotate_left" => self.smt.bvor(
312 self.smt.bvshl(source, wrapped_amount),
313 self.smt.bvlshr(source, wrapped_delta),
314 ),
315 "rotate_right" => self.smt.bvor(
316 self.smt.bvshl(source, wrapped_delta),
317 self.smt.bvlshr(source, wrapped_amount),
318 ),
319 _ => unreachable!(),
320 }
321 }
322
323 fn rotate_symbolic(
327 &mut self,
328 source: SExpr,
329 source_width: usize,
330 amount: SExpr,
331 op: &str,
332 ) -> SExpr {
333 if self.find_widths {
334 return source;
335 }
336 let (s, a) = if self.find_widths {
337 let extract_source = self.smt.extract(
340 source_width.checked_sub(1).unwrap().try_into().unwrap(),
341 0,
342 source,
343 );
344
345 let extract_amount = self.smt.extract(
346 source_width.checked_sub(1).unwrap().try_into().unwrap(),
347 0,
348 amount,
349 );
350 (extract_source, extract_amount)
351 } else {
352 (source, amount)
353 };
354
355 let rotate = self.encode_rotate(op, s, a, source_width);
357
358 let unconstrained_bits = self.bitwidth.checked_sub(source_width).unwrap();
368
369 if unconstrained_bits == 0 || !self.find_widths {
371 rotate
372 } else {
373 let padding = self.smt.extract(
374 self.bitwidth.checked_sub(1).unwrap().try_into().unwrap(),
375 self.bitwidth
376 .checked_sub(unconstrained_bits)
377 .unwrap()
378 .try_into()
379 .unwrap(),
380 source,
381 );
382 self.smt.concat(padding, rotate)
383 }
384 }
385
386 fn rotate_symbolic_dyn_source_width(
392 &mut self,
393 source: SExpr,
394 source_width: SExpr,
395 amount: SExpr,
396 op: &str,
397 ) -> SExpr {
398 if self.find_widths {
399 return source;
400 }
401 let mut some_match = vec![];
402 let mut ite_str = source;
403
404 let matching = self.smt.eq(self.bv(0, self.bitwidth), amount);
406 some_match.push(matching);
407 ite_str = self.smt.ite(matching, source, ite_str);
408
409 for possible_source in [8usize, 16, 32, 64] {
411 let matching = self.smt.eq(self.smt.numeral(possible_source), source_width);
413 some_match.push(matching);
414
415 let extract_source = self.smt.extract(
418 possible_source.checked_sub(1).unwrap().try_into().unwrap(),
419 0,
420 source,
421 );
422 let extract_amount = self.smt.extract(
423 possible_source.checked_sub(1).unwrap().try_into().unwrap(),
424 0,
425 amount,
426 );
427
428 let rotate = self.encode_rotate(op, extract_source, extract_amount, possible_source);
432
433 let unconstrained_bits = self.bitwidth.checked_sub(possible_source).unwrap();
443
444 let rotate = if unconstrained_bits == 0 {
446 rotate
447 } else {
448 let padding = self.smt.extract(
449 self.bitwidth.checked_sub(1).unwrap().try_into().unwrap(),
450 self.bitwidth
451 .checked_sub(unconstrained_bits)
452 .unwrap()
453 .try_into()
454 .unwrap(),
455 source,
456 );
457 self.smt.concat(padding, rotate)
458 };
459
460 ite_str = self.smt.ite(matching, rotate, ite_str);
461 }
462 let some_shift_matches = self.smt.or_many(some_match);
463 self.width_assumptions.push(some_shift_matches);
464 ite_str
465 }
466
467 pub fn widen_to_register_width(
468 &mut self,
469 tyvar: u32,
470 narrow_width: usize,
471 narrow_decl: SExpr,
472 name: Option<String>,
473 ) -> SExpr {
474 let width = self.bitwidth.checked_sub(narrow_width).unwrap();
475 if width > 0 {
476 let mut narrow_name = format!("narrow__{}", tyvar);
477 let mut wide_name = format!("wide__{}", tyvar);
478 if let Some(s) = name {
479 narrow_name = format!("{}_{}", s, narrow_name);
480 wide_name = format!("{}_{}", s, wide_name);
481 }
482 self.assume(self.smt.eq(self.smt.atom(&narrow_name), narrow_decl));
483 self.additional_decls.push((
484 narrow_name.clone(),
485 self.smt.bit_vec_sort(self.smt.numeral(narrow_width)),
486 ));
487 self.additional_decls.push((
488 wide_name.clone(),
489 self.smt.bit_vec_sort(self.smt.numeral(self.bitwidth)),
490 ));
491 let padding = self.new_fresh_bits(width);
492 self.assume(self.smt.eq(
493 self.smt.atom(&wide_name),
494 self.smt.concat(padding, self.smt.atom(narrow_name)),
495 ));
496 self.smt.atom(wide_name)
497 } else if let Some(s) = name {
498 self.assume(self.smt.eq(self.smt.atom(&s), narrow_decl));
499 self.smt.atom(&s)
500 } else {
501 narrow_decl
502 }
503 }
504
505 pub fn get_expr_width_var(&self, e: &Expr) -> Option<SExpr> {
506 if let Some(tyvar) = self.tyctx.tyvars.get(e) {
507 self.width_vars.get(tyvar).map(|s| self.smt.atom(s))
508 } else {
509 None
510 }
511 }
512
513 pub fn vir_to_smt_ty(&self, ty: &Type) -> SExpr {
514 match ty {
515 Type::BitVector(w) => {
516 let width = w.unwrap_or(self.bitwidth);
517 self.smt.bit_vec_sort(self.smt.numeral(width))
518 }
519 Type::Int => self.smt.int_sort(),
520 Type::Bool | Type::Unit => self.smt.bool_sort(),
521 }
522 }
523
524 pub fn get_type(&self, x: &Expr) -> Option<&Type> {
525 self.tyctx.tymap.get(self.tyctx.tyvars.get(x)?)
526 }
527
528 pub fn get_expr_value(&self, e: &Expr) -> Option<i128> {
529 if let Some(tyvar) = self.tyctx.tyvars.get(e) {
530 self.tyctx.tyvals.get(tyvar).copied()
531 } else {
532 None
533 }
534 }
535
536 pub fn static_width(&self, x: &Expr) -> Option<usize> {
537 match self.get_type(x) {
538 Some(Type::BitVector(w)) => *w,
539 _ => None,
540 }
541 }
542
543 pub fn assume_same_width(&mut self, x: &Expr, y: &Expr) {
544 let xw = self.get_expr_width_var(x).unwrap();
545 let yw = self.get_expr_width_var(y).unwrap();
546 self.width_assumptions.push(self.smt.eq(xw, yw));
547 }
548
549 pub fn assume_same_width_from_sexpr(&mut self, x: SExpr, y: &Expr) {
550 let yw = self.get_expr_width_var(y).unwrap();
551 self.width_assumptions.push(self.smt.eq(x, yw));
552 }
553
554 pub fn assume_comparable_types(&mut self, x: &Expr, y: &Expr) {
555 match (self.get_type(x), self.get_type(y)) {
556 (None, _) | (_, None) => panic!("Missing type(s) {:?} {:?}", x, y),
557 (Some(Type::Bool), Some(Type::Bool))
558 | (Some(Type::Int), Some(Type::Int))
559 | (Some(Type::Unit), Some(Type::Unit)) => (),
560 (Some(Type::BitVector(Some(xw))), Some(Type::BitVector(Some(yw)))) => {
561 assert_eq!(xw, yw, "incompatible {:?} {:?}", x, y)
562 }
563 (_, _) => self.assume_same_width(x, y),
564 }
565 }
566
567 pub fn vir_expr_to_sexp(&mut self, e: Expr) -> SExpr {
568 let tyvar = self.tyctx.tyvars.get(&e);
569 let ty = self.get_type(&e);
570 let width = self.get_expr_width_var(&e);
571 let static_expr_width = self.static_width(&e);
572 match e {
573 Expr::Terminal(t) => match t {
574 Terminal::Literal(v, tyvar) => {
575 let lit = self.smt.atom(v);
576 if self.find_widths && matches!(ty.unwrap(), Type::BitVector(_)) {
577 self.widen_to_register_width(tyvar, static_expr_width.unwrap(), lit, None)
578 } else {
579 lit
580 }
581 }
582 Terminal::Var(v) => match self.var_map.get(&v) {
583 Some(o) => *o,
584 None => self.smt.atom(v),
585 },
586 Terminal::Const(i, _) => match ty.unwrap() {
587 Type::BitVector(w) => {
588 let width = w.unwrap_or(self.bitwidth);
589 let narrow_decl = self.bv(i, width);
590 if self.find_widths {
591 self.zero_extend(self.bitwidth - width, narrow_decl)
592 } else {
593 narrow_decl
594 }
595 }
596 Type::Int => self.smt.numeral(i),
597 Type::Bool => {
598 if i == 0 {
599 self.smt.false_()
600 } else {
601 self.smt.true_()
602 }
603 }
604 Type::Unit => self.smt.true_(),
605 },
606 Terminal::True => self.smt.true_(),
607 Terminal::False => self.smt.false_(),
608 Terminal::Wildcard(_) => match ty.unwrap() {
609 Type::BitVector(Some(w)) if !self.find_widths => self.new_fresh_bits(*w),
610 Type::BitVector(_) => self.new_fresh_bits(self.bitwidth),
611 Type::Int => self.new_fresh_int(),
612 Type::Bool => self.new_fresh_bool(),
613 Type::Unit => self.smt.true_(),
614 },
615 },
616 Expr::Unary(op, arg) => {
617 let op = match op {
618 UnaryOp::Not => "not",
619 UnaryOp::BVNeg => {
620 if self.find_widths {
621 self.assume_same_width_from_sexpr(width.unwrap(), &arg);
622 }
623 "bvneg"
624 }
625 UnaryOp::BVNot => {
626 if self.find_widths {
627 self.assume_same_width_from_sexpr(width.unwrap(), &arg);
628 }
629 "bvnot"
630 }
631 };
632 let subexp = self.vir_expr_to_sexp(*arg);
633 self.smt.list(vec![self.smt.atom(op), subexp])
634 }
635 Expr::Binary(op, x, y) => {
636 if self.find_widths {
637 match op {
638 BinaryOp::BVMul
639 | BinaryOp::BVUDiv
640 | BinaryOp::BVSDiv
641 | BinaryOp::BVUrem
642 | BinaryOp::BVSrem
643 | BinaryOp::BVAdd
644 | BinaryOp::BVSub
645 | BinaryOp::BVAnd
646 | BinaryOp::BVOr
647 | BinaryOp::BVShl
648 | BinaryOp::BVShr
649 | BinaryOp::BVAShr
650 | BinaryOp::BVRotl
651 | BinaryOp::BVRotr => self.assume_same_width_from_sexpr(width.unwrap(), &x),
652 BinaryOp::Eq => {
653 if let Some(Type::BitVector(_)) = self.get_type(&x) {
654 self.assume_comparable_types(&x, &y)
655 }
656 }
657 _ => (),
658 };
659 self.assume_comparable_types(&x, &y);
660 }
661 match op {
662 BinaryOp::BVRotl => {
663 let source_width = self.static_width(&x);
664 match source_width {
665 Some(w) => {
666 let xs = self.vir_expr_to_sexp(*x);
667 let ys = self.vir_expr_to_sexp(*y);
668 return self.rotate_symbolic(xs, w, ys, "rotate_left");
669 }
670 None => {
671 let arg_width = self.get_expr_width_var(&x).unwrap();
672 let xs = self.vir_expr_to_sexp(*x);
673 let ys = self.vir_expr_to_sexp(*y);
674 return self.rotate_symbolic_dyn_source_width(
675 xs,
676 arg_width,
677 ys,
678 "rotate_left",
679 );
680 }
681 }
682 }
683 BinaryOp::BVRotr => {
684 let source_width = self.static_width(&x);
685 match source_width {
686 Some(w) => {
687 let xs = self.vir_expr_to_sexp(*x);
688 let ys = self.vir_expr_to_sexp(*y);
689 return self.rotate_symbolic(xs, w, ys, "rotate_right");
690 }
691 None => {
692 let arg_width = self.get_expr_width_var(&x).unwrap();
693 let xs = self.vir_expr_to_sexp(*x);
694 let ys = self.vir_expr_to_sexp(*y);
695 return self.rotate_symbolic_dyn_source_width(
696 xs,
697 arg_width,
698 ys,
699 "rotate_right",
700 );
701 }
702 }
703 }
704 BinaryOp::BVShr => {
706 let arg_width = if self.find_widths {
707 self.get_expr_width_var(&x).unwrap()
708 } else {
709 self.smt.numeral(self.static_width(&x).unwrap())
710 };
711 let xs = self.vir_expr_to_sexp(*x);
712
713 if self.find_widths {
718 let y_static_width = self.static_width(&y).unwrap_or(8);
720 let y_rec = self.vir_expr_to_sexp(*y);
721 if self.find_widths {
722 return xs;
723 }
724 let extract = self.smt.extract(
725 y_static_width.checked_sub(1).unwrap().try_into().unwrap(),
726 0,
727 y_rec,
728 );
729 let ys = self.zero_extend(self.bitwidth - y_static_width, extract);
730 let arg_width_as_bv = self.int2bv(self.bitwidth, arg_width);
731 let bitwidth_as_bv =
732 self.bv(self.bitwidth.try_into().unwrap(), self.bitwidth);
733 let extra_shift = self.smt.bvsub(bitwidth_as_bv, arg_width_as_bv);
734 let shl_to_zero = self.smt.bvshl(xs, extra_shift);
735
736 let amt_plus_extra = self.smt.bvadd(ys, extra_shift);
737 return self.smt.bvlshr(shl_to_zero, amt_plus_extra);
738 } else {
739 let ys = self.vir_expr_to_sexp(*y);
740 return self.smt.bvlshr(xs, ys);
741 }
742 }
743 BinaryOp::BVAShr => {
744 let arg_width = if self.find_widths {
745 self.get_expr_width_var(&x).unwrap()
746 } else {
747 self.smt.numeral(self.static_width(&x).unwrap())
748 };
749 let xs = self.vir_expr_to_sexp(*x);
750
751 if self.find_widths {
756 let y_static_width = self.static_width(&y).unwrap_or(8);
758 let ys = self.vir_expr_to_sexp(*y);
759 let extract = self.smt.extract(
760 y_static_width.checked_sub(1).unwrap().try_into().unwrap(),
761 0,
762 ys,
763 );
764 let ysext = self.zero_extend(self.bitwidth - y_static_width, extract);
765
766 let arg_width_as_bv = self.int2bv(self.bitwidth, arg_width);
767 let bitwidth_as_bv =
768 self.bv(self.bitwidth.try_into().unwrap(), self.bitwidth);
769 let extra_shift = self.smt.bvsub(bitwidth_as_bv, arg_width_as_bv);
770 let shl_to_zero = self.smt.bvshl(xs, extra_shift);
771
772 let amt_plus_extra = self.smt.bvadd(ysext, extra_shift);
773 return self.smt.bvashr(shl_to_zero, amt_plus_extra);
774 } else {
775 let ys = self.vir_expr_to_sexp(*y);
776 return self.smt.bvashr(xs, ys);
777 }
778 }
779 _ => (),
780 };
781 let op_str = match op {
782 BinaryOp::And => "and",
783 BinaryOp::Or => "or",
784 BinaryOp::Imp => "=>",
785 BinaryOp::Eq => "=",
786 BinaryOp::Lte => match (self.get_type(&x), self.get_type(&y)) {
787 (Some(Type::Int), Some(Type::Int)) => "<=",
788 (Some(Type::BitVector(_)), Some(Type::BitVector(_))) => "bvule",
789 _ => unreachable!(),
790 },
791 BinaryOp::Lt => match (self.get_type(&x), self.get_type(&y)) {
792 (Some(Type::Int), Some(Type::Int)) => "<",
793 (Some(Type::BitVector(_)), Some(Type::BitVector(_))) => "bvult",
794 _ => unreachable!(),
795 },
796 BinaryOp::BVSgt => "bvsgt",
797 BinaryOp::BVSgte => "bvsge",
798 BinaryOp::BVSlt => "bvslt",
799 BinaryOp::BVSlte => "bvsle",
800 BinaryOp::BVUgt => "bvugt",
801 BinaryOp::BVUgte => "bvuge",
802 BinaryOp::BVUlt => "bvult",
803 BinaryOp::BVUlte => "bvule",
804 BinaryOp::BVMul => "bvmul",
805 BinaryOp::BVUDiv => "bvudiv",
806 BinaryOp::BVSDiv => "bvsdiv",
807 BinaryOp::BVAdd => "bvadd",
808 BinaryOp::BVSub => "bvsub",
809 BinaryOp::BVUrem => "bvurem",
810 BinaryOp::BVSrem => "bvsrem",
811 BinaryOp::BVAnd => "bvand",
812 BinaryOp::BVOr => "bvor",
813 BinaryOp::BVXor => "bvxor",
814 BinaryOp::BVShl => "bvshl",
815 BinaryOp::BVSaddo => "bvsaddo",
816 _ => unreachable!("{:?}", op),
817 };
818 match static_expr_width {
821 Some(w) if w < self.bitwidth && self.find_widths => {
822 let h: i32 = (w - 1).try_into().unwrap();
823 let x_sexp = self.vir_expr_to_sexp(*x);
824 let y_sexp = self.vir_expr_to_sexp(*y);
825 self.zero_extend(
826 self.bitwidth.checked_sub(w).unwrap(),
827 self.smt.list(vec![
828 self.smt.atom(op_str),
829 self.smt.extract(h, 0, x_sexp),
830 self.smt.extract(h, 0, y_sexp),
831 ]),
832 )
833 }
834 _ => {
835 let x_sexp = self.vir_expr_to_sexp(*x);
836 let y_sexp = self.vir_expr_to_sexp(*y);
837 self.smt.list(vec![self.smt.atom(op_str), x_sexp, y_sexp])
838 }
839 }
840 }
841 Expr::BVIntToBV(w, x) => {
842 let x_sexp = self.vir_expr_to_sexp(*x);
843 if self.find_widths {
844 let padded_width = self.bitwidth - w;
845 self.zero_extend(padded_width, self.int2bv(w, x_sexp))
846 } else {
847 self.int2bv(w, x_sexp)
848 }
849 }
850 Expr::BVToInt(x) => {
851 let x_sexp = self.vir_expr_to_sexp(*x);
852 self.bv2nat(x_sexp)
853 }
854 Expr::BVZeroExtTo(i, x) => {
855 let arg_width = if self.find_widths {
856 let expr_width = width.unwrap();
857 self.width_assumptions
858 .push(self.smt.eq(expr_width, self.smt.numeral(i)));
859 self.get_expr_width_var(&x).unwrap()
860 } else {
861 self.smt.numeral(self.static_width(&x).unwrap())
862 };
863 let static_width = self.static_width(&x);
864 let xs = self.vir_expr_to_sexp(*x);
865 if let Some(size) = static_width {
866 self.extend_concrete(i, xs, size, "zero_extend")
867 } else {
868 self.extend_symbolic(self.smt.numeral(i), xs, arg_width, "zero_extend")
869 }
870 }
871 Expr::BVZeroExtToVarWidth(i, x) => {
872 let static_arg_width = self.static_width(&x);
873 let arg_width = self.get_expr_width_var(&x);
874 let is = self.vir_expr_to_sexp(*i);
875 let xs = self.vir_expr_to_sexp(*x);
876 if self.find_widths {
877 let expr_width = width.unwrap();
878 self.width_assumptions.push(self.smt.eq(expr_width, is));
879 }
880 if let (Some(arg_size), Some(e_size)) = (static_arg_width, static_expr_width) {
881 self.extend_concrete(e_size, xs, arg_size, "zero_extend")
882 } else {
883 self.extend_symbolic(is, xs, arg_width.unwrap(), "zero_extend")
884 }
885 }
886 Expr::BVSignExtTo(i, x) => {
887 let arg_width = if self.find_widths {
888 let expr_width = width.unwrap();
889 self.width_assumptions
890 .push(self.smt.eq(expr_width, self.smt.numeral(i)));
891 self.get_expr_width_var(&x).unwrap()
892 } else {
893 self.smt.numeral(self.static_width(&x).unwrap())
894 };
895 let static_width = self.static_width(&x);
896 let xs = self.vir_expr_to_sexp(*x);
897 if let Some(size) = static_width {
898 self.extend_concrete(i, xs, size, "sign_extend")
899 } else {
900 self.extend_symbolic(self.smt.numeral(i), xs, arg_width, "sign_extend")
901 }
902 }
903 Expr::BVSignExtToVarWidth(i, x) => {
904 let static_arg_width = self.static_width(&x);
905 let arg_width = self.get_expr_width_var(&x);
906 let is = self.vir_expr_to_sexp(*i);
907 let xs = self.vir_expr_to_sexp(*x);
908 if self.find_widths {
909 let expr_width = width.unwrap();
910 self.width_assumptions.push(self.smt.eq(expr_width, is));
911 }
912 if let (Some(arg_size), Some(e_size)) = (static_arg_width, static_expr_width) {
913 self.extend_concrete(e_size, xs, arg_size, "sign_extend")
914 } else {
915 self.extend_symbolic(is, xs, arg_width.unwrap(), "sign_extend")
916 }
917 }
918 Expr::BVConvTo(x, y) => {
919 if self.find_widths {
920 let expr_width = width.unwrap();
921 let dyn_width = self.vir_expr_to_sexp(*x);
922 let eq = self.smt.eq(expr_width, dyn_width);
923 self.width_assumptions.push(eq);
924 self.vir_expr_to_sexp(*y)
925 } else {
926 let arg_width = self.static_width(&y).unwrap();
927 match ty {
928 Some(Type::BitVector(Some(w))) => match arg_width.cmp(w) {
929 Ordering::Less => {
930 let padding =
931 self.new_fresh_bits(w.checked_sub(arg_width).unwrap());
932 let ys = self.vir_expr_to_sexp(*y);
933 self.smt.concat(padding, ys)
934 }
935 Ordering::Greater => {
936 let new = (w - 1).try_into().unwrap();
937 let ys = self.vir_expr_to_sexp(*y);
938 self.smt.extract(new, 0, ys)
939 }
940 Ordering::Equal => self.vir_expr_to_sexp(*y),
941 },
942 _ => unreachable!("{:?}, {:?}", x, y),
943 }
944 }
945 }
946 Expr::WidthOf(x) => {
947 if self.find_widths {
948 self.get_expr_width_var(&x).unwrap()
949 } else {
950 self.smt.numeral(self.static_width(&x).unwrap())
951 }
952 }
953 Expr::BVExtract(i, j, x) => {
954 assert!(i >= j);
955 if self.get_type(&x).is_some() {
956 let xs = self.vir_expr_to_sexp(*x);
957 if j == 0 && i == self.bitwidth - 1 && self.find_widths {
959 return xs;
960 }
961 let extract =
962 self.smt
963 .extract(i.try_into().unwrap(), j.try_into().unwrap(), xs);
964 let new_width = i - j + 1;
965 if new_width < self.bitwidth && self.find_widths {
966 let padding =
967 self.new_fresh_bits(self.bitwidth.checked_sub(new_width).unwrap());
968 self.smt.concat(padding, extract)
969 } else {
970 extract
971 }
972 } else {
973 unreachable!("Must perform extraction on bv with known width")
974 }
975 }
976 Expr::Conditional(c, t, e) => {
977 if self.find_widths && matches!(ty, Some(Type::BitVector(_))) {
978 self.assume_same_width_from_sexpr(width.unwrap(), &t);
979 self.assume_same_width_from_sexpr(width.unwrap(), &e);
980 }
981 let cs = self.vir_expr_to_sexp(*c);
982 let ts = self.vir_expr_to_sexp(*t);
983 let es = self.vir_expr_to_sexp(*e);
984 self.smt.ite(cs, ts, es)
985 }
986 Expr::Switch(c, cases) => {
987 if self.find_widths {
988 if matches!(ty, Some(Type::BitVector(_))) {
989 for (_, b) in &cases {
990 self.assume_same_width_from_sexpr(width.unwrap(), b);
991 }
992 }
993 let cty = self.get_type(&c);
994 if matches!(cty, Some(Type::BitVector(_))) {
995 let cwidth = self.get_expr_width_var(&c);
996 for (m, _) in &cases {
997 self.assume_same_width_from_sexpr(cwidth.unwrap(), m);
998 }
999 }
1000 }
1001 let cs = self.vir_expr_to_sexp(*c);
1002 let mut case_sexprs: Vec<(SExpr, SExpr)> = cases
1003 .iter()
1004 .map(|(m, b)| {
1005 (
1006 self.vir_expr_to_sexp(m.clone()),
1007 self.vir_expr_to_sexp(b.clone()),
1008 )
1009 })
1010 .collect();
1011
1012 let some_case_matches: Vec<SExpr> = case_sexprs
1014 .iter()
1015 .map(|(m, _)| self.smt.eq(cs, *m))
1016 .collect();
1017 self.assert(self.smt.or_many(some_case_matches.clone()));
1018
1019 let (_, last_body) = case_sexprs.remove(case_sexprs.len() - 1);
1020
1021 case_sexprs.iter().rev().fold(last_body, |acc, (m, b)| {
1023 self.smt.ite(self.smt.eq(cs, *m), *b, acc)
1024 })
1025 }
1026 Expr::CLZ(e) => {
1027 let tyvar = *tyvar.unwrap();
1028 if self.find_widths {
1029 self.assume_same_width_from_sexpr(width.unwrap(), &e);
1030 }
1031 let es = self.vir_expr_to_sexp(*e);
1032 match static_expr_width {
1033 Some(1) => clz::clz1(self, es, tyvar),
1034 Some(8) => clz::clz8(self, es, tyvar),
1035 Some(16) => clz::clz16(self, es, tyvar),
1036 Some(32) => clz::clz32(self, es, tyvar),
1037 Some(64) => clz::clz64(self, es, tyvar),
1038 Some(w) => unreachable!("Unexpected CLZ width {}", w),
1039 None => unreachable!("Need static CLZ width"),
1040 }
1041 }
1042 Expr::CLS(e) => {
1043 let tyvar = *tyvar.unwrap();
1044 if self.find_widths {
1045 self.assume_same_width_from_sexpr(width.unwrap(), &e);
1046 }
1047 let es = self.vir_expr_to_sexp(*e);
1048 match static_expr_width {
1049 Some(1) => cls::cls1(self, tyvar),
1050 Some(8) => cls::cls8(self, es, tyvar),
1051 Some(16) => cls::cls16(self, es, tyvar),
1052 Some(32) => cls::cls32(self, es, tyvar),
1053 Some(64) => cls::cls64(self, es, tyvar),
1054 Some(w) => unreachable!("Unexpected CLS width {}", w),
1055 None => unreachable!("Need static CLS width"),
1056 }
1057 }
1058 Expr::Rev(e) => {
1059 let tyvar = *tyvar.unwrap();
1060 if self.find_widths {
1061 self.assume_same_width_from_sexpr(width.unwrap(), &e);
1062 }
1063 let es = self.vir_expr_to_sexp(*e);
1064 match static_expr_width {
1065 Some(1) => rev::rev1(self, es, tyvar),
1066 Some(8) => rev::rev8(self, es, tyvar),
1067 Some(16) => rev::rev16(self, es, tyvar),
1068 Some(32) => rev::rev32(self, es, tyvar),
1069 Some(64) => rev::rev64(self, es, tyvar),
1070 Some(w) => unreachable!("Unexpected CLS width {}", w),
1071 None => unreachable!("Need static CLS width"),
1072 }
1073 }
1074 Expr::BVSubs(ty, x, y) => {
1075 let tyvar = *tyvar.unwrap();
1076 if self.find_widths {
1077 self.assume_comparable_types(&x, &y);
1078 }
1079 let ety = self.vir_expr_to_sexp(*ty);
1080 let ex = self.vir_expr_to_sexp(*x);
1081 let ey = self.vir_expr_to_sexp(*y);
1082
1083 let encoded_32 = subs::subs(self, 32, ex, ey, tyvar);
1084 let encoded_64 = subs::subs(self, 64, ex, ey, tyvar);
1085
1086 self.smt.ite(
1087 self.smt.eq(ety, self.smt.numeral(32)),
1088 encoded_32,
1089 encoded_64,
1090 )
1091 }
1092 Expr::BVPopcnt(x) => {
1093 let tyvar = *tyvar.unwrap();
1094 if self.find_widths {
1095 self.assume_same_width_from_sexpr(width.unwrap(), &x);
1096 }
1097 let ex = self.vir_expr_to_sexp(*x);
1098
1099 match static_expr_width {
1100 Some(8) => {
1101 let p = popcnt(self, 8, ex, tyvar);
1102 if self.find_widths {
1103 self.zero_extend(self.bitwidth - 8, p)
1104 } else {
1105 p
1106 }
1107 }
1108 Some(16) => {
1109 let p = popcnt(self, 16, ex, tyvar);
1110 if self.find_widths {
1111 self.zero_extend(self.bitwidth - 8, p)
1112 } else {
1113 self.zero_extend(8, p)
1114 }
1115 }
1116 Some(32) => {
1117 let p = popcnt(self, 32, ex, tyvar);
1118 if self.find_widths {
1119 self.zero_extend(self.bitwidth - 8, p)
1120 } else {
1121 self.zero_extend(24, p)
1122 }
1123 }
1124 Some(64) => {
1125 let p = popcnt(self, 64, ex, tyvar);
1126 if self.find_widths {
1127 self.zero_extend(self.bitwidth - 8, p)
1128 } else {
1129 self.zero_extend(56, p)
1130 }
1131 }
1132 Some(w) => unreachable!("Unexpected popcnt width {}", w),
1133 None => unreachable!("Need static popcnt width"),
1134 }
1135 }
1136 Expr::BVConcat(xs) => {
1137 if self.find_widths {
1138 let widths: Vec<SExpr> = xs
1139 .iter()
1140 .map(|x| self.get_expr_width_var(x).unwrap())
1141 .collect();
1142 let sum = self.smt.plus_many(widths);
1143 self.width_assumptions
1144 .push(self.smt.eq(width.unwrap(), sum));
1145 }
1146 let mut sexprs: Vec<SExpr> = xs
1147 .iter()
1148 .map(|x| self.vir_expr_to_sexp(x.clone()))
1149 .collect();
1150 let last = sexprs.remove(sexprs.len() - 1);
1151
1152 if self.find_widths {
1154 return sexprs[0];
1155 }
1156 sexprs
1158 .iter()
1159 .rev()
1160 .fold(last, |acc, x| self.smt.concat(*x, acc))
1161 }
1162 Expr::LoadEffect(x, y, z) => {
1163 let ex = self.vir_expr_to_sexp(*x);
1164 let ey = self.vir_expr_to_sexp(*y);
1165 let ez = self.vir_expr_to_sexp(*z);
1166
1167 if self.find_widths {
1168 self.width_assumptions.push(self.smt.eq(width.unwrap(), ey));
1169 }
1170
1171 if self.lhs_flag {
1172 if self.lhs_load_args.is_some() {
1173 panic!("Only one load on the LHS currently supported, found multiple.")
1174 }
1175 self.lhs_load_args = Some(vec![ex, ey, ez]);
1176 let load_ret = if self.find_widths {
1177 self.new_fresh_bits(self.bitwidth)
1178 } else {
1179 self.new_fresh_bits(static_expr_width.unwrap())
1180 };
1181 self.load_return = Some(load_ret);
1182 load_ret
1183 } else {
1184 if self.rhs_load_args.is_some() {
1185 panic!("Only one load on the RHS currently supported, found miltiple.")
1186 }
1187 self.rhs_load_args = Some(vec![ex, ey, ez]);
1188 self.load_return.unwrap()
1189 }
1190 }
1191 Expr::StoreEffect(w, x, y, z) => {
1192 let ew = self.vir_expr_to_sexp(*w);
1193 let ex = self.vir_expr_to_sexp(*x);
1194 let ez = self.vir_expr_to_sexp(*z);
1195
1196 if self.find_widths {
1197 let y_width = self.get_expr_width_var(&y).unwrap();
1198 self.width_assumptions.push(self.smt.eq(y_width, ex));
1199 }
1200 let ey = self.vir_expr_to_sexp(*y);
1201
1202 if self.lhs_flag {
1203 self.lhs_store_args = Some(vec![ew, ex, ey, ez]);
1204 } else {
1205 self.rhs_store_args = Some(vec![ew, ex, ey, ez]);
1206 }
1207 self.smt.atom("true")
1208 }
1209 }
1210 }
1211
1212 fn check_assumptions_feasibility(
1214 &mut self,
1215 assumptions: &[SExpr],
1216 term_input_bs: &[String],
1217 config: &Config,
1218 ) -> VerificationResult {
1219 log::debug!("Checking assumption feasibility");
1220 self.smt.push().unwrap();
1221 for (i, a) in assumptions.iter().enumerate() {
1222 self.smt
1223 .assert(self.smt.named(format!("assum{i}"), *a))
1224 .unwrap();
1225 }
1226
1227 let res = match self.smt.check() {
1228 Ok(Response::Sat) => {
1229 if !config.distinct_check || term_input_bs.is_empty() {
1230 log::debug!("Assertion list is feasible for at least one input!");
1231 self.smt.pop().unwrap();
1232 return VerificationResult::Success;
1233 }
1234 let mut not_all_same = vec![];
1236 let atoms: Vec<SExpr> = term_input_bs.iter().map(|n| self.smt.atom(n)).collect();
1237 let solution = self.smt.get_value(atoms).unwrap();
1238 assert_eq!(term_input_bs.len(), solution.len());
1239 for (variable, value) in solution {
1240 not_all_same.push(self.smt.not(self.smt.eq(variable, value)));
1241 }
1242 match not_all_same.len().cmp(&1) {
1243 Ordering::Equal => self.smt.assert(not_all_same[0]).unwrap(),
1244 Ordering::Greater => self.smt.assert(self.smt.and_many(not_all_same)).unwrap(),
1245 Ordering::Less => unreachable!("must have some BV inputs"),
1246 }
1247 match self.smt.check() {
1248 Ok(Response::Sat) => {
1249 log::debug!("Assertion list is feasible for two distinct inputs");
1250 VerificationResult::Success
1251 }
1252 Ok(Response::Unsat) => {
1253 log::debug!("Assertion list is only feasible for one input with distinct BV values!");
1254 VerificationResult::NoDistinctModels
1255 }
1256 Ok(Response::Unknown) => {
1257 panic!("Solver said 'unk'");
1258 }
1259 Err(err) => {
1260 unreachable!("Error! {:?}", err);
1261 }
1262 }
1263 }
1264 Ok(Response::Unsat) => {
1265 log::debug!("Assertion list is infeasible!");
1266 let unsat = self.smt.get_unsat_core().unwrap();
1267 log::debug!("Unsat core:\n{}", self.smt.display(unsat));
1268 VerificationResult::InapplicableRule
1269 }
1270 Ok(Response::Unknown) => {
1271 panic!("Solver said 'unk'");
1272 }
1273 Err(err) => {
1274 unreachable!("Error! {:?}", err);
1275 }
1276 };
1277 self.smt.pop().unwrap();
1278 res
1279 }
1280
1281 fn display_hex_to_bin(&self, value: SExpr) -> String {
1282 let sexpr_hex_prefix = "#x";
1283 let val_str = self.smt.display(value).to_string();
1284 if val_str.starts_with(sexpr_hex_prefix) {
1285 let without_prefix = val_str.trim_start_matches("#x");
1286 let as_unsigned = u128::from_str_radix(without_prefix, 16).unwrap();
1287 match without_prefix.len() {
1290 2 => format!("{}|{:#010b}", self.smt.display(value), as_unsigned),
1291 3 => format!("{}|{:#014b}", self.smt.display(value), as_unsigned),
1292 4 => format!("{}|{:#018b}", self.smt.display(value), as_unsigned),
1293 8 => format!("{}|{:#034b}", self.smt.display(value), as_unsigned),
1294 16 => format!("{}|{:#068b}", self.smt.display(value), as_unsigned),
1295 17 => format!("{}|{:#070b}", self.smt.display(value), as_unsigned),
1296 32 => format!("{}|{:#0130b}", self.smt.display(value), as_unsigned),
1297 _ => {
1298 format!("{}|{:#b}", self.smt.display(value), as_unsigned)
1299 }
1300 }
1301 } else {
1302 val_str
1303 }
1304 }
1305
1306 fn display_value(&self, variable: SExpr, value: SExpr) -> (String, String) {
1307 let var_str = self.smt.display(variable).to_string();
1308 (var_str, self.display_hex_to_bin(value))
1309 }
1310
1311 fn display_isle_pattern(
1312 &mut self,
1313 termenv: &TermEnv,
1314 typeenv: &TypeEnv,
1315 vars: &Vec<(String, String)>,
1316 rule: &Rule,
1317 pat: &Pattern,
1318 ) -> SExpr {
1319 let mut to_sexpr = |p| self.display_isle_pattern(termenv, typeenv, vars, rule, p);
1320
1321 match pat {
1322 isle::sema::Pattern::Term(_, term_id, args) => {
1323 let sym = termenv.terms[term_id.index()].name;
1324 let name = typeenv.syms[sym.index()].clone();
1325
1326 let mut sexprs = args.iter().map(&mut to_sexpr).collect::<Vec<SExpr>>();
1327
1328 sexprs.insert(0, self.smt.atom(name));
1329 self.smt.list(sexprs)
1330 }
1331 isle::sema::Pattern::Var(_, var_id) => {
1332 let sym = rule.vars[var_id.index()].name;
1333 let ident = typeenv.syms[sym.index()].clone();
1334 let smt_ident_prefix = format!("{}__clif{}__", ident, var_id.index());
1335
1336 let var = self.display_var_from_smt_prefix(vars, &ident, &smt_ident_prefix);
1337 self.smt.atom(var)
1338 }
1339 isle::sema::Pattern::BindPattern(_, var_id, subpat) => {
1340 let sym = rule.vars[var_id.index()].name;
1341 let ident = &typeenv.syms[sym.index()];
1342 let smt_ident_prefix = format!("{}__clif{}__", ident, var_id.index(),);
1343 let subpat_node = to_sexpr(subpat);
1344
1345 let var = self.display_var_from_smt_prefix(vars, ident, &smt_ident_prefix);
1346
1347 if matches!(**subpat, isle::sema::Pattern::Wildcard(_)) {
1349 self.smt.atom(var)
1350 } else {
1351 self.smt
1352 .list(vec![self.smt.atom(var), self.smt.atom("@"), subpat_node])
1353 }
1354 }
1355 isle::sema::Pattern::Wildcard(_) => self.smt.list(vec![self.smt.atom("_")]),
1356
1357 isle::sema::Pattern::ConstPrim(_, sym) => {
1358 let name = typeenv.syms[sym.index()].clone();
1359 self.smt.list(vec![self.smt.atom(name)])
1360 }
1361 isle::sema::Pattern::ConstBool(_, val) => {
1362 self.smt.list(vec![self.smt.atom(format!("{val}"))])
1363 }
1364 isle::sema::Pattern::ConstInt(_, num) => {
1365 let _smt_name_prefix = format!("{}__", num);
1366 self.smt.list(vec![self.smt.atom(num.to_string())])
1367 }
1368 isle::sema::Pattern::And(_, subpats) => {
1369 let mut sexprs = subpats.iter().map(to_sexpr).collect::<Vec<SExpr>>();
1370
1371 sexprs.insert(0, self.smt.atom("and"));
1372 self.smt.list(sexprs)
1373 }
1374 }
1375 }
1376
1377 fn display_var_from_smt_prefix(
1378 &self,
1379 vars: &Vec<(String, String)>,
1380 ident: &str,
1381 prefix: &str,
1382 ) -> String {
1383 let matches: Vec<&(String, String)> =
1384 vars.iter().filter(|(v, _)| v.starts_with(prefix)).collect();
1385 if matches.is_empty() {
1386 panic!("Can't find match for: {}\n{:?}", prefix, vars);
1387 } else if matches.len() == 3 {
1388 assert!(
1389 self.find_widths,
1390 "Only expect multiple matches with dynamic widths"
1391 );
1392 for (name, model) in matches {
1393 if name.contains("narrow") {
1394 return format!("[{}|{}]", self.smt.display(self.smt.atom(ident)), model);
1395 }
1396 }
1397 panic!("narrow not found");
1398 } else if matches.len() == 1 {
1399 let model = &matches.first().unwrap().1;
1400 format!("[{}|{}]", self.smt.display(self.smt.atom(ident)), model)
1401 } else {
1402 panic!("Unexpected number of matches!")
1403 }
1404 }
1405
1406 fn display_isle_expr(
1407 &self,
1408 termenv: &TermEnv,
1409 typeenv: &TypeEnv,
1410 vars: &Vec<(String, String)>,
1411 rule: &Rule,
1412 expr: &isle::sema::Expr,
1413 ) -> SExpr {
1414 let to_sexpr = |e| self.display_isle_expr(termenv, typeenv, vars, rule, e);
1415
1416 match expr {
1417 isle::sema::Expr::Term(_, term_id, args) => {
1418 let sym = termenv.terms[term_id.index()].name;
1419 let name = typeenv.syms[sym.index()].clone();
1420
1421 let mut sexprs = args.iter().map(to_sexpr).collect::<Vec<SExpr>>();
1422
1423 sexprs.insert(0, self.smt.atom(name));
1424 self.smt.list(sexprs)
1425 }
1426 isle::sema::Expr::Var(_, var_id) => {
1427 let sym = rule.vars[var_id.index()].name;
1428 let ident = typeenv.syms[sym.index()].clone();
1429 let smt_ident_prefix = format!("{}__clif{}__", ident, var_id.index());
1430
1431 let var = self.display_var_from_smt_prefix(vars, &ident, &smt_ident_prefix);
1432 self.smt.atom(var)
1433 }
1434 isle::sema::Expr::ConstPrim(_, sym) => {
1435 let name = typeenv.syms[sym.index()].clone();
1436 self.smt.list(vec![self.smt.atom(name)])
1437 }
1438 isle::sema::Expr::ConstBool(_, val) => {
1439 self.smt.list(vec![self.smt.atom(format!("{val}"))])
1440 }
1441 isle::sema::Expr::ConstInt(_, num) => {
1442 let _smt_name_prefix = format!("{}__", num);
1443 self.smt.list(vec![self.smt.atom(num.to_string())])
1444 }
1445 isle::sema::Expr::Let { bindings, body, .. } => {
1446 let mut sexprs = vec![];
1447 for (varid, _, expr) in bindings {
1448 let sym = rule.vars[varid.index()].name;
1449 let ident = typeenv.syms[sym.index()].clone();
1450 let smt_prefix = format!("{}__clif{}__", ident, varid.index());
1451 let var = self.display_var_from_smt_prefix(vars, &ident, &smt_prefix);
1452
1453 sexprs.push(self.smt.list(vec![self.smt.atom(var), to_sexpr(expr)]));
1454 }
1455 self.smt.list(vec![
1456 self.smt.atom("let"),
1457 self.smt.list(sexprs),
1458 to_sexpr(body),
1459 ])
1460 }
1461 }
1462 }
1463
1464 fn display_model(
1465 &mut self,
1466 termenv: &TermEnv,
1467 typeenv: &TypeEnv,
1468 rule: &Rule,
1469 lhs_sexpr: SExpr,
1470 rhs_sexpr: SExpr,
1471 ) {
1472 let mut vars = vec![];
1473 let mut lhs_value = None;
1474 let mut rhs_value = None;
1475 for (name, atom) in &self.var_map {
1476 let solution = self
1477 .smt
1478 .get_value(vec![self.smt.atom(name), *atom])
1479 .unwrap();
1480 for (variable, value) in solution {
1481 let display = self.display_value(variable, value);
1482 vars.push(display.clone());
1483 if variable == lhs_sexpr {
1484 lhs_value = Some(display.1);
1485 } else if variable == rhs_sexpr {
1486 rhs_value = Some(display.1);
1487 }
1488 }
1489 }
1490 for (name, _) in &self.additional_decls {
1491 let solution = self.smt.get_value(vec![self.smt.atom(name)]).unwrap();
1492 for (variable, value) in solution {
1493 vars.push(self.display_value(variable, value));
1494 }
1495 }
1496 vars.sort_by_key(|x| x.0.clone());
1497 vars.dedup();
1498
1499 println!("Counterexample summary");
1501 let lhs = self.display_isle_pattern(
1502 termenv,
1503 typeenv,
1504 &vars,
1505 rule,
1506 &Pattern::Term(
1507 cranelift_isle::sema::TypeId(0),
1508 rule.root_term,
1509 rule.args.clone(),
1510 ),
1511 );
1512 println!("{}", self.smt.display(lhs));
1513
1514 if !&rule.iflets.is_empty() {
1516 print!("(if-let ");
1517 }
1518 for if_let_struct in &rule.iflets {
1519 let if_lhs = &if_let_struct.lhs;
1520 let if_rhs: &cranelift_isle::sema::Expr = &if_let_struct.rhs;
1521
1522 let if_lhs_expr = self.display_isle_pattern(termenv, typeenv, &vars, rule, if_lhs);
1523
1524 let if_rhs_expr = self.display_isle_expr(termenv, typeenv, &vars, rule, if_rhs);
1525
1526 println!(
1527 "({} {})",
1528 self.smt.display(if_lhs_expr),
1529 self.smt.display(if_rhs_expr)
1530 );
1531 }
1532 println!(")");
1533
1534 println!("=>");
1535 let rhs = self.display_isle_expr(termenv, typeenv, &vars, rule, &rule.rhs);
1536 println!("{}", self.smt.display(rhs));
1537
1538 println!("\n{} =>\n{}\n", lhs_value.unwrap(), rhs_value.unwrap(),);
1539 }
1540
1541 fn declare_variables(
1542 &mut self,
1543 rule_sem: &RuleSemantics,
1544 config: &Config,
1545 ) -> (Vec<SExpr>, Vec<SExpr>) {
1546 let mut assumptions: Vec<SExpr> = vec![];
1547 log::trace!("Declaring quantified variables");
1548 for v in &rule_sem.quantified_vars {
1549 let name = &v.name;
1550 let ty = self.tyctx.tymap[&v.tyvar];
1551 let var_ty = self.vir_to_smt_ty(&ty);
1552 log::trace!("\t{} : {}", name, self.smt.display(var_ty));
1553 if let Type::BitVector(w) = ty {
1554 if self.find_widths {
1555 let wide = self.widen_to_register_width(
1556 v.tyvar,
1557 w.unwrap_or(self.bitwidth),
1558 self.smt.atom(name),
1559 Some(name.to_string()),
1560 );
1561 self.var_map.insert(name.clone(), wide);
1562 } else {
1563 self.var_map.insert(name.clone(), self.smt.atom(name));
1564 }
1565 } else {
1566 self.var_map.insert(name.clone(), self.smt.atom(name));
1567 }
1568 self.smt.declare_const(name, var_ty).unwrap();
1569 }
1570 self.lhs_flag = true;
1571 for a in &rule_sem.lhs_assumptions {
1572 let p = self.vir_expr_to_sexp(a.clone());
1573 assumptions.push(p)
1574 }
1575 self.lhs_flag = false;
1576 for a in &rule_sem.rhs_assumptions {
1577 let p = self.vir_expr_to_sexp(a.clone());
1578 assumptions.push(p)
1579 }
1580 if self.find_widths {
1581 for a in &self.width_assumptions {
1582 assumptions.push(*a);
1583 }
1584 }
1585 self.additional_assumptions.is_empty();
1586 for a in &self.additional_assumptions {
1587 assumptions.push(*a);
1588 }
1589 let assertions: Vec<SExpr> = rule_sem
1591 .rhs_assertions
1592 .iter()
1593 .map(|a| self.vir_expr_to_sexp(a.clone()))
1594 .collect();
1595
1596 for (name, ty) in &self.additional_decls {
1597 self.smt.declare_const(name, *ty).unwrap();
1598 }
1599
1600 if let Some(a) = &config.custom_assumptions {
1601 let term_args = rule_sem
1602 .term_args
1603 .iter()
1604 .map(|s| self.smt.atom(s))
1605 .collect();
1606 let custom_assumptions = a(&self.smt, term_args);
1607 log::debug!(
1608 "Custom assumptions:\n\t{}\n",
1609 self.smt.display(custom_assumptions)
1610 );
1611 assumptions.push(custom_assumptions);
1612 }
1613 (assumptions, assertions)
1614 }
1615}
1616
1617pub fn run_solver(
1621 rule_sem: &RuleSemantics,
1622 rule: &Rule,
1623 termenv: &TermEnv,
1624 typeenv: &TypeEnv,
1625 concrete: &Option<ConcreteTest>,
1626 config: &Config,
1627 _types: &TermSignature,
1628) -> VerificationResult {
1629 if std::env::var("SKIP_SOLVER").is_ok() {
1630 log::debug!("Environment variable SKIP_SOLVER set, returning Unknown");
1631 return VerificationResult::Unknown;
1632 }
1633
1634 let mut solver = easy_smt::ContextBuilder::new()
1635 .replay_file(Some(std::fs::File::create("dynamic_widths.smt2").unwrap()))
1636 .solver("z3", ["-smt2", "-in"])
1637 .build()
1638 .unwrap();
1639
1640 solver
1641 .set_option(":produce-unsat-cores", solver.true_())
1642 .unwrap();
1643
1644 let mut ctx = SolverCtx {
1646 smt: solver,
1647 find_widths: true,
1649 tyctx: rule_sem.tyctx.clone(),
1650 bitwidth: MAX_WIDTH,
1651 var_map: HashMap::new(),
1652 width_vars: HashMap::new(),
1653 width_assumptions: vec![],
1654 additional_decls: vec![],
1655 additional_assumptions: vec![],
1656 additional_assertions: vec![],
1657 fresh_bits_idx: 0,
1658 lhs_load_args: None,
1659 rhs_load_args: None,
1660 lhs_store_args: None,
1661 rhs_store_args: None,
1662 load_return: None,
1663 lhs_flag: true,
1664 };
1665
1666 let mut unresolved_widths = vec![];
1667
1668 for (_e, t) in &ctx.tyctx.tyvars {
1671 let ty = &ctx.tyctx.tymap[t];
1672 if let Type::BitVector(w) = ty {
1673 let width_name = format!("width__{}", t);
1674 ctx.additional_decls
1675 .push((width_name.clone(), ctx.smt.int_sort()));
1676 match *w {
1677 Some(bitwidth) => {
1678 let eq = ctx
1679 .smt
1680 .eq(ctx.smt.atom(&width_name), ctx.smt.numeral(bitwidth));
1681 ctx.width_assumptions.push(eq);
1682 }
1683 None => {
1684 log::debug!("Unresolved width: {:?} ({})", &_e, *t);
1685 ctx.width_assumptions
1686 .push(ctx.smt.gt(ctx.smt.atom(&width_name), ctx.smt.numeral(0)));
1687 unresolved_widths.push(width_name.clone());
1688 }
1689 };
1690 ctx.width_vars.insert(*t, width_name.clone());
1691 }
1692 }
1693
1694 if unresolved_widths.is_empty() {
1695 log::debug!("All widths resolved after basic type inference");
1696 return run_solver_with_static_widths(
1697 &RuleCtx {
1698 rule_sem,
1699 rule,
1700 termenv,
1701 typeenv,
1702 config,
1703 },
1704 &ctx.tyctx,
1705 concrete,
1706 );
1707 }
1708
1709 log::debug!("Some unresolved widths after basic type inference");
1710 log::debug!("Finding widths from the solver");
1711 ctx.find_widths = true;
1712 let (assumptions, _) = ctx.declare_variables(rule_sem, config);
1713 ctx.smt.push().unwrap();
1714 for (i, a) in assumptions.iter().enumerate() {
1715 ctx.smt
1716 .assert(ctx.smt.named(format!("dyn{i}"), *a))
1717 .unwrap();
1718 }
1719
1720 resolve_dynamic_widths(
1721 RuleCtx {
1722 rule_sem,
1723 rule,
1724 termenv,
1725 typeenv,
1726 config,
1727 },
1728 concrete,
1729 &mut ctx,
1730 unresolved_widths,
1731 0,
1732 )
1733}
1734
1735fn resolve_dynamic_widths(
1736 rulectx: RuleCtx,
1737 concrete: &Option<ConcreteTest>,
1738 ctx: &mut SolverCtx,
1739 unresolved_widths: Vec<String>,
1740 attempt: usize,
1741) -> VerificationResult {
1742 if attempt > 10 {
1743 panic!("Unexpected number of attempts to resolve dynamic widths!")
1744 }
1745 match ctx.smt.check() {
1746 Ok(Response::Sat) => {
1747 let mut cur_tyctx = ctx.tyctx.clone();
1748 let mut width_resolutions = HashMap::new();
1749 for (e, t) in &ctx.tyctx.tyvars {
1750 let ty = &ctx.tyctx.tymap[t];
1751 if let Type::BitVector(w) = ty {
1752 let width_name = format!("width__{}", t);
1753 let atom = ctx.smt.atom(&width_name);
1754 let width = ctx.smt.get_value(vec![atom]).unwrap().first().unwrap().1;
1755 let width_int = u8::try_from(ctx.smt.get(width)).unwrap();
1756
1757 if let Some(before_width) = w {
1759 assert_eq!(*before_width, width_int as usize)
1760 };
1761
1762 if width_int == 0 {
1764 panic!("Unexpected, zero width! {} {:?}", t, e);
1765 }
1766
1767 if unresolved_widths.contains(&width_name) {
1768 log::debug!("\tResolved width: {}, {}", width_name, width_int);
1769 width_resolutions.insert(width_name, width_int);
1770 cur_tyctx
1771 .tymap
1772 .insert(*t, Type::BitVector(Some(width_int as usize)));
1773 }
1774 }
1775 }
1776 let static_result = run_solver_with_static_widths(&rulectx, &cur_tyctx, concrete);
1777
1778 if !matches!(static_result, VerificationResult::Success) {
1780 return static_result;
1781 }
1782
1783 let not_equals = width_resolutions.iter().map(|(s, w)| {
1786 ctx.smt.not(
1787 ctx.smt
1788 .eq(ctx.smt.atom(s.clone()), ctx.smt.atom((*w).to_string())),
1789 )
1790 });
1791 ctx.smt.assert(ctx.smt.or_many(not_equals)).unwrap();
1792
1793 resolve_dynamic_widths(rulectx, concrete, ctx, unresolved_widths, attempt + 1)
1794 }
1795 Ok(Response::Unsat) => {
1796 if attempt == 0 {
1797 log::warn!(
1798 "Rule not applicable as written for rule assumptions, skipping full query"
1799 );
1800 let unsat = ctx.smt.get_unsat_core().unwrap();
1801 log::warn!("Unsat core:\n{}", ctx.smt.display(unsat));
1802 VerificationResult::InapplicableRule
1803 } else {
1804 VerificationResult::Success
1807 }
1808 }
1809 Ok(Response::Unknown) => {
1810 panic!("Solver said 'unk'");
1811 }
1812 Err(err) => {
1813 unreachable!("Error! {:?}", err);
1814 }
1815 }
1816}
1817
1818pub fn run_solver_with_static_widths(
1819 rulectx: &RuleCtx,
1820 tyctx: &TypeContext,
1821 concrete: &Option<ConcreteTest>,
1822) -> VerificationResult {
1823 let mut solver = easy_smt::ContextBuilder::new()
1825 .replay_file(Some(std::fs::File::create("static_widths.smt2").unwrap()))
1826 .solver("z3", ["-smt2", "-in"])
1827 .build()
1828 .unwrap();
1829 solver
1830 .set_option(":produce-unsat-cores", solver.true_())
1831 .unwrap();
1832 let mut ctx = SolverCtx {
1833 smt: solver,
1834 find_widths: false,
1835 tyctx: tyctx.clone(),
1836 bitwidth: MAX_WIDTH,
1837 var_map: HashMap::new(),
1838 width_vars: HashMap::new(),
1839 width_assumptions: vec![],
1840 additional_decls: vec![],
1841 additional_assumptions: vec![],
1842 additional_assertions: vec![],
1843 fresh_bits_idx: 0,
1844 lhs_load_args: None,
1845 rhs_load_args: None,
1846 lhs_store_args: None,
1847 rhs_store_args: None,
1848 load_return: None,
1849 lhs_flag: true,
1850 };
1851 let (assumptions, mut assertions) = ctx.declare_variables(rulectx.rule_sem, rulectx.config);
1852
1853 let lhs = ctx.vir_expr_to_sexp(rulectx.rule_sem.lhs.clone());
1854 ctx.lhs_flag = false;
1855 let rhs = ctx.vir_expr_to_sexp(rulectx.rule_sem.rhs.clone());
1856
1857 let unnamed_rule = String::from("<unnamed rule>");
1859 let rulename = rulectx
1860 .rule
1861 .name
1862 .map(|name| &rulectx.typeenv.syms[name.index()])
1863 .unwrap_or(&unnamed_rule);
1864 let unit = "()".to_string();
1865 let widthname = ctx
1866 .static_width(&rulectx.rule_sem.lhs)
1867 .map_or(unit, |s| format!("width {}", s));
1868
1869 let feasibility = ctx.check_assumptions_feasibility(
1871 &assumptions,
1872 &rulectx.rule_sem.term_input_bvs,
1873 rulectx.config,
1874 );
1875 if feasibility != VerificationResult::Success {
1876 log::warn!("Rule not applicable as written for rule assumptions, skipping full query");
1877 return feasibility;
1878 }
1879
1880 if let Some(concrete) = concrete {
1883 return test_concrete_with_static_widths(
1884 rulectx,
1885 concrete,
1886 lhs,
1887 rhs,
1888 &mut ctx,
1889 assumptions,
1890 );
1891 }
1892
1893 let condition = if let Some(condition) = &rulectx.config.custom_verification_condition {
1894 let term_args = rulectx
1895 .rule_sem
1896 .term_args
1897 .iter()
1898 .map(|s| ctx.smt.atom(s))
1899 .collect();
1900 let custom_condition = condition(&ctx.smt, term_args, lhs, rhs);
1901 log::debug!(
1902 "Custom verification condition:\n\t{}\n",
1903 ctx.smt.display(custom_condition)
1904 );
1905 custom_condition
1906 } else {
1907 let side_equality = ctx.smt.eq(lhs, rhs);
1909 log::debug!(
1910 "LHS and RHS equality condition:{}",
1911 ctx.smt.display(side_equality)
1912 );
1913 side_equality
1914 };
1915
1916 for a in &ctx.additional_assertions {
1917 assertions.push(*a);
1918 }
1919
1920 let assumption_conjunction = ctx.smt.and_many(assumptions);
1921 let mut full_condition = if !assertions.is_empty() {
1922 let assertion_conjunction = ctx.smt.and_many(assertions.clone());
1923 ctx.smt.and(condition, assertion_conjunction)
1924 } else {
1925 condition
1926 };
1927
1928 let mut load_conditions = vec![];
1929 match (&ctx.lhs_load_args, &ctx.rhs_load_args) {
1930 (Some(_), Some(_)) => {
1931 let lhs_args_vec = ctx.lhs_load_args.clone().unwrap();
1932 let rhs_args_vec = ctx.rhs_load_args.clone().unwrap();
1933 log::debug!("Load argument conditions:");
1934 for i in 0..lhs_args_vec.len() {
1935 let arg_equal = ctx.smt.eq(lhs_args_vec[i], rhs_args_vec[i]);
1936 load_conditions.push(arg_equal);
1937 log::debug!("\t{}", ctx.smt.display(arg_equal));
1938 full_condition = ctx.smt.and(full_condition, arg_equal);
1939 }
1940 }
1941 (None, None) => (),
1942 (Some(_), None) => {
1943 log::error!("Verification failed for {}, {}", rulename, widthname);
1944 log::error!("Left hand side has load statement but right hand side does not.");
1945 return VerificationResult::Failure(Counterexample {});
1946 }
1947 (None, Some(_)) => {
1948 log::error!("Verification failed for {}, {}", rulename, widthname);
1949 log::error!("Right hand side has load statement but left hand side does not.");
1950 return VerificationResult::Failure(Counterexample {});
1951 }
1952 }
1953
1954 let mut store_conditions = vec![];
1955 match (&ctx.lhs_store_args, &ctx.rhs_store_args) {
1956 (Some(_), Some(_)) => {
1957 let lhs_args_vec = ctx.lhs_store_args.clone().unwrap();
1958 let rhs_args_vec = ctx.rhs_store_args.clone().unwrap();
1959 log::debug!("Store argument conditions:");
1960
1961 for i in 0..lhs_args_vec.len() {
1962 let arg_equal = ctx.smt.eq(lhs_args_vec[i], rhs_args_vec[i]);
1963 store_conditions.push(arg_equal);
1964 log::debug!("\t{}", ctx.smt.display(arg_equal));
1965 full_condition = ctx.smt.and(full_condition, arg_equal)
1966 }
1967 }
1968 (None, None) => (),
1969 (Some(_), None) => {
1970 log::error!("Verification failed for {}, {}", rulename, widthname);
1971 log::error!("Left hand side has store statement but right hand side does not.");
1972 return VerificationResult::Failure(Counterexample {});
1973 }
1974 (None, Some(_)) => {
1975 log::error!("Verification failed for {}, {}", rulename, widthname);
1976 log::error!("Right hand side has store statement but left hand side does not.");
1977 return VerificationResult::Failure(Counterexample {});
1978 }
1979 }
1980
1981 log::trace!(
1982 "Full verification condition:{}",
1983 ctx.smt.display(full_condition)
1984 );
1985 let query = ctx
1986 .smt
1987 .not(ctx.smt.imp(assumption_conjunction, full_condition));
1988 log::trace!("Running query");
1989 ctx.smt.assert(query).unwrap();
1990
1991 match ctx.smt.check() {
1992 Ok(Response::Sat) => {
1993 println!("Verification failed for {}, {}", rulename, widthname);
1994 ctx.display_model(rulectx.termenv, rulectx.typeenv, rulectx.rule, lhs, rhs);
1995 let vals = ctx.smt.get_value(vec![condition]).unwrap();
1996 for (variable, value) in vals {
1997 if value == ctx.smt.false_() {
1998 println!("Failed condition:\n{}", ctx.smt.display(variable));
1999 } else if value == ctx.smt.true_() {
2000 println!("Condition met, but failed some assertion(s).")
2001 }
2002 }
2003
2004 if !assertions.is_empty() {
2005 let vals = ctx.smt.get_value(assertions).unwrap();
2006 for (variable, value) in vals {
2007 if value == ctx.smt.false_() {
2008 println!("Failed assertion:\n{}", ctx.smt.display(variable));
2009 }
2010 }
2011 }
2012
2013 if !load_conditions.is_empty() {
2014 let vals = ctx.smt.get_value(load_conditions).unwrap();
2015 for (variable, value) in vals {
2016 if value == ctx.smt.false_() {
2017 log::error!("Failed load condition:\n{}", ctx.smt.display(variable));
2018 }
2019 }
2020 }
2021 VerificationResult::Failure(Counterexample {})
2022 }
2023 Ok(Response::Unsat) => {
2024 println!("Verification succeeded for {}, {}", rulename, widthname);
2025 VerificationResult::Success
2026 }
2027 Ok(Response::Unknown) => {
2028 panic!("Solver said 'unk'");
2029 }
2030 Err(err) => {
2031 unreachable!("Error! {:?}", err);
2032 }
2033 }
2034}
2035
2036pub fn test_concrete_with_static_widths(
2037 rulectx: &RuleCtx,
2038 concrete: &ConcreteTest,
2039 lhs: SExpr,
2040 rhs: SExpr,
2041 ctx: &mut SolverCtx,
2042 assumptions: Vec<SExpr>,
2043) -> VerificationResult {
2044 for (i, a) in assumptions.iter().enumerate() {
2047 ctx.smt
2048 .assert(ctx.smt.named(format!("conc{i}"), *a))
2049 .unwrap();
2050 }
2051 for (i, e) in ctx.additional_assertions.iter().enumerate() {
2052 ctx.smt
2053 .assert(ctx.smt.named(format!("conc_assert{i}"), *e))
2054 .unwrap();
2055 }
2056 ctx.smt.push().unwrap();
2057 let eq = ctx
2058 .smt
2059 .eq(rhs, ctx.smt.atom(concrete.output.literal.clone()));
2060
2061 ctx.smt
2062 .assert(ctx.smt.named("conceq".to_string(), eq))
2063 .unwrap();
2064
2065 for (i, a) in rulectx.rule_sem.rhs_assertions.iter().enumerate() {
2066 let p = ctx.vir_expr_to_sexp(a.clone());
2067 ctx.smt
2068 .assert(ctx.smt.named(format!("rhs_assert{i}"), p))
2069 .unwrap();
2070 }
2071
2072 if !matches!(ctx.smt.check(), Ok(Response::Sat)) {
2073 ctx.smt.pop().unwrap();
2076 assert!(matches!(ctx.smt.check(), Ok(Response::Sat)));
2078 let val = ctx.smt.get_value(vec![rhs]).unwrap()[0].1;
2080 ctx.display_model(rulectx.termenv, rulectx.typeenv, rulectx.rule, lhs, rhs);
2081 panic!(
2082 "Expected {}, got {}",
2083 concrete.output.literal,
2084 ctx.display_hex_to_bin(val)
2085 );
2086 } else {
2087 log::debug!(
2088 "Expected concrete result matched: {}",
2089 concrete.output.literal
2090 );
2091 ctx.smt.pop().unwrap();
2092 }
2093
2094 ctx.smt.push().unwrap();
2096 ctx.smt
2097 .assert(
2098 ctx.smt.not(
2099 ctx.smt
2100 .eq(rhs, ctx.smt.atom(concrete.output.literal.clone())),
2101 ),
2102 )
2103 .unwrap();
2104 if !matches!(ctx.smt.check(), Ok(Response::Unsat)) {
2105 let val = ctx.smt.get_value(vec![rhs]).unwrap()[0].1;
2107 ctx.display_model(rulectx.termenv, rulectx.typeenv, rulectx.rule, lhs, rhs);
2108 log::error!(
2110 "WARNING: Expected ONLY {}, got POSSIBLE {}",
2111 concrete.output.literal,
2112 ctx.display_hex_to_bin(val)
2113 );
2114 }
2115 ctx.smt.pop().unwrap();
2116 VerificationResult::Success
2117}