diff --git a/crates/cranelift/src/compiler.rs b/crates/cranelift/src/compiler.rs index 6b3390b96281..a4ed9a0c43a1 100644 --- a/crates/cranelift/src/compiler.rs +++ b/crates/cranelift/src/compiler.rs @@ -1,4 +1,4 @@ -use crate::debug::{DwarfSectionRelocTarget, ModuleMemoryOffset}; +use crate::debug::DwarfSectionRelocTarget; use crate::func_environ::FuncEnvironment; use crate::DEBUG_ASSERT_TRAP_CODE; use crate::{array_call_signature, CompiledFunction, ModuleTextBuilder}; @@ -12,11 +12,9 @@ use cranelift_codegen::isa::{ use cranelift_codegen::print_errors::pretty_error; use cranelift_codegen::Context; use cranelift_codegen::{CompiledCode, MachStackMap}; -use cranelift_entity::{EntityRef, PrimaryMap}; +use cranelift_entity::PrimaryMap; use cranelift_frontend::FunctionBuilder; -use cranelift_wasm::{ - DefinedFuncIndex, FuncTranslator, MemoryIndex, OwnedMemoryIndex, WasmFuncType, WasmValType, -}; +use cranelift_wasm::{DefinedFuncIndex, FuncTranslator, WasmFuncType, WasmValType}; use object::write::{Object, StandardSegment, SymbolId}; use object::{RelocationEncoding, RelocationFlags, RelocationKind, SectionKind}; use std::any::Any; @@ -29,7 +27,8 @@ use wasmparser::{FuncValidatorAllocations, FunctionBody}; use wasmtime_environ::{ AddressMapSection, BuiltinFunctionIndex, CacheStore, CompileError, FlagValue, FunctionBodyData, FunctionLoc, ModuleTranslation, ModuleTypesBuilder, PtrSize, RelocationTarget, - StackMapInformation, TrapEncodingBuilder, Tunables, VMOffsets, WasmFunctionInfo, + StackMapInformation, StaticModuleIndex, TrapEncodingBuilder, Tunables, VMOffsets, + WasmFunctionInfo, }; #[cfg(feature = "component-model")] @@ -440,63 +439,33 @@ impl wasmtime_environ::Compiler for Compiler { self } - fn append_dwarf( + fn append_dwarf<'a>( &self, obj: &mut Object<'_>, - translation: &ModuleTranslation<'_>, - funcs: &PrimaryMap, - dwarf_package_bytes: Option<&[u8]>, - tunables: &Tunables, + translations: &'a PrimaryMap>, + get_func: &'a dyn Fn( + StaticModuleIndex, + DefinedFuncIndex, + ) -> (SymbolId, &'a (dyn Any + Send)), + dwarf_package_bytes: Option<&'a [u8]>, + tunables: &'a Tunables, ) -> Result<()> { - let ofs = VMOffsets::new( - self.isa - .triple() - .architecture - .pointer_width() - .unwrap() - .bytes(), - &translation.module, - ); - - let memory_offset = if ofs.num_imported_memories > 0 { - ModuleMemoryOffset::Imported { - offset_to_vm_memory_definition: ofs.vmctx_vmmemory_import(MemoryIndex::new(0)) - + u32::from(ofs.vmmemory_import_from()), - offset_to_memory_base: ofs.ptr.vmmemory_definition_base().into(), - } - } else if ofs.num_defined_memories > 0 { - // The addition of shared memory makes the following assumption, - // "owned memory index = 0", possibly false. If the first memory - // is a shared memory, the base pointer will not be stored in - // the `owned_memories` array. The following code should - // eventually be fixed to not only handle shared memories but - // also multiple memories. - assert_eq!( - ofs.num_defined_memories, ofs.num_owned_memories, - "the memory base pointer may be incorrect due to sharing memory" - ); - ModuleMemoryOffset::Defined( - ofs.vmctx_vmmemory_definition_base(OwnedMemoryIndex::new(0)), + let get_func = move |m, f| { + let (sym, any) = get_func(m, f); + ( + sym, + any.downcast_ref::().unwrap().metadata(), ) - } else { - ModuleMemoryOffset::None }; - let functions_info = funcs - .iter() - .map(|(_, (_, func))| { - let f = func.downcast_ref::().unwrap(); - f.metadata() - }) - .collect(); - let dwarf_sections = crate::debug::emit_dwarf( + let mut compilation = crate::debug::Compilation::new( &*self.isa, - &translation.debuginfo, - &functions_info, - &memory_offset, + translations, + &get_func, dwarf_package_bytes, tunables, - ) - .with_context(|| "failed to emit DWARF debug information")?; + ); + let dwarf_sections = crate::debug::emit_dwarf(&*self.isa, &mut compilation) + .with_context(|| "failed to emit DWARF debug information")?; let (debug_bodies, debug_relocs): (Vec<_>, Vec<_>) = dwarf_sections .iter() @@ -515,7 +484,7 @@ impl wasmtime_environ::Compiler for Compiler { let section_id = *dwarf_sections_ids.get(name).unwrap(); for reloc in relocs { let target_symbol = match reloc.target { - DwarfSectionRelocTarget::Func(index) => funcs[DefinedFuncIndex::new(index)].0, + DwarfSectionRelocTarget::Func(id) => compilation.symbol_id(id), DwarfSectionRelocTarget::Section(name) => { obj.section_symbol(dwarf_sections_ids[name]) } diff --git a/crates/cranelift/src/debug.rs b/crates/cranelift/src/debug.rs index 083a3ba4745e..ed9c473105ec 100644 --- a/crates/cranelift/src/debug.rs +++ b/crates/cranelift/src/debug.rs @@ -1,5 +1,14 @@ //! Debug utils for WebAssembly using Cranelift. +use crate::CompiledFunctionMetadata; +use cranelift_codegen::isa::TargetIsa; +use object::write::SymbolId; +use std::collections::HashMap; +use wasmtime_environ::{ + DefinedFuncIndex, EntityRef, MemoryIndex, ModuleTranslation, OwnedMemoryIndex, PrimaryMap, + PtrSize, StaticModuleIndex, Tunables, VMOffsets, +}; + /// Memory definition offset in the VMContext structure. #[derive(Debug, Clone)] pub enum ModuleMemoryOffset { @@ -18,6 +27,154 @@ pub enum ModuleMemoryOffset { }, } +type Reader<'input> = gimli::EndianSlice<'input, gimli::LittleEndian>; + +/// "Package structure" to collect together various artifacts/results of a +/// compilation. +/// +/// This structure is threaded through a number of top-level functions of DWARF +/// processing within in this submodule to pass along all the bits-and-pieces of +/// the compilation context. +pub struct Compilation<'a> { + /// All module translations which were present in this compilation. + /// + /// This map has one entry for core wasm modules and may have multiple (or + /// zero) for components. + translations: &'a PrimaryMap>, + + /// Accessor of a particular compiled function for a module. + /// + /// This returns the `object`-based-symbol for the function as well as the + /// `&CompiledFunction`. + get_func: + &'a dyn Fn(StaticModuleIndex, DefinedFuncIndex) -> (SymbolId, &'a CompiledFunctionMetadata), + + /// Optionally-specified `*.dwp` file, currently only supported for core + /// wasm modules. + dwarf_package_bytes: Option<&'a [u8]>, + + /// Compilation settings used when producing functions. + tunables: &'a Tunables, + + /// Translation between `SymbolId` and a `usize`-based symbol which gimli + /// uses. + symbol_index_to_id: Vec, + symbol_id_to_index: HashMap, + + /// The `ModuleMemoryOffset` for each module within `translations`. + /// + /// Note that this doesn't support multi-memory at this time. + module_memory_offsets: PrimaryMap, +} + +impl<'a> Compilation<'a> { + pub fn new( + isa: &dyn TargetIsa, + translations: &'a PrimaryMap>, + get_func: &'a dyn Fn( + StaticModuleIndex, + DefinedFuncIndex, + ) -> (SymbolId, &'a CompiledFunctionMetadata), + dwarf_package_bytes: Option<&'a [u8]>, + tunables: &'a Tunables, + ) -> Compilation<'a> { + // Build the `module_memory_offsets` map based on the modules in + // `translations`. + let mut module_memory_offsets = PrimaryMap::new(); + for (i, translation) in translations { + let ofs = VMOffsets::new( + isa.triple().architecture.pointer_width().unwrap().bytes(), + &translation.module, + ); + + let memory_offset = if ofs.num_imported_memories > 0 { + ModuleMemoryOffset::Imported { + offset_to_vm_memory_definition: ofs.vmctx_vmmemory_import(MemoryIndex::new(0)) + + u32::from(ofs.vmmemory_import_from()), + offset_to_memory_base: ofs.ptr.vmmemory_definition_base().into(), + } + } else if ofs.num_defined_memories > 0 { + // The addition of shared memory makes the following assumption, + // "owned memory index = 0", possibly false. If the first memory + // is a shared memory, the base pointer will not be stored in + // the `owned_memories` array. The following code should + // eventually be fixed to not only handle shared memories but + // also multiple memories. + assert_eq!( + ofs.num_defined_memories, ofs.num_owned_memories, + "the memory base pointer may be incorrect due to sharing memory" + ); + ModuleMemoryOffset::Defined( + ofs.vmctx_vmmemory_definition_base(OwnedMemoryIndex::new(0)), + ) + } else { + ModuleMemoryOffset::None + }; + let j = module_memory_offsets.push(memory_offset); + assert_eq!(i, j); + } + + // Build the `symbol <=> usize` mappings + let mut symbol_index_to_id = Vec::new(); + let mut symbol_id_to_index = HashMap::new(); + + for (module, translation) in translations { + for func in translation.module.defined_func_indices() { + let (sym, _func) = get_func(module, func); + symbol_id_to_index.insert(sym, (symbol_index_to_id.len(), module, func)); + symbol_index_to_id.push(sym); + } + } + + Compilation { + translations, + get_func, + dwarf_package_bytes, + tunables, + symbol_index_to_id, + symbol_id_to_index, + module_memory_offsets, + } + } + + /// Returns an iterator over all function indexes present in this + /// compilation. + /// + /// Each function is additionally accompanied with its module index. + fn indexes(&self) -> impl Iterator + '_ { + self.translations + .iter() + .flat_map(|(i, t)| t.module.defined_func_indices().map(move |j| (i, j))) + } + + /// Returns an iterator of all functions with their module, symbol, and + /// function metadata that were produced during compilation. + fn functions( + &self, + ) -> impl Iterator + '_ { + self.indexes().map(move |(module, func)| { + let (sym, func) = self.function(module, func); + (module, sym, func) + }) + } + + /// Returns the symbol and metadata associated with a specific function. + fn function( + &self, + module: StaticModuleIndex, + func: DefinedFuncIndex, + ) -> (usize, &'a CompiledFunctionMetadata) { + let (sym, func) = (self.get_func)(module, func); + (self.symbol_id_to_index[&sym].0, func) + } + + /// Maps a `usize`-based symbol used by gimli to the object-based + /// `SymbolId`. + pub fn symbol_id(&self, sym: usize) -> SymbolId { + self.symbol_index_to_id[sym] + } +} + pub use write_debuginfo::{emit_dwarf, DwarfSectionRelocTarget}; mod gc; diff --git a/crates/cranelift/src/debug/gc.rs b/crates/cranelift/src/debug/gc.rs index c6398fb82245..bb45e8e838a7 100644 --- a/crates/cranelift/src/debug/gc.rs +++ b/crates/cranelift/src/debug/gc.rs @@ -1,8 +1,10 @@ use crate::debug::transform::AddressTransform; +use crate::debug::{Compilation, Reader}; use gimli::constants; use gimli::read; -use gimli::{Reader, UnitSectionOffset}; +use gimli::UnitSectionOffset; use std::collections::{HashMap, HashSet}; +use wasmtime_environ::{PrimaryMap, StaticModuleIndex}; #[derive(Debug)] pub struct Dependencies { @@ -65,23 +67,26 @@ impl Dependencies { } } -pub fn build_dependencies>( - dwarf: &read::Dwarf, - dwp: &Option>, - at: &AddressTransform, +pub fn build_dependencies( + compilation: &mut Compilation<'_>, + dwp: &Option>>, + at: &PrimaryMap, ) -> read::Result { let mut deps = Dependencies::new(); - let mut units = dwarf.units(); - while let Some(unit) = units.next()? { - build_unit_dependencies(unit, dwarf, dwp, at, &mut deps)?; + for (i, translation) in compilation.translations.iter() { + let dwarf = &translation.debuginfo.dwarf; + let mut units = dwarf.units(); + while let Some(unit) = units.next()? { + build_unit_dependencies(unit, dwarf, dwp, &at[i], &mut deps)?; + } } Ok(deps) } -fn build_unit_dependencies>( - header: read::UnitHeader, - dwarf: &read::Dwarf, - dwp: &Option>, +fn build_unit_dependencies( + header: read::UnitHeader>, + dwarf: &read::Dwarf>, + dwp: &Option>>, at: &AddressTransform, deps: &mut Dependencies, ) -> read::Result<()> { @@ -103,7 +108,7 @@ fn build_unit_dependencies>( Ok(()) } -fn has_die_back_edge>(die: &read::DebuggingInformationEntry) -> bool { +fn has_die_back_edge(die: &read::DebuggingInformationEntry>) -> bool { match die.tag() { constants::DW_TAG_variable | constants::DW_TAG_constant @@ -123,10 +128,10 @@ fn has_die_back_edge>(die: &read::DebuggingInformation } } -fn has_valid_code_range>( - die: &read::DebuggingInformationEntry, - dwarf: &read::Dwarf, - unit: &read::Unit, +fn has_valid_code_range( + die: &read::DebuggingInformationEntry>, + dwarf: &read::Dwarf>, + unit: &read::Unit>, at: &AddressTransform, ) -> read::Result { match die.tag() { @@ -199,10 +204,10 @@ fn has_valid_code_range>( Ok(false) } -fn build_die_dependencies>( - die: read::EntriesTreeNode, - dwarf: &read::Dwarf, - unit: &read::Unit, +fn build_die_dependencies( + die: read::EntriesTreeNode>, + dwarf: &read::Dwarf>, + unit: &read::Unit>, at: &AddressTransform, deps: &mut Dependencies, ) -> read::Result<()> { @@ -229,11 +234,11 @@ fn build_die_dependencies>( Ok(()) } -fn build_attr_dependencies>( - attr: &read::Attribute, +fn build_attr_dependencies( + attr: &read::Attribute>, offset: UnitSectionOffset, - _dwarf: &read::Dwarf, - unit: &read::Unit, + _dwarf: &read::Dwarf>, + unit: &read::Unit>, _at: &AddressTransform, deps: &mut Dependencies, ) -> read::Result<()> { diff --git a/crates/cranelift/src/debug/transform/address_transform.rs b/crates/cranelift/src/debug/transform/address_transform.rs index 5a437c8c1b6f..2bb126dde764 100644 --- a/crates/cranelift/src/debug/transform/address_transform.rs +++ b/crates/cranelift/src/debug/transform/address_transform.rs @@ -1,7 +1,8 @@ -use crate::{CompiledFunctionsMetadata, FunctionAddressMap}; +use crate::debug::Compilation; +use crate::FunctionAddressMap; use gimli::write; use std::collections::BTreeMap; -use wasmtime_environ::{DefinedFuncIndex, EntityRef, FilePos, PrimaryMap, WasmFileInfo}; +use wasmtime_environ::{DefinedFuncIndex, FilePos, PrimaryMap, StaticModuleIndex}; pub type GeneratedAddress = usize; pub type WasmAddress = u64; @@ -18,6 +19,7 @@ pub struct AddressMap { /// length, and instructions addresses. #[derive(Debug)] pub struct FunctionMap { + pub symbol: usize, pub offset: GeneratedAddress, pub len: GeneratedAddress, pub wasm_start: WasmAddress, @@ -187,11 +189,19 @@ fn build_function_lookup( } fn build_function_addr_map( - funcs: &CompiledFunctionsMetadata, - code_section_offset: u64, + compilation: &Compilation<'_>, + module: StaticModuleIndex, ) -> PrimaryMap { let mut map = PrimaryMap::new(); - for (_, metadata) in funcs { + for idx in compilation.translations[module] + .module + .defined_func_indices() + { + let (symbol, metadata) = compilation.function(module, idx); + let code_section_offset = compilation.translations[module] + .debuginfo + .wasm_file + .code_section_offset; let ft = &metadata.address_map; let mut fn_map = Vec::new(); for t in ft.instructions.iter() { @@ -213,6 +223,7 @@ fn build_function_addr_map( } map.push(FunctionMap { + symbol, offset: ft.body_offset, len: ft.body_len as usize, wasm_start: get_wasm_code_offset(ft.start_srcloc, code_section_offset), @@ -352,7 +363,7 @@ impl<'a> Iterator for TransformRangeEndIter<'a> { } // Utility iterator to iterate by translated function ranges. -pub struct TransformRangeIter<'a> { +struct TransformRangeIter<'a> { func: &'a FuncTransform, start_it: TransformRangeStartIter<'a>, end_it: TransformRangeEndIter<'a>, @@ -448,11 +459,18 @@ impl<'a> Iterator for TransformRangeIter<'a> { } impl AddressTransform { - pub fn new(funcs: &CompiledFunctionsMetadata, wasm_file: &WasmFileInfo) -> Self { - let code_section_offset = wasm_file.code_section_offset; - + pub fn new(compilation: &Compilation<'_>, module: StaticModuleIndex) -> Self { let mut func = BTreeMap::new(); - for (i, metadata) in funcs { + let code_section_offset = compilation.translations[module] + .debuginfo + .wasm_file + .code_section_offset; + + for idx in compilation.translations[module] + .module + .defined_func_indices() + { + let (_, metadata) = compilation.function(module, idx); let (fn_start, fn_end, lookup) = build_function_lookup(&metadata.address_map, code_section_offset); @@ -461,17 +479,56 @@ impl AddressTransform { FuncTransform { start: fn_start, end: fn_end, - index: i, + index: idx, lookup, }, ); } - let map = build_function_addr_map(funcs, code_section_offset); + let map = build_function_addr_map(compilation, module); let func = Vec::from_iter(func.into_iter()); AddressTransform { map, func } } + #[cfg(test)] + pub fn mock( + module_map: &wasmtime_environ::PrimaryMap< + wasmtime_environ::DefinedFuncIndex, + &crate::CompiledFunctionMetadata, + >, + wasm_file: wasmtime_environ::WasmFileInfo, + ) -> Self { + let mut translations = wasmtime_environ::PrimaryMap::new(); + let mut translation = wasmtime_environ::ModuleTranslation::default(); + translation.debuginfo.wasm_file = wasm_file; + translation + .module + .push_function(wasmtime_environ::ModuleInternedTypeIndex::from_u32(0)); + translations.push(translation); + + let mut dummy_obj = object::write::Object::new( + object::BinaryFormat::Elf, + object::Architecture::Wasm32, + object::Endianness::Little, + ); + let dummy_symbol = dummy_obj.add_file_symbol(Vec::new()); + let func_lookup = move |_, f| (dummy_symbol, module_map[f]); + let tunables = wasmtime_environ::Tunables::default_host(); + let compile = Compilation::new( + &*cranelift_codegen::isa::lookup(target_lexicon::Triple::host()) + .unwrap() + .finish(cranelift_codegen::settings::Flags::new( + cranelift_codegen::settings::builder(), + )) + .unwrap(), + &translations, + &func_lookup, + None, + &tunables, + ); + Self::new(&compile, StaticModuleIndex::from_u32(0)) + } + fn find_func(&self, addr: u64) -> Option<&FuncTransform> { // TODO check if we need to include end address let func = match self.func.binary_search_by(|entry| entry.0.cmp(&addr)) { @@ -494,20 +551,20 @@ impl AddressTransform { self.find_func(addr).map(|f| f.index) } - pub fn translate_raw(&self, addr: u64) -> Option<(DefinedFuncIndex, GeneratedAddress)> { + fn translate_raw(&self, addr: u64) -> Option<(usize, GeneratedAddress)> { if addr == 0 { // It's normally 0 for debug info without the linked code. return None; } if let Some(func) = self.find_func(addr) { + let map = &self.map[func.index]; if addr == func.end { // Clamp last address to the end to extend translation to the end // of the function. - let map = &self.map[func.index]; - return Some((func.index, map.len)); + return Some((map.symbol, map.len)); } let first_result = TransformRangeStartIter::new(func, addr).next(); - first_result.map(|(address, _)| (func.index, address)) + first_result.map(|(address, _)| (map.symbol, address)) } else { // Address was not found: function was not compiled? None @@ -520,8 +577,8 @@ impl AddressTransform { pub fn translate(&self, addr: u64) -> Option { self.translate_raw(addr) - .map(|(func_index, address)| write::Address::Symbol { - symbol: func_index.index(), + .map(|(symbol, address)| write::Address::Symbol { + symbol, addend: address as i64, }) } @@ -530,14 +587,15 @@ impl AddressTransform { &'a self, start: u64, end: u64, - ) -> Option<(DefinedFuncIndex, impl Iterator + 'a)> { + ) -> Option<(usize, impl Iterator + 'a)> { if start == 0 { // It's normally 0 for debug info without the linked code. return None; } if let Some(func) = self.find_func(start) { let result = TransformRangeIter::new(func, start, end); - return Some((func.index, result)); + let symbol = self.map[func.index].symbol; + return Some((symbol, result)); } // Address was not found: function was not compiled? None @@ -578,8 +636,8 @@ impl AddressTransform { } match self.translate_ranges_raw(start, end) { - Some((func_index, ranges)) => TranslateRangesResult::Raw { - symbol: func_index.index(), + Some((symbol, ranges)) => TranslateRangesResult::Raw { + symbol, it: Box::new(ranges), }, None => TranslateRangesResult::Empty, @@ -731,9 +789,9 @@ mod tests { ..Default::default() }; let input = PrimaryMap::from_iter([&func]); - let at = AddressTransform::new( + let at = AddressTransform::mock( &input, - &WasmFileInfo { + WasmFileInfo { path: None, code_section_offset: 1, imported_func_count: 0, diff --git a/crates/cranelift/src/debug/transform/expression.rs b/crates/cranelift/src/debug/transform/expression.rs index 17c560906d3b..19015ea1ef42 100644 --- a/crates/cranelift/src/debug/transform/expression.rs +++ b/crates/cranelift/src/debug/transform/expression.rs @@ -13,7 +13,6 @@ use std::cmp::PartialEq; use std::collections::{HashMap, HashSet}; use std::hash::{Hash, Hasher}; use std::rc::Rc; -use wasmtime_environ::{DefinedFuncIndex, EntityRef}; #[derive(Debug)] pub struct FunctionFrameInfo<'a> { @@ -254,9 +253,7 @@ impl CompiledExpression { Box + 'a>, Vec, ), - Ranges( - Box)>> + 'a>, - ), + Ranges(Box)>> + 'a>), } impl Iterator for BuildWithLocalsResult<'_> { type Item = Result<(write::Address, u64, write::Expression)>; @@ -267,10 +264,10 @@ impl CompiledExpression { .next() .map(|(addr, len)| Ok((addr, len, write::Expression::raw(code.to_vec())))), BuildWithLocalsResult::Ranges(it) => it.next().map(|r| { - r.map(|(func_index, start, end, code_buf)| { + r.map(|(symbol, start, end, code_buf)| { ( write::Address::Symbol { - symbol: func_index.index(), + symbol, addend: start as i64, }, (end - start) as u64, @@ -650,7 +647,7 @@ where #[derive(Debug, Clone)] struct CachedValueLabelRange { - func_index: DefinedFuncIndex, + func_index: usize, start: usize, end: usize, label_location: HashMap, @@ -1176,7 +1173,7 @@ mod tests { imported_func_count: 0, path: None, }; - AddressTransform::new(&module_map, &fi) + AddressTransform::mock(&module_map, fi) } fn create_mock_value_ranges() -> (ValueLabelsRanges, (ValueLabel, ValueLabel, ValueLabel)) { @@ -1225,7 +1222,6 @@ mod tests { fn test_debug_value_range_builder() { use super::ValueLabelRangesBuilder; use crate::debug::ModuleMemoryOffset; - use wasmtime_environ::{DefinedFuncIndex, EntityRef}; let addr_tr = create_mock_address_transform(); let (value_ranges, value_labels) = create_mock_value_ranges(); @@ -1238,7 +1234,7 @@ mod tests { let builder = ValueLabelRangesBuilder::new(&[(10, 20)], &addr_tr, Some(&fi)); let ranges = builder.into_ranges().collect::>(); assert_eq!(ranges.len(), 1); - assert_eq!(ranges[0].func_index, DefinedFuncIndex::new(0)); + assert_eq!(ranges[0].func_index, 0); assert_eq!(ranges[0].start, 0); assert_eq!(ranges[0].end, 30); diff --git a/crates/cranelift/src/debug/transform/line_program.rs b/crates/cranelift/src/debug/transform/line_program.rs index 148fe80f3beb..715428aeef94 100644 --- a/crates/cranelift/src/debug/transform/line_program.rs +++ b/crates/cranelift/src/debug/transform/line_program.rs @@ -6,7 +6,7 @@ use gimli::{ write, AttributeValue::DebugLineRef, DebugLine, DebugLineOffset, DebugStr, DebuggingInformationEntry, LineEncoding, Unit, }; -use wasmtime_environ::{DefinedFuncIndex, EntityRef}; +use wasmtime_environ::DefinedFuncIndex; #[derive(Debug)] enum SavedLineProgramRow { @@ -222,7 +222,7 @@ where continue; // no code generated } }; - let symbol = index.index(); + let symbol = map.symbol; let base_addr = map.offset; out_program.begin_sequence(Some(write::Address::Symbol { symbol, addend: 0 })); // TODO track and place function declaration line here diff --git a/crates/cranelift/src/debug/transform/mod.rs b/crates/cranelift/src/debug/transform/mod.rs index 0695c3f3a4e7..cc988280dec9 100644 --- a/crates/cranelift/src/debug/transform/mod.rs +++ b/crates/cranelift/src/debug/transform/mod.rs @@ -2,8 +2,7 @@ use self::refs::DebugInfoRefsMap; use self::simulate::generate_simulated_dwarf; use self::unit::clone_unit; use crate::debug::gc::build_dependencies; -use crate::debug::ModuleMemoryOffset; -use crate::CompiledFunctionsMetadata; +use crate::debug::Compilation; use anyhow::Error; use cranelift_codegen::isa::TargetIsa; use gimli::{ @@ -12,7 +11,9 @@ use gimli::{ }; use std::{collections::HashSet, fmt::Debug}; use thiserror::Error; -use wasmtime_environ::{DebugInfoData, ModuleTranslation, Tunables}; +use wasmtime_environ::{ + DefinedFuncIndex, ModuleTranslation, PrimaryMap, StaticModuleIndex, Tunables, +}; pub use address_transform::AddressTransform; @@ -26,6 +27,21 @@ mod simulate; mod unit; mod utils; +impl<'a> Compilation<'a> { + fn function_frame_info( + &mut self, + module: StaticModuleIndex, + func: DefinedFuncIndex, + ) -> expression::FunctionFrameInfo<'a> { + let (_, func) = self.function(module, func); + + expression::FunctionFrameInfo { + value_ranges: &func.value_labels_ranges, + memory_offset: self.module_memory_offsets[module].clone(), + } + } +} + pub(crate) trait Reader: gimli::Reader + Send + Sync {} impl<'input, Endian> Reader for gimli::EndianSlice<'input, Endian> where @@ -134,34 +150,25 @@ fn read_dwarf_package_from_bytes<'data>( pub fn transform_dwarf( isa: &dyn TargetIsa, - di: &DebugInfoData, - funcs: &CompiledFunctionsMetadata, - memory_offset: &ModuleMemoryOffset, - dwarf_package_bytes: Option<&[u8]>, - tunables: &Tunables, + compilation: &mut Compilation<'_>, ) -> Result { - let addr_tr = AddressTransform::new(funcs, &di.wasm_file); + let mut transforms = PrimaryMap::new(); + for (i, _) in compilation.translations.iter() { + transforms.push(AddressTransform::new(compilation, i)); + } let buffer = Vec::new(); - let dwarf_package = dwarf_package_bytes + let dwarf_package = compilation + .dwarf_package_bytes .map( |bytes| -> Option>> { - read_dwarf_package_from_bytes(bytes, &buffer, tunables) + read_dwarf_package_from_bytes(bytes, &buffer, compilation.tunables) }, ) .flatten(); - let reachable = build_dependencies(&di.dwarf, &dwarf_package, &addr_tr)?.get_reachable(); - - let context = DebugInputContext { - debug_str: &di.dwarf.debug_str, - debug_line: &di.dwarf.debug_line, - debug_addr: &di.dwarf.debug_addr, - rnglists: &di.dwarf.ranges, - loclists: &di.dwarf.locations, - reachable: &reachable, - }; + let reachable = build_dependencies(compilation, &dwarf_package, &transforms)?.get_reachable(); let out_encoding = gimli::Encoding { format: gimli::Format::Dwarf32, @@ -178,51 +185,61 @@ pub fn transform_dwarf( let mut di_ref_map = DebugInfoRefsMap::new(); let mut translated = HashSet::new(); - let mut iter = di.dwarf.debug_info.units(); - while let Some(header) = iter.next().unwrap_or(None) { - let unit = di.dwarf.unit(header)?; + for (module, translation) in compilation.translations.iter() { + let addr_tr = &transforms[module]; + let di = &translation.debuginfo; + let context = DebugInputContext { + debug_str: &di.dwarf.debug_str, + debug_line: &di.dwarf.debug_line, + debug_addr: &di.dwarf.debug_addr, + rnglists: &di.dwarf.ranges, + loclists: &di.dwarf.locations, + reachable: &reachable, + }; + let mut iter = di.dwarf.debug_info.units(); - let mut resolved_unit = None; - let mut split_dwarf = None; + while let Some(header) = iter.next().unwrap_or(None) { + let unit = di.dwarf.unit(header)?; - if let gimli::UnitType::Skeleton(_dwo_id) = unit.header.type_() { - if let Some(dwarf_package) = &dwarf_package { - if let Some((fused, fused_dwarf)) = - replace_unit_from_split_dwarf(&unit, dwarf_package, &di.dwarf) - { - resolved_unit = Some(fused); - split_dwarf = Some(fused_dwarf); + let mut resolved_unit = None; + let mut split_dwarf = None; + + if let gimli::UnitType::Skeleton(_dwo_id) = unit.header.type_() { + if let Some(dwarf_package) = &dwarf_package { + if let Some((fused, fused_dwarf)) = + replace_unit_from_split_dwarf(&unit, dwarf_package, &di.dwarf) + { + resolved_unit = Some(fused); + split_dwarf = Some(fused_dwarf); + } } } - } - if let Some((id, ref_map, pending_refs)) = clone_unit( - &di.dwarf, - &unit, - resolved_unit.as_ref(), - split_dwarf.as_ref(), - &context, - &addr_tr, - funcs, - memory_offset, - out_encoding, - &mut out_units, - &mut out_strings, - &mut translated, - isa, - )? { - di_ref_map.insert(&header, id, ref_map); - pending_di_refs.push((id, pending_refs)); + if let Some((id, ref_map, pending_refs)) = clone_unit( + compilation, + module, + &unit, + resolved_unit.as_ref(), + split_dwarf.as_ref(), + &context, + &addr_tr, + out_encoding, + &mut out_units, + &mut out_strings, + &mut translated, + isa, + )? { + di_ref_map.insert(&header, id, ref_map); + pending_di_refs.push((id, pending_refs)); + } } } di_ref_map.patch(pending_di_refs.into_iter(), &mut out_units); generate_simulated_dwarf( - &addr_tr, - di, - memory_offset, - funcs, + compilation, + &transforms, &translated, out_encoding, &mut out_units, diff --git a/crates/cranelift/src/debug/transform/range_info_builder.rs b/crates/cranelift/src/debug/transform/range_info_builder.rs index 7fda1c33e4d4..568eeac94086 100644 --- a/crates/cranelift/src/debug/transform/range_info_builder.rs +++ b/crates/cranelift/src/debug/transform/range_info_builder.rs @@ -2,7 +2,7 @@ use super::address_transform::AddressTransform; use super::{DebugInputContext, Reader}; use anyhow::Error; use gimli::{write, AttributeValue, DebuggingInformationEntry, RangeListsOffset, Unit}; -use wasmtime_environ::{DefinedFuncIndex, EntityRef}; +use wasmtime_environ::DefinedFuncIndex; pub(crate) enum RangeInfoBuilder { Undefined, @@ -172,8 +172,8 @@ impl RangeInfoBuilder { } } RangeInfoBuilder::Function(index) => { + let symbol = addr_tr.map()[*index].symbol; let range = addr_tr.func_range(*index); - let symbol = index.index(); let addr = write::Address::Symbol { symbol, addend: range.0 as i64, diff --git a/crates/cranelift/src/debug/transform/simulate.rs b/crates/cranelift/src/debug/transform/simulate.rs index af3a241258d6..b93caf0e492e 100644 --- a/crates/cranelift/src/debug/transform/simulate.rs +++ b/crates/cranelift/src/debug/transform/simulate.rs @@ -1,8 +1,7 @@ use super::expression::{CompiledExpression, FunctionFrameInfo}; -use super::utils::{add_internal_types, append_vmctx_info, get_function_frame_info}; +use super::utils::{add_internal_types, append_vmctx_info}; use super::AddressTransform; -use crate::debug::ModuleMemoryOffset; -use crate::CompiledFunctionsMetadata; +use crate::debug::{Compilation, ModuleMemoryOffset}; use anyhow::{Context, Error}; use cranelift_codegen::isa::TargetIsa; use cranelift_wasm::get_vmctx_value_label; @@ -12,7 +11,7 @@ use std::collections::{HashMap, HashSet}; use std::path::PathBuf; use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; use wasmtime_environ::{ - DebugInfoData, DefinedFuncIndex, EntityRef, FuncIndex, FunctionMetadata, WasmFileInfo, + DebugInfoData, EntityRef, FunctionMetadata, PrimaryMap, StaticModuleIndex, WasmFileInfo, WasmValType, }; @@ -31,8 +30,8 @@ macro_rules! assert_dwarf_str { } fn generate_line_info( - addr_tr: &AddressTransform, - translated: &HashSet, + addr_tr: &PrimaryMap, + translated: &HashSet, out_encoding: gimli::Encoding, w: &WasmFileInfo, comp_dir_id: write::StringId, @@ -58,12 +57,17 @@ fn generate_line_info( None, ); - for (i, map) in addr_tr.map() { - let symbol = i.index(); - if translated.contains(&i) { - continue; - } - + let maps = addr_tr.iter().flat_map(|(_, transform)| { + transform.map().iter().filter_map(|(_, map)| { + if translated.contains(&map.symbol) { + None + } else { + Some((map.symbol, map)) + } + }) + }); + + for (symbol, map) in maps { let base_addr = map.offset; out_program.begin_sequence(Some(write::Address::Symbol { symbol, addend: 0 })); for addr_map in map.addresses.iter() { @@ -279,26 +283,24 @@ fn check_invalid_chars_in_path(path: PathBuf) -> Option { } pub fn generate_simulated_dwarf( - addr_tr: &AddressTransform, - di: &DebugInfoData, - memory_offset: &ModuleMemoryOffset, - funcs: &CompiledFunctionsMetadata, - translated: &HashSet, + compilation: &mut Compilation<'_>, + addr_tr: &PrimaryMap, + translated: &HashSet, out_encoding: gimli::Encoding, out_units: &mut write::UnitTable, out_strings: &mut write::StringTable, isa: &dyn TargetIsa, ) -> Result<(), Error> { - let path = di - .wasm_file - .path - .to_owned() - .and_then(check_invalid_chars_in_path) - .unwrap_or_else(|| autogenerate_dwarf_wasm_path(di)); - - let func_names = &di.name_section.func_names; - let locals_names = &di.name_section.locals_names; - let imported_func_count = di.wasm_file.imported_func_count; + let (wasm_file, path) = { + let di = &compilation.translations.iter().next().unwrap().1.debuginfo; + let path = di + .wasm_file + .path + .to_owned() + .and_then(check_invalid_chars_in_path) + .unwrap_or_else(|| autogenerate_dwarf_wasm_path(di)); + (&di.wasm_file, path) + }; let (unit, root_id, name_id) = { let comp_dir_id = out_strings.add(assert_dwarf_str!(path @@ -317,7 +319,7 @@ pub fn generate_simulated_dwarf( addr_tr, translated, out_encoding, - &di.wasm_file, + wasm_file, comp_dir_id, name_id, name, @@ -343,14 +345,21 @@ pub fn generate_simulated_dwarf( (unit, root_id, name_id) }; - let wasm_types = add_wasm_types(unit, root_id, out_strings, memory_offset); + let mut module_wasm_types = PrimaryMap::new(); + for (module, memory_offset) in compilation.module_memory_offsets.iter() { + let wasm_types = add_wasm_types(unit, root_id, out_strings, memory_offset); + let i = module_wasm_types.push(wasm_types); + assert_eq!(i, module); + } - for (i, map) in addr_tr.map().iter() { - let index = i.index(); - if translated.contains(&i) { + for (module, index) in compilation.indexes().collect::>() { + let (symbol, _) = compilation.function(module, index); + if translated.contains(&symbol) { continue; } + let addr_tr = &addr_tr[module]; + let map = &addr_tr.map()[index]; let start = map.offset as u64; let end = start + map.len as u64; let die_id = unit.add(root_id, gimli::DW_TAG_subprogram); @@ -358,7 +367,7 @@ pub fn generate_simulated_dwarf( die.set( gimli::DW_AT_low_pc, write::AttributeValue::Address(write::Address::Symbol { - symbol: index, + symbol, addend: start as i64, }), ); @@ -367,13 +376,17 @@ pub fn generate_simulated_dwarf( write::AttributeValue::Udata(end - start), ); - let func_index = imported_func_count + (index as u32); - let id = match func_names - .get(&FuncIndex::from_u32(func_index)) + let translation = &compilation.translations[module]; + let func_index = translation.module.func_index(index); + let di = &translation.debuginfo; + let id = match di + .name_section + .func_names + .get(&func_index) .and_then(|s| check_invalid_chars_in_name(s)) { Some(n) => out_strings.add(assert_dwarf_str!(n)), - None => out_strings.add(format!("wasm-function[{}]", func_index)), + None => out_strings.add(format!("wasm-function[{}]", func_index.as_u32())), }; die.set(gimli::DW_AT_name, write::AttributeValue::StringRef(id)); @@ -390,21 +403,20 @@ pub fn generate_simulated_dwarf( write::AttributeValue::Udata(wasm_offset), ); - if let Some(frame_info) = get_function_frame_info(memory_offset, funcs, i) { - let source_range = addr_tr.func_source_range(i); - generate_vars( - unit, - die_id, - addr_tr, - &frame_info, - &[(source_range.0, source_range.1)], - &wasm_types, - &di.wasm_file.funcs[index], - locals_names.get(&FuncIndex::from_u32(index as u32)), - out_strings, - isa, - )?; - } + let frame_info = compilation.function_frame_info(module, index); + let source_range = addr_tr.func_source_range(index); + generate_vars( + unit, + die_id, + addr_tr, + &frame_info, + &[(source_range.0, source_range.1)], + &module_wasm_types[module], + &di.wasm_file.funcs[index.as_u32() as usize], + di.name_section.locals_names.get(&func_index), + out_strings, + isa, + )?; } Ok(()) diff --git a/crates/cranelift/src/debug/transform/unit.rs b/crates/cranelift/src/debug/transform/unit.rs index 0b7e070ad27c..107c8d6ff2ee 100644 --- a/crates/cranelift/src/debug/transform/unit.rs +++ b/crates/cranelift/src/debug/transform/unit.rs @@ -4,17 +4,16 @@ use super::expression::compile_expression; use super::line_program::clone_line_program; use super::range_info_builder::RangeInfoBuilder; use super::refs::{PendingDebugInfoRefs, PendingUnitRefs, UnitRefsMap}; -use super::utils::{add_internal_types, append_vmctx_info, get_function_frame_info}; -use super::{DebugInputContext, Reader}; -use crate::debug::ModuleMemoryOffset; -use crate::CompiledFunctionsMetadata; +use super::utils::{add_internal_types, append_vmctx_info}; +use super::DebugInputContext; +use crate::debug::{Compilation, Reader}; use anyhow::{Context, Error}; use cranelift_codegen::ir::Endianness; use cranelift_codegen::isa::TargetIsa; use gimli::write; use gimli::{AttributeValue, DebuggingInformationEntry, Unit}; use std::collections::HashSet; -use wasmtime_environ::DefinedFuncIndex; +use wasmtime_environ::StaticModuleIndex; use wasmtime_versioned_export_macros::versioned_stringify_ident; struct InheritedAttr { @@ -45,14 +44,11 @@ impl InheritedAttr { } } -fn get_base_type_name( - type_entry: &DebuggingInformationEntry, - unit: &Unit, - context: &DebugInputContext, -) -> Result -where - R: Reader, -{ +fn get_base_type_name( + type_entry: &DebuggingInformationEntry>, + unit: &Unit, usize>, + context: &DebugInputContext>, +) -> Result { // FIXME remove recursion. if let Some(AttributeValue::UnitRef(ref offset)) = type_entry.attr_value(gimli::DW_AT_type)? { let mut entries = unit.entries_at_offset(*offset)?; @@ -107,20 +103,17 @@ enum WebAssemblyPtrKind { /// /// Notice that "resolve_vmctx_memory_ptr" is external/builtin /// subprogram that is not part of Wasm code. -fn replace_pointer_type( +fn replace_pointer_type( parent_id: write::UnitEntryId, kind: WebAssemblyPtrKind, comp_unit: &mut write::Unit, wp_die_id: write::UnitEntryId, - pointer_type_entry: &DebuggingInformationEntry, - unit: &Unit, - context: &DebugInputContext, + pointer_type_entry: &DebuggingInformationEntry>, + unit: &Unit, usize>, + context: &DebugInputContext>, out_strings: &mut write::StringTable, pending_die_refs: &mut PendingUnitRefs, -) -> Result -where - R: Reader, -{ +) -> Result { const WASM_PTR_LEN: u8 = 4; macro_rules! add_tag { @@ -245,7 +238,7 @@ where Ok(wrapper_die_id) } -fn is_dead_code(entry: &DebuggingInformationEntry) -> bool { +fn is_dead_code(entry: &DebuggingInformationEntry>) -> bool { const TOMBSTONE: u64 = u32::MAX as u64; match entry.attr_value(gimli::DW_AT_low_pc) { @@ -254,24 +247,20 @@ fn is_dead_code(entry: &DebuggingInformationEntry) -> bool { } } -pub(crate) fn clone_unit<'a, R>( - dwarf: &gimli::Dwarf, - unit: &Unit, - split_unit: Option<&Unit>, - split_dwarf: Option<&gimli::Dwarf>, - context: &DebugInputContext, - addr_tr: &'a AddressTransform, - funcs: &'a CompiledFunctionsMetadata, - memory_offset: &ModuleMemoryOffset, +pub(crate) fn clone_unit( + compilation: &mut Compilation<'_>, + module: StaticModuleIndex, + unit: &Unit, usize>, + split_unit: Option<&Unit, usize>>, + split_dwarf: Option<&gimli::Dwarf>>, + context: &DebugInputContext>, + addr_tr: &AddressTransform, out_encoding: gimli::Encoding, out_units: &mut write::UnitTable, out_strings: &mut write::StringTable, - translated: &mut HashSet, + translated: &mut HashSet, isa: &dyn TargetIsa, -) -> Result, Error> -where - R: Reader, -{ +) -> Result, Error> { let mut die_ref_map = UnitRefsMap::new(); let mut pending_die_refs = PendingUnitRefs::new(); let mut pending_di_refs = PendingDebugInfoRefs::new(); @@ -291,6 +280,9 @@ where } } + let dwarf = &compilation.translations[module].debuginfo.dwarf; + let memory_offset = &compilation.module_memory_offsets[module]; + // Iterate over all of this compilation unit's entries. let mut entries = program_unit.entries(); let (mut comp_unit, unit_id, file_map, file_index_base, cu_low_pc, wp_die_id, vmctx_die_id) = @@ -403,12 +395,11 @@ where let range_builder = RangeInfoBuilder::from_subprogram_die( dwarf, &unit, entry, context, addr_tr, cu_low_pc, )?; - if let RangeInfoBuilder::Function(func_index) = range_builder { - if let Some(frame_info) = get_function_frame_info(memory_offset, funcs, func_index) - { - current_value_range.push(new_stack_len, frame_info); - } - translated.insert(func_index); + if let RangeInfoBuilder::Function(func) = range_builder { + let frame_info = compilation.function_frame_info(module, func); + current_value_range.push(new_stack_len, frame_info); + let (symbol, _) = compilation.function(module, func); + translated.insert(symbol); current_scope_ranges.push(new_stack_len, range_builder.get_ranges(addr_tr)); Some(range_builder) } else { diff --git a/crates/cranelift/src/debug/transform/utils.rs b/crates/cranelift/src/debug/transform/utils.rs index 951833bf4f0d..1ddbfe7d7af5 100644 --- a/crates/cranelift/src/debug/transform/utils.rs +++ b/crates/cranelift/src/debug/transform/utils.rs @@ -1,12 +1,11 @@ use super::address_transform::AddressTransform; use super::expression::{CompiledExpression, FunctionFrameInfo}; use crate::debug::ModuleMemoryOffset; -use crate::CompiledFunctionsMetadata; use anyhow::Error; use cranelift_codegen::isa::TargetIsa; use gimli::write; -use wasmtime_environ::DefinedFuncIndex; use wasmtime_versioned_export_macros::versioned_stringify_ident; + ///Adds internal Wasm utility types DIEs such as WebAssemblyPtr and /// WasmtimeVMContext. /// @@ -164,23 +163,3 @@ pub(crate) fn append_vmctx_info( Ok(()) } - -pub(crate) fn get_function_frame_info<'a, 'b, 'c>( - memory_offset: &ModuleMemoryOffset, - funcs: &'b CompiledFunctionsMetadata, - func_index: DefinedFuncIndex, -) -> Option> -where - 'b: 'a, - 'c: 'a, -{ - if let Some(func) = funcs.get(func_index) { - let frame_info = FunctionFrameInfo { - value_ranges: &func.value_labels_ranges, - memory_offset: memory_offset.clone(), - }; - Some(frame_info) - } else { - None - } -} diff --git a/crates/cranelift/src/debug/write_debuginfo.rs b/crates/cranelift/src/debug/write_debuginfo.rs index e5099a70467f..66b2b1dcc07a 100644 --- a/crates/cranelift/src/debug/write_debuginfo.rs +++ b/crates/cranelift/src/debug/write_debuginfo.rs @@ -1,15 +1,12 @@ pub use crate::debug::transform::transform_dwarf; -use crate::debug::ModuleMemoryOffset; -use crate::CompiledFunctionsMetadata; +use crate::debug::Compilation; use cranelift_codegen::ir::Endianness; use cranelift_codegen::isa::{ unwind::{CfaUnwindInfo, UnwindInfo}, TargetIsa, }; -use cranelift_entity::EntityRef; use gimli::write::{Address, Dwarf, EndianVec, FrameTable, Result, Sections, Writer}; use gimli::{RunTimeEndian, SectionId}; -use wasmtime_environ::DebugInfoData; #[allow(missing_docs)] pub struct DwarfSection { @@ -142,13 +139,13 @@ impl Writer for WriterRelocate { fn create_frame_table( isa: &dyn TargetIsa, - funcs: &CompiledFunctionsMetadata, + compilation: &mut Compilation<'_>, ) -> Option { let mut table = FrameTable::default(); let cie_id = table.add_cie(isa.create_systemv_cie()?); - for (i, metadata) in funcs { + for (_, symbol, metadata) in compilation.functions() { // The CFA-based unwind info will either be natively present, or we // have generated it and placed into the "cfa_unwind_info" auxiliary // field. We shouldn't emit both, though, it'd be wasteful. @@ -161,13 +158,7 @@ fn create_frame_table( } if let Some(info) = unwind_info { - table.add_fde( - cie_id, - info.to_fde(Address::Symbol { - symbol: i.index(), - addend: 0, - }), - ); + table.add_fde(cie_id, info.to_fde(Address::Symbol { symbol, addend: 0 })); } } @@ -176,21 +167,10 @@ fn create_frame_table( pub fn emit_dwarf( isa: &dyn TargetIsa, - debuginfo_data: &DebugInfoData, - funcs: &CompiledFunctionsMetadata, - memory_offset: &ModuleMemoryOffset, - dwarf_package_bytes: Option<&[u8]>, - tunables: &wasmtime_environ::Tunables, + compilation: &mut Compilation<'_>, ) -> anyhow::Result> { - let dwarf = transform_dwarf( - isa, - debuginfo_data, - funcs, - memory_offset, - dwarf_package_bytes, - tunables, - )?; - let frame_table = create_frame_table(isa, funcs); + let dwarf = transform_dwarf(isa, compilation)?; + let frame_table = create_frame_table(isa, compilation); let sections = emit_dwarf_sections(isa, dwarf, frame_table)?; Ok(sections) } diff --git a/crates/cranelift/src/lib.rs b/crates/cranelift/src/lib.rs index 9a977560d7cb..396b97c9c344 100644 --- a/crates/cranelift/src/lib.rs +++ b/crates/cranelift/src/lib.rs @@ -11,9 +11,7 @@ use cranelift_codegen::{ settings, FinalizedMachReloc, FinalizedRelocTarget, MachTrap, }; use cranelift_entity::PrimaryMap; -use cranelift_wasm::{ - DefinedFuncIndex, FuncIndex, WasmFuncType, WasmHeapTopType, WasmHeapType, WasmValType, -}; +use cranelift_wasm::{FuncIndex, WasmFuncType, WasmHeapTopType, WasmHeapType, WasmValType}; use target_lexicon::Architecture; use wasmtime_environ::{ @@ -34,8 +32,6 @@ mod debug; mod func_environ; mod gc; -type CompiledFunctionsMetadata<'a> = PrimaryMap; - /// Trap code used for debug assertions we emit in our JIT code. const DEBUG_ASSERT_TRAP_CODE: u16 = u16::MAX; diff --git a/crates/environ/src/compile/mod.rs b/crates/environ/src/compile/mod.rs index 9a4f80eefe0f..05151c8f1794 100644 --- a/crates/environ/src/compile/mod.rs +++ b/crates/environ/src/compile/mod.rs @@ -5,7 +5,7 @@ use crate::prelude::*; use crate::{obj, Tunables}; use crate::{ BuiltinFunctionIndex, DefinedFuncIndex, FlagValue, FuncIndex, FunctionLoc, ObjectKind, - PrimaryMap, WasmError, WasmFuncType, WasmFunctionInfo, + PrimaryMap, StaticModuleIndex, WasmError, WasmFuncType, WasmFunctionInfo, }; use anyhow::Result; use object::write::{Object, SymbolId}; @@ -353,15 +353,20 @@ pub trait Compiler: Send + Sync { #[cfg(feature = "component-model")] fn component_compiler(&self) -> &dyn crate::component::ComponentCompiler; - /// Appends generated DWARF sections to the `obj` specified for the compiled - /// functions. - fn append_dwarf( + /// Appends generated DWARF sections to the `obj` specified. + /// + /// The `translations` track all compiled functions and `get_func` can be + /// used to acquire the metadata for a particular function within a module. + fn append_dwarf<'a>( &self, obj: &mut Object<'_>, - translation: &ModuleTranslation<'_>, - funcs: &PrimaryMap, - dwarf_package_bytes: Option<&[u8]>, - tunables: &Tunables, + translations: &'a PrimaryMap>, + get_func: &'a dyn Fn( + StaticModuleIndex, + DefinedFuncIndex, + ) -> (SymbolId, &'a (dyn Any + Send)), + dwarf_package_bytes: Option<&'a [u8]>, + tunables: &'a Tunables, ) -> Result<()>; /// Creates a new System V Common Information Entry for the ISA. diff --git a/crates/environ/src/module.rs b/crates/environ/src/module.rs index 7052010896da..cd82115de1c2 100644 --- a/crates/environ/src/module.rs +++ b/crates/environ/src/module.rs @@ -676,6 +676,12 @@ impl Module { func_ref: FuncRefIndex::reserved_value(), }) } + + /// Returns an iterator over all of the defined function indices in this + /// module. + pub fn defined_func_indices(&self) -> impl Iterator { + (0..self.functions.len() - self.num_imported_funcs).map(|i| DefinedFuncIndex::new(i)) + } } /// Type information about functions in a wasm module. diff --git a/crates/wasmtime/src/compile.rs b/crates/wasmtime/src/compile.rs index d06d903435ee..a9811814ef67 100644 --- a/crates/wasmtime/src/compile.rs +++ b/crates/wasmtime/src/compile.rs @@ -710,42 +710,18 @@ impl FunctionIndices { )?; // If requested, generate and add DWARF information. - if tunables.generate_native_debuginfo && - // We can only add DWARF once. Supporting DWARF for components of - // multiple Wasm modules will require merging the DWARF sections - // together. - translations.len() == 1 - { - for (module, translation) in &translations { - let funcs: PrimaryMap<_, _> = self - .indices - .get(&CompileKey::WASM_FUNCTION_KIND) - .map(|xs| { - xs.range( - CompileKey::wasm_function(module, DefinedFuncIndex::from_u32(0)) - ..=CompileKey::wasm_function( - module, - DefinedFuncIndex::from_u32(u32::MAX - 1), - ), - ) - }) - .into_iter() - .flat_map(|x| x) - .map(|(_, x)| { - let i = x.unwrap_function(); - (symbol_ids_and_locs[i].0, &*compiled_funcs[i].1) - }) - .collect(); - if !funcs.is_empty() { - compiler.append_dwarf( - &mut obj, - translation, - &funcs, - dwarf_package_bytes, - tunables, - )?; - } - } + if tunables.generate_native_debuginfo { + compiler.append_dwarf( + &mut obj, + &translations, + &|module, func| { + let bucket = &self.indices[&CompileKey::WASM_FUNCTION_KIND]; + let i = bucket[&CompileKey::wasm_function(module, func)].unwrap_function(); + (symbol_ids_and_locs[i].0, &*compiled_funcs[i].1) + }, + dwarf_package_bytes, + tunables, + )?; } let mut obj = wasmtime_environ::ObjectBuilder::new(obj, tunables); diff --git a/crates/winch/src/compiler.rs b/crates/winch/src/compiler.rs index 5df12b118012..f6438723dc6b 100644 --- a/crates/winch/src/compiler.rs +++ b/crates/winch/src/compiler.rs @@ -9,7 +9,7 @@ use wasmtime_cranelift::{CompiledFunction, ModuleTextBuilder}; use wasmtime_environ::{ AddressMapSection, BuiltinFunctionIndex, CompileError, DefinedFuncIndex, FunctionBodyData, FunctionLoc, ModuleTranslation, ModuleTypesBuilder, PrimaryMap, RelocationTarget, - TrapEncodingBuilder, Tunables, VMOffsets, WasmFunctionInfo, + StaticModuleIndex, TrapEncodingBuilder, Tunables, VMOffsets, WasmFunctionInfo, }; use winch_codegen::{BuiltinFunctions, TargetIsa}; @@ -208,13 +208,16 @@ impl wasmtime_environ::Compiler for Compiler { self.trampolines.component_compiler() } - fn append_dwarf( + fn append_dwarf<'a>( &self, _obj: &mut Object<'_>, - _translation: &ModuleTranslation<'_>, - _funcs: &PrimaryMap, - _dwarf_package: Option<&[u8]>, - _tunables: &wasmtime_environ::Tunables, + _translations: &'a PrimaryMap>, + _get_func: &'a dyn Fn( + StaticModuleIndex, + DefinedFuncIndex, + ) -> (SymbolId, &'a (dyn Any + Send)), + _dwarf_package_bytes: Option<&'a [u8]>, + _tunables: &'a Tunables, ) -> Result<()> { todo!() } diff --git a/tests/all/debug/lldb.rs b/tests/all/debug/lldb.rs index d0c653e0669f..dadf8a154a89 100644 --- a/tests/all/debug/lldb.rs +++ b/tests/all/debug/lldb.rs @@ -356,7 +356,7 @@ check: exited with status = 0 #[test] #[ignore] fn dwarf_simple() -> Result<()> { - for wasm in [DWARF_SIMPLE] { + for wasm in [DWARF_SIMPLE, DWARF_SIMPLE_COMPONENT] { test_dwarf_simple(wasm, &[])?; } Ok(())