zingen/
asm.rs

1//! Low level assembler implementation for EVM.
2//!
3//! TODO: refactor this module with Result as outputs. (issue-21)
4
5use crate::{Buffer, Error, Result};
6use opcodes::{for_each_cancun_operator, Cancun as OpCode, OpCode as _};
7
8const MAX_STACK_SIZE: u16 = 1024;
9
10/// Low level assembler implementation for EVM.
11#[derive(Default, Clone, Debug)]
12pub struct Assembler {
13    /// Buffer of the assembler.
14    buffer: Buffer,
15    /// Gas counter.
16    ///
17    /// This is used to calculate the gas cost of the generated code.
18    ///
19    /// TODO: use a more precise type, eq `u256`. (issue-20)
20    gas: u128,
21    /// Memory pointer for byte offset.
22    pub mp: usize,
23    /// Stack pointer, maximum `MAX_STACK_SIZE` items.
24    pub sp: u16,
25}
26
27impl Assembler {
28    /// Buffer of the assembler.
29    pub fn buffer(&self) -> &[u8] {
30        &self.buffer
31    }
32
33    /// Mutable buffer of the assembler.
34    pub fn buffer_mut(&mut self) -> &mut Buffer {
35        &mut self.buffer
36    }
37
38    /// Increment the gas counter.
39    ///
40    /// TODO: use number bigger than `u256` for throwing proper errors. (#21)
41    pub fn increment_gas(&mut self, gas: u128) {
42        self.gas += gas;
43    }
44
45    /// Increment stack pointer
46    pub fn increment_sp(&mut self, items: u16) -> Result<()> {
47        if items == 0 {
48            return Ok(());
49        }
50
51        tracing::trace!(
52            "increment stack pointer {}.add({items}) -> {}",
53            self.sp,
54            self.sp + items
55        );
56        self.sp = self
57            .sp
58            .checked_add(items)
59            .ok_or(Error::StackOverflow(self.sp, items))?;
60
61        if self.sp > MAX_STACK_SIZE {
62            return Err(Error::StackOverflow(self.sp, items));
63        }
64
65        Ok(())
66    }
67
68    /// Decrement stack pointer
69    pub fn decrement_sp(&mut self, items: u16) -> Result<()> {
70        if items == 0 {
71            return Ok(());
72        }
73
74        tracing::trace!(
75            "decrement stack pointer {}.sub({items}) -> {}",
76            self.sp,
77            self.sp - items
78        );
79        self.sp = if self.sp == items {
80            0
81        } else {
82            self.sp
83                .checked_sub(items)
84                .ok_or(Error::StackUnderflow(self.sp, items))?
85        };
86
87        Ok(())
88    }
89
90    /// Increment memory pointer
91    pub fn increment_mp(&mut self, offset: usize) -> Result<()> {
92        self.mp = self
93            .mp
94            .checked_add(offset)
95            .ok_or(Error::MemoryOutOfBounds)?;
96
97        Ok(())
98    }
99
100    /// Decrement memory pointer
101    pub fn decrement_mp(&mut self, offset: usize) -> Result<()> {
102        self.mp = self
103            .mp
104            .checked_sub(offset)
105            .ok_or(Error::MemoryOutOfBounds)?;
106        Ok(())
107    }
108
109    /// Emit a byte.
110    pub fn emit(&mut self, byte: u8) {
111        self.buffer.push(byte);
112    }
113
114    /// Emit n bytes.
115    pub fn emitn(&mut self, bytes: &[u8]) {
116        self.buffer.extend_from_slice(bytes);
117    }
118
119    /// Emit a single opcode.
120    ///
121    /// Mock the stack input and output for checking
122    /// the stack usages.
123    pub fn emit_op(&mut self, opcode: OpCode) -> Result<()> {
124        tracing::trace!("emit opcode: {:?}", opcode);
125        self.decrement_sp(opcode.stack_in())?;
126        self.emit(opcode.into());
127        self.increment_gas(opcode.gas().into());
128        self.increment_sp(opcode.stack_out())?;
129
130        Ok(())
131    }
132}
133
134macro_rules! impl_opcodes {
135    ($($name:ident => $opcode:ident),+) => {
136        $(
137            #[doc = concat!(" Emit ", stringify!($opcode))]
138            pub fn $name(&mut self) -> Result<()> {
139                self.emit_op(OpCode::$opcode)?;
140                Ok(())
141            }
142        )*
143    };
144}
145
146/// Basic instruction implementations
147impl Assembler {
148    for_each_cancun_operator!(impl_opcodes);
149}