From d086522df70d3f8c0ad82da6363f17be48d2d7b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20S=C3=A1nchez=20Mu=C3=B1oz?= Date: Thu, 21 Mar 2024 17:54:53 +0100 Subject: [PATCH] Allow `llvm.x86.sse2.pause` instrinsic to be called without SSE2 The instrinsic is compiled to a `pause` instruction, which behaves like a no-op when SSE2 is not available. https://www.felixcloutier.com/x86/pause.html --- src/shims/x86/mod.rs | 13 ++++++++++ src/shims/x86/sse2.rs | 6 ----- .../pass/intrinsics-x86-pause-without-sse2.rs | 25 +++++++++++++++++++ tests/pass/intrinsics-x86-sse2.rs | 5 ++++ 4 files changed, 43 insertions(+), 6 deletions(-) create mode 100644 tests/pass/intrinsics-x86-pause-without-sse2.rs diff --git a/src/shims/x86/mod.rs b/src/shims/x86/mod.rs index 7cd397625d..7b7921219e 100644 --- a/src/shims/x86/mod.rs +++ b/src/shims/x86/mod.rs @@ -88,6 +88,19 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: this.write_immediate(*sub, &this.project_field(dest, 1)?)?; } + // Used to implement the `_mm_pause` function. + // The intrinsic is used to hint the processor that the code is in a spin-loop. + // It is compiled down to a `pause` instruction. When SSE2 is not available, + // the instruction behaves like a no-op, so it is always safe to call the + // intrinsic. + "sse2.pause" => { + let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + // Only exhibit the spin-loop hint behavior when SSE2 is enabled. + if this.tcx.sess.unstable_target_features.contains(&Symbol::intern("sse2")) { + this.yield_active_thread(); + } + } + name if name.starts_with("sse.") => { return sse::EvalContextExt::emulate_x86_sse_intrinsic( this, link_name, abi, args, dest, diff --git a/src/shims/x86/sse2.rs b/src/shims/x86/sse2.rs index 18ff5d809e..eb2cc9d37c 100644 --- a/src/shims/x86/sse2.rs +++ b/src/shims/x86/sse2.rs @@ -580,12 +580,6 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: this.copy_op(&this.project_index(&left, i)?, &this.project_index(&dest, i)?)?; } } - // Used to implement the `_mm_pause` function. - // The intrinsic is used to hint the processor that the code is in a spin-loop. - "pause" => { - let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - this.yield_active_thread(); - } _ => return Ok(EmulateForeignItemResult::NotSupported), } Ok(EmulateForeignItemResult::NeedsJumping) diff --git a/tests/pass/intrinsics-x86-pause-without-sse2.rs b/tests/pass/intrinsics-x86-pause-without-sse2.rs new file mode 100644 index 0000000000..c8b92fd545 --- /dev/null +++ b/tests/pass/intrinsics-x86-pause-without-sse2.rs @@ -0,0 +1,25 @@ +// Ignore everything except x86 and x86_64 +// Any new targets that are added to CI should be ignored here. +// (We cannot use `cfg`-based tricks here since the `target-feature` flags below only work on x86.) +//@ignore-target-aarch64 +//@ignore-target-arm +//@ignore-target-avr +//@ignore-target-s390x +//@ignore-target-thumbv7em +//@ignore-target-wasm32 +//@compile-flags: -C target-feature=-sse2 + +#[cfg(target_arch = "x86")] +use std::arch::x86::*; +#[cfg(target_arch = "x86_64")] +use std::arch::x86_64::*; + +fn main() { + assert!(!is_x86_feature_detected!("sse2")); + + unsafe { + // This is a SSE2 intrinsic, but it behaves as a no-op when SSE2 + // is not available, so it is always safe to call. + _mm_pause(); + } +} diff --git a/tests/pass/intrinsics-x86-sse2.rs b/tests/pass/intrinsics-x86-sse2.rs index e636d6c8aa..e0088b9eb2 100644 --- a/tests/pass/intrinsics-x86-sse2.rs +++ b/tests/pass/intrinsics-x86-sse2.rs @@ -54,6 +54,11 @@ mod tests { } } + fn test_mm_pause() { + unsafe { _mm_pause() } + } + test_mm_pause(); + #[target_feature(enable = "sse2")] unsafe fn test_mm_avg_epu8() { let (a, b) = (_mm_set1_epi8(3), _mm_set1_epi8(9));