revmc_codegen/compiler/translate/
peephole.rs1use super::FunctionCx;
8use crate::{Backend, Builder, InstData, IntCC};
9use revm_bytecode::opcode as op;
10use revm_interpreter::InstructionResult;
11use revm_primitives::U256;
12use revmc_builtins::Builtin;
13
14const INT_MIN: U256 = U256::ONE.wrapping_shl(255);
16
17impl<'a, B: Backend> FunctionCx<'a, B> {
18 pub(super) fn try_peephole(&mut self, data: &InstData) -> bool {
23 match data.opcode {
24 op::DIV => self.peephole_div(),
25 op::SDIV => self.peephole_sdiv(),
26 op::MOD => self.peephole_mod(),
27 op::SMOD => self.peephole_smod(),
28 op::ADDMOD => self.peephole_addmod(),
29 op::MULMOD => self.peephole_mulmod(),
30 op::EXP => self.peephole_exp(),
31 op::SIGNEXTEND => self.peephole_signextend(),
32 op::BYTE => self.peephole_byte(),
33
34 op::CALLDATALOAD | op::SLOAD => self.peephole_load(data.opcode),
35
36 op::KECCAK256 => self.peephole_keccak256(),
37 op::RETURN | op::REVERT => self.peephole_return(data.opcode),
38
39 _ => false,
40 }
41 }
42
43 fn peephole_div(&mut self) -> bool {
49 let [dividend, divisor] = self.const_operands();
50 match divisor {
51 Some(U256::ZERO) => self.fold_const(0),
53 Some(U256::ONE) => {
55 let [a, _] = self.popn();
56 self.push(a);
57 }
58 Some(d) if d.is_power_of_two() => {
60 let [a, _] = self.popn();
61 let d = self.bcx.iconst_256(d);
62 let r = self.bcx.udiv(a, d);
63 self.push(r);
64 }
65 _ if dividend == Some(U256::ZERO) => self.fold_const(0),
67 _ => return false,
68 }
69 true
70 }
71
72 fn peephole_sdiv(&mut self) -> bool {
77 let [dividend, divisor] = self.const_operands();
78 match divisor {
79 Some(U256::ZERO) => self.fold_const(0),
81 Some(U256::ONE) => {
83 let [a, _] = self.popn();
84 self.push(a);
85 }
86 Some(U256::MAX) => {
88 let [a, _] = self.popn();
89 let zero = self.bcx.iconst_256(0);
90 let r = self.bcx.isub(zero, a);
91 self.push(r);
92 }
93 Some(INT_MIN) => {
95 let [a, _] = self.popn();
96 let min = self.bcx.iconst_256(INT_MIN);
97 let cmp = self.bcx.icmp(IntCC::Equal, a, min);
98 let r = self.bcx.zext(self.word_type, cmp);
99 self.push(r);
100 }
101 _ if dividend == Some(U256::ZERO) => self.fold_const(0),
103 _ => return false,
104 }
105 true
106 }
107
108 fn peephole_mod(&mut self) -> bool {
112 let [dividend, modulus] = self.const_operands();
113 match modulus {
114 Some(U256::ZERO) | Some(U256::ONE) => self.fold_const(0),
116 Some(m) if m.is_power_of_two() => {
118 let [a, _] = self.popn();
119 let m = self.bcx.iconst_256(m);
120 let r = self.bcx.urem(a, m);
121 self.push(r);
122 }
123 _ if dividend == Some(U256::ZERO) => self.fold_const(0),
125 _ => return false,
126 }
127 true
128 }
129
130 fn peephole_smod(&mut self) -> bool {
132 let [dividend, modulus] = self.const_operands();
133 match modulus {
134 Some(U256::ZERO) | Some(U256::ONE) | Some(U256::MAX) => self.fold_const(0),
136 _ if dividend == Some(U256::ZERO) => self.fold_const(0),
138 _ => return false,
139 }
140 true
141 }
142
143 fn peephole_addmod(&mut self) -> bool {
145 let [a, b, modulus] = self.const_operands();
146 match modulus {
147 Some(U256::ZERO) | Some(U256::ONE) => self.fold_const(0),
149 _ => match (a, b) {
150 (Some(U256::ZERO), Some(U256::ZERO)) => self.fold_const(0),
152 _ => return false,
153 },
154 }
155 true
156 }
157
158 fn peephole_mulmod(&mut self) -> bool {
160 let [a, b, modulus] = self.const_operands();
161 match modulus {
162 Some(U256::ZERO) | Some(U256::ONE) => self.fold_const(0),
164 _ => {
165 if a == Some(U256::ZERO) || b == Some(U256::ZERO) {
167 self.fold_const(0);
168 } else {
169 return false;
170 }
171 }
172 }
173 true
174 }
175
176 fn peephole_exp(&mut self) -> bool {
183 let [base, exponent] = self.const_operands();
184 match exponent {
185 Some(U256::ZERO) => self.fold_const(1),
187 Some(U256::ONE) => {
189 let [a, _] = self.popn();
190 self.push(a);
191 }
192 Some(e) if e == 2 => {
194 let [a, _] = self.popn();
195 let r = self.bcx.imul(a, a);
196 self.push(r);
197 }
198 Some(_) => return false,
199 None => {}
200 }
201 if exponent.is_some() {
202 return true;
203 }
204 match base {
205 Some(U256::ZERO) => {
207 self.pay_exp_dynamic_gas();
208 let [_, exponent] = self.popn();
209 let is_zero = self.bcx.icmp_imm(IntCC::Equal, exponent, 0);
210 let one = self.bcx.iconst_256(1);
211 let zero = self.bcx.iconst_256(0);
212 let r = self.bcx.select(is_zero, one, zero);
213 self.push(r);
214 }
215 Some(U256::ONE) => {
217 self.pay_exp_dynamic_gas();
218 self.fold_const(1);
219 }
220 Some(U256::MAX) => {
222 self.pay_exp_dynamic_gas();
223 let [_, exponent] = self.popn();
224 let is_odd = self.bcx.bitand_imm(exponent, 1);
225 let is_even = self.bcx.icmp_imm(IntCC::Equal, is_odd, 0);
226 let one = self.bcx.iconst_256(1);
227 let minus_one = self.bcx.iconst_256(U256::MAX);
228 let r = self.bcx.select(is_even, one, minus_one);
229 self.push(r);
230 }
231 Some(base) if base.is_power_of_two() => {
233 self.pay_exp_dynamic_gas();
234 let k = base.trailing_zeros();
235 let threshold = i64::from(256_u16.div_ceil(k as u16));
236 let [_, exponent] = self.popn();
237 let in_range = self.bcx.icmp_imm(IntCC::UnsignedLessThan, exponent, threshold);
238 let shift = if k == 1 { exponent } else { self.bcx.imul_imm(exponent, k as i64) };
239 let one = self.bcx.iconst_256(1);
240 let shifted = self.bcx.ishl(one, shift);
241 let zero = self.bcx.iconst_256(0);
242 let r = self.bcx.select(in_range, shifted, zero);
243 self.push(r);
244 }
245 _ => return false,
246 }
247 true
248 }
249
250 fn pay_exp_dynamic_gas(&mut self) {
251 let exponent = self.sp_after_inputs_with(&[1]);
252 self.call_fallible_builtin(Builtin::ExpGas, &[self.ecx, exponent]);
253 }
254
255 fn peephole_signextend(&mut self) -> bool {
257 let [ext, _x] = self.const_operands();
258 match ext {
259 Some(e) if e >= 31 => {
261 let [_, x] = self.popn();
262 self.push(x);
263 }
264 _ => return false,
265 }
266 true
267 }
268
269 fn peephole_byte(&mut self) -> bool {
271 let [index, _value] = self.const_operands();
272 match index {
273 Some(i) if i >= 32 => self.fold_const(0),
275 _ => return false,
276 }
277 true
278 }
279
280 fn peephole_load(&mut self, op: u8) -> bool {
281 if let [Some(offset)] = self.const_operands()
282 && let Ok(offset) = u64::try_from(offset)
283 {
284 let builtin = match op {
285 op::SLOAD => Builtin::SloadC,
286 op::CALLDATALOAD => Builtin::CallDataLoadC,
287 _ => unreachable!(),
288 };
289 let sp = self.sp_from_top(1);
290 let offset = self.bcx.iconst(self.isize_type, offset as i64);
291 let args = &[self.ecx, sp, offset];
292 if op == op::CALLDATALOAD {
293 let _ = self.call_builtin(builtin, args);
294 } else {
295 self.call_fallible_builtin(builtin, args);
296 }
297 let off = self.section_len_offset - 1;
299 let value = self.load_word(sp, "builtin.out");
300 self.vstack.set_at_offset(off, value);
301 true
302 } else {
303 false
304 }
305 }
306
307 fn peephole_keccak256(&mut self) -> bool {
308 if let Some((offset, len)) = self.const_memory_operands(self.const_operands()) {
309 let offset = self.bcx.iconst(self.isize_type, offset as i64);
310 let len = self.bcx.iconst(self.isize_type, len as i64);
311 let sp = self.sp_after_inputs_with(&[]);
312 self.call_fallible_builtin(Builtin::Keccak256CC, &[self.ecx, sp, offset, len]);
313 true
314 } else {
315 false
316 }
317 }
318
319 fn peephole_return(&mut self, op: u8) -> bool {
320 if let Some((offset, len)) = self.const_memory_operands(self.const_operands()) {
321 self.pop_ignore(2);
323 let ir = match op {
324 op::RETURN => InstructionResult::Return,
325 op::REVERT => InstructionResult::Revert,
326 _ => unreachable!(),
327 };
328 let ir_const = self.bcx.iconst(self.i8_type, ir as i64);
329 let offset = self.bcx.iconst(self.isize_type, offset as i64);
330 let len = self.bcx.iconst(self.isize_type, len as i64);
331 let _ = self.call_builtin(Builtin::DoReturnCC, &[self.ecx, offset, len, ir_const]);
332 self.bcx.unreachable();
333 true
334 } else {
335 false
336 }
337 }
338
339 fn const_memory_operands(&self, args: [Option<U256>; 2]) -> Option<(u64, u64)> {
342 if let [offset, Some(len)] = args
343 && let Ok(len) = u64::try_from(len)
344 && let Some(offset) = offset
346 .and_then(|offset| u64::try_from(offset).ok())
347 .or_else(|| (len == 0).then_some(0))
348 {
349 Some((offset, len))
350 } else {
351 None
352 }
353 }
354}