diff --git a/cranelift/src/wasm.rs b/cranelift/src/wasm.rs index 1c853efafa..f632a2162f 100644 --- a/cranelift/src/wasm.rs +++ b/cranelift/src/wasm.rs @@ -4,7 +4,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, DummyRuntime}; +use cton_wasm::{translate_module, DummyRuntime, WasmRuntime}; use std::path::PathBuf; use cretonne::Context; use cretonne::settings::FlagsOrIsa; @@ -113,7 +113,9 @@ fn handle_module( vprint!(flag_verbose, "Compiling... "); } terminal.reset().unwrap(); - for func in &translation.functions { + let num_func_imports = dummy_runtime.get_num_func_imports(); + for (def_index, func) in translation.functions.iter().enumerate() { + let func_index = num_func_imports + def_index; let mut context = Context::new(); context.func = func.clone(); if flag_check_translation { @@ -131,6 +133,14 @@ fn handle_module( } if flag_print { vprintln!(flag_verbose, ""); + if let Some(start_func) = dummy_runtime.start_func { + if func_index == start_func { + println!("; Selected as wasm start function"); + } + } + for export_name in &dummy_runtime.functions[func_index].export_names { + println!("; Exported as \"{}\"", export_name); + } println!("{}", context.func.display(fisa.isa)); vprintln!(flag_verbose, ""); } diff --git a/lib/wasm/src/module_translator.rs b/lib/wasm/src/module_translator.rs index 7aee73c974..434f2b0da3 100644 --- a/lib/wasm/src/module_translator.rs +++ b/lib/wasm/src/module_translator.rs @@ -5,10 +5,8 @@ use sections_translator::{SectionParsingError, parse_function_signatures, parse_ parse_function_section, parse_export_section, parse_start_section, parse_memory_section, parse_global_section, parse_table_section, parse_elements_section, parse_data_section}; -use translation_utils::FunctionIndex; -use cretonne::ir::{Function, FunctionName}; +use cretonne::ir::Function; use func_translator::FuncTranslator; -use std::collections::HashMap; use std::error::Error; use runtime::WasmRuntime; @@ -34,7 +32,6 @@ pub fn translate_module( } ref s => panic!("modules should begin properly: {:?}", s), } - let mut exports: HashMap = HashMap::new(); let mut next_input = ParserInput::Default; loop { match *parser.read_with_input(next_input) { @@ -92,8 +89,8 @@ pub fn translate_module( next_input = ParserInput::Default; } ParserState::BeginSection { code: SectionCode::Export, .. } => { - match parse_export_section(&mut parser) { - Ok(exps) => exports = exps, + match parse_export_section(&mut parser, runtime) { + Ok(()) => {} Err(SectionParsingError::WrongSectionContent(s)) => { return Err(format!("wrong content in the export section: {}", s)) } @@ -155,9 +152,7 @@ pub fn translate_module( func.signature = runtime .get_signature(runtime.get_func_type(function_index)) .clone(); - if let Some(name) = exports.get(&function_index) { - func.name = FunctionName::new(name.clone()); - } + func.name = runtime.get_name(function_index); trans .translate_from_reader(parser.create_binary_reader(), &mut func, runtime) .map_err(|e| String::from(e.description()))?; diff --git a/lib/wasm/src/runtime/dummy.rs b/lib/wasm/src/runtime/dummy.rs index 6c7a12c88a..09a985a087 100644 --- a/lib/wasm/src/runtime/dummy.rs +++ b/lib/wasm/src/runtime/dummy.rs @@ -6,25 +6,51 @@ use cretonne::ir::types::*; use cretonne::cursor::FuncCursor; use cretonne::settings; +/// 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(), + } + } +} + /// This runtime implementation is a "naïve" one, doing essentially nothing and emitting /// placeholders when forced to. Don't try to execute code translated with this runtime, it is /// essentially here for translation debug purposes. pub struct DummyRuntime { - // Unprocessed signatures exactly as provided by `declare_signature()`. - signatures: Vec, - globals: Vec, + /// Compilation setting flags. + pub flags: settings::Flags, - // Types of functions, imported and local. - func_types: Vec, + /// Signatures as provided by `declare_signature`. + pub signatures: Vec, - // Names of imported functions. - imported_funcs: Vec, + /// Module and field names of imported functions as provided by `declare_func_import`. + pub imported_funcs: Vec<(String, String)>, - // Compilation setting flags. - flags: settings::Flags, + /// Functions, imported and local. + pub functions: Vec>, - // The start function. - start_func: Option, + /// Tables as provided by `declare_table`. + pub tables: Vec>, + + /// Memories as provided by `declare_memory`. + pub memories: Vec>, + + /// Globals as provided by `declare_global`. + pub globals: Vec>, + + /// The start function. + pub start_func: Option, } impl DummyRuntime { @@ -36,11 +62,13 @@ impl DummyRuntime { /// Allocates the runtime data structures with the given flags. pub fn with_flags(flags: settings::Flags) -> Self { Self { - signatures: Vec::new(), - globals: Vec::new(), - func_types: Vec::new(), - imported_funcs: Vec::new(), flags, + signatures: Vec::new(), + imported_funcs: Vec::new(), + functions: Vec::new(), + tables: Vec::new(), + memories: Vec::new(), + globals: Vec::new(), start_func: None, } } @@ -57,7 +85,7 @@ impl FuncEnvironment for DummyRuntime { let gv = func.create_global_var(ir::GlobalVarData::VmCtx { offset }); GlobalValue::Memory { gv, - ty: self.globals[index].ty, + ty: self.globals[index].entity.ty, } } @@ -77,16 +105,11 @@ impl FuncEnvironment for DummyRuntime { } fn make_direct_func(&mut self, func: &mut ir::Function, index: FunctionIndex) -> ir::FuncRef { - let sigidx = self.func_types[index]; + let sigidx = self.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.signatures[sigidx].clone()); - - let name = match self.imported_funcs.get(index) { - Some(name) => name.clone(), - None => ir::FunctionName::new(format!("localfunc{}", index)), - }; - + let name = self.get_name(index); func.import_function(ir::ExtFuncData { name, signature }) } @@ -123,6 +146,10 @@ impl FuncEnvironment for DummyRuntime { } impl WasmRuntime for DummyRuntime { + fn get_name(&self, func_index: FunctionIndex) -> ir::FunctionName { + ir::FunctionName::new(format!("wasm_0x{:x}", func_index)) + } + fn declare_signature(&mut self, sig: &ir::Signature) { self.signatures.push(sig.clone()); } @@ -131,19 +158,17 @@ impl WasmRuntime for DummyRuntime { &self.signatures[sig_index] } - fn declare_func_import(&mut self, sig_index: SignatureIndex, module: &[u8], field: &[u8]) { + fn declare_func_import(&mut self, sig_index: SignatureIndex, module: &str, field: &str) { assert_eq!( - self.func_types.len(), + self.functions.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)); + self.functions.push(Exportable::new(sig_index)); + self.imported_funcs.push(( + String::from(module), + String::from(field), + )); } fn get_num_func_imports(&self) -> usize { @@ -151,40 +176,69 @@ impl WasmRuntime for DummyRuntime { } fn declare_func_type(&mut self, sig_index: SignatureIndex) { - self.func_types.push(sig_index); + self.functions.push(Exportable::new(sig_index)); } fn get_func_type(&self, func_index: FunctionIndex) -> SignatureIndex { - self.func_types[func_index] + self.functions[func_index].entity } fn declare_global(&mut self, global: Global) { - self.globals.push(global); + self.globals.push(Exportable::new(global)); } fn get_global(&self, global_index: GlobalIndex) -> &Global { - &self.globals[global_index] + &self.globals[global_index].entity } - fn declare_table(&mut self, _: Table) { - //We do nothing + fn declare_table(&mut self, table: Table) { + self.tables.push(Exportable::new(table)); } - fn declare_table_elements(&mut self, _: TableIndex, _: usize, _: &[FunctionIndex]) { - //We do nothing + fn declare_table_elements( + &mut self, + _table_index: TableIndex, + _offset: usize, + _elements: &[FunctionIndex], + ) { + // We do nothing } - fn declare_memory(&mut self, _: Memory) { - //We do nothing + fn declare_memory(&mut self, memory: Memory) { + self.memories.push(Exportable::new(memory)); } fn declare_data_initialization( &mut self, - _: MemoryIndex, - _: usize, - _: &[u8], + _memory_index: MemoryIndex, + _offset: usize, + _data: &[u8], ) -> Result<(), String> { // We do nothing Ok(()) } + fn declare_func_export(&mut self, func_index: FunctionIndex, name: &str) { + self.functions[func_index].export_names.push( + String::from(name), + ); + } + + fn declare_table_export(&mut self, table_index: TableIndex, name: &str) { + self.tables[table_index].export_names.push( + String::from(name), + ); + } + + fn declare_memory_export(&mut self, memory_index: MemoryIndex, name: &str) { + self.memories[memory_index].export_names.push( + String::from(name), + ); + } + + fn declare_global_export(&mut self, global_index: GlobalIndex, name: &str) { + self.globals[global_index].export_names.push( + String::from(name), + ); + } + fn declare_start_func(&mut self, func_index: FunctionIndex) { debug_assert!(self.start_func.is_none()); self.start_func = Some(func_index); @@ -197,12 +251,3 @@ 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 f914f642d4..0eb74de809 100644 --- a/lib/wasm/src/runtime/spec.rs +++ b/lib/wasm/src/runtime/spec.rs @@ -150,6 +150,9 @@ pub trait FuncEnvironment { /// [`translate_module`](fn.translate_module.html) function. These methods should not be called /// by the user, they are only for `cretonne-wasm` internal use. pub trait WasmRuntime: FuncEnvironment { + /// Return the name for the given function index. + fn get_name(&self, func_index: FunctionIndex) -> ir::FunctionName; + /// Declares a function signature to the runtime. fn declare_signature(&mut self, sig: &ir::Signature); @@ -157,7 +160,7 @@ pub trait WasmRuntime: FuncEnvironment { fn get_signature(&self, sig_index: SignatureIndex) -> &ir::Signature; /// Declares a function import to the runtime. - fn declare_func_import(&mut self, sig_index: SignatureIndex, module: &[u8], field: &[u8]); + fn declare_func_import(&mut self, sig_index: SignatureIndex, module: &str, field: &str); /// Return the number of imported funcs. fn get_num_func_imports(&self) -> usize; @@ -193,6 +196,15 @@ pub trait WasmRuntime: FuncEnvironment { data: &[u8], ) -> Result<(), String>; + /// Declares a function export to the runtime. + fn declare_func_export(&mut self, func_index: FunctionIndex, name: &str); + /// Declares a table export to the runtime. + fn declare_table_export(&mut self, table_index: TableIndex, name: &str); + /// Declares a memory export to the runtime. + fn declare_memory_export(&mut self, memory_index: MemoryIndex, name: &str); + /// Declares a global export to the runtime. + fn declare_global_export(&mut self, global_index: GlobalIndex, name: &str); + /// Declares a start function. fn declare_start_func(&mut self, index: FunctionIndex); diff --git a/lib/wasm/src/sections_translator.rs b/lib/wasm/src/sections_translator.rs index f0d5810253..a63bad8e05 100644 --- a/lib/wasm/src/sections_translator.rs +++ b/lib/wasm/src/sections_translator.rs @@ -14,7 +14,6 @@ use cretonne; use wasmparser::{Parser, ParserState, FuncType, ImportSectionEntryType, ExternalKind, WasmDecoder, MemoryType, Operator}; use wasmparser; -use std::collections::HashMap; use std::str::from_utf8; use runtime::WasmRuntime; @@ -68,7 +67,12 @@ pub fn parse_import_section( module, field, } => { - runtime.declare_func_import(sig as SignatureIndex, module, field); + // The input has already been validated, so we should be able to + // assume valid UTF-8 and use `from_utf8_unchecked` if performance + // becomes a concern here. + let module_name = from_utf8(module).unwrap(); + let field_name = from_utf8(field).unwrap(); + runtime.declare_func_import(sig as SignatureIndex, module_name, field_name); } ParserState::ImportSectionEntry { ty: ImportSectionEntryType::Memory(MemoryType { limits: ref memlimits }), .. @@ -126,8 +130,8 @@ pub fn parse_function_section( /// Retrieves the names of the functions from the export section pub fn parse_export_section( parser: &mut Parser, -) -> Result, SectionParsingError> { - let mut exports: HashMap = HashMap::new(); + runtime: &mut WasmRuntime, +) -> Result<(), SectionParsingError> { loop { match *parser.read() { ParserState::ExportSectionEntry { @@ -135,21 +139,23 @@ pub fn parse_export_section( ref kind, index, } => { + // The input has already been validated, so we should be able to + // assume valid UTF-8 and use `from_utf8_unchecked` if performance + // becomes a concern here. + let name = from_utf8(field).unwrap(); + let func_index = index as FunctionIndex; match *kind { - ExternalKind::Function => { - exports.insert( - index as FunctionIndex, - String::from(from_utf8(field).unwrap()), - ); - } - _ => (),//TODO: deal with other kind of exports + ExternalKind::Function => runtime.declare_func_export(func_index, name), + ExternalKind::Table => runtime.declare_table_export(func_index, name), + ExternalKind::Memory => runtime.declare_memory_export(func_index, name), + ExternalKind::Global => runtime.declare_global_export(func_index, name), } } ParserState::EndSection => break, ref s => return Err(SectionParsingError::WrongSectionContent(format!("{:?}", s))), }; } - Ok(exports) + Ok(()) } /// Retrieves the start function index from the start section