Split codegen into multiple modules

This commit is contained in:
Lachlan Sneff
2019-01-16 17:59:12 -08:00
parent 5872d879f9
commit bb9ed66cea
14 changed files with 1310 additions and 100 deletions

View File

@ -25,10 +25,11 @@ 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,
GlobalInit as WasmerGlobalInit, Initializer as WasmerInitializer, Map, GlobalInit as WasmerGlobalInit, Initializer as WasmerInitializer,
Memory as WasmerMemory, MemoryIndex as WasmerMemoryIndex, SigIndex as WasmerSignatureIndex, Memory as WasmerMemory, MemoryIndex as WasmerMemoryIndex, SigIndex as WasmerSignatureIndex,
Table as WasmerTable, TableIndex as WasmerTableIndex, Type as WasmerType, TypedIndex, Table as WasmerTable, TableIndex as WasmerTableIndex, Type as WasmerType,
}, },
structures::{TypedIndex, Map},
vm::{self, Ctx as WasmerVMContext}, vm::{self, Ctx as WasmerVMContext},
}; };
@ -94,7 +95,7 @@ pub mod converter {
imported_globals, imported_globals,
exports, exports,
data_initializers, data_initializers,
table_initializers, elem_initializers,
start_func, start_func,
sig_registry, sig_registry,
.. ..
@ -112,7 +113,7 @@ pub mod converter {
imported_globals, imported_globals,
exports, exports,
data_initializers, data_initializers,
table_initializers, elem_initializers,
start_func, start_func,
func_assoc, func_assoc,
sig_registry, sig_registry,
@ -255,7 +256,7 @@ pub struct CraneliftModule {
pub data_initializers: Vec<DataInitializer>, pub data_initializers: Vec<DataInitializer>,
// Function indices to add to table. // Function indices to add to table.
pub table_initializers: Vec<TableInitializer>, pub elem_initializers: Vec<TableInitializer>,
// The start function index. // The start function index.
pub start_func: Option<WasmerFuncIndex>, pub start_func: Option<WasmerFuncIndex>,
@ -289,7 +290,7 @@ impl CraneliftModule {
imported_globals: Map::new(), imported_globals: Map::new(),
exports: HashMap::new(), exports: HashMap::new(),
data_initializers: Vec::new(), data_initializers: Vec::new(),
table_initializers: Vec::new(), elem_initializers: Vec::new(),
start_func: None, start_func: None,
sig_registry: SigRegistry::new(), sig_registry: SigRegistry::new(),
}; };
@ -922,7 +923,7 @@ impl<'data> ModuleEnvironment<'data> for CraneliftModule {
}; };
// Add table initializer to list of table initializers // Add table initializer to list of table initializers
self.table_initializers.push(TableInitializer { self.elem_initializers.push(TableInitializer {
table_index: WasmerTableIndex::new(table_index.index()), table_index: WasmerTableIndex::new(table_index.index()),
base, base,
elements: elements elements: elements

View File

@ -0,0 +1,607 @@
use crate::{module::Converter, module_env::ModuleEnv};
use cranelift_codegen::{
cursor::FuncCursor,
ir::{self, InstBuilder},
isa,
};
use cranelift_entity::EntityRef;
use cranelift_wasm::{self, FuncEnvironment, ModuleEnvironment};
use wasmer_runtime::{
memory::LinearMemory,
structures::TypedIndex,
types::{FuncIndex, GlobalIndex, LocalOrImport, MemoryIndex, TableIndex},
vm,
};
pub struct FuncEnv<'env, 'module, 'isa> {
env: &'env ModuleEnv<'module, 'isa>,
}
impl<'env, 'module, 'isa> FuncEnv<'env, 'module, 'isa> {
pub fn new(env: &'env ModuleEnv<'module, 'isa>) -> Self {
Self { env }
}
/// Creates a signature with VMContext as the last param
pub fn generate_signature(&self, sig_index: cranelift_wasm::SignatureIndex) -> ir::Signature {
// Get signature
let mut signature = self.env.signatures[Converter(sig_index).into()].clone();
// Add the vmctx parameter type to it
signature.params.push(ir::AbiParam::special(
self.pointer_type(),
ir::ArgumentPurpose::VMContext,
));
// Return signature
signature
}
}
impl<'env, 'module, 'isa> FuncEnvironment for FuncEnv<'env, 'module, 'isa> {
/// Gets configuration information needed for compiling functions
fn target_config(&self) -> isa::TargetFrontendConfig {
self.env.target_config()
}
/// Gets native pointers types.
///
/// `I64` on 64-bit arch; `I32` on 32-bit arch.
fn pointer_type(&self) -> ir::Type {
ir::Type::int(u16::from(self.target_config().pointer_bits())).unwrap()
}
/// Gets the size of a native pointer in bytes.
fn pointer_bytes(&self) -> u8 {
self.target_config().pointer_bytes()
}
/// Sets up the necessary preamble definitions in `func` to access the global identified
/// by `index`.
///
/// The index space covers both imported and locally declared globals.
fn make_global(
&mut self,
func: &mut ir::Function,
clif_global_index: cranelift_wasm::GlobalIndex,
) -> cranelift_wasm::GlobalVariable {
let global_index: GlobalIndex = Converter(clif_global_index).into();
// Create VMContext value.
let vmctx = func.create_global_value(ir::GlobalValueData::VMContext);
let ptr_type = self.pointer_type();
match global_index.local_or_import(self.env.module) {
LocalOrImport::Local(local_global_index) => {
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,
});
let offset = local_global_index.index() * 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: ptr_type,
});
// Create global variable based on the data above.
cranelift_wasm::GlobalVariable::Memory {
gv: local_global_addr,
offset: (vm::LocalGlobal::offset_data() as i32).into(),
ty: self.env.get_global(clif_global_index).ty,
}
}
LocalOrImport::Import(imported_global_index) => {
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: ptr_type,
readonly: true,
});
let offset = imported_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: ptr_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: ptr_type,
readonly: true,
});
cranelift_wasm::GlobalVariable::Memory {
gv: local_global_addr,
offset: (vm::LocalGlobal::offset_data() as i32).into(),
ty: self.env.get_global(clif_global_index).ty,
}
}
}
}
/// Sets up the necessary preamble definitions in `func` to access the linear memory identified
/// by `index`.
///
/// The index space covers both imported and locally declared memories.
fn make_heap(
&mut self,
func: &mut ir::Function,
clif_mem_index: cranelift_wasm::MemoryIndex,
) -> ir::Heap {
let mem_index: MemoryIndex = Converter(clif_mem_index).into();
// Create VMContext value.
let vmctx = func.create_global_value(ir::GlobalValueData::VMContext);
let ptr_type = self.pointer_type();
match mem_index.local_or_import(self.env.module) {
LocalOrImport::Local(local_mem_index) => {
let memories_base_addr = func.create_global_value(ir::GlobalValueData::Load {
base: vmctx,
offset: (vm::Ctx::offset_memories() as i32).into(),
global_type: ptr_type,
readonly: true,
});
let memory_offset = local_mem_index.index() * vm::LocalMemory::size() as usize;
let memory_struct_addr = func.create_global_value(ir::GlobalValueData::IAddImm {
base: memories_base_addr,
offset: (memory_offset as i64).into(),
global_type: ptr_type,
});
let memory_base_addr = func.create_global_value(ir::GlobalValueData::Load {
base: memory_struct_addr,
offset: (vm::LocalMemory::offset_base() as i32).into(),
global_type: ptr_type,
readonly: true,
});
func.create_heap(ir::HeapData {
base: memory_base_addr,
min_size: (self.env.module.memories[local_mem_index].min as u64).into(),
offset_guard_size: (LinearMemory::DEFAULT_GUARD_SIZE as u64).into(),
style: ir::HeapStyle::Static {
bound: (LinearMemory::DEFAULT_HEAP_SIZE as u64).into(),
},
index_type: ir::types::I32,
})
}
LocalOrImport::Import(imported_mem_index) => {
let imported_memories_base = func.create_global_value(ir::GlobalValueData::Load {
base: vmctx,
offset: (vm::Ctx::offset_imported_memories() as i32).into(),
global_type: ptr_type,
readonly: true,
});
let imported_memory_offset =
imported_mem_index.index() * vm::ImportedMemory::size() as usize;
let imported_memory_struct_addr =
func.create_global_value(ir::GlobalValueData::IAddImm {
base: imported_memories_base,
offset: (imported_memory_offset as i64).into(),
global_type: ptr_type,
});
let local_memory_struct_addr =
func.create_global_value(ir::GlobalValueData::Load {
base: imported_memory_struct_addr,
offset: (vm::ImportedMemory::offset_memory() as i32).into(),
global_type: ptr_type,
readonly: true,
});
let local_memory_base = func.create_global_value(ir::GlobalValueData::Load {
base: local_memory_struct_addr,
offset: (vm::LocalMemory::offset_base() as i32).into(),
global_type: ptr_type,
readonly: true,
});
func.create_heap(ir::HeapData {
base: local_memory_base,
min_size: (self.env.module.imported_memories[imported_mem_index].1.min as u64)
.into(),
offset_guard_size: (LinearMemory::DEFAULT_GUARD_SIZE as u64).into(),
style: ir::HeapStyle::Static {
bound: (LinearMemory::DEFAULT_HEAP_SIZE as u64).into(),
},
index_type: ir::types::I32,
})
}
}
}
/// Sets up the necessary preamble definitions in `func` to access the table identified
/// by `index`.
///
/// The index space covers both imported and locally declared tables.
fn make_table(
&mut self,
func: &mut ir::Function,
clif_table_index: cranelift_wasm::TableIndex,
) -> ir::Table {
let table_index: TableIndex = Converter(clif_table_index).into();
// Create VMContext value.
let vmctx = func.create_global_value(ir::GlobalValueData::VMContext);
let ptr_type = self.pointer_type();
match table_index.local_or_import(self.env.module) {
LocalOrImport::Local(local_table_index) => {
let tables_base = func.create_global_value(ir::GlobalValueData::Load {
base: vmctx,
offset: (vm::Ctx::offset_tables() as i32).into(),
global_type: ptr_type,
readonly: true,
});
let table_struct_offset =
local_table_index.index() * vm::LocalTable::size() as usize;
let table_struct_addr = func.create_global_value(ir::GlobalValueData::IAddImm {
base: tables_base,
offset: (table_struct_offset as i64).into(),
global_type: ptr_type,
});
let table_base = func.create_global_value(ir::GlobalValueData::Load {
base: table_struct_addr,
offset: (vm::LocalTable::offset_base() as i32).into(),
global_type: ptr_type,
// we will support growing tables, so this cannot be readonly.
readonly: false,
});
let table_bound = func.create_global_value(ir::GlobalValueData::Load {
base: table_struct_addr,
offset: (vm::LocalTable::offset_current_elements() as i32).into(),
// the number of elements in a table will always fit in an `i32`.
global_type: ir::types::I32,
readonly: false,
});
func.create_table(ir::TableData {
base_gv: table_base,
min_size: (self.env.module.tables[local_table_index].min as u64).into(),
bound_gv: table_bound,
element_size: (vm::Anyfunc::size() as u64).into(),
index_type: ir::types::I32,
})
}
LocalOrImport::Import(imported_table_index) => {
let imported_tables_base = func.create_global_value(ir::GlobalValueData::Load {
base: vmctx,
offset: (vm::Ctx::offset_imported_memories() as i32).into(),
global_type: ptr_type,
readonly: true,
});
let imported_table_struct_offset =
imported_table_index.index() * vm::ImportedTable::size() as usize;
let imported_table_struct_addr =
func.create_global_value(ir::GlobalValueData::IAddImm {
base: imported_tables_base,
offset: (imported_table_struct_offset as i64).into(),
global_type: ptr_type,
});
let local_table_struct_addr = func.create_global_value(ir::GlobalValueData::Load {
base: imported_table_struct_addr,
offset: (vm::ImportedTable::offset_table() as i32).into(),
global_type: ptr_type,
readonly: true,
});
let local_table_base = func.create_global_value(ir::GlobalValueData::Load {
base: local_table_struct_addr,
offset: (vm::LocalTable::offset_base() as i32).into(),
global_type: ptr_type,
readonly: false,
});
let local_table_bound = func.create_global_value(ir::GlobalValueData::Load {
base: local_table_struct_addr,
offset: (vm::LocalTable::offset_current_elements() as i32).into(),
global_type: ir::types::I32,
readonly: false,
});
func.create_table(ir::TableData {
base_gv: local_table_base,
min_size: (self.env.module.imported_tables[imported_table_index].1.min as u64)
.into(),
bound_gv: local_table_bound,
element_size: (vm::Anyfunc::size() as u64).into(),
index_type: ir::types::I32,
})
}
}
}
/// Sets up a signature definition in `func`'s preamble.
///
/// Signature may contain additional argument, but arguments marked as ArgumentPurpose::Normal`
/// must correspond to the arguments in the wasm signature
fn make_indirect_sig(
&mut self,
func: &mut ir::Function,
index: cranelift_wasm::SignatureIndex,
) -> ir::SigRef {
// Create a signature reference out of specified signature (with VMContext param added).
func.import_signature(self.generate_signature(index))
}
/// Sets up an external function definition in the preamble of `func` that can be used to
/// directly call the function `index`.
///
/// The index space covers both imported functions and functions defined in the current module.
fn make_direct_func(
&mut self,
func: &mut ir::Function,
func_index: cranelift_wasm::FuncIndex,
) -> ir::FuncRef {
// Get signature of function.
let signature_index = self.env.get_func_type(func_index);
// Create a signature reference from specified signature (with VMContext param added).
let signature = func.import_signature(self.generate_signature(signature_index));
// Get name of function.
let name = ir::ExternalName::user(0, func_index.as_u32());
// Create function reference from fuction data.
func.import_function(ir::ExtFuncData {
name,
signature,
colocated: false,
})
}
/// Generates an indirect call IR with `callee` and `call_args`.
///
/// Inserts instructions at `pos` to the function `callee` in the table
/// `table_index` with WebAssembly signature `sig_index`
#[cfg_attr(feature = "cargo-clippy", allow(clippy::too_many_arguments))]
fn translate_call_indirect(
&mut self,
mut pos: FuncCursor,
_table_index: cranelift_wasm::TableIndex,
table: ir::Table,
sig_index: cranelift_wasm::SignatureIndex,
sig_ref: ir::SigRef,
callee: ir::Value,
call_args: &[ir::Value],
) -> cranelift_wasm::WasmResult<ir::Inst> {
// Get the pointer type based on machine's pointer size.
let ptr_type = self.pointer_type();
// The `callee` value is an index into a table of Anyfunc structures.
let entry_addr = pos.ins().table_addr(ptr_type, table, callee, 0);
let mflags = ir::MemFlags::trusted();
let func_ptr = pos.ins().load(
ptr_type,
mflags,
entry_addr,
vm::Anyfunc::offset_func() as i32,
);
let vmctx_ptr = pos.ins().load(
ptr_type,
mflags,
entry_addr,
vm::Anyfunc::offset_vmctx() as i32,
);
let found_sig = pos.ins().load(
ir::types::I32,
mflags,
entry_addr,
vm::Anyfunc::offset_sig_id() as i32,
);
pos.ins().trapz(func_ptr, ir::TrapCode::IndirectCallToNull);
let deduplicated_sig_index = self
.env
.module
.sig_registry
.lookup_deduplicated_sigindex(Converter(sig_index).into());
let expected_sig = pos
.ins()
.iconst(ir::types::I32, deduplicated_sig_index.index() as i64);
let not_equal_flags = pos.ins().ifcmp(found_sig, expected_sig);
pos.ins().trapif(
ir::condcodes::IntCC::NotEqual,
not_equal_flags,
ir::TrapCode::BadSignature,
);
// Build a value list for the indirect call instruction containing the call_args
// and the vmctx parameter.
let mut args = Vec::with_capacity(call_args.len() + 1);
args.extend(call_args.iter().cloned());
args.push(vmctx_ptr);
Ok(pos.ins().call_indirect(sig_ref, func_ptr, &args))
}
/// Generates a call IR with `callee` and `call_args` and inserts it at `pos`
/// TODO: add support for imported functions
fn translate_call(
&mut self,
mut pos: FuncCursor,
clif_callee_index: cranelift_wasm::FuncIndex,
callee: ir::FuncRef,
call_args: &[ir::Value],
) -> cranelift_wasm::WasmResult<ir::Inst> {
let callee_index: FuncIndex = Converter(clif_callee_index).into();
match callee_index.local_or_import(self.env.module) {
LocalOrImport::Local(_) => {
// this is an internal function
let vmctx = pos
.func
.special_param(ir::ArgumentPurpose::VMContext)
.expect("missing vmctx parameter");
let mut args = Vec::with_capacity(call_args.len() + 1);
args.extend(call_args.iter().cloned());
args.push(vmctx);
Ok(pos.ins().call(callee, &args))
}
LocalOrImport::Import(imported_func_index) => {
let ptr_type = self.pointer_type();
// this is an imported function
let vmctx = pos.func.create_global_value(ir::GlobalValueData::VMContext);
let imported_funcs = pos.func.create_global_value(ir::GlobalValueData::Load {
base: vmctx,
offset: (vm::Ctx::offset_imported_funcs() as i32).into(),
global_type: ptr_type,
readonly: true,
});
let imported_func_offset =
imported_func_index.index() * vm::ImportedFunc::size() as usize;
let imported_func_struct_addr =
pos.func.create_global_value(ir::GlobalValueData::IAddImm {
base: imported_funcs,
offset: (imported_func_offset as i64).into(),
global_type: ptr_type,
});
let imported_func_addr = pos.func.create_global_value(ir::GlobalValueData::Load {
base: imported_func_struct_addr,
offset: (vm::ImportedFunc::offset_func() as i32).into(),
global_type: ptr_type,
readonly: true,
});
let imported_vmctx_addr = pos.func.create_global_value(ir::GlobalValueData::Load {
base: imported_func_struct_addr,
offset: (vm::ImportedFunc::offset_vmctx() as i32).into(),
global_type: ptr_type,
readonly: true,
});
let imported_func_addr = pos.ins().global_value(ptr_type, imported_func_addr);
let imported_vmctx_addr = pos.ins().global_value(ptr_type, imported_vmctx_addr);
let sig_ref = pos.func.dfg.ext_funcs[callee].signature;
let mut args = Vec::with_capacity(call_args.len() + 1);
args.extend(call_args.iter().cloned());
args.push(imported_vmctx_addr);
Ok(pos
.ins()
.call_indirect(sig_ref, imported_func_addr, &args[..]))
}
}
}
/// Generates code corresponding to wasm `memory.grow`.
///
/// `index` refers to the linear memory to query.
///
/// `heap` refers to the IR generated by `make_heap`.
///
/// `val` refers the value to grow the memory by.
fn translate_memory_grow(
&mut self,
mut pos: FuncCursor,
index: cranelift_wasm::MemoryIndex,
_heap: ir::Heap,
val: ir::Value,
) -> cranelift_wasm::WasmResult<ir::Value> {
let signature = pos.func.import_signature(ir::Signature {
call_conv: self.target_config().default_call_conv,
params: vec![
ir::AbiParam::new(ir::types::I32),
ir::AbiParam::new(ir::types::I32),
ir::AbiParam::special(self.pointer_type(), ir::ArgumentPurpose::VMContext),
],
returns: vec![ir::AbiParam::new(ir::types::I32)],
});
let grow_mem_func = pos.func.import_function(ir::ExtFuncData {
// `ir::ExternalName` for static_grow_memory`
name: ir::ExternalName::user(1, 0),
signature,
colocated: false,
});
// Create a memory index value.
let memory_index = pos.ins().iconst(ir::types::I32, index.index() as i64);
// Create a VMContext value.
let vmctx = pos
.func
.special_param(ir::ArgumentPurpose::VMContext)
.expect("missing vmctx parameter");
// Insert call instructions for `grow_memory`.
let call_inst = pos.ins().call(grow_mem_func, &[memory_index, val, vmctx]);
// Return value.
Ok(*pos.func.dfg.inst_results(call_inst).first().unwrap())
}
/// Generates code corresponding to wasm `memory.size`.
///
/// `index` refers to the linear memory to query.
///
/// `heap` refers to the IR generated by `make_heap`.
fn translate_memory_size(
&mut self,
mut pos: FuncCursor,
index: cranelift_wasm::MemoryIndex,
_heap: ir::Heap,
) -> cranelift_wasm::WasmResult<ir::Value> {
let signature = pos.func.import_signature(ir::Signature {
call_conv: self.target_config().default_call_conv,
params: vec![
ir::AbiParam::new(ir::types::I32),
ir::AbiParam::special(self.pointer_type(), ir::ArgumentPurpose::VMContext),
],
returns: vec![ir::AbiParam::new(ir::types::I32)],
});
let size_mem_func = pos.func.import_function(ir::ExtFuncData {
// `ir::ExternalName` for static_grow_memory`
name: ir::ExternalName::user(1, 1),
signature,
colocated: false,
});
// Create a memory index value.
let memory_index = pos.ins().iconst(ir::types::I32, index.index() as i64);
// Create a VMContext value.
let vmctx = pos
.func
.special_param(ir::ArgumentPurpose::VMContext)
.expect("missing vmctx parameter");
// Insert call instructions for `grow_memory`.
let call_inst = pos.ins().call(size_mem_func, &[memory_index, vmctx]);
// Return value.
Ok(*pos.func.dfg.inst_results(call_inst).first().unwrap())
}
}

View File

@ -1,8 +1,10 @@
pub mod codegen; // pub mod codegen;
mod func_env;
mod libcalls; mod libcalls;
mod module;
mod module_env;
mod relocation; mod relocation;
mod resolver; mod resolver;
// mod module;
use cranelift_codegen::{ use cranelift_codegen::{
isa, isa,
@ -12,9 +14,6 @@ use target_lexicon::Triple;
use wasmer_runtime::{backend::Compiler, module::ModuleInner}; use wasmer_runtime::{backend::Compiler, module::ModuleInner};
use wasmparser::{self, WasmDecoder}; use wasmparser::{self, WasmDecoder};
use self::codegen::converter;
use self::codegen::CraneliftModule;
pub struct CraneliftCompiler {} pub struct CraneliftCompiler {}
impl CraneliftCompiler { impl CraneliftCompiler {
@ -29,14 +28,12 @@ impl Compiler for CraneliftCompiler {
validate(wasm)?; validate(wasm)?;
let isa = get_isa(); let isa = get_isa();
// Generate a Cranlift module from wasm binary
let cranelift_module = CraneliftModule::from_bytes(&wasm.to_vec(), isa.frontend_config())?;
// Convert Cranelift module to wasmer module let mut module = module::Module::empty();
let wasmer_module = converter::convert_module(cranelift_module); let module_env = module_env::ModuleEnv::new(&mut module, &*isa);
let func_bodies = module_env.translate(wasm)?;
// Return new wasmer module module.compile(&*isa, func_bodies)
Ok(wasmer_module)
} }
} }
@ -61,11 +58,11 @@ fn validate(bytes: &[u8]) -> Result<(), String> {
loop { loop {
let state = parser.read(); let state = parser.read();
match *state { match *state {
wasmparser::ParserState::EndWasm => return Ok(()), wasmparser::ParserState::EndWasm => break Ok(()),
wasmparser::ParserState::Error(err) => { wasmparser::ParserState::Error(err) => {
return Err(format!("Validation error: {}", err.message)); return Err(format!("Validation error: {}", err.message));
} }
_ => (), _ => {}
} }
} }
} }

View File

@ -1,20 +1,158 @@
use crate::resolver::FuncResolverBuilder;
use cranelift_codegen::{ir, isa};
use cranelift_entity::EntityRef;
use cranelift_wasm;
use hashbrown::HashMap;
use std::{
ops::{Deref, DerefMut},
ptr::NonNull,
};
use wasmer_runtime::{ use wasmer_runtime::{
backend::FuncResolver,
backend::SigRegistry, backend::SigRegistry,
memory::LinearMemory, module::ModuleInner,
module::{ structures::{Map, TypedIndex},
DataInitializer, ExportIndex, ImportName, ModuleInner, TableInitializer,
},
types::{ types::{
ElementType, FuncIndex, FuncSig, FuncIndex, FuncSig, GlobalIndex, LocalFuncIndex, MemoryIndex, SigIndex, TableIndex, Type,
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}, vm,
}; };
/// This is a wasmer module. struct PlaceholderFuncResolver;
pub struct Module {
impl FuncResolver for PlaceholderFuncResolver {
fn get(
&self,
_module: &ModuleInner,
_local_func_index: LocalFuncIndex,
) -> Option<NonNull<vm::Func>> {
None
}
}
/// This contains all of the items in a `ModuleInner` except the `func_resolver`.
pub struct Module {
module: ModuleInner,
}
impl Module {
pub fn empty() -> Self {
Self {
module: ModuleInner {
// this is a placeholder
func_resolver: Box::new(PlaceholderFuncResolver),
memories: Map::new(),
globals: Map::new(),
tables: Map::new(),
imported_functions: Map::new(),
imported_memories: Map::new(),
imported_tables: Map::new(),
imported_globals: Map::new(),
exports: HashMap::new(),
data_initializers: Vec::new(),
elem_initializers: Vec::new(),
start_func: None,
func_assoc: Map::new(),
sig_registry: SigRegistry::new(),
},
}
}
pub fn compile(
mut self,
isa: &isa::TargetIsa,
functions: Map<LocalFuncIndex, ir::Function>,
) -> Result<ModuleInner, String> {
// we have to deduplicate `module.func_assoc`
let func_assoc = &mut self.module.func_assoc;
let sig_registry = &self.module.sig_registry;
func_assoc.iter_mut().for_each(|(_, sig_index)| {
*sig_index = sig_registry.lookup_deduplicated_sigindex(*sig_index);
});
let func_resolver_builder = FuncResolverBuilder::new(isa, functions)?;
self.module.func_resolver = Box::new(func_resolver_builder.finalize()?);
Ok(self.module)
}
}
impl Deref for Module {
type Target = ModuleInner;
fn deref(&self) -> &ModuleInner {
&self.module
}
}
impl DerefMut for Module {
fn deref_mut(&mut self) -> &mut ModuleInner {
&mut self.module
}
}
pub struct Converter<T>(pub T);
macro_rules! convert_clif_to_runtime_index {
($clif_index:ident, $runtime_index:ident) => {
impl From<Converter<cranelift_wasm::$clif_index>> for $runtime_index {
fn from(clif_index: Converter<cranelift_wasm::$clif_index>) -> Self {
$runtime_index::new(clif_index.0.index())
}
}
impl From<Converter<$runtime_index>> for cranelift_wasm::$clif_index {
fn from(runtime_index: Converter<$runtime_index>) -> Self {
cranelift_wasm::$clif_index::new(runtime_index.0.index())
}
}
};
($(($clif_index:ident: $runtime_index:ident),)*) => {
$(
convert_clif_to_runtime_index!($clif_index, $runtime_index);
)*
};
}
convert_clif_to_runtime_index![
(FuncIndex: FuncIndex),
(MemoryIndex: MemoryIndex),
(TableIndex: TableIndex),
(GlobalIndex: GlobalIndex),
(SignatureIndex: SigIndex),
];
impl<'a> From<Converter<&'a ir::Signature>> for FuncSig {
fn from(signature: Converter<&'a ir::Signature>) -> Self {
FuncSig {
params: signature
.0
.params
.iter()
.map(|param| Converter(param.value_type).into())
.collect(),
returns: signature
.0
.returns
.iter()
.map(|ret| Converter(ret.value_type).into())
.collect(),
}
}
}
impl From<Converter<ir::Type>> for Type {
fn from(ty: Converter<ir::Type>) -> Self {
match ty.0 {
ir::types::I32 => Type::I32,
ir::types::I64 => Type::I64,
ir::types::F32 => Type::F32,
ir::types::F64 => Type::F64,
_ => panic!("unsupported wasm type"),
}
}
} }

View File

@ -0,0 +1,364 @@
use crate::{
func_env::FuncEnv,
module::{Converter, Module},
};
use cranelift_codegen::{ir, isa};
use cranelift_wasm::{self, translate_module, FuncTranslator, ModuleEnvironment};
use wasmer_runtime::{
module::{DataInitializer, ExportIndex, ImportName, TableInitializer},
structures::{Map, TypedIndex},
types::{
ElementType, Global, GlobalDesc, GlobalIndex, Initializer, LocalFuncIndex, LocalOrImport,
Memory, SigIndex, Table, Value,
},
};
pub struct ModuleEnv<'module, 'isa> {
pub module: &'module mut Module,
isa: &'isa isa::TargetIsa,
pub signatures: Map<SigIndex, ir::Signature>,
globals: Map<GlobalIndex, cranelift_wasm::Global>,
func_bodies: Map<LocalFuncIndex, ir::Function>,
}
impl<'module, 'isa> ModuleEnv<'module, 'isa> {
pub fn new(module: &'module mut Module, isa: &'isa isa::TargetIsa) -> Self {
Self {
module,
isa,
signatures: Map::new(),
globals: Map::new(),
func_bodies: Map::new(),
}
}
pub fn translate(mut self, wasm: &[u8]) -> Result<Map<LocalFuncIndex, ir::Function>, String> {
translate_module(wasm, &mut self).map_err(|e| e.to_string())?;
Ok(self.func_bodies)
}
}
impl<'module, 'isa, 'data> ModuleEnvironment<'data> for ModuleEnv<'module, 'isa> {
/// Get the information needed to produce Cranelift IR for the current target.
fn target_config(&self) -> isa::TargetFrontendConfig {
self.isa.frontend_config()
}
/// Declares a function signature to the environment.
fn declare_signature(&mut self, sig: &ir::Signature) {
self.signatures.push(sig.clone());
self.module.sig_registry.register(Converter(sig).into());
}
/// Return the signature with the given index.
fn get_signature(&self, sig_index: cranelift_wasm::SignatureIndex) -> &ir::Signature {
&self.signatures[Converter(sig_index).into()]
}
/// Declares a function import to the environment.
fn declare_func_import(
&mut self,
sig_index: cranelift_wasm::SignatureIndex,
namespace: &'data str,
name: &'data str,
) {
self.module.func_assoc.push(Converter(sig_index).into());
// Add import names to list of imported functions
self.module.imported_functions.push(ImportName {
namespace: namespace.to_string(),
name: name.to_string(),
});
}
/// Return the number of imported funcs.
fn get_num_func_imports(&self) -> usize {
self.module.imported_functions.len()
}
/// Declares the type (signature) of a local function in the module.
fn declare_func_type(&mut self, sig_index: cranelift_wasm::SignatureIndex) {
self.module.func_assoc.push(Converter(sig_index).into());
}
/// Return the signature index for the given function index.
fn get_func_type(
&self,
func_index: cranelift_wasm::FuncIndex,
) -> cranelift_wasm::SignatureIndex {
Converter(self.module.func_assoc[Converter(func_index).into()]).into()
}
/// Declares a global to the environment.
fn declare_global(&mut self, global: cranelift_wasm::Global) {
use cranelift_wasm::GlobalInit;
let desc = GlobalDesc {
mutable: global.mutability,
ty: Converter(global.ty).into(),
};
let init = match global.initializer {
GlobalInit::I32Const(x) => Initializer::Const(Value::I32(x)),
GlobalInit::I64Const(x) => Initializer::Const(Value::I64(x)),
GlobalInit::F32Const(x) => Initializer::Const(Value::F32(f32::from_bits(x))),
GlobalInit::F64Const(x) => Initializer::Const(Value::F64(f64::from_bits(x))),
GlobalInit::GetGlobal(global_index) => {
assert!(!desc.mutable);
let global_index: GlobalIndex = Converter(global_index).into();
let imported_global_index = global_index
.local_or_import(self.module)
.import()
.expect("invalid global initializer when declaring an imported global");
Initializer::GetGlobal(imported_global_index)
}
_ => panic!("invalid global initializer when declaring a local global"),
};
// Add global ir to the list of globals
self.module.globals.push(Global { desc, init });
self.globals.push(global);
}
/// Declares a global import to the environment.
fn declare_global_import(
&mut self,
global: cranelift_wasm::Global,
namespace: &'data str,
name: &'data str,
) {
assert!(match global.initializer {
cranelift_wasm::GlobalInit::Import => true,
_ => false,
});
let import_name = ImportName {
namespace: namespace.to_string(),
name: name.to_string(),
};
let desc = GlobalDesc {
mutable: global.mutability,
ty: Converter(global.ty).into(),
};
// Add global ir to the list of globals
self.module.imported_globals.push((import_name, desc));
self.globals.push(global);
}
/// Return the global for the given global index.
fn get_global(&self, global_index: cranelift_wasm::GlobalIndex) -> &cranelift_wasm::Global {
&self.globals[Converter(global_index).into()]
}
/// Declares a table to the environment.
fn declare_table(&mut self, table: cranelift_wasm::Table) {
use cranelift_wasm::TableElementType;
// Add table ir to the list of tables
self.module.tables.push(Table {
ty: match table.ty {
TableElementType::Func => ElementType::Anyfunc,
_ => unimplemented!(),
},
min: table.minimum,
max: table.maximum,
});
}
/// Declares a table import to the environment.
fn declare_table_import(
&mut self,
table: cranelift_wasm::Table,
namespace: &'data str,
name: &'data str,
) {
use cranelift_wasm::TableElementType;
let import_name = ImportName {
namespace: namespace.to_string(),
name: name.to_string(),
};
let imported_table = Table {
ty: match table.ty {
TableElementType::Func => ElementType::Anyfunc,
_ => unimplemented!(),
},
min: table.minimum,
max: table.maximum,
};
// Add import names to list of imported tables
self.module
.imported_tables
.push((import_name, imported_table));
}
/// Fills a declared table with references to functions in the module.
fn declare_table_elements(
&mut self,
table_index: cranelift_wasm::TableIndex,
base: Option<cranelift_wasm::GlobalIndex>,
offset: usize,
elements: Vec<cranelift_wasm::FuncIndex>,
) {
// Convert Cranelift GlobalIndex to wamser GlobalIndex
// let base = base.map(|index| WasmerGlobalIndex::new(index.index()));
let base = match base {
Some(global_index) => {
let global_index: GlobalIndex = Converter(global_index).into();
Initializer::GetGlobal(match global_index.local_or_import(self.module) {
LocalOrImport::Import(imported_global_index) => imported_global_index,
LocalOrImport::Local(_) => {
panic!("invalid global initializer when declaring an imported global")
}
})
}
None => Initializer::Const((offset as i32).into()),
};
// Add table initializer to list of table initializers
self.module.elem_initializers.push(TableInitializer {
table_index: Converter(table_index).into(),
base,
elements: elements
.iter()
.map(|&func_index| Converter(func_index).into())
.collect(),
});
}
/// Declares a memory to the environment
fn declare_memory(&mut self, memory: cranelift_wasm::Memory) {
self.module.memories.push(Memory {
min: memory.minimum,
max: memory.maximum,
shared: memory.shared,
});
}
/// Declares a memory import to the environment.
fn declare_memory_import(
&mut self,
memory: cranelift_wasm::Memory,
namespace: &'data str,
name: &'data str,
) {
let import_name = ImportName {
namespace: namespace.to_string(),
name: name.to_string(),
};
let memory = Memory {
min: memory.minimum,
max: memory.maximum,
shared: memory.shared,
};
// Add import names to list of imported memories
self.module.imported_memories.push((import_name, memory));
}
/// Fills a declared memory with bytes at module instantiation.
fn declare_data_initialization(
&mut self,
memory_index: cranelift_wasm::MemoryIndex,
base: Option<cranelift_wasm::GlobalIndex>,
offset: usize,
data: &'data [u8],
) {
// Convert Cranelift GlobalIndex to wamser GlobalIndex
let base = match base {
Some(global_index) => {
let global_index: GlobalIndex = Converter(global_index).into();
Initializer::GetGlobal(match global_index.local_or_import(self.module) {
LocalOrImport::Import(imported_global_index) => imported_global_index,
LocalOrImport::Local(_) => {
panic!("invalid global initializer when declaring an imported global")
}
})
}
None => Initializer::Const((offset as i32).into()),
};
// Add data initializer to list of data initializers
self.module.data_initializers.push(DataInitializer {
memory_index: Converter(memory_index).into(),
base,
data: data.to_vec(),
});
}
/// Declares a function export to the environment.
fn declare_func_export(&mut self, func_index: cranelift_wasm::FuncIndex, name: &'data str) {
self.module.exports.insert(
name.to_string(),
ExportIndex::Func(Converter(func_index).into()),
);
}
/// Declares a table export to the environment.
fn declare_table_export(&mut self, table_index: cranelift_wasm::TableIndex, name: &'data str) {
self.module.exports.insert(
name.to_string(),
ExportIndex::Table(Converter(table_index).into()),
);
}
/// Declares a memory export to the environment.
fn declare_memory_export(
&mut self,
memory_index: cranelift_wasm::MemoryIndex,
name: &'data str,
) {
self.module.exports.insert(
name.to_string(),
ExportIndex::Memory(Converter(memory_index).into()),
);
}
/// Declares a global export to the environment.
fn declare_global_export(
&mut self,
global_index: cranelift_wasm::GlobalIndex,
name: &'data str,
) {
self.module.exports.insert(
name.to_string(),
ExportIndex::Global(Converter(global_index).into()),
);
}
/// Declares a start function.
fn declare_start_func(&mut self, func_index: cranelift_wasm::FuncIndex) {
self.module.start_func = Some(Converter(func_index).into());
}
/// Provides the contents of a function body.
fn define_function_body(&mut self, body_bytes: &'data [u8]) -> cranelift_wasm::WasmResult<()> {
let mut func_translator = FuncTranslator::new();
let func_body = {
let mut func_env = FuncEnv::new(self);
let func_index = self.func_bodies.next_index();
let name = ir::ExternalName::user(0, func_index.index() as u32);
let sig = func_env.generate_signature(
self.get_func_type(Converter(func_index.convert_up(self.module)).into()),
);
let mut func = ir::Function::with_name_signature(name, sig);
println!("translating function");
func_translator.translate(body_bytes, &mut func, &mut func_env)?;
println!("done translating function");
func
};
// Add function body to list of function bodies.
self.func_bodies.push(func_body);
Ok(())
}
}

View File

@ -4,6 +4,7 @@
//! function addrs in runtime with the functions we need. //! function addrs in runtime with the functions we need.
use cranelift_codegen::binemit; use cranelift_codegen::binemit;
use cranelift_codegen::ir::{self, ExternalName, LibCall, SourceLoc, TrapCode}; use cranelift_codegen::ir::{self, ExternalName, LibCall, SourceLoc, TrapCode};
use wasmer_runtime::{structures::TypedIndex, types::LocalFuncIndex};
pub use cranelift_codegen::binemit::Reloc; pub use cranelift_codegen::binemit::Reloc;
@ -22,11 +23,11 @@ pub struct Relocation {
/// Specify the type of relocation /// Specify the type of relocation
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum RelocationType { pub enum RelocationType {
Normal(u32), Normal(LocalFuncIndex),
Intrinsic(String), Intrinsic(String),
LibCall(LibCall), LibCall(LibCall),
GrowMemory, StaticGrowMemory,
CurrentMemory, StaticCurrentMemory,
} }
/// Implementation of a relocation sink that just saves all the information for later /// Implementation of a relocation sink that just saves all the information for later
@ -61,22 +62,33 @@ impl binemit::RelocSink for RelocSink {
reloc, reloc,
offset, offset,
addend, addend,
target: RelocationType::Normal(index as _), target: RelocationType::Normal(LocalFuncIndex::new(index as usize)),
}); });
} }
ExternalName::TestCase { length, ascii } => { ExternalName::User {
let (slice, _) = ascii.split_at(length as usize); namespace: 1,
let name = String::from_utf8(slice.to_vec()).unwrap(); index,
let relocation_type = match name.as_str() { } => {
"current_memory" => RelocationType::CurrentMemory, let target = match index {
"grow_memory" => RelocationType::GrowMemory, 0 => RelocationType::StaticGrowMemory,
_ => RelocationType::Intrinsic(name), 1 => RelocationType::StaticCurrentMemory,
_ => unimplemented!(),
}; };
self.func_relocs.push(Relocation { self.func_relocs.push(Relocation {
reloc, reloc,
offset, offset,
addend, addend,
target: relocation_type, target,
});
}
ExternalName::TestCase { length, ascii } => {
let (slice, _) = ascii.split_at(length as usize);
let name = String::from_utf8(slice.to_vec()).unwrap();
self.func_relocs.push(Relocation {
reloc,
offset,
addend,
target: RelocationType::Intrinsic(name),
}); });
} }
ExternalName::LibCall(libcall) => { ExternalName::LibCall(libcall) => {

View File

@ -7,7 +7,8 @@ use std::ptr::{write_unaligned, NonNull};
use wasmer_runtime::{ use wasmer_runtime::{
self, self,
backend::{self, Mmap, Protect}, backend::{self, Mmap, Protect},
types::{LocalFuncIndex, Map, TypedIndex}, structures::Map,
types::LocalFuncIndex,
vm, vmcalls, vm, vmcalls,
}; };
@ -21,8 +22,7 @@ pub struct FuncResolverBuilder {
impl FuncResolverBuilder { impl FuncResolverBuilder {
pub fn new( pub fn new(
isa: &isa::TargetIsa, isa: &isa::TargetIsa,
function_bodies: Vec<ir::Function>, function_bodies: Map<LocalFuncIndex, ir::Function>,
num_imported_funcs: usize,
) -> Result<Self, String> { ) -> Result<Self, String> {
let mut compiled_functions: Vec<Vec<u8>> = Vec::with_capacity(function_bodies.len()); let mut compiled_functions: Vec<Vec<u8>> = Vec::with_capacity(function_bodies.len());
let mut relocations = Map::with_capacity(function_bodies.len()); let mut relocations = Map::with_capacity(function_bodies.len());
@ -31,7 +31,7 @@ impl FuncResolverBuilder {
let mut ctx = Context::new(); let mut ctx = Context::new();
let mut total_size = 0; let mut total_size = 0;
for func in function_bodies.into_iter() { for (_, func) in function_bodies {
ctx.func = func; ctx.func = func;
let mut code_buf = Vec::new(); let mut code_buf = Vec::new();
let mut reloc_sink = RelocSink::new(); let mut reloc_sink = RelocSink::new();
@ -71,11 +71,7 @@ impl FuncResolverBuilder {
} }
Ok(Self { Ok(Self {
resolver: FuncResolver { resolver: FuncResolver { map, memory },
num_imported_funcs,
map,
memory,
},
relocations, relocations,
trap_sinks, trap_sinks,
}) })
@ -85,17 +81,14 @@ impl FuncResolverBuilder {
for (index, relocs) in self.relocations.iter() { for (index, relocs) in self.relocations.iter() {
for ref reloc in relocs { for ref reloc in relocs {
let target_func_address: isize = match reloc.target { let target_func_address: isize = match reloc.target {
RelocationType::Normal(func_index) => { RelocationType::Normal(local_func_index) => {
// This will always be an internal function // This will always be an internal function
// because imported functions are not // because imported functions are not
// called in this way. // called in this way.
self.resolver self.resolver.lookup(local_func_index).unwrap().as_ptr() as isize
.lookup(FuncIndex::new(func_index as _))
.unwrap()
.as_ptr() as isize
} }
RelocationType::CurrentMemory => vmcalls::memory_size as isize, RelocationType::StaticCurrentMemory => vmcalls::memory_size as isize,
RelocationType::GrowMemory => vmcalls::memory_grow_static as isize, RelocationType::StaticGrowMemory => vmcalls::memory_grow_static as isize,
RelocationType::LibCall(libcall) => match libcall { RelocationType::LibCall(libcall) => match libcall {
ir::LibCall::CeilF32 => libcalls::ceilf32 as isize, ir::LibCall::CeilF32 => libcalls::ceilf32 as isize,
ir::LibCall::FloorF32 => libcalls::floorf32 as isize, ir::LibCall::FloorF32 => libcalls::floorf32 as isize,
@ -157,7 +150,6 @@ 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,
map: Map<LocalFuncIndex, usize>, map: Map<LocalFuncIndex, usize>,
memory: Mmap, memory: Mmap,
} }
@ -176,7 +168,7 @@ impl backend::FuncResolver for FuncResolver {
fn get( fn get(
&self, &self,
_module: &wasmer_runtime::module::ModuleInner, _module: &wasmer_runtime::module::ModuleInner,
index: FuncIndex, index: LocalFuncIndex,
) -> Option<NonNull<vm::Func>> { ) -> Option<NonNull<vm::Func>> {
self.lookup(index) self.lookup(index)
} }

View File

@ -42,7 +42,7 @@ impl LocalBacking {
let vm_memories = Self::finalize_memories(module, imports, &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, globals); let vm_globals = Self::finalize_globals(module, imports, globals);
Self { Self {
memories, memories,
@ -86,16 +86,27 @@ impl LocalBacking {
.iter() .iter()
.filter(|init| init.data.len() > 0) .filter(|init| init.data.len() > 0)
{ {
assert!(init.base.is_none(), "global base not supported yet"); 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.ty == Type::I32 {
unsafe { (*imports.globals[imported_global_index].global).data as u32 }
} else {
panic!("unsupported global type for initialzer")
}
}
} as usize;
match init.memory_index.local_or_import(module) { match init.memory_index.local_or_import(module) {
LocalOrImport::Local(local_memory_index) => { LocalOrImport::Local(local_memory_index) => {
let memory_desc = &module.memories[local_memory_index]; let memory_desc = &module.memories[local_memory_index];
let data_top = init.offset + init.data.len(); let data_top = init_base + init.data.len();
assert!(memory_desc.min as usize >= data_top); println!("data_top: {}", data_top);
assert!((memory_desc.min * LinearMemory::PAGE_SIZE) as usize >= data_top);
let mem: &mut LinearMemory = &mut memories[local_memory_index]; let mem: &mut LinearMemory = &mut memories[local_memory_index];
let to_init = &mut mem[init.offset..init.offset + init.data.len()]; let to_init = &mut mem[init_base..init_base + init.data.len()];
to_init.copy_from_slice(&init.data); to_init.copy_from_slice(&init.data);
} }
LocalOrImport::Import(imported_memory_index) => { LocalOrImport::Import(imported_memory_index) => {
@ -130,12 +141,12 @@ impl LocalBacking {
tables: &mut SliceMap<LocalTableIndex, TableBacking>, tables: &mut SliceMap<LocalTableIndex, TableBacking>,
vmctx: *mut vm::Ctx, vmctx: *mut vm::Ctx,
) -> BoxedMap<LocalTableIndex, vm::LocalTable> { ) -> BoxedMap<LocalTableIndex, vm::LocalTable> {
for init in &module.table_initializers { for init in &module.elem_initializers {
let init_base = match init.base { let init_base = match init.base {
Initializer::Const(Value::I32(offset)) => offset as u32, Initializer::Const(Value::I32(offset)) => offset as u32,
Initializer::Const(_) => panic!("a const initializer must be the i32 type"), Initializer::Const(_) => panic!("a const initializer must be the i32 type"),
Initializer::GetGlobal(imported_global_index) => { Initializer::GetGlobal(imported_global_index) => {
if module.imported_globals[imported_global_index].1.desc.ty == Type::I32 { if module.imported_globals[imported_global_index].1.ty == Type::I32 {
unsafe { (*imports.globals[imported_global_index].global).data as u32 } unsafe { (*imports.globals[imported_global_index].global).data as u32 }
} else { } else {
panic!("unsupported global type for initialzer") panic!("unsupported global type for initialzer")
@ -243,14 +254,20 @@ impl LocalBacking {
fn finalize_globals( fn finalize_globals(
module: &ModuleInner, module: &ModuleInner,
imports: &ImportBacking,
mut globals: BoxedMap<LocalGlobalIndex, vm::LocalGlobal>, mut globals: BoxedMap<LocalGlobalIndex, vm::LocalGlobal>,
) -> BoxedMap<LocalGlobalIndex, vm::LocalGlobal> { ) -> BoxedMap<LocalGlobalIndex, vm::LocalGlobal> {
for ((_, to), (_, from)) in globals.iter_mut().zip(module.globals.into_iter()) { for ((_, to), (_, from)) in globals.iter_mut().zip(module.globals.iter()) {
to.data = match from.init { to.data = match from.init {
Value::I32(x) => x as u64, Initializer::Const(ref value) => match value {
Value::I64(x) => x as u64, Value::I32(x) => *x as u64,
Value::I64(x) => *x as u64,
Value::F32(x) => x.to_bits() as u64, Value::F32(x) => x.to_bits() as u64,
Value::F64(x) => x.to_bits(), Value::F64(x) => x.to_bits(),
},
Initializer::GetGlobal(imported_global_index) => unsafe {
(*imports.globals[imported_global_index].global).data
},
}; };
} }
@ -416,13 +433,13 @@ fn import_globals(
imports: &mut Imports, imports: &mut Imports,
) -> Result<BoxedMap<ImportedGlobalIndex, vm::ImportedGlobal>, String> { ) -> Result<BoxedMap<ImportedGlobalIndex, vm::ImportedGlobal>, String> {
let mut globals = Map::with_capacity(module.imported_globals.len()); let mut globals = Map::with_capacity(module.imported_globals.len());
for (_, (ImportName { namespace, name }, imported_global)) in &module.imported_globals { for (_, (ImportName { namespace, name }, imported_global_desc)) 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 == imported_global.desc { if global == *imported_global_desc {
globals.push(vm::ImportedGlobal { globals.push(vm::ImportedGlobal {
global: local.inner(), global: local.inner(),
}); });

View File

@ -277,13 +277,16 @@ impl InstanceInner {
) )
} }
LocalOrImport::Import(imported_global_index) => { LocalOrImport::Import(imported_global_index) => {
let &(_, imported_global) = &module let &(_, imported_global_desc) = &module
.imported_globals .imported_globals
.get(imported_global_index) .get(imported_global_index)
.expect("missing imported global index"); .expect("missing imported global index");
let vm::ImportedGlobal { global } = let vm::ImportedGlobal { global } =
&self.import_backing.globals[imported_global_index]; &self.import_backing.globals[imported_global_index];
(unsafe { GlobalPointer::new(*global) }, imported_global.desc) (
unsafe { GlobalPointer::new(*global) },
*imported_global_desc,
)
} }
} }
} }

View File

@ -4,7 +4,7 @@ use crate::{
sig_registry::SigRegistry, sig_registry::SigRegistry,
structures::Map, structures::Map,
types::{ types::{
FuncIndex, Global, GlobalIndex, ImportedFuncIndex, ImportedGlobal, ImportedGlobalIndex, FuncIndex, Global, GlobalDesc, GlobalIndex, ImportedFuncIndex, ImportedGlobalIndex,
ImportedMemoryIndex, ImportedTableIndex, Initializer, LocalGlobalIndex, LocalMemoryIndex, ImportedMemoryIndex, ImportedTableIndex, Initializer, LocalGlobalIndex, LocalMemoryIndex,
LocalTableIndex, Memory, MemoryIndex, SigIndex, Table, TableIndex, LocalTableIndex, Memory, MemoryIndex, SigIndex, Table, TableIndex,
}, },
@ -26,12 +26,13 @@ pub struct ModuleInner {
pub imported_functions: Map<ImportedFuncIndex, ImportName>, pub imported_functions: Map<ImportedFuncIndex, ImportName>,
pub imported_memories: Map<ImportedMemoryIndex, (ImportName, Memory)>, pub imported_memories: Map<ImportedMemoryIndex, (ImportName, Memory)>,
pub imported_tables: Map<ImportedTableIndex, (ImportName, Table)>, pub imported_tables: Map<ImportedTableIndex, (ImportName, Table)>,
pub imported_globals: Map<ImportedGlobalIndex, (ImportName, ImportedGlobal)>, pub imported_globals: Map<ImportedGlobalIndex, (ImportName, GlobalDesc)>,
pub exports: HashMap<String, ExportIndex>, pub exports: HashMap<String, ExportIndex>,
pub data_initializers: Vec<DataInitializer>, pub data_initializers: Vec<DataInitializer>,
pub table_initializers: Vec<TableInitializer>, pub elem_initializers: Vec<TableInitializer>,
pub start_func: Option<FuncIndex>, pub start_func: Option<FuncIndex>,
pub func_assoc: Map<FuncIndex, SigIndex>, pub func_assoc: Map<FuncIndex, SigIndex>,
@ -82,10 +83,8 @@ pub enum ExportIndex {
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,
/// Optionally a globalvalue base to initialize at. /// Either a constant offset or a `get_global`
pub base: Option<GlobalIndex>, pub base: Initializer,
/// A constant offset to initialize at.
pub offset: usize,
/// The initialization data. /// The initialization data.
pub data: Vec<u8>, pub data: Vec<u8>,
} }

View File

@ -4,7 +4,7 @@ use std::{
marker::PhantomData, marker::PhantomData,
mem, mem,
ops::{Deref, DerefMut}, ops::{Deref, DerefMut},
slice, slice, vec,
}; };
/// Dense item map /// Dense item map
@ -45,6 +45,10 @@ where
K::new(len) K::new(len)
} }
pub fn next_index(&self) -> K {
K::new(self.len())
}
pub fn reserve_exact(&mut self, size: usize) { pub fn reserve_exact(&mut self, size: usize) {
self.elems.reserve_exact(size); self.elems.reserve_exact(size);
} }
@ -105,6 +109,49 @@ where
} }
} }
pub struct IntoIter<K, V>
where
K: TypedIndex,
{
enumerated: iter::Enumerate<vec::IntoIter<V>>,
_marker: PhantomData<K>,
}
impl<K, V> IntoIter<K, V>
where
K: TypedIndex,
{
pub(in crate::structures) fn new(into_iter: vec::IntoIter<V>) -> Self {
Self {
enumerated: into_iter.enumerate(),
_marker: PhantomData,
}
}
}
impl<K, V> Iterator for IntoIter<K, V>
where
K: TypedIndex,
{
type Item = (K, V);
fn next(&mut self) -> Option<(K, V)> {
self.enumerated.next().map(|(i, v)| (K::new(i), v))
}
}
impl<K, V> IntoIterator for Map<K, V>
where
K: TypedIndex,
{
type Item = (K, V);
type IntoIter = IntoIter<K, V>;
fn into_iter(self) -> Self::IntoIter {
IntoIter::new(self.elems.into_iter())
}
}
impl<'a, K, V> IntoIterator for &'a Map<K, V> impl<'a, K, V> IntoIterator for &'a Map<K, V>
where where
K: TypedIndex, K: TypedIndex,

View File

@ -7,6 +7,8 @@ pub use self::map::{Iter, IterMut, Map};
pub use self::slice::SliceMap; pub use self::slice::SliceMap;
pub trait TypedIndex { pub trait TypedIndex {
#[doc(hidden)]
fn new(index: usize) -> Self; fn new(index: usize) -> Self;
#[doc(hidden)]
fn index(&self) -> usize; fn index(&self) -> usize;
} }

View File

@ -101,23 +101,11 @@ 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: Value, pub init: Initializer,
} }
/// A wasm memory. /// A wasm memory.
@ -174,10 +162,12 @@ macro_rules! define_map_index {
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct $ty (u32); pub struct $ty (u32);
impl TypedIndex for $ty { impl TypedIndex for $ty {
#[doc(hidden)]
fn new(index: usize) -> Self { fn new(index: usize) -> Self {
$ty (index as _) $ty (index as _)
} }
#[doc(hidden)]
fn index(&self) -> usize { fn index(&self) -> usize {
self.0 as usize self.0 as usize
} }
@ -247,10 +237,12 @@ define_local_or_import![
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct SigIndex(u32); pub struct SigIndex(u32);
impl TypedIndex for SigIndex { impl TypedIndex for SigIndex {
#[doc(hidden)]
fn new(index: usize) -> Self { fn new(index: usize) -> Self {
SigIndex(index as _) SigIndex(index as _)
} }
#[doc(hidden)]
fn index(&self) -> usize { fn index(&self) -> usize {
self.0 as usize self.0 as usize
} }
@ -263,3 +255,22 @@ where
Local(T::Local), Local(T::Local),
Import(T::Import), Import(T::Import),
} }
impl<T> LocalOrImport<T>
where
T: LocalImport,
{
pub fn local(self) -> Option<T::Local> {
match self {
LocalOrImport::Local(local) => Some(local),
LocalOrImport::Import(_) => None,
}
}
pub fn import(self) -> Option<T::Import> {
match self {
LocalOrImport::Import(import) => Some(import),
LocalOrImport::Local(_) => None,
}
}
}

View File

@ -127,6 +127,10 @@ impl LocalTable {
pub fn offset_current_elements() -> u8 { pub fn offset_current_elements() -> u8 {
1 * (mem::size_of::<usize>() as u8) 1 * (mem::size_of::<usize>() as u8)
} }
pub fn size() -> u8 {
mem::size_of::<Self>() as u8
}
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -146,6 +150,10 @@ impl ImportedTable {
pub fn offset_vmctx() -> u8 { pub fn offset_vmctx() -> u8 {
1 * (mem::size_of::<usize>() as u8) 1 * (mem::size_of::<usize>() as u8)
} }
pub fn size() -> u8 {
mem::size_of::<Self>() as u8
}
} }
/// Definition of a memory used by the VM. /// Definition of a memory used by the VM.
@ -166,6 +174,10 @@ impl LocalMemory {
pub fn offset_size() -> u8 { pub fn offset_size() -> u8 {
1 * (mem::size_of::<usize>() as u8) 1 * (mem::size_of::<usize>() as u8)
} }
pub fn size() -> u8 {
mem::size_of::<Self>() as u8
}
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -184,6 +196,10 @@ impl ImportedMemory {
pub fn offset_vmctx() -> u8 { pub fn offset_vmctx() -> u8 {
1 * (mem::size_of::<usize>() as u8) 1 * (mem::size_of::<usize>() as u8)
} }
pub fn size() -> u8 {
mem::size_of::<Self>() as u8
}
} }
/// Definition of a global used by the VM. /// Definition of a global used by the VM.
@ -257,6 +273,10 @@ impl Anyfunc {
pub fn offset_sig_id() -> u8 { pub fn offset_sig_id() -> u8 {
2 * (mem::size_of::<usize>() as u8) 2 * (mem::size_of::<usize>() as u8)
} }
pub fn size() -> u8 {
mem::size_of::<Self>() as u8
}
} }
#[cfg(test)] #[cfg(test)]