mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-22 06:44:35 +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-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
|
||||
|
@ -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(())
|
||||
}
|
||||
|
@ -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")));
|
||||
|
@ -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 });
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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")
|
||||
|
@ -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}"),
|
||||
}
|
||||
}
|
||||
|
@ -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):
|
||||
|
@ -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):
|
||||
|
@ -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):
|
||||
|
@ -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):
|
||||
|
@ -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
|
@ -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
|
@ -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
|
||||
|
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);
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
= 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):
|
||||
|
@ -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):
|
||||
|
@ -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();
|
||||
|
@ -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() {
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user