veri_engine_lib/solver/encoded_ops/
mod.rs

1pub mod cls;
2pub mod clz;
3pub mod popcnt;
4pub mod rev;
5pub mod subs;
6
7#[cfg(test)]
8mod tests {
9    use super::*;
10    use crate::solver::SolverCtx;
11    use easy_smt::{Response, SExpr};
12    use std::collections::HashMap;
13    use veri_ir::TypeContext;
14
15    fn get_ctx() -> SolverCtx {
16        let smt = easy_smt::ContextBuilder::new()
17            .replay_file(Some(std::fs::File::create("encoding_tests.smt2").unwrap()))
18            .solver("z3", ["-smt2", "-in"])
19            .build()
20            .unwrap();
21        SolverCtx {
22            smt,
23            find_widths: false,
24            tyctx: TypeContext {
25                tyvars: HashMap::new(),
26                tymap: HashMap::new(),
27                tyvals: HashMap::new(),
28                bv_unknown_width_sets: HashMap::new(),
29            },
30            bitwidth: 64,
31            var_map: HashMap::new(),
32            width_vars: HashMap::new(),
33            width_assumptions: vec![],
34            additional_decls: vec![],
35            additional_assumptions: vec![],
36            additional_assertions: vec![],
37            fresh_bits_idx: 0,
38            lhs_load_args: None,
39            rhs_load_args: None,
40            lhs_store_args: None,
41            rhs_store_args: None,
42            lhs_flag: true,
43            load_return: None,
44        }
45    }
46
47    /// Check that the solver encoding meets expectations for the given input and output.
48    /// Right now, only works for encodings with a single argument that return a value with
49    /// the same width as the input.
50    /// Check that the output is equal to the expected output, and no other output is possible.
51    fn check_unary_encoding_with_solver(encoding: &str, input: &str, output: &str, width: usize) {
52        let mut ctx = get_ctx();
53
54        // Set up an input variable
55        let ty = ctx.smt.bit_vec_sort(ctx.smt.numeral(width));
56        let input_var = ctx.declare("input".to_string(), ty);
57
58        // Set the input equal to our expected input
59        ctx.additional_assumptions
60            .push(ctx.smt.eq(input_var, ctx.smt.atom(input)));
61
62        // Call the encoding function to be tested
63        let output_from_call = match (encoding, width) {
64            ("rev", 8) => rev::rev8(&mut ctx, input_var, 0),
65            ("rev", 16) => rev::rev16(&mut ctx, input_var, 0),
66            ("rev", 32) => rev::rev32(&mut ctx, input_var, 0),
67            ("rev", 64) => rev::rev64(&mut ctx, input_var, 0),
68
69            ("clz", 8) => clz::clz8(&mut ctx, input_var, 0),
70            ("clz", 16) => clz::clz16(&mut ctx, input_var, 0),
71            ("clz", 32) => clz::clz32(&mut ctx, input_var, 0),
72            ("clz", 64) => clz::clz64(&mut ctx, input_var, 0),
73
74            ("cls", 8) => cls::cls8(&mut ctx, input_var, 0),
75            ("cls", 16) => cls::cls16(&mut ctx, input_var, 0),
76            ("cls", 32) => cls::cls32(&mut ctx, input_var, 0),
77            ("cls", 64) => cls::cls64(&mut ctx, input_var, 0),
78
79            ("popcnt", ty) => popcnt::popcnt(&mut ctx, ty, input_var, 0),
80            _ => unreachable!(),
81        };
82        check_encoding_with_solver(ctx, output_from_call, output, width)
83    }
84
85    fn check_encoding_with_solver(mut ctx: SolverCtx, call: SExpr, output: &str, width: usize) {
86        // Extract the width of bits that we care about.
87        let output_care_bits = ctx.smt.extract((width - 1).try_into().unwrap(), 0, call);
88        ctx.smt.display(output_care_bits).to_string();
89
90        // Bookkeeping: declare declarations, assert assumptions
91        for (name, ty) in &ctx.additional_decls {
92            ctx.smt.declare_const(name, *ty).unwrap();
93        }
94        if ctx.additional_assumptions.len() > 1 {
95            ctx.smt
96                .assert(ctx.smt.and_many(ctx.additional_assumptions.clone()))
97                .unwrap();
98        }
99
100        // Check that our expected output is valid
101        ctx.smt.push().unwrap();
102        ctx.smt
103            .assert(ctx.smt.eq(output_care_bits, ctx.smt.atom(output)))
104            .unwrap();
105        if !matches!(ctx.smt.check(), Ok(Response::Sat)) {
106            // Bad! This is a bug!
107            // Pop the output assertion
108            ctx.smt.pop().unwrap();
109            // Try again
110            assert!(matches!(ctx.smt.check(), Ok(Response::Sat)));
111
112            let model = ctx.smt.get_model().unwrap();
113            println!("{}", ctx.smt.display(model));
114
115            // Get the value for what output is to panic with a useful message
116            let val = ctx.smt.get_value(vec![output_care_bits]).unwrap()[0].1;
117
118            panic!("Expected {}, got {}", output, ctx.display_hex_to_bin(val));
119        } else {
120            ctx.smt.pop().unwrap();
121        }
122
123        // Check that there is no other possible output
124        ctx.smt.push().unwrap();
125        ctx.smt
126            .assert(
127                ctx.smt
128                    .not(ctx.smt.eq(output_care_bits, ctx.smt.atom(output))),
129            )
130            .unwrap();
131        if !matches!(ctx.smt.check(), Ok(Response::Unsat)) {
132            let model = ctx.smt.get_model().unwrap();
133            println!("{}", ctx.smt.display(model));
134
135            // Get the value for what output is to panic with a useful message
136            let val = ctx.smt.get_value(vec![output_care_bits]).unwrap()[0].1;
137            panic!(
138                "Multiple possible outputs! Expected only {}, got {}",
139                output,
140                ctx.display_hex_to_bin(val)
141            );
142        }
143        ctx.smt.pop().unwrap();
144    }
145
146    fn check(ctx: &SolverCtx, expr: SExpr, expected: &str) {
147        let expr_s = format!("{}", ctx.smt.display(expr));
148        assert_eq!(expr_s, expected);
149    }
150
151    #[test]
152    fn rev1_test() {
153        let mut ctx = get_ctx();
154
155        let x = ctx.smt.atom("x");
156        let res = rev::rev1(&mut ctx, x, 42);
157
158        check(&ctx, res, "(concat fresh0 rev1ret_42)");
159        check(&ctx, ctx.additional_decls[0].1, "(_ BitVec 1)");
160        check(
161            &ctx,
162            ctx.additional_assumptions[0],
163            "(= rev1ret_42 ((_ extract 0 0) x))",
164        );
165    }
166
167    #[test]
168    fn test_rev8_with_solver() {
169        check_unary_encoding_with_solver("rev", "#b01010101", "#b10101010", 8);
170        check_unary_encoding_with_solver("rev", "#b11110000", "#b00001111", 8);
171        check_unary_encoding_with_solver("rev", "#b00000000", "#b00000000", 8);
172        check_unary_encoding_with_solver("rev", "#b11111111", "#b11111111", 8);
173    }
174
175    #[test]
176    fn test_rev16_with_solver() {
177        check_unary_encoding_with_solver("rev", "#b0101010101010101", "#b1010101010101010", 16);
178        check_unary_encoding_with_solver("rev", "#b1111111100000000", "#b0000000011111111", 16);
179        check_unary_encoding_with_solver("rev", "#b0000000000000000", "#b0000000000000000", 16);
180        check_unary_encoding_with_solver("rev", "#b1111111111111111", "#b1111111111111111", 16);
181    }
182
183    #[test]
184    fn test_rev32_with_solver() {
185        check_unary_encoding_with_solver(
186            "rev",
187            "#b01010101010101010101010101010101",
188            "#b10101010101010101010101010101010",
189            32,
190        );
191        check_unary_encoding_with_solver(
192            "rev",
193            "#b11111111111111110000000000000000",
194            "#b00000000000000001111111111111111",
195            32,
196        );
197        check_unary_encoding_with_solver(
198            "rev",
199            "#b00000000000000000000000000000000",
200            "#b00000000000000000000000000000000",
201            32,
202        );
203        check_unary_encoding_with_solver(
204            "rev",
205            "#b11111111111111111111111111111111",
206            "#b11111111111111111111111111111111",
207            32,
208        );
209    }
210
211    #[test]
212    fn test_rev64_with_solver() {
213        check_unary_encoding_with_solver(
214            "rev",
215            "#b0101010101010101010101010101010101010101010101010101010101010101",
216            "#b1010101010101010101010101010101010101010101010101010101010101010",
217            64,
218        );
219        check_unary_encoding_with_solver(
220            "rev",
221            "#b1111111111111111111111111111111100000000000000000000000000000000",
222            "#b0000000000000000000000000000000011111111111111111111111111111111",
223            64,
224        );
225        check_unary_encoding_with_solver(
226            "rev",
227            "#b0000000000000000000000000000000000000000000000000000000000000000",
228            "#b0000000000000000000000000000000000000000000000000000000000000000",
229            64,
230        );
231        check_unary_encoding_with_solver(
232            "rev",
233            "#b1111111111111111111111111111111111111111111111111111111111111111",
234            "#b1111111111111111111111111111111111111111111111111111111111111111",
235            64,
236        );
237    }
238
239    #[test]
240    fn test_clz8_with_solver() {
241        check_unary_encoding_with_solver("clz", "#b00000000", "#b00001000", 8);
242        check_unary_encoding_with_solver("clz", "#b01111111", "#b00000001", 8);
243        check_unary_encoding_with_solver("clz", "#b11111111", "#b00000000", 8);
244    }
245
246    #[test]
247    fn test_clz16_with_solver() {
248        check_unary_encoding_with_solver("clz", "#b0000000000000000", "#b0000000000010000", 16);
249        check_unary_encoding_with_solver("clz", "#b0000000000000001", "#b0000000000001111", 16);
250        check_unary_encoding_with_solver("clz", "#b0111111111111111", "#b0000000000000001", 16);
251        check_unary_encoding_with_solver("clz", "#b1111111111111111", "#b0000000000000000", 16);
252    }
253
254    #[test]
255    fn test_clz32_with_solver() {
256        check_unary_encoding_with_solver(
257            "clz",
258            "#b00000000000000000000000000000000",
259            "#b00000000000000000000000000100000",
260            32,
261        );
262        check_unary_encoding_with_solver(
263            "clz",
264            "#b00000000000000000000000000000001",
265            "#b00000000000000000000000000011111",
266            32,
267        );
268        check_unary_encoding_with_solver(
269            "clz",
270            "#b01000000000000000000000000000000",
271            "#b00000000000000000000000000000001",
272            32,
273        );
274        check_unary_encoding_with_solver(
275            "clz",
276            "#b11111111111111111111111111111111",
277            "#b00000000000000000000000000000000",
278            32,
279        );
280    }
281
282    #[test]
283    fn test_clz64_with_solver() {
284        check_unary_encoding_with_solver(
285            "clz",
286            "#b0000000000000000000000000000000000000000000000000000000000000000",
287            "#b0000000000000000000000000000000000000000000000000000000001000000",
288            64,
289        );
290        check_unary_encoding_with_solver(
291            "clz",
292            "#b0000000000000000000000000000000000000000000000000000000000000001",
293            "#b0000000000000000000000000000000000000000000000000000000000111111",
294            64,
295        );
296        check_unary_encoding_with_solver(
297            "clz",
298            "#b0100000000000000000000000000000000000000000000000000000000000000",
299            "#b0000000000000000000000000000000000000000000000000000000000000001",
300            64,
301        );
302        check_unary_encoding_with_solver(
303            "clz",
304            "#b1111111111111111111111111111111111111111111111111111111111111111",
305            "#b0000000000000000000000000000000000000000000000000000000000000000",
306            64,
307        );
308    }
309
310    #[test]
311    fn test_cls8_with_solver() {
312        check_unary_encoding_with_solver("cls", "#b00000000", "#b00000111", 8);
313        check_unary_encoding_with_solver("cls", "#b01111111", "#b00000000", 8);
314        check_unary_encoding_with_solver("cls", "#b00111111", "#b00000001", 8);
315        check_unary_encoding_with_solver("cls", "#b11000000", "#b00000001", 8);
316        check_unary_encoding_with_solver("cls", "#b11111111", "#b00000111", 8);
317    }
318
319    #[test]
320    fn test_cls16_with_solver() {
321        check_unary_encoding_with_solver("cls", "#b0000000000000000", "#b0000000000001111", 16);
322        check_unary_encoding_with_solver("cls", "#b0111111111111111", "#b0000000000000000", 16);
323        check_unary_encoding_with_solver("cls", "#b0011111111111111", "#b0000000000000001", 16);
324        check_unary_encoding_with_solver("cls", "#b1111111111111111", "#b0000000000001111", 16);
325    }
326
327    #[test]
328    fn test_cls32_with_solver() {
329        check_unary_encoding_with_solver(
330            "cls",
331            "#b00000000000000000000000000000000",
332            "#b00000000000000000000000000011111",
333            32,
334        );
335        check_unary_encoding_with_solver(
336            "cls",
337            "#b01111111111111111111111111111111",
338            "#b00000000000000000000000000000000",
339            32,
340        );
341        check_unary_encoding_with_solver(
342            "cls",
343            "#b00100000000000000000000000000000",
344            "#b00000000000000000000000000000001",
345            32,
346        );
347        check_unary_encoding_with_solver(
348            "cls",
349            "#b11111111111111111111111111111111",
350            "#b00000000000000000000000000011111",
351            32,
352        );
353    }
354
355    #[test]
356    fn test_cls64_with_solver() {
357        check_unary_encoding_with_solver(
358            "cls",
359            "#b0000000000000000000000000000000000000000000000000000000000000000",
360            "#b0000000000000000000000000000000000000000000000000000000000111111",
361            64,
362        );
363        check_unary_encoding_with_solver(
364            "cls",
365            "#b0010000000000000000000000000000000000000000000000000000000000000",
366            "#b0000000000000000000000000000000000000000000000000000000000000001",
367            64,
368        );
369        check_unary_encoding_with_solver(
370            "cls",
371            "#b0111111111111111111111111111111111111111111111111111111111111111",
372            "#b0000000000000000000000000000000000000000000000000000000000000000",
373            64,
374        );
375        check_unary_encoding_with_solver(
376            "cls",
377            "#b1111111111111111111111111111111111111111111111111111111111111111",
378            "#b0000000000000000000000000000000000000000000000000000000000111111",
379            64,
380        );
381    }
382
383    #[test]
384    fn test_popcnt_8_with_solver() {
385        check_unary_encoding_with_solver("popcnt", "#b00000000", "#b00000000", 8);
386        check_unary_encoding_with_solver("popcnt", "#b11111111", "#b00001000", 8);
387        check_unary_encoding_with_solver("popcnt", "#b01010101", "#b00000100", 8);
388    }
389
390    fn check_subs_with_solver(width: usize, x_str: &str, y_str: &str, output: &str) {
391        let mut ctx = get_ctx();
392
393        // Set up an input variable
394        let ty = ctx.smt.bit_vec_sort(ctx.smt.numeral(width));
395        let x = ctx.declare("x".to_string(), ty);
396        let y = ctx.declare("y".to_string(), ty);
397
398        // Set the input equal to our expected input
399        ctx.additional_assumptions
400            .push(ctx.smt.eq(x, ctx.smt.atom(x_str)));
401        ctx.additional_assumptions
402            .push(ctx.smt.eq(y, ctx.smt.atom(y_str)));
403
404        // Call the encoding function to be tested
405        let call = subs::subs(&mut ctx, width, x, y, 0);
406
407        // Output width always 68 bits
408        check_encoding_with_solver(ctx, call, output, 68)
409    }
410
411    #[test]
412    fn test_subs_32_with_solver() {
413        check_subs_with_solver(
414            32,
415            "#b00000000000000000000000000000000",
416            "#b00000000000000000000000000000000",
417            "#b01100000000000000000000000000000000000000000000000000000000000000000",
418        );
419
420        check_subs_with_solver(
421            32,
422            "#b11111111111111111111111111111111",
423            "#b00000000000000000000000000000000",
424            "#b10100000000000000000000000000000000011111111111111111111111111111111",
425        );
426
427        check_subs_with_solver(
428            32,
429            "#b10000000000010000000000000000000",
430            "#b00100111110000100011111110111000",
431            "#b00110000000000000000000000000000000001011000010001011100000001001000",
432        );
433    }
434
435    #[test]
436    fn test_subs_64_with_solver() {
437        check_subs_with_solver(
438            64,
439            "#b0000000000000000000000000000000000000000000000000000000000000000",
440            "#b0000000000000000000000000000000000000000000000000000000000000000",
441            "#b01100000000000000000000000000000000000000000000000000000000000000000",
442        );
443    }
444}