1pub(crate) use self::stack::Stack;
2use self::{
3 instr_ptr::InstructionPtr,
4 instrs::{dispatch_host_func, execute_instrs},
5 stack::CallFrame,
6};
7use crate::{
8 engine::{
9 CallParams,
10 CallResults,
11 EngineInner,
12 ResumableCallBase,
13 ResumableCallHostTrap,
14 ResumableCallOutOfFuel,
15 },
16 func::HostFuncEntity,
17 ir::{Slot, SlotSpan},
18 store::CallHooks,
19 CallHook,
20 Error,
21 Func,
22 FuncEntity,
23 Store,
24 StoreContextMut,
25};
26
27use super::{code_map::CodeMap, ResumableError};
28
29mod cache;
30mod instr_ptr;
31mod instrs;
32mod stack;
33
34impl EngineInner {
35 pub fn execute_func<T, Results>(
43 &self,
44 ctx: StoreContextMut<T>,
45 func: &Func,
46 params: impl CallParams,
47 results: Results,
48 ) -> Result<<Results as CallResults>::Results, Error>
49 where
50 Results: CallResults,
51 {
52 let mut stack = self.stacks.lock().reuse_or_new();
53 let results = EngineExecutor::new(&self.code_map, &mut stack)
54 .execute_root_func(ctx.store, func, params, results)
55 .map_err(|error| match error.into_resumable() {
56 Ok(error) => error.into_error(),
57 Err(error) => error,
58 });
59 self.stacks.lock().recycle(stack);
60 results
61 }
62
63 pub fn execute_func_resumable<T, Results>(
71 &self,
72 ctx: StoreContextMut<T>,
73 func: &Func,
74 params: impl CallParams,
75 results: Results,
76 ) -> Result<ResumableCallBase<<Results as CallResults>::Results>, Error>
77 where
78 Results: CallResults,
79 {
80 let store = ctx.store;
81 let mut stack = self.stacks.lock().reuse_or_new();
82 let results = EngineExecutor::new(&self.code_map, &mut stack)
83 .execute_root_func(store, func, params, results);
84 match results {
85 Ok(results) => {
86 self.stacks.lock().recycle(stack);
87 Ok(ResumableCallBase::Finished(results))
88 }
89 Err(error) => match error.into_resumable() {
90 Ok(ResumableError::HostTrap(error)) => {
91 let host_func = *error.host_func();
92 let caller_results = *error.caller_results();
93 let host_error = error.into_error();
94 Ok(ResumableCallBase::HostTrap(ResumableCallHostTrap::new(
95 store.engine().clone(),
96 *func,
97 host_func,
98 host_error,
99 caller_results,
100 stack,
101 )))
102 }
103 Ok(ResumableError::OutOfFuel(error)) => {
104 let required_fuel = error.required_fuel();
105 Ok(ResumableCallBase::OutOfFuel(ResumableCallOutOfFuel::new(
106 store.engine().clone(),
107 *func,
108 stack,
109 required_fuel,
110 )))
111 }
112 Err(error) => {
113 self.stacks.lock().recycle(stack);
114 Err(error)
115 }
116 },
117 }
118 }
119
120 pub fn resume_func_host_trap<T, Results>(
128 &self,
129 ctx: StoreContextMut<T>,
130 mut invocation: ResumableCallHostTrap,
131 params: impl CallParams,
132 results: Results,
133 ) -> Result<ResumableCallBase<<Results as CallResults>::Results>, Error>
134 where
135 Results: CallResults,
136 {
137 let caller_results = invocation.caller_results();
138 let mut executor = EngineExecutor::new(&self.code_map, invocation.common.stack_mut());
139 let results = executor.resume_func_host_trap(ctx.store, params, caller_results, results);
140 match results {
141 Ok(results) => {
142 self.stacks.lock().recycle(invocation.common.take_stack());
143 Ok(ResumableCallBase::Finished(results))
144 }
145 Err(error) => match error.into_resumable() {
146 Ok(ResumableError::HostTrap(error)) => {
147 let host_func = *error.host_func();
148 let caller_results = *error.caller_results();
149 invocation.update(host_func, error.into_error(), caller_results);
150 Ok(ResumableCallBase::HostTrap(invocation))
151 }
152 Ok(ResumableError::OutOfFuel(error)) => {
153 let required_fuel = error.required_fuel();
154 let invocation = invocation.update_to_out_of_fuel(required_fuel);
155 Ok(ResumableCallBase::OutOfFuel(invocation))
156 }
157 Err(error) => {
158 self.stacks.lock().recycle(invocation.common.take_stack());
159 Err(error)
160 }
161 },
162 }
163 }
164
165 pub fn resume_func_out_of_fuel<T, Results>(
173 &self,
174 ctx: StoreContextMut<T>,
175 mut invocation: ResumableCallOutOfFuel,
176 results: Results,
177 ) -> Result<ResumableCallBase<<Results as CallResults>::Results>, Error>
178 where
179 Results: CallResults,
180 {
181 let mut executor = EngineExecutor::new(&self.code_map, invocation.common.stack_mut());
182 let results = executor.resume_func_out_of_fuel(ctx.store, results);
183 match results {
184 Ok(results) => {
185 self.stacks.lock().recycle(invocation.common.take_stack());
186 Ok(ResumableCallBase::Finished(results))
187 }
188 Err(error) => match error.into_resumable() {
189 Ok(ResumableError::HostTrap(error)) => {
190 let host_func = *error.host_func();
191 let caller_results = *error.caller_results();
192 let invocation = invocation.update_to_host_trap(
193 host_func,
194 error.into_error(),
195 caller_results,
196 );
197 Ok(ResumableCallBase::HostTrap(invocation))
198 }
199 Ok(ResumableError::OutOfFuel(error)) => {
200 invocation.update(error.required_fuel());
201 Ok(ResumableCallBase::OutOfFuel(invocation))
202 }
203 Err(error) => {
204 self.stacks.lock().recycle(invocation.common.take_stack());
205 Err(error)
206 }
207 },
208 }
209 }
210}
211
212#[derive(Debug)]
214pub struct EngineExecutor<'engine> {
215 code_map: &'engine CodeMap,
217 stack: &'engine mut Stack,
219}
220
221#[inline]
223fn do_nothing<T>(_: &mut T) {}
224
225impl<'engine> EngineExecutor<'engine> {
226 fn new(code_map: &'engine CodeMap, stack: &'engine mut Stack) -> Self {
228 Self { code_map, stack }
229 }
230
231 fn execute_root_func<T, Results>(
241 &mut self,
242 store: &mut Store<T>,
243 func: &Func,
244 params: impl CallParams,
245 results: Results,
246 ) -> Result<<Results as CallResults>::Results, Error>
247 where
248 Results: CallResults,
249 {
250 self.stack.reset();
251 match store.inner.resolve_func(func) {
252 FuncEntity::Wasm(wasm_func) => {
253 let len_results = results.len_results();
255 self.stack.values.extend_by(len_results, do_nothing)?;
256 let instance = *wasm_func.instance();
257 let engine_func = wasm_func.func_body();
258 let compiled_func = self
259 .code_map
260 .get(Some(store.inner.fuel_mut()), engine_func)?;
261 let (mut uninit_params, offsets) = self
262 .stack
263 .values
264 .alloc_call_frame(compiled_func, do_nothing)?;
265 for value in params.call_params() {
266 unsafe { uninit_params.init_next(value) };
267 }
268 uninit_params.init_zeroes();
269 self.stack.calls.push(
270 CallFrame::new(
271 InstructionPtr::new(compiled_func.instrs().as_ptr()),
272 offsets,
273 SlotSpan::new(Slot::from(0)),
274 ),
275 Some(instance),
276 )?;
277 store.invoke_call_hook(CallHook::CallingWasm)?;
278 self.execute_func(store)?;
279 store.invoke_call_hook(CallHook::ReturningFromWasm)?;
280 }
281 FuncEntity::Host(host_func) => {
282 let len_params = host_func.len_params();
287 let len_results = host_func.len_results();
288 let max_inout = len_params.max(len_results);
289 let uninit = self
290 .stack
291 .values
292 .extend_by(usize::from(max_inout), do_nothing)?;
293 for (uninit, param) in uninit.iter_mut().zip(params.call_params()) {
294 uninit.write(param);
295 }
296 let host_func = *host_func;
297 self.dispatch_host_func(store, host_func)?;
298 }
299 };
300 let results = self.write_results_back(results);
301 Ok(results)
302 }
303
304 fn resume_func_host_trap<T, Results>(
314 &mut self,
315 store: &mut Store<T>,
316 params: impl CallParams,
317 caller_results: SlotSpan,
318 results: Results,
319 ) -> Result<<Results as CallResults>::Results, Error>
320 where
321 Results: CallResults,
322 {
323 let caller = self
324 .stack
325 .calls
326 .peek()
327 .expect("must have caller call frame on stack upon function resumption");
328 let mut caller_sp = unsafe { self.stack.values.stack_ptr_at(caller.base_offset()) };
329 let call_params = params.call_params();
330 let len_params = call_params.len();
331 for (result, param) in caller_results.iter_sized(len_params).zip(call_params) {
332 unsafe { caller_sp.set(result, param) };
333 }
334 self.execute_func(store)?;
335 let results = self.write_results_back(results);
336 Ok(results)
337 }
338
339 fn resume_func_out_of_fuel<T, Results>(
348 &mut self,
349 store: &mut Store<T>,
350 results: Results,
351 ) -> Result<<Results as CallResults>::Results, Error>
352 where
353 Results: CallResults,
354 {
355 self.execute_func(store)?;
356 let results = self.write_results_back(results);
357 Ok(results)
358 }
359
360 #[inline(always)]
366 fn execute_func<T>(&mut self, store: &mut Store<T>) -> Result<(), Error> {
367 execute_instrs(store.prune(), self.stack, self.code_map)
368 }
369
370 #[inline(always)]
372 fn dispatch_host_func<T>(
373 &mut self,
374 store: &mut Store<T>,
375 host_func: HostFuncEntity,
376 ) -> Result<(), Error> {
377 dispatch_host_func(
378 store.prune(),
379 &mut self.stack.values,
380 host_func,
381 None,
382 CallHooks::Ignore,
383 )?;
384 Ok(())
385 }
386
387 #[inline(always)]
397 fn write_results_back<Results>(&mut self, results: Results) -> <Results as CallResults>::Results
398 where
399 Results: CallResults,
400 {
401 let len_results = results.len_results();
402 results.call_results(&self.stack.values.as_slice()[..len_results])
403 }
404}