From be09a1065ad8fe4850464e1f0bac2265c12f690c Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 10 Nov 2023 08:30:19 +0100 Subject: [PATCH 1/8] data_race: link to docs for 'unusual' race conditions --- src/tools/miri/src/diagnostics.rs | 3 ++- src/tools/miri/tests/fail/data_race/mixed_size_read.stderr | 1 + src/tools/miri/tests/fail/data_race/mixed_size_write.stderr | 1 + src/tools/miri/tests/fail/data_race/read_read_race1.stderr | 1 + src/tools/miri/tests/fail/data_race/read_read_race2.stderr | 1 + src/tools/miri/tests/fail/weak_memory/racing_mixed_size.stderr | 1 + .../miri/tests/fail/weak_memory/racing_mixed_size_read.stderr | 1 + 7 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/tools/miri/src/diagnostics.rs b/src/tools/miri/src/diagnostics.rs index fe445c6dcd962..c2ef77100110a 100644 --- a/src/tools/miri/src/diagnostics.rs +++ b/src/tools/miri/src/diagnostics.rs @@ -270,7 +270,8 @@ pub fn report_error<'tcx, 'mir>( DataRace { op1, extra, .. } => { let mut helps = vec![(Some(op1.span), format!("and (1) occurred earlier here"))]; if let Some(extra) = extra { - helps.push((None, format!("{extra}"))) + helps.push((None, format!("{extra}"))); + helps.push((None, format!("see https://doc.rust-lang.org/nightly/std/sync/atomic/index.html#memory-model-for-atomic-accesses for more information about the Rust memory model"))); } helps.push((None, format!("this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior"))); helps.push((None, format!("see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information"))); diff --git a/src/tools/miri/tests/fail/data_race/mixed_size_read.stderr b/src/tools/miri/tests/fail/data_race/mixed_size_read.stderr index cb7dc89359a3f..8988655208a44 100644 --- a/src/tools/miri/tests/fail/data_race/mixed_size_read.stderr +++ b/src/tools/miri/tests/fail/data_race/mixed_size_read.stderr @@ -10,6 +10,7 @@ help: and (1) occurred earlier here LL | a16.load(Ordering::SeqCst); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ = help: overlapping unsynchronized atomic accesses must use the same access size + = help: see https://doc.rust-lang.org/nightly/std/sync/atomic/index.html#memory-model-for-atomic-accesses for more information about the Rust memory model = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information = note: BACKTRACE (of the first span): diff --git a/src/tools/miri/tests/fail/data_race/mixed_size_write.stderr b/src/tools/miri/tests/fail/data_race/mixed_size_write.stderr index b3908e9c6bfdb..55c9011f1b48f 100644 --- a/src/tools/miri/tests/fail/data_race/mixed_size_write.stderr +++ b/src/tools/miri/tests/fail/data_race/mixed_size_write.stderr @@ -10,6 +10,7 @@ help: and (1) occurred earlier here LL | a16.store(1, Ordering::SeqCst); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = help: overlapping unsynchronized atomic accesses must use the same access size + = help: see https://doc.rust-lang.org/nightly/std/sync/atomic/index.html#memory-model-for-atomic-accesses for more information about the Rust memory model = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information = note: BACKTRACE (of the first span): diff --git a/src/tools/miri/tests/fail/data_race/read_read_race1.stderr b/src/tools/miri/tests/fail/data_race/read_read_race1.stderr index 0846a88f362b6..e1009472fee03 100644 --- a/src/tools/miri/tests/fail/data_race/read_read_race1.stderr +++ b/src/tools/miri/tests/fail/data_race/read_read_race1.stderr @@ -10,6 +10,7 @@ help: and (1) occurred earlier here LL | unsafe { ptr.read() }; | ^^^^^^^^^^ = help: overlapping atomic and non-atomic accesses must be synchronized, even if both are read-only + = help: see https://doc.rust-lang.org/nightly/std/sync/atomic/index.html#memory-model-for-atomic-accesses for more information about the Rust memory model = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information = note: BACKTRACE (of the first span): diff --git a/src/tools/miri/tests/fail/data_race/read_read_race2.stderr b/src/tools/miri/tests/fail/data_race/read_read_race2.stderr index c6181cc45b200..22017ae633d97 100644 --- a/src/tools/miri/tests/fail/data_race/read_read_race2.stderr +++ b/src/tools/miri/tests/fail/data_race/read_read_race2.stderr @@ -10,6 +10,7 @@ help: and (1) occurred earlier here LL | a.load(Ordering::SeqCst); | ^^^^^^^^^^^^^^^^^^^^^^^^ = help: overlapping atomic and non-atomic accesses must be synchronized, even if both are read-only + = help: see https://doc.rust-lang.org/nightly/std/sync/atomic/index.html#memory-model-for-atomic-accesses for more information about the Rust memory model = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information = note: BACKTRACE (of the first span): diff --git a/src/tools/miri/tests/fail/weak_memory/racing_mixed_size.stderr b/src/tools/miri/tests/fail/weak_memory/racing_mixed_size.stderr index 03b5a4e4c1700..d35970205f24f 100644 --- a/src/tools/miri/tests/fail/weak_memory/racing_mixed_size.stderr +++ b/src/tools/miri/tests/fail/weak_memory/racing_mixed_size.stderr @@ -10,6 +10,7 @@ help: and (1) occurred earlier here LL | x.store(1, Relaxed); | ^^^^^^^^^^^^^^^^^^^ = help: overlapping unsynchronized atomic accesses must use the same access size + = help: see https://doc.rust-lang.org/nightly/std/sync/atomic/index.html#memory-model-for-atomic-accesses for more information about the Rust memory model = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information = note: BACKTRACE (of the first span): diff --git a/src/tools/miri/tests/fail/weak_memory/racing_mixed_size_read.stderr b/src/tools/miri/tests/fail/weak_memory/racing_mixed_size_read.stderr index 05eba41f4d54f..e85d76951b648 100644 --- a/src/tools/miri/tests/fail/weak_memory/racing_mixed_size_read.stderr +++ b/src/tools/miri/tests/fail/weak_memory/racing_mixed_size_read.stderr @@ -10,6 +10,7 @@ help: and (1) occurred earlier here LL | x.load(Relaxed); | ^^^^^^^^^^^^^^^ = help: overlapping unsynchronized atomic accesses must use the same access size + = help: see https://doc.rust-lang.org/nightly/std/sync/atomic/index.html#memory-model-for-atomic-accesses for more information about the Rust memory model = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information = note: BACKTRACE (of the first span): From be6f27b6860171987c1e52f66995740382b76669 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Thu, 26 Oct 2023 20:38:32 +0100 Subject: [PATCH 2/8] freebsd interceptions update proposal --- src/tools/miri/ci.sh | 2 +- .../src/shims/unix/freebsd/foreign_items.rs | 16 +++++++-- .../miri/tests/pass-dep/shims/pthreads.rs | 34 +++++++++++++++++-- 3 files changed, 45 insertions(+), 7 deletions(-) diff --git a/src/tools/miri/ci.sh b/src/tools/miri/ci.sh index 5078e9eaab382..f0917556c64f8 100755 --- a/src/tools/miri/ci.sh +++ b/src/tools/miri/ci.sh @@ -108,7 +108,7 @@ case $HOST_TARGET in MIRI_TEST_TARGET=aarch64-unknown-linux-gnu run_tests MIRI_TEST_TARGET=aarch64-apple-darwin run_tests MIRI_TEST_TARGET=i686-pc-windows-gnu run_tests - MIRI_TEST_TARGET=x86_64-unknown-freebsd run_tests_minimal hello integer vec panic/panic concurrency/simple atomic env/var + MIRI_TEST_TARGET=x86_64-unknown-freebsd run_tests_minimal hello integer vec panic/panic concurrency/simple pthreads atomic env/var MIRI_TEST_TARGET=aarch64-linux-android run_tests_minimal hello integer vec panic/panic MIRI_TEST_TARGET=wasm32-wasi run_tests_minimal no_std integer strings wasm MIRI_TEST_TARGET=wasm32-unknown-unknown run_tests_minimal no_std integer strings wasm diff --git a/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs b/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs index 869434e8876ed..96e322c4cf536 100644 --- a/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs @@ -29,13 +29,23 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { "pthread_set_name_np" => { let [thread, name] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let max_len = usize::MAX; // freebsd does not seem to have a limit. - let res = this.pthread_setname_np( + let max_len = usize::MAX; // FreeBSD does not seem to have a limit. + // FreeBSD's pthread_set_name_np does not return anything. + this.pthread_setname_np( this.read_scalar(thread)?, this.read_scalar(name)?, max_len, )?; - this.write_scalar(res, dest)?; + } + "pthread_get_name_np" => { + let [thread, name, len] = + this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + // FreeBSD's pthread_get_name_np does not return anything. + this.pthread_getname_np( + this.read_scalar(thread)?, + this.read_scalar(name)?, + this.read_scalar(len)?, + )?; } // errno diff --git a/src/tools/miri/tests/pass-dep/shims/pthreads.rs b/src/tools/miri/tests/pass-dep/shims/pthreads.rs index 80b8d67401ad1..e982508cdbb26 100644 --- a/src/tools/miri/tests/pass-dep/shims/pthreads.rs +++ b/src/tools/miri/tests/pass-dep/shims/pthreads.rs @@ -1,18 +1,26 @@ //@ignore-target-windows: No libc on Windows -use std::ffi::{CStr, CString}; +use std::ffi::CStr; +#[cfg(not(target_os = "freebsd"))] +use std::ffi::CString; use std::thread; fn main() { + #[cfg(not(target_os = "freebsd"))] test_mutex_libc_init_recursive(); + #[cfg(not(target_os = "freebsd"))] test_mutex_libc_init_normal(); + #[cfg(not(target_os = "freebsd"))] test_mutex_libc_init_errorcheck(); + #[cfg(not(target_os = "freebsd"))] test_rwlock_libc_static_initializer(); + test_named_thread_truncation(); #[cfg(target_os = "linux")] test_mutex_libc_static_initializer_recursive(); } +#[cfg(not(target_os = "freebsd"))] fn test_mutex_libc_init_recursive() { unsafe { let mut attr: libc::pthread_mutexattr_t = std::mem::zeroed(); @@ -37,6 +45,7 @@ fn test_mutex_libc_init_recursive() { } } +#[cfg(not(target_os = "freebsd"))] fn test_mutex_libc_init_normal() { unsafe { let mut mutexattr: libc::pthread_mutexattr_t = std::mem::zeroed(); @@ -59,6 +68,7 @@ fn test_mutex_libc_init_normal() { } } +#[cfg(not(target_os = "freebsd"))] fn test_mutex_libc_init_errorcheck() { unsafe { let mut mutexattr: libc::pthread_mutexattr_t = std::mem::zeroed(); @@ -104,6 +114,7 @@ fn test_mutex_libc_static_initializer_recursive() { // Testing the behavior of std::sync::RwLock does not fully exercise the pthread rwlock shims, we // need to go a layer deeper and test the behavior of the libc functions, because // std::sys::unix::rwlock::RWLock itself keeps track of write_locked and num_readers. +#[cfg(not(target_os = "freebsd"))] fn test_rwlock_libc_static_initializer() { let rw = std::cell::UnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER); unsafe { @@ -137,6 +148,14 @@ fn test_named_thread_truncation() { fn set_thread_name(name: &CStr) -> i32 { #[cfg(target_os = "linux")] return unsafe { libc::pthread_setname_np(libc::pthread_self(), name.as_ptr().cast()) }; + #[cfg(target_os = "freebsd")] + unsafe { + // pthread_set_name_np does not return anything only it can fail if the + // thread id is invalid + libc::pthread_set_name_np(libc::pthread_self(), name.as_ptr().cast()) + }; + #[cfg(target_os = "freebsd")] + return 0; #[cfg(target_os = "macos")] return unsafe { libc::pthread_setname_np(name.as_ptr().cast()) }; } @@ -147,16 +166,25 @@ fn test_named_thread_truncation() { // But the system is limited -- make sure we successfully set a truncation. let mut buf = vec![0u8; long_name.len() + 1]; + #[cfg(not(target_os = "freebsd"))] unsafe { libc::pthread_getname_np(libc::pthread_self(), buf.as_mut_ptr().cast(), buf.len()) }; + #[cfg(target_os = "freebsd")] + unsafe { + // pthread_get_name_np does not return anything only it can fail if the + // thread id is invalid + libc::pthread_get_name_np(libc::pthread_self(), buf.as_mut_ptr().cast(), buf.len()) + }; let cstr = CStr::from_bytes_until_nul(&buf).unwrap(); - assert!(cstr.to_bytes().len() >= 15); // POSIX seems to promise at least 15 chars + assert!(cstr.to_bytes().len() >= 15, "name is too short: len={}", cstr.to_bytes().len()); // POSIX seems to promise at least 15 chars assert!(long_name.as_bytes().starts_with(cstr.to_bytes())); // Also test directly calling pthread_setname to check its return value. assert_eq!(set_thread_name(&cstr), 0); - // But with a too long name it should fail. + // But with a too long name it should fail (except on FreeBSD where the + // function has no return, hence cannot indicate failure). + #[cfg(not(target_os = "freebsd"))] assert_ne!(set_thread_name(&CString::new(long_name).unwrap()), 0); }); result.unwrap().join().unwrap(); From 887ffc68eeaf9405cbbb796b923f92f948d3c0bf Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 12 Nov 2023 14:14:04 +0100 Subject: [PATCH 3/8] tweak comments --- src/tools/miri/tests/pass-dep/shims/pthreads.rs | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/tools/miri/tests/pass-dep/shims/pthreads.rs b/src/tools/miri/tests/pass-dep/shims/pthreads.rs index e982508cdbb26..3bb6f83ec2af9 100644 --- a/src/tools/miri/tests/pass-dep/shims/pthreads.rs +++ b/src/tools/miri/tests/pass-dep/shims/pthreads.rs @@ -5,6 +5,8 @@ use std::ffi::CString; use std::thread; fn main() { + test_named_thread_truncation(); + #[cfg(not(target_os = "freebsd"))] test_mutex_libc_init_recursive(); #[cfg(not(target_os = "freebsd"))] @@ -14,8 +16,6 @@ fn main() { #[cfg(not(target_os = "freebsd"))] test_rwlock_libc_static_initializer(); - test_named_thread_truncation(); - #[cfg(target_os = "linux")] test_mutex_libc_static_initializer_recursive(); } @@ -150,12 +150,10 @@ fn test_named_thread_truncation() { return unsafe { libc::pthread_setname_np(libc::pthread_self(), name.as_ptr().cast()) }; #[cfg(target_os = "freebsd")] unsafe { - // pthread_set_name_np does not return anything only it can fail if the - // thread id is invalid - libc::pthread_set_name_np(libc::pthread_self(), name.as_ptr().cast()) + // pthread_set_name_np does not return anything + libc::pthread_set_name_np(libc::pthread_self(), name.as_ptr().cast()); + return 0; }; - #[cfg(target_os = "freebsd")] - return 0; #[cfg(target_os = "macos")] return unsafe { libc::pthread_setname_np(name.as_ptr().cast()) }; } @@ -172,8 +170,6 @@ fn test_named_thread_truncation() { }; #[cfg(target_os = "freebsd")] unsafe { - // pthread_get_name_np does not return anything only it can fail if the - // thread id is invalid libc::pthread_get_name_np(libc::pthread_self(), buf.as_mut_ptr().cast(), buf.len()) }; let cstr = CStr::from_bytes_until_nul(&buf).unwrap(); From ba523be7a88f2fdf113d3cbc97bc243bc9728ee3 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 12 Nov 2023 14:18:36 +0100 Subject: [PATCH 4/8] better error when calling pthread shims on unsupported unixes --- src/tools/miri/src/shims/unix/sync.rs | 70 +++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/src/tools/miri/src/shims/unix/sync.rs b/src/tools/miri/src/shims/unix/sync.rs index 6666ffbd1d578..1a91219e01610 100644 --- a/src/tools/miri/src/shims/unix/sync.rs +++ b/src/tools/miri/src/shims/unix/sync.rs @@ -277,6 +277,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); + if !matches!(&*this.tcx.sess.target.os, "linux" | "macos") { + throw_unsup_format!( + "`pthread_mutexattr_init` is not supported on {}", + this.tcx.sess.target.os + ); + } + let default_kind = this.eval_libc_i32("PTHREAD_MUTEX_DEFAULT"); mutexattr_set_kind(this, attr_op, default_kind)?; @@ -359,6 +366,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); + if !matches!(&*this.tcx.sess.target.os, "linux" | "macos") { + throw_unsup_format!( + "`pthread_mutex_init` is not supported on {}", + this.tcx.sess.target.os + ); + } + let attr = this.read_pointer(attr_op)?; let kind = if this.ptr_is_null(attr)? { this.eval_libc_i32("PTHREAD_MUTEX_DEFAULT") @@ -513,6 +527,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); + if !matches!(&*this.tcx.sess.target.os, "linux" | "macos") { + throw_unsup_format!( + "`pthread_rwlock_rdlock` is not supported on {}", + this.tcx.sess.target.os + ); + } + let id = rwlock_get_id(this, rwlock_op)?; let active_thread = this.get_active_thread(); @@ -531,6 +552,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); + if !matches!(&*this.tcx.sess.target.os, "linux" | "macos") { + throw_unsup_format!( + "`pthread_rwlock_tryrdlock` is not supported on {}", + this.tcx.sess.target.os + ); + } + let id = rwlock_get_id(this, rwlock_op)?; let active_thread = this.get_active_thread(); @@ -548,6 +576,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); + if !matches!(&*this.tcx.sess.target.os, "linux" | "macos") { + throw_unsup_format!( + "`pthread_rwlock_wrlock` is not supported on {}", + this.tcx.sess.target.os + ); + } + let id = rwlock_get_id(this, rwlock_op)?; let active_thread = this.get_active_thread(); @@ -578,6 +613,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); + if !matches!(&*this.tcx.sess.target.os, "linux" | "macos") { + throw_unsup_format!( + "`pthread_rwlock_trywrlock` is not supported on {}", + this.tcx.sess.target.os + ); + } + let id = rwlock_get_id(this, rwlock_op)?; let active_thread = this.get_active_thread(); @@ -595,6 +637,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); + if !matches!(&*this.tcx.sess.target.os, "linux" | "macos") { + throw_unsup_format!( + "`pthread_rwlock_unlock` is not supported on {}", + this.tcx.sess.target.os + ); + } + let id = rwlock_get_id(this, rwlock_op)?; let active_thread = this.get_active_thread(); @@ -614,6 +663,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); + if !matches!(&*this.tcx.sess.target.os, "linux" | "macos") { + throw_unsup_format!( + "`pthread_rwlock_destroy` is not supported on {}", + this.tcx.sess.target.os + ); + } + let id = rwlock_get_id(this, rwlock_op)?; if this.rwlock_is_locked(id) { @@ -638,6 +694,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); + if !matches!(&*this.tcx.sess.target.os, "linux" | "macos") { + throw_unsup_format!( + "`pthread_condattr_init` is not supported on {}", + this.tcx.sess.target.os + ); + } + // The default value of the clock attribute shall refer to the system // clock. // https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_condattr_setclock.html @@ -704,6 +767,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); + if !matches!(&*this.tcx.sess.target.os, "linux" | "macos") { + throw_unsup_format!( + "`pthread_cond_init` is not supported on {}", + this.tcx.sess.target.os + ); + } + let attr = this.read_pointer(attr_op)?; let clock_id = if this.ptr_is_null(attr)? { this.eval_libc_i32("CLOCK_REALTIME") From 7f201f88a4711925671cb43364fa491c05308cfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20S=C3=A1nchez=20Mu=C3=B1oz?= Date: Sun, 12 Nov 2023 16:23:10 +0100 Subject: [PATCH 5/8] Implement roundps and roundpd SSE4.1 intrinsics --- src/tools/miri/src/shims/x86/sse41.rs | 80 +++++++++++++++---- .../miri/tests/pass/intrinsics-x86-sse41.rs | 60 ++++++++++++++ 2 files changed, 124 insertions(+), 16 deletions(-) diff --git a/src/tools/miri/src/shims/x86/sse41.rs b/src/tools/miri/src/shims/x86/sse41.rs index cfa06ded6e681..523f3bfc26f3a 100644 --- a/src/tools/miri/src/shims/x86/sse41.rs +++ b/src/tools/miri/src/shims/x86/sse41.rs @@ -148,6 +148,14 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: round_first::(this, left, right, rounding, dest)?; } + // Used to implement the _mm_floor_ps, _mm_ceil_ps and _mm_round_ps + // functions. Rounds the elements of `op` according to `rounding`. + "round.ps" => { + let [op, rounding] = + this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + + round_all::(this, op, rounding, dest)?; + } // Used to implement the _mm_floor_sd, _mm_ceil_sd and _mm_round_sd // functions. Rounds the first element of `right` according to `rounding` // and copies the remaining elements from `left`. @@ -157,6 +165,14 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: round_first::(this, left, right, rounding, dest)?; } + // Used to implement the _mm_floor_pd, _mm_ceil_pd and _mm_round_pd + // functions. Rounds the elements of `op` according to `rounding`. + "round.pd" => { + let [op, rounding] = + this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + + round_all::(this, op, rounding, dest)?; + } // Used to implement the _mm_minpos_epu16 function. // Find the minimum unsinged 16-bit integer in `op` and // returns its value and position. @@ -283,22 +299,7 @@ fn round_first<'tcx, F: rustc_apfloat::Float>( assert_eq!(dest_len, left_len); assert_eq!(dest_len, right_len); - // The fourth bit of `rounding` only affects the SSE status - // register, which cannot be accessed from Miri (or from Rust, - // for that matter), so we can ignore it. - let rounding = match this.read_scalar(rounding)?.to_i32()? & !0b1000 { - // When the third bit is 0, the rounding mode is determined by the - // first two bits. - 0b000 => rustc_apfloat::Round::NearestTiesToEven, - 0b001 => rustc_apfloat::Round::TowardNegative, - 0b010 => rustc_apfloat::Round::TowardPositive, - 0b011 => rustc_apfloat::Round::TowardZero, - // When the third bit is 1, the rounding mode is determined by the - // SSE status register. Since we do not support modifying it from - // Miri (or Rust), we assume it to be at its default mode (round-to-nearest). - 0b100..=0b111 => rustc_apfloat::Round::NearestTiesToEven, - rounding => throw_unsup_format!("unsupported rounding mode 0x{rounding:02x}"), - }; + let rounding = rounding_from_imm(this.read_scalar(rounding)?.to_i32()?)?; let op0: F = this.read_scalar(&this.project_index(&right, 0)?)?.to_float()?; let res = op0.round_to_integral(rounding).value; @@ -317,3 +318,50 @@ fn round_first<'tcx, F: rustc_apfloat::Float>( Ok(()) } + +// Rounds all elements of `op` according to `rounding`. +fn round_all<'tcx, F: rustc_apfloat::Float>( + this: &mut crate::MiriInterpCx<'_, 'tcx>, + op: &OpTy<'tcx, Provenance>, + rounding: &OpTy<'tcx, Provenance>, + dest: &PlaceTy<'tcx, Provenance>, +) -> InterpResult<'tcx, ()> { + let (op, op_len) = this.operand_to_simd(op)?; + let (dest, dest_len) = this.place_to_simd(dest)?; + + assert_eq!(dest_len, op_len); + + let rounding = rounding_from_imm(this.read_scalar(rounding)?.to_i32()?)?; + + for i in 0..dest_len { + let op: F = this.read_scalar(&this.project_index(&op, i)?)?.to_float()?; + let res = op.round_to_integral(rounding).value; + this.write_scalar( + Scalar::from_uint(res.to_bits(), Size::from_bits(F::BITS)), + &this.project_index(&dest, i)?, + )?; + } + + Ok(()) +} + +/// Gets equivalent `rustc_apfloat::Round` from rounding mode immediate of +/// `round.{ss,sd,ps,pd}` intrinsics. +fn rounding_from_imm<'tcx>(rounding: i32) -> InterpResult<'tcx, rustc_apfloat::Round> { + // The fourth bit of `rounding` only affects the SSE status + // register, which cannot be accessed from Miri (or from Rust, + // for that matter), so we can ignore it. + match rounding & !0b1000 { + // When the third bit is 0, the rounding mode is determined by the + // first two bits. + 0b000 => Ok(rustc_apfloat::Round::NearestTiesToEven), + 0b001 => Ok(rustc_apfloat::Round::TowardNegative), + 0b010 => Ok(rustc_apfloat::Round::TowardPositive), + 0b011 => Ok(rustc_apfloat::Round::TowardZero), + // When the third bit is 1, the rounding mode is determined by the + // SSE status register. Since we do not support modifying it from + // Miri (or Rust), we assume it to be at its default mode (round-to-nearest). + 0b100..=0b111 => Ok(rustc_apfloat::Round::NearestTiesToEven), + rounding => throw_unsup_format!("unsupported rounding mode 0x{rounding:02x}"), + } +} diff --git a/src/tools/miri/tests/pass/intrinsics-x86-sse41.rs b/src/tools/miri/tests/pass/intrinsics-x86-sse41.rs index d5489ffaf4ba1..db106bb9833a5 100644 --- a/src/tools/miri/tests/pass/intrinsics-x86-sse41.rs +++ b/src/tools/miri/tests/pass/intrinsics-x86-sse41.rs @@ -147,6 +147,36 @@ unsafe fn test_sse41() { } test_mm_round_sd(); + #[target_feature(enable = "sse4.1")] + unsafe fn test_mm_round_pd() { + let a = _mm_setr_pd(-1.75, -4.25); + let r = _mm_round_pd::<_MM_FROUND_TO_NEAREST_INT>(a); + let e = _mm_setr_pd(-2.0, -4.0); + assert_eq_m128d(r, e); + + let a = _mm_setr_pd(-1.75, -4.25); + let r = _mm_round_pd::<_MM_FROUND_TO_NEG_INF>(a); + let e = _mm_setr_pd(-2.0, -5.0); + assert_eq_m128d(r, e); + + let a = _mm_setr_pd(-1.75, -4.25); + let r = _mm_round_pd::<_MM_FROUND_TO_POS_INF>(a); + let e = _mm_setr_pd(-1.0, -4.0); + assert_eq_m128d(r, e); + + let a = _mm_setr_pd(-1.75, -4.25); + let r = _mm_round_pd::<_MM_FROUND_TO_ZERO>(a); + let e = _mm_setr_pd(-1.0, -4.0); + assert_eq_m128d(r, e); + + // Assume round-to-nearest by default + let a = _mm_setr_pd(-1.75, -4.25); + let r = _mm_round_pd::<_MM_FROUND_CUR_DIRECTION>(a); + let e = _mm_setr_pd(-2.0, -4.0); + assert_eq_m128d(r, e); + } + test_mm_round_pd(); + #[target_feature(enable = "sse4.1")] unsafe fn test_mm_round_ss() { let a = _mm_setr_ps(1.5, 3.5, 7.5, 15.5); @@ -182,6 +212,36 @@ unsafe fn test_sse41() { } test_mm_round_ss(); + #[target_feature(enable = "sse4.1")] + unsafe fn test_mm_round_ps() { + let a = _mm_setr_ps(-1.75, -4.25, -8.5, -16.5); + let r = _mm_round_ps::<_MM_FROUND_TO_NEAREST_INT>(a); + let e = _mm_setr_ps(-2.0, -4.0, -8.0, -16.0); + assert_eq_m128(r, e); + + let a = _mm_setr_ps(-1.75, -4.25, -8.5, -16.5); + let r = _mm_round_ps::<_MM_FROUND_TO_NEG_INF>(a); + let e = _mm_setr_ps(-2.0, -5.0, -9.0, -17.0); + assert_eq_m128(r, e); + + let a = _mm_setr_ps(-1.75, -4.25, -8.5, -16.5); + let r = _mm_round_ps::<_MM_FROUND_TO_POS_INF>(a); + let e = _mm_setr_ps(-1.0, -4.0, -8.0, -16.0); + assert_eq_m128(r, e); + + let a = _mm_setr_ps(-1.75, -4.25, -8.5, -16.5); + let r = _mm_round_ps::<_MM_FROUND_TO_ZERO>(a); + let e = _mm_setr_ps(-1.0, -4.0, -8.0, -16.0); + assert_eq_m128(r, e); + + // Assume round-to-nearest by default + let a = _mm_setr_ps(-1.75, -4.25, -8.5, -16.5); + let r = _mm_round_ps::<_MM_FROUND_CUR_DIRECTION>(a); + let e = _mm_setr_ps(-2.0, -4.0, -8.0, -16.0); + assert_eq_m128(r, e); + } + test_mm_round_ps(); + #[target_feature(enable = "sse4.1")] unsafe fn test_mm_minpos_epu16() { let a = _mm_setr_epi16(23, 18, 44, 97, 50, 13, 67, 66); From c226533cbd51c5b8057942695a10f8ebbe484126 Mon Sep 17 00:00:00 2001 From: max-heller Date: Sun, 4 Jun 2023 12:08:41 -0400 Subject: [PATCH 6/8] allow allocations referenced by main thread TLS to leak --- src/tools/miri/src/concurrency/thread.rs | 32 +++++++++++++++---- src/tools/miri/src/eval.rs | 7 ++-- src/tools/miri/tests/fail/leak_in_lib_tls.rs | 21 ++++++++++++ .../miri/tests/fail/leak_in_lib_tls.stderr | 32 +++++++++++++++++++ .../miri/tests/fail/leak_in_static_tls.rs | 22 +++++++++++++ .../miri/tests/fail/leak_in_static_tls.stderr | 23 +++++++++++++ .../tls_leak_main_thread_allowed.rs | 21 ++++++++++++ 7 files changed, 148 insertions(+), 10 deletions(-) create mode 100644 src/tools/miri/tests/fail/leak_in_lib_tls.rs create mode 100644 src/tools/miri/tests/fail/leak_in_lib_tls.stderr create mode 100644 src/tools/miri/tests/fail/leak_in_static_tls.rs create mode 100644 src/tools/miri/tests/fail/leak_in_static_tls.stderr create mode 100644 src/tools/miri/tests/pass/concurrency/tls_leak_main_thread_allowed.rs diff --git a/src/tools/miri/src/concurrency/thread.rs b/src/tools/miri/src/concurrency/thread.rs index 9041683fbc46b..6449ed29cf8f7 100644 --- a/src/tools/miri/src/concurrency/thread.rs +++ b/src/tools/miri/src/concurrency/thread.rs @@ -33,6 +33,15 @@ enum SchedulingAction { Sleep(Duration), } +/// What to do with TLS allocations from terminated threads +pub enum TlsAllocAction { + /// Deallocate backing memory of thread-local statics as usual + Deallocate, + /// Skip deallocating backing memory of thread-local statics and consider all memory reachable + /// from them as "allowed to leak" (like global `static`s). + Leak, +} + /// Trait for callbacks that can be executed when some event happens, such as after a timeout. pub trait MachineCallback<'mir, 'tcx>: VisitTags { fn call(&self, ecx: &mut InterpCx<'mir, 'tcx, MiriMachine<'mir, 'tcx>>) -> InterpResult<'tcx>; @@ -1051,7 +1060,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // See if this thread can do something else. match this.run_on_stack_empty()? { Poll::Pending => {} // keep going - Poll::Ready(()) => this.terminate_active_thread()?, + Poll::Ready(()) => + this.terminate_active_thread(TlsAllocAction::Deallocate)?, } } } @@ -1066,21 +1076,29 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } /// Handles thread termination of the active thread: wakes up threads joining on this one, - /// and deallocated thread-local statics. + /// and deals with the thread's thread-local statics according to `tls_alloc_action`. /// /// This is called by the eval loop when a thread's on_stack_empty returns `Ready`. #[inline] - fn terminate_active_thread(&mut self) -> InterpResult<'tcx> { + fn terminate_active_thread(&mut self, tls_alloc_action: TlsAllocAction) -> InterpResult<'tcx> { let this = self.eval_context_mut(); let thread = this.active_thread_mut(); assert!(thread.stack.is_empty(), "only threads with an empty stack can be terminated"); thread.state = ThreadState::Terminated; let current_span = this.machine.current_span(); - for ptr in - this.machine.threads.thread_terminated(this.machine.data_race.as_mut(), current_span) - { - this.deallocate_ptr(ptr.into(), None, MiriMemoryKind::Tls.into())?; + let thread_local_allocations = + this.machine.threads.thread_terminated(this.machine.data_race.as_mut(), current_span); + for ptr in thread_local_allocations { + match tls_alloc_action { + TlsAllocAction::Deallocate => + this.deallocate_ptr(ptr.into(), None, MiriMemoryKind::Tls.into())?, + TlsAllocAction::Leak => + if let Some(alloc) = ptr.provenance.get_alloc_id() { + trace!("Thread-local static leaked and stored as static root: {:?}", alloc); + this.machine.static_roots.push(alloc); + }, + } } Ok(()) } diff --git a/src/tools/miri/src/eval.rs b/src/tools/miri/src/eval.rs index 5b785c0143e4d..1345b22a34a7f 100644 --- a/src/tools/miri/src/eval.rs +++ b/src/tools/miri/src/eval.rs @@ -11,6 +11,7 @@ use log::info; use rustc_middle::ty::Ty; use crate::borrow_tracker::RetagFields; +use crate::concurrency::thread::TlsAllocAction; use crate::diagnostics::report_leaks; use rustc_data_structures::fx::FxHashSet; use rustc_hir::def::Namespace; @@ -244,9 +245,9 @@ impl MainThreadState { // Figure out exit code. let ret_place = this.machine.main_fn_ret_place.clone().unwrap(); let exit_code = this.read_target_isize(&ret_place)?; - // Need to call this ourselves since we are not going to return to the scheduler - // loop, and we want the main thread TLS to not show up as memory leaks. - this.terminate_active_thread()?; + // Deal with our thread-local memory. We do *not* want to actually free it, instead we consider TLS + // to be like a global `static`, so that all memory reached by it is considered to "not leak". + this.terminate_active_thread(TlsAllocAction::Leak)?; // Stop interpreter loop. throw_machine_stop!(TerminationInfo::Exit { code: exit_code, leak_check: true }); } diff --git a/src/tools/miri/tests/fail/leak_in_lib_tls.rs b/src/tools/miri/tests/fail/leak_in_lib_tls.rs new file mode 100644 index 0000000000000..996d7ed4a2360 --- /dev/null +++ b/src/tools/miri/tests/fail/leak_in_lib_tls.rs @@ -0,0 +1,21 @@ +//@error-in-other-file: memory leaked +//@normalize-stderr-test: ".*│.*" -> "$$stripped$$" + +use std::cell::Cell; + +pub fn main() { + thread_local! { + static TLS: Cell> = Cell::new(None); + } + + std::thread::spawn(|| { + TLS.with(|cell| { + cell.set(Some(Box::leak(Box::new(123)))); + }); + }) + .join() + .unwrap(); + + // Imagine the program running for a long time while the thread is gone + // and this memory still sits around, unused -- leaked. +} diff --git a/src/tools/miri/tests/fail/leak_in_lib_tls.stderr b/src/tools/miri/tests/fail/leak_in_lib_tls.stderr new file mode 100644 index 0000000000000..e3c99710f8b9e --- /dev/null +++ b/src/tools/miri/tests/fail/leak_in_lib_tls.stderr @@ -0,0 +1,32 @@ +error: memory leaked: ALLOC (Rust heap, size: 4, align: 4), allocated here: + --> RUSTLIB/alloc/src/alloc.rs:LL:CC + | +LL | __rust_alloc(layout.size(), layout.align()) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: inside `std::alloc::alloc` at RUSTLIB/alloc/src/alloc.rs:LL:CC + = note: inside `std::alloc::Global::alloc_impl` at RUSTLIB/alloc/src/alloc.rs:LL:CC + = note: inside `::allocate` at RUSTLIB/alloc/src/alloc.rs:LL:CC + = note: inside `alloc::alloc::exchange_malloc` at RUSTLIB/alloc/src/alloc.rs:LL:CC + = note: inside `std::boxed::Box::::new` at RUSTLIB/alloc/src/boxed.rs:LL:CC +note: inside closure + --> $DIR/leak_in_lib_tls.rs:LL:CC + | +LL | cell.set(Some(Box::leak(Box::new(123)))); + | ^^^^^^^^^^^^^ + = note: inside `std::thread::LocalKey::>>::try_with::<{closure@$DIR/leak_in_lib_tls.rs:LL:CC}, ()>` at RUSTLIB/std/src/thread/local.rs:LL:CC + = note: inside `std::thread::LocalKey::>>::with::<{closure@$DIR/leak_in_lib_tls.rs:LL:CC}, ()>` at RUSTLIB/std/src/thread/local.rs:LL:CC +note: inside closure + --> $DIR/leak_in_lib_tls.rs:LL:CC + | +LL | / TLS.with(|cell| { +LL | | cell.set(Some(Box::leak(Box::new(123)))); +LL | | }); + | |__________^ + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +note: the evaluated program leaked memory, pass `-Zmiri-ignore-leaks` to disable this check + +error: aborting due to previous error + diff --git a/src/tools/miri/tests/fail/leak_in_static_tls.rs b/src/tools/miri/tests/fail/leak_in_static_tls.rs new file mode 100644 index 0000000000000..637d648fb3fbf --- /dev/null +++ b/src/tools/miri/tests/fail/leak_in_static_tls.rs @@ -0,0 +1,22 @@ +//@error-in-other-file: memory leaked +//@normalize-stderr-test: ".*│.*" -> "$$stripped$$" + +#![feature(thread_local)] + +use std::cell::Cell; + +/// Ensure that leaks through `thread_local` statics *not in the main thread* +/// are detected. +pub fn main() { + #[thread_local] + static TLS: Cell> = Cell::new(None); + + std::thread::spawn(|| { + TLS.set(Some(Box::leak(Box::new(123)))); + }) + .join() + .unwrap(); + + // Imagine the program running for a long time while the thread is gone + // and this memory still sits around, unused -- leaked. +} diff --git a/src/tools/miri/tests/fail/leak_in_static_tls.stderr b/src/tools/miri/tests/fail/leak_in_static_tls.stderr new file mode 100644 index 0000000000000..7ef25a52c1754 --- /dev/null +++ b/src/tools/miri/tests/fail/leak_in_static_tls.stderr @@ -0,0 +1,23 @@ +error: memory leaked: ALLOC (Rust heap, size: 4, align: 4), allocated here: + --> RUSTLIB/alloc/src/alloc.rs:LL:CC + | +LL | __rust_alloc(layout.size(), layout.align()) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: inside `std::alloc::alloc` at RUSTLIB/alloc/src/alloc.rs:LL:CC + = note: inside `std::alloc::Global::alloc_impl` at RUSTLIB/alloc/src/alloc.rs:LL:CC + = note: inside `::allocate` at RUSTLIB/alloc/src/alloc.rs:LL:CC + = note: inside `alloc::alloc::exchange_malloc` at RUSTLIB/alloc/src/alloc.rs:LL:CC + = note: inside `std::boxed::Box::::new` at RUSTLIB/alloc/src/boxed.rs:LL:CC +note: inside closure + --> $DIR/leak_in_static_tls.rs:LL:CC + | +LL | TLS.set(Some(Box::leak(Box::new(123)))); + | ^^^^^^^^^^^^^ + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +note: the evaluated program leaked memory, pass `-Zmiri-ignore-leaks` to disable this check + +error: aborting due to previous error + diff --git a/src/tools/miri/tests/pass/concurrency/tls_leak_main_thread_allowed.rs b/src/tools/miri/tests/pass/concurrency/tls_leak_main_thread_allowed.rs new file mode 100644 index 0000000000000..500b7a80892ca --- /dev/null +++ b/src/tools/miri/tests/pass/concurrency/tls_leak_main_thread_allowed.rs @@ -0,0 +1,21 @@ +//@ignore-target-windows: Windows uses a different mechanism for `thread_local!` +#![feature(thread_local)] + +use std::cell::Cell; + +// Thread-local variables in the main thread are basically like `static` (they live +// as long as the program does), so make sure we treat them the same for leak purposes. +pub fn main() { + thread_local! { + static TLS_KEY: Cell> = Cell::new(None); + } + + TLS_KEY.with(|cell| { + cell.set(Some(Box::leak(Box::new(123)))); + }); + + #[thread_local] + static TLS: Cell> = Cell::new(None); + + TLS.set(Some(Box::leak(Box::new(123)))); +} From 10c9e7594ed9a3936a18ad9cb7c75efe713eb2ed Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 12 Nov 2023 17:12:53 +0100 Subject: [PATCH 7/8] more consistent naming for TLS tests --- ..._const_drop_panic.rs => tls_macro_const_drop_panic.rs} | 0 ...rop_panic.stderr => tls_macro_const_drop_panic.stderr} | 2 +- ...thread_local_drop_panic.rs => tls_macro_drop_panic.rs} | 0 ...ocal_drop_panic.stderr => tls_macro_drop_panic.stderr} | 2 +- .../tls_static_dealloc.rs} | 0 .../tls_static_dealloc.stderr} | 4 ++-- .../tests/fail/{leak_in_lib_tls.rs => tls_macro_leak.rs} | 0 .../{leak_in_lib_tls.stderr => tls_macro_leak.stderr} | 8 ++++---- .../fail/{leak_in_static_tls.rs => tls_static_leak.rs} | 0 .../{leak_in_static_tls.stderr => tls_static_leak.stderr} | 2 +- .../{concurrency => tls}/tls_leak_main_thread_allowed.rs | 2 ++ .../tls_lib_drop.rs => tls/tls_macro_drop.rs} | 0 .../tls_macro_drop.stack.stdout} | 0 .../tls_macro_drop.tree.stdout} | 0 .../tls_macro_drop_single_thread.rs} | 3 ++- .../tls_macro_drop_single_thread.stderr} | 0 .../{concurrency/thread_locals.rs => tls/tls_static.rs} | 0 17 files changed, 13 insertions(+), 10 deletions(-) rename src/tools/miri/tests/fail/panic/{thread_local_const_drop_panic.rs => tls_macro_const_drop_panic.rs} (100%) rename src/tools/miri/tests/fail/panic/{thread_local_const_drop_panic.stderr => tls_macro_const_drop_panic.stderr} (68%) rename src/tools/miri/tests/fail/panic/{thread_local_drop_panic.rs => tls_macro_drop_panic.rs} (100%) rename src/tools/miri/tests/fail/panic/{thread_local_drop_panic.stderr => tls_macro_drop_panic.stderr} (70%) rename src/tools/miri/tests/fail/{concurrency/thread_local_static_dealloc.rs => tls/tls_static_dealloc.rs} (100%) rename src/tools/miri/tests/fail/{concurrency/thread_local_static_dealloc.stderr => tls/tls_static_dealloc.stderr} (84%) rename src/tools/miri/tests/fail/{leak_in_lib_tls.rs => tls_macro_leak.rs} (100%) rename src/tools/miri/tests/fail/{leak_in_lib_tls.stderr => tls_macro_leak.stderr} (81%) rename src/tools/miri/tests/fail/{leak_in_static_tls.rs => tls_static_leak.rs} (100%) rename src/tools/miri/tests/fail/{leak_in_static_tls.stderr => tls_static_leak.stderr} (96%) rename src/tools/miri/tests/pass/{concurrency => tls}/tls_leak_main_thread_allowed.rs (91%) rename src/tools/miri/tests/pass/{concurrency/tls_lib_drop.rs => tls/tls_macro_drop.rs} (100%) rename src/tools/miri/tests/pass/{concurrency/tls_lib_drop.stack.stdout => tls/tls_macro_drop.stack.stdout} (100%) rename src/tools/miri/tests/pass/{concurrency/tls_lib_drop.tree.stdout => tls/tls_macro_drop.tree.stdout} (100%) rename src/tools/miri/tests/pass/{concurrency/tls_lib_drop_single_thread.rs => tls/tls_macro_drop_single_thread.rs} (89%) rename src/tools/miri/tests/pass/{concurrency/tls_lib_drop_single_thread.stderr => tls/tls_macro_drop_single_thread.stderr} (100%) rename src/tools/miri/tests/pass/{concurrency/thread_locals.rs => tls/tls_static.rs} (100%) diff --git a/src/tools/miri/tests/fail/panic/thread_local_const_drop_panic.rs b/src/tools/miri/tests/fail/panic/tls_macro_const_drop_panic.rs similarity index 100% rename from src/tools/miri/tests/fail/panic/thread_local_const_drop_panic.rs rename to src/tools/miri/tests/fail/panic/tls_macro_const_drop_panic.rs diff --git a/src/tools/miri/tests/fail/panic/thread_local_const_drop_panic.stderr b/src/tools/miri/tests/fail/panic/tls_macro_const_drop_panic.stderr similarity index 68% rename from src/tools/miri/tests/fail/panic/thread_local_const_drop_panic.stderr rename to src/tools/miri/tests/fail/panic/tls_macro_const_drop_panic.stderr index 550d009607d13..17e92fec6fd30 100644 --- a/src/tools/miri/tests/fail/panic/thread_local_const_drop_panic.stderr +++ b/src/tools/miri/tests/fail/panic/tls_macro_const_drop_panic.stderr @@ -1,4 +1,4 @@ -thread $NAME panicked at $DIR/thread_local_const_drop_panic.rs:LL:CC: +thread $NAME panicked at $DIR/tls_macro_const_drop_panic.rs:LL:CC: ow fatal runtime error: thread local panicked on drop error: abnormal termination: the program aborted execution diff --git a/src/tools/miri/tests/fail/panic/thread_local_drop_panic.rs b/src/tools/miri/tests/fail/panic/tls_macro_drop_panic.rs similarity index 100% rename from src/tools/miri/tests/fail/panic/thread_local_drop_panic.rs rename to src/tools/miri/tests/fail/panic/tls_macro_drop_panic.rs diff --git a/src/tools/miri/tests/fail/panic/thread_local_drop_panic.stderr b/src/tools/miri/tests/fail/panic/tls_macro_drop_panic.stderr similarity index 70% rename from src/tools/miri/tests/fail/panic/thread_local_drop_panic.stderr rename to src/tools/miri/tests/fail/panic/tls_macro_drop_panic.stderr index 3d6c41faabc40..b1a384bbb5211 100644 --- a/src/tools/miri/tests/fail/panic/thread_local_drop_panic.stderr +++ b/src/tools/miri/tests/fail/panic/tls_macro_drop_panic.stderr @@ -1,4 +1,4 @@ -thread $NAME panicked at $DIR/thread_local_drop_panic.rs:LL:CC: +thread $NAME panicked at $DIR/tls_macro_drop_panic.rs:LL:CC: ow fatal runtime error: thread local panicked on drop error: abnormal termination: the program aborted execution diff --git a/src/tools/miri/tests/fail/concurrency/thread_local_static_dealloc.rs b/src/tools/miri/tests/fail/tls/tls_static_dealloc.rs similarity index 100% rename from src/tools/miri/tests/fail/concurrency/thread_local_static_dealloc.rs rename to src/tools/miri/tests/fail/tls/tls_static_dealloc.rs diff --git a/src/tools/miri/tests/fail/concurrency/thread_local_static_dealloc.stderr b/src/tools/miri/tests/fail/tls/tls_static_dealloc.stderr similarity index 84% rename from src/tools/miri/tests/fail/concurrency/thread_local_static_dealloc.stderr rename to src/tools/miri/tests/fail/tls/tls_static_dealloc.stderr index 7069e8cccfee1..ae8a421ca406b 100644 --- a/src/tools/miri/tests/fail/concurrency/thread_local_static_dealloc.stderr +++ b/src/tools/miri/tests/fail/tls/tls_static_dealloc.stderr @@ -1,5 +1,5 @@ error: Undefined Behavior: memory access failed: ALLOC has been freed, so this pointer is dangling - --> $DIR/thread_local_static_dealloc.rs:LL:CC + --> $DIR/tls_static_dealloc.rs:LL:CC | LL | let _val = *dangling_ptr.0; | ^^^^^^^^^^^^^^^ memory access failed: ALLOC has been freed, so this pointer is dangling @@ -7,7 +7,7 @@ LL | let _val = *dangling_ptr.0; = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information = note: BACKTRACE: - = note: inside `main` at $DIR/thread_local_static_dealloc.rs:LL:CC + = note: inside `main` at $DIR/tls_static_dealloc.rs:LL:CC note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/leak_in_lib_tls.rs b/src/tools/miri/tests/fail/tls_macro_leak.rs similarity index 100% rename from src/tools/miri/tests/fail/leak_in_lib_tls.rs rename to src/tools/miri/tests/fail/tls_macro_leak.rs diff --git a/src/tools/miri/tests/fail/leak_in_lib_tls.stderr b/src/tools/miri/tests/fail/tls_macro_leak.stderr similarity index 81% rename from src/tools/miri/tests/fail/leak_in_lib_tls.stderr rename to src/tools/miri/tests/fail/tls_macro_leak.stderr index e3c99710f8b9e..e9daa78543c9b 100644 --- a/src/tools/miri/tests/fail/leak_in_lib_tls.stderr +++ b/src/tools/miri/tests/fail/tls_macro_leak.stderr @@ -10,14 +10,14 @@ LL | __rust_alloc(layout.size(), layout.align()) = note: inside `alloc::alloc::exchange_malloc` at RUSTLIB/alloc/src/alloc.rs:LL:CC = note: inside `std::boxed::Box::::new` at RUSTLIB/alloc/src/boxed.rs:LL:CC note: inside closure - --> $DIR/leak_in_lib_tls.rs:LL:CC + --> $DIR/tls_macro_leak.rs:LL:CC | LL | cell.set(Some(Box::leak(Box::new(123)))); | ^^^^^^^^^^^^^ - = note: inside `std::thread::LocalKey::>>::try_with::<{closure@$DIR/leak_in_lib_tls.rs:LL:CC}, ()>` at RUSTLIB/std/src/thread/local.rs:LL:CC - = note: inside `std::thread::LocalKey::>>::with::<{closure@$DIR/leak_in_lib_tls.rs:LL:CC}, ()>` at RUSTLIB/std/src/thread/local.rs:LL:CC + = note: inside `std::thread::LocalKey::>>::try_with::<{closure@$DIR/tls_macro_leak.rs:LL:CC}, ()>` at RUSTLIB/std/src/thread/local.rs:LL:CC + = note: inside `std::thread::LocalKey::>>::with::<{closure@$DIR/tls_macro_leak.rs:LL:CC}, ()>` at RUSTLIB/std/src/thread/local.rs:LL:CC note: inside closure - --> $DIR/leak_in_lib_tls.rs:LL:CC + --> $DIR/tls_macro_leak.rs:LL:CC | LL | / TLS.with(|cell| { LL | | cell.set(Some(Box::leak(Box::new(123)))); diff --git a/src/tools/miri/tests/fail/leak_in_static_tls.rs b/src/tools/miri/tests/fail/tls_static_leak.rs similarity index 100% rename from src/tools/miri/tests/fail/leak_in_static_tls.rs rename to src/tools/miri/tests/fail/tls_static_leak.rs diff --git a/src/tools/miri/tests/fail/leak_in_static_tls.stderr b/src/tools/miri/tests/fail/tls_static_leak.stderr similarity index 96% rename from src/tools/miri/tests/fail/leak_in_static_tls.stderr rename to src/tools/miri/tests/fail/tls_static_leak.stderr index 7ef25a52c1754..bcfaf80229a99 100644 --- a/src/tools/miri/tests/fail/leak_in_static_tls.stderr +++ b/src/tools/miri/tests/fail/tls_static_leak.stderr @@ -10,7 +10,7 @@ LL | __rust_alloc(layout.size(), layout.align()) = note: inside `alloc::alloc::exchange_malloc` at RUSTLIB/alloc/src/alloc.rs:LL:CC = note: inside `std::boxed::Box::::new` at RUSTLIB/alloc/src/boxed.rs:LL:CC note: inside closure - --> $DIR/leak_in_static_tls.rs:LL:CC + --> $DIR/tls_static_leak.rs:LL:CC | LL | TLS.set(Some(Box::leak(Box::new(123)))); | ^^^^^^^^^^^^^ diff --git a/src/tools/miri/tests/pass/concurrency/tls_leak_main_thread_allowed.rs b/src/tools/miri/tests/pass/tls/tls_leak_main_thread_allowed.rs similarity index 91% rename from src/tools/miri/tests/pass/concurrency/tls_leak_main_thread_allowed.rs rename to src/tools/miri/tests/pass/tls/tls_leak_main_thread_allowed.rs index 500b7a80892ca..7732e3f921774 100644 --- a/src/tools/miri/tests/pass/concurrency/tls_leak_main_thread_allowed.rs +++ b/src/tools/miri/tests/pass/tls/tls_leak_main_thread_allowed.rs @@ -5,6 +5,8 @@ use std::cell::Cell; // Thread-local variables in the main thread are basically like `static` (they live // as long as the program does), so make sure we treat them the same for leak purposes. +// +// The test covers both TLS statics and the TLS macro. pub fn main() { thread_local! { static TLS_KEY: Cell> = Cell::new(None); diff --git a/src/tools/miri/tests/pass/concurrency/tls_lib_drop.rs b/src/tools/miri/tests/pass/tls/tls_macro_drop.rs similarity index 100% rename from src/tools/miri/tests/pass/concurrency/tls_lib_drop.rs rename to src/tools/miri/tests/pass/tls/tls_macro_drop.rs diff --git a/src/tools/miri/tests/pass/concurrency/tls_lib_drop.stack.stdout b/src/tools/miri/tests/pass/tls/tls_macro_drop.stack.stdout similarity index 100% rename from src/tools/miri/tests/pass/concurrency/tls_lib_drop.stack.stdout rename to src/tools/miri/tests/pass/tls/tls_macro_drop.stack.stdout diff --git a/src/tools/miri/tests/pass/concurrency/tls_lib_drop.tree.stdout b/src/tools/miri/tests/pass/tls/tls_macro_drop.tree.stdout similarity index 100% rename from src/tools/miri/tests/pass/concurrency/tls_lib_drop.tree.stdout rename to src/tools/miri/tests/pass/tls/tls_macro_drop.tree.stdout diff --git a/src/tools/miri/tests/pass/concurrency/tls_lib_drop_single_thread.rs b/src/tools/miri/tests/pass/tls/tls_macro_drop_single_thread.rs similarity index 89% rename from src/tools/miri/tests/pass/concurrency/tls_lib_drop_single_thread.rs rename to src/tools/miri/tests/pass/tls/tls_macro_drop_single_thread.rs index 2766ba36d12b6..f36c460ae5341 100644 --- a/src/tools/miri/tests/pass/concurrency/tls_lib_drop_single_thread.rs +++ b/src/tools/miri/tests/pass/tls/tls_macro_drop_single_thread.rs @@ -1,4 +1,5 @@ -//! Check that destructors of the thread locals are executed on all OSes. +//! Check that destructors of the thread locals are executed on all OSes +//! (even when we do not support concurrency, and cannot run the other test). use std::cell::RefCell; diff --git a/src/tools/miri/tests/pass/concurrency/tls_lib_drop_single_thread.stderr b/src/tools/miri/tests/pass/tls/tls_macro_drop_single_thread.stderr similarity index 100% rename from src/tools/miri/tests/pass/concurrency/tls_lib_drop_single_thread.stderr rename to src/tools/miri/tests/pass/tls/tls_macro_drop_single_thread.stderr diff --git a/src/tools/miri/tests/pass/concurrency/thread_locals.rs b/src/tools/miri/tests/pass/tls/tls_static.rs similarity index 100% rename from src/tools/miri/tests/pass/concurrency/thread_locals.rs rename to src/tools/miri/tests/pass/tls/tls_static.rs From d927f41fa4140245fd210278473d7784816c8f4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20S=C3=A1nchez=20Mu=C3=B1oz?= Date: Sun, 12 Nov 2023 16:48:11 +0100 Subject: [PATCH 8/8] Improve SSE4.1 rounding tests coverage To make sure the rounding direction is working as expected --- .../miri/tests/pass/intrinsics-x86-sse41.rs | 442 ++++++++++++------ 1 file changed, 305 insertions(+), 137 deletions(-) diff --git a/src/tools/miri/tests/pass/intrinsics-x86-sse41.rs b/src/tools/miri/tests/pass/intrinsics-x86-sse41.rs index db106bb9833a5..8c565a2d6e0fc 100644 --- a/src/tools/miri/tests/pass/intrinsics-x86-sse41.rs +++ b/src/tools/miri/tests/pass/intrinsics-x86-sse41.rs @@ -73,174 +73,342 @@ unsafe fn test_sse41() { test_mm_dp_ps(); #[target_feature(enable = "sse4.1")] - unsafe fn test_mm_floor_sd() { - let a = _mm_setr_pd(2.5, 4.5); - let b = _mm_setr_pd(-1.5, -3.5); - let r = _mm_floor_sd(a, b); - let e = _mm_setr_pd(-2.0, 4.5); - assert_eq_m128d(r, e); + unsafe fn test_round_nearest_f32() { + #[target_feature(enable = "sse4.1")] + unsafe fn test(x: f32, res: f32) { + let a = _mm_setr_ps(3.5, 2.5, 1.5, 4.5); + let b = _mm_setr_ps(x, -1.5, -3.5, -2.5); + let e = _mm_setr_ps(res, 2.5, 1.5, 4.5); + let r = _mm_round_ss::<_MM_FROUND_TO_NEAREST_INT>(a, b); + assert_eq_m128(r, e); + // Assume round-to-nearest by default + let r = _mm_round_ss::<_MM_FROUND_CUR_DIRECTION>(a, b); + assert_eq_m128(r, e); + + let a = _mm_set1_ps(x); + let e = _mm_set1_ps(res); + let r = _mm_round_ps::<_MM_FROUND_TO_NEAREST_INT>(a); + assert_eq_m128(r, e); + // Assume round-to-nearest by default + let r = _mm_round_ps::<_MM_FROUND_CUR_DIRECTION>(a); + assert_eq_m128(r, e); + } + + // Test rounding direction + test(-2.5, -2.0); + test(-1.75, -2.0); + test(-1.5, -2.0); + test(-1.25, -1.0); + test(-1.0, -1.0); + test(0.0, 0.0); + test(1.0, 1.0); + test(1.25, 1.0); + test(1.5, 2.0); + test(1.75, 2.0); + test(2.5, 2.0); + + // Test that each element is rounded + let a = _mm_setr_ps(1.5, 3.5, 5.5, 7.5); + let e = _mm_setr_ps(2.0, 4.0, 6.0, 8.0); + let r = _mm_round_ps::<_MM_FROUND_TO_NEAREST_INT>(a); + assert_eq_m128(r, e); + // Assume round-to-nearest by default + let r = _mm_round_ps::<_MM_FROUND_CUR_DIRECTION>(a); + assert_eq_m128(r, e); } - test_mm_floor_sd(); + test_round_nearest_f32(); #[target_feature(enable = "sse4.1")] - unsafe fn test_mm_floor_ss() { - let a = _mm_setr_ps(2.5, 4.5, 8.5, 16.5); - let b = _mm_setr_ps(-1.5, -3.5, -7.5, -15.5); - let r = _mm_floor_ss(a, b); - let e = _mm_setr_ps(-2.0, 4.5, 8.5, 16.5); + unsafe fn test_round_floor_f32() { + #[target_feature(enable = "sse4.1")] + unsafe fn test(x: f32, res: f32) { + let a = _mm_setr_ps(3.5, 2.5, 1.5, 4.5); + let b = _mm_setr_ps(x, -1.5, -3.5, -2.5); + let e = _mm_setr_ps(res, 2.5, 1.5, 4.5); + let r = _mm_floor_ss(a, b); + assert_eq_m128(r, e); + let r = _mm_round_ss::<_MM_FROUND_TO_NEG_INF>(a, b); + assert_eq_m128(r, e); + + let a = _mm_set1_ps(x); + let e = _mm_set1_ps(res); + let r = _mm_floor_ps(a); + assert_eq_m128(r, e); + let r = _mm_round_ps::<_MM_FROUND_TO_NEG_INF>(a); + assert_eq_m128(r, e); + } + + // Test rounding direction + test(-2.5, -3.0); + test(-1.75, -2.0); + test(-1.5, -2.0); + test(-1.25, -2.0); + test(-1.0, -1.0); + test(0.0, 0.0); + test(1.0, 1.0); + test(1.25, 1.0); + test(1.5, 1.0); + test(1.75, 1.0); + test(2.5, 2.0); + + // Test that each element is rounded + let a = _mm_setr_ps(1.5, 3.5, 5.5, 7.5); + let e = _mm_setr_ps(1.0, 3.0, 5.0, 7.0); + let r = _mm_floor_ps(a); + assert_eq_m128(r, e); + let r = _mm_round_ps::<_MM_FROUND_TO_NEG_INF>(a); assert_eq_m128(r, e); } - test_mm_floor_ss(); + test_round_floor_f32(); #[target_feature(enable = "sse4.1")] - unsafe fn test_mm_ceil_sd() { - let a = _mm_setr_pd(1.5, 3.5); - let b = _mm_setr_pd(-2.5, -4.5); - let r = _mm_ceil_sd(a, b); - let e = _mm_setr_pd(-2.0, 3.5); - assert_eq_m128d(r, e); + unsafe fn test_round_ceil_f32() { + #[target_feature(enable = "sse4.1")] + unsafe fn test(x: f32, res: f32) { + let a = _mm_setr_ps(3.5, 2.5, 1.5, 4.5); + let b = _mm_setr_ps(x, -1.5, -3.5, -2.5); + let e = _mm_setr_ps(res, 2.5, 1.5, 4.5); + let r = _mm_ceil_ss(a, b); + assert_eq_m128(r, e); + let r = _mm_round_ss::<_MM_FROUND_TO_POS_INF>(a, b); + assert_eq_m128(r, e); + + let a = _mm_set1_ps(x); + let e = _mm_set1_ps(res); + let r = _mm_ceil_ps(a); + assert_eq_m128(r, e); + let r = _mm_round_ps::<_MM_FROUND_TO_POS_INF>(a); + assert_eq_m128(r, e); + } + + // Test rounding direction + test(-2.5, -2.0); + test(-1.75, -1.0); + test(-1.5, -1.0); + test(-1.25, -1.0); + test(-1.0, -1.0); + test(0.0, 0.0); + test(1.0, 1.0); + test(1.25, 2.0); + test(1.5, 2.0); + test(1.75, 2.0); + test(2.5, 3.0); + + // Test that each element is rounded + let a = _mm_setr_ps(1.5, 3.5, 5.5, 7.5); + let e = _mm_setr_ps(2.0, 4.0, 6.0, 8.0); + let r = _mm_ceil_ps(a); + assert_eq_m128(r, e); + let r = _mm_round_ps::<_MM_FROUND_TO_POS_INF>(a); + assert_eq_m128(r, e); } - test_mm_ceil_sd(); + test_round_ceil_f32(); #[target_feature(enable = "sse4.1")] - unsafe fn test_mm_ceil_ss() { - let a = _mm_setr_ps(1.5, 3.5, 7.5, 15.5); - let b = _mm_setr_ps(-2.5, -4.5, -8.5, -16.5); - let r = _mm_ceil_ss(a, b); - let e = _mm_setr_ps(-2.0, 3.5, 7.5, 15.5); + unsafe fn test_round_trunc_f32() { + #[target_feature(enable = "sse4.1")] + unsafe fn test(x: f32, res: f32) { + let a = _mm_setr_ps(3.5, 2.5, 1.5, 4.5); + let b = _mm_setr_ps(x, -1.5, -3.5, -2.5); + let e = _mm_setr_ps(res, 2.5, 1.5, 4.5); + let r = _mm_round_ss::<_MM_FROUND_TO_ZERO>(a, b); + assert_eq_m128(r, e); + + let a = _mm_set1_ps(x); + let e = _mm_set1_ps(res); + let r = _mm_round_ps::<_MM_FROUND_TO_ZERO>(a); + assert_eq_m128(r, e); + } + + // Test rounding direction + test(-2.5, -2.0); + test(-1.75, -1.0); + test(-1.5, -1.0); + test(-1.25, -1.0); + test(-1.0, -1.0); + test(0.0, 0.0); + test(1.0, 1.0); + test(1.25, 1.0); + test(1.5, 1.0); + test(1.75, 1.0); + test(2.5, 2.0); + + // Test that each element is rounded + let a = _mm_setr_ps(1.5, 3.5, 5.5, 7.5); + let e = _mm_setr_ps(1.0, 3.0, 5.0, 7.0); + let r = _mm_round_ps::<_MM_FROUND_TO_ZERO>(a); assert_eq_m128(r, e); } - test_mm_ceil_ss(); + test_round_trunc_f32(); #[target_feature(enable = "sse4.1")] - unsafe fn test_mm_round_sd() { - let a = _mm_setr_pd(1.5, 3.5); - let b = _mm_setr_pd(-2.5, -4.5); - let r = _mm_round_sd::<_MM_FROUND_TO_NEAREST_INT>(a, b); - let e = _mm_setr_pd(-2.0, 3.5); - assert_eq_m128d(r, e); - - let a = _mm_setr_pd(1.5, 3.5); - let b = _mm_setr_pd(-2.5, -4.5); - let r = _mm_round_sd::<_MM_FROUND_TO_NEG_INF>(a, b); - let e = _mm_setr_pd(-3.0, 3.5); - assert_eq_m128d(r, e); - - let a = _mm_setr_pd(1.5, 3.5); - let b = _mm_setr_pd(-2.5, -4.5); - let r = _mm_round_sd::<_MM_FROUND_TO_POS_INF>(a, b); - let e = _mm_setr_pd(-2.0, 3.5); - assert_eq_m128d(r, e); - + unsafe fn test_round_nearest_f64() { + #[target_feature(enable = "sse4.1")] + unsafe fn test(x: f64, res: f64) { + let a = _mm_setr_pd(3.5, 2.5); + let b = _mm_setr_pd(x, -1.5); + let e = _mm_setr_pd(res, 2.5); + let r = _mm_round_sd::<_MM_FROUND_TO_NEAREST_INT>(a, b); + assert_eq_m128d(r, e); + // Assume round-to-nearest by default + let r = _mm_round_sd::<_MM_FROUND_CUR_DIRECTION>(a, b); + assert_eq_m128d(r, e); + + let a = _mm_set1_pd(x); + let e = _mm_set1_pd(res); + let r = _mm_round_pd::<_MM_FROUND_TO_NEAREST_INT>(a); + assert_eq_m128d(r, e); + // Assume round-to-nearest by default + let r = _mm_round_pd::<_MM_FROUND_CUR_DIRECTION>(a); + assert_eq_m128d(r, e); + } + + // Test rounding direction + test(-2.5, -2.0); + test(-1.75, -2.0); + test(-1.5, -2.0); + test(-1.25, -1.0); + test(-1.0, -1.0); + test(0.0, 0.0); + test(1.0, 1.0); + test(1.25, 1.0); + test(1.5, 2.0); + test(1.75, 2.0); + test(2.5, 2.0); + + // Test that each element is rounded let a = _mm_setr_pd(1.5, 3.5); - let b = _mm_setr_pd(-2.5, -4.5); - let r = _mm_round_sd::<_MM_FROUND_TO_ZERO>(a, b); - let e = _mm_setr_pd(-2.0, 3.5); + let e = _mm_setr_pd(2.0, 4.0); + let r = _mm_round_pd::<_MM_FROUND_TO_NEAREST_INT>(a); assert_eq_m128d(r, e); - // Assume round-to-nearest by default - let a = _mm_setr_pd(1.5, 3.5); - let b = _mm_setr_pd(-2.5, -4.5); - let r = _mm_round_sd::<_MM_FROUND_CUR_DIRECTION>(a, b); - let e = _mm_setr_pd(-2.0, 3.5); + let r = _mm_round_pd::<_MM_FROUND_CUR_DIRECTION>(a); assert_eq_m128d(r, e); } - test_mm_round_sd(); + test_round_nearest_f64(); #[target_feature(enable = "sse4.1")] - unsafe fn test_mm_round_pd() { - let a = _mm_setr_pd(-1.75, -4.25); - let r = _mm_round_pd::<_MM_FROUND_TO_NEAREST_INT>(a); - let e = _mm_setr_pd(-2.0, -4.0); + unsafe fn test_round_floor_f64() { + #[target_feature(enable = "sse4.1")] + unsafe fn test(x: f64, res: f64) { + let a = _mm_setr_pd(3.5, 2.5); + let b = _mm_setr_pd(x, -1.5); + let e = _mm_setr_pd(res, 2.5); + let r = _mm_floor_sd(a, b); + assert_eq_m128d(r, e); + let r = _mm_round_sd::<_MM_FROUND_TO_NEG_INF>(a, b); + assert_eq_m128d(r, e); + + let a = _mm_set1_pd(x); + let e = _mm_set1_pd(res); + let r = _mm_floor_pd(a); + assert_eq_m128d(r, e); + let r = _mm_round_pd::<_MM_FROUND_TO_NEG_INF>(a); + assert_eq_m128d(r, e); + } + + // Test rounding direction + test(-2.5, -3.0); + test(-1.75, -2.0); + test(-1.5, -2.0); + test(-1.25, -2.0); + test(-1.0, -1.0); + test(0.0, 0.0); + test(1.0, 1.0); + test(1.25, 1.0); + test(1.5, 1.0); + test(1.75, 1.0); + test(2.5, 2.0); + + // Test that each element is rounded + let a = _mm_setr_pd(1.5, 3.5); + let e = _mm_setr_pd(1.0, 3.0); + let r = _mm_floor_pd(a); assert_eq_m128d(r, e); - - let a = _mm_setr_pd(-1.75, -4.25); let r = _mm_round_pd::<_MM_FROUND_TO_NEG_INF>(a); - let e = _mm_setr_pd(-2.0, -5.0); - assert_eq_m128d(r, e); - - let a = _mm_setr_pd(-1.75, -4.25); - let r = _mm_round_pd::<_MM_FROUND_TO_POS_INF>(a); - let e = _mm_setr_pd(-1.0, -4.0); - assert_eq_m128d(r, e); - - let a = _mm_setr_pd(-1.75, -4.25); - let r = _mm_round_pd::<_MM_FROUND_TO_ZERO>(a); - let e = _mm_setr_pd(-1.0, -4.0); - assert_eq_m128d(r, e); - - // Assume round-to-nearest by default - let a = _mm_setr_pd(-1.75, -4.25); - let r = _mm_round_pd::<_MM_FROUND_CUR_DIRECTION>(a); - let e = _mm_setr_pd(-2.0, -4.0); assert_eq_m128d(r, e); } - test_mm_round_pd(); + test_round_floor_f64(); #[target_feature(enable = "sse4.1")] - unsafe fn test_mm_round_ss() { - let a = _mm_setr_ps(1.5, 3.5, 7.5, 15.5); - let b = _mm_setr_ps(-1.75, -4.5, -8.5, -16.5); - let r = _mm_round_ss::<_MM_FROUND_TO_NEAREST_INT>(a, b); - let e = _mm_setr_ps(-2.0, 3.5, 7.5, 15.5); - assert_eq_m128(r, e); - - let a = _mm_setr_ps(1.5, 3.5, 7.5, 15.5); - let b = _mm_setr_ps(-1.75, -4.5, -8.5, -16.5); - let r = _mm_round_ss::<_MM_FROUND_TO_NEG_INF>(a, b); - let e = _mm_setr_ps(-2.0, 3.5, 7.5, 15.5); - assert_eq_m128(r, e); - - let a = _mm_setr_ps(1.5, 3.5, 7.5, 15.5); - let b = _mm_setr_ps(-1.75, -4.5, -8.5, -16.5); - let r = _mm_round_ss::<_MM_FROUND_TO_POS_INF>(a, b); - let e = _mm_setr_ps(-1.0, 3.5, 7.5, 15.5); - assert_eq_m128(r, e); - - let a = _mm_setr_ps(1.5, 3.5, 7.5, 15.5); - let b = _mm_setr_ps(-1.75, -4.5, -8.5, -16.5); - let r = _mm_round_ss::<_MM_FROUND_TO_ZERO>(a, b); - let e = _mm_setr_ps(-1.0, 3.5, 7.5, 15.5); - assert_eq_m128(r, e); - - // Assume round-to-nearest by default - let a = _mm_setr_ps(1.5, 3.5, 7.5, 15.5); - let b = _mm_setr_ps(-1.75, -4.5, -8.5, -16.5); - let r = _mm_round_ss::<_MM_FROUND_CUR_DIRECTION>(a, b); - let e = _mm_setr_ps(-2.0, 3.5, 7.5, 15.5); - assert_eq_m128(r, e); + unsafe fn test_round_ceil_f64() { + #[target_feature(enable = "sse4.1")] + unsafe fn test(x: f64, res: f64) { + let a = _mm_setr_pd(3.5, 2.5); + let b = _mm_setr_pd(x, -1.5); + let e = _mm_setr_pd(res, 2.5); + let r = _mm_ceil_sd(a, b); + assert_eq_m128d(r, e); + let r = _mm_round_sd::<_MM_FROUND_TO_POS_INF>(a, b); + assert_eq_m128d(r, e); + + let a = _mm_set1_pd(x); + let e = _mm_set1_pd(res); + let r = _mm_ceil_pd(a); + assert_eq_m128d(r, e); + let r = _mm_round_pd::<_MM_FROUND_TO_POS_INF>(a); + assert_eq_m128d(r, e); + } + + // Test rounding direction + test(-2.5, -2.0); + test(-1.75, -1.0); + test(-1.5, -1.0); + test(-1.25, -1.0); + test(-1.0, -1.0); + test(0.0, 0.0); + test(1.0, 1.0); + test(1.25, 2.0); + test(1.5, 2.0); + test(1.75, 2.0); + test(2.5, 3.0); + + // Test that each element is rounded + let a = _mm_setr_pd(1.5, 3.5); + let e = _mm_setr_pd(2.0, 4.0); + let r = _mm_ceil_pd(a); + assert_eq_m128d(r, e); + let r = _mm_round_pd::<_MM_FROUND_TO_POS_INF>(a); + assert_eq_m128d(r, e); } - test_mm_round_ss(); + test_round_ceil_f64(); #[target_feature(enable = "sse4.1")] - unsafe fn test_mm_round_ps() { - let a = _mm_setr_ps(-1.75, -4.25, -8.5, -16.5); - let r = _mm_round_ps::<_MM_FROUND_TO_NEAREST_INT>(a); - let e = _mm_setr_ps(-2.0, -4.0, -8.0, -16.0); - assert_eq_m128(r, e); - - let a = _mm_setr_ps(-1.75, -4.25, -8.5, -16.5); - let r = _mm_round_ps::<_MM_FROUND_TO_NEG_INF>(a); - let e = _mm_setr_ps(-2.0, -5.0, -9.0, -17.0); - assert_eq_m128(r, e); - - let a = _mm_setr_ps(-1.75, -4.25, -8.5, -16.5); - let r = _mm_round_ps::<_MM_FROUND_TO_POS_INF>(a); - let e = _mm_setr_ps(-1.0, -4.0, -8.0, -16.0); - assert_eq_m128(r, e); - - let a = _mm_setr_ps(-1.75, -4.25, -8.5, -16.5); - let r = _mm_round_ps::<_MM_FROUND_TO_ZERO>(a); - let e = _mm_setr_ps(-1.0, -4.0, -8.0, -16.0); - assert_eq_m128(r, e); - - // Assume round-to-nearest by default - let a = _mm_setr_ps(-1.75, -4.25, -8.5, -16.5); - let r = _mm_round_ps::<_MM_FROUND_CUR_DIRECTION>(a); - let e = _mm_setr_ps(-2.0, -4.0, -8.0, -16.0); - assert_eq_m128(r, e); + unsafe fn test_round_trunc_f64() { + #[target_feature(enable = "sse4.1")] + unsafe fn test(x: f64, res: f64) { + let a = _mm_setr_pd(3.5, 2.5); + let b = _mm_setr_pd(x, -1.5); + let e = _mm_setr_pd(res, 2.5); + let r = _mm_round_sd::<_MM_FROUND_TO_ZERO>(a, b); + assert_eq_m128d(r, e); + + let a = _mm_set1_pd(x); + let e = _mm_set1_pd(res); + let r = _mm_round_pd::<_MM_FROUND_TO_ZERO>(a); + assert_eq_m128d(r, e); + } + + // Test rounding direction + test(-2.5, -2.0); + test(-1.75, -1.0); + test(-1.5, -1.0); + test(-1.25, -1.0); + test(-1.0, -1.0); + test(0.0, 0.0); + test(1.0, 1.0); + test(1.25, 1.0); + test(1.5, 1.0); + test(1.75, 1.0); + test(2.5, 2.0); + + // Test that each element is rounded + let a = _mm_setr_pd(1.5, 3.5); + let e = _mm_setr_pd(1.0, 3.0); + let r = _mm_round_pd::<_MM_FROUND_TO_ZERO>(a); + assert_eq_m128d(r, e); } - test_mm_round_ps(); + test_round_trunc_f64(); #[target_feature(enable = "sse4.1")] unsafe fn test_mm_minpos_epu16() {