Support imported functions

This commit is contained in:
Lachlan Sneff
2019-01-08 21:57:28 -05:00
parent 55b7cae523
commit bba168e61e
14 changed files with 265 additions and 151 deletions

View File

@ -1,18 +1,4 @@
use wasmer_runtime::{
FuncResolver,
LinearMemory,
ModuleInner as WasmerModule,
SigRegistry,
module::{DataInitializer, Export, ImportName, TableInitializer},
types::{
ElementType as WasmerElementType, FuncIndex as WasmerFuncIndex, FuncSig as WasmerSignature,
Global as WasmerGlobal, GlobalDesc as WasmerGlobalDesc, GlobalIndex as WasmerGlobalIndex,
Initializer as WasmerInitializer, Map, MapIndex, Memory as WasmerMemory,
MemoryIndex as WasmerMemoryIndex, SigIndex as WasmerSignatureIndex, Table as WasmerTable,
TableIndex as WasmerTableIndex, Type as WasmerType,
},
vm::Ctx as WasmerVMContext,
};
use crate::resolver::FuncResolverBuilder;
use cranelift_codegen::cursor::FuncCursor;
use cranelift_codegen::ir::immediates::{Offset32, Uimm64};
use cranelift_codegen::ir::types::{self, *};
@ -29,7 +15,18 @@ use cranelift_wasm::{
};
use hashbrown::HashMap;
use target_lexicon;
use crate::resolver::{FuncResolverBuilder};
use wasmer_runtime::{
module::{DataInitializer, Export, ImportName, TableInitializer},
types::{
ElementType as WasmerElementType, FuncIndex as WasmerFuncIndex, FuncSig as WasmerSignature,
Global as WasmerGlobal, GlobalDesc as WasmerGlobalDesc, GlobalIndex as WasmerGlobalIndex,
Initializer as WasmerInitializer, Map, MapIndex, Memory as WasmerMemory,
MemoryIndex as WasmerMemoryIndex, SigIndex as WasmerSignatureIndex, Table as WasmerTable,
TableIndex as WasmerTableIndex, Type as WasmerType,
},
vm::{self, Ctx as WasmerVMContext},
LinearMemory, ModuleInner as WasmerModule, SigRegistry,
};
/// The converter namespace contains functions for converting a Cranelift module
/// to a Wasmer module.
@ -72,8 +69,17 @@ pub mod converter {
func_assoc.push(WasmerSignatureIndex::new(signature_index.index()));
}
let function_bodies: Vec<_> = cranelift_module.function_bodies.into_iter().map(|(_, v)| v.clone()).collect();
let func_resolver_builder = FuncResolverBuilder::new(&*crate::get_isa(), function_bodies).unwrap();
let function_bodies: Vec<_> = cranelift_module
.function_bodies
.into_iter()
.map(|(_, v)| v.clone())
.collect();
let func_resolver_builder = FuncResolverBuilder::new(
&*crate::get_isa(),
function_bodies,
cranelift_module.imported_functions.len(),
)
.unwrap();
// Create func_resolver.
let func_resolver = Box::new(func_resolver_builder.finalize().unwrap());
@ -217,9 +223,6 @@ pub struct CraneliftModule {
/// The external function declaration for implementing wasm's `grow_memory`.
pub grow_memory_extfunc: Option<FuncRef>,
/// A function that takes a Wasmer module and resolves a function index to a vm::Func.
pub func_resolver: Option<Box<dyn FuncResolver>>,
// An array holding information about the wasm instance memories.
pub memories: Vec<Memory>,
@ -271,7 +274,6 @@ impl CraneliftModule {
memories_base: None,
current_memory_extfunc: None,
grow_memory_extfunc: None,
func_resolver: None,
memories: Vec::new(),
globals: Vec::new(),
tables: Vec::new(),
@ -286,8 +288,7 @@ impl CraneliftModule {
};
// Translate wasm to cranelift IR.
translate_module(&buffer_source, &mut cranelift_module)
.map_err(|e| e.to_string())?;
translate_module(&buffer_source, &mut cranelift_module).map_err(|e| e.to_string())?;
// Return translated module.
Ok(cranelift_module)
@ -560,12 +561,68 @@ impl<'environment> FuncEnvironmentTrait for FuncEnvironment<'environment> {
fn translate_call(
&mut self,
mut pos: FuncCursor,
_callee_index: FuncIndex,
callee_index: FuncIndex,
callee: ir::FuncRef,
call_args: &[ir::Value],
) -> WasmResult<ir::Inst> {
// Insert call instructions for `callee`.
Ok(pos.ins().call(callee, call_args))
if callee_index.index() < self.module.imported_functions.len() {
// this is an imported function
let vmctx = pos.func.create_global_value(ir::GlobalValueData::VMContext);
let imported_funcs = pos.func.create_global_value(ir::GlobalValueData::Load {
base: vmctx,
offset: (WasmerVMContext::offset_imported_funcs() as i32).into(),
global_type: self.pointer_type(),
readonly: true,
});
let imported_func_struct_addr =
pos.func.create_global_value(ir::GlobalValueData::IAddImm {
base: imported_funcs,
offset: (callee_index.index() as i64 * vm::ImportedFunc::size() as i64).into(),
global_type: self.pointer_type(),
});
let imported_func_addr = pos.func.create_global_value(ir::GlobalValueData::Load {
base: imported_func_struct_addr,
offset: (vm::ImportedFunc::offset_func() as i32).into(),
global_type: self.pointer_type(),
readonly: true,
});
let imported_func_addr = pos
.ins()
.global_value(self.pointer_type(), imported_func_addr);
let sig_ref = pos.func.dfg.ext_funcs[callee].signature;
let vmctx = pos
.func
.special_param(ir::ArgumentPurpose::VMContext)
.expect("missing vmctx parameter");
let mut args = Vec::with_capacity(call_args.len() + 1);
args.extend(call_args.iter().cloned());
args.push(vmctx);
Ok(pos
.ins()
.call_indirect(sig_ref, imported_func_addr, &args[..]))
} else {
// this is an internal function
let vmctx = pos
.func
.special_param(ir::ArgumentPurpose::VMContext)
.expect("missing vmctx parameter");
let mut args = Vec::with_capacity(call_args.len() + 1);
args.extend(call_args.iter().cloned());
args.push(vmctx);
Ok(pos.ins().call(callee, &args[..]))
}
}
/// Generates code corresponding to wasm `memory.grow`.

View File

@ -3,13 +3,16 @@ mod libcalls;
mod relocation;
mod resolver;
use wasmer_runtime::{Compiler, Module};
use cranelift_codegen::{settings::{self, Configurable}, isa};
use cranelift_codegen::{
isa,
settings::{self, Configurable},
};
use target_lexicon::Triple;
use wasmer_runtime::{Compiler, Module};
use wasmparser::{self, WasmDecoder};
use self::codegen::CraneliftModule;
use self::codegen::converter;
use self::codegen::CraneliftModule;
pub struct CraneliftCompiler {}
@ -59,12 +62,9 @@ fn validate(bytes: &[u8]) -> Result<(), String> {
match *state {
wasmparser::ParserState::EndWasm => return Ok(()),
wasmparser::ParserState::Error(err) => {
return Err(format!(
"Validation error: {}",
err.message
));
return Err(format!("Validation error: {}", err.message));
}
_ => (),
}
}
}
}

View File

@ -1,19 +1,14 @@
use cranelift_codegen::{
ir,
isa,
Context,
};
use crate::libcalls;
use crate::relocation::{Reloc, RelocSink, Relocation, RelocationType, TrapSink};
use cranelift_codegen::{ir, isa, Context};
use std::mem;
use std::ptr::{write_unaligned, NonNull};
use wasmer_runtime::{
self,
types::{Map, MapIndex, FuncIndex},
mmap::{Mmap, Protect},
vm,
vmcalls,
types::{FuncIndex, Map, MapIndex},
vm, vmcalls,
};
use crate::relocation::{Reloc, RelocSink, Relocation, RelocationType, TrapSink};
use crate::libcalls;
use std::ptr::{write_unaligned, NonNull};
use std::mem;
#[allow(dead_code)]
pub struct FuncResolverBuilder {
@ -23,7 +18,11 @@ pub struct FuncResolverBuilder {
}
impl FuncResolverBuilder {
pub fn new(isa: &isa::TargetIsa, function_bodies: Vec<ir::Function>) -> Result<Self, String> {
pub fn new(
isa: &isa::TargetIsa,
function_bodies: Vec<ir::Function>,
num_imported_funcs: usize,
) -> Result<Self, String> {
let mut compiled_functions: Vec<Vec<u8>> = Vec::with_capacity(function_bodies.len());
let mut relocations = Map::with_capacity(function_bodies.len());
let mut trap_sinks = Map::with_capacity(function_bodies.len());
@ -37,11 +36,8 @@ impl FuncResolverBuilder {
let mut reloc_sink = RelocSink::new();
let mut trap_sink = TrapSink::new();
ctx
.compile_and_emit(isa, &mut code_buf, &mut reloc_sink, &mut trap_sink)
.map_err(|e| {
format!("compile error: {}", e.to_string())
})?;
ctx.compile_and_emit(isa, &mut code_buf, &mut reloc_sink, &mut trap_sink)
.map_err(|e| format!("compile error: {}", e.to_string()))?;
ctx.clear();
// Round up each function's size to pointer alignment.
total_size += round_up(code_buf.len(), mem::size_of::<usize>());
@ -62,7 +58,8 @@ impl FuncResolverBuilder {
for compiled in compiled_functions.iter() {
let new_end = previous_end + round_up(compiled.len(), mem::size_of::<usize>());
unsafe {
memory.as_slice_mut()[previous_end..previous_end + compiled.len()].copy_from_slice(&compiled[..]);
memory.as_slice_mut()[previous_end..previous_end + compiled.len()]
.copy_from_slice(&compiled[..]);
}
map.push(previous_end);
previous_end = new_end;
@ -70,6 +67,7 @@ impl FuncResolverBuilder {
Ok(Self {
resolver: FuncResolver {
num_imported_funcs,
map,
memory,
},
@ -86,7 +84,10 @@ impl FuncResolverBuilder {
// This will always be an internal function
// because imported functions are not
// called in this way.
self.resolver.lookup(FuncIndex::new(func_index as _)).unwrap().as_ptr() as isize
self.resolver
.lookup(FuncIndex::new(func_index as _))
.unwrap()
.as_ptr() as isize
}
RelocationType::CurrentMemory => vmcalls::memory_size as isize,
RelocationType::GrowMemory => vmcalls::memory_grow_static as isize,
@ -135,7 +136,9 @@ impl FuncResolverBuilder {
}
unsafe {
self.resolver.memory.protect(0..self.resolver.memory.size(), Protect::ReadExec)?;
self.resolver
.memory
.protect(0..self.resolver.memory.size(), Protect::ReadExec)?;
}
Ok(self.resolver)
@ -144,16 +147,17 @@ impl FuncResolverBuilder {
/// Resolves a function index to a function address.
pub struct FuncResolver {
num_imported_funcs: usize,
map: Map<FuncIndex, usize>,
memory: Mmap,
}
impl FuncResolver {
fn lookup(&self, index: FuncIndex) -> Option<NonNull<vm::Func>> {
let offset = *self.map.get(index)?;
let ptr = unsafe {
self.memory.as_ptr().add(offset)
};
let offset = *self
.map
.get(FuncIndex::new(index.index() - self.num_imported_funcs))?;
let ptr = unsafe { self.memory.as_ptr().add(offset) };
NonNull::new(ptr).map(|nonnull| nonnull.cast())
}
@ -169,4 +173,4 @@ impl wasmer_runtime::FuncResolver for FuncResolver {
#[inline]
fn round_up(n: usize, multiple: usize) -> usize {
(n + multiple - 1) & !(multiple - 1)
}
}