More conservatively patch closure descriptors

Previously `wasm-bindgen` would take its `breaks_if_inlined` shims and
attempt to remove them entirely, replacing calls to `breaks_if_inlined`
to the imported closure factories. This worked great in that it would
remove the `breaks_if_inlined` funtion entirely, removing the "cost" of
the `#[inline(never)]`.

Unfortunately as #864 discovered this is "too clever by half". LLVM's
aggressive optimizations won't inline `breaks_if_inlined`, but it may
still change the ABI! We can't replace calls to `breaks_if_inlined` if
the signature changes, because the function its calling has a fixed signature.

This commit cops out a bit and instead of replacing calls to
`breaks_if_inlined` to the imported closure factories, we instead
rewrite calls to `__wbindgen_describe_closure` to the closure factories.
This means that the `breaks_if_inlined` shims do not get removed. It
also means that the closure factory shims have a third and final
argument (what would be the function pointer of the descriptor function)
which is dead and unused.

This should be a functional solution for now and let us iterate on a
true fix later on (if needed). For now the cost of this
`#[inline(never)]` and the extra unused argument should be quite small.

Closes #864
This commit is contained in:
Alex Crichton
2018-09-20 22:24:00 -07:00
parent 70c821b442
commit a117c057fb
2 changed files with 86 additions and 51 deletions

View File

@ -226,14 +226,22 @@ impl Interpreter {
) -> Option<&[u32]> {
// Call the `code_idx` function. This is an internal `#[inline(never)]`
// whose code is completely controlled by the `wasm-bindgen` crate, so
// it should take two arguments and return one (all of which we don't
// care about here). What we're interested in is that while executing
// this function it'll call `__wbindgen_describe_closure` with an
// argument that we look for.
// it should take some arguments (the number of arguments depends on the
// optimization level) and return one (all of which we don't care about
// here). What we're interested in is that while executing this function
// it'll call `__wbindgen_describe_closure` with an argument that we
// look for.
assert!(self.descriptor_table_idx.is_none());
let closure_descriptor_idx = (code_idx + self.imports) as u32;
self.stack.push(0);
self.stack.push(0);
let code_sig = sections.functions.entries()[code_idx].type_ref();
let function_ty = match &sections.types.types()[code_sig as usize] {
Type::Function(t) => t,
};
for _ in 0..function_ty.params().len() {
self.stack.push(0);
}
self.call(closure_descriptor_idx, sections);
assert_eq!(self.stack.len(), 1);
self.stack.pop();
@ -346,8 +354,8 @@ impl Interpreter {
} else if Some(*idx) == self.describe_closure_idx {
self.descriptor_table_idx =
Some(self.stack.pop().unwrap() as u32);
assert_eq!(self.stack.pop(), Some(0));
assert_eq!(self.stack.pop(), Some(0));
self.stack.pop();
self.stack.pop();
self.stack.push(0);
} else {
self.call(*idx, sections);