From 03a3a5939a3f2adca42c694cae4781db21130ff2 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 18 Aug 2021 12:15:02 -0500 Subject: [PATCH] Move module translation from cranelift to wasmtime (#3196) The main purpose for doing this is that this is a large piece of functionality used by Wasmtime which is entirely independent of Cranelift. Eventually Wasmtime wants to be able to compile without Cranelift, but it can't also depend on `cranelift-wasm` in that situation for module translation which means that something needs to happen. One option is to refactor what's in `cranelift-wasm` into a separate crate (since all these pieces don't actually depend on `cranelift-codegen`), but I personally chose to not do this because: * The `ModuleEnvironment` trait, AFAIK, only has a primary user of Wasmtime. The Spidermonkey integration, for example, does not use this. * This is an extra layer of abstraction between Wasmtime and the compilation phase which was a bit of a pain to maintain. It couldn't be Wasmtime-specific as it was part of Cranelift but at the same time it had lots of Wasmtime-centric functionality (such as module linking). * Updating the "dummy" implementation has become pretty onerous over time as frequent additions are made and the "dummy" implementation was never actually used anywhere. This ended up feeling like effectively busy-work to update this. For these reasons I've opted to to move the meat of `cranelift-wasm` used by `wasmtime-environ` directly into `wasmtime-environ`. This means that the only real meat that Wasmtime uses from `cranelift-wasm` is the function-translation bits in the `wasmtime-cranelift` crate. The changes in `wasmtime-environ` are largely to inline module parsing together so it's a bit easier to follow instead of trying to connect the dots between lots of various function calls. --- cranelift/wasm/src/translation_utils.rs | 46 +- crates/environ/src/module_environ.rs | 1410 +++++++++++++---------- 2 files changed, 832 insertions(+), 624 deletions(-) diff --git a/cranelift/wasm/src/translation_utils.rs b/cranelift/wasm/src/translation_utils.rs index 0a1d43c9eb..f29eb2f18c 100644 --- a/cranelift/wasm/src/translation_utils.rs +++ b/cranelift/wasm/src/translation_utils.rs @@ -1,7 +1,7 @@ //! Helper functions and structures for the translation. use crate::environ::{TargetEnvironment, WasmResult, WasmType}; -use crate::wasm_unsupported; -use core::convert::TryInto; +use crate::{wasm_unsupported, WasmError}; +use core::convert::{TryFrom, TryInto}; use core::u32; use cranelift_codegen::entity::entity_impl; use cranelift_codegen::ir; @@ -195,6 +195,17 @@ pub enum GlobalInit { Import, } +impl Global { + /// Creates a new `Global` type from wasmparser's representation. + pub fn new(ty: wasmparser::GlobalType, initializer: GlobalInit) -> WasmResult { + Ok(Global { + wasm_ty: ty.content_type.try_into()?, + mutability: ty.mutable, + initializer, + }) + } +} + /// WebAssembly table. #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] @@ -207,6 +218,18 @@ pub struct Table { pub maximum: Option, } +impl TryFrom for Table { + type Error = WasmError; + + fn try_from(ty: wasmparser::TableType) -> WasmResult { + Ok(Table { + wasm_ty: ty.element_type.try_into()?, + minimum: ty.initial, + maximum: ty.maximum, + }) + } +} + /// WebAssembly table element. Can be a function or a scalar type. #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] @@ -231,6 +254,17 @@ pub struct Memory { pub memory64: bool, } +impl From for Memory { + fn from(ty: wasmparser::MemoryType) -> Memory { + Memory { + minimum: ty.initial, + maximum: ty.maximum, + shared: ty.shared, + memory64: ty.memory64, + } + } +} + /// WebAssembly event. #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] @@ -239,6 +273,14 @@ pub struct Tag { pub ty: TypeIndex, } +impl From for Tag { + fn from(ty: wasmparser::TagType) -> Tag { + Tag { + ty: TypeIndex::from_u32(ty.type_index), + } + } +} + /// Helper function translating wasmparser types to Cranelift types when possible. pub fn type_to_type( ty: wasmparser::Type, diff --git a/crates/environ/src/module_environ.rs b/crates/environ/src/module_environ.rs index 320d647f29..85d1c056fa 100644 --- a/crates/environ/src/module_environ.rs +++ b/crates/environ/src/module_environ.rs @@ -3,21 +3,25 @@ use crate::module::{ ModuleSignature, ModuleType, ModuleUpvar, TableInitializer, TablePlan, TypeTables, }; use crate::tunables::Tunables; +use cranelift_codegen::ir::immediates::V128Imm; use cranelift_codegen::packed_option::ReservedValue; use cranelift_entity::PrimaryMap; use cranelift_wasm::{ - self, translate_module, Alias, DataIndex, DefinedFuncIndex, ElemIndex, EntityIndex, EntityType, - FuncIndex, Global, GlobalIndex, GlobalInit, InstanceIndex, InstanceTypeIndex, Memory, - MemoryIndex, ModuleIndex, ModuleTypeIndex, SignatureIndex, Table, TableIndex, TypeIndex, - WasmError, WasmFuncType, WasmResult, + self, DataIndex, DefinedFuncIndex, ElemIndex, EntityIndex, EntityType, FuncIndex, Global, + GlobalIndex, GlobalInit, InstanceIndex, InstanceTypeIndex, MemoryIndex, ModuleIndex, + ModuleTypeIndex, SignatureIndex, TableIndex, TypeIndex, WasmError, WasmFuncType, WasmResult, }; use std::collections::{hash_map::Entry, HashMap}; -use std::convert::TryFrom; +use std::convert::{TryFrom, TryInto}; use std::mem; use std::path::PathBuf; use std::sync::Arc; use wasmparser::Type as WasmType; -use wasmparser::{FuncValidator, FunctionBody, ValidatorResources, WasmFeatures}; +use wasmparser::{ + Alias, DataKind, ElementItem, ElementKind, ExternalKind, FuncValidator, FunctionBody, + ImportSectionEntryType, NameSectionReader, Naming, Operator, Parser, Payload, TypeDef, + Validator, ValidatorResources, WasmFeatures, +}; /// Object containing the standalone environment information. pub struct ModuleEnvironment<'data> { @@ -165,16 +169,713 @@ impl<'data> ModuleEnvironment<'data> { mut self, data: &'data [u8], ) -> WasmResult<(usize, Vec>, TypeTables)> { - translate_module(data, &mut self)?; + let mut validator = Validator::new(); + validator.wasm_features(self.features); + + for payload in Parser::new(0).parse_all(data) { + self.translate_payload(&mut validator, payload?)?; + } + assert!(self.results.len() > 0); Ok((self.results.len() - 1, self.results, self.types)) } - fn declare_export(&mut self, export: EntityIndex, name: &str) -> WasmResult<()> { - self.result - .module - .exports - .insert(String::from(name), export); + fn translate_payload( + &mut self, + validator: &mut Validator, + payload: Payload<'data>, + ) -> WasmResult<()> { + match payload { + Payload::Version { num, range } => { + validator.version(num, &range)?; + + // If this is the first time this method is called, nothing to + // do. + if self.first_module { + self.first_module = false; + } else { + // Reset our internal state for a new module by saving the + // current module in `results`. + let in_progress = mem::replace(&mut self.result, ModuleTranslation::default()); + self.in_progress.push(in_progress); + self.modules_to_be -= 1; + } + } + + Payload::End => { + validator.end()?; + + self.result.creation_artifacts.shrink_to_fit(); + self.result.creation_modules.shrink_to_fit(); + + let (record_initializer, mut done) = match self.in_progress.pop() { + Some(m) => (true, mem::replace(&mut self.result, m)), + None => (false, mem::take(&mut self.result)), + }; + + if record_initializer { + // Record the type of the module we just finished in our own + // module's list of modules. + let sig = self.gen_type_of_module(&done.module); + self.result.module.modules.push(sig); + + // The root module will store the artifacts for this + // finished module at `artifact_index`. This then needs to + // be inherited by all later modules coming down to our + // now-current `self.result`... + let mut artifact_index = self.results.len(); + for result in self.in_progress.iter_mut().chain(Some(&mut self.result)) { + result.creation_artifacts.push(artifact_index); + artifact_index = result.creation_artifacts.len() - 1; + } + // ... and then `self.result` needs to create a new module + // with whatever was record to save off as its own + // artifacts/modules. + self.result + .module + .initializers + .push(Initializer::CreateModule { + artifact_index, + artifacts: mem::take(&mut done.creation_artifacts), + modules: mem::take(&mut done.creation_modules), + }); + } + + // And the final step is to insert the module into the list of + // finished modules to get returned at the end. + self.results.push(done); + } + + Payload::TypeSection(types) => { + validator.type_section(&types)?; + let num = usize::try_from(types.get_count()).unwrap(); + self.result.module.types.reserve(num); + self.types.wasm_signatures.reserve(num); + + for ty in types { + match ty? { + TypeDef::Func(wasm_func_ty) => { + self.declare_type_func(wasm_func_ty.try_into()?)?; + } + TypeDef::Module(t) => { + let imports = t + .imports + .iter() + .map(|i| Ok((i.module, i.field, self.entity_type(i.ty)?))) + .collect::>>()?; + let exports = t + .exports + .iter() + .map(|e| Ok((e.name, self.entity_type(e.ty)?))) + .collect::>>()?; + self.declare_type_module(&imports, &exports)?; + } + TypeDef::Instance(t) => { + let exports = t + .exports + .iter() + .map(|e| Ok((e.name, self.entity_type(e.ty)?))) + .collect::>>()?; + self.declare_type_instance(&exports)?; + } + } + } + } + + Payload::ImportSection(imports) => { + validator.import_section(&imports)?; + + let cnt = usize::try_from(imports.get_count()).unwrap(); + self.result.module.initializers.reserve(cnt); + + for entry in imports { + let import = entry?; + let ty = match import.ty { + ImportSectionEntryType::Function(index) => { + let index = TypeIndex::from_u32(index); + let sig_index = self.result.module.types[index].unwrap_function(); + self.result.module.num_imported_funcs += 1; + self.result.debuginfo.wasm_file.imported_func_count += 1; + EntityType::Function(sig_index) + } + ImportSectionEntryType::Module(index) => { + let index = TypeIndex::from_u32(index); + let signature = self.type_to_module_type(index)?; + EntityType::Module(signature) + } + ImportSectionEntryType::Instance(index) => { + let index = TypeIndex::from_u32(index); + let signature = self.type_to_instance_type(index)?; + EntityType::Instance(signature) + } + ImportSectionEntryType::Memory(ty) => { + if ty.shared { + return Err(WasmError::Unsupported("shared memories".to_owned())); + } + self.result.module.num_imported_memories += 1; + EntityType::Memory(ty.into()) + } + ImportSectionEntryType::Global(ty) => { + self.result.module.num_imported_globals += 1; + EntityType::Global(Global::new(ty, GlobalInit::Import)?) + } + ImportSectionEntryType::Table(ty) => { + self.result.module.num_imported_tables += 1; + EntityType::Table(ty.try_into()?) + } + + // doesn't get past validation + ImportSectionEntryType::Tag(_) => unreachable!(), + }; + self.declare_import(import.module, import.field, ty); + } + } + + Payload::FunctionSection(functions) => { + validator.function_section(&functions)?; + + let cnt = usize::try_from(functions.get_count()).unwrap(); + self.result.module.functions.reserve_exact(cnt); + + for entry in functions { + let sigindex = entry?; + let ty = TypeIndex::from_u32(sigindex); + let sig_index = self.result.module.types[ty].unwrap_function(); + self.result.module.functions.push(sig_index); + } + } + + Payload::TableSection(tables) => { + validator.table_section(&tables)?; + let cnt = usize::try_from(tables.get_count()).unwrap(); + self.result.module.table_plans.reserve_exact(cnt); + + for entry in tables { + let table = entry?.try_into()?; + let plan = TablePlan::for_table(table, &self.tunables); + self.result.module.table_plans.push(plan); + } + } + + Payload::MemorySection(memories) => { + validator.memory_section(&memories)?; + + let cnt = usize::try_from(memories.get_count()).unwrap(); + self.result.module.memory_plans.reserve_exact(cnt); + + for entry in memories { + let memory = entry?; + if memory.shared { + return Err(WasmError::Unsupported("shared memories".to_owned())); + } + let plan = MemoryPlan::for_memory(memory.into(), &self.tunables); + self.result.module.memory_plans.push(plan); + } + } + + Payload::TagSection(tags) => { + validator.tag_section(&tags)?; + + // This feature isn't enabled at this time, so we should + // never get here. + unreachable!(); + } + + Payload::GlobalSection(globals) => { + validator.global_section(&globals)?; + + let cnt = usize::try_from(globals.get_count()).unwrap(); + self.result.module.globals.reserve_exact(cnt); + + for entry in globals { + let wasmparser::Global { ty, init_expr } = entry?; + let mut init_expr_reader = init_expr.get_binary_reader(); + let initializer = match init_expr_reader.read_operator()? { + Operator::I32Const { value } => GlobalInit::I32Const(value), + Operator::I64Const { value } => GlobalInit::I64Const(value), + Operator::F32Const { value } => GlobalInit::F32Const(value.bits()), + Operator::F64Const { value } => GlobalInit::F64Const(value.bits()), + Operator::V128Const { value } => { + GlobalInit::V128Const(V128Imm::from(value.bytes().to_vec().as_slice())) + } + Operator::RefNull { ty: _ } => GlobalInit::RefNullConst, + Operator::RefFunc { function_index } => { + let index = FuncIndex::from_u32(function_index); + self.flag_func_possibly_exported(index); + GlobalInit::RefFunc(index) + } + Operator::GlobalGet { global_index } => { + GlobalInit::GetGlobal(GlobalIndex::from_u32(global_index)) + } + s => { + return Err(WasmError::Unsupported(format!( + "unsupported init expr in global section: {:?}", + s + ))); + } + }; + let ty = Global::new(ty, initializer)?; + self.result.module.globals.push(ty); + } + } + + Payload::ExportSection(exports) => { + validator.export_section(&exports)?; + + let cnt = usize::try_from(exports.get_count()).unwrap(); + self.result.module.exports.reserve(cnt); + + for entry in exports { + let wasmparser::Export { field, kind, index } = entry?; + let entity = match kind { + ExternalKind::Function => { + let index = FuncIndex::from_u32(index); + self.flag_func_possibly_exported(index); + EntityIndex::Function(index) + } + ExternalKind::Table => EntityIndex::Table(TableIndex::from_u32(index)), + ExternalKind::Memory => EntityIndex::Memory(MemoryIndex::from_u32(index)), + ExternalKind::Global => EntityIndex::Global(GlobalIndex::from_u32(index)), + ExternalKind::Module => EntityIndex::Module(ModuleIndex::from_u32(index)), + ExternalKind::Instance => { + EntityIndex::Instance(InstanceIndex::from_u32(index)) + } + + // this never gets past validation + ExternalKind::Tag | ExternalKind::Type => unreachable!(), + }; + self.result + .module + .exports + .insert(String::from(field), entity); + } + } + + Payload::StartSection { func, range } => { + validator.start_section(func, &range)?; + + let func_index = FuncIndex::from_u32(func); + self.flag_func_possibly_exported(func_index); + debug_assert!(self.result.module.start_func.is_none()); + self.result.module.start_func = Some(func_index); + } + + Payload::ElementSection(elements) => { + validator.element_section(&elements)?; + + let cnt = usize::try_from(elements.get_count()).unwrap(); + self.result.module.table_initializers.reserve_exact(cnt); + + for (index, entry) in elements.into_iter().enumerate() { + let wasmparser::Element { kind, items, ty: _ } = entry?; + + // Build up a list of `FuncIndex` corresponding to all the + // entries listed in this segment. Note that it's not + // possible to create anything other than a `ref.null + // extern` for externref segments, so those just get + // translate to the reserved value of `FuncIndex`. + let items_reader = items.get_items_reader()?; + let mut elements = + Vec::with_capacity(usize::try_from(items_reader.get_count()).unwrap()); + for item in items_reader { + elements.push(match item? { + ElementItem::Func(f) => { + let f = FuncIndex::from_u32(f); + self.flag_func_possibly_exported(f); + f + } + ElementItem::Null(_ty) => FuncIndex::reserved_value(), + }); + } + + match kind { + ElementKind::Active { + table_index, + init_expr, + } => { + let table_index = TableIndex::from_u32(table_index); + let mut init_expr_reader = init_expr.get_binary_reader(); + let (base, offset) = match init_expr_reader.read_operator()? { + Operator::I32Const { value } => (None, value as u32), + Operator::GlobalGet { global_index } => { + (Some(GlobalIndex::from_u32(global_index)), 0) + } + ref s => { + return Err(WasmError::Unsupported(format!( + "unsupported init expr in element section: {:?}", + s + ))); + } + }; + self.result + .module + .table_initializers + .push(TableInitializer { + table_index, + base, + offset, + elements: elements.into(), + }); + } + + ElementKind::Passive => { + let elem_index = ElemIndex::from_u32(index as u32); + let index = self.result.module.passive_elements.len(); + self.result.module.passive_elements.push(elements.into()); + self.result + .module + .passive_elements_map + .insert(elem_index, index); + } + + ElementKind::Declared => {} + } + } + } + + Payload::CodeSectionStart { count, range, .. } => { + validator.code_section_start(count, &range)?; + let cnt = usize::try_from(count).unwrap(); + self.result.function_body_inputs.reserve_exact(cnt); + self.result.debuginfo.wasm_file.code_section_offset = range.start as u64; + } + + Payload::CodeSectionEntry(mut body) => { + let validator = validator.code_section_entry()?; + let func_index = + self.result.code_index + self.result.module.num_imported_funcs as u32; + let func_index = FuncIndex::from_u32(func_index); + + if self.tunables.generate_native_debuginfo { + let sig_index = self.result.module.functions[func_index]; + let sig = &self.types.wasm_signatures[sig_index]; + let mut locals = Vec::new(); + for pair in body.get_locals_reader()? { + locals.push(pair?); + } + self.result + .debuginfo + .wasm_file + .funcs + .push(FunctionMetadata { + locals: locals.into_boxed_slice(), + params: sig.params.iter().cloned().map(|i| i.into()).collect(), + }); + } + body.allow_memarg64(self.features.memory64); + self.result + .function_body_inputs + .push(FunctionBodyData { validator, body }); + self.result.code_index += 1; + } + + Payload::DataSection(data) => { + validator.data_section(&data)?; + + let initializers = match &mut self.result.module.memory_initialization { + MemoryInitialization::Segmented(i) => i, + _ => unreachable!(), + }; + + let cnt = usize::try_from(data.get_count()).unwrap(); + initializers.reserve_exact(cnt); + + for (index, entry) in data.into_iter().enumerate() { + let wasmparser::Data { kind, data } = entry?; + match kind { + DataKind::Active { + memory_index, + init_expr, + } => { + let memory_index = MemoryIndex::from_u32(memory_index); + let mut init_expr_reader = init_expr.get_binary_reader(); + let (base, offset) = match init_expr_reader.read_operator()? { + Operator::I32Const { value } => (None, value as u64), + Operator::I64Const { value } => (None, value as u64), + Operator::GlobalGet { global_index } => { + (Some(GlobalIndex::from_u32(global_index)), 0) + } + s => { + return Err(WasmError::Unsupported(format!( + "unsupported init expr in data section: {:?}", + s + ))); + } + }; + initializers.push(MemoryInitializer { + memory_index, + base, + offset, + data: data.into(), + }); + } + DataKind::Passive => { + let data_index = DataIndex::from_u32(index as u32); + let index = self.result.module.passive_data.len(); + self.result.module.passive_data.push(Arc::from(data)); + self.result + .module + .passive_data_map + .insert(data_index, index); + } + } + } + } + + Payload::DataCountSection { count, range } => { + validator.data_count_section(count, &range)?; + + // Note: the count passed in here is the *total* segment count + // There is no way to reserve for just the passive segments as + // they are discovered when iterating the data section entries + // Given that the total segment count might be much larger than + // the passive count, do not reserve anything here. + } + + Payload::InstanceSection(s) => { + validator.instance_section(&s)?; + + let cnt = usize::try_from(s.get_count()).unwrap(); + self.result.module.instances.reserve(cnt); + self.result.module.initializers.reserve(cnt); + + for instance in s { + let instance = instance?; + let module = ModuleIndex::from_u32(instance.module()); + let args = instance + .args()? + .into_iter() + .map(|arg| { + let arg = arg?; + let index = match arg.kind { + ExternalKind::Function => { + EntityIndex::Function(FuncIndex::from_u32(arg.index)) + } + ExternalKind::Table => { + EntityIndex::Table(TableIndex::from_u32(arg.index)) + } + ExternalKind::Memory => { + EntityIndex::Memory(MemoryIndex::from_u32(arg.index)) + } + ExternalKind::Global => { + EntityIndex::Global(GlobalIndex::from_u32(arg.index)) + } + ExternalKind::Module => { + EntityIndex::Module(ModuleIndex::from_u32(arg.index)) + } + ExternalKind::Instance => { + EntityIndex::Instance(InstanceIndex::from_u32(arg.index)) + } + + // this won't pass validation + ExternalKind::Tag | ExternalKind::Type => unreachable!(), + }; + Ok((arg.name.to_string(), index)) + }) + .collect::>()?; + + // Record the type of this instance with the type signature of the + // module we're instantiating and then also add an initializer which + // records that we'll be adding to the instance index space here. + let module_ty = self.result.module.modules[module]; + let instance_ty = self.types.module_signatures[module_ty].exports; + self.result.module.instances.push(instance_ty); + self.result + .module + .initializers + .push(Initializer::Instantiate { module, args }); + } + } + Payload::AliasSection(s) => { + validator.alias_section(&s)?; + + for alias in s { + match alias? { + // Types are easy, we statically know everything so + // we're just copying some pointers from our parent + // module to our own module. + // + // Note that we don't add an initializer for this alias + // because we statically know where all types point to. + Alias::OuterType { + relative_depth, + index, + } => { + let index = TypeIndex::from_u32(index); + let module_idx = self.in_progress.len() - 1 - (relative_depth as usize); + let ty = self.in_progress[module_idx].module.types[index]; + self.result.module.types.push(ty); + } + + // Modules are a bit trickier since we need to record + // how to track the state from the original module down + // to our own. + Alias::OuterModule { + relative_depth, + index, + } => { + let index = ModuleIndex::from_u32(index); + + // First we can copy the type from the parent module + // into our own module to record what type our + // module definition will have. + let module_idx = self.in_progress.len() - 1 - (relative_depth as usize); + let module_ty = self.in_progress[module_idx].module.modules[index]; + self.result.module.modules.push(module_ty); + + // Next we'll be injecting a module value that is + // closed over, and that will be used to define the + // module into the index space. Record an + // initializer about where our module is sourced + // from (which will be stored within each module + // value itself). + let module_index = self.result.creation_modules.len(); + self.result + .module + .initializers + .push(Initializer::DefineModule(module_index)); + + // And finally we need to record a breadcrumb trail + // of how to get the module value into + // `module_index`. The module just after our + // destination module will use a `ModuleIndex` to + // fetch the module value, and everything else + // inbetween will inherit that module's closed-over + // value. + let mut upvar = ModuleUpvar::Local(index); + for outer in self.in_progress[module_idx + 1..].iter_mut() { + let upvar = mem::replace( + &mut upvar, + ModuleUpvar::Inherit(outer.creation_modules.len()), + ); + outer.creation_modules.push(upvar); + } + self.result.creation_modules.push(upvar); + } + + // This case is slightly more involved, we'll be + // recording all the type information for each kind of + // entity, and then we also need to record an + // initialization step to get the export from the + // instance. + Alias::InstanceExport { + instance, + export, + kind: _, + } => { + let instance = InstanceIndex::from_u32(instance); + let ty = self.result.module.instances[instance]; + match &self.types.instance_signatures[ty].exports[export] { + EntityType::Global(g) => { + self.result.module.globals.push(g.clone()); + self.result.module.num_imported_globals += 1; + } + EntityType::Memory(mem) => { + let plan = MemoryPlan::for_memory(*mem, &self.tunables); + self.result.module.memory_plans.push(plan); + self.result.module.num_imported_memories += 1; + } + EntityType::Table(t) => { + let plan = TablePlan::for_table(*t, &self.tunables); + self.result.module.table_plans.push(plan); + self.result.module.num_imported_tables += 1; + } + EntityType::Function(sig) => { + self.result.module.functions.push(*sig); + self.result.module.num_imported_funcs += 1; + self.result.debuginfo.wasm_file.imported_func_count += 1; + } + EntityType::Instance(sig) => { + self.result.module.instances.push(*sig); + } + EntityType::Module(sig) => { + self.result.module.modules.push(*sig); + } + EntityType::Tag(_) => unimplemented!(), + } + self.result + .module + .initializers + .push(Initializer::AliasInstanceExport { + instance, + export: export.to_string(), + }) + } + } + } + } + + Payload::ModuleSectionStart { + count, + range, + size: _, + } => { + validator.module_section_start(count, &range)?; + + // Go ahead and reserve space in the final `results` array for `amount` + // more modules. + self.modules_to_be += count as usize; + self.results.reserve(self.modules_to_be); + + // Then also reserve space in our own local module's metadata fields + // we'll be adding to. + self.result.module.modules.reserve(count as usize); + self.result.module.initializers.reserve(count as usize); + } + + Payload::ModuleSectionEntry { .. } => { + validator.module_section_entry(); + // note that nothing else happens here since we rely on the next + // `Version` payload to recurse in the parsed modules. + } + + Payload::CustomSection { + name: "name", + data, + data_offset, + range: _, + } => { + let result = NameSectionReader::new(data, data_offset) + .map_err(|e| e.into()) + .and_then(|s| self.name_section(s)); + if let Err(e) = result { + log::warn!("failed to parse name section {:?}", e); + } + } + + Payload::CustomSection { + name: "webidl-bindings", + .. + } + | Payload::CustomSection { + name: "wasm-interface-types", + .. + } => { + return Err(WasmError::Unsupported( + "\ +Support for interface types has temporarily been removed from `wasmtime`. + +For more information about this temoprary you can read on the issue online: + + https://github.com/bytecodealliance/wasmtime/issues/1271 + +and for re-adding support for interface types you can see this issue: + + https://github.com/bytecodealliance/wasmtime/issues/677 +" + .to_string(), + )) + } + + Payload::CustomSection { name, data, .. } => { + self.register_dwarf_section(name, data); + } + + Payload::UnknownSection { id, range, .. } => { + validator.unknown_section(id, &range)?; + unreachable!(); + } + } Ok(()) } @@ -309,6 +1010,26 @@ impl<'data> ModuleEnvironment<'data> { } } + fn entity_type(&self, ty: ImportSectionEntryType) -> WasmResult { + Ok(match ty { + ImportSectionEntryType::Function(sig) => { + EntityType::Function(self.type_to_signature(TypeIndex::from_u32(sig))?) + } + ImportSectionEntryType::Module(sig) => { + EntityType::Module(self.type_to_module_type(TypeIndex::from_u32(sig))?) + } + ImportSectionEntryType::Instance(sig) => { + EntityType::Instance(self.type_to_instance_type(TypeIndex::from_u32(sig))?) + } + ImportSectionEntryType::Memory(ty) => EntityType::Memory(ty.into()), + ImportSectionEntryType::Tag(t) => EntityType::Tag(t.into()), + ImportSectionEntryType::Global(ty) => { + EntityType::Global(Global::new(ty, GlobalInit::Import)?) + } + ImportSectionEntryType::Table(ty) => EntityType::Table(ty.try_into()?), + }) + } + fn push_type(&mut self, ty: EntityType) -> EntityIndex { match ty { EntityType::Function(ty) => { @@ -357,24 +1078,10 @@ impl<'data> ModuleEnvironment<'data> { } fn flag_func_possibly_exported(&mut self, func: FuncIndex) { - if func.is_reserved_value() { - return; - } if let Some(idx) = self.result.module.defined_func_index(func) { self.result.module.possibly_exported_funcs.insert(idx); } } -} - -/// This trait is useful for `translate_module` because it tells how to translate -/// environment-dependent wasm instructions. These functions should not be called by the user. -impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data> { - fn reserve_types(&mut self, num: u32) -> WasmResult<()> { - let num = usize::try_from(num).unwrap(); - self.result.module.types.reserve(num); - self.types.wasm_signatures.reserve(num); - Ok(()) - } fn declare_type_func(&mut self, wasm: WasmFuncType) -> WasmResult<()> { // Deduplicate wasm function signatures through `interned_func_types`, @@ -478,607 +1185,66 @@ impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data } } - fn reserve_imports(&mut self, num: u32) -> WasmResult<()> { - Ok(self - .result - .module - .initializers - .reserve(usize::try_from(num).unwrap())) - } - - fn declare_func_import( - &mut self, - index: TypeIndex, - module: &'data str, - field: Option<&'data str>, - ) -> WasmResult<()> { - debug_assert_eq!( - self.result.module.functions.len(), - self.result.module.num_imported_funcs, - "Imported functions must be declared first" - ); - let sig_index = self.result.module.types[index].unwrap_function(); - self.declare_import(module, field, EntityType::Function(sig_index)); - self.result.module.num_imported_funcs += 1; - self.result.debuginfo.wasm_file.imported_func_count += 1; - Ok(()) - } - - fn declare_table_import( - &mut self, - table: Table, - module: &'data str, - field: Option<&'data str>, - ) -> WasmResult<()> { - debug_assert_eq!( - self.result.module.table_plans.len(), - self.result.module.num_imported_tables, - "Imported tables must be declared first" - ); - self.declare_import(module, field, EntityType::Table(table)); - self.result.module.num_imported_tables += 1; - Ok(()) - } - - fn declare_memory_import( - &mut self, - memory: Memory, - module: &'data str, - field: Option<&'data str>, - ) -> WasmResult<()> { - debug_assert_eq!( - self.result.module.memory_plans.len(), - self.result.module.num_imported_memories, - "Imported memories must be declared first" - ); - if memory.shared { - return Err(WasmError::Unsupported("shared memories".to_owned())); - } - self.declare_import(module, field, EntityType::Memory(memory)); - self.result.module.num_imported_memories += 1; - Ok(()) - } - - fn declare_global_import( - &mut self, - global: Global, - module: &'data str, - field: Option<&'data str>, - ) -> WasmResult<()> { - debug_assert_eq!( - self.result.module.globals.len(), - self.result.module.num_imported_globals, - "Imported globals must be declared first" - ); - self.declare_import(module, field, EntityType::Global(global)); - self.result.module.num_imported_globals += 1; - Ok(()) - } - - fn declare_module_import( - &mut self, - ty_index: TypeIndex, - module: &'data str, - field: Option<&'data str>, - ) -> WasmResult<()> { - let signature = self.type_to_module_type(ty_index)?; - self.declare_import(module, field, EntityType::Module(signature)); - Ok(()) - } - - fn declare_instance_import( - &mut self, - ty_index: TypeIndex, - module: &'data str, - field: Option<&'data str>, - ) -> WasmResult<()> { - let signature = self.type_to_instance_type(ty_index)?; - self.declare_import(module, field, EntityType::Instance(signature)); - Ok(()) - } - - fn reserve_func_types(&mut self, num: u32) -> WasmResult<()> { - self.result - .module - .functions - .reserve_exact(usize::try_from(num).unwrap()); - self.result - .function_body_inputs - .reserve_exact(usize::try_from(num).unwrap()); - Ok(()) - } - - fn declare_func_type(&mut self, index: TypeIndex) -> WasmResult<()> { - let sig_index = self.result.module.types[index].unwrap_function(); - self.result.module.functions.push(sig_index); - Ok(()) - } - - fn reserve_tables(&mut self, num: u32) -> WasmResult<()> { - self.result - .module - .table_plans - .reserve_exact(usize::try_from(num).unwrap()); - Ok(()) - } - - fn declare_table(&mut self, table: Table) -> WasmResult<()> { - let plan = TablePlan::for_table(table, &self.tunables); - self.result.module.table_plans.push(plan); - Ok(()) - } - - fn reserve_memories(&mut self, num: u32) -> WasmResult<()> { - self.result - .module - .memory_plans - .reserve_exact(usize::try_from(num).unwrap()); - Ok(()) - } - - fn declare_memory(&mut self, memory: Memory) -> WasmResult<()> { - if memory.shared { - return Err(WasmError::Unsupported("shared memories".to_owned())); - } - let plan = MemoryPlan::for_memory(memory, &self.tunables); - self.result.module.memory_plans.push(plan); - Ok(()) - } - - fn reserve_globals(&mut self, num: u32) -> WasmResult<()> { - self.result - .module - .globals - .reserve_exact(usize::try_from(num).unwrap()); - Ok(()) - } - - fn declare_global(&mut self, global: Global) -> WasmResult<()> { - if let GlobalInit::RefFunc(index) = global.initializer { - self.flag_func_possibly_exported(index); - } - self.result.module.globals.push(global); - Ok(()) - } - - fn reserve_exports(&mut self, num: u32) -> WasmResult<()> { - self.result - .module - .exports - .reserve(usize::try_from(num).unwrap()); - Ok(()) - } - - fn declare_func_export(&mut self, func_index: FuncIndex, name: &str) -> WasmResult<()> { - self.flag_func_possibly_exported(func_index); - self.declare_export(EntityIndex::Function(func_index), name) - } - - fn declare_table_export(&mut self, table_index: TableIndex, name: &str) -> WasmResult<()> { - self.declare_export(EntityIndex::Table(table_index), name) - } - - fn declare_memory_export(&mut self, memory_index: MemoryIndex, name: &str) -> WasmResult<()> { - self.declare_export(EntityIndex::Memory(memory_index), name) - } - - fn declare_global_export(&mut self, global_index: GlobalIndex, name: &str) -> WasmResult<()> { - self.declare_export(EntityIndex::Global(global_index), name) - } - - fn declare_module_export(&mut self, index: ModuleIndex, name: &str) -> WasmResult<()> { - self.declare_export(EntityIndex::Module(index), name) - } - - fn declare_instance_export(&mut self, index: InstanceIndex, name: &str) -> WasmResult<()> { - self.declare_export(EntityIndex::Instance(index), name) - } - - fn declare_start_func(&mut self, func_index: FuncIndex) -> WasmResult<()> { - self.flag_func_possibly_exported(func_index); - debug_assert!(self.result.module.start_func.is_none()); - self.result.module.start_func = Some(func_index); - Ok(()) - } - - fn reserve_table_elements(&mut self, num: u32) -> WasmResult<()> { - self.result - .module - .table_initializers - .reserve_exact(usize::try_from(num).unwrap()); - Ok(()) - } - - fn declare_table_elements( - &mut self, - table_index: TableIndex, - base: Option, - offset: u32, - elements: Box<[FuncIndex]>, - ) -> WasmResult<()> { - for element in elements.iter() { - self.flag_func_possibly_exported(*element); - } - self.result - .module - .table_initializers - .push(TableInitializer { - table_index, - base, - offset, - elements, - }); - Ok(()) - } - - fn declare_passive_element( - &mut self, - elem_index: ElemIndex, - segments: Box<[FuncIndex]>, - ) -> WasmResult<()> { - for element in segments.iter() { - self.flag_func_possibly_exported(*element); - } - let index = self.result.module.passive_elements.len(); - self.result.module.passive_elements.push(segments); - let old = self - .result - .module - .passive_elements_map - .insert(elem_index, index); - debug_assert!( - old.is_none(), - "should never get duplicate element indices, that would be a bug in `cranelift_wasm`'s \ - translation" - ); - Ok(()) - } - - fn declare_elements(&mut self, segments: Box<[FuncIndex]>) -> WasmResult<()> { - for element in segments.iter() { - self.flag_func_possibly_exported(*element); - } - Ok(()) - } - - fn reserve_function_bodies(&mut self, _count: u32, offset: u64) { - self.result.debuginfo.wasm_file.code_section_offset = offset; - } - - fn define_function_body( - &mut self, - validator: FuncValidator, - mut body: FunctionBody<'data>, - ) -> WasmResult<()> { - if self.tunables.generate_native_debuginfo { - let func_index = self.result.code_index + self.result.module.num_imported_funcs as u32; - let func_index = FuncIndex::from_u32(func_index); - let sig_index = self.result.module.functions[func_index]; - let sig = &self.types.wasm_signatures[sig_index]; - let mut locals = Vec::new(); - for pair in body.get_locals_reader()? { - locals.push(pair?); - } - self.result - .debuginfo - .wasm_file - .funcs - .push(FunctionMetadata { - locals: locals.into_boxed_slice(), - params: sig.params.iter().cloned().map(|i| i.into()).collect(), - }); - } - body.allow_memarg64(self.features.memory64); - self.result - .function_body_inputs - .push(FunctionBodyData { validator, body }); - self.result.code_index += 1; - Ok(()) - } - - fn reserve_data_initializers(&mut self, num: u32) -> WasmResult<()> { - match &mut self.result.module.memory_initialization { - MemoryInitialization::Segmented(initializers) => { - initializers.reserve_exact(usize::try_from(num).unwrap()) - } - _ => unreachable!(), - } - Ok(()) - } - - fn declare_data_initialization( - &mut self, - memory_index: MemoryIndex, - base: Option, - offset: u64, - data: &'data [u8], - ) -> WasmResult<()> { - match &mut self.result.module.memory_initialization { - MemoryInitialization::Segmented(initializers) => { - initializers.push(MemoryInitializer { - memory_index, - base, - offset, - data: data.into(), - }); - } - _ => unreachable!(), - } - Ok(()) - } - - fn reserve_passive_data(&mut self, _count: u32) -> WasmResult<()> { - // Note: the count passed in here is the *total* segment count - // There is no way to reserve for just the passive segments as they are discovered when iterating the data section entries - // Given that the total segment count might be much larger than the passive count, do not reserve - Ok(()) - } - - fn declare_passive_data(&mut self, data_index: DataIndex, data: &'data [u8]) -> WasmResult<()> { - let index = self.result.module.passive_data.len(); - self.result.module.passive_data.push(Arc::from(data)); - let old = self - .result - .module - .passive_data_map - .insert(data_index, index); - debug_assert!( - old.is_none(), - "a module can't have duplicate indices, this would be a cranelift-wasm bug" - ); - Ok(()) - } - - fn declare_module_name(&mut self, name: &'data str) { - self.result.module.name = Some(name.to_string()); - if self.tunables.generate_native_debuginfo { - self.result.debuginfo.name_section.module_name = Some(name); - } - } - - fn declare_func_name(&mut self, func_index: FuncIndex, name: &'data str) { - self.result - .module - .func_names - .insert(func_index, name.to_string()); - if self.tunables.generate_native_debuginfo { - self.result - .debuginfo - .name_section - .func_names - .insert(func_index.as_u32(), name); - } - } - - fn declare_local_name(&mut self, func_index: FuncIndex, local: u32, name: &'data str) { - if self.tunables.generate_native_debuginfo { - self.result - .debuginfo - .name_section - .locals_names - .entry(func_index.as_u32()) - .or_insert(HashMap::new()) - .insert(local, name); - } - } - - fn custom_section(&mut self, name: &'data str, data: &'data [u8]) -> WasmResult<()> { - self.register_dwarf_section(name, data); - - match name { - "webidl-bindings" | "wasm-interface-types" => Err(WasmError::Unsupported( - "\ -Support for interface types has temporarily been removed from `wasmtime`. - -For more information about this temoprary you can read on the issue online: - - https://github.com/bytecodealliance/wasmtime/issues/1271 - -and for re-adding support for interface types you can see this issue: - - https://github.com/bytecodealliance/wasmtime/issues/677 -" - .to_owned(), - )), - - // skip other sections - _ => Ok(()), - } - } - - fn wasm_features(&self) -> WasmFeatures { - self.features - } - - fn reserve_modules(&mut self, amount: u32) { - // Go ahead and reserve space in the final `results` array for `amount` - // more modules. - self.modules_to_be += amount as usize; - self.results.reserve(self.modules_to_be); - - // Then also reserve space in our own local module's metadata fields - // we'll be adding to. - self.result.module.modules.reserve(amount as usize); - self.result.module.initializers.reserve(amount as usize); - } - - fn module_start(&mut self) { - // If this is the first time this method is called, nothing to do. - if self.first_module { - self.first_module = false; - return; - } - // Reset our internal state for a new module by saving the current - // module in `results`. - let in_progress = mem::replace(&mut self.result, ModuleTranslation::default()); - self.in_progress.push(in_progress); - self.modules_to_be -= 1; - } - - fn module_end(&mut self) { - self.result.creation_artifacts.shrink_to_fit(); - self.result.creation_modules.shrink_to_fit(); - - let (record_initializer, mut done) = match self.in_progress.pop() { - Some(m) => (true, mem::replace(&mut self.result, m)), - None => (false, mem::take(&mut self.result)), - }; - - if record_initializer { - // Record the type of the module we just finished in our own - // module's list of modules. - let sig = self.gen_type_of_module(&done.module); - self.result.module.modules.push(sig); - - // The root module will store the artifacts for this finished - // module at `artifact_index`. This then needs to be inherited by - // all later modules coming down to our now-current `self.result`... - let mut artifact_index = self.results.len(); - for result in self.in_progress.iter_mut().chain(Some(&mut self.result)) { - result.creation_artifacts.push(artifact_index); - artifact_index = result.creation_artifacts.len() - 1; - } - // ... and then `self.result` needs to create a new module with - // whatever was record to save off as its own artifacts/modules. - self.result - .module - .initializers - .push(Initializer::CreateModule { - artifact_index, - artifacts: mem::take(&mut done.creation_artifacts), - modules: mem::take(&mut done.creation_modules), - }); - } - - // And the final step is to insert the module into the list of finished - // modules to get returned at the end. - self.results.push(done); - } - - fn reserve_instances(&mut self, amt: u32) { - self.result.module.instances.reserve(amt as usize); - self.result.module.initializers.reserve(amt as usize); - } - - fn declare_instance( - &mut self, - module: ModuleIndex, - args: Vec<(&'data str, EntityIndex)>, - ) -> WasmResult<()> { - let args = args.into_iter().map(|(s, i)| (s.to_string(), i)).collect(); - // Record the type of this instance with the type signature of the - // module we're instantiating and then also add an initializer which - // records that we'll be adding to the instance index space here. - let module_ty = self.result.module.modules[module]; - let instance_ty = self.types.module_signatures[module_ty].exports; - self.result.module.instances.push(instance_ty); - self.result - .module - .initializers - .push(Initializer::Instantiate { module, args }); - Ok(()) - } - - fn declare_alias(&mut self, alias: Alias) -> WasmResult<()> { - match alias { - // Types are easy, we statically know everything so we're just - // copying some pointers from our parent module to our own module. - // - // Note that we don't add an initializer for this alias because - // we statically know where all types point to. - Alias::OuterType { - relative_depth, - index, - } => { - let module_idx = self.in_progress.len() - 1 - (relative_depth as usize); - let ty = self.in_progress[module_idx].module.types[index]; - self.result.module.types.push(ty); - } - - // Modules are a bit trickier since we need to record how to track - // the state from the original module down to our own. - Alias::OuterModule { - relative_depth, - index, - } => { - // First we can copy the type from the parent module into our - // own module to record what type our module definition will - // have. - let module_idx = self.in_progress.len() - 1 - (relative_depth as usize); - let module_ty = self.in_progress[module_idx].module.modules[index]; - self.result.module.modules.push(module_ty); - - // Next we'll be injecting a module value that is closed over, - // and that will be used to define the module into the index - // space. Record an initializer about where our module is - // sourced from (which will be stored within each module value - // itself). - let module_index = self.result.creation_modules.len(); - self.result - .module - .initializers - .push(Initializer::DefineModule(module_index)); - - // And finally we need to record a breadcrumb trail of how to - // get the module value into `module_index`. The module just - // after our destination module will use a `ModuleIndex` to - // fetch the module value, and everything else inbetween will - // inherit that module's closed-over value. - let mut upvar = ModuleUpvar::Local(index); - for outer in self.in_progress[module_idx + 1..].iter_mut() { - let upvar = mem::replace( - &mut upvar, - ModuleUpvar::Inherit(outer.creation_modules.len()), - ); - outer.creation_modules.push(upvar); + /// Parses the Name section of the wasm module. + fn name_section(&mut self, names: NameSectionReader<'data>) -> WasmResult<()> { + for subsection in names { + match subsection? { + wasmparser::Name::Function(f) => { + let mut names = f.get_map()?; + for _ in 0..names.get_count() { + let Naming { index, name } = names.read()?; + let index = FuncIndex::from_u32(index); + self.result + .module + .func_names + .insert(index, name.to_string()); + if self.tunables.generate_native_debuginfo { + self.result + .debuginfo + .name_section + .func_names + .insert(index.as_u32(), name); + } + } } - self.result.creation_modules.push(upvar); - } - - // This case is slightly more involved, we'll be recording all the - // type information for each kind of entity, and then we also need - // to record an initialization step to get the export from the - // instance. - Alias::InstanceExport { instance, export } => { - let ty = self.result.module.instances[instance]; - match &self.types.instance_signatures[ty].exports[export] { - EntityType::Global(g) => { - self.result.module.globals.push(g.clone()); - self.result.module.num_imported_globals += 1; + wasmparser::Name::Module(module) => { + let name = module.get_name()?; + self.result.module.name = Some(name.to_string()); + if self.tunables.generate_native_debuginfo { + self.result.debuginfo.name_section.module_name = Some(name); } - EntityType::Memory(mem) => { - let plan = MemoryPlan::for_memory(*mem, &self.tunables); - self.result.module.memory_plans.push(plan); - self.result.module.num_imported_memories += 1; - } - EntityType::Table(t) => { - let plan = TablePlan::for_table(*t, &self.tunables); - self.result.module.table_plans.push(plan); - self.result.module.num_imported_tables += 1; - } - EntityType::Function(sig) => { - self.result.module.functions.push(*sig); - self.result.module.num_imported_funcs += 1; - self.result.debuginfo.wasm_file.imported_func_count += 1; - } - EntityType::Instance(sig) => { - self.result.module.instances.push(*sig); - } - EntityType::Module(sig) => { - self.result.module.modules.push(*sig); - } - EntityType::Tag(_) => unimplemented!(), } - self.result - .module - .initializers - .push(Initializer::AliasInstanceExport { - instance, - export: export.to_string(), - }) + wasmparser::Name::Local(l) => { + if !self.tunables.generate_native_debuginfo { + continue; + } + let mut reader = l.get_indirect_map()?; + for _ in 0..reader.get_indirect_count() { + let f = reader.read()?; + let mut map = f.get_map()?; + for _ in 0..map.get_count() { + let Naming { index, name } = map.read()?; + + self.result + .debuginfo + .name_section + .locals_names + .entry(f.indirect_index) + .or_insert(HashMap::new()) + .insert(index, name); + } + } + } + wasmparser::Name::Label(_) + | wasmparser::Name::Type(_) + | wasmparser::Name::Table(_) + | wasmparser::Name::Global(_) + | wasmparser::Name::Memory(_) + | wasmparser::Name::Element(_) + | wasmparser::Name::Data(_) + | wasmparser::Name::Unknown { .. } => {} } } - Ok(()) } }