Added first tests for the vmctx (memory usage)

This commit is contained in:
Syrus Akbary 2018-10-18 19:01:09 +02:00
parent 80f3bf161e
commit 6fce21e4d5
6 changed files with 179 additions and 59 deletions

View File

@ -5,16 +5,38 @@ use std::rc::Rc;
use cranelift_codegen::ir::types; use cranelift_codegen::ir::types;
use cranelift_entity::EntityRef; use cranelift_entity::EntityRef;
use libffi::high::call::*; use libffi::high::call::*;
use libffi::high::types::CType; use libffi::high::types::{CType, Type};
use libffi::middle;
use std::clone::Clone;
use std::iter::Iterator; use std::iter::Iterator;
use std::marker::{Copy, PhantomData};
use wabt::script::{Action, Value}; use wabt::script::{Action, Value};
// use crate::webassembly::instance::InvokeResult; // use crate::webassembly::instance::InvokeResult;
use super::{run_single_file, InvokationResult, ScriptHandler}; use super::{run_single_file, InvokationResult, ScriptHandler};
use crate::webassembly::{ use crate::webassembly::{
compile, instantiate, Error, ErrorKind, Export, ImportObject, Instance, Module, ResultObject, compile, instantiate, Error, ErrorKind, Export, ImportObject, Instance, Module, ResultObject,
VmCtx,
}; };
// impl Clone for VmCtx {
// fn clone(&self) -> Self {
// unimplemented!()
// }
// }
// impl Copy for VmCtx {
// }
// unsafe impl<> CType for VmCtx {
// fn reify() -> Type<Self> {
// // Type::make(middle::Type::i64())
// Type {
// untyped: middle::Type::i64(),
// _marker: PhantomData,
// }
// }
// }
struct StoreCtrl<'module> { struct StoreCtrl<'module> {
last_module: Option<ResultObject>, last_module: Option<ResultObject>,
modules: HashMap<String, Rc<&'module ResultObject>>, modules: HashMap<String, Rc<&'module ResultObject>>,
@ -73,7 +95,7 @@ impl<'module> ScriptHandler for StoreCtrl<'module> {
args: Vec<Value>, args: Vec<Value>,
) -> InvokationResult { ) -> InvokationResult {
if let Some(result) = &mut self.last_module { if let Some(result) = &mut self.last_module {
let instance = &result.instance; let instance = &mut result.instance;
let module = &result.module; let module = &result.module;
let func_index = match module.info.exports.get(&field) { let func_index = match module.info.exports.get(&field) {
Some(&Export::Function(index)) => index, Some(&Export::Function(index)) => index,
@ -81,7 +103,7 @@ impl<'module> ScriptHandler for StoreCtrl<'module> {
}; };
// We map the arguments provided into the raw Arguments provided // We map the arguments provided into the raw Arguments provided
// to libffi // to libffi
let call_args: Vec<Arg> = args let mut call_args: Vec<Arg> = args
.iter() .iter()
.map(|a| match a { .map(|a| match a {
Value::I32(v) => arg(v), Value::I32(v) => arg(v),
@ -90,6 +112,10 @@ impl<'module> ScriptHandler for StoreCtrl<'module> {
Value::F64(v) => arg(v), Value::F64(v) => arg(v),
}) })
.collect(); .collect();
// let vmctx = &instance.generate_context();
// let vmctx_ref = vmctx as *const _;
// let u8_ref = &(vmctx_ref as *const isize);
// call_args.push(arg(u8_ref));
// We use libffi to call a function with a vector of arguments // We use libffi to call a function with a vector of arguments
let call_func: fn() = instance.get_function(func_index); let call_func: fn() = instance.get_function(func_index);
let result: i64 = unsafe { call(CodePtr(call_func as *mut _), &call_args) }; let result: i64 = unsafe { call(CodePtr(call_func as *mut _), &call_args) };
@ -178,7 +204,7 @@ mod tests {
#[macro_use] #[macro_use]
use crate::webassembly::{ use crate::webassembly::{
compile, instantiate, Error, ErrorKind, Export, Instance, Module, ResultObject, compile, instantiate, Error, ErrorKind, Export, Instance, Module, ResultObject,
ImportObject, ImportObject, VmCtx,
}; };
use wabt::wat2wasm; use wabt::wat2wasm;
@ -232,12 +258,29 @@ mod tests {
}); });
} }
#[test]
fn test_memory() {
let result_object = instantiate_from_wast!("/src/spec/tests/memory2.wast");
let mut instance = result_object.instance;
let module = result_object.module;
let func_index = match module.info.exports.get("memsize") {
Some(&Export::Function(index)) => index,
_ => panic!("Function not found"),
};
let func: fn(&VmCtx) -> i32 = get_instance_function!(instance, func_index);
let ctx = instance.generate_context();
assert_eq!(func(ctx), 1, "Identity function not working.");
// b.iter(|| {
// func(1);
// });
}
wasm_tests!{ wasm_tests!{
_type, _type,
br_if, br_if,
call, call,
import, import,
memory, // memory,
} }
} }

View File

@ -0,0 +1,6 @@
(module
(memory (data "a"))
(func (export "memsize") (result i32)
(memory.size)
)
)

View File

@ -69,6 +69,7 @@ fn get_function_addr(
// } // }
/// Zero-sized, non-instantiable type. /// Zero-sized, non-instantiable type.
#[derive(Debug)]
pub enum VmCtx {} pub enum VmCtx {}
impl VmCtx { impl VmCtx {
@ -85,15 +86,20 @@ impl VmCtx {
} }
} }
#[derive(Debug)]
#[repr(C)] #[repr(C)]
pub struct VmCtxData<'phantom> { pub struct VmCtxData<'phantom> {
pub user_data: UserData, pub user_data: UserData,
globals: UncheckedSlice<u8>, // globals: UncheckedSlice<u8>,
memories: UncheckedSlice<UncheckedSlice<u8>>, // memories: UncheckedSlice<UncheckedSlice<u8>>,
tables: UncheckedSlice<BoundedSlice<usize>>, // tables: UncheckedSlice<BoundedSlice<usize>>,
globals: Vec<u8>,
memories: Vec<Vec<u8>>,
tables: Vec<Vec<usize>>,
phantom: PhantomData<&'phantom ()>, phantom: PhantomData<&'phantom ()>,
} }
#[derive(Debug)]
#[repr(C)] #[repr(C)]
pub struct UserData { pub struct UserData {
// pub process: Dispatch<Process>, // pub process: Dispatch<Process>,
@ -445,6 +451,12 @@ impl Instance {
}) })
} }
// pub fn memory_mut(&self, memory_index: usize) -> &mut LinearMemory {
// self.memories
// .get_mut(memory_index)
// .unwrap_or_else(|| panic!("no memory for index {}", memory_index))
// }
pub fn memories(&self) -> Arc<Vec<LinearMemory>> { pub fn memories(&self) -> Arc<Vec<LinearMemory>> {
self.memories.clone() self.memories.clone()
} }
@ -491,46 +503,56 @@ impl Instance {
result result
} }
pub fn start(&self) { pub fn start(&mut self, vmctx: &VmCtx) {
if let Some(func_index) = self.start_func { if let Some(func_index) = self.start_func {
// let vmctx: &VmCtx = ptr::null(); let func: fn(&VmCtx) = get_instance_function!(self, func_index);
let func: fn() = self.get_function(func_index); func(vmctx)
func()
} }
} }
// pub fn generate_context(&mut self) -> &VmCtx { pub fn generate_context(&mut self) -> &VmCtx {
// let memories: Vec<UncheckedSlice<u8>> = self.memories.iter() let mut memories: Vec<Vec<u8>> = self.memories.iter().map(|mem| mem[..].into()).collect();
// .map(|mem| mem.into())
// .collect();
// let tables: Vec<BoundedSlice<usize>> = self.tables.iter() let tables: Vec<Vec<usize>> = self.tables.iter().map(|table| table[..].into()).collect();
// .map(|table| table.write()[..].into())
// .collect();
// let globals: UncheckedSlice<u8> = self.globals[..].into(); let globals: Vec<u8> = self.globals[..].into();
// assert!(memories.len() >= 1, "modules must have at least one memory"); assert!(memories.len() >= 1, "modules must have at least one memory");
// // the first memory has a space of `mem::size_of::<VmCtxData>()` rounded // the first memory has a space of `mem::size_of::<VmCtxData>()` rounded
// // up to the 4KiB before it. We write the VmCtxData into that. // up to the 4KiB before it. We write the VmCtxData into that.
// let data = VmCtxData { let instance = self.clone();
// globals: globals, let data = VmCtxData {
// memories: memories[1..].into(), globals: globals,
// tables: tables[..].into(), memories: memories[1..].into(),
// user_data: UserData { tables: tables[..].into(),
// // process, user_data: UserData {
// instance, // process,
// }, instance,
// phantom: PhantomData, },
// }; phantom: PhantomData,
};
// let main_heap_ptr = memories[0].as_mut_ptr() as *mut VmCtxData; let main_heap_ptr = memories[0].as_mut_ptr() as *mut VmCtxData;
// unsafe { unsafe {
// main_heap_ptr main_heap_ptr.sub(1).write(data);
// .sub(1) &*(main_heap_ptr as *const VmCtx)
// .write(data); }
// &*(main_heap_ptr as *const VmCtx) }
// }
/// Returns a slice of the contents of allocated linear memory.
pub fn inspect_memory(&self, memory_index: usize, address: usize, len: usize) -> &[u8] {
&self
.memories
.get(memory_index)
.unwrap_or_else(|| panic!("no memory for index {}", memory_index))
.as_ref()[address..address + len]
}
// Shows the value of a global variable.
// pub fn inspect_global(&self, global_index: GlobalIndex, ty: ir::Type) -> &[u8] {
// let offset = global_index * 8;
// let len = ty.bytes() as usize;
// &self.globals[offset..offset + len]
// } // }
// pub fn start_func(&self) -> extern fn(&VmCtx) { // pub fn start_func(&self) -> extern fn(&VmCtx) {
@ -552,9 +574,22 @@ 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: &VmCtx) -> i32 {
return 0; // return 0;
// unimplemented!(); unimplemented!();
// let instance = &vmctx
// .data()
// .user_data
// .instance;
// let mut memory = &mut instance.memories[memory_index as usize];
// if let Some(old_size) = memory.grow(size) {
// old_size as i32
// } else {
-1
// }
// unsafe { // unsafe {
// let instance = (*vmctx.offset(4)) as *mut Instance; // let instance = (*vmctx.offset(4)) as *mut Instance;
// (*instance) // (*instance)
@ -564,8 +599,29 @@ 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: &VmCtx) -> u32 {
return 0; // return 0;
println!("current_memory::init {:?}", memory_index);
let instance = &vmctx.data().user_data.instance;
let memory = &instance.memories[memory_index as usize];
println!(
"INSPECTED MEMORY ({:?}) {:?}",
memory.current_size(),
instance.inspect_memory(0, 0, 1)
);
memory.current_size() as u32
// return 1;
// let vm = unsafe {
// (*vmctx) as *mut VmCtx
// };
// println!("current_memory::instance {:?} {:?}", memory_index, vmctx);
// let memory = &instance.memories[0];
// println!("MEMORY INDEX {:?}", memory_index);
// unimplemented!()
// memory.current_size() as u32
// unimplemented!(); // unimplemented!();
// unsafe { // unsafe {
// let instance = (*vmctx.offset(4)) as *mut Instance; // let instance = (*vmctx.offset(4)) as *mut Instance;

View File

@ -1,5 +1,6 @@
use memmap::MmapMut; use memmap::MmapMut;
use std::fmt; use std::fmt;
use std::ops::{Deref, DerefMut};
const PAGE_SIZE: u32 = 65536; const PAGE_SIZE: u32 = 65536;
const MAX_PAGES: u32 = 65536; const MAX_PAGES: u32 = 65536;
@ -34,16 +35,9 @@ impl LinearMemory {
"Instantiate LinearMemory(initial={:?}, maximum={:?})", "Instantiate LinearMemory(initial={:?}, maximum={:?})",
initial, maximum initial, maximum
); );
let initial = if initial > 0 { initial } else { 1 };
let len: u64 = PAGE_SIZE as u64 * match maximum { let len: u64 = PAGE_SIZE as u64 * match maximum {
Some(val) => { Some(val) => val as u64,
if val > initial {
val as u64
} else {
initial as u64
}
}
None => initial as u64, None => initial as u64,
}; };
let len = if len == 0 { 1 } else { len }; let len = if len == 0 { 1 } else { len };
@ -71,7 +65,7 @@ impl LinearMemory {
/// ///
/// Returns `None` if memory can't be grown by the specified amount /// Returns `None` if memory can't be grown by the specified amount
/// of pages. /// of pages.
pub fn grow(&mut self, add_pages: u32) -> Option<u32> { pub fn grow(&mut self, add_pages: u32) -> Option<i32> {
let new_pages = match self.current.checked_add(add_pages) { let new_pages = match self.current.checked_add(add_pages) {
Some(new_pages) => new_pages, Some(new_pages) => new_pages,
None => return None, None => return None,
@ -110,7 +104,7 @@ impl LinearMemory {
assert!(self.mmap[i] == 0); assert!(self.mmap[i] == 0);
} }
Some(prev_pages) Some(prev_pages as i32)
} }
} }
@ -134,3 +128,16 @@ impl AsMut<[u8]> for LinearMemory {
&mut self.mmap &mut self.mmap
} }
} }
impl Deref for LinearMemory {
type Target = [u8];
fn deref(&self) -> &[u8] {
&*self.mmap
}
}
impl DerefMut for LinearMemory {
fn deref_mut(&mut self) -> &mut [u8] {
&mut *self.mmap
}
}

View File

@ -16,7 +16,7 @@ use wasmparser;
pub use self::errors::{Error, ErrorKind}; pub use self::errors::{Error, ErrorKind};
pub use self::import_object::ImportObject; pub use self::import_object::ImportObject;
pub use self::instance::Instance; pub use self::instance::{Instance, VmCtx};
pub use self::memory::LinearMemory; pub use self::memory::LinearMemory;
pub use self::module::{Export, Module, ModuleInfo}; pub use self::module::{Export, Module, ModuleInfo};

View File

@ -577,7 +577,8 @@ impl<'environment> FuncEnvironmentTrait for FuncEnvironment<'environment> {
// argument_bytes: None, // argument_bytes: None,
params: vec![ params: vec![
AbiParam::new(I32), AbiParam::new(I32),
AbiParam::special(I64, ArgumentPurpose::VMContext), // AbiParam::special(I64, ArgumentPurpose::VMContext),
AbiParam::special(self.pointer_type(), ArgumentPurpose::VMContext),
], ],
returns: vec![AbiParam::new(I32)], returns: vec![AbiParam::new(I32)],
}); });
@ -608,7 +609,13 @@ impl<'environment> FuncEnvironmentTrait for FuncEnvironment<'environment> {
let sig_ref = pos.func.import_signature(Signature { let sig_ref = pos.func.import_signature(Signature {
call_conv: CallConv::SystemV, call_conv: CallConv::SystemV,
// argument_bytes: None, // argument_bytes: None,
params: vec![AbiParam::special(I64, ArgumentPurpose::VMContext)], params: vec![
// The memory index
AbiParam::new(I32),
// The vmctx reference
AbiParam::special(self.pointer_type(), ArgumentPurpose::VMContext),
// AbiParam::special(I64, ArgumentPurpose::VMContext),
],
returns: vec![AbiParam::new(I32)], returns: vec![AbiParam::new(I32)],
}); });
@ -621,9 +628,10 @@ impl<'environment> FuncEnvironmentTrait for FuncEnvironment<'environment> {
// self.mod_info.current_memory_extfunc = cur_mem_func; // self.mod_info.current_memory_extfunc = cur_mem_func;
let memory_index = pos.ins().iconst(I32, index as i64);
let vmctx = pos.func.special_param(ArgumentPurpose::VMContext).unwrap(); let vmctx = pos.func.special_param(ArgumentPurpose::VMContext).unwrap();
let call_inst = pos.ins().call(cur_mem_func, &[vmctx]); let call_inst = pos.ins().call(cur_mem_func, &[memory_index, vmctx]);
Ok(*pos.func.dfg.inst_results(call_inst).first().unwrap()) Ok(*pos.func.dfg.inst_results(call_inst).first().unwrap())
// Ok(pos.ins().iconst(I32, -1)) // Ok(pos.ins().iconst(I32, -1))
} }