mirror of
https://github.com/rust-lang/rust.git
synced 2025-06-06 04:08:40 +00:00
Auto merge of #117855 - RalfJung:miri, r=RalfJung
Miri subtree update r? `@ghost`
This commit is contained in:
commit
0828c159c5
@ -108,7 +108,7 @@ case $HOST_TARGET in
|
|||||||
MIRI_TEST_TARGET=aarch64-unknown-linux-gnu run_tests
|
MIRI_TEST_TARGET=aarch64-unknown-linux-gnu run_tests
|
||||||
MIRI_TEST_TARGET=aarch64-apple-darwin run_tests
|
MIRI_TEST_TARGET=aarch64-apple-darwin run_tests
|
||||||
MIRI_TEST_TARGET=i686-pc-windows-gnu 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=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-wasi run_tests_minimal no_std integer strings wasm
|
||||||
MIRI_TEST_TARGET=wasm32-unknown-unknown run_tests_minimal no_std integer strings wasm
|
MIRI_TEST_TARGET=wasm32-unknown-unknown run_tests_minimal no_std integer strings wasm
|
||||||
|
@ -33,6 +33,15 @@ enum SchedulingAction {
|
|||||||
Sleep(Duration),
|
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.
|
/// Trait for callbacks that can be executed when some event happens, such as after a timeout.
|
||||||
pub trait MachineCallback<'mir, 'tcx>: VisitTags {
|
pub trait MachineCallback<'mir, 'tcx>: VisitTags {
|
||||||
fn call(&self, ecx: &mut InterpCx<'mir, 'tcx, MiriMachine<'mir, 'tcx>>) -> InterpResult<'tcx>;
|
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.
|
// See if this thread can do something else.
|
||||||
match this.run_on_stack_empty()? {
|
match this.run_on_stack_empty()? {
|
||||||
Poll::Pending => {} // keep going
|
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,
|
/// 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`.
|
/// This is called by the eval loop when a thread's on_stack_empty returns `Ready`.
|
||||||
#[inline]
|
#[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 this = self.eval_context_mut();
|
||||||
let thread = this.active_thread_mut();
|
let thread = this.active_thread_mut();
|
||||||
assert!(thread.stack.is_empty(), "only threads with an empty stack can be terminated");
|
assert!(thread.stack.is_empty(), "only threads with an empty stack can be terminated");
|
||||||
thread.state = ThreadState::Terminated;
|
thread.state = ThreadState::Terminated;
|
||||||
|
|
||||||
let current_span = this.machine.current_span();
|
let current_span = this.machine.current_span();
|
||||||
for ptr in
|
let thread_local_allocations =
|
||||||
this.machine.threads.thread_terminated(this.machine.data_race.as_mut(), current_span)
|
this.machine.threads.thread_terminated(this.machine.data_race.as_mut(), current_span);
|
||||||
{
|
for ptr in thread_local_allocations {
|
||||||
this.deallocate_ptr(ptr.into(), None, MiriMemoryKind::Tls.into())?;
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -270,7 +270,8 @@ pub fn report_error<'tcx, 'mir>(
|
|||||||
DataRace { op1, extra, .. } => {
|
DataRace { op1, extra, .. } => {
|
||||||
let mut helps = vec![(Some(op1.span), format!("and (1) occurred earlier here"))];
|
let mut helps = vec![(Some(op1.span), format!("and (1) occurred earlier here"))];
|
||||||
if let Some(extra) = extra {
|
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!("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")));
|
helps.push((None, format!("see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information")));
|
||||||
|
@ -11,6 +11,7 @@ use log::info;
|
|||||||
use rustc_middle::ty::Ty;
|
use rustc_middle::ty::Ty;
|
||||||
|
|
||||||
use crate::borrow_tracker::RetagFields;
|
use crate::borrow_tracker::RetagFields;
|
||||||
|
use crate::concurrency::thread::TlsAllocAction;
|
||||||
use crate::diagnostics::report_leaks;
|
use crate::diagnostics::report_leaks;
|
||||||
use rustc_data_structures::fx::FxHashSet;
|
use rustc_data_structures::fx::FxHashSet;
|
||||||
use rustc_hir::def::Namespace;
|
use rustc_hir::def::Namespace;
|
||||||
@ -244,9 +245,9 @@ impl MainThreadState {
|
|||||||
// Figure out exit code.
|
// Figure out exit code.
|
||||||
let ret_place = this.machine.main_fn_ret_place.clone().unwrap();
|
let ret_place = this.machine.main_fn_ret_place.clone().unwrap();
|
||||||
let exit_code = this.read_target_isize(&ret_place)?;
|
let exit_code = this.read_target_isize(&ret_place)?;
|
||||||
// Need to call this ourselves since we are not going to return to the scheduler
|
// Deal with our thread-local memory. We do *not* want to actually free it, instead we consider TLS
|
||||||
// loop, and we want the main thread TLS to not show up as memory leaks.
|
// to be like a global `static`, so that all memory reached by it is considered to "not leak".
|
||||||
this.terminate_active_thread()?;
|
this.terminate_active_thread(TlsAllocAction::Leak)?;
|
||||||
// Stop interpreter loop.
|
// Stop interpreter loop.
|
||||||
throw_machine_stop!(TerminationInfo::Exit { code: exit_code, leak_check: true });
|
throw_machine_stop!(TerminationInfo::Exit { code: exit_code, leak_check: true });
|
||||||
}
|
}
|
||||||
|
@ -29,13 +29,23 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||||||
"pthread_set_name_np" => {
|
"pthread_set_name_np" => {
|
||||||
let [thread, name] =
|
let [thread, name] =
|
||||||
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
|
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 max_len = usize::MAX; // FreeBSD does not seem to have a limit.
|
||||||
let res = this.pthread_setname_np(
|
// FreeBSD's pthread_set_name_np does not return anything.
|
||||||
|
this.pthread_setname_np(
|
||||||
this.read_scalar(thread)?,
|
this.read_scalar(thread)?,
|
||||||
this.read_scalar(name)?,
|
this.read_scalar(name)?,
|
||||||
max_len,
|
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
|
// errno
|
||||||
|
@ -277,6 +277,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||||||
) -> InterpResult<'tcx, i32> {
|
) -> InterpResult<'tcx, i32> {
|
||||||
let this = self.eval_context_mut();
|
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");
|
let default_kind = this.eval_libc_i32("PTHREAD_MUTEX_DEFAULT");
|
||||||
mutexattr_set_kind(this, attr_op, default_kind)?;
|
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> {
|
) -> InterpResult<'tcx, i32> {
|
||||||
let this = self.eval_context_mut();
|
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 attr = this.read_pointer(attr_op)?;
|
||||||
let kind = if this.ptr_is_null(attr)? {
|
let kind = if this.ptr_is_null(attr)? {
|
||||||
this.eval_libc_i32("PTHREAD_MUTEX_DEFAULT")
|
this.eval_libc_i32("PTHREAD_MUTEX_DEFAULT")
|
||||||
@ -513,6 +527,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||||||
) -> InterpResult<'tcx, i32> {
|
) -> InterpResult<'tcx, i32> {
|
||||||
let this = self.eval_context_mut();
|
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 id = rwlock_get_id(this, rwlock_op)?;
|
||||||
let active_thread = this.get_active_thread();
|
let active_thread = this.get_active_thread();
|
||||||
|
|
||||||
@ -531,6 +552,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||||||
) -> InterpResult<'tcx, i32> {
|
) -> InterpResult<'tcx, i32> {
|
||||||
let this = self.eval_context_mut();
|
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 id = rwlock_get_id(this, rwlock_op)?;
|
||||||
let active_thread = this.get_active_thread();
|
let active_thread = this.get_active_thread();
|
||||||
|
|
||||||
@ -548,6 +576,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||||||
) -> InterpResult<'tcx, i32> {
|
) -> InterpResult<'tcx, i32> {
|
||||||
let this = self.eval_context_mut();
|
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 id = rwlock_get_id(this, rwlock_op)?;
|
||||||
let active_thread = this.get_active_thread();
|
let active_thread = this.get_active_thread();
|
||||||
|
|
||||||
@ -578,6 +613,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||||||
) -> InterpResult<'tcx, i32> {
|
) -> InterpResult<'tcx, i32> {
|
||||||
let this = self.eval_context_mut();
|
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 id = rwlock_get_id(this, rwlock_op)?;
|
||||||
let active_thread = this.get_active_thread();
|
let active_thread = this.get_active_thread();
|
||||||
|
|
||||||
@ -595,6 +637,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||||||
) -> InterpResult<'tcx, i32> {
|
) -> InterpResult<'tcx, i32> {
|
||||||
let this = self.eval_context_mut();
|
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 id = rwlock_get_id(this, rwlock_op)?;
|
||||||
let active_thread = this.get_active_thread();
|
let active_thread = this.get_active_thread();
|
||||||
|
|
||||||
@ -614,6 +663,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||||||
) -> InterpResult<'tcx, i32> {
|
) -> InterpResult<'tcx, i32> {
|
||||||
let this = self.eval_context_mut();
|
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)?;
|
let id = rwlock_get_id(this, rwlock_op)?;
|
||||||
|
|
||||||
if this.rwlock_is_locked(id) {
|
if this.rwlock_is_locked(id) {
|
||||||
@ -638,6 +694,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||||||
) -> InterpResult<'tcx, i32> {
|
) -> InterpResult<'tcx, i32> {
|
||||||
let this = self.eval_context_mut();
|
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
|
// The default value of the clock attribute shall refer to the system
|
||||||
// clock.
|
// clock.
|
||||||
// https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_condattr_setclock.html
|
// 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> {
|
) -> InterpResult<'tcx, i32> {
|
||||||
let this = self.eval_context_mut();
|
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 attr = this.read_pointer(attr_op)?;
|
||||||
let clock_id = if this.ptr_is_null(attr)? {
|
let clock_id = if this.ptr_is_null(attr)? {
|
||||||
this.eval_libc_i32("CLOCK_REALTIME")
|
this.eval_libc_i32("CLOCK_REALTIME")
|
||||||
|
@ -148,6 +148,14 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>:
|
|||||||
|
|
||||||
round_first::<rustc_apfloat::ieee::Single>(this, left, right, rounding, dest)?;
|
round_first::<rustc_apfloat::ieee::Single>(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::<rustc_apfloat::ieee::Single>(this, op, rounding, dest)?;
|
||||||
|
}
|
||||||
// Used to implement the _mm_floor_sd, _mm_ceil_sd and _mm_round_sd
|
// Used to implement the _mm_floor_sd, _mm_ceil_sd and _mm_round_sd
|
||||||
// functions. Rounds the first element of `right` according to `rounding`
|
// functions. Rounds the first element of `right` according to `rounding`
|
||||||
// and copies the remaining elements from `left`.
|
// and copies the remaining elements from `left`.
|
||||||
@ -157,6 +165,14 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>:
|
|||||||
|
|
||||||
round_first::<rustc_apfloat::ieee::Double>(this, left, right, rounding, dest)?;
|
round_first::<rustc_apfloat::ieee::Double>(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::<rustc_apfloat::ieee::Double>(this, op, rounding, dest)?;
|
||||||
|
}
|
||||||
// Used to implement the _mm_minpos_epu16 function.
|
// Used to implement the _mm_minpos_epu16 function.
|
||||||
// Find the minimum unsinged 16-bit integer in `op` and
|
// Find the minimum unsinged 16-bit integer in `op` and
|
||||||
// returns its value and position.
|
// 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, left_len);
|
||||||
assert_eq!(dest_len, right_len);
|
assert_eq!(dest_len, right_len);
|
||||||
|
|
||||||
// The fourth bit of `rounding` only affects the SSE status
|
let rounding = rounding_from_imm(this.read_scalar(rounding)?.to_i32()?)?;
|
||||||
// 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 op0: F = this.read_scalar(&this.project_index(&right, 0)?)?.to_float()?;
|
let op0: F = this.read_scalar(&this.project_index(&right, 0)?)?.to_float()?;
|
||||||
let res = op0.round_to_integral(rounding).value;
|
let res = op0.round_to_integral(rounding).value;
|
||||||
@ -317,3 +318,50 @@ fn round_first<'tcx, F: rustc_apfloat::Float>(
|
|||||||
|
|
||||||
Ok(())
|
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}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -10,6 +10,7 @@ help: and (1) occurred earlier here
|
|||||||
LL | a16.load(Ordering::SeqCst);
|
LL | a16.load(Ordering::SeqCst);
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
= help: overlapping unsynchronized atomic accesses must use the same access size
|
= 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: 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
|
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
|
||||||
= note: BACKTRACE (of the first span):
|
= note: BACKTRACE (of the first span):
|
||||||
|
@ -10,6 +10,7 @@ help: and (1) occurred earlier here
|
|||||||
LL | a16.store(1, Ordering::SeqCst);
|
LL | a16.store(1, Ordering::SeqCst);
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
= help: overlapping unsynchronized atomic accesses must use the same access size
|
= 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: 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
|
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
|
||||||
= note: BACKTRACE (of the first span):
|
= note: BACKTRACE (of the first span):
|
||||||
|
@ -10,6 +10,7 @@ help: and (1) occurred earlier here
|
|||||||
LL | unsafe { ptr.read() };
|
LL | unsafe { ptr.read() };
|
||||||
| ^^^^^^^^^^
|
| ^^^^^^^^^^
|
||||||
= help: overlapping atomic and non-atomic accesses must be synchronized, even if both are read-only
|
= 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: 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
|
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
|
||||||
= note: BACKTRACE (of the first span):
|
= note: BACKTRACE (of the first span):
|
||||||
|
@ -10,6 +10,7 @@ help: and (1) occurred earlier here
|
|||||||
LL | a.load(Ordering::SeqCst);
|
LL | a.load(Ordering::SeqCst);
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
= help: overlapping atomic and non-atomic accesses must be synchronized, even if both are read-only
|
= 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: 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
|
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
|
||||||
= note: BACKTRACE (of the first span):
|
= note: BACKTRACE (of the first span):
|
||||||
|
@ -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
|
ow
|
||||||
fatal runtime error: thread local panicked on drop
|
fatal runtime error: thread local panicked on drop
|
||||||
error: abnormal termination: the program aborted execution
|
error: abnormal termination: the program aborted execution
|
@ -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
|
ow
|
||||||
fatal runtime error: thread local panicked on drop
|
fatal runtime error: thread local panicked on drop
|
||||||
error: abnormal termination: the program aborted execution
|
error: abnormal termination: the program aborted execution
|
@ -1,5 +1,5 @@
|
|||||||
error: Undefined Behavior: memory access failed: ALLOC has been freed, so this pointer is dangling
|
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;
|
LL | let _val = *dangling_ptr.0;
|
||||||
| ^^^^^^^^^^^^^^^ memory access failed: ALLOC has been freed, so this pointer is dangling
|
| ^^^^^^^^^^^^^^^ 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: 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
|
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
|
||||||
= note: BACKTRACE:
|
= 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
|
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||||
|
|
21
src/tools/miri/tests/fail/tls_macro_leak.rs
Normal file
21
src/tools/miri/tests/fail/tls_macro_leak.rs
Normal file
@ -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<Option<&'static i32>> = 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.
|
||||||
|
}
|
32
src/tools/miri/tests/fail/tls_macro_leak.stderr
Normal file
32
src/tools/miri/tests/fail/tls_macro_leak.stderr
Normal file
@ -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 `<std::alloc::Global as std::alloc::Allocator>::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::<i32>::new` at RUSTLIB/alloc/src/boxed.rs:LL:CC
|
||||||
|
note: inside closure
|
||||||
|
--> $DIR/tls_macro_leak.rs:LL:CC
|
||||||
|
|
|
||||||
|
LL | cell.set(Some(Box::leak(Box::new(123))));
|
||||||
|
| ^^^^^^^^^^^^^
|
||||||
|
= note: inside `std::thread::LocalKey::<std::cell::Cell<std::option::Option<&i32>>>::try_with::<{closure@$DIR/tls_macro_leak.rs:LL:CC}, ()>` at RUSTLIB/std/src/thread/local.rs:LL:CC
|
||||||
|
= note: inside `std::thread::LocalKey::<std::cell::Cell<std::option::Option<&i32>>>::with::<{closure@$DIR/tls_macro_leak.rs:LL:CC}, ()>` at RUSTLIB/std/src/thread/local.rs:LL:CC
|
||||||
|
note: inside closure
|
||||||
|
--> $DIR/tls_macro_leak.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
|
||||||
|
|
22
src/tools/miri/tests/fail/tls_static_leak.rs
Normal file
22
src/tools/miri/tests/fail/tls_static_leak.rs
Normal file
@ -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<Option<&'static i32>> = 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.
|
||||||
|
}
|
23
src/tools/miri/tests/fail/tls_static_leak.stderr
Normal file
23
src/tools/miri/tests/fail/tls_static_leak.stderr
Normal file
@ -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 `<std::alloc::Global as std::alloc::Allocator>::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::<i32>::new` at RUSTLIB/alloc/src/boxed.rs:LL:CC
|
||||||
|
note: inside closure
|
||||||
|
--> $DIR/tls_static_leak.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
|
||||||
|
|
@ -10,6 +10,7 @@ help: and (1) occurred earlier here
|
|||||||
LL | x.store(1, Relaxed);
|
LL | x.store(1, Relaxed);
|
||||||
| ^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^
|
||||||
= help: overlapping unsynchronized atomic accesses must use the same access size
|
= 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: 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
|
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
|
||||||
= note: BACKTRACE (of the first span):
|
= note: BACKTRACE (of the first span):
|
||||||
|
@ -10,6 +10,7 @@ help: and (1) occurred earlier here
|
|||||||
LL | x.load(Relaxed);
|
LL | x.load(Relaxed);
|
||||||
| ^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^
|
||||||
= help: overlapping unsynchronized atomic accesses must use the same access size
|
= 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: 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
|
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
|
||||||
= note: BACKTRACE (of the first span):
|
= note: BACKTRACE (of the first span):
|
||||||
|
@ -1,18 +1,26 @@
|
|||||||
//@ignore-target-windows: No libc on Windows
|
//@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;
|
use std::thread;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
test_mutex_libc_init_recursive();
|
|
||||||
test_mutex_libc_init_normal();
|
|
||||||
test_mutex_libc_init_errorcheck();
|
|
||||||
test_rwlock_libc_static_initializer();
|
|
||||||
test_named_thread_truncation();
|
test_named_thread_truncation();
|
||||||
|
|
||||||
|
#[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();
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
test_mutex_libc_static_initializer_recursive();
|
test_mutex_libc_static_initializer_recursive();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "freebsd"))]
|
||||||
fn test_mutex_libc_init_recursive() {
|
fn test_mutex_libc_init_recursive() {
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut attr: libc::pthread_mutexattr_t = std::mem::zeroed();
|
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() {
|
fn test_mutex_libc_init_normal() {
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut mutexattr: libc::pthread_mutexattr_t = std::mem::zeroed();
|
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() {
|
fn test_mutex_libc_init_errorcheck() {
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut mutexattr: libc::pthread_mutexattr_t = std::mem::zeroed();
|
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
|
// 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
|
// 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.
|
// 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() {
|
fn test_rwlock_libc_static_initializer() {
|
||||||
let rw = std::cell::UnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER);
|
let rw = std::cell::UnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER);
|
||||||
unsafe {
|
unsafe {
|
||||||
@ -137,6 +148,12 @@ fn test_named_thread_truncation() {
|
|||||||
fn set_thread_name(name: &CStr) -> i32 {
|
fn set_thread_name(name: &CStr) -> i32 {
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
return unsafe { libc::pthread_setname_np(libc::pthread_self(), name.as_ptr().cast()) };
|
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
|
||||||
|
libc::pthread_set_name_np(libc::pthread_self(), name.as_ptr().cast());
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
return unsafe { libc::pthread_setname_np(name.as_ptr().cast()) };
|
return unsafe { libc::pthread_setname_np(name.as_ptr().cast()) };
|
||||||
}
|
}
|
||||||
@ -147,16 +164,23 @@ fn test_named_thread_truncation() {
|
|||||||
|
|
||||||
// But the system is limited -- make sure we successfully set a truncation.
|
// But the system is limited -- make sure we successfully set a truncation.
|
||||||
let mut buf = vec![0u8; long_name.len() + 1];
|
let mut buf = vec![0u8; long_name.len() + 1];
|
||||||
|
#[cfg(not(target_os = "freebsd"))]
|
||||||
unsafe {
|
unsafe {
|
||||||
libc::pthread_getname_np(libc::pthread_self(), buf.as_mut_ptr().cast(), buf.len())
|
libc::pthread_getname_np(libc::pthread_self(), buf.as_mut_ptr().cast(), buf.len())
|
||||||
};
|
};
|
||||||
|
#[cfg(target_os = "freebsd")]
|
||||||
|
unsafe {
|
||||||
|
libc::pthread_get_name_np(libc::pthread_self(), buf.as_mut_ptr().cast(), buf.len())
|
||||||
|
};
|
||||||
let cstr = CStr::from_bytes_until_nul(&buf).unwrap();
|
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()));
|
assert!(long_name.as_bytes().starts_with(cstr.to_bytes()));
|
||||||
|
|
||||||
// Also test directly calling pthread_setname to check its return value.
|
// Also test directly calling pthread_setname to check its return value.
|
||||||
assert_eq!(set_thread_name(&cstr), 0);
|
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);
|
assert_ne!(set_thread_name(&CString::new(long_name).unwrap()), 0);
|
||||||
});
|
});
|
||||||
result.unwrap().join().unwrap();
|
result.unwrap().join().unwrap();
|
||||||
|
@ -73,114 +73,342 @@ unsafe fn test_sse41() {
|
|||||||
test_mm_dp_ps();
|
test_mm_dp_ps();
|
||||||
|
|
||||||
#[target_feature(enable = "sse4.1")]
|
#[target_feature(enable = "sse4.1")]
|
||||||
unsafe fn test_mm_floor_sd() {
|
unsafe fn test_round_nearest_f32() {
|
||||||
let a = _mm_setr_pd(2.5, 4.5);
|
#[target_feature(enable = "sse4.1")]
|
||||||
let b = _mm_setr_pd(-1.5, -3.5);
|
unsafe fn test(x: f32, res: f32) {
|
||||||
let r = _mm_floor_sd(a, b);
|
let a = _mm_setr_ps(3.5, 2.5, 1.5, 4.5);
|
||||||
let e = _mm_setr_pd(-2.0, 4.5);
|
let b = _mm_setr_ps(x, -1.5, -3.5, -2.5);
|
||||||
assert_eq_m128d(r, e);
|
let e = _mm_setr_ps(res, 2.5, 1.5, 4.5);
|
||||||
}
|
let r = _mm_round_ss::<_MM_FROUND_TO_NEAREST_INT>(a, b);
|
||||||
test_mm_floor_sd();
|
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);
|
||||||
|
|
||||||
#[target_feature(enable = "sse4.1")]
|
let a = _mm_set1_ps(x);
|
||||||
unsafe fn test_mm_floor_ss() {
|
let e = _mm_set1_ps(res);
|
||||||
let a = _mm_setr_ps(2.5, 4.5, 8.5, 16.5);
|
let r = _mm_round_ps::<_MM_FROUND_TO_NEAREST_INT>(a);
|
||||||
let b = _mm_setr_ps(-1.5, -3.5, -7.5, -15.5);
|
assert_eq_m128(r, e);
|
||||||
let r = _mm_floor_ss(a, b);
|
// Assume round-to-nearest by default
|
||||||
let e = _mm_setr_ps(-2.0, 4.5, 8.5, 16.5);
|
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);
|
assert_eq_m128(r, e);
|
||||||
}
|
|
||||||
test_mm_floor_ss();
|
|
||||||
|
|
||||||
#[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);
|
|
||||||
}
|
|
||||||
test_mm_ceil_sd();
|
|
||||||
|
|
||||||
#[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);
|
|
||||||
assert_eq_m128(r, e);
|
|
||||||
}
|
|
||||||
test_mm_ceil_ss();
|
|
||||||
|
|
||||||
#[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);
|
|
||||||
|
|
||||||
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);
|
|
||||||
assert_eq_m128d(r, e);
|
|
||||||
|
|
||||||
// Assume round-to-nearest by default
|
// Assume round-to-nearest by default
|
||||||
let a = _mm_setr_pd(1.5, 3.5);
|
let r = _mm_round_ps::<_MM_FROUND_CUR_DIRECTION>(a);
|
||||||
let b = _mm_setr_pd(-2.5, -4.5);
|
assert_eq_m128(r, e);
|
||||||
let r = _mm_round_sd::<_MM_FROUND_CUR_DIRECTION>(a, b);
|
|
||||||
let e = _mm_setr_pd(-2.0, 3.5);
|
|
||||||
assert_eq_m128d(r, e);
|
|
||||||
}
|
}
|
||||||
test_mm_round_sd();
|
test_round_nearest_f32();
|
||||||
|
|
||||||
#[target_feature(enable = "sse4.1")]
|
#[target_feature(enable = "sse4.1")]
|
||||||
unsafe fn test_mm_round_ss() {
|
unsafe fn test_round_floor_f32() {
|
||||||
let a = _mm_setr_ps(1.5, 3.5, 7.5, 15.5);
|
#[target_feature(enable = "sse4.1")]
|
||||||
let b = _mm_setr_ps(-1.75, -4.5, -8.5, -16.5);
|
unsafe fn test(x: f32, res: f32) {
|
||||||
let r = _mm_round_ss::<_MM_FROUND_TO_NEAREST_INT>(a, b);
|
let a = _mm_setr_ps(3.5, 2.5, 1.5, 4.5);
|
||||||
let e = _mm_setr_ps(-2.0, 3.5, 7.5, 15.5);
|
let b = _mm_setr_ps(x, -1.5, -3.5, -2.5);
|
||||||
assert_eq_m128(r, e);
|
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_setr_ps(1.5, 3.5, 7.5, 15.5);
|
let a = _mm_set1_ps(x);
|
||||||
let b = _mm_setr_ps(-1.75, -4.5, -8.5, -16.5);
|
let e = _mm_set1_ps(res);
|
||||||
let r = _mm_round_ss::<_MM_FROUND_TO_NEG_INF>(a, b);
|
let r = _mm_floor_ps(a);
|
||||||
let e = _mm_setr_ps(-2.0, 3.5, 7.5, 15.5);
|
assert_eq_m128(r, e);
|
||||||
assert_eq_m128(r, e);
|
let r = _mm_round_ps::<_MM_FROUND_TO_NEG_INF>(a);
|
||||||
|
assert_eq_m128(r, e);
|
||||||
|
}
|
||||||
|
|
||||||
let a = _mm_setr_ps(1.5, 3.5, 7.5, 15.5);
|
// Test rounding direction
|
||||||
let b = _mm_setr_ps(-1.75, -4.5, -8.5, -16.5);
|
test(-2.5, -3.0);
|
||||||
let r = _mm_round_ss::<_MM_FROUND_TO_POS_INF>(a, b);
|
test(-1.75, -2.0);
|
||||||
let e = _mm_setr_ps(-1.0, 3.5, 7.5, 15.5);
|
test(-1.5, -2.0);
|
||||||
assert_eq_m128(r, e);
|
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);
|
||||||
|
|
||||||
let a = _mm_setr_ps(1.5, 3.5, 7.5, 15.5);
|
// Test that each element is rounded
|
||||||
let b = _mm_setr_ps(-1.75, -4.5, -8.5, -16.5);
|
let a = _mm_setr_ps(1.5, 3.5, 5.5, 7.5);
|
||||||
let r = _mm_round_ss::<_MM_FROUND_TO_ZERO>(a, b);
|
let e = _mm_setr_ps(1.0, 3.0, 5.0, 7.0);
|
||||||
let e = _mm_setr_ps(-1.0, 3.5, 7.5, 15.5);
|
let r = _mm_floor_ps(a);
|
||||||
assert_eq_m128(r, e);
|
assert_eq_m128(r, e);
|
||||||
|
let r = _mm_round_ps::<_MM_FROUND_TO_NEG_INF>(a);
|
||||||
// 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);
|
assert_eq_m128(r, e);
|
||||||
}
|
}
|
||||||
test_mm_round_ss();
|
test_round_floor_f32();
|
||||||
|
|
||||||
|
#[target_feature(enable = "sse4.1")]
|
||||||
|
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_round_ceil_f32();
|
||||||
|
|
||||||
|
#[target_feature(enable = "sse4.1")]
|
||||||
|
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_round_trunc_f32();
|
||||||
|
|
||||||
|
#[target_feature(enable = "sse4.1")]
|
||||||
|
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 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 r = _mm_round_pd::<_MM_FROUND_CUR_DIRECTION>(a);
|
||||||
|
assert_eq_m128d(r, e);
|
||||||
|
}
|
||||||
|
test_round_nearest_f64();
|
||||||
|
|
||||||
|
#[target_feature(enable = "sse4.1")]
|
||||||
|
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 r = _mm_round_pd::<_MM_FROUND_TO_NEG_INF>(a);
|
||||||
|
assert_eq_m128d(r, e);
|
||||||
|
}
|
||||||
|
test_round_floor_f64();
|
||||||
|
|
||||||
|
#[target_feature(enable = "sse4.1")]
|
||||||
|
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_round_ceil_f64();
|
||||||
|
|
||||||
|
#[target_feature(enable = "sse4.1")]
|
||||||
|
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_round_trunc_f64();
|
||||||
|
|
||||||
#[target_feature(enable = "sse4.1")]
|
#[target_feature(enable = "sse4.1")]
|
||||||
unsafe fn test_mm_minpos_epu16() {
|
unsafe fn test_mm_minpos_epu16() {
|
||||||
|
@ -0,0 +1,23 @@
|
|||||||
|
//@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.
|
||||||
|
//
|
||||||
|
// The test covers both TLS statics and the TLS macro.
|
||||||
|
pub fn main() {
|
||||||
|
thread_local! {
|
||||||
|
static TLS_KEY: Cell<Option<&'static i32>> = Cell::new(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
TLS_KEY.with(|cell| {
|
||||||
|
cell.set(Some(Box::leak(Box::new(123))));
|
||||||
|
});
|
||||||
|
|
||||||
|
#[thread_local]
|
||||||
|
static TLS: Cell<Option<&'static i32>> = Cell::new(None);
|
||||||
|
|
||||||
|
TLS.set(Some(Box::leak(Box::new(123))));
|
||||||
|
}
|
@ -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;
|
use std::cell::RefCell;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user