neo_solidity/cli/ir_optimize/
constant_folding.rs

1fn fold_constant_binary_ops(block: &mut ir::BasicBlock) {
2    let mut optimized = Vec::with_capacity(block.instructions.len());
3    let mut i = 0;
4
5    while i < block.instructions.len() {
6        // Try constant folding for binary ops
7        if i + 2 < block.instructions.len() {
8            if let (
9                ir::Instruction::PushLiteral(lhs),
10                ir::Instruction::PushLiteral(rhs),
11                ir::Instruction::BinaryOp(op),
12            ) = (
13                &block.instructions[i],
14                &block.instructions[i + 1],
15                &block.instructions[i + 2],
16            ) {
17                if let Some(result) = evaluate_binary_literal(lhs, rhs, *op) {
18                    optimized.push(ir::Instruction::PushLiteral(result));
19                    i += 3;
20                    continue;
21                }
22            }
23        }
24
25        // Try identity elimination: x + 0, x * 1, etc.
26        if i + 1 < block.instructions.len() {
27            if let Some(simplified) = try_identity_elimination(
28                &block.instructions[i],
29                &block.instructions[i + 1],
30            ) {
31                if let Some(instr) = simplified {
32                    optimized.push(instr);
33                }
34                // Skip both instructions (or just drop if None)
35                i += 2;
36                continue;
37            }
38        }
39
40        optimized.push(block.instructions[i].clone());
41        i += 1;
42    }
43
44    block.instructions = optimized;
45}
46
47/// Try to eliminate identity operations
48fn try_identity_elimination(
49    first: &ir::Instruction,
50    second: &ir::Instruction,
51) -> Option<Option<ir::Instruction>> {
52    use ir::{Instruction::*, LiteralValue::*};
53
54    match (first, second) {
55        // PUSH 0, ADD -> no-op (keep original value)
56        (PushLiteral(Integer(n)), BinaryOp(ir::BinaryOperator::Add)) if n.is_zero() => {
57            Some(None)
58        }
59        // PUSH 1, MUL -> no-op
60        (PushLiteral(Integer(n)), BinaryOp(ir::BinaryOperator::Mul)) if *n == 1.into() => {
61            Some(None)
62        }
63        // PUSH 0, MUL -> replace with PUSH 0
64        (PushLiteral(Integer(n)), BinaryOp(ir::BinaryOperator::Mul)) if n.is_zero() => {
65            Some(Some(PushLiteral(Integer(0.into()))))
66        }
67        // PUSH 1, DIV -> no-op
68        (PushLiteral(Integer(n)), BinaryOp(ir::BinaryOperator::Div)) if *n == 1.into() => {
69            Some(None)
70        }
71        _ => None,
72    }
73}
74
75fn evaluate_binary_literal(
76    lhs: &ir::LiteralValue,
77    rhs: &ir::LiteralValue,
78    op: ir::BinaryOperator,
79) -> Option<ir::LiteralValue> {
80    use ir::LiteralValue::*;
81
82    match (lhs, rhs) {
83        (Integer(a), Integer(b)) => match op {
84            ir::BinaryOperator::Add => Some(Integer(a + b)),
85            ir::BinaryOperator::Sub => Some(Integer(a - b)),
86            ir::BinaryOperator::Mul => Some(Integer(a * b)),
87            ir::BinaryOperator::Div => {
88                if b.is_zero() {
89                    None
90                } else {
91                    Some(Integer(a / b))
92                }
93            }
94            ir::BinaryOperator::Mod => {
95                if b.is_zero() {
96                    None
97                } else {
98                    Some(Integer(a % b))
99                }
100            }
101            ir::BinaryOperator::BitAnd => Some(Integer(a & b)),
102            ir::BinaryOperator::BitOr => Some(Integer(a | b)),
103            ir::BinaryOperator::BitXor => Some(Integer(a ^ b)),
104            ir::BinaryOperator::Shl => {
105                let shift = b.to_u64()?;
106                Some(Integer(a << shift))
107            }
108            ir::BinaryOperator::Shr => {
109                let shift = b.to_u64()?;
110                Some(Integer(a >> shift))
111            }
112            ir::BinaryOperator::Lt => Some(Boolean(a < b)),
113            ir::BinaryOperator::Le => Some(Boolean(a <= b)),
114            ir::BinaryOperator::Gt => Some(Boolean(a > b)),
115            ir::BinaryOperator::Ge => Some(Boolean(a >= b)),
116            ir::BinaryOperator::Eq => Some(Boolean(a == b)),
117            ir::BinaryOperator::Ne => Some(Boolean(a != b)),
118        },
119        (Boolean(a), Boolean(b)) => match op {
120            ir::BinaryOperator::Eq => Some(Boolean(a == b)),
121            ir::BinaryOperator::Ne => Some(Boolean(a != b)),
122            _ => None,
123        },
124        _ => None,
125    }
126}
127