diff --git a/cranelift/src/wasm.rs b/cranelift/src/wasm.rs index 87efd7d629..0dda98b524 100644 --- a/cranelift/src/wasm.rs +++ b/cranelift/src/wasm.rs @@ -5,7 +5,7 @@ //! IL. Can also executes the `start` function of the module by laying out the memories, globals //! and tables, then emitting the translated code with hardcoded addresses to memory. -use cton_wasm::{translate_module, FunctionTranslation, DummyRuntime, WasmRuntime}; +use cton_wasm::{translate_module, DummyRuntime, WasmRuntime}; use std::path::PathBuf; use cretonne::loop_analysis::LoopAnalysis; use cretonne::flowgraph::ControlFlowGraph; @@ -149,13 +149,9 @@ fn handle_module( vprint!(flag_verbose, "Checking... "); terminal.reset().unwrap(); for func in &translation.functions { - let il = match *func { - FunctionTranslation::Import() => continue, - FunctionTranslation::Code { ref il, .. } => il.clone(), - }; - match verifier::verify_function(&il, None) { + match verifier::verify_function(func, None) { Ok(()) => (), - Err(err) => return Err(pretty_verifier_error(&il, None, err)), + Err(err) => return Err(pretty_verifier_error(func, None, err)), } } terminal.fg(term::color::GREEN).unwrap(); @@ -167,10 +163,7 @@ fn handle_module( vprint!(flag_verbose, "Optimizing... "); terminal.reset().unwrap(); for func in &translation.functions { - let mut il = match *func { - FunctionTranslation::Import() => continue, - FunctionTranslation::Code { ref il, .. } => il.clone(), - }; + let mut il = func.clone(); let mut loop_analysis = LoopAnalysis::new(); let mut cfg = ControlFlowGraph::new(); cfg.compute(&il); diff --git a/lib/wasm/src/code_translator.rs b/lib/wasm/src/code_translator.rs index ee3c6c6f57..59bb306817 100644 --- a/lib/wasm/src/code_translator.rs +++ b/lib/wasm/src/code_translator.rs @@ -21,40 +21,19 @@ //! //! That is why `translate_function_body` takes an object having the `WasmRuntime` trait as //! argument. -use cretonne::ir::{self, Function, Signature, Type, InstBuilder, FunctionName, Ebb, FuncRef, - SigRef, ExtFuncData, MemFlags}; +use cretonne::ir::{self, Function, Signature, Type, InstBuilder, FunctionName, Ebb, MemFlags}; use cretonne::ir::types::*; use cretonne::ir::immediates::{Ieee32, Ieee64}; use cretonne::ir::condcodes::{IntCC, FloatCC}; use cton_frontend::{ILBuilder, FunctionBuilder}; use wasmparser::{Parser, ParserState, Operator, WasmDecoder, MemoryImmediate}; use translation_utils::{f32_translation, f64_translation, type_to_type, translate_type, Local, - FunctionIndex, SignatureIndex}; + FunctionIndex}; use state::{TranslationState, ControlStackFrame}; use std::collections::HashMap; use runtime::{FuncEnvironment, GlobalValue, WasmRuntime}; use std::u32; - -/// Holds mappings between the function and signatures indexes in the Wasm module and their -/// references as imports of the Cretonne IL function. -#[derive(Clone, Debug)] -pub struct FunctionImports { - /// Mappings index in function index space -> index in function local imports - pub functions: HashMap, - /// Mappings index in signature index space -> index in signature local imports - pub signatures: HashMap, -} - -impl FunctionImports { - fn new() -> Self { - Self { - functions: HashMap::new(), - signatures: HashMap::new(), - } - } -} - /// Returns a well-formed Cretonne IL function from a wasm function body and a signature. pub fn translate_function_body( parser: &mut Parser, @@ -62,11 +41,9 @@ pub fn translate_function_body( sig: &Signature, locals: &[(usize, Type)], exports: &Option>, - signatures: &[Signature], - functions: &[SignatureIndex], il_builder: &mut ILBuilder, runtime: &mut WasmRuntime, -) -> Result<(Function, FunctionImports), String> { +) -> Result { runtime.next_function(); // First we build the Function object with its name and signature let mut func = Function::new(); @@ -77,7 +54,6 @@ pub fn translate_function_body( func.name = FunctionName::new(name.clone()); } } - let mut func_imports = FunctionImports::new(); // We introduce an arbitrary scope for the FunctionBuilder object { let mut builder = FunctionBuilder::new(&mut func, il_builder); @@ -121,16 +97,7 @@ pub fn translate_function_body( if state.in_unreachable_code() { translate_unreachable_operator(op, &mut builder, &mut state) } else { - translate_operator( - op, - &mut builder, - runtime, - &mut state, - functions, - signatures, - exports, - &mut func_imports, - ) + translate_operator(op, &mut builder, &mut state, runtime) } } @@ -160,7 +127,7 @@ pub fn translate_function_body( state.stack.truncate(cut_index); } } - Ok((func, func_imports)) + Ok(func) } /// Translates wasm operators into Cretonne IL instructions. Returns `true` if it inserted @@ -168,12 +135,8 @@ pub fn translate_function_body( fn translate_operator( op: &Operator, builder: &mut FunctionBuilder, - runtime: &mut WasmRuntime, state: &mut TranslationState, - functions: &[SignatureIndex], - signatures: &[Signature], - exports: &Option>, - func_imports: &mut FunctionImports, + runtime: &mut WasmRuntime, ) { // This big match treats all Wasm code operators. match *op { @@ -465,25 +428,13 @@ fn translate_operator( * argument referring to an index in the external functions table of the module. ************************************************************************************/ Operator::Call { function_index } => { - let args_num = args_count(function_index as usize, functions, signatures); - let cut_index = state.stack.len() - args_num; - let internal_function_index = find_function_import( - function_index as usize, - builder, - func_imports, - functions, - exports, - signatures, - ); - let call_inst = builder.ins().call( - internal_function_index, - &state.stack[cut_index..], - ); - state.stack.truncate(cut_index); + let (fref, num_args) = state.get_direct_func(builder.func, function_index, runtime); + // TODO: Let the function environment override the call instruction. It may want to add + // arguments. + let call_inst = builder.ins().call(fref, &state.peekn(num_args)); + state.popn(num_args); let ret_values = builder.inst_results(call_inst); - for val in ret_values { - state.push1(*val); - } + state.pushn(ret_values); } Operator::CallIndirect { index, @@ -1098,60 +1049,3 @@ fn translate_store( base, ); } -fn args_count( - index: FunctionIndex, - functions: &[SignatureIndex], - signatures: &[Signature], -) -> usize { - signatures[functions[index]].argument_types.len() -} - -// Given an index in the function index space, search for it in the function imports and if it is -// not there add it to the function imports. -fn find_function_import( - index: FunctionIndex, - builder: &mut FunctionBuilder, - func_imports: &mut FunctionImports, - functions: &[SignatureIndex], - exports: &Option>, - signatures: &[Signature], -) -> FuncRef { - if let Some(local_index) = func_imports.functions.get(&index) { - return *local_index; - } - // We have to import the function - let sig_index = functions[index]; - if let Some(local_sig_index) = func_imports.signatures.get(&sig_index) { - let local_func_index = builder.import_function(ExtFuncData { - name: match *exports { - None => FunctionName::new(""), - Some(ref exports) => { - match exports.get(&index) { - None => FunctionName::new(""), - Some(name) => FunctionName::new(name.clone()), - } - } - }, - signature: *local_sig_index, - }); - func_imports.functions.insert(index, local_func_index); - return local_func_index; - } - // We have to import the signature - let sig_local_index = builder.import_signature(signatures[sig_index].clone()); - func_imports.signatures.insert(sig_index, sig_local_index); - let local_func_index = builder.import_function(ExtFuncData { - name: match *exports { - None => FunctionName::new(""), - Some(ref exports) => { - match exports.get(&index) { - None => FunctionName::new(""), - Some(name) => FunctionName::new(name.clone()), - } - } - }, - signature: sig_local_index, - }); - func_imports.functions.insert(index, local_func_index); - local_func_index -} diff --git a/lib/wasm/src/lib.rs b/lib/wasm/src/lib.rs index bb750572a1..22fb1c9291 100644 --- a/lib/wasm/src/lib.rs +++ b/lib/wasm/src/lib.rs @@ -23,8 +23,7 @@ mod sections_translator; mod state; mod translation_utils; -pub use module_translator::{translate_module, TranslationResult, FunctionTranslation, - ImportMappings}; -pub use runtime::{WasmRuntime, DummyRuntime}; +pub use module_translator::{translate_module, TranslationResult}; +pub use runtime::{FuncEnvironment, WasmRuntime, DummyRuntime}; pub use translation_utils::{Local, FunctionIndex, GlobalIndex, TableIndex, MemoryIndex, RawByte, MemoryAddress, SignatureIndex, Global, GlobalInit, Table, Memory}; diff --git a/lib/wasm/src/module_translator.rs b/lib/wasm/src/module_translator.rs index c2346e52b3..113330d4cf 100644 --- a/lib/wasm/src/module_translator.rs +++ b/lib/wasm/src/module_translator.rs @@ -5,8 +5,8 @@ use sections_translator::{SectionParsingError, parse_function_signatures, parse_ parse_function_section, parse_export_section, parse_memory_section, parse_global_section, parse_table_section, parse_elements_section, parse_data_section}; -use translation_utils::{type_to_type, Import, SignatureIndex, FunctionIndex, invert_hashmaps}; -use cretonne::ir::{Function, Type, FuncRef, SigRef}; +use translation_utils::{type_to_type, Import, SignatureIndex, FunctionIndex}; +use cretonne::ir::{Function, Type}; use code_translator::translate_function_body; use cton_frontend::ILBuilder; use std::collections::HashMap; @@ -15,45 +15,14 @@ use runtime::WasmRuntime; /// Output of the [`translate_module`](fn.translate_module.html) function. pub struct TranslationResult { /// The translated functions. - pub functions: Vec, + pub functions: Vec, /// When present, the index of the function defined as `start` of the module. + /// + /// Note that this is a WebAssembly function index and not an index into the `functions` vector + /// above. The imported functions are numbered before the local functions. pub start_index: Option, } -/// A function in a WebAssembly module can be either imported, or defined inside it. -#[derive(Clone)] -pub enum FunctionTranslation { - /// A function defined inside the WebAssembly module. - Code { - /// The translation in Cretonne IL. - il: Function, - /// The mappings between Cretonne imports and indexes in the function index space. - imports: ImportMappings, - }, - /// An imported function. - Import(), -} - -#[derive(Clone, Debug)] -/// Mappings describing the relations between imports of the Cretonne IL functions and the -/// functions in the WebAssembly module. -pub struct ImportMappings { - /// Find the index of a function in the WebAssembly module thanks to a `FuncRef`. - pub functions: HashMap, - /// Find the index of a signature in the WebAssembly module thanks to a `SigRef`. - pub signatures: HashMap, -} - -impl ImportMappings { - /// Create a new empty `ImportMappings`. - pub fn new() -> Self { - Self { - functions: HashMap::new(), - signatures: HashMap::new(), - } - } -} - /// Translate a sequence of bytes forming a valid Wasm binary into a list of valid Cretonne IL /// [`Function`](../cretonne/ir/function/struct.Function.html). /// Returns the functions and also the mappings for imported functions and signature between the @@ -76,7 +45,6 @@ pub fn translate_module( let mut exports: Option> = None; let mut next_input = ParserInput::Default; let mut function_index: FunctionIndex = 0; - let mut function_imports_count = 0; let mut start_index: Option = None; loop { match *parser.read_with_input(next_input) { @@ -90,7 +58,7 @@ pub fn translate_module( next_input = ParserInput::Default; } ParserState::BeginSection { code: SectionCode::Import, .. } => { - match parse_import_section(&mut parser) { + match parse_import_section(&mut parser, runtime) { Ok(imps) => { for import in imps { match import { @@ -121,11 +89,10 @@ pub fn translate_module( return Err(format!("wrong content in the import section: {}", s)) } } - function_imports_count = function_index; next_input = ParserInput::Default; } ParserState::BeginSection { code: SectionCode::Function, .. } => { - match parse_function_section(&mut parser) { + match parse_function_section(&mut parser, runtime) { Ok(funcs) => { match functions { None => functions = Some(funcs), @@ -233,8 +200,7 @@ pub fn translate_module( None => return Err(String::from("missing a function section")), Some(functions) => functions, }; - let mut il_functions: Vec = Vec::new(); - il_functions.resize(function_imports_count, FunctionTranslation::Import()); + let mut il_functions: Vec = Vec::new(); let mut il_builder = ILBuilder::new(); runtime.begin_translation(); loop { @@ -263,17 +229,10 @@ pub fn translate_module( &signature, &locals, &exports, - &signatures, - &functions, &mut il_builder, runtime, ) { - Ok((il_func, imports)) => { - il_functions.push(FunctionTranslation::Code { - il: il_func, - imports: invert_hashmaps(&imports), - }) - } + Ok(il_func) => il_functions.push(il_func), Err(s) => return Err(s), } function_index += 1; diff --git a/lib/wasm/src/runtime/dummy.rs b/lib/wasm/src/runtime/dummy.rs index 7ff9747dc6..1bb48257c9 100644 --- a/lib/wasm/src/runtime/dummy.rs +++ b/lib/wasm/src/runtime/dummy.rs @@ -12,6 +12,12 @@ pub struct DummyRuntime { // Unprocessed signatures exactly as provided by `declare_signature()`. signatures: Vec, globals: Vec, + + // Types of functions, imported and local. + func_types: Vec, + + // Names of imported functions. + imported_funcs: Vec, } impl DummyRuntime { @@ -20,6 +26,8 @@ impl DummyRuntime { Self { signatures: Vec::new(), globals: Vec::new(), + func_types: Vec::new(), + imported_funcs: Vec::new(), } } } @@ -53,6 +61,20 @@ impl FuncEnvironment for DummyRuntime { // signature index arguments. func.dfg.signatures.push(self.signatures[index].clone()) } + + fn make_direct_func(&self, func: &mut ir::Function, index: FunctionIndex) -> ir::FuncRef { + let sigidx = self.func_types[index]; + // A real implementation would probably add a `vmctx` argument. + // And maybe attempt some signature de-duplication. + let signature = func.dfg.signatures.push(self.signatures[sigidx].clone()); + + let name = match self.imported_funcs.get(index) { + Some(name) => name.clone(), + None => ir::FunctionName::new(format!("localfunc{}", index)), + }; + + func.dfg.ext_funcs.push(ir::ExtFuncData { name, signature }) + } } impl WasmRuntime for DummyRuntime { @@ -77,6 +99,25 @@ impl WasmRuntime for DummyRuntime { self.signatures.push(sig.clone()); } + fn declare_func_import(&mut self, sig_index: SignatureIndex, module: &[u8], field: &[u8]) { + assert_eq!( + self.func_types.len(), + self.imported_funcs.len(), + "Imported functions must be declared first" + ); + self.func_types.push(sig_index); + + let mut name = Vec::new(); + name.extend(module.iter().cloned().map(name_fold)); + name.push(b'_'); + name.extend(field.iter().cloned().map(name_fold)); + self.imported_funcs.push(ir::FunctionName::new(name)); + } + + fn declare_func_type(&mut self, sig_index: SignatureIndex) { + self.func_types.push(sig_index); + } + fn declare_global(&mut self, global: Global) { self.globals.push(global); } @@ -106,3 +147,12 @@ impl WasmRuntime for DummyRuntime { // We do nothing } } + +// Generate characters suitable for printable `FuncName`s. +fn name_fold(c: u8) -> u8 { + if (c as char).is_alphanumeric() { + c + } else { + b'_' + } +} diff --git a/lib/wasm/src/runtime/spec.rs b/lib/wasm/src/runtime/spec.rs index 3dee0833bf..b261181491 100644 --- a/lib/wasm/src/runtime/spec.rs +++ b/lib/wasm/src/runtime/spec.rs @@ -51,6 +51,19 @@ pub trait FuncEnvironment { /// The signature will only be used for indirect calls, even if the module has direct function /// calls with the same WebAssembly type. fn make_indirect_sig(&self, func: &mut ir::Function, index: SignatureIndex) -> ir::SigRef; + + /// Set up an external function definition in the preamble of `func` that can be used to + /// directly call the function `index`. + /// + /// The index space covers both imported functions and functions defined in the current module. + /// + /// The function's signature may contain additional arguments needed for a direct call, but the + /// arguments marked as `ArgumentPurpose::Normal` must correspond to the WebAssembly signature + /// arguments. + /// + /// The function's signature will only be used for direct calls, even if the module has + /// indirect calls with the same WebAssembly type. + fn make_direct_func(&self, func: &mut ir::Function, index: FunctionIndex) -> ir::FuncRef; } /// An object satisfyng the `WasmRuntime` trait can be passed as argument to the @@ -60,6 +73,12 @@ pub trait WasmRuntime: FuncEnvironment { /// Declares a function signature to the runtime. fn declare_signature(&mut self, sig: &ir::Signature); + /// Declares a function import to the runtime. + fn declare_func_import(&mut self, sig_index: SignatureIndex, module: &[u8], field: &[u8]); + + /// Declares the type (signature) of a local function in the module. + fn declare_func_type(&mut self, sig_index: SignatureIndex); + /// Declares a global to the runtime. fn declare_global(&mut self, global: Global); /// Declares a table to the runtime. diff --git a/lib/wasm/src/sections_translator.rs b/lib/wasm/src/sections_translator.rs index 591146c716..8c8b7aac71 100644 --- a/lib/wasm/src/sections_translator.rs +++ b/lib/wasm/src/sections_translator.rs @@ -64,13 +64,21 @@ pub fn parse_function_signatures( } /// Retrieves the imports from the imports section of the binary. -pub fn parse_import_section(parser: &mut Parser) -> Result, SectionParsingError> { +pub fn parse_import_section( + parser: &mut Parser, + runtime: &mut WasmRuntime, +) -> Result, SectionParsingError> { let mut imports = Vec::new(); loop { match *parser.read() { ParserState::ImportSectionEntry { - ty: ImportSectionEntryType::Function(sig), .. - } => imports.push(Import::Function { sig_index: sig }), + ty: ImportSectionEntryType::Function(sig), + module, + field, + } => { + runtime.declare_func_import(sig as SignatureIndex, module, field); + imports.push(Import::Function { sig_index: sig }); + } ParserState::ImportSectionEntry { ty: ImportSectionEntryType::Memory(MemoryType { limits: ref memlimits }), .. } => { @@ -110,11 +118,15 @@ pub fn parse_import_section(parser: &mut Parser) -> Result, SectionP /// Retrieves the correspondances between functions and signatures from the function section pub fn parse_function_section( parser: &mut Parser, + runtime: &mut WasmRuntime, ) -> Result, SectionParsingError> { let mut funcs = Vec::new(); loop { match *parser.read() { - ParserState::FunctionSectionEntry(sigindex) => funcs.push(sigindex as SignatureIndex), + ParserState::FunctionSectionEntry(sigindex) => { + runtime.declare_func_type(sigindex as SignatureIndex); + funcs.push(sigindex as SignatureIndex); + } ParserState::EndSection => break, ref s => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), }; diff --git a/lib/wasm/src/state.rs b/lib/wasm/src/state.rs index 5747ceda72..cfe06e081a 100644 --- a/lib/wasm/src/state.rs +++ b/lib/wasm/src/state.rs @@ -6,7 +6,7 @@ use cretonne::ir::{self, Ebb, Inst, Type, Value}; use runtime::{FuncEnvironment, GlobalValue}; use std::collections::HashMap; -use translation_utils::{GlobalIndex, MemoryIndex, SignatureIndex}; +use translation_utils::{GlobalIndex, MemoryIndex, SignatureIndex, FunctionIndex}; /// A control stack frame can be an `if`, a `block` or a `loop`, each one having the following /// fields: @@ -118,6 +118,11 @@ pub struct TranslationState { // `FuncEnvironment::make_indirect_sig()`. // Stores both the signature reference and the number of WebAssembly arguments signatures: HashMap, + + // Imported and local functions that have been created by + // `FuncEnvironment::make_direct_func()`. + // Stores both the function reference and the number of WebAssembly arguments + functions: HashMap, } impl TranslationState { @@ -130,6 +135,7 @@ impl TranslationState { globals: HashMap::new(), heaps: HashMap::new(), signatures: HashMap::new(), + functions: HashMap::new(), } } @@ -141,6 +147,7 @@ impl TranslationState { self.globals.clear(); self.heaps.clear(); self.signatures.clear(); + self.functions.clear(); } /// Initialize the state for compiling a function with the given signature. @@ -291,17 +298,37 @@ impl TranslationState { index: u32, environ: &FE, ) -> (ir::SigRef, usize) { - let index = index as MemoryIndex; + let index = index as SignatureIndex; *self.signatures.entry(index).or_insert_with(|| { let sig = environ.make_indirect_sig(func, index); - // Count the number of normal arguments. - // The environment is allowed to add special purpose arguments to the signature. - let args = func.dfg.signatures[sig] - .argument_types - .iter() - .filter(|arg| arg.purpose == ir::ArgumentPurpose::Normal) - .count(); - (sig, args) + (sig, normal_args(&func.dfg.signatures[sig])) + }) + } + + /// Get the `FuncRef` reference that should be used to make a direct call to function + /// `index`. Also return the number of WebAssembly arguments in the signature. + /// + /// Create the function reference if necessary. + pub fn get_direct_func( + &mut self, + func: &mut ir::Function, + index: u32, + environ: &FE, + ) -> (ir::FuncRef, usize) { + let index = index as FunctionIndex; + *self.functions.entry(index).or_insert_with(|| { + let fref = environ.make_direct_func(func, index); + let sig = func.dfg.ext_funcs[fref].signature; + (fref, normal_args(&func.dfg.signatures[sig])) }) } } + +/// Count the number of normal arguments in a signature. +/// Exclude special-purpose arguments that represent runtime stuff and not WebAssembly arguments. +fn normal_args(sig: &ir::Signature) -> usize { + sig.argument_types + .iter() + .filter(|arg| arg.purpose == ir::ArgumentPurpose::Normal) + .count() +} diff --git a/lib/wasm/src/translation_utils.rs b/lib/wasm/src/translation_utils.rs index d378864e5c..e95a624f83 100644 --- a/lib/wasm/src/translation_utils.rs +++ b/lib/wasm/src/translation_utils.rs @@ -2,8 +2,6 @@ use wasmparser; use cretonne; use std::u32; -use code_translator; -use module_translator; /// Index of a function (imported or defined) inside the WebAssembly module. pub type FunctionIndex = usize; @@ -135,19 +133,3 @@ pub fn translate_type(ty: wasmparser::Type) -> Result, ( _ => panic!("unsupported return value type"), } } - -/// Inverts the key-value relation in the imports hashmap. Indeed, these hashmaps are built by -/// feeding the function indexes in the module but are used by the runtime with the `FuncRef` as -/// keys. -pub fn invert_hashmaps( - imports: &code_translator::FunctionImports, -) -> module_translator::ImportMappings { - let mut new_imports = module_translator::ImportMappings::new(); - for (func_index, func_ref) in &imports.functions { - new_imports.functions.insert(*func_ref, *func_index); - } - for (sig_index, sig_ref) in &imports.signatures { - new_imports.signatures.insert(*sig_ref, *sig_index); - } - new_imports -} diff --git a/lib/wasm/tests/testsuite.rs b/lib/wasm/tests/testsuite.rs index 91f514fde4..9e59b2bab7 100644 --- a/lib/wasm/tests/testsuite.rs +++ b/lib/wasm/tests/testsuite.rs @@ -1,7 +1,7 @@ extern crate cton_wasm; extern crate cretonne; -use cton_wasm::{translate_module, FunctionTranslation, DummyRuntime, WasmRuntime}; +use cton_wasm::{translate_module, DummyRuntime, WasmRuntime}; use std::path::PathBuf; use std::fs::File; use std::error::Error; @@ -69,14 +69,10 @@ fn handle_module(path: PathBuf) -> Result<(), String> { } } }; - for func in translation.functions { - let il = match func { - FunctionTranslation::Import() => continue, - FunctionTranslation::Code { ref il, .. } => il.clone(), - }; - match verifier::verify_function(&il, None) { + for func in &translation.functions { + match verifier::verify_function(func, None) { Ok(()) => (), - Err(err) => return Err(pretty_verifier_error(&il, None, err)), + Err(err) => return Err(pretty_verifier_error(func, None, err)), } } Ok(())