mirror of
https://github.com/fluencelabs/wasm-bindgen
synced 2025-03-16 18:20:51 +00:00
Add gc support for locals in functions
This commit adds support for running a gc pass over locals in a function. This will remove dead local declarations for a function (completely unused) as well as compact existing entries to ensure that we don't have two local declarations of the same type. While this was initially intended for some future support of emitting shims in `wasm-bindgen`, it turns out this pass is firing quite a lot over existing functions generated by LLVM. Looks like we may see benefit from this today with slightly smaller wasm binaries!
This commit is contained in:
parent
1ec68009e8
commit
fab5c795ae
@ -8,8 +8,9 @@ extern crate parity_wasm;
|
||||
extern crate log;
|
||||
extern crate rustc_demangle;
|
||||
|
||||
use std::mem;
|
||||
use std::collections::HashSet;
|
||||
use std::iter;
|
||||
use std::mem;
|
||||
|
||||
use parity_wasm::elements::*;
|
||||
|
||||
@ -132,6 +133,8 @@ fn run(config: &mut Config, module: &mut Module) {
|
||||
module.sections_mut().remove(i);
|
||||
}
|
||||
}
|
||||
|
||||
gc_locals(module);
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
@ -839,3 +842,124 @@ impl<'a> RemapContext<'a> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn gc_locals(module: &mut Module) {
|
||||
let mut code = None;
|
||||
let mut types = None;
|
||||
let mut functions = None;
|
||||
let mut locals = None;
|
||||
let mut imported_functions = 0;
|
||||
for section in module.sections_mut() {
|
||||
match section {
|
||||
Section::Import(s) => imported_functions = s.functions(),
|
||||
Section::Code(s) => code = Some(s),
|
||||
Section::Function(s) => functions = Some(s),
|
||||
Section::Type(s) => types = Some(s),
|
||||
Section::Name(NameSection::Local(s)) => locals = Some(s),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
let code = match code {
|
||||
Some(s) => s,
|
||||
None => return,
|
||||
};
|
||||
let types = types.unwrap();
|
||||
let functions = functions.unwrap();
|
||||
|
||||
for (i, body) in code.bodies_mut().iter_mut().enumerate() {
|
||||
let ty_idx = functions.entries()[i].type_ref();
|
||||
let ty = match &types.types()[ty_idx as usize] {
|
||||
Type::Function(f) => f,
|
||||
};
|
||||
gc_body((i + imported_functions) as u32, body, ty, &mut locals);
|
||||
}
|
||||
}
|
||||
|
||||
/// Each body of a function has a list of locals, and each local has a type and
|
||||
/// a number of how many locals of this type there are. The purpose of this is
|
||||
/// to collapse something like:
|
||||
///
|
||||
/// Local::new(1, ValueType::I32);
|
||||
/// Local::new(1, ValueType::I32);
|
||||
///
|
||||
/// to ...
|
||||
///
|
||||
/// Local::new(2, ValueType::I32);
|
||||
///
|
||||
/// to encode this a bit more compactly
|
||||
fn gc_body(
|
||||
idx: u32,
|
||||
body: &mut FuncBody,
|
||||
ty: &FunctionType,
|
||||
names: &mut Option<&mut LocalNameSection>,
|
||||
) {
|
||||
let mut local_tys = ty.params().to_vec();
|
||||
for local in body.locals_mut().drain(..) {
|
||||
local_tys.extend(iter::repeat(local.value_type()).take(local.count() as usize));
|
||||
}
|
||||
let mut used = vec![false; local_tys.len()];
|
||||
|
||||
for instr in body.code().elements() {
|
||||
match instr {
|
||||
Instruction::GetLocal(i) => used[*i as usize] = true,
|
||||
Instruction::SetLocal(i) => used[*i as usize] = true,
|
||||
Instruction::TeeLocal(i) => used[*i as usize] = true,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
let mut map = vec![None; local_tys.len()];
|
||||
for i in 0..ty.params().len() {
|
||||
map[i] = Some(i as u32);
|
||||
}
|
||||
let mut next = ty.params().len() as u32;
|
||||
for i in ty.params().len()..used.len() {
|
||||
if !used[i] {
|
||||
continue
|
||||
}
|
||||
// We're using this local, so map it to the next index (the lowest).
|
||||
// Find all other locals with the same type and lump then into our
|
||||
// `Local` declaration.
|
||||
let before = next;
|
||||
map[i] = Some(next);
|
||||
next += 1;
|
||||
for j in i + 1..used.len() {
|
||||
if used[j] && local_tys[i] == local_tys[j] {
|
||||
used[j] = false; // make sure we don't visit this later
|
||||
map[j] = Some(next);
|
||||
next += 1;
|
||||
}
|
||||
}
|
||||
body.locals_mut().push(Local::new((next - before) as u32, local_tys[i]));
|
||||
}
|
||||
|
||||
for instr in body.code_mut().elements_mut() {
|
||||
let get = |i: &u32| {
|
||||
map[*i as usize].unwrap()
|
||||
};
|
||||
match instr {
|
||||
Instruction::GetLocal(i) => *i = get(i),
|
||||
Instruction::SetLocal(i) => *i = get(i),
|
||||
Instruction::TeeLocal(i) => *i = get(i),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(names) = names {
|
||||
remap_local_names(idx, &map, names);
|
||||
}
|
||||
}
|
||||
|
||||
fn remap_local_names(idx: u32, map: &[Option<u32>], names: &mut LocalNameSection) {
|
||||
let prev = match names.local_names_mut().remove(idx) {
|
||||
Some(map) => map,
|
||||
None => return,
|
||||
};
|
||||
let mut new = IndexMap::with_capacity(map.len());
|
||||
for (idx, name) in prev {
|
||||
if let Some(new_idx) = map[idx as usize] {
|
||||
new.insert(new_idx, name);
|
||||
}
|
||||
}
|
||||
names.local_names_mut().insert(idx, new);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user