wasmer_compiler_cranelift/trampoline/
function_call.rs

1// This file contains code from external sources.
2// Attributions: https://github.com/wasmerio/wasmer/blob/main/docs/ATTRIBUTIONS.md
3
4//! A trampoline generator for calling Wasm functions easily.
5//!
6//! That way, you can start calling Wasm functions doing things like:
7//! ```ignore
8//! let my_func = instance.exports.get("func");
9//! my_func.call([1, 2])
10//! ```
11use crate::translator::{compiled_function_unwind_info, signature_to_cranelift_ir};
12use cranelift_codegen::{
13    ir::{self, InstBuilder},
14    isa::TargetIsa,
15    Context,
16};
17use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext};
18use std::mem;
19use wasmer_compiler::types::function::FunctionBody;
20use wasmer_types::{CompileError, FunctionType};
21
22/// Create a trampoline for invoking a WebAssembly function.
23pub fn make_trampoline_function_call(
24    isa: &dyn TargetIsa,
25    fn_builder_ctx: &mut FunctionBuilderContext,
26    func_type: &FunctionType,
27) -> Result<FunctionBody, CompileError> {
28    let pointer_type = isa.pointer_type();
29    let frontend_config = isa.frontend_config();
30    let signature = signature_to_cranelift_ir(func_type, frontend_config);
31    let mut wrapper_sig = ir::Signature::new(frontend_config.default_call_conv);
32
33    // Add the callee `vmctx` parameter.
34    wrapper_sig.params.push(ir::AbiParam::special(
35        pointer_type,
36        ir::ArgumentPurpose::VMContext,
37    ));
38
39    // Add the `callee_address` parameter.
40    wrapper_sig.params.push(ir::AbiParam::new(pointer_type));
41
42    // Add the `values_vec` parameter.
43    wrapper_sig.params.push(ir::AbiParam::new(pointer_type));
44
45    let mut context = Context::new();
46    context.func = ir::Function::with_name_signature(ir::UserFuncName::user(0, 0), wrapper_sig);
47
48    let value_size = mem::size_of::<u128>();
49    {
50        let mut builder = FunctionBuilder::new(&mut context.func, fn_builder_ctx);
51        let block0 = builder.create_block();
52
53        builder.append_block_params_for_function_params(block0);
54        builder.switch_to_block(block0);
55        builder.seal_block(block0);
56
57        let (vmctx_ptr_val, callee_value, values_vec_ptr_val) = {
58            let params = builder.func.dfg.block_params(block0);
59            (params[0], params[1], params[2])
60        };
61
62        // Load the argument values out of `values_vec`.
63        let mflags = ir::MemFlags::trusted();
64        let callee_args = signature
65            .params
66            .iter()
67            .enumerate()
68            .map(|(i, r)| {
69                match i {
70                    0 => vmctx_ptr_val,
71                    _ =>
72                    // i - 1 because vmctx is not passed through `values_vec`.
73                    {
74                        builder.ins().load(
75                            r.value_type,
76                            mflags,
77                            values_vec_ptr_val,
78                            ((i - 1) * value_size) as i32,
79                        )
80                    }
81                }
82            })
83            .collect::<Vec<_>>();
84
85        let new_sig = builder.import_signature(signature);
86
87        let call = builder
88            .ins()
89            .call_indirect(new_sig, callee_value, &callee_args);
90
91        let results = builder.func.dfg.inst_results(call).to_vec();
92
93        // Store the return values into `values_vec`.
94        let mflags = ir::MemFlags::trusted();
95        for (i, r) in results.iter().enumerate() {
96            builder
97                .ins()
98                .store(mflags, *r, values_vec_ptr_val, (i * value_size) as i32);
99        }
100
101        builder.ins().return_(&[]);
102        builder.finalize()
103    }
104
105    let mut code_buf = Vec::new();
106
107    context
108        .compile_and_emit(isa, &mut code_buf, &mut Default::default())
109        .map_err(|error| CompileError::Codegen(error.inner.to_string()))?;
110
111    let unwind_info = compiled_function_unwind_info(isa, &context)?.maybe_into_to_windows_unwind();
112
113    Ok(FunctionBody {
114        body: code_buf,
115        unwind_info,
116    })
117}