Implement the remaining valid spec tests.
And lots of other miscellaneous changes. Rename InstanceWorld to InstancePlus and reorganize its contents. This still isn't a great name, but at least now it has a clear purpose.
This commit is contained in:
@@ -1,16 +1,14 @@
|
||||
use cranelift_codegen::ir::types;
|
||||
use cranelift_codegen::{ir, isa};
|
||||
use cranelift_entity::PrimaryMap;
|
||||
use cranelift_wasm::{Global, GlobalInit, Memory, Table, TableElementType};
|
||||
use std::ptr;
|
||||
use cranelift_wasm::{DefinedFuncIndex, Global, GlobalInit, Memory, Table, TableElementType};
|
||||
use std::rc::Rc;
|
||||
use target_lexicon::HOST;
|
||||
use wasmtime_environ::{
|
||||
translate_signature, MemoryPlan, MemoryStyle, Module, TablePlan, TableStyle,
|
||||
};
|
||||
use wasmtime_execute::{Export, Resolver};
|
||||
use wasmtime_runtime::{
|
||||
Imports, Instance, VMFunctionBody, VMGlobalDefinition, VMMemoryDefinition, VMTableDefinition,
|
||||
translate_signature, Export, MemoryPlan, MemoryStyle, Module, TablePlan, TableStyle,
|
||||
};
|
||||
use wasmtime_execute::{ActionError, InstancePlus};
|
||||
use wasmtime_runtime::{Imports, VMFunctionBody};
|
||||
|
||||
extern "C" fn spectest_print() {}
|
||||
|
||||
@@ -46,195 +44,181 @@ extern "C" fn spectest_print_f64_f64(x: f64, y: f64) {
|
||||
println!("{}: f64", y);
|
||||
}
|
||||
|
||||
pub struct SpecTest {
|
||||
instance: Instance,
|
||||
spectest_global_i32: VMGlobalDefinition,
|
||||
spectest_global_f32: VMGlobalDefinition,
|
||||
spectest_global_f64: VMGlobalDefinition,
|
||||
spectest_table: VMTableDefinition,
|
||||
spectest_memory: VMMemoryDefinition,
|
||||
}
|
||||
/// Return an instance implementing the "spectest" interface used in the
|
||||
/// spec testsuite.
|
||||
pub fn instantiate_spectest() -> Result<InstancePlus, ActionError> {
|
||||
let call_conv = isa::CallConv::triple_default(&HOST);
|
||||
let pointer_type = types::Type::triple_pointer_type(&HOST);
|
||||
let mut module = Module::new();
|
||||
let mut finished_functions: PrimaryMap<DefinedFuncIndex, *const VMFunctionBody> =
|
||||
PrimaryMap::new();
|
||||
|
||||
impl SpecTest {
|
||||
pub fn new() -> Result<Self, String> {
|
||||
let finished_functions = PrimaryMap::new();
|
||||
let imports = Imports::none();
|
||||
let data_initializers = Vec::new();
|
||||
Ok(Self {
|
||||
instance: Instance::new(
|
||||
&Module::new(),
|
||||
&finished_functions.into_boxed_slice(),
|
||||
imports,
|
||||
&data_initializers,
|
||||
)?,
|
||||
spectest_global_i32: VMGlobalDefinition::new(&Global {
|
||||
ty: types::I32,
|
||||
mutability: true,
|
||||
initializer: GlobalInit::I32Const(0),
|
||||
}),
|
||||
spectest_global_f32: VMGlobalDefinition::new(&Global {
|
||||
ty: types::I32,
|
||||
mutability: true,
|
||||
initializer: GlobalInit::F32Const(0),
|
||||
}),
|
||||
spectest_global_f64: VMGlobalDefinition::new(&Global {
|
||||
ty: types::I32,
|
||||
mutability: true,
|
||||
initializer: GlobalInit::F64Const(0),
|
||||
}),
|
||||
spectest_table: VMTableDefinition {
|
||||
base: ptr::null_mut(),
|
||||
current_elements: 0,
|
||||
},
|
||||
spectest_memory: VMMemoryDefinition {
|
||||
base: ptr::null_mut(),
|
||||
current_length: 0,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
let sig = module.signatures.push(translate_signature(
|
||||
ir::Signature {
|
||||
params: vec![],
|
||||
returns: vec![],
|
||||
call_conv,
|
||||
},
|
||||
pointer_type,
|
||||
));
|
||||
let func = module.functions.push(sig);
|
||||
module
|
||||
.exports
|
||||
.insert("print".to_owned(), Export::Function(func));
|
||||
finished_functions.push(spectest_print as *const VMFunctionBody);
|
||||
|
||||
impl Resolver for SpecTest {
|
||||
fn resolve(&mut self, module: &str, field: &str) -> Option<Export> {
|
||||
let call_conv = isa::CallConv::triple_default(&HOST);
|
||||
let pointer_type = types::Type::triple_pointer_type(&HOST);
|
||||
match module {
|
||||
"spectest" => match field {
|
||||
"print" => Some(Export::function(
|
||||
spectest_print as *const VMFunctionBody,
|
||||
translate_signature(
|
||||
ir::Signature {
|
||||
params: vec![],
|
||||
returns: vec![],
|
||||
call_conv,
|
||||
},
|
||||
pointer_type,
|
||||
),
|
||||
)),
|
||||
"print_i32" => Some(Export::function(
|
||||
spectest_print_i32 as *const VMFunctionBody,
|
||||
translate_signature(
|
||||
ir::Signature {
|
||||
params: vec![ir::AbiParam::new(types::I32)],
|
||||
returns: vec![],
|
||||
call_conv,
|
||||
},
|
||||
pointer_type,
|
||||
),
|
||||
)),
|
||||
"print_i64" => Some(Export::function(
|
||||
spectest_print_i64 as *const VMFunctionBody,
|
||||
translate_signature(
|
||||
ir::Signature {
|
||||
params: vec![ir::AbiParam::new(types::I64)],
|
||||
returns: vec![],
|
||||
call_conv,
|
||||
},
|
||||
pointer_type,
|
||||
),
|
||||
)),
|
||||
"print_f32" => Some(Export::function(
|
||||
spectest_print_f32 as *const VMFunctionBody,
|
||||
translate_signature(
|
||||
ir::Signature {
|
||||
params: vec![ir::AbiParam::new(types::F32)],
|
||||
returns: vec![],
|
||||
call_conv,
|
||||
},
|
||||
pointer_type,
|
||||
),
|
||||
)),
|
||||
"print_f64" => Some(Export::function(
|
||||
spectest_print_f64 as *const VMFunctionBody,
|
||||
translate_signature(
|
||||
ir::Signature {
|
||||
params: vec![ir::AbiParam::new(types::F64)],
|
||||
returns: vec![],
|
||||
call_conv,
|
||||
},
|
||||
pointer_type,
|
||||
),
|
||||
)),
|
||||
"print_i32_f32" => Some(Export::function(
|
||||
spectest_print_i32_f32 as *const VMFunctionBody,
|
||||
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(Export::function(
|
||||
spectest_print_f64_f64 as *const VMFunctionBody,
|
||||
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(Export::global(
|
||||
&mut self.spectest_global_i32,
|
||||
Global {
|
||||
ty: ir::types::I32,
|
||||
mutability: false,
|
||||
initializer: GlobalInit::I32Const(0),
|
||||
},
|
||||
)),
|
||||
"global_f32" => Some(Export::global(
|
||||
&mut self.spectest_global_f32,
|
||||
Global {
|
||||
ty: ir::types::F32,
|
||||
mutability: false,
|
||||
initializer: GlobalInit::F32Const(0),
|
||||
},
|
||||
)),
|
||||
"global_f64" => Some(Export::global(
|
||||
&mut self.spectest_global_f64,
|
||||
Global {
|
||||
ty: ir::types::F64,
|
||||
mutability: false,
|
||||
initializer: GlobalInit::F64Const(0),
|
||||
},
|
||||
)),
|
||||
"table" => Some(Export::table(
|
||||
&mut self.spectest_table,
|
||||
self.instance.vmctx_mut(),
|
||||
TablePlan {
|
||||
table: Table {
|
||||
ty: TableElementType::Func,
|
||||
minimum: 0,
|
||||
maximum: None,
|
||||
},
|
||||
style: TableStyle::CallerChecksSignature,
|
||||
},
|
||||
)),
|
||||
"memory" => Some(Export::memory(
|
||||
&mut self.spectest_memory,
|
||||
self.instance.vmctx_mut(),
|
||||
MemoryPlan {
|
||||
memory: Memory {
|
||||
minimum: 0,
|
||||
maximum: None,
|
||||
shared: false,
|
||||
},
|
||||
style: MemoryStyle::Dynamic,
|
||||
offset_guard_size: 0,
|
||||
},
|
||||
)),
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
let sig = module.signatures.push(translate_signature(
|
||||
ir::Signature {
|
||||
params: vec![ir::AbiParam::new(types::I32)],
|
||||
returns: vec![],
|
||||
call_conv,
|
||||
},
|
||||
pointer_type,
|
||||
));
|
||||
let func = module.functions.push(sig);
|
||||
module
|
||||
.exports
|
||||
.insert("print_i32".to_owned(), Export::Function(func));
|
||||
finished_functions.push(spectest_print_i32 as *const VMFunctionBody);
|
||||
|
||||
let sig = module.signatures.push(translate_signature(
|
||||
ir::Signature {
|
||||
params: vec![ir::AbiParam::new(types::I64)],
|
||||
returns: vec![],
|
||||
call_conv,
|
||||
},
|
||||
pointer_type,
|
||||
));
|
||||
let func = module.functions.push(sig);
|
||||
module
|
||||
.exports
|
||||
.insert("print_i64".to_owned(), Export::Function(func));
|
||||
finished_functions.push(spectest_print_i64 as *const VMFunctionBody);
|
||||
|
||||
let sig = module.signatures.push(translate_signature(
|
||||
ir::Signature {
|
||||
params: vec![ir::AbiParam::new(types::F32)],
|
||||
returns: vec![],
|
||||
call_conv,
|
||||
},
|
||||
pointer_type,
|
||||
));
|
||||
let func = module.functions.push(sig);
|
||||
module
|
||||
.exports
|
||||
.insert("print_f32".to_owned(), Export::Function(func));
|
||||
finished_functions.push(spectest_print_f32 as *const VMFunctionBody);
|
||||
|
||||
let sig = module.signatures.push(translate_signature(
|
||||
ir::Signature {
|
||||
params: vec![ir::AbiParam::new(types::F64)],
|
||||
returns: vec![],
|
||||
call_conv,
|
||||
},
|
||||
pointer_type,
|
||||
));
|
||||
let func = module.functions.push(sig);
|
||||
module
|
||||
.exports
|
||||
.insert("print_f64".to_owned(), Export::Function(func));
|
||||
finished_functions.push(spectest_print_f64 as *const VMFunctionBody);
|
||||
|
||||
let sig = module.signatures.push(translate_signature(
|
||||
ir::Signature {
|
||||
params: vec![ir::AbiParam::new(types::I32), ir::AbiParam::new(types::F32)],
|
||||
returns: vec![],
|
||||
call_conv,
|
||||
},
|
||||
pointer_type,
|
||||
));
|
||||
let func = module.functions.push(sig);
|
||||
module
|
||||
.exports
|
||||
.insert("print_i32_f32".to_owned(), Export::Function(func));
|
||||
finished_functions.push(spectest_print_i32_f32 as *const VMFunctionBody);
|
||||
|
||||
let sig = module.signatures.push(translate_signature(
|
||||
ir::Signature {
|
||||
params: vec![ir::AbiParam::new(types::F64), ir::AbiParam::new(types::F64)],
|
||||
returns: vec![],
|
||||
call_conv,
|
||||
},
|
||||
pointer_type,
|
||||
));
|
||||
let func = module.functions.push(sig);
|
||||
module
|
||||
.exports
|
||||
.insert("print_f64_f64".to_owned(), Export::Function(func));
|
||||
finished_functions.push(spectest_print_f64_f64 as *const VMFunctionBody);
|
||||
|
||||
let global = module.globals.push(Global {
|
||||
ty: types::I32,
|
||||
mutability: false,
|
||||
initializer: GlobalInit::I32Const(666),
|
||||
});
|
||||
module
|
||||
.exports
|
||||
.insert("global_i32".to_owned(), Export::Global(global));
|
||||
|
||||
let global = module.globals.push(Global {
|
||||
ty: types::I64,
|
||||
mutability: false,
|
||||
initializer: GlobalInit::I64Const(666),
|
||||
});
|
||||
module
|
||||
.exports
|
||||
.insert("global_i64".to_owned(), Export::Global(global));
|
||||
|
||||
let global = module.globals.push(Global {
|
||||
ty: types::F32,
|
||||
mutability: false,
|
||||
initializer: GlobalInit::F32Const(0x44268000),
|
||||
});
|
||||
module
|
||||
.exports
|
||||
.insert("global_f32".to_owned(), Export::Global(global));
|
||||
|
||||
let global = module.globals.push(Global {
|
||||
ty: types::F64,
|
||||
mutability: false,
|
||||
initializer: GlobalInit::F64Const(0x4084d00000000000),
|
||||
});
|
||||
module
|
||||
.exports
|
||||
.insert("global_f64".to_owned(), Export::Global(global));
|
||||
|
||||
let table = module.table_plans.push(TablePlan {
|
||||
table: Table {
|
||||
ty: TableElementType::Func,
|
||||
minimum: 10,
|
||||
maximum: Some(20),
|
||||
},
|
||||
style: TableStyle::CallerChecksSignature,
|
||||
});
|
||||
module
|
||||
.exports
|
||||
.insert("table".to_owned(), Export::Table(table));
|
||||
|
||||
let memory = module.memory_plans.push(MemoryPlan {
|
||||
memory: Memory {
|
||||
minimum: 1,
|
||||
maximum: Some(2),
|
||||
shared: false,
|
||||
},
|
||||
style: MemoryStyle::Static { bound: 65536 },
|
||||
offset_guard_size: 0x80000000,
|
||||
});
|
||||
module
|
||||
.exports
|
||||
.insert("memory".to_owned(), Export::Memory(memory));
|
||||
|
||||
let imports = Imports::none();
|
||||
let data_initializers = Vec::new();
|
||||
|
||||
InstancePlus::with_parts(
|
||||
Rc::new(module),
|
||||
finished_functions.into_boxed_slice(),
|
||||
imports,
|
||||
data_initializers,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
use cranelift_codegen::isa;
|
||||
use cranelift_entity::PrimaryMap;
|
||||
use spectest::SpecTest;
|
||||
use spectest::instantiate_spectest;
|
||||
use std::collections::HashMap;
|
||||
use std::io::Read;
|
||||
use std::path::Path;
|
||||
use std::{fmt, fs, io, str};
|
||||
use wabt::script::{Action, Command, CommandKind, ModuleBinary, ScriptParser, Value};
|
||||
use wasmtime_execute::{ActionError, ActionOutcome, Code, InstanceWorld, RuntimeValue};
|
||||
use wasmtime_execute::{ActionError, ActionOutcome, InstancePlus, JITCode, Resolver, RuntimeValue};
|
||||
use wasmtime_runtime::Export;
|
||||
|
||||
/// Translate from a script::Value to a RuntimeValue.
|
||||
fn runtime_value(v: Value) -> RuntimeValue {
|
||||
@@ -72,45 +73,70 @@ pub struct WastFileError {
|
||||
error: WastError,
|
||||
}
|
||||
|
||||
/// An opaque reference to an `InstanceWorld`.
|
||||
/// An opaque reference to an `InstancePlus`.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub struct WorldIndex(u32);
|
||||
entity_impl!(WorldIndex, "world");
|
||||
pub struct InstancePlusIndex(u32);
|
||||
entity_impl!(InstancePlusIndex, "instance");
|
||||
|
||||
struct WasmNamespace {
|
||||
names: HashMap<String, InstancePlusIndex>,
|
||||
instances: PrimaryMap<InstancePlusIndex, InstancePlus>,
|
||||
}
|
||||
|
||||
impl WasmNamespace {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
names: HashMap::new(),
|
||||
instances: PrimaryMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolver for WasmNamespace {
|
||||
fn resolve(&mut self, module: &str, field: &str) -> Option<Export> {
|
||||
if let Some(index) = self.names.get(module) {
|
||||
self.instances[*index].instance.lookup(field)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The wast test script language allows modules to be defined and actions
|
||||
/// to be performed on them.
|
||||
pub struct WastContext {
|
||||
/// A namespace of wasm modules, keyed by an optional name.
|
||||
worlds: PrimaryMap<WorldIndex, InstanceWorld>,
|
||||
current: Option<WorldIndex>,
|
||||
namespace: HashMap<String, WorldIndex>,
|
||||
code: Code,
|
||||
spectest: SpecTest,
|
||||
current: Option<InstancePlusIndex>,
|
||||
namespace: WasmNamespace,
|
||||
jit_code: JITCode,
|
||||
}
|
||||
|
||||
impl WastContext {
|
||||
/// Construct a new instance of `WastContext`.
|
||||
pub fn new() -> Result<Self, String> {
|
||||
Ok(Self {
|
||||
worlds: PrimaryMap::new(),
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
current: None,
|
||||
namespace: HashMap::new(),
|
||||
code: Code::new(),
|
||||
spectest: SpecTest::new()?,
|
||||
})
|
||||
namespace: WasmNamespace::new(),
|
||||
jit_code: JITCode::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn instantiate(
|
||||
&mut self,
|
||||
isa: &isa::TargetIsa,
|
||||
module: ModuleBinary,
|
||||
) -> Result<InstanceWorld, ActionError> {
|
||||
InstanceWorld::new(&mut self.code, isa, &module.into_vec(), &mut self.spectest)
|
||||
) -> Result<InstancePlus, ActionError> {
|
||||
InstancePlus::new(
|
||||
&mut self.jit_code,
|
||||
isa,
|
||||
&module.into_vec(),
|
||||
&mut self.namespace,
|
||||
)
|
||||
}
|
||||
|
||||
fn get_world(&mut self, module: &Option<String>) -> Result<WorldIndex, WastError> {
|
||||
fn get_instance(&mut self, module: &Option<String>) -> Result<InstancePlusIndex, WastError> {
|
||||
let index = *if let Some(name) = module {
|
||||
self.namespace.get_mut(name).ok_or_else(|| {
|
||||
self.namespace.names.get_mut(name).ok_or_else(|| {
|
||||
WastError::Module(UnknownModule {
|
||||
module: Some(name.to_owned()),
|
||||
})
|
||||
@@ -124,6 +150,14 @@ impl WastContext {
|
||||
Ok(index)
|
||||
}
|
||||
|
||||
/// Register "spectest" which is used by the spec testsuite.
|
||||
pub fn register_spectest(&mut self) -> Result<(), ActionError> {
|
||||
let instance = instantiate_spectest()?;
|
||||
let index = self.namespace.instances.push(instance);
|
||||
self.register("spectest".to_owned(), index);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Define a module and register it.
|
||||
pub fn module(
|
||||
&mut self,
|
||||
@@ -131,21 +165,18 @@ impl WastContext {
|
||||
name: Option<String>,
|
||||
module: ModuleBinary,
|
||||
) -> Result<(), ActionError> {
|
||||
let world = self.instantiate(isa, module)?;
|
||||
let index = if let Some(name) = name {
|
||||
self.register(name, world)
|
||||
} else {
|
||||
self.worlds.push(world)
|
||||
};
|
||||
let instance = self.instantiate(isa, module)?;
|
||||
let index = self.namespace.instances.push(instance);
|
||||
if let Some(name) = name {
|
||||
self.register(name, index);
|
||||
}
|
||||
self.current = Some(index);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Register a module to make it available for performing actions.
|
||||
pub fn register(&mut self, name: String, world: InstanceWorld) -> WorldIndex {
|
||||
let index = self.worlds.push(world);
|
||||
self.namespace.insert(name, index);
|
||||
index
|
||||
pub fn register(&mut self, name: String, index: InstancePlusIndex) {
|
||||
self.namespace.names.insert(name, index);
|
||||
}
|
||||
|
||||
/// Invoke an exported function from a defined module.
|
||||
@@ -160,16 +191,18 @@ impl WastContext {
|
||||
for arg in args {
|
||||
value_args.push(runtime_value(*arg));
|
||||
}
|
||||
let index = self.get_world(&module)?;
|
||||
self.worlds[index]
|
||||
.invoke(&mut self.code, isa, &field, &value_args)
|
||||
let index = self.get_instance(&module)?;
|
||||
self.namespace.instances[index]
|
||||
.invoke(&mut self.jit_code, isa, &field, &value_args)
|
||||
.map_err(WastError::Action)
|
||||
}
|
||||
|
||||
/// Get the value of an exported global from a defined module.
|
||||
pub fn get(&mut self, module: Option<String>, field: &str) -> Result<RuntimeValue, WastError> {
|
||||
let index = self.get_world(&module)?;
|
||||
self.worlds[index].get(&field).map_err(WastError::Action)
|
||||
let index = self.get_instance(&module)?;
|
||||
self.namespace.instances[index]
|
||||
.get(&field)
|
||||
.map_err(WastError::Action)
|
||||
}
|
||||
|
||||
fn perform_action(
|
||||
@@ -211,11 +244,13 @@ impl WastContext {
|
||||
error: WastError::Action(error),
|
||||
})?;
|
||||
}
|
||||
CommandKind::Register {
|
||||
name: _name,
|
||||
as_name: _as_name,
|
||||
} => {
|
||||
println!("{}:{}: TODO: Implement register", filename, line);
|
||||
CommandKind::Register { name, as_name } => {
|
||||
let index = self.get_instance(&name).map_err(|error| WastFileError {
|
||||
filename: filename.to_string(),
|
||||
line,
|
||||
error,
|
||||
})?;
|
||||
self.register(as_name, index);
|
||||
}
|
||||
CommandKind::PerformAction(action) => match self
|
||||
.perform_action(isa, action)
|
||||
|
||||
Reference in New Issue
Block a user