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
10impl<B: Backend> Default for Builtins<B> {
11 fn default() -> Self {
12 Self::new()
13 }
14}
15
16impl<B: Backend> Builtins<B> {
17 pub fn new() -> Self {
19 Self([None; Builtin::COUNT])
20 }
21
22 pub fn clear(&mut self) {
24 *self = Self::new();
25 }
26
27 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, ¶ms, 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 #[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 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}