Auto merge of #117855 - RalfJung:miri, r=RalfJung

Miri subtree update

r? `@ghost`
This commit is contained in:
bors 2023-11-12 22:47:43 +00:00
commit 0828c159c5
32 changed files with 667 additions and 139 deletions

View File

@ -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

View File

@ -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(())
}

View File

@ -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")));

View File

@ -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 });
}

View File

@ -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

View File

@ -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")

View File

@ -148,6 +148,14 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>:
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
// 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::<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.
// 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}"),
}
}

View File

@ -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):

View File

@ -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):

View File

@ -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):

View File

@ -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):

View File

@ -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

View File

@ -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

View File

@ -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

View 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.
}

View 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

View 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.
}

View 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

View File

@ -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):

View File

@ -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):

View File

@ -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() {
test_mutex_libc_init_recursive();
test_mutex_libc_init_normal();
test_mutex_libc_init_errorcheck();
test_rwlock_libc_static_initializer();
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")]
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,12 @@ 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
libc::pthread_set_name_np(libc::pthread_self(), name.as_ptr().cast());
return 0;
};
#[cfg(target_os = "macos")]
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.
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 {
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();

View File

@ -73,114 +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);
}
test_mm_floor_sd();
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);
#[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);
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);
}
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
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);
assert_eq_m128d(r, e);
let r = _mm_round_ps::<_MM_FROUND_CUR_DIRECTION>(a);
assert_eq_m128(r, e);
}
test_mm_round_sd();
test_round_nearest_f32();
#[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);
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_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_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);
}
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);
// 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);
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);
// 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);
// 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);
let r = _mm_round_ps::<_MM_FROUND_TO_NEG_INF>(a);
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")]
unsafe fn test_mm_minpos_epu16() {

View File

@ -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))));
}

View File

@ -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;