1use revmc_backend::{
2 Attribute, Backend, Builder, CallConv, FunctionAttributeLocation, TypeMethods,
3};
4
5const MANGLE_PREFIX: &str = "__revmc_builtin_";
7
8#[derive(Debug)]
10pub struct Builtins<B: Backend>([Option<B::Function>; Builtin::COUNT]);
11
12unsafe impl<B: Backend> Send for Builtins<B> {}
13
14impl<B: Backend> Default for Builtins<B> {
15 fn default() -> Self {
16 Self::new()
17 }
18}
19
20impl<B: Backend> Builtins<B> {
21 pub fn new() -> Self {
23 Self([None; Builtin::COUNT])
24 }
25
26 pub fn clear(&mut self) {
28 *self = Self::new();
29 }
30
31 pub fn get(&mut self, builtin: Builtin, bcx: &mut B::Builder<'_>) -> B::Function {
33 *self.0[builtin as usize].get_or_insert_with(|| Self::init(builtin, bcx))
34 }
35
36 #[cold]
37 fn init(builtin: Builtin, bcx: &mut B::Builder<'_>) -> B::Function {
38 let name = builtin.name();
39 debug_assert!(name.starts_with(MANGLE_PREFIX), "{name:?}");
40 if builtin.call_conv() == CallConv::Default
41 && let Some(r) = bcx.get_function(name)
42 {
43 trace!(name, ?r, "pre-existing");
44 return r;
45 }
46
47 let r = Self::build(name, builtin, bcx);
48 trace!(name, ?r, "built");
49 r
50 }
51
52 fn build(name: &str, builtin: Builtin, bcx: &mut B::Builder<'_>) -> B::Function {
53 let ret = builtin.ret(bcx);
54 let params = builtin.params(bcx);
55 let address = builtin.addr();
56 let linkage = revmc_backend::Linkage::Import;
57 let f = bcx.add_function(name, ¶ms, ret, Some(address), linkage, CallConv::Default);
58 Self::add_attrs(builtin, f, bcx);
59 match builtin.call_conv() {
60 CallConv::Default => f,
61 call_conv => {
62 let f = bcx.add_function_stub(f, call_conv);
63 Self::add_attrs(builtin, f, bcx);
64 f
65 }
66 }
67 }
68
69 fn add_attrs(builtin: Builtin, f: B::Function, bcx: &mut B::Builder<'_>) {
70 let param_attrs = builtin.param_attrs();
71 let mut attrs = Vec::with_capacity(16);
72 attrs.extend(builtin.attrs());
73 attrs.extend([
74 Attribute::NoFree,
75 Attribute::NoRecurse,
76 Attribute::NoSync,
77 Attribute::NoUnwind,
78 ]);
79 let evm_ctx_size = core::mem::size_of::<revmc_context::EvmContext<'static>>() as u64;
85 let writes_ecx = param_attrs.iter().any(|p| {
86 let writable = p.iter().any(|a| matches!(a, Attribute::Writable));
87 let is_ecx =
88 p.iter().any(|a| matches!(a, Attribute::Dereferenceable(s) if *s == evm_ctx_size));
89 writable && is_ecx
90 });
91 if !writes_ecx {
92 attrs.push(Attribute::ArgMemOnly);
93 }
94 for attr in attrs {
95 bcx.add_function_attribute(Some(f), attr, FunctionAttributeLocation::Function);
96 }
97 for (i, param_attrs) in param_attrs.iter().enumerate() {
98 for attr in param_attrs {
99 bcx.add_function_attribute(
100 Some(f),
101 *attr,
102 FunctionAttributeLocation::Param(i as u32),
103 );
104 }
105 }
106 }
107}
108
109macro_rules! builtins {
110 (@count) => { 0 };
111 (@count $first:tt $(, $rest:tt)*) => { 1 + builtins!(@count $($rest),*) };
112
113 (@param_attr $default:ident) => { $default() };
114 (@param_attr $default:ident $name:expr) => { $name };
115
116 (@types |$bcx:ident| { $($types_init:tt)* }
117 @param_attrs |$op:ident| { $($attrs_init:tt)* }
118 $($ident:ident = $(#[$attr:expr])* $name:ident($($(@[$param_attr:expr])? $params:expr),* $(,)?) $ret:expr),* $(,)?
119 ) => { paste::paste! {
120 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
122 pub enum Builtin {
123 $($ident,)*
124 }
125
126 #[allow(unused_variables)]
127 impl Builtin {
128 pub const COUNT: usize = builtins!(@count $($ident),*);
129 pub const ALL: &[Self; Self::COUNT] = &[$(Self::$ident),*];
130
131 pub const fn name(self) -> &'static str {
132 match self {
133 $(Self::$ident => stringify!($name),)*
134 }
135 }
136
137 pub fn addr(self) -> usize {
138 match self {
139 $(Self::$ident => crate::$name as *const () as usize,)*
140 }
141 }
142
143 pub fn parse(name: &str) -> Option<Self> {
144 match name {
145 $(stringify!($name) => Some(Self::$ident),)*
146 _ => None,
147 }
148 }
149
150 pub fn ret<B: TypeMethods>(self, $bcx: &mut B) -> Option<B::Type> {
151 $($types_init)*
152 match self {
153 $(Self::$ident => $ret,)*
154 }
155 }
156
157 pub fn params<B: TypeMethods>(self, $bcx: &mut B) -> Vec<B::Type> {
158 $($types_init)*
159 match self {
160 $(Self::$ident => vec![$($params),*],)*
161 }
162 }
163
164 pub fn attrs(self) -> &'static [Attribute] {
165 #[allow(unused_imports)]
166 use Attribute::*;
167 match self {
168 $(Self::$ident => &[$($attr),*]),*
169 }
170 }
171
172 #[allow(non_upper_case_globals)]
173 pub fn param_attrs(self) -> Vec<Vec<Attribute>> {
174 #[allow(unused_imports)]
175 use Attribute::*;
176 let $op = self;
177 let default = || vec![Attribute::NoUndef];
178 $($attrs_init)*
179 match self {
180 $(Self::$ident => vec![$(builtins!(@param_attr default $($param_attr)?)),*]),*
181 }
182 }
183
184 fn op(self) -> u8 {
185 use revm_bytecode::opcode::*;
186
187 const _0_0: u8 = STOP;
189 const _0_1: u8 = PUSH0;
190 const _1_0: u8 = POP;
191
192 const PANIC: u8 = _0_0;
193 const ASSERTSPECID: u8 = _0_0;
194
195 const EXPGAS: u8 = _1_0;
196 const KECCAK256CC: u8 = _0_1;
197
198 const CALLDATALOADC: u8 = _0_1;
199 const SLOADC: u8 = _0_1;
200
201 const MRESIZE: u8 = _0_0;
202
203 const LOG: u8 = _0_0;
204 const DORETURN: u8 = RETURN;
205 const DORETURNCC: u8 = _0_0;
206
207 match self {
208 $(Self::$ident => [<$ident:upper>]),*
209 }
210 }
211 }
212 }};
213}
214
215builtins! {
216 @types |bcx| {
217 let ptr = bcx.type_ptr();
218 let usize = bcx.type_ptr_sized_int();
219 let u8 = bcx.type_int(8);
220 }
221
222 @param_attrs |op| {
223 fn size_and_align<T>() -> Vec<Attribute> {
224 size_and_align_with(Some(core::mem::size_of::<T>()), core::mem::align_of::<T>())
225 }
226
227 fn size_and_align_with(size: Option<usize>, align: usize) -> Vec<Attribute> {
228 let mut vec = Vec::with_capacity(8);
229 vec.push(Attribute::NoAlias);
230 vec.push(Attribute::NoCapture);
231 vec.push(Attribute::NoUndef);
232 vec.push(Attribute::Align(align as u64));
233 if let Some(size) = size {
234 vec.push(Attribute::Dereferenceable(size as u64));
235 }
236 vec
237 }
238
239 let op = op.op();
240 let (inputs, outputs) = if let Some(info) = revm_bytecode::opcode::OPCODE_INFO[op as usize] {
241 (info.inputs(), info.outputs())
242 } else {
243 (0, 0)
244 };
245
246 let ecx_base = size_and_align::<revmc_context::EvmContext<'static>>();
247 let mut ecx = ecx_base.clone();
248 ecx.push(Attribute::Writable);
249 let mut ecx_ro = ecx_base;
250 ecx_ro.push(Attribute::ReadOnly);
251
252 let sp_base = size_and_align_with(None, core::mem::align_of::<revmc_context::EvmWord>());
253 let mut sp_dyn = sp_base.clone();
254 sp_dyn.push(Attribute::Writable);
255
256 let mut sp = sp_base;
257 let n_stack_words = inputs.max(outputs);
259 let size_of_word = core::mem::size_of::<revmc_context::EvmWord>();
260 sp.push(Attribute::Dereferenceable(size_of_word as u64 * n_stack_words as u64));
261 match (inputs, outputs) {
262 (0, 0) => sp.push(Attribute::ReadNone),
263 (0, 1..) => {
264 sp.push(Attribute::Writable);
265 sp.push(Attribute::WriteOnly);
266 sp.push(Attribute::Initializes(size_of_word as u64 * outputs as u64));
267 }
268 (1.., 0) => sp.push(Attribute::ReadOnly),
269 (1.., 1..) => sp.push(Attribute::Writable),
270 }
271 }
272
273 Panic = #[Cold] #[NoReturn] __revmc_builtin_panic(ptr, usize) None,
274 AssertSpecId = __revmc_builtin_assert_spec_id(@[ecx] ptr, u8) None,
275
276 Div = __revmc_builtin_div(@[sp] ptr) None,
277 SDiv = __revmc_builtin_sdiv(@[sp] ptr) None,
278 Mod = __revmc_builtin_mod(@[sp] ptr) None,
279 SMod = __revmc_builtin_smod(@[sp] ptr) None,
280 AddMod = __revmc_builtin_addmod(@[sp] ptr) None,
281 MulMod = __revmc_builtin_mulmod(@[sp] ptr) None,
282 Exp = __revmc_builtin_exp(@[ecx] ptr, @[sp] ptr) None,
283 ExpGas = __revmc_builtin_exp_gas(@[ecx] ptr, @[sp] ptr) None,
284 Keccak256 = __revmc_builtin_keccak256(@[ecx] ptr, @[sp] ptr) None,
285 Keccak256CC = __revmc_builtin_keccak256_cc(@[ecx] ptr, @[sp] ptr, usize, usize) None,
286 Balance = __revmc_builtin_balance(@[ecx] ptr, @[sp] ptr) None,
287 Origin = __revmc_builtin_origin(@[ecx_ro] ptr, @[sp] ptr) None,
288 CallDataLoad = __revmc_builtin_calldataload(@[ecx_ro] ptr, @[sp] ptr) None,
289 CallDataLoadC = __revmc_builtin_calldataload_c(@[ecx_ro] ptr, @[sp] ptr, usize) None,
290 CallDataCopy = __revmc_builtin_calldatacopy(@[ecx] ptr, @[sp] ptr) None,
291 CodeCopy = __revmc_builtin_codecopy(@[ecx] ptr, @[sp] ptr) None,
292 GasPrice = __revmc_builtin_gas_price(@[ecx_ro] ptr, @[sp] ptr) None,
293 ExtCodeSize = __revmc_builtin_extcodesize(@[ecx] ptr, @[sp] ptr) None,
294 ExtCodeCopy = __revmc_builtin_extcodecopy(@[ecx] ptr, @[sp] ptr) None,
295 ReturnDataCopy = __revmc_builtin_returndatacopy(@[ecx] ptr, @[sp] ptr) None,
296 ExtCodeHash = __revmc_builtin_extcodehash(@[ecx] ptr, @[sp] ptr) None,
297 BlockHash = __revmc_builtin_blockhash(@[ecx] ptr, @[sp] ptr) None,
298 Coinbase = __revmc_builtin_coinbase(@[ecx_ro] ptr, @[sp] ptr) None,
299 Timestamp = __revmc_builtin_timestamp(@[ecx_ro] ptr, @[sp] ptr) None,
300 Number = __revmc_builtin_number(@[ecx_ro] ptr, @[sp] ptr) None,
301 Difficulty = __revmc_builtin_difficulty(@[ecx_ro] ptr, @[sp] ptr) None,
302 GasLimit = __revmc_builtin_gaslimit(@[ecx_ro] ptr, @[sp] ptr) None,
303 ChainId = __revmc_builtin_chainid(@[ecx_ro] ptr, @[sp] ptr) None,
304 SelfBalance = __revmc_builtin_self_balance(@[ecx] ptr, @[sp] ptr) None,
305 Basefee = __revmc_builtin_basefee(@[ecx_ro] ptr, @[sp] ptr) None,
306 BlobHash = __revmc_builtin_blob_hash(@[ecx_ro] ptr, @[sp] ptr) None,
307 BlobBaseFee = __revmc_builtin_blob_base_fee(@[ecx_ro] ptr, @[sp] ptr) None,
308 SlotNum = __revmc_builtin_slot_num(@[ecx_ro] ptr, @[sp] ptr) None,
309 Mresize = __revmc_builtin_mresize(@[ecx] ptr, usize) None,
310 Sload = __revmc_builtin_sload(@[ecx] ptr, @[sp] ptr) None,
311 SloadC = __revmc_builtin_sload_c(@[ecx] ptr, @[sp] ptr, usize) None,
312 Sstore = __revmc_builtin_sstore(@[ecx] ptr, @[sp] ptr) None,
313 Tload = __revmc_builtin_tload(@[ecx] ptr, @[sp] ptr) None,
314 Tstore = __revmc_builtin_tstore(@[ecx] ptr, @[sp] ptr) None,
315 Mcopy = __revmc_builtin_mcopy(@[ecx] ptr, @[sp] ptr) None,
316 Log = __revmc_builtin_log(@[ecx] ptr, @[sp_dyn] ptr, u8) None,
317
318 Create = __revmc_builtin_create(@[ecx] ptr, @[sp_dyn] ptr, u8) None,
319 Call = __revmc_builtin_call(@[ecx] ptr, @[sp_dyn] ptr, u8) None,
320 DoReturn = #[NoReturn] __revmc_builtin_do_return(@[ecx] ptr, @[sp] ptr, u8) None,
321 DoReturnCC = #[NoReturn] __revmc_builtin_do_return_cc(@[ecx] ptr, usize, usize, u8) None,
322 SelfDestruct = #[NoReturn] __revmc_builtin_selfdestruct(@[ecx] ptr, @[sp] ptr) None,
323}
324
325impl Builtin {
326 pub const fn call_conv(self) -> CallConv {
327 match self {
328 Self::Mresize => CallConv::Cold,
329 _ => CallConv::Default,
330 }
331 }
332}