diff --git a/src/main.rs b/src/main.rs index c2c467245..043724955 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,6 +7,8 @@ extern crate cranelift_codegen; extern crate cranelift_native; extern crate cranelift_wasm; extern crate cranelift_entity; +#[macro_use] +extern crate target_lexicon; use std::path::PathBuf; use std::fs::File; diff --git a/src/spec/tests.rs b/src/spec/tests.rs index 4c05a2849..771a9e9c0 100644 --- a/src/spec/tests.rs +++ b/src/spec/tests.rs @@ -4,12 +4,12 @@ use std::rc::Rc; use wabt::script::{Value, Action}; use super::{InvokationResult, ScriptHandler, run_single_file}; -use crate::webassembly::{compile, instantiate, Error, ErrorKind, Module, Instance, ResultObject}; -use crate::webassembly::instance::InvokeResult; +use crate::webassembly::{compile, instantiate, Error, ErrorKind, ModuleInstance}; +// use crate::webassembly::instance::InvokeResult; struct StoreCtrl<'module> { - last_module: Option, - modules: HashMap> + last_module: Option, + modules: HashMap> } impl<'module> StoreCtrl<'module> { @@ -29,7 +29,7 @@ impl<'module> StoreCtrl<'module> { } } - fn add_module(&mut self, name: Option, module: &'module ResultObject) { + fn add_module(&mut self, name: Option, module: &'module ModuleInstance) { // self.last_module = Some(Rc::new(module)); // if let Some(name) = name { // // self.modules[&name] = module; @@ -43,7 +43,7 @@ impl<'module> StoreCtrl<'module> { // self.last_module = Some(module); } - fn get_module(self, name: Option) -> &'module ResultObject { + fn get_module(self, name: Option) -> &'module ModuleInstance { unimplemented!() // self.last_module.expect("exists") // return self @@ -66,42 +66,43 @@ impl<'module> ScriptHandler for StoreCtrl<'module> { ) -> InvokationResult { // let modu = (&self.last_module); // let x = modu.unwrap(); - if let Some(m) = &mut self.last_module { - // let function = module.exports.get(field).expect("field not found"); - // let mut m = &mut m; - let mut instance = &mut m.instance; - // println!("HEEY {:?}", module.instance); - let x = instance.execute_fn( - &m.module, - &m.compilation, - field, - ).unwrap(); - println!("X value {:?}", x); - let res = match x { - InvokeResult::VOID => { - vec![] - }, - InvokeResult::I32(v) => vec![Value::I32(v)], - InvokeResult::I64(v) => vec![Value::I64(v)], - InvokeResult::F32(v) => vec![Value::F32(v)], - InvokeResult::F64(v) => vec![Value::F64(v)], - }; - InvokationResult::Vals(res) - // unimplemented!() - // InvokationResult::Vals(vec![Value::I32(*x)]) - // unimplemented!(); - // let result = Rc::try_unwrap(module); - // let mut mutinstance = Rc::make_mut(&module.instance); - // execute_fn( - // &module.module, - // &module.compilation, - // &mut (&mut module.instance), - // field, - // ); - } - else { - panic!("module not found"); - } + unimplemented!() + // if let Some(m) = &mut self.last_module { + // // let function = module.exports.get(field).expect("field not found"); + // // let mut m = &mut m; + // let mut instance = &mut m.instance; + // // println!("HEEY {:?}", module.instance); + // let x = instance.execute_fn( + // &m.module, + // &m.compilation, + // field, + // ).unwrap(); + // println!("X value {:?}", x); + // let res = match x { + // InvokeResult::VOID => { + // vec![] + // }, + // InvokeResult::I32(v) => vec![Value::I32(v)], + // InvokeResult::I64(v) => vec![Value::I64(v)], + // InvokeResult::F32(v) => vec![Value::F32(v)], + // InvokeResult::F64(v) => vec![Value::F64(v)], + // }; + // InvokationResult::Vals(res) + // // unimplemented!() + // // InvokationResult::Vals(vec![Value::I32(*x)]) + // // unimplemented!(); + // // let result = Rc::try_unwrap(module); + // // let mut mutinstance = Rc::make_mut(&module.instance); + // // execute_fn( + // // &module.module, + // // &module.compilation, + // // &mut (&mut module.instance), + // // field, + // // ); + // } + // else { + // panic!("module not found"); + // } // match module { // Some(m) => { // println!("HEEY {:?}", m); @@ -127,18 +128,18 @@ impl<'module> ScriptHandler for StoreCtrl<'module> { // println!("ADD MODULE {}", name.unwrap_or("no name".to_string())) } fn assert_malformed(&mut self, bytes: Vec) { - let module_wrapped = compile(bytes); + let module_wrapped = instantiate(bytes, None); match module_wrapped { - Err(Error(ErrorKind::CompileError(v), _)) => {} + Err(ErrorKind::CompileError(v)) => {} _ => panic!("Module compilation should have failed") } } fn assert_invalid(&mut self, bytes: Vec) { // print!("IS INVALID"); - let module_wrapped = compile(bytes); + let module_wrapped = instantiate(bytes, None); // print!("IS INVALID?? {:?}", module_wrapped); match module_wrapped { - Err(Error(ErrorKind::CompileError(v), _)) => {} + Err(ErrorKind::CompileError(v)) => {} _ => assert!(false, "Module compilation should have failed") } } diff --git a/src/webassembly/env.rs b/src/webassembly/env.rs new file mode 100644 index 000000000..43ac9361e --- /dev/null +++ b/src/webassembly/env.rs @@ -0,0 +1,609 @@ +//! "" implementations of `ModuleEnvironment` and `FuncEnvironment` for testing +//! wasm translation. + +use cranelift_codegen::cursor::FuncCursor; +use cranelift_codegen::ir::immediates::{Imm64, Offset32}; +use cranelift_codegen::ir::types::*; +use cranelift_codegen::ir::{self, InstBuilder, FuncRef, ExtFuncData, ExternalName, Signature, AbiParam, + ArgumentPurpose, ArgumentLoc, ArgumentExtension, Function}; +use cranelift_codegen::settings; +use cranelift_entity::{EntityRef, PrimaryMap}; +use std::string::String; +use std::vec::Vec; +use target_lexicon::Triple; +use cranelift_wasm::{ + FuncTranslator, + FuncEnvironment as FuncEnvironmentTrait, GlobalVariable, ModuleEnvironment, ReturnMode, WasmResult, + DefinedFuncIndex, FuncIndex, Global, GlobalIndex, Memory, MemoryIndex, SignatureIndex, Table, + TableIndex, +}; + +// use alloc::vec::Vec; +// use alloc::string::String; + + +/// Compute a `ir::ExternalName` for a given wasm function index. +fn get_func_name(func_index: FuncIndex) -> ir::ExternalName { + ir::ExternalName::user(0, func_index.index() as u32) +} + +/// A collection of names under which a given entity is exported. +pub struct Exportable { + /// A wasm entity. + pub entity: T, + + /// Names under which the entity is exported. + pub export_names: Vec, +} + +impl Exportable { + pub fn new(entity: T) -> Self { + Self { + entity, + export_names: Vec::new(), + } + } +} + +/// The main state belonging to a `ModuleInstance`. This is split out from +/// `ModuleInstance` to allow it to be borrowed separately from the +/// `FuncTranslator` field. +pub struct ModuleInfo { + /// Target description. + pub triple: Triple, + + /// Compilation setting flags. + pub flags: settings::Flags, + + /// Signatures as provided by `declare_signature`. + pub signatures: Vec, + + /// Module and field names of imported functions as provided by `declare_func_import`. + pub imported_funcs: Vec<(String, String)>, + + /// Functions, imported and local. + pub functions: PrimaryMap>, + + /// Function bodies. + pub function_bodies: PrimaryMap, + + /// Tables as provided by `declare_table`. + pub tables: Vec>, + + /// WebAssembly table initializers. + // Should be Vec + // instead of Vec> ?? + pub table_elements: Vec>, + + /// Memories as provided by `declare_memory`. + pub memories: Vec>, + + /// Globals as provided by `declare_global`. + pub globals: Vec>, + + /// The start function. + pub start_func: Option, + + pub data_initializers: Vec, +} + +impl ModuleInfo { + /// Allocates the data structures with the given flags. + pub fn with_triple_flags(triple: Triple, flags: settings::Flags) -> Self { + Self { + triple, + flags, + signatures: Vec::new(), + imported_funcs: Vec::new(), + functions: PrimaryMap::new(), + function_bodies: PrimaryMap::new(), + tables: Vec::new(), + memories: Vec::new(), + globals: Vec::new(), + table_elements: Vec::new(), + start_func: None, + data_initializers: Vec::new(), + } + } +} + + +/// A data initializer for linear memory. +#[derive(Debug)] +pub struct DataInitializer { + /// The index of the memory to initialize. + pub memory_index: MemoryIndex, + /// Optionally a globalvalue base to initialize at. + pub base: Option, + /// A constant offset to initialize at. + pub offset: usize, + /// The initialization data. + pub data: Vec, +} + + +/// Possible values for a WebAssembly table element. +#[derive(Clone, Debug)] +pub enum TableElement { + /// A element that, if called, produces a trap. + Trap(), + /// A function. + Function(FuncIndex), +} + +/// A WebAssembly table initializer. +#[derive(Clone, Debug)] +pub struct TableElements { + /// The index of a table to initialize. + pub table_index: TableIndex, + /// Optionally, a global variable giving a base index. + pub base: Option, + /// The offset to add to the base. + pub offset: usize, + /// The values to write into the table elements. + pub elements: Vec, +} + + +/// This `ModuleEnvironment` implementation is a "naïve" one, doing essentially nothing and +/// emitting placeholders when forced to. Don't try to execute code translated for this +/// environment, essentially here for translation debug purposes. +pub struct ModuleInstance { + /// Module information. + pub info: ModuleInfo, + + /// Function translation. + trans: FuncTranslator, + + /// Vector of wasm bytecode size for each function. + pub func_bytecode_sizes: Vec, + + /// How to return from functions. + return_mode: ReturnMode, +} + +impl ModuleInstance { + /// Allocates the data structures with default flags. + pub fn with_triple(triple: Triple) -> Self { + Self::with_triple_flags( + triple, + settings::Flags::new(settings::builder()), + ReturnMode::NormalReturns, + ) + } + + /// Allocates the data structures with the given triple. + pub fn with_triple_flags( + triple: Triple, + flags: settings::Flags, + return_mode: ReturnMode, + ) -> Self { + let mut x = Self { + info: ModuleInfo::with_triple_flags(triple, flags), + trans: FuncTranslator::new(), + func_bytecode_sizes: Vec::new(), + return_mode, + }; + x + } + + /// Return a `FuncEnvironment` for translating functions within this + /// `ModuleInstance`. + pub fn func_env(&self) -> FuncEnvironment { + FuncEnvironment::new(&self.info, self.return_mode) + } + + fn native_pointer(&self) -> ir::Type { + self.func_env().pointer_type() + } + +} + +/// The `FuncEnvironment` implementation for use by the `ModuleInstance`. +pub struct FuncEnvironment<'dummy_environment> { + pub mod_info: &'dummy_environment ModuleInfo, + + return_mode: ReturnMode, +} + +impl<'dummy_environment> FuncEnvironment<'dummy_environment> { + pub fn new(mod_info: &'dummy_environment ModuleInfo, return_mode: ReturnMode) -> Self { + Self { + mod_info, + return_mode, + } + } + + fn get_real_call_args(func: &Function, call_args: &[ir::Value]) -> Vec { + let mut real_call_args = Vec::with_capacity(call_args.len() + 1); + real_call_args.extend_from_slice(call_args); + real_call_args.push(func.special_param(ArgumentPurpose::VMContext).unwrap()); + real_call_args + } + + // Create a signature for `sigidx` amended with a `vmctx` argument after the standard wasm + // arguments. + fn vmctx_sig(&self, sigidx: SignatureIndex) -> ir::Signature { + let mut sig = self.mod_info.signatures[sigidx].clone(); + sig.params.push(ir::AbiParam::special( + self.pointer_type(), + ir::ArgumentPurpose::VMContext, + )); + sig + } +} + +impl<'dummy_environment> FuncEnvironmentTrait for FuncEnvironment<'dummy_environment> { + fn triple(&self) -> &Triple { + &self.mod_info.triple + } + + fn flags(&self) -> &settings::Flags { + &self.mod_info.flags + } + + fn make_global(&mut self, func: &mut ir::Function, index: GlobalIndex) -> GlobalVariable { + // Just create a dummy `vmctx` global. + let offset = ((index * 8) as i64 + 8).into(); + let vmctx = func.create_global_value(ir::GlobalValueData::VMContext {}); + let iadd = func.create_global_value(ir::GlobalValueData::IAddImm { + base: vmctx, + offset, + global_type: self.pointer_type(), + }); + GlobalVariable::Memory { + gv: iadd, + ty: self.mod_info.globals[index].entity.ty, + } + } + + fn make_heap(&mut self, func: &mut ir::Function, _index: MemoryIndex) -> ir::Heap { + // Create a static heap whose base address is stored at `vmctx+0`. + let addr = func.create_global_value(ir::GlobalValueData::VMContext); + let gv = func.create_global_value(ir::GlobalValueData::Load { + base: addr, + offset: Offset32::new(0), + global_type: self.pointer_type(), + }); + + func.create_heap(ir::HeapData { + base: gv, + min_size: 0.into(), + guard_size: 0x8000_0000.into(), + style: ir::HeapStyle::Static { + bound: 0x1_0000_0000.into(), + }, + index_type: I32, + }) + } + + fn make_table(&mut self, func: &mut ir::Function, table_index: TableIndex) -> ir::Table { + // OLD + // Create a table whose base address is stored at `vmctx+0`. + let vmctx = func.create_global_value(ir::GlobalValueData::VMContext); + let base_gv = func.create_global_value(ir::GlobalValueData::Load { + base: vmctx, + offset: Offset32::new(0), + global_type: self.pointer_type(), + }); + let bound_gv = func.create_global_value(ir::GlobalValueData::Load { + base: vmctx, + offset: Offset32::new(0), + global_type: I32, + }); + + func.create_table(ir::TableData { + base_gv, + min_size: Imm64::new(0), + bound_gv, + element_size: Imm64::new(i64::from(self.pointer_bytes()) * 2), + index_type: I32, + }) + + // let ptr_size = self.ptr_size(); + + // self.info.tables[table_index].unwrap_or_else(|| { + // let base = self.tables_base.unwrap_or_else(|| { + // let tables_offset = self.ptr_size() as i32 * -1; + // let new_base = func.create_global_value(ir::GlobalValueData::VMContext { + // offset: tables_offset.into(), + // }); + // self.globals_base = Some(new_base); + // new_base + // }); + + // let table_data_offset = (table_index as usize * ptr_size * 2) as i32; + + // let new_table_addr_addr = func.create_global_value(ir::GlobalValueData::Deref { + // base, + // offset: table_data_offset.into(), + // }); + // let new_table_addr = func.create_global_value(ir::GlobalValueData::Deref { + // base: new_table_addr_addr, + // offset: 0.into(), + // }); + + // let new_table_bounds_addr = func.create_global_value(ir::GlobalValueData::Deref { + // base, + // offset: (table_data_offset + ptr_size as i32).into(), + // }); + // let new_table_bounds = func.create_global_value(ir::GlobalValueData::Deref { + // base: new_table_bounds_addr, + // offset: 0.into(), + // }); + + // let table = func.create_table(ir::TableData { + // base_gv: new_table_addr, + // min_size: (self.module.tables[table_index].size as i64).into(), + // bound_gv: new_table_bounds, + // element_size: (ptr_size as i64).into(), + // }); + + // self.info.tables[table_index] = Some(table); + // table + // }) + + } + + fn make_indirect_sig(&mut self, func: &mut ir::Function, index: SignatureIndex) -> ir::SigRef { + // A real implementation would probably change the calling convention and add `vmctx` and + // signature index arguments. + func.import_signature(self.vmctx_sig(index)) + } + + fn make_direct_func(&mut self, func: &mut ir::Function, index: FuncIndex) -> ir::FuncRef { + let sigidx = self.mod_info.functions[index].entity; + // A real implementation would probably add a `vmctx` argument. + // And maybe attempt some signature de-duplication. + let signature = func.import_signature(self.vmctx_sig(sigidx)); + let name = get_func_name(index); + func.import_function(ir::ExtFuncData { + name, + signature, + colocated: false, + }) + } + + fn translate_call_indirect( + &mut self, + mut pos: FuncCursor, + _table_index: TableIndex, + _table: ir::Table, + _sig_index: SignatureIndex, + sig_ref: ir::SigRef, + callee: ir::Value, + call_args: &[ir::Value], + ) -> WasmResult { + // Pass the current function's vmctx parameter on to the callee. + let vmctx = pos + .func + .special_param(ir::ArgumentPurpose::VMContext) + .expect("Missing vmctx parameter"); + + // The `callee` value is an index into a table of function pointers. + // Apparently, that table is stored at absolute address 0 in this dummy environment. + // TODO: Generate bounds checking code. + let ptr = self.pointer_type(); + let callee_offset = if ptr == I32 { + pos.ins().imul_imm(callee, 4) + } else { + let ext = pos.ins().uextend(I64, callee); + pos.ins().imul_imm(ext, 4) + }; + let mut mflags = ir::MemFlags::new(); + mflags.set_notrap(); + mflags.set_aligned(); + let func_ptr = pos.ins().load(ptr, mflags, callee_offset, 0); + + // Build a value list for the indirect call instruction containing the callee, call_args, + // and the vmctx parameter. + let mut args = ir::ValueList::default(); + args.push(func_ptr, &mut pos.func.dfg.value_lists); + args.extend(call_args.iter().cloned(), &mut pos.func.dfg.value_lists); + args.push(vmctx, &mut pos.func.dfg.value_lists); + + Ok(pos + .ins() + .CallIndirect(ir::Opcode::CallIndirect, INVALID, sig_ref, args) + .0) + } + + fn translate_call( + &mut self, + mut pos: FuncCursor, + _callee_index: FuncIndex, + callee: ir::FuncRef, + call_args: &[ir::Value], + ) -> WasmResult { + // Pass the current function's vmctx parameter on to the callee. + let vmctx = pos + .func + .special_param(ir::ArgumentPurpose::VMContext) + .expect("Missing vmctx parameter"); + + // Build a value list for the call instruction containing the call_args and the vmctx + // parameter. + let mut args = ir::ValueList::default(); + args.extend(call_args.iter().cloned(), &mut pos.func.dfg.value_lists); + args.push(vmctx, &mut pos.func.dfg.value_lists); + + Ok(pos.ins().Call(ir::Opcode::Call, INVALID, callee, args).0) + } + + fn translate_memory_grow( + &mut self, + mut pos: FuncCursor, + _index: MemoryIndex, + _heap: ir::Heap, + _val: ir::Value, + ) -> WasmResult { + Ok(pos.ins().iconst(I32, -1)) + } + + fn translate_memory_size( + &mut self, + mut pos: FuncCursor, + _index: MemoryIndex, + _heap: ir::Heap, + ) -> WasmResult { + Ok(pos.ins().iconst(I32, -1)) + } + + fn return_mode(&self) -> ReturnMode { + self.return_mode + } +} + +impl<'data> ModuleEnvironment<'data> for ModuleInstance { + fn flags(&self) -> &settings::Flags { + &self.info.flags + } + + fn get_func_name(&self, func_index: FuncIndex) -> ir::ExternalName { + get_func_name(func_index) + } + + fn declare_signature(&mut self, sig: &ir::Signature) { + // OLD + // self.info.signatures.push(sig.clone()); + + // NEW + let mut sig = sig.clone(); + sig.params.push(AbiParam { + value_type: self.native_pointer(), + purpose: ArgumentPurpose::VMContext, + extension: ArgumentExtension::None, + location: ArgumentLoc::Unassigned, + }); + // TODO: Deduplicate signatures. + self.info.signatures.push(sig); + } + + fn get_signature(&self, sig_index: SignatureIndex) -> &ir::Signature { + &self.info.signatures[sig_index] + } + + fn declare_func_import( + &mut self, + sig_index: SignatureIndex, + module: &'data str, + field: &'data str, + ) { + assert_eq!( + self.info.functions.len(), + self.info.imported_funcs.len(), + "Imported functions must be declared first" + ); + self.info.functions.push(Exportable::new(sig_index)); + self.info + .imported_funcs + .push((String::from(module), String::from(field))); + } + + fn get_num_func_imports(&self) -> usize { + self.info.imported_funcs.len() + } + + fn declare_func_type(&mut self, sig_index: SignatureIndex) { + self.info.functions.push(Exportable::new(sig_index)); + } + + fn get_func_type(&self, func_index: FuncIndex) -> SignatureIndex { + self.info.functions[func_index].entity + } + + fn declare_global(&mut self, global: Global) { + self.info.globals.push(Exportable::new(global)); + } + + fn get_global(&self, global_index: GlobalIndex) -> &Global { + &self.info.globals[global_index].entity + } + + fn declare_table(&mut self, table: Table) { + self.info.tables.push(Exportable::new(table)); + } + + fn declare_table_elements( + &mut self, + table_index: TableIndex, + base: Option, + offset: usize, + elements: Vec, + ) { + // NEW + debug_assert!(base.is_none(), "global-value offsets not supported yet"); + self.info.table_elements.push(Exportable::new(TableElements { + table_index, + base, + offset, + elements, + })); + } + + fn declare_memory(&mut self, memory: Memory) { + self.info.memories.push(Exportable::new(memory)); + } + + fn declare_data_initialization( + &mut self, + memory_index: MemoryIndex, + base: Option, + offset: usize, + data: &'data [u8], + ) { + debug_assert!(base.is_none(), "global-value offsets not supported yet"); + self.info.data_initializers.push(DataInitializer { + memory_index, + base, + offset, + data: data.to_vec(), + }); + } + + fn declare_func_export(&mut self, func_index: FuncIndex, name: &'data str) { + self.info.functions[func_index] + .export_names + .push(String::from(name)); + } + + fn declare_table_export(&mut self, table_index: TableIndex, name: &'data str) { + self.info.tables[table_index] + .export_names + .push(String::from(name)); + } + + fn declare_memory_export(&mut self, memory_index: MemoryIndex, name: &'data str) { + self.info.memories[memory_index] + .export_names + .push(String::from(name)); + } + + fn declare_global_export(&mut self, global_index: GlobalIndex, name: &'data str) { + self.info.globals[global_index] + .export_names + .push(String::from(name)); + } + + fn declare_start_func(&mut self, func_index: FuncIndex) { + debug_assert!(self.info.start_func.is_none()); + self.info.start_func = Some(func_index); + } + + fn define_function_body(&mut self, body_bytes: &'data [u8]) -> WasmResult<()> { + let func = { + let mut func_environ = FuncEnvironment::new(&self.info, self.return_mode); + let func_index = + FuncIndex::new(self.get_num_func_imports() + self.info.function_bodies.len()); + let name = get_func_name(func_index); + let sig = func_environ.vmctx_sig(self.get_func_type(func_index)); + let mut func = ir::Function::with_name_signature(name, sig); + self.trans + .translate(body_bytes, &mut func, &mut func_environ)?; + func + }; + self.func_bytecode_sizes.push(body_bytes.len()); + self.info.function_bodies.push(func); + Ok(()) + } +} diff --git a/src/webassembly/memory.rs b/src/webassembly/memory.rs index 4830b9386..f5dcf4278 100644 --- a/src/webassembly/memory.rs +++ b/src/webassembly/memory.rs @@ -1,113 +1,158 @@ -use memmap; -use std::fmt; +use errno; +use libc; +use region; +use std::mem; +use std::ptr; -const PAGE_SIZE: u32 = 65536; -const MAX_PAGES: u32 = 65536; - -/// A linear memory instance. -/// -/// This linear memory has a stable base address and at the same time allows -/// for dynamical growing. -pub struct LinearMemory { - mmap: memmap::MmapMut, - current: u32, - maximum: Option, +/// Round `size` up to the nearest multiple of `page_size`. +fn round_up_to_page_size(size: usize, page_size: usize) -> usize { + (size + (page_size - 1)) & !(page_size - 1) } -impl LinearMemory { - /// Create a new linear memory instance with specified initial and maximum number of pages. - /// - /// `maximum` cannot be set to more than `65536` pages. - pub fn new(initial: u32, maximum: Option) -> Self { - assert!(initial <= MAX_PAGES); - assert!(maximum.is_none() || maximum.unwrap() <= MAX_PAGES); +/// A simple struct consisting of a pointer and length. +struct PtrLen { + ptr: *mut u8, + len: usize, +} - let len = PAGE_SIZE * match maximum { - Some(val) => val, - None => initial, - }; - let mmap = memmap::MmapMut::map_anon(len as usize).unwrap(); +impl PtrLen { + /// Create a new empty `PtrLen`. + fn new() -> Self { Self { - mmap, - current: initial, - maximum, + ptr: ptr::null_mut(), + len: 0, } } - /// Returns an base address of this linear memory. - pub fn base_addr(&mut self) -> *mut u8 { - self.mmap.as_mut_ptr() + /// Create a new `PtrLen` pointing to at least `size` bytes of memory, + /// suitably sized and aligned for memory protection. + #[cfg(not(target_os = "windows"))] + fn with_size(size: usize) -> Result { + let page_size = region::page::size(); + let alloc_size = round_up_to_page_size(size, page_size); + unsafe { + let mut ptr: *mut libc::c_void = mem::uninitialized(); + let err = libc::posix_memalign(&mut ptr, page_size, alloc_size); + if err == 0 { + Ok(Self { + ptr: ptr as *mut u8, + len: alloc_size, + }) + } else { + Err(errno::Errno(err).to_string()) + } + } } - /// Returns a number of allocated wasm pages. - pub fn current_size(&self) -> u32 { - self.current - } + #[cfg(target_os = "windows")] + fn with_size(size: usize) -> Result { + use winapi::um::memoryapi::VirtualAlloc; + use winapi::um::winnt::{MEM_COMMIT, MEM_RESERVE, PAGE_READWRITE}; - /// Grow memory by the specified amount of pages. - /// - /// Returns `None` if memory can't be grown by the specified amount - /// of pages. - pub fn grow(&mut self, add_pages: u32) -> Option { - let new_pages = match self.current.checked_add(add_pages) { - Some(new_pages) => new_pages, - None => return None, + let page_size = region::page::size(); + + // VirtualAlloc always rounds up to the next multiple of the page size + let ptr = unsafe { + VirtualAlloc( + ptr::null_mut(), + size, + MEM_COMMIT | MEM_RESERVE, + PAGE_READWRITE, + ) }; - if let Some(val) = self.maximum { - if new_pages > val { - return None; - } + if !ptr.is_null() { + Ok(Self { + ptr: ptr as *mut u8, + len: round_up_to_page_size(size, page_size), + }) } else { - // Wasm linear memories are never allowed to grow beyond what is - // indexable. If the memory has no maximum, enforce the greatest - // limit here. - if new_pages >= 65536 { - return None; + Err(errno::errno().to_string()) + } + } +} + +/// JIT memory manager. This manages pages of suitably aligned and +/// accessible memory. +pub struct Memory { + allocations: Vec, + executable: usize, + current: PtrLen, + position: usize, +} + +impl Memory { + pub fn new() -> Self { + Self { + allocations: Vec::new(), + executable: 0, + current: PtrLen::new(), + position: 0, + } + } + + fn finish_current(&mut self) { + self.allocations + .push(mem::replace(&mut self.current, PtrLen::new())); + self.position = 0; + } + + /// TODO: Use a proper error type. + pub fn allocate(&mut self, size: usize) -> Result<*mut u8, String> { + if size <= self.current.len - self.position { + // TODO: Ensure overflow is not possible. + let ptr = unsafe { self.current.ptr.offset(self.position as isize) }; + self.position += size; + return Ok(ptr); + } + + self.finish_current(); + + // TODO: Allocate more at a time. + self.current = PtrLen::with_size(size)?; + self.position = size; + Ok(self.current.ptr) + } + + /// Set all memory allocated in this `Memory` up to now as readable and executable. + pub fn set_readable_and_executable(&mut self) { + self.finish_current(); + + for &PtrLen { ptr, len } in &self.allocations[self.executable..] { + if len != 0 { + unsafe { + region::protect(ptr, len, region::Protection::ReadExecute) + .expect("unable to make memory readable+executable"); + } } } + } - let prev_pages = self.current; - let new_bytes = (new_pages * PAGE_SIZE) as usize; + /// Set all memory allocated in this `Memory` up to now as readonly. + pub fn set_readonly(&mut self) { + self.finish_current(); - if self.mmap.len() < new_bytes { - // If we have no maximum, this is a "dynamic" heap, and it's allowed - // to move. - assert!(self.maximum.is_none()); - let mut new_mmap = memmap::MmapMut::map_anon(new_bytes).unwrap(); - new_mmap.copy_from_slice(&self.mmap); - self.mmap = new_mmap; + for &PtrLen { ptr, len } in &self.allocations[self.executable..] { + if len != 0 { + unsafe { + region::protect(ptr, len, region::Protection::Read) + .expect("unable to make memory readonly"); + } + } } - - self.current = new_pages; - - // Ensure that newly allocated area is zeroed. - let new_start_offset = (prev_pages * PAGE_SIZE) as usize; - let new_end_offset = (new_pages * PAGE_SIZE) as usize; - for i in new_start_offset..new_end_offset { - assert!(self.mmap[i] == 0); - } - - Some(prev_pages) } } -impl fmt::Debug for LinearMemory { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("LinearMemory") - .field("current", &self.current) - .field("maximum", &self.maximum) - .finish() - } -} +// TODO: Implement Drop to unprotect and deallocate the memory? -impl AsRef<[u8]> for LinearMemory { - fn as_ref(&self) -> &[u8] { - &self.mmap - } -} +#[cfg(test)] +mod tests { + use super::*; -impl AsMut<[u8]> for LinearMemory { - fn as_mut(&mut self) -> &mut [u8] { - &mut self.mmap + #[test] + fn test_round_up_to_page_size() { + assert_eq!(round_up_to_page_size(0, 4096), 0); + assert_eq!(round_up_to_page_size(1, 4096), 4096); + assert_eq!(round_up_to_page_size(4096, 4096), 4096); + assert_eq!(round_up_to_page_size(4097, 4096), 8192); } } diff --git a/src/webassembly/mod.rs b/src/webassembly/mod.rs index f6714571b..ecb0ae814 100644 --- a/src/webassembly/mod.rs +++ b/src/webassembly/mod.rs @@ -1,37 +1,49 @@ -pub mod module; -pub mod compilation; -pub mod memory; -pub mod environ; -pub mod instance; +// pub mod module; +// pub mod compilation; +// pub mod memory; +// pub mod environ; +// pub mod instance; pub mod errors; -pub mod execute; +// pub mod execute; pub mod utils; +pub mod env; +use std::str::FromStr; use std::time::{Duration, Instant}; use std::panic; use cranelift_native; use cranelift_codegen::isa::TargetIsa; -use cranelift_codegen::settings; +// use cranelift_codegen::settings; use cranelift_codegen::settings::Configurable; -pub use self::compilation::{compile_module, Compilation}; -pub use self::environ::{ModuleEnvironment}; -pub use self::module::Module; -pub use self::instance::Instance; +use target_lexicon::{self, Triple}; + +use cranelift_codegen::isa; +use cranelift_codegen::print_errors::pretty_verifier_error; +use cranelift_codegen::settings::{self, Flags}; +use cranelift_codegen::verifier; +use cranelift_wasm::{translate_module, ReturnMode}; + +pub use self::env::ModuleInstance; + +// pub use self::compilation::{compile_module, Compilation}; +// pub use self::environ::{ModuleEnvironment}; +// pub use self::module::Module; +// pub use self::instance::Instance; pub use self::errors::{Error, ErrorKind}; -pub use self::execute::{compile_and_link_module,execute}; +// pub use self::execute::{compile_and_link_module,execute}; use wasmparser; -pub struct ResultObject { - /// A WebAssembly.Module object representing the compiled WebAssembly module. - /// This Module can be instantiated again - pub module: Module, - /// A WebAssembly.Instance object that contains all the Exported WebAssembly - /// functions. - pub instance: Instance, +// pub struct ResultObject { +// /// A WebAssembly.Module object representing the compiled WebAssembly module. +// /// This Module can be instantiated again +// pub module: Module, +// /// A WebAssembly.Instance object that contains all the Exported WebAssembly +// /// functions. +// pub instance: Instance, - pub compilation: Compilation, -} +// pub compilation: Compilation, +// } pub struct ImportObject { } @@ -53,42 +65,60 @@ pub struct ImportObject { /// If the operation fails, the Result rejects with a /// WebAssembly.CompileError, WebAssembly.LinkError, or /// WebAssembly.RuntimeError, depending on the cause of the failure. -pub fn instantiate(buffer_source: Vec, import_object: Option) -> Result { - let now = Instant::now(); - let isa = construct_isa(); - println!("instantiate::init {:?}", now.elapsed()); - // if !validate(&buffer_source) { - // return Err(ErrorKind::CompileError("Module not valid".to_string())); - // } - println!("instantiate::validation {:?}", now.elapsed()); - let mut module = Module::new(); - let environ = ModuleEnvironment::new(&*isa, &mut module); - let translation = environ.translate(&buffer_source).map_err(|e| ErrorKind::CompileError(e.to_string()))?; - println!("instantiate::compile and link {:?}", now.elapsed()); - let compilation = compile_and_link_module(&*isa, &translation)?; - // let (compilation, relocations) = compile_module(&translation, &*isa)?; - println!("instantiate::instantiate {:?}", now.elapsed()); +pub fn instantiate(buffer_source: Vec, import_object: Option) -> Result { + let flags = Flags::new(settings::builder()); + let return_mode = ReturnMode::NormalReturns; + let mut dummy_environ = + ModuleInstance::with_triple_flags(triple!("riscv64"), flags.clone(), return_mode); - let mut instance = Instance::new( - translation.module, - &compilation, - &translation.lazy.data_initializers, - ); - println!("instantiate::execute {:?}", now.elapsed()); + translate_module(&buffer_source, &mut dummy_environ).map_err(|e| ErrorKind::CompileError(e.to_string()))?; - let x = execute(&module, &compilation, &mut instance)?; + let isa = isa::lookup(dummy_environ.info.triple) + .unwrap() + .finish(dummy_environ.info.flags); - // let instance = Instance { - // tables: Vec::new(), - // memories: Vec::new(), - // globals: Vec::new(), - // }; + for func in dummy_environ.info.function_bodies.values() { + verifier::verify_function(func, &*isa) + .map_err(|errors| panic!(pretty_verifier_error(func, Some(&*isa), None, errors))) + .unwrap(); + }; + unimplemented!() + // Ok(dummy_environ) + // let now = Instant::now(); + // let isa = construct_isa(); + // println!("instantiate::init {:?}", now.elapsed()); + // // if !validate(&buffer_source) { + // // return Err(ErrorKind::CompileError("Module not valid".to_string())); + // // } + // println!("instantiate::validation {:?}", now.elapsed()); + // let mut module = Module::new(); + // let environ = ModuleEnvironment::new(&*isa, &mut module); + // let translation = environ.translate(&buffer_source).map_err(|e| ErrorKind::CompileError(e.to_string()))?; + // println!("instantiate::compile and link {:?}", now.elapsed()); + // let compilation = compile_and_link_module(&*isa, &translation)?; + // // let (compilation, relocations) = compile_module(&translation, &*isa)?; + // println!("instantiate::instantiate {:?}", now.elapsed()); - Ok(ResultObject { - module, - compilation, - instance: instance, - }) + // let mut instance = Instance::new( + // translation.module, + // &compilation, + // &translation.lazy.data_initializers, + // ); + // println!("instantiate::execute {:?}", now.elapsed()); + + // let x = execute(&module, &compilation, &mut instance)?; + + // // let instance = Instance { + // // tables: Vec::new(), + // // memories: Vec::new(), + // // globals: Vec::new(), + // // }; + + // Ok(ResultObject { + // module, + // compilation, + // instance: instance, + // }) } /// The WebAssembly.compile() function compiles a WebAssembly.Module @@ -103,35 +133,16 @@ pub fn instantiate(buffer_source: Vec, import_object: Option) /// Errors: /// If the operation fails, the Result rejects with a /// WebAssembly.CompileError. -pub fn compile(buffer_source: Vec) -> Result { - let isa = construct_isa(); +pub fn compile(buffer_source: Vec) -> Result { + unimplemented!(); + // let isa = construct_isa(); - let mut module = Module::new(); - let environ = ModuleEnvironment::new(&*isa, &mut module); - let translation = environ.translate(&buffer_source).map_err(|e| ErrorKind::CompileError(e.to_string()))?; - // compile_module(&translation, &*isa)?; - compile_and_link_module(&*isa, &translation)?; - Ok(module) -} - -fn construct_isa() -> Box { - let (mut flag_builder, isa_builder) = cranelift_native::builders().unwrap_or_else(|_| { - panic!("host machine is not a supported target"); - }); - - // Enable verifier passes in debug mode. - // if cfg!(debug_assertions) { - // flag_builder.enable("enable_verifier").unwrap(); - // flag_builder.set("opt_level", "fastest"); - - // } - - // Enable optimization if requested. - // if args.flag_optimize { - flag_builder.set("opt_level", "fastest").unwrap(); - // } - - isa_builder.finish(settings::Flags::new(flag_builder)) + // let mut module = Module::new(); + // let environ = ModuleEnvironment::new(&*isa, &mut module); + // let translation = environ.translate(&buffer_source).map_err(|e| ErrorKind::CompileError(e.to_string()))?; + // // compile_module(&translation, &*isa)?; + // compile_and_link_module(&*isa, &translation)?; + // Ok(module) } /// The WebAssembly.validate() function validates a given typed