diff --git a/cranelift/wasm/src/environ/dummy.rs b/cranelift/wasm/src/environ/dummy.rs index 8199cadee6..98cdb3f44e 100644 --- a/cranelift/wasm/src/environ/dummy.rs +++ b/cranelift/wasm/src/environ/dummy.rs @@ -17,7 +17,7 @@ use cranelift_codegen::ir::immediates::{Offset32, Uimm64}; use cranelift_codegen::ir::types::*; use cranelift_codegen::ir::{self, InstBuilder}; use cranelift_codegen::isa::TargetFrontendConfig; -use cranelift_entity::{EntityRef, PrimaryMap}; +use cranelift_entity::{EntityRef, PrimaryMap, SecondaryMap}; use std::boxed::Box; use std::string::String; use std::vec::Vec; @@ -124,6 +124,9 @@ pub struct DummyEnvironment { /// Instructs to collect debug data during translation. debug_info: bool, + + /// Function names. + function_names: SecondaryMap, } impl DummyEnvironment { @@ -135,6 +138,7 @@ impl DummyEnvironment { func_bytecode_sizes: Vec::new(), return_mode, debug_info, + function_names: SecondaryMap::new(), } } @@ -152,6 +156,12 @@ impl DummyEnvironment { pub fn get_num_func_imports(&self) -> usize { self.info.imported_funcs.len() } + + /// Return the name of the function, if a name for the function with + /// the corresponding index exists. + pub fn get_func_name(&self, func_index: FuncIndex) -> Option<&str> { + self.function_names.get(func_index).map(String::as_ref) + } } /// The `FuncEnvironment` implementation for use by the `DummyEnvironment`. @@ -539,4 +549,9 @@ impl<'data> ModuleEnvironment<'data> for DummyEnvironment { self.info.function_bodies.push(func); Ok(()) } + + fn declare_func_name(&mut self, func_index: FuncIndex, name: &'data str) -> WasmResult<()> { + self.function_names[func_index] = String::from(name); + Ok(()) + } } diff --git a/cranelift/wasm/src/environ/spec.rs b/cranelift/wasm/src/environ/spec.rs index e01c9b3cf9..ba87cc992a 100644 --- a/cranelift/wasm/src/environ/spec.rs +++ b/cranelift/wasm/src/environ/spec.rs @@ -468,6 +468,14 @@ pub trait ModuleEnvironment<'data> { data: &'data [u8], ) -> WasmResult<()>; + /// Declares the name of a function to the environment. + /// + /// By default this does nothing, but implementations can use this to read + /// the function name subsection of the custom name section if desired. + fn declare_func_name(&mut self, _func_index: FuncIndex, _name: &'data str) -> WasmResult<()> { + Ok(()) + } + /// Indicates that a custom section has been found in the wasm file fn custom_section(&mut self, name: &'data str, data: &'data [u8]) -> WasmResult<()> { drop((name, data)); diff --git a/cranelift/wasm/src/module_translator.rs b/cranelift/wasm/src/module_translator.rs index 1ff8dd2dd8..8d8b55397d 100644 --- a/cranelift/wasm/src/module_translator.rs +++ b/cranelift/wasm/src/module_translator.rs @@ -4,10 +4,10 @@ use crate::environ::{ModuleEnvironment, WasmError, WasmResult}; use crate::sections_translator::{ parse_code_section, parse_data_section, parse_element_section, parse_export_section, parse_function_section, parse_global_section, parse_import_section, parse_memory_section, - parse_start_section, parse_table_section, parse_type_section, + parse_name_section, parse_start_section, parse_table_section, parse_type_section, }; use cranelift_codegen::timing; -use wasmparser::{ModuleReader, SectionCode}; +use wasmparser::{CustomSectionKind, ModuleReader, SectionCode}; /// Translate a sequence of bytes forming a valid Wasm binary into a list of valid Cranelift IR /// [`Function`](cranelift_codegen::ir::Function). @@ -83,6 +83,14 @@ pub fn translate_module<'data>( }); } + SectionCode::Custom { + kind: CustomSectionKind::Name, + name: _, + } => { + let names = section.get_name_section_reader()?; + parse_name_section(names, environ)?; + } + SectionCode::Custom { name, kind: _ } => { let mut reader = section.get_binary_reader(); let len = reader.bytes_remaining(); diff --git a/cranelift/wasm/src/sections_translator.rs b/cranelift/wasm/src/sections_translator.rs index 4ca8a66b87..67830af3ff 100644 --- a/cranelift/wasm/src/sections_translator.rs +++ b/cranelift/wasm/src/sections_translator.rs @@ -12,7 +12,7 @@ use crate::translation_utils::{ tabletype_to_type, type_to_type, FuncIndex, Global, GlobalIndex, GlobalInit, Memory, MemoryIndex, SignatureIndex, Table, TableElementType, TableIndex, }; -use crate::wasm_unsupported; +use crate::{wasm_unsupported, HashMap}; use core::convert::TryFrom; use cranelift_codegen::ir::{self, AbiParam, Signature}; use cranelift_entity::EntityRef; @@ -21,8 +21,8 @@ use wasmparser::{ self, CodeSectionReader, Data, DataKind, DataSectionReader, Element, ElementKind, ElementSectionReader, Export, ExportSectionReader, ExternalKind, FuncType, FunctionSectionReader, GlobalSectionReader, GlobalType, ImportSectionEntryType, - ImportSectionReader, MemorySectionReader, MemoryType, Operator, TableSectionReader, - TypeSectionReader, + ImportSectionReader, MemorySectionReader, MemoryType, NameSectionReader, Naming, NamingReader, + Operator, TableSectionReader, TypeSectionReader, }; /// Parses the Type section of the wasm module. @@ -351,3 +351,47 @@ pub fn parse_data_section<'data>( Ok(()) } + +/// Parses the Name section of the wasm module. +pub fn parse_name_section<'data>( + mut names: NameSectionReader<'data>, + environ: &mut dyn ModuleEnvironment<'data>, +) -> WasmResult<()> { + while let Ok(subsection) = names.read() { + match subsection { + wasmparser::Name::Function(function_subsection) => { + if let Some(function_names) = function_subsection + .get_map() + .ok() + .and_then(parse_function_name_subsection) + { + for (index, name) in function_names { + environ.declare_func_name(index, name)?; + } + } + return Ok(()); + } + wasmparser::Name::Local(_) | wasmparser::Name::Module(_) => {} + }; + } + Ok(()) +} + +fn parse_function_name_subsection<'data>( + mut naming_reader: NamingReader<'data>, +) -> Option> { + let mut function_names = HashMap::new(); + for _ in 0..naming_reader.get_count() { + let Naming { index, name } = naming_reader.read().ok()?; + if function_names + .insert(FuncIndex::from_u32(index), name) + .is_some() + { + // If the function index has been previously seen, then we + // break out of the loop and early return `None`, because these + // should be unique. + return None; + } + } + return Some(function_names); +} diff --git a/cranelift/wasm/tests/wasm_testsuite.rs b/cranelift/wasm/tests/wasm_testsuite.rs index fd46458d53..69db90ca6c 100644 --- a/cranelift/wasm/tests/wasm_testsuite.rs +++ b/cranelift/wasm/tests/wasm_testsuite.rs @@ -2,7 +2,7 @@ use cranelift_codegen::isa; use cranelift_codegen::print_errors::pretty_verifier_error; use cranelift_codegen::settings::{self, Flags}; use cranelift_codegen::verifier; -use cranelift_wasm::{translate_module, DummyEnvironment, ReturnMode}; +use cranelift_wasm::{translate_module, DummyEnvironment, FuncIndex, ReturnMode}; use std::fs; use std::fs::File; use std::io; @@ -10,7 +10,7 @@ use std::io::prelude::*; use std::path::Path; use std::str::FromStr; use target_lexicon::triple; -use wabt::{wat2wasm_with_features, Features}; +use wabt::{wat2wasm_with_features, Features, Wat2Wasm}; #[test] fn testsuite() { @@ -31,17 +31,42 @@ fn testsuite() { let flags = Flags::new(settings::builder()); for path in paths { let path = path.path(); - handle_module(&path, &flags, ReturnMode::NormalReturns); + let data = read_module(&path); + handle_module(data, &flags, ReturnMode::NormalReturns); } } #[test] fn use_fallthrough_return() { let flags = Flags::new(settings::builder()); - handle_module( - Path::new("../wasmtests/use_fallthrough_return.wat"), - &flags, - ReturnMode::FallthroughReturn, + let path = Path::new("../wasmtests/use_fallthrough_return.wat"); + let data = read_module(&path); + handle_module(data, &flags, ReturnMode::FallthroughReturn); +} + +#[test] +fn use_name_section() { + let wat = r#" + (module $module_name + (func $func_name (local $loc_name i32) + ) + )"#; + let data = Wat2Wasm::new() + .write_debug_names(true) + .convert(wat) + .unwrap_or_else(|e| panic!("error converting wat to wasm: {:?}", e)); + + let flags = Flags::new(settings::builder()); + let triple = triple!("riscv64"); + let isa = isa::lookup(triple).unwrap().finish(flags.clone()); + let return_mode = ReturnMode::NormalReturns; + let mut dummy_environ = DummyEnvironment::new(isa.frontend_config(), return_mode, false); + + translate_module(data.as_ref(), &mut dummy_environ).unwrap(); + + assert_eq!( + dummy_environ.get_func_name(FuncIndex::from_u32(0)).unwrap(), + "func_name" ); } @@ -52,10 +77,10 @@ fn read_file(path: &Path) -> io::Result> { Ok(buf) } -fn handle_module(path: &Path, flags: &Flags, return_mode: ReturnMode) { +fn read_module(path: &Path) -> Vec { let mut features = Features::new(); features.enable_all(); - let data = match path.extension() { + match path.extension() { None => { panic!("the file extension is not wasm or wat"); } @@ -72,7 +97,10 @@ fn handle_module(path: &Path, flags: &Flags, return_mode: ReturnMode) { } None | Some(&_) => panic!("the file extension for {:?} is not wasm or wat", path), }, - }; + } +} + +fn handle_module(data: Vec, flags: &Flags, return_mode: ReturnMode) { let triple = triple!("riscv64"); let isa = isa::lookup(triple).unwrap().finish(flags.clone()); let mut dummy_environ = DummyEnvironment::new(isa.frontend_config(), return_mode, false);