Remove "Roc host" specifics from Wasm dead code elimination

This commit is contained in:
Brian Carroll 2022-11-16 08:07:22 +00:00
parent 18195c8bd7
commit e57ca0aa12
No known key found for this signature in database
GPG key ID: 5C7B2EC4101703C0
3 changed files with 39 additions and 34 deletions

View file

@ -56,7 +56,8 @@ pub struct WasmBackend<'a> {
module: WasmModule<'a>, module: WasmModule<'a>,
layout_ids: LayoutIds<'a>, layout_ids: LayoutIds<'a>,
pub fn_index_offset: u32, pub fn_index_offset: u32,
called_preload_fns: BitVec<usize>, import_fn_count: u32,
called_fns: BitVec<usize>,
pub proc_lookup: Vec<'a, ProcLookupData<'a>>, pub proc_lookup: Vec<'a, ProcLookupData<'a>>,
host_lookup: Vec<'a, (&'a str, u32)>, host_lookup: Vec<'a, (&'a str, u32)>,
helper_proc_gen: CodeGenHelp<'a>, helper_proc_gen: CodeGenHelp<'a>,
@ -104,9 +105,12 @@ impl<'a> WasmBackend<'a> {
} }
module.link_host_to_app_calls(env.arena, host_to_app_map); module.link_host_to_app_calls(env.arena, host_to_app_map);
let import_fn_count = module.import.function_count();
let host_function_count = module.import.imports.len() let host_function_count = import_fn_count
+ (module.code.dead_import_dummy_count + module.code.function_count) as usize; + module.code.dead_import_dummy_count as usize
+ module.code.function_count as usize;
let mut called_fns = BitVec::repeat(false, host_function_count);
called_fns.extend(std::iter::repeat(true).take(proc_lookup.len()));
WasmBackend { WasmBackend {
env, env,
@ -114,10 +118,10 @@ impl<'a> WasmBackend<'a> {
// Module-level data // Module-level data
module, module,
layout_ids, layout_ids,
fn_index_offset, fn_index_offset,
called_preload_fns: BitVec::repeat(false, host_function_count), import_fn_count: import_fn_count as u32,
called_fns,
proc_lookup, proc_lookup,
host_lookup, host_lookup,
helper_proc_gen, helper_proc_gen,
@ -261,6 +265,8 @@ impl<'a> WasmBackend<'a> {
source, source,
}); });
self.called_fns.push(true);
let linker_symbol = SymInfo::Function(WasmObjectSymbol::ExplicitlyNamed { let linker_symbol = SymInfo::Function(WasmObjectSymbol::ExplicitlyNamed {
flags: 0, flags: 0,
index: wasm_fn_index, index: wasm_fn_index,
@ -278,7 +284,7 @@ impl<'a> WasmBackend<'a> {
self.maybe_call_host_main(); self.maybe_call_host_main();
let fn_table_size = 1 + self.module.element.max_table_index(); let fn_table_size = 1 + self.module.element.max_table_index();
self.module.table.function_table.limits = Limits::MinMax(fn_table_size, fn_table_size); self.module.table.function_table.limits = Limits::MinMax(fn_table_size, fn_table_size);
(self.module, self.called_preload_fns) (self.module, self.called_fns)
} }
/// If the host has a `main` function then we need to insert a `_start` to call it. /// If the host has a `main` function then we need to insert a `_start` to call it.
@ -329,7 +335,7 @@ impl<'a> WasmBackend<'a> {
self.code_builder.build_fn_header_and_footer(&[], 0, None); self.code_builder.build_fn_header_and_footer(&[], 0, None);
self.reset(); self.reset();
self.called_preload_fns.set(main_fn_index as usize, true); self.called_fns.set(main_fn_index as usize, true);
} }
/// Register the debug names of Symbols in a global lookup table /// Register the debug names of Symbols in a global lookup table
@ -1337,10 +1343,9 @@ impl<'a> WasmBackend<'a> {
.find(|(fn_name, _)| *fn_name == name) .find(|(fn_name, _)| *fn_name == name)
.unwrap_or_else(|| panic!("The Roc app tries to call `{}` but I can't find it!", name)); .unwrap_or_else(|| panic!("The Roc app tries to call `{}` but I can't find it!", name));
self.called_preload_fns.set(*fn_index as usize, true); self.called_fns.set(*fn_index as usize, true);
let host_import_count = self.fn_index_offset - self.module.code.function_count; if *fn_index < self.import_fn_count {
if *fn_index < host_import_count {
self.code_builder self.code_builder
.call_import(*fn_index, num_wasm_args, has_return_val); .call_import(*fn_index, num_wasm_args, has_return_val);
} else { } else {

View file

@ -177,17 +177,17 @@ impl<'a> WasmModule<'a> {
}) })
} }
pub fn eliminate_dead_code(&mut self, arena: &'a Bump, called_host_fns: BitVec<usize>) { pub fn eliminate_dead_code(&mut self, arena: &'a Bump, called_fns: BitVec<usize>) {
if DEBUG_SETTINGS.skip_dead_code_elim { if DEBUG_SETTINGS.skip_dead_code_elim {
return; return;
} }
// //
// Mark all live host functions // Mark all live functions
// //
let import_count = self.import.imports.len(); let import_count = self.import.imports.len();
let host_fn_min = import_count as u32 + self.code.dead_import_dummy_count; let fn_index_min = import_count as u32 + self.code.dead_import_dummy_count;
let host_fn_max = host_fn_min + self.code.function_count; let fn_index_max = called_fns.len() as u32;
// All functions exported to JS must be kept alive // All functions exported to JS must be kept alive
let exported_fns = self let exported_fns = self
@ -213,13 +213,13 @@ impl<'a> WasmModule<'a> {
); );
// Trace callees of the live functions, and mark those as live too // Trace callees of the live functions, and mark those as live too
let live_flags = self.trace_live_host_functions( let live_flags = self.trace_live_functions(
arena, arena,
called_host_fns, called_fns,
exported_fns, exported_fns,
indirect_callees_and_signatures, indirect_callees_and_signatures,
host_fn_min, fn_index_min,
host_fn_max, fn_index_max,
); );
// //
@ -267,9 +267,9 @@ impl<'a> WasmModule<'a> {
self.names.function_names[old_index].1 = new_name; self.names.function_names[old_index].1 = new_name;
} }
// Relocate calls from host to JS imports // Relocate calls from to JS imports
// This must happen *before* we run dead code elimination on the code section, // This must happen *before* we run dead code elimination on the code section,
// so that byte offsets in the host's linking data will still be valid. // so that byte offsets in the linking data will still be valid.
for (new_index, &old_index) in live_import_fns.iter().enumerate() { for (new_index, &old_index) in live_import_fns.iter().enumerate() {
if new_index == old_index { if new_index == old_index {
continue; continue;
@ -288,7 +288,7 @@ impl<'a> WasmModule<'a> {
// //
let mut buffer = Vec::with_capacity_in(self.code.bytes.len(), arena); let mut buffer = Vec::with_capacity_in(self.code.bytes.len(), arena);
self.code.function_count.serialize(&mut buffer); self.code.function_count.serialize(&mut buffer);
for (i, fn_index) in (host_fn_min..host_fn_max).enumerate() { for (i, fn_index) in (fn_index_min..fn_index_max).enumerate() {
if live_flags[fn_index as usize] { if live_flags[fn_index as usize] {
let code_start = self.code.function_offsets[i] as usize; let code_start = self.code.function_offsets[i] as usize;
let code_end = self.code.function_offsets[i + 1] as usize; let code_end = self.code.function_offsets[i + 1] as usize;
@ -301,14 +301,14 @@ impl<'a> WasmModule<'a> {
self.code.bytes = buffer; self.code.bytes = buffer;
} }
fn trace_live_host_functions<I: Iterator<Item = u32>>( fn trace_live_functions<I: Iterator<Item = u32>>(
&self, &self,
arena: &'a Bump, arena: &'a Bump,
called_host_fns: BitVec<usize>, called_fns: BitVec<usize>,
exported_fns: I, exported_fns: I,
indirect_callees_and_signatures: Vec<'a, (u32, u32)>, indirect_callees_and_signatures: Vec<'a, (u32, u32)>,
host_fn_min: u32, fn_index_min: u32,
host_fn_max: u32, fn_index_max: u32,
) -> BitVec<usize> { ) -> BitVec<usize> {
let reloc_len = self.reloc_code.entries.len(); let reloc_len = self.reloc_code.entries.len();
@ -345,10 +345,10 @@ impl<'a> WasmModule<'a> {
); );
// Loop variables for the main loop below // Loop variables for the main loop below
let mut live_flags = BitVec::repeat(false, called_host_fns.len()); let mut live_flags = BitVec::repeat(false, called_fns.len());
let mut next_pass_fns = BitVec::repeat(false, called_host_fns.len()); let mut next_pass_fns = BitVec::repeat(false, called_fns.len());
let mut current_pass_fns = called_host_fns; let mut current_pass_fns = called_fns;
for index in exported_fns.filter(|i| *i < host_fn_max) { for index in exported_fns.filter(|i| *i < fn_index_max) {
current_pass_fns.set(index as usize, true); current_pass_fns.set(index as usize, true);
} }
@ -360,12 +360,12 @@ impl<'a> WasmModule<'a> {
// For each live function in the current pass // For each live function in the current pass
for fn_index in current_pass_fns.iter_ones() { for fn_index in current_pass_fns.iter_ones() {
// Skip JS imports and Roc functions // Skip JS imports and Roc functions
if fn_index < host_fn_min as usize || fn_index >= host_fn_max as usize { if fn_index < fn_index_min as usize || fn_index >= fn_index_max as usize {
continue; continue;
} }
// Find where the function body is // Find where the function body is
let offset_index = fn_index - host_fn_min as usize; let offset_index = fn_index - fn_index_min as usize;
let code_start = self.code.function_offsets[offset_index]; let code_start = self.code.function_offsets[offset_index];
let code_end = self.code.function_offsets[offset_index + 1]; let code_end = self.code.function_offsets[offset_index + 1];

View file

@ -527,8 +527,8 @@ impl<'a> Serialize for FunctionSection<'a> {
* *
* Table section * Table section
* *
* Defines tables used for indirect references to host memory. * Defines tables used for indirect references to external code or data.
* The table *contents* are elsewhere, in the ElementSection. * The table *contents* are in the ElementSection.
* *
*******************************************************************/ *******************************************************************/