diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b2c106e5dc..0cc1c0df5e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ All PRs to the Wasmer repository must add to this file. Blocks of changes will separated by version increments. ## **[Unreleased]** +- [#348](https://github.com/wasmerio/wasmer/pull/348) Refactor internal runtime ↔️ backend abstraction. - [#351](https://github.com/wasmerio/wasmer/pull/351) Add hidden option to specify wasm program name (can be used to improve error messages) - [#350](https://github.com/wasmerio/wasmer/pull/350) Enforce that CHANGELOG.md is updated through CI. - [#349](https://github.com/wasmerio/wasmer/pull/349) Add [CHANGELOG.md](https://github.com/wasmerio/wasmer/blob/master/CHANGELOG.md). diff --git a/Cargo.lock b/Cargo.lock index 71c131da9f2..602f89a90ea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2363,6 +2363,7 @@ dependencies = [ "serde-bench 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)", "serde_bytes 0.10.5 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", "wasmparser 0.29.2 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/bors.toml b/bors.toml index c56141cd5ff..3ccbdc00891 100644 --- a/bors.toml +++ b/bors.toml @@ -2,8 +2,9 @@ status = [ "ci/circleci: lint", "ci/circleci: test", "ci/circleci: test-macos", + "ci/circleci: test-rust-nightly", "continuous-integration/appveyor/branch" ] required_approvals = 1 timeout_sec = 900 -delete_merged_branches = true \ No newline at end of file +delete_merged_branches = true diff --git a/lib/clif-backend/src/module.rs b/lib/clif-backend/src/module.rs index 11f93342eef..ad0576e595e 100644 --- a/lib/clif-backend/src/module.rs +++ b/lib/clif-backend/src/module.rs @@ -73,16 +73,15 @@ impl Module { handler_data.clone(), )?; - let protected_caller = Caller::new(&self.info, handler_data, trampolines); - let cache_gen = Box::new(CacheGenerator::new( backend_cache, Arc::clone(&func_resolver.memory), )); + let runnable_module = Caller::new(handler_data, trampolines, func_resolver); + Ok(ModuleInner { - func_resolver: Box::new(func_resolver), - protected_caller: Box::new(protected_caller), + runnable_module: Box::new(runnable_module), cache_gen, info: self.info, @@ -103,16 +102,15 @@ impl Module { ) .map_err(|e| CacheError::Unknown(format!("{:?}", e)))?; - let protected_caller = Caller::new(&info, handler_data, trampolines); - let cache_gen = Box::new(CacheGenerator::new( backend_cache, Arc::clone(&func_resolver.memory), )); + let runnable_module = Caller::new(handler_data, trampolines, func_resolver); + Ok(ModuleInner { - func_resolver: Box::new(func_resolver), - protected_caller: Box::new(protected_caller), + runnable_module: Box::new(runnable_module), cache_gen, info, diff --git a/lib/clif-backend/src/resolver.rs b/lib/clif-backend/src/resolver.rs index e2706b6e11f..a55bf56d795 100644 --- a/lib/clif-backend/src/resolver.rs +++ b/lib/clif-backend/src/resolver.rs @@ -21,7 +21,6 @@ use wasmer_runtime_core::cache::Error as CacheError; use wasmer_runtime_core::{ self, backend::{ - self, sys::{Memory, Protect}, SigRegistry, }, @@ -357,13 +356,8 @@ pub struct FuncResolver { pub(crate) memory: Arc, } -// Implements FuncResolver trait. -impl backend::FuncResolver for FuncResolver { - fn get( - &self, - _module: &wasmer_runtime_core::module::ModuleInner, - index: LocalFuncIndex, - ) -> Option> { +impl FuncResolver { + pub fn lookup(&self, index: LocalFuncIndex) -> Option> { lookup_func(&self.map, &self.memory, index) } } diff --git a/lib/clif-backend/src/signal/mod.rs b/lib/clif-backend/src/signal/mod.rs index 766ad75fbfb..f5fcf5d5207 100644 --- a/lib/clif-backend/src/signal/mod.rs +++ b/lib/clif-backend/src/signal/mod.rs @@ -1,16 +1,14 @@ use crate::relocation::{TrapData, TrapSink}; +use crate::resolver::FuncResolver; use crate::trampoline::Trampolines; -use hashbrown::HashSet; use libc::c_void; use std::{any::Any, cell::Cell, ptr::NonNull, sync::Arc}; use wasmer_runtime_core::{ - backend::{ProtectedCaller, Token, UserTrapper}, - error::RuntimeResult, - export::Context, - module::{ExportIndex, ModuleInfo, ModuleInner}, + backend::{RunnableModule, UserTrapper}, + module::ModuleInfo, typed_func::{Wasm, WasmTrapInfo}, - types::{FuncIndex, FuncSig, LocalOrImport, SigIndex, Type, Value}, - vm::{self, ImportBacking}, + types::{LocalFuncIndex, SigIndex}, + vm, }; #[cfg(unix)] @@ -39,130 +37,44 @@ impl UserTrapper for Trapper { } pub struct Caller { - func_export_set: HashSet, handler_data: HandlerData, trampolines: Arc, + resolver: FuncResolver, } impl Caller { pub fn new( - module: &ModuleInfo, handler_data: HandlerData, trampolines: Arc, + resolver: FuncResolver, ) -> Self { - let mut func_export_set = HashSet::new(); - for export_index in module.exports.values() { - if let ExportIndex::Func(func_index) = export_index { - func_export_set.insert(*func_index); - } - } - if let Some(start_func_index) = module.start_func { - func_export_set.insert(start_func_index); - } - Self { - func_export_set, handler_data, trampolines, + resolver, } } } -impl ProtectedCaller for Caller { - fn call( - &self, - module: &ModuleInner, - func_index: FuncIndex, - params: &[Value], - import_backing: &ImportBacking, - vmctx: *mut vm::Ctx, - _: Token, - ) -> RuntimeResult> { - 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!(self.func_export_set.contains(&func_index)); - - assert!( - signature.returns().len() <= 1, - "multi-value returns not yet supported" - ); - - assert!( - signature.check_param_value_types(params), - "incorrect signature" - ); - - let param_vec: Vec = 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 = self - .trampolines - .lookup(sig_index) - .expect("that trampoline doesn't exist"); - - #[cfg(not(target_os = "windows"))] - call_protected(&self.handler_data, || unsafe { - // Leap of faith. - trampoline( - vmctx_ptr, - func_ptr, - param_vec.as_ptr(), - return_vec.as_mut_ptr(), - ); - })?; - - // the trampoline is called from C on windows - #[cfg(target_os = "windows")] - call_protected( - &self.handler_data, - 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()) +impl RunnableModule for Caller { + fn get_func(&self, _: &ModuleInfo, func_index: LocalFuncIndex) -> Option> { + self.resolver.lookup(func_index) } - fn get_wasm_trampoline(&self, module: &ModuleInner, sig_index: SigIndex) -> Option { + fn get_trampoline(&self, _: &ModuleInfo, sig_index: SigIndex) -> Option { unsafe extern "C" fn invoke( trampoline: unsafe extern "C" fn(*mut vm::Ctx, NonNull, *const u64, *mut u64), ctx: *mut vm::Ctx, func: NonNull, args: *const u64, rets: *mut u64, - trap_info: *mut WasmTrapInfo, + _trap_info: *mut WasmTrapInfo, invoke_env: Option>, ) -> bool { let handler_data = &*invoke_env.unwrap().cast().as_ptr(); #[cfg(not(target_os = "windows"))] - let res = call_protected(handler_data, || unsafe { + let res = call_protected(handler_data, || { // Leap of faith. trampoline(ctx, func, args, rets); }) @@ -194,40 +106,6 @@ impl ProtectedCaller for Caller { } } -fn get_func_from_index<'a>( - module: &'a ModuleInner, - import_backing: &ImportBacking, - func_index: FuncIndex, -) -> (NonNull, 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(), - Context::Internal, - ), - LocalOrImport::Import(imported_func_index) => { - let imported_func = import_backing.imported_func(imported_func_index); - ( - NonNull::new(imported_func.func as *mut _).unwrap(), - Context::External(imported_func.vmctx), - ) - } - }; - - let signature = &module.info.signatures[sig_index]; - - (func_ptr, ctx, signature, sig_index) -} - unsafe impl Send for HandlerData {} unsafe impl Sync for HandlerData {} diff --git a/lib/clif-backend/src/trampoline.rs b/lib/clif-backend/src/trampoline.rs index 696a0b624da..09ee4cf74cc 100644 --- a/lib/clif-backend/src/trampoline.rs +++ b/lib/clif-backend/src/trampoline.rs @@ -6,7 +6,6 @@ use cranelift_codegen::{ isa, Context, }; use hashbrown::HashMap; -use std::ffi::c_void; use std::{iter, mem, ptr::NonNull}; use wasmer_runtime_core::{ backend::sys::{Memory, Protect}, diff --git a/lib/llvm-backend/src/backend.rs b/lib/llvm-backend/src/backend.rs index 927dbeca504..172d667263c 100644 --- a/lib/llvm-backend/src/backend.rs +++ b/lib/llvm-backend/src/backend.rs @@ -18,15 +18,12 @@ use std::{ sync::Once, }; use wasmer_runtime_core::{ - backend::{FuncResolver, ProtectedCaller, Token, UserTrapper}, - error::{RuntimeError, RuntimeResult}, - export::Context, - module::{ModuleInfo, ModuleInner}, + backend::{RunnableModule, UserTrapper}, + module::ModuleInfo, structures::TypedIndex, typed_func::{Wasm, WasmTrapInfo}, - types::{FuncIndex, FuncSig, LocalFuncIndex, LocalOrImport, SigIndex, Type, Value}, - vm::{self, ImportBacking}, - vmcalls, + types::{LocalFuncIndex, SigIndex}, + vm, vmcalls, }; #[repr(C)] @@ -216,7 +213,7 @@ pub struct LLVMBackend { } impl LLVMBackend { - pub fn new(module: Module, _intrinsics: Intrinsics) -> (Self, LLVMProtectedCaller) { + pub fn new(module: Module, _intrinsics: Intrinsics) -> Self { Target::initialize_x86(&InitializationConfig { asm_parser: true, asm_printer: true, @@ -265,16 +262,21 @@ impl LLVMBackend { panic!("failed to load object") } - ( - Self { - module, - memory_buffer, - }, - LLVMProtectedCaller { module }, - ) + Self { + module, + memory_buffer, + } } +} + +impl Drop for LLVMBackend { + fn drop(&mut self) { + unsafe { module_delete(self.module) } + } +} - pub fn get_func( +impl RunnableModule for LLVMBackend { + fn get_func( &self, info: &ModuleInfo, local_func_index: LocalFuncIndex, @@ -291,126 +293,8 @@ impl LLVMBackend { NonNull::new(ptr as _) } -} - -impl Drop for LLVMBackend { - fn drop(&mut self) { - unsafe { module_delete(self.module) } - } -} - -impl FuncResolver for LLVMBackend { - fn get( - &self, - module: &ModuleInner, - local_func_index: LocalFuncIndex, - ) -> Option> { - self.get_func(&module.info, local_func_index) - } -} - -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> { - 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 = 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, - NonNull, - *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) - }; - - let mut trap_out = WasmTrapInfo::Unknown; - - // Here we go. - let success = unsafe { - invoke_trampoline( - trampoline, - vmctx_ptr, - func_ptr, - param_vec.as_ptr(), - return_vec.as_mut_ptr(), - &mut trap_out, - None, - ) - }; - - if success { - 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()) - } else { - Err(RuntimeError::Trap { - msg: trap_out.to_string().into(), - }) - } - } - - fn get_wasm_trampoline(&self, _module: &ModuleInner, sig_index: SigIndex) -> Option { + fn get_trampoline(&self, _: &ModuleInfo, sig_index: SigIndex) -> Option { let trampoline: unsafe extern "C" fn( *mut vm::Ctx, NonNull, @@ -438,46 +322,14 @@ impl ProtectedCaller for LLVMProtectedCaller { } } +struct Placeholder; + impl UserTrapper for Placeholder { unsafe fn do_early_trap(&self, data: Box) -> ! { throw_any(Box::leak(data)) } } -fn get_func_from_index<'a>( - module: &'a ModuleInner, - import_backing: &ImportBacking, - func_index: FuncIndex, -) -> (NonNull, 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(), - Context::Internal, - ), - LocalOrImport::Import(imported_func_index) => { - let imported_func = import_backing.imported_func(imported_func_index); - ( - NonNull::new(imported_func.func as *mut _).unwrap(), - Context::External(imported_func.vmctx), - ) - } - }; - - let signature = &module.info.signatures[sig_index]; - - (func_ptr, ctx, signature, sig_index) -} - #[cfg(feature = "disasm")] unsafe fn disass_ptr(ptr: *const u8, size: usize, inst_count: usize) { use capstone::arch::BuildsCapstone; diff --git a/lib/llvm-backend/src/lib.rs b/lib/llvm-backend/src/lib.rs index 50b06173160..bc43c5773fe 100644 --- a/lib/llvm-backend/src/lib.rs +++ b/lib/llvm-backend/src/lib.rs @@ -38,7 +38,7 @@ impl Compiler for LLVMCompiler { let (info, code_reader) = read_info::read_module(wasm, compiler_config).unwrap(); let (module, intrinsics) = code::parse_function_bodies(&info, code_reader).unwrap(); - let (backend, protected_caller) = backend::LLVMBackend::new(module, intrinsics); + let backend = backend::LLVMBackend::new(module, intrinsics); // Create placeholder values here. let cache_gen = { @@ -61,8 +61,7 @@ impl Compiler for LLVMCompiler { }; Ok(ModuleInner { - func_resolver: Box::new(backend), - protected_caller: Box::new(protected_caller), + runnable_module: Box::new(backend), cache_gen, info, @@ -104,7 +103,9 @@ fn validate(bytes: &[u8]) -> Result<(), CompileError> { fn test_read_module() { use std::mem::transmute; use wabt::wat2wasm; - use wasmer_runtime_core::{structures::TypedIndex, types::LocalFuncIndex, vm, vmcalls}; + use wasmer_runtime_core::{ + backend::RunnableModule, structures::TypedIndex, types::LocalFuncIndex, vm, + }; // let wasm = include_bytes!("../../spectests/examples/simple/simple.wasm") as &[u8]; let wat = r#" (module @@ -122,7 +123,7 @@ fn test_read_module() { let (module, intrinsics) = code::parse_function_bodies(&info, code_reader).unwrap(); - let (backend, _caller) = backend::LLVMBackend::new(module, intrinsics); + let backend = backend::LLVMBackend::new(module, intrinsics); let func_ptr = backend.get_func(&info, LocalFuncIndex::new(0)).unwrap(); diff --git a/lib/runtime-core/Cargo.toml b/lib/runtime-core/Cargo.toml index 41d4726268c..321ee501fc0 100644 --- a/lib/runtime-core/Cargo.toml +++ b/lib/runtime-core/Cargo.toml @@ -17,6 +17,7 @@ indexmap = "1.0.2" errno = "0.2.4" libc = "0.2.49" hex = "0.3.2" +smallvec = "0.6.9" # Dependencies for caching. [dependencies.serde] diff --git a/lib/runtime-core/src/backend.rs b/lib/runtime-core/src/backend.rs index 1044a7ee56a..c6ecffcb06e 100644 --- a/lib/runtime-core/src/backend.rs +++ b/lib/runtime-core/src/backend.rs @@ -1,10 +1,8 @@ use crate::{ - backing::ImportBacking, error::CompileResult, - error::RuntimeResult, module::ModuleInner, typed_func::Wasm, - types::{FuncIndex, LocalFuncIndex, SigIndex, Value}, + types::{LocalFuncIndex, SigIndex}, vm, }; @@ -67,59 +65,25 @@ pub trait Compiler { unsafe fn from_cache(&self, cache: Artifact, _: Token) -> Result; } -/// The functionality exposed by this trait is expected to be used -/// for calling functions exported by a webassembly module from -/// host code only. -pub trait ProtectedCaller: Send + Sync { - /// This calls the exported function designated by `local_func_index`. - /// Important to note, this supports calling imported functions that are - /// then exported. - /// - /// It's invalid to attempt to call a local function that isn't exported and - /// the implementation is expected to check for that. The implementation - /// is also expected to check for correct parameter types and correct - /// parameter number. - /// - /// The `returns` parameter is filled with dummy values when passed in and upon function - /// return, will be filled with the return values of the wasm function, as long as the - /// call completed successfully. - /// - /// The existance of the Token parameter ensures that this can only be called from - /// within the runtime crate. - /// - /// TODO(lachlan): Now that `get_wasm_trampoline` exists, `ProtectedCaller::call` - /// can be removed. That should speed up calls a little bit, since sanity checks - /// would only occur once. - fn call( - &self, - module: &ModuleInner, - func_index: FuncIndex, - params: &[Value], - import_backing: &ImportBacking, - vmctx: *mut vm::Ctx, - _: Token, - ) -> RuntimeResult>; - - /// A wasm trampoline contains the necesarry data to dynamically call an exported wasm function. - /// Given a particular signature index, we are returned a trampoline that is matched with that - /// signature and an invoke function that can call the trampoline. - fn get_wasm_trampoline(&self, module: &ModuleInner, sig_index: SigIndex) -> Option; - - fn get_early_trapper(&self) -> Box; -} - pub trait UserTrapper { unsafe fn do_early_trap(&self, data: Box) -> !; } -pub trait FuncResolver: Send + Sync { +pub trait RunnableModule: Send + Sync { /// This returns a pointer to the function designated by the `local_func_index` /// parameter. - fn get( + fn get_func( &self, - module: &ModuleInner, + info: &ModuleInfo, local_func_index: LocalFuncIndex, ) -> Option>; + + /// A wasm trampoline contains the necesarry data to dynamically call an exported wasm function. + /// Given a particular signature index, we are returned a trampoline that is matched with that + /// signature and an invoke function that can call the trampoline. + fn get_trampoline(&self, info: &ModuleInfo, sig_index: SigIndex) -> Option; + + fn get_early_trapper(&self) -> Box; } pub trait CacheGen: Send + Sync { diff --git a/lib/runtime-core/src/backing.rs b/lib/runtime-core/src/backing.rs index 48f2d225678..b79180ad6d5 100644 --- a/lib/runtime-core/src/backing.rs +++ b/lib/runtime-core/src/backing.rs @@ -72,8 +72,8 @@ impl LocalBacking { (0..module.info.func_assoc.len() - module.info.imported_functions.len()) .map(|index| { module - .func_resolver - .get(module, LocalFuncIndex::new(index)) + .runnable_module + .get_func(&module.info, LocalFuncIndex::new(index)) .unwrap() .as_ptr() as *const _ }) @@ -216,8 +216,8 @@ impl LocalBacking { let (func, ctx) = match func_index.local_or_import(&module.info) { LocalOrImport::Local(local_func_index) => ( module - .func_resolver - .get(module, local_func_index) + .runnable_module + .get_func(&module.info, local_func_index) .unwrap() .as_ptr() as *const vm::Func, @@ -255,8 +255,8 @@ impl LocalBacking { let (func, ctx) = match func_index.local_or_import(&module.info) { LocalOrImport::Local(local_func_index) => ( module - .func_resolver - .get(module, local_func_index) + .runnable_module + .get_func(&module.info, local_func_index) .unwrap() .as_ptr() as *const vm::Func, diff --git a/lib/runtime-core/src/instance.rs b/lib/runtime-core/src/instance.rs index 1249998cc6c..db9a2847f10 100644 --- a/lib/runtime-core/src/instance.rs +++ b/lib/runtime-core/src/instance.rs @@ -1,18 +1,19 @@ use crate::{ - backend::Token, + backend::RunnableModule, backing::{ImportBacking, LocalBacking}, - error::{CallError, CallResult, ResolveError, ResolveResult, Result}, + error::{CallError, CallResult, ResolveError, ResolveResult, Result, RuntimeError}, export::{Context, Export, ExportIter, FuncPointer}, global::Global, import::{ImportObject, LikeNamespace}, memory::Memory, - module::{ExportIndex, Module, ModuleInner}, + module::{ExportIndex, Module, ModuleInfo, ModuleInner}, sig_registry::SigRegistry, table::Table, - typed_func::{Func, Wasm, WasmTypeList}, - types::{FuncIndex, FuncSig, GlobalIndex, LocalOrImport, MemoryIndex, TableIndex, Value}, + typed_func::{Func, Wasm, WasmTrapInfo, WasmTypeList}, + types::{FuncIndex, FuncSig, GlobalIndex, LocalOrImport, MemoryIndex, TableIndex, Type, Value}, vm, }; +use smallvec::{smallvec, SmallVec}; use std::{mem, ptr::NonNull, sync::Arc}; pub(crate) struct InstanceInner { @@ -82,7 +83,45 @@ impl Instance { }; if let Some(start_index) = instance.module.info.start_func { - instance.call_with_index(start_index, &[])?; + // We know that the start function takes no arguments and returns no values. + // Therefore, we can call it without doing any signature checking, etc. + + let func_ptr = match start_index.local_or_import(&instance.module.info) { + LocalOrImport::Local(local_func_index) => instance + .module + .runnable_module + .get_func(&instance.module.info, local_func_index) + .unwrap(), + LocalOrImport::Import(import_func_index) => NonNull::new( + instance.inner.import_backing.vm_functions[import_func_index].func as *mut _, + ) + .unwrap(), + }; + + let ctx_ptr = match start_index.local_or_import(&instance.module.info) { + LocalOrImport::Local(_) => instance.inner.vmctx, + LocalOrImport::Import(imported_func_index) => { + instance.inner.import_backing.vm_functions[imported_func_index].vmctx + } + }; + + let sig_index = *instance + .module + .info + .func_assoc + .get(start_index) + .expect("broken invariant, incorrect func index"); + + let wasm_trampoline = instance + .module + .runnable_module + .get_trampoline(&instance.module.info, sig_index) + .expect("wasm trampoline"); + + let start_func: Func<(), (), Wasm> = + unsafe { Func::from_raw_parts(wasm_trampoline, func_ptr, ctx_ptr) }; + + start_func.call()?; } Ok(instance) @@ -147,15 +186,15 @@ impl Instance { let func_wasm_inner = self .module - .protected_caller - .get_wasm_trampoline(&self.module, sig_index) + .runnable_module + .get_trampoline(&self.module.info, sig_index) .unwrap(); let func_ptr = match func_index.local_or_import(&self.module.info) { LocalOrImport::Local(local_func_index) => self .module - .func_resolver - .get(&self.module, local_func_index) + .runnable_module + .get_func(&self.module.info, local_func_index) .unwrap(), LocalOrImport::Import(import_func_index) => NonNull::new( self.inner.import_backing.vm_functions[import_func_index].func as *mut _, @@ -245,7 +284,7 @@ impl Instance { /// # Ok(()) /// # } /// ``` - pub fn call(&self, name: &str, args: &[Value]) -> CallResult> { + pub fn call(&self, name: &str, params: &[Value]) -> CallResult> { let export_index = self.module .info @@ -264,7 +303,19 @@ impl Instance { .into()); }; - self.call_with_index(func_index, args) + let mut results = Vec::new(); + + call_func_with_index( + &self.module.info, + &*self.module.runnable_module, + &self.inner.import_backing, + self.inner.vmctx, + func_index, + params, + &mut results, + )?; + + Ok(results) } /// Returns an immutable reference to the @@ -295,45 +346,6 @@ impl Instance { } } -impl Instance { - fn call_with_index(&self, func_index: FuncIndex, args: &[Value]) -> CallResult> { - let sig_index = *self - .module - .info - .func_assoc - .get(func_index) - .expect("broken invariant, incorrect func index"); - let signature = &self.module.info.signatures[sig_index]; - - if !signature.check_param_value_types(args) { - Err(ResolveError::Signature { - expected: signature.clone(), - found: args.iter().map(|val| val.ty()).collect(), - })? - } - - let vmctx = match func_index.local_or_import(&self.module.info) { - LocalOrImport::Local(_) => self.inner.vmctx, - LocalOrImport::Import(imported_func_index) => { - self.inner.import_backing.vm_functions[imported_func_index].vmctx - } - }; - - let token = Token::generate(); - - let returns = self.module.protected_caller.call( - &self.module, - func_index, - args, - &self.inner.import_backing, - vmctx, - token, - )?; - - Ok(returns) - } -} - impl InstanceInner { pub(crate) fn get_export_from_index( &self, @@ -382,8 +394,8 @@ impl InstanceInner { 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) + .runnable_module + .get_func(&module.info, local_func_index) .expect("broken invariant, func resolver not synced with module.exports") .cast() .as_ptr() as *const _, @@ -452,6 +464,128 @@ impl LikeNamespace for Instance { } } +#[must_use] +fn call_func_with_index( + info: &ModuleInfo, + runnable: &dyn RunnableModule, + import_backing: &ImportBacking, + local_ctx: *mut vm::Ctx, + func_index: FuncIndex, + args: &[Value], + rets: &mut Vec, +) -> CallResult<()> { + rets.clear(); + + let sig_index = *info + .func_assoc + .get(func_index) + .expect("broken invariant, incorrect func index"); + + let signature = &info.signatures[sig_index]; + let num_results = signature.returns().len(); + rets.reserve(num_results); + + if !signature.check_param_value_types(args) { + Err(ResolveError::Signature { + expected: signature.clone(), + found: args.iter().map(|val| val.ty()).collect(), + })? + } + + let func_ptr = match func_index.local_or_import(info) { + LocalOrImport::Local(local_func_index) => { + runnable.get_func(info, local_func_index).unwrap() + } + LocalOrImport::Import(import_func_index) => { + NonNull::new(import_backing.vm_functions[import_func_index].func as *mut _).unwrap() + } + }; + + let ctx_ptr = match func_index.local_or_import(info) { + LocalOrImport::Local(_) => local_ctx, + LocalOrImport::Import(imported_func_index) => { + import_backing.vm_functions[imported_func_index].vmctx + } + }; + + let raw_args: SmallVec<[u64; 8]> = args + .iter() + .map(|v| match v { + Value::I32(i) => *i as u64, + Value::I64(i) => *i as u64, + Value::F32(f) => f.to_bits() as u64, + Value::F64(f) => f.to_bits(), + }) + .collect(); + + let Wasm { + trampoline, + invoke, + invoke_env, + } = runnable + .get_trampoline(info, sig_index) + .expect("wasm trampoline"); + + let run_wasm = |result_space: *mut u64| unsafe { + let mut trap_info = WasmTrapInfo::Unknown; + + let success = invoke( + trampoline, + ctx_ptr, + func_ptr, + raw_args.as_ptr(), + result_space, + &mut trap_info, + invoke_env, + ); + + if success { + Ok(()) + } else { + Err(RuntimeError::Trap { + msg: trap_info.to_string().into(), + }) + } + }; + + let raw_to_value = |raw, ty| match ty { + Type::I32 => Value::I32(raw as i32), + Type::I64 => Value::I64(raw as i64), + Type::F32 => Value::F32(f32::from_bits(raw as u32)), + Type::F64 => Value::F64(f64::from_bits(raw)), + }; + + match signature.returns() { + &[] => { + run_wasm(0 as *mut u64)?; + Ok(()) + } + &[ty] => { + let mut result = 0u64; + + run_wasm(&mut result)?; + + rets.push(raw_to_value(result, ty)); + + Ok(()) + } + result_tys @ _ => { + let mut results: SmallVec<[u64; 8]> = smallvec![0; num_results]; + + run_wasm(results.as_mut_ptr())?; + + rets.extend( + results + .iter() + .zip(result_tys.iter()) + .map(|(&raw, &ty)| raw_to_value(raw, ty)), + ); + + Ok(()) + } + } +} + /// A representation of an exported WebAssembly function. pub struct DynFunc<'a> { pub(crate) signature: Arc, @@ -484,32 +618,19 @@ impl<'a> DynFunc<'a> { /// # } /// ``` pub fn call(&self, params: &[Value]) -> CallResult> { - if !self.signature.check_param_value_types(params) { - Err(ResolveError::Signature { - expected: (*self.signature).clone(), - found: params.iter().map(|val| val.ty()).collect(), - })? - } + let mut results = Vec::new(); - let vmctx = match self.func_index.local_or_import(&self.module.info) { - LocalOrImport::Local(_) => self.instance_inner.vmctx, - LocalOrImport::Import(imported_func_index) => { - self.instance_inner.import_backing.vm_functions[imported_func_index].vmctx - } - }; - - let token = Token::generate(); - - let returns = self.module.protected_caller.call( - &self.module, + call_func_with_index( + &self.module.info, + &*self.module.runnable_module, + &self.instance_inner.import_backing, + self.instance_inner.vmctx, self.func_index, params, - &self.instance_inner.import_backing, - vmctx, - token, + &mut results, )?; - Ok(returns) + Ok(results) } pub fn signature(&self) -> &FuncSig { @@ -520,8 +641,8 @@ impl<'a> DynFunc<'a> { match self.func_index.local_or_import(&self.module.info) { LocalOrImport::Local(local_func_index) => self .module - .func_resolver - .get(self.module, local_func_index) + .runnable_module + .get_func(&self.module.info, local_func_index) .unwrap() .as_ptr(), LocalOrImport::Import(import_func_index) => { diff --git a/lib/runtime-core/src/module.rs b/lib/runtime-core/src/module.rs index 65817399d25..9e9e93052b6 100644 --- a/lib/runtime-core/src/module.rs +++ b/lib/runtime-core/src/module.rs @@ -1,5 +1,5 @@ use crate::{ - backend::{Backend, FuncResolver, ProtectedCaller}, + backend::{Backend, RunnableModule}, cache::{Artifact, Error as CacheError}, error, import::ImportObject, @@ -22,9 +22,7 @@ use std::sync::Arc; /// This is used to instantiate a new WebAssembly module. #[doc(hidden)] pub struct ModuleInner { - pub func_resolver: Box, - pub protected_caller: Box, - + pub runnable_module: Box, pub cache_gen: Box, pub info: ModuleInfo, @@ -96,7 +94,7 @@ impl Module { pub(crate) fn new(inner: Arc) -> Self { unsafe { EARLY_TRAPPER - .with(|ucell| *ucell.get() = Some(inner.protected_caller.get_early_trapper())); + .with(|ucell| *ucell.get() = Some(inner.runnable_module.get_early_trapper())); } Module { inner } } diff --git a/lib/runtime-core/src/typed_func.rs b/lib/runtime-core/src/typed_func.rs index d18055ba14e..5e38db62822 100644 --- a/lib/runtime-core/src/typed_func.rs +++ b/lib/runtime-core/src/typed_func.rs @@ -71,9 +71,9 @@ pub type Invoke = unsafe extern "C" fn( /// as well as the environment that the invoke function may or may not require. #[derive(Copy, Clone)] pub struct Wasm { - trampoline: Trampoline, - invoke: Invoke, - invoke_env: Option>, + pub(crate) trampoline: Trampoline, + pub(crate) invoke: Invoke, + pub(crate) invoke_env: Option>, } impl Wasm { diff --git a/lib/runtime-core/src/vm.rs b/lib/runtime-core/src/vm.rs index 3b2ca9938dd..1d955d40d23 100644 --- a/lib/runtime-core/src/vm.rs +++ b/lib/runtime-core/src/vm.rs @@ -544,42 +544,23 @@ mod vm_ctx_tests { fn generate_module() -> ModuleInner { use super::Func; - use crate::backend::{ - sys::Memory, Backend, CacheGen, FuncResolver, ProtectedCaller, Token, UserTrapper, - }; + use crate::backend::{sys::Memory, Backend, CacheGen, RunnableModule, UserTrapper}; use crate::cache::Error as CacheError; - use crate::error::RuntimeResult; use crate::typed_func::Wasm; - use crate::types::{FuncIndex, LocalFuncIndex, SigIndex, Value}; + use crate::types::{LocalFuncIndex, SigIndex}; use hashbrown::HashMap; use std::ptr::NonNull; struct Placeholder; - impl FuncResolver for Placeholder { - fn get( + impl RunnableModule for Placeholder { + fn get_func( &self, - _module: &ModuleInner, + _module: &ModuleInfo, _local_func_index: LocalFuncIndex, ) -> Option> { None } - } - impl ProtectedCaller for Placeholder { - fn call( - &self, - _module: &ModuleInner, - _func_index: FuncIndex, - _params: &[Value], - _import_backing: &ImportBacking, - _vmctx: *mut Ctx, - _: Token, - ) -> RuntimeResult> { - Ok(vec![]) - } - fn get_wasm_trampoline( - &self, - _module: &ModuleInner, - _sig_index: SigIndex, - ) -> Option { + + fn get_trampoline(&self, _module: &ModuleInfo, _sig_index: SigIndex) -> Option { unimplemented!() } fn get_early_trapper(&self) -> Box { @@ -596,8 +577,7 @@ mod vm_ctx_tests { } ModuleInner { - func_resolver: Box::new(Placeholder), - protected_caller: Box::new(Placeholder), + runnable_module: Box::new(Placeholder), cache_gen: Box::new(Placeholder), info: ModuleInfo { memories: Map::new(), diff --git a/lib/singlepass-backend/src/codegen.rs b/lib/singlepass-backend/src/codegen.rs index 798dea114c0..ee3ad277ee2 100644 --- a/lib/singlepass-backend/src/codegen.rs +++ b/lib/singlepass-backend/src/codegen.rs @@ -1,15 +1,15 @@ use wasmer_runtime_core::{ - backend::{FuncResolver, ProtectedCaller}, + backend::RunnableModule, module::ModuleInfo, structures::Map, types::{FuncIndex, FuncSig, SigIndex}, }; use wasmparser::{Operator, Type as WpType}; -pub trait ModuleCodeGenerator { +pub trait ModuleCodeGenerator { fn check_precondition(&mut self, module_info: &ModuleInfo) -> Result<(), CodegenError>; fn next_function(&mut self) -> Result<&mut FCG, CodegenError>; - fn finalize(self, module_info: &ModuleInfo) -> Result<(PC, FR), CodegenError>; + fn finalize(self, module_info: &ModuleInfo) -> Result; fn feed_signatures(&mut self, signatures: Map) -> Result<(), CodegenError>; fn feed_function_signatures( &mut self, diff --git a/lib/singlepass-backend/src/codegen_x64.rs b/lib/singlepass-backend/src/codegen_x64.rs index c574bfc146a..0bd3e4dac20 100644 --- a/lib/singlepass-backend/src/codegen_x64.rs +++ b/lib/singlepass-backend/src/codegen_x64.rs @@ -11,17 +11,16 @@ use smallvec::SmallVec; use std::ptr::NonNull; use std::{any::Any, collections::HashMap, sync::Arc}; use wasmer_runtime_core::{ - backend::{FuncResolver, ProtectedCaller, Token, UserTrapper}, - error::RuntimeResult, + backend::{RunnableModule, UserTrapper}, memory::MemoryType, - module::{ModuleInfo, ModuleInner}, + module::ModuleInfo, structures::{Map, TypedIndex}, typed_func::Wasm, types::{ FuncIndex, FuncSig, GlobalIndex, LocalFuncIndex, LocalOrImport, MemoryIndex, SigIndex, - TableIndex, Type, Value, + TableIndex, Type, }, - vm::{self, ImportBacking, LocalGlobal, LocalMemory, LocalTable}, + vm::{self, LocalGlobal, LocalMemory, LocalTable}, vmcalls, }; use wasmparser::{Operator, Type as WpType}; @@ -132,7 +131,6 @@ pub struct X64FunctionCode { signatures: Arc>, function_signatures: Arc>, - begin_offset: AssemblyOffset, assembler: Option, function_labels: Option)>>, br_table_data: Option>>, @@ -154,7 +152,9 @@ unsafe impl Send for FuncPtr {} unsafe impl Sync for FuncPtr {} pub struct X64ExecutionContext { + #[allow(dead_code)] code: ExecutableBuffer, + #[allow(dead_code)] functions: Vec, function_pointers: Vec, signatures: Arc>, @@ -162,10 +162,6 @@ pub struct X64ExecutionContext { func_import_count: usize, } -pub struct X64RuntimeResolver { - local_function_pointers: Vec, -} - #[derive(Debug)] pub struct ControlFrame { pub label: DynamicLabel, @@ -182,86 +178,33 @@ pub enum IfElseState { Else, } -impl X64ExecutionContext { - fn get_runtime_resolver( +impl RunnableModule for X64ExecutionContext { + fn get_func( &self, - _module_info: &ModuleInfo, - ) -> Result { - Ok(X64RuntimeResolver { - local_function_pointers: self.function_pointers[self.func_import_count..].to_vec(), - }) - } -} - -impl FuncResolver for X64RuntimeResolver { - fn get( - &self, - _module: &ModuleInner, - _local_func_index: LocalFuncIndex, + _: &ModuleInfo, + local_func_index: LocalFuncIndex, ) -> Option> { - NonNull::new( - self.local_function_pointers[_local_func_index.index() as usize].0 as *mut vm::Func, - ) - } -} - -impl ProtectedCaller for X64ExecutionContext { - fn call( - &self, - _module: &ModuleInner, - _func_index: FuncIndex, - _params: &[Value], - _import_backing: &ImportBacking, - _vmctx: *mut vm::Ctx, - _: Token, - ) -> RuntimeResult> { - let index = _func_index.index() - self.func_import_count; - let ptr = self.code.ptr(self.functions[index].begin_offset); - let return_ty = self.functions[index].returns.last().cloned(); - let buffer: Vec = _params - .iter() - .rev() - .map(|x| match *x { - Value::I32(x) => x as u32 as u64, - Value::I64(x) => x as u64, - Value::F32(x) => f32::to_bits(x) as u64, - Value::F64(x) => f64::to_bits(x), - }) - .collect(); - let ret = unsafe { - protect_unix::call_protected(|| { - CONSTRUCT_STACK_AND_CALL_WASM( - buffer.as_ptr(), - buffer.as_ptr().offset(buffer.len() as isize), - _vmctx, - ptr as _, - ) - }) - }?; - Ok(if let Some(ty) = return_ty { - vec![match ty { - WpType::I32 => Value::I32(ret as i32), - WpType::I64 => Value::I64(ret as i64), - WpType::F32 => Value::F32(f32::from_bits(ret as u32)), - WpType::F64 => Value::F64(f64::from_bits(ret as u64)), - _ => unreachable!(), - }] - } else { - vec![] - }) + self.function_pointers[self.func_import_count..] + .get(local_func_index.index()) + .and_then(|ptr| NonNull::new(ptr.0 as *mut vm::Func)) } - fn get_wasm_trampoline(&self, module: &ModuleInner, sig_index: SigIndex) -> Option { + fn get_trampoline(&self, _: &ModuleInfo, sig_index: SigIndex) -> Option { use std::ffi::c_void; use wasmer_runtime_core::typed_func::WasmTrapInfo; unsafe extern "C" fn invoke( - trampoline: unsafe extern "C" fn(*mut vm::Ctx, NonNull, *const u64, *mut u64), + _trampoline: unsafe extern "C" fn( + *mut vm::Ctx, + NonNull, + *const u64, + *mut u64, + ), ctx: *mut vm::Ctx, func: NonNull, args: *const u64, rets: *mut u64, - trap_info: *mut WasmTrapInfo, + _trap_info: *mut WasmTrapInfo, num_params_plus_one: Option>, ) -> bool { let args = ::std::slice::from_raw_parts( @@ -330,9 +273,7 @@ impl X64ModuleCodeGenerator { } } -impl ModuleCodeGenerator - for X64ModuleCodeGenerator -{ +impl ModuleCodeGenerator for X64ModuleCodeGenerator { fn check_precondition(&mut self, _module_info: &ModuleInfo) -> Result<(), CodegenError> { Ok(()) } @@ -367,7 +308,6 @@ impl ModuleCodeGenerator Result<(X64ExecutionContext, X64RuntimeResolver), CodegenError> { + fn finalize(mut self, _: &ModuleInfo) -> Result { let (assembler, mut br_table_data) = match self.functions.last_mut() { Some(x) => (x.assembler.take().unwrap(), x.br_table_data.take().unwrap()), None => { @@ -431,17 +368,14 @@ impl ModuleCodeGenerator) -> Result<(), CodegenError> { diff --git a/lib/singlepass-backend/src/lib.rs b/lib/singlepass-backend/src/lib.rs index cdb275cd3c0..c388bee496c 100644 --- a/lib/singlepass-backend/src/lib.rs +++ b/lib/singlepass-backend/src/lib.rs @@ -62,11 +62,10 @@ impl Compiler for SinglePassCompiler { ) -> CompileResult { let mut mcg = codegen_x64::X64ModuleCodeGenerator::new(); let info = parse::read_module(wasm, Backend::Singlepass, &mut mcg, &compiler_config)?; - let (ec, resolver) = mcg.finalize(&info)?; + let exec_context = mcg.finalize(&info)?; Ok(ModuleInner { cache_gen: Box::new(Placeholder), - func_resolver: Box::new(resolver), - protected_caller: Box::new(ec), + runnable_module: Box::new(exec_context), info: info, }) } diff --git a/lib/singlepass-backend/src/parse.rs b/lib/singlepass-backend/src/parse.rs index b198ea4c586..cb4d932969a 100644 --- a/lib/singlepass-backend/src/parse.rs +++ b/lib/singlepass-backend/src/parse.rs @@ -1,7 +1,7 @@ use crate::codegen::{CodegenError, FunctionCodeGenerator, ModuleCodeGenerator}; use hashbrown::HashMap; use wasmer_runtime_core::{ - backend::{Backend, CompilerConfig, FuncResolver, ProtectedCaller}, + backend::{Backend, CompilerConfig, RunnableModule}, module::{ DataInitializer, ExportIndex, ImportName, ModuleInfo, StringTable, StringTableBuilder, TableInitializer, @@ -63,10 +63,9 @@ fn validate(bytes: &[u8]) -> Result<(), LoadError> { } pub fn read_module< - MCG: ModuleCodeGenerator, + MCG: ModuleCodeGenerator, FCG: FunctionCodeGenerator, - PC: ProtectedCaller, - FR: FuncResolver, + RM: RunnableModule, >( wasm: &[u8], backend: Backend,