1use revmc_backend::{Attribute, Backend, Builder, FunctionAttributeLocation, TypeMethods};
2
3const MANGLE_PREFIX: &str = "__revmc_builtin_";
5
6#[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 pub fn new() -> Self {
21 Self([None; Builtin::COUNT])
22 }
23
24 pub fn clear(&mut self) {
26 *self = Self::new();
27 }
28
29 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, ¶ms, 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 #[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 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}