Get control flow (at least according to the llvm verifier) working.

Next up:
- Importing vm intrinsics.
This commit is contained in:
Lachlan Sneff
2019-03-01 15:48:43 -08:00
parent b80252e165
commit 3717c5720d
7 changed files with 408 additions and 105 deletions

View File

@ -11,14 +11,17 @@ use libc::{
};
use std::{
ffi::CString,
mem,
ptr::{self, NonNull},
};
use wasmer_runtime_core::{
backend::FuncResolver,
backend::{FuncResolver, ProtectedCaller, Token, UserTrapper},
error::RuntimeResult,
export::Context,
module::{ModuleInfo, ModuleInner},
structures::TypedIndex,
types::LocalFuncIndex,
vm,
types::{FuncIndex, FuncSig, LocalFuncIndex, LocalOrImport, SigIndex, Type, Value},
vm::{self, ImportBacking},
};
#[repr(C)]
@ -155,7 +158,7 @@ pub struct LLVMBackend {
}
impl LLVMBackend {
pub fn new(module: Module, intrinsics: Intrinsics) -> Self {
pub fn new(module: Module, intrinsics: Intrinsics) -> (Self, LLVMProtectedCaller) {
Target::initialize_x86(&InitializationConfig {
asm_parser: true,
asm_printer: true,
@ -198,10 +201,13 @@ impl LLVMBackend {
panic!("failed to load object")
}
Self {
module,
memory_buffer,
}
(
Self {
module,
memory_buffer,
},
LLVMProtectedCaller { module },
)
}
pub fn get_func(
@ -239,6 +245,137 @@ impl FuncResolver for LLVMBackend {
}
}
struct Placeholder;
unsafe impl Send for LLVMProtectedCaller {}
unsafe impl Sync for LLVMProtectedCaller {}
pub struct LLVMProtectedCaller {
module: *mut LLVMModule,
}
impl ProtectedCaller for LLVMProtectedCaller {
fn call(
&self,
module: &ModuleInner,
func_index: FuncIndex,
params: &[Value],
import_backing: &ImportBacking,
vmctx: *mut vm::Ctx,
_: Token,
) -> RuntimeResult<Vec<Value>> {
let (func_ptr, ctx, signature, sig_index) =
get_func_from_index(&module, import_backing, func_index);
let vmctx_ptr = match ctx {
Context::External(external_vmctx) => external_vmctx,
Context::Internal => vmctx,
};
assert!(
signature.returns().len() <= 1,
"multi-value returns not yet supported"
);
assert!(
signature.check_param_value_types(params),
"incorrect signature"
);
let param_vec: Vec<u64> = params
.iter()
.map(|val| match val {
Value::I32(x) => *x as u64,
Value::I64(x) => *x as u64,
Value::F32(x) => x.to_bits() as u64,
Value::F64(x) => x.to_bits(),
})
.collect();
let mut return_vec = vec![0; signature.returns().len()];
let trampoline: unsafe extern "C" fn(*mut vm::Ctx, *const vm::Func, *const u64, *mut u64) = unsafe {
let name = if cfg!(target_os = "macos") {
format!("_trmp{}", sig_index.index())
} else {
format!("trmp{}", sig_index.index())
};
let c_str = CString::new(name).unwrap();
let symbol = get_func_symbol(self.module, c_str.as_ptr());
assert!(!symbol.is_null());
mem::transmute(symbol)
};
// Here we go.
unsafe {
trampoline(
vmctx_ptr,
func_ptr,
param_vec.as_ptr(),
return_vec.as_mut_ptr(),
);
}
Ok(return_vec
.iter()
.zip(signature.returns().iter())
.map(|(&x, ty)| match ty {
Type::I32 => Value::I32(x as i32),
Type::I64 => Value::I64(x as i64),
Type::F32 => Value::F32(f32::from_bits(x as u32)),
Type::F64 => Value::F64(f64::from_bits(x as u64)),
})
.collect())
}
fn get_early_trapper(&self) -> Box<dyn UserTrapper> {
Box::new(Placeholder)
}
}
impl UserTrapper for Placeholder {
unsafe fn do_early_trap(&self, msg: String) -> ! {
unimplemented!("do early trap: {}", msg)
}
}
fn get_func_from_index<'a>(
module: &'a ModuleInner,
import_backing: &ImportBacking,
func_index: FuncIndex,
) -> (*const vm::Func, Context, &'a FuncSig, SigIndex) {
let sig_index = *module
.info
.func_assoc
.get(func_index)
.expect("broken invariant, incorrect func index");
let (func_ptr, ctx) = match func_index.local_or_import(&module.info) {
LocalOrImport::Local(local_func_index) => (
module
.func_resolver
.get(&module, local_func_index)
.expect("broken invariant, func resolver not synced with module.exports")
.cast()
.as_ptr() as *const _,
Context::Internal,
),
LocalOrImport::Import(imported_func_index) => {
let imported_func = import_backing.imported_func(imported_func_index);
(
imported_func.func as *const _,
Context::External(imported_func.vmctx),
)
}
};
let signature = &module.info.signatures[sig_index];
(func_ptr, ctx, signature, sig_index)
}
unsafe fn disass_ptr(ptr: *const u8, size: usize, inst_count: usize) {
use capstone::arch::BuildsCapstone;
let mut cs = capstone::Capstone::new() // Call builder-pattern

View File

@ -24,6 +24,7 @@ use wasmparser::{
use crate::intrinsics::{CtxType, GlobalCache, Intrinsics};
use crate::read_info::type_to_type;
use crate::state::{ControlFrame, IfElseState, State};
use crate::trampolines::generate_trampolines;
fn func_sig_to_llvm(context: &Context, intrinsics: &Intrinsics, sig: &FuncSig) -> FunctionType {
let user_param_types = sig.params().iter().map(|&ty| type_to_llvm(intrinsics, ty));
@ -102,10 +103,19 @@ pub fn parse_function_bodies(
LocalFuncIndex::new(local_func_index),
locals_reader,
op_reader,
)?;
)
.map_err(|e| BinaryReaderError {
message: e.message,
offset: local_func_index,
})?;
}
// module.print_to_stderr();
generate_trampolines(info, &signatures, &module, &context, &builder, &intrinsics);
let pass_manager = PassManager::create_for_module();
pass_manager.add_verifier_pass();
pass_manager.add_function_inlining_pass();
pass_manager.add_promote_memory_to_register_pass();
pass_manager.add_cfg_simplification_pass();
@ -116,7 +126,6 @@ pub fn parse_function_bodies(
pass_manager.add_gvn_pass();
// pass_manager.add_new_gvn_pass();
pass_manager.add_aggressive_dce_pass();
// pass_manager.add_verifier_pass();
pass_manager.run_on_module(&module);
Ok((module, intrinsics))
@ -209,8 +218,34 @@ fn parse_function(
locals.push(alloca);
}
let mut unreachable_depth = 0;
for op in op_reader {
match op? {
let op = op?;
if !state.reachable {
match op {
Operator::Block { ty: _ } | Operator::Loop { ty: _ } | Operator::If { ty: _ } => {
unreachable_depth += 1;
continue;
}
Operator::Else => {
if unreachable_depth != 0 {
continue;
}
}
Operator::End => {
if unreachable_depth != 0 {
unreachable_depth -= 1;
continue;
}
}
_ => {
continue;
}
}
}
match op {
/***************************
* Control Flow instructions.
* https://github.com/sunfishcode/wasm-reference-manual/blob/master/WebAssembly.md#control-flow-instructions
@ -356,7 +391,7 @@ fn parse_function(
builder.build_switch(index.into_int_value(), default_frame.br_dest(), &cases[..]);
state.popn(res_len)?;
builder.build_unreachable();
state.reachable = false;
}
Operator::If { ty } => {
let current_block = builder.get_insert_block().ok_or(BinaryReaderError {
@ -440,8 +475,22 @@ fn parse_function(
builder.build_unconditional_branch(frame.code_after());
for phi in frame.phis().iter().rev() {
let value = state.pop1()?;
phi.add_incoming(&[(&value, &current_block)])
if phi.count_incoming() != 0 {
let value = state.pop1()?;
phi.add_incoming(&[(&value, &current_block)])
} else {
let basic_ty = phi.as_basic_value().get_type();
let placeholder_value = match basic_ty {
BasicTypeEnum::IntType(int_ty) => {
int_ty.const_int(0, false).as_basic_value_enum()
}
BasicTypeEnum::FloatType(float_ty) => {
float_ty.const_float(0.0).as_basic_value_enum()
}
_ => unimplemented!(),
};
phi.add_incoming(&[(&placeholder_value, &current_block)]);
}
}
}
@ -466,13 +515,40 @@ fn parse_function(
// Push each phi value to the value stack.
for phi in frame.phis() {
state.push1(phi.as_basic_value());
if phi.count_incoming() != 0 {
state.push1(phi.as_basic_value());
} else {
let basic_ty = phi.as_basic_value().get_type();
let placeholder_value = match basic_ty {
BasicTypeEnum::IntType(int_ty) => {
int_ty.const_int(0, false).as_basic_value_enum()
}
BasicTypeEnum::FloatType(float_ty) => {
float_ty.const_float(0.0).as_basic_value_enum()
}
_ => unimplemented!(),
};
state.push1(placeholder_value);
phi.as_instruction().erase_from_basic_block();
}
}
}
Operator::Return => {
let frame = state.outermost_frame()?;
let current_block = builder.get_insert_block().ok_or(BinaryReaderError {
message: "not currently in a block",
offset: -1isize as usize,
})?;
builder.build_unconditional_branch(frame.br_dest());
let phis = frame.phis().to_vec();
for phi in phis.iter() {
let arg = state.pop1()?;
phi.add_incoming(&[(&arg, &current_block)]);
}
state.reachable = false;
}
@ -508,7 +584,7 @@ fn parse_function(
Operator::F32Const { value } => {
let f = intrinsics
.f32_ty
.const_float(f64::from_bits(value.bits() as u64));
.const_float(f32::from_bits(value.bits()) as f64);
state.push1(f);
}
Operator::F64Const { value } => {
@ -562,15 +638,20 @@ fn parse_function(
Operator::Select => {
let (v1, v2, cond) = state.pop3()?;
let cond = cond.into_int_value();
let res = builder.build_select(cond, v1, v2, &state.var_name());
let cond_value = builder.build_int_compare(
IntPredicate::NE,
cond.into_int_value(),
intrinsics.i32_zero,
&state.var_name(),
);
let res = builder.build_select(cond_value, v1, v2, &state.var_name());
state.push1(res);
}
Operator::Call { function_index } => {
let func_index = FuncIndex::new(function_index as usize);
let sigindex = info.func_assoc[func_index];
let llvm_sig = signatures[sigindex];
let func_sig = &info.signatures[sig_index];
let func_sig = &info.signatures[sigindex];
let call_site = match func_index.local_or_import(info) {
LocalOrImport::Local(local_func_index) => {
@ -710,7 +791,6 @@ fn parse_function(
let args: Vec<_> = std::iter::once(ctx_ptr)
.chain(pushed_args.into_iter())
.collect();
println!("args: {:?}", args);
let typed_func_ptr = builder.build_pointer_cast(
func_ptr,
@ -948,22 +1028,24 @@ fn parse_function(
}
Operator::I32Eqz => {
let input = state.pop1()?.into_int_value();
let res = builder.build_int_compare(
let cond = builder.build_int_compare(
IntPredicate::EQ,
input,
intrinsics.i32_zero,
&state.var_name(),
);
let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name());
state.push1(res);
}
Operator::I64Eqz => {
let input = state.pop1()?.into_int_value();
let res = builder.build_int_compare(
let cond = builder.build_int_compare(
IntPredicate::EQ,
input,
intrinsics.i64_zero,
&state.var_name(),
);
let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name());
state.push1(res);
}
@ -1170,61 +1252,71 @@ fn parse_function(
Operator::I32Eq | Operator::I64Eq => {
let (v1, v2) = state.pop2()?;
let (v1, v2) = (v1.into_int_value(), v2.into_int_value());
let res = builder.build_int_compare(IntPredicate::EQ, v1, v2, &state.var_name());
let cond = builder.build_int_compare(IntPredicate::EQ, v1, v2, &state.var_name());
let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name());
state.push1(res);
}
Operator::I32Ne | Operator::I64Ne => {
let (v1, v2) = state.pop2()?;
let (v1, v2) = (v1.into_int_value(), v2.into_int_value());
let res = builder.build_int_compare(IntPredicate::NE, v1, v2, &state.var_name());
let cond = builder.build_int_compare(IntPredicate::NE, v1, v2, &state.var_name());
let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name());
state.push1(res);
}
Operator::I32LtS | Operator::I64LtS => {
let (v1, v2) = state.pop2()?;
let (v1, v2) = (v1.into_int_value(), v2.into_int_value());
let res = builder.build_int_compare(IntPredicate::SLT, v1, v2, &state.var_name());
let cond = builder.build_int_compare(IntPredicate::SLT, v1, v2, &state.var_name());
let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name());
state.push1(res);
}
Operator::I32LtU | Operator::I64LtU => {
let (v1, v2) = state.pop2()?;
let (v1, v2) = (v1.into_int_value(), v2.into_int_value());
let res = builder.build_int_compare(IntPredicate::ULT, v1, v2, &state.var_name());
let cond = builder.build_int_compare(IntPredicate::ULT, v1, v2, &state.var_name());
let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name());
state.push1(res);
}
Operator::I32LeS | Operator::I64LeS => {
let (v1, v2) = state.pop2()?;
let (v1, v2) = (v1.into_int_value(), v2.into_int_value());
let res = builder.build_int_compare(IntPredicate::SLE, v1, v2, &state.var_name());
let cond = builder.build_int_compare(IntPredicate::SLE, v1, v2, &state.var_name());
let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name());
state.push1(res);
}
Operator::I32LeU | Operator::I64LeU => {
let (v1, v2) = state.pop2()?;
let (v1, v2) = (v1.into_int_value(), v2.into_int_value());
let res = builder.build_int_compare(IntPredicate::ULE, v1, v2, &state.var_name());
let cond = builder.build_int_compare(IntPredicate::ULE, v1, v2, &state.var_name());
let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name());
state.push1(res);
}
Operator::I32GtS | Operator::I64GtS => {
let (v1, v2) = state.pop2()?;
let (v1, v2) = (v1.into_int_value(), v2.into_int_value());
let res = builder.build_int_compare(IntPredicate::SGT, v1, v2, &state.var_name());
let cond = builder.build_int_compare(IntPredicate::SGT, v1, v2, &state.var_name());
let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name());
state.push1(res);
}
Operator::I32GtU | Operator::I64GtU => {
let (v1, v2) = state.pop2()?;
let (v1, v2) = (v1.into_int_value(), v2.into_int_value());
let res = builder.build_int_compare(IntPredicate::UGT, v1, v2, &state.var_name());
let cond = builder.build_int_compare(IntPredicate::UGT, v1, v2, &state.var_name());
let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name());
state.push1(res);
}
Operator::I32GeS | Operator::I64GeS => {
let (v1, v2) = state.pop2()?;
let (v1, v2) = (v1.into_int_value(), v2.into_int_value());
let res = builder.build_int_compare(IntPredicate::SGE, v1, v2, &state.var_name());
let cond = builder.build_int_compare(IntPredicate::SGE, v1, v2, &state.var_name());
let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name());
state.push1(res);
}
Operator::I32GeU | Operator::I64GeU => {
let (v1, v2) = state.pop2()?;
let (v1, v2) = (v1.into_int_value(), v2.into_int_value());
let res = builder.build_int_compare(IntPredicate::UGE, v1, v2, &state.var_name());
let cond = builder.build_int_compare(IntPredicate::UGE, v1, v2, &state.var_name());
let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name());
state.push1(res);
}
@ -1235,43 +1327,49 @@ fn parse_function(
Operator::F32Eq | Operator::F64Eq => {
let (v1, v2) = state.pop2()?;
let (v1, v2) = (v1.into_float_value(), v2.into_float_value());
let res =
let cond =
builder.build_float_compare(FloatPredicate::OEQ, v1, v2, &state.var_name());
let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name());
state.push1(res);
}
Operator::F32Ne | Operator::F64Ne => {
let (v1, v2) = state.pop2()?;
let (v1, v2) = (v1.into_float_value(), v2.into_float_value());
let res =
let cond =
builder.build_float_compare(FloatPredicate::UNE, v1, v2, &state.var_name());
let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name());
state.push1(res);
}
Operator::F32Lt | Operator::F64Lt => {
let (v1, v2) = state.pop2()?;
let (v1, v2) = (v1.into_float_value(), v2.into_float_value());
let res =
let cond =
builder.build_float_compare(FloatPredicate::OLT, v1, v2, &state.var_name());
let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name());
state.push1(res);
}
Operator::F32Le | Operator::F64Le => {
let (v1, v2) = state.pop2()?;
let (v1, v2) = (v1.into_float_value(), v2.into_float_value());
let res =
let cond =
builder.build_float_compare(FloatPredicate::OLE, v1, v2, &state.var_name());
let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name());
state.push1(res);
}
Operator::F32Gt | Operator::F64Gt => {
let (v1, v2) = state.pop2()?;
let (v1, v2) = (v1.into_float_value(), v2.into_float_value());
let res =
let cond =
builder.build_float_compare(FloatPredicate::OGT, v1, v2, &state.var_name());
let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name());
state.push1(res);
}
Operator::F32Ge | Operator::F64Ge => {
let (v1, v2) = state.pop2()?;
let (v1, v2) = (v1.into_float_value(), v2.into_float_value());
let res =
let cond =
builder.build_float_compare(FloatPredicate::OGE, v1, v2, &state.var_name());
let res = builder.build_int_z_extend(cond, intrinsics.i32_ty, &state.var_name());
state.push1(res);
}
@ -1816,9 +1914,5 @@ fn resolve_memory_ptr(
let mem_base_int = builder.build_ptr_to_int(mem_base, intrinsics.i64_ty, &state.var_name());
let effective_address_int =
builder.build_int_add(mem_base_int, effective_offset, &state.var_name());
Ok(builder.build_int_to_ptr(
effective_address_int,
intrinsics.i32_ptr_ty,
&state.var_name(),
))
Ok(builder.build_int_to_ptr(effective_address_int, ptr_ty, &state.var_name()))
}

View File

@ -639,7 +639,6 @@ impl<'a> CtxType<'a> {
let int = builder.build_ptr_to_int(global_ptr, intrinsics.i64_ty, "global_ptr_int");
builder.build_int_to_ptr(int, llvm_ptr_ty, "global_ptr_typed")
};
println!("global_ptr: {:?}", global_ptr_typed);
if mutable {
GlobalCache::Mut {

View File

@ -9,12 +9,14 @@ use wasmer_runtime_core::{
error::CompileError,
module::ModuleInner,
};
use wasmparser::{self, WasmDecoder};
mod backend;
mod code;
mod intrinsics;
mod read_info;
mod state;
mod trampolines;
pub struct LLVMCompiler {
_private: (),
@ -28,13 +30,15 @@ impl LLVMCompiler {
impl Compiler for LLVMCompiler {
fn compile(&self, wasm: &[u8], _: Token) -> Result<ModuleInner, CompileError> {
validate(wasm)?;
let (info, code_reader) = read_info::read_module(wasm).unwrap();
let (module, intrinsics) = code::parse_function_bodies(&info, code_reader).unwrap();
let backend = backend::LLVMBackend::new(module, intrinsics);
let (backend, protected_caller) = backend::LLVMBackend::new(module, intrinsics);
// Create placeholder values here.
let (protected_caller, cache_gen) = {
let cache_gen = {
use wasmer_runtime_core::backend::{
sys::Memory, CacheGen, ProtectedCaller, UserTrapper,
};
@ -44,22 +48,6 @@ impl Compiler for LLVMCompiler {
use wasmer_runtime_core::types::{FuncIndex, Value};
use wasmer_runtime_core::vm;
struct Placeholder;
impl ProtectedCaller for Placeholder {
fn call(
&self,
_module: &ModuleInner,
_func_index: FuncIndex,
_params: &[Value],
_import_backing: &vm::ImportBacking,
_vmctx: *mut vm::Ctx,
_: Token,
) -> RuntimeResult<Vec<Value>> {
unimplemented!("the llvm-based backend does not yet implement ProtectedCaller")
}
fn get_early_trapper(&self) -> Box<dyn UserTrapper> {
Box::new(Placeholder)
}
}
impl CacheGen for Placeholder {
fn generate_cache(
&self,
@ -68,18 +56,13 @@ impl Compiler for LLVMCompiler {
unimplemented!()
}
}
impl UserTrapper for Placeholder {
unsafe fn do_early_trap(&self, msg: String) -> ! {
unimplemented!("do early trap: {}", msg)
}
}
(Box::new(Placeholder), Box::new(Placeholder))
Box::new(Placeholder)
};
Ok(ModuleInner {
func_resolver: Box::new(backend),
protected_caller,
protected_caller: Box::new(protected_caller),
cache_gen,
info,
@ -91,6 +74,20 @@ impl Compiler for LLVMCompiler {
}
}
fn validate(bytes: &[u8]) -> Result<(), CompileError> {
let mut parser = wasmparser::ValidatingParser::new(bytes, None);
loop {
let state = parser.read();
match *state {
wasmparser::ParserState::EndWasm => break Ok(()),
wasmparser::ParserState::Error(err) => Err(CompileError::ValidationError {
msg: err.message.to_string(),
})?,
_ => {}
}
}
}
#[test]
fn test_read_module() {
use std::mem::transmute;

View File

@ -1,3 +1,4 @@
use crate::intrinsics::Intrinsics;
use inkwell::{
builder::Builder,
context::Context,
@ -9,30 +10,111 @@ use inkwell::{
};
use wasmer_runtime_core::{
module::ModuleInfo,
types::{SigIndex, FuncSig},
structures::{TypedIndex, SliceMap},
structures::{SliceMap, TypedIndex},
types::{FuncSig, SigIndex, Type},
};
use crate::intrinsics::Intrinsics;
pub fn generate_trampolines(info: &ModuleInfo, signatures: &SliceMap<SigIndex, FunctionType>, module: &Module, builder: &Builder, intrinsics: &Intrinsics) -> Result<(), String> {
let trampoline_sig = intrinsics.void_ty.fn_type(&[
intrinsics.ctx_ptr_ty, // vmctx ptr
intrinsics.i64_ptr_ty, // func ptr
intrinsics.i64_ptr_ty,
intrinsics.i64_ptr_ty,
], false);
pub fn generate_trampolines(
info: &ModuleInfo,
signatures: &SliceMap<SigIndex, FunctionType>,
module: &Module,
context: &Context,
builder: &Builder,
intrinsics: &Intrinsics,
) {
for (sig_index, sig) in info.signatures.iter() {
let func_type = signatures[sig_index];
let trampoline_sig = intrinsics.void_ty.fn_type(
&[
intrinsics.ctx_ptr_ty.as_basic_type_enum(), // vmctx ptr
func_type
.ptr_type(AddressSpace::Generic)
.as_basic_type_enum(), // func ptr
intrinsics.i64_ptr_ty.as_basic_type_enum(), // args ptr
intrinsics.i64_ptr_ty.as_basic_type_enum(), // returns ptr
],
false,
);
let trampoline_func = module.add_function(
&format!("trmp{}", sig_index.index()),
trampoline_sig,
Some(Linkage::External),
);
generate_trampoline(
trampoline_func,
func_type,
sig,
context,
builder,
intrinsics,
);
}
}
pub fn generate_trampoline(sig_index: usize, trampoline_sig: FunctionType, sig: &FuncSig, builder: &Builder, intrinsics: &Intrinsics) {
let function = module.add_function(
&format!("tramp{}", sig_index.index()),
signatures[sig_index],
Some(Linkage::External),
);
fn generate_trampoline(
trampoline_func: FunctionValue,
sig_type: FunctionType,
func_sig: &FuncSig,
context: &Context,
builder: &Builder,
intrinsics: &Intrinsics,
) {
let entry_block = context.append_basic_block(&trampoline_func, "entry");
builder.position_at_end(&entry_block);
let (vmctx_ptr, func_ptr, args_ptr, returns_ptr) = match trampoline_func.get_params().as_slice()
{
&[vmctx_ptr, func_ptr, args_ptr, returns_ptr] => (
vmctx_ptr,
func_ptr.into_pointer_value(),
args_ptr.into_pointer_value(),
returns_ptr.into_pointer_value(),
),
_ => unimplemented!(),
};
}
let cast_ptr_ty = |wasmer_ty| match wasmer_ty {
Type::I32 => intrinsics.i32_ptr_ty,
Type::I64 => intrinsics.i64_ptr_ty,
Type::F32 => intrinsics.f32_ptr_ty,
Type::F64 => intrinsics.f64_ptr_ty,
};
let mut args_vec = Vec::with_capacity(func_sig.params().len() + 1);
args_vec.push(vmctx_ptr);
for (i, param_ty) in func_sig.params().iter().enumerate() {
let index = intrinsics.i32_ty.const_int(i as _, false);
let item_pointer = unsafe { builder.build_in_bounds_gep(args_ptr, &[index], "arg_ptr") };
let casted_pointer_type = cast_ptr_ty(*param_ty);
let typed_item_pointer =
builder.build_pointer_cast(item_pointer, casted_pointer_type, "typed_arg_pointer");
let arg = builder.build_load(typed_item_pointer, "arg");
args_vec.push(arg);
}
let call_site = builder.build_call(func_ptr, &args_vec, "call");
match func_sig.returns() {
&[] => {}
&[one_ret] => {
let ret_ptr_type = cast_ptr_ty(one_ret);
let typed_ret_ptr =
builder.build_pointer_cast(returns_ptr, ret_ptr_type, "typed_ret_ptr");
builder.build_store(
typed_ret_ptr,
call_site.try_as_basic_value().left().unwrap(),
);
}
_ => unimplemented!("multi-value returns"),
}
builder.build_return(None);
}