1use crate::{
14 mir::{
15 Function, Immediate, InstId, InstKind, MirType, Terminator, Value, ValueId,
16 utils as mir_utils,
17 },
18 pass::FunctionPass,
19 utils::evm_word,
20};
21use alloy_primitives::U256;
22use solar_data_structures::map::{FxHashMap, FxHashSet};
23
24#[derive(Debug, Default)]
26pub struct InstSimplifier {
27 pub simplified_count: usize,
29}
30
31pub struct InstSimplifyPass;
33
34impl FunctionPass for InstSimplifyPass {
35 fn name(&self) -> &str {
36 "inst-simplify"
37 }
38
39 fn run_on_function(&mut self, func: &mut Function) -> bool {
40 InstSimplifier::new().run_to_fixpoint(func) != 0
41 }
42}
43
44impl InstSimplifier {
45 pub fn new() -> Self {
47 Self::default()
48 }
49
50 pub fn run(&mut self, func: &mut Function) -> usize {
52 self.simplified_count = 0;
53
54 let inst_results = func.inst_results();
55 let mut replacements: FxHashMap<ValueId, ValueId> = FxHashMap::default();
56 let mut dead: FxHashSet<InstId> = FxHashSet::default();
57 let block_ids: Vec<_> = func.blocks.indices().collect();
58
59 for block_id in block_ids {
60 let inst_ids = func.blocks[block_id].instructions.clone();
61 for inst_id in inst_ids {
62 let kind = func.instructions[inst_id].kind.clone();
63
64 if self.is_dead_noop_inst(func, &kind, &replacements) {
65 dead.insert(inst_id);
66 self.simplified_count += 1;
67 continue;
68 }
69
70 if let Some(new_kind) = self.rewrite_inst(func, &kind, &replacements) {
71 func.instructions[inst_id].kind = new_kind;
72 self.simplified_count += 1;
73 continue;
74 }
75
76 let Some(&result) = inst_results.get(&inst_id) else {
77 continue;
78 };
79 let Some(replacement) = self.simplify_inst(func, &kind, &replacements) else {
80 continue;
81 };
82 let replacement = mir_utils::resolve_replacement(replacement, &replacements);
83 if replacement == result {
84 continue;
85 }
86 replacements.insert(result, replacement);
87 dead.insert(inst_id);
88 self.simplified_count += 1;
89 }
90 }
91
92 if !replacements.is_empty() {
93 func.replace_uses_canonicalized(&replacements);
94 }
95 if !dead.is_empty() {
96 for block in func.blocks.iter_mut() {
97 block.instructions.retain(|id| !dead.contains(id));
98 }
99 }
100 self.simplified_count += self.rewrite_terminators(func, &replacements);
101
102 self.simplified_count
103 }
104
105 pub fn run_to_fixpoint(&mut self, func: &mut Function) -> usize {
107 let mut total = 0;
108 loop {
109 let simplified = self.run(func);
110 if simplified == 0 {
111 break;
112 }
113 total += simplified;
114 }
115 total
116 }
117
118 fn rewrite_inst(
119 &mut self,
120 func: &mut Function,
121 kind: &InstKind,
122 replacements: &FxHashMap<ValueId, ValueId>,
123 ) -> Option<InstKind> {
124 let resolve = |value| mir_utils::resolve_replacement(value, replacements);
125
126 match kind {
127 InstKind::Add(a, b) => {
128 let (a, b) = (resolve(*a), resolve(*b));
129 self.rewrite_add(func, a, b)
130 }
131 InstKind::Sub(a, b) => {
132 let (a, b) = (resolve(*a), resolve(*b));
133 self.rewrite_sub(func, a, b)
134 }
135 InstKind::Mul(a, b) => {
136 let (a, b) = (resolve(*a), resolve(*b));
137 self.rewrite_mul(func, a, b)
138 }
139 InstKind::Div(a, b) => {
140 let (a, b) = (resolve(*a), resolve(*b));
141 self.rewrite_div(func, a, b)
142 }
143 InstKind::Mod(a, b) => {
144 let (a, b) = (resolve(*a), resolve(*b));
145 self.rewrite_mod(func, a, b)
146 }
147 InstKind::Exp(a, b) => {
148 let (a, b) = (resolve(*a), resolve(*b));
149 if Self::is_const(func, b, U256::from(2)) {
150 Some(InstKind::Mul(a, a))
151 } else {
152 None
153 }
154 }
155 InstKind::And(a, b) => {
156 let (a, b) = (resolve(*a), resolve(*b));
157 self.rewrite_and(func, a, b)
158 }
159 InstKind::Xor(a, b) => {
160 let (a, b) = (resolve(*a), resolve(*b));
161 if Self::is_all_ones(func, a) {
162 Some(InstKind::Not(b))
163 } else if Self::is_all_ones(func, b) {
164 Some(InstKind::Not(a))
165 } else {
166 None
167 }
168 }
169 InstKind::Eq(a, b) => {
170 let (a, b) = (resolve(*a), resolve(*b));
171 if a != b && Self::is_zero(func, a) {
172 Some(InstKind::IsZero(b))
173 } else if a != b && Self::is_zero(func, b) {
174 Some(InstKind::IsZero(a))
175 } else {
176 None
177 }
178 }
179 InstKind::Lt(a, b) => {
181 let (a, b) = (resolve(*a), resolve(*b));
182 Self::is_one(func, b).then_some(InstKind::IsZero(a))
183 }
184 InstKind::Gt(a, b) => {
186 let (a, b) = (resolve(*a), resolve(*b));
187 Self::is_one(func, a).then_some(InstKind::IsZero(b))
188 }
189 InstKind::Select(condition, then_value, else_value) => {
190 let (condition, then_value, else_value) =
191 (resolve(*condition), resolve(*then_value), resolve(*else_value));
192 if Self::is_bool_value(func, condition)
193 && Self::is_zero(func, then_value)
194 && Self::is_one(func, else_value)
195 {
196 Some(InstKind::IsZero(condition))
197 } else {
198 None
199 }
200 }
201 InstKind::Balance(addr) => {
202 let addr = resolve(*addr);
203 Self::is_current_address(func, addr).then_some(InstKind::SelfBalance)
204 }
205 _ => None,
206 }
207 }
208
209 fn simplify_inst(
210 &mut self,
211 func: &mut Function,
212 kind: &InstKind,
213 replacements: &FxHashMap<ValueId, ValueId>,
214 ) -> Option<ValueId> {
215 let resolve = |value| mir_utils::resolve_replacement(value, replacements);
216
217 if let Some(value) = Self::const_fold_inst(func, kind, replacements) {
218 return Some(value);
219 }
220
221 match kind {
222 InstKind::Add(a, b) => {
223 let (a, b) = (resolve(*a), resolve(*b));
224 if Self::is_zero(func, b) {
225 Some(a)
226 } else if Self::is_zero(func, a) {
227 Some(b)
228 } else {
229 None
230 }
231 }
232 InstKind::Sub(a, b) => {
233 let (a, b) = (resolve(*a), resolve(*b));
234 if Self::is_zero(func, b) {
235 Some(a)
236 } else if a == b {
237 Some(Self::imm_u256(func, U256::ZERO))
238 } else {
239 None
240 }
241 }
242 InstKind::Mul(a, b) => {
243 let (a, b) = (resolve(*a), resolve(*b));
244 if Self::is_zero(func, a) || Self::is_one(func, b) {
245 Some(a)
246 } else if Self::is_zero(func, b) || Self::is_one(func, a) {
247 Some(b)
248 } else {
249 None
250 }
251 }
252 InstKind::Div(a, b) | InstKind::SDiv(a, b) => {
253 let (a, b) = (resolve(*a), resolve(*b));
254 if Self::is_zero(func, a) || Self::is_one(func, b) {
255 Some(a)
256 } else if Self::is_zero(func, b) {
257 Some(Self::imm_u256(func, U256::ZERO))
258 } else {
259 None
260 }
261 }
262 InstKind::Mod(a, b) | InstKind::SMod(a, b) => {
263 let (a, b) = (resolve(*a), resolve(*b));
264 if Self::is_zero(func, a) {
265 Some(a)
266 } else if Self::is_zero(func, b) || Self::is_one(func, b) || a == b {
267 Some(Self::imm_u256(func, U256::ZERO))
268 } else {
269 None
270 }
271 }
272 InstKind::Exp(a, b) => {
273 let (a, b) = (resolve(*a), resolve(*b));
274 if Self::is_zero(func, b) {
275 Some(Self::imm_u256(func, U256::from(1)))
276 } else if Self::is_one(func, a) || Self::is_one(func, b) {
277 Some(a)
278 } else {
279 None
280 }
281 }
282 InstKind::And(a, b) => {
283 let (a, b) = (resolve(*a), resolve(*b));
284 if a == b
285 || Self::is_zero(func, a)
286 || Self::is_all_ones(func, b)
287 || (Self::is_uint160_mask(func, b) && Self::is_clean_address(func, a))
288 {
289 Some(a)
290 } else if Self::is_zero(func, b)
291 || Self::is_all_ones(func, a)
292 || (Self::is_uint160_mask(func, a) && Self::is_clean_address(func, b))
293 {
294 Some(b)
295 } else if Self::is_bitwise_complement_pair(func, a, b) {
296 Some(Self::imm_u256(func, U256::ZERO))
297 } else {
298 None
299 }
300 }
301 InstKind::Or(a, b) => {
302 let (a, b) = (resolve(*a), resolve(*b));
303 if a == b || Self::is_all_ones(func, a) || Self::is_zero(func, b) {
304 Some(a)
305 } else if Self::is_all_ones(func, b) || Self::is_zero(func, a) {
306 Some(b)
307 } else if Self::is_bitwise_complement_pair(func, a, b) {
308 Some(Self::imm_u256(func, U256::MAX))
309 } else {
310 None
311 }
312 }
313 InstKind::Xor(a, b) => {
314 let (a, b) = (resolve(*a), resolve(*b));
315 if a == b {
316 Some(Self::imm_u256(func, U256::ZERO))
317 } else if Self::is_zero(func, a) {
318 Some(b)
319 } else if Self::is_zero(func, b) {
320 Some(a)
321 } else if Self::is_bitwise_complement_pair(func, a, b) {
322 Some(Self::imm_u256(func, U256::MAX))
323 } else {
324 None
325 }
326 }
327 InstKind::Not(a) => {
328 let a = resolve(*a);
329 Self::not_operand(func, a)
330 .or_else(|| func.value_u256(a).map(|v| Self::imm_u256(func, !v)))
331 }
332 InstKind::IsZero(a) => {
333 let a = resolve(*a);
334 func.value_u256(a).map(|v| Self::imm_bool(func, v.is_zero())).or_else(|| {
335 let inner = Self::iszero_operand(func, a)?;
336 Self::is_bool_value(func, inner).then_some(inner)
337 })
338 }
339 InstKind::Shl(a, b) | InstKind::Shr(a, b) | InstKind::Sar(a, b) => {
340 let (shift, value) = (resolve(*a), resolve(*b));
341 if Self::is_zero(func, shift) || Self::is_zero(func, value) {
342 Some(value)
343 } else if !matches!(kind, InstKind::Sar(_, _))
344 && func.value_u256(shift).is_some_and(|shift| shift >= U256::from(256))
345 {
346 Some(Self::imm_u256(func, U256::ZERO))
347 } else {
348 None
349 }
350 }
351 InstKind::Byte(a, b) => {
352 let (a, value) = (resolve(*a), resolve(*b));
353 (Self::is_zero(func, value)
354 || func.value_u256(a).is_some_and(|index| index >= U256::from(32)))
355 .then(|| Self::imm_u256(func, U256::ZERO))
356 }
357 InstKind::Eq(a, b) => {
358 let (a, b) = (resolve(*a), resolve(*b));
359 if a == b {
360 Some(Self::imm_bool(func, true))
361 } else if Self::is_bool_value(func, a) && Self::is_one(func, b) {
362 Some(a)
363 } else if Self::is_bool_value(func, b) && Self::is_one(func, a) {
364 Some(b)
365 } else {
366 None
367 }
368 }
369 InstKind::Lt(a, b) | InstKind::Gt(a, b) | InstKind::SLt(a, b) | InstKind::SGt(a, b) => {
370 let (a, b) = (resolve(*a), resolve(*b));
371 if a == b {
372 Some(Self::imm_bool(func, false))
373 } else {
374 match kind {
375 InstKind::Lt(_, _) if Self::is_zero(func, b) => {
376 Some(Self::imm_bool(func, false))
377 }
378 InstKind::Lt(_, _)
379 if Self::is_zero(func, a) && Self::is_bool_value(func, b) =>
380 {
381 Some(b)
382 }
383 InstKind::Gt(_, _) if Self::is_zero(func, a) => {
384 Some(Self::imm_bool(func, false))
385 }
386 InstKind::Gt(_, _)
387 if Self::is_zero(func, b) && Self::is_bool_value(func, a) =>
388 {
389 Some(a)
390 }
391 InstKind::Lt(_, _)
392 if Self::is_bool_value(func, a)
393 && func
394 .value_u256(b)
395 .is_some_and(|constant| constant > U256::from(1)) =>
396 {
397 Some(Self::imm_bool(func, true))
398 }
399 InstKind::Lt(_, _)
400 if Self::is_bool_value(func, b)
401 && func
402 .value_u256(a)
403 .is_some_and(|constant| constant >= U256::from(1)) =>
404 {
405 Some(Self::imm_bool(func, false))
406 }
407 InstKind::Gt(_, _)
408 if Self::is_bool_value(func, b)
409 && func
410 .value_u256(a)
411 .is_some_and(|constant| constant > U256::from(1)) =>
412 {
413 Some(Self::imm_bool(func, true))
414 }
415 InstKind::Gt(_, _)
416 if Self::is_bool_value(func, a)
417 && func
418 .value_u256(b)
419 .is_some_and(|constant| constant >= U256::from(1)) =>
420 {
421 Some(Self::imm_bool(func, false))
422 }
423 _ => None,
424 }
425 }
426 }
427 InstKind::AddMod(a, b, n) => {
428 let (a, b, n) = (resolve(*a), resolve(*b), resolve(*n));
429 if Self::is_zero(func, n)
430 || Self::is_one(func, n)
431 || (Self::is_zero(func, a) && Self::is_zero(func, b))
432 {
433 Some(Self::imm_u256(func, U256::ZERO))
434 } else {
435 None
436 }
437 }
438 InstKind::MulMod(a, b, n) => {
439 let (a, b, n) = (resolve(*a), resolve(*b), resolve(*n));
440 if Self::is_zero(func, n)
441 || Self::is_one(func, n)
442 || Self::is_zero(func, a)
443 || Self::is_zero(func, b)
444 {
445 Some(Self::imm_u256(func, U256::ZERO))
446 } else {
447 None
448 }
449 }
450 InstKind::SignExtend(a, b) => {
451 let (byte, value) = (resolve(*a), resolve(*b));
452 if Self::is_zero(func, value)
453 || func.value_u256(byte).is_some_and(|byte| byte >= U256::from(31))
454 {
455 Some(value)
456 } else {
457 None
458 }
459 }
460 InstKind::Select(condition, then_value, else_value) => {
461 let (condition, then_value, else_value) =
462 (resolve(*condition), resolve(*then_value), resolve(*else_value));
463 if Self::is_one(func, condition) {
464 Some(then_value)
465 } else if Self::is_zero(func, condition) {
466 Some(else_value)
467 } else if Self::same_value(func, then_value, else_value) {
468 Some(then_value)
469 } else if Self::is_bool_value(func, condition)
470 && Self::is_one(func, then_value)
471 && Self::is_zero(func, else_value)
472 {
473 Some(condition)
474 } else {
475 None
476 }
477 }
478 InstKind::Phi(incoming) => {
479 let &(_, first) = incoming.first()?;
480 let first = resolve(first);
481 incoming
482 .iter()
483 .all(|&(_, value)| Self::same_value(func, resolve(value), first))
484 .then_some(first)
485 }
486 _ => None,
487 }
488 }
489
490 fn const_fold_inst(
491 func: &mut Function,
492 kind: &InstKind,
493 replacements: &FxHashMap<ValueId, ValueId>,
494 ) -> Option<ValueId> {
495 let resolve = |value| mir_utils::resolve_replacement(value, replacements);
496 let constant = |func: &Function, value| func.value_u256(resolve(value));
497
498 match *kind {
499 InstKind::Add(a, b) => {
500 Some(Self::imm_u256(func, constant(func, a)?.wrapping_add(constant(func, b)?)))
501 }
502 InstKind::Sub(a, b) => {
503 Some(Self::imm_u256(func, constant(func, a)?.wrapping_sub(constant(func, b)?)))
504 }
505 InstKind::Mul(a, b) => {
506 Some(Self::imm_u256(func, constant(func, a)?.wrapping_mul(constant(func, b)?)))
507 }
508 InstKind::Div(a, b) => {
509 let a = constant(func, a)?;
510 let b = constant(func, b)?;
511 Some(Self::imm_u256(func, if b.is_zero() { U256::ZERO } else { a / b }))
512 }
513 InstKind::SDiv(a, b) => {
514 let a = constant(func, a)?;
515 let b = constant(func, b)?;
516 Some(Self::imm_u256(func, evm_word::signed_div(a, b)))
517 }
518 InstKind::Mod(a, b) => {
519 let a = constant(func, a)?;
520 let b = constant(func, b)?;
521 Some(Self::imm_u256(func, if b.is_zero() { U256::ZERO } else { a % b }))
522 }
523 InstKind::SMod(a, b) => {
524 let a = constant(func, a)?;
525 let b = constant(func, b)?;
526 Some(Self::imm_u256(func, evm_word::signed_mod(a, b)))
527 }
528 InstKind::Exp(a, b) => {
529 Some(Self::imm_u256(func, constant(func, a)?.wrapping_pow(constant(func, b)?)))
530 }
531 InstKind::AddMod(a, b, n) => {
532 let a = constant(func, a)?;
533 let b = constant(func, b)?;
534 let n = constant(func, n)?;
535 if n.is_zero() {
536 Some(Self::imm_u256(func, U256::ZERO))
537 } else {
538 Some(Self::imm_u256(func, a.add_mod(b, n)))
539 }
540 }
541 InstKind::MulMod(a, b, n) => {
542 let a = constant(func, a)?;
543 let b = constant(func, b)?;
544 let n = constant(func, n)?;
545 if n.is_zero() {
546 Some(Self::imm_u256(func, U256::ZERO))
547 } else {
548 Some(Self::imm_u256(func, a.mul_mod(b, n)))
549 }
550 }
551 InstKind::And(a, b) => {
552 Some(Self::imm_u256(func, constant(func, a)? & constant(func, b)?))
553 }
554 InstKind::Or(a, b) => {
555 Some(Self::imm_u256(func, constant(func, a)? | constant(func, b)?))
556 }
557 InstKind::Xor(a, b) => {
558 Some(Self::imm_u256(func, constant(func, a)? ^ constant(func, b)?))
559 }
560 InstKind::Not(a) => Some(Self::imm_u256(func, !constant(func, a)?)),
561 InstKind::Shl(shift, value) => {
562 let shift = constant(func, shift)?;
563 let value = constant(func, value)?;
564 let folded = if shift >= U256::from(256) {
565 U256::ZERO
566 } else {
567 value << shift.to::<usize>()
568 };
569 Some(Self::imm_u256(func, folded))
570 }
571 InstKind::Shr(shift, value) => {
572 let shift = constant(func, shift)?;
573 let value = constant(func, value)?;
574 let folded = if shift >= U256::from(256) {
575 U256::ZERO
576 } else {
577 value >> shift.to::<usize>()
578 };
579 Some(Self::imm_u256(func, folded))
580 }
581 InstKind::Sar(shift, value) => Some(Self::imm_u256(
582 func,
583 evm_word::sar(constant(func, value)?, constant(func, shift)?),
584 )),
585 InstKind::Byte(index, value) => Some(Self::imm_u256(
586 func,
587 evm_word::byte(constant(func, index)?, constant(func, value)?),
588 )),
589 InstKind::SignExtend(size, value) => Some(Self::imm_u256(
590 func,
591 evm_word::signextend(constant(func, size)?, constant(func, value)?),
592 )),
593 InstKind::Lt(a, b) => {
594 Some(Self::imm_bool(func, constant(func, a)? < constant(func, b)?))
595 }
596 InstKind::Gt(a, b) => {
597 Some(Self::imm_bool(func, constant(func, a)? > constant(func, b)?))
598 }
599 InstKind::SLt(a, b) => Some(Self::imm_bool(
600 func,
601 evm_word::signed_lt(constant(func, a)?, constant(func, b)?),
602 )),
603 InstKind::SGt(a, b) => Some(Self::imm_bool(
604 func,
605 evm_word::signed_gt(constant(func, a)?, constant(func, b)?),
606 )),
607 InstKind::Eq(a, b) => {
608 Some(Self::imm_bool(func, constant(func, a)? == constant(func, b)?))
609 }
610 InstKind::IsZero(a) => Some(Self::imm_bool(func, constant(func, a)?.is_zero())),
611 InstKind::Select(condition, then_value, else_value) => {
612 let condition = constant(func, condition)?;
613 Some(if condition.is_zero() { resolve(else_value) } else { resolve(then_value) })
614 }
615 _ => None,
616 }
617 }
618
619 fn is_dead_noop_inst(
620 &self,
621 func: &Function,
622 kind: &InstKind,
623 replacements: &FxHashMap<ValueId, ValueId>,
624 ) -> bool {
625 let resolve = |value| mir_utils::resolve_replacement(value, replacements);
626 match kind {
627 InstKind::MCopy(_, _, size)
628 | InstKind::CalldataCopy(_, _, size)
629 | InstKind::CodeCopy(_, _, size) => Self::is_zero(func, resolve(*size)),
630 InstKind::ReturnDataCopy(_, offset, size) => {
631 Self::is_zero(func, resolve(*offset)) && Self::is_zero(func, resolve(*size))
632 }
633 _ => false,
634 }
635 }
636
637 fn rewrite_add(&self, func: &mut Function, a: ValueId, b: ValueId) -> Option<InstKind> {
638 if Self::is_zero(func, a) || Self::is_zero(func, b) {
639 return None;
640 }
641 match (func.value_u256(a), func.value_u256(b)) {
642 (None, Some(offset)) => Self::offset_base(func, a).map(|(base, existing)| {
643 self.add_offset_kind(func, base, existing.wrapping_add(offset))
644 }),
645 (Some(offset), None) => Self::offset_base(func, b)
646 .map(|(base, existing)| {
647 self.add_offset_kind(func, base, existing.wrapping_add(offset))
648 })
649 .or(Some(InstKind::Add(b, a))),
650 _ => None,
651 }
652 }
653
654 fn rewrite_sub(&self, func: &mut Function, a: ValueId, b: ValueId) -> Option<InstKind> {
655 let offset = func.value_u256(b)?;
656 let (base, existing) = Self::offset_base(func, a)?;
657 Some(self.add_offset_kind(func, base, existing.wrapping_sub(offset)))
658 }
659
660 fn rewrite_mul(&self, func: &mut Function, a: ValueId, b: ValueId) -> Option<InstKind> {
661 if Self::is_zero(func, a)
662 || Self::is_zero(func, b)
663 || Self::is_one(func, a)
664 || Self::is_one(func, b)
665 {
666 return None;
667 }
668 if func.value_u256(a).is_some() && func.value_u256(b).is_none() {
669 return Some(InstKind::Mul(b, a));
670 }
671 let (value, constant) = Self::const_operand(func, a, b)?;
672 let shift = Self::power_of_two_shift(constant)?;
673 if shift.is_zero() {
674 return None;
675 }
676 let shift = Self::imm_u256(func, shift);
677 Some(InstKind::Shl(shift, value))
678 }
679
680 fn rewrite_div(&self, func: &mut Function, a: ValueId, b: ValueId) -> Option<InstKind> {
681 let shift = Self::power_of_two_shift(func.value_u256(b)?)?;
682 if shift.is_zero() {
683 return None;
684 }
685 let shift = Self::imm_u256(func, shift);
686 Some(InstKind::Shr(shift, a))
687 }
688
689 fn rewrite_mod(&self, func: &mut Function, a: ValueId, b: ValueId) -> Option<InstKind> {
690 let constant = func.value_u256(b)?;
691 let shift = Self::power_of_two_shift(constant)?;
692 if shift.is_zero() {
693 return None;
694 }
695 let mask = Self::imm_u256(func, constant - U256::from(1));
696 Some(InstKind::And(a, mask))
697 }
698
699 fn rewrite_and(&self, func: &mut Function, a: ValueId, b: ValueId) -> Option<InstKind> {
700 if a == b
701 || Self::is_zero(func, a)
702 || Self::is_zero(func, b)
703 || Self::is_all_ones(func, a)
704 || Self::is_all_ones(func, b)
705 || (Self::is_uint160_mask(func, a) && Self::is_clean_address(func, b))
706 || (Self::is_uint160_mask(func, b) && Self::is_clean_address(func, a))
707 {
708 return None;
709 }
710 if func.value_u256(a).is_some() && func.value_u256(b).is_none() {
711 return Some(InstKind::And(b, a));
712 }
713 let (value, mask) = Self::const_operand(func, a, b)?;
714 let (base, existing_mask) = Self::and_mask_base(func, value)?;
715 let combined = Self::imm_u256(func, mask & existing_mask);
716 Some(InstKind::And(base, combined))
717 }
718
719 fn add_offset_kind(&self, func: &mut Function, base: ValueId, offset: U256) -> InstKind {
720 let offset = Self::imm_u256(func, offset);
721 InstKind::Add(base, offset)
722 }
723
724 fn const_operand(func: &Function, a: ValueId, b: ValueId) -> Option<(ValueId, U256)> {
725 if let Some(constant) = func.value_u256(b) {
726 Some((a, constant))
727 } else {
728 func.value_u256(a).map(|constant| (b, constant))
729 }
730 }
731
732 fn offset_base(func: &Function, value: ValueId) -> Option<(ValueId, U256)> {
733 let Value::Inst(inst_id) = func.value(value) else { return None };
734 match func.instructions[*inst_id].kind {
735 InstKind::Add(a, b) => Self::const_operand(func, a, b),
736 InstKind::Sub(a, b) => {
737 let offset = func.value_u256(b)?;
738 Some((a, U256::ZERO.wrapping_sub(offset)))
739 }
740 _ => None,
741 }
742 }
743
744 fn and_mask_base(func: &Function, value: ValueId) -> Option<(ValueId, U256)> {
745 let Value::Inst(inst_id) = func.value(value) else { return None };
746 match func.instructions[*inst_id].kind {
747 InstKind::And(a, b) => Self::const_operand(func, a, b),
748 _ => None,
749 }
750 }
751
752 fn power_of_two_shift(value: U256) -> Option<U256> {
753 if value.is_zero() || (value & (value - U256::from(1))) != U256::ZERO {
754 return None;
755 }
756 Some(U256::from(value.trailing_zeros()))
757 }
758
759 fn imm_u256(func: &mut Function, value: U256) -> ValueId {
760 func.alloc_value(Value::Immediate(Immediate::uint256(value)))
761 }
762
763 fn imm_bool(func: &mut Function, value: bool) -> ValueId {
764 func.alloc_value(Value::Immediate(Immediate::bool(value)))
765 }
766
767 fn is_const(func: &Function, value: ValueId, expected: U256) -> bool {
768 func.value_u256(value) == Some(expected)
769 }
770
771 fn is_zero(func: &Function, value: ValueId) -> bool {
772 Self::is_const(func, value, U256::ZERO)
773 }
774
775 fn is_one(func: &Function, value: ValueId) -> bool {
776 Self::is_const(func, value, U256::from(1))
777 }
778
779 fn is_bool_value(func: &Function, value: ValueId) -> bool {
780 match &func.values[value] {
781 Value::Immediate(Immediate::Bool(_)) => true,
782 Value::Arg { ty: MirType::Bool, .. } => true,
783 Value::Inst(inst_id) => func.instructions[*inst_id].result_ty == Some(MirType::Bool),
784 _ => false,
785 }
786 }
787
788 fn same_value(func: &Function, a: ValueId, b: ValueId) -> bool {
789 a == b
790 || match (&func.values[a], &func.values[b]) {
791 (Value::Immediate(a), Value::Immediate(b)) => a == b,
792 _ => false,
793 }
794 }
795
796 fn is_all_ones(func: &Function, value: ValueId) -> bool {
797 Self::is_const(func, value, U256::MAX)
798 }
799
800 fn is_uint160_mask(func: &Function, value: ValueId) -> bool {
801 let mask = (U256::from(1) << 160) - U256::from(1);
802 Self::is_const(func, value, mask)
803 }
804
805 fn is_clean_address(func: &Function, value: ValueId) -> bool {
806 match &func.values[value] {
807 Value::Immediate(Immediate::Address(_)) => true,
808 Value::Inst(inst_id) => matches!(
809 func.instructions[*inst_id].kind,
810 InstKind::Address
811 | InstKind::Caller
812 | InstKind::Origin
813 | InstKind::Coinbase
814 | InstKind::Create(_, _, _)
815 | InstKind::Create2(_, _, _, _)
816 ),
817 _ => false,
818 }
819 }
820
821 fn is_current_address(func: &Function, value: ValueId) -> bool {
822 match &func.values[value] {
823 Value::Inst(inst_id) => matches!(func.instructions[*inst_id].kind, InstKind::Address),
824 _ => false,
825 }
826 }
827
828 fn rewrite_terminators(
829 &mut self,
830 func: &mut Function,
831 replacements: &FxHashMap<ValueId, ValueId>,
832 ) -> usize {
833 let mut rewrites = Vec::new();
834 for block_id in func.blocks.indices() {
835 let Some(Terminator::Branch { condition, .. }) = func.blocks[block_id].terminator
836 else {
837 continue;
838 };
839 let condition = mir_utils::resolve_replacement(condition, replacements);
840 if let Some(inner) = Self::iszero_operand(func, condition) {
841 rewrites.push((
842 block_id,
843 mir_utils::resolve_replacement(inner, replacements),
844 true,
845 ));
846 } else if let Some(inner) = Self::nonzero_test_operand(func, condition) {
847 rewrites.push((
850 block_id,
851 mir_utils::resolve_replacement(inner, replacements),
852 false,
853 ));
854 }
855 }
856
857 for (block_id, inner, swap) in rewrites.iter().copied() {
858 {
859 let Some(Terminator::Branch { condition, then_block, else_block }) =
860 &mut func.blocks[block_id].terminator
861 else {
862 continue;
863 };
864 *condition = inner;
865 if swap {
866 std::mem::swap(then_block, else_block);
867 }
868 }
869 }
870
871 rewrites.len()
872 }
873
874 fn nonzero_test_operand(func: &Function, value: ValueId) -> Option<ValueId> {
877 match &func.values[value] {
878 Value::Inst(inst_id) => match func.instructions[*inst_id].kind {
879 InstKind::Gt(a, b) if Self::is_zero(func, b) => Some(a),
880 InstKind::Lt(a, b) if Self::is_zero(func, a) => Some(b),
881 _ => None,
882 },
883 _ => None,
884 }
885 }
886
887 fn iszero_operand(func: &Function, value: ValueId) -> Option<ValueId> {
888 match &func.values[value] {
889 Value::Inst(inst_id) => match func.instructions[*inst_id].kind {
890 InstKind::IsZero(inner) => Some(inner),
891 _ => None,
892 },
893 _ => None,
894 }
895 }
896
897 fn not_operand(func: &Function, value: ValueId) -> Option<ValueId> {
898 match &func.values[value] {
899 Value::Inst(inst_id) => match func.instructions[*inst_id].kind {
900 InstKind::Not(inner) => Some(inner),
901 _ => None,
902 },
903 _ => None,
904 }
905 }
906
907 fn is_bitwise_complement_pair(func: &Function, a: ValueId, b: ValueId) -> bool {
908 Self::not_operand(func, a) == Some(b) || Self::not_operand(func, b) == Some(a)
909 }
910}
911
912#[cfg(test)]
913mod tests {
914 use super::*;
915 use crate::mir::{FunctionBuilder, MirType};
916 use solar_interface::Ident;
917
918 fn test_func() -> Function {
919 Function::new(Ident::DUMMY)
920 }
921
922 #[test]
923 fn removes_add_zero() {
924 let mut func = test_func();
925 let mut builder = FunctionBuilder::new(&mut func);
926 let arg = builder.add_param(MirType::uint256());
927 let zero = builder.imm_u64(0);
928 let sum = builder.add(arg, zero);
929 builder.ret(vec![sum]);
930
931 let mut pass = InstSimplifier::new();
932 assert_eq!(pass.run(&mut func), 1);
933
934 let block = &func.blocks[func.entry_block];
935 assert!(block.instructions.is_empty());
936 let Some(Terminator::Return { values }) = &block.terminator else {
937 panic!("expected return terminator");
938 };
939 assert_eq!(values.as_slice(), &[arg]);
940 }
941
942 #[test]
943 fn preserves_non_constant_side_of_mul_one() {
944 let mut func = test_func();
945 let mut builder = FunctionBuilder::new(&mut func);
946 let arg = builder.add_param(MirType::uint256());
947 let one = builder.imm_u64(1);
948 let product = builder.mul(one, arg);
949 builder.ret(vec![product]);
950
951 let mut pass = InstSimplifier::new();
952 assert_eq!(pass.run(&mut func), 1);
953
954 let block = &func.blocks[func.entry_block];
955 let Some(Terminator::Return { values }) = &block.terminator else {
956 panic!("expected return terminator");
957 };
958 assert_eq!(values.as_slice(), &[arg]);
959 }
960
961 #[test]
962 fn removes_self_sub() {
963 let mut func = test_func();
964 let mut builder = FunctionBuilder::new(&mut func);
965 let arg = builder.add_param(MirType::uint256());
966 let diff = builder.sub(arg, arg);
967 builder.ret(vec![diff]);
968
969 let mut pass = InstSimplifier::new();
970 assert_eq!(pass.run(&mut func), 1);
971
972 let block = &func.blocks[func.entry_block];
973 assert!(block.instructions.is_empty());
974 let Some(Terminator::Return { values }) = &block.terminator else {
975 panic!("expected return terminator");
976 };
977 assert_eq!(
978 func.values[values[0]].as_immediate().and_then(Immediate::as_u256),
979 Some(U256::ZERO)
980 );
981 }
982
983 #[test]
984 fn removes_idempotent_logic_ops() {
985 let mut func = test_func();
986 let mut builder = FunctionBuilder::new(&mut func);
987 let arg = builder.add_param(MirType::uint256());
988 let and = builder.and(arg, arg);
989 let or = builder.or(arg, arg);
990 let xor = builder.xor(arg, arg);
991 builder.ret(vec![and, or, xor]);
992
993 let mut pass = InstSimplifier::new();
994 assert_eq!(pass.run(&mut func), 3);
995
996 let block = &func.blocks[func.entry_block];
997 assert!(block.instructions.is_empty());
998 let Some(Terminator::Return { values }) = &block.terminator else {
999 panic!("expected return terminator");
1000 };
1001 assert_eq!(&values[..2], &[arg, arg]);
1002 assert_eq!(
1003 func.values[values[2]].as_immediate().and_then(Immediate::as_u256),
1004 Some(U256::ZERO)
1005 );
1006 }
1007
1008 #[test]
1009 fn folds_not_not() {
1010 let mut func = test_func();
1011 let mut builder = FunctionBuilder::new(&mut func);
1012 let arg = builder.add_param(MirType::uint256());
1013 let not = builder.not(arg);
1014 let restored = builder.not(not);
1015 builder.ret(vec![restored]);
1016
1017 let mut pass = InstSimplifier::new();
1018 assert_eq!(pass.run_to_fixpoint(&mut func), 1);
1019
1020 let block = &func.blocks[func.entry_block];
1021 let Some(Terminator::Return { values }) = &block.terminator else {
1022 panic!("expected return terminator");
1023 };
1024 assert_eq!(values.as_slice(), &[arg]);
1025 }
1026
1027 #[test]
1028 fn folds_iszero_immediate() {
1029 let mut func = test_func();
1030 let mut builder = FunctionBuilder::new(&mut func);
1031 let zero = builder.imm_u64(0);
1032 let is_zero = builder.iszero(zero);
1033 builder.ret(vec![is_zero]);
1034
1035 let mut pass = InstSimplifier::new();
1036 assert_eq!(pass.run(&mut func), 1);
1037
1038 let block = &func.blocks[func.entry_block];
1039 assert!(block.instructions.is_empty());
1040 let Some(Terminator::Return { values }) = &block.terminator else {
1041 panic!("expected return terminator");
1042 };
1043 assert_eq!(
1044 func.values[values[0]].as_immediate().and_then(Immediate::as_u256),
1045 Some(U256::from(1))
1046 );
1047 }
1048
1049 #[test]
1050 fn rewrites_eq_zero_to_iszero() {
1051 let mut func = test_func();
1052 let mut builder = FunctionBuilder::new(&mut func);
1053 let arg = builder.add_param(MirType::uint256());
1054 let zero = builder.imm_u64(0);
1055 let eq = builder.eq(arg, zero);
1056 builder.ret(vec![eq]);
1057
1058 let mut pass = InstSimplifier::new();
1059 assert_eq!(pass.run(&mut func), 1);
1060
1061 let block = &func.blocks[func.entry_block];
1062 assert_eq!(block.instructions.len(), 1);
1063 let eq_inst = func.instructions[block.instructions[0]].kind.clone();
1064 assert!(matches!(eq_inst, InstKind::IsZero(value) if value == arg));
1065 }
1066
1067 #[test]
1068 fn preserves_non_constant_side_of_and_all_ones() {
1069 let mut func = test_func();
1070 let mut builder = FunctionBuilder::new(&mut func);
1071 let arg = builder.add_param(MirType::uint256());
1072 let all_ones = builder.imm_u256(U256::MAX);
1073 let masked = builder.and(all_ones, arg);
1074 builder.ret(vec![masked]);
1075
1076 let mut pass = InstSimplifier::new();
1077 assert_eq!(pass.run(&mut func), 1);
1078
1079 let block = &func.blocks[func.entry_block];
1080 let Some(Terminator::Return { values }) = &block.terminator else {
1081 panic!("expected return terminator");
1082 };
1083 assert_eq!(values.as_slice(), &[arg]);
1084 }
1085
1086 #[test]
1087 fn removes_address_mask() {
1088 let mut func = test_func();
1089 let mut builder = FunctionBuilder::new(&mut func);
1090 let addr = builder.address();
1091 let mask = builder.imm_u256((U256::from(1) << 160) - U256::from(1));
1092 let masked = builder.and(addr, mask);
1093 builder.ret(vec![masked]);
1094
1095 let mut pass = InstSimplifier::new();
1096 assert_eq!(pass.run(&mut func), 1);
1097
1098 let block = &func.blocks[func.entry_block];
1099 assert_eq!(block.instructions.len(), 1);
1100 let Some(Terminator::Return { values }) = &block.terminator else {
1101 panic!("expected return terminator");
1102 };
1103 assert_eq!(values.as_slice(), &[addr]);
1104 }
1105
1106 #[test]
1107 fn rewrites_own_balance_to_selfbalance() {
1108 let mut func = test_func();
1109 let mut builder = FunctionBuilder::new(&mut func);
1110 let addr = builder.address();
1111 let balance = builder.balance(addr);
1112 builder.ret(vec![balance]);
1113
1114 let mut pass = InstSimplifier::new();
1115 assert_eq!(pass.run(&mut func), 1);
1116
1117 let block = &func.blocks[func.entry_block];
1118 assert_eq!(block.instructions.len(), 2);
1119 let balance_inst = func.instructions[*block.instructions.last().unwrap()].kind.clone();
1120 assert!(matches!(balance_inst, InstKind::SelfBalance));
1121 }
1122
1123 #[test]
1124 fn inverts_iszero_branch_condition() {
1125 let mut func = test_func();
1126 let mut builder = FunctionBuilder::new(&mut func);
1127 let arg = builder.add_param(MirType::uint256());
1128 let zero = builder.imm_u64(0);
1129 let cmp = builder.lt(zero, arg);
1130 let inverted = builder.iszero(cmp);
1131 let then_block = builder.create_block();
1132 let else_block = builder.create_block();
1133 builder.branch(inverted, then_block, else_block);
1134
1135 let mut pass = InstSimplifier::new();
1136 assert_eq!(pass.run(&mut func), 1);
1137
1138 let block = &func.blocks[func.entry_block];
1139 let Some(Terminator::Branch { condition, then_block: new_then, else_block: new_else }) =
1140 block.terminator
1141 else {
1142 panic!("expected branch terminator");
1143 };
1144 assert_eq!(condition, cmp);
1145 assert_eq!(new_then, else_block);
1146 assert_eq!(new_else, then_block);
1147 }
1148
1149 #[test]
1150 fn mask_rewrite_feeds_selfbalance() {
1151 let mut func = test_func();
1152 let mut builder = FunctionBuilder::new(&mut func);
1153 let addr = builder.address();
1154 let mask = builder.imm_u256((U256::from(1) << 160) - U256::from(1));
1155 let masked = builder.and(addr, mask);
1156 let balance = builder.balance(masked);
1157 builder.ret(vec![balance]);
1158
1159 let mut pass = InstSimplifier::new();
1160 assert_eq!(pass.run_to_fixpoint(&mut func), 2);
1161
1162 let block = &func.blocks[func.entry_block];
1163 assert_eq!(block.instructions.len(), 2);
1164 let balance_inst = func.instructions[*block.instructions.last().unwrap()].kind.clone();
1165 assert!(matches!(balance_inst, InstKind::SelfBalance));
1166 }
1167}