veri_engine_lib/
solver.rs

1/// Convert our internal Verification IR to an external SMT AST and pass
2/// queries to that solver.
3///
4/// This uses the easy-smt crate to interact with any solver.
5///
6use 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    /// Construct a constant bit-vector value of the given width. (This is used so pervasively that
97    /// perhaps we should submit it for inclusion in the easy_smt library...)
98    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    /// Convert an SMT integer to a bit vector of a given width.
112    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    /// Convert an SMT bit vector to a nat.
124    fn bv2nat(&self, value: SExpr) -> SExpr {
125        self.smt.list(vec![self.smt.atom("bv2nat"), value])
126    }
127
128    /// Zero-extend an SMT bit vector to a wider bit vector by adding `padding` zeroes to the
129    /// front.
130    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    /// Sign-extend an SMT bit vector to a wider bit vector by adding `padding` zeroes to the
145    /// front.
146    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    // Extend with concrete source and destination sizes. Includes extracting relevant bits.
158    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        // Extract the relevant bits of the source (which is modeled with a wider,
196        // register-width bitvector).
197        let extract = self
198            .smt
199            .extract(source_width.wrapping_sub(1).try_into().unwrap(), 0, source);
200
201        // Do the extend itself.
202        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        // Pad the extended result back to the full register bitwidth. Use the bits
212        // that were already in the source register. That is, given:
213        //                       reg - source width              source width
214        //                                |                           |
215        // SOURCE: [               don't care bits           |   care bits    ]
216        //
217        //                             dest width
218        //                                |
219        // OUT:    [ same don't care bits |  defined extend  |   care bits     ]
220        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 we are extending to the full register width, no padding needed
231        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    // SMT-LIB only supports extends (zero or sign) by concrete amounts, but we
248    // need symbolic ones. This method essentially does if-conversion over possible
249    // concrete forms, outputting nested ITE blocks. We consider both the starting
250    // width and the destination width to be potentially symbolic.
251    // For safety, we add an assertion that some arm of this ITE must match.
252    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        // Symbolic expression for amount to shift
263        let shift = self.smt.sub(dest_width, source_width);
264
265        let mut some_match = vec![];
266        let mut ite_str = source;
267
268        // Special case: if we are asked to extend by 0, just return the source
269        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        // Possible amounts to extend by
274        for possible_delta in 1..self.bitwidth + 1 {
275            // Possible starting widths
276            for possible_source in 1..self.bitwidth + 1 {
277                // For now, ignore extends beyond the bitwidth. This is safe because
278                // we will fail the rule feasibility check if this is violated.
279                if possible_source + possible_delta > self.bitwidth {
280                    continue;
281                }
282
283                // Statement meaning the symbolic case matches this concrete case
284                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        // SMT bitvector rotate_left requires that the rotate amount be
305        // statically specified. Instead, to use a dynamic amount, desugar
306        // to shifts and bit arithmetic.
307        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    // SMT bitvector rotate requires that the rotate amount be
324    // statically specified. Instead, to use a dynamic amount, desugar
325    // to shifts and bit arithmetic.
326    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            // Extract the relevant bits of the source (which is modeled with a wider,
338            // register-width bitvector).
339            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        // Do the rotate itself.
356        let rotate = self.encode_rotate(op, s, a, source_width);
357
358        // Pad the extended result back to the full register bitwidth. Use the bits
359        // that were already in the source register. That is, given:
360        //                       reg - source width              source width
361        //                                |                           |
362        // SOURCE: [               don't care bits           |   care bits    ]
363        //
364        //                             dest width
365        //                                |
366        // OUT:    [ same don't care bits                   |   care bits     ]
367        let unconstrained_bits = self.bitwidth.checked_sub(source_width).unwrap();
368
369        // If we are extending to the full register width, no padding needed
370        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    // SMT-LIB only supports rotates by concrete amounts, but we
387    // need symbolic ones. This method essentially does if-conversion over possible
388    // concrete forms, outputting nested ITE blocks. We consider both the starting
389    // width and the rotate amount to be potentially symbolic.
390    // For safety, we add an assertion that some arm of this ITE must match.
391    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        // Special case: if we are asked to rotate by 0, just return the source
405        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        // Possible starting widths
410        for possible_source in [8usize, 16, 32, 64] {
411            // Statement meaning the symbolic case matches this concrete case
412            let matching = self.smt.eq(self.smt.numeral(possible_source), source_width);
413            some_match.push(matching);
414
415            // Extract the relevant bits of the source (which is modeled with a wider,
416            // register-width bitvector).
417            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            // SMT bitvector rotate_left requires that the rotate amount be
429            // statically specified. Instead, to use a dynamic amount, desugar
430            // to shifts and bit arithmetic.
431            let rotate = self.encode_rotate(op, extract_source, extract_amount, possible_source);
432
433            // Pad the extended result back to the full register bitwidth. Use the bits
434            // that were already in the source register. That is, given:
435            //                       reg - source width              source width
436            //                                |                           |
437            // SOURCE: [               don't care bits           |   care bits    ]
438            //
439            //                             dest width
440            //                                |
441            // OUT:    [ same don't care bits                   |   care bits     ]
442            let unconstrained_bits = self.bitwidth.checked_sub(possible_source).unwrap();
443
444            // If we are extending to the full register width, no padding needed
445            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                    // To shift right, we need to make sure the bits to the right get zeroed. Shift left first.
705                    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                        // Strategy: shift left by (bitwidth - arg width) to zero bits to the right
714                        // of the bits in the argument size. Then shift right by (amt + (bitwidth - arg width))
715
716                        // Width math
717                        if self.find_widths {
718                            // The shift arg needs to be extracted to the right width, default to 8 if unknown
719                            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                        // Strategy: shift left by (bitwidth - arg width) to eliminate bits to the left
752                        // of the bits in the argument size. Then shift right by (amt + (bitwidth - arg width))
753
754                        // Width math
755                        if self.find_widths {
756                            // The shift arg needs to be extracted to the right width, default to 8 if unknown
757                            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                // If we have some static width that isn't the bitwidth, extract based on it
819                // before performing the operation for the dynamic case.
820                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                    // No-op if we are extracting exactly the full bitwidth
958                    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                // Assert that some case must match
1013                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                // Reverse to keep the order of the switch
1022                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                // Width hack for now
1153                if self.find_widths {
1154                    return sexprs[0];
1155                }
1156                // Reverse to keep the order of the cases
1157                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    // Checks whether the assumption list is always false
1213    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                // Check that there is a model with distinct bitvector inputs
1235                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            // SMT-LIB: bvhexX where X is a hexadecimal numeral of length m defines the bitvector
1288            // constant with value X and size 4*m.
1289            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                // Special case: elide bind patterns to wildcars
1348                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        // TODO VERBOSE
1500        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-let statement processing
1515        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        // Look at RHS assertions, which are checked, not trusted
1590        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
1617/// Overall query for single rule:
1618/// <declare vars>
1619/// (not (=> <assumptions> (= <LHS> <RHS>))))))
1620pub 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    // We start with logic to determine the width of all bitvectors
1645    let mut ctx = SolverCtx {
1646        smt: solver,
1647        // Always find widths at first
1648        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    // Check whether the non-solver type inference was able to resolve all bitvector widths,
1669    // and add assumptions for known widths
1670    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                    // Check that we haven't contradicted previous widths
1758                    if let Some(before_width) = w {
1759                        assert_eq!(*before_width, width_int as usize)
1760                    };
1761
1762                    // Check that the width is nonzero
1763                    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 we have a failure or unknown, return right away
1779            if !matches!(static_result, VerificationResult::Success) {
1780                return static_result;
1781            }
1782
1783            // Otherwise, try again, but adding the assertion that some width is
1784            // different than our current assigment
1785            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                // If this is not the first attempt, some previous width assignment must
1805                // have succeeded.
1806                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    // Declare variables again, this time with all static widths
1824    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    // For debugging
1858    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    // Check whether the assumptions are possible
1870    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    // Correctness query
1881    // Verification condition: first rule's LHS and RHS are equal
1882    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        // Note: this is where we ask if the LHS and the RHS are equal
1908        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    // Test code only: test against concrete input/output
2045    // Check that our expected output is valid
2046    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        // Bad! This is a bug!
2074        // Pop the output assertion
2075        ctx.smt.pop().unwrap();
2076        // Try again
2077        assert!(matches!(ctx.smt.check(), Ok(Response::Sat)));
2078        // Get the value for what output is to panic with a useful message
2079        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    // Check that there is no other possible output
2095    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        // Get the value for what output is to panic with a useful message
2106        let val = ctx.smt.get_value(vec![rhs]).unwrap()[0].1;
2107        ctx.display_model(rulectx.termenv, rulectx.typeenv, rulectx.rule, lhs, rhs);
2108        // AVH TODO: should probably elevate back to an error with custom verification condition
2109        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}