From 332beecabe9a1b78515f2d738800cf4fbde704e1 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 1 Oct 2018 15:27:34 -0700 Subject: [PATCH] Add a number of `#[inline]` annotation through crates Adding `#[inline]` will typically improve codegen for optimized builds without LTO (so far the majority in practice) by allowing functions that otherwise couldn't be inlined across codegen units to get inlined across codegen units. Right now `wasm-bindgen` has a lot of functions that are very small and delegate to other functions, but aren't otherwise candidates for inlining because they're concrete. I was poking around in release-mode wasm recently and noticed an alarming number of functions for tiny pieces of functionality, which motivates this patch! --- crates/backend/src/codegen.rs | 28 +++++++++++++++++++++++++ src/closure.rs | 4 ++++ src/convert/impls.rs | 6 ++++++ src/lib.rs | 39 ++++++++++++++++++++++++++++------- 4 files changed, 70 insertions(+), 7 deletions(-) diff --git a/crates/backend/src/codegen.rs b/crates/backend/src/codegen.rs index 85c0f881..8057ec33 100644 --- a/crates/backend/src/codegen.rs +++ b/crates/backend/src/codegen.rs @@ -546,22 +546,26 @@ impl ToTokens for ast::ImportType { impl IntoWasmAbi for #rust_name { type Abi = ::Abi; + #[inline] fn into_abi(self, extra: &mut Stack) -> Self::Abi { self.obj.into_abi(extra) } } impl OptionIntoWasmAbi for #rust_name { + #[inline] fn none() -> Self::Abi { 0 } } impl<'a> OptionIntoWasmAbi for &'a #rust_name { + #[inline] fn none() -> Self::Abi { 0 } } impl FromWasmAbi for #rust_name { type Abi = ::Abi; + #[inline] unsafe fn from_abi(js: Self::Abi, extra: &mut Stack) -> Self { #rust_name { obj: JsValue::from_abi(js, extra), @@ -570,12 +574,14 @@ impl ToTokens for ast::ImportType { } impl OptionFromWasmAbi for #rust_name { + #[inline] fn is_none(abi: &Self::Abi) -> bool { *abi == 0 } } impl<'a> IntoWasmAbi for &'a #rust_name { type Abi = <&'a JsValue as IntoWasmAbi>::Abi; + #[inline] fn into_abi(self, extra: &mut Stack) -> Self::Abi { (&self.obj).into_abi(extra) } @@ -585,6 +591,7 @@ impl ToTokens for ast::ImportType { type Abi = ::Abi; type Anchor = ManuallyDrop<#rust_name>; + #[inline] unsafe fn ref_from_abi(js: Self::Abi, extra: &mut Stack) -> Self::Anchor { let tmp = ::ref_from_abi(js, extra); ManuallyDrop::new(#rust_name { @@ -595,17 +602,20 @@ impl ToTokens for ast::ImportType { // TODO: remove this on the next major version impl From for #rust_name { + #[inline] fn from(obj: JsValue) -> #rust_name { #rust_name { obj } } } impl AsRef for #rust_name { + #[inline] fn as_ref(&self) -> &JsValue { &self.obj } } impl From<#rust_name> for JsValue { + #[inline] fn from(obj: #rust_name) -> JsValue { obj.obj } @@ -630,10 +640,12 @@ impl ToTokens for ast::ImportType { panic!("cannot check instanceof on non-wasm targets"); } + #[inline] fn unchecked_from_js(val: JsValue) -> Self { #rust_name { obj: val } } + #[inline] fn unchecked_from_js_ref(val: &JsValue) -> &Self { // Should be safe because `#rust_name` is a transparent // wrapper around `val` @@ -648,6 +660,7 @@ impl ToTokens for ast::ImportType { for superclass in self.extends.iter() { (quote! { impl From<#rust_name> for #superclass { + #[inline] fn from(obj: #rust_name) -> #superclass { use wasm_bindgen::JsCast; #superclass::unchecked_from_js(obj.into()) @@ -655,6 +668,7 @@ impl ToTokens for ast::ImportType { } impl AsRef<#superclass> for #rust_name { + #[inline] fn as_ref(&self) -> &#superclass { use wasm_bindgen::JsCast; #superclass::unchecked_from_js_ref(self.as_ref()) @@ -724,6 +738,7 @@ impl ToTokens for ast::ImportEnum { type Abi = <::wasm_bindgen::JsValue as ::wasm_bindgen::convert::IntoWasmAbi>::Abi; + #[inline] fn into_abi(self, extra: &mut ::wasm_bindgen::convert::Stack) -> Self::Abi { ::wasm_bindgen::JsValue::from(self).into_abi(extra) } @@ -971,6 +986,7 @@ impl ToTokens for ast::Enum { impl ::wasm_bindgen::convert::IntoWasmAbi for #enum_name { type Abi = u32; + #[inline] fn into_abi(self, _extra: &mut ::wasm_bindgen::convert::Stack) -> u32 { self as u32 } @@ -1135,11 +1151,13 @@ impl ToTokens for ast::Dictionary { // interop w/ JsValue impl From<#name> for JsValue { + #[inline] fn from(val: #name) -> JsValue { val.obj.into() } } impl AsRef for #name { + #[inline] fn as_ref(&self) -> &JsValue { self.obj.as_ref() } } @@ -1152,6 +1170,7 @@ impl ToTokens for ast::Dictionary { impl IntoWasmAbi for #name { type Abi = ::Abi; + #[inline] fn into_abi(self, extra: &mut Stack) -> Self::Abi { self.obj.into_abi(extra) } @@ -1159,6 +1178,7 @@ impl ToTokens for ast::Dictionary { impl<'a> IntoWasmAbi for &'a #name { type Abi = <&'a Object as IntoWasmAbi>::Abi; + #[inline] fn into_abi(self, extra: &mut Stack) -> Self::Abi { (&self.obj).into_abi(extra) } @@ -1166,18 +1186,22 @@ impl ToTokens for ast::Dictionary { impl FromWasmAbi for #name { type Abi = ::Abi; + #[inline] unsafe fn from_abi(abi: Self::Abi, extra: &mut Stack) -> Self { #name { obj: Object::from_abi(abi, extra) } } } impl OptionIntoWasmAbi for #name { + #[inline] fn none() -> Self::Abi { Object::none() } } impl<'a> OptionIntoWasmAbi for &'a #name { + #[inline] fn none() -> Self::Abi { <&'a Object>::none() } } impl OptionFromWasmAbi for #name { + #[inline] fn is_none(abi: &Self::Abi) -> bool { Object::is_none(abi) } } @@ -1185,6 +1209,7 @@ impl ToTokens for ast::Dictionary { type Abi = ::Abi; type Anchor = ManuallyDrop<#name>; + #[inline] unsafe fn ref_from_abi(js: Self::Abi, extra: &mut Stack) -> Self::Anchor { let tmp = ::ref_from_abi(js, extra); ManuallyDrop::new(#name { @@ -1194,14 +1219,17 @@ impl ToTokens for ast::Dictionary { } impl JsCast for #name { + #[inline] fn instanceof(val: &JsValue) -> bool { Object::instanceof(val) } + #[inline] fn unchecked_from_js(val: JsValue) -> Self { #name { obj: Object::unchecked_from_js(val) } } + #[inline] fn unchecked_from_js_ref(val: &JsValue) -> &Self { unsafe { &*(val as *const JsValue as *const #name) } } diff --git a/src/closure.rs b/src/closure.rs index 7ec3b9d2..05e13715 100644 --- a/src/closure.rs +++ b/src/closure.rs @@ -327,6 +327,7 @@ macro_rules! doit { <&Self>::describe(); } + #[inline] fn invoke_fn() -> u32 { #[allow(non_snake_case)] unsafe extern fn invoke<$($var: FromWasmAbi,)* R: ReturnWasmAbi>( @@ -354,6 +355,7 @@ macro_rules! doit { invoke::<$($var,)* R> as u32 } + #[inline] fn destroy_fn() -> u32 { unsafe extern fn destroy<$($var: FromWasmAbi,)* R: ReturnWasmAbi>( a: usize, @@ -376,6 +378,7 @@ macro_rules! doit { <&mut Self>::describe(); } + #[inline] fn invoke_fn() -> u32 { #[allow(non_snake_case)] unsafe extern fn invoke<$($var: FromWasmAbi,)* R: ReturnWasmAbi>( @@ -404,6 +407,7 @@ macro_rules! doit { invoke::<$($var,)* R> as u32 } + #[inline] fn destroy_fn() -> u32 { unsafe extern fn destroy<$($var: FromWasmAbi,)* R: ReturnWasmAbi>( a: usize, diff --git a/src/convert/impls.rs b/src/convert/impls.rs index 5193800f..0c17f22e 100644 --- a/src/convert/impls.rs +++ b/src/convert/impls.rs @@ -77,6 +77,7 @@ macro_rules! type_wasm_native { impl IntoWasmAbi for Option<$t> { type Abi = $r; + #[inline] fn into_abi(self, _extra: &mut Stack) -> $r { match self { None => $r { @@ -94,6 +95,7 @@ macro_rules! type_wasm_native { impl FromWasmAbi for Option<$t> { type Abi = $r; + #[inline] unsafe fn from_abi(js: $r, _extra: &mut Stack) -> Self { if js.present == 0 { None @@ -170,6 +172,7 @@ macro_rules! type_64 { impl IntoWasmAbi for Option<$t> { type Abi = WasmOptional64; + #[inline] fn into_abi(self, _extra: &mut Stack) -> WasmOptional64 { match self { None => WasmOptional64 { @@ -191,6 +194,7 @@ macro_rules! type_64 { impl FromWasmAbi for Option<$t> { type Abi = WasmOptional64; + #[inline] unsafe fn from_abi(js: WasmOptional64, _extra: &mut Stack) -> Self { if js.present == 0 { None @@ -257,6 +261,7 @@ impl FromWasmAbi for char { impl IntoWasmAbi for Option { type Abi = WasmOptionalU32; + #[inline] fn into_abi(self, _extra: &mut Stack) -> WasmOptionalU32 { match self { None => WasmOptionalU32 { @@ -274,6 +279,7 @@ impl IntoWasmAbi for Option { impl FromWasmAbi for Option { type Abi = WasmOptionalU32; + #[inline] unsafe fn from_abi(js: WasmOptionalU32, _extra: &mut Stack) -> Self { if js.present == 0 { None diff --git a/src/lib.rs b/src/lib.rs index 9a79f12c..4f808f47 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -91,6 +91,7 @@ impl JsValue { /// /// The utf-8 string provided is copied to the JS heap and the string will /// be owned by the JS garbage collector. + #[inline] pub fn from_str(s: &str) -> JsValue { unsafe { JsValue { @@ -103,6 +104,7 @@ impl JsValue { /// /// This function creates a JS value representing a number (a heap /// allocated number) and returns a handle to the JS version of it. + #[inline] pub fn from_f64(n: f64) -> JsValue { unsafe { JsValue { @@ -115,22 +117,21 @@ impl JsValue { /// /// This function creates a JS object representing a boolean (a heap /// allocated boolean) and returns a handle to the JS version of it. + #[inline] pub fn from_bool(b: bool) -> JsValue { - JsValue { - idx: if b { JSIDX_TRUE } else { JSIDX_FALSE }, - } + if b { JsValue::TRUE } else { JsValue::FALSE } } /// Creates a new JS value representing `undefined`. + #[inline] pub fn undefined() -> JsValue { - JsValue { - idx: JSIDX_UNDEFINED, - } + JsValue::UNDEFINED } /// Creates a new JS value representing `null`. + #[inline] pub fn null() -> JsValue { - JsValue { idx: JSIDX_NULL } + JsValue::NULL } /// Creates a new JS symbol with the optional description specified. @@ -263,50 +264,59 @@ impl JsValue { } /// Tests whether this JS value is `null` + #[inline] pub fn is_null(&self) -> bool { unsafe { __wbindgen_is_null(self.idx) == 1 } } /// Tests whether this JS value is `undefined` + #[inline] pub fn is_undefined(&self) -> bool { unsafe { __wbindgen_is_undefined(self.idx) == 1 } } /// Tests whether the type of this JS value is `symbol` + #[inline] pub fn is_symbol(&self) -> bool { unsafe { __wbindgen_is_symbol(self.idx) == 1 } } /// Tests whether `typeof self == "object" && self !== null`. + #[inline] pub fn is_object(&self) -> bool { unsafe { __wbindgen_is_object(self.idx) == 1 } } /// Tests whether the type of this JS value is `function`. + #[inline] pub fn is_function(&self) -> bool { unsafe { __wbindgen_is_function(self.idx) == 1 } } } impl PartialEq for JsValue { + #[inline] fn eq(&self, other: &JsValue) -> bool { unsafe { __wbindgen_jsval_eq(self.idx, other.idx) != 0 } } } impl PartialEq for JsValue { + #[inline] fn eq(&self, other: &bool) -> bool { self.as_bool() == Some(*other) } } impl PartialEq for JsValue { + #[inline] fn eq(&self, other: &str) -> bool { *self == JsValue::from_str(other) } } impl<'a> PartialEq<&'a str> for JsValue { + #[inline] fn eq(&self, other: &&'a str) -> bool { >::eq(self, other) } @@ -314,11 +324,13 @@ impl<'a> PartialEq<&'a str> for JsValue { if_std! { impl PartialEq for JsValue { + #[inline] fn eq(&self, other: &String) -> bool { >::eq(self, other) } } impl<'a> PartialEq<&'a String> for JsValue { + #[inline] fn eq(&self, other: &&'a String) -> bool { >::eq(self, other) } @@ -326,6 +338,7 @@ if_std! { } impl<'a> From<&'a str> for JsValue { + #[inline] fn from(s: &'a str) -> JsValue { JsValue::from_str(s) } @@ -333,12 +346,14 @@ impl<'a> From<&'a str> for JsValue { if_std! { impl<'a> From<&'a String> for JsValue { + #[inline] fn from(s: &'a String) -> JsValue { JsValue::from_str(s) } } impl From for JsValue { + #[inline] fn from(s: String) -> JsValue { JsValue::from_str(&s) } @@ -346,6 +361,7 @@ if_std! { } impl From for JsValue { + #[inline] fn from(s: bool) -> JsValue { JsValue::from_bool(s) } @@ -355,6 +371,7 @@ impl<'a, T> From<&'a T> for JsValue where T: JsCast, { + #[inline] fn from(s: &'a T) -> JsValue { s.as_ref().clone() } @@ -364,6 +381,7 @@ impl From> for JsValue where JsValue: From, { + #[inline] fn from(s: Option) -> JsValue { match s { Some(s) => s.into(), @@ -374,18 +392,22 @@ where impl JsCast for JsValue { // everything is a `JsValue`! + #[inline] fn instanceof(_val: &JsValue) -> bool { true } + #[inline] fn unchecked_from_js(val: JsValue) -> Self { val } + #[inline] fn unchecked_from_js_ref(val: &JsValue) -> &Self { val } } impl AsRef for JsValue { + #[inline] fn as_ref(&self) -> &JsValue { self } @@ -394,12 +416,14 @@ impl AsRef for JsValue { macro_rules! numbers { ($($n:ident)*) => ($( impl PartialEq<$n> for JsValue { + #[inline] fn eq(&self, other: &$n) -> bool { self.as_f64() == Some(f64::from(*other)) } } impl From<$n> for JsValue { + #[inline] fn from(n: $n) -> JsValue { JsValue::from_f64(n.into()) } @@ -495,6 +519,7 @@ impl fmt::Debug for JsValue { } impl Drop for JsValue { + #[inline] fn drop(&mut self) { unsafe { // if the first bit is set then this is a stack value, so we for