1use crate::{
27 analysis::CfgInfo,
28 mir::{
29 BlockId, Function, InstKind, Terminator, Value, ValueId, utils::repair_reachability_phis,
30 },
31 pass::FunctionPass,
32};
33use alloy_primitives::U256;
34use solar_data_structures::map::{FxHashMap, FxHashSet};
35
36const MAX_DEPTH: usize = 12;
38
39#[derive(Debug, Default, Clone)]
41pub struct CheckElimStats {
42 pub branches_folded: usize,
44}
45
46pub struct CheckElimPass;
48
49impl FunctionPass for CheckElimPass {
50 fn name(&self) -> &str {
51 "check-elim"
52 }
53
54 fn run_on_function(&mut self, func: &mut Function) -> bool {
55 CheckEliminator::new().run(func) != 0
56 }
57}
58
59#[derive(Clone, Copy, Debug, PartialEq, Eq)]
61struct Range {
62 lo: U256,
63 hi: U256,
64}
65
66impl Range {
67 const FULL: Self = Self { lo: U256::ZERO, hi: U256::MAX };
68
69 const fn new(lo: U256, hi: U256) -> Self {
70 Self { lo, hi }
71 }
72
73 fn singleton(value: U256) -> Self {
74 Self { lo: value, hi: value }
75 }
76
77 fn is_singleton(self) -> bool {
78 self.lo == self.hi
79 }
80
81 fn intersect(self, other: Self) -> Option<Self> {
84 let lo = self.lo.max(other.lo);
85 let hi = self.hi.min(other.hi);
86 (lo <= hi).then_some(Self { lo, hi })
87 }
88
89 fn union(self, other: Self) -> Self {
90 Self { lo: self.lo.min(other.lo), hi: self.hi.max(other.hi) }
91 }
92}
93
94#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
99enum Relation {
100 Lt(ValueId, ValueId),
101 Le(ValueId, ValueId),
102 Eq(ValueId, ValueId),
103 Ne(ValueId, ValueId),
104}
105
106fn ordered(a: ValueId, b: ValueId) -> (ValueId, ValueId) {
107 if a.index() <= b.index() { (a, b) } else { (b, a) }
108}
109
110#[derive(Default)]
112pub struct CheckEliminator {
113 pub stats: CheckElimStats,
115 ranges: FxHashMap<ValueId, Range>,
116 relations: FxHashSet<Relation>,
117 range_undo: Vec<(ValueId, Option<Range>)>,
118 relation_undo: Vec<Relation>,
119}
120
121impl CheckEliminator {
122 #[must_use]
124 pub fn new() -> Self {
125 Self::default()
126 }
127
128 pub fn run(&mut self, func: &mut Function) -> usize {
131 self.stats = CheckElimStats::default();
132 if func.blocks.is_empty() {
133 return 0;
134 }
135
136 let cfg = CfgInfo::new(func);
137
138 let mut preds: Vec<Vec<BlockId>> = vec![Vec::new(); func.blocks.len()];
141 for &block in cfg.rpo() {
142 for &succ in cfg.successors(block) {
143 preds[succ.index()].push(block);
144 }
145 }
146
147 let folds = self.collect_folds(func, &cfg, &preds);
148 self.ranges.clear();
149 self.relations.clear();
150 self.range_undo.clear();
151 self.relation_undo.clear();
152
153 if folds.is_empty() {
154 return 0;
155 }
156 for &(block, keep) in &folds {
157 func.blocks[block].terminator = Some(Terminator::Jump(keep));
158 }
159 repair_reachability_phis(func);
160 self.stats.branches_folded = folds.len();
161 folds.len()
162 }
163
164 fn collect_folds(
167 &mut self,
168 func: &Function,
169 cfg: &CfgInfo,
170 preds: &[Vec<BlockId>],
171 ) -> Vec<(BlockId, BlockId)> {
172 enum Walk {
173 Enter(BlockId),
174 Exit { range_mark: usize, relation_mark: usize },
175 }
176
177 let mut folds = Vec::new();
178 let mut stack = vec![Walk::Enter(func.entry_block)];
179 while let Some(item) = stack.pop() {
180 match item {
181 Walk::Exit { range_mark, relation_mark } => {
182 while self.range_undo.len() > range_mark {
183 let (value, old) = self.range_undo.pop().expect("checked len");
184 match old {
185 Some(range) => self.ranges.insert(value, range),
186 None => self.ranges.remove(&value),
187 };
188 }
189 while self.relation_undo.len() > relation_mark {
190 let relation = self.relation_undo.pop().expect("checked len");
191 self.relations.remove(&relation);
192 }
193 }
194 Walk::Enter(block) => {
195 stack.push(Walk::Exit {
196 range_mark: self.range_undo.len(),
197 relation_mark: self.relation_undo.len(),
198 });
199
200 if let Some((condition, is_true)) = dominating_edge_fact(func, preds, block) {
201 self.assume(func, condition, is_true, MAX_DEPTH);
202 }
203
204 if let Some(Terminator::Branch { condition, then_block, else_block }) =
205 func.blocks[block].terminator.as_ref()
206 && then_block != else_block
207 && let Some(truth) = self.eval_truth(func, *condition, MAX_DEPTH)
208 {
209 folds.push((block, if truth { *then_block } else { *else_block }));
210 }
211
212 for &child in cfg.dominators().children(block) {
213 stack.push(Walk::Enter(child));
214 }
215 }
216 }
217 }
218 folds
219 }
220
221 fn assume(&mut self, func: &Function, value: ValueId, truth: bool, depth: usize) {
226 if truth {
228 self.narrow(value, Range::new(U256::from(1), U256::MAX));
229 } else {
230 self.narrow(value, Range::singleton(U256::ZERO));
231 }
232 let Some(depth) = depth.checked_sub(1) else { return };
233 let Some(kind) = inst_kind(func, value) else { return };
234 match *kind {
235 InstKind::IsZero(a) => self.assume(func, a, !truth, depth),
236 InstKind::Lt(a, b) => self.assume_lt(func, a, b, truth, depth),
237 InstKind::Gt(a, b) => self.assume_lt(func, b, a, truth, depth),
238 InstKind::Eq(a, b) => self.assume_eq(func, a, b, truth, depth),
239 InstKind::Sub(a, b) | InstKind::Xor(a, b) => self.assume_eq(func, a, b, !truth, depth),
241 InstKind::And(a, b) if truth => {
243 self.assume(func, a, true, depth);
244 self.assume(func, b, true, depth);
245 }
246 InstKind::Or(a, b) if !truth => {
248 self.assume(func, a, false, depth);
249 self.assume(func, b, false, depth);
250 }
251 _ => {}
252 }
253 }
254
255 fn assume_lt(&mut self, func: &Function, a: ValueId, b: ValueId, truth: bool, depth: usize) {
257 if truth {
258 self.add_relation(Relation::Lt(a, b));
259 let hi_b = self.range_of(func, b, depth).hi;
261 if hi_b > U256::ZERO {
262 self.narrow(a, Range::new(U256::ZERO, hi_b - U256::from(1)));
263 }
264 let lo_a = self.range_of(func, a, depth).lo;
266 if lo_a < U256::MAX {
267 self.narrow(b, Range::new(lo_a + U256::from(1), U256::MAX));
268 }
269 } else {
270 self.add_relation(Relation::Le(b, a));
272 let lo_b = self.range_of(func, b, depth).lo;
273 self.narrow(a, Range::new(lo_b, U256::MAX));
274 let hi_a = self.range_of(func, a, depth).hi;
275 self.narrow(b, Range::new(U256::ZERO, hi_a));
276 }
277 }
278
279 fn assume_eq(&mut self, func: &Function, a: ValueId, b: ValueId, truth: bool, depth: usize) {
281 let (x, y) = ordered(a, b);
282 if truth {
283 self.add_relation(Relation::Eq(x, y));
284 let range = self.range_of(func, a, depth);
285 self.narrow(b, range);
286 let range = self.range_of(func, b, depth);
287 self.narrow(a, range);
288 } else {
289 self.add_relation(Relation::Ne(x, y));
290 self.exclude_boundary(func, a, b, depth);
291 self.exclude_boundary(func, b, a, depth);
292 }
293 }
294
295 fn exclude_boundary(&mut self, func: &Function, a: ValueId, b: ValueId, depth: usize) {
298 let rb = self.range_of(func, b, depth);
299 if !rb.is_singleton() {
300 return;
301 }
302 let ra = self.range_of(func, a, depth);
303 if ra.is_singleton() {
304 return;
305 }
306 if ra.lo == rb.lo {
307 self.narrow(a, Range::new(ra.lo + U256::from(1), ra.hi));
308 } else if ra.hi == rb.hi {
309 self.narrow(a, Range::new(ra.lo, ra.hi - U256::from(1)));
310 }
311 }
312
313 fn narrow(&mut self, value: ValueId, range: Range) {
318 let old = self.ranges.get(&value).copied();
319 let Some(new) = old.unwrap_or(Range::FULL).intersect(range) else { return };
320 if Some(new) == old {
321 return;
322 }
323 self.range_undo.push((value, old));
324 self.ranges.insert(value, new);
325 }
326
327 fn add_relation(&mut self, relation: Relation) {
328 if self.relations.insert(relation) {
329 self.relation_undo.push(relation);
330 }
331 }
332
333 fn has_relation(&self, relation: Relation) -> bool {
334 self.relations.contains(&relation)
335 }
336
337 fn range_of(&mut self, func: &Function, value: ValueId, depth: usize) -> Range {
342 if let Some(constant) = const_of(func, value) {
343 return Range::singleton(constant);
344 }
345 let mut range = self.ranges.get(&value).copied().unwrap_or(Range::FULL);
346 let Some(depth) = depth.checked_sub(1) else { return range };
347 let Some(kind) = inst_kind(func, value) else { return range };
348 let derived = match *kind {
349 InstKind::Add(a, b) => {
350 let ra = self.range_of(func, a, depth);
351 let rb = self.range_of(func, b, depth);
352 match ra.hi.checked_add(rb.hi) {
353 Some(hi) => Range::new(ra.lo.wrapping_add(rb.lo), hi),
354 None => Range::FULL,
355 }
356 }
357 InstKind::Sub(a, b) => {
358 let ra = self.range_of(func, a, depth);
359 let rb = self.range_of(func, b, depth);
360 if ra.lo >= rb.hi { Range::new(ra.lo - rb.hi, ra.hi - rb.lo) } else { Range::FULL }
361 }
362 InstKind::Mul(a, b) => {
363 let ra = self.range_of(func, a, depth);
364 let rb = self.range_of(func, b, depth);
365 match ra.hi.checked_mul(rb.hi) {
366 Some(hi) => Range::new(ra.lo.wrapping_mul(rb.lo), hi),
367 None => Range::FULL,
368 }
369 }
370 InstKind::Div(a, b) => {
371 let ra = self.range_of(func, a, depth);
374 let rb = self.range_of(func, b, depth);
375 let lo = if rb.lo > U256::ZERO { ra.lo / rb.hi } else { U256::ZERO };
376 Range::new(lo, ra.hi)
377 }
378 InstKind::Mod(a, b) => {
379 let ra = self.range_of(func, a, depth);
382 let rb = self.range_of(func, b, depth);
383 let bound = if rb.hi > U256::ZERO { rb.hi - U256::from(1) } else { U256::ZERO };
384 Range::new(U256::ZERO, bound.min(ra.hi))
385 }
386 InstKind::And(a, b) => {
387 let ra = self.range_of(func, a, depth);
388 let rb = self.range_of(func, b, depth);
389 Range::new(U256::ZERO, ra.hi.min(rb.hi))
390 }
391 InstKind::Lt(..)
392 | InstKind::Gt(..)
393 | InstKind::SLt(..)
394 | InstKind::SGt(..)
395 | InstKind::Eq(..)
396 | InstKind::IsZero(..) => match self.eval_truth(func, value, depth) {
397 Some(true) => Range::singleton(U256::from(1)),
398 Some(false) => Range::singleton(U256::ZERO),
399 None => Range::new(U256::ZERO, U256::from(1)),
400 },
401 InstKind::Select(condition, then_value, else_value) => {
402 match self.eval_truth(func, condition, depth) {
403 Some(true) => self.range_of(func, then_value, depth),
404 Some(false) => self.range_of(func, else_value, depth),
405 None => self
406 .range_of(func, then_value, depth)
407 .union(self.range_of(func, else_value, depth)),
408 }
409 }
410 _ => Range::FULL,
411 };
412 if let Some(intersection) = range.intersect(derived) {
416 range = intersection;
417 }
418 range
419 }
420
421 fn eval_truth(&mut self, func: &Function, value: ValueId, depth: usize) -> Option<bool> {
423 if let Some(constant) = const_of(func, value) {
424 return Some(!constant.is_zero());
425 }
426 if let Some(range) = self.ranges.get(&value) {
427 if range.lo > U256::ZERO {
428 return Some(true);
429 }
430 if range.hi.is_zero() {
431 return Some(false);
432 }
433 }
434 let depth = depth.checked_sub(1)?;
435 let kind = inst_kind(func, value)?;
436 match *kind {
437 InstKind::Lt(a, b) => self.eval_lt(func, a, b, depth),
438 InstKind::Gt(a, b) => self.eval_lt(func, b, a, depth),
439 InstKind::Eq(a, b) => self.eval_eq(func, a, b, depth),
440 InstKind::IsZero(a) => self.eval_truth(func, a, depth).map(|truth| !truth),
441 InstKind::Sub(a, b) | InstKind::Xor(a, b) => {
442 self.eval_eq(func, a, b, depth).map(|eq| !eq)
443 }
444 InstKind::And(a, b) => {
445 let ta = self.eval_truth(func, a, depth);
446 let tb = self.eval_truth(func, b, depth);
447 if ta == Some(false) || tb == Some(false) {
448 return Some(false);
449 }
450 let one = Range::singleton(U256::from(1));
452 if self.range_of(func, a, depth) == one && self.range_of(func, b, depth) == one {
453 return Some(true);
454 }
455 None
456 }
457 InstKind::Or(a, b) => {
458 let ta = self.eval_truth(func, a, depth);
459 let tb = self.eval_truth(func, b, depth);
460 if ta == Some(true) || tb == Some(true) {
461 return Some(true);
462 }
463 if ta == Some(false) && tb == Some(false) {
464 return Some(false);
465 }
466 None
467 }
468 _ => {
469 let range = self.range_of(func, value, depth);
470 if range.lo > U256::ZERO {
471 return Some(true);
472 }
473 if range.hi.is_zero() {
474 return Some(false);
475 }
476 None
477 }
478 }
479 }
480
481 fn eval_lt(&mut self, func: &Function, a: ValueId, b: ValueId, depth: usize) -> Option<bool> {
483 if a == b {
484 return Some(false);
485 }
486 let (x, y) = ordered(a, b);
487 if self.has_relation(Relation::Lt(a, b)) {
488 return Some(true);
489 }
490 if self.has_relation(Relation::Lt(b, a))
491 || self.has_relation(Relation::Le(b, a))
492 || self.has_relation(Relation::Eq(x, y))
493 {
494 return Some(false);
495 }
496
497 let ra = self.range_of(func, a, depth);
498 let rb = self.range_of(func, b, depth);
499 if ra.hi < rb.lo {
500 return Some(true);
501 }
502 if ra.lo >= rb.hi {
503 return Some(false);
504 }
505
506 if let Some(&InstKind::Add(x, y)) = inst_kind(func, a)
510 && (b == x || b == y)
511 {
512 let rx = self.range_of(func, x, depth);
513 let ry = self.range_of(func, y, depth);
514 if rx.hi.checked_add(ry.hi).is_some() {
515 return Some(false);
516 }
517 if rx.lo.checked_add(ry.lo).is_none() {
518 return Some(true);
519 }
520 }
521
522 if let Some(&InstKind::Sub(x, y)) = inst_kind(func, b)
525 && a == x
526 && let Some(reduced_depth) = depth.checked_sub(1)
527 {
528 return self.eval_lt(func, x, y, reduced_depth);
529 }
530
531 None
532 }
533
534 fn eval_eq(&mut self, func: &Function, a: ValueId, b: ValueId, depth: usize) -> Option<bool> {
536 if a == b {
537 return Some(true);
538 }
539 let (x, y) = ordered(a, b);
540 if self.has_relation(Relation::Eq(x, y)) {
541 return Some(true);
542 }
543 if self.has_relation(Relation::Ne(x, y))
544 || self.has_relation(Relation::Lt(a, b))
545 || self.has_relation(Relation::Lt(b, a))
546 {
547 return Some(false);
548 }
549
550 let ra = self.range_of(func, a, depth);
551 let rb = self.range_of(func, b, depth);
552 if ra.hi < rb.lo || rb.hi < ra.lo {
553 return Some(false);
554 }
555 if ra.is_singleton() && ra == rb {
556 return Some(true);
557 }
558
559 if let Some(truth) = self.eval_muldiv_roundtrip(func, a, b, depth) {
562 return Some(truth);
563 }
564 if let Some(truth) = self.eval_muldiv_roundtrip(func, b, a, depth) {
565 return Some(truth);
566 }
567
568 None
569 }
570
571 fn eval_muldiv_roundtrip(
574 &mut self,
575 func: &Function,
576 div_value: ValueId,
577 expected: ValueId,
578 depth: usize,
579 ) -> Option<bool> {
580 let InstKind::Div(mul_value, divisor) = *inst_kind(func, div_value)? else { return None };
581 let InstKind::Mul(p, q) = *inst_kind(func, mul_value)? else { return None };
582 for (x, y) in [(p, q), (q, p)] {
583 if x != expected || !values_equal(func, divisor, y) {
584 continue;
585 }
586 let ry = self.range_of(func, y, depth);
587 if ry.lo.is_zero() {
588 continue;
589 }
590 let rx = self.range_of(func, x, depth);
591 if rx.hi.checked_mul(ry.hi).is_some() {
592 return Some(true);
593 }
594 }
595 None
596 }
597}
598
599fn dominating_edge_fact(
602 func: &Function,
603 preds: &[Vec<BlockId>],
604 block: BlockId,
605) -> Option<(ValueId, bool)> {
606 if block == func.entry_block {
609 return None;
610 }
611 let preds = &preds[block.index()];
612 let (&first, rest) = preds.split_first()?;
613 if rest.iter().any(|&pred| pred != first) {
614 return None;
615 }
616 let Terminator::Branch { condition, then_block, else_block } =
617 func.blocks[first].terminator.as_ref()?
618 else {
619 return None;
620 };
621 if then_block == else_block {
623 return None;
624 }
625 if *then_block == block {
626 Some((*condition, true))
627 } else if *else_block == block {
628 Some((*condition, false))
629 } else {
630 None
631 }
632}
633
634fn const_of(func: &Function, value: ValueId) -> Option<U256> {
635 match func.value(value) {
636 Value::Immediate(imm) => imm.as_u256(),
637 _ => None,
638 }
639}
640
641fn inst_kind(func: &Function, value: ValueId) -> Option<&InstKind> {
642 match func.value(value) {
643 Value::Inst(inst_id) => Some(&func.instructions[*inst_id].kind),
644 _ => None,
645 }
646}
647
648fn values_equal(func: &Function, a: ValueId, b: ValueId) -> bool {
650 if a == b {
651 return true;
652 }
653 match (const_of(func, a), const_of(func, b)) {
654 (Some(a), Some(b)) => a == b,
655 _ => false,
656 }
657}
658
659#[cfg(test)]
660mod tests {
661 use super::*;
662
663 #[test]
664 fn range_intersection_and_union() {
665 let a = Range::new(U256::from(0), U256::from(10));
666 let b = Range::new(U256::from(5), U256::from(20));
667 assert_eq!(a.intersect(b), Some(Range::new(U256::from(5), U256::from(10))));
668 assert_eq!(a.union(b), Range::new(U256::from(0), U256::from(20)));
669
670 let disjoint = Range::new(U256::from(11), U256::from(12));
671 assert_eq!(a.intersect(disjoint), None);
672 }
673}