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::{
ElementType as WasmerElementType, FuncIndex as WasmerFuncIndex, FuncSig as WasmerSignature,
Global as WasmerGlobal, GlobalDesc as WasmerGlobalDesc, GlobalIndex as WasmerGlobalIndex,
Initializer as WasmerInitializer, Map, MapIndex, Memory as WasmerMemory,
MemoryIndex as WasmerMemoryIndex, SigIndex as WasmerSignatureIndex, Table as WasmerTable,
TableIndex as WasmerTableIndex, Type as WasmerType,
GlobalInit as WasmerGlobalInit, Initializer as WasmerInitializer, Map,
Memory as WasmerMemory, MemoryIndex as WasmerMemoryIndex, SigIndex as WasmerSignatureIndex,
Table as WasmerTable, TableIndex as WasmerTableIndex, Type as WasmerType, TypedIndex,
},
vm::{self, Ctx as WasmerVMContext},
};
@ -142,14 +142,14 @@ pub mod converter {
// TODO: WasmerGlobal does not support `Import` as Global values.
let init = match global.initializer {
I32Const(val) => Const(val.into()),
I64Const(val) => Const(val.into()),
F32Const(val) => Const(f32::from_bits(val).into()),
F64Const(val) => Const(f64::from_bits(val).into()),
GlobalInit::GetGlobal(index) => {
WasmerInitializer::GetGlobal(WasmerGlobalIndex::new(index.index()))
},
GlobalInit::Import => WasmerInitializer::Import
I32Const(val) => WasmerGlobalInit::Init(Const(val.into())),
I64Const(val) => WasmerGlobalInit::Init(Const(val.into())),
F32Const(val) => WasmerGlobalInit::Init(Const(f32::from_bits(val).into())),
F64Const(val) => WasmerGlobalInit::Init(Const(f64::from_bits(val).into())),
GlobalInit::GetGlobal(index) => WasmerGlobalInit::Init(WasmerInitializer::GetGlobal(
WasmerGlobalIndex::new(index.index()),
)),
GlobalInit::Import => WasmerGlobalInit::Import,
};
WasmerGlobal { desc, init }
@ -173,6 +173,7 @@ pub mod converter {
/// Converts a Cranelift table to a Wasmer table.
pub fn convert_memory(memory: Memory) -> WasmerMemory {
println!("codegen memory: {:?}", memory);
WasmerMemory {
shared: memory.shared,
min: memory.minimum,
@ -350,29 +351,71 @@ impl<'environment> FuncEnvironmentTrait for FuncEnvironment<'environment> {
/// by `index`.
///
/// 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.
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.
let globals_base_addr = func.create_global_value(ir::GlobalValueData::Load {
base: vmctx,
offset: Offset32::new(globals_offset as i32),
global_type: self.pointer_type(),
readonly: false,
});
if global_index.index() < self.module.imported_globals.len() {
// imported global
// *Ctx.globals -> [ u8, u8, .. ]
// Based on the index provided, we need to know the offset into globals array
let offset = index.index() * ptr_size as usize;
let imported_globals_base_addr = func.create_global_value(ir::GlobalValueData::Load {
base: vmctx,
offset: (vm::Ctx::offset_imported_globals() as i32).into(),
global_type: self.pointer_type(),
readonly: true,
});
// Create global variable based on the data above.
GlobalVariable::Memory {
gv: globals_base_addr,
offset: (offset as i32).into(),
ty: self.module.get_global(index).ty,
let offset = global_index.index() * vm::ImportedGlobal::size() as usize;
let imported_global_addr = func.create_global_value(ir::GlobalValueData::IAddImm {
base: imported_globals_base_addr,
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>,
) {
// 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
self.table_initializers.push(TableInitializer {
table_index: WasmerTableIndex::new(table_index.index()),
base,
offset,
elements: elements
.iter()
.map(|index| WasmerFuncIndex::new(index.index()))

View File

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

View File

@ -88,8 +88,8 @@ static IMPORT_MODULE: &str = r#"
(type $t1 (func))
(func $print_i32 (export "print_i32") (type $t0) (param $lhs i32))
(func $print (export "print") (type $t1))
(table $table (export "table") 10 anyfunc)
(memory $memory (export "memory") 1)
(table $table (export "table") 10 20 anyfunc)
(memory $memory (export "memory") 1 2)
(global $global_i32 (export "global_i32") i32 (i32.const 666)))
"#;
@ -350,6 +350,7 @@ fn test_module_{}() {{
format!(
"fn create_module_{}() -> Instance {{
let module_str = \"{}\";
println!(\"{{}}\", module_str);
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\");
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;
pub use crate::mmap::{Mmap, Protect};
@ -10,5 +10,9 @@ pub trait Compiler {
}
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,
memory::LinearMemory,
module::{ImportName, ModuleInner},
structures::{BoxedMap, Map, SliceMap, TypedIndex},
table::{TableBacking, TableElements},
types::{Initializer, MapIndex, Value},
types::{
ElementType, ImportedFuncIndex, ImportedGlobalIndex, ImportedMemoryIndex,
ImportedTableIndex, Initializer, LocalGlobalIndex, LocalMemoryIndex, LocalOrImport,
LocalTableIndex, Type, Value,
},
vm,
};
use std::slice;
#[derive(Debug)]
pub struct LocalBacking {
pub(crate) memories: Box<[LinearMemory]>,
pub(crate) tables: Box<[TableBacking]>,
pub(crate) memories: BoxedMap<LocalMemoryIndex, LinearMemory>,
pub(crate) tables: BoxedMap<LocalTableIndex, TableBacking>,
pub(crate) vm_memories: Box<[vm::LocalMemory]>,
pub(crate) vm_tables: Box<[vm::LocalTable]>,
pub(crate) vm_globals: Box<[vm::LocalGlobal]>,
pub(crate) vm_memories: BoxedMap<LocalMemoryIndex, vm::LocalMemory>,
pub(crate) vm_tables: BoxedMap<LocalTableIndex, vm::LocalTable>,
pub(crate) vm_globals: BoxedMap<LocalGlobalIndex, vm::LocalGlobal>,
}
impl LocalBacking {
pub fn memory(&mut self, index: u32) -> &mut LinearMemory {
&mut self.memories[index as usize]
pub fn memory(&mut self, local_memory_index: LocalMemoryIndex) -> &mut LinearMemory {
&mut self.memories[local_memory_index]
}
pub fn table(&mut self, index: u32) -> &mut TableBacking {
&mut self.tables[index as usize]
pub fn table(&mut self, local_table_index: LocalTableIndex) -> &mut TableBacking {
&mut self.tables[local_table_index]
}
}
@ -34,9 +40,9 @@ impl LocalBacking {
let mut tables = Self::generate_tables(module);
let globals = Self::generate_globals(module);
let vm_memories = Self::finalize_memories(module, &mut memories[..]);
let vm_tables = Self::finalize_tables(module, imports, &mut tables[..], vmctx);
let vm_globals = Self::finalize_globals(module, imports, globals);
let vm_memories = Self::finalize_memories(module, imports, &mut memories);
let vm_tables = Self::finalize_tables(module, imports, &mut tables, vmctx);
let vm_globals = Self::finalize_globals(module, globals);
Self {
memories,
@ -48,8 +54,8 @@ impl LocalBacking {
}
}
fn generate_memories(module: &ModuleInner) -> Box<[LinearMemory]> {
let mut memories = Vec::with_capacity(module.memories.len());
fn generate_memories(module: &ModuleInner) -> BoxedMap<LocalMemoryIndex, LinearMemory> {
let mut memories = Map::with_capacity(module.memories.len());
for (_, mem) in &module.memories {
// If we use emscripten, we set a fixed initial and maximum
@ -66,75 +72,155 @@ impl LocalBacking {
memories.push(memory);
}
memories.into_boxed_slice()
memories.into_boxed_map()
}
fn finalize_memories(
module: &ModuleInner,
memories: &mut [LinearMemory],
) -> Box<[vm::LocalMemory]> {
for init in &module.data_initializers {
imports: &ImportBacking,
memories: &mut SliceMap<LocalMemoryIndex, LinearMemory>,
) -> 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.offset + init.data.len() <= memories[init.memory_index.index()].size());
let offset = init.offset;
let mem: &mut LinearMemory = &mut memories[init.memory_index.index()];
// let end_of_init = offset + init.data.len();
// if end_of_init > mem.current_size() {
// let grow_pages = (end_of_init / LinearMemory::PAGE_SIZE as usize) + 1;
// mem.grow(grow_pages as u32)
// .expect("failed to grow memory for data initializers");
// }
let to_init = &mut mem[offset..offset + init.data.len()];
to_init.copy_from_slice(&init.data);
match init.memory_index.local_or_import(module) {
LocalOrImport::Local(local_memory_index) => {
let memory_desc = &module.memories[local_memory_index];
let data_top = init.offset + init.data.len();
assert!(memory_desc.min as usize >= data_top);
let mem: &mut LinearMemory = &mut memories[local_memory_index];
let to_init = &mut mem[init.offset..init.offset + init.data.len()];
to_init.copy_from_slice(&init.data);
}
LocalOrImport::Import(imported_memory_index) => {
let _ = imported_memory_index;
let _ = imports;
unimplemented!()
}
}
}
memories
.iter_mut()
.map(|mem| mem.into_vm_memory())
.collect::<Vec<_>>()
.into_boxed_slice()
.map(|(_, mem)| mem.into_vm_memory())
.collect::<Map<_, _>>()
.into_boxed_map()
}
fn generate_tables(module: &ModuleInner) -> Box<[TableBacking]> {
let mut tables = Vec::with_capacity(module.tables.len());
fn generate_tables(module: &ModuleInner) -> BoxedMap<LocalTableIndex, TableBacking> {
let mut tables = Map::with_capacity(module.tables.len());
for (_, table) in &module.tables {
let table_backing = TableBacking::new(table);
tables.push(table_backing);
}
tables.into_boxed_slice()
tables.into_boxed_map()
}
fn finalize_tables(
module: &ModuleInner,
imports: &ImportBacking,
tables: &mut [TableBacking],
tables: &mut SliceMap<LocalTableIndex, TableBacking>,
vmctx: *mut vm::Ctx,
) -> Box<[vm::LocalTable]> {
) -> BoxedMap<LocalTableIndex, vm::LocalTable> {
for init in &module.table_initializers {
assert!(init.base.is_none(), "global base not supported yet");
let table = &mut tables[init.table_index.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 init_base = match init.base {
Initializer::Const(Value::I32(offset)) => offset as u32,
Initializer::Const(_) => panic!("a const initializer must be the i32 type"),
Initializer::GetGlobal(imported_global_index) => {
if module.imported_globals[imported_global_index].1.desc.ty == Type::I32 {
unsafe { (*imports.globals[imported_global_index].global).data as u32 }
} else {
panic!("unsupported global type for initialzer")
}
}
} as usize;
let func_data = if module.is_imported_function(func_index) {
imports.functions[func_index.index()].clone()
} else {
vm::ImportedFunc {
func: module
.func_resolver
.get(module, func_index)
.unwrap()
.as_ptr(),
vmctx,
assert!(
init_base + init.elements.len()
<= match init.table_index.local_or_import(module) {
LocalOrImport::Local(local_table_index) => {
module.tables[local_table_index].min
}
LocalOrImport::Import(imported_table_index) => {
let (_, table_desc) = module.imported_tables[imported_table_index];
table_desc.min
}
} 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
.iter_mut()
.map(|table| table.into_vm_table())
.collect::<Vec<_>>()
.into_boxed_slice()
.map(|(_, table)| table.into_vm_table())
.collect::<Map<_, _>>()
.into_boxed_map()
}
fn generate_globals(module: &ModuleInner) -> Box<[vm::LocalGlobal]> {
let globals = vec![vm::LocalGlobal::null(); module.globals.len()];
fn generate_globals(module: &ModuleInner) -> BoxedMap<LocalGlobalIndex, vm::LocalGlobal> {
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(
module: &ModuleInner,
imports: &ImportBacking,
mut globals: Box<[vm::LocalGlobal]>,
) -> Box<[vm::LocalGlobal]> {
for (to, (global_index, from)) in globals.iter_mut().zip(module.globals.into_iter()) {
mut globals: BoxedMap<LocalGlobalIndex, vm::LocalGlobal>,
) -> BoxedMap<LocalGlobalIndex, vm::LocalGlobal> {
for ((_, to), (_, from)) in globals.iter_mut().zip(module.globals.into_iter()) {
to.data = match from.init {
Initializer::Const(Value::I32(x)) => x as u64,
Initializer::Const(Value::I64(x)) => x as u64,
Initializer::Const(Value::F32(x)) => x.to_bits() as u64,
Initializer::Const(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
},
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(),
};
}
@ -179,10 +260,10 @@ impl LocalBacking {
#[derive(Debug)]
pub struct ImportBacking {
pub functions: Box<[vm::ImportedFunc]>,
pub memories: Box<[vm::ImportedMemory]>,
pub tables: Box<[vm::ImportedTable]>,
pub globals: Box<[vm::ImportedGlobal]>,
pub functions: BoxedMap<ImportedFuncIndex, vm::ImportedFunc>,
pub memories: BoxedMap<ImportedMemoryIndex, vm::ImportedMemory>,
pub tables: BoxedMap<ImportedTableIndex, vm::ImportedTable>,
pub globals: BoxedMap<ImportedGlobalIndex, vm::ImportedGlobal>,
}
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(
module: &ModuleInner,
imports: &mut Imports,
vmctx: *mut vm::Ctx,
) -> Result<Box<[vm::ImportedMemory]>, String> {
let mut memories = Vec::with_capacity(module.imported_memories.len());
) -> Result<BoxedMap<ImportedMemoryIndex, vm::ImportedMemory>, String> {
let mut memories = Map::with_capacity(module.imported_memories.len());
for (_index, (ImportName { namespace, name }, expected_memory_desc)) in
&module.imported_memories
{
@ -241,15 +366,15 @@ fn import_memories(
}
}
}
Ok(memories.into_boxed_slice())
Ok(memories.into_boxed_map())
}
fn import_tables(
module: &ModuleInner,
imports: &mut Imports,
vmctx: *mut vm::Ctx,
) -> Result<Box<[vm::ImportedTable]>, String> {
let mut tables = Vec::with_capacity(module.imported_tables.len());
) -> Result<BoxedMap<ImportedTableIndex, vm::ImportedTable>, String> {
let mut tables = Map::with_capacity(module.imported_tables.len());
for (_index, (ImportName { namespace, name }, expected_table_desc)) in &module.imported_tables {
let table_import = imports
.get_namespace(namespace)
@ -283,65 +408,21 @@ fn import_tables(
}
}
}
Ok(tables.into_boxed_slice())
}
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())
Ok(tables.into_boxed_map())
}
fn import_globals(
module: &ModuleInner,
imports: &mut Imports,
) -> Result<Box<[vm::ImportedGlobal]>, String> {
let mut globals = Vec::with_capacity(module.imported_globals.len());
for (_, (ImportName { namespace, name }, global_desc)) in &module.imported_globals {
) -> Result<BoxedMap<ImportedGlobalIndex, vm::ImportedGlobal>, String> {
let mut globals = Map::with_capacity(module.imported_globals.len());
for (_, (ImportName { namespace, name }, imported_global)) in &module.imported_globals {
let import = imports
.get_namespace(namespace)
.and_then(|namespace| namespace.get_export(name));
match import {
Some(Export::Global { local, global }) => {
if &global == global_desc {
if global == imported_global.desc {
globals.push(vm::ImportedGlobal {
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},
module::{ExportIndex, Module, ModuleInner},
types::{
FuncIndex, FuncSig, GlobalDesc, GlobalIndex, MapIndex, Memory, MemoryIndex, Table,
FuncIndex, FuncSig, GlobalDesc, GlobalIndex, LocalOrImport, Memory, MemoryIndex, Table,
TableIndex, Type, Value,
},
vm,
@ -202,22 +202,23 @@ impl InstanceInner {
.get(func_index)
.expect("broken invariant, incorrect func index");
let (func_ptr, ctx) = if module.is_imported_function(func_index) {
let imported_func = &self.import_backing.functions[func_index.index()];
(
imported_func.func as *const _,
Context::External(imported_func.vmctx),
)
} else {
(
let (func_ptr, ctx) = match func_index.local_or_import(module) {
LocalOrImport::Local(local_func_index) => (
module
.func_resolver
.get(&module, func_index)
.get(&module, local_func_index)
.expect("broken invariant, func resolver not synced with module.exports")
.cast()
.as_ptr() as *const _,
Context::Internal,
)
),
LocalOrImport::Import(imported_func_index) => {
let imported_func = &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();
@ -230,28 +231,31 @@ impl InstanceInner {
module: &ModuleInner,
mem_index: MemoryIndex,
) -> (MemoryPointer, Context, Memory) {
if module.is_imported_memory(mem_index) {
let &(_, mem) = &module
.imported_memories
.get(mem_index)
.expect("missing imported memory index");
let vm::ImportedMemory { memory, vmctx } =
&self.import_backing.memories[mem_index.index()];
(
unsafe { MemoryPointer::new(*memory) },
Context::External(*vmctx),
*mem,
)
} else {
let vm_mem = &mut self.backing.memories[mem_index.index() as usize];
(
unsafe { MemoryPointer::new(&mut vm_mem.into_vm_memory()) },
Context::Internal,
*module
.memories
.get(mem_index)
.expect("broken invariant, memories"),
)
match mem_index.local_or_import(module) {
LocalOrImport::Local(local_mem_index) => {
let vm_mem = &mut self.backing.memories[local_mem_index];
(
unsafe { MemoryPointer::new(&mut vm_mem.into_vm_memory()) },
Context::Internal,
*module
.memories
.get(local_mem_index)
.expect("broken invariant, memories"),
)
}
LocalOrImport::Import(imported_mem_index) => {
let &(_, mem) = &module
.imported_memories
.get(imported_mem_index)
.expect("missing imported memory index");
let vm::ImportedMemory { memory, vmctx } =
&self.import_backing.memories[imported_mem_index];
(
unsafe { MemoryPointer::new(*memory) },
Context::External(*vmctx),
*mem,
)
}
}
}
@ -260,23 +264,27 @@ impl InstanceInner {
module: &ModuleInner,
global_index: GlobalIndex,
) -> (GlobalPointer, GlobalDesc) {
if module.is_imported_global(global_index) {
let &(_, desc) = &module
.imported_globals
.get(global_index)
.expect("missing imported global index");
let vm::ImportedGlobal { global } = &self.import_backing.globals[global_index.index()];
(unsafe { GlobalPointer::new(*global) }, *desc)
} else {
let vm_global = &mut self.backing.vm_globals[global_index.index() as usize];
(
unsafe { GlobalPointer::new(vm_global) },
module
.globals
.get(global_index)
.expect("broken invariant, globals")
.desc,
)
match global_index.local_or_import(module) {
LocalOrImport::Local(local_global_index) => {
let vm_global = &mut self.backing.vm_globals[local_global_index];
(
unsafe { GlobalPointer::new(vm_global) },
module
.globals
.get(local_global_index)
.expect("broken invariant, globals")
.desc,
)
}
LocalOrImport::Import(imported_global_index) => {
let &(_, imported_global) = &module
.imported_globals
.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,
table_index: TableIndex,
) -> (TablePointer, Context, Table) {
if module.is_imported_table(table_index) {
let &(_, tab) = &module
.imported_tables
.get(table_index)
.expect("missing imported table index");
let vm::ImportedTable { table, vmctx } =
&self.import_backing.tables[table_index.index()];
(
unsafe { TablePointer::new(*table) },
Context::External(*vmctx),
*tab,
)
} else {
let vm_table = &mut self.backing.tables[table_index.index() as usize];
(
unsafe { TablePointer::new(&mut vm_table.into_vm_table()) },
Context::Internal,
*module
.tables
.get(table_index)
.expect("broken invariant, tables"),
)
match table_index.local_or_import(module) {
LocalOrImport::Local(local_table_index) => {
let vm_table = &mut self.backing.tables[local_table_index];
(
unsafe { TablePointer::new(&mut vm_table.into_vm_table()) },
Context::Internal,
*module
.tables
.get(local_table_index)
.expect("broken invariant, tables"),
)
}
LocalOrImport::Import(imported_table_index) => {
let &(_, tab) = &module
.imported_tables
.get(imported_table_index)
.expect("missing imported table index");
let vm::ImportedTable { table, vmctx } =
&self.import_backing.tables[imported_table_index];
(
unsafe { TablePointer::new(*table) },
Context::External(*vmctx),
*tab,
)
}
}
}
}

View File

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

View File

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

View File

@ -2,9 +2,11 @@ use crate::{
backend::FuncResolver,
import::Imports,
sig_registry::SigRegistry,
structures::Map,
types::{
FuncIndex, Global, GlobalDesc, GlobalIndex, Map, MapIndex, Memory, MemoryIndex, SigIndex,
Table, TableIndex,
FuncIndex, Global, GlobalIndex, ImportedFuncIndex, ImportedGlobal, ImportedGlobalIndex,
ImportedMemoryIndex, ImportedTableIndex, Initializer, LocalGlobalIndex, LocalMemoryIndex,
LocalTableIndex, Memory, MemoryIndex, SigIndex, Table, TableIndex,
},
Instance,
};
@ -15,14 +17,16 @@ use std::rc::Rc;
#[doc(hidden)]
pub struct ModuleInner {
pub func_resolver: Box<dyn FuncResolver>,
pub memories: Map<MemoryIndex, Memory>,
pub globals: Map<GlobalIndex, Global>,
pub tables: Map<TableIndex, Table>,
// This are strictly local and the typsystem ensures that.
pub memories: Map<LocalMemoryIndex, Memory>,
pub globals: Map<LocalGlobalIndex, Global>,
pub tables: Map<LocalTableIndex, Table>,
pub imported_functions: Map<FuncIndex, ImportName>,
pub imported_memories: Map<MemoryIndex, (ImportName, Memory)>,
pub imported_tables: Map<TableIndex, (ImportName, Table)>,
pub imported_globals: Map<GlobalIndex, (ImportName, GlobalDesc)>,
// These are strictly imported and the typesystem ensures that.
pub imported_functions: Map<ImportedFuncIndex, ImportName>,
pub imported_memories: Map<ImportedMemoryIndex, (ImportName, Memory)>,
pub imported_tables: Map<ImportedTableIndex, (ImportName, Table)>,
pub imported_globals: Map<ImportedGlobalIndex, (ImportName, ImportedGlobal)>,
pub exports: HashMap<String, ExportIndex>,
@ -47,23 +51,7 @@ impl Module {
}
}
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()
}
}
impl ModuleInner {}
#[doc(hidden)]
#[derive(Debug, Clone)]
@ -90,7 +78,7 @@ pub enum ExportIndex {
}
/// A data initializer for linear memory.
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct DataInitializer {
/// The index of the memory to initialize.
pub memory_index: MemoryIndex,
@ -103,14 +91,12 @@ pub struct DataInitializer {
}
/// A WebAssembly table initializer.
#[derive(Clone, Debug)]
#[derive(Debug, Clone)]
pub struct TableInitializer {
/// The index of a table to initialize.
pub table_index: TableIndex,
/// Optionally, a global variable giving a base index.
pub base: Option<GlobalIndex>,
/// The offset to add to the base.
pub offset: usize,
/// Either a constant offset or a `get_global`
pub base: Initializer,
/// The values to write into the table elements.
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;
#[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 std::{
iter, mem,
ops::{Index, IndexMut},
slice,
};
use crate::{module::ModuleInner, structures::TypedIndex};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Type {
@ -82,21 +77,22 @@ pub struct Table {
impl Table {
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.
/// Overtime, this will be able to represent more and more
/// A const value initializer.
/// Over time, this will be able to represent more and more
/// complex expressions.
#[derive(Debug, Clone, PartialEq)]
pub enum Initializer {
/// Corresponds to a `const.*` instruction.
Const(Value),
/// Corresponds to a `get_global` instruction.
GetGlobal(GlobalIndex),
/// Initialized externally
Import,
GetGlobal(ImportedGlobalIndex),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@ -105,11 +101,23 @@ pub struct GlobalDesc {
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.
#[derive(Debug, Clone)]
pub struct Global {
pub desc: GlobalDesc,
pub init: Initializer,
pub init: Value,
}
/// A wasm memory.
@ -129,11 +137,15 @@ impl Memory {
}
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)]
pub struct FuncSig {
pub params: Vec<Type>,
@ -151,158 +163,17 @@ impl FuncSig {
}
}
pub trait MapIndex {
fn new(index: usize) -> Self;
fn index(&self) -> usize;
}
/// 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))
}
pub trait LocalImport {
type Local: TypedIndex;
type Import: TypedIndex;
}
#[rustfmt::skip]
macro_rules! define_map_index {
($ty:ident) => {
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct $ty (u32);
impl MapIndex for $ty {
impl TypedIndex for $ty {
fn new(index: usize) -> Self {
$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)]
pub struct GlobalIndex(u32);
impl MapIndex for GlobalIndex {
pub struct SigIndex(u32);
impl TypedIndex for SigIndex {
fn new(index: usize) -> Self {
GlobalIndex(index as _)
SigIndex(index as _)
}
fn index(&self) -> usize {
@ -333,88 +256,10 @@ impl MapIndex for GlobalIndex {
}
}
#[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[..],
}
}
pub enum LocalOrImport<T>
where
T: LocalImport,
{
Local(T::Local),
Import(T::Import),
}

View File

@ -201,6 +201,10 @@ impl LocalGlobal {
pub fn null() -> Self {
Self { data: 0 }
}
pub fn size() -> u8 {
mem::size_of::<Self>() as u8
}
}
#[derive(Debug, Clone)]
@ -213,6 +217,10 @@ impl ImportedGlobal {
pub fn offset_global() -> u8 {
0 * (mem::size_of::<usize>() as u8)
}
pub fn size() -> u8 {
mem::size_of::<Self>() as u8
}
}
#[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(
memory_index: u32,
memory_index: LocalMemoryIndex,
by_pages: u32,
ctx: *mut vm::Ctx,
) -> i32 {
@ -10,7 +10,7 @@ pub unsafe extern "C" fn memory_grow_static(
.grow_static(by_pages)
{
// 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
} 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()
}
pub unsafe extern "C" fn memory_grow_dynamic(
memory_index: u32,
memory_index: LocalMemoryIndex,
by_pages: u32,
ctx: *mut vm::Ctx,
) -> i32 {
@ -32,7 +32,7 @@ pub unsafe extern "C" fn memory_grow_dynamic(
.grow_dynamic(by_pages)
{
// 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
} else {