diff --git a/src/webassembly/compilation.rs b/src/webassembly/compilation.rs deleted file mode 100644 index 1566b74e6..000000000 --- a/src/webassembly/compilation.rs +++ /dev/null @@ -1,181 +0,0 @@ -//! A `Compilation` contains the compiled function bodies for a WebAssembly -//! module. -use cranelift_codegen::binemit; -use cranelift_codegen::ir; -use cranelift_codegen::ir::ExternalName; -use cranelift_codegen::isa; -use cranelift_codegen::Context; -use cranelift_entity::{EntityRef, PrimaryMap}; -use cranelift_wasm::{DefinedFuncIndex, FuncIndex, FuncTranslator}; -use region::protect; -use region::Protection; - -use super::environ::{get_func_name, ModuleTranslation}; - -pub fn protect_codebuf(code_buf: &Vec) -> Result<(), String> { - match unsafe { - protect( - code_buf.as_ptr(), - code_buf.len(), - Protection::ReadWriteExecute, - ) - } { - Err(err) => { - return Err(format!( - "failed to give executable permission to code: {}", - err - )) - }, - Ok(()) => Ok(()), - } -} - -/// The result of compiling a WebAssemby module's functions. -#[derive(Debug)] -pub struct LazyFunction { -} - -#[derive(Debug)] -pub struct Compilation { - /// Compiled machine code for the function bodies. - pub lazy_functions: PrimaryMap, - pub functions: PrimaryMap>, -} - -impl Compilation { - /// Allocates the compilation result with the given function bodies. - pub fn new(functions: PrimaryMap>, lazy_functions: PrimaryMap) -> Self { - Self { lazy_functions, functions } - } -} - -/// Implementation of a relocation sink that just saves all the information for later -pub struct RelocSink { - /// Relocations recorded for the function. - pub func_relocs: Vec, -} - -impl binemit::RelocSink for RelocSink { - fn reloc_ebb( - &mut self, - _offset: binemit::CodeOffset, - _reloc: binemit::Reloc, - _ebb_offset: binemit::CodeOffset, - ) { - // This should use the `offsets` field of `ir::Function`. - panic!("ebb headers not yet implemented"); - } - fn reloc_external( - &mut self, - offset: binemit::CodeOffset, - reloc: binemit::Reloc, - name: &ExternalName, - addend: binemit::Addend, - ) { - // let reloc_target = if let ExternalName::User { namespace, index } = *name { - // debug_assert!(namespace == 0); - // RelocationTarget::UserFunc(FuncIndex::new(index as usize)) - // } else if *name == ExternalName::testcase("grow_memory") { - // RelocationTarget::GrowMemory - // } else if *name == ExternalName::testcase("current_memory") { - // RelocationTarget::CurrentMemory - // } else { - // panic!("unrecognized external name") - // }; - // self.func_relocs.push(Relocation { - // reloc, - // reloc_target, - // offset, - // addend, - // }); - } - fn reloc_jt( - &mut self, - _offset: binemit::CodeOffset, - _reloc: binemit::Reloc, - _jt: ir::JumpTable, - ) { - panic!("jump tables not yet implemented"); - } -} - -impl RelocSink { - fn new() -> Self { - Self { - func_relocs: Vec::new(), - } - } -} - -/// A record of a relocation to perform. -#[derive(Debug, Clone)] -pub struct Relocation { - /// The relocation code. - pub reloc: binemit::Reloc, - /// Relocation target. - pub reloc_target: RelocationTarget, - /// The offset where to apply the relocation. - pub offset: binemit::CodeOffset, - /// The addend to add to the relocation value. - pub addend: binemit::Addend, -} - -/// Destination function. Can be either user function or some special one, like grow_memory. -#[derive(Debug, Copy, Clone)] -pub enum RelocationTarget { - /// The user function index. - UserFunc(FuncIndex), - /// Function for growing the default memory by the specified amount of pages. - GrowMemory, - /// Function for query current size of the default linear memory. - CurrentMemory, -} - -/// Relocations to apply to function bodies. -pub type Relocations = PrimaryMap>; - -/// Compile the module, producing a compilation result with associated -/// relocations. -pub fn compile_module<'data, 'module>( - translation: &ModuleTranslation<'data, 'module>, - isa: &isa::TargetIsa, -) -> Result<(Compilation, Relocations), String> { - println!("compile_module::1"); - let mut functions = PrimaryMap::new(); - let mut relocations = PrimaryMap::new(); - let mut lazy_functions = PrimaryMap::new(); - println!("compile_module::2"); - for (i, input) in translation.lazy.function_body_inputs.iter() { - // println!("compile_module::{:?}::3", i); - let func_index = translation.module.func_index(i); - let mut context = Context::new(); - // println!("compile_module::{:?}::4", i); - context.func.name = get_func_name(func_index); - context.func.signature = - translation.module.signatures[translation.module.functions[func_index]].clone(); - - let mut trans = FuncTranslator::new(); - // println!("compile_module::{:?}::5", i); - - trans - .translate(input, &mut context.func, &mut translation.func_env()) - .map_err(|e| e.to_string())?; - // println!("compile_module::{:?}::6", i); - - let mut code_buf: Vec = Vec::new(); - let mut reloc_sink = RelocSink::new(); - let mut trap_sink = binemit::NullTrapSink {}; - - // println!("compile_module::{:?}::7", i); - context - .compile_and_emit(isa, &mut code_buf, &mut reloc_sink, &mut trap_sink) - .map_err(|e| e.to_string())?; - protect_codebuf(&code_buf)?; - - // println!("compile_module::{:?}::8", i); - - functions.push(code_buf); - relocations.push(reloc_sink.func_relocs); - } - Ok((Compilation::new(functions, lazy_functions), relocations)) -} diff --git a/src/webassembly/env.rs b/src/webassembly/env.rs deleted file mode 100644 index 83267e6d0..000000000 --- a/src/webassembly/env.rs +++ /dev/null @@ -1,698 +0,0 @@ -//! "" 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 super::errors::ErrorKind; -use std::string::String; -use std::vec::Vec; -use target_lexicon::{Triple, PointerWidth}; -use cranelift_wasm::{ - FuncTranslator, - FuncEnvironment as FuncEnvironmentTrait, GlobalVariable, ModuleEnvironment, ReturnMode, WasmResult, - DefinedFuncIndex, FuncIndex, Global, GlobalIndex, Memory, MemoryIndex, SignatureIndex, Table, - TableIndex, translate_module -}; - -// 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 `Module`. This is split out from -/// `Module` 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, - - pub main_memory_base: Option, - - /// The Cranelift global holding the base address of the memories vector. - pub memory_base: Option, - - /// 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, - /// The base of tables. - pub tables_base: Option, - - /// Memories as provided by `declare_memory`. - pub memories: Vec>, - - /// The Cranelift global holding the base address of the globals vector. - pub globals_base: Option, - - /// 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(), - globals_base: None, - table_elements: Vec::new(), - tables_base: None, - start_func: None, - data_initializers: Vec::new(), - main_memory_base: None, - memory_base: None, - } - } -} - - -/// 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 Module { - /// 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 Module { - /// 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 { - // Self { - // info: ModuleInfo::with_triple_flags(triple, flags), - // trans: FuncTranslator::new(), - // func_bytecode_sizes: Vec::new(), - // return_mode, - // } - // } - - pub fn from_bytes(buffer_source: Vec, triple: Triple, flags: Option) -> Result { - let return_mode = ReturnMode::NormalReturns; - let flags = flags.unwrap_or_else(|| { - settings::Flags::new(settings::builder()) - }); - let mut module = Self { - info: ModuleInfo::with_triple_flags(triple, flags), - trans: FuncTranslator::new(), - func_bytecode_sizes: Vec::new(), - return_mode, - }; - // We iterate through the source bytes, generating the compiled module - translate_module(&buffer_source, &mut module).map_err(|e| ErrorKind::CompileError(e.to_string()))?; - - Ok(module) - } - - /// Return a `FuncEnvironment` for translating functions within this - /// `Module`. - 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 `Module`. -pub struct FuncEnvironment<'environment> { - pub mod_info: &'environment ModuleInfo, - - return_mode: ReturnMode, -} - -impl<'environment> FuncEnvironment<'environment> { - pub fn new(mod_info: &'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 - } - - fn ptr_size(&self) -> usize { - if self.triple().pointer_width().unwrap() == PointerWidth::U64 { - 8 - } else { - 4 - } - } -} - -impl<'environment> FuncEnvironmentTrait for FuncEnvironment<'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 { - // OLD - // 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, - }) - // use memory::WasmMemory; - // if index == 0 { - // let heap_base = self.main_memory_base.unwrap_or_else(|| { - // let new_base = func.create_global_value(ir::GlobalValueData::VMContext { - // offset: 0.into(), - // }); - // self.main_memory_base = Some(new_base); - // new_base - // }); - - // func.create_heap(ir::HeapData { - // base: heap_base, - // min_size: 0.into(), - // guard_size: (WasmMemory::DEFAULT_GUARD_SIZE as i64).into(), - // style: ir::HeapStyle::Static { - // bound: (WasmMemory::DEFAULT_HEAP_SIZE as i64).into(), - // }, - // }) - // } else { - // let memory_base = self.memory_base.unwrap_or_else(|| { - // let memories_offset = self.ptr_size() as i32 * -2; - // let new_base = func.create_global_value(ir::GlobalValueData::VMContext { - // offset: memories_offset.into(), - // }); - // self.memory_base = Some(new_base); - // new_base - // }); - - // let memory_offset = (index - 1) * self.ptr_size(); - // let heap_base = func.create_global_value(ir::GlobalValueData::Deref { - // base: memory_base, - // offset: (memory_offset as i32).into(), - // }); - - // func.create_heap(ir::HeapData { - // base: heap_base, - // min_size: 0.into(), - // guard_size: (WasmMemory::DEFAULT_GUARD_SIZE as i64).into(), - // style: ir::HeapStyle::Static { - // bound: (WasmMemory::DEFAULT_HEAP_SIZE as i64).into(), - // }, - // }) - // } - } - - 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(); - - let base = self.mod_info.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.mod_info.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::Load { - base, - offset: table_data_offset.into(), - global_type: self.pointer_type(), // Might be I32 - }); - let new_table_addr = func.create_global_value(ir::GlobalValueData::Load { - base: new_table_addr_addr, - offset: 0.into(), - global_type: self.pointer_type(), // Might be I32 - }); - - let new_table_bounds_addr = func.create_global_value(ir::GlobalValueData::Load { - base, - offset: (table_data_offset + ptr_size as i32).into(), - global_type: self.pointer_type(), // Might be I32 - }); - let new_table_bounds = func.create_global_value(ir::GlobalValueData::Load { - base: new_table_bounds_addr, - offset: 0.into(), - global_type: I32, // Might be self.pointer_type() - }); - - let table = func.create_table(ir::TableData { - base_gv: new_table_addr, - min_size: Imm64::new(0), - // min_size: (self.mod_info.tables[table_index].size as i64).into(), - bound_gv: new_table_bounds, - element_size: (ptr_size as i64).into(), - index_type: I32 - }); - - 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.module.signatures[index].clone()) - 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 Module { - 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(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/environ.rs b/src/webassembly/environ.rs deleted file mode 100644 index a22bc7dea..000000000 --- a/src/webassembly/environ.rs +++ /dev/null @@ -1,499 +0,0 @@ -use cranelift_codegen::cursor::FuncCursor; -use cranelift_codegen::ir; -use cranelift_codegen::ir::immediates::{Imm64, Offset32}; -use cranelift_codegen::ir::types::*; -use cranelift_codegen::ir::{ - AbiParam, ArgumentPurpose, ExtFuncData, ExternalName, FuncRef, Function, InstBuilder, Signature, -}; -use cranelift_codegen::isa; -use cranelift_codegen::settings; -use cranelift_entity::EntityRef; -use cranelift_wasm::{ - self, translate_module, FuncIndex, Global, GlobalIndex, GlobalVariable, Memory, MemoryIndex, - SignatureIndex, Table, TableIndex, WasmResult, ReturnMode -}; -use target_lexicon::Triple; - -use super::module::{DataInitializer, Export, LazyContents, Module, TableElements}; - -/// Compute a `ir::ExternalName` for a given wasm function index. -pub fn get_func_name(func_index: FuncIndex) -> ir::ExternalName { - debug_assert!(FuncIndex::new(func_index.index() as u32 as usize) == func_index); - ir::ExternalName::user(0, func_index.index() as u32) -} - -/// Object containing the standalone environment information. To be passed after creation as -/// argument to `compile_module`. -pub struct ModuleEnvironment<'data, 'module> { - /// Compilation setting flags. - pub isa: &'module isa::TargetIsa, - - /// Module information. - pub module: &'module mut Module, - - /// References to information to be decoded later. - pub lazy: LazyContents<'data>, -} - -impl<'data, 'module> ModuleEnvironment<'data, 'module> { - /// Allocates the enironment data structures with the given isa. - pub fn new(isa: &'module isa::TargetIsa, module: &'module mut Module) -> Self { - Self { - isa, - module, - lazy: LazyContents::new(), - } - } - - fn func_env(&self) -> FuncEnvironment { - FuncEnvironment::new(self.isa, &self.module) - } - - fn pointer_type(&self) -> ir::Type { - use cranelift_wasm::FuncEnvironment; - self.func_env().pointer_type() - } - - /// Translate the given wasm module data using this environment. This consumes the - /// `ModuleEnvironment` with its mutable reference to the `Module` and produces a - /// `ModuleTranslation` with an immutable reference to the `Module` (which has - /// become fully populated). - pub fn translate(mut self, data: &'data [u8]) -> WasmResult> { - // print!("translate::1"); - translate_module(data, &mut self)?; - // print!("translate::2"); - - Ok(ModuleTranslation { - isa: self.isa, - module: self.module, - lazy: self.lazy, - }) - } -} - -/// The FuncEnvironment implementation for use by the `ModuleEnvironment`. -pub struct FuncEnvironment<'module_environment> { - /// Compilation setting flags. - isa: &'module_environment isa::TargetIsa, - - /// The module-level environment which this function-level environment belongs to. - pub module: &'module_environment Module, - - /// The Cranelift global holding the base address of the memories vector. - pub memories_base: Option, - - /// The Cranelift global holding the base address of the globals vector. - pub globals_base: Option, - - /// The external function declaration for implementing wasm's `current_memory`. - pub current_memory_extfunc: Option, - - /// The external function declaration for implementing wasm's `grow_memory`. - pub grow_memory_extfunc: Option, -} - -impl<'module_environment> FuncEnvironment<'module_environment> { - pub fn new( - isa: &'module_environment isa::TargetIsa, - module: &'module_environment Module, - ) -> Self { - Self { - isa, - module, - memories_base: None, - globals_base: None, - current_memory_extfunc: None, - grow_memory_extfunc: None, - } - } - - /// Transform the call argument list in preparation for making a call. - 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 - } - - fn pointer_bytes(&self) -> usize { - usize::from(self.isa.pointer_bytes()) - } -} - -/// This trait is useful for `translate_module` because it tells how to translate -/// enironment-dependent wasm instructions. These functions should not be called by the user. -impl<'data, 'module> cranelift_wasm::ModuleEnvironment<'data> - for ModuleEnvironment<'data, 'module> -{ - fn get_func_name(&self, func_index: FuncIndex) -> ir::ExternalName { - get_func_name(func_index) - } - - fn flags(&self) -> &settings::Flags { - self.isa.flags() - } - - fn declare_signature(&mut self, sig: &ir::Signature) { - let mut sig = sig.clone(); - sig.params.push(AbiParam::special( - self.pointer_type(), - ArgumentPurpose::VMContext, - )); - // TODO: Deduplicate signatures. - self.module.signatures.push(sig); - } - - fn get_signature(&self, sig_index: SignatureIndex) -> &ir::Signature { - &self.module.signatures[sig_index] - } - - fn declare_func_import(&mut self, sig_index: SignatureIndex, module: &str, field: &str) { - debug_assert_eq!( - self.module.functions.len(), - self.module.imported_funcs.len(), - "Imported functions must be declared first" - ); - self.module.functions.push(sig_index); - - self.module - .imported_funcs - .push((String::from(module), String::from(field))); - } - - fn get_num_func_imports(&self) -> usize { - self.module.imported_funcs.len() - } - - fn declare_func_type(&mut self, sig_index: SignatureIndex) { - self.module.functions.push(sig_index); - } - - fn get_func_type(&self, func_index: FuncIndex) -> SignatureIndex { - self.module.functions[func_index] - } - - fn declare_global(&mut self, global: Global) { - self.module.globals.push(global); - } - - fn get_global(&self, global_index: GlobalIndex) -> &Global { - &self.module.globals[global_index] - } - - fn declare_table(&mut self, table: Table) { - self.module.tables.push(table); - } - - fn declare_table_elements( - &mut self, - table_index: TableIndex, - base: Option, - offset: usize, - elements: Vec, - ) { - // debug_assert!(base.is_none(), "global-value offsets not supported yet"); - self.module.table_elements.push(TableElements { - table_index, - base, - offset, - elements, - }); - } - - fn declare_memory(&mut self, memory: Memory) { - self.module.memories.push(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.lazy.data_initializers.push(DataInitializer { - memory_index, - base, - offset, - data, - }); - } - - fn declare_func_export(&mut self, func_index: FuncIndex, name: &str) { - self.module - .exports - .insert(String::from(name), Export::Function(func_index)); - } - - fn declare_table_export(&mut self, table_index: TableIndex, name: &str) { - self.module - .exports - .insert(String::from(name), Export::Table(table_index)); - } - - fn declare_memory_export(&mut self, memory_index: MemoryIndex, name: &str) { - self.module - .exports - .insert(String::from(name), Export::Memory(memory_index)); - } - - fn declare_global_export(&mut self, global_index: GlobalIndex, name: &str) { - self.module - .exports - .insert(String::from(name), Export::Global(global_index)); - } - - fn declare_start_func(&mut self, func_index: FuncIndex) { - debug_assert!(self.module.start_func.is_none()); - self.module.start_func = Some(func_index); - } - - fn define_function_body(&mut self, body_bytes: &'data [u8]) -> WasmResult<()> { - self.lazy.function_body_inputs.push(body_bytes); - Ok(()) - } -} - -impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'module_environment> { - fn flags(&self) -> &settings::Flags { - &self.isa.flags() - } - - fn triple(&self) -> &Triple { - self.isa.triple() - } - - fn make_global(&mut self, func: &mut ir::Function, index: GlobalIndex) -> GlobalVariable { - let pointer_bytes = self.pointer_bytes(); - let globals_base = self.globals_base.unwrap_or_else(|| { - let new_base = func.create_global_value(ir::GlobalValueData::VMContext { - offset: Offset32::new(0), - }); - self.globals_base = Some(new_base); - new_base - }); - let offset = index * pointer_bytes; - let offset32 = offset as i32; - debug_assert_eq!(offset32 as usize, offset); - let gv = func.create_global_value(ir::GlobalValueData::Deref { - base: globals_base, - offset: Offset32::new(offset32), - memory_type: self.pointer_type(), - }); - GlobalVariable::Memory { - gv, - ty: self.module.globals[index].ty, - } - } - - fn make_heap(&mut self, func: &mut ir::Function, index: MemoryIndex) -> ir::Heap { - let pointer_bytes = self.pointer_bytes(); - let memories_base = self.memories_base.unwrap_or_else(|| { - let new_base = func.create_global_value(ir::GlobalValueData::VMContext { - offset: Offset32::new(pointer_bytes as i32), - }); - self.globals_base = Some(new_base); - new_base - }); - let offset = index * pointer_bytes; - let offset32 = offset as i32; - debug_assert_eq!(offset32 as usize, offset); - let heap_base_addr = func.create_global_value(ir::GlobalValueData::Deref { - base: memories_base, - offset: Offset32::new(offset32), - memory_type: self.pointer_type(), - }); - let heap_base = func.create_global_value(ir::GlobalValueData::Deref { - base: heap_base_addr, - offset: Offset32::new(0), - memory_type: self.pointer_type(), - }); - func.create_heap(ir::HeapData { - base: heap_base, - 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, _index: TableIndex) -> ir::Table { - let pointer_bytes = self.pointer_bytes(); - let base_gv_addr = func.create_global_value(ir::GlobalValueData::VMContext { - offset: Offset32::new(pointer_bytes as i32 * 2), - }); - let base_gv = func.create_global_value(ir::GlobalValueData::Deref { - base: base_gv_addr, - offset: 0.into(), - memory_type: self.pointer_type(), - }); - let bound_gv_addr = func.create_global_value(ir::GlobalValueData::VMContext { - offset: Offset32::new(pointer_bytes as i32 * 3), - }); - let bound_gv = func.create_global_value(ir::GlobalValueData::Deref { - base: bound_gv_addr, - offset: 0.into(), - memory_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() as i64)), - index_type: I32, - }) - } - - fn make_indirect_sig(&mut self, func: &mut ir::Function, index: SignatureIndex) -> ir::SigRef { - func.import_signature(self.module.signatures[index].clone()) - } - - fn make_direct_func(&mut self, func: &mut ir::Function, index: FuncIndex) -> ir::FuncRef { - let sigidx = self.module.functions[index]; - let signature = func.import_signature(self.module.signatures[sigidx].clone()); - let name = get_func_name(index); - // We currently allocate all code segments independently, so nothing - // is colocated. - let colocated = false; - func.import_function(ir::ExtFuncData { - name, - signature, - colocated, - }) - } - - 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 { - // TODO: Cranelift's call_indirect doesn't implement signature checking, - // so we need to implement it ourselves. - debug_assert_eq!(table_index, 0, "non-default tables not supported yet"); - - let table_entry_addr = pos.ins().table_addr(I64, table, callee, 0); - - // Dereference table_entry_addr to get the function address. - let mut mem_flags = ir::MemFlags::new(); - mem_flags.set_notrap(); - mem_flags.set_aligned(); - let func_addr = pos - .ins() - .load(self.pointer_type(), mem_flags, table_entry_addr, 0); - - let real_call_args = FuncEnvironment::get_real_call_args(pos.func, call_args); - Ok(pos.ins().call_indirect(sig_ref, func_addr, &real_call_args)) - } - - fn translate_call( - &mut self, - mut pos: FuncCursor, - _callee_index: FuncIndex, - callee: ir::FuncRef, - call_args: &[ir::Value], - ) -> WasmResult { - let real_call_args = FuncEnvironment::get_real_call_args(pos.func, call_args); - Ok(pos.ins().call(callee, &real_call_args)) - } - - fn translate_memory_grow( - &mut self, - mut pos: FuncCursor, - index: MemoryIndex, - _heap: ir::Heap, - val: ir::Value, - ) -> WasmResult { - let grow_mem_func = self.grow_memory_extfunc.unwrap_or_else(|| { - let sig_ref = pos.func.import_signature(Signature { - call_conv: self.isa.flags().call_conv(), - params: vec![ - AbiParam::new(I32), - AbiParam::new(I32), - AbiParam::special(self.pointer_type(), ArgumentPurpose::VMContext), - ], - returns: vec![AbiParam::new(I32)], - }); - // We currently allocate all code segments independently, so nothing - // is colocated. - let colocated = false; - // FIXME: Use a real ExternalName system. - pos.func.import_function(ExtFuncData { - name: ExternalName::testcase("grow_memory"), - signature: sig_ref, - colocated, - }) - }); - self.grow_memory_extfunc = Some(grow_mem_func); - let memory_index = pos.ins().iconst(I32, index as i64); - let vmctx = pos.func.special_param(ArgumentPurpose::VMContext).unwrap(); - let call_inst = pos.ins().call(grow_mem_func, &[val, memory_index, vmctx]); - Ok(*pos.func.dfg.inst_results(call_inst).first().unwrap()) - } - - fn translate_memory_size( - &mut self, - mut pos: FuncCursor, - index: MemoryIndex, - _heap: ir::Heap, - ) -> WasmResult { - let cur_mem_func = self.current_memory_extfunc.unwrap_or_else(|| { - let sig_ref = pos.func.import_signature(Signature { - call_conv: self.isa.flags().call_conv(), - params: vec![ - AbiParam::new(I32), - AbiParam::special(self.pointer_type(), ArgumentPurpose::VMContext), - ], - returns: vec![AbiParam::new(I32)], - }); - // We currently allocate all code segments independently, so nothing - // is colocated. - let colocated = false; - // FIXME: Use a real ExternalName system. - pos.func.import_function(ExtFuncData { - name: ExternalName::testcase("current_memory"), - signature: sig_ref, - colocated, - }) - }); - self.current_memory_extfunc = Some(cur_mem_func); - let memory_index = pos.ins().iconst(I32, index as i64); - let vmctx = pos.func.special_param(ArgumentPurpose::VMContext).unwrap(); - let call_inst = pos.ins().call(cur_mem_func, &[memory_index, vmctx]); - Ok(*pos.func.dfg.inst_results(call_inst).first().unwrap()) - } - - fn return_mode(&self) -> ReturnMode { - // self.return_mode - ReturnMode::NormalReturns - } - -} - -/// The result of translating via `ModuleEnvironment`. -pub struct ModuleTranslation<'data, 'module> { - /// Compilation setting flags. - pub isa: &'module isa::TargetIsa, - - /// Module information. - pub module: &'module Module, - - /// Pointers into the raw data buffer. - pub lazy: LazyContents<'data>, -} - -/// Convenience functions for the user to be called after execution for debug purposes. -impl<'data, 'module> ModuleTranslation<'data, 'module> { - /// Return a new `FuncEnvironment` for translation a function. - pub fn func_env(&self) -> FuncEnvironment { - FuncEnvironment::new(self.isa, &self.module) - } -} diff --git a/src/webassembly/errors.rs b/src/webassembly/errors.rs index 55e9d41ea..7bb4e9174 100644 --- a/src/webassembly/errors.rs +++ b/src/webassembly/errors.rs @@ -1,19 +1,19 @@ -// WebAssembly.CompileError(message, fileName, lineNumber) +// webassembly::CompileError(message, fileName, lineNumber) -// The WebAssembly.CompileError() constructor creates a new WebAssembly +// The webassembly::CompileError() constructor creates a new WebAssembly // CompileError object, which indicates an error during WebAssembly // decoding or validation -// WebAssembly.LinkError(message, fileName, lineNumber) +// webassembly::LinkError(message, fileName, lineNumber) -// The WebAssembly.LinkError() constructor creates a new WebAssembly +// The webassembly::LinkError() constructor creates a new WebAssembly // LinkError object, which indicates an error during module instantiation // (besides traps from the start function). -// new WebAssembly.RuntimeError(message, fileName, lineNumber) +// new webassembly::RuntimeError(message, fileName, lineNumber) -// The WebAssembly.RuntimeError() constructor creates a new WebAssembly +// The webassembly::RuntimeError() constructor creates a new WebAssembly // RuntimeError object — the type that is thrown whenever WebAssembly // specifies a trap. diff --git a/src/webassembly/execute.rs b/src/webassembly/execute.rs deleted file mode 100644 index 430cf0bf4..000000000 --- a/src/webassembly/execute.rs +++ /dev/null @@ -1,210 +0,0 @@ -use cranelift_codegen::binemit::Reloc; -use cranelift_codegen::isa::TargetIsa; -use cranelift_entity::PrimaryMap; -use cranelift_wasm::{DefinedFuncIndex, MemoryIndex}; -use std::mem::transmute; -use std::ptr::{self, write_unaligned}; - -use super::compilation::{ - compile_module, Compilation, Relocation, RelocationTarget, -}; -use super::memory::LinearMemory; -use super::module::{Module, Export}; -use super::instance::Instance; -use super::environ::ModuleTranslation; - -/// Executes a module that has been translated with the `wasmtime-environ` environment -/// implementation. -pub fn compile_and_link_module<'data, 'module>( - isa: &TargetIsa, - translation: &ModuleTranslation<'data, 'module>, -) -> Result { - println!("compile_and_link_module::1"); - let (mut compilation, relocations) = compile_module(&translation, isa)?; - println!("compile_and_link_module::2"); - - // Apply relocations, now that we have virtual addresses for everything. - relocate(&mut compilation, &relocations, &translation.module); - - println!("compile_and_link_module::3"); - - Ok(compilation) -} - -/// Performs the relocations inside the function bytecode, provided the necessary metadata -fn relocate( - compilation: &mut Compilation, - relocations: &PrimaryMap>, - module: &Module, -) { - // 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 r in function_relocs { - let target_func_address: isize = match r.reloc_target { - RelocationTarget::UserFunc(index) => { - compilation.functions[module.defined_func_index(index).expect( - "relocation to imported function not supported yet", - )].as_ptr() as isize - } - RelocationTarget::GrowMemory => grow_memory as isize, - RelocationTarget::CurrentMemory => current_memory as isize, - }; - - let body = &mut compilation.functions[i]; - match r.reloc { - Reloc::Abs8 => unsafe { - let reloc_address = body.as_mut_ptr().offset(r.offset as isize) as i64; - let reloc_addend = r.addend; - let reloc_abs = target_func_address as i64 + reloc_addend; - write_unaligned(reloc_address as *mut i64, reloc_abs); - }, - Reloc::X86PCRel4 => unsafe { - let reloc_address = body.as_mut_ptr().offset(r.offset as isize) as isize; - let reloc_addend = r.addend as isize; - // TODO: Handle overflow. - let reloc_delta_i32 = - (target_func_address - reloc_address + reloc_addend) as i32; - write_unaligned(reloc_address as *mut i32, reloc_delta_i32); - }, - _ => panic!("unsupported reloc kind"), - } - } - } -} - -extern "C" fn grow_memory(size: u32, memory_index: u32, vmctx: *mut *mut u8) -> u32 { - unsafe { - let instance = (*vmctx.offset(4)) as *mut Instance; - (*instance) - .memory_mut(memory_index as MemoryIndex) - .grow(size) - .unwrap_or(u32::max_value()) - } -} - -extern "C" fn current_memory(memory_index: u32, vmctx: *mut *mut u8) -> u32 { - unsafe { - let instance = (*vmctx.offset(4)) as *mut Instance; - (*instance) - .memory_mut(memory_index as MemoryIndex) - .current_size() - } -} - -/// Create the VmCtx data structure for the JIT'd code to use. This must -/// match the VmCtx layout in the environment. -pub fn make_vmctx(instance: &mut Instance, mem_base_addrs: &mut [*mut u8]) -> Vec<*mut u8> { - debug_assert!( - instance.tables.len() <= 1, - "non-default tables is not supported" - ); - - let (default_table_ptr, default_table_len) = instance - .tables - .get_mut(0) - .map(|table| (table.as_mut_ptr() as *mut u8, table.len())) - .unwrap_or((ptr::null_mut(), 0)); - - let mut vmctx = Vec::new(); - vmctx.push(instance.globals.as_mut_ptr()); - vmctx.push(mem_base_addrs.as_mut_ptr() as *mut u8); - vmctx.push(default_table_ptr); - vmctx.push(default_table_len as *mut u8); - vmctx.push(instance as *mut Instance as *mut u8); - - vmctx -} - - -/// Jumps to the code region of memory and execute the start function of the module. -pub fn execute( - module: &Module, - compilation: &Compilation, - instance: &mut Instance, -) -> Result<(), String> { - // println!("execute"); - - let start_index = module.start_func.or_else(|| { - match module.exports.get("main") { - Some(&Export::Function(index)) => Some(index), - _ => None - } - }) ; - // else { - // // TODO: We really need to handle this error nicely - // return Err("need to have a start function".to_string()); - // } - // let start_index = module - // .start_func - // .ok_or_else(|| String::from("No start function defined, aborting execution"))?; - - // We have to relocate here - - - // TODO: Put all the function bodies into a page-aligned memory region, and - // then make them ReadExecute rather than ReadWriteExecute. - - let code_buf = start_index.map(|i| { - &compilation.functions[module - .defined_func_index(i) - .expect("imported start functions not supported yet")] - }); - - code_buf.map(|code_buf_pt|{ - - // Collect all memory base addresses and Vec. - let mut mem_base_addrs = instance - .memories - .iter_mut() - .map(LinearMemory::base_addr) - .collect::>(); - - let vmctx = make_vmctx(instance, &mut mem_base_addrs); - // Rather than writing inline assembly to jump to the code region, we use the fact that - // the Rust ABI for calling a function with no arguments and no return matches the one of - // the generated code. Thanks to this, we can transmute the code region into a first-class - // Rust function and call it. - unsafe { - let start_func = transmute::<_, fn(*const *mut u8)>(code_buf_pt.as_ptr()); - start_func(vmctx.as_ptr()); - } - }); - // println!("{:?}", module.exports); - // println!("execute end"); - - - Ok(()) -} - -// pub fn execute_fn( -// instance: &mut Instance, -// func_name: String -// ) -> Result<(), String> { -// println!("execute"); - -// let start_index = match instance.module.exports.get(&func_name) { -// Some(&Export::Function(index)) => index, -// _ => panic!("No func name") -// }; - -// let code_buf = &instance.compilation.functions[instance.module -// .defined_func_index(start_index) -// .expect("imported start functions not supported yet")]; - - -// let mut mem_base_addrs = instance -// .memories -// .iter_mut() -// .map(LinearMemory::base_addr) -// .collect::>(); - -// let vmctx = make_vmctx(instance, &mut mem_base_addrs); - -// unsafe { -// let start_func = transmute::<_, fn(*const *mut u8)>(code_buf.as_ptr()); -// start_func(vmctx.as_ptr()) -// } - -// Ok(()) -// } diff --git a/src/webassembly/instance.rs b/src/webassembly/instance.rs index a15b9a708..0ce9d16b5 100644 --- a/src/webassembly/instance.rs +++ b/src/webassembly/instance.rs @@ -1,8 +1,14 @@ -//! An 'Instance' contains all the runtime state used by execution of a wasm module +//! A webassembly::Instance object is a stateful, executable instance of a +//! webassembly::Module. Instance objects contain all the Exported +//! WebAssembly functions that allow calling into WebAssembly code. +//! The webassembly::Instance() constructor function can be called to +//! synchronously instantiate a given webassembly::Module object. However, the +//! primary way to get an Instance is through the asynchronous +//! webassembly::instantiateStreaming() function. use cranelift_wasm::{GlobalInit, FuncIndex}; -use super::env::Module; -use super::env::{DataInitializer, Exportable}; +use super::module::Module; +use super::module::{DataInitializer, Exportable}; use cranelift_entity::EntityRef; use super::memory::LinearMemory; @@ -77,6 +83,7 @@ impl Instance { // instantiate_tables { + // Reserve table space tables.reserve_exact(module.info.tables.len()); for table in &module.info.tables { let len = table.entity.size; @@ -153,6 +160,10 @@ impl Instance { self.memories.clone() } + pub fn invoke(&self, func_index: FuncIndex, args: Vec) { + unimplemented!() + } + // pub fn start_func(&self) -> extern fn(&VmCtx) { // self.start_func // } diff --git a/src/webassembly/memory.rs b/src/webassembly/memory.rs index 4830b9386..a3126a1a7 100644 --- a/src/webassembly/memory.rs +++ b/src/webassembly/memory.rs @@ -10,10 +10,19 @@ const MAX_PAGES: u32 = 65536; /// for dynamical growing. pub struct LinearMemory { mmap: memmap::MmapMut, + // The initial size of the WebAssembly Memory, in units of + // WebAssembly pages. current: u32, + // The maximum size the WebAssembly Memory is allowed to grow + // to, in units of WebAssembly pages. When present, the maximum + // parameter acts as a hint to the engine to reserve memory up + // front. However, the engine may ignore or clamp this reservation + // request. In general, most WebAssembly modules shouldn't need + // to set a maximum. maximum: Option, } +/// It holds the raw bytes of memory accessed by a WebAssembly Instance impl LinearMemory { /// Create a new linear memory instance with specified initial and maximum number of pages. /// diff --git a/src/webassembly/mod.rs b/src/webassembly/mod.rs index ec9886a51..f8cc89373 100644 --- a/src/webassembly/mod.rs +++ b/src/webassembly/mod.rs @@ -6,7 +6,7 @@ pub mod errors; // pub mod execute; pub mod utils; -pub mod env; +pub mod module; pub mod memory; pub mod instance; @@ -26,7 +26,7 @@ use cranelift_codegen::settings::{self, Flags}; use cranelift_codegen::verifier; use cranelift_wasm::{translate_module, ReturnMode}; -pub use self::env::Module; +pub use self::module::Module; pub use self::instance::Instance; // pub use self::compilation::{compile_module, Compilation}; @@ -38,10 +38,10 @@ pub use self::errors::{Error, ErrorKind}; use wasmparser; // pub struct ResultObject { -// /// A WebAssembly.Module object representing the compiled WebAssembly module. +// /// 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 +// /// A webassembly::Instance object that contains all the Exported WebAssembly // /// functions. // pub instance: Instance, @@ -51,7 +51,7 @@ use wasmparser; pub struct ImportObject { } -/// The WebAssembly.instantiate() function allows you to compile and +/// The webassembly::instantiate() function allows you to compile and /// instantiate WebAssembly code /// Params: @@ -60,14 +60,14 @@ pub struct ImportObject { /// * `import_object`: An object containing the values to be imported /// into the newly-created Instance, such as functions or -/// WebAssembly.Memory objects. There must be one matching property +/// webassembly::Memory objects. There must be one matching property /// for each declared import of the compiled module or else a -/// WebAssembly.LinkError is thrown. +/// webassembly::LinkError is thrown. /// Errors: /// If the operation fails, the Result rejects with a -/// WebAssembly.CompileError, WebAssembly.LinkError, or -/// WebAssembly.RuntimeError, depending on the cause of the failure. +/// webassembly::CompileError, webassembly::LinkError, or +/// webassembly::RuntimeError, depending on the cause of the failure. pub fn instantiate(buffer_source: Vec, import_object: Option) -> Result { // TODO: This should be automatically validated when creating the Module if !validate(&buffer_source) { @@ -126,10 +126,10 @@ pub fn instantiate(buffer_source: Vec, import_object: Option) // }) } -/// The WebAssembly.compile() function compiles a WebAssembly.Module +/// The webassembly::compile() function compiles a webassembly::Module /// from WebAssembly binary code. This function is useful if it /// is necessary to a compile a module before it can be instantiated -/// (otherwise, the WebAssembly.instantiate() function should be used). +/// (otherwise, the webassembly::instantiate() function should be used). /// Params: /// * `buffer_source`: A `Vec` containing the @@ -137,7 +137,7 @@ pub fn instantiate(buffer_source: Vec, import_object: Option) /// Errors: /// If the operation fails, the Result rejects with a -/// WebAssembly.CompileError. +/// webassembly::CompileError. pub fn compile(buffer_source: Vec) -> Result { unimplemented!(); // let isa = construct_isa(); @@ -150,7 +150,7 @@ pub fn compile(buffer_source: Vec) -> Result { // Ok(module) } -/// The WebAssembly.validate() function validates a given typed +/// The webassembly::validate() function validates a given typed /// array of WebAssembly binary code, returning whether the bytes /// form a valid wasm module (true) or not (false). diff --git a/src/webassembly/module.rs b/src/webassembly/module.rs index 9c6f063a4..966632767 100644 --- a/src/webassembly/module.rs +++ b/src/webassembly/module.rs @@ -1,15 +1,152 @@ -/// A WebAssembly.Module object representing the compiled WebAssembly module. -extern crate cranelift_codegen; -extern crate cranelift_entity; -extern crate cranelift_wasm; +//! "" implementations of `ModuleEnvironment` and `FuncEnvironment` for testing +//! wasm translation. -use cranelift_codegen::ir; +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 super::errors::ErrorKind; +use std::string::String; +use std::vec::Vec; +use target_lexicon::{Triple, PointerWidth}; use cranelift_wasm::{ + FuncTranslator, + FuncEnvironment as FuncEnvironmentTrait, GlobalVariable, ModuleEnvironment, ReturnMode, WasmResult, DefinedFuncIndex, FuncIndex, Global, GlobalIndex, Memory, MemoryIndex, SignatureIndex, Table, - TableIndex, + TableIndex, translate_module }; -use std::collections::HashMap; +use super::memory::LinearMemory; + +// 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 `Module`. This is split out from +/// `Module` 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, + + pub main_memory_base: Option, + + /// The Cranelift global holding the base address of the memories vector. + pub memory_base: Option, + + /// 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, + /// The base of tables. + pub tables_base: Option, + + /// Memories as provided by `declare_memory`. + pub memories: Vec>, + + /// The Cranelift global holding the base address of the globals vector. + pub globals_base: Option, + + /// 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(), + globals_base: None, + table_elements: Vec::new(), + tables_base: None, + start_func: None, + data_initializers: Vec::new(), + main_memory_base: None, + memory_base: None, + } + } +} + + +/// 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)] @@ -24,112 +161,556 @@ pub struct TableElements { pub elements: Vec, } -/// An entity to export. -#[derive(Clone, Debug)] -pub enum Export { - /// Function export. - Function(FuncIndex), - /// Table export. - Table(TableIndex), - /// Memory export. - Memory(MemoryIndex), - /// Global export. - Global(GlobalIndex), -} -/// A translated WebAssembly module, excluding the function bodies and -/// memory initializers. -#[derive(Clone, Debug)] +/// 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 Module { - /// Unprocessed signatures exactly as provided by `declare_signature()`. - pub signatures: Vec, + /// Module information. + pub info: ModuleInfo, - /// Names of imported functions. - pub imported_funcs: Vec<(String, String)>, + /// Function translation. + trans: FuncTranslator, - /// Types of functions, imported and local. - pub functions: PrimaryMap, + /// Vector of wasm bytecode size for each function. + pub func_bytecode_sizes: Vec, - /// WebAssembly tables. - pub tables: Vec, - - /// WebAssembly linear memories. - pub memories: Vec, - - /// WebAssembly global variables. - pub globals: Vec, - - /// Exported entities. - pub exports: HashMap, - - /// The module "start" function, if present. - pub start_func: Option, - - /// WebAssembly table initializers. - pub table_elements: Vec, + /// How to return from functions. + return_mode: ReturnMode, } impl Module { - /// Allocates the module data structures. - pub fn new() -> Self { - Self { - signatures: Vec::new(), - imported_funcs: Vec::new(), - functions: PrimaryMap::new(), - tables: Vec::new(), - memories: Vec::new(), - globals: Vec::new(), - exports: HashMap::new(), - start_func: None, - table_elements: Vec::new(), - } + /// 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 { + // Self { + // info: ModuleInfo::with_triple_flags(triple, flags), + // trans: FuncTranslator::new(), + // func_bytecode_sizes: Vec::new(), + // return_mode, + // } + // } + + pub fn from_bytes(buffer_source: Vec, triple: Triple, flags: Option) -> Result { + let return_mode = ReturnMode::NormalReturns; + let flags = flags.unwrap_or_else(|| { + settings::Flags::new(settings::builder()) + }); + let mut module = Self { + info: ModuleInfo::with_triple_flags(triple, flags), + trans: FuncTranslator::new(), + func_bytecode_sizes: Vec::new(), + return_mode, + }; + // We iterate through the source bytes, generating the compiled module + translate_module(&buffer_source, &mut module).map_err(|e| ErrorKind::CompileError(e.to_string()))?; + + Ok(module) + } + + /// Return a `FuncEnvironment` for translating functions within this + /// `Module`. + pub fn func_env(&self) -> FuncEnvironment { + FuncEnvironment::new(&self.info, self.return_mode) + } + + fn native_pointer(&self) -> ir::Type { + self.func_env().pointer_type() } /// Convert a `DefinedFuncIndex` into a `FuncIndex`. pub fn func_index(&self, defined_func: DefinedFuncIndex) -> FuncIndex { - FuncIndex::new(self.imported_funcs.len() + defined_func.index()) + FuncIndex::new(self.info.imported_funcs.len() + defined_func.index()) } /// Convert a `FuncIndex` into a `DefinedFuncIndex`. Returns None if the /// index is an imported function. pub fn defined_func_index(&self, func: FuncIndex) -> Option { - if func.index() < self.imported_funcs.len() { + if func.index() < self.info.imported_funcs.len() { None } else { Some(DefinedFuncIndex::new( - func.index() - self.imported_funcs.len(), + func.index() - self.info.imported_funcs.len(), )) } } + } -/// A data initializer for linear memory. -pub struct DataInitializer<'data> { - /// The index of the memory to initialize. - pub memory_index: MemoryIndex, - /// Optionally a globalvar base to initialize at. - pub base: Option, - /// A constant offset to initialize at. - pub offset: usize, - /// The initialization data. - pub data: &'data [u8], +/// The `FuncEnvironment` implementation for use by the `Module`. +pub struct FuncEnvironment<'environment> { + pub mod_info: &'environment ModuleInfo, + + return_mode: ReturnMode, } -/// References to the input wasm data buffer to be decoded and processed later, -/// separately from the main module translation. -pub struct LazyContents<'data> { - /// References to the function bodies. - pub function_body_inputs: PrimaryMap, - - /// References to the data initializers. - pub data_initializers: Vec>, -} - -impl<'data> LazyContents<'data> { - pub fn new() -> Self { +impl<'environment> FuncEnvironment<'environment> { + pub fn new(mod_info: &'environment ModuleInfo, return_mode: ReturnMode) -> Self { Self { - function_body_inputs: PrimaryMap::new(), - data_initializers: Vec::new(), + 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 + } + + fn ptr_size(&self) -> usize { + if self.triple().pointer_width().unwrap() == PointerWidth::U64 { + 8 + } else { + 4 } } } + +impl<'environment> FuncEnvironmentTrait for FuncEnvironment<'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 { + // OLD + // 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, + }) + // use memory::WasmMemory; + // if index == 0 { + // let heap_base = self.main_memory_base.unwrap_or_else(|| { + // let new_base = func.create_global_value(ir::GlobalValueData::VMContext { + // offset: 0.into(), + // }); + // self.main_memory_base = Some(new_base); + // new_base + // }); + + // func.create_heap(ir::HeapData { + // base: heap_base, + // min_size: 0.into(), + // guard_size: (WasmMemory::DEFAULT_GUARD_SIZE as i64).into(), + // style: ir::HeapStyle::Static { + // bound: (WasmMemory::DEFAULT_HEAP_SIZE as i64).into(), + // }, + // }) + // } else { + // let memory_base = self.memory_base.unwrap_or_else(|| { + // let memories_offset = self.ptr_size() as i32 * -2; + // let new_base = func.create_global_value(ir::GlobalValueData::VMContext { + // offset: memories_offset.into(), + // }); + // self.memory_base = Some(new_base); + // new_base + // }); + + // let memory_offset = (index - 1) * self.ptr_size(); + // let heap_base = func.create_global_value(ir::GlobalValueData::Deref { + // base: memory_base, + // offset: (memory_offset as i32).into(), + // }); + + // func.create_heap(ir::HeapData { + // base: heap_base, + // min_size: 0.into(), + // guard_size: (WasmMemory::DEFAULT_GUARD_SIZE as i64).into(), + // style: ir::HeapStyle::Static { + // bound: (WasmMemory::DEFAULT_HEAP_SIZE as i64).into(), + // }, + // }) + // } + } + + 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(); + + let base = self.mod_info.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.mod_info.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::Load { + base, + offset: table_data_offset.into(), + global_type: self.pointer_type(), // Might be I32 + }); + let new_table_addr = func.create_global_value(ir::GlobalValueData::Load { + base: new_table_addr_addr, + offset: 0.into(), + global_type: self.pointer_type(), // Might be I32 + }); + + let new_table_bounds_addr = func.create_global_value(ir::GlobalValueData::Load { + base, + offset: (table_data_offset + ptr_size as i32).into(), + global_type: self.pointer_type(), // Might be I32 + }); + let new_table_bounds = func.create_global_value(ir::GlobalValueData::Load { + base: new_table_bounds_addr, + offset: 0.into(), + global_type: I32, // Might be self.pointer_type() + }); + + let table = func.create_table(ir::TableData { + base_gv: new_table_addr, + min_size: Imm64::new(0), + // min_size: (self.mod_info.tables[table_index].size as i64).into(), + bound_gv: new_table_bounds, + element_size: (ptr_size as i64).into(), + index_type: I32 + }); + + 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.module.signatures[index].clone()) + 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 Module { + 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(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(()) + } +}