mirror of
https://github.com/fluencelabs/wasm-bindgen
synced 2025-06-19 07:51:26 +00:00
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:
@ -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 §ions.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);
|
||||
|
Reference in New Issue
Block a user