Merge branch 'master' into feature/trace-macro

This commit is contained in:
Mark McCaskey
2019-05-21 11:44:50 -07:00
committed by GitHub
119 changed files with 11269 additions and 733 deletions

View File

@ -39,16 +39,26 @@ impl Token {
}
}
#[derive(Copy, Clone, Debug)]
pub enum MemoryBoundCheckMode {
Default,
Enable,
Disable,
}
impl Default for MemoryBoundCheckMode {
fn default() -> MemoryBoundCheckMode {
MemoryBoundCheckMode::Default
}
}
/// Configuration data for the compiler
#[derive(Default)]
pub struct CompilerConfig {
/// Symbol information generated from emscripten; used for more detailed debug messages
pub symbol_map: Option<HashMap<u32, String>>,
}
impl Default for CompilerConfig {
fn default() -> CompilerConfig {
CompilerConfig { symbol_map: None }
}
pub memory_bound_check_mode: MemoryBoundCheckMode,
pub enforce_stack_check: bool,
}
pub trait Compiler {
@ -80,6 +90,16 @@ pub trait RunnableModule: Send + Sync {
fn get_trampoline(&self, info: &ModuleInfo, sig_index: SigIndex) -> Option<Wasm>;
unsafe fn do_early_trap(&self, data: Box<dyn Any>) -> !;
/// Returns the machine code associated with this module.
fn get_code(&self) -> Option<&[u8]> {
None
}
/// Returns the beginning offsets of all functions, including import trampolines.
fn get_offsets(&self) -> Option<Vec<usize>> {
None
}
}
pub trait CacheGen: Send + Sync {

View File

@ -456,10 +456,17 @@ fn import_functions(
});
}
None => {
link_errors.push(LinkError::ImportNotFound {
namespace: namespace.to_string(),
name: name.to_string(),
});
if imports.allow_missing_functions {
functions.push(vm::ImportedFunc {
func: ::std::ptr::null(),
vmctx: ::std::ptr::null_mut(),
});
} else {
link_errors.push(LinkError::ImportNotFound {
namespace: namespace.to_string(),
name: name.to_string(),
});
}
}
}
}

View File

@ -42,21 +42,29 @@ impl fmt::Debug for InternalEvent {
pub struct BkptInfo {}
pub trait ModuleCodeGenerator<FCG: FunctionCodeGenerator<E>, RM: RunnableModule, E: Debug> {
/// Creates a new module code generator.
fn new() -> Self;
/// Returns the backend id associated with this MCG.
fn backend_id() -> Backend;
fn check_precondition(&mut self, module_info: &ModuleInfo) -> Result<(), E>;
/// Creates a new function and returns the function-scope code generator for it.
fn next_function(&mut self) -> Result<&mut FCG, E>;
fn finalize(self, module_info: &ModuleInfo) -> Result<(RM, Box<dyn CacheGen>), E>;
fn feed_signatures(&mut self, signatures: Map<SigIndex, FuncSig>) -> Result<(), E>;
/// Sets function signatures.
fn feed_function_signatures(&mut self, assoc: Map<FuncIndex, SigIndex>) -> Result<(), E>;
/// Feeds the compiler config.
fn feed_compiler_config(&mut self, _config: &CompilerConfig) -> Result<(), E> {
Ok(())
}
/// Adds an import function.
fn feed_import_function(&mut self) -> Result<(), E>;
fn feed_signatures(&mut self, signatures: Map<SigIndex, FuncSig>) -> Result<(), E>;
/// Sets function signatures.
fn feed_function_signatures(&mut self, assoc: Map<FuncIndex, SigIndex>) -> Result<(), E>;
/// Checks the precondition for a module.
fn check_precondition(&mut self, module_info: &ModuleInfo) -> Result<(), E>;
/// Creates a new function and returns the function-scope code generator for it.
fn next_function(&mut self) -> Result<&mut FCG, E>;
/// Finalizes this module.
fn finalize(self, module_info: &ModuleInfo) -> Result<(RM, Box<dyn CacheGen>), E>;
/// Creates a module from cache.
unsafe fn from_cache(cache: Artifact, _: Token) -> Result<ModuleInner, CacheError>;
}

View File

@ -47,6 +47,7 @@ impl IsExport for Export {
pub struct ImportObject {
map: Rc<RefCell<HashMap<String, Box<dyn LikeNamespace>>>>,
state_creator: Option<Rc<Fn() -> (*mut c_void, fn(*mut c_void))>>,
pub allow_missing_functions: bool,
}
impl ImportObject {
@ -55,6 +56,7 @@ impl ImportObject {
Self {
map: Rc::new(RefCell::new(HashMap::new())),
state_creator: None,
allow_missing_functions: false,
}
}
@ -65,6 +67,7 @@ impl ImportObject {
Self {
map: Rc::new(RefCell::new(HashMap::new())),
state_creator: Some(Rc::new(state_creator)),
allow_missing_functions: false,
}
}
@ -116,6 +119,7 @@ impl ImportObject {
Self {
map: Rc::clone(&self.map),
state_creator: self.state_creator.clone(),
allow_missing_functions: false,
}
}

View File

@ -5,9 +5,11 @@ use crate::{
export::{Context, Export, ExportIter, FuncPointer},
global::Global,
import::{ImportObject, LikeNamespace},
loader::Loader,
memory::Memory,
module::{ExportIndex, Module, ModuleInfo, ModuleInner},
sig_registry::SigRegistry,
structures::TypedIndex,
table::Table,
typed_func::{Func, Wasm, WasmTrapInfo, WasmTypeList},
types::{FuncIndex, FuncSig, GlobalIndex, LocalOrImport, MemoryIndex, TableIndex, Type, Value},
@ -38,7 +40,7 @@ impl Drop for InstanceInner {
///
/// [`ImportObject`]: struct.ImportObject.html
pub struct Instance {
module: Arc<ModuleInner>,
pub module: Arc<ModuleInner>,
inner: Box<InstanceInner>,
#[allow(dead_code)]
import_object: ImportObject,
@ -127,6 +129,12 @@ impl Instance {
Ok(instance)
}
pub fn load<T: Loader>(&self, loader: T) -> ::std::result::Result<T::Instance, T::Error> {
loader.load(&*self.module.runnable_module, &self.module.info, unsafe {
&*self.inner.vmctx
})
}
/// Through generic magic and the awe-inspiring power of traits, we bring you...
///
/// # "Func"
@ -214,6 +222,26 @@ impl Instance {
}
}
pub fn resolve_func(&self, name: &str) -> ResolveResult<usize> {
let export_index =
self.module
.info
.exports
.get(name)
.ok_or_else(|| ResolveError::ExportNotFound {
name: name.to_string(),
})?;
if let ExportIndex::Func(func_index) = export_index {
Ok(func_index.index())
} else {
Err(ResolveError::ExportWrongType {
name: name.to_string(),
}
.into())
}
}
/// This returns the representation of a function that can be called
/// safely.
///

View File

@ -21,6 +21,7 @@ pub mod export;
pub mod global;
pub mod import;
pub mod instance;
pub mod loader;
pub mod memory;
pub mod module;
pub mod parse;

View File

@ -0,0 +1,169 @@
use crate::{backend::RunnableModule, module::ModuleInfo, types::Value, vm::Ctx};
#[cfg(unix)]
use libc::{mmap, mprotect, munmap, MAP_ANON, MAP_PRIVATE, PROT_EXEC, PROT_READ, PROT_WRITE};
use std::{
fmt::Debug,
ops::{Deref, DerefMut},
};
pub trait Loader {
type Instance: Instance;
type Error: Debug;
fn load(
&self,
rm: &dyn RunnableModule,
module: &ModuleInfo,
ctx: &Ctx,
) -> Result<Self::Instance, Self::Error>;
}
pub trait Instance {
type Error: Debug;
fn call(&mut self, id: usize, args: &[Value]) -> Result<u64, Self::Error>;
fn read_memory(&mut self, _offset: u32, _len: u32) -> Result<Vec<u8>, Self::Error> {
unimplemented!()
}
fn write_memory(&mut self, _offset: u32, _len: u32, _buf: &[u8]) -> Result<(), Self::Error> {
unimplemented!()
}
}
pub struct LocalLoader;
impl Loader for LocalLoader {
type Instance = LocalInstance;
type Error = String;
fn load(
&self,
rm: &dyn RunnableModule,
_module: &ModuleInfo,
_ctx: &Ctx,
) -> Result<Self::Instance, Self::Error> {
let code = rm.get_code().unwrap();
let mut code_mem = CodeMemory::new(code.len());
code_mem[..code.len()].copy_from_slice(code);
code_mem.make_executable();
Ok(LocalInstance {
code: code_mem,
offsets: rm.get_offsets().unwrap(),
})
}
}
pub struct LocalInstance {
code: CodeMemory,
offsets: Vec<usize>,
}
impl Instance for LocalInstance {
type Error = String;
fn call(&mut self, id: usize, args: &[Value]) -> Result<u64, Self::Error> {
let offset = self.offsets[id];
let addr: *const u8 = unsafe { self.code.as_ptr().offset(offset as isize) };
use std::mem::transmute;
Ok(unsafe {
match args.len() {
0 => (transmute::<_, extern "C" fn() -> u64>(addr))(),
1 => (transmute::<_, extern "C" fn(u64) -> u64>(addr))(args[0].to_u64()),
2 => (transmute::<_, extern "C" fn(u64, u64) -> u64>(addr))(
args[0].to_u64(),
args[1].to_u64(),
),
3 => (transmute::<_, extern "C" fn(u64, u64, u64) -> u64>(addr))(
args[0].to_u64(),
args[1].to_u64(),
args[2].to_u64(),
),
4 => (transmute::<_, extern "C" fn(u64, u64, u64, u64) -> u64>(addr))(
args[0].to_u64(),
args[1].to_u64(),
args[2].to_u64(),
args[3].to_u64(),
),
5 => (transmute::<_, extern "C" fn(u64, u64, u64, u64, u64) -> u64>(addr))(
args[0].to_u64(),
args[1].to_u64(),
args[2].to_u64(),
args[3].to_u64(),
args[4].to_u64(),
),
_ => return Err("too many arguments".into()),
}
})
}
}
pub struct CodeMemory {
ptr: *mut u8,
size: usize,
}
#[cfg(not(unix))]
impl CodeMemory {
pub fn new(_size: usize) -> CodeMemory {
unimplemented!();
}
pub fn make_executable(&mut self) {
unimplemented!();
}
}
#[cfg(unix)]
impl CodeMemory {
pub fn new(size: usize) -> CodeMemory {
fn round_up_to_page_size(size: usize) -> usize {
(size + (4096 - 1)) & !(4096 - 1)
}
let size = round_up_to_page_size(size);
let ptr = unsafe {
mmap(
::std::ptr::null_mut(),
size,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON,
-1,
0,
)
};
if ptr as isize == -1 {
panic!("cannot allocate code memory");
}
CodeMemory {
ptr: ptr as _,
size: size,
}
}
pub fn make_executable(&mut self) {
if unsafe { mprotect(self.ptr as _, self.size, PROT_READ | PROT_EXEC) } != 0 {
panic!("cannot set code memory to executable");
}
}
}
#[cfg(unix)]
impl Drop for CodeMemory {
fn drop(&mut self) {
unsafe {
munmap(self.ptr as _, self.size);
}
}
}
impl Deref for CodeMemory {
type Target = [u8];
fn deref(&self) -> &[u8] {
unsafe { ::std::slice::from_raw_parts(self.ptr, self.size) }
}
}
impl DerefMut for CodeMemory {
fn deref_mut(&mut self) -> &mut [u8] {
unsafe { ::std::slice::from_raw_parts_mut(self.ptr, self.size) }
}
}

View File

@ -53,6 +53,8 @@ pub fn read_module<
middlewares: &mut MiddlewareChain,
compiler_config: &CompilerConfig,
) -> Result<ModuleInfo, LoadError> {
mcg.feed_compiler_config(compiler_config)
.map_err(|x| LoadError::Codegen(format!("{:?}", x)))?;
let mut info = ModuleInfo {
memories: Map::new(),
globals: Map::new(),

View File

@ -45,6 +45,15 @@ impl Value {
Value::F64(_) => Type::F64,
}
}
pub fn to_u64(&self) -> u64 {
match *self {
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),
}
}
}
impl From<i32> for Value {

View File

@ -1,9 +1,10 @@
pub use crate::backing::{ImportBacking, LocalBacking};
use crate::{
memory::Memory,
module::ModuleInner,
memory::{Memory, MemoryType},
module::{ModuleInfo, ModuleInner},
structures::TypedIndex,
types::{LocalOrImport, MemoryIndex},
vmcalls,
};
use std::{ffi::c_void, mem, ptr};
@ -25,7 +26,7 @@ use hashbrown::HashMap;
#[repr(C)]
pub struct Ctx {
// `internal` must be the first field of `Ctx`.
pub(crate) internal: InternalCtx,
pub internal: InternalCtx,
pub(crate) local_functions: *const *const Func,
@ -84,6 +85,83 @@ pub struct InternalCtx {
/// signature id. This is used to allow call-indirect to other
/// modules safely.
pub dynamic_sigindices: *const SigId,
pub intrinsics: *const Intrinsics,
pub stack_lower_bound: *mut u8,
pub memory_base: *mut u8,
pub memory_bound: usize,
}
#[repr(C)]
pub struct Intrinsics {
pub memory_grow: *const Func,
pub memory_size: *const Func,
/*pub memory_grow: unsafe extern "C" fn(
ctx: &mut Ctx,
memory_index: usize,
delta: Pages,
) -> i32,
pub memory_size: unsafe extern "C" fn(
ctx: &Ctx,
memory_index: usize,
) -> Pages,*/
}
unsafe impl Send for Intrinsics {}
unsafe impl Sync for Intrinsics {}
impl Intrinsics {
#[allow(clippy::erasing_op)]
pub fn offset_memory_grow() -> u8 {
(0 * ::std::mem::size_of::<usize>()) as u8
}
pub fn offset_memory_size() -> u8 {
(1 * ::std::mem::size_of::<usize>()) as u8
}
}
pub static INTRINSICS_LOCAL_STATIC_MEMORY: Intrinsics = Intrinsics {
memory_grow: vmcalls::local_static_memory_grow as _,
memory_size: vmcalls::local_static_memory_size as _,
};
pub static INTRINSICS_LOCAL_DYNAMIC_MEMORY: Intrinsics = Intrinsics {
memory_grow: vmcalls::local_dynamic_memory_grow as _,
memory_size: vmcalls::local_dynamic_memory_size as _,
};
pub static INTRINSICS_IMPORTED_STATIC_MEMORY: Intrinsics = Intrinsics {
memory_grow: vmcalls::imported_static_memory_grow as _,
memory_size: vmcalls::imported_static_memory_size as _,
};
pub static INTRINSICS_IMPORTED_DYNAMIC_MEMORY: Intrinsics = Intrinsics {
memory_grow: vmcalls::imported_dynamic_memory_grow as _,
memory_size: vmcalls::imported_dynamic_memory_size as _,
};
fn get_intrinsics_for_module(m: &ModuleInfo) -> *const Intrinsics {
if m.memories.len() == 0 && m.imported_memories.len() == 0 {
::std::ptr::null()
} else {
match MemoryIndex::new(0).local_or_import(m) {
LocalOrImport::Local(local_mem_index) => {
let mem_desc = &m.memories[local_mem_index];
match mem_desc.memory_type() {
MemoryType::Dynamic => &INTRINSICS_LOCAL_DYNAMIC_MEMORY,
MemoryType::Static => &INTRINSICS_LOCAL_STATIC_MEMORY,
MemoryType::SharedStatic => unimplemented!(),
}
}
LocalOrImport::Import(import_mem_index) => {
let mem_desc = &m.imported_memories[import_mem_index].1;
match mem_desc.memory_type() {
MemoryType::Dynamic => &INTRINSICS_IMPORTED_DYNAMIC_MEMORY,
MemoryType::Static => &INTRINSICS_IMPORTED_STATIC_MEMORY,
MemoryType::SharedStatic => unimplemented!(),
}
}
}
}
}
impl Ctx {
@ -93,6 +171,16 @@ impl Ctx {
import_backing: &mut ImportBacking,
module: &ModuleInner,
) -> Self {
let (mem_base, mem_bound): (*mut u8, usize) =
if module.info.memories.len() == 0 && module.info.imported_memories.len() == 0 {
(::std::ptr::null_mut(), 0)
} else {
let mem = match MemoryIndex::new(0).local_or_import(&module.info) {
LocalOrImport::Local(index) => local_backing.vm_memories[index],
LocalOrImport::Import(index) => import_backing.vm_memories[index],
};
((*mem).base, (*mem).bound)
};
Self {
internal: InternalCtx {
memories: local_backing.vm_memories.as_mut_ptr(),
@ -105,6 +193,13 @@ impl Ctx {
imported_funcs: import_backing.vm_functions.as_mut_ptr(),
dynamic_sigindices: local_backing.dynamic_sigindices.as_ptr(),
intrinsics: get_intrinsics_for_module(&module.info),
stack_lower_bound: ::std::ptr::null_mut(),
memory_base: mem_base,
memory_bound: mem_bound,
},
local_functions: local_backing.local_functions.as_ptr(),
@ -125,6 +220,16 @@ impl Ctx {
data: *mut c_void,
data_finalizer: fn(*mut c_void),
) -> Self {
let (mem_base, mem_bound): (*mut u8, usize) =
if module.info.memories.len() == 0 && module.info.imported_memories.len() == 0 {
(::std::ptr::null_mut(), 0)
} else {
let mem = match MemoryIndex::new(0).local_or_import(&module.info) {
LocalOrImport::Local(index) => local_backing.vm_memories[index],
LocalOrImport::Import(index) => import_backing.vm_memories[index],
};
((*mem).base, (*mem).bound)
};
Self {
internal: InternalCtx {
memories: local_backing.vm_memories.as_mut_ptr(),
@ -137,6 +242,13 @@ impl Ctx {
imported_funcs: import_backing.vm_functions.as_mut_ptr(),
dynamic_sigindices: local_backing.dynamic_sigindices.as_ptr(),
intrinsics: get_intrinsics_for_module(&module.info),
stack_lower_bound: ::std::ptr::null_mut(),
memory_base: mem_base,
memory_bound: mem_bound,
},
local_functions: local_backing.local_functions.as_ptr(),
@ -186,6 +298,11 @@ impl Ctx {
pub unsafe fn borrow_symbol_map(&self) -> &Option<HashMap<u32, String>> {
&(*self.module).info.em_symbol_map
}
/// Returns the number of dynamic sigindices.
pub fn dynamic_sigindice_count(&self) -> usize {
unsafe { (*self.local_backing).dynamic_sigindices.len() }
}
}
#[doc(hidden)]
@ -223,9 +340,25 @@ impl Ctx {
7 * (mem::size_of::<usize>() as u8)
}
pub fn offset_local_functions() -> u8 {
pub fn offset_intrinsics() -> u8 {
8 * (mem::size_of::<usize>() as u8)
}
pub fn offset_stack_lower_bound() -> u8 {
9 * (mem::size_of::<usize>() as u8)
}
pub fn offset_memory_base() -> u8 {
10 * (mem::size_of::<usize>() as u8)
}
pub fn offset_memory_bound() -> u8 {
11 * (mem::size_of::<usize>() as u8)
}
pub fn offset_local_functions() -> u8 {
12 * (mem::size_of::<usize>() as u8)
}
}
enum InnerFunc {}
@ -419,6 +552,26 @@ mod vm_offset_tests {
offset_of!(InternalCtx => imported_funcs).get_byte_offset(),
);
assert_eq!(
Ctx::offset_intrinsics() as usize,
offset_of!(InternalCtx => intrinsics).get_byte_offset(),
);
assert_eq!(
Ctx::offset_stack_lower_bound() as usize,
offset_of!(InternalCtx => stack_lower_bound).get_byte_offset(),
);
assert_eq!(
Ctx::offset_memory_base() as usize,
offset_of!(InternalCtx => memory_base).get_byte_offset(),
);
assert_eq!(
Ctx::offset_memory_bound() as usize,
offset_of!(InternalCtx => memory_bound).get_byte_offset(),
);
assert_eq!(
Ctx::offset_local_functions() as usize,
offset_of!(Ctx => local_functions).get_byte_offset(),

View File

@ -20,10 +20,15 @@ pub unsafe extern "C" fn local_static_memory_grow(
let local_memory = *ctx.internal.memories.add(memory_index.index());
let memory = (*local_memory).memory as *mut StaticMemory;
match (*memory).grow(delta, &mut *local_memory) {
let ret = match (*memory).grow(delta, &mut *local_memory) {
Ok(old) => old.0 as i32,
Err(_) => -1,
}
};
ctx.internal.memory_base = (*local_memory).base;
ctx.internal.memory_bound = (*local_memory).bound;
ret
}
pub unsafe extern "C" fn local_static_memory_size(
@ -44,10 +49,15 @@ pub unsafe extern "C" fn local_dynamic_memory_grow(
let local_memory = *ctx.internal.memories.add(memory_index.index());
let memory = (*local_memory).memory as *mut DynamicMemory;
match (*memory).grow(delta, &mut *local_memory) {
let ret = match (*memory).grow(delta, &mut *local_memory) {
Ok(old) => old.0 as i32,
Err(_) => -1,
}
};
ctx.internal.memory_base = (*local_memory).base;
ctx.internal.memory_bound = (*local_memory).bound;
ret
}
pub unsafe extern "C" fn local_dynamic_memory_size(
@ -75,10 +85,15 @@ pub unsafe extern "C" fn imported_static_memory_grow(
.add(import_memory_index.index());
let memory = (*local_memory).memory as *mut StaticMemory;
match (*memory).grow(delta, &mut *local_memory) {
let ret = match (*memory).grow(delta, &mut *local_memory) {
Ok(old) => old.0 as i32,
Err(_) => -1,
}
};
ctx.internal.memory_base = (*local_memory).base;
ctx.internal.memory_bound = (*local_memory).bound;
ret
}
pub unsafe extern "C" fn imported_static_memory_size(
@ -102,10 +117,15 @@ pub unsafe extern "C" fn imported_dynamic_memory_grow(
let local_memory = *ctx.internal.imported_memories.add(memory_index.index());
let memory = (*local_memory).memory as *mut DynamicMemory;
match (*memory).grow(delta, &mut *local_memory) {
let ret = match (*memory).grow(delta, &mut *local_memory) {
Ok(old) => old.0 as i32,
Err(_) => -1,
}
};
ctx.internal.memory_base = (*local_memory).base;
ctx.internal.memory_bound = (*local_memory).bound;
ret
}
pub unsafe extern "C" fn imported_dynamic_memory_size(