Added working mock for memory (grow and size)

This commit is contained in:
Syrus Akbary 2018-10-18 00:09:04 +02:00
parent d86e372b8e
commit 96f2440960
9 changed files with 192 additions and 36 deletions

View File

@ -1,5 +1,5 @@
use libc::putchar;
use crate::webassembly::ImportObject; use crate::webassembly::ImportObject;
use libc::putchar;
pub fn generate_libc_env<'a, 'b>() -> ImportObject<&'a str, &'b str> { pub fn generate_libc_env<'a, 'b>() -> ImportObject<&'a str, &'b str> {
let mut import_object = ImportObject::new(); let mut import_object = ImportObject::new();
@ -9,18 +9,17 @@ pub fn generate_libc_env<'a, 'b>() -> ImportObject<&'a str, &'b str> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::generate_libc_env;
use crate::webassembly::{ use crate::webassembly::{
instantiate, ErrorKind, Export, ImportObject, Instance, Module, ResultObject, instantiate, ErrorKind, Export, ImportObject, Instance, Module, ResultObject,
}; };
use super::generate_libc_env;
use libc::putchar; use libc::putchar;
#[test] #[test]
fn test_putchar() { fn test_putchar() {
let wasm_bytes = include_wast2wasm_bytes!("tests/putchar.wast"); let wasm_bytes = include_wast2wasm_bytes!("tests/putchar.wast");
let import_object = generate_libc_env(); let import_object = generate_libc_env();
let result_object = let result_object = instantiate(wasm_bytes, import_object).expect("Not compiled properly");
instantiate(wasm_bytes, import_object).expect("Not compiled properly");
let module = result_object.module; let module = result_object.module;
let instance = result_object.instance; let instance = result_object.instance;
let func_index = match module.info.exports.get("main") { let func_index = match module.info.exports.get("main") {

View File

@ -18,16 +18,16 @@ macro_rules! include_wast2wasm_bytes {
}}; }};
} }
#[cfg(feature= "debug")] // #[cfg(feature= "debug")]
#[macro_export] #[macro_export]
macro_rules! debug { macro_rules! debug {
($fmt:expr) => (println!(concat!("Wasmer::", $fmt))); ($fmt:expr) => (println!(concat!("Wasmer::", $fmt)));
($fmt:expr, $($arg:tt)*) => (println!(concat!("Wasmer::", $fmt, "\n"), $($arg)*)); ($fmt:expr, $($arg:tt)*) => (println!(concat!("Wasmer::", $fmt, "\n"), $($arg)*));
} }
#[cfg(not(feature= "debug"))] // #[cfg(not(feature= "debug"))]
#[macro_export] // #[macro_export]
macro_rules! debug { // macro_rules! debug {
($fmt:expr) => {}; // ($fmt:expr) => {};
($fmt:expr, $($arg:tt)*) => {}; // ($fmt:expr, $($arg:tt)*) => {};
} // }

View File

@ -71,13 +71,15 @@ fn execute_wasm(wasm_path: PathBuf) -> Result<(), String> {
} }
let import_object = integrations::generate_libc_env(); let import_object = integrations::generate_libc_env();
let webassembly::ResultObject {module, instance} = webassembly::instantiate(wasm_binary, import_object).map_err(|err| String::from(err.description()))?; let webassembly::ResultObject { module, instance } =
let func_index = instance.start_func.unwrap_or_else(|| { webassembly::instantiate(wasm_binary, import_object)
match module.info.exports.get("main") { .map_err(|err| String::from(err.description()))?;
let func_index = instance
.start_func
.unwrap_or_else(|| match module.info.exports.get("main") {
Some(&webassembly::Export::Function(index)) => index, Some(&webassembly::Export::Function(index)) => index,
_ => panic!("Main function not found"), _ => panic!("Main function not found"),
} });
});
let main: fn() = get_instance_function!(instance, func_index); let main: fn() = get_instance_function!(instance, func_index);
main(); main();
Ok(()) Ok(())

View File

@ -111,7 +111,7 @@ impl<'module> ScriptHandler for StoreCtrl<'module> {
}; };
println!( println!(
"Function {:?}(index: {:?}) ({:?}) => {:?}", "Function {:?}(index: {:?}) ({:?}) => returned {:?}",
field.to_string(), field.to_string(),
func_index, func_index,
call_args, call_args,
@ -177,7 +177,8 @@ mod tests {
use std::path::Path; use std::path::Path;
#[macro_use] #[macro_use]
use crate::webassembly::{ use crate::webassembly::{
compile, instantiate, Error, ErrorKind, Export, Instance, Module, ResultObject, ImportObject compile, instantiate, Error, ErrorKind, Export, Instance, Module, ResultObject,
ImportObject,
}; };
use wabt::wat2wasm; use wabt::wat2wasm;
@ -209,7 +210,8 @@ mod tests {
macro_rules! instantiate_from_wast { macro_rules! instantiate_from_wast {
($x:expr) => {{ ($x:expr) => {{
let wasm_bytes = include_wast2wasm_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), $x)); let wasm_bytes = include_wast2wasm_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), $x));
let result_object = instantiate(wasm_bytes, ImportObject::new()).expect("Not compiled properly"); let result_object =
instantiate(wasm_bytes, ImportObject::new()).expect("Not compiled properly");
result_object result_object
}}; }};
} }
@ -235,6 +237,7 @@ mod tests {
br_if, br_if,
call, call,
import, import,
memory,
} }
} }

View File

@ -0,0 +1,75 @@
(module (memory 0 0))
(module (memory 0 1))
(module (memory 1 256))
(module (memory 0 65536))
(assert_invalid (module (memory 0) (memory 0)) "multiple memories")
(assert_invalid (module (memory (import "spectest" "memory") 0) (memory 0)) "multiple memories")
(module (memory (data)) (func (export "memsize") (result i32) (memory.size)))
(assert_return (invoke "memsize") (i32.const 0))
;; (module (memory (data "")) (func (export "memsize") (result i32) (memory.size)))
;; (assert_return (invoke "memsize") (i32.const 0))
;; (module (memory (data "x")) (func (export "memsize") (result i32) (memory.size)))
;; (assert_return (invoke "memsize") (i32.const 1))
;; (assert_invalid (module (data (i32.const 0))) "unknown memory")
;; (assert_invalid (module (data (i32.const 0) "")) "unknown memory")
;; (assert_invalid (module (data (i32.const 0) "x")) "unknown memory")
;; (assert_invalid
;; (module (func (drop (f32.load (i32.const 0)))))
;; "unknown memory"
;; )
;; (assert_invalid
;; (module (func (f32.store (f32.const 0) (i32.const 0))))
;; "unknown memory"
;; )
;; (assert_invalid
;; (module (func (drop (i32.load8_s (i32.const 0)))))
;; "unknown memory"
;; )
;; (assert_invalid
;; (module (func (i32.store8 (i32.const 0) (i32.const 0))))
;; "unknown memory"
;; )
;; (assert_invalid
;; (module (func (drop (memory.size))))
;; "unknown memory"
;; )
;; (assert_invalid
;; (module (func (drop (memory.grow (i32.const 0)))))
;; "unknown memory"
;; )
;; (assert_invalid
;; (module (memory 1 0))
;; "size minimum must not be greater than maximum"
;; )
;; (assert_invalid
;; (module (memory 65537))
;; "memory size must be at most 65536 pages (4GiB)"
;; )
;; (assert_invalid
;; (module (memory 2147483648))
;; "memory size must be at most 65536 pages (4GiB)"
;; )
;; (assert_invalid
;; (module (memory 4294967295))
;; "memory size must be at most 65536 pages (4GiB)"
;; )
;; (assert_invalid
;; (module (memory 0 65537))
;; "memory size must be at most 65536 pages (4GiB)"
;; )
;; (assert_invalid
;; (module (memory 0 2147483648))
;; "memory size must be at most 65536 pages (4GiB)"
;; )
;; (assert_invalid
;; (module (memory 0 4294967295))
;; "memory size must be at most 65536 pages (4GiB)"
;; )

View File

@ -232,6 +232,12 @@ impl Instance {
RelocationType::Normal(func_index) => { RelocationType::Normal(func_index) => {
get_function_addr(&FuncIndex::new(*func_index as usize), &import_functions, &functions) as isize get_function_addr(&FuncIndex::new(*func_index as usize), &import_functions, &functions) as isize
}, },
RelocationType::CurrentMemory => {
current_memory as isize
},
RelocationType::GrowMemory => {
grow_memory as isize
},
_ => unimplemented!() _ => unimplemented!()
// RelocationType::Intrinsic(name) => { // RelocationType::Intrinsic(name) => {
// get_abi_intrinsic(name)? // get_abi_intrinsic(name)?
@ -547,7 +553,8 @@ impl Clone for Instance {
} }
extern "C" fn grow_memory(size: u32, memory_index: u32, vmctx: *mut *mut u8) -> u32 { extern "C" fn grow_memory(size: u32, memory_index: u32, vmctx: *mut *mut u8) -> u32 {
unimplemented!(); return 0;
// unimplemented!();
// unsafe { // unsafe {
// let instance = (*vmctx.offset(4)) as *mut Instance; // let instance = (*vmctx.offset(4)) as *mut Instance;
// (*instance) // (*instance)
@ -558,7 +565,8 @@ extern "C" fn grow_memory(size: u32, memory_index: u32, vmctx: *mut *mut u8) ->
} }
extern "C" fn current_memory(memory_index: u32, vmctx: *mut *mut u8) -> u32 { extern "C" fn current_memory(memory_index: u32, vmctx: *mut *mut u8) -> u32 {
unimplemented!(); return 0;
// unimplemented!();
// unsafe { // unsafe {
// let instance = (*vmctx.offset(4)) as *mut Instance; // let instance = (*vmctx.offset(4)) as *mut Instance;
// (*instance) // (*instance)

View File

@ -34,16 +34,20 @@ impl LinearMemory {
"Instantiate LinearMemory(initial={:?}, maximum={:?})", "Instantiate LinearMemory(initial={:?}, maximum={:?})",
initial, maximum initial, maximum
); );
let len = PAGE_SIZE * match maximum { let initial = if initial > 0 { initial } else { 1 };
Some(val) => val,
None => { let len: u64 = PAGE_SIZE as u64 * match maximum {
if initial > 0 { Some(val) => {
initial if val > initial {
val as u64
} else { } else {
1 initial as u64
} }
} }
None => initial as u64,
}; };
let len = if len == 0 { 1 } else { len };
let mmap = MmapMut::map_anon(len as usize).unwrap(); let mmap = MmapMut::map_anon(len as usize).unwrap();
debug!("LinearMemory instantiated"); debug!("LinearMemory instantiated");
Self { Self {

View File

@ -13,6 +13,7 @@ use cranelift_codegen::ir::{
FuncRef, Function, InstBuilder, Signature, FuncRef, Function, InstBuilder, Signature,
}; };
use cranelift_codegen::print_errors::pretty_verifier_error; use cranelift_codegen::print_errors::pretty_verifier_error;
use cranelift_codegen::settings::CallConv;
use cranelift_codegen::{isa, settings, verifier}; use cranelift_codegen::{isa, settings, verifier};
use cranelift_entity::{EntityRef, PrimaryMap}; use cranelift_entity::{EntityRef, PrimaryMap};
@ -127,6 +128,12 @@ pub struct ModuleInfo {
/// We use this in order to have a O(1) allocation of the exports /// We use this in order to have a O(1) allocation of the exports
/// rather than iterating through the Exportable elements. /// rather than iterating through the Exportable elements.
pub exports: HashMap<String, Export>, pub exports: HashMap<String, Export>,
/// The external function declaration for implementing wasm's `current_memory`.
pub current_memory_extfunc: Option<FuncRef>,
/// The external function declaration for implementing wasm's `grow_memory`.
pub grow_memory_extfunc: Option<FuncRef>,
} }
impl ModuleInfo { impl ModuleInfo {
@ -150,8 +157,13 @@ impl ModuleInfo {
main_memory_base: None, main_memory_base: None,
memory_base: None, memory_base: None,
exports: HashMap::new(), exports: HashMap::new(),
current_memory_extfunc: None,
grow_memory_extfunc: None,
} }
} }
pub fn set_current_memory_extfunc(&mut self, func: FuncRef) {
self.current_memory_extfunc = Some(func);
}
} }
/// A data initializer for linear memory. /// A data initializer for linear memory.
@ -557,20 +569,66 @@ impl<'environment> FuncEnvironmentTrait for FuncEnvironment<'environment> {
fn translate_memory_grow( fn translate_memory_grow(
&mut self, &mut self,
mut pos: FuncCursor, mut pos: FuncCursor,
_index: MemoryIndex, index: MemoryIndex,
_heap: ir::Heap, heap: ir::Heap,
_val: ir::Value, val: ir::Value,
) -> WasmResult<ir::Value> { ) -> WasmResult<ir::Value> {
Ok(pos.ins().iconst(I32, -1)) debug_assert_eq!(index, 0, "non-default memories not supported yet");
let grow_mem_func = self.mod_info.grow_memory_extfunc.unwrap_or_else(|| {
let sig_ref = pos.func.import_signature(Signature {
call_conv: CallConv::SystemV,
// argument_bytes: None,
params: vec![
AbiParam::new(I32),
AbiParam::special(I64, ArgumentPurpose::VMContext),
],
returns: vec![AbiParam::new(I32)],
});
pos.func.import_function(ExtFuncData {
name: ExternalName::testcase("grow_memory"),
signature: sig_ref,
colocated: false,
})
});
// self.mod_info.grow_memory_extfunc = Some(grow_mem_func);
let vmctx = pos.func.special_param(ArgumentPurpose::VMContext).unwrap();
let call_inst = pos.ins().call(grow_mem_func, &[val, vmctx]);
Ok(*pos.func.dfg.inst_results(call_inst).first().unwrap())
} }
fn translate_memory_size( fn translate_memory_size(
&mut self, &mut self,
mut pos: FuncCursor, mut pos: FuncCursor,
_index: MemoryIndex, index: MemoryIndex,
_heap: ir::Heap, heap: ir::Heap,
) -> WasmResult<ir::Value> { ) -> WasmResult<ir::Value> {
Ok(pos.ins().iconst(I32, -1)) debug_assert_eq!(index, 0, "non-default memories not supported yet");
let cur_mem_func = self.mod_info.current_memory_extfunc.unwrap_or_else(|| {
let sig_ref = pos.func.import_signature(Signature {
call_conv: CallConv::SystemV,
// argument_bytes: None,
params: vec![AbiParam::special(I64, ArgumentPurpose::VMContext)],
returns: vec![AbiParam::new(I32)],
});
pos.func.import_function(ExtFuncData {
name: ExternalName::testcase("current_memory"),
signature: sig_ref,
colocated: false,
})
});
// self.mod_info.set_current_memory_extfunc(f);
let vmctx = pos.func.special_param(ArgumentPurpose::VMContext).unwrap();
let call_inst = pos.ins().call(cur_mem_func, &[vmctx]);
Ok(*pos.func.dfg.inst_results(call_inst).first().unwrap())
// Ok(pos.ins().iconst(I32, -1))
} }
// fn return_mode(&self) -> ReturnMode { // fn return_mode(&self) -> ReturnMode {
@ -677,6 +735,7 @@ impl<'data> ModuleEnvironment<'data> for Module {
data: &'data [u8], data: &'data [u8],
) { ) {
debug_assert!(base.is_none(), "global-value offsets not supported yet"); debug_assert!(base.is_none(), "global-value offsets not supported yet");
// debug!("DATA INITIALIZATION {:?} {:?}", memory_index, base);
self.info.data_initializers.push(DataInitializer { self.info.data_initializers.push(DataInitializer {
memory_index, memory_index,
base, base,

View File

@ -19,6 +19,8 @@ pub struct Relocation {
pub enum RelocationType { pub enum RelocationType {
Normal(u32), Normal(u32),
Intrinsic(String), Intrinsic(String),
GrowMemory,
CurrentMemory,
} }
/// Implementation of a relocation sink that just saves all the information for later /// Implementation of a relocation sink that just saves all the information for later
@ -62,14 +64,18 @@ impl binemit::RelocSink for RelocSink {
ExternalName::TestCase { length, ascii } => { ExternalName::TestCase { length, ascii } => {
let (slice, _) = ascii.split_at(length as usize); let (slice, _) = ascii.split_at(length as usize);
let name = String::from_utf8(slice.to_vec()).unwrap(); let name = String::from_utf8(slice.to_vec()).unwrap();
let relocation_type = match name.as_str() {
"current_memory" => RelocationType::CurrentMemory,
"grow_memory" => RelocationType::GrowMemory,
_ => RelocationType::Intrinsic(name),
};
self.func_relocs.push(( self.func_relocs.push((
Relocation { Relocation {
reloc, reloc,
offset, offset,
addend, addend,
}, },
RelocationType::Intrinsic(name), relocation_type,
)); ));
} }
_ => { _ => {