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