Support imports.
This commit is contained in:
@@ -7,7 +7,9 @@ use cranelift_codegen::ir::ExternalName;
|
|||||||
use cranelift_codegen::isa;
|
use cranelift_codegen::isa;
|
||||||
use cranelift_codegen::Context;
|
use cranelift_codegen::Context;
|
||||||
use cranelift_entity::{EntityRef, PrimaryMap};
|
use cranelift_entity::{EntityRef, PrimaryMap};
|
||||||
use cranelift_wasm::{DefinedFuncIndex, FuncIndex, FuncTranslator};
|
use cranelift_wasm::{
|
||||||
|
DefinedFuncIndex, FuncIndex, FuncTranslator, GlobalIndex, MemoryIndex, TableIndex,
|
||||||
|
};
|
||||||
use environ::{get_func_name, get_memory_grow_name, get_memory_size_name, ModuleTranslation};
|
use environ::{get_func_name, get_memory_grow_name, get_memory_size_name, ModuleTranslation};
|
||||||
use std::string::{String, ToString};
|
use std::string::{String, ToString};
|
||||||
use std::vec::Vec;
|
use std::vec::Vec;
|
||||||
@@ -17,12 +19,30 @@ use std::vec::Vec;
|
|||||||
pub struct Compilation {
|
pub struct Compilation {
|
||||||
/// Compiled machine code for the function bodies.
|
/// Compiled machine code for the function bodies.
|
||||||
pub functions: PrimaryMap<DefinedFuncIndex, Vec<u8>>,
|
pub functions: PrimaryMap<DefinedFuncIndex, Vec<u8>>,
|
||||||
|
|
||||||
|
/// Resolved function addresses for imported functions.
|
||||||
|
pub resolved_func_imports: PrimaryMap<FuncIndex, usize>,
|
||||||
|
|
||||||
|
/// Resolved function addresses for imported tables.
|
||||||
|
pub resolved_table_imports: PrimaryMap<TableIndex, usize>,
|
||||||
|
|
||||||
|
/// Resolved function addresses for imported globals.
|
||||||
|
pub resolved_global_imports: PrimaryMap<GlobalIndex, usize>,
|
||||||
|
|
||||||
|
/// Resolved function addresses for imported memories.
|
||||||
|
pub resolved_memory_imports: PrimaryMap<MemoryIndex, usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Compilation {
|
impl Compilation {
|
||||||
/// Allocates the compilation result with the given function bodies.
|
/// Allocates the compilation result with the given function bodies.
|
||||||
pub fn new(functions: PrimaryMap<DefinedFuncIndex, Vec<u8>>) -> Self {
|
pub fn new(functions: PrimaryMap<DefinedFuncIndex, Vec<u8>>) -> Self {
|
||||||
Self { functions }
|
Self {
|
||||||
|
functions,
|
||||||
|
resolved_func_imports: PrimaryMap::new(),
|
||||||
|
resolved_table_imports: PrimaryMap::new(),
|
||||||
|
resolved_memory_imports: PrimaryMap::new(),
|
||||||
|
resolved_global_imports: PrimaryMap::new(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,5 +165,6 @@ pub fn compile_module<'data, 'module>(
|
|||||||
functions.push(code_buf);
|
functions.push(code_buf);
|
||||||
relocations.push(reloc_sink.func_relocs);
|
relocations.push(reloc_sink.func_relocs);
|
||||||
}
|
}
|
||||||
|
// TODO: Reorganize where we create the Vec for the resolved imports.
|
||||||
Ok((Compilation::new(functions), relocations))
|
Ok((Compilation::new(functions), relocations))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use cast;
|
|||||||
use cranelift_codegen::cursor::FuncCursor;
|
use cranelift_codegen::cursor::FuncCursor;
|
||||||
use cranelift_codegen::ir;
|
use cranelift_codegen::ir;
|
||||||
use cranelift_codegen::ir::condcodes::*;
|
use cranelift_codegen::ir::condcodes::*;
|
||||||
use cranelift_codegen::ir::immediates::{Imm64, Offset32, Uimm64};
|
use cranelift_codegen::ir::immediates::{Offset32, Uimm64};
|
||||||
use cranelift_codegen::ir::types::*;
|
use cranelift_codegen::ir::types::*;
|
||||||
use cranelift_codegen::ir::{
|
use cranelift_codegen::ir::{
|
||||||
AbiParam, ArgumentPurpose, ExtFuncData, FuncRef, Function, InstBuilder, Signature,
|
AbiParam, ArgumentPurpose, ExtFuncData, FuncRef, Function, InstBuilder, Signature,
|
||||||
@@ -169,11 +169,7 @@ impl<'data, 'module> cranelift_wasm::ModuleEnvironment<'data>
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn declare_signature(&mut self, sig: &ir::Signature) {
|
fn declare_signature(&mut self, sig: &ir::Signature) {
|
||||||
let mut sig = sig.clone();
|
let sig = translate_signature(sig.clone(), self.pointer_type());
|
||||||
sig.params.push(AbiParam::special(
|
|
||||||
self.pointer_type(),
|
|
||||||
ArgumentPurpose::VMContext,
|
|
||||||
));
|
|
||||||
// TODO: Deduplicate signatures.
|
// TODO: Deduplicate signatures.
|
||||||
self.module.signatures.push(sig);
|
self.module.signatures.push(sig);
|
||||||
}
|
}
|
||||||
@@ -207,8 +203,17 @@ impl<'data, 'module> cranelift_wasm::ModuleEnvironment<'data>
|
|||||||
self.module.functions[func_index]
|
self.module.functions[func_index]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn declare_global_import(&mut self, _global: Global, _module: &str, _field: &str) {
|
fn declare_global_import(&mut self, global: Global, module: &str, field: &str) {
|
||||||
unimplemented!("imported globals");
|
debug_assert_eq!(
|
||||||
|
self.module.globals.len(),
|
||||||
|
self.module.imported_globals.len(),
|
||||||
|
"Imported globals must be declared first"
|
||||||
|
);
|
||||||
|
self.module.globals.push(global);
|
||||||
|
|
||||||
|
self.module
|
||||||
|
.imported_globals
|
||||||
|
.push((String::from(module), String::from(field)));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn declare_global(&mut self, global: Global) {
|
fn declare_global(&mut self, global: Global) {
|
||||||
@@ -219,8 +224,18 @@ impl<'data, 'module> cranelift_wasm::ModuleEnvironment<'data>
|
|||||||
&self.module.globals[global_index]
|
&self.module.globals[global_index]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn declare_table_import(&mut self, _table: Table, _module: &str, _field: &str) {
|
fn declare_table_import(&mut self, table: Table, module: &str, field: &str) {
|
||||||
unimplemented!("imported tables");
|
debug_assert_eq!(
|
||||||
|
self.module.table_plans.len(),
|
||||||
|
self.module.imported_tables.len(),
|
||||||
|
"Imported tables must be declared first"
|
||||||
|
);
|
||||||
|
let plan = TablePlan::for_table(table, &self.tunables);
|
||||||
|
self.module.table_plans.push(plan);
|
||||||
|
|
||||||
|
self.module
|
||||||
|
.imported_tables
|
||||||
|
.push((String::from(module), String::from(field)));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn declare_table(&mut self, table: Table) {
|
fn declare_table(&mut self, table: Table) {
|
||||||
@@ -235,7 +250,6 @@ impl<'data, 'module> cranelift_wasm::ModuleEnvironment<'data>
|
|||||||
offset: usize,
|
offset: usize,
|
||||||
elements: Vec<FuncIndex>,
|
elements: Vec<FuncIndex>,
|
||||||
) {
|
) {
|
||||||
debug_assert!(base.is_none(), "global-value offsets not supported yet");
|
|
||||||
self.module.table_elements.push(TableElements {
|
self.module.table_elements.push(TableElements {
|
||||||
table_index,
|
table_index,
|
||||||
base,
|
base,
|
||||||
@@ -244,8 +258,18 @@ impl<'data, 'module> cranelift_wasm::ModuleEnvironment<'data>
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn declare_memory_import(&mut self, _memory: Memory, _module: &str, _field: &str) {
|
fn declare_memory_import(&mut self, memory: Memory, module: &str, field: &str) {
|
||||||
unimplemented!("imported memories");
|
debug_assert_eq!(
|
||||||
|
self.module.memory_plans.len(),
|
||||||
|
self.module.imported_memories.len(),
|
||||||
|
"Imported memories must be declared first"
|
||||||
|
);
|
||||||
|
let plan = MemoryPlan::for_memory(memory, &self.tunables);
|
||||||
|
self.module.memory_plans.push(plan);
|
||||||
|
|
||||||
|
self.module
|
||||||
|
.imported_memories
|
||||||
|
.push((String::from(module), String::from(field)));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn declare_memory(&mut self, memory: Memory) {
|
fn declare_memory(&mut self, memory: Memory) {
|
||||||
@@ -260,7 +284,6 @@ impl<'data, 'module> cranelift_wasm::ModuleEnvironment<'data>
|
|||||||
offset: usize,
|
offset: usize,
|
||||||
data: &'data [u8],
|
data: &'data [u8],
|
||||||
) {
|
) {
|
||||||
debug_assert!(base.is_none(), "global-value offsets not supported yet");
|
|
||||||
self.lazy.data_initializers.push(DataInitializer {
|
self.lazy.data_initializers.push(DataInitializer {
|
||||||
memory_index,
|
memory_index,
|
||||||
base,
|
base,
|
||||||
@@ -313,7 +336,7 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
|
|||||||
let pointer_type = self.pointer_type();
|
let pointer_type = self.pointer_type();
|
||||||
|
|
||||||
let vmctx = self.vmctx(func);
|
let vmctx = self.vmctx(func);
|
||||||
let globals_base = self.globals_base.unwrap_or_else(|| {
|
let mut globals_base = self.globals_base.unwrap_or_else(|| {
|
||||||
let new_base = func.create_global_value(ir::GlobalValueData::Load {
|
let new_base = func.create_global_value(ir::GlobalValueData::Load {
|
||||||
base: vmctx,
|
base: vmctx,
|
||||||
offset: Offset32::new(i32::from(self.offsets.vmctx_globals())),
|
offset: Offset32::new(i32::from(self.offsets.vmctx_globals())),
|
||||||
@@ -323,13 +346,24 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
|
|||||||
self.globals_base = Some(new_base);
|
self.globals_base = Some(new_base);
|
||||||
new_base
|
new_base
|
||||||
});
|
});
|
||||||
let gv = func.create_global_value(ir::GlobalValueData::IAddImm {
|
let mut offset = self.offsets.index_vmglobal(index.as_u32());
|
||||||
|
|
||||||
|
// For imported memories, the `VMGlobal` array contains a pointer to
|
||||||
|
// the `VMGlobalDefinition` rather than containing the `VMGlobalDefinition`
|
||||||
|
// inline, so we do an extra indirection.
|
||||||
|
if self.module.is_imported_global(index) {
|
||||||
|
globals_base = func.create_global_value(ir::GlobalValueData::Load {
|
||||||
base: globals_base,
|
base: globals_base,
|
||||||
offset: Imm64::new(i64::from(self.offsets.index_vmglobal(index.as_u32()))),
|
offset: Offset32::new(self.offsets.index_vmglobal_import_from(index.as_u32())),
|
||||||
global_type: pointer_type,
|
global_type: pointer_type,
|
||||||
|
readonly: true,
|
||||||
});
|
});
|
||||||
|
offset = self.offsets.index_vmglobal(0);
|
||||||
|
}
|
||||||
|
|
||||||
GlobalVariable::Memory {
|
GlobalVariable::Memory {
|
||||||
gv,
|
gv: globals_base,
|
||||||
|
offset: offset.into(),
|
||||||
ty: self.module.globals[index].ty,
|
ty: self.module.globals[index].ty,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -338,7 +372,7 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
|
|||||||
let pointer_type = self.pointer_type();
|
let pointer_type = self.pointer_type();
|
||||||
|
|
||||||
let vmctx = self.vmctx(func);
|
let vmctx = self.vmctx(func);
|
||||||
let memories_base = self.memories_base.unwrap_or_else(|| {
|
let mut memories_base = self.memories_base.unwrap_or_else(|| {
|
||||||
let new_base = func.create_global_value(ir::GlobalValueData::Load {
|
let new_base = func.create_global_value(ir::GlobalValueData::Load {
|
||||||
base: vmctx,
|
base: vmctx,
|
||||||
offset: Offset32::new(i32::from(self.offsets.vmctx_memories())),
|
offset: Offset32::new(i32::from(self.offsets.vmctx_memories())),
|
||||||
@@ -348,6 +382,25 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
|
|||||||
self.memories_base = Some(new_base);
|
self.memories_base = Some(new_base);
|
||||||
new_base
|
new_base
|
||||||
});
|
});
|
||||||
|
let mut base_offset = self.offsets.index_vmmemory_definition_base(index.as_u32());
|
||||||
|
let mut current_length_offset = self
|
||||||
|
.offsets
|
||||||
|
.index_vmmemory_definition_current_length(index.as_u32());
|
||||||
|
|
||||||
|
// For imported memories, the `VMMemory` array contains a pointer to
|
||||||
|
// the `VMMemoryDefinition` rather than containing the `VMMemoryDefinition`
|
||||||
|
// inline, so we do an extra indirection.
|
||||||
|
if self.module.is_imported_memory(index) {
|
||||||
|
memories_base = func.create_global_value(ir::GlobalValueData::Load {
|
||||||
|
base: memories_base,
|
||||||
|
offset: Offset32::new(self.offsets.index_vmmemory_import_from(index.as_u32())),
|
||||||
|
global_type: pointer_type,
|
||||||
|
readonly: true,
|
||||||
|
});
|
||||||
|
base_offset = self.offsets.index_vmmemory_definition_base(0);
|
||||||
|
current_length_offset = self.offsets.index_vmmemory_definition_current_length(0);
|
||||||
|
}
|
||||||
|
|
||||||
// If we have a declared maximum, we can make this a "static" heap, which is
|
// If we have a declared maximum, we can make this a "static" heap, which is
|
||||||
// allocated up front and never moved.
|
// allocated up front and never moved.
|
||||||
let (offset_guard_size, heap_style, readonly_base) = match self.module.memory_plans[index] {
|
let (offset_guard_size, heap_style, readonly_base) = match self.module.memory_plans[index] {
|
||||||
@@ -358,9 +411,7 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
|
|||||||
} => {
|
} => {
|
||||||
let heap_bound = func.create_global_value(ir::GlobalValueData::Load {
|
let heap_bound = func.create_global_value(ir::GlobalValueData::Load {
|
||||||
base: memories_base,
|
base: memories_base,
|
||||||
offset: Offset32::new(
|
offset: Offset32::new(current_length_offset),
|
||||||
self.offsets.index_vmmemory_current_length(index.as_u32()),
|
|
||||||
),
|
|
||||||
global_type: I32,
|
global_type: I32,
|
||||||
readonly: false,
|
readonly: false,
|
||||||
});
|
});
|
||||||
@@ -387,7 +438,7 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
|
|||||||
|
|
||||||
let heap_base = func.create_global_value(ir::GlobalValueData::Load {
|
let heap_base = func.create_global_value(ir::GlobalValueData::Load {
|
||||||
base: memories_base,
|
base: memories_base,
|
||||||
offset: Offset32::new(self.offsets.index_vmmemory_base(index.as_u32())),
|
offset: Offset32::new(base_offset),
|
||||||
global_type: pointer_type,
|
global_type: pointer_type,
|
||||||
readonly: readonly_base,
|
readonly: readonly_base,
|
||||||
});
|
});
|
||||||
@@ -404,7 +455,7 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
|
|||||||
let pointer_type = self.pointer_type();
|
let pointer_type = self.pointer_type();
|
||||||
|
|
||||||
let vmctx = self.vmctx(func);
|
let vmctx = self.vmctx(func);
|
||||||
let tables_base = self.tables_base.unwrap_or_else(|| {
|
let mut tables_base = self.tables_base.unwrap_or_else(|| {
|
||||||
let new_base = func.create_global_value(ir::GlobalValueData::Load {
|
let new_base = func.create_global_value(ir::GlobalValueData::Load {
|
||||||
base: vmctx,
|
base: vmctx,
|
||||||
offset: Offset32::new(i32::from(self.offsets.vmctx_tables())),
|
offset: Offset32::new(i32::from(self.offsets.vmctx_tables())),
|
||||||
@@ -414,15 +465,34 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
|
|||||||
self.tables_base = Some(new_base);
|
self.tables_base = Some(new_base);
|
||||||
new_base
|
new_base
|
||||||
});
|
});
|
||||||
|
let mut base_offset = self.offsets.index_vmtable_definition_base(index.as_u32());
|
||||||
|
let mut current_elements_offset = self
|
||||||
|
.offsets
|
||||||
|
.index_vmtable_definition_current_elements(index.as_u32());
|
||||||
|
|
||||||
|
// For imported tables, the `VMTable` array contains a pointer to
|
||||||
|
// the `VMTableDefinition` rather than containing the `VMTableDefinition`
|
||||||
|
// inline, so we do an extra indirection.
|
||||||
|
if self.module.is_imported_table(index) {
|
||||||
|
tables_base = func.create_global_value(ir::GlobalValueData::Load {
|
||||||
|
base: tables_base,
|
||||||
|
offset: Offset32::new(self.offsets.index_vmtable_import_from(index.as_u32())),
|
||||||
|
global_type: pointer_type,
|
||||||
|
readonly: true,
|
||||||
|
});
|
||||||
|
base_offset = self.offsets.index_vmtable_definition_base(0);
|
||||||
|
current_elements_offset = self.offsets.index_vmtable_definition_current_elements(0);
|
||||||
|
}
|
||||||
|
|
||||||
let base_gv = func.create_global_value(ir::GlobalValueData::Load {
|
let base_gv = func.create_global_value(ir::GlobalValueData::Load {
|
||||||
base: tables_base,
|
base: tables_base,
|
||||||
offset: Offset32::new(self.offsets.index_vmtable_base(index.as_u32())),
|
offset: Offset32::new(base_offset),
|
||||||
global_type: pointer_type,
|
global_type: pointer_type,
|
||||||
readonly: false,
|
readonly: false,
|
||||||
});
|
});
|
||||||
let bound_gv = func.create_global_value(ir::GlobalValueData::Load {
|
let bound_gv = func.create_global_value(ir::GlobalValueData::Load {
|
||||||
base: tables_base,
|
base: tables_base,
|
||||||
offset: Offset32::new(self.offsets.index_vmtable_current_elements(index.as_u32())),
|
offset: Offset32::new(current_elements_offset),
|
||||||
global_type: I32,
|
global_type: I32,
|
||||||
readonly: false,
|
readonly: false,
|
||||||
});
|
});
|
||||||
@@ -492,7 +562,7 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
|
|||||||
let sig_id_type = Type::int(u16::from(sig_id_size) * 8).unwrap();
|
let sig_id_type = Type::int(u16::from(sig_id_size) * 8).unwrap();
|
||||||
|
|
||||||
let vmctx = self.vmctx(pos.func);
|
let vmctx = self.vmctx(pos.func);
|
||||||
let signature_ids_base = self.globals_base.unwrap_or_else(|| {
|
let signature_ids_base = self.signature_ids_base.unwrap_or_else(|| {
|
||||||
let new_base = pos.func.create_global_value(ir::GlobalValueData::Load {
|
let new_base = pos.func.create_global_value(ir::GlobalValueData::Load {
|
||||||
base: vmctx,
|
base: vmctx,
|
||||||
offset: Offset32::new(i32::from(self.offsets.vmctx_signature_ids())),
|
offset: Offset32::new(i32::from(self.offsets.vmctx_signature_ids())),
|
||||||
@@ -563,13 +633,13 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
|
|||||||
) -> WasmResult<ir::Value> {
|
) -> WasmResult<ir::Value> {
|
||||||
let memory_grow_func = self.memory_grow_extfunc.unwrap_or_else(|| {
|
let memory_grow_func = self.memory_grow_extfunc.unwrap_or_else(|| {
|
||||||
let sig_ref = pos.func.import_signature(Signature {
|
let sig_ref = pos.func.import_signature(Signature {
|
||||||
call_conv: self.isa.frontend_config().default_call_conv,
|
|
||||||
params: vec![
|
params: vec![
|
||||||
AbiParam::new(I32),
|
AbiParam::new(I32),
|
||||||
AbiParam::new(I32),
|
AbiParam::new(I32),
|
||||||
AbiParam::special(self.pointer_type(), ArgumentPurpose::VMContext),
|
AbiParam::special(self.pointer_type(), ArgumentPurpose::VMContext),
|
||||||
],
|
],
|
||||||
returns: vec![AbiParam::new(I32)],
|
returns: vec![AbiParam::new(I32)],
|
||||||
|
call_conv: self.isa.frontend_config().default_call_conv,
|
||||||
});
|
});
|
||||||
// We currently allocate all code segments independently, so nothing
|
// We currently allocate all code segments independently, so nothing
|
||||||
// is colocated.
|
// is colocated.
|
||||||
@@ -597,12 +667,12 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
|
|||||||
) -> WasmResult<ir::Value> {
|
) -> WasmResult<ir::Value> {
|
||||||
let memory_size_func = self.memory_size_extfunc.unwrap_or_else(|| {
|
let memory_size_func = self.memory_size_extfunc.unwrap_or_else(|| {
|
||||||
let sig_ref = pos.func.import_signature(Signature {
|
let sig_ref = pos.func.import_signature(Signature {
|
||||||
call_conv: self.isa.frontend_config().default_call_conv,
|
|
||||||
params: vec![
|
params: vec![
|
||||||
AbiParam::new(I32),
|
AbiParam::new(I32),
|
||||||
AbiParam::special(self.pointer_type(), ArgumentPurpose::VMContext),
|
AbiParam::special(self.pointer_type(), ArgumentPurpose::VMContext),
|
||||||
],
|
],
|
||||||
returns: vec![AbiParam::new(I32)],
|
returns: vec![AbiParam::new(I32)],
|
||||||
|
call_conv: self.isa.frontend_config().default_call_conv,
|
||||||
});
|
});
|
||||||
// We currently allocate all code segments independently, so nothing
|
// We currently allocate all code segments independently, so nothing
|
||||||
// is colocated.
|
// is colocated.
|
||||||
@@ -642,3 +712,10 @@ impl<'data, 'module> ModuleTranslation<'data, 'module> {
|
|||||||
FuncEnvironment::new(self.isa, &self.module)
|
FuncEnvironment::new(self.isa, &self.module)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add environment-specific function parameters.
|
||||||
|
pub fn translate_signature(mut sig: ir::Signature, pointer_type: ir::Type) -> ir::Signature {
|
||||||
|
sig.params
|
||||||
|
.push(AbiParam::special(pointer_type, ArgumentPurpose::VMContext));
|
||||||
|
sig
|
||||||
|
}
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ mod vmoffsets;
|
|||||||
pub use compilation::{
|
pub use compilation::{
|
||||||
compile_module, Compilation, RelocSink, Relocation, RelocationTarget, Relocations,
|
compile_module, Compilation, RelocSink, Relocation, RelocationTarget, Relocations,
|
||||||
};
|
};
|
||||||
pub use environ::{ModuleEnvironment, ModuleTranslation};
|
pub use environ::{translate_signature, ModuleEnvironment, ModuleTranslation};
|
||||||
pub use module::{
|
pub use module::{
|
||||||
DataInitializer, Export, MemoryPlan, MemoryStyle, Module, TableElements, TablePlan, TableStyle,
|
DataInitializer, Export, MemoryPlan, MemoryStyle, Module, TableElements, TablePlan, TableStyle,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -139,6 +139,15 @@ pub struct Module {
|
|||||||
/// Names of imported functions.
|
/// Names of imported functions.
|
||||||
pub imported_funcs: PrimaryMap<FuncIndex, (String, String)>,
|
pub imported_funcs: PrimaryMap<FuncIndex, (String, String)>,
|
||||||
|
|
||||||
|
/// Names of imported tables.
|
||||||
|
pub imported_tables: PrimaryMap<TableIndex, (String, String)>,
|
||||||
|
|
||||||
|
/// Names of imported globals.
|
||||||
|
pub imported_globals: PrimaryMap<GlobalIndex, (String, String)>,
|
||||||
|
|
||||||
|
/// Names of imported memories.
|
||||||
|
pub imported_memories: PrimaryMap<MemoryIndex, (String, String)>,
|
||||||
|
|
||||||
/// Types of functions, imported and local.
|
/// Types of functions, imported and local.
|
||||||
pub functions: PrimaryMap<FuncIndex, SignatureIndex>,
|
pub functions: PrimaryMap<FuncIndex, SignatureIndex>,
|
||||||
|
|
||||||
@@ -167,6 +176,9 @@ impl Module {
|
|||||||
Self {
|
Self {
|
||||||
signatures: PrimaryMap::new(),
|
signatures: PrimaryMap::new(),
|
||||||
imported_funcs: PrimaryMap::new(),
|
imported_funcs: PrimaryMap::new(),
|
||||||
|
imported_memories: PrimaryMap::new(),
|
||||||
|
imported_globals: PrimaryMap::new(),
|
||||||
|
imported_tables: PrimaryMap::new(),
|
||||||
functions: PrimaryMap::new(),
|
functions: PrimaryMap::new(),
|
||||||
table_plans: PrimaryMap::new(),
|
table_plans: PrimaryMap::new(),
|
||||||
memory_plans: PrimaryMap::new(),
|
memory_plans: PrimaryMap::new(),
|
||||||
@@ -193,6 +205,26 @@ impl Module {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Test whether the given function index is for an imported function.
|
||||||
|
pub fn is_imported_function(&self, index: FuncIndex) -> bool {
|
||||||
|
index.index() < self.imported_funcs.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test whether the given table index is for an imported table.
|
||||||
|
pub fn is_imported_table(&self, index: TableIndex) -> bool {
|
||||||
|
index.index() < self.imported_tables.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test whether the given global index is for an imported global.
|
||||||
|
pub fn is_imported_global(&self, index: GlobalIndex) -> bool {
|
||||||
|
index.index() < self.imported_globals.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test whether the given memory index is for an imported memory.
|
||||||
|
pub fn is_imported_memory(&self, index: MemoryIndex) -> bool {
|
||||||
|
index.index() < self.imported_memories.len()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A data initializer for linear memory.
|
/// A data initializer for linear memory.
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
//! Offsets and sizes of various structs in wasmtime-execute's vmcontext
|
||||||
|
//! module.
|
||||||
|
|
||||||
/// This class computes offsets to fields within `VMContext` and other
|
/// This class computes offsets to fields within `VMContext` and other
|
||||||
/// related structs that JIT code accesses directly.
|
/// related structs that JIT code accesses directly.
|
||||||
pub struct VMOffsets {
|
pub struct VMOffsets {
|
||||||
@@ -11,44 +14,108 @@ impl VMOffsets {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Offsets for `wasmtime_execute::VMMemory`.
|
/// Offsets for `wasmtime_execute::VMMemoryDefinition`.
|
||||||
impl VMOffsets {
|
impl VMOffsets {
|
||||||
/// The offset of the `base` field.
|
/// The offset of the `base` field.
|
||||||
pub fn vmmemory_base(&self) -> u8 {
|
pub fn vmmemory_definition_base(&self) -> u8 {
|
||||||
0 * self.pointer_size
|
0 * self.pointer_size
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The offset of the `current_length` field.
|
/// The offset of the `current_length` field.
|
||||||
pub fn vmmemory_current_length(&self) -> u8 {
|
pub fn vmmemory_definition_current_length(&self) -> u8 {
|
||||||
1 * self.pointer_size
|
1 * self.pointer_size
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return the size of `VMMemoryDefinition`.
|
||||||
|
pub fn size_of_vmmemory_definition(&self) -> u8 {
|
||||||
|
2 * self.pointer_size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Offsets for `wasmtime_execute::VMMemoryImport`.
|
||||||
|
impl VMOffsets {
|
||||||
|
/// The offset of the `from` field.
|
||||||
|
pub fn vmmemory_import_from(&self) -> u8 {
|
||||||
|
0 * self.pointer_size
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the size of `VMMemoryImport`.
|
||||||
|
pub fn size_of_vmmemory_import(&self) -> u8 {
|
||||||
|
1 * self.pointer_size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Offsets for `wasmtime_execute::VMMemory`.
|
||||||
|
impl VMOffsets {
|
||||||
/// Return the size of `VMMemory`.
|
/// Return the size of `VMMemory`.
|
||||||
pub fn size_of_vmmemory(&self) -> u8 {
|
pub fn size_of_vmmemory(&self) -> u8 {
|
||||||
2 * self.pointer_size
|
2 * self.pointer_size
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Offsets for `wasmtime_execute::VMGlobalDefinition`.
|
||||||
|
impl VMOffsets {
|
||||||
|
/// Return the size of `VMGlobalDefinition`.
|
||||||
|
pub fn size_of_vmglobal_definition(&self) -> u8 {
|
||||||
|
8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Offsets for `wasmtime_execute::VMGlobalImport`.
|
||||||
|
impl VMOffsets {
|
||||||
|
/// The offset of the `from` field.
|
||||||
|
pub fn vmglobal_import_from(&self) -> u8 {
|
||||||
|
0 * self.pointer_size
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the size of `VMGlobalImport`.
|
||||||
|
pub fn size_of_vmglobal_import(&self) -> u8 {
|
||||||
|
1 * self.pointer_size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Offsets for `wasmtime_execute::VMGlobal`.
|
/// Offsets for `wasmtime_execute::VMGlobal`.
|
||||||
impl VMOffsets {
|
impl VMOffsets {
|
||||||
/// Return the size of `VMGlobal`.
|
/// Return the size of `VMGlobal`.
|
||||||
pub fn size_of_vmglobal(&self) -> u8 {
|
pub fn size_of_vmglobal(&self) -> u8 {
|
||||||
8
|
assert!(self.size_of_vmglobal_import() <= self.size_of_vmglobal_definition());
|
||||||
|
self.size_of_vmglobal_definition()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Offsets for `wasmtime_execute::VMTableDefinition`.
|
||||||
|
impl VMOffsets {
|
||||||
|
/// The offset of the `base` field.
|
||||||
|
pub fn vmtable_definition_base(&self) -> u8 {
|
||||||
|
0 * self.pointer_size
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The offset of the `current_elements` field.
|
||||||
|
pub fn vmtable_definition_current_elements(&self) -> u8 {
|
||||||
|
1 * self.pointer_size
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the size of `VMTableDefinition`.
|
||||||
|
pub fn size_of_vmtable_definition(&self) -> u8 {
|
||||||
|
2 * self.pointer_size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Offsets for `wasmtime_execute::VMTableImport`.
|
||||||
|
impl VMOffsets {
|
||||||
|
/// The offset of the `from` field.
|
||||||
|
pub fn vmtable_import_from(&self) -> u8 {
|
||||||
|
0 * self.pointer_size
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the size of `VMTableImport`.
|
||||||
|
pub fn size_of_vmtable_import(&self) -> u8 {
|
||||||
|
1 * self.pointer_size
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Offsets for `wasmtime_execute::VMTable`.
|
/// Offsets for `wasmtime_execute::VMTable`.
|
||||||
impl VMOffsets {
|
impl VMOffsets {
|
||||||
/// The offset of the `base` field.
|
|
||||||
pub fn vmtable_base(&self) -> u8 {
|
|
||||||
0 * self.pointer_size
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The offset of the `current_elements` field.
|
|
||||||
pub fn vmtable_current_elements(&self) -> u8 {
|
|
||||||
1 * self.pointer_size
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return the size of `VMTable`.
|
/// Return the size of `VMTable`.
|
||||||
pub fn size_of_vmtable(&self) -> u8 {
|
pub fn size_of_vmtable(&self) -> u8 {
|
||||||
2 * self.pointer_size
|
2 * self.pointer_size
|
||||||
@@ -141,33 +208,57 @@ impl VMOffsets {
|
|||||||
|
|
||||||
/// Return the offset from the `memories` pointer to the `base` field in
|
/// Return the offset from the `memories` pointer to the `base` field in
|
||||||
/// `VMMemory` index `index`.
|
/// `VMMemory` index `index`.
|
||||||
pub fn index_vmmemory_base(&self, index: u32) -> i32 {
|
pub fn index_vmmemory_definition_base(&self, index: u32) -> i32 {
|
||||||
self.index_vmmemory(index)
|
self.index_vmmemory(index)
|
||||||
.checked_add(i32::from(self.vmmemory_base()))
|
.checked_add(i32::from(self.vmmemory_definition_base()))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the offset from the `memories` pointer to the `current_length` field in
|
/// Return the offset from the `memories` pointer to the `current_length` field in
|
||||||
/// `VMMemory` index `index`.
|
/// `VMMemoryDefinition` index `index`.
|
||||||
pub fn index_vmmemory_current_length(&self, index: u32) -> i32 {
|
pub fn index_vmmemory_definition_current_length(&self, index: u32) -> i32 {
|
||||||
self.index_vmmemory(index)
|
self.index_vmmemory(index)
|
||||||
.checked_add(i32::from(self.vmmemory_current_length()))
|
.checked_add(i32::from(self.vmmemory_definition_current_length()))
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the offset from the `memories` pointer to the `from` field in
|
||||||
|
/// `VMMemoryImport` index `index`.
|
||||||
|
pub fn index_vmmemory_import_from(&self, index: u32) -> i32 {
|
||||||
|
self.index_vmmemory(index)
|
||||||
|
.checked_add(i32::from(self.vmmemory_import_from()))
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the offset from the `globals` pointer to the `from` field in
|
||||||
|
/// `VMGlobal` index `index`.
|
||||||
|
pub fn index_vmglobal_import_from(&self, index: u32) -> i32 {
|
||||||
|
self.index_vmglobal(index)
|
||||||
|
.checked_add(i32::from(self.vmglobal_import_from()))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the offset from the `tables` pointer to the `base` field in
|
/// Return the offset from the `tables` pointer to the `base` field in
|
||||||
/// `VMTable` index `index`.
|
/// `VMTable` index `index`.
|
||||||
pub fn index_vmtable_base(&self, index: u32) -> i32 {
|
pub fn index_vmtable_definition_base(&self, index: u32) -> i32 {
|
||||||
self.index_vmtable(index)
|
self.index_vmtable(index)
|
||||||
.checked_add(i32::from(self.vmtable_base()))
|
.checked_add(i32::from(self.vmtable_definition_base()))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the offset from the `tables` pointer to the `current_elements` field in
|
/// Return the offset from the `tables` pointer to the `current_elements` field in
|
||||||
/// `VMTable` index `index`.
|
/// `VMTable` index `index`.
|
||||||
pub fn index_vmtable_current_elements(&self, index: u32) -> i32 {
|
pub fn index_vmtable_definition_current_elements(&self, index: u32) -> i32 {
|
||||||
self.index_vmtable(index)
|
self.index_vmtable(index)
|
||||||
.checked_add(i32::from(self.vmtable_current_elements()))
|
.checked_add(i32::from(self.vmtable_definition_current_elements()))
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the offset from the `tables` pointer to the `from` field in
|
||||||
|
/// `VMTableImport` index `index`.
|
||||||
|
pub fn index_vmtable_import_from(&self, index: u32) -> i32 {
|
||||||
|
self.index_vmtable(index)
|
||||||
|
.checked_add(i32::from(self.vmtable_import_from()))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,66 +5,282 @@ use code::Code;
|
|||||||
use cranelift_codegen::binemit::Reloc;
|
use cranelift_codegen::binemit::Reloc;
|
||||||
use cranelift_codegen::isa::TargetIsa;
|
use cranelift_codegen::isa::TargetIsa;
|
||||||
use cranelift_entity::{EntityRef, PrimaryMap};
|
use cranelift_entity::{EntityRef, PrimaryMap};
|
||||||
use cranelift_wasm::{DefinedFuncIndex, MemoryIndex};
|
use cranelift_wasm::{
|
||||||
|
DefinedFuncIndex, Global, GlobalInit, Memory, MemoryIndex, Table, TableElementType,
|
||||||
|
};
|
||||||
|
use export::{ExportValue, Resolver};
|
||||||
use instance::Instance;
|
use instance::Instance;
|
||||||
use invoke::{invoke_by_index, InvokeOutcome};
|
use invoke::{invoke_by_index, InvokeOutcome};
|
||||||
use region::protect;
|
use region::{protect, Protection};
|
||||||
use region::Protection;
|
|
||||||
use std::ptr::write_unaligned;
|
use std::ptr::write_unaligned;
|
||||||
use std::string::String;
|
use std::string::String;
|
||||||
use std::vec::Vec;
|
use std::vec::Vec;
|
||||||
use vmcontext::VMContext;
|
use vmcontext::VMContext;
|
||||||
use wasmtime_environ::{
|
use wasmtime_environ::{
|
||||||
compile_module, Compilation, Module, ModuleTranslation, Relocation, RelocationTarget,
|
compile_module, Compilation, MemoryPlan, MemoryStyle, Module, ModuleTranslation, Relocation,
|
||||||
|
RelocationTarget, TablePlan, TableStyle,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Executes a module that has been translated with the `wasmtime-environ` environment
|
/// Executes a module that has been translated with the `wasmtime-environ` environment
|
||||||
/// implementation.
|
/// implementation.
|
||||||
pub fn compile_and_link_module<'data, 'module, F>(
|
pub fn compile_and_link_module<'data, 'module>(
|
||||||
isa: &TargetIsa,
|
isa: &TargetIsa,
|
||||||
translation: &ModuleTranslation<'data, 'module>,
|
translation: &ModuleTranslation<'data, 'module>,
|
||||||
imports: F,
|
resolver: &mut Resolver,
|
||||||
) -> Result<Compilation, String>
|
) -> Result<Compilation, String> {
|
||||||
where
|
|
||||||
F: Fn(&str, &str) -> Option<usize>,
|
|
||||||
{
|
|
||||||
let (mut compilation, relocations) = compile_module(&translation, isa)?;
|
let (mut compilation, relocations) = compile_module(&translation, isa)?;
|
||||||
|
|
||||||
|
for (index, (ref module, ref field)) in translation.module.imported_funcs.iter() {
|
||||||
|
match resolver.resolve(module, field) {
|
||||||
|
Some(export_value) => match export_value {
|
||||||
|
ExportValue::Function { address, signature } => {
|
||||||
|
let import_signature =
|
||||||
|
&translation.module.signatures[translation.module.functions[index]];
|
||||||
|
if signature != *import_signature {
|
||||||
|
return Err(format!(
|
||||||
|
"{}/{}: exported function with signature {} incompatible with function import with signature {}",
|
||||||
|
module, field,
|
||||||
|
signature, import_signature,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
compilation.resolved_func_imports.push(address);
|
||||||
|
}
|
||||||
|
ExportValue::Table { .. }
|
||||||
|
| ExportValue::Memory { .. }
|
||||||
|
| ExportValue::Global { .. } => {
|
||||||
|
return Err(format!(
|
||||||
|
"{}/{}: export not compatible with function import",
|
||||||
|
module, field
|
||||||
|
));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => return Err(format!("{}/{}: no provided import function", module, field)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (index, (ref module, ref field)) in translation.module.imported_globals.iter() {
|
||||||
|
match resolver.resolve(module, field) {
|
||||||
|
Some(export_value) => match export_value {
|
||||||
|
ExportValue::Global { address, global } => {
|
||||||
|
let imported_global = translation.module.globals[index];
|
||||||
|
if !is_global_compatible(&global, &imported_global) {
|
||||||
|
return Err(format!(
|
||||||
|
"{}/{}: exported global incompatible with global import",
|
||||||
|
module, field,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
compilation.resolved_global_imports.push(address as usize);
|
||||||
|
}
|
||||||
|
ExportValue::Table { .. }
|
||||||
|
| ExportValue::Memory { .. }
|
||||||
|
| ExportValue::Function { .. } => {
|
||||||
|
return Err(format!(
|
||||||
|
"{}/{}: exported global incompatible with global import",
|
||||||
|
module, field
|
||||||
|
));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
return Err(format!(
|
||||||
|
"no provided import global for {}/{}",
|
||||||
|
module, field
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (index, (ref module, ref field)) in translation.module.imported_tables.iter() {
|
||||||
|
match resolver.resolve(module, field) {
|
||||||
|
Some(export_value) => match export_value {
|
||||||
|
ExportValue::Table { address, table } => {
|
||||||
|
let import_table = &translation.module.table_plans[index];
|
||||||
|
if !is_table_compatible(&table, import_table) {
|
||||||
|
return Err(format!(
|
||||||
|
"{}/{}: exported table incompatible with table import",
|
||||||
|
module, field,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
compilation.resolved_table_imports.push(address as usize);
|
||||||
|
}
|
||||||
|
ExportValue::Global { .. }
|
||||||
|
| ExportValue::Memory { .. }
|
||||||
|
| ExportValue::Function { .. } => {
|
||||||
|
return Err(format!(
|
||||||
|
"{}/{}: export not compatible with table import",
|
||||||
|
module, field
|
||||||
|
));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => return Err(format!("no provided import table for {}/{}", module, field)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (index, (ref module, ref field)) in translation.module.imported_memories.iter() {
|
||||||
|
match resolver.resolve(module, field) {
|
||||||
|
Some(export_value) => match export_value {
|
||||||
|
ExportValue::Memory { address, memory } => {
|
||||||
|
let import_memory = &translation.module.memory_plans[index];
|
||||||
|
if is_memory_compatible(&memory, import_memory) {
|
||||||
|
return Err(format!(
|
||||||
|
"{}/{}: exported memory incompatible with memory import",
|
||||||
|
module, field
|
||||||
|
));
|
||||||
|
}
|
||||||
|
compilation.resolved_memory_imports.push(address as usize);
|
||||||
|
}
|
||||||
|
ExportValue::Table { .. }
|
||||||
|
| ExportValue::Global { .. }
|
||||||
|
| ExportValue::Function { .. } => {
|
||||||
|
return Err(format!(
|
||||||
|
"{}/{}: export not compatible with memory import",
|
||||||
|
module, field
|
||||||
|
));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
return Err(format!(
|
||||||
|
"no provided import memory for {}/{}",
|
||||||
|
module, field
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Apply relocations, now that we have virtual addresses for everything.
|
// Apply relocations, now that we have virtual addresses for everything.
|
||||||
relocate(&mut compilation, &relocations, &translation.module, imports);
|
relocate(&mut compilation, &relocations, &translation.module)?;
|
||||||
|
|
||||||
Ok(compilation)
|
Ok(compilation)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_global_compatible(exported: &Global, imported: &Global) -> bool {
|
||||||
|
match imported.initializer {
|
||||||
|
GlobalInit::Import => (),
|
||||||
|
_ => panic!("imported Global should have an Imported initializer"),
|
||||||
|
}
|
||||||
|
|
||||||
|
let Global {
|
||||||
|
ty: exported_ty,
|
||||||
|
mutability: exported_mutability,
|
||||||
|
initializer: _exported_initializer,
|
||||||
|
} = exported;
|
||||||
|
let Global {
|
||||||
|
ty: imported_ty,
|
||||||
|
mutability: imported_mutability,
|
||||||
|
initializer: _imported_initializer,
|
||||||
|
} = imported;
|
||||||
|
exported_ty == imported_ty && imported_mutability == exported_mutability
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_table_style_compatible(exported_style: &TableStyle, imported_style: &TableStyle) -> bool {
|
||||||
|
match exported_style {
|
||||||
|
TableStyle::CallerChecksSignature => match imported_style {
|
||||||
|
TableStyle::CallerChecksSignature => true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_table_element_type_compatible(
|
||||||
|
exported_type: TableElementType,
|
||||||
|
imported_type: TableElementType,
|
||||||
|
) -> bool {
|
||||||
|
match exported_type {
|
||||||
|
TableElementType::Func => match imported_type {
|
||||||
|
TableElementType::Func => true,
|
||||||
|
_ => false,
|
||||||
|
},
|
||||||
|
TableElementType::Val(exported_val_ty) => match imported_type {
|
||||||
|
TableElementType::Val(imported_val_ty) => exported_val_ty == imported_val_ty,
|
||||||
|
_ => false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_table_compatible(exported: &TablePlan, imported: &TablePlan) -> bool {
|
||||||
|
let TablePlan {
|
||||||
|
table:
|
||||||
|
Table {
|
||||||
|
ty: exported_ty,
|
||||||
|
minimum: exported_minimum,
|
||||||
|
maximum: exported_maximum,
|
||||||
|
},
|
||||||
|
style: exported_style,
|
||||||
|
} = exported;
|
||||||
|
let TablePlan {
|
||||||
|
table:
|
||||||
|
Table {
|
||||||
|
ty: imported_ty,
|
||||||
|
minimum: imported_minimum,
|
||||||
|
maximum: imported_maximum,
|
||||||
|
},
|
||||||
|
style: imported_style,
|
||||||
|
} = imported;
|
||||||
|
|
||||||
|
is_table_element_type_compatible(*exported_ty, *imported_ty)
|
||||||
|
&& imported_minimum >= exported_minimum
|
||||||
|
&& imported_maximum <= exported_maximum
|
||||||
|
&& is_table_style_compatible(imported_style, exported_style)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_memory_style_compatible(exported_style: &MemoryStyle, imported_style: &MemoryStyle) -> bool {
|
||||||
|
match exported_style {
|
||||||
|
MemoryStyle::Dynamic => match imported_style {
|
||||||
|
MemoryStyle::Dynamic => true,
|
||||||
|
_ => false,
|
||||||
|
},
|
||||||
|
MemoryStyle::Static {
|
||||||
|
bound: imported_bound,
|
||||||
|
} => match imported_style {
|
||||||
|
MemoryStyle::Static {
|
||||||
|
bound: exported_bound,
|
||||||
|
} => exported_bound >= imported_bound,
|
||||||
|
_ => false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_memory_compatible(exported: &MemoryPlan, imported: &MemoryPlan) -> bool {
|
||||||
|
let MemoryPlan {
|
||||||
|
memory:
|
||||||
|
Memory {
|
||||||
|
minimum: exported_minimum,
|
||||||
|
maximum: exported_maximum,
|
||||||
|
shared: exported_shared,
|
||||||
|
},
|
||||||
|
style: exported_style,
|
||||||
|
offset_guard_size: exported_offset_guard_size,
|
||||||
|
} = exported;
|
||||||
|
let MemoryPlan {
|
||||||
|
memory:
|
||||||
|
Memory {
|
||||||
|
minimum: imported_minimum,
|
||||||
|
maximum: imported_maximum,
|
||||||
|
shared: imported_shared,
|
||||||
|
},
|
||||||
|
style: imported_style,
|
||||||
|
offset_guard_size: imported_offset_guard_size,
|
||||||
|
} = imported;
|
||||||
|
|
||||||
|
imported_minimum >= exported_minimum
|
||||||
|
&& imported_maximum <= exported_maximum
|
||||||
|
&& exported_shared == imported_shared
|
||||||
|
&& is_memory_style_compatible(exported_style, imported_style)
|
||||||
|
&& exported_offset_guard_size >= imported_offset_guard_size
|
||||||
|
}
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
pub fn __rust_probestack();
|
pub fn __rust_probestack();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Performs the relocations inside the function bytecode, provided the necessary metadata
|
/// Performs the relocations inside the function bytecode, provided the necessary metadata.
|
||||||
fn relocate<F>(
|
fn relocate(
|
||||||
compilation: &mut Compilation,
|
compilation: &mut Compilation,
|
||||||
relocations: &PrimaryMap<DefinedFuncIndex, Vec<Relocation>>,
|
relocations: &PrimaryMap<DefinedFuncIndex, Vec<Relocation>>,
|
||||||
module: &Module,
|
module: &Module,
|
||||||
imports: F,
|
) -> Result<(), String> {
|
||||||
) where
|
// The relocations are relative to the relocation's address plus four bytes.
|
||||||
F: Fn(&str, &str) -> Option<usize>,
|
|
||||||
{
|
|
||||||
// The relocations are relative to the relocation's address plus four bytes
|
|
||||||
// TODO: Support architectures other than x64, and other reloc kinds.
|
|
||||||
for (i, function_relocs) in relocations.iter() {
|
for (i, function_relocs) in relocations.iter() {
|
||||||
for r in function_relocs {
|
for r in function_relocs {
|
||||||
let target_func_address: usize = match r.reloc_target {
|
let target_func_address: usize = match r.reloc_target {
|
||||||
RelocationTarget::UserFunc(index) => match module.defined_func_index(index) {
|
RelocationTarget::UserFunc(index) => match module.defined_func_index(index) {
|
||||||
Some(f) => compilation.functions[f].as_ptr() as usize,
|
Some(f) => compilation.functions[f].as_ptr() as usize,
|
||||||
None => {
|
None => compilation.resolved_func_imports[index],
|
||||||
let func = &module.imported_funcs[index];
|
|
||||||
match imports(&func.0, &func.1) {
|
|
||||||
Some(ptr) => ptr,
|
|
||||||
None => {
|
|
||||||
panic!("no provided import function for {}/{}", &func.0, &func.1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
RelocationTarget::MemoryGrow => wasmtime_memory_grow as usize,
|
RelocationTarget::MemoryGrow => wasmtime_memory_grow as usize,
|
||||||
RelocationTarget::MemorySize => wasmtime_memory_size as usize,
|
RelocationTarget::MemorySize => wasmtime_memory_size as usize,
|
||||||
@@ -111,6 +327,7 @@ fn relocate<F>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" fn wasmtime_memory_grow(size: u32, memory_index: u32, vmctx: *mut VMContext) -> u32 {
|
extern "C" fn wasmtime_memory_grow(size: u32, memory_index: u32, vmctx: *mut VMContext) -> u32 {
|
||||||
|
|||||||
76
lib/execute/src/export.rs
Normal file
76
lib/execute/src/export.rs
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
use cranelift_codegen::ir;
|
||||||
|
use cranelift_wasm::Global;
|
||||||
|
use vmcontext::{VMGlobal, VMMemory, VMTable};
|
||||||
|
use wasmtime_environ::{MemoryPlan, TablePlan};
|
||||||
|
|
||||||
|
/// The value of an export passed from one instance to another.
|
||||||
|
pub enum ExportValue {
|
||||||
|
/// A function export value.
|
||||||
|
Function {
|
||||||
|
/// The address of the native-code function.
|
||||||
|
address: usize,
|
||||||
|
/// The function signature declaration, used for compatibilty checking.
|
||||||
|
signature: ir::Signature,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// A table export value.
|
||||||
|
Table {
|
||||||
|
/// The address of the table descriptor.
|
||||||
|
address: *mut VMTable,
|
||||||
|
/// The table declaration, used for compatibilty checking.
|
||||||
|
table: TablePlan,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// A memory export value.
|
||||||
|
Memory {
|
||||||
|
/// The address of the memory descriptor.
|
||||||
|
address: *mut VMMemory,
|
||||||
|
/// The memory declaration, used for compatibilty checking.
|
||||||
|
memory: MemoryPlan,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// A global export value.
|
||||||
|
Global {
|
||||||
|
/// The address of the global storage.
|
||||||
|
address: *mut VMGlobal,
|
||||||
|
/// The global declaration, used for compatibilty checking.
|
||||||
|
global: Global,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ExportValue {
|
||||||
|
/// Construct a function export value.
|
||||||
|
pub fn function(address: usize, signature: ir::Signature) -> Self {
|
||||||
|
ExportValue::Function { address, signature }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct a table export value.
|
||||||
|
pub fn table(address: *mut VMTable, table: TablePlan) -> Self {
|
||||||
|
ExportValue::Table { address, table }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct a memory export value.
|
||||||
|
pub fn memory(address: *mut VMMemory, memory: MemoryPlan) -> Self {
|
||||||
|
ExportValue::Memory { address, memory }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct a global export value.
|
||||||
|
pub fn global(address: *mut VMGlobal, global: Global) -> Self {
|
||||||
|
ExportValue::Global { address, global }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Import resolver connects imports with available exported values.
|
||||||
|
pub trait Resolver {
|
||||||
|
/// Resolve the given module/field combo.
|
||||||
|
fn resolve(&mut self, module: &str, field: &str) -> Option<ExportValue>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Resolver` implementation that always resolves to `None`.
|
||||||
|
pub struct NullResolver {}
|
||||||
|
|
||||||
|
impl Resolver for NullResolver {
|
||||||
|
fn resolve(&mut self, _module: &str, _field: &str) -> Option<ExportValue> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
39
lib/execute/src/get.rs
Normal file
39
lib/execute/src/get.rs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
//! Support for reading the value of a wasm global from outside the module.
|
||||||
|
|
||||||
|
use cranelift_codegen::ir;
|
||||||
|
use cranelift_wasm::GlobalIndex;
|
||||||
|
use invoke::Value;
|
||||||
|
use std::string::String;
|
||||||
|
use vmcontext::VMContext;
|
||||||
|
use wasmtime_environ::{Export, Module};
|
||||||
|
|
||||||
|
/// Jumps to the code region of memory and invoke the exported function
|
||||||
|
pub fn get(module: &Module, vmctx: *mut VMContext, global_name: &str) -> Result<Value, String> {
|
||||||
|
let global_index = match module.exports.get(global_name) {
|
||||||
|
Some(Export::Global(index)) => *index,
|
||||||
|
Some(_) => return Err(format!("exported item \"{}\" is not a global", global_name)),
|
||||||
|
None => return Err(format!("no export named \"{}\"", global_name)),
|
||||||
|
};
|
||||||
|
|
||||||
|
get_by_index(module, vmctx, global_index)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_by_index(
|
||||||
|
module: &Module,
|
||||||
|
vmctx: *mut VMContext,
|
||||||
|
global_index: GlobalIndex,
|
||||||
|
) -> Result<Value, String> {
|
||||||
|
// TODO: Return Err if the index is out of bounds.
|
||||||
|
unsafe {
|
||||||
|
let vmctx = &mut *vmctx;
|
||||||
|
let vmglobal = vmctx.global(global_index);
|
||||||
|
let definition = vmglobal.get_definition(module.is_imported_global(global_index));
|
||||||
|
Ok(match module.globals[global_index].ty {
|
||||||
|
ir::types::I32 => Value::I32(*definition.as_i32()),
|
||||||
|
ir::types::I64 => Value::I64(*definition.as_i64()),
|
||||||
|
ir::types::F32 => Value::F32(*definition.as_f32_bits()),
|
||||||
|
ir::types::F64 => Value::F64(*definition.as_f64_bits()),
|
||||||
|
other => return Err(format!("global with type {} not supported", other)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ use cranelift_entity::PrimaryMap;
|
|||||||
use cranelift_wasm::{GlobalIndex, MemoryIndex, TableIndex};
|
use cranelift_wasm::{GlobalIndex, MemoryIndex, TableIndex};
|
||||||
use memory::LinearMemory;
|
use memory::LinearMemory;
|
||||||
use sig_registry::SignatureRegistry;
|
use sig_registry::SignatureRegistry;
|
||||||
|
use std::ptr;
|
||||||
use std::string::String;
|
use std::string::String;
|
||||||
use table::Table;
|
use table::Table;
|
||||||
use vmcontext::{VMCallerCheckedAnyfunc, VMContext, VMGlobal, VMMemory, VMTable};
|
use vmcontext::{VMCallerCheckedAnyfunc, VMContext, VMGlobal, VMMemory, VMTable};
|
||||||
@@ -44,7 +45,7 @@ impl Instance {
|
|||||||
compilation: &Compilation,
|
compilation: &Compilation,
|
||||||
data_initializers: &[DataInitializer],
|
data_initializers: &[DataInitializer],
|
||||||
) -> Result<Self, String> {
|
) -> Result<Self, String> {
|
||||||
let mut sig_registry = SignatureRegistry::new();
|
let mut sig_registry = instantiate_signatures(module);
|
||||||
let mut memories = instantiate_memories(module, data_initializers)?;
|
let mut memories = instantiate_memories(module, data_initializers)?;
|
||||||
let mut tables = instantiate_tables(module, compilation, &mut sig_registry);
|
let mut tables = instantiate_tables(module, compilation, &mut sig_registry);
|
||||||
|
|
||||||
@@ -131,6 +132,14 @@ impl Instance {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn instantiate_signatures(module: &Module) -> SignatureRegistry {
|
||||||
|
let mut sig_registry = SignatureRegistry::new();
|
||||||
|
for (sig_index, sig) in module.signatures.iter() {
|
||||||
|
sig_registry.register(sig_index, sig);
|
||||||
|
}
|
||||||
|
sig_registry
|
||||||
|
}
|
||||||
|
|
||||||
/// Allocate memory for just the memories of the current module.
|
/// Allocate memory for just the memories of the current module.
|
||||||
fn instantiate_memories(
|
fn instantiate_memories(
|
||||||
module: &Module,
|
module: &Module,
|
||||||
@@ -171,7 +180,7 @@ fn instantiate_tables(
|
|||||||
let code_buf = &compilation.functions[module
|
let code_buf = &compilation.functions[module
|
||||||
.defined_func_index(*func_idx)
|
.defined_func_index(*func_idx)
|
||||||
.expect("table element initializer with imported function not supported yet")];
|
.expect("table element initializer with imported function not supported yet")];
|
||||||
let type_id = sig_registry.register(callee_sig, &module.signatures[callee_sig]);
|
let type_id = sig_registry.lookup(callee_sig);
|
||||||
subslice[i] = VMCallerCheckedAnyfunc {
|
subslice[i] = VMCallerCheckedAnyfunc {
|
||||||
func_ptr: code_buf.as_ptr(),
|
func_ptr: code_buf.as_ptr(),
|
||||||
type_id,
|
type_id,
|
||||||
@@ -187,8 +196,13 @@ fn instantiate_tables(
|
|||||||
fn instantiate_globals(module: &Module) -> PrimaryMap<GlobalIndex, VMGlobal> {
|
fn instantiate_globals(module: &Module) -> PrimaryMap<GlobalIndex, VMGlobal> {
|
||||||
let mut vmctx_globals = PrimaryMap::with_capacity(module.globals.len());
|
let mut vmctx_globals = PrimaryMap::with_capacity(module.globals.len());
|
||||||
|
|
||||||
for _ in 0..module.globals.len() {
|
for (index, global) in module.globals.iter() {
|
||||||
vmctx_globals.push(VMGlobal::default());
|
if module.is_imported_global(index) {
|
||||||
|
// FIXME: get the actual import
|
||||||
|
vmctx_globals.push(VMGlobal::import(ptr::null_mut()));
|
||||||
|
} else {
|
||||||
|
vmctx_globals.push(VMGlobal::definition(global));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vmctx_globals
|
vmctx_globals
|
||||||
|
|||||||
@@ -114,12 +114,16 @@ pub fn invoke_by_index(
|
|||||||
fn_index: FuncIndex,
|
fn_index: FuncIndex,
|
||||||
args: &[Value],
|
args: &[Value],
|
||||||
) -> Result<InvokeOutcome, String> {
|
) -> Result<InvokeOutcome, String> {
|
||||||
let code_buf = &compilation.functions[module
|
// TODO: Return Err if fn_index is out of bounds.
|
||||||
.defined_func_index(fn_index)
|
let exec_code_buf = match module.defined_func_index(fn_index) {
|
||||||
.expect("imported start functions not supported yet")];
|
Some(def_fn_index) => {
|
||||||
let sig = &module.signatures[module.functions[fn_index]];
|
let code_buf = &compilation.functions[def_fn_index];
|
||||||
|
code.allocate_copy_of_slice(&code_buf)?.as_ptr() as usize
|
||||||
|
}
|
||||||
|
None => compilation.resolved_func_imports[fn_index],
|
||||||
|
};
|
||||||
|
|
||||||
let exec_code_buf = code.allocate_copy_of_slice(&code_buf)?.as_ptr();
|
let sig = &module.signatures[module.functions[fn_index]];
|
||||||
|
|
||||||
// TODO: Move this out to be done once per thread rather than per call.
|
// TODO: Move this out to be done once per thread rather than per call.
|
||||||
let mut traps = TrapContext {
|
let mut traps = TrapContext {
|
||||||
@@ -138,7 +142,7 @@ pub fn invoke_by_index(
|
|||||||
return Err("failed to install signal handlers".to_string());
|
return Err("failed to install signal handlers".to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
call_through_wrapper(code, isa, exec_code_buf as usize, vmctx, args, &sig)
|
call_through_wrapper(code, isa, exec_code_buf, vmctx, args, &sig)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call_through_wrapper(
|
fn call_through_wrapper(
|
||||||
|
|||||||
@@ -43,6 +43,8 @@ extern crate cast;
|
|||||||
|
|
||||||
mod code;
|
mod code;
|
||||||
mod execute;
|
mod execute;
|
||||||
|
mod export;
|
||||||
|
mod get;
|
||||||
mod instance;
|
mod instance;
|
||||||
mod invoke;
|
mod invoke;
|
||||||
mod libcalls;
|
mod libcalls;
|
||||||
@@ -57,10 +59,12 @@ mod world;
|
|||||||
|
|
||||||
pub use code::Code;
|
pub use code::Code;
|
||||||
pub use execute::{compile_and_link_module, finish_instantiation};
|
pub use execute::{compile_and_link_module, finish_instantiation};
|
||||||
|
pub use export::{ExportValue, NullResolver, Resolver};
|
||||||
|
pub use get::get;
|
||||||
pub use instance::Instance;
|
pub use instance::Instance;
|
||||||
pub use invoke::{invoke, InvokeOutcome, Value};
|
pub use invoke::{invoke, InvokeOutcome, Value};
|
||||||
pub use traphandlers::{call_wasm, LookupCodeSegment, RecordTrap, Unwind};
|
pub use traphandlers::{call_wasm, LookupCodeSegment, RecordTrap, Unwind};
|
||||||
pub use vmcontext::VMContext;
|
pub use vmcontext::{VMContext, VMGlobal, VMMemory, VMTable};
|
||||||
pub use world::InstanceWorld;
|
pub use world::InstanceWorld;
|
||||||
|
|
||||||
#[cfg(not(feature = "std"))]
|
#[cfg(not(feature = "std"))]
|
||||||
|
|||||||
@@ -124,8 +124,9 @@ impl LinearMemory {
|
|||||||
Some(prev_pages)
|
Some(prev_pages)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return a `VMMemory` for exposing the memory to JIT code.
|
||||||
pub fn vmmemory(&mut self) -> VMMemory {
|
pub fn vmmemory(&mut self) -> VMMemory {
|
||||||
VMMemory::new(self.mmap.as_mut_ptr(), self.mmap.len())
|
VMMemory::definition(self.mmap.as_mut_ptr(), self.mmap.len())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
use cast;
|
use cast;
|
||||||
use cranelift_codegen::ir;
|
use cranelift_codegen::ir;
|
||||||
use cranelift_entity::SecondaryMap;
|
use cranelift_entity::PrimaryMap;
|
||||||
use cranelift_wasm::SignatureIndex;
|
use cranelift_wasm::SignatureIndex;
|
||||||
use std::collections::{hash_map, HashMap};
|
use std::collections::{hash_map, HashMap};
|
||||||
use vmcontext::VMSignatureId;
|
use vmcontext::VMSignatureId;
|
||||||
@@ -11,14 +11,14 @@ use vmcontext::VMSignatureId;
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct SignatureRegistry {
|
pub struct SignatureRegistry {
|
||||||
signature_hash: HashMap<ir::Signature, VMSignatureId>,
|
signature_hash: HashMap<ir::Signature, VMSignatureId>,
|
||||||
signature_ids: SecondaryMap<SignatureIndex, VMSignatureId>,
|
signature_ids: PrimaryMap<SignatureIndex, VMSignatureId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SignatureRegistry {
|
impl SignatureRegistry {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
signature_hash: HashMap::new(),
|
signature_hash: HashMap::new(),
|
||||||
signature_ids: SecondaryMap::new(),
|
signature_ids: PrimaryMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,7 +27,12 @@ impl SignatureRegistry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Register the given signature.
|
/// Register the given signature.
|
||||||
pub fn register(&mut self, sig_index: SignatureIndex, sig: &ir::Signature) -> VMSignatureId {
|
pub fn register(&mut self, sig_index: SignatureIndex, sig: &ir::Signature) {
|
||||||
|
// TODO: Refactor this interface so that we're not passing in redundant
|
||||||
|
// information.
|
||||||
|
debug_assert_eq!(sig_index.index(), self.signature_ids.len());
|
||||||
|
use cranelift_entity::EntityRef;
|
||||||
|
|
||||||
let len = self.signature_hash.len();
|
let len = self.signature_hash.len();
|
||||||
let sig_id = match self.signature_hash.entry(sig.clone()) {
|
let sig_id = match self.signature_hash.entry(sig.clone()) {
|
||||||
hash_map::Entry::Occupied(entry) => *entry.get(),
|
hash_map::Entry::Occupied(entry) => *entry.get(),
|
||||||
@@ -37,7 +42,11 @@ impl SignatureRegistry {
|
|||||||
sig_id
|
sig_id
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
self.signature_ids[sig_index] = sig_id;
|
self.signature_ids.push(sig_id);
|
||||||
sig_id
|
}
|
||||||
|
|
||||||
|
/// Return the identifying runtime index for the given signature.
|
||||||
|
pub fn lookup(&mut self, sig_index: SignatureIndex) -> VMSignatureId {
|
||||||
|
self.signature_ids[sig_index]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,8 +39,9 @@ impl Table {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return a `VMTable` for exposing the table to JIT code.
|
||||||
pub fn vmtable(&mut self) -> VMTable {
|
pub fn vmtable(&mut self) -> VMTable {
|
||||||
VMTable::new(self.vec.as_mut_ptr() as *mut u8, self.vec.len())
|
VMTable::definition(self.vec.as_mut_ptr() as *mut u8, self.vec.len())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,20 +2,86 @@
|
|||||||
//! fields that JIT code accesses directly.
|
//! fields that JIT code accesses directly.
|
||||||
|
|
||||||
use cranelift_entity::EntityRef;
|
use cranelift_entity::EntityRef;
|
||||||
use cranelift_wasm::{GlobalIndex, MemoryIndex, TableIndex};
|
use cranelift_wasm::{Global, GlobalIndex, GlobalInit, MemoryIndex, TableIndex};
|
||||||
use instance::Instance;
|
use instance::Instance;
|
||||||
use std::mem::size_of;
|
use std::fmt;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
use std::slice;
|
|
||||||
|
|
||||||
/// The main fields a JIT needs to access to utilize a WebAssembly linear,
|
/// The fields a JIT needs to access to utilize a WebAssembly linear
|
||||||
/// memory, namely the start address and the size in bytes.
|
/// memory defined within the instance, namely the start address and the
|
||||||
#[derive(Debug)]
|
/// size in bytes.
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct VMMemory {
|
pub struct VMMemoryDefinition {
|
||||||
|
/// The start address.
|
||||||
base: *mut u8,
|
base: *mut u8,
|
||||||
|
/// The current size of linear memory in bytes.
|
||||||
current_length: usize,
|
current_length: usize,
|
||||||
// If more elements are added here, remember to add offset_of tests below!
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test_vmmemory_definition {
|
||||||
|
use super::VMMemoryDefinition;
|
||||||
|
use std::mem::size_of;
|
||||||
|
use wasmtime_environ::VMOffsets;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn check_vmmemory_definition_offsets() {
|
||||||
|
let offsets = VMOffsets::new(size_of::<*mut u8>() as u8);
|
||||||
|
assert_eq!(
|
||||||
|
size_of::<VMMemoryDefinition>(),
|
||||||
|
usize::from(offsets.size_of_vmmemory_definition())
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
offset_of!(VMMemoryDefinition, base),
|
||||||
|
usize::from(offsets.vmmemory_definition_base())
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
offset_of!(VMMemoryDefinition, current_length),
|
||||||
|
usize::from(offsets.vmmemory_definition_current_length())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The fields a JIT needs to access to utilize a WebAssembly linear
|
||||||
|
/// memory imported from another instance.
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct VMMemoryImport {
|
||||||
|
/// A pointer to the imported memory description.
|
||||||
|
from: *mut VMMemoryDefinition,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test_vmmemory_import {
|
||||||
|
use super::VMMemoryImport;
|
||||||
|
use std::mem::size_of;
|
||||||
|
use wasmtime_environ::VMOffsets;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn check_vmmemory_import_offsets() {
|
||||||
|
let offsets = VMOffsets::new(size_of::<*mut u8>() as u8);
|
||||||
|
assert_eq!(
|
||||||
|
size_of::<VMMemoryImport>(),
|
||||||
|
usize::from(offsets.size_of_vmmemory_import())
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
offset_of!(VMMemoryImport, from),
|
||||||
|
usize::from(offsets.vmmemory_import_from())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The main fields a JIT needs to access to utilize a WebAssembly linear
|
||||||
|
/// memory. It must know whether the memory is defined within the instance
|
||||||
|
/// or imported.
|
||||||
|
#[repr(C)]
|
||||||
|
pub union VMMemory {
|
||||||
|
/// A linear memory defined within the instance.
|
||||||
|
definition: VMMemoryDefinition,
|
||||||
|
|
||||||
|
/// An imported linear memory.
|
||||||
|
import: VMMemoryImport,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -31,71 +97,155 @@ mod test_vmmemory {
|
|||||||
size_of::<VMMemory>(),
|
size_of::<VMMemory>(),
|
||||||
usize::from(offsets.size_of_vmmemory())
|
usize::from(offsets.size_of_vmmemory())
|
||||||
);
|
);
|
||||||
assert_eq!(
|
|
||||||
offset_of!(VMMemory, base),
|
|
||||||
usize::from(offsets.vmmemory_base())
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
offset_of!(VMMemory, current_length),
|
|
||||||
usize::from(offsets.vmmemory_current_length())
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VMMemory {
|
impl VMMemory {
|
||||||
pub fn new(base: *mut u8, current_length: usize) -> Self {
|
/// Construct a `VMMemoryDefinition` variant of `VMMemory`.
|
||||||
|
pub fn definition(base: *mut u8, current_length: usize) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
definition: VMMemoryDefinition {
|
||||||
base,
|
base,
|
||||||
current_length,
|
current_length,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_slice(&self) -> &[u8] {
|
/// Construct a `VMMemoryImmport` variant of `VMMemory`.
|
||||||
unsafe { slice::from_raw_parts(self.base, self.current_length) }
|
pub fn import(from: *mut VMMemoryDefinition) -> Self {
|
||||||
}
|
Self {
|
||||||
|
import: VMMemoryImport { from },
|
||||||
pub fn as_mut_slice(&mut self) -> &mut [u8] {
|
|
||||||
unsafe { slice::from_raw_parts_mut(self.base, self.current_length) }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn as_ptr(&self) -> *const u8 {
|
|
||||||
self.base
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn as_mut_ptr(&mut self) -> *mut u8 {
|
|
||||||
self.base
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn len(&self) -> usize {
|
|
||||||
self.current_length
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The storage for a WebAssembly global.
|
/// Get the underlying `VMMemoryDefinition`.
|
||||||
|
pub unsafe fn get_definition(&mut self, is_import: bool) -> &mut VMMemoryDefinition {
|
||||||
|
if is_import {
|
||||||
|
&mut *self.import.from
|
||||||
|
} else {
|
||||||
|
&mut self.definition
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for VMMemory {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "VMMemory {{")?;
|
||||||
|
write!(f, " definition: {:?},", unsafe { self.definition })?;
|
||||||
|
write!(f, " import: {:?},", unsafe { self.import })?;
|
||||||
|
write!(f, "}}")?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The storage for a WebAssembly global defined within the instance.
|
||||||
///
|
///
|
||||||
/// TODO: Pack the globals more densely, rather than using the same size
|
/// TODO: Pack the globals more densely, rather than using the same size
|
||||||
/// for every type.
|
/// for every type.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
#[repr(C, align(8))]
|
#[repr(C, align(8))]
|
||||||
pub struct VMGlobal {
|
pub struct VMGlobalDefinition {
|
||||||
storage: [u8; 8],
|
storage: [u8; 8],
|
||||||
// If more elements are added here, remember to add offset_of tests below!
|
// If more elements are added here, remember to add offset_of tests below!
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test_vmglobal {
|
mod test_vmglobal_definition {
|
||||||
use super::VMGlobal;
|
use super::VMGlobalDefinition;
|
||||||
use std::mem::{align_of, size_of};
|
use std::mem::{align_of, size_of};
|
||||||
use wasmtime_environ::VMOffsets;
|
use wasmtime_environ::VMOffsets;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn check_vmglobal_alignment() {
|
fn check_vmglobal_definition_alignment() {
|
||||||
assert!(align_of::<VMGlobal>() >= align_of::<i32>());
|
assert!(align_of::<VMGlobalDefinition>() >= align_of::<i32>());
|
||||||
assert!(align_of::<VMGlobal>() >= align_of::<i64>());
|
assert!(align_of::<VMGlobalDefinition>() >= align_of::<i64>());
|
||||||
assert!(align_of::<VMGlobal>() >= align_of::<f32>());
|
assert!(align_of::<VMGlobalDefinition>() >= align_of::<f32>());
|
||||||
assert!(align_of::<VMGlobal>() >= align_of::<f64>());
|
assert!(align_of::<VMGlobalDefinition>() >= align_of::<f64>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn check_vmglobal_definition_offsets() {
|
||||||
|
let offsets = VMOffsets::new(size_of::<*mut u8>() as u8);
|
||||||
|
assert_eq!(
|
||||||
|
size_of::<VMGlobalDefinition>(),
|
||||||
|
usize::from(offsets.size_of_vmglobal_definition())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VMGlobalDefinition {
|
||||||
|
pub unsafe fn as_i32(&mut self) -> &mut i32 {
|
||||||
|
&mut *(self.storage.as_mut().as_mut_ptr() as *mut u8 as *mut i32)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn as_i64(&mut self) -> &mut i64 {
|
||||||
|
&mut *(self.storage.as_mut().as_mut_ptr() as *mut u8 as *mut i64)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn as_f32(&mut self) -> &mut f32 {
|
||||||
|
&mut *(self.storage.as_mut().as_mut_ptr() as *mut u8 as *mut f32)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn as_f32_bits(&mut self) -> &mut u32 {
|
||||||
|
&mut *(self.storage.as_mut().as_mut_ptr() as *mut u8 as *mut u32)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn as_f64(&mut self) -> &mut f64 {
|
||||||
|
&mut *(self.storage.as_mut().as_mut_ptr() as *mut u8 as *mut f64)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn as_f64_bits(&mut self) -> &mut u64 {
|
||||||
|
&mut *(self.storage.as_mut().as_mut_ptr() as *mut u8 as *mut u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The fields a JIT needs to access to utilize a WebAssembly global
|
||||||
|
/// variable imported from another instance.
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct VMGlobalImport {
|
||||||
|
/// A pointer to the imported global variable description.
|
||||||
|
from: *mut VMGlobalDefinition,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test_vmglobal_import {
|
||||||
|
use super::VMGlobalImport;
|
||||||
|
use std::mem::size_of;
|
||||||
|
use wasmtime_environ::VMOffsets;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn check_vmglobal_import_offsets() {
|
||||||
|
let offsets = VMOffsets::new(size_of::<*mut u8>() as u8);
|
||||||
|
assert_eq!(
|
||||||
|
size_of::<VMGlobalImport>(),
|
||||||
|
usize::from(offsets.size_of_vmglobal_import())
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
offset_of!(VMGlobalImport, from),
|
||||||
|
usize::from(offsets.vmglobal_import_from())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The main fields a JIT needs to access to utilize a WebAssembly global
|
||||||
|
/// variable. It must know whether the global variable is defined within the
|
||||||
|
/// instance or imported.
|
||||||
|
#[repr(C)]
|
||||||
|
pub union VMGlobal {
|
||||||
|
/// A global variable defined within the instance.
|
||||||
|
definition: VMGlobalDefinition,
|
||||||
|
|
||||||
|
/// An imported global variable.
|
||||||
|
import: VMGlobalImport,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test_vmglobal {
|
||||||
|
use super::VMGlobal;
|
||||||
|
use std::mem::size_of;
|
||||||
|
use wasmtime_environ::VMOffsets;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn check_vmglobal_offsets() {
|
fn check_vmglobal_offsets() {
|
||||||
let offsets = VMOffsets::new(size_of::<*mut u8>() as u8);
|
let offsets = VMOffsets::new(size_of::<*mut u8>() as u8);
|
||||||
@@ -106,20 +256,122 @@ mod test_vmglobal {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for VMGlobal {
|
impl VMGlobal {
|
||||||
fn default() -> Self {
|
/// Construct a `VMGlobalDefinition` variant of `VMGlobal`.
|
||||||
VMGlobal { storage: [0; 8] }
|
pub fn definition(global: &Global) -> Self {
|
||||||
|
let mut result = VMGlobalDefinition { storage: [0; 8] };
|
||||||
|
unsafe {
|
||||||
|
match global.initializer {
|
||||||
|
GlobalInit::I32Const(x) => *result.as_i32() = x,
|
||||||
|
GlobalInit::I64Const(x) => *result.as_i64() = x,
|
||||||
|
GlobalInit::F32Const(x) => *result.as_f32_bits() = x,
|
||||||
|
GlobalInit::F64Const(x) => *result.as_f64_bits() = x,
|
||||||
|
GlobalInit::GetGlobal(_x) => unimplemented!("globals init with get_global"),
|
||||||
|
GlobalInit::Import => panic!("attempting to initialize imported global"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Self { definition: result }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct a `VMGlobalImmport` variant of `VMGlobal`.
|
||||||
|
pub fn import(from: *mut VMGlobalDefinition) -> Self {
|
||||||
|
Self {
|
||||||
|
import: VMGlobalImport { from },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
/// Get the underlying `VMGlobalDefinition`.
|
||||||
/// The main fields a JIT needs to access to utilize a WebAssembly table,
|
pub unsafe fn get_definition(&mut self, is_import: bool) -> &mut VMGlobalDefinition {
|
||||||
/// namely the start address and the number of elements.
|
if is_import {
|
||||||
|
&mut *self.import.from
|
||||||
|
} else {
|
||||||
|
&mut self.definition
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for VMGlobal {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "VMGlobal {{")?;
|
||||||
|
write!(f, " definition: {:?},", unsafe { self.definition })?;
|
||||||
|
write!(f, " import: {:?},", unsafe { self.import })?;
|
||||||
|
write!(f, "}}")?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The fields a JIT needs to access to utilize a WebAssembly table
|
||||||
|
/// defined within the instance.
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct VMTable {
|
pub struct VMTableDefinition {
|
||||||
base: *mut u8,
|
base: *mut u8,
|
||||||
current_elements: usize,
|
current_elements: usize,
|
||||||
// If more elements are added here, remember to add offset_of tests below!
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test_vmtable_definition {
|
||||||
|
use super::VMTableDefinition;
|
||||||
|
use std::mem::size_of;
|
||||||
|
use wasmtime_environ::VMOffsets;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn check_vmtable_definition_offsets() {
|
||||||
|
let offsets = VMOffsets::new(size_of::<*mut u8>() as u8);
|
||||||
|
assert_eq!(
|
||||||
|
size_of::<VMTableDefinition>(),
|
||||||
|
usize::from(offsets.size_of_vmtable_definition())
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
offset_of!(VMTableDefinition, base),
|
||||||
|
usize::from(offsets.vmtable_definition_base())
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
offset_of!(VMTableDefinition, current_elements),
|
||||||
|
usize::from(offsets.vmtable_definition_current_elements())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The fields a JIT needs to access to utilize a WebAssembly table
|
||||||
|
/// imported from another instance.
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct VMTableImport {
|
||||||
|
/// A pointer to the imported table description.
|
||||||
|
from: *mut VMTableDefinition,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test_vmtable_import {
|
||||||
|
use super::VMTableImport;
|
||||||
|
use std::mem::size_of;
|
||||||
|
use wasmtime_environ::VMOffsets;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn check_vmtable_import_offsets() {
|
||||||
|
let offsets = VMOffsets::new(size_of::<*mut u8>() as u8);
|
||||||
|
assert_eq!(
|
||||||
|
size_of::<VMTableImport>(),
|
||||||
|
usize::from(offsets.size_of_vmtable_import())
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
offset_of!(VMTableImport, from),
|
||||||
|
usize::from(offsets.vmtable_import_from())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The main fields a JIT needs to access to utilize a WebAssembly table.
|
||||||
|
/// It must know whether the table is defined within the instance
|
||||||
|
/// or imported.
|
||||||
|
#[repr(C)]
|
||||||
|
pub union VMTable {
|
||||||
|
/// A table defined within the instance.
|
||||||
|
definition: VMTableDefinition,
|
||||||
|
|
||||||
|
/// An imported table.
|
||||||
|
import: VMTableImport,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -132,43 +384,44 @@ mod test_vmtable {
|
|||||||
fn check_vmtable_offsets() {
|
fn check_vmtable_offsets() {
|
||||||
let offsets = VMOffsets::new(size_of::<*mut u8>() as u8);
|
let offsets = VMOffsets::new(size_of::<*mut u8>() as u8);
|
||||||
assert_eq!(size_of::<VMTable>(), usize::from(offsets.size_of_vmtable()));
|
assert_eq!(size_of::<VMTable>(), usize::from(offsets.size_of_vmtable()));
|
||||||
assert_eq!(
|
|
||||||
offset_of!(VMTable, base),
|
|
||||||
usize::from(offsets.vmtable_base())
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
offset_of!(VMTable, current_elements),
|
|
||||||
usize::from(offsets.vmtable_current_elements())
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VMTable {
|
impl VMTable {
|
||||||
pub fn new(base: *mut u8, current_elements: usize) -> Self {
|
/// Construct a `VMTableDefinition` variant of `VMTable`.
|
||||||
|
pub fn definition(base: *mut u8, current_elements: usize) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
definition: VMTableDefinition {
|
||||||
base,
|
base,
|
||||||
current_elements,
|
current_elements,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_slice(&self) -> &[u8] {
|
/// Construct a `VMTableImmport` variant of `VMTable`.
|
||||||
unsafe { slice::from_raw_parts(self.base, self.current_elements) }
|
pub fn import(from: *mut VMTableDefinition) -> Self {
|
||||||
|
Self {
|
||||||
|
import: VMTableImport { from },
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_mut_slice(&mut self) -> &mut [u8] {
|
/// Get the underlying `VMTableDefinition`.
|
||||||
unsafe { slice::from_raw_parts_mut(self.base, self.current_elements) }
|
pub unsafe fn get_definition(&mut self, is_import: bool) -> &mut VMTableDefinition {
|
||||||
|
if is_import {
|
||||||
|
&mut *self.import.from
|
||||||
|
} else {
|
||||||
|
&mut self.definition
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_ptr(&self) -> *const u8 {
|
impl fmt::Debug for VMTable {
|
||||||
self.base
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
}
|
write!(f, "VMTable {{")?;
|
||||||
|
write!(f, " definition: {:?},", unsafe { self.definition })?;
|
||||||
pub fn as_mut_ptr(&mut self) -> *mut u8 {
|
write!(f, " import: {:?},", unsafe { self.import })?;
|
||||||
self.base
|
write!(f, "}}")?;
|
||||||
}
|
Ok(())
|
||||||
|
|
||||||
pub fn len(&self) -> usize {
|
|
||||||
self.current_elements
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -238,6 +491,10 @@ impl Default for VMCallerCheckedAnyfunc {
|
|||||||
/// The VM "context", which is pointed to by the `vmctx` arg in Cranelift.
|
/// The VM "context", which is pointed to by the `vmctx` arg in Cranelift.
|
||||||
/// This has pointers to the globals, memories, tables, and other runtime
|
/// This has pointers to the globals, memories, tables, and other runtime
|
||||||
/// state associated with the current instance.
|
/// state associated with the current instance.
|
||||||
|
///
|
||||||
|
/// TODO: The number of memories, globals, tables, and signature IDs does
|
||||||
|
/// not change dynamically, and pointer arrays are not indexed dynamically,
|
||||||
|
/// so these fields could all be contiguously allocated.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct VMContext {
|
pub struct VMContext {
|
||||||
@@ -300,28 +557,8 @@ impl VMContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Return the base pointer of the globals array.
|
/// Return the base pointer of the globals array.
|
||||||
pub unsafe fn global_storage(&mut self, index: GlobalIndex) -> *mut VMGlobal {
|
pub unsafe fn global(&mut self, index: GlobalIndex) -> &mut VMGlobal {
|
||||||
self.globals.add(index.index() * size_of::<VMGlobal>())
|
&mut *self.globals.add(index.index())
|
||||||
}
|
|
||||||
|
|
||||||
/// Return a mutable reference to global `index` which has type i32.
|
|
||||||
pub unsafe fn global_i32(&mut self, index: GlobalIndex) -> &mut i32 {
|
|
||||||
&mut *(self.global_storage(index) as *mut i32)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return a mutable reference to global `index` which has type i64.
|
|
||||||
pub unsafe fn global_i64(&mut self, index: GlobalIndex) -> &mut i64 {
|
|
||||||
&mut *(self.global_storage(index) as *mut i64)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return a mutable reference to global `index` which has type f32.
|
|
||||||
pub unsafe fn global_f32(&mut self, index: GlobalIndex) -> &mut f32 {
|
|
||||||
&mut *(self.global_storage(index) as *mut f32)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return a mutable reference to global `index` which has type f64.
|
|
||||||
pub unsafe fn global_f64(&mut self, index: GlobalIndex) -> &mut f64 {
|
|
||||||
&mut *(self.global_storage(index) as *mut f64)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a mutable reference to linear memory `index`.
|
/// Return a mutable reference to linear memory `index`.
|
||||||
|
|||||||
@@ -1,7 +1,13 @@
|
|||||||
use cranelift_codegen::isa;
|
use cranelift_codegen::isa;
|
||||||
|
use cranelift_wasm::{GlobalIndex, MemoryIndex};
|
||||||
|
use export::Resolver;
|
||||||
use std::str;
|
use std::str;
|
||||||
|
use vmcontext::VMGlobal;
|
||||||
use wasmtime_environ::{Compilation, Module, ModuleEnvironment, Tunables};
|
use wasmtime_environ::{Compilation, Module, ModuleEnvironment, Tunables};
|
||||||
use {compile_and_link_module, finish_instantiation, invoke, Code, Instance, InvokeOutcome, Value};
|
use {
|
||||||
|
compile_and_link_module, finish_instantiation, get, invoke, Code, Instance, InvokeOutcome,
|
||||||
|
Value,
|
||||||
|
};
|
||||||
|
|
||||||
/// A module, an instance of that module, and accompanying compilation artifacts.
|
/// A module, an instance of that module, and accompanying compilation artifacts.
|
||||||
///
|
///
|
||||||
@@ -14,8 +20,14 @@ pub struct InstanceWorld {
|
|||||||
|
|
||||||
impl InstanceWorld {
|
impl InstanceWorld {
|
||||||
/// Create a new `InstanceWorld` by compiling the wasm module in `data` and instatiating it.
|
/// Create a new `InstanceWorld` by compiling the wasm module in `data` and instatiating it.
|
||||||
pub fn new(code: &mut Code, isa: &isa::TargetIsa, data: &[u8]) -> Result<Self, String> {
|
pub fn new(
|
||||||
|
code: &mut Code,
|
||||||
|
isa: &isa::TargetIsa,
|
||||||
|
data: &[u8],
|
||||||
|
resolver: &mut Resolver,
|
||||||
|
) -> Result<Self, String> {
|
||||||
let mut module = Module::new();
|
let mut module = Module::new();
|
||||||
|
// TODO: Allow the tunables to be overridden.
|
||||||
let tunables = Tunables::default();
|
let tunables = Tunables::default();
|
||||||
let (instance, compilation) = {
|
let (instance, compilation) = {
|
||||||
let translation = {
|
let translation = {
|
||||||
@@ -24,9 +36,7 @@ impl InstanceWorld {
|
|||||||
environ.translate(&data).map_err(|e| e.to_string())?
|
environ.translate(&data).map_err(|e| e.to_string())?
|
||||||
};
|
};
|
||||||
|
|
||||||
let imports_resolver = |_env: &str, _function: &str| None;
|
let compilation = compile_and_link_module(isa, &translation, resolver)?;
|
||||||
|
|
||||||
let compilation = compile_and_link_module(isa, &translation, &imports_resolver)?;
|
|
||||||
let mut instance = Instance::new(
|
let mut instance = Instance::new(
|
||||||
translation.module,
|
translation.module,
|
||||||
&compilation,
|
&compilation,
|
||||||
@@ -64,4 +74,19 @@ impl InstanceWorld {
|
|||||||
)
|
)
|
||||||
.map_err(|e| e.to_string())
|
.map_err(|e| e.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Read a global in this `InstanceWorld` by name.
|
||||||
|
pub fn get(&mut self, global_name: &str) -> Result<Value, String> {
|
||||||
|
get(&self.module, self.instance.vmctx(), global_name).map_err(|e| e.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a slice of the contents of allocated linear memory.
|
||||||
|
pub fn inspect_memory(&self, memory_index: MemoryIndex, address: usize, len: usize) -> &[u8] {
|
||||||
|
self.instance.inspect_memory(memory_index, address, len)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Shows the value of a global variable.
|
||||||
|
pub fn inspect_global(&self, global_index: GlobalIndex) -> &VMGlobal {
|
||||||
|
self.instance.inspect_global(global_index)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,4 +3,3 @@ for writing out native object files, using the wasm ABI defined by
|
|||||||
[`wasmtime-environ`].
|
[`wasmtime-environ`].
|
||||||
|
|
||||||
[`wasmtime-environ`]: https://crates.io/crates/wasmtime-environ
|
[`wasmtime-environ`]: https://crates.io/crates/wasmtime-environ
|
||||||
|
|
||||||
|
|||||||
@@ -12,8 +12,11 @@ readme = "README.md"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
cranelift-codegen = { git = "https://github.com/sunfishcode/cranelift.git", branch = "guard-offset" }
|
cranelift-codegen = { git = "https://github.com/sunfishcode/cranelift.git", branch = "guard-offset" }
|
||||||
cranelift-native = { git = "https://github.com/sunfishcode/cranelift.git", branch = "guard-offset" }
|
cranelift-native = { git = "https://github.com/sunfishcode/cranelift.git", branch = "guard-offset" }
|
||||||
|
cranelift-wasm = { git = "https://github.com/sunfishcode/cranelift.git", branch = "guard-offset" }
|
||||||
wasmtime-execute = { path = "../execute" }
|
wasmtime-execute = { path = "../execute" }
|
||||||
|
wasmtime-environ = { path = "../environ" }
|
||||||
wabt = "0.7"
|
wabt = "0.7"
|
||||||
|
target-lexicon = "0.2.0"
|
||||||
|
|
||||||
[badges]
|
[badges]
|
||||||
maintenance = { status = "experimental" }
|
maintenance = { status = "experimental" }
|
||||||
|
|||||||
@@ -94,8 +94,7 @@ fn ignore(testsuite: &str, name: &str) -> bool {
|
|||||||
match testsuite {
|
match testsuite {
|
||||||
"spec_testsuite" => match name {
|
"spec_testsuite" => match name {
|
||||||
// These are the remaining spec testsuite failures.
|
// These are the remaining spec testsuite failures.
|
||||||
"data" | "elem" | "exports" | "func" | "func_ptrs" | "globals" | "imports"
|
"data" | "elem" | "imports" | "linking" => true,
|
||||||
| "linking" | "names" | "start" => true,
|
|
||||||
_ => false,
|
_ => false,
|
||||||
},
|
},
|
||||||
_ => false,
|
_ => false,
|
||||||
|
|||||||
@@ -23,9 +23,13 @@
|
|||||||
)]
|
)]
|
||||||
|
|
||||||
extern crate cranelift_codegen;
|
extern crate cranelift_codegen;
|
||||||
|
extern crate cranelift_wasm;
|
||||||
|
extern crate target_lexicon;
|
||||||
extern crate wabt;
|
extern crate wabt;
|
||||||
|
extern crate wasmtime_environ;
|
||||||
extern crate wasmtime_execute;
|
extern crate wasmtime_execute;
|
||||||
|
|
||||||
|
mod spectest;
|
||||||
mod wast;
|
mod wast;
|
||||||
|
|
||||||
pub use wast::{wast_buffer, wast_file};
|
pub use wast::{wast_buffer, wast_file};
|
||||||
|
|||||||
210
lib/wast/src/spectest.rs
Normal file
210
lib/wast/src/spectest.rs
Normal file
@@ -0,0 +1,210 @@
|
|||||||
|
use cranelift_codegen::ir::types;
|
||||||
|
use cranelift_codegen::{ir, isa};
|
||||||
|
use cranelift_wasm::{Global, GlobalInit, Memory, Table, TableElementType};
|
||||||
|
use std::ptr;
|
||||||
|
use target_lexicon::HOST;
|
||||||
|
use wasmtime_environ::{translate_signature, MemoryPlan, MemoryStyle, TablePlan, TableStyle};
|
||||||
|
use wasmtime_execute::{ExportValue, Resolver, VMGlobal, VMMemory, VMTable};
|
||||||
|
|
||||||
|
extern "C" fn spectest_print() {}
|
||||||
|
|
||||||
|
extern "C" fn spectest_print_i32(x: i32) {
|
||||||
|
println!("{}: i32", x);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" fn spectest_print_i64(x: i64) {
|
||||||
|
println!("{}: i64", x);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" fn spectest_print_f32(x: f32) {
|
||||||
|
println!("{}: f32", x);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" fn spectest_print_f64(x: f64) {
|
||||||
|
println!("{}: f64", x);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" fn spectest_print_i32_f32(x: i32, y: f32) {
|
||||||
|
println!("{}: i32", x);
|
||||||
|
println!("{}: f32", y);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" fn spectest_print_f64_f64(x: f64, y: f64) {
|
||||||
|
println!("{}: f64", x);
|
||||||
|
println!("{}: f64", y);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SpecTest {
|
||||||
|
spectest_global_i32: VMGlobal,
|
||||||
|
spectest_global_f32: VMGlobal,
|
||||||
|
spectest_global_f64: VMGlobal,
|
||||||
|
spectest_table: VMTable,
|
||||||
|
spectest_memory: VMMemory,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SpecTest {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
spectest_global_i32: VMGlobal::definition(&Global {
|
||||||
|
ty: types::I32,
|
||||||
|
mutability: false,
|
||||||
|
initializer: GlobalInit::I32Const(0),
|
||||||
|
}),
|
||||||
|
spectest_global_f32: VMGlobal::definition(&Global {
|
||||||
|
ty: types::I32,
|
||||||
|
mutability: false,
|
||||||
|
initializer: GlobalInit::F32Const(0),
|
||||||
|
}),
|
||||||
|
spectest_global_f64: VMGlobal::definition(&Global {
|
||||||
|
ty: types::I32,
|
||||||
|
mutability: false,
|
||||||
|
initializer: GlobalInit::F64Const(0),
|
||||||
|
}),
|
||||||
|
spectest_table: VMTable::definition(ptr::null_mut(), 0),
|
||||||
|
spectest_memory: VMMemory::definition(ptr::null_mut(), 0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Resolver for SpecTest {
|
||||||
|
fn resolve(&mut self, module: &str, field: &str) -> Option<ExportValue> {
|
||||||
|
let call_conv = isa::CallConv::triple_default(&HOST);
|
||||||
|
let pointer_type = types::Type::triple_pointer_type(&HOST);
|
||||||
|
match module {
|
||||||
|
"spectest" => match field {
|
||||||
|
"print" => Some(ExportValue::function(
|
||||||
|
spectest_print as usize,
|
||||||
|
translate_signature(
|
||||||
|
ir::Signature {
|
||||||
|
params: vec![],
|
||||||
|
returns: vec![],
|
||||||
|
call_conv,
|
||||||
|
},
|
||||||
|
pointer_type,
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
"print_i32" => Some(ExportValue::function(
|
||||||
|
spectest_print_i32 as usize,
|
||||||
|
translate_signature(
|
||||||
|
ir::Signature {
|
||||||
|
params: vec![ir::AbiParam::new(types::I32)],
|
||||||
|
returns: vec![],
|
||||||
|
call_conv,
|
||||||
|
},
|
||||||
|
pointer_type,
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
"print_i64" => Some(ExportValue::function(
|
||||||
|
spectest_print_i64 as usize,
|
||||||
|
translate_signature(
|
||||||
|
ir::Signature {
|
||||||
|
params: vec![ir::AbiParam::new(types::I64)],
|
||||||
|
returns: vec![],
|
||||||
|
call_conv,
|
||||||
|
},
|
||||||
|
pointer_type,
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
"print_f32" => Some(ExportValue::function(
|
||||||
|
spectest_print_f32 as usize,
|
||||||
|
translate_signature(
|
||||||
|
ir::Signature {
|
||||||
|
params: vec![ir::AbiParam::new(types::F32)],
|
||||||
|
returns: vec![],
|
||||||
|
call_conv,
|
||||||
|
},
|
||||||
|
pointer_type,
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
"print_f64" => Some(ExportValue::function(
|
||||||
|
spectest_print_f64 as usize,
|
||||||
|
translate_signature(
|
||||||
|
ir::Signature {
|
||||||
|
params: vec![ir::AbiParam::new(types::F64)],
|
||||||
|
returns: vec![],
|
||||||
|
call_conv,
|
||||||
|
},
|
||||||
|
pointer_type,
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
"print_i32_f32" => Some(ExportValue::function(
|
||||||
|
spectest_print_i32_f32 as usize,
|
||||||
|
translate_signature(
|
||||||
|
ir::Signature {
|
||||||
|
params: vec![
|
||||||
|
ir::AbiParam::new(types::I32),
|
||||||
|
ir::AbiParam::new(types::F32),
|
||||||
|
],
|
||||||
|
returns: vec![],
|
||||||
|
call_conv,
|
||||||
|
},
|
||||||
|
pointer_type,
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
"print_f64_f64" => Some(ExportValue::function(
|
||||||
|
spectest_print_f64_f64 as usize,
|
||||||
|
translate_signature(
|
||||||
|
ir::Signature {
|
||||||
|
params: vec![
|
||||||
|
ir::AbiParam::new(types::F64),
|
||||||
|
ir::AbiParam::new(types::F64),
|
||||||
|
],
|
||||||
|
returns: vec![],
|
||||||
|
call_conv,
|
||||||
|
},
|
||||||
|
pointer_type,
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
"global_i32" => Some(ExportValue::global(
|
||||||
|
&mut self.spectest_global_i32,
|
||||||
|
Global {
|
||||||
|
ty: ir::types::I32,
|
||||||
|
mutability: false,
|
||||||
|
initializer: GlobalInit::I32Const(0),
|
||||||
|
},
|
||||||
|
)),
|
||||||
|
"global_f32" => Some(ExportValue::global(
|
||||||
|
&mut self.spectest_global_f32,
|
||||||
|
Global {
|
||||||
|
ty: ir::types::F32,
|
||||||
|
mutability: false,
|
||||||
|
initializer: GlobalInit::F32Const(0),
|
||||||
|
},
|
||||||
|
)),
|
||||||
|
"global_f64" => Some(ExportValue::global(
|
||||||
|
&mut self.spectest_global_f64,
|
||||||
|
Global {
|
||||||
|
ty: ir::types::F64,
|
||||||
|
mutability: false,
|
||||||
|
initializer: GlobalInit::F64Const(0),
|
||||||
|
},
|
||||||
|
)),
|
||||||
|
"table" => Some(ExportValue::table(
|
||||||
|
&mut self.spectest_table,
|
||||||
|
TablePlan {
|
||||||
|
table: Table {
|
||||||
|
ty: TableElementType::Func,
|
||||||
|
minimum: 0,
|
||||||
|
maximum: None,
|
||||||
|
},
|
||||||
|
style: TableStyle::CallerChecksSignature,
|
||||||
|
},
|
||||||
|
)),
|
||||||
|
"memory" => Some(ExportValue::memory(
|
||||||
|
&mut self.spectest_memory,
|
||||||
|
MemoryPlan {
|
||||||
|
memory: Memory {
|
||||||
|
minimum: 0,
|
||||||
|
maximum: None,
|
||||||
|
shared: false,
|
||||||
|
},
|
||||||
|
style: MemoryStyle::Dynamic,
|
||||||
|
offset_guard_size: 0,
|
||||||
|
},
|
||||||
|
)),
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
use cranelift_codegen::isa;
|
use cranelift_codegen::isa;
|
||||||
|
use spectest::SpecTest;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::io;
|
use std::io;
|
||||||
@@ -12,6 +13,7 @@ struct Instances {
|
|||||||
current: Option<InstanceWorld>,
|
current: Option<InstanceWorld>,
|
||||||
namespace: HashMap<String, InstanceWorld>,
|
namespace: HashMap<String, InstanceWorld>,
|
||||||
code: Code,
|
code: Code,
|
||||||
|
spectest: SpecTest,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Instances {
|
impl Instances {
|
||||||
@@ -20,11 +22,12 @@ impl Instances {
|
|||||||
current: None,
|
current: None,
|
||||||
namespace: HashMap::new(),
|
namespace: HashMap::new(),
|
||||||
code: Code::new(),
|
code: Code::new(),
|
||||||
|
spectest: SpecTest::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn instantiate(&mut self, isa: &isa::TargetIsa, module: ModuleBinary) -> InstanceWorld {
|
fn instantiate(&mut self, isa: &isa::TargetIsa, module: ModuleBinary) -> InstanceWorld {
|
||||||
InstanceWorld::new(&mut self.code, isa, &module.into_vec()).unwrap()
|
InstanceWorld::new(&mut self.code, isa, &module.into_vec(), &mut self.spectest).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn define_unnamed_module(&mut self, isa: &isa::TargetIsa, module: ModuleBinary) {
|
pub fn define_unnamed_module(&mut self, isa: &isa::TargetIsa, module: ModuleBinary) {
|
||||||
@@ -41,6 +44,7 @@ impl Instances {
|
|||||||
self.namespace.insert(name, world);
|
self.namespace.insert(name, world);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// fixme: Rename InvokeOutcome to ActionOutcome.
|
||||||
pub fn perform_action(&mut self, isa: &isa::TargetIsa, action: Action) -> InvokeOutcome {
|
pub fn perform_action(&mut self, isa: &isa::TargetIsa, action: Action) -> InvokeOutcome {
|
||||||
match action {
|
match action {
|
||||||
Action::Invoke {
|
Action::Invoke {
|
||||||
@@ -72,7 +76,25 @@ impl Instances {
|
|||||||
.expect(&format!("error invoking {} in module {}", field, name)),
|
.expect(&format!("error invoking {} in module {}", field, name)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => panic!("unsupported action {:?}", action),
|
Action::Get { module, field } => {
|
||||||
|
let value = match module {
|
||||||
|
None => match self.current {
|
||||||
|
None => panic!("get performed with no module present"),
|
||||||
|
Some(ref mut instance_world) => instance_world
|
||||||
|
.get(&field)
|
||||||
|
.expect(&format!("error getting {} in current module", field)),
|
||||||
|
},
|
||||||
|
Some(name) => self
|
||||||
|
.namespace
|
||||||
|
.get_mut(&name)
|
||||||
|
.expect(&format!("module {} not declared", name))
|
||||||
|
.get(&field)
|
||||||
|
.expect(&format!("error getting {} in module {}", field, name)),
|
||||||
|
};
|
||||||
|
InvokeOutcome::Returned {
|
||||||
|
values: vec![value],
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ extern crate cranelift_entity;
|
|||||||
extern crate cranelift_native;
|
extern crate cranelift_native;
|
||||||
extern crate cranelift_wasm;
|
extern crate cranelift_wasm;
|
||||||
extern crate docopt;
|
extern crate docopt;
|
||||||
extern crate wasmtime_environ;
|
|
||||||
extern crate wasmtime_execute;
|
extern crate wasmtime_execute;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate serde_derive;
|
extern crate serde_derive;
|
||||||
@@ -57,8 +56,7 @@ use std::io::stdout;
|
|||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::process::exit;
|
use std::process::exit;
|
||||||
use wasmtime_environ::{Module, ModuleEnvironment, Tunables};
|
use wasmtime_execute::{Code, InstanceWorld, NullResolver};
|
||||||
use wasmtime_execute::{compile_and_link_module, finish_instantiation, invoke, Code, Instance};
|
|
||||||
|
|
||||||
static LOG_FILENAME_PREFIX: &str = "cranelift.dbg.";
|
static LOG_FILENAME_PREFIX: &str = "cranelift.dbg.";
|
||||||
|
|
||||||
@@ -147,51 +145,14 @@ fn handle_module(args: &Args, path: PathBuf, isa: &TargetIsa) -> Result<(), Stri
|
|||||||
if !data.starts_with(&[b'\0', b'a', b's', b'm']) {
|
if !data.starts_with(&[b'\0', b'a', b's', b'm']) {
|
||||||
data = wabt::wat2wasm(data).map_err(|err| String::from(err.description()))?;
|
data = wabt::wat2wasm(data).map_err(|err| String::from(err.description()))?;
|
||||||
}
|
}
|
||||||
let mut module = Module::new();
|
let mut resolver = NullResolver {};
|
||||||
// TODO: Expose the tunables as command-line flags.
|
|
||||||
let tunables = Tunables::default();
|
|
||||||
let environ = ModuleEnvironment::new(isa, &mut module, tunables);
|
|
||||||
|
|
||||||
let imports_resolver = |_env: &str, _function: &str| None;
|
|
||||||
|
|
||||||
let translation = environ.translate(&data).map_err(|e| e.to_string())?;
|
|
||||||
|
|
||||||
let mut code = Code::new();
|
let mut code = Code::new();
|
||||||
|
let mut world = InstanceWorld::new(&mut code, isa, &data, &mut resolver)?;
|
||||||
let instance = match compile_and_link_module(isa, &translation, &imports_resolver) {
|
|
||||||
Ok(compilation) => {
|
|
||||||
let mut instance = Instance::new(
|
|
||||||
translation.module,
|
|
||||||
&compilation,
|
|
||||||
&translation.lazy.data_initializers,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
finish_instantiation(
|
|
||||||
&mut code,
|
|
||||||
isa,
|
|
||||||
&translation.module,
|
|
||||||
&compilation,
|
|
||||||
&mut instance,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
if let Some(ref f) = args.flag_function {
|
if let Some(ref f) = args.flag_function {
|
||||||
invoke(
|
world.invoke(&mut code, isa, &f, &[])?;
|
||||||
&mut code,
|
|
||||||
isa,
|
|
||||||
&translation.module,
|
|
||||||
&compilation,
|
|
||||||
instance.vmctx(),
|
|
||||||
&f,
|
|
||||||
&[],
|
|
||||||
)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
instance
|
|
||||||
}
|
|
||||||
Err(s) => {
|
|
||||||
return Err(s);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if args.flag_memory {
|
if args.flag_memory {
|
||||||
let mut input = String::new();
|
let mut input = String::new();
|
||||||
println!("Inspecting memory");
|
println!("Inspecting memory");
|
||||||
@@ -210,7 +171,7 @@ fn handle_module(args: &Args, path: PathBuf, isa: &TargetIsa) -> Result<(), Stri
|
|||||||
if split.len() != 3 {
|
if split.len() != 3 {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
let memory = instance.inspect_memory(
|
let memory = world.inspect_memory(
|
||||||
MemoryIndex::new(str::parse(split[0]).unwrap()),
|
MemoryIndex::new(str::parse(split[0]).unwrap()),
|
||||||
str::parse(split[1]).unwrap(),
|
str::parse(split[1]).unwrap(),
|
||||||
str::parse(split[2]).unwrap(),
|
str::parse(split[2]).unwrap(),
|
||||||
@@ -235,7 +196,7 @@ mod tests {
|
|||||||
use cranelift_codegen::settings::Configurable;
|
use cranelift_codegen::settings::Configurable;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use wabt;
|
use wabt;
|
||||||
use wasmtime_environ::{Module, ModuleEnvironment, Tunables};
|
use wasmtime_execute::{Code, InstanceWorld, NullResolver};
|
||||||
|
|
||||||
const PATH_MODULE_RS2WASM_ADD_FUNC: &str = r"filetests/rs2wasm-add-func.wat";
|
const PATH_MODULE_RS2WASM_ADD_FUNC: &str = r"filetests/rs2wasm-add-func.wat";
|
||||||
|
|
||||||
@@ -257,11 +218,9 @@ mod tests {
|
|||||||
});
|
});
|
||||||
let isa = isa_builder.finish(settings::Flags::new(flag_builder));
|
let isa = isa_builder.finish(settings::Flags::new(flag_builder));
|
||||||
|
|
||||||
let mut module = Module::new();
|
let mut resolver = NullResolver {};
|
||||||
let tunables = Tunables::default();
|
let mut code = Code::new();
|
||||||
let environ = ModuleEnvironment::new(&*isa, &mut module, tunables);
|
let world = InstanceWorld::new(&mut code, &*isa, &data, &mut resolver);
|
||||||
|
assert!(world.is_ok());
|
||||||
let translation = environ.translate(&data);
|
|
||||||
assert!(translation.is_ok());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user