From 9af9e75bfd152a1b06bf4b57c7566e6eb1a2b3ad Mon Sep 17 00:00:00 2001 From: Steve Akinyemi Date: Mon, 31 Dec 2018 14:20:05 +0100 Subject: [PATCH] Add signatures conversion --- src/compilers/cranelift/codegen.rs | 311 ++++++++++++++++------------- src/compilers/cranelift/mod.rs | 8 +- src/webassembly/mod.rs | 2 +- 3 files changed, 178 insertions(+), 143 deletions(-) diff --git a/src/compilers/cranelift/codegen.rs b/src/compilers/cranelift/codegen.rs index 6a14e00d0..f4de19301 100644 --- a/src/compilers/cranelift/codegen.rs +++ b/src/compilers/cranelift/codegen.rs @@ -1,3 +1,17 @@ +use crate::runtime::{ + backend::FuncResolver, + memory::LinearMemory, + module::{DataInitializer, Export, ImportName, Module as WasmerModule, TableInitializer}, + types::{ + ElementType as WasmerElementType, FuncIndex as WasmerFuncIndex, FuncSig as WasmerSignature, + Global as WasmerGlobal, GlobalDesc as WasmerGlobalDesc, GlobalIndex as WasmerGlobalIndex, + Initializer as WasmerInitializer, Map, MapIndex, Memory as WasmerMemory, + MemoryIndex as WasmerMemoryIndex, SigIndex as WasmerSignatureIndex, Table as WasmerTable, + TableIndex as WasmerTableIndex, Type as WasmerType, + }, + vm::{self, Ctx as WasmerVMContext}, +}; +use crate::webassembly::errors::ErrorKind; use cranelift_codegen::cursor::FuncCursor; use cranelift_codegen::ir::immediates::{Offset32, Uimm64}; use cranelift_codegen::ir::types::{self, *}; @@ -5,81 +19,63 @@ use cranelift_codegen::ir::{ self, AbiParam, ArgumentPurpose, ExtFuncData, ExternalName, FuncRef, InstBuilder, Signature, TrapCode, }; -use cranelift_codegen::isa::{self, CallConv, TargetFrontendConfig}; -use cranelift_codegen::settings::{self, Configurable}; +use cranelift_codegen::isa::TargetFrontendConfig; use cranelift_entity::{EntityRef, PrimaryMap}; use cranelift_wasm::{ translate_module, DefinedFuncIndex, FuncEnvironment as FuncEnvironmentTrait, FuncIndex, FuncTranslator, Global, GlobalIndex, GlobalVariable, Memory, MemoryIndex, ModuleEnvironment, ReturnMode, SignatureIndex, Table, TableIndex, WasmResult, }; +use hashbrown::HashMap; use std::ptr::NonNull; use target_lexicon; -use crate::webassembly::errors::ErrorKind; -use crate::runtime::{ - module::{DataInitializer, Export, ImportName, Module as WasmerModule, TableInitializer}, - types::{ - Type as WasmerType, - FuncIndex as WasmerFuncIndex, - GlobalIndex as WasmerGlobalIndex, - Global as WasmerGlobal, - GlobalDesc as WasmerGlobalDesc, - MemoryIndex as WasmerMemoryIndex, - Memory as WasmerMemory, - Table as WasmerTable, - TableIndex as WasmerTableIndex, - Initializer as WasmerInitializer, - ElementType as WasmerElementType, - FuncSig as WasmerSignature, - SigIndex as WasmerSignatureIndex, - MapIndex, - Map, - }, - vm::{ - self, - Ctx as WasmerVMContext, - }, - memory::{ - LinearMemory, - }, - backend::{ - FuncResolver, - } -}; -use hashbrown::HashMap; /// The converter namespace contains functions for converting a Cranelift module -/// to a wasmer module. +/// to a Wasmer module. pub mod converter { use super::*; - /// Converts a Cranelift module to a wasmer module. + /// Converts a Cranelift module to a Wasmer module. pub fn convert_module(cranelift_module: CraneliftModule) -> WasmerModule { - // Convert Cranelift globals to wasmer globals - let mut globals: Map = Map::with_capacity(cranelift_module.globals.len()); + // Convert Cranelift globals to Wasmer globals + let mut globals: Map = + Map::with_capacity(cranelift_module.globals.len()); for global in cranelift_module.globals { globals.push(convert_global(global)); } - // Convert Cranelift memories to wasmer memories. - let mut memories: Map = Map::with_capacity(cranelift_module.memories.len()); + // Convert Cranelift memories to Wasmer memories. + let mut memories: Map = + Map::with_capacity(cranelift_module.memories.len()); for memory in cranelift_module.memories { memories.push(convert_memory(memory)); } - // Convert Cranelift tables to wasmer tables. - let mut tables: Map = Map::with_capacity(cranelift_module.tables.len()); + // Convert Cranelift tables to Wasmer tables. + let mut tables: Map = + Map::with_capacity(cranelift_module.tables.len()); for table in cranelift_module.tables { tables.push(convert_table(table)); } - // TODO: signatures, signatures_assoc, func_resolver - let signatures_len = cranelift_module.signatures.len(); - let signatures: Map = Map::with_capacity(signatures_len); - let signature_assoc: Map = Map::with_capacity(signatures_len); - let func_resolver = cranelift_module.func_resolver.unwrap(); + // Convert Cranelift signatures to Wasmer signatures. + let mut signatures: Map = + Map::with_capacity(cranelift_module.signatures.len()); + for signature in cranelift_module.signatures { + signatures.push(convert_signature(signature)); + } - // Get other fields directly from the cranelift_module. + // Convert Cranelift signature indices to Wasmer signature indices. + let mut signature_assoc: Map = + Map::with_capacity(cranelift_module.functions.len()); + for (_, signature_index) in cranelift_module.functions.iter() { + signature_assoc.push(WasmerSignatureIndex::new(signature_index.index())); + } + + // Create func_resolver. + let func_resolver = Box::new(CraneliftFunctionResolver::new()); + + // Get other fields from the cranelift_module. let CraneliftModule { imported_functions, imported_memories, @@ -92,7 +88,7 @@ pub mod converter { .. } = cranelift_module; - // Create wasmer module from data above + // Create Wasmer module from data above WasmerModule { func_resolver, memories, @@ -111,7 +107,7 @@ pub mod converter { } } - /// Converts from Cranelift type to a wasmer type. + /// Converts from Cranelift type to a Wasmer type. pub fn convert_type(ty: types::Type) -> WasmerType { match ty { I32 => WasmerType::I32, @@ -122,7 +118,7 @@ pub mod converter { } } - /// Converts a Cranelift global to a wasmer global. + /// Converts a Cranelift global to a Wasmer global. pub fn convert_global(global: Global) -> WasmerGlobal { let desc = WasmerGlobalDesc { mutable: global.mutability, @@ -138,17 +134,16 @@ pub mod converter { I64Const(val) => Const(val.into()), F32Const(val) => Const((val as f32).into()), F64Const(val) => Const((val as f64).into()), - GlobalInit::GetGlobal(index) => - WasmerInitializer::GetGlobal( - WasmerGlobalIndex::new(index.index()) - ), + GlobalInit::GetGlobal(index) => { + WasmerInitializer::GetGlobal(WasmerGlobalIndex::new(index.index())) + } Import => unimplemented!("TODO: imported globals are not supported yet!"), }; - WasmerGlobal {desc, init} + WasmerGlobal { desc, init } } - /// Converts a Cranelift table to a wasmer table. + /// Converts a Cranelift table to a Wasmer table. pub fn convert_table(table: Table) -> WasmerTable { use cranelift_wasm::TableElementType::*; @@ -164,7 +159,7 @@ pub mod converter { } } - /// Converts a Cranelift table to a wasmer table. + /// Converts a Cranelift table to a Wasmer table. pub fn convert_memory(memory: Memory) -> WasmerMemory { WasmerMemory { shared: memory.shared, @@ -173,15 +168,19 @@ pub mod converter { } } - /// Converts a Cranelift signature to a wasmer signature. + /// Converts a Cranelift signature to a Wasmer signature. pub fn convert_signature(sig: ir::Signature) -> WasmerSignature { WasmerSignature { - params: sig.params.iter().map( - |param| convert_type(param.value_type) - ).collect(), - returns: sig.returns.iter().map( - |ret| convert_type(ret.value_type) - ).collect(), + params: sig + .params + .iter() + .map(|param| convert_type(param.value_type)) + .collect(), + returns: sig + .returns + .iter() + .map(|ret| convert_type(ret.value_type)) + .collect(), } } } @@ -215,7 +214,7 @@ pub struct CraneliftModule { /// The external function declaration for implementing wasm's `grow_memory`. pub grow_memory_extfunc: Option, - /// A function that takes a wasmer module and resolves a function index to a vm::Func. + /// A function that takes a Wasmer module and resolves a function index to a vm::Func. pub func_resolver: Option>, // An array holding information about the wasm instance memories. @@ -252,11 +251,10 @@ pub struct CraneliftModule { pub start_func: Option, } -/// impl CraneliftModule { /// Translates wasm bytes into a Cranelift module pub fn from_bytes( - buffer_source: Vec, + buffer_source: &Vec, config: TargetFrontendConfig, ) -> Result { // Create a cranelift module @@ -270,7 +268,7 @@ impl CraneliftModule { memories_base: None, current_memory_extfunc: None, grow_memory_extfunc: None, - func_resolver: Some(Box::new(MockFuncResolver {})), + func_resolver: None, memories: Vec::new(), globals: Vec::new(), tables: Vec::new(), @@ -291,10 +289,21 @@ impl CraneliftModule { // Return translated module. Ok(cranelift_module) } +} - /// Creates a new `FuncEnvironment` for the module. - fn func_env(&self) -> FuncEnvironment { - FuncEnvironment::new(&self) +// Resolves a function index to a function address. +pub struct CraneliftFunctionResolver {} + +impl CraneliftFunctionResolver { + fn new() -> Self { + Self {} + } +} + +// Implements FuncResolver trait. +impl FuncResolver for CraneliftFunctionResolver { + fn get(&self, module: &WasmerModule, index: WasmerFuncIndex) -> Option> { + None } } @@ -332,6 +341,7 @@ impl<'environment> FuncEnvironmentTrait for FuncEnvironment<'environment> { } /// Gets native pointers types. + /// /// `I64` on 64-bit arch; `I32` on 32-bit arch. fn pointer_type(&self) -> ir::Type { ir::Type::int(u16::from(self.module.config.pointer_bits())).unwrap() @@ -377,11 +387,8 @@ impl<'environment> FuncEnvironmentTrait for FuncEnvironment<'environment> { /// /// The index space covers both imported and locally declared memories. fn make_heap(&mut self, func: &mut ir::Function, index: MemoryIndex) -> ir::Heap { - debug_assert_eq!( - index.index(), - 0, - "non-default memories not supported yet" - ); + // Only the first memory is supported for now. + debug_assert_eq!(index.index(), 0, "non-default memories not supported yet"); // Create VMContext value. let vmctx = func.create_global_value(ir::GlobalValueData::VMContext); @@ -425,11 +432,8 @@ impl<'environment> FuncEnvironmentTrait for FuncEnvironment<'environment> { /// /// The index space covers both imported and locally declared tables. fn make_table(&mut self, func: &mut ir::Function, index: TableIndex) -> ir::Table { - debug_assert_eq!( - index.index(), - 0, - "non-default tables not supported yet" - ); + // Only the first table is supported for now. + debug_assert_eq!(index.index(), 0, "non-default tables not supported yet"); // Create VMContext value. let vmctx = func.create_global_value(ir::GlobalValueData::VMContext); @@ -474,10 +478,12 @@ impl<'environment> FuncEnvironmentTrait for FuncEnvironment<'environment> { }) } - /// Sets up a signature definition in `func`'s preamble + /// Sets up a signature definition in `func`'s preamble. + /// /// Signature may contain additional argument, but arguments marked as ArgumentPurpose::Normal` /// must correspond to the arguments in the wasm signature fn make_indirect_sig(&mut self, func: &mut ir::Function, index: SignatureIndex) -> ir::SigRef { + // Create a signature reference out of specified signature (with VMContext param added). func.import_signature(self.generate_signature(index)) } @@ -486,9 +492,16 @@ impl<'environment> FuncEnvironmentTrait for FuncEnvironment<'environment> { /// /// The index space covers both imported functions and functions defined in the current module. fn make_direct_func(&mut self, func: &mut ir::Function, index: FuncIndex) -> ir::FuncRef { + // Get signature of function. let signature_index = self.module.get_func_type(index); + + // Create a signature reference from specified signature (with VMContext param added). let signature = func.import_signature(self.generate_signature(signature_index)); + + // Get name of function. let name = ExternalName::user(0, index.as_u32()); + + // Create function reference from fuction data. func.import_function(ir::ExtFuncData { name, signature, @@ -496,10 +509,12 @@ impl<'environment> FuncEnvironmentTrait for FuncEnvironment<'environment> { }) } - /// Generates an indirect call IR with `callee` and `call_args` + /// Generates an indirect call IR with `callee` and `call_args`. + /// /// Inserts instructions at `pos` to the function `callee` in the table /// `table_index` with WebAssembly signature `sig_index` - #[cfg_attr(feature = "cargo-clippy", allow(too_many_arguments))] + /// TODO: Generate bounds checking code. + #[cfg_attr(feature = "cargo-clippy", allow(clippy::too_many_arguments))] fn translate_call_indirect( &mut self, mut pos: FuncCursor, @@ -510,29 +525,30 @@ impl<'environment> FuncEnvironmentTrait for FuncEnvironment<'environment> { callee: ir::Value, call_args: &[ir::Value], ) -> WasmResult { - // Pass the current function's vmctx parameter on to the callee. + // Create a VMContext value. let vmctx = pos .func .special_param(ir::ArgumentPurpose::VMContext) - .expect("Missing vmctx parameter"); + .expect("missing vmctx parameter"); + + // Get the pointer type based on machine's pointer size. + let ptr_type = self.pointer_type(); // 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_type = self.pointer_type(); + // Set callee to an appropriate type based on machine's pointer size. let callee_offset = if ptr_type == I32 { callee } else { pos.ins().uextend(ptr_type, callee) }; - // let entry_size = native_pointer_size() as i64 * 2; - // let callee_scaled = pos.ins().imul_imm(callee_offset, entry_size); + // The `callee` value is an index into a table of function pointers. let entry_addr = pos.ins().table_addr(ptr_type, table, callee_offset, 0); let mut mflags = ir::MemFlags::new(); mflags.set_notrap(); mflags.set_aligned(); + let func_ptr = pos.ins().load(ptr_type, mflags, entry_addr, 0); pos.ins().trapz(func_ptr, TrapCode::IndirectCallToNull); @@ -560,12 +576,16 @@ impl<'environment> FuncEnvironmentTrait for FuncEnvironment<'environment> { callee: ir::FuncRef, call_args: &[ir::Value], ) -> WasmResult { + // Insert call instructions for `callee`. Ok(pos.ins().call(callee, call_args)) } - /// Generates code corresponding to wasm `memory.grow` + /// Generates code corresponding to wasm `memory.grow`. + /// /// `index` refers to the linear memory to query. + /// /// `heap` refers to the IR generated by `make_heap`. + /// /// `val` refers the value to grow the memory by. fn translate_memory_grow( &mut self, @@ -574,96 +594,114 @@ impl<'environment> FuncEnvironmentTrait for FuncEnvironment<'environment> { heap: ir::Heap, val: ir::Value, ) -> WasmResult { + // Only the first memory is supported for now. let grow_mem_func = self.module.grow_memory_extfunc.unwrap_or_else(|| { - let sig_ref = pos.func.import_signature(Signature { - call_conv: CallConv::SystemV, - // argument_bytes: None, + // Create signature reference from specified signature. + let signature_ref = pos.func.import_signature(Signature { + // Get the default calling convention of the isa. + call_conv: self.module.config.default_call_conv, + // Paramters types. params: vec![ - // Size + // Param for new size. AbiParam::new(I32), - // Memory index + // Param for memory index. AbiParam::new(I32), - // VMContext + // Param for VMcontext. AbiParam::special(self.pointer_type(), ArgumentPurpose::VMContext), ], + // Return type for previous memory size. returns: vec![AbiParam::new(I32)], }); + // Create function reference to a linked `grow_memory` function. pos.func.import_function(ExtFuncData { name: ExternalName::testcase("grow_memory"), - signature: sig_ref, + signature: signature_ref, colocated: false, }) }); - // self.mod_info.grow_memory_extfunc = Some(grow_mem_func); - let memory_index_value = pos.ins().iconst(I32, to_imm64(index.index())); - let vmctx = pos.func.special_param(ArgumentPurpose::VMContext).unwrap(); + // Create a memory index value. + let memory_index = pos.ins().iconst(I32, to_imm64(index.index())); - let call_inst = pos - .ins() - .call(grow_mem_func, &[val, memory_index_value, vmctx]); + // Create a VMContext value. + let vmctx = pos + .func + .special_param(ArgumentPurpose::VMContext) + .expect("missing vmctx parameter"); + // Insert call instructions for `grow_memory`. + let call_inst = pos.ins().call(grow_mem_func, &[val, memory_index, vmctx]); + + // Return value. Ok(*pos.func.dfg.inst_results(call_inst).first().unwrap()) } - /// Generates code corresponding to wasm `memory.size` + /// Generates code corresponding to wasm `memory.size`. + /// /// `index` refers to the linear memory to query. - /// `heap` refers to the IR generated by `make_heap` + /// + /// `heap` refers to the IR generated by `make_heap`. fn translate_memory_size( &mut self, mut pos: FuncCursor, index: MemoryIndex, heap: ir::Heap, ) -> WasmResult { - debug_assert_eq!( - index.index(), - 0, - "non-default memories not supported yet" - ); - + debug_assert_eq!(index.index(), 0, "non-default memories not supported yet"); + // Only the first memory is supported for now. let cur_mem_func = self.module.current_memory_extfunc.unwrap_or_else(|| { - let sig_ref = pos.func.import_signature(Signature { - call_conv: CallConv::SystemV, - // argument_bytes: None, + // Create signature reference from specified signature. + let signature_ref = pos.func.import_signature(Signature { + // Get the default calling convention of the isa. + call_conv: self.module.config.default_call_conv, + // Paramters types. params: vec![ - // The memory index + // Param for memory index. AbiParam::new(I32), - // The vmctx reference + // Param for VMcontext. AbiParam::special(self.pointer_type(), ArgumentPurpose::VMContext), - // AbiParam::special(I64, ArgumentPurpose::VMContext), ], + // Return type for current memory size. returns: vec![AbiParam::new(I32)], }); + // Create function reference to a linked `current_memory` function. pos.func.import_function(ExtFuncData { name: ExternalName::testcase("current_memory"), - signature: sig_ref, + signature: signature_ref, colocated: false, }) }); - // self.mod_info.current_memory_extfunc = cur_mem_func; - + // Create a memory index value. let memory_index_value = pos.ins().iconst(I32, to_imm64(index.index())); + + // Create a VMContext value. let vmctx = pos.func.special_param(ArgumentPurpose::VMContext).unwrap(); + // Insert call instructions for `current_memory`. let call_inst = pos.ins().call(cur_mem_func, &[memory_index_value, vmctx]); + + // Return value. Ok(*pos.func.dfg.inst_results(call_inst).first().unwrap()) } /// Generates code at the beginning of loops. + /// /// Currently not used. fn translate_loop_header(&mut self, _pos: FuncCursor) { // By default, don't emit anything. } /// Determines the type of return each function should have. - /// It normal returns for now. + /// + /// It is normal returns for now. fn return_mode(&self) -> ReturnMode { ReturnMode::NormalReturns } } + /// Convert a usize offset into a `Imm64` for an iadd_imm. fn to_imm64(offset: usize) -> ir::immediates::Imm64 { (offset as i64).into() @@ -678,7 +716,6 @@ impl<'data> ModuleEnvironment<'data> for CraneliftModule { /// Declares a function signature to the environment. fn declare_signature(&mut self, sig: &ir::Signature) { self.signatures.push(sig.clone()); - // TODO: push to signatures_assoc here. } /// Return the signature with the given index. @@ -698,9 +735,8 @@ impl<'data> ModuleEnvironment<'data> for CraneliftModule { self.functions.push(sig_index); // Add import names to list of imported functions - self.imported_functions.push( - (String::from(module), String::from(field)).into() - ); + self.imported_functions + .push((String::from(module), String::from(field)).into()); } /// Return the number of imported funcs. @@ -742,7 +778,7 @@ impl<'data> ModuleEnvironment<'data> for CraneliftModule { } /// Declares a table to the environment. - fn declare_table(&mut self, table: Table){ + fn declare_table(&mut self, table: Table) { // Add table ir to the list of tables self.tables.push(table); } @@ -775,9 +811,10 @@ impl<'data> ModuleEnvironment<'data> for CraneliftModule { table_index: WasmerTableIndex::new(table_index.index()), base, offset, - elements: elements.iter().map( - |index| WasmerFuncIndex::new(index.index()) - ).collect(), + elements: elements + .iter() + .map(|index| WasmerFuncIndex::new(index.index())) + .collect(), }); } @@ -874,8 +911,7 @@ impl<'data> ModuleEnvironment<'data> for CraneliftModule { let mut function = ir::Function::with_name_signature(name, sig); // Complete function creation with translated function body. - FuncTranslator::new() - .translate(body_bytes, &mut function, &mut func_environ)?; + FuncTranslator::new().translate(body_bytes, &mut function, &mut func_environ)?; function }; @@ -886,10 +922,3 @@ impl<'data> ModuleEnvironment<'data> for CraneliftModule { Ok(()) } } - -struct MockFuncResolver {} -impl FuncResolver for MockFuncResolver { - fn get(&self, module: &WasmerModule, index: WasmerFuncIndex) -> Option> { - unimplemented!() - } -} diff --git a/src/compilers/cranelift/mod.rs b/src/compilers/cranelift/mod.rs index 99806905d..04d958bfd 100644 --- a/src/compilers/cranelift/mod.rs +++ b/src/compilers/cranelift/mod.rs @@ -10,6 +10,12 @@ use crate::webassembly; pub struct CraneliftCompiler {} +impl CraneliftCompiler { + pub fn new() -> Self { + Self {} + } +} + impl Compiler for CraneliftCompiler { // Compiles wasm binary to a wasmer module fn compile(&self, wasm: &[u8]) -> Result, String> { @@ -17,7 +23,7 @@ impl Compiler for CraneliftCompiler { let isa = webassembly::get_isa(); // Generate a Cranlift module from wasm binary - let cranelift_module = CraneliftModule::from_bytes(wasm.to_vec(), isa.frontend_config()) + let cranelift_module = CraneliftModule::from_bytes(&wasm.to_vec(), isa.frontend_config()) .map_err(|err| format!("{}", err))?; // Convert Cranelift module to wasmer module diff --git a/src/webassembly/mod.rs b/src/webassembly/mod.rs index 834859e14..8b032a06f 100644 --- a/src/webassembly/mod.rs +++ b/src/webassembly/mod.rs @@ -65,7 +65,7 @@ pub fn instantiate( debug!("webassembly - creating instance"); //let instance = Instance::new(&module, import_object, options)?; - let instance = runtime::instantiate(buffer_source, &CraneliftCompiler {}, import_object) + let instance = runtime::instantiate(buffer_source, &CraneliftCompiler::new(), import_object) .map_err(|e| ErrorKind::CompileError(e))?; let isa = get_isa();