1use super::default_attrs;
4use crate::{
5 Backend, Builder, Bytecode, EvmContext, Inst, InstData, InstFlags, IntCC, Result, StackSection,
6 decode_pair, decode_single,
7};
8use oxc_index::IndexVec;
9use revm_bytecode::opcode as op;
10use revm_interpreter::{InputsImpl, InstructionResult};
11use revm_primitives::U256;
12use revmc_backend::{Attribute, BackendTypes, FunctionAttributeLocation, Pointer, TypeMethods};
13use revmc_builtins::{Builtin, Builtins, CallKind, CreateKind};
14use std::mem;
15
16mod peephole;
17
18mod vstack;
19use vstack::{VSlot, VStack};
20
21const STACK_CAP: usize = 1024;
22#[derive(Clone, Copy, Debug)]
25pub(super) struct FcxConfig {
26 pub(super) comments: bool,
27 pub(super) debug_assertions: bool,
28 pub(super) frame_pointers: bool,
29
30 pub(super) debug: bool,
31 pub(super) inspect_stack: bool,
32 pub(super) stack_bound_checks: bool,
33 pub(super) gas_metering: bool,
34 pub(super) single_error: bool,
35}
36
37impl Default for FcxConfig {
38 fn default() -> Self {
39 Self {
40 debug_assertions: cfg!(debug_assertions),
41 comments: false,
42 frame_pointers: cfg!(debug_assertions) || cfg!(force_frame_pointers),
43 debug: false,
44 inspect_stack: false,
45 stack_bound_checks: true,
46 gas_metering: true,
47 single_error: true,
48 }
49 }
50}
51
52type Incoming<B> = Vec<(<B as BackendTypes>::Value, <B as BackendTypes>::BasicBlock)>;
54
55#[allow(dead_code)]
57type SwitchTargets<B> = Vec<(u64, <B as BackendTypes>::BasicBlock)>;
58
59pub(super) struct FunctionCx<'a, B: Backend> {
60 config: FcxConfig,
62
63 bcx: B::Builder<'a>,
65
66 isize_type: B::Type,
68 word_type: B::Type,
69 address_type: B::Type,
70 i8_type: B::Type,
71
72 stack_len: Pointer<B::Builder<'a>>,
75 stack: Pointer<B::Builder<'a>>,
78 sp_arg: Option<B::Value>,
81 ecx: B::Value,
83 len_before: B::Value,
85 len_offset: i8,
87
88 vstack: VStack<B::Value>,
91 section_start_len: B::Value,
94 section_start_sp: B::Value,
98 section_len_offset: i32,
101 stored_len_offset: i32,
105 cached_mem_base: Option<B::Value>,
106
107 bytecode: &'a Bytecode<'a>,
109 inst_lines: IndexVec<Inst, u32>,
111 inst_entries: IndexVec<Inst, B::BasicBlock>,
113 current_inst: Option<Inst>,
115
116 incoming_dynamic_jumps: Incoming<B>,
119 dynamic_jump_table: B::BasicBlock,
121
122 incoming_failures: Incoming<B>,
124 failure_block: Option<B::BasicBlock>,
126 incoming_returns: Incoming<B>,
128 return_block: Option<B::BasicBlock>,
130
131 resume_blocks: Vec<B::BasicBlock>,
133 suspend_blocks: Incoming<B>,
135 suspend_block: B::BasicBlock,
137
138 builtins: &'a mut Builtins<B>,
140}
141
142impl<'a, B: Backend> FunctionCx<'a, B> {
143 #[allow(rustdoc::invalid_rust_codeblocks)] pub(super) fn translate(
199 mut bcx: B::Builder<'a>,
200 config: FcxConfig,
201 builtins: &'a mut Builtins<B>,
202 bytecode: &'a Bytecode<'a>,
203 ) -> Result<()> {
204 let entry_block = bcx.current_block().unwrap();
205
206 if config.debug {
208 bcx.clear_debug_location();
209 }
210
211 let isize_type = bcx.type_ptr_sized_int();
213 let i8_type = bcx.type_int(8);
214 let word_type = bcx.type_int(256);
215 let address_type = bcx.type_int(160);
216
217 let ecx = bcx.fn_param(0);
219
220 let sp_arg = bcx.fn_param(1);
221 let local_stack = !config.inspect_stack;
225 let stack = if local_stack {
226 let stack_type = bcx.type_array(word_type, STACK_CAP as u32);
227 bcx.new_stack_slot(stack_type, "stack.addr")
228 } else {
229 Pointer::new_address(word_type, sp_arg)
230 };
231
232 let stack_len_arg = bcx.fn_param(2);
233 let stack_len = bcx.new_stack_slot(isize_type, "len.addr");
235
236 let unreachable_block = bcx.create_block("unreachable");
239 let inst_entries: IndexVec<Inst, _> = bytecode
240 .iter_all_insts()
241 .map(|(i, data)| {
242 if data.is_dead_code() {
243 unreachable_block
244 } else {
245 let name = if config.debug { &bytecode.op_block_name(Some(i), "") } else { "" };
246 bcx.create_block(name)
247 }
248 })
249 .collect();
250 assert!(!inst_entries.is_empty(), "translating empty bytecode");
251
252 let dynamic_jump_table = bcx.create_block("dynamic_jump_table");
253 let suspend_block = bcx.create_block("suspend");
254 let failure_block = bcx.create_block("failure");
255 let return_block = bcx.create_block("return");
256
257 let section_start_sp = stack.addr(&mut bcx);
258 let zero = bcx.iconst(isize_type, 0);
259 let mut fx = FunctionCx {
260 config,
261
262 isize_type,
263 word_type,
264 address_type,
265 i8_type,
266 stack_len,
267 stack,
268 sp_arg: local_stack.then_some(sp_arg),
269 ecx,
270 len_before: zero,
271 len_offset: 0,
272 section_start_len: zero,
273 section_start_sp,
274 section_len_offset: 0,
275 stored_len_offset: 0,
276 cached_mem_base: None,
277 bcx,
278
279 bytecode,
280 inst_lines: if config.debug { bytecode.take_inst_lines() } else { IndexVec::new() },
281 inst_entries,
282 current_inst: None,
283
284 incoming_dynamic_jumps: Vec::new(),
285 dynamic_jump_table,
286
287 incoming_failures: Vec::new(),
288 failure_block: Some(failure_block),
289 incoming_returns: Vec::new(),
290 return_block: Some(return_block),
291
292 resume_blocks: Vec::new(),
293 suspend_blocks: Vec::new(),
294 suspend_block,
295
296 builtins,
297
298 vstack: VStack::default(),
299 };
300
301 if config.debug_assertions {
303 let compiled_spec = fx.bcx.iconst(fx.i8_type, bytecode.spec_id as i64);
305 let _ = fx.call_builtin(Builtin::AssertSpecId, &[ecx, compiled_spec]);
306 }
307
308 let first_inst_block = fx.inst_entries[Inst::from_usize(0)];
310 let post_entry_block = fx.bcx.create_block_after(entry_block, "entry.post");
311 let resume_block = fx.bcx.create_block_after(post_entry_block, "resume");
312 fx.bcx.br(post_entry_block);
313
314 for (inst, _) in bytecode.iter_insts() {
316 fx.translate_inst(inst)?;
317 }
318
319 if config.debug {
321 fx.bcx.clear_debug_location();
322 }
323
324 fx.bcx.switch_to_block(unreachable_block);
326 fx.bcx.unreachable();
327 if bytecode.has_dynamic_jumps() {
328 fx.bcx.switch_to_block(fx.dynamic_jump_table);
329 let jumpdests = bytecode.iter_insts().filter(|(_, data)| data.opcode == op::JUMPDEST);
330 let targets = jumpdests
331 .map(|(inst, data)| (data.jumpdest_pc() as u64, fx.effective_entry(inst)))
332 .collect::<Vec<_>>();
333 let i64_type = fx.bcx.type_int(64);
334 let index = fx.bcx.phi(i64_type, &fx.incoming_dynamic_jumps);
335 let invalid_jump = fx.add_invalid_jump();
336 fx.bcx.switch(index, invalid_jump, &targets, true);
337 } else {
338 debug_assert!(fx.incoming_dynamic_jumps.is_empty());
340 fx.bcx.switch_to_block(fx.dynamic_jump_table);
341 fx.bcx.unreachable();
342 }
343
344 let load_len_at_start = |fx: &mut Self| {
347 if config.inspect_stack {
349 let stack_len = fx.bcx.load(fx.isize_type, stack_len_arg, "stack_len");
350 fx.stack_len.store(&mut fx.bcx, stack_len);
351 fx.copy_stack_from_arg(stack_len);
352 } else {
353 fx.stack_len.store_imm(&mut fx.bcx, 0);
354 }
355 };
356 let generate_resume = bytecode.may_suspend();
357 if generate_resume {
358 let get_ecx_resume_at_ptr = |fx: &mut Self| {
359 fx.get_field(
360 fx.ecx,
361 mem::offset_of!(EvmContext<'_>, resume_at),
362 "ecx.resume_at.addr",
363 )
364 };
365
366 let resume_ty = fx.isize_type;
367
368 {
371 let no_resume_block = fx.bcx.create_block_after(resume_block, "no_resume");
373
374 fx.bcx.switch_to_block(post_entry_block);
375 let resume_at = get_ecx_resume_at_ptr(&mut fx);
376 let resume_at = fx.bcx.load_aligned(resume_ty, resume_at, 1, "ecx.resume_at");
377 let no_resume = fx.bcx.icmp_imm(IntCC::Equal, resume_at, 0);
378 fx.bcx.brif(no_resume, no_resume_block, resume_block);
379
380 fx.bcx.switch_to_block(no_resume_block);
381 load_len_at_start(&mut fx);
382 fx.bcx.br(first_inst_block);
383
384 fx.bcx.switch_to_block(resume_block);
386 let stack_len = fx.bcx.load(fx.isize_type, stack_len_arg, "stack_len");
387 fx.stack_len.store(&mut fx.bcx, stack_len);
388 fx.copy_stack_from_arg(stack_len);
389 let default = fx.bcx.create_block_after(resume_block, "resume_invalid");
390 fx.bcx.switch_to_block(default);
391 fx.call_panic("invalid `resume_at` value");
392
393 fx.bcx.switch_to_block(resume_block);
394 let targets = fx
395 .resume_blocks
396 .iter()
397 .enumerate()
398 .map(|(i, b)| (i as u64 + 1, *b))
399 .collect::<Vec<_>>();
400 fx.bcx.switch(resume_at, default, &targets, true);
401 }
402
403 {
405 fx.bcx.switch_to_block(fx.suspend_block);
406 let resume_value = fx.bcx.phi(resume_ty, &fx.suspend_blocks);
407 let resume_at = get_ecx_resume_at_ptr(&mut fx);
408 fx.bcx.store_aligned(resume_value, resume_at, 1);
409
410 if !config.inspect_stack {
413 fx.copy_stack_to_arg();
414 fx.save_stack_len();
415 }
416
417 fx.build_return_imm(InstructionResult::Stop);
418 }
419 } else {
420 debug_assert!(fx.resume_blocks.is_empty());
421 debug_assert!(fx.suspend_blocks.is_empty());
422
423 fx.bcx.switch_to_block(post_entry_block);
424 load_len_at_start(&mut fx);
425 fx.bcx.br(first_inst_block);
426
427 fx.bcx.switch_to_block(resume_block);
428 fx.bcx.unreachable();
429 fx.bcx.switch_to_block(fx.suspend_block);
430 fx.bcx.unreachable();
431 }
432
433 fx.bcx.switch_to_block(fx.failure_block.unwrap());
435 if !fx.incoming_failures.is_empty() {
436 let failure_value = if config.single_error {
441 fx.bcx.iconst(fx.i8_type, InstructionResult::OutOfGas as i64)
442 } else {
443 fx.bcx.phi(fx.i8_type, &fx.incoming_failures)
444 };
445 fx.bcx.set_current_block_cold();
446 fx.build_return_inner(failure_value);
451 } else {
452 fx.bcx.unreachable();
453 }
454
455 fx.bcx.switch_to_block(fx.return_block.unwrap());
457 if !fx.incoming_returns.is_empty() {
458 let return_value = fx.bcx.phi(fx.i8_type, &fx.incoming_returns);
459 if config.inspect_stack {
460 fx.copy_stack_to_arg();
461 fx.save_stack_len();
462 }
463 fx.bcx.ret(&[return_value]);
464 } else {
465 fx.bcx.unreachable();
466 }
467
468 fx.bcx.seal_all_blocks();
469
470 Ok(())
471 }
472
473 fn lexical_next_inst(&self, inst: Inst) -> Option<Inst> {
475 let next = inst + 1;
476 self.inst_entries.get(next)?;
477 Some(next)
478 }
479
480 fn effective_inst(&self, inst: Inst) -> Inst {
482 self.bytecode.redirects.get(&inst).copied().unwrap_or(inst)
483 }
484
485 fn effective_entry(&self, inst: Inst) -> B::BasicBlock {
487 self.inst_entries[self.effective_inst(inst)]
488 }
489
490 fn effective_next_inst(&self, inst: Inst) -> Option<Inst> {
492 self.lexical_next_inst(inst).map(|next| self.effective_inst(next))
493 }
494
495 fn effective_next_entry(&self, inst: Inst) -> Option<B::BasicBlock> {
497 self.effective_next_inst(inst).map(|next| self.effective_entry(next))
498 }
499
500 #[instrument(level = "debug", skip_all, fields(inst = %self.bytecode.inst(inst).to_op()))]
501 fn translate_inst(&mut self, inst: Inst) -> Result<()> {
502 self.current_inst = Some(inst);
503 let data = self.bytecode.inst(inst);
504 let opcode = data.opcode;
505 let entry_block = self.inst_entries[inst];
506 self.bcx.switch_to_block(entry_block);
507
508 if self.config.debug {
509 self.bcx.set_debug_location(self.inst_lines[inst], 1);
510 }
511
512 let branch_to_next_opcode = |this: &mut Self| {
515 debug_assert!(
516 !this.bytecode.is_instr_diverging(inst),
517 "attempted to branch to next instruction in a diverging instruction: {data:?}",
518 );
519 if let Some(next) = this.effective_next_entry(inst) {
520 this.bcx.br(next);
521 }
522 };
523
524 macro_rules! goto_return {
528 (no_branch $($comment:expr)?) => {
529 $(
530 if self.config.comments {
531 self.add_comment($comment);
532 }
533 )?
534 return Ok(());
535 };
536 (build $ret:expr) => {{
537 self.build_return_imm($ret);
538 goto_return!(no_branch);
539 }};
540 (fail $ret:expr) => {{
541 self.build_fail_imm($ret);
542 goto_return!(no_branch);
543 }};
544 ($($comment:expr)?) => {
545 if let Some(next_inst) = self.effective_next_inst(inst) {
547 let next = self.bytecode.inst(next_inst);
548 if !next.is_dead_code() && next.is_stack_section_head() {
549 self.materialize_live_stack();
550 } else {
551 self.relieve_vstack_pressure();
552 }
553 }
554 branch_to_next_opcode(self);
555 goto_return!(no_branch $($comment)?);
556 };
557 }
558
559 debug_assert!(!data.flags.contains(InstFlags::DEAD_CODE));
561
562 #[cfg(test)]
563 if opcode == crate::TEST_SUSPEND {
564 self.suspend();
565 goto_return!(no_branch);
566 }
567
568 if data.flags.contains(InstFlags::DISABLED) {
570 goto_return!(fail InstructionResult::NotActivated);
571 }
572 if data.flags.contains(InstFlags::UNKNOWN) {
573 goto_return!(fail InstructionResult::OpcodeNotFound);
574 }
575
576 self.gas_cost_imm(data.gas_section.gas_cost as u64);
578
579 let (inp, out) = data.stack_io();
583 let diff = effective_stack_diff(inp, out, data);
584 self.len_offset = 0;
585 if data.is_stack_section_head() {
586 self.section_start_len = self.stack_len.load(&mut self.bcx, "stack_len");
587 self.section_start_sp = self.sp_at(self.section_start_len);
588 self.section_len_offset = 0;
589 self.stored_len_offset = 0;
590 self.cached_mem_base = None;
591
592 let section = data.stack_section;
593 self.vstack.reset(section.inputs as usize, section.max_growth.max(0) as usize);
594 }
595 self.len_before = if self.section_len_offset == 0 {
596 self.section_start_len
597 } else {
598 self.bcx.iadd_imm(self.section_start_len, self.section_len_offset as i64)
599 };
600
601 if self.config.stack_bound_checks && data.is_stack_section_head() {
603 self.check_stack_bounds(data.stack_section);
604 }
605
606 if data.flags.contains(InstFlags::NOOP) {
608 self.sync_noop_diff(inst, diff);
609 self.section_len_offset += diff;
610 goto_return!("noop");
611 }
612
613 let new_len_offset = self.section_len_offset + diff;
616 if new_len_offset != self.stored_len_offset {
617 let len_changed = self.bcx.iadd_imm(self.len_before, diff as i64);
618 self.stack_len.store(&mut self.bcx, len_changed);
619 self.stored_len_offset = new_len_offset;
620 }
621
622 if out >= 1
631 && let Some(const_out) = self.bytecode.const_output(inst)
632 {
633 debug_assert!(
634 out == 1 || out == inp + 1,
635 "const_output assumes single synthesized push: inp={inp}, out={out}",
636 );
637 debug_assert!(!data.may_suspend() && !data.is_branching());
638 let drop_count = (inp + 1 - out) as usize;
642 self.vstack.drop_top(drop_count);
643 self.len_offset -= drop_count as i8;
644 let value = self.bcx.iconst_256(const_out);
645 self.push(value);
646 self.section_len_offset += diff;
647 goto_return!("const output");
648 }
649
650 if self.try_peephole(data) {
651 self.sync_virtual_stack_diff(diff);
652 self.section_len_offset += diff;
653 if self.current_inst().is_diverging() {
654 goto_return!(no_branch);
655 } else {
656 goto_return!("peephole");
657 }
658 }
659
660 macro_rules! unop {
662 ($op:ident) => {{
663 let mut a = self.pop();
664 a = self.bcx.$op(a);
665 self.push(a);
666 }};
667 }
668
669 macro_rules! binop {
670 ($op:ident) => {{
671 let [a, b] = self.popn();
672 let r = self.bcx.$op(a, b);
673 self.push(r);
674 }};
675 (@shift $op:ident, | $value:ident, $shift:ident | $default:expr) => {{
676 let [$shift, $value] = self.popn();
677 let r = self.bcx.$op($value, $shift);
678 let overflow = self.bcx.icmp_imm(IntCC::UnsignedGreaterThan, $shift, 255);
679 let default = $default;
680 let r = self.bcx.select(overflow, default, r);
681 self.push(r);
682 }};
683 }
684
685 macro_rules! field {
687 (@get $base:expr, $($paths:path),*; $($spec:tt).*) => {
689 self.get_field($base, 0 $(+ mem::offset_of!($paths, $spec))*, stringify!($($spec).*.addr))
690 };
691 (@load $(@[endian = $endian:tt])? $ty:expr, $base:expr, $($paths:path),*; $($spec:tt).*) => {{
694 let ptr = field!(@get $base, $($paths),*; $($spec).*);
695 #[allow(unused_mut)]
696 let mut value = self.bcx.load_aligned($ty, ptr, 1, stringify!($($spec).*));
697 $(
698 if !cfg!(target_endian = $endian) {
699 value = self.bcx.bswap(value);
700 }
701 )?
702 value
703 }};
704 (@push $(@[endian = $endian:tt])? $ty:expr, $base:expr, $($rest:tt)*) => {{
707 let mut value = field!(@load $(@[endian = $endian])? $ty, $base, $($rest)*);
708 if self.bcx.type_bit_width($ty) < 256 {
709 value = self.bcx.zext(self.word_type, value);
710 }
711 self.push(value);
712 }};
713 }
714
715 match data.opcode {
716 op::STOP => goto_return!(build InstructionResult::Stop),
717
718 op::ADD => binop!(iadd),
719 op::MUL => binop!(imul),
720 op::SUB => binop!(isub),
721 op::DIV => {
722 let sp = self.sp_after_inputs();
723 let _ = self.call_builtin(Builtin::Div, &[sp]);
724 }
725 op::SDIV => {
726 let sp = self.sp_after_inputs();
727 let _ = self.call_builtin(Builtin::SDiv, &[sp]);
728 }
729 op::MOD => {
730 let sp = self.sp_after_inputs();
731 let _ = self.call_builtin(Builtin::Mod, &[sp]);
732 }
733 op::SMOD => {
734 let sp = self.sp_after_inputs();
735 let _ = self.call_builtin(Builtin::SMod, &[sp]);
736 }
737 op::ADDMOD => {
738 let sp = self.sp_after_inputs();
739 let _ = self.call_builtin(Builtin::AddMod, &[sp]);
740 }
741 op::MULMOD => {
742 let sp = self.sp_after_inputs();
743 let _ = self.call_builtin(Builtin::MulMod, &[sp]);
744 }
745 op::EXP => {
746 let sp = self.sp_after_inputs();
747 self.call_fallible_builtin(Builtin::Exp, &[self.ecx, sp]);
748 }
749 op::SIGNEXTEND => {
750 let [ext, x] = self.popn();
755
756 let might_do_something = self.bcx.icmp_imm(IntCC::UnsignedLessThan, ext, 31);
757
758 let shift = self.bcx.imul_imm(ext, 8);
759 let c248 = self.bcx.iconst_256(248);
760 let shift = self.bcx.isub(c248, shift);
761 let shifted = self.bcx.ishl(x, shift);
762 let sext = self.bcx.sshr(shifted, shift);
763
764 let r = self.bcx.select(might_do_something, sext, x);
765 self.push(r);
766 }
767
768 op::LT | op::GT | op::SLT | op::SGT | op::EQ => {
769 let cond = match opcode {
770 op::LT => IntCC::UnsignedLessThan,
771 op::GT => IntCC::UnsignedGreaterThan,
772 op::SLT => IntCC::SignedLessThan,
773 op::SGT => IntCC::SignedGreaterThan,
774 op::EQ => IntCC::Equal,
775 _ => unreachable!(),
776 };
777
778 let [a, b] = self.popn();
779 let r = self.bcx.icmp(cond, a, b);
780 let r = self.bcx.zext(self.word_type, r);
781 self.push(r);
782 }
783 op::ISZERO => {
784 let a = self.pop();
785 let r = self.bcx.icmp_imm(IntCC::Equal, a, 0);
786 let r = self.bcx.zext(self.word_type, r);
787 self.push(r);
788 }
789 op::AND => binop!(bitand),
790 op::OR => binop!(bitor),
791 op::XOR => binop!(bitxor),
792 op::NOT => unop!(bitnot),
793 op::BYTE => {
794 let [index, value] = self.popn();
798
799 let in_range = self.bcx.icmp_imm(IntCC::UnsignedLessThan, index, 32);
800
801 let shift = self.bcx.imul_imm(index, 8);
802 let c248 = self.bcx.iconst_256(248);
803 let shift = self.bcx.isub(c248, shift);
804 let shifted = self.bcx.ushr(value, shift);
805 let mask = self.bcx.iconst_256(0xFF);
806 let byte = self.bcx.bitand(shifted, mask);
807
808 let zero = self.bcx.iconst_256(0);
809
810 let r = self.bcx.select(in_range, byte, zero);
811 self.push(r);
812 }
813 op::SHL => binop!(@shift ishl, |value, shift| self.bcx.iconst_256(0)),
814 op::SHR => binop!(@shift ushr, |value, shift| self.bcx.iconst_256(0)),
815 op::SAR => binop!(@shift sshr, |value, shift| {
816 let is_negative = self.bcx.icmp_imm(IntCC::SignedLessThan, value, 0);
817 let max = self.bcx.iconst_256(U256::MAX);
818 let zero = self.bcx.iconst_256(0);
819 self.bcx.select(is_negative, max, zero)
820 }),
821 op::CLZ => unop!(clz),
822
823 op::KECCAK256 => {
824 let sp = self.sp_after_inputs();
825 self.call_fallible_builtin(Builtin::Keccak256, &[self.ecx, sp]);
826 }
827
828 op::ADDRESS => {
829 let input = self.load_input();
830 field!(@push @[endian = "big"] self.address_type, input, InputsImpl; target_address);
831 }
832 op::BALANCE => {
833 let sp = self.sp_after_inputs();
834 self.call_fallible_builtin(Builtin::Balance, &[self.ecx, sp]);
835 }
836 op::ORIGIN => {
837 let slot = self.sp_at_top();
838 let _ = self.call_builtin(Builtin::Origin, &[self.ecx, slot]);
839 self.narrow_to_address(slot);
840 }
841 op::CALLER => {
842 let input = self.load_input();
843 field!(@push @[endian = "big"] self.address_type, input, InputsImpl; caller_address);
844 }
845 op::CALLVALUE => {
846 let input = self.load_input();
847 field!(@push self.word_type, input, InputsImpl; call_value);
848 }
849 op::CALLDATALOAD => {
850 let sp = self.sp_after_inputs();
851 let _ = self.call_builtin(Builtin::CallDataLoad, &[self.ecx, sp]);
852 }
853 op::CALLDATASIZE => {
854 field!(@push self.isize_type, self.ecx, EvmContext<'_>; calldatasize);
855 }
856 op::CALLDATACOPY => {
857 let sp = self.sp_after_inputs();
858 self.call_fallible_builtin(Builtin::CallDataCopy, &[self.ecx, sp]);
859 }
860 op::CODESIZE => {
861 let len = self.bcx.iconst(self.word_type, self.bytecode.codesize() as i64);
862 self.push(len);
863 }
864 op::CODECOPY => {
865 let sp = self.sp_after_inputs();
866 self.call_fallible_builtin(Builtin::CodeCopy, &[self.ecx, sp]);
867 }
868
869 op::GASPRICE => {
870 let sp = self.sp_after_inputs();
871 let _ = self.call_builtin(Builtin::GasPrice, &[self.ecx, sp]);
872 }
873 op::EXTCODESIZE => {
874 let sp = self.sp_after_inputs();
875 self.call_fallible_builtin(Builtin::ExtCodeSize, &[self.ecx, sp]);
876 }
877 op::EXTCODECOPY => {
878 let sp = self.sp_after_inputs();
879 self.call_fallible_builtin(Builtin::ExtCodeCopy, &[self.ecx, sp]);
880 }
881 op::RETURNDATASIZE => {
882 field!(@push self.isize_type, self.ecx, EvmContext<'_>, pf::Slice; return_data.len);
883 }
884 op::RETURNDATACOPY => {
885 let sp = self.sp_after_inputs();
886 self.call_fallible_builtin(Builtin::ReturnDataCopy, &[self.ecx, sp]);
887 }
888 op::EXTCODEHASH => {
889 let sp = self.sp_after_inputs();
890 self.call_fallible_builtin(Builtin::ExtCodeHash, &[self.ecx, sp]);
891 }
892 op::BLOCKHASH => {
893 let sp = self.sp_after_inputs();
894 self.call_fallible_builtin(Builtin::BlockHash, &[self.ecx, sp]);
895 }
896 op::COINBASE => {
897 let slot = self.sp_at_top();
898 let _ = self.call_builtin(Builtin::Coinbase, &[self.ecx, slot]);
899 self.narrow_to_address(slot);
900 }
901 op::TIMESTAMP => {
902 let slot = self.sp_at_top();
903 let _ = self.call_builtin(Builtin::Timestamp, &[self.ecx, slot]);
904 }
905 op::NUMBER => {
906 let slot = self.sp_at_top();
907 let _ = self.call_builtin(Builtin::Number, &[self.ecx, slot]);
908 }
909 op::DIFFICULTY => {
910 let slot = self.sp_at_top();
911 let _ = self.call_builtin(Builtin::Difficulty, &[self.ecx, slot]);
912 }
913 op::GASLIMIT => {
914 let slot = self.sp_at_top();
915 let _ = self.call_builtin(Builtin::GasLimit, &[self.ecx, slot]);
916 }
917 op::CHAINID => {
918 let slot = self.sp_at_top();
919 let _ = self.call_builtin(Builtin::ChainId, &[self.ecx, slot]);
920 }
921 op::SELFBALANCE => {
922 let slot = self.sp_at_top();
923 self.call_fallible_builtin(Builtin::SelfBalance, &[self.ecx, slot]);
924 }
925 op::BASEFEE => {
926 let slot = self.sp_at_top();
927 let _ = self.call_builtin(Builtin::Basefee, &[self.ecx, slot]);
928 }
929 op::BLOBHASH => {
930 let sp = self.sp_after_inputs();
931 let _ = self.call_builtin(Builtin::BlobHash, &[self.ecx, sp]);
932 }
933 op::BLOBBASEFEE => {
934 let slot = self.sp_at_top();
935 let _ = self.call_builtin(Builtin::BlobBaseFee, &[self.ecx, slot]);
936 }
937 op::SLOTNUM => {
938 let slot = self.sp_at_top();
939 let _ = self.call_builtin(Builtin::SlotNum, &[self.ecx, slot]);
940 }
941
942 op::POP => {
943 self.pop_ignore(1);
944 }
945 op::MLOAD => {
946 let offset = self.pop();
947 let addr = self.build_ensure_memory(offset, 32);
948 let value = self.bcx.load_aligned(self.word_type, addr, 1, "mload.value");
949 let value = if self.little_endian() { self.bcx.bswap(value) } else { value };
950 self.push(value);
951 }
952 op::MSTORE => {
953 let [offset, value] = self.popn();
954 let addr = self.build_ensure_memory(offset, 32);
955 let value = if self.little_endian() { self.bcx.bswap(value) } else { value };
956 self.bcx.store_aligned(value, addr, 1);
957 }
958 op::MSTORE8 => {
959 let [offset, value] = self.popn();
960 let addr = self.build_ensure_memory(offset, 1);
961 let value = self.bcx.ireduce(self.i8_type, value);
962 self.bcx.store_aligned(value, addr, 1);
963 }
964 op::SLOAD => {
965 let sp = self.sp_after_inputs();
966 self.call_fallible_builtin(Builtin::Sload, &[self.ecx, sp]);
967 }
968 op::SSTORE => {
969 let sp = self.sp_after_inputs();
970 self.call_fallible_builtin(Builtin::Sstore, &[self.ecx, sp]);
971 }
972 op::JUMP | op::JUMPI => {
973 let is_invalid = data.flags.contains(InstFlags::INVALID_JUMP);
974 let has_const_jumpi_condition = data.has_const_jumpi_condition();
975 if is_invalid && opcode == op::JUMP {
976 self.pop_ignore(1);
978 self.build_fail_imm(InstructionResult::InvalidJump);
979 } else {
980 let target = if is_invalid {
981 debug_assert_eq!(*data, op::JUMPI);
982 self.pop_ignore(1);
984 self.add_invalid_jump()
985 } else if data.flags.contains(InstFlags::MULTI_JUMP) {
986 let target_value = self.pop();
987 let targets = self.bytecode.multi_jump_targets(inst).unwrap();
988
989 if opcode == op::JUMPI && !has_const_jumpi_condition {
990 let cond_word = self.pop();
991 self.materialize_live_stack();
992 let cond = self.bcx.icmp_imm(IntCC::NotEqual, cond_word, 0);
993 let next = self
994 .effective_next_entry(inst)
995 .expect("JUMPI must have a fallthrough target");
996 let switch_block = self.bcx.create_block("multi_jump");
997 self.bcx.brif(cond, switch_block, next);
998 self.bcx.switch_to_block(switch_block);
999 } else {
1000 if opcode == op::JUMPI {
1001 self.pop_ignore(1);
1002 }
1003 self.materialize_live_stack();
1004 }
1005
1006 let switch_targets: Vec<_> = targets
1007 .iter()
1008 .map(|&t| {
1009 let pc = self.bytecode.inst(t).jumpdest_pc() as u64;
1010 (pc, self.effective_entry(t))
1011 })
1012 .collect();
1013 let invalid_jump = self.add_invalid_jump();
1014 self.bcx.switch(target_value, invalid_jump, &switch_targets, true);
1015
1016 goto_return!(no_branch);
1017 } else if data.flags.contains(InstFlags::STATIC_JUMP) {
1018 self.pop_ignore(1);
1020 let target_inst = data.static_jump_target();
1021 debug_assert!(
1022 *self.bytecode.inst(target_inst) == op::JUMPDEST
1023 || (opcode == op::JUMPI && has_const_jumpi_condition),
1024 "jumping to non-JUMPDEST; target_inst={target_inst}",
1025 );
1026 self.effective_entry(target_inst)
1027 } else {
1028 debug_assert!(self.bytecode.has_dynamic_jumps());
1030 let target = self.pop();
1031 let target = self.u256_to_u64_saturating(target, 64);
1032 self.incoming_dynamic_jumps
1033 .push((target, self.bcx.current_block().unwrap()));
1034 self.dynamic_jump_table
1035 };
1036
1037 if opcode == op::JUMPI && !has_const_jumpi_condition {
1038 let cond_word = self.pop();
1039 self.materialize_live_stack();
1041 let cond = self.bcx.icmp_imm(IntCC::NotEqual, cond_word, 0);
1042 let next = self
1043 .effective_next_entry(inst)
1044 .expect("JUMPI must have a fallthrough target");
1045 self.bcx.brif(cond, target, next);
1046 } else {
1047 if opcode == op::JUMPI {
1048 self.pop_ignore(1);
1049 }
1050 self.materialize_live_stack();
1052 self.bcx.br(target);
1053 }
1054 }
1055
1056 goto_return!(no_branch);
1057 }
1058 op::PC => {
1059 let pc = self.bcx.iconst_256(data.pc_imm());
1060 self.push(pc);
1061 }
1062 op::MSIZE => {
1063 let mem_len_field = self.get_field(
1064 self.ecx,
1065 mem::offset_of!(EvmContext<'_>, mem_len),
1066 "ecx.mem_len.addr",
1067 );
1068 let mem_len = self.bcx.load(self.isize_type, mem_len_field, "ecx.mem_len");
1069 let msize = self.bcx.zext(self.word_type, mem_len);
1070 self.push(msize);
1071 }
1072 op::GAS => {
1073 let addr = self.gas_remaining_addr();
1074 let i64_type = self.bcx.type_int(64);
1075 let remaining = self.bcx.load(i64_type, addr, "gas.remaining");
1076 let remaining = self.bcx.zext(self.word_type, remaining);
1077 self.push(remaining);
1078 }
1079 op::JUMPDEST => {
1080 self.bcx.nop();
1081 }
1082 op::TLOAD => {
1083 let sp = self.sp_after_inputs();
1084 let _ = self.call_builtin(Builtin::Tload, &[self.ecx, sp]);
1085 }
1086 op::TSTORE => {
1087 let sp = self.sp_after_inputs();
1088 self.call_fallible_builtin(Builtin::Tstore, &[self.ecx, sp]);
1089 }
1090 op::MCOPY => {
1091 let sp = self.sp_after_inputs();
1092 self.call_fallible_builtin(Builtin::Mcopy, &[self.ecx, sp]);
1093 }
1094
1095 op::PUSH0..=op::PUSH32 => {
1096 unreachable!("handled in const_output");
1097 }
1098
1099 op::DUP1..=op::DUP16 => self.dup((opcode - op::DUP1 + 1) as usize),
1100 op::DUPN => match decode_single(data.imm_byte()) {
1101 Some(n) => self.dup(n as usize),
1102 None => goto_return!(fail InstructionResult::InvalidImmediateEncoding),
1103 },
1104
1105 op::SWAP1..=op::SWAP16 => self.swap((opcode - op::SWAP1 + 1) as usize),
1106 op::SWAPN => match decode_single(data.imm_byte()) {
1107 Some(n) => self.swap(n as usize),
1108 None => goto_return!(fail InstructionResult::InvalidImmediateEncoding),
1109 },
1110
1111 op::EXCHANGE => match decode_pair(data.imm_byte()) {
1112 Some((n, m)) => self.exchange(n as usize, (m - n) as usize),
1113 None => goto_return!(fail InstructionResult::InvalidImmediateEncoding),
1114 },
1115
1116 op::LOG0..=op::LOG4 => {
1117 let n = opcode - op::LOG0;
1118 let sp = self.sp_after_inputs();
1119 let n = self.bcx.iconst(self.i8_type, n as i64);
1120 self.call_fallible_builtin(Builtin::Log, &[self.ecx, sp, n]);
1121 }
1122
1123 op::CREATE => {
1124 self.create_common(CreateKind::Create);
1125 goto_return!(no_branch);
1126 }
1127 op::CALL => {
1128 self.call_common(CallKind::Call);
1129 goto_return!(no_branch);
1130 }
1131 op::CALLCODE => {
1132 self.call_common(CallKind::CallCode);
1133 goto_return!(no_branch);
1134 }
1135 op::RETURN => {
1136 self.return_common(InstructionResult::Return);
1137 goto_return!(no_branch);
1138 }
1139 op::DELEGATECALL => {
1140 self.call_common(CallKind::DelegateCall);
1141 goto_return!(no_branch);
1142 }
1143 op::CREATE2 => {
1144 self.create_common(CreateKind::Create2);
1145 goto_return!(no_branch);
1146 }
1147
1148 op::STATICCALL => {
1149 self.call_common(CallKind::StaticCall);
1150 goto_return!(no_branch);
1151 }
1152
1153 op::REVERT => {
1154 self.return_common(InstructionResult::Revert);
1155 goto_return!(no_branch);
1156 }
1157 op::INVALID => goto_return!(fail InstructionResult::InvalidFEOpcode),
1158 op::SELFDESTRUCT => {
1159 let sp = self.sp_after_inputs();
1160 self.sync_diverging_stack_effect();
1161 let _ = self.call_builtin(Builtin::SelfDestruct, &[self.ecx, sp]);
1162 self.bcx.unreachable();
1163 goto_return!(no_branch);
1164 }
1165
1166 _ => unreachable!("unimplemented instruction: {data:?}"),
1167 }
1168
1169 self.sync_virtual_stack_diff(diff);
1170 self.section_len_offset += diff;
1171 goto_return!("normal exit");
1172 }
1173
1174 fn sync_noop_diff(&mut self, inst: Inst, diff: i32) {
1180 let expected_top = self.section_len_offset + diff;
1181 let current_top = self.vstack.top_offset();
1182 if current_top == expected_top {
1183 return;
1184 }
1185 let delta = expected_top - current_top;
1186 if delta < 0 {
1187 self.vstack.drop_top((-delta) as usize);
1188 } else {
1189 if delta == 1
1192 && let Some(c) = self.bytecode.const_output(inst)
1193 {
1194 let value = self.bcx.iconst_256(c);
1195 self.vstack.push(value);
1196 } else {
1197 for _ in 0..delta {
1198 self.vstack.push_mem();
1199 }
1200 }
1201 }
1202 }
1203
1204 fn sync_virtual_stack_diff(&mut self, diff: i32) {
1213 let expected_top = self.section_len_offset + diff;
1214 let current_top = self.vstack.top_offset();
1215 if current_top == expected_top {
1216 return;
1217 }
1218 let delta = expected_top - current_top;
1219 if expected_top < self.vstack.live_range().start {
1220 let inst = self.current_inst.unwrap();
1221 let mut head = inst;
1223 for i in (0..inst.index()).rev() {
1224 let idx = crate::Inst::from_usize(i);
1225 let d = self.bytecode.inst(idx);
1226 if d.is_dead_code() {
1227 continue;
1228 }
1229 if d.is_stack_section_head() {
1230 head = idx;
1231 break;
1232 }
1233 }
1234 let mut section_dump = String::new();
1236 for i in head.index()..=inst.index() {
1237 let idx = crate::Inst::from_usize(i);
1238 let d = self.bytecode.inst(idx);
1239 if d.is_dead_code() {
1240 continue;
1241 }
1242 use std::fmt::Write;
1243 let _ = write!(
1244 section_dump,
1245 "\n ic{i} pc={} {:?} io={:?} flags={:?} gas={:?} stack={:?}{}{}",
1246 self.bytecode.pc(idx),
1247 d.to_op(),
1248 d.stack_io(),
1249 d.flags,
1250 d.gas_section,
1251 d.stack_section,
1252 if d.is_stack_section_head() { " SECTION_HEAD" } else { "" },
1253 if d.is_dead_code() { " DEAD" } else { "" },
1254 );
1255 }
1256 let head_data = self.bytecode.inst(head);
1257 panic!(
1258 "sync: expected_top={expected_top} < base={}, section_len_offset={}, \
1259 diff={diff}, current_top={current_top}, inst={:?} (ic{})\n\
1260 section head=ic{}, head_stack_section={:?}, section:{section_dump}",
1261 self.vstack.live_range().start,
1262 self.section_len_offset,
1263 self.current_inst().to_op(),
1264 inst.index(),
1265 head.index(),
1266 head_data.stack_section,
1267 );
1268 }
1269 if delta < 0 {
1270 self.vstack.drop_top((-delta) as usize);
1271 } else {
1272 for _ in 0..delta {
1273 self.vstack.push_mem();
1274 }
1275 }
1276
1277 let (_, outputs) = self.current_inst().stack_io();
1282 let outputs = outputs as i32;
1283 if outputs > 0 {
1284 self.vstack.mark_materialized_range(expected_top - outputs..expected_top);
1285 }
1286
1287 debug_assert_eq!(
1288 self.vstack.top_offset(),
1289 expected_top,
1290 "virtual stack sync mismatch after {:?}",
1291 self.current_inst().to_op(),
1292 );
1293 }
1294
1295 fn push(&mut self, value: B::Value) {
1297 self.vstack.push(value);
1298 self.len_offset += 1;
1299 }
1300
1301 fn const_operands<const N: usize>(&self) -> [Option<U256>; N] {
1304 let inst = self.current_inst.unwrap();
1305 std::array::from_fn(|i| self.bytecode.const_operand(inst, i))
1306 }
1307
1308 fn fold_const(&mut self, value: impl TryInto<U256>) {
1310 self.pop_ignore(self.current_inst().stack_io().0 as usize);
1311 let v = self.bcx.iconst_256(value);
1312 self.push(v);
1313 }
1314
1315 fn pop_ignore(&mut self, n: usize) {
1317 self.vstack.drop_top(n);
1318 self.len_offset -= n as i8;
1319 }
1320
1321 fn pop(&mut self) -> B::Value {
1323 self.popn::<1>()[0]
1324 }
1325
1326 fn popn<const N: usize>(&mut self) -> [B::Value; N] {
1328 assert_ne!(N, 0);
1329
1330 let operand_depth_base = (-self.len_offset) as usize;
1331 let values = std::array::from_fn(|i| {
1332 let operand_depth = operand_depth_base + i;
1333 let name = b'a' + i as u8;
1334 self.stack_value_at_depth(operand_depth, i, std::str::from_utf8(&[name]).unwrap())
1335 });
1336 self.pop_ignore(N);
1337 values
1338 }
1339
1340 fn dup(&mut self, n: usize) {
1343 assert_ne!(n, 0);
1344 let name = if self.config.debug { &format!("dup{n}") } else { "" };
1345 let value = self.stack_value_at_depth(n - 1, n - 1, name);
1346 self.push(value);
1347 }
1348
1349 fn swap(&mut self, n: usize) {
1352 self.exchange(0, n);
1353 }
1354
1355 fn exchange(&mut self, n: usize, m: usize) {
1359 assert_ne!(m, 0);
1360 let a = self.stack_value_at_depth(n, n, "swap.a");
1361 let b = self.stack_value_at_depth(n + m, n + m, "swap.b");
1362 self.vstack.set(n, b);
1363 self.vstack.set(n + m, a);
1364 }
1365
1366 fn return_common(&mut self, ir: InstructionResult) {
1368 let sp = self.sp_after_inputs();
1369 let ir_const = self.bcx.iconst(self.i8_type, ir as i64);
1370 self.sync_diverging_stack_effect();
1371 let _ = self.call_builtin(Builtin::DoReturn, &[self.ecx, sp, ir_const]);
1372 self.bcx.unreachable();
1373 }
1374
1375 fn sync_diverging_stack_effect(&mut self) {
1376 let data = self.current_inst();
1377 let (inp, out) = data.stack_io();
1378 let diff = effective_stack_diff(inp, out, data);
1379 self.sync_virtual_stack_diff(diff);
1380 if self.config.inspect_stack {
1381 self.materialize_live_stack();
1382 self.copy_stack_to_arg();
1383 self.save_stack_len();
1384 }
1385 }
1386
1387 fn create_common(&mut self, create_kind: CreateKind) {
1389 let sp = self.sp_after_inputs();
1390 let create_kind = self.bcx.iconst(self.i8_type, create_kind as i64);
1391 self.call_fallible_builtin(Builtin::Create, &[self.ecx, sp, create_kind]);
1392 self.suspend();
1393 }
1394
1395 fn call_common(&mut self, call_kind: CallKind) {
1397 let sp = self.sp_after_inputs();
1398 let call_kind = self.bcx.iconst(self.i8_type, call_kind as i64);
1399 self.call_fallible_builtin(Builtin::Call, &[self.ecx, sp, call_kind]);
1400 self.suspend();
1401 }
1402
1403 fn suspend(&mut self) {
1405 self.materialize_live_stack();
1407
1408 let idx = self.resume_blocks.len();
1410 let resume_at = self
1411 .effective_next_entry(self.current_inst.unwrap())
1412 .expect("suspending instruction must have a resume target");
1413 self.add_resume_at(resume_at);
1414
1415 let value = self.bcx.iconst(self.isize_type, idx as i64 + 1);
1417 self.suspend_blocks.push((value, self.bcx.current_block().unwrap()));
1418
1419 self.bcx.br(self.suspend_block);
1421 }
1422
1423 fn add_resume_at(&mut self, block: B::BasicBlock) {
1425 self.resume_blocks.push(block);
1426 }
1427
1428 fn load_word(&mut self, ptr: B::Value, name: &str) -> B::Value {
1430 self.bcx.load(self.word_type, ptr, name)
1431 }
1432
1433 fn get_field(&mut self, ptr: B::Value, offset: usize, name: &str) -> B::Value {
1435 get_field(&mut self.bcx, ptr, offset, name)
1436 }
1437
1438 fn load_input(&mut self) -> B::Value {
1440 let ptr_type = self.bcx.type_ptr();
1441 let input_field = get_field(
1442 &mut self.bcx,
1443 self.ecx,
1444 mem::offset_of!(EvmContext<'_>, input),
1445 "ecx.input.addr",
1446 );
1447 self.bcx.load(ptr_type, input_field, "ecx.input")
1448 }
1449
1450 #[allow(clippy::assertions_on_constants)]
1456 fn narrow_to_address(&mut self, slot: B::Value) {
1457 debug_assert!(self.little_endian(), "big-endian not yet supported");
1458 let value = self.bcx.load(self.address_type, slot, "address");
1459 let value = self.bcx.zext(self.word_type, value);
1460 self.bcx.store(value, slot);
1461 }
1462
1463 fn gas_remaining_addr(&mut self) -> B::Value {
1464 const OFFSET: usize =
1465 mem::offset_of!(EvmContext<'_>, gas) + mem::offset_of!(pf::Gas, tracker.remaining);
1466 let offset = self.bcx.iconst(self.isize_type, OFFSET as i64);
1467 self.bcx.gep(self.i8_type, self.ecx, &[offset], "gas.remaining.addr")
1468 }
1469
1470 fn save_stack_len(&mut self) {
1472 let len = self.stack_len.load(&mut self.bcx, "stack_len");
1473 let ptr = self.stack_len_arg();
1474 self.bcx.store(len, ptr);
1475 }
1476
1477 fn copy_stack_from_arg(&mut self, len: B::Value) {
1480 if let Some(src) = self.sp_arg {
1481 let dst = self.stack.addr(&mut self.bcx);
1482 let word_size = 32i64;
1483 let byte_len = self.bcx.imul_imm(len, word_size);
1484 self.bcx.memcpy(dst, src, byte_len);
1485 }
1486 }
1487
1488 fn copy_stack_to_arg(&mut self) {
1490 if let Some(dst) = self.sp_arg {
1491 let len = self.stack_len.load(&mut self.bcx, "stack_len");
1492 let src = self.stack.addr(&mut self.bcx);
1493 let word_size = 32i64;
1494 let byte_len = self.bcx.imul_imm(len, word_size);
1495 self.bcx.memcpy(dst, src, byte_len);
1496 }
1497 }
1498
1499 fn stack_len_arg(&mut self) -> B::Value {
1501 self.bcx.fn_param(2)
1502 }
1503
1504 #[must_use]
1509 fn sp_at_top(&mut self) -> B::Value {
1510 self.sp_from_section(self.section_len_offset as i64)
1511 }
1512
1513 #[must_use]
1525 fn sp_after_inputs(&mut self) -> B::Value {
1526 let (inputs, outputs) = self.current_inst().stack_io();
1527 let inputs = inputs as usize;
1528 let outputs = outputs as usize;
1529 let top = self.section_len_offset;
1530 let start = top - inputs as i32;
1531 let window = inputs.max(outputs) as i32;
1532 self.materialize_range(start, start + window);
1533 self.write_const_operands(inputs);
1534 self.sp_from_top(inputs)
1535 }
1536
1537 #[must_use]
1540 fn sp_after_inputs_with(&mut self, depths: &[usize]) -> B::Value {
1541 let (inputs, _) = self.current_inst().stack_io();
1542 let inputs = inputs as usize;
1543 let top = self.section_len_offset;
1544 for &depth in depths {
1545 let off = top - inputs as i32 + (inputs - 1 - depth) as i32;
1546 self.materialize_range(off, off + 1);
1547 }
1548 self.write_const_operands(inputs);
1549 self.sp_from_top(inputs)
1550 }
1551
1552 fn write_const_operands(&mut self, inputs: usize) {
1555 let inst = self.current_inst.unwrap();
1556 let top = self.section_len_offset;
1557 for depth in 0..inputs {
1558 let off = top - inputs as i32 + (inputs - 1 - depth) as i32;
1559 if let VSlot::Materialized = self.vstack.get_at_offset(off)
1560 && let Some(c) = self.bytecode.const_operand(inst, depth)
1561 {
1562 let value = self.bcx.iconst_256(c);
1563 let sp = self.sp_from_section(off as i64);
1564 self.bcx.store(value, sp);
1565 }
1566 }
1567 }
1568
1569 fn sp_from_section(&mut self, offset: i64) -> B::Value {
1571 if offset == 0 {
1572 return self.section_start_sp;
1573 }
1574 let offset = self.bcx.iconst(self.isize_type, offset);
1575 self.bcx.gep(self.word_type, self.section_start_sp, &[offset], "sp")
1576 }
1577
1578 fn sp_at(&mut self, len: B::Value) -> B::Value {
1580 let ptr = self.stack.addr(&mut self.bcx);
1581 self.bcx.gep(self.word_type, ptr, &[len], "sp")
1582 }
1583
1584 fn sp_from_top(&mut self, n: usize) -> B::Value {
1586 self.sp_from_section(self.section_len_offset as i64 - n as i64)
1587 }
1588
1589 fn stack_value_at_depth(
1597 &mut self,
1598 operand_depth: usize,
1599 live_depth: usize,
1600 name: &str,
1601 ) -> B::Value {
1602 let inst = self.current_inst.unwrap();
1603 if let Some(c) = self.bytecode.const_operand(inst, operand_depth) {
1604 return self.bcx.iconst_256(c);
1605 }
1606 match self.vstack.get(live_depth) {
1607 VSlot::Virtual(v) => v,
1608 VSlot::Materialized => {
1609 let off = self.vstack.offset_at_depth(live_depth);
1610 let sp = self.sp_from_section(off as i64);
1611 let value = self.load_word(sp, name);
1612 self.vstack.set(live_depth, value);
1613 value
1614 }
1615 }
1616 }
1617
1618 fn materialize_live_stack(&mut self) {
1620 let range = self.vstack.live_range();
1621 self.materialize_range(range.start, range.end);
1622 }
1623
1624 fn relieve_vstack_pressure(&mut self) {
1627 const HIGH_WATER: usize = 2;
1629 const KEEP_HOT: usize = 2;
1631
1632 let live = self.vstack.live_range();
1633 if (live.end - live.start) as usize <= HIGH_WATER {
1634 return;
1635 }
1636
1637 let virtual_count = self.vstack.virtual_count();
1638 if virtual_count <= HIGH_WATER {
1639 return;
1640 }
1641
1642 let cold_end = (self.vstack.top_offset() - KEEP_HOT as i32).max(live.start);
1644 if cold_end > live.start {
1645 self.materialize_range(live.start, cold_end);
1646 }
1647 }
1648
1649 fn materialize_range(&mut self, start: i32, end: i32) {
1651 let pending: Vec<_> = self.vstack.pending_stores(start..end).collect();
1652 for (off, value) in pending {
1653 let sp = self.sp_from_section(off as i64);
1654 self.bcx.store(value, sp);
1655 }
1656 self.vstack.mark_materialized_range(start..end);
1657 }
1658
1659 fn gas_cost_imm(&mut self, cost: u64) {
1661 if !self.config.gas_metering || cost == 0 {
1662 return;
1663 }
1664 let value = self.bcx.iconst(self.isize_type, cost as i64);
1665 self.gas_cost(value);
1666 }
1667
1668 fn gas_cost(&mut self, cost: B::Value) {
1670 if !self.config.gas_metering {
1671 return;
1672 }
1673
1674 let addr = self.gas_remaining_addr();
1677 let i64_type = self.bcx.type_int(64);
1678 let gas_remaining = self.bcx.load(i64_type, addr, "gas.remaining");
1679 let (res, overflow) = self.bcx.usub_overflow(gas_remaining, cost);
1680 self.bcx.store(res, addr);
1681 self.build_check(overflow, InstructionResult::OutOfGas);
1682 }
1683
1684 fn build_ensure_memory(&mut self, offset: B::Value, len: u64) -> B::Value {
1687 let offset = self.u256_to_u64_saturating(offset, 63);
1689 if self.current_inst.is_some_and(|inst| self.can_skip_ensure_memory(inst)) {
1691 return self.build_memory_addr(offset);
1692 }
1693
1694 let isize_type = self.isize_type;
1695 let len_const = self.bcx.iconst(isize_type, len as i64);
1696 let min_size = self.bcx.iadd(offset, len_const);
1697
1698 let direct_resize_size = self
1699 .current_inst
1700 .map(|inst| self.bytecode.memory_section(inst).direct_resize_size)
1701 .unwrap_or_default();
1702 if direct_resize_size != 0 {
1704 self.call_fallible_builtin(Builtin::Mresize, &[self.ecx, min_size]);
1705 self.cached_mem_base = None;
1706 return self.build_memory_addr(offset);
1707 }
1708
1709 let mem_len_field =
1712 self.get_field(self.ecx, mem::offset_of!(EvmContext<'_>, mem_len), "ecx.mem_len.addr");
1713 let mem_len = self.bcx.load(isize_type, mem_len_field, "ecx.mem_len");
1714 let exceeds = self.bcx.icmp(IntCC::UnsignedGreaterThan, min_size, mem_len);
1715 self.cached_mem_base = None;
1716
1717 self.if_then(exceeds, |this| {
1718 this.bcx.set_current_block_cold();
1719 this.call_fallible_builtin(Builtin::Mresize, &[this.ecx, min_size]);
1720 });
1721 self.build_memory_addr(offset)
1722 }
1723
1724 fn can_skip_ensure_memory(&self, inst: Inst) -> bool {
1725 let section = self.bytecode.memory_section(inst);
1726 if section.known_size < section.required_size {
1727 return false;
1728 }
1729 let mut has_memory_access = false;
1730 for (offset, len) in self.bytecode.const_memory_accesses(inst).into_iter().flatten() {
1731 has_memory_access = true;
1732 if offset.is_none() || len.is_none() {
1733 return false;
1734 }
1735 }
1736 has_memory_access
1737 }
1738
1739 fn build_memory_addr(&mut self, offset: B::Value) -> B::Value {
1740 let mem_base = self.cached_mem_base.unwrap_or_else(|| self.load_memory_base());
1741 self.cached_mem_base = Some(mem_base);
1742 self.bcx.gep(self.i8_type, mem_base, &[offset], "mem.addr")
1743 }
1744
1745 fn load_memory_base(&mut self) -> B::Value {
1746 let ptr_type = self.bcx.type_ptr();
1747 let mem_base_field = self.get_field(
1748 self.ecx,
1749 mem::offset_of!(EvmContext<'_>, mem_base),
1750 "ecx.mem_base.addr",
1751 );
1752 self.bcx.load(ptr_type, mem_base_field, "ecx.mem_base")
1753 }
1754
1755 fn check_stack_bounds(&mut self, stack_section: StackSection) {
1766 let inp = stack_section.inputs;
1767 let diff = stack_section.max_growth as i64;
1768
1769 let underflow = |this: &mut Self| {
1770 debug_assert!(inp > 0);
1771 this.bcx.icmp_imm(IntCC::UnsignedLessThan, this.len_before, inp as i64)
1772 };
1773 let overflow = |this: &mut Self| {
1774 debug_assert!(diff > 0);
1775 if diff > STACK_CAP as i64 {
1776 return this.bcx.bool_const(true);
1777 }
1778 this.bcx.icmp_imm(IntCC::UnsignedGreaterThan, this.len_before, STACK_CAP as i64 - diff)
1779 };
1780
1781 let may_underflow = inp > 0;
1782 let may_overflow = diff > 0;
1783 if may_underflow && may_overflow {
1784 let underflow = underflow(self);
1785 let overflow = overflow(self);
1786 let cond = self.bcx.bitor(underflow, overflow);
1787 let ret = {
1788 let under = self.bcx.iconst(self.i8_type, InstructionResult::StackUnderflow as i64);
1789 let over = self.bcx.iconst(self.i8_type, InstructionResult::StackOverflow as i64);
1790 self.bcx.select(underflow, under, over)
1791 };
1792 let target = self.build_check_inner(true, cond, ret);
1793 self.bcx.switch_to_block(target);
1794 } else if may_underflow {
1795 let cond = underflow(self);
1796 self.build_check(cond, InstructionResult::StackUnderflow);
1797 } else if may_overflow {
1798 let cond = overflow(self);
1799 self.build_check(cond, InstructionResult::StackOverflow);
1800 }
1801 }
1802
1803 fn build_check(&mut self, failure_cond: B::Value, ret: InstructionResult) {
1807 self.build_check_imm_inner(true, failure_cond, ret);
1808 }
1809
1810 fn build_check_imm_inner(&mut self, is_failure: bool, cond: B::Value, ret: InstructionResult) {
1811 let ret_value = self.bcx.iconst(self.i8_type, ret as i64);
1812 let target = self.build_check_inner(is_failure, cond, ret_value);
1813 if self.config.comments {
1814 self.add_comment(&format!("check {ret:?}"));
1815 }
1816 self.bcx.switch_to_block(target);
1817 }
1818
1819 #[must_use]
1820 fn build_check_inner(
1821 &mut self,
1822 is_failure: bool,
1823 cond: B::Value,
1824 ret: B::Value,
1825 ) -> B::BasicBlock {
1826 let current_block = self.current_block();
1827 let target = self.create_block_after(current_block, "contd");
1828
1829 let exit_block = if is_failure {
1830 if let Some(failure_block) = self.failure_block {
1831 self.incoming_failures.push((ret, current_block));
1832 failure_block
1833 } else {
1834 self.create_block_after(target, "failure")
1835 }
1836 } else if let Some(return_block) = self.return_block {
1837 self.incoming_returns.push((ret, current_block));
1838 return_block
1839 } else {
1840 self.create_block_after(target, "return")
1841 };
1842 let then_block = if is_failure { exit_block } else { target };
1843 let else_block = if is_failure { target } else { exit_block };
1844 self.bcx.brif_cold(cond, then_block, else_block, is_failure);
1845
1846 if (is_failure && self.failure_block.is_none())
1847 || (!is_failure && self.return_block.is_none())
1848 {
1849 self.bcx.switch_to_block(exit_block);
1850 self.bcx.ret(&[ret]);
1851 }
1852
1853 target
1854 }
1855
1856 fn build_fail_imm(&mut self, ret: InstructionResult) {
1858 let ret_value = self.bcx.iconst(self.i8_type, ret as i64);
1859 self.build_fail(ret_value);
1860 if self.config.comments {
1861 self.add_comment(&format!("fail {ret:?}"));
1862 }
1863 }
1864
1865 fn build_fail(&mut self, ret: B::Value) {
1867 if self.config.inspect_stack {
1868 self.materialize_live_stack();
1869 }
1870 if let Some(block) = self.failure_block {
1871 self.incoming_failures.push((ret, self.bcx.current_block().unwrap()));
1872 self.bcx.br(block);
1873 } else {
1874 self.bcx.ret(&[ret]);
1875 }
1876 }
1877
1878 fn build_return_imm(&mut self, ret: InstructionResult) {
1880 let ret_value = self.bcx.iconst(self.i8_type, ret as i64);
1881 self.build_return(ret_value);
1882 if self.config.comments {
1883 self.add_comment(&format!("return {ret:?}"));
1884 }
1885 }
1886
1887 fn build_return(&mut self, ret: B::Value) {
1889 if self.config.inspect_stack {
1890 self.materialize_live_stack();
1891 }
1892 self.build_return_inner(ret);
1893 }
1894
1895 fn build_return_inner(&mut self, ret: B::Value) {
1896 if let Some(block) = self.return_block {
1897 self.incoming_returns.push((ret, self.bcx.current_block().unwrap()));
1898 self.bcx.br(block);
1899 } else {
1900 self.bcx.ret(&[ret]);
1901 }
1902 }
1903
1904 fn add_invalid_jump(&mut self) -> B::BasicBlock {
1905 let block = self.failure_block.unwrap();
1906 self.incoming_failures.push((
1907 self.bcx.iconst(self.i8_type, InstructionResult::InvalidJump as i64),
1908 self.bcx.current_block().unwrap(),
1909 ));
1910 block
1911 }
1912
1913 fn call_panic(&mut self, msg: &str) {
1915 let function = self.builtin_function(Builtin::Panic);
1916 let ptr = self.bcx.str_const(msg);
1917 let len = self.bcx.iconst(self.isize_type, msg.len() as i64);
1918 let _ = self.bcx.call(function, &[ptr, len]);
1919 self.bcx.unreachable();
1920 }
1921
1922 #[allow(dead_code)]
1923 fn call_printf(&mut self, template: &std::ffi::CStr, values: &[B::Value]) {
1924 let mut args = Vec::with_capacity(values.len() + 1);
1925 args.push(self.bcx.cstr_const(template));
1926 args.extend_from_slice(values);
1927 let printf = self.bcx.get_printf_function();
1928 let _ = self.bcx.call(printf, &args);
1929 }
1930
1931 fn call_fallible_builtin(&mut self, builtin: Builtin, args: &[B::Value]) {
1935 let _ = self.call_builtin(builtin, args);
1936 }
1937
1938 #[must_use]
1940 fn call_builtin(&mut self, builtin: Builtin, args: &[B::Value]) -> Option<B::Value> {
1941 let function = self.builtin_function(builtin);
1942 let invalidate_mem_base =
1947 !self.current_inst.is_some_and(|inst| self.can_skip_ensure_memory(inst));
1948 let value = self.bcx.call(function, args);
1949 if invalidate_mem_base {
1950 self.cached_mem_base = None;
1951 }
1952 value
1953 }
1954
1955 fn builtin_function(&mut self, builtin: Builtin) -> B::Function {
1957 self.builtins.get(builtin, &mut self.bcx)
1958 }
1959
1960 fn add_comment(&mut self, comment: &str) {
1962 if comment.is_empty() || !self.config.comments {
1963 return;
1964 }
1965 self.bcx.add_comment_to_current_inst(comment);
1966 }
1967
1968 fn current_inst(&self) -> &InstData {
1970 self.bytecode.inst(self.current_inst.unwrap())
1971 }
1972
1973 fn if_then(&mut self, cond: B::Value, then: impl FnOnce(&mut Self)) {
1974 let current_block = self.current_block();
1975 let then_block = self.create_block_after(current_block, "then");
1976 let done_block = self.create_block_after(then_block, "contd");
1977
1978 self.bcx.brif(cond, then_block, done_block);
1979
1980 self.bcx.switch_to_block(then_block);
1981 then(self);
1982 self.bcx.br(done_block);
1983
1984 self.bcx.switch_to_block(done_block);
1985 }
1986
1987 fn current_block(&mut self) -> B::BasicBlock {
1989 self.bcx.current_block().expect("no blocks")
1991 }
1992
1993 fn little_endian(&self) -> bool {
1994 true
1995 }
1996
1997 fn create_block_after(&mut self, after: B::BasicBlock, name: &str) -> B::BasicBlock {
2007 let name = self.op_block_name(name);
2008 self.bcx.create_block_after(after, &name)
2009 }
2010
2011 fn op_block_name(&self, name: &str) -> String {
2013 if !self.config.debug {
2014 return String::new();
2015 }
2016 self.bytecode.op_block_name(self.current_inst, name)
2017 }
2018
2019 fn u256_to_u64_saturating(&mut self, value: B::Value, bits: usize) -> B::Value {
2022 let i64_type = self.bcx.type_int(64);
2023 let reduced = self.bcx.ireduce(i64_type, value);
2024 let sentinel_lit = 1u128.checked_shl(bits as u32).unwrap_or(0).wrapping_sub(1);
2025 let sentinel_u256 = self.bcx.iconst_256(U256::from(sentinel_lit));
2026 let fits = self.bcx.icmp(IntCC::UnsignedLessThanOrEqual, value, sentinel_u256);
2027 let sentinel = self.bcx.iconst(i64_type, sentinel_lit as i64);
2028 self.bcx.select(fits, reduced, sentinel)
2029 }
2030}
2031
2032impl<B: Backend> FunctionCx<'_, B> {
2034 #[allow(dead_code)]
2035 #[must_use]
2036 fn call_ir_builtin(
2037 &mut self,
2038 name: &str,
2039 args: &[B::Value],
2040 arg_types: &[B::Type],
2041 ret: Option<B::Type>,
2042 build: impl FnOnce(&mut Self),
2043 ) -> Option<B::Value> {
2044 let prefix = "__revmc_ir_builtin_";
2045 let name = &format!("{prefix}{name}")[..];
2046
2047 debug_assert_eq!(args.len(), arg_types.len());
2050 let linkage = revmc_backend::Linkage::Private;
2051 let debug_location = self
2055 .config
2056 .debug
2057 .then(|| self.current_inst.map(|inst| self.inst_lines[inst]))
2058 .flatten();
2059 self.bcx.clear_debug_location();
2060
2061 let this = unsafe { &mut *(self as *mut Self) };
2062 let f = self.bcx.get_or_build_function(name, arg_types, ret, linkage, |bcx| {
2063 let prev_return_block = this.return_block.take();
2064 let prev_failure_block = this.failure_block.take();
2065 unsafe { std::ptr::swap(&mut this.bcx, bcx) };
2067
2068 for attr in default_attrs::for_fn().chain(std::iter::once(Attribute::NoUnwind)) {
2069 this.bcx.add_function_attribute(None, attr, FunctionAttributeLocation::Function)
2070 }
2071 for i in 0..this.bcx.num_fn_params() as u32 {
2072 for attr in default_attrs::for_param() {
2073 this.bcx.add_function_attribute(None, attr, FunctionAttributeLocation::Param(i))
2074 }
2075 }
2076 build(this);
2077
2078 unsafe { std::ptr::swap(&mut this.bcx, bcx) };
2080 this.failure_block = prev_failure_block;
2081 this.return_block = prev_return_block;
2082 });
2083 if let Some(line) = debug_location {
2084 self.bcx.set_debug_location(line, 1);
2085 }
2086 self.bcx.call(f, args)
2087 }
2088}
2089
2090#[allow(dead_code)]
2093mod pf {
2094 use super::*;
2095
2096 #[repr(C)] pub(super) struct Slice {
2098 pub(super) ptr: *const u8,
2099 pub(super) len: usize,
2100 }
2101 const _: [(); mem::size_of::<&'static [u8]>()] = [(); mem::size_of::<Slice>()];
2102
2103 pub(super) struct Gas {
2104 pub(super) tracker: GasTracker,
2106 pub(super) memory: MemoryGas,
2108 }
2109
2110 pub(super) struct GasTracker {
2111 pub(super) limit: u64,
2113 pub(super) remaining: u64,
2115 pub(super) reservoir: u64,
2117 pub(super) state_gas_spent: u64,
2119 pub(super) refunded: i64,
2121 }
2122
2123 #[repr(C)]
2124 pub(super) struct MemoryGas {
2125 pub(super) words_num: usize,
2126 pub(super) expansion_cost: u64,
2127 }
2128 const _: [(); mem::size_of::<revm_interpreter::Gas>()] = [(); mem::size_of::<Gas>()];
2129}
2130
2131fn effective_stack_diff(inp: u8, out: u8, data: &InstData) -> i32 {
2133 let mut diff = out as i32 - inp as i32;
2134 if data.may_suspend() {
2136 diff -= 1;
2137 }
2138 diff
2139}
2140
2141fn get_field<B: Builder>(bcx: &mut B, ptr: B::Value, offset: usize, name: &str) -> B::Value {
2142 let offset = bcx.iconst(bcx.type_ptr_sized_int(), offset as i64);
2143 bcx.gep(bcx.type_int(8), ptr, &[offset], name)
2144}
2145
2146#[allow(unused)]
2147macro_rules! format_printf {
2148 ($($t:tt)*) => {
2149 &std::ffi::CString::new(format!($($t)*)).unwrap()
2150 };
2151}
2152#[allow(unused)]
2153use format_printf;