neo_solidity/cli/ir_optimize/
constant_folding.rs1fn 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 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 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 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
47fn 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 (PushLiteral(Integer(n)), BinaryOp(ir::BinaryOperator::Add)) if n.is_zero() => {
57 Some(None)
58 }
59 (PushLiteral(Integer(n)), BinaryOp(ir::BinaryOperator::Mul)) if *n == 1.into() => {
61 Some(None)
62 }
63 (PushLiteral(Integer(n)), BinaryOp(ir::BinaryOperator::Mul)) if n.is_zero() => {
65 Some(Some(PushLiteral(Integer(0.into()))))
66 }
67 (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