Skip to main content

solar_codegen/transform/
inst_simplify.rs

1//! Local MIR instruction simplification.
2//!
3//! This pass removes algebraic no-ops and rewrites a few equivalent EVM
4//! instruction patterns before stack scheduling. It is intentionally local and
5//! conservative: it only applies identities that are exact for EVM word
6//! semantics.
7//!
8//! Safety contract:
9//! - do not remove or reorder side effects
10//! - replace an instruction with a value only when the equality is exact for all 256-bit EVM words
11//! - preserve boolean-only rewrites behind explicit MIR boolean type checks
12
13use 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/// Local MIR instruction simplification pass.
25#[derive(Debug, Default)]
26pub struct InstSimplifier {
27    /// Number of instructions simplified in the last run.
28    pub simplified_count: usize,
29}
30
31/// Function pass for local instruction simplification.
32pub 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    /// Creates a new instruction simplifier.
46    pub fn new() -> Self {
47        Self::default()
48    }
49
50    /// Runs instruction simplification on a function.
51    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    /// Runs instruction simplification until no more changes are found.
106    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            // `a < 1` is `a == 0` for unsigned comparisons.
180            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            // `1 > b` is `b == 0` for unsigned comparisons.
185            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                // `branch gt(x, 0)` / `branch lt(0, x)` test exactly `x != 0`,
848                // which is what `branch x` already does.
849                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    /// Returns `x` when `value` computes `gt(x, 0)` or `lt(0, x)`, both of
875    /// which are the unsigned nonzero test.
876    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}