From d7a05129acd54d162668c87e3f41d9bdffff2ee9 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Sat, 14 Jul 2018 11:04:47 -0500 Subject: [PATCH] Improve documentation around `link_this_library` (#471) I've started noticing this in non-LTO builds and initially tried to remove it. I was unsuccessful but decided to better document my adventures to hopefully improve future onlookers! --- crates/backend/src/codegen.rs | 6 +++-- src/convert.rs | 12 ++++++--- src/lib.rs | 50 ++++++++++++++++++++++++++++++++++- 3 files changed, 62 insertions(+), 6 deletions(-) diff --git a/crates/backend/src/codegen.rs b/crates/backend/src/codegen.rs index a83d1923..8813075a 100644 --- a/crates/backend/src/codegen.rs +++ b/crates/backend/src/codegen.rs @@ -452,7 +452,8 @@ impl ToTokens for ast::Export { #[allow(non_snake_case)] #[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] pub extern fn #generated_name(#(#args),*) #ret_ty { - ::wasm_bindgen::__rt::link_this_library(); + // See definition of `link_mem_intrinsics` for what this is doing + ::wasm_bindgen::__rt::link_mem_intrinsics(); let #ret = { let mut __stack = unsafe { ::wasm_bindgen::convert::GlobalStack::new() @@ -794,7 +795,8 @@ impl ToTokens for ast::ImportFunction { #[allow(bad_style)] #[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] #vis fn #rust_name(#me #(#arguments),*) #ret { - ::wasm_bindgen::__rt::link_this_library(); + // See definition of `link_mem_intrinsics` for what this is doing + ::wasm_bindgen::__rt::link_mem_intrinsics(); #[wasm_import_module = "__wbindgen_placeholder__"] extern { fn #import_name(#(#abi_arguments),*) -> #abi_ret; diff --git a/src/convert.rs b/src/convert.rs index 65e652cd..5d2771e4 100644 --- a/src/convert.rs +++ b/src/convert.rs @@ -447,7 +447,13 @@ pub struct GlobalStack { } const GLOBAL_STACK_CAP: usize = 16; -static mut GLOBAL_STACK: [u32; GLOBAL_STACK_CAP] = [0; GLOBAL_STACK_CAP]; + +// Increase the alignment to 8 here because this can be used as a +// BigUint64Array pointer base which requires alignment 8 +#[repr(align(8))] +struct GlobalData([u32; GLOBAL_STACK_CAP]); + +static mut GLOBAL_STACK: GlobalData = GlobalData([0; GLOBAL_STACK_CAP]); impl GlobalStack { #[inline] @@ -461,7 +467,7 @@ impl Stack for GlobalStack { fn push(&mut self, val: u32) { unsafe { assert!(self.next < GLOBAL_STACK_CAP); - GLOBAL_STACK[self.next] = val; + GLOBAL_STACK.0[self.next] = val; self.next += 1; } } @@ -470,7 +476,7 @@ impl Stack for GlobalStack { #[doc(hidden)] #[no_mangle] pub unsafe extern "C" fn __wbindgen_global_argument_ptr() -> *mut u32 { - GLOBAL_STACK.as_mut_ptr() + GLOBAL_STACK.0.as_mut_ptr() } macro_rules! stack_closures { diff --git a/src/lib.rs b/src/lib.rs index ee2d19c6..779fc878 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -687,5 +687,53 @@ pub mod __rt { } } - pub fn link_this_library() {} + /// This is a curious function necessary to get wasm-bindgen working today, + /// and it's a bit of an unfortunate hack. + /// + /// The general problem is that somehow we need the above two symbols to + /// exist in the final output binary (__wbindgen_malloc and + /// __wbindgen_free). These symbols may be called by JS for various + /// bindings, so we for sure need to make sure they're exported. + /// + /// The problem arises, though, when what if no Rust code uses the symbols? + /// For all intents and purposes it looks to LLVM and the linker like the + /// above two symbols are dead code, so they're completely discarded! + /// + /// Specifically what happens is this: + /// + /// * The above two symbols are generated into some object file inside of + /// libwasm_bindgen.rlib + /// * The linker, LLD, will not load this object file unless *some* symbol + /// is loaded from the object. In this case, if the Rust code never calls + /// __wbindgen_malloc or __wbindgen_free then the symbols never get linked + /// in. + /// * Later when `wasm-bindgen` attempts to use the symbols they don't + /// exist, causing an error. + /// + /// This function is a weird hack for this problem. We inject a call to this + /// function in all generated code. Usage of this function should then + /// ensure that the above two intrinsics are translated. + /// + /// Due to how rustc creates object files this function (and anything inside + /// it) will be placed into the same object file as the two intrinsics + /// above. That means if this function is called and referenced we'll pull + /// in the object file and link the intrinsics. + /// + /// Note that this is an #[inline] function to remove the function call + /// overhead we inject in functions, but right now it's unclear how to do + /// this in a zero-cost fashion. The lowest cost seems to be generating a + /// store that can't be optimized away (to a global), which is listed below. + /// + /// Ideas for how to improve this are most welcome! + #[inline] + pub fn link_mem_intrinsics() { + // the above symbols only exist with the `std` feature enabled. + if !cfg!(feature = "std") { + return + } + + use core::sync::atomic::*; + static FOO: AtomicUsize = ATOMIC_USIZE_INIT; + FOO.store(0, Ordering::SeqCst); + } }