veri_engine_lib/solver/encoded_ops/subs.rs
1use crate::solver::SolverCtx;
2use easy_smt::SExpr;
3
4// Future work: likely remove this when we add rule-chaining
5
6// Build the results of a subtraction with flags. Put the 4 flags in the high bits.
7// Encoding adapted from SAIL ISLA: https://github.com/rems-project/isla
8//
9// N: Set to 1 when the result of the operation is negative
10// Z: Set to 1 when the result of the operation is zero
11// C: Set to 1 when the operation results in a carry, or when a subtraction results in no borrow
12// V: Set to 1 when the operation causes overflow
13//
14// 67 66 65 64 63 ... 0
15// [ N | Z | C | V | ... result ... ]
16pub fn subs(s: &mut SolverCtx, ty: usize, x: SExpr, y: SExpr, id: u32) -> SExpr {
17 let id = format!("{ty}_{id}");
18 let (size, wide_size, x, y, zero, one, w_minus_one) = match ty {
19 32 => (
20 s.smt.numeral(32),
21 s.smt.numeral(32 * 2),
22 s.smt.extract(31, 0, x),
23 s.smt.extract(31, 0, y),
24 s.bv(0, 32),
25 s.bv(1, 32 * 2),
26 s.bv(31, 32),
27 ),
28 64 => (
29 s.smt.numeral(64),
30 s.smt.numeral(64 * 2),
31 s.smt.extract(63, 0, x),
32 s.smt.extract(63, 0, y),
33 s.bv(0, 64),
34 s.bv(1, 64 * 2),
35 s.bv(63, 64),
36 ),
37 _ => unreachable!(),
38 };
39
40 let b0 = s.bv(0, 1);
41 let b1 = s.bv(1, 1);
42
43 // (define-const ynot (bvnot y))
44 let ynot = s.declare(
45 format!("ynot_{id}", id = id),
46 s.smt
47 .list(vec![s.smt.atoms().und, s.smt.atom("BitVec"), size]),
48 );
49 s.assume(s.smt.eq(ynot, s.smt.bvnot(y)));
50
51 // (define-const
52 // subs_wide
53 // (bvadd (bvadd ((_ zero_extend 64) x) ((_ zero_extend 64) ynot)) #x00000000000000000000000000000001))
54 let subs_wide = s.declare(
55 format!("subs_wide_{id}", id = id),
56 s.smt
57 .list(vec![s.smt.atoms().und, s.smt.atom("BitVec"), wide_size]),
58 );
59 s.assume(s.smt.eq(
60 subs_wide,
61 s.smt.bvadd(
62 s.smt.bvadd(s.zero_extend(ty, x), s.zero_extend(ty, ynot)),
63 one,
64 ),
65 ));
66
67 // (define-const subs ((_ extract 63 0) subs_wide))
68 let subs = s.declare(
69 format!("subs_{id}", id = id),
70 s.smt
71 .list(vec![s.smt.atoms().und, s.smt.atom("BitVec"), size]),
72 );
73 s.assume(s.smt.eq(
74 subs,
75 s.smt.extract((ty - 1).try_into().unwrap(), 0, subs_wide),
76 ));
77
78 // (define-const flags
79 // (concat (concat (concat
80 // ((_ extract 0 0) (bvlshr subs #x000000000000003f))
81 // (ite (= subs #x0000000000000000) #b1 #b0))
82 // (ite (= ((_ zero_extend 64) subs) subs_wide) #b0 #b1))
83 // (ite (= ((_ sign_extend 64) subs) (bvadd (bvadd ((_ sign_extend 64) x) ((_ sign_extend 64) ynot)) #x00000000000000000000000000000001)) #b0 #b1)))
84 let flags = s.declare(
85 format!("flags_{id}", id = id),
86 s.smt.list(vec![
87 s.smt.atoms().und,
88 s.smt.atom("BitVec"),
89 s.smt.numeral(4),
90 ]),
91 );
92
93 // N: Set to 1 when the result of the operation is negative
94 // Z: Set to 1 when the result of the operation is zero
95 // C: Set to 1 when the operation results in a carry, or when a subtraction results in no borrow
96 // V: Set to 1 when the operation causes overflow
97 s.assume(
98 s.smt.eq(
99 flags,
100 s.smt.concat(
101 s.smt.concat(
102 s.smt.concat(
103 // N flag: result is negative
104 s.smt.extract(0, 0, s.smt.bvlshr(subs, w_minus_one)),
105 // Z flag: result is zero
106 s.smt.ite(s.smt.eq(subs, zero), b1, b0),
107 ),
108 // C flag: result has carry/subtraction has no borrow
109 s.smt
110 .ite(s.smt.eq(s.zero_extend(ty, subs), subs_wide), b0, b1),
111 ),
112 // V: operation causes overflow
113 s.smt.ite(
114 s.smt.eq(
115 s.sign_extend(ty, subs),
116 s.smt.bvadd(
117 s.smt.bvadd(s.sign_extend(ty, x), s.sign_extend(ty, ynot)),
118 one,
119 ),
120 ),
121 b0,
122 b1,
123 ),
124 ),
125 ),
126 );
127
128 let ret = s.declare(
129 format!("subs_ret_{id}", id = id),
130 s.smt.list(vec![
131 s.smt.atoms().und,
132 s.smt.atom("BitVec"),
133 s.smt.numeral(68),
134 ]),
135 );
136
137 s.assume(s.smt.eq(
138 ret,
139 match ty {
140 // Pad 32 back to full reg width of 64 before adding flags to the left
141 32 => s.smt.concat(flags, s.zero_extend(ty, subs)),
142 64 => s.smt.concat(flags, subs),
143 _ => unreachable!(),
144 },
145 ));
146 ret
147}