Local/Import index differentiation

This commit is contained in:
Lachlan Sneff
2019-01-16 10:26:10 -08:00
parent de1da689f8
commit 5872d879f9
20 changed files with 935 additions and 542 deletions

View File

@ -25,9 +25,9 @@ use wasmer_runtime::{
types::{ types::{
ElementType as WasmerElementType, FuncIndex as WasmerFuncIndex, FuncSig as WasmerSignature, ElementType as WasmerElementType, FuncIndex as WasmerFuncIndex, FuncSig as WasmerSignature,
Global as WasmerGlobal, GlobalDesc as WasmerGlobalDesc, GlobalIndex as WasmerGlobalIndex, Global as WasmerGlobal, GlobalDesc as WasmerGlobalDesc, GlobalIndex as WasmerGlobalIndex,
Initializer as WasmerInitializer, Map, MapIndex, Memory as WasmerMemory, GlobalInit as WasmerGlobalInit, Initializer as WasmerInitializer, Map,
MemoryIndex as WasmerMemoryIndex, SigIndex as WasmerSignatureIndex, Table as WasmerTable, Memory as WasmerMemory, MemoryIndex as WasmerMemoryIndex, SigIndex as WasmerSignatureIndex,
TableIndex as WasmerTableIndex, Type as WasmerType, Table as WasmerTable, TableIndex as WasmerTableIndex, Type as WasmerType, TypedIndex,
}, },
vm::{self, Ctx as WasmerVMContext}, vm::{self, Ctx as WasmerVMContext},
}; };
@ -142,14 +142,14 @@ pub mod converter {
// TODO: WasmerGlobal does not support `Import` as Global values. // TODO: WasmerGlobal does not support `Import` as Global values.
let init = match global.initializer { let init = match global.initializer {
I32Const(val) => Const(val.into()), I32Const(val) => WasmerGlobalInit::Init(Const(val.into())),
I64Const(val) => Const(val.into()), I64Const(val) => WasmerGlobalInit::Init(Const(val.into())),
F32Const(val) => Const(f32::from_bits(val).into()), F32Const(val) => WasmerGlobalInit::Init(Const(f32::from_bits(val).into())),
F64Const(val) => Const(f64::from_bits(val).into()), F64Const(val) => WasmerGlobalInit::Init(Const(f64::from_bits(val).into())),
GlobalInit::GetGlobal(index) => { GlobalInit::GetGlobal(index) => WasmerGlobalInit::Init(WasmerInitializer::GetGlobal(
WasmerInitializer::GetGlobal(WasmerGlobalIndex::new(index.index())) WasmerGlobalIndex::new(index.index()),
}, )),
GlobalInit::Import => WasmerInitializer::Import GlobalInit::Import => WasmerGlobalInit::Import,
}; };
WasmerGlobal { desc, init } WasmerGlobal { desc, init }
@ -173,6 +173,7 @@ pub mod converter {
/// Converts a Cranelift table to a Wasmer table. /// Converts a Cranelift table to a Wasmer table.
pub fn convert_memory(memory: Memory) -> WasmerMemory { pub fn convert_memory(memory: Memory) -> WasmerMemory {
println!("codegen memory: {:?}", memory);
WasmerMemory { WasmerMemory {
shared: memory.shared, shared: memory.shared,
min: memory.minimum, min: memory.minimum,
@ -350,29 +351,71 @@ impl<'environment> FuncEnvironmentTrait for FuncEnvironment<'environment> {
/// by `index`. /// by `index`.
/// ///
/// The index space covers both imported and locally declared globals. /// The index space covers both imported and locally declared globals.
fn make_global(&mut self, func: &mut ir::Function, index: GlobalIndex) -> GlobalVariable { fn make_global(
&mut self,
func: &mut ir::Function,
global_index: GlobalIndex,
) -> GlobalVariable {
// Create VMContext value. // Create VMContext value.
let vmctx = func.create_global_value(ir::GlobalValueData::VMContext); let vmctx = func.create_global_value(ir::GlobalValueData::VMContext);
let ptr_size = self.pointer_bytes();
let globals_offset = WasmerVMContext::offset_globals();
// Load value at (vmctx + globals_offset), i.e. the address at Ctx.globals. if global_index.index() < self.module.imported_globals.len() {
let globals_base_addr = func.create_global_value(ir::GlobalValueData::Load { // imported global
base: vmctx,
offset: Offset32::new(globals_offset as i32),
global_type: self.pointer_type(),
readonly: false,
});
// *Ctx.globals -> [ u8, u8, .. ] let imported_globals_base_addr = func.create_global_value(ir::GlobalValueData::Load {
// Based on the index provided, we need to know the offset into globals array base: vmctx,
let offset = index.index() * ptr_size as usize; offset: (vm::Ctx::offset_imported_globals() as i32).into(),
global_type: self.pointer_type(),
readonly: true,
});
// Create global variable based on the data above. let offset = global_index.index() * vm::ImportedGlobal::size() as usize;
GlobalVariable::Memory {
gv: globals_base_addr, let imported_global_addr = func.create_global_value(ir::GlobalValueData::IAddImm {
offset: (offset as i32).into(), base: imported_globals_base_addr,
ty: self.module.get_global(index).ty, offset: (offset as i64).into(),
global_type: self.pointer_type(),
});
let local_global_addr = func.create_global_value(ir::GlobalValueData::Load {
base: imported_global_addr,
offset: (vm::ImportedGlobal::offset_global() as i32).into(),
global_type: self.pointer_type(),
readonly: true,
});
GlobalVariable::Memory {
gv: local_global_addr,
offset: (vm::LocalGlobal::offset_data() as i32).into(),
ty: self.module.get_global(global_index).ty,
}
} else {
// locally defined global
let globals_base_addr = func.create_global_value(ir::GlobalValueData::Load {
base: vmctx,
offset: (vm::Ctx::offset_globals() as i32).into(),
global_type: self.pointer_type(),
readonly: true,
});
// *Ctx.globals -> [ u8, u8, .. ]
// Based on the index provided, we need to know the offset into globals array
let offset = (global_index.index() - self.module.imported_globals.len())
* vm::LocalGlobal::size() as usize;
let local_global_addr = func.create_global_value(ir::GlobalValueData::IAddImm {
base: globals_base_addr,
offset: (offset as i64).into(),
global_type: self.pointer_type(),
});
// Create global variable based on the data above.
GlobalVariable::Memory {
gv: local_global_addr,
offset: (vm::LocalGlobal::offset_data() as i32).into(),
ty: self.module.get_global(global_index).ty,
}
} }
} }
@ -870,13 +913,18 @@ impl<'data> ModuleEnvironment<'data> for CraneliftModule {
elements: Vec<FuncIndex>, elements: Vec<FuncIndex>,
) { ) {
// Convert Cranelift GlobalIndex to wamser GlobalIndex // Convert Cranelift GlobalIndex to wamser GlobalIndex
let base = base.map(|index| WasmerGlobalIndex::new(index.index())); // let base = base.map(|index| WasmerGlobalIndex::new(index.index()));
let base = match base {
Some(global_index) => {
WasmerInitializer::GetGlobal(WasmerGlobalIndex::new(global_index.index()))
}
None => WasmerInitializer::Const((offset as i32).into()),
};
// Add table initializer to list of table initializers // Add table initializer to list of table initializers
self.table_initializers.push(TableInitializer { self.table_initializers.push(TableInitializer {
table_index: WasmerTableIndex::new(table_index.index()), table_index: WasmerTableIndex::new(table_index.index()),
base, base,
offset,
elements: elements elements: elements
.iter() .iter()
.map(|index| WasmerFuncIndex::new(index.index())) .map(|index| WasmerFuncIndex::new(index.index()))

View File

@ -2,6 +2,7 @@ pub mod codegen;
mod libcalls; mod libcalls;
mod relocation; mod relocation;
mod resolver; mod resolver;
// mod module;
use cranelift_codegen::{ use cranelift_codegen::{
isa, isa,

View File

@ -0,0 +1,20 @@
use wasmer_runtime::{
backend::SigRegistry,
memory::LinearMemory,
module::{
DataInitializer, ExportIndex, ImportName, ModuleInner, TableInitializer,
},
types::{
ElementType, FuncIndex, FuncSig,
Global, GlobalDesc, GlobalIndex,
Initializer, Map, TypedIndex, Memory,
MemoryIndex as WasmerMemoryIndex, SigIndex as WasmerSignatureIndex, Table as WasmerTable,
TableIndex as WasmerTableIndex, Type as WasmerType, GlobalInit as WasmerGlobalInit,
},
vm::{self, Ctx as WasmerVMContext},
};
/// This is a wasmer module.
pub struct Module {
}

View File

@ -7,15 +7,15 @@ use std::ptr::{write_unaligned, NonNull};
use wasmer_runtime::{ use wasmer_runtime::{
self, self,
backend::{self, Mmap, Protect}, backend::{self, Mmap, Protect},
types::{FuncIndex, Map, MapIndex}, types::{LocalFuncIndex, Map, TypedIndex},
vm, vmcalls, vm, vmcalls,
}; };
#[allow(dead_code)] #[allow(dead_code)]
pub struct FuncResolverBuilder { pub struct FuncResolverBuilder {
resolver: FuncResolver, resolver: FuncResolver,
relocations: Map<FuncIndex, Vec<Relocation>>, relocations: Map<LocalFuncIndex, Vec<Relocation>>,
trap_sinks: Map<FuncIndex, TrapSink>, trap_sinks: Map<LocalFuncIndex, TrapSink>,
} }
impl FuncResolverBuilder { impl FuncResolverBuilder {
@ -158,15 +158,13 @@ impl FuncResolverBuilder {
/// Resolves a function index to a function address. /// Resolves a function index to a function address.
pub struct FuncResolver { pub struct FuncResolver {
num_imported_funcs: usize, num_imported_funcs: usize,
map: Map<FuncIndex, usize>, map: Map<LocalFuncIndex, usize>,
memory: Mmap, memory: Mmap,
} }
impl FuncResolver { impl FuncResolver {
fn lookup(&self, index: FuncIndex) -> Option<NonNull<vm::Func>> { fn lookup(&self, local_func_index: LocalFuncIndex) -> Option<NonNull<vm::Func>> {
let offset = *self let offset = *self.map.get(local_func_index)?;
.map
.get(FuncIndex::new(index.index() - self.num_imported_funcs))?;
let ptr = unsafe { self.memory.as_ptr().add(offset) }; let ptr = unsafe { self.memory.as_ptr().add(offset) };
NonNull::new(ptr).map(|nonnull| nonnull.cast()) NonNull::new(ptr).map(|nonnull| nonnull.cast())

View File

@ -88,8 +88,8 @@ static IMPORT_MODULE: &str = r#"
(type $t1 (func)) (type $t1 (func))
(func $print_i32 (export "print_i32") (type $t0) (param $lhs i32)) (func $print_i32 (export "print_i32") (type $t0) (param $lhs i32))
(func $print (export "print") (type $t1)) (func $print (export "print") (type $t1))
(table $table (export "table") 10 anyfunc) (table $table (export "table") 10 20 anyfunc)
(memory $memory (export "memory") 1) (memory $memory (export "memory") 1 2)
(global $global_i32 (export "global_i32") i32 (i32.const 666))) (global $global_i32 (export "global_i32") i32 (i32.const 666)))
"#; "#;
@ -350,6 +350,7 @@ fn test_module_{}() {{
format!( format!(
"fn create_module_{}() -> Instance {{ "fn create_module_{}() -> Instance {{
let module_str = \"{}\"; let module_str = \"{}\";
println!(\"{{}}\", module_str);
let wasm_binary = wat2wasm(module_str.as_bytes()).expect(\"WAST not valid or malformed\"); let wasm_binary = wat2wasm(module_str.as_bytes()).expect(\"WAST not valid or malformed\");
let module = wasmer_runtime::compile(&wasm_binary[..], &CraneliftCompiler::new()).expect(\"WASM can't be compiled\"); let module = wasmer_runtime::compile(&wasm_binary[..], &CraneliftCompiler::new()).expect(\"WASM can't be compiled\");
module.instantiate(&mut generate_imports()).expect(\"WASM can't be instantiated\") module.instantiate(&mut generate_imports()).expect(\"WASM can't be instantiated\")

View File

@ -1,4 +1,4 @@
use crate::{module::ModuleInner, types::FuncIndex, vm}; use crate::{module::ModuleInner, types::LocalFuncIndex, vm};
use std::ptr::NonNull; use std::ptr::NonNull;
pub use crate::mmap::{Mmap, Protect}; pub use crate::mmap::{Mmap, Protect};
@ -10,5 +10,9 @@ pub trait Compiler {
} }
pub trait FuncResolver { pub trait FuncResolver {
fn get(&self, module: &ModuleInner, index: FuncIndex) -> Option<NonNull<vm::Func>>; fn get(
&self,
module: &ModuleInner,
local_func_index: LocalFuncIndex,
) -> Option<NonNull<vm::Func>>;
} }

View File

@ -3,28 +3,34 @@ use crate::{
import::Imports, import::Imports,
memory::LinearMemory, memory::LinearMemory,
module::{ImportName, ModuleInner}, module::{ImportName, ModuleInner},
structures::{BoxedMap, Map, SliceMap, TypedIndex},
table::{TableBacking, TableElements}, table::{TableBacking, TableElements},
types::{Initializer, MapIndex, Value}, types::{
ElementType, ImportedFuncIndex, ImportedGlobalIndex, ImportedMemoryIndex,
ImportedTableIndex, Initializer, LocalGlobalIndex, LocalMemoryIndex, LocalOrImport,
LocalTableIndex, Type, Value,
},
vm, vm,
}; };
use std::slice;
#[derive(Debug)] #[derive(Debug)]
pub struct LocalBacking { pub struct LocalBacking {
pub(crate) memories: Box<[LinearMemory]>, pub(crate) memories: BoxedMap<LocalMemoryIndex, LinearMemory>,
pub(crate) tables: Box<[TableBacking]>, pub(crate) tables: BoxedMap<LocalTableIndex, TableBacking>,
pub(crate) vm_memories: Box<[vm::LocalMemory]>, pub(crate) vm_memories: BoxedMap<LocalMemoryIndex, vm::LocalMemory>,
pub(crate) vm_tables: Box<[vm::LocalTable]>, pub(crate) vm_tables: BoxedMap<LocalTableIndex, vm::LocalTable>,
pub(crate) vm_globals: Box<[vm::LocalGlobal]>, pub(crate) vm_globals: BoxedMap<LocalGlobalIndex, vm::LocalGlobal>,
} }
impl LocalBacking { impl LocalBacking {
pub fn memory(&mut self, index: u32) -> &mut LinearMemory { pub fn memory(&mut self, local_memory_index: LocalMemoryIndex) -> &mut LinearMemory {
&mut self.memories[index as usize] &mut self.memories[local_memory_index]
} }
pub fn table(&mut self, index: u32) -> &mut TableBacking { pub fn table(&mut self, local_table_index: LocalTableIndex) -> &mut TableBacking {
&mut self.tables[index as usize] &mut self.tables[local_table_index]
} }
} }
@ -34,9 +40,9 @@ impl LocalBacking {
let mut tables = Self::generate_tables(module); let mut tables = Self::generate_tables(module);
let globals = Self::generate_globals(module); let globals = Self::generate_globals(module);
let vm_memories = Self::finalize_memories(module, &mut memories[..]); let vm_memories = Self::finalize_memories(module, imports, &mut memories);
let vm_tables = Self::finalize_tables(module, imports, &mut tables[..], vmctx); let vm_tables = Self::finalize_tables(module, imports, &mut tables, vmctx);
let vm_globals = Self::finalize_globals(module, imports, globals); let vm_globals = Self::finalize_globals(module, globals);
Self { Self {
memories, memories,
@ -48,8 +54,8 @@ impl LocalBacking {
} }
} }
fn generate_memories(module: &ModuleInner) -> Box<[LinearMemory]> { fn generate_memories(module: &ModuleInner) -> BoxedMap<LocalMemoryIndex, LinearMemory> {
let mut memories = Vec::with_capacity(module.memories.len()); let mut memories = Map::with_capacity(module.memories.len());
for (_, mem) in &module.memories { for (_, mem) in &module.memories {
// If we use emscripten, we set a fixed initial and maximum // If we use emscripten, we set a fixed initial and maximum
@ -66,75 +72,155 @@ impl LocalBacking {
memories.push(memory); memories.push(memory);
} }
memories.into_boxed_slice() memories.into_boxed_map()
} }
fn finalize_memories( fn finalize_memories(
module: &ModuleInner, module: &ModuleInner,
memories: &mut [LinearMemory], imports: &ImportBacking,
) -> Box<[vm::LocalMemory]> { memories: &mut SliceMap<LocalMemoryIndex, LinearMemory>,
for init in &module.data_initializers { ) -> BoxedMap<LocalMemoryIndex, vm::LocalMemory> {
// For each init that has some data...
for init in module
.data_initializers
.iter()
.filter(|init| init.data.len() > 0)
{
assert!(init.base.is_none(), "global base not supported yet"); assert!(init.base.is_none(), "global base not supported yet");
assert!(init.offset + init.data.len() <= memories[init.memory_index.index()].size());
let offset = init.offset; match init.memory_index.local_or_import(module) {
let mem: &mut LinearMemory = &mut memories[init.memory_index.index()]; LocalOrImport::Local(local_memory_index) => {
// let end_of_init = offset + init.data.len(); let memory_desc = &module.memories[local_memory_index];
// if end_of_init > mem.current_size() { let data_top = init.offset + init.data.len();
// let grow_pages = (end_of_init / LinearMemory::PAGE_SIZE as usize) + 1; assert!(memory_desc.min as usize >= data_top);
// mem.grow(grow_pages as u32) let mem: &mut LinearMemory = &mut memories[local_memory_index];
// .expect("failed to grow memory for data initializers");
// } let to_init = &mut mem[init.offset..init.offset + init.data.len()];
let to_init = &mut mem[offset..offset + init.data.len()]; to_init.copy_from_slice(&init.data);
to_init.copy_from_slice(&init.data); }
LocalOrImport::Import(imported_memory_index) => {
let _ = imported_memory_index;
let _ = imports;
unimplemented!()
}
}
} }
memories memories
.iter_mut() .iter_mut()
.map(|mem| mem.into_vm_memory()) .map(|(_, mem)| mem.into_vm_memory())
.collect::<Vec<_>>() .collect::<Map<_, _>>()
.into_boxed_slice() .into_boxed_map()
} }
fn generate_tables(module: &ModuleInner) -> Box<[TableBacking]> { fn generate_tables(module: &ModuleInner) -> BoxedMap<LocalTableIndex, TableBacking> {
let mut tables = Vec::with_capacity(module.tables.len()); let mut tables = Map::with_capacity(module.tables.len());
for (_, table) in &module.tables { for (_, table) in &module.tables {
let table_backing = TableBacking::new(table); let table_backing = TableBacking::new(table);
tables.push(table_backing); tables.push(table_backing);
} }
tables.into_boxed_slice() tables.into_boxed_map()
} }
fn finalize_tables( fn finalize_tables(
module: &ModuleInner, module: &ModuleInner,
imports: &ImportBacking, imports: &ImportBacking,
tables: &mut [TableBacking], tables: &mut SliceMap<LocalTableIndex, TableBacking>,
vmctx: *mut vm::Ctx, vmctx: *mut vm::Ctx,
) -> Box<[vm::LocalTable]> { ) -> BoxedMap<LocalTableIndex, vm::LocalTable> {
for init in &module.table_initializers { for init in &module.table_initializers {
assert!(init.base.is_none(), "global base not supported yet"); let init_base = match init.base {
let table = &mut tables[init.table_index.index()]; Initializer::Const(Value::I32(offset)) => offset as u32,
match table.elements { Initializer::Const(_) => panic!("a const initializer must be the i32 type"),
TableElements::Anyfunc(ref mut elements) => { Initializer::GetGlobal(imported_global_index) => {
for (i, &func_index) in init.elements.iter().enumerate() { if module.imported_globals[imported_global_index].1.desc.ty == Type::I32 {
let sig_index = module.func_assoc[func_index]; unsafe { (*imports.globals[imported_global_index].global).data as u32 }
let sig_id = vm::SigId(sig_index.index() as u32); } else {
panic!("unsupported global type for initialzer")
}
}
} as usize;
let func_data = if module.is_imported_function(func_index) { assert!(
imports.functions[func_index.index()].clone() init_base + init.elements.len()
} else { <= match init.table_index.local_or_import(module) {
vm::ImportedFunc { LocalOrImport::Local(local_table_index) => {
func: module module.tables[local_table_index].min
.func_resolver }
.get(module, func_index) LocalOrImport::Import(imported_table_index) => {
.unwrap() let (_, table_desc) = module.imported_tables[imported_table_index];
.as_ptr(), table_desc.min
vmctx, }
} as usize
);
match init.table_index.local_or_import(module) {
LocalOrImport::Local(local_table_index) => {
let table = &mut tables[local_table_index];
match table.elements {
TableElements::Anyfunc(ref mut elements) => {
for (i, &func_index) in init.elements.iter().enumerate() {
let sig_index = module.func_assoc[func_index];
let sig_id = vm::SigId(sig_index.index() as u32);
let func_data = match func_index.local_or_import(module) {
LocalOrImport::Local(local_func_index) => vm::ImportedFunc {
func: module
.func_resolver
.get(module, local_func_index)
.unwrap()
.as_ptr(),
vmctx,
},
LocalOrImport::Import(imported_func_index) => {
imports.functions[imported_func_index].clone()
}
};
elements[init_base + i] = vm::Anyfunc { func_data, sig_id };
} }
}; }
}
}
LocalOrImport::Import(imported_table_index) => {
let imported_table = &imports.tables[imported_table_index];
elements[init.offset + i] = vm::Anyfunc { func_data, sig_id }; let imported_local_table_slice = unsafe {
let imported_local_table = (*imported_table).table;
slice::from_raw_parts_mut(
(*imported_local_table).base as *mut vm::Anyfunc,
(*imported_local_table).current_elements,
)
};
let (_, table_description) = module.imported_tables[imported_table_index];
match table_description.ty {
ElementType::Anyfunc => {
for (i, &func_index) in init.elements.iter().enumerate() {
let sig_index = module.func_assoc[func_index];
let sig_id = vm::SigId(sig_index.index() as u32);
let func_data = match func_index.local_or_import(module) {
LocalOrImport::Local(local_func_index) => vm::ImportedFunc {
func: module
.func_resolver
.get(module, local_func_index)
.unwrap()
.as_ptr(),
vmctx,
},
LocalOrImport::Import(imported_func_index) => {
imports.functions[imported_func_index].clone()
}
};
imported_local_table_slice[init_base + i] =
vm::Anyfunc { func_data, sig_id };
}
}
} }
} }
} }
@ -142,34 +228,29 @@ impl LocalBacking {
tables tables
.iter_mut() .iter_mut()
.map(|table| table.into_vm_table()) .map(|(_, table)| table.into_vm_table())
.collect::<Vec<_>>() .collect::<Map<_, _>>()
.into_boxed_slice() .into_boxed_map()
} }
fn generate_globals(module: &ModuleInner) -> Box<[vm::LocalGlobal]> { fn generate_globals(module: &ModuleInner) -> BoxedMap<LocalGlobalIndex, vm::LocalGlobal> {
let globals = vec![vm::LocalGlobal::null(); module.globals.len()]; let mut globals = Map::with_capacity(module.globals.len());
globals.into_boxed_slice() globals.resize(module.globals.len(), vm::LocalGlobal::null());
globals.into_boxed_map()
} }
fn finalize_globals( fn finalize_globals(
module: &ModuleInner, module: &ModuleInner,
imports: &ImportBacking, mut globals: BoxedMap<LocalGlobalIndex, vm::LocalGlobal>,
mut globals: Box<[vm::LocalGlobal]>, ) -> BoxedMap<LocalGlobalIndex, vm::LocalGlobal> {
) -> Box<[vm::LocalGlobal]> { for ((_, to), (_, from)) in globals.iter_mut().zip(module.globals.into_iter()) {
for (to, (global_index, from)) in globals.iter_mut().zip(module.globals.into_iter()) {
to.data = match from.init { to.data = match from.init {
Initializer::Const(Value::I32(x)) => x as u64, Value::I32(x) => x as u64,
Initializer::Const(Value::I64(x)) => x as u64, Value::I64(x) => x as u64,
Initializer::Const(Value::F32(x)) => x.to_bits() as u64, Value::F32(x) => x.to_bits() as u64,
Initializer::Const(Value::F64(x)) => x.to_bits(), Value::F64(x) => x.to_bits(),
Initializer::GetGlobal(index) => unsafe {
(*imports.globals[index.index()].global).data
},
Initializer::Import => unsafe {
(*imports.globals[global_index.index()].global).data
},
}; };
} }
@ -179,10 +260,10 @@ impl LocalBacking {
#[derive(Debug)] #[derive(Debug)]
pub struct ImportBacking { pub struct ImportBacking {
pub functions: Box<[vm::ImportedFunc]>, pub functions: BoxedMap<ImportedFuncIndex, vm::ImportedFunc>,
pub memories: Box<[vm::ImportedMemory]>, pub memories: BoxedMap<ImportedMemoryIndex, vm::ImportedMemory>,
pub tables: Box<[vm::ImportedTable]>, pub tables: BoxedMap<ImportedTableIndex, vm::ImportedTable>,
pub globals: Box<[vm::ImportedGlobal]>, pub globals: BoxedMap<ImportedGlobalIndex, vm::ImportedGlobal>,
} }
impl ImportBacking { impl ImportBacking {
@ -200,12 +281,56 @@ impl ImportBacking {
} }
} }
fn import_functions(
module: &ModuleInner,
imports: &mut Imports,
vmctx: *mut vm::Ctx,
) -> Result<BoxedMap<ImportedFuncIndex, vm::ImportedFunc>, String> {
let mut functions = Map::with_capacity(module.imported_functions.len());
for (index, ImportName { namespace, name }) in &module.imported_functions {
let sig_index = module.func_assoc[index.convert_up(module)];
let expected_sig = module.sig_registry.lookup_func_sig(sig_index);
let import = imports
.get_namespace(namespace)
.and_then(|namespace| namespace.get_export(name));
match import {
Some(Export::Function {
func,
ctx,
signature,
}) => {
if expected_sig == &signature {
functions.push(vm::ImportedFunc {
func: func.inner(),
vmctx: match ctx {
Context::External(ctx) => ctx,
Context::Internal => vmctx,
},
});
} else {
return Err(format!(
"unexpected signature for {:?}:{:?}",
namespace, name
));
}
}
Some(_) => {
return Err(format!("incorrect import type for {}:{}", namespace, name));
}
None => {
return Err(format!("import not found: {}:{}", namespace, name));
}
}
}
Ok(functions.into_boxed_map())
}
fn import_memories( fn import_memories(
module: &ModuleInner, module: &ModuleInner,
imports: &mut Imports, imports: &mut Imports,
vmctx: *mut vm::Ctx, vmctx: *mut vm::Ctx,
) -> Result<Box<[vm::ImportedMemory]>, String> { ) -> Result<BoxedMap<ImportedMemoryIndex, vm::ImportedMemory>, String> {
let mut memories = Vec::with_capacity(module.imported_memories.len()); let mut memories = Map::with_capacity(module.imported_memories.len());
for (_index, (ImportName { namespace, name }, expected_memory_desc)) in for (_index, (ImportName { namespace, name }, expected_memory_desc)) in
&module.imported_memories &module.imported_memories
{ {
@ -241,15 +366,15 @@ fn import_memories(
} }
} }
} }
Ok(memories.into_boxed_slice()) Ok(memories.into_boxed_map())
} }
fn import_tables( fn import_tables(
module: &ModuleInner, module: &ModuleInner,
imports: &mut Imports, imports: &mut Imports,
vmctx: *mut vm::Ctx, vmctx: *mut vm::Ctx,
) -> Result<Box<[vm::ImportedTable]>, String> { ) -> Result<BoxedMap<ImportedTableIndex, vm::ImportedTable>, String> {
let mut tables = Vec::with_capacity(module.imported_tables.len()); let mut tables = Map::with_capacity(module.imported_tables.len());
for (_index, (ImportName { namespace, name }, expected_table_desc)) in &module.imported_tables { for (_index, (ImportName { namespace, name }, expected_table_desc)) in &module.imported_tables {
let table_import = imports let table_import = imports
.get_namespace(namespace) .get_namespace(namespace)
@ -283,65 +408,21 @@ fn import_tables(
} }
} }
} }
Ok(tables.into_boxed_slice()) Ok(tables.into_boxed_map())
}
fn import_functions(
module: &ModuleInner,
imports: &mut Imports,
vmctx: *mut vm::Ctx,
) -> Result<Box<[vm::ImportedFunc]>, String> {
let mut functions = Vec::with_capacity(module.imported_functions.len());
for (index, ImportName { namespace, name }) in &module.imported_functions {
let sig_index = module.func_assoc[index];
let expected_sig = module.sig_registry.lookup_func_sig(sig_index);
let import = imports
.get_namespace(namespace)
.and_then(|namespace| namespace.get_export(name));
match import {
Some(Export::Function {
func,
ctx,
signature,
}) => {
if expected_sig == &signature {
functions.push(vm::ImportedFunc {
func: func.inner(),
vmctx: match ctx {
Context::External(ctx) => ctx,
Context::Internal => vmctx,
},
});
} else {
return Err(format!(
"unexpected signature for {:?}:{:?}",
namespace, name
));
}
}
Some(_) => {
return Err(format!("incorrect import type for {}:{}", namespace, name));
}
None => {
return Err(format!("import not found: {}:{}", namespace, name));
}
}
}
Ok(functions.into_boxed_slice())
} }
fn import_globals( fn import_globals(
module: &ModuleInner, module: &ModuleInner,
imports: &mut Imports, imports: &mut Imports,
) -> Result<Box<[vm::ImportedGlobal]>, String> { ) -> Result<BoxedMap<ImportedGlobalIndex, vm::ImportedGlobal>, String> {
let mut globals = Vec::with_capacity(module.imported_globals.len()); let mut globals = Map::with_capacity(module.imported_globals.len());
for (_, (ImportName { namespace, name }, global_desc)) in &module.imported_globals { for (_, (ImportName { namespace, name }, imported_global)) in &module.imported_globals {
let import = imports let import = imports
.get_namespace(namespace) .get_namespace(namespace)
.and_then(|namespace| namespace.get_export(name)); .and_then(|namespace| namespace.get_export(name));
match import { match import {
Some(Export::Global { local, global }) => { Some(Export::Global { local, global }) => {
if &global == global_desc { if global == imported_global.desc {
globals.push(vm::ImportedGlobal { globals.push(vm::ImportedGlobal {
global: local.inner(), global: local.inner(),
}); });
@ -360,5 +441,5 @@ fn import_globals(
} }
} }
} }
Ok(globals.into_boxed_slice()) Ok(globals.into_boxed_map())
} }

View File

@ -7,7 +7,7 @@ use crate::{
import::{Imports, Namespace}, import::{Imports, Namespace},
module::{ExportIndex, Module, ModuleInner}, module::{ExportIndex, Module, ModuleInner},
types::{ types::{
FuncIndex, FuncSig, GlobalDesc, GlobalIndex, MapIndex, Memory, MemoryIndex, Table, FuncIndex, FuncSig, GlobalDesc, GlobalIndex, LocalOrImport, Memory, MemoryIndex, Table,
TableIndex, Type, Value, TableIndex, Type, Value,
}, },
vm, vm,
@ -202,22 +202,23 @@ impl InstanceInner {
.get(func_index) .get(func_index)
.expect("broken invariant, incorrect func index"); .expect("broken invariant, incorrect func index");
let (func_ptr, ctx) = if module.is_imported_function(func_index) { let (func_ptr, ctx) = match func_index.local_or_import(module) {
let imported_func = &self.import_backing.functions[func_index.index()]; LocalOrImport::Local(local_func_index) => (
(
imported_func.func as *const _,
Context::External(imported_func.vmctx),
)
} else {
(
module module
.func_resolver .func_resolver
.get(&module, func_index) .get(&module, local_func_index)
.expect("broken invariant, func resolver not synced with module.exports") .expect("broken invariant, func resolver not synced with module.exports")
.cast() .cast()
.as_ptr() as *const _, .as_ptr() as *const _,
Context::Internal, Context::Internal,
) ),
LocalOrImport::Import(imported_func_index) => {
let imported_func = &self.import_backing.functions[imported_func_index];
(
imported_func.func as *const _,
Context::External(imported_func.vmctx),
)
}
}; };
let signature = module.sig_registry.lookup_func_sig(sig_index).clone(); let signature = module.sig_registry.lookup_func_sig(sig_index).clone();
@ -230,28 +231,31 @@ impl InstanceInner {
module: &ModuleInner, module: &ModuleInner,
mem_index: MemoryIndex, mem_index: MemoryIndex,
) -> (MemoryPointer, Context, Memory) { ) -> (MemoryPointer, Context, Memory) {
if module.is_imported_memory(mem_index) { match mem_index.local_or_import(module) {
let &(_, mem) = &module LocalOrImport::Local(local_mem_index) => {
.imported_memories let vm_mem = &mut self.backing.memories[local_mem_index];
.get(mem_index) (
.expect("missing imported memory index"); unsafe { MemoryPointer::new(&mut vm_mem.into_vm_memory()) },
let vm::ImportedMemory { memory, vmctx } = Context::Internal,
&self.import_backing.memories[mem_index.index()]; *module
( .memories
unsafe { MemoryPointer::new(*memory) }, .get(local_mem_index)
Context::External(*vmctx), .expect("broken invariant, memories"),
*mem, )
) }
} else { LocalOrImport::Import(imported_mem_index) => {
let vm_mem = &mut self.backing.memories[mem_index.index() as usize]; let &(_, mem) = &module
( .imported_memories
unsafe { MemoryPointer::new(&mut vm_mem.into_vm_memory()) }, .get(imported_mem_index)
Context::Internal, .expect("missing imported memory index");
*module let vm::ImportedMemory { memory, vmctx } =
.memories &self.import_backing.memories[imported_mem_index];
.get(mem_index) (
.expect("broken invariant, memories"), unsafe { MemoryPointer::new(*memory) },
) Context::External(*vmctx),
*mem,
)
}
} }
} }
@ -260,23 +264,27 @@ impl InstanceInner {
module: &ModuleInner, module: &ModuleInner,
global_index: GlobalIndex, global_index: GlobalIndex,
) -> (GlobalPointer, GlobalDesc) { ) -> (GlobalPointer, GlobalDesc) {
if module.is_imported_global(global_index) { match global_index.local_or_import(module) {
let &(_, desc) = &module LocalOrImport::Local(local_global_index) => {
.imported_globals let vm_global = &mut self.backing.vm_globals[local_global_index];
.get(global_index) (
.expect("missing imported global index"); unsafe { GlobalPointer::new(vm_global) },
let vm::ImportedGlobal { global } = &self.import_backing.globals[global_index.index()]; module
(unsafe { GlobalPointer::new(*global) }, *desc) .globals
} else { .get(local_global_index)
let vm_global = &mut self.backing.vm_globals[global_index.index() as usize]; .expect("broken invariant, globals")
( .desc,
unsafe { GlobalPointer::new(vm_global) }, )
module }
.globals LocalOrImport::Import(imported_global_index) => {
.get(global_index) let &(_, imported_global) = &module
.expect("broken invariant, globals") .imported_globals
.desc, .get(imported_global_index)
) .expect("missing imported global index");
let vm::ImportedGlobal { global } =
&self.import_backing.globals[imported_global_index];
(unsafe { GlobalPointer::new(*global) }, imported_global.desc)
}
} }
} }
@ -285,28 +293,31 @@ impl InstanceInner {
module: &ModuleInner, module: &ModuleInner,
table_index: TableIndex, table_index: TableIndex,
) -> (TablePointer, Context, Table) { ) -> (TablePointer, Context, Table) {
if module.is_imported_table(table_index) { match table_index.local_or_import(module) {
let &(_, tab) = &module LocalOrImport::Local(local_table_index) => {
.imported_tables let vm_table = &mut self.backing.tables[local_table_index];
.get(table_index) (
.expect("missing imported table index"); unsafe { TablePointer::new(&mut vm_table.into_vm_table()) },
let vm::ImportedTable { table, vmctx } = Context::Internal,
&self.import_backing.tables[table_index.index()]; *module
( .tables
unsafe { TablePointer::new(*table) }, .get(local_table_index)
Context::External(*vmctx), .expect("broken invariant, tables"),
*tab, )
) }
} else { LocalOrImport::Import(imported_table_index) => {
let vm_table = &mut self.backing.tables[table_index.index() as usize]; let &(_, tab) = &module
( .imported_tables
unsafe { TablePointer::new(&mut vm_table.into_vm_table()) }, .get(imported_table_index)
Context::Internal, .expect("missing imported table index");
*module let vm::ImportedTable { table, vmctx } =
.tables &self.import_backing.tables[imported_table_index];
.get(table_index) (
.expect("broken invariant, tables"), unsafe { TablePointer::new(*table) },
) Context::External(*vmctx),
*tab,
)
}
} }
} }
} }

View File

@ -16,6 +16,7 @@ pub mod module;
mod recovery; mod recovery;
mod sig_registry; mod sig_registry;
mod sighandler; mod sighandler;
pub mod structures;
pub mod table; pub mod table;
pub mod types; pub mod types;
pub mod vm; pub mod vm;

View File

@ -93,7 +93,7 @@ impl LinearMemory {
self.mmap.as_ptr() self.mmap.as_ptr()
} }
/// Returns a number of allocated wasm pages. /// Returns the size in bytes
pub(crate) fn size(&self) -> usize { pub(crate) fn size(&self) -> usize {
self.current as usize * Self::PAGE_SIZE as usize self.current as usize * Self::PAGE_SIZE as usize
} }

View File

@ -2,9 +2,11 @@ use crate::{
backend::FuncResolver, backend::FuncResolver,
import::Imports, import::Imports,
sig_registry::SigRegistry, sig_registry::SigRegistry,
structures::Map,
types::{ types::{
FuncIndex, Global, GlobalDesc, GlobalIndex, Map, MapIndex, Memory, MemoryIndex, SigIndex, FuncIndex, Global, GlobalIndex, ImportedFuncIndex, ImportedGlobal, ImportedGlobalIndex,
Table, TableIndex, ImportedMemoryIndex, ImportedTableIndex, Initializer, LocalGlobalIndex, LocalMemoryIndex,
LocalTableIndex, Memory, MemoryIndex, SigIndex, Table, TableIndex,
}, },
Instance, Instance,
}; };
@ -15,14 +17,16 @@ use std::rc::Rc;
#[doc(hidden)] #[doc(hidden)]
pub struct ModuleInner { pub struct ModuleInner {
pub func_resolver: Box<dyn FuncResolver>, pub func_resolver: Box<dyn FuncResolver>,
pub memories: Map<MemoryIndex, Memory>, // This are strictly local and the typsystem ensures that.
pub globals: Map<GlobalIndex, Global>, pub memories: Map<LocalMemoryIndex, Memory>,
pub tables: Map<TableIndex, Table>, pub globals: Map<LocalGlobalIndex, Global>,
pub tables: Map<LocalTableIndex, Table>,
pub imported_functions: Map<FuncIndex, ImportName>, // These are strictly imported and the typesystem ensures that.
pub imported_memories: Map<MemoryIndex, (ImportName, Memory)>, pub imported_functions: Map<ImportedFuncIndex, ImportName>,
pub imported_tables: Map<TableIndex, (ImportName, Table)>, pub imported_memories: Map<ImportedMemoryIndex, (ImportName, Memory)>,
pub imported_globals: Map<GlobalIndex, (ImportName, GlobalDesc)>, pub imported_tables: Map<ImportedTableIndex, (ImportName, Table)>,
pub imported_globals: Map<ImportedGlobalIndex, (ImportName, ImportedGlobal)>,
pub exports: HashMap<String, ExportIndex>, pub exports: HashMap<String, ExportIndex>,
@ -47,23 +51,7 @@ impl Module {
} }
} }
impl ModuleInner { impl ModuleInner {}
pub(crate) fn is_imported_function(&self, func_index: FuncIndex) -> bool {
func_index.index() < self.imported_functions.len()
}
pub(crate) fn is_imported_memory(&self, memory_index: MemoryIndex) -> bool {
memory_index.index() < self.imported_memories.len()
}
pub(crate) fn is_imported_table(&self, table_index: TableIndex) -> bool {
table_index.index() < self.imported_tables.len()
}
pub(crate) fn is_imported_global(&self, global_index: GlobalIndex) -> bool {
global_index.index() < self.imported_globals.len()
}
}
#[doc(hidden)] #[doc(hidden)]
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -90,7 +78,7 @@ pub enum ExportIndex {
} }
/// A data initializer for linear memory. /// A data initializer for linear memory.
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct DataInitializer { pub struct DataInitializer {
/// The index of the memory to initialize. /// The index of the memory to initialize.
pub memory_index: MemoryIndex, pub memory_index: MemoryIndex,
@ -103,14 +91,12 @@ pub struct DataInitializer {
} }
/// A WebAssembly table initializer. /// A WebAssembly table initializer.
#[derive(Clone, Debug)] #[derive(Debug, Clone)]
pub struct TableInitializer { pub struct TableInitializer {
/// The index of a table to initialize. /// The index of a table to initialize.
pub table_index: TableIndex, pub table_index: TableIndex,
/// Optionally, a global variable giving a base index. /// Either a constant offset or a `get_global`
pub base: Option<GlobalIndex>, pub base: Initializer,
/// The offset to add to the base.
pub offset: usize,
/// The values to write into the table elements. /// The values to write into the table elements.
pub elements: Vec<FuncIndex>, pub elements: Vec<FuncIndex>,
} }

View File

@ -1,4 +1,7 @@
use crate::types::{FuncSig, Map, SigIndex}; use crate::{
structures::Map,
types::{FuncSig, SigIndex},
};
use hashbrown::HashMap; use hashbrown::HashMap;
#[derive(Debug)] #[derive(Debug)]

View File

@ -0,0 +1,46 @@
use super::{SliceMap, TypedIndex};
use std::{
marker::PhantomData,
mem,
ops::{Deref, DerefMut},
};
#[derive(Debug, Clone)]
pub struct BoxedMap<K, V>
where
K: TypedIndex,
{
elems: Box<[V]>,
_marker: PhantomData<K>,
}
impl<K, V> BoxedMap<K, V>
where
K: TypedIndex,
{
pub(in crate::structures) fn new(elems: Box<[V]>) -> Self {
Self {
elems,
_marker: PhantomData,
}
}
}
impl<K, V> Deref for BoxedMap<K, V>
where
K: TypedIndex,
{
type Target = SliceMap<K, V>;
fn deref(&self) -> &SliceMap<K, V> {
unsafe { mem::transmute::<&[V], _>(&*self.elems) }
}
}
impl<K, V> DerefMut for BoxedMap<K, V>
where
K: TypedIndex,
{
fn deref_mut(&mut self) -> &mut SliceMap<K, V> {
unsafe { mem::transmute::<&mut [V], _>(&mut *self.elems) }
}
}

View File

@ -0,0 +1,174 @@
use super::{BoxedMap, SliceMap, TypedIndex};
use std::{
iter::{self, Extend, FromIterator},
marker::PhantomData,
mem,
ops::{Deref, DerefMut},
slice,
};
/// Dense item map
#[derive(Debug, Clone)]
pub struct Map<K, V>
where
K: TypedIndex,
{
elems: Vec<V>,
_marker: PhantomData<K>,
}
impl<K, V> Map<K, V>
where
K: TypedIndex,
{
pub fn new() -> Self {
Self {
elems: Vec::new(),
_marker: PhantomData,
}
}
pub fn with_capacity(capacity: usize) -> Self {
Self {
elems: Vec::with_capacity(capacity),
_marker: PhantomData,
}
}
pub fn len(&self) -> usize {
self.elems.len()
}
pub fn push(&mut self, value: V) -> K {
let len = self.len();
self.elems.push(value);
K::new(len)
}
pub fn reserve_exact(&mut self, size: usize) {
self.elems.reserve_exact(size);
}
pub fn into_boxed_map(self) -> BoxedMap<K, V> {
BoxedMap::new(self.elems.into_boxed_slice())
}
}
impl<K, V> Map<K, V>
where
K: TypedIndex,
V: Clone,
{
pub fn resize(&mut self, new_len: usize, value: V) {
self.elems.resize(new_len, value);
}
}
impl<K, V> Extend<V> for Map<K, V>
where
K: TypedIndex,
{
fn extend<I: IntoIterator<Item = V>>(&mut self, iter: I) {
self.elems.extend(iter);
}
}
impl<K, V> FromIterator<V> for Map<K, V>
where
K: TypedIndex,
{
fn from_iter<I: IntoIterator<Item = V>>(iter: I) -> Self {
let elems: Vec<V> = iter.into_iter().collect();
Self {
elems,
_marker: PhantomData,
}
}
}
impl<K, V> Deref for Map<K, V>
where
K: TypedIndex,
{
type Target = SliceMap<K, V>;
fn deref(&self) -> &SliceMap<K, V> {
unsafe { mem::transmute::<&[V], _>(self.elems.as_slice()) }
}
}
impl<K, V> DerefMut for Map<K, V>
where
K: TypedIndex,
{
fn deref_mut(&mut self) -> &mut SliceMap<K, V> {
unsafe { mem::transmute::<&mut [V], _>(self.elems.as_mut_slice()) }
}
}
impl<'a, K, V> IntoIterator for &'a Map<K, V>
where
K: TypedIndex,
{
type Item = (K, &'a V);
type IntoIter = Iter<'a, K, V>;
fn into_iter(self) -> Self::IntoIter {
Iter::new(self.elems.iter())
}
}
impl<'a, K, V> IntoIterator for &'a mut Map<K, V>
where
K: TypedIndex,
{
type Item = (K, &'a mut V);
type IntoIter = IterMut<'a, K, V>;
fn into_iter(self) -> Self::IntoIter {
IterMut::new(self.elems.iter_mut())
}
}
pub struct Iter<'a, K: TypedIndex, V: 'a> {
enumerated: iter::Enumerate<slice::Iter<'a, V>>,
_marker: PhantomData<K>,
}
impl<'a, K: TypedIndex, V: 'a> Iter<'a, K, V> {
pub(in crate::structures) fn new(iter: slice::Iter<'a, V>) -> Self {
Self {
enumerated: iter.enumerate(),
_marker: PhantomData,
}
}
}
impl<'a, K: TypedIndex, V: 'a> Iterator for Iter<'a, K, V> {
type Item = (K, &'a V);
fn next(&mut self) -> Option<Self::Item> {
self.enumerated.next().map(|(i, v)| (K::new(i), v))
}
}
pub struct IterMut<'a, K: TypedIndex, V: 'a> {
enumerated: iter::Enumerate<slice::IterMut<'a, V>>,
_marker: PhantomData<K>,
}
impl<'a, K: TypedIndex, V: 'a> IterMut<'a, K, V> {
pub(in crate::structures) fn new(iter: slice::IterMut<'a, V>) -> Self {
Self {
enumerated: iter.enumerate(),
_marker: PhantomData,
}
}
}
impl<'a, K: TypedIndex, V: 'a> Iterator for IterMut<'a, K, V> {
type Item = (K, &'a mut V);
fn next(&mut self) -> Option<Self::Item> {
self.enumerated.next().map(|(i, v)| (K::new(i), v))
}
}

View File

@ -0,0 +1,12 @@
mod boxed;
mod map;
mod slice;
pub use self::boxed::BoxedMap;
pub use self::map::{Iter, IterMut, Map};
pub use self::slice::SliceMap;
pub trait TypedIndex {
fn new(index: usize) -> Self;
fn index(&self) -> usize;
}

View File

@ -0,0 +1,86 @@
#[derive(Debug, Clone)]
enum MonoVecInner<T> {
None,
Inline(T),
Heap(Vec<T>),
}
/// A type that can hold zero items,
/// one item, or many items.
#[derive(Debug, Clone)]
pub struct MonoVec<T> {
inner: MonoVecInner<T>,
}
impl<T> MonoVec<T> {
pub fn new() -> Self {
Self {
inner: MonoVecInner::None,
}
}
pub fn new_inline(item: T) -> Self {
Self {
inner: MonoVecInner::Inline(item),
}
}
pub fn with_capacity(capacity: usize) -> Self {
match capacity {
0 | 1 => Self::new(),
_ => Self {
inner: MonoVecInner::Heap(Vec::with_capacity(capacity)),
},
}
}
pub fn push(&mut self, item: T) {
let uninit = unsafe { mem::uninitialized() };
let prev = mem::replace(&mut self.inner, uninit);
let next = match prev {
MonoVecInner::None => MonoVecInner::Inline(item),
MonoVecInner::Inline(previous_item) => MonoVecInner::Heap(vec![previous_item, item]),
MonoVecInner::Heap(mut v) => {
v.push(item);
MonoVecInner::Heap(v)
}
};
let uninit = mem::replace(&mut self.inner, next);
mem::forget(uninit);
}
pub fn pop(&mut self) -> Option<T> {
match self.inner {
MonoVecInner::None => None,
MonoVecInner::Inline(ref mut item) => {
let uninit = unsafe { mem::uninitialized() };
let item = mem::replace(item, uninit);
let uninit = mem::replace(&mut self.inner, MonoVecInner::None);
mem::forget(uninit);
Some(item)
}
MonoVecInner::Heap(ref mut v) => v.pop(),
}
}
pub fn as_slice(&self) -> &[T] {
match self.inner {
MonoVecInner::None => unsafe {
slice::from_raw_parts(mem::align_of::<T>() as *const T, 0)
},
MonoVecInner::Inline(ref item) => slice::from_ref(item),
MonoVecInner::Heap(ref v) => &v[..],
}
}
pub fn as_slice_mut(&mut self) -> &mut [T] {
match self.inner {
MonoVecInner::None => unsafe {
slice::from_raw_parts_mut(mem::align_of::<T>() as *mut T, 0)
},
MonoVecInner::Inline(ref mut item) => slice::from_mut(item),
MonoVecInner::Heap(ref mut v) => &mut v[..],
}
}
}

View File

@ -0,0 +1,68 @@
use super::{Iter, IterMut, TypedIndex};
use std::{
marker::PhantomData,
ops::{Index, IndexMut},
};
/// This is a dynamically-sized slice
/// that can only be indexed by the
/// correct index type.
pub struct SliceMap<K, V>
where
K: TypedIndex,
{
_marker: PhantomData<K>,
slice: [V],
}
impl<K, V> SliceMap<K, V>
where
K: TypedIndex,
{
pub fn get(&self, index: K) -> Option<&V> {
self.slice.get(index.index())
}
pub fn get_mut(&mut self, index: K) -> Option<&mut V> {
self.slice.get_mut(index.index())
}
pub fn len(&self) -> usize {
self.slice.len()
}
pub fn iter(&self) -> Iter<K, V> {
Iter::new(self.slice.iter())
}
pub fn iter_mut(&mut self) -> IterMut<K, V> {
IterMut::new(self.slice.iter_mut())
}
pub fn as_ptr(&self) -> *const V {
self as *const SliceMap<K, V> as *const V
}
pub fn as_mut_ptr(&mut self) -> *mut V {
self as *mut SliceMap<K, V> as *mut V
}
}
impl<K, V> Index<K> for SliceMap<K, V>
where
K: TypedIndex,
{
type Output = V;
fn index(&self, index: K) -> &V {
&self.slice[index.index()]
}
}
impl<K, V> IndexMut<K> for SliceMap<K, V>
where
K: TypedIndex,
{
fn index_mut(&mut self, index: K) -> &mut V {
&mut self.slice[index.index()]
}
}

View File

@ -1,9 +1,4 @@
use std::marker::PhantomData; use crate::{module::ModuleInner, structures::TypedIndex};
use std::{
iter, mem,
ops::{Index, IndexMut},
slice,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Type { pub enum Type {
@ -82,21 +77,22 @@ pub struct Table {
impl Table { impl Table {
pub(crate) fn fits_in_imported(&self, imported: &Table) -> bool { pub(crate) fn fits_in_imported(&self, imported: &Table) -> bool {
self.max == imported.max && self.min <= imported.min // TODO: We should define implementation limits.
let imported_max = imported.max.unwrap_or(u32::max_value());
let self_max = self.max.unwrap_or(u32::max_value());
self.ty == imported.ty && imported_max <= self_max && self.min <= imported.min
} }
} }
/// A global value initializer. /// A const value initializer.
/// Overtime, this will be able to represent more and more /// Over time, this will be able to represent more and more
/// complex expressions. /// complex expressions.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum Initializer { pub enum Initializer {
/// Corresponds to a `const.*` instruction. /// Corresponds to a `const.*` instruction.
Const(Value), Const(Value),
/// Corresponds to a `get_global` instruction. /// Corresponds to a `get_global` instruction.
GetGlobal(GlobalIndex), GetGlobal(ImportedGlobalIndex),
/// Initialized externally
Import,
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
@ -105,11 +101,23 @@ pub struct GlobalDesc {
pub ty: Type, pub ty: Type,
} }
#[derive(Debug, Clone, PartialEq)]
pub enum ImportedGlobalInit {
GetGlobal(ImportedGlobalIndex),
Import,
}
#[derive(Debug, Clone, PartialEq)]
pub struct ImportedGlobal {
pub desc: GlobalDesc,
pub init: ImportedGlobalInit,
}
/// A wasm global. /// A wasm global.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Global { pub struct Global {
pub desc: GlobalDesc, pub desc: GlobalDesc,
pub init: Initializer, pub init: Value,
} }
/// A wasm memory. /// A wasm memory.
@ -129,11 +137,15 @@ impl Memory {
} }
pub(crate) fn fits_in_imported(&self, imported: &Memory) -> bool { pub(crate) fn fits_in_imported(&self, imported: &Memory) -> bool {
self.shared == imported.shared && self.max == imported.max && self.min <= imported.min let imported_max = imported.max.unwrap_or(65_536);
let self_max = self.max.unwrap_or(65_536);
self.shared == imported.shared && imported_max <= self_max && self.min <= imported.min
} }
} }
/// A wasm func. /// The signature of a function that is either implemented
/// in a wasm module or exposed to wasm by the host.
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct FuncSig { pub struct FuncSig {
pub params: Vec<Type>, pub params: Vec<Type>,
@ -151,158 +163,17 @@ impl FuncSig {
} }
} }
pub trait MapIndex { pub trait LocalImport {
fn new(index: usize) -> Self; type Local: TypedIndex;
fn index(&self) -> usize; type Import: TypedIndex;
}
/// Dense item map
#[derive(Debug, Clone)]
pub struct Map<I, T>
where
I: MapIndex,
{
elems: Vec<T>,
_marker: PhantomData<I>,
}
impl<I, T> Map<I, T>
where
I: MapIndex,
{
pub fn new() -> Self {
Self {
elems: Vec::new(),
_marker: PhantomData,
}
}
pub fn with_capacity(capacity: usize) -> Self {
Self {
elems: Vec::with_capacity(capacity),
_marker: PhantomData,
}
}
pub fn get(&self, index: I) -> Option<&T> {
self.elems.get(index.index())
}
pub fn len(&self) -> usize {
self.elems.len()
}
pub fn push(&mut self, value: T) -> I {
let len = self.len();
self.elems.push(value);
I::new(len)
}
pub fn as_ptr(&self) -> *const T {
self.elems.as_ptr()
}
pub fn reserve_exact(&mut self, size: usize) {
self.elems.reserve_exact(size);
}
pub fn iter(&self) -> Iter<T, I> {
Iter::new(self.elems.iter())
}
}
impl<I, T> Index<I> for Map<I, T>
where
I: MapIndex,
{
type Output = T;
fn index(&self, index: I) -> &T {
&self.elems[index.index()]
}
}
impl<I, T> IndexMut<I> for Map<I, T>
where
I: MapIndex,
{
fn index_mut(&mut self, index: I) -> &mut T {
&mut self.elems[index.index()]
}
}
impl<'a, I, T> IntoIterator for &'a Map<I, T>
where
I: MapIndex,
{
type Item = (I, &'a T);
type IntoIter = Iter<'a, T, I>;
fn into_iter(self) -> Self::IntoIter {
Iter::new(self.elems.iter())
}
}
impl<'a, I, T> IntoIterator for &'a mut Map<I, T>
where
I: MapIndex,
{
type Item = (I, &'a mut T);
type IntoIter = IterMut<'a, T, I>;
fn into_iter(self) -> Self::IntoIter {
IterMut::new(self.elems.iter_mut())
}
}
pub struct Iter<'a, T: 'a, I: MapIndex> {
enumerated: iter::Enumerate<slice::Iter<'a, T>>,
_marker: PhantomData<I>,
}
impl<'a, T: 'a, I: MapIndex> Iter<'a, T, I> {
fn new(iter: slice::Iter<'a, T>) -> Self {
Self {
enumerated: iter.enumerate(),
_marker: PhantomData,
}
}
}
impl<'a, T: 'a, I: MapIndex> Iterator for Iter<'a, T, I> {
type Item = (I, &'a T);
fn next(&mut self) -> Option<Self::Item> {
self.enumerated.next().map(|(i, v)| (I::new(i), v))
}
}
pub struct IterMut<'a, T: 'a, I: MapIndex> {
enumerated: iter::Enumerate<slice::IterMut<'a, T>>,
_marker: PhantomData<I>,
}
impl<'a, T: 'a, I: MapIndex> IterMut<'a, T, I> {
fn new(iter: slice::IterMut<'a, T>) -> Self {
Self {
enumerated: iter.enumerate(),
_marker: PhantomData,
}
}
}
impl<'a, T: 'a, I: MapIndex> Iterator for IterMut<'a, T, I> {
type Item = (I, &'a mut T);
fn next(&mut self) -> Option<Self::Item> {
self.enumerated.next().map(|(i, v)| (I::new(i), v))
}
} }
#[rustfmt::skip]
macro_rules! define_map_index { macro_rules! define_map_index {
($ty:ident) => { ($ty:ident) => {
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct $ty (u32); pub struct $ty (u32);
impl MapIndex for $ty { impl TypedIndex for $ty {
fn new(index: usize) -> Self { fn new(index: usize) -> Self {
$ty (index as _) $ty (index as _)
} }
@ -312,20 +183,72 @@ macro_rules! define_map_index {
} }
} }
}; };
($($ty:ident,)*) => { ($($normal_ty:ident,)* | local: $($local_ty:ident,)* | imported: $($imported_ty:ident,)*) => {
$( $(
define_map_index!($ty); define_map_index!($normal_ty);
define_map_index!($local_ty);
define_map_index!($imported_ty);
impl LocalImport for $normal_ty {
type Local = $local_ty;
type Import = $imported_ty;
}
)* )*
}; };
} }
define_map_index![FuncIndex, MemoryIndex, TableIndex, SigIndex,]; #[rustfmt::skip]
define_map_index![
FuncIndex, MemoryIndex, TableIndex, GlobalIndex,
| local: LocalFuncIndex, LocalMemoryIndex, LocalTableIndex, LocalGlobalIndex,
| imported: ImportedFuncIndex, ImportedMemoryIndex, ImportedTableIndex, ImportedGlobalIndex,
];
#[rustfmt::skip]
macro_rules! define_local_or_import {
($ty:ident, $local_ty:ident, $imported_ty:ident, $imports:ident) => {
impl $ty {
pub fn local_or_import(self, module: &ModuleInner) -> LocalOrImport<$ty> {
if self.index() < module.$imports.len() {
LocalOrImport::Import(<Self as LocalImport>::Import::new(self.index()))
} else {
LocalOrImport::Local(<Self as LocalImport>::Local::new(self.index() - module.$imports.len()))
}
}
}
impl $local_ty {
pub fn convert_up(self, module: &ModuleInner) -> $ty {
$ty ((self.index() + module.$imports.len()) as u32)
}
}
impl $imported_ty {
pub fn convert_up(self, _module: &ModuleInner) -> $ty {
$ty (self.index() as u32)
}
}
};
($(($ty:ident | ($local_ty:ident, $imported_ty:ident): $imports:ident),)*) => {
$(
define_local_or_import!($ty, $local_ty, $imported_ty, $imports);
)*
};
}
#[rustfmt::skip]
define_local_or_import![
(FuncIndex | (LocalFuncIndex, ImportedFuncIndex): imported_functions),
(MemoryIndex | (LocalMemoryIndex, ImportedMemoryIndex): imported_memories),
(TableIndex | (LocalTableIndex, ImportedTableIndex): imported_tables),
(GlobalIndex | (LocalGlobalIndex, ImportedGlobalIndex): imported_globals),
];
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct GlobalIndex(u32); pub struct SigIndex(u32);
impl MapIndex for GlobalIndex { impl TypedIndex for SigIndex {
fn new(index: usize) -> Self { fn new(index: usize) -> Self {
GlobalIndex(index as _) SigIndex(index as _)
} }
fn index(&self) -> usize { fn index(&self) -> usize {
@ -333,88 +256,10 @@ impl MapIndex for GlobalIndex {
} }
} }
#[derive(Debug, Clone)] pub enum LocalOrImport<T>
enum MonoVecInner<T> { where
None, T: LocalImport,
Inline(T), {
Heap(Vec<T>), Local(T::Local),
} Import(T::Import),
/// A type that can hold zero items,
/// one item, or many items.
#[derive(Debug, Clone)]
pub struct MonoVec<T> {
inner: MonoVecInner<T>,
}
impl<T> MonoVec<T> {
pub fn new() -> Self {
Self {
inner: MonoVecInner::None,
}
}
pub fn new_inline(item: T) -> Self {
Self {
inner: MonoVecInner::Inline(item),
}
}
pub fn with_capacity(capacity: usize) -> Self {
match capacity {
0 | 1 => Self::new(),
_ => Self {
inner: MonoVecInner::Heap(Vec::with_capacity(capacity)),
},
}
}
pub fn push(&mut self, item: T) {
let uninit = unsafe { mem::uninitialized() };
let prev = mem::replace(&mut self.inner, uninit);
let next = match prev {
MonoVecInner::None => MonoVecInner::Inline(item),
MonoVecInner::Inline(previous_item) => MonoVecInner::Heap(vec![previous_item, item]),
MonoVecInner::Heap(mut v) => {
v.push(item);
MonoVecInner::Heap(v)
}
};
let uninit = mem::replace(&mut self.inner, next);
mem::forget(uninit);
}
pub fn pop(&mut self) -> Option<T> {
match self.inner {
MonoVecInner::None => None,
MonoVecInner::Inline(ref mut item) => {
let uninit = unsafe { mem::uninitialized() };
let item = mem::replace(item, uninit);
let uninit = mem::replace(&mut self.inner, MonoVecInner::None);
mem::forget(uninit);
Some(item)
}
MonoVecInner::Heap(ref mut v) => v.pop(),
}
}
pub fn as_slice(&self) -> &[T] {
match self.inner {
MonoVecInner::None => unsafe {
slice::from_raw_parts(mem::align_of::<T>() as *const T, 0)
},
MonoVecInner::Inline(ref item) => slice::from_ref(item),
MonoVecInner::Heap(ref v) => &v[..],
}
}
pub fn as_slice_mut(&mut self) -> &mut [T] {
match self.inner {
MonoVecInner::None => unsafe {
slice::from_raw_parts_mut(mem::align_of::<T>() as *mut T, 0)
},
MonoVecInner::Inline(ref mut item) => slice::from_mut(item),
MonoVecInner::Heap(ref mut v) => &mut v[..],
}
}
} }

View File

@ -201,6 +201,10 @@ impl LocalGlobal {
pub fn null() -> Self { pub fn null() -> Self {
Self { data: 0 } Self { data: 0 }
} }
pub fn size() -> u8 {
mem::size_of::<Self>() as u8
}
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -213,6 +217,10 @@ impl ImportedGlobal {
pub fn offset_global() -> u8 { pub fn offset_global() -> u8 {
0 * (mem::size_of::<usize>() as u8) 0 * (mem::size_of::<usize>() as u8)
} }
pub fn size() -> u8 {
mem::size_of::<Self>() as u8
}
} }
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]

View File

@ -1,7 +1,7 @@
use crate::{memory::LinearMemory, vm}; use crate::{memory::LinearMemory, structures::TypedIndex, types::LocalMemoryIndex, vm};
pub unsafe extern "C" fn memory_grow_static( pub unsafe extern "C" fn memory_grow_static(
memory_index: u32, memory_index: LocalMemoryIndex,
by_pages: u32, by_pages: u32,
ctx: *mut vm::Ctx, ctx: *mut vm::Ctx,
) -> i32 { ) -> i32 {
@ -10,7 +10,7 @@ pub unsafe extern "C" fn memory_grow_static(
.grow_static(by_pages) .grow_static(by_pages)
{ {
// Store the new size back into the vmctx. // Store the new size back into the vmctx.
(*(*ctx).memories.add(memory_index as usize)).size = (*(*ctx).memories.add(memory_index.index())).size =
(old as usize + by_pages as usize) * LinearMemory::PAGE_SIZE as usize; (old as usize + by_pages as usize) * LinearMemory::PAGE_SIZE as usize;
old old
} else { } else {
@ -18,12 +18,12 @@ pub unsafe extern "C" fn memory_grow_static(
} }
} }
pub unsafe extern "C" fn memory_size(memory_index: u32, ctx: *mut vm::Ctx) -> u32 { pub unsafe extern "C" fn memory_size(memory_index: LocalMemoryIndex, ctx: *mut vm::Ctx) -> u32 {
(*(*ctx).local_backing).memory(memory_index).pages() (*(*ctx).local_backing).memory(memory_index).pages()
} }
pub unsafe extern "C" fn memory_grow_dynamic( pub unsafe extern "C" fn memory_grow_dynamic(
memory_index: u32, memory_index: LocalMemoryIndex,
by_pages: u32, by_pages: u32,
ctx: *mut vm::Ctx, ctx: *mut vm::Ctx,
) -> i32 { ) -> i32 {
@ -32,7 +32,7 @@ pub unsafe extern "C" fn memory_grow_dynamic(
.grow_dynamic(by_pages) .grow_dynamic(by_pages)
{ {
// Store the new size back into the vmctx. // Store the new size back into the vmctx.
(*(*ctx).memories.add(memory_index as usize)).size = (*(*ctx).memories.add(memory_index.index())).size =
(old as usize + by_pages as usize) * LinearMemory::PAGE_SIZE as usize; (old as usize + by_pages as usize) * LinearMemory::PAGE_SIZE as usize;
old old
} else { } else {