neo_solidity/cli/ir_optimize/
neovm.rs

1/// NeoVM-specific peephole optimization: removes redundant stack operations
2fn neovm_peephole_optimize(block: &mut ir::BasicBlock) {
3    let mut optimized = Vec::with_capacity(block.instructions.len());
4    let mut i = 0;
5
6    while i < block.instructions.len() {
7        // Pattern: PUSH x followed by DROP → remove both
8        if i + 1 < block.instructions.len() {
9            if matches!(&block.instructions[i], ir::Instruction::PushLiteral(_))
10                && matches!(&block.instructions[i + 1], ir::Instruction::Drop(_))
11            {
12                i += 2;
13                continue;
14            }
15
16            // Pattern: LoadLocal x followed by DROP → remove both
17            if matches!(&block.instructions[i], ir::Instruction::LoadLocal(_))
18                && matches!(&block.instructions[i + 1], ir::Instruction::Drop(_))
19            {
20                i += 2;
21                continue;
22            }
23
24            // Pattern: DUP followed by DROP → remove both
25            if matches!(&block.instructions[i], ir::Instruction::Dup)
26                && matches!(&block.instructions[i + 1], ir::Instruction::Drop(_))
27            {
28                i += 2;
29                continue;
30            }
31        }
32
33        // Pattern: StoreLocal x, LoadLocal x → DUP, StoreLocal x
34        if i + 1 < block.instructions.len() {
35            if let (
36                ir::Instruction::StoreLocal(store_idx),
37                ir::Instruction::LoadLocal(load_idx),
38            ) = (&block.instructions[i], &block.instructions[i + 1]) {
39                if store_idx == load_idx {
40                    optimized.push(ir::Instruction::Dup);
41                    optimized.push(ir::Instruction::StoreLocal(*store_idx));
42                    i += 2;
43                    continue;
44                }
45            }
46        }
47
48        // Pattern: SWAP followed by SWAP → remove both
49        if i + 1 < block.instructions.len()
50            && matches!(&block.instructions[i], ir::Instruction::Swap)
51            && matches!(&block.instructions[i + 1], ir::Instruction::Swap)
52        {
53            i += 2;
54            continue;
55        }
56
57        optimized.push(block.instructions[i].clone());
58        i += 1;
59    }
60
61    block.instructions = optimized;
62}
63
64/// NeoVM-specific: simplify identity operations (x + 0, x * 1, x & MAX, etc.)
65fn neovm_simplify_identity_ops(block: &mut ir::BasicBlock) {
66    use num_bigint::BigInt;
67    use num_traits::Zero;
68
69    let mut optimized = Vec::with_capacity(block.instructions.len());
70    let mut i = 0;
71
72    while i < block.instructions.len() {
73        // Pattern: PUSH 0, BinaryOp::Add → remove (x + 0 = x)
74        if i + 1 < block.instructions.len() {
75            if let ir::Instruction::PushLiteral(ir::LiteralValue::Integer(val)) =
76                &block.instructions[i]
77            {
78                if val.is_zero() {
79                    if let ir::Instruction::BinaryOp(ir::BinaryOperator::Add) =
80                        &block.instructions[i + 1]
81                    {
82                        // Skip both instructions, identity operation
83                        i += 2;
84                        continue;
85                    }
86                    if let ir::Instruction::BinaryOp(ir::BinaryOperator::Sub) =
87                        &block.instructions[i + 1]
88                    {
89                        // x - 0 = x, skip both
90                        i += 2;
91                        continue;
92                    }
93                    if let ir::Instruction::BinaryOp(ir::BinaryOperator::BitOr) =
94                        &block.instructions[i + 1]
95                    {
96                        // x | 0 = x, skip both
97                        i += 2;
98                        continue;
99                    }
100                    if let ir::Instruction::BinaryOp(ir::BinaryOperator::BitXor) =
101                        &block.instructions[i + 1]
102                    {
103                        // x ^ 0 = x, skip both
104                        i += 2;
105                        continue;
106                    }
107                }
108                // Pattern: PUSH 1, MUL → identity (x * 1 = x)
109                if *val == BigInt::from(1) {
110                    if let ir::Instruction::BinaryOp(ir::BinaryOperator::Mul) =
111                        &block.instructions[i + 1]
112                    {
113                        i += 2;
114                        continue;
115                    }
116                    if let ir::Instruction::BinaryOp(ir::BinaryOperator::Div) =
117                        &block.instructions[i + 1]
118                    {
119                        // x / 1 = x
120                        i += 2;
121                        continue;
122                    }
123                }
124            }
125        }
126
127        optimized.push(block.instructions[i].clone());
128        i += 1;
129    }
130
131    block.instructions = optimized;
132}
133
134/// NeoVM-specific: optimize boolean patterns
135fn neovm_bool_optimize(block: &mut ir::BasicBlock) {
136    let mut optimized = Vec::with_capacity(block.instructions.len());
137    let mut i = 0;
138
139    while i < block.instructions.len() {
140        // Pattern: PUSH true, EQ → identity for booleans (x == true = x)
141        if i + 1 < block.instructions.len() {
142            if let ir::Instruction::PushLiteral(ir::LiteralValue::Boolean(true)) =
143                &block.instructions[i]
144            {
145                if let ir::Instruction::BinaryOp(ir::BinaryOperator::Eq) =
146                    &block.instructions[i + 1]
147                {
148                    // x == true → x (skip both)
149                    i += 2;
150                    continue;
151                }
152            }
153
154            // Pattern: PUSH false, NE → identity for booleans (x != false = x)
155            if let ir::Instruction::PushLiteral(ir::LiteralValue::Boolean(false)) =
156                &block.instructions[i]
157            {
158                if let ir::Instruction::BinaryOp(ir::BinaryOperator::Ne) =
159                    &block.instructions[i + 1]
160                {
161                    // x != false → x (skip both)
162                    i += 2;
163                    continue;
164                }
165            }
166
167            // Pattern: PUSH true, NE → converts to negation (x != true = !x)
168            // Keep this pattern as-is since we don't have a simple NOT instruction
169            // The codegen will handle it appropriately
170        }
171
172        // Pattern: BitwiseNot followed by BitwiseNot → identity (removes both)
173        if i + 1 < block.instructions.len()
174            && matches!(&block.instructions[i], ir::Instruction::BitwiseNot)
175            && matches!(&block.instructions[i + 1], ir::Instruction::BitwiseNot)
176        {
177            i += 2;
178            continue;
179        }
180
181        optimized.push(block.instructions[i].clone());
182        i += 1;
183    }
184
185    block.instructions = optimized;
186}