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
130 pub const fn name(self) -> &'static str {
131 match self {
132 $(Self::$ident => stringify!($name),)*
133 }
134 }
135
136 pub fn addr(self) -> usize {
137 match self {
138 $(Self::$ident => crate::$name as *const () as usize,)*
139 }
140 }
141
142 pub fn ret<B: TypeMethods>(self, $bcx: &mut B) -> Option<B::Type> {
143 $($types_init)*
144 match self {
145 $(Self::$ident => $ret,)*
146 }
147 }
148
149 pub fn params<B: TypeMethods>(self, $bcx: &mut B) -> Vec<B::Type> {
150 $($types_init)*
151 match self {
152 $(Self::$ident => vec![$($params),*],)*
153 }
154 }
155
156 pub fn attrs(self) -> &'static [Attribute] {
157 #[allow(unused_imports)]
158 use Attribute::*;
159 match self {
160 $(Self::$ident => &[$($attr),*]),*
161 }
162 }
163
164 #[allow(non_upper_case_globals)]
165 pub fn param_attrs(self) -> Vec<Vec<Attribute>> {
166 #[allow(unused_imports)]
167 use Attribute::*;
168 let $op = self;
169 let default = || vec![Attribute::NoUndef];
170 $($attrs_init)*
171 match self {
172 $(Self::$ident => vec![$(builtins!(@param_attr default $($param_attr)?)),*]),*
173 }
174 }
175
176 fn op(self) -> u8 {
177 use revm_bytecode::opcode::*;
178
179 const _0_0: u8 = STOP;
181 const _0_1: u8 = PUSH0;
182 const _1_0: u8 = POP;
183
184 const PANIC: u8 = _0_0;
185 const ASSERTSPECID: u8 = _0_0;
186
187 const EXPGAS: u8 = _1_0;
188 const KECCAK256CC: u8 = _0_1;
189
190 const CALLDATALOADC: u8 = _0_1;
191 const SLOADC: u8 = _0_1;
192
193 const MRESIZE: u8 = _0_0;
194
195 const LOG: u8 = _0_0;
196 const DORETURN: u8 = RETURN;
197 const DORETURNCC: u8 = _0_0;
198
199 match self {
200 $(Self::$ident => [<$ident:upper>]),*
201 }
202 }
203 }
204 }};
205}
206
207builtins! {
208 @types |bcx| {
209 let ptr = bcx.type_ptr();
210 let usize = bcx.type_ptr_sized_int();
211 let u8 = bcx.type_int(8);
212 }
213
214 @param_attrs |op| {
215 fn size_and_align<T>() -> Vec<Attribute> {
216 size_and_align_with(Some(core::mem::size_of::<T>()), core::mem::align_of::<T>())
217 }
218
219 fn size_and_align_with(size: Option<usize>, align: usize) -> Vec<Attribute> {
220 let mut vec = Vec::with_capacity(8);
221 vec.push(Attribute::NoAlias);
222 vec.push(Attribute::NoCapture);
223 vec.push(Attribute::NoUndef);
224 vec.push(Attribute::Align(align as u64));
225 if let Some(size) = size {
226 vec.push(Attribute::Dereferenceable(size as u64));
227 }
228 vec
229 }
230
231 let op = op.op();
232 let (inputs, outputs) = if let Some(info) = revm_bytecode::opcode::OPCODE_INFO[op as usize] {
233 (info.inputs(), info.outputs())
234 } else {
235 (0, 0)
236 };
237
238 let ecx_base = size_and_align::<revmc_context::EvmContext<'static>>();
239 let mut ecx = ecx_base.clone();
240 ecx.push(Attribute::Writable);
241 let mut ecx_ro = ecx_base;
242 ecx_ro.push(Attribute::ReadOnly);
243
244 let sp_base = size_and_align_with(None, core::mem::align_of::<revmc_context::EvmWord>());
245 let mut sp_dyn = sp_base.clone();
246 sp_dyn.push(Attribute::Writable);
247
248 let mut sp = sp_base;
249 let n_stack_words = inputs.max(outputs);
251 let size_of_word = core::mem::size_of::<revmc_context::EvmWord>();
252 sp.push(Attribute::Dereferenceable(size_of_word as u64 * n_stack_words as u64));
253 match (inputs, outputs) {
254 (0, 0) => sp.push(Attribute::ReadNone),
255 (0, 1..) => {
256 sp.push(Attribute::Writable);
257 sp.push(Attribute::WriteOnly);
258 sp.push(Attribute::Initializes(size_of_word as u64 * outputs as u64));
259 }
260 (1.., 0) => sp.push(Attribute::ReadOnly),
261 (1.., 1..) => sp.push(Attribute::Writable),
262 }
263 }
264
265 Panic = #[Cold] #[NoReturn] __revmc_builtin_panic(ptr, usize) None,
266 AssertSpecId = __revmc_builtin_assert_spec_id(@[ecx] ptr, u8) None,
267
268 Div = __revmc_builtin_div(@[sp] ptr) None,
269 SDiv = __revmc_builtin_sdiv(@[sp] ptr) None,
270 Mod = __revmc_builtin_mod(@[sp] ptr) None,
271 SMod = __revmc_builtin_smod(@[sp] ptr) None,
272 AddMod = __revmc_builtin_addmod(@[sp] ptr) None,
273 MulMod = __revmc_builtin_mulmod(@[sp] ptr) None,
274 Exp = __revmc_builtin_exp(@[ecx] ptr, @[sp] ptr) None,
275 ExpGas = __revmc_builtin_exp_gas(@[ecx] ptr, @[sp] ptr) None,
276 Keccak256 = __revmc_builtin_keccak256(@[ecx] ptr, @[sp] ptr) None,
277 Keccak256CC = __revmc_builtin_keccak256_cc(@[ecx] ptr, @[sp] ptr, usize, usize) None,
278 Balance = __revmc_builtin_balance(@[ecx] ptr, @[sp] ptr) None,
279 Origin = __revmc_builtin_origin(@[ecx_ro] ptr, @[sp] ptr) None,
280 CallDataLoad = __revmc_builtin_calldataload(@[ecx_ro] ptr, @[sp] ptr) None,
281 CallDataLoadC = __revmc_builtin_calldataload_c(@[ecx_ro] ptr, @[sp] ptr, usize) None,
282 CallDataCopy = __revmc_builtin_calldatacopy(@[ecx] ptr, @[sp] ptr) None,
283 CodeCopy = __revmc_builtin_codecopy(@[ecx] ptr, @[sp] ptr) None,
284 GasPrice = __revmc_builtin_gas_price(@[ecx_ro] ptr, @[sp] ptr) None,
285 ExtCodeSize = __revmc_builtin_extcodesize(@[ecx] ptr, @[sp] ptr) None,
286 ExtCodeCopy = __revmc_builtin_extcodecopy(@[ecx] ptr, @[sp] ptr) None,
287 ReturnDataCopy = __revmc_builtin_returndatacopy(@[ecx] ptr, @[sp] ptr) None,
288 ExtCodeHash = __revmc_builtin_extcodehash(@[ecx] ptr, @[sp] ptr) None,
289 BlockHash = __revmc_builtin_blockhash(@[ecx] ptr, @[sp] ptr) None,
290 Coinbase = __revmc_builtin_coinbase(@[ecx_ro] ptr, @[sp] ptr) None,
291 Timestamp = __revmc_builtin_timestamp(@[ecx_ro] ptr, @[sp] ptr) None,
292 Number = __revmc_builtin_number(@[ecx_ro] ptr, @[sp] ptr) None,
293 Difficulty = __revmc_builtin_difficulty(@[ecx_ro] ptr, @[sp] ptr) None,
294 GasLimit = __revmc_builtin_gaslimit(@[ecx_ro] ptr, @[sp] ptr) None,
295 ChainId = __revmc_builtin_chainid(@[ecx_ro] ptr, @[sp] ptr) None,
296 SelfBalance = __revmc_builtin_self_balance(@[ecx] ptr, @[sp] ptr) None,
297 Basefee = __revmc_builtin_basefee(@[ecx_ro] ptr, @[sp] ptr) None,
298 BlobHash = __revmc_builtin_blob_hash(@[ecx_ro] ptr, @[sp] ptr) None,
299 BlobBaseFee = __revmc_builtin_blob_base_fee(@[ecx_ro] ptr, @[sp] ptr) None,
300 SlotNum = __revmc_builtin_slot_num(@[ecx_ro] ptr, @[sp] ptr) None,
301 Mresize = __revmc_builtin_mresize(@[ecx] ptr, usize) None,
302 Sload = __revmc_builtin_sload(@[ecx] ptr, @[sp] ptr) None,
303 SloadC = __revmc_builtin_sload_c(@[ecx] ptr, @[sp] ptr, usize) None,
304 Sstore = __revmc_builtin_sstore(@[ecx] ptr, @[sp] ptr) None,
305 Tload = __revmc_builtin_tload(@[ecx] ptr, @[sp] ptr) None,
306 Tstore = __revmc_builtin_tstore(@[ecx] ptr, @[sp] ptr) None,
307 Mcopy = __revmc_builtin_mcopy(@[ecx] ptr, @[sp] ptr) None,
308 Log = __revmc_builtin_log(@[ecx] ptr, @[sp_dyn] ptr, u8) None,
309
310 Create = __revmc_builtin_create(@[ecx] ptr, @[sp_dyn] ptr, u8) None,
311 Call = __revmc_builtin_call(@[ecx] ptr, @[sp_dyn] ptr, u8) None,
312 DoReturn = #[NoReturn] __revmc_builtin_do_return(@[ecx] ptr, @[sp] ptr, u8) None,
313 DoReturnCC = #[NoReturn] __revmc_builtin_do_return_cc(@[ecx] ptr, usize, usize, u8) None,
314 SelfDestruct = #[NoReturn] __revmc_builtin_selfdestruct(@[ecx] ptr, @[sp] ptr) None,
315}
316
317impl Builtin {
318 pub const fn call_conv(self) -> CallConv {
319 match self {
320 Self::Mresize => CallConv::Cold,
321 _ => CallConv::Default,
322 }
323 }
324}