Skip to main content

revmc_builtins/
ir.rs

1use revmc_backend::{Attribute, Backend, Builder, FunctionAttributeLocation, TypeMethods};
2
3// Must be kept in sync with `remvc-build`.
4const MANGLE_PREFIX: &str = "__revmc_builtin_";
5
6/// Builtin cache.
7#[derive(Debug)]
8pub struct Builtins<B: Backend>([Option<B::Function>; Builtin::COUNT]);
9
10unsafe impl<B: Backend> Send for Builtins<B> {}
11
12impl<B: Backend> Default for Builtins<B> {
13    fn default() -> Self {
14        Self::new()
15    }
16}
17
18impl<B: Backend> Builtins<B> {
19    /// Create a new cache.
20    pub fn new() -> Self {
21        Self([None; Builtin::COUNT])
22    }
23
24    /// Clear the cache.
25    pub fn clear(&mut self) {
26        *self = Self::new();
27    }
28
29    /// Get the function for the given builtin.
30    pub fn get(&mut self, builtin: Builtin, bcx: &mut B::Builder<'_>) -> B::Function {
31        *self.0[builtin as usize].get_or_insert_with(|| Self::init(builtin, bcx))
32    }
33
34    #[cold]
35    fn init(builtin: Builtin, bcx: &mut B::Builder<'_>) -> B::Function {
36        let name = builtin.name();
37        debug_assert!(name.starts_with(MANGLE_PREFIX), "{name:?}");
38        bcx.get_function(name).inspect(|r| trace!(name, ?r, "pre-existing")).unwrap_or_else(|| {
39            let r = Self::build(name, builtin, bcx);
40            trace!(name, ?r, "built");
41            r
42        })
43    }
44
45    fn build(name: &str, builtin: Builtin, bcx: &mut B::Builder<'_>) -> B::Function {
46        let ret = builtin.ret(bcx);
47        let params = builtin.params(bcx);
48        let address = builtin.addr();
49        let linkage = revmc_backend::Linkage::Import;
50        let f = bcx.add_function(name, &params, ret, Some(address), linkage);
51        let default_attrs: &[Attribute] = if builtin == Builtin::Panic {
52            &[
53                Attribute::Cold,
54                Attribute::NoReturn,
55                Attribute::NoFree,
56                Attribute::NoRecurse,
57                Attribute::NoSync,
58            ]
59        } else {
60            &[
61                Attribute::WillReturn,
62                Attribute::NoFree,
63                Attribute::NoRecurse,
64                Attribute::NoSync,
65                Attribute::NoUnwind,
66            ]
67        };
68        for attr in default_attrs.iter().chain(builtin.attrs()).copied() {
69            bcx.add_function_attribute(Some(f), attr, FunctionAttributeLocation::Function);
70        }
71        let param_attrs = builtin.param_attrs();
72        for (i, param_attrs) in param_attrs.iter().enumerate() {
73            for attr in param_attrs {
74                bcx.add_function_attribute(
75                    Some(f),
76                    *attr,
77                    FunctionAttributeLocation::Param(i as u32),
78                );
79            }
80        }
81        f
82    }
83}
84
85macro_rules! builtins {
86    (@count) => { 0 };
87    (@count $first:tt $(, $rest:tt)*) => { 1 + builtins!(@count $($rest),*) };
88
89    (@param_attr $default:ident) => { $default() };
90    (@param_attr $default:ident $name:expr) => { $name };
91
92    (@types |$bcx:ident| { $($types_init:tt)* }
93     @param_attrs |$op:ident| { $($attrs_init:tt)* }
94     $($ident:ident = $(#[$attr:expr])* $name:ident($($(@[$param_attr:expr])? $params:expr),* $(,)?) $ret:expr),* $(,)?
95    ) => { paste::paste! {
96        /// Builtins that can be called by the compiled functions.
97        #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
98        pub enum Builtin {
99            $($ident,)*
100        }
101
102        #[allow(unused_variables)]
103        impl Builtin {
104            pub const COUNT: usize = builtins!(@count $($ident),*);
105
106            pub const fn name(self) -> &'static str {
107                match self {
108                    $(Self::$ident => stringify!($name),)*
109                }
110            }
111
112            pub fn addr(self) -> usize {
113                match self {
114                    $(Self::$ident => crate::$name as *const () as usize,)*
115                }
116            }
117
118            pub fn ret<B: TypeMethods>(self, $bcx: &mut B) -> Option<B::Type> {
119                $($types_init)*
120                match self {
121                    $(Self::$ident => $ret,)*
122                }
123            }
124
125            pub fn params<B: TypeMethods>(self, $bcx: &mut B) -> Vec<B::Type> {
126                $($types_init)*
127                match self {
128                    $(Self::$ident => vec![$($params),*],)*
129                }
130            }
131
132            pub fn attrs(self) -> &'static [Attribute] {
133                #[allow(unused_imports)]
134                use Attribute::*;
135                match self {
136                    $(Self::$ident => &[$($attr)*]),*
137                }
138            }
139
140            #[allow(non_upper_case_globals)]
141            pub fn param_attrs(self) -> Vec<Vec<Attribute>> {
142                #[allow(unused_imports)]
143                use Attribute::*;
144                let $op = self;
145                let default = || vec![Attribute::NoUndef];
146                $($attrs_init)*
147                match self {
148                    $(Self::$ident => vec![$(builtins!(@param_attr default $($param_attr)?)),*]),*
149                }
150            }
151
152            fn op(self) -> u8 {
153                use revm_bytecode::opcode::*;
154                const PANIC: u8 = 0;
155                const LOG: u8 = LOG0;
156                const DORETURN: u8 = RETURN;
157                const RESIZEMEMORY: u8 = 0;
158                const UDIV: u8 = DIV;
159                const UREM: u8 = MOD;
160                const SREM: u8 = SMOD;
161
162                match self {
163                    $(Self::$ident => [<$ident:upper>]),*
164                }
165            }
166        }
167    }};
168}
169
170builtins! {
171    @types |bcx| {
172        let ptr = bcx.type_ptr();
173        let usize = bcx.type_ptr_sized_int();
174        let bool = bcx.type_int(1);
175        let u8 = bcx.type_int(8);
176    }
177
178    @param_attrs |op| {
179        fn size_and_align<T>() -> Vec<Attribute> {
180            size_and_align2(Some(core::mem::size_of::<T>()), core::mem::align_of::<T>())
181        }
182
183        fn size_and_align2(size: Option<usize>, align: usize) -> Vec<Attribute> {
184            let mut vec = Vec::with_capacity(5);
185            vec.push(Attribute::NoAlias);
186            vec.push(Attribute::NoCapture);
187            vec.push(Attribute::NoUndef);
188            vec.push(Attribute::Align(align as u64));
189            if let Some(size) = size {
190                vec.push(Attribute::Dereferenceable(size as u64));
191            }
192            vec
193        }
194
195        let op = op.op();
196        let (inputs, outputs) = if let Some(info) = revm_bytecode::opcode::OPCODE_INFO[op as usize] {
197            (info.inputs(), info.outputs())
198        } else {
199            (0, 0)
200        };
201
202        let ecx = size_and_align::<revmc_context::EvmContext<'static>>();
203
204        let sp_dyn = size_and_align2(None, core::mem::align_of::<revmc_context::EvmWord>());
205
206        let mut sp = sp_dyn.clone();
207        // `sp` is at `top - inputs`, we have access to `max(inputs, outputs)` words.
208        let n_stack_words = inputs.max(outputs);
209        let size_of_word = core::mem::size_of::<revmc_context::EvmWord>();
210        sp.push(Attribute::Dereferenceable(size_of_word as u64 * n_stack_words as u64));
211        match (inputs, outputs) {
212            (0, 0) => sp.push(Attribute::ReadNone),
213            (0, 1..) => sp.push(Attribute::WriteOnly),
214            (1.., 0) => sp.push(Attribute::ReadOnly),
215            (1.., 1..) => {}
216        }
217    }
218
219    Panic          = __revmc_builtin_panic(ptr, usize) None,
220
221    UDiv           = __revmc_builtin_udiv(@[sp] ptr) None,
222    URem           = __revmc_builtin_urem(@[sp] ptr) None,
223    SDiv           = __revmc_builtin_sdiv(@[sp] ptr) None,
224    SRem           = __revmc_builtin_srem(@[sp] ptr) None,
225    AddMod         = __revmc_builtin_addmod(@[sp] ptr) None,
226    MulMod         = __revmc_builtin_mulmod(@[sp] ptr) None,
227    Exp            = __revmc_builtin_exp(@[ecx] ptr, @[sp] ptr) Some(u8),
228    Keccak256      = __revmc_builtin_keccak256(@[ecx] ptr, @[sp] ptr) Some(u8),
229    Balance        = __revmc_builtin_balance(@[ecx] ptr, @[sp] ptr, u8) Some(u8),
230    Origin         = __revmc_builtin_origin(@[ecx] ptr, @[sp] ptr) None,
231    CallDataLoad   = __revmc_builtin_calldataload(@[ecx] ptr, @[sp] ptr) None,
232    CallDataSize   = __revmc_builtin_calldatasize(@[ecx] ptr) Some(usize),
233    CallDataCopy   = __revmc_builtin_calldatacopy(@[ecx] ptr, @[sp] ptr) Some(u8),
234    CodeCopy       = __revmc_builtin_codecopy(@[ecx] ptr, @[sp] ptr) Some(u8),
235    GasPrice       = __revmc_builtin_gas_price(@[ecx] ptr, @[sp] ptr) None,
236    ExtCodeSize    = __revmc_builtin_extcodesize(@[ecx] ptr, @[sp] ptr, u8) Some(u8),
237    ExtCodeCopy    = __revmc_builtin_extcodecopy(@[ecx] ptr, @[sp] ptr, u8) Some(u8),
238    ReturnDataCopy = __revmc_builtin_returndatacopy(@[ecx] ptr, @[sp] ptr) Some(u8),
239    ExtCodeHash    = __revmc_builtin_extcodehash(@[ecx] ptr, @[sp] ptr, u8) Some(u8),
240    BlockHash      = __revmc_builtin_blockhash(@[ecx] ptr, @[sp] ptr) Some(u8),
241    Coinbase       = __revmc_builtin_coinbase(@[ecx] ptr, @[sp] ptr) None,
242    Timestamp      = __revmc_builtin_timestamp(@[ecx] ptr, @[sp] ptr) None,
243    Number         = __revmc_builtin_number(@[ecx] ptr, @[sp] ptr) None,
244    Difficulty     = __revmc_builtin_difficulty(@[ecx] ptr, @[sp] ptr, u8) None,
245    GasLimit       = __revmc_builtin_gaslimit(@[ecx] ptr, @[sp] ptr) None,
246    ChainId        = __revmc_builtin_chainid(@[ecx] ptr, @[sp] ptr) None,
247    SelfBalance    = __revmc_builtin_self_balance(@[ecx] ptr, @[sp] ptr) Some(u8),
248    Basefee        = __revmc_builtin_basefee(@[ecx] ptr, @[sp] ptr) None,
249    BlobHash       = __revmc_builtin_blob_hash(@[ecx] ptr, @[sp] ptr) None,
250    BlobBaseFee    = __revmc_builtin_blob_base_fee(@[ecx] ptr, @[sp] ptr) None,
251    Sload          = __revmc_builtin_sload(@[ecx] ptr, @[sp] ptr, u8) Some(u8),
252    Sstore         = __revmc_builtin_sstore(@[ecx] ptr, @[sp] ptr, u8) Some(u8),
253    Msize          = __revmc_builtin_msize(@[ecx] ptr) Some(usize),
254    Tstore         = __revmc_builtin_tstore(@[ecx] ptr, @[sp] ptr) Some(u8),
255    Tload          = __revmc_builtin_tload(@[ecx] ptr, @[sp] ptr) None,
256    Mcopy          = __revmc_builtin_mcopy(@[ecx] ptr, @[sp] ptr) Some(u8),
257    Log            = __revmc_builtin_log(@[ecx] ptr, @[sp_dyn] ptr, u8) Some(u8),
258
259    Create         = __revmc_builtin_create(@[ecx] ptr, @[sp_dyn] ptr, u8, u8) Some(u8),
260    Call           = __revmc_builtin_call(@[ecx] ptr, @[sp_dyn] ptr, u8, u8) Some(u8),
261    DoReturn       = __revmc_builtin_do_return(@[ecx] ptr, @[sp] ptr, u8) Some(u8),
262    SelfDestruct   = __revmc_builtin_selfdestruct(@[ecx] ptr, @[sp] ptr, u8) Some(u8),
263
264    ResizeMemory   = __revmc_builtin_resize_memory(@[ecx] ptr, usize) Some(u8),
265    Mload          = __revmc_builtin_mload(@[ecx] ptr, @[sp] ptr) Some(u8),
266    Mstore         = __revmc_builtin_mstore(@[ecx] ptr, @[sp] ptr) Some(u8),
267    Mstore8        = __revmc_builtin_mstore8(@[ecx] ptr, @[sp] ptr) Some(u8),
268}