From 1e5f659be1946dba1ffe71ec44cbc9f27e0468df Mon Sep 17 00:00:00 2001 From: Brian Carroll Date: Sat, 9 Apr 2022 09:59:23 +0100 Subject: [PATCH] wasm: Use Export section to initialise Name section Works even if the preloaded Name section is missing. --- compiler/gen_wasm/src/backend.rs | 39 +++++++----- compiler/gen_wasm/src/wasm_module/mod.rs | 14 ++++- compiler/gen_wasm/src/wasm_module/sections.rs | 60 +++++++------------ 3 files changed, 56 insertions(+), 57 deletions(-) diff --git a/compiler/gen_wasm/src/backend.rs b/compiler/gen_wasm/src/backend.rs index 03b1541e89..30bb3e2303 100644 --- a/compiler/gen_wasm/src/backend.rs +++ b/compiler/gen_wasm/src/backend.rs @@ -22,12 +22,11 @@ use crate::storage::{Storage, StoredValue, StoredValueKind}; use crate::wasm_module::linking::{DataSymbol, LinkingSegment, WasmObjectSymbol}; use crate::wasm_module::sections::{DataMode, DataSegment}; use crate::wasm_module::{ - code_builder, CodeBuilder, Export, ExportType, LocalId, Signature, SymInfo, ValueType, - WasmModule, + code_builder, CodeBuilder, ExportType, LocalId, Signature, SymInfo, ValueType, WasmModule, }; use crate::{ - copy_memory, round_up_to_alignment, CopyMemoryConfig, Env, DEBUG_LOG_SETTINGS, MEMORY_NAME, - PTR_SIZE, PTR_TYPE, STACK_POINTER_GLOBAL_ID, STACK_POINTER_NAME, TARGET_INFO, + copy_memory, round_up_to_alignment, CopyMemoryConfig, Env, DEBUG_LOG_SETTINGS, PTR_SIZE, + PTR_TYPE, TARGET_INFO, }; pub struct WasmBackend<'a> { @@ -62,22 +61,32 @@ impl<'a> WasmBackend<'a> { fn_index_offset: u32, helper_proc_gen: CodeGenHelp<'a>, ) -> Self { - module.export.append(Export { - name: MEMORY_NAME.as_bytes(), - ty: ExportType::Mem, - index: 0, - }); - module.export.append(Export { - name: STACK_POINTER_NAME.as_bytes(), - ty: ExportType::Global, - index: STACK_POINTER_GLOBAL_ID, - }); + // The preloaded builtins object file exports all functions, but the final app binary doesn't. + // Remove the function exports and use them to populate the Name section (debug info) + let platform_and_builtins_exports = + std::mem::replace(&mut module.export.exports, bumpalo::vec![in env.arena]); + let mut app_exports = Vec::with_capacity_in(32, env.arena); + for ex in platform_and_builtins_exports.into_iter() { + match ex.ty { + ExportType::Func => module.names.append_function(ex.index, ex.name), + _ => app_exports.push(ex), + } + } // The preloaded binary has a global to tell us where its data section ends // Note: We need this to account for zero data (.bss), which doesn't have an explicit DataSegment! - let data_end_idx = module.export.globals_lookup["__data_end".as_bytes()]; + let data_end_name = "__data_end".as_bytes(); + let data_end_idx = app_exports + .iter() + .find(|ex| ex.name == data_end_name) + .map(|ex| ex.index) + .unwrap_or_else(|| { + internal_error!("Preloaded Wasm binary must export global constant `__data_end`") + }); let next_constant_addr = module.global.parse_u32_at_index(data_end_idx); + module.export.exports = app_exports; + WasmBackend { env, interns, diff --git a/compiler/gen_wasm/src/wasm_module/mod.rs b/compiler/gen_wasm/src/wasm_module/mod.rs index ce89c4ee07..4479b2bb6c 100644 --- a/compiler/gen_wasm/src/wasm_module/mod.rs +++ b/compiler/gen_wasm/src/wasm_module/mod.rs @@ -5,7 +5,7 @@ pub mod opcodes; pub mod sections; pub mod serialize; -use bumpalo::Bump; +use bumpalo::{collections::Vec, Bump}; pub use code_builder::{Align, CodeBuilder, LocalId, ValueType, VmSymbolState}; pub use linking::SymInfo; use roc_error_macros::internal_error; @@ -144,7 +144,7 @@ impl<'a> WasmModule<'a> { let global = GlobalSection::preload(arena, bytes, &mut cursor); - let export = ExportSection::preload_globals(arena, bytes, &mut cursor); + let export = ExportSection::preload(arena, bytes, &mut cursor); let start = OpaqueSection::preload(SectionId::Start, arena, bytes, &mut cursor); @@ -190,10 +190,18 @@ impl<'a> WasmModule<'a> { arena: &'a Bump, called_preload_fns: T, ) { + let exported_fn_iter = self + .export + .exports + .iter() + .filter(|ex| ex.ty == ExportType::Func) + .map(|ex| ex.index); + let function_indices = Vec::from_iter_in(exported_fn_iter, arena); + self.code.remove_dead_preloads( arena, self.import.function_count, - &self.export.function_indices, + &function_indices, called_preload_fns, ) } diff --git a/compiler/gen_wasm/src/wasm_module/sections.rs b/compiler/gen_wasm/src/wasm_module/sections.rs index 12a3f2da24..fd6d66ed48 100644 --- a/compiler/gen_wasm/src/wasm_module/sections.rs +++ b/compiler/gen_wasm/src/wasm_module/sections.rs @@ -716,56 +716,35 @@ impl Serialize for Export<'_> { #[derive(Debug)] pub struct ExportSection<'a> { - pub count: u32, - pub bytes: Vec<'a, u8>, - /// List of exported functions to keep during dead-code-elimination - pub function_indices: Vec<'a, u32>, - /// name -> index - pub globals_lookup: MutMap<&'a [u8], u32>, + pub exports: Vec<'a, Export<'a>>, } impl<'a> ExportSection<'a> { const ID: SectionId = SectionId::Export; - pub fn append(&mut self, export: Export) { - export.serialize(&mut self.bytes); - self.count += 1; - if matches!(export.ty, ExportType::Func) { - self.function_indices.push(export.index); - } + pub fn append(&mut self, export: Export<'a>) { + self.exports.push(export); } pub fn size(&self) -> usize { - section_size(&self.bytes) + self.exports + .iter() + .map(|ex| ex.name.len() + 1 + MAX_SIZE_ENCODED_U32) + .sum() } - fn empty(arena: &'a Bump) -> Self { - ExportSection { - count: 0, - bytes: Vec::with_capacity_in(256, arena), - function_indices: Vec::with_capacity_in(4, arena), - globals_lookup: MutMap::default(), - } - } - - /// Preload from object file. Keep only the Global exports, ignore the rest. - pub fn preload_globals(arena: &'a Bump, module_bytes: &[u8], cursor: &mut usize) -> Self { + /// Preload from object file. + pub fn preload(arena: &'a Bump, module_bytes: &[u8], cursor: &mut usize) -> Self { let (num_exports, body_bytes) = parse_section(Self::ID, module_bytes, cursor); - let mut export_section = ExportSection::empty(arena); + let mut export_section = ExportSection { + exports: Vec::with_capacity_in(num_exports as usize, arena), + }; let mut body_cursor = 0; - for _ in 0..num_exports { - let export_start = body_cursor; + while body_cursor < body_bytes.len() { let export = Export::parse(arena, body_bytes, &mut body_cursor); - if matches!(export.ty, ExportType::Global) { - let global_bytes = &body_bytes[export_start..body_cursor]; - export_section.bytes.extend_from_slice(global_bytes); - export_section.count += 1; - export_section - .globals_lookup - .insert(export.name, export.index); - } + export_section.exports.push(export); } export_section @@ -774,10 +753,9 @@ impl<'a> ExportSection<'a> { impl<'a> Serialize for ExportSection<'a> { fn serialize(&self, buffer: &mut T) { - if !self.bytes.is_empty() { + if !self.exports.is_empty() { let header_indices = write_section_header(buffer, Self::ID); - buffer.encode_u32(self.count); - buffer.append_slice(&self.bytes); + self.exports.serialize(buffer); update_section_size(buffer, header_indices); } } @@ -1155,8 +1133,12 @@ impl<'a> NameSection<'a> { } pub fn parse(arena: &'a Bump, module_bytes: &[u8], cursor: &mut usize) -> Self { + // If we're already past the end of the preloaded file then there is no Name section if *cursor >= module_bytes.len() { - internal_error!("NameSection not found in preloaded object file"); + return NameSection { + bytes: bumpalo::vec![in arena], + functions: MutMap::default(), + }; } // Custom section ID