diff options
| author | bors <bors@rust-lang.org> | 2019-03-28 05:44:01 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2019-03-28 05:44:01 +0000 |
| commit | 6bfe4b7b51f47ca014d535506cbc5682f00d8d2a (patch) | |
| tree | 28385771e29b4c966bd93f984b147ec2959d7e0f /src | |
| parent | d20e0002725d2b3efff23c97450806b3517ba804 (diff) | |
| parent | 0c127e849487323a9f6be09d25c0da0aeb57314d (diff) | |
| download | rust-6bfe4b7b51f47ca014d535506cbc5682f00d8d2a.tar.gz rust-6bfe4b7b51f47ca014d535506cbc5682f00d8d2a.zip | |
Auto merge of #59336 - gnzlbg:hint_black_box, r=alexcrichton
Moves test::black_box to core::hint and fix black_box on wasm32 and asm.js This changes removes a cyclic dependency between the "test" and "libtest" crates, where "libtest" depends on "test" for "black_box", but "test" depends on "libtest" for everything else. I've chosen the "hint" module because there seems to be enough consensus in the discussion of RFC2360 that this module is where such an intrinsic would belong, but this PR does not implement that RFC! If that RFC ever gets merged, the API, docs, etc. of this API will need to change. This PR just move the implementation of the already existing API. For backwards compatibility reasons I've chosen to also keep the "test" feature gate for these instead of adding a new feature gate. If we change the feature gate, we'll potentially all benchmarks, and while that's something that we could do, it seems unnecessary to do that now - if RFC2360 gets merged, we'll need to do that anyways. Backwards compatibility is also why we continue to re-export "black_box" from the "test" crate. This PR also fixes black_box on the wasm32 target, which now supports inline assembly, and uses volatile loads on the asm.js target. r? @Amanieu (cc @rust-lang/libs)
Diffstat (limited to 'src')
| -rw-r--r-- | src/libcore/hint.rs | 36 | ||||
| -rw-r--r-- | src/libcore/internal_macros.rs | 81 | ||||
| -rw-r--r-- | src/libtest/lib.rs | 18 | ||||
| -rw-r--r-- | src/tools/tidy/src/pal.rs | 4 |
4 files changed, 122 insertions, 17 deletions
diff --git a/src/libcore/hint.rs b/src/libcore/hint.rs index b2f82ef0d17..d1ccc148654 100644 --- a/src/libcore/hint.rs +++ b/src/libcore/hint.rs @@ -91,3 +91,39 @@ pub fn spin_loop() { } } } + +/// A function that is opaque to the optimizer, to allow benchmarks to +/// pretend to use outputs to assist in avoiding dead-code +/// elimination. +/// +/// This function is a no-op, and does not even read from `dummy`. +#[inline] +#[unstable(feature = "test", issue = "27812")] +pub fn black_box<T>(dummy: T) -> T { + cfg_if! { + if #[cfg(any( + target_arch = "asmjs", + all( + target_arch = "wasm32", + target_os = "emscripten" + ) + ))] { + #[inline] + unsafe fn black_box_impl<T>(d: T) -> T { + // these targets do not support inline assembly + let ret = crate::ptr::read_volatile(&d); + crate::mem::forget(d); + ret + } + } else { + #[inline] + unsafe fn black_box_impl<T>(d: T) -> T { + // we need to "use" the argument in some way LLVM can't + // introspect. + asm!("" : : "r"(&d)); + d + } + } + } + unsafe { black_box_impl(dummy) } +} diff --git a/src/libcore/internal_macros.rs b/src/libcore/internal_macros.rs index b5c20582986..ee6b7d3db48 100644 --- a/src/libcore/internal_macros.rs +++ b/src/libcore/internal_macros.rs @@ -119,3 +119,84 @@ macro_rules! impl_fn_for_zst { )+ } } + +/// A macro for defining `#[cfg]` if-else statements. +/// +/// The macro provided by this crate, `cfg_if`, is similar to the `if/elif` C +/// preprocessor macro by allowing definition of a cascade of `#[cfg]` cases, +/// emitting the implementation which matches first. +/// +/// This allows you to conveniently provide a long list `#[cfg]`'d blocks of code +/// without having to rewrite each clause multiple times. +/// +/// # Example +/// +/// ``` +/// #[macro_use] +/// extern crate cfg_if; +/// +/// cfg_if! { +/// if #[cfg(unix)] { +/// fn foo() { /* unix specific functionality */ } +/// } else if #[cfg(target_pointer_width = "32")] { +/// fn foo() { /* non-unix, 32-bit functionality */ } +/// } else { +/// fn foo() { /* fallback implementation */ } +/// } +/// } +/// +/// # fn main() {} +/// ``` +macro_rules! cfg_if { + // match if/else chains with a final `else` + ($( + if #[cfg($($meta:meta),*)] { $($it:item)* } + ) else * else { + $($it2:item)* + }) => { + cfg_if! { + @__items + () ; + $( ( ($($meta),*) ($($it)*) ), )* + ( () ($($it2)*) ), + } + }; + + // match if/else chains lacking a final `else` + ( + if #[cfg($($i_met:meta),*)] { $($i_it:item)* } + $( + else if #[cfg($($e_met:meta),*)] { $($e_it:item)* } + )* + ) => { + cfg_if! { + @__items + () ; + ( ($($i_met),*) ($($i_it)*) ), + $( ( ($($e_met),*) ($($e_it)*) ), )* + ( () () ), + } + }; + + // Internal and recursive macro to emit all the items + // + // Collects all the negated cfgs in a list at the beginning and after the + // semicolon is all the remaining items + (@__items ($($not:meta,)*) ; ) => {}; + (@__items ($($not:meta,)*) ; ( ($($m:meta),*) ($($it:item)*) ), $($rest:tt)*) => { + // Emit all items within one block, applying an approprate #[cfg]. The + // #[cfg] will require all `$m` matchers specified and must also negate + // all previous matchers. + cfg_if! { @__apply cfg(all($($m,)* not(any($($not),*)))), $($it)* } + + // Recurse to emit all other items in `$rest`, and when we do so add all + // our `$m` matchers to the list of `$not` matchers as future emissions + // will have to negate everything we just matched as well. + cfg_if! { @__items ($($not,)* $($m,)*) ; $($rest)* } + }; + + // Internal macro to Apply a cfg attribute to a list of items + (@__apply $m:meta, $($it:item)*) => { + $(#[$m] $it)* + }; +} diff --git a/src/libtest/lib.rs b/src/libtest/lib.rs index cb0ce480e42..5c91c0ec43b 100644 --- a/src/libtest/lib.rs +++ b/src/libtest/lib.rs @@ -27,23 +27,7 @@ pub use libtest::{ TestResult, TrFailed, TrFailedMsg, TrIgnored, TrOk, stats::Summary }; -/// A function that is opaque to the optimizer, to allow benchmarks to -/// pretend to use outputs to assist in avoiding dead-code -/// elimination. -/// -/// This function is a no-op, and does not even read from `dummy`. -#[cfg(not(any(target_arch = "asmjs", target_arch = "wasm32")))] -pub fn black_box<T>(dummy: T) -> T { - // we need to "use" the argument in some way LLVM can't - // introspect. - unsafe { asm!("" : : "r"(&dummy)) } - dummy -} -#[cfg(any(target_arch = "asmjs", target_arch = "wasm32"))] -#[inline(never)] -pub fn black_box<T>(dummy: T) -> T { - dummy -} +pub use std::hint::black_box; #[cfg(test)] mod tests { diff --git a/src/tools/tidy/src/pal.rs b/src/tools/tidy/src/pal.rs index 0be3ec839e9..b725fbe3d74 100644 --- a/src/tools/tidy/src/pal.rs +++ b/src/tools/tidy/src/pal.rs @@ -42,6 +42,10 @@ const EXCEPTION_PATHS: &[&str] = &[ "src/libpanic_abort", "src/libpanic_unwind", "src/libunwind", + // black_box implementation is LLVM-version specific and it uses + // target_os to tell targets with different LLVM-versions appart + // (e.g. `wasm32-unknown-emscripten` vs `wasm32-unknown-unknown`): + "src/libcore/hint.rs", "src/libstd/sys/", // Platform-specific code for std lives here. // This has the trailing slash so that sys_common is not excepted. "src/libstd/os", // Platform-specific public interfaces |
