1use super::*;
2
3pub(crate) fn normalize_constraints_for_solver(constraints: &[BoolExpr]) -> Vec<BoolExpr> {
5 let normalized = normalize_constraint_batch(
6 constraints.iter().cloned().map(normalize_bool_for_solver),
7 constraints.len(),
8 );
9 if matches!(normalized.as_slice(), [BoolExpr::Const(false)]) {
10 return normalized;
11 }
12
13 let context = ConstraintContext::new(&normalized);
14 let normalized_len = normalized.len();
15 normalize_constraint_batch(
16 normalized.into_iter().map(|constraint| context.normalize_bool(constraint)),
17 normalized_len,
18 )
19}
20
21fn normalize_constraint_batch(
22 constraints: impl IntoIterator<Item = BoolExpr>,
23 capacity: usize,
24) -> Vec<BoolExpr> {
25 let mut normalized = Vec::with_capacity(capacity);
26 for constraint in constraints {
27 if matches!(constraint, BoolExpr::Const(false)) {
28 return vec![BoolExpr::Const(false)];
29 }
30 collect_normalized_conjunct(constraint, &mut normalized);
31 }
32 normalized.sort();
33 normalized.dedup();
34 normalized
35}
36
37fn collect_normalized_conjunct(expr: BoolExpr, out: &mut Vec<BoolExpr>) {
38 match expr {
39 BoolExpr::Const(true) => {}
40 BoolExpr::And(values) => {
41 for value in values {
42 collect_normalized_conjunct(value, out);
43 }
44 }
45 value => out.push(value),
46 }
47}
48
49pub(crate) fn normalize_bool_for_solver(expr: BoolExpr) -> BoolExpr {
51 if let Some(normalized) = normalize_udiv_bool_for_solver(&expr) {
52 return normalized;
53 }
54
55 match expr {
56 BoolExpr::Const(value) => BoolExpr::Const(value),
57 BoolExpr::Not(value) => normalize_bool_for_solver(*value).not(),
58 BoolExpr::And(values) => {
59 BoolExpr::and(values.into_iter().map(normalize_bool_for_solver).collect())
60 }
61 BoolExpr::Eq(left, right) => {
62 let normalized =
63 BoolExpr::Eq(normalize_expr_for_solver(left), normalize_expr_for_solver(right));
64 normalize_udiv_bool_for_solver(&normalized).unwrap_or_else(|| match normalized {
65 BoolExpr::Eq(left, right) => BoolExpr::eq(left, right),
66 _ => unreachable!(),
67 })
68 }
69 BoolExpr::Cmp(op, left, right) => {
70 let normalized = BoolExpr::Cmp(
71 op,
72 normalize_expr_for_solver(left),
73 normalize_expr_for_solver(right),
74 );
75 normalize_udiv_bool_for_solver(&normalized).unwrap_or(normalized)
76 }
77 }
78}
79
80#[derive(Default)]
82struct ConstraintContext {
83 upper_bounds: BTreeMap<Expr, U256>,
84}
85
86impl ConstraintContext {
87 fn new(constraints: &[BoolExpr]) -> Self {
88 let mut context = Self::default();
89 for constraint in constraints {
90 context.record_upper_bound_constraint(constraint);
91 }
92 context
93 }
94
95 fn upper_bound(&self, expr: &Expr) -> Option<U256> {
96 self.upper_bounds.get(expr).copied()
97 }
98
99 fn normalize_bool(&self, expr: BoolExpr) -> BoolExpr {
100 match &expr {
101 BoolExpr::Eq(left, Expr::Const(value)) | BoolExpr::Eq(Expr::Const(value), left)
102 if value.is_zero() && self.word_bool_always_true(left) =>
103 {
104 BoolExpr::Const(false)
105 }
106 BoolExpr::Not(value) => match value.as_ref() {
107 BoolExpr::Eq(left, Expr::Const(value)) | BoolExpr::Eq(Expr::Const(value), left)
108 if value.is_zero() && self.word_bool_always_true(left) =>
109 {
110 BoolExpr::Const(true)
111 }
112 _ => expr,
113 },
114 _ => expr,
115 }
116 }
117
118 fn record_upper_bound_constraint(&mut self, constraint: &BoolExpr) {
119 if let Some((expr, bound)) = self.upper_bound_constraint(constraint) {
120 self.record_upper_bound(expr.clone(), bound);
121 }
122 }
123
124 fn record_upper_bound(&mut self, expr: Expr, bound: U256) {
125 self.upper_bounds
126 .entry(expr)
127 .and_modify(|existing| *existing = (*existing).min(bound))
128 .or_insert(bound);
129 }
130
131 fn upper_bound_constraint<'a>(&self, constraint: &'a BoolExpr) -> Option<(&'a Expr, U256)> {
132 match constraint {
133 BoolExpr::Eq(left, Expr::Const(value)) | BoolExpr::Eq(Expr::Const(value), left) => {
134 Some((left, *value))
135 }
136 BoolExpr::Cmp(op, left, right) => match (*op, left, right) {
137 (BoolExprOp::Ult, expr, Expr::Const(bound)) => {
138 (!bound.is_zero()).then(|| (expr, *bound - U256::from(1)))
139 }
140 (BoolExprOp::Ule, expr, Expr::Const(bound)) => Some((expr, *bound)),
141 (BoolExprOp::Ugt, Expr::Const(bound), expr) => {
142 (!bound.is_zero()).then(|| (expr, *bound - U256::from(1)))
143 }
144 (BoolExprOp::Uge, Expr::Const(bound), expr) => Some((expr, *bound)),
145 _ => None,
146 },
147 BoolExpr::Not(value) => match value.as_ref() {
148 BoolExpr::Cmp(BoolExprOp::Ugt, left, Expr::Const(bound)) => Some((left, *bound)),
149 BoolExpr::Cmp(BoolExprOp::Uge, left, Expr::Const(bound)) => {
150 (!bound.is_zero()).then(|| (left, *bound - U256::from(1)))
151 }
152 BoolExpr::Cmp(BoolExprOp::Ult, Expr::Const(bound), right) => Some((right, *bound)),
153 BoolExpr::Cmp(BoolExprOp::Ule, Expr::Const(bound), right) => {
154 (!bound.is_zero()).then(|| (right, *bound - U256::from(1)))
155 }
156 _ => None,
157 },
158 BoolExpr::Eq(_, _) | BoolExpr::Const(_) | BoolExpr::And(_) => None,
159 }
160 }
161}
162
163pub(crate) fn normalize_expr_for_solver(expr: Expr) -> Expr {
165 if let Some(rebuilt) = rebuild_word_from_extracted_byte_terms(&expr)
166 && rebuilt != expr
167 {
168 return normalize_expr_for_solver(rebuilt);
169 }
170
171 match expr {
172 Expr::Const(_)
173 | Expr::Var(_)
174 | Expr::GasLeft(_)
175 | Expr::Keccak { .. }
176 | Expr::Hash { .. } => expr,
177 Expr::Not(value) => Expr::Not(Box::new(normalize_expr_for_solver(*value))),
178 Expr::Op(op, left, right) => {
179 let left = normalize_expr_for_solver(*left);
180 let right = normalize_expr_for_solver(*right);
181 if matches!(op, ExprOp::Add | ExprOp::Mul | ExprOp::And | ExprOp::Or | ExprOp::Xor)
182 && right < left
183 {
184 Expr::op(op, right, left)
185 } else {
186 Expr::op(op, left, right)
187 }
188 }
189 Expr::Ite(cond, left, right) => normalize_ite_expr_for_solver(*cond, *left, *right),
190 }
191}
192
193pub(crate) fn normalize_ite_expr_for_solver(cond: BoolExpr, left: Expr, right: Expr) -> Expr {
195 let cond = normalize_bool_for_solver(cond);
196 let left = normalize_expr_for_solver(left);
197 let right = normalize_expr_for_solver(right);
198 if left == right {
199 return left;
200 }
201 if let Some(condition) = guarded_self_div_word_condition(&cond, &left, &right) {
202 return word_from_bool_expr(condition);
203 }
204 if matches!(left, Expr::Const(value) if value == U256::from(1))
205 && bool_from_word_expr(&right).as_ref() == Some(&cond)
206 {
207 return right;
208 }
209 if matches!(right, Expr::Const(value) if value.is_zero())
210 && bool_from_word_expr(&left).as_ref() == Some(&cond)
211 {
212 return left;
213 }
214 Expr::Ite(Box::new(cond), Box::new(left), Box::new(right))
215}
216
217fn word_from_bool_expr(condition: BoolExpr) -> Expr {
219 Expr::Ite(
220 Box::new(condition),
221 Box::new(Expr::Const(U256::from(1))),
222 Box::new(Expr::Const(U256::ZERO)),
223 )
224}
225
226fn guarded_self_div_word_condition(cond: &BoolExpr, left: &Expr, right: &Expr) -> Option<BoolExpr> {
228 if matches!(left, Expr::Const(value) if value.is_zero())
229 && self_div_expr_matches_zero_check(cond, right)
230 {
231 return Some(cond.clone().not());
232 }
233 None
234}
235
236fn self_div_expr_matches_zero_check(cond: &BoolExpr, expr: &Expr) -> bool {
238 let Some(zero_operand) = zero_check_operand(cond) else { return false };
239 let Some((numerator, denominator)) = udiv_operands(expr) else { return false };
240 numerator == zero_operand && denominator == zero_operand
241}
242
243pub(crate) fn rebuild_word_from_extracted_byte_terms(expr: &Expr) -> Option<Expr> {
245 let mut terms = Vec::new();
246 collect_or_terms(expr, &mut terms);
247 if terms.len() <= 1 {
248 return None;
249 }
250
251 let mut source = None;
252 let mut seen = [false; 32];
253 for term in terms {
254 if matches!(term, Expr::Const(value) if value.is_zero()) {
255 continue;
256 }
257 let (term_source, index) = extracted_shifted_byte_term(term)?;
258 match &source {
259 Some(source) if source != &term_source => return None,
260 Some(_) => {}
261 None => source = Some(term_source),
262 }
263 seen[index] = true;
264 }
265
266 let source = source?;
267 for (index, seen) in seen.into_iter().enumerate() {
268 if !seen && expr_known_byte(&source, index) != Some(0) {
269 return None;
270 }
271 }
272 Some(source)
273}
274
275pub(crate) fn collect_or_terms<'a>(expr: &'a Expr, terms: &mut Vec<&'a Expr>) {
277 match expr {
278 Expr::Op(ExprOp::Or, left, right) => {
279 collect_or_terms(left, terms);
280 collect_or_terms(right, terms);
281 }
282 expr => terms.push(expr),
283 }
284}
285
286pub(crate) fn extracted_shifted_byte_term(term: &Expr) -> Option<(Expr, usize)> {
288 match term {
289 Expr::Op(ExprOp::Shl, byte, shift) => {
290 let Expr::Const(shift) = shift.as_ref() else { return None };
291 let shift = shift.to::<usize>();
292 if shift % 8 != 0 || shift > 248 {
293 return None;
294 }
295 let index = 31 - shift / 8;
296 let source = extracted_unshifted_byte_source(byte, index)?;
297 Some((source, index))
298 }
299 term => extracted_unshifted_byte_source(term, 31).map(|source| (source, 31)),
300 }
301}
302
303pub(crate) fn extracted_unshifted_byte_source(term: &Expr, index: usize) -> Option<Expr> {
305 let expr = strip_low_byte_mask(term)?;
306 if index == 31 {
307 return Some(expr.clone());
308 }
309 let Expr::Op(ExprOp::Shr, source, shift) = expr else { return None };
310 let Expr::Const(shift) = shift.as_ref() else { return None };
311 (*shift == U256::from((31 - index) * 8)).then(|| *source.clone())
312}
313
314pub(crate) fn normalize_udiv_bool_for_solver(expr: &BoolExpr) -> Option<BoolExpr> {
316 match expr {
317 BoolExpr::Eq(left, Expr::Const(value)) if value.is_zero() => {
318 bool_from_word_expr(left).map(BoolExpr::not).or_else(|| {
319 if word_bool_always_true(left) {
320 Some(BoolExpr::Const(false))
321 } else {
322 normalize_udiv_eq_zero(left, &Expr::Const(U256::ZERO))
323 }
324 })
325 }
326 BoolExpr::Eq(Expr::Const(value), right) if value.is_zero() => {
327 bool_from_word_expr(right).map(BoolExpr::not).or_else(|| {
328 if word_bool_always_true(right) {
329 Some(BoolExpr::Const(false))
330 } else {
331 normalize_udiv_eq_zero(&Expr::Const(U256::ZERO), right)
332 }
333 })
334 }
335 BoolExpr::Eq(left, Expr::Const(value)) if *value == U256::from(1) => {
336 bool_from_word_expr(left)
337 }
338 BoolExpr::Eq(Expr::Const(value), right) if *value == U256::from(1) => {
339 bool_from_word_expr(right)
340 }
341 BoolExpr::Not(value) => match value.as_ref() {
342 BoolExpr::Cmp(op, left, right) => {
343 normalize_add_overflow_cmp_for_solver(*op, left, right)
344 .map(BoolExpr::not)
345 .or_else(|| normalize_udiv_cmp_for_solver(*op, left, right).map(BoolExpr::not))
346 }
347 BoolExpr::Eq(left, Expr::Const(value)) if value.is_zero() => {
348 if word_bool_always_true(left) {
349 Some(BoolExpr::Const(true))
350 } else {
351 normalize_udiv_eq_zero(left, &Expr::Const(U256::ZERO)).map(BoolExpr::not)
352 }
353 }
354 BoolExpr::Eq(Expr::Const(value), right) if value.is_zero() => {
355 if word_bool_always_true(right) {
356 Some(BoolExpr::Const(true))
357 } else {
358 normalize_udiv_eq_zero(&Expr::Const(U256::ZERO), right).map(BoolExpr::not)
359 }
360 }
361 BoolExpr::Eq(left, right) => normalize_udiv_eq_zero(left, right).map(BoolExpr::not),
362 _ => None,
363 },
364 BoolExpr::Eq(left, right) => normalize_udiv_eq_zero(left, right),
365 BoolExpr::Cmp(op, left, right) => normalize_add_overflow_cmp_for_solver(*op, left, right)
366 .or_else(|| normalize_udiv_cmp_for_solver(*op, left, right)),
367 BoolExpr::Const(_) | BoolExpr::And(_) => None,
368 }
369}
370
371pub(crate) fn bool_from_word_expr(expr: &Expr) -> Option<BoolExpr> {
373 let expr = strip_low_byte_mask(expr)?;
374 let Expr::Ite(condition, then_expr, else_expr) = expr else { return None };
375 match (then_expr.as_ref(), else_expr.as_ref()) {
376 (Expr::Const(then_value), Expr::Const(else_value))
377 if *then_value == U256::from(1) && else_value.is_zero() =>
378 {
379 Some(normalize_bool_for_solver((**condition).clone()))
380 }
381 (Expr::Const(then_value), Expr::Const(else_value))
382 if then_value.is_zero() && *else_value == U256::from(1) =>
383 {
384 Some(normalize_bool_for_solver((**condition).clone()).not())
385 }
386 _ => None,
387 }
388}
389
390pub(crate) fn expr_contains_udiv(expr: &Expr) -> bool {
392 match expr {
393 Expr::Const(_) | Expr::Var(_) | Expr::GasLeft(_) => false,
394 Expr::Keccak { len, bytes, .. } => {
395 expr_contains_udiv(len) || bytes.iter().any(expr_contains_udiv)
396 }
397 Expr::Hash { bytes, .. } => bytes.iter().any(expr_contains_udiv),
398 Expr::Not(value) => expr_contains_udiv(value),
399 Expr::Op(op, left, right) => {
400 matches!(op, ExprOp::UDiv) || expr_contains_udiv(left) || expr_contains_udiv(right)
401 }
402 Expr::Ite(condition, then_expr, else_expr) => {
403 bool_contains_udiv(condition)
404 || expr_contains_udiv(then_expr)
405 || expr_contains_udiv(else_expr)
406 }
407 }
408}
409
410pub(crate) fn bool_contains_udiv(expr: &BoolExpr) -> bool {
412 match expr {
413 BoolExpr::Const(_) => false,
414 BoolExpr::Not(value) => bool_contains_udiv(value),
415 BoolExpr::And(values) => values.iter().any(bool_contains_udiv),
416 BoolExpr::Eq(left, right) | BoolExpr::Cmp(_, left, right) => {
417 expr_contains_udiv(left) || expr_contains_udiv(right)
418 }
419 }
420}
421
422pub(crate) fn normalize_add_overflow_cmp_for_solver(
424 op: BoolExprOp,
425 left: &Expr,
426 right: &Expr,
427) -> Option<BoolExpr> {
428 match op {
429 BoolExprOp::Ugt if add_overflow_check(left, right) => Some(BoolExpr::Const(false)),
430 BoolExprOp::Ult if add_overflow_check(right, left) => Some(BoolExpr::Const(false)),
431 _ => None,
432 }
433}
434
435pub(crate) fn add_overflow_check(left: &Expr, right: &Expr) -> bool {
437 let Some((base, increment)) = add_with_operand(right, left) else { return false };
438 base == left && add_cannot_overflow_256(base, increment)
439}
440
441pub(crate) fn add_with_operand<'a>(expr: &'a Expr, operand: &Expr) -> Option<(&'a Expr, &'a Expr)> {
443 let Expr::Op(ExprOp::Add, left, right) = expr else { return None };
444 if left.as_ref() == operand {
445 Some((left, right))
446 } else if right.as_ref() == operand {
447 Some((right, left))
448 } else {
449 None
450 }
451}
452
453pub(crate) fn add_cannot_overflow_256(left: &Expr, right: &Expr) -> bool {
455 expr_unsigned_bits(left).max(expr_unsigned_bits(right)).saturating_add(1) <= 256
456}
457
458pub(crate) fn word_bool_always_true(expr: &Expr) -> bool {
460 ConstraintContext::default().word_bool_always_true(expr)
461}
462
463pub(crate) fn word_bool_term(expr: &Expr) -> Option<&BoolExpr> {
465 let Expr::Ite(condition, then_expr, else_expr) = expr else { return None };
466 match (then_expr.as_ref(), else_expr.as_ref()) {
467 (Expr::Const(then_value), Expr::Const(else_value))
468 if *then_value == U256::from(1) && else_value.is_zero() =>
469 {
470 Some(condition)
471 }
472 _ => None,
473 }
474}
475
476pub(crate) fn zero_check_operand(expr: &BoolExpr) -> Option<&Expr> {
478 match expr {
479 BoolExpr::Eq(left, Expr::Const(value)) if value.is_zero() => Some(left),
480 BoolExpr::Eq(Expr::Const(value), right) if value.is_zero() => Some(right),
481 _ => None,
482 }
483}
484
485impl ConstraintContext {
486 fn word_bool_always_true(&self, expr: &Expr) -> bool {
487 let mut terms = Vec::new();
488 collect_or_terms(expr, &mut terms);
489 if terms.len() <= 1 {
490 return false;
491 }
492
493 let bool_terms = terms.iter().filter_map(|term| word_bool_term(term)).collect::<Vec<_>>();
494 if bool_terms.iter().any(|term| {
495 let negated = (*term).clone().not();
496 bool_terms.iter().any(|other| **other == negated)
497 }) {
498 return true;
499 }
500 for zero_term in &bool_terms {
501 let Some(zero_operand) = zero_check_operand(zero_term) else { continue };
502 if bool_terms.iter().any(|term| self.checked_mul_guard_for_operand(term, zero_operand))
503 {
504 return true;
505 }
506 }
507 false
508 }
509
510 fn checked_mul_guard_for_operand(&self, expr: &BoolExpr, zero_operand: &Expr) -> bool {
511 let BoolExpr::Eq(left, right) = expr else { return false };
512 self.checked_mul_guard_side(left, right, zero_operand)
513 || self.checked_mul_guard_side(right, left, zero_operand)
514 }
515
516 fn checked_mul_guard_side(
517 &self,
518 div_expr: &Expr,
519 expected: &Expr,
520 zero_operand: &Expr,
521 ) -> bool {
522 let Expr::Ite(condition, then_expr, else_expr) = div_expr else { return false };
523 if zero_check_operand(condition).is_none_or(|operand| operand != zero_operand) {
524 return false;
525 }
526 if !matches!(then_expr.as_ref(), Expr::Const(value) if value.is_zero()) {
527 return false;
528 }
529 let Some((numerator, denominator)) = udiv_operands(else_expr) else { return false };
530 if denominator != zero_operand {
531 return false;
532 }
533 let Expr::Op(ExprOp::Mul, left, right) = numerator else { return false };
534 let other = if left.as_ref() == zero_operand {
535 right.as_ref()
536 } else if right.as_ref() == zero_operand {
537 left.as_ref()
538 } else {
539 return false;
540 };
541 other == expected && self.mul_cannot_overflow_256(zero_operand, other)
542 }
543
544 fn mul_cannot_overflow_256(&self, left: &Expr, right: &Expr) -> bool {
545 self.expr_unsigned_bits(left).saturating_add(self.expr_unsigned_bits(right)) <= 256
546 }
547
548 fn expr_unsigned_bits(&self, expr: &Expr) -> usize {
549 let bits = match expr {
550 Expr::Const(_)
551 | Expr::Var(_)
552 | Expr::GasLeft(_)
553 | Expr::Keccak { .. }
554 | Expr::Hash { .. }
555 | Expr::Not(_) => expr_unsigned_bits(expr),
556 Expr::Op(ExprOp::And, left, right) => match (left.as_ref(), right.as_ref()) {
557 (expr, Expr::Const(mask)) | (Expr::Const(mask), expr) => {
558 self.expr_unsigned_bits(expr).min(mask.bit_len())
559 }
560 _ => 256,
561 },
562 Expr::Op(ExprOp::Add, left, right) => self
563 .expr_unsigned_bits(left)
564 .max(self.expr_unsigned_bits(right))
565 .saturating_add(1)
566 .min(256),
567 Expr::Op(ExprOp::Mul, left, right) => self
568 .expr_unsigned_bits(left)
569 .saturating_add(self.expr_unsigned_bits(right))
570 .min(256),
571 Expr::Op(ExprOp::UDiv, left, _) => self.expr_unsigned_bits(left),
572 Expr::Ite(_, left, right) => {
573 self.expr_unsigned_bits(left).max(self.expr_unsigned_bits(right))
574 }
575 _ => 256,
576 };
577
578 self.upper_bound(expr).map(|bound| bits.min(bound.bit_len().max(1))).unwrap_or(bits)
579 }
580}
581
582pub(crate) fn mul_cannot_overflow_256(left: &Expr, right: &Expr) -> bool {
584 expr_unsigned_bits(left).saturating_add(expr_unsigned_bits(right)) <= 256
585}
586
587pub(crate) fn expr_unsigned_bits(expr: &Expr) -> usize {
589 match expr {
590 Expr::Const(value) => value.bit_len().max(1),
591 Expr::Op(ExprOp::And, left, right) => match (left.as_ref(), right.as_ref()) {
592 (expr, Expr::Const(mask)) | (Expr::Const(mask), expr) => {
593 expr_unsigned_bits(expr).min(mask.bit_len())
594 }
595 _ => 256,
596 },
597 Expr::Op(ExprOp::Add, left, right) => {
598 expr_unsigned_bits(left).max(expr_unsigned_bits(right)).saturating_add(1).min(256)
599 }
600 Expr::Op(ExprOp::Mul, left, right) => {
601 expr_unsigned_bits(left).saturating_add(expr_unsigned_bits(right)).min(256)
602 }
603 Expr::Op(ExprOp::UDiv, left, _) => expr_unsigned_bits(left),
604 Expr::Ite(_, left, right) => expr_unsigned_bits(left).max(expr_unsigned_bits(right)),
605 _ => 256,
606 }
607}
608
609pub(crate) fn normalize_udiv_eq_zero(left: &Expr, right: &Expr) -> Option<BoolExpr> {
611 if matches!(right, Expr::Const(value) if value.is_zero())
612 && let Some(condition) = normalize_expr_eq_zero_for_solver(left)
613 {
614 return Some(condition);
615 }
616 if matches!(left, Expr::Const(value) if value.is_zero())
617 && let Some(condition) = normalize_expr_eq_zero_for_solver(right)
618 {
619 return Some(condition);
620 }
621 None
622}
623
624pub(crate) fn normalize_expr_eq_zero_for_solver(expr: &Expr) -> Option<BoolExpr> {
626 if let Some((numerator, denominator)) = udiv_operands(expr) {
627 return Some(udiv_zero_condition(numerator, denominator));
628 }
629 if let Expr::Ite(condition, then_expr, else_expr) = expr {
630 let then_zero = normalize_expr_eq_zero_for_solver(then_expr).unwrap_or_else(|| {
631 BoolExpr::eq(normalize_expr_for_solver((**then_expr).clone()), Expr::Const(U256::ZERO))
632 });
633 let else_zero = normalize_expr_eq_zero_for_solver(else_expr).unwrap_or_else(|| {
634 BoolExpr::eq(normalize_expr_for_solver((**else_expr).clone()), Expr::Const(U256::ZERO))
635 });
636 if bool_contains_udiv(&then_zero) || bool_contains_udiv(&else_zero) {
637 return None;
638 }
639 return Some(BoolExpr::or(vec![
640 BoolExpr::and(vec![normalize_bool_for_solver((**condition).clone()), then_zero]),
641 BoolExpr::and(vec![normalize_bool_for_solver((**condition).clone()).not(), else_zero]),
642 ]));
643 }
644 None
645}
646
647pub(crate) fn normalize_expr_ne_zero_for_solver(expr: &Expr) -> Option<BoolExpr> {
649 if let Some((numerator, denominator)) = udiv_operands(expr) {
650 return Some(udiv_nonzero_condition(numerator, denominator));
651 }
652 if let Expr::Ite(condition, then_expr, else_expr) = expr {
653 let then_nonzero = normalize_expr_ne_zero_for_solver(then_expr).unwrap_or_else(|| {
654 BoolExpr::eq(normalize_expr_for_solver((**then_expr).clone()), Expr::Const(U256::ZERO))
655 .not()
656 });
657 let else_nonzero = normalize_expr_ne_zero_for_solver(else_expr).unwrap_or_else(|| {
658 BoolExpr::eq(normalize_expr_for_solver((**else_expr).clone()), Expr::Const(U256::ZERO))
659 .not()
660 });
661 if bool_contains_udiv(&then_nonzero) || bool_contains_udiv(&else_nonzero) {
662 return None;
663 }
664 return Some(BoolExpr::or(vec![
665 BoolExpr::and(vec![normalize_bool_for_solver((**condition).clone()), then_nonzero]),
666 BoolExpr::and(vec![
667 normalize_bool_for_solver((**condition).clone()).not(),
668 else_nonzero,
669 ]),
670 ]));
671 }
672 None
673}
674
675pub(crate) fn normalize_udiv_cmp_for_solver(
677 op: BoolExprOp,
678 left: &Expr,
679 right: &Expr,
680) -> Option<BoolExpr> {
681 match (op, left, right) {
682 (BoolExprOp::Ugt, div, Expr::Const(value)) if value.is_zero() => {
683 normalize_expr_ne_zero_for_solver(div)
684 }
685 (BoolExprOp::Uge, div, Expr::Const(value)) if *value == U256::from(1) => {
686 normalize_expr_ne_zero_for_solver(div)
687 }
688 (BoolExprOp::Ule, div, Expr::Const(value)) if value.is_zero() => {
689 normalize_expr_eq_zero_for_solver(div)
690 }
691 (BoolExprOp::Ult, div, Expr::Const(value)) if *value == U256::from(1) => {
692 normalize_expr_eq_zero_for_solver(div)
693 }
694 (BoolExprOp::Ult, Expr::Const(value), div) if value.is_zero() => {
695 normalize_expr_ne_zero_for_solver(div)
696 }
697 (BoolExprOp::Ule, Expr::Const(value), div) if *value == U256::from(1) => {
698 normalize_expr_ne_zero_for_solver(div)
699 }
700 (BoolExprOp::Uge, Expr::Const(value), div) if value.is_zero() => {
701 normalize_expr_eq_zero_for_solver(div)
702 }
703 (BoolExprOp::Ugt, Expr::Const(value), div) if *value == U256::from(1) => {
704 normalize_expr_eq_zero_for_solver(div)
705 }
706 _ => None,
707 }
708}
709
710pub(crate) fn udiv_operands(expr: &Expr) -> Option<(&Expr, &Expr)> {
712 match expr {
713 Expr::Op(ExprOp::UDiv, numerator, denominator) => Some((numerator, denominator)),
714 _ => None,
715 }
716}
717
718pub(crate) fn udiv_zero_condition(numerator: &Expr, denominator: &Expr) -> BoolExpr {
720 BoolExpr::or(vec![
721 BoolExpr::eq(normalize_expr_for_solver(denominator.clone()), Expr::Const(U256::ZERO)),
722 BoolExpr::cmp(
723 BoolExprOp::Ult,
724 normalize_expr_for_solver(numerator.clone()),
725 normalize_expr_for_solver(denominator.clone()),
726 ),
727 ])
728}
729
730pub(crate) fn udiv_nonzero_condition(numerator: &Expr, denominator: &Expr) -> BoolExpr {
732 BoolExpr::and(vec![
733 BoolExpr::eq(normalize_expr_for_solver(denominator.clone()), Expr::Const(U256::ZERO)).not(),
734 BoolExpr::cmp(
735 BoolExprOp::Uge,
736 normalize_expr_for_solver(numerator.clone()),
737 normalize_expr_for_solver(denominator.clone()),
738 ),
739 ])
740}