diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 554a12909f..dec85e9232 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,10 +35,6 @@ jobs: steps: - uses: actions/checkout@v3 - - name: Set the tag GC interval to 1 on linux - if: runner.os == 'Linux' - run: echo "MIRIFLAGS=-Zmiri-provenance-gc=1" >> $GITHUB_ENV - # Cache the global cargo directory, but NOT the local `target` directory which # we cannot reuse anyway when the nightly changes (and it grows quite large # over time). diff --git a/cargo-miri/src/setup.rs b/cargo-miri/src/setup.rs index d921741d5d..8ae5b8c3e8 100644 --- a/cargo-miri/src/setup.rs +++ b/cargo-miri/src/setup.rs @@ -2,6 +2,7 @@ use std::env; use std::ffi::OsStr; +use std::fmt::Write; use std::path::PathBuf; use std::process::{self, Command}; @@ -140,12 +141,20 @@ pub fn setup( // Do the build. if print_sysroot { // Be silent. - } else if only_setup { - // We want to be explicit. - eprintln!("Preparing a sysroot for Miri (target: {target})..."); } else { - // We want to be quiet, but still let the user know that something is happening. - eprint!("Preparing a sysroot for Miri (target: {target})... "); + let mut msg = String::new(); + write!(msg, "Preparing a sysroot for Miri (target: {target})").unwrap(); + if verbose > 0 { + write!(msg, " in {}", sysroot_dir.display()).unwrap(); + } + write!(msg, "...").unwrap(); + if only_setup { + // We want to be explicit. + eprintln!("{msg}"); + } else { + // We want to be quiet, but still let the user know that something is happening. + eprint!("{msg} "); + } } SysrootBuilder::new(&sysroot_dir, target) .build_mode(BuildMode::Check) diff --git a/ci.sh b/ci.sh index 7808c9acf9..6bcc68ebf7 100755 --- a/ci.sh +++ b/ci.sh @@ -30,16 +30,23 @@ endgroup # Test function run_tests { - if [ -n "${MIRI_TEST_TARGET+exists}" ]; then + if [ -n "${MIRI_TEST_TARGET:-}" ]; then begingroup "Testing foreign architecture $MIRI_TEST_TARGET" else begingroup "Testing host architecture" fi ## ui test suite - ./miri test - if [ -z "${MIRI_TEST_TARGET+exists}" ]; then - # Host-only tests: running these on all targets is unlikely to catch more problems and would + # On the host and on Linux, also stress-test the GC. + if [ -z "${MIRI_TEST_TARGET:-}" ] || [ "$HOST_TARGET" = x86_64-unknown-linux-gnu ]; then + MIRIFLAGS="${MIRIFLAGS:-} -Zmiri-provenance-gc=1" ./miri test + else + ./miri test + fi + + # Host-only tests + if [ -z "${MIRI_TEST_TARGET:-}" ]; then + # Running these on all targets is unlikely to catch more problems and would # cost a lot of CI time. # Tests with optimizations (`-O` is what cargo passes, but crank MIR optimizations up all the @@ -85,10 +92,11 @@ function run_tests { } function run_tests_minimal { - if [ -n "${MIRI_TEST_TARGET+exists}" ]; then + if [ -n "${MIRI_TEST_TARGET:-}" ]; then begingroup "Testing MINIMAL foreign architecture $MIRI_TEST_TARGET: only testing $@" else - begingroup "Testing MINIMAL host architecture: only testing $@" + echo "run_tests_minimal requires MIRI_TEST_TARGET to be set" + exit 1 fi ./miri test -- "$@" @@ -99,16 +107,22 @@ function run_tests_minimal { endgroup } -# host +## Main Testing Logic ## + +# Host target. run_tests +# Extra targets. +# In particular, fully cover all tier 1 targets. case $HOST_TARGET in x86_64-unknown-linux-gnu) MIRI_TEST_TARGET=i686-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=i686-pc-windows-gnu run_tests - MIRI_TEST_TARGET=x86_64-unknown-freebsd run_tests_minimal hello integer vec panic/panic concurrency/simple pthread-threadname libc-getentropy libc-getrandom libc-misc atomic env align + # Some targets are only partially supported. + MIRI_TEST_TARGET=x86_64-unknown-freebsd run_tests_minimal hello integer vec panic/panic concurrency/simple pthread-threadname libc-getentropy libc-getrandom libc-misc libc-fs atomic env align + MIRI_TEST_TARGET=i686-unknown-freebsd run_tests_minimal hello integer vec panic/panic concurrency/simple pthread-threadname libc-getentropy libc-getrandom libc-misc libc-fs atomic env align MIRI_TEST_TARGET=aarch64-linux-android run_tests_minimal hello integer vec panic/panic MIRI_TEST_TARGET=wasm32-wasi run_tests_minimal no_std integer strings wasm MIRI_TEST_TARGET=wasm32-unknown-unknown run_tests_minimal no_std integer strings wasm diff --git a/miri-script/src/commands.rs b/miri-script/src/commands.rs index c24035ae08..e4789c696b 100644 --- a/miri-script/src/commands.rs +++ b/miri-script/src/commands.rs @@ -286,7 +286,7 @@ impl Command { "This will pull a copy of the rust-lang/rust history into this Miri checkout, growing it by about 1GB." ); print!( - "To avoid that, abort now and set the `--rustc-git` flag to an existing rustc checkout. Proceed? [y/N] " + "To avoid that, abort now and set the `RUSTC_GIT` environment variable to an existing rustc checkout. Proceed? [y/N] " ); std::io::stdout().flush()?; let mut answer = String::new(); diff --git a/rust-version b/rust-version index 2a19781775..ecebb8a331 100644 --- a/rust-version +++ b/rust-version @@ -1 +1 @@ -820f06b21f8373060ff7b515715b8440a6a6c197 +3668a8af1b81447c4afa1f82f60d7b94b71a549f diff --git a/src/helpers.rs b/src/helpers.rs index 965cd534d1..21f1d68492 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -5,17 +5,18 @@ use std::time::Duration; use log::trace; +use rustc_apfloat::ieee::{Double, Single}; use rustc_hir::def::{DefKind, Namespace}; use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX}; use rustc_index::IndexVec; use rustc_middle::mir; use rustc_middle::ty::{ self, - layout::{IntegerExt as _, LayoutOf, TyAndLayout}, - IntTy, Ty, TyCtxt, UintTy, + layout::{LayoutOf, TyAndLayout}, + FloatTy, IntTy, Ty, TyCtxt, UintTy, }; use rustc_span::{def_id::CrateNum, sym, Span, Symbol}; -use rustc_target::abi::{Align, FieldIdx, FieldsShape, Integer, Size, Variants}; +use rustc_target::abi::{Align, FieldIdx, FieldsShape, Size, Variants}; use rustc_target::spec::abi::Abi; use rand::RngCore; @@ -565,10 +566,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { /// is part of the UNIX family. It panics showing a message with the `name` of the foreign function /// if this is not the case. fn assert_target_os_is_unix(&self, name: &str) { - assert!( - target_os_is_unix(self.eval_context_ref().tcx.sess.target.os.as_ref()), - "`{name}` is only available for supported UNIX family targets", - ); + assert!(self.target_os_is_unix(), "`{name}` is only available for unix targets",); + } + + fn target_os_is_unix(&self) -> bool { + self.eval_context_ref().tcx.sess.target.families.iter().any(|f| f == "unix") } /// Get last error variable as a place, lazily allocating thread-local storage for it if @@ -985,65 +987,74 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } } - /// Converts `f` to integer type `dest_ty` after rounding with mode `round`. + /// Converts `src` from floating point to integer type `dest_ty` + /// after rounding with mode `round`. /// Returns `None` if `f` is NaN or out of range. - fn float_to_int_checked( + fn float_to_int_checked( &self, - f: F, + src: &ImmTy<'tcx, Provenance>, cast_to: TyAndLayout<'tcx>, round: rustc_apfloat::Round, - ) -> Option> - where - F: rustc_apfloat::Float + Into>, - { + ) -> InterpResult<'tcx, Option>> { let this = self.eval_context_ref(); - let val = match cast_to.ty.kind() { - // Unsigned - ty::Uint(t) => { - let size = Integer::from_uint_ty(this, *t).size(); - let res = f.to_u128_r(size.bits_usize(), round, &mut false); - if res.status.intersects( - rustc_apfloat::Status::INVALID_OP - | rustc_apfloat::Status::OVERFLOW - | rustc_apfloat::Status::UNDERFLOW, - ) { - // Floating point value is NaN (flagged with INVALID_OP) or outside the range - // of values of the integer type (flagged with OVERFLOW or UNDERFLOW). - return None; - } else { - // Floating point value can be represented by the integer type after rounding. - // The INEXACT flag is ignored on purpose to allow rounding. - Scalar::from_uint(res.value, size) + fn float_to_int_inner<'tcx, F: rustc_apfloat::Float>( + this: &MiriInterpCx<'_, 'tcx>, + src: F, + cast_to: TyAndLayout<'tcx>, + round: rustc_apfloat::Round, + ) -> (Scalar, rustc_apfloat::Status) { + let int_size = cast_to.layout.size; + match cast_to.ty.kind() { + // Unsigned + ty::Uint(_) => { + let res = src.to_u128_r(int_size.bits_usize(), round, &mut false); + (Scalar::from_uint(res.value, int_size), res.status) } - } - // Signed - ty::Int(t) => { - let size = Integer::from_int_ty(this, *t).size(); - let res = f.to_i128_r(size.bits_usize(), round, &mut false); - if res.status.intersects( - rustc_apfloat::Status::INVALID_OP - | rustc_apfloat::Status::OVERFLOW - | rustc_apfloat::Status::UNDERFLOW, - ) { - // Floating point value is NaN (flagged with INVALID_OP) or outside the range - // of values of the integer type (flagged with OVERFLOW or UNDERFLOW). - return None; - } else { - // Floating point value can be represented by the integer type after rounding. - // The INEXACT flag is ignored on purpose to allow rounding. - Scalar::from_int(res.value, size) + // Signed + ty::Int(_) => { + let res = src.to_i128_r(int_size.bits_usize(), round, &mut false); + (Scalar::from_int(res.value, int_size), res.status) } + // Nothing else + _ => + span_bug!( + this.cur_span(), + "attempted float-to-int conversion with non-int output type {}", + cast_to.ty, + ), } + } + + let (val, status) = match src.layout.ty.kind() { + // f32 + ty::Float(FloatTy::F32) => + float_to_int_inner::(this, src.to_scalar().to_f32()?, cast_to, round), + // f64 + ty::Float(FloatTy::F64) => + float_to_int_inner::(this, src.to_scalar().to_f64()?, cast_to, round), // Nothing else _ => span_bug!( this.cur_span(), - "attempted float-to-int conversion with non-int output type {}", - cast_to.ty, + "attempted float-to-int conversion with non-float input type {}", + src.layout.ty, ), }; - Some(ImmTy::from_scalar(val, cast_to)) + + if status.intersects( + rustc_apfloat::Status::INVALID_OP + | rustc_apfloat::Status::OVERFLOW + | rustc_apfloat::Status::UNDERFLOW, + ) { + // Floating point value is NaN (flagged with INVALID_OP) or outside the range + // of values of the integer type (flagged with OVERFLOW or UNDERFLOW). + Ok(None) + } else { + // Floating point value can be represented by the integer type after rounding. + // The INEXACT flag is ignored on purpose to allow rounding. + Ok(Some(ImmTy::from_scalar(val, cast_to))) + } } /// Returns an integer type that is twice wide as `ty` @@ -1063,6 +1074,24 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { _ => span_bug!(this.cur_span(), "unexpected type: {ty:?}"), } } + + /// Checks that target feature `target_feature` is enabled. + /// + /// If not enabled, emits an UB error that states that the feature is + /// required by `intrinsic`. + fn expect_target_feature_for_intrinsic( + &self, + intrinsic: Symbol, + target_feature: &str, + ) -> InterpResult<'tcx, ()> { + let this = self.eval_context_ref(); + if !this.tcx.sess.unstable_target_features.contains(&Symbol::intern(target_feature)) { + throw_ub_format!( + "attempted to call intrinsic `{intrinsic}` that requires missing target feature {target_feature}" + ); + } + Ok(()) + } } impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { @@ -1143,12 +1172,6 @@ pub fn get_local_crates(tcx: TyCtxt<'_>) -> Vec { local_crates } -/// Helper function used inside the shims of foreign functions to check that -/// `target_os` is a supported UNIX OS. -pub fn target_os_is_unix(target_os: &str) -> bool { - matches!(target_os, "linux" | "macos" | "freebsd" | "android") -} - pub(crate) fn bool_to_simd_element(b: bool, size: Size) -> Scalar { // SIMD uses all-1 as pattern for "true". In two's complement, // -1 has all its bits set to one and `from_int` will truncate or diff --git a/src/lib.rs b/src/lib.rs index d906553ec4..1eb40ab826 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -125,7 +125,7 @@ pub use crate::machine::{ }; pub use crate::mono_hash_map::MonoHashMap; pub use crate::operator::EvalContextExt as _; -pub use crate::provenance_gc::{EvalContextExt as _, VisitProvenance, VisitWith, LiveAllocs}; +pub use crate::provenance_gc::{EvalContextExt as _, LiveAllocs, VisitProvenance, VisitWith}; pub use crate::range_map::RangeMap; /// Insert rustc arguments at the beginning of the argument list that Miri wants to be diff --git a/src/provenance_gc.rs b/src/provenance_gc.rs index ef6d49f041..ab178f82d9 100644 --- a/src/provenance_gc.rs +++ b/src/provenance_gc.rs @@ -157,15 +157,13 @@ pub struct LiveAllocs<'a, 'mir, 'tcx> { impl LiveAllocs<'_, '_, '_> { pub fn is_live(&self, id: AllocId) -> bool { - self.collected.contains(&id) || - self.ecx.is_alloc_live(id) + self.collected.contains(&id) || self.ecx.is_alloc_live(id) } } impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} pub trait EvalContextExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> { fn run_provenance_gc(&mut self) { - // We collect all tags from various parts of the interpreter, but also let this = self.eval_context_mut(); @@ -196,10 +194,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> { fn remove_unreachable_allocs(&mut self, allocs: FxHashSet) { let this = self.eval_context_mut(); - let allocs = LiveAllocs { - ecx: this, - collected: allocs, - }; + let allocs = LiveAllocs { ecx: this, collected: allocs }; this.machine.allocation_spans.borrow_mut().retain(|id, _| allocs.is_live(*id)); this.machine.intptrcast.borrow_mut().remove_unreachable_allocs(&allocs); if let Some(borrow_tracker) = &this.machine.borrow_tracker { diff --git a/src/shims/env.rs b/src/shims/env.rs index 4243898590..9e19f72021 100644 --- a/src/shims/env.rs +++ b/src/shims/env.rs @@ -9,7 +9,6 @@ use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::Ty; use rustc_target::abi::Size; -use crate::helpers::target_os_is_unix; use crate::*; /// Check whether an operation that writes to a target buffer was successful. @@ -53,16 +52,15 @@ impl<'tcx> EnvVars<'tcx> { ecx: &mut InterpCx<'mir, 'tcx, MiriMachine<'mir, 'tcx>>, config: &MiriConfig, ) -> InterpResult<'tcx> { - let target_os = ecx.tcx.sess.target.os.as_ref(); - + // Initialize the `env_vars` map. // Skip the loop entirely if we don't want to forward anything. if ecx.machine.communicate() || !config.forwarded_env_vars.is_empty() { for (name, value) in &config.env { let forward = ecx.machine.communicate() || config.forwarded_env_vars.iter().any(|v| **v == *name); if forward { - let var_ptr = match target_os { - target if target_os_is_unix(target) => + let var_ptr = match ecx.tcx.sess.target.os.as_ref() { + _ if ecx.target_os_is_unix() => alloc_env_var_as_c_str(name.as_ref(), value.as_ref(), ecx)?, "windows" => alloc_env_var_as_wide_str(name.as_ref(), value.as_ref(), ecx)?, unsupported => @@ -75,7 +73,17 @@ impl<'tcx> EnvVars<'tcx> { } } } - ecx.update_environ() + + // Initialize the `environ` pointer when needed. + if ecx.target_os_is_unix() { + // This is memory backing an extern static, hence `ExternStatic`, not `Env`. + let layout = ecx.machine.layouts.mut_raw_ptr; + let place = ecx.allocate(layout, MiriMemoryKind::ExternStatic.into())?; + ecx.write_null(&place)?; + ecx.machine.env_vars.environ = Some(place); + ecx.update_environ()?; + } + Ok(()) } pub(crate) fn cleanup<'mir>( @@ -87,9 +95,11 @@ impl<'tcx> EnvVars<'tcx> { ecx.deallocate_ptr(ptr, None, MiriMemoryKind::Runtime.into())?; } // Deallocate environ var list. - let environ = ecx.machine.env_vars.environ.as_ref().unwrap(); - let old_vars_ptr = ecx.read_pointer(environ)?; - ecx.deallocate_ptr(old_vars_ptr, None, MiriMemoryKind::Runtime.into())?; + if ecx.target_os_is_unix() { + let environ = ecx.machine.env_vars.environ.as_ref().unwrap(); + let old_vars_ptr = ecx.read_pointer(environ)?; + ecx.deallocate_ptr(old_vars_ptr, None, MiriMemoryKind::Runtime.into())?; + } Ok(()) } } @@ -127,6 +137,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let name_ptr = this.read_pointer(name_op)?; let name = this.read_os_str_from_c_str(name_ptr)?; + this.read_environ()?; Ok(match this.machine.env_vars.map.get(name) { Some(var_ptr) => { // The offset is used to strip the "{name}=" part of the string. @@ -275,7 +286,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // Delete environment variable `{name}` if let Some(var) = this.machine.env_vars.map.remove(&name) { this.deallocate_ptr(var, None, MiriMemoryKind::Runtime.into())?; - this.update_environ()?; } Ok(this.eval_windows("c", "TRUE")) } else { @@ -284,7 +294,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { if let Some(var) = this.machine.env_vars.map.insert(name, var_ptr) { this.deallocate_ptr(var, None, MiriMemoryKind::Runtime.into())?; } - this.update_environ()?; Ok(this.eval_windows("c", "TRUE")) } } @@ -431,15 +440,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn update_environ(&mut self) -> InterpResult<'tcx> { let this = self.eval_context_mut(); // Deallocate the old environ list, if any. - if let Some(environ) = this.machine.env_vars.environ.as_ref() { - let old_vars_ptr = this.read_pointer(environ)?; + let environ = this.machine.env_vars.environ.as_ref().unwrap().clone(); + let old_vars_ptr = this.read_pointer(&environ)?; + if !this.ptr_is_null(old_vars_ptr)? { this.deallocate_ptr(old_vars_ptr, None, MiriMemoryKind::Runtime.into())?; - } else { - // No `environ` allocated yet, let's do that. - // This is memory backing an extern static, hence `ExternStatic`, not `Env`. - let layout = this.machine.layouts.mut_raw_ptr; - let place = this.allocate(layout, MiriMemoryKind::ExternStatic.into())?; - this.machine.env_vars.environ = Some(place); } // Collect all the pointers to each variable in a vector. @@ -459,8 +463,17 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let place = this.project_field(&vars_place, idx)?; this.write_pointer(var, &place)?; } - this.write_pointer(vars_place.ptr(), &this.machine.env_vars.environ.clone().unwrap())?; + this.write_pointer(vars_place.ptr(), &environ)?; + + Ok(()) + } + /// Reads from the `environ` static. + /// We don't actually care about the result, but we care about this potentially causing a data race. + fn read_environ(&self) -> InterpResult<'tcx> { + let this = self.eval_context_ref(); + let environ = this.machine.env_vars.environ.as_ref().unwrap(); + let _vars_ptr = this.read_pointer(environ)?; Ok(()) } diff --git a/src/shims/foreign_items.rs b/src/shims/foreign_items.rs index d7aaa08dbf..0957e72ee9 100644 --- a/src/shims/foreign_items.rs +++ b/src/shims/foreign_items.rs @@ -22,7 +22,6 @@ use rustc_target::{ }; use super::backtrace::EvalContextExt as _; -use crate::helpers::target_os_is_unix; use crate::*; /// Type of dynamic symbols (for `dlsym` et al) @@ -1012,9 +1011,11 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { "llvm.arm.hint" if this.tcx.sess.target.arch == "arm" => { let [arg] = this.check_shim(abi, Abi::Unadjusted, link_name, args)?; let arg = this.read_scalar(arg)?.to_i32()?; + // Note that different arguments might have different target feature requirements. match arg { // YIELD 1 => { + this.expect_target_feature_for_intrinsic(link_name, "v6")?; this.yield_active_thread(); } _ => { @@ -1058,7 +1059,7 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // Platform-specific shims _ => return match this.tcx.sess.target.os.as_ref() { - target_os if target_os_is_unix(target_os) => + _ if this.target_os_is_unix() => shims::unix::foreign_items::EvalContextExt::emulate_foreign_item_inner( this, link_name, abi, args, dest, ), diff --git a/src/shims/intrinsics/mod.rs b/src/shims/intrinsics/mod.rs index 8c90ceba1e..66918db995 100644 --- a/src/shims/intrinsics/mod.rs +++ b/src/shims/intrinsics/mod.rs @@ -365,36 +365,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let [val] = check_arg_count(args)?; let val = this.read_immediate(val)?; - let res = match val.layout.ty.kind() { - ty::Float(FloatTy::F32) => { - let f = val.to_scalar().to_f32()?; - this - .float_to_int_checked(f, dest.layout, Round::TowardZero) - .ok_or_else(|| { - err_ub_format!( - "`float_to_int_unchecked` intrinsic called on {f} which cannot be represented in target type `{:?}`", - dest.layout.ty - ) - })? - } - ty::Float(FloatTy::F64) => { - let f = val.to_scalar().to_f64()?; - this - .float_to_int_checked(f, dest.layout, Round::TowardZero) - .ok_or_else(|| { - err_ub_format!( - "`float_to_int_unchecked` intrinsic called on {f} which cannot be represented in target type `{:?}`", - dest.layout.ty - ) - })? - } - _ => - span_bug!( - this.cur_span(), - "`float_to_int_unchecked` called with non-float input type {:?}", - val.layout.ty - ), - }; + let res = this + .float_to_int_checked(&val, dest.layout, Round::TowardZero)? + .ok_or_else(|| { + err_ub_format!( + "`float_to_int_unchecked` intrinsic called on {val} which cannot be represented in target type `{:?}`", + dest.layout.ty + ) + })?; this.write_immediate(*res, dest)?; } diff --git a/src/shims/intrinsics/simd.rs b/src/shims/intrinsics/simd.rs index 70f90aac2c..d0a293d5f8 100644 --- a/src/shims/intrinsics/simd.rs +++ b/src/shims/intrinsics/simd.rs @@ -447,22 +447,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { (ty::Float(_), ty::Int(_) | ty::Uint(_)) if safe_cast => this.float_to_float_or_int(&op, dest.layout)?, // Float-to-int in unchecked mode - (ty::Float(FloatTy::F32), ty::Int(_) | ty::Uint(_)) if unsafe_cast => { - let f = op.to_scalar().to_f32()?; - this.float_to_int_checked(f, dest.layout, Round::TowardZero) + (ty::Float(_), ty::Int(_) | ty::Uint(_)) if unsafe_cast => { + this.float_to_int_checked(&op, dest.layout, Round::TowardZero)? .ok_or_else(|| { err_ub_format!( - "`simd_cast` intrinsic called on {f} which cannot be represented in target type `{:?}`", - dest.layout.ty - ) - })? - } - (ty::Float(FloatTy::F64), ty::Int(_) | ty::Uint(_)) if unsafe_cast => { - let f = op.to_scalar().to_f64()?; - this.float_to_int_checked(f, dest.layout, Round::TowardZero) - .ok_or_else(|| { - err_ub_format!( - "`simd_cast` intrinsic called on {f} which cannot be represented in target type `{:?}`", + "`simd_cast` intrinsic called on {op} which cannot be represented in target type `{:?}`", dest.layout.ty ) })? diff --git a/src/shims/time.rs b/src/shims/time.rs index 792122a001..13ce9d119b 100644 --- a/src/shims/time.rs +++ b/src/shims/time.rs @@ -31,8 +31,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let mut relative_clocks; match this.tcx.sess.target.os.as_ref() { - "linux" => { - // Linux has two main kinds of clocks. REALTIME clocks return the actual time since the + "linux" | "freebsd" => { + // Linux and FreeBSD have two main kinds of clocks. REALTIME clocks return the actual time since the // Unix epoch, including effects which may cause time to move backwards such as NTP. // Linux further distinguishes regular and "coarse" clocks, but the "coarse" version // is just specified to be "faster and less precise", so we implement both the same way. diff --git a/src/shims/unix/android/foreign_items.rs b/src/shims/unix/android/foreign_items.rs deleted file mode 100644 index f61ebd5a3a..0000000000 --- a/src/shims/unix/android/foreign_items.rs +++ /dev/null @@ -1,30 +0,0 @@ -use rustc_span::Symbol; -use rustc_target::spec::abi::Abi; - -use crate::*; -use shims::foreign_items::EmulateForeignItemResult; - -impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} - -pub fn is_dyn_sym(_name: &str) -> bool { - false -} - -pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { - #[allow(unused, clippy::match_single_binding)] // there isn't anything here yet - fn emulate_foreign_item_inner( - &mut self, - link_name: Symbol, - abi: Abi, - args: &[OpTy<'tcx, Provenance>], - dest: &PlaceTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, EmulateForeignItemResult> { - let this = self.eval_context_mut(); - - match link_name.as_str() { - _ => return Ok(EmulateForeignItemResult::NotSupported), - } - - Ok(EmulateForeignItemResult::NeedsJumping) - } -} diff --git a/src/shims/unix/android/mod.rs b/src/shims/unix/android/mod.rs deleted file mode 100644 index 09c6507b24..0000000000 --- a/src/shims/unix/android/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod foreign_items; diff --git a/src/shims/unix/foreign_items.rs b/src/shims/unix/foreign_items.rs index d155623eb7..23342c8045 100644 --- a/src/shims/unix/foreign_items.rs +++ b/src/shims/unix/foreign_items.rs @@ -15,7 +15,6 @@ use shims::unix::mem::EvalContextExt as _; use shims::unix::sync::EvalContextExt as _; use shims::unix::thread::EvalContextExt as _; -use shims::unix::android::foreign_items as android; use shims::unix::freebsd::foreign_items as freebsd; use shims::unix::linux::foreign_items as linux; use shims::unix::macos::foreign_items as macos; @@ -32,11 +31,10 @@ fn is_dyn_sym(name: &str, target_os: &str) -> bool { // Give specific OSes a chance to allow their symbols. _ => match target_os { - "android" => android::is_dyn_sym(name), "freebsd" => freebsd::is_dyn_sym(name), "linux" => linux::is_dyn_sym(name), "macos" => macos::is_dyn_sym(name), - target_os => panic!("unsupported Unix OS {target_os}"), + _ => false, }, } } @@ -163,6 +161,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { "ftruncate64" => { let [fd, length] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + let fd = this.read_scalar(fd)?.to_i32()?; + let length = this.read_scalar(length)?.to_i64()?; + let result = this.ftruncate64(fd, length.into())?; + this.write_scalar(result, dest)?; + } + "ftruncate" => { + let [fd, length] = + this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + let fd = this.read_scalar(fd)?.to_i32()?; + let length = this.read_scalar(length)?.to_int(this.libc_ty_layout("off_t").size)?; let result = this.ftruncate64(fd, length)?; this.write_scalar(result, dest)?; } @@ -696,7 +704,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { _ => { let target_os = &*this.tcx.sess.target.os; return match target_os { - "android" => android::EvalContextExt::emulate_foreign_item_inner(this, link_name, abi, args, dest), "freebsd" => freebsd::EvalContextExt::emulate_foreign_item_inner(this, link_name, abi, args, dest), "linux" => linux::EvalContextExt::emulate_foreign_item_inner(this, link_name, abi, args, dest), "macos" => macos::EvalContextExt::emulate_foreign_item_inner(this, link_name, abi, args, dest), diff --git a/src/shims/unix/fs.rs b/src/shims/unix/fs.rs index 471eadb8c3..ba40a1b3c3 100644 --- a/src/shims/unix/fs.rs +++ b/src/shims/unix/fs.rs @@ -1504,16 +1504,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } } - fn ftruncate64( - &mut self, - fd_op: &OpTy<'tcx, Provenance>, - length_op: &OpTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, Scalar> { + fn ftruncate64(&mut self, fd: i32, length: i128) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); - let fd = this.read_scalar(fd_op)?.to_i32()?; - let length = this.read_scalar(length_op)?.to_i64()?; - // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`ftruncate64`", reject_with)?; diff --git a/src/shims/unix/macos/foreign_items.rs b/src/shims/unix/macos/foreign_items.rs index e8f35e7ba5..07e19cadd6 100644 --- a/src/shims/unix/macos/foreign_items.rs +++ b/src/shims/unix/macos/foreign_items.rs @@ -72,13 +72,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let result = this.lseek64(fd, offset, whence)?; this.write_scalar(result, dest)?; } - "ftruncate" => { - let [fd, length] = - this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - // macOS is 64bit-only, so this is ftruncate64 - let result = this.ftruncate64(fd, length)?; - this.write_scalar(result, dest)?; - } "realpath$DARWIN_EXTSN" => { let [path, resolved_path] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; diff --git a/src/shims/unix/mod.rs b/src/shims/unix/mod.rs index 2f80149335..638473da02 100644 --- a/src/shims/unix/mod.rs +++ b/src/shims/unix/mod.rs @@ -5,7 +5,6 @@ mod mem; mod sync; mod thread; -mod android; mod freebsd; mod linux; mod macos; diff --git a/src/shims/x86/aesni.rs b/src/shims/x86/aesni.rs index aef930595b..fb0b701512 100644 --- a/src/shims/x86/aesni.rs +++ b/src/shims/x86/aesni.rs @@ -18,6 +18,7 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: dest: &PlaceTy<'tcx, Provenance>, ) -> InterpResult<'tcx, EmulateForeignItemResult> { let this = self.eval_context_mut(); + this.expect_target_feature_for_intrinsic(link_name, "aes")?; // Prefix should have already been checked. let unprefixed_name = link_name.as_str().strip_prefix("llvm.x86.aesni.").unwrap(); diff --git a/src/shims/x86/mod.rs b/src/shims/x86/mod.rs index d88a3127ec..2ae269db41 100644 --- a/src/shims/x86/mod.rs +++ b/src/shims/x86/mod.rs @@ -1,4 +1,4 @@ -use rustc_middle::mir; +use rustc_middle::{mir, ty}; use rustc_span::Symbol; use rustc_target::abi::Size; use rustc_target::spec::abi::Abi; @@ -119,53 +119,32 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: } } -/// Floating point comparison operation -/// -/// -/// -/// -/// -#[derive(Copy, Clone)] -enum FloatCmpOp { - Eq, - Lt, - Le, - Unord, - Neq, - /// Not less-than - Nlt, - /// Not less-or-equal - Nle, - /// Ordered, i.e. neither of them is NaN - Ord, -} - -impl FloatCmpOp { - /// Convert from the `imm` argument used to specify the comparison - /// operation in intrinsics such as `llvm.x86.sse.cmp.ss`. - fn from_intrinsic_imm(imm: i8, intrinsic: &str) -> InterpResult<'_, Self> { - match imm { - 0 => Ok(Self::Eq), - 1 => Ok(Self::Lt), - 2 => Ok(Self::Le), - 3 => Ok(Self::Unord), - 4 => Ok(Self::Neq), - 5 => Ok(Self::Nlt), - 6 => Ok(Self::Nle), - 7 => Ok(Self::Ord), - imm => { - throw_unsup_format!("invalid `imm` parameter of {intrinsic}: {imm}"); - } - } - } -} - #[derive(Copy, Clone)] enum FloatBinOp { /// Arithmetic operation Arith(mir::BinOp), /// Comparison - Cmp(FloatCmpOp), + /// + /// The semantics of this operator is a case distinction: we compare the two operands, + /// and then we return one of the four booleans `gt`, `lt`, `eq`, `unord` depending on + /// which class they fall into. + /// + /// AVX supports all 16 combinations, SSE only a subset + /// + /// + /// + /// + /// + Cmp { + /// Result when lhs < rhs + gt: bool, + /// Result when lhs > rhs + lt: bool, + /// Result when lhs == rhs + eq: bool, + /// Result when lhs is NaN or rhs is NaN + unord: bool, + }, /// Minimum value (with SSE semantics) /// /// @@ -182,6 +161,51 @@ enum FloatBinOp { Max, } +impl FloatBinOp { + /// Convert from the `imm` argument used to specify the comparison + /// operation in intrinsics such as `llvm.x86.sse.cmp.ss`. + fn cmp_from_imm<'tcx>( + this: &crate::MiriInterpCx<'_, 'tcx>, + imm: i8, + intrinsic: Symbol, + ) -> InterpResult<'tcx, Self> { + // Only bits 0..=4 are used, remaining should be zero. + if imm & !0b1_1111 != 0 { + throw_unsup_format!("invalid `imm` parameter of {intrinsic}: 0x{imm:x}"); + } + // Bit 4 specifies whether the operation is quiet or signaling, which + // we do not care in Miri. + // Bits 0..=2 specifies the operation. + // `gt` indicates the result to be returned when the LHS is strictly + // greater than the RHS, and so on. + let (gt, lt, eq, mut unord) = match imm & 0b111 { + // Equal + 0x0 => (false, false, true, false), + // Less-than + 0x1 => (false, true, false, false), + // Less-or-equal + 0x2 => (false, true, true, false), + // Unordered (either is NaN) + 0x3 => (false, false, false, true), + // Not equal + 0x4 => (true, true, false, true), + // Not less-than + 0x5 => (true, false, true, true), + // Not less-or-equal + 0x6 => (true, false, false, true), + // Ordered (neither is NaN) + 0x7 => (true, true, true, false), + _ => unreachable!(), + }; + // When bit 3 is 1 (only possible in AVX), unord is toggled. + if imm & 0b1000 != 0 { + this.expect_target_feature_for_intrinsic(intrinsic, "avx")?; + unord = !unord; + } + Ok(Self::Cmp { gt, lt, eq, unord }) + } +} + /// Performs `which` scalar operation on `left` and `right` and returns /// the result. fn bin_op_float<'tcx, F: rustc_apfloat::Float>( @@ -195,20 +219,15 @@ fn bin_op_float<'tcx, F: rustc_apfloat::Float>( let res = this.wrapping_binary_op(which, left, right)?; Ok(res.to_scalar()) } - FloatBinOp::Cmp(which) => { + FloatBinOp::Cmp { gt, lt, eq, unord } => { let left = left.to_scalar().to_float::()?; let right = right.to_scalar().to_float::()?; - // FIXME: Make sure that these operations match the semantics - // of cmpps/cmpss/cmppd/cmpsd - let res = match which { - FloatCmpOp::Eq => left == right, - FloatCmpOp::Lt => left < right, - FloatCmpOp::Le => left <= right, - FloatCmpOp::Unord => left.is_nan() || right.is_nan(), - FloatCmpOp::Neq => left != right, - FloatCmpOp::Nlt => !(left < right), - FloatCmpOp::Nle => !(left <= right), - FloatCmpOp::Ord => !left.is_nan() && !right.is_nan(), + + let res = match left.partial_cmp(&right) { + None => unord, + Some(std::cmp::Ordering::Less) => lt, + Some(std::cmp::Ordering::Equal) => eq, + Some(std::cmp::Ordering::Greater) => gt, }; Ok(bool_to_simd_element(res, Size::from_bits(F::BITS))) } @@ -312,6 +331,43 @@ fn bin_op_simd_float_all<'tcx, F: rustc_apfloat::Float>( Ok(()) } +/// Converts each element of `op` from floating point to signed integer. +/// +/// When the input value is NaN or out of range, fall back to minimum value. +/// +/// If `op` has more elements than `dest`, extra elements are ignored. If `op` +/// has less elements than `dest`, the rest is filled with zeros. +fn convert_float_to_int<'tcx>( + this: &mut crate::MiriInterpCx<'_, 'tcx>, + op: &OpTy<'tcx, Provenance>, + rnd: rustc_apfloat::Round, + dest: &PlaceTy<'tcx, Provenance>, +) -> InterpResult<'tcx, ()> { + let (op, op_len) = this.operand_to_simd(op)?; + let (dest, dest_len) = this.place_to_simd(dest)?; + + // Output must be *signed* integers. + assert!(matches!(dest.layout.field(this, 0).ty.kind(), ty::Int(_))); + + for i in 0..op_len.min(dest_len) { + let op = this.read_immediate(&this.project_index(&op, i)?)?; + let dest = this.project_index(&dest, i)?; + + let res = this.float_to_int_checked(&op, dest.layout, rnd)?.unwrap_or_else(|| { + // Fallback to minimum acording to SSE/AVX semantics. + ImmTy::from_int(dest.layout.size.signed_int_min(), dest.layout) + }); + this.write_immediate(*res, &dest)?; + } + // Fill remainder with zeros + for i in op_len..dest_len { + let dest = this.project_index(&dest, i)?; + this.write_scalar(Scalar::from_int(0, dest.layout.size), &dest)?; + } + + Ok(()) +} + /// Horizontaly performs `which` operation on adjacent values of /// `left` and `right` SIMD vectors and stores the result in `dest`. fn horizontal_bin_op<'tcx>( diff --git a/src/shims/x86/sse.rs b/src/shims/x86/sse.rs index 831228b7a2..6e06f62b34 100644 --- a/src/shims/x86/sse.rs +++ b/src/shims/x86/sse.rs @@ -5,7 +5,7 @@ use rustc_target::spec::abi::Abi; use rand::Rng as _; -use super::{bin_op_simd_float_all, bin_op_simd_float_first, FloatBinOp, FloatCmpOp}; +use super::{bin_op_simd_float_all, bin_op_simd_float_first, FloatBinOp}; use crate::*; use shims::foreign_items::EmulateForeignItemResult; @@ -21,6 +21,7 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: dest: &PlaceTy<'tcx, Provenance>, ) -> InterpResult<'tcx, EmulateForeignItemResult> { let this = self.eval_context_mut(); + this.expect_target_feature_for_intrinsic(link_name, "sse")?; // Prefix should have already been checked. let unprefixed_name = link_name.as_str().strip_prefix("llvm.x86.sse.").unwrap(); // All these intrinsics operate on 128-bit (f32x4) SIMD vectors unless stated otherwise. @@ -95,33 +96,37 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: unary_op_ps(this, which, op, dest)?; } - // Used to implement the _mm_cmp_ss function. + // Used to implement the _mm_cmp*_ss functions. // Performs a comparison operation on the first component of `left` // and `right`, returning 0 if false or `u32::MAX` if true. The remaining // components are copied from `left`. + // _mm_cmp_ss is actually an AVX function where the operation is specified + // by a const parameter. + // _mm_cmp{eq,lt,le,gt,ge,neq,nlt,nle,ngt,nge,ord,unord}_ss are SSE functions + // with hard-coded operations. "cmp.ss" => { let [left, right, imm] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let which = FloatBinOp::Cmp(FloatCmpOp::from_intrinsic_imm( - this.read_scalar(imm)?.to_i8()?, - "llvm.x86.sse.cmp.ss", - )?); + let which = + FloatBinOp::cmp_from_imm(this, this.read_scalar(imm)?.to_i8()?, link_name)?; bin_op_simd_float_first::(this, which, left, right, dest)?; } - // Used to implement the _mm_cmp_ps function. + // Used to implement the _mm_cmp*_ps functions. // Performs a comparison operation on each component of `left` // and `right`. For each component, returns 0 if false or u32::MAX // if true. + // _mm_cmp_ps is actually an AVX function where the operation is specified + // by a const parameter. + // _mm_cmp{eq,lt,le,gt,ge,neq,nlt,nle,ngt,nge,ord,unord}_ps are SSE functions + // with hard-coded operations. "cmp.ps" => { let [left, right, imm] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let which = FloatBinOp::Cmp(FloatCmpOp::from_intrinsic_imm( - this.read_scalar(imm)?.to_i8()?, - "llvm.x86.sse.cmp.ps", - )?); + let which = + FloatBinOp::cmp_from_imm(this, this.read_scalar(imm)?.to_i8()?, link_name)?; bin_op_simd_float_all::(this, which, left, right, dest)?; } @@ -163,7 +168,7 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: let [op] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; let (op, _) = this.operand_to_simd(op)?; - let op = this.read_scalar(&this.project_index(&op, 0)?)?.to_f32()?; + let op = this.read_immediate(&this.project_index(&op, 0)?)?; let rnd = match unprefixed_name { // "current SSE rounding mode", assume nearest @@ -175,7 +180,7 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: _ => unreachable!(), }; - let res = this.float_to_int_checked(op, dest.layout, rnd).unwrap_or_else(|| { + let res = this.float_to_int_checked(&op, dest.layout, rnd)?.unwrap_or_else(|| { // Fallback to minimum acording to SSE semantics. ImmTy::from_int(dest.layout.size.signed_int_min(), dest.layout) }); diff --git a/src/shims/x86/sse2.rs b/src/shims/x86/sse2.rs index 3f2b9f5f0a..49bf7547ab 100644 --- a/src/shims/x86/sse2.rs +++ b/src/shims/x86/sse2.rs @@ -4,7 +4,7 @@ use rustc_middle::ty::Ty; use rustc_span::Symbol; use rustc_target::spec::abi::Abi; -use super::{bin_op_simd_float_all, bin_op_simd_float_first, FloatBinOp, FloatCmpOp}; +use super::{bin_op_simd_float_all, bin_op_simd_float_first, convert_float_to_int, FloatBinOp}; use crate::*; use shims::foreign_items::EmulateForeignItemResult; @@ -20,6 +20,7 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: dest: &PlaceTy<'tcx, Provenance>, ) -> InterpResult<'tcx, EmulateForeignItemResult> { let this = self.eval_context_mut(); + this.expect_target_feature_for_intrinsic(link_name, "sse2")?; // Prefix should have already been checked. let unprefixed_name = link_name.as_str().strip_prefix("llvm.x86.sse2.").unwrap(); @@ -259,37 +260,42 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: this.write_scalar(Scalar::from_u64(res), &dest)?; } } - // Used to implement the _mm_cvtps_epi32 and _mm_cvttps_epi32 functions. - // Converts packed f32 to packed i32. - "cvtps2dq" | "cvttps2dq" => { + // Used to implement the _mm_cvtps_epi32, _mm_cvttps_epi32, _mm_cvtpd_epi32 + // and _mm_cvttpd_epi32 functions. + // Converts packed f32/f64 to packed i32. + "cvtps2dq" | "cvttps2dq" | "cvtpd2dq" | "cvttpd2dq" => { let [op] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - 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 (op_len, _) = op.layout.ty.simd_size_and_type(*this.tcx); + let (dest_len, _) = dest.layout.ty.simd_size_and_type(*this.tcx); + match unprefixed_name { + "cvtps2dq" | "cvttps2dq" => { + // f32x4 to i32x4 conversion + assert_eq!(op_len, 4); + assert_eq!(dest_len, op_len); + } + "cvtpd2dq" | "cvttpd2dq" => { + // f64x2 to i32x4 conversion + // the last two values are filled with zeros + assert_eq!(op_len, 2); + assert_eq!(dest_len, 4); + } + _ => unreachable!(), + } let rnd = match unprefixed_name { // "current SSE rounding mode", assume nearest // https://www.felixcloutier.com/x86/cvtps2dq - "cvtps2dq" => rustc_apfloat::Round::NearestTiesToEven, + // https://www.felixcloutier.com/x86/cvtpd2dq + "cvtps2dq" | "cvtpd2dq" => rustc_apfloat::Round::NearestTiesToEven, // always truncate // https://www.felixcloutier.com/x86/cvttps2dq - "cvttps2dq" => rustc_apfloat::Round::TowardZero, + // https://www.felixcloutier.com/x86/cvttpd2dq + "cvttps2dq" | "cvttpd2dq" => rustc_apfloat::Round::TowardZero, _ => unreachable!(), }; - for i in 0..dest_len { - let op = this.read_scalar(&this.project_index(&op, i)?)?.to_f32()?; - let dest = this.project_index(&dest, i)?; - - let res = - this.float_to_int_checked(op, dest.layout, rnd).unwrap_or_else(|| { - // Fallback to minimum acording to SSE2 semantics. - ImmTy::from_int(i32::MIN, this.machine.layouts.i32) - }); - this.write_immediate(*res, &dest)?; - } + convert_float_to_int(this, op, rnd, dest)?; } // Used to implement the _mm_packs_epi16 function. // Converts two 16-bit integer vectors to a single 8-bit integer @@ -461,18 +467,20 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: this.write_scalar(res, &dest)?; } } - // Used to implement the _mm_cmp*_sd function. + // Used to implement the _mm_cmp*_sd functions. // Performs a comparison operation on the first component of `left` // and `right`, returning 0 if false or `u64::MAX` if true. The remaining // components are copied from `left`. + // _mm_cmp_sd is actually an AVX function where the operation is specified + // by a const parameter. + // _mm_cmp{eq,lt,le,gt,ge,neq,nlt,nle,ngt,nge,ord,unord}_sd are SSE2 functions + // with hard-coded operations. "cmp.sd" => { let [left, right, imm] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let which = FloatBinOp::Cmp(FloatCmpOp::from_intrinsic_imm( - this.read_scalar(imm)?.to_i8()?, - "llvm.x86.sse2.cmp.sd", - )?); + let which = + FloatBinOp::cmp_from_imm(this, this.read_scalar(imm)?.to_i8()?, link_name)?; bin_op_simd_float_first::(this, which, left, right, dest)?; } @@ -480,14 +488,16 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: // Performs a comparison operation on each component of `left` // and `right`. For each component, returns 0 if false or `u64::MAX` // if true. + // _mm_cmp_pd is actually an AVX function where the operation is specified + // by a const parameter. + // _mm_cmp{eq,lt,le,gt,ge,neq,nlt,nle,ngt,nge,ord,unord}_pd are SSE2 functions + // with hard-coded operations. "cmp.pd" => { let [left, right, imm] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let which = FloatBinOp::Cmp(FloatCmpOp::from_intrinsic_imm( - this.read_scalar(imm)?.to_i8()?, - "llvm.x86.sse2.cmp.pd", - )?); + let which = + FloatBinOp::cmp_from_imm(this, this.read_scalar(imm)?.to_i8()?, link_name)?; bin_op_simd_float_all::(this, which, left, right, dest)?; } @@ -522,45 +532,6 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: }; this.write_scalar(Scalar::from_i32(i32::from(res)), dest)?; } - // Used to implement the _mm_cvtpd_epi32 and _mm_cvttpd_epi32 functions. - // Converts packed f64 to packed i32. - "cvtpd2dq" | "cvttpd2dq" => { - let [op] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - - let (op, op_len) = this.operand_to_simd(op)?; - let (dest, dest_len) = this.place_to_simd(dest)?; - - // op is f64x2, dest is i32x4 - assert_eq!(op_len, 2); - assert_eq!(dest_len, 4); - - let rnd = match unprefixed_name { - // "current SSE rounding mode", assume nearest - // https://www.felixcloutier.com/x86/cvtpd2dq - "cvtpd2dq" => rustc_apfloat::Round::NearestTiesToEven, - // always truncate - // https://www.felixcloutier.com/x86/cvttpd2dq - "cvttpd2dq" => rustc_apfloat::Round::TowardZero, - _ => unreachable!(), - }; - - for i in 0..op_len { - let op = this.read_scalar(&this.project_index(&op, i)?)?.to_f64()?; - let dest = this.project_index(&dest, i)?; - - let res = - this.float_to_int_checked(op, dest.layout, rnd).unwrap_or_else(|| { - // Fallback to minimum acording to SSE2 semantics. - ImmTy::from_int(i32::MIN, this.machine.layouts.i32) - }); - this.write_immediate(*res, &dest)?; - } - // Fill the remaining with zeros - for i in op_len..dest_len { - let dest = this.project_index(&dest, i)?; - this.write_scalar(Scalar::from_i32(0), &dest)?; - } - } // Use to implement the _mm_cvtsd_si32, _mm_cvttsd_si32, // _mm_cvtsd_si64 and _mm_cvttsd_si64 functions. // Converts the first component of `op` from f64 to i32/i64. @@ -568,7 +539,7 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: let [op] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; let (op, _) = this.operand_to_simd(op)?; - let op = this.read_scalar(&this.project_index(&op, 0)?)?.to_f64()?; + let op = this.read_immediate(&this.project_index(&op, 0)?)?; let rnd = match unprefixed_name { // "current SSE rounding mode", assume nearest @@ -580,7 +551,7 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: _ => unreachable!(), }; - let res = this.float_to_int_checked(op, dest.layout, rnd).unwrap_or_else(|| { + let res = this.float_to_int_checked(&op, dest.layout, rnd)?.unwrap_or_else(|| { // Fallback to minimum acording to SSE semantics. ImmTy::from_int(dest.layout.size.signed_int_min(), dest.layout) }); diff --git a/src/shims/x86/sse3.rs b/src/shims/x86/sse3.rs index 270da36f0e..99a7a4f2f8 100644 --- a/src/shims/x86/sse3.rs +++ b/src/shims/x86/sse3.rs @@ -18,6 +18,7 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: dest: &PlaceTy<'tcx, Provenance>, ) -> InterpResult<'tcx, EmulateForeignItemResult> { let this = self.eval_context_mut(); + this.expect_target_feature_for_intrinsic(link_name, "sse3")?; // Prefix should have already been checked. let unprefixed_name = link_name.as_str().strip_prefix("llvm.x86.sse3.").unwrap(); diff --git a/src/shims/x86/sse41.rs b/src/shims/x86/sse41.rs index 523f3bfc26..b3d1056ab0 100644 --- a/src/shims/x86/sse41.rs +++ b/src/shims/x86/sse41.rs @@ -18,6 +18,7 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: dest: &PlaceTy<'tcx, Provenance>, ) -> InterpResult<'tcx, EmulateForeignItemResult> { let this = self.eval_context_mut(); + this.expect_target_feature_for_intrinsic(link_name, "sse4.1")?; // Prefix should have already been checked. let unprefixed_name = link_name.as_str().strip_prefix("llvm.x86.sse41.").unwrap(); @@ -199,7 +200,7 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: Scalar::from_u16(min_index.try_into().unwrap()), &this.project_index(&dest, 1)?, )?; - // Fill remaining with zeros + // Fill remainder with zeros for i in 2..dest_len { this.write_scalar(Scalar::from_u16(0), &this.project_index(&dest, i)?)?; } diff --git a/src/shims/x86/ssse3.rs b/src/shims/x86/ssse3.rs index dbc2b947b3..724150fd2f 100644 --- a/src/shims/x86/ssse3.rs +++ b/src/shims/x86/ssse3.rs @@ -18,6 +18,7 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: dest: &PlaceTy<'tcx, Provenance>, ) -> InterpResult<'tcx, EmulateForeignItemResult> { let this = self.eval_context_mut(); + this.expect_target_feature_for_intrinsic(link_name, "ssse3")?; // Prefix should have already been checked. let unprefixed_name = link_name.as_str().strip_prefix("llvm.x86.ssse3.").unwrap(); diff --git a/tests/fail-dep/shims/env-set_var-data-race.rs b/tests/fail-dep/shims/env-set_var-data-race.rs new file mode 100644 index 0000000000..2b9e7a34d6 --- /dev/null +++ b/tests/fail-dep/shims/env-set_var-data-race.rs @@ -0,0 +1,17 @@ +//@compile-flags: -Zmiri-disable-isolation -Zmiri-preemption-rate=0 +//@ignore-target-windows: No libc on Windows + +use std::env; +use std::thread; + +fn main() { + let t = thread::spawn(|| unsafe { + // Access the environment in another thread without taking the env lock. + // This represents some C code that queries the environment. + libc::getenv(b"TZ\0".as_ptr().cast()); //~ERROR: Data race detected + }); + // Meanwhile, the main thread uses the "safe" Rust env accessor. + env::set_var("MY_RUST_VAR", "Ferris"); + + t.join().unwrap(); +} diff --git a/tests/fail-dep/shims/env-set_var-data-race.stderr b/tests/fail-dep/shims/env-set_var-data-race.stderr new file mode 100644 index 0000000000..b81e8db9e0 --- /dev/null +++ b/tests/fail-dep/shims/env-set_var-data-race.stderr @@ -0,0 +1,20 @@ +error: Undefined Behavior: Data race detected between (1) non-atomic write on thread `main` and (2) non-atomic read on thread `` at ALLOC. (2) just happened here + --> $DIR/env-set_var-data-race.rs:LL:CC + | +LL | libc::getenv(b"TZ/0".as_ptr().cast()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Data race detected between (1) non-atomic write on thread `main` and (2) non-atomic read on thread `` at ALLOC. (2) just happened here + | +help: and (1) occurred earlier here + --> $DIR/env-set_var-data-race.rs:LL:CC + | +LL | env::set_var("MY_RUST_VAR", "Ferris"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = 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): + = note: inside closure at $DIR/env-set_var-data-race.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/tests/fail/intrinsics/float_to_int_32_inf1.stderr b/tests/fail/intrinsics/float_to_int_32_inf1.stderr index 560771bc24..2de8f3420b 100644 --- a/tests/fail/intrinsics/float_to_int_32_inf1.stderr +++ b/tests/fail/intrinsics/float_to_int_32_inf1.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: `float_to_int_unchecked` intrinsic called on +Inf which cannot be represented in target type `i32` +error: Undefined Behavior: `float_to_int_unchecked` intrinsic called on +Inf_f32 which cannot be represented in target type `i32` --> $DIR/float_to_int_32_inf1.rs:LL:CC | LL | float_to_int_unchecked::(f32::INFINITY); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `float_to_int_unchecked` intrinsic called on +Inf which cannot be represented in target type `i32` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `float_to_int_unchecked` intrinsic called on +Inf_f32 which cannot be represented in target type `i32` | = 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 diff --git a/tests/fail/intrinsics/float_to_int_32_infneg1.stderr b/tests/fail/intrinsics/float_to_int_32_infneg1.stderr index ae78af3dcb..53ed208bde 100644 --- a/tests/fail/intrinsics/float_to_int_32_infneg1.stderr +++ b/tests/fail/intrinsics/float_to_int_32_infneg1.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: `float_to_int_unchecked` intrinsic called on -Inf which cannot be represented in target type `i32` +error: Undefined Behavior: `float_to_int_unchecked` intrinsic called on -Inf_f32 which cannot be represented in target type `i32` --> $DIR/float_to_int_32_infneg1.rs:LL:CC | LL | float_to_int_unchecked::(f32::NEG_INFINITY); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `float_to_int_unchecked` intrinsic called on -Inf which cannot be represented in target type `i32` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `float_to_int_unchecked` intrinsic called on -Inf_f32 which cannot be represented in target type `i32` | = 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 diff --git a/tests/fail/intrinsics/float_to_int_32_nan.stderr b/tests/fail/intrinsics/float_to_int_32_nan.stderr index 2db3efeaac..afda7124c1 100644 --- a/tests/fail/intrinsics/float_to_int_32_nan.stderr +++ b/tests/fail/intrinsics/float_to_int_32_nan.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: `float_to_int_unchecked` intrinsic called on NaN which cannot be represented in target type `u32` +error: Undefined Behavior: `float_to_int_unchecked` intrinsic called on NaN_f32 which cannot be represented in target type `u32` --> $DIR/float_to_int_32_nan.rs:LL:CC | LL | float_to_int_unchecked::(f32::NAN); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `float_to_int_unchecked` intrinsic called on NaN which cannot be represented in target type `u32` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `float_to_int_unchecked` intrinsic called on NaN_f32 which cannot be represented in target type `u32` | = 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 diff --git a/tests/fail/intrinsics/float_to_int_32_nanneg.stderr b/tests/fail/intrinsics/float_to_int_32_nanneg.stderr index 769c35255b..8ba46de864 100644 --- a/tests/fail/intrinsics/float_to_int_32_nanneg.stderr +++ b/tests/fail/intrinsics/float_to_int_32_nanneg.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: `float_to_int_unchecked` intrinsic called on NaN which cannot be represented in target type `u32` +error: Undefined Behavior: `float_to_int_unchecked` intrinsic called on NaN_f32 which cannot be represented in target type `u32` --> $DIR/float_to_int_32_nanneg.rs:LL:CC | LL | float_to_int_unchecked::(-f32::NAN); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `float_to_int_unchecked` intrinsic called on NaN which cannot be represented in target type `u32` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `float_to_int_unchecked` intrinsic called on NaN_f32 which cannot be represented in target type `u32` | = 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 diff --git a/tests/fail/intrinsics/float_to_int_32_neg.stderr b/tests/fail/intrinsics/float_to_int_32_neg.stderr index 55df31dddd..9f4b2af216 100644 --- a/tests/fail/intrinsics/float_to_int_32_neg.stderr +++ b/tests/fail/intrinsics/float_to_int_32_neg.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: `float_to_int_unchecked` intrinsic called on -1 which cannot be represented in target type `u32` +error: Undefined Behavior: `float_to_int_unchecked` intrinsic called on -1f32 which cannot be represented in target type `u32` --> $DIR/float_to_int_32_neg.rs:LL:CC | LL | float_to_int_unchecked::(-1.000000001f32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `float_to_int_unchecked` intrinsic called on -1 which cannot be represented in target type `u32` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `float_to_int_unchecked` intrinsic called on -1f32 which cannot be represented in target type `u32` | = 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 diff --git a/tests/fail/intrinsics/float_to_int_32_too_big1.stderr b/tests/fail/intrinsics/float_to_int_32_too_big1.stderr index 7a87de7b3b..a8e56ddb59 100644 --- a/tests/fail/intrinsics/float_to_int_32_too_big1.stderr +++ b/tests/fail/intrinsics/float_to_int_32_too_big1.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: `float_to_int_unchecked` intrinsic called on 2.14748365E+9 which cannot be represented in target type `i32` +error: Undefined Behavior: `float_to_int_unchecked` intrinsic called on 2.14748365E+9f32 which cannot be represented in target type `i32` --> $DIR/float_to_int_32_too_big1.rs:LL:CC | LL | float_to_int_unchecked::(2147483648.0f32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `float_to_int_unchecked` intrinsic called on 2.14748365E+9 which cannot be represented in target type `i32` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `float_to_int_unchecked` intrinsic called on 2.14748365E+9f32 which cannot be represented in target type `i32` | = 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 diff --git a/tests/fail/intrinsics/float_to_int_32_too_big2.stderr b/tests/fail/intrinsics/float_to_int_32_too_big2.stderr index 6be9b4924b..a966e2e639 100644 --- a/tests/fail/intrinsics/float_to_int_32_too_big2.stderr +++ b/tests/fail/intrinsics/float_to_int_32_too_big2.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: `float_to_int_unchecked` intrinsic called on 4.2949673E+9 which cannot be represented in target type `u32` +error: Undefined Behavior: `float_to_int_unchecked` intrinsic called on 4.2949673E+9f32 which cannot be represented in target type `u32` --> $DIR/float_to_int_32_too_big2.rs:LL:CC | LL | float_to_int_unchecked::((u32::MAX - 127) as f32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `float_to_int_unchecked` intrinsic called on 4.2949673E+9 which cannot be represented in target type `u32` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `float_to_int_unchecked` intrinsic called on 4.2949673E+9f32 which cannot be represented in target type `u32` | = 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 diff --git a/tests/fail/intrinsics/float_to_int_32_too_small1.stderr b/tests/fail/intrinsics/float_to_int_32_too_small1.stderr index 5a467f39d9..6115b381eb 100644 --- a/tests/fail/intrinsics/float_to_int_32_too_small1.stderr +++ b/tests/fail/intrinsics/float_to_int_32_too_small1.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: `float_to_int_unchecked` intrinsic called on -2.1474839E+9 which cannot be represented in target type `i32` +error: Undefined Behavior: `float_to_int_unchecked` intrinsic called on -2.1474839E+9f32 which cannot be represented in target type `i32` --> $DIR/float_to_int_32_too_small1.rs:LL:CC | LL | float_to_int_unchecked::(-2147483904.0f32); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `float_to_int_unchecked` intrinsic called on -2.1474839E+9 which cannot be represented in target type `i32` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `float_to_int_unchecked` intrinsic called on -2.1474839E+9f32 which cannot be represented in target type `i32` | = 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 diff --git a/tests/fail/intrinsics/float_to_int_64_inf1.stderr b/tests/fail/intrinsics/float_to_int_64_inf1.stderr index 5cbf191213..5db9fb1417 100644 --- a/tests/fail/intrinsics/float_to_int_64_inf1.stderr +++ b/tests/fail/intrinsics/float_to_int_64_inf1.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: `float_to_int_unchecked` intrinsic called on +Inf which cannot be represented in target type `u128` +error: Undefined Behavior: `float_to_int_unchecked` intrinsic called on +Inf_f64 which cannot be represented in target type `u128` --> $DIR/float_to_int_64_inf1.rs:LL:CC | LL | float_to_int_unchecked::(f64::INFINITY); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `float_to_int_unchecked` intrinsic called on +Inf which cannot be represented in target type `u128` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `float_to_int_unchecked` intrinsic called on +Inf_f64 which cannot be represented in target type `u128` | = 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 diff --git a/tests/fail/intrinsics/float_to_int_64_infneg1.stderr b/tests/fail/intrinsics/float_to_int_64_infneg1.stderr index 4a4d71f53e..3e8dadb79e 100644 --- a/tests/fail/intrinsics/float_to_int_64_infneg1.stderr +++ b/tests/fail/intrinsics/float_to_int_64_infneg1.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: `float_to_int_unchecked` intrinsic called on -Inf which cannot be represented in target type `u128` +error: Undefined Behavior: `float_to_int_unchecked` intrinsic called on -Inf_f64 which cannot be represented in target type `u128` --> $DIR/float_to_int_64_infneg1.rs:LL:CC | LL | float_to_int_unchecked::(f64::NEG_INFINITY); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `float_to_int_unchecked` intrinsic called on -Inf which cannot be represented in target type `u128` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `float_to_int_unchecked` intrinsic called on -Inf_f64 which cannot be represented in target type `u128` | = 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 diff --git a/tests/fail/intrinsics/float_to_int_64_infneg2.stderr b/tests/fail/intrinsics/float_to_int_64_infneg2.stderr index 1f970b3985..cb59974bf4 100644 --- a/tests/fail/intrinsics/float_to_int_64_infneg2.stderr +++ b/tests/fail/intrinsics/float_to_int_64_infneg2.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: `float_to_int_unchecked` intrinsic called on -Inf which cannot be represented in target type `i128` +error: Undefined Behavior: `float_to_int_unchecked` intrinsic called on -Inf_f64 which cannot be represented in target type `i128` --> $DIR/float_to_int_64_infneg2.rs:LL:CC | LL | float_to_int_unchecked::(f64::NEG_INFINITY); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `float_to_int_unchecked` intrinsic called on -Inf which cannot be represented in target type `i128` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `float_to_int_unchecked` intrinsic called on -Inf_f64 which cannot be represented in target type `i128` | = 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 diff --git a/tests/fail/intrinsics/float_to_int_64_nan.stderr b/tests/fail/intrinsics/float_to_int_64_nan.stderr index 85e9aa87a9..6869706051 100644 --- a/tests/fail/intrinsics/float_to_int_64_nan.stderr +++ b/tests/fail/intrinsics/float_to_int_64_nan.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: `float_to_int_unchecked` intrinsic called on NaN which cannot be represented in target type `u32` +error: Undefined Behavior: `float_to_int_unchecked` intrinsic called on NaN_f64 which cannot be represented in target type `u32` --> $DIR/float_to_int_64_nan.rs:LL:CC | LL | float_to_int_unchecked::(f64::NAN); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `float_to_int_unchecked` intrinsic called on NaN which cannot be represented in target type `u32` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `float_to_int_unchecked` intrinsic called on NaN_f64 which cannot be represented in target type `u32` | = 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 diff --git a/tests/fail/intrinsics/float_to_int_64_neg.stderr b/tests/fail/intrinsics/float_to_int_64_neg.stderr index a8f495f704..505a6463cd 100644 --- a/tests/fail/intrinsics/float_to_int_64_neg.stderr +++ b/tests/fail/intrinsics/float_to_int_64_neg.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: `float_to_int_unchecked` intrinsic called on -1.0000000000000999 which cannot be represented in target type `u128` +error: Undefined Behavior: `float_to_int_unchecked` intrinsic called on -1.0000000000000999f64 which cannot be represented in target type `u128` --> $DIR/float_to_int_64_neg.rs:LL:CC | LL | float_to_int_unchecked::(-1.0000000000001f64); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `float_to_int_unchecked` intrinsic called on -1.0000000000000999 which cannot be represented in target type `u128` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `float_to_int_unchecked` intrinsic called on -1.0000000000000999f64 which cannot be represented in target type `u128` | = 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 diff --git a/tests/fail/intrinsics/float_to_int_64_too_big1.stderr b/tests/fail/intrinsics/float_to_int_64_too_big1.stderr index 3da8bdf923..bcfd394686 100644 --- a/tests/fail/intrinsics/float_to_int_64_too_big1.stderr +++ b/tests/fail/intrinsics/float_to_int_64_too_big1.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: `float_to_int_unchecked` intrinsic called on 2147483648 which cannot be represented in target type `i32` +error: Undefined Behavior: `float_to_int_unchecked` intrinsic called on 2147483648f64 which cannot be represented in target type `i32` --> $DIR/float_to_int_64_too_big1.rs:LL:CC | LL | float_to_int_unchecked::(2147483648.0f64); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `float_to_int_unchecked` intrinsic called on 2147483648 which cannot be represented in target type `i32` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `float_to_int_unchecked` intrinsic called on 2147483648f64 which cannot be represented in target type `i32` | = 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 diff --git a/tests/fail/intrinsics/float_to_int_64_too_big2.stderr b/tests/fail/intrinsics/float_to_int_64_too_big2.stderr index 57383892c3..ac6139d7e7 100644 --- a/tests/fail/intrinsics/float_to_int_64_too_big2.stderr +++ b/tests/fail/intrinsics/float_to_int_64_too_big2.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: `float_to_int_unchecked` intrinsic called on 9.2233720368547758E+18 which cannot be represented in target type `i64` +error: Undefined Behavior: `float_to_int_unchecked` intrinsic called on 9.2233720368547758E+18f64 which cannot be represented in target type `i64` --> $DIR/float_to_int_64_too_big2.rs:LL:CC | LL | float_to_int_unchecked::(9223372036854775808.0f64); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `float_to_int_unchecked` intrinsic called on 9.2233720368547758E+18 which cannot be represented in target type `i64` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `float_to_int_unchecked` intrinsic called on 9.2233720368547758E+18f64 which cannot be represented in target type `i64` | = 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 diff --git a/tests/fail/intrinsics/float_to_int_64_too_big3.stderr b/tests/fail/intrinsics/float_to_int_64_too_big3.stderr index 7558fafd97..e289b4c0fc 100644 --- a/tests/fail/intrinsics/float_to_int_64_too_big3.stderr +++ b/tests/fail/intrinsics/float_to_int_64_too_big3.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: `float_to_int_unchecked` intrinsic called on 1.8446744073709552E+19 which cannot be represented in target type `u64` +error: Undefined Behavior: `float_to_int_unchecked` intrinsic called on 1.8446744073709552E+19f64 which cannot be represented in target type `u64` --> $DIR/float_to_int_64_too_big3.rs:LL:CC | LL | float_to_int_unchecked::(18446744073709551616.0f64); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `float_to_int_unchecked` intrinsic called on 1.8446744073709552E+19 which cannot be represented in target type `u64` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `float_to_int_unchecked` intrinsic called on 1.8446744073709552E+19f64 which cannot be represented in target type `u64` | = 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 diff --git a/tests/fail/intrinsics/float_to_int_64_too_big4.stderr b/tests/fail/intrinsics/float_to_int_64_too_big4.stderr index d01c19b739..657c72daa6 100644 --- a/tests/fail/intrinsics/float_to_int_64_too_big4.stderr +++ b/tests/fail/intrinsics/float_to_int_64_too_big4.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: `float_to_int_unchecked` intrinsic called on 3.4028236692093846E+38 which cannot be represented in target type `u128` +error: Undefined Behavior: `float_to_int_unchecked` intrinsic called on 3.4028236692093846E+38f64 which cannot be represented in target type `u128` --> $DIR/float_to_int_64_too_big4.rs:LL:CC | LL | float_to_int_unchecked::(u128::MAX as f64); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `float_to_int_unchecked` intrinsic called on 3.4028236692093846E+38 which cannot be represented in target type `u128` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `float_to_int_unchecked` intrinsic called on 3.4028236692093846E+38f64 which cannot be represented in target type `u128` | = 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 diff --git a/tests/fail/intrinsics/float_to_int_64_too_big5.stderr b/tests/fail/intrinsics/float_to_int_64_too_big5.stderr index 0af13b8bbf..2a61b5fe15 100644 --- a/tests/fail/intrinsics/float_to_int_64_too_big5.stderr +++ b/tests/fail/intrinsics/float_to_int_64_too_big5.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: `float_to_int_unchecked` intrinsic called on 2.4028236692093845E+38 which cannot be represented in target type `i128` +error: Undefined Behavior: `float_to_int_unchecked` intrinsic called on 2.4028236692093845E+38f64 which cannot be represented in target type `i128` --> $DIR/float_to_int_64_too_big5.rs:LL:CC | LL | float_to_int_unchecked::(240282366920938463463374607431768211455.0f64); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `float_to_int_unchecked` intrinsic called on 2.4028236692093845E+38 which cannot be represented in target type `i128` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `float_to_int_unchecked` intrinsic called on 2.4028236692093845E+38f64 which cannot be represented in target type `i128` | = 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 diff --git a/tests/fail/intrinsics/float_to_int_64_too_big6.stderr b/tests/fail/intrinsics/float_to_int_64_too_big6.stderr index a99bbe1582..1b4b76ced1 100644 --- a/tests/fail/intrinsics/float_to_int_64_too_big6.stderr +++ b/tests/fail/intrinsics/float_to_int_64_too_big6.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: `float_to_int_unchecked` intrinsic called on 1.7976931348623157E+308 which cannot be represented in target type `u128` +error: Undefined Behavior: `float_to_int_unchecked` intrinsic called on 1.7976931348623157E+308f64 which cannot be represented in target type `u128` --> $DIR/float_to_int_64_too_big6.rs:LL:CC | LL | float_to_int_unchecked::(f64::MAX); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `float_to_int_unchecked` intrinsic called on 1.7976931348623157E+308 which cannot be represented in target type `u128` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `float_to_int_unchecked` intrinsic called on 1.7976931348623157E+308f64 which cannot be represented in target type `u128` | = 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 diff --git a/tests/fail/intrinsics/float_to_int_64_too_big7.stderr b/tests/fail/intrinsics/float_to_int_64_too_big7.stderr index cef4acee68..47df8d90c0 100644 --- a/tests/fail/intrinsics/float_to_int_64_too_big7.stderr +++ b/tests/fail/intrinsics/float_to_int_64_too_big7.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: `float_to_int_unchecked` intrinsic called on -1.7976931348623157E+308 which cannot be represented in target type `i128` +error: Undefined Behavior: `float_to_int_unchecked` intrinsic called on -1.7976931348623157E+308f64 which cannot be represented in target type `i128` --> $DIR/float_to_int_64_too_big7.rs:LL:CC | LL | float_to_int_unchecked::(f64::MIN); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `float_to_int_unchecked` intrinsic called on -1.7976931348623157E+308 which cannot be represented in target type `i128` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `float_to_int_unchecked` intrinsic called on -1.7976931348623157E+308f64 which cannot be represented in target type `i128` | = 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 diff --git a/tests/fail/intrinsics/float_to_int_64_too_small1.stderr b/tests/fail/intrinsics/float_to_int_64_too_small1.stderr index 0823e84aaf..c5eb405ee9 100644 --- a/tests/fail/intrinsics/float_to_int_64_too_small1.stderr +++ b/tests/fail/intrinsics/float_to_int_64_too_small1.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: `float_to_int_unchecked` intrinsic called on -2147483649 which cannot be represented in target type `i32` +error: Undefined Behavior: `float_to_int_unchecked` intrinsic called on -2147483649f64 which cannot be represented in target type `i32` --> $DIR/float_to_int_64_too_small1.rs:LL:CC | LL | float_to_int_unchecked::(-2147483649.0f64); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `float_to_int_unchecked` intrinsic called on -2147483649 which cannot be represented in target type `i32` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `float_to_int_unchecked` intrinsic called on -2147483649f64 which cannot be represented in target type `i32` | = 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 diff --git a/tests/fail/intrinsics/float_to_int_64_too_small2.stderr b/tests/fail/intrinsics/float_to_int_64_too_small2.stderr index 3260faab78..e7d12a18a2 100644 --- a/tests/fail/intrinsics/float_to_int_64_too_small2.stderr +++ b/tests/fail/intrinsics/float_to_int_64_too_small2.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: `float_to_int_unchecked` intrinsic called on -9.2233720368547778E+18 which cannot be represented in target type `i64` +error: Undefined Behavior: `float_to_int_unchecked` intrinsic called on -9.2233720368547778E+18f64 which cannot be represented in target type `i64` --> $DIR/float_to_int_64_too_small2.rs:LL:CC | LL | float_to_int_unchecked::(-9223372036854777856.0f64); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `float_to_int_unchecked` intrinsic called on -9.2233720368547778E+18 which cannot be represented in target type `i64` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `float_to_int_unchecked` intrinsic called on -9.2233720368547778E+18f64 which cannot be represented in target type `i64` | = 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 diff --git a/tests/fail/intrinsics/float_to_int_64_too_small3.stderr b/tests/fail/intrinsics/float_to_int_64_too_small3.stderr index 4f64fabc33..3d8366c725 100644 --- a/tests/fail/intrinsics/float_to_int_64_too_small3.stderr +++ b/tests/fail/intrinsics/float_to_int_64_too_small3.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: `float_to_int_unchecked` intrinsic called on -2.4028236692093845E+38 which cannot be represented in target type `i128` +error: Undefined Behavior: `float_to_int_unchecked` intrinsic called on -2.4028236692093845E+38f64 which cannot be represented in target type `i128` --> $DIR/float_to_int_64_too_small3.rs:LL:CC | LL | float_to_int_unchecked::(-240282366920938463463374607431768211455.0f64); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `float_to_int_unchecked` intrinsic called on -2.4028236692093845E+38 which cannot be represented in target type `i128` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `float_to_int_unchecked` intrinsic called on -2.4028236692093845E+38f64 which cannot be represented in target type `i128` | = 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 diff --git a/tests/fail/intrinsics/simd-float-to-int.stderr b/tests/fail/intrinsics/simd-float-to-int.stderr index d4d1830e96..d75fefe1de 100644 --- a/tests/fail/intrinsics/simd-float-to-int.stderr +++ b/tests/fail/intrinsics/simd-float-to-int.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: `simd_cast` intrinsic called on 3.40282347E+38 which cannot be represented in target type `i32` +error: Undefined Behavior: `simd_cast` intrinsic called on 3.40282347E+38f32 which cannot be represented in target type `i32` --> $DIR/simd-float-to-int.rs:LL:CC | LL | let _x: i32x2 = f32x2::from_array([f32::MAX, f32::MIN]).to_int_unchecked(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `simd_cast` intrinsic called on 3.40282347E+38 which cannot be represented in target type `i32` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `simd_cast` intrinsic called on 3.40282347E+38f32 which cannot be represented in target type `i32` | = 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 diff --git a/tests/fail/shims/intrinsic_target_feature.rs b/tests/fail/shims/intrinsic_target_feature.rs new file mode 100644 index 0000000000..9263ad381d --- /dev/null +++ b/tests/fail/shims/intrinsic_target_feature.rs @@ -0,0 +1,42 @@ +// Ignore everything except x86 and x86_64 +// Any new targets that are added to CI should be ignored here. +// We cannot use `cfg`-based tricks here since the output would be +// different for non-x86 targets. +//@ignore-target-aarch64 +//@ignore-target-arm +//@ignore-target-avr +//@ignore-target-s390x +//@ignore-target-thumbv7em +//@ignore-target-wasm32 +// Explicitly disable SSE4.1 because it is enabled by default on macOS +//@compile-flags: -C target-feature=-sse4.1 + +#![feature(link_llvm_intrinsics, simd_ffi)] + +#[cfg(target_arch = "x86")] +use std::arch::x86::*; +#[cfg(target_arch = "x86_64")] +use std::arch::x86_64::*; + +fn main() { + assert!(is_x86_feature_detected!("sse")); + assert!(!is_x86_feature_detected!("sse4.1")); + + unsafe { + // Pass, since SSE is enabled + addss(_mm_setzero_ps(), _mm_setzero_ps()); + + // Fail, since SSE4.1 is not enabled + dpps(_mm_setzero_ps(), _mm_setzero_ps(), 0); + //~^ ERROR: Undefined Behavior: attempted to call intrinsic `llvm.x86.sse41.dpps` that requires missing target feature sse4.1 + } +} + +#[allow(improper_ctypes)] +extern "C" { + #[link_name = "llvm.x86.sse.add.ss"] + fn addss(a: __m128, b: __m128) -> __m128; + + #[link_name = "llvm.x86.sse41.dpps"] + fn dpps(a: __m128, b: __m128, imm8: u8) -> __m128; +} diff --git a/tests/fail/shims/intrinsic_target_feature.stderr b/tests/fail/shims/intrinsic_target_feature.stderr new file mode 100644 index 0000000000..8e83d20854 --- /dev/null +++ b/tests/fail/shims/intrinsic_target_feature.stderr @@ -0,0 +1,15 @@ +error: Undefined Behavior: attempted to call intrinsic `llvm.x86.sse41.dpps` that requires missing target feature sse4.1 + --> $DIR/intrinsic_target_feature.rs:LL:CC + | +LL | dpps(_mm_setzero_ps(), _mm_setzero_ps(), 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ attempted to call intrinsic `llvm.x86.sse41.dpps` that requires missing target feature sse4.1 + | + = 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/intrinsic_target_feature.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/tests/pass-dep/shims/env-cleanup-data-race.rs b/tests/pass-dep/shims/env-cleanup-data-race.rs index d36ffe7032..5c29a1b15a 100644 --- a/tests/pass-dep/shims/env-cleanup-data-race.rs +++ b/tests/pass-dep/shims/env-cleanup-data-race.rs @@ -2,15 +2,13 @@ //@ignore-target-windows: No libc on Windows use std::ffi::CStr; -use std::ffi::CString; use std::thread; fn main() { unsafe { thread::spawn(|| { // Access the environment in another thread without taking the env lock - let k = CString::new("MIRI_ENV_VAR_TEST".as_bytes()).unwrap(); - let s = libc::getenv(k.as_ptr()) as *const libc::c_char; + let s = libc::getenv("MIRI_ENV_VAR_TEST\0".as_ptr().cast()); if s.is_null() { panic!("null"); } @@ -19,5 +17,6 @@ fn main() { thread::yield_now(); // After the main thread exits, env vars will be cleaned up -- but because we have not *joined* // the other thread, those accesses technically race with those in the other thread. + // We don't want to emit an error here, though. } } diff --git a/tests/pass-dep/shims/libc-fs-with-isolation.rs b/tests/pass-dep/shims/libc-fs-with-isolation.rs index 5185db0b0e..adfece5866 100644 --- a/tests/pass-dep/shims/libc-fs-with-isolation.rs +++ b/tests/pass-dep/shims/libc-fs-with-isolation.rs @@ -1,4 +1,5 @@ //@ignore-target-windows: no libc on Windows +//@ignore-target-freebsd: FIXME needs foreign function `stat@FBSD_1.0` //@compile-flags: -Zmiri-isolation-error=warn-nobacktrace //@normalize-stderr-test: "(stat(x)?)" -> "$$STAT" diff --git a/tests/pass-dep/shims/libc-fs.rs b/tests/pass-dep/shims/libc-fs.rs index 767a4fdbed..697970a088 100644 --- a/tests/pass-dep/shims/libc-fs.rs +++ b/tests/pass-dep/shims/libc-fs.rs @@ -23,6 +23,7 @@ fn main() { test_file_open_unix_extra_third_arg(); #[cfg(target_os = "linux")] test_o_tmpfile_flag(); + test_posix_mkstemp(); } /// Prepare: compute filename and make sure the file does not exist. @@ -151,3 +152,45 @@ fn test_o_tmpfile_flag() { .raw_os_error(), ); } + +fn test_posix_mkstemp() { + use std::ffi::OsStr; + use std::os::unix::io::FromRawFd; + use std::path::Path; + + let valid_template = "fooXXXXXX"; + // C needs to own this as `mkstemp(3)` says: + // "Since it will be modified, `template` must not be a string constant, but + // should be declared as a character array." + // There seems to be no `as_mut_ptr` on `CString` so we need to use `into_raw`. + let ptr = CString::new(valid_template).unwrap().into_raw(); + let fd = unsafe { libc::mkstemp(ptr) }; + // Take ownership back in Rust to not leak memory. + let slice = unsafe { CString::from_raw(ptr) }; + assert!(fd > 0); + let osstr = OsStr::from_bytes(slice.to_bytes()); + let path: &Path = osstr.as_ref(); + let name = path.file_name().unwrap().to_string_lossy(); + assert!(name.ne("fooXXXXXX")); + assert!(name.starts_with("foo")); + assert_eq!(name.len(), 9); + assert_eq!( + name.chars().skip(3).filter(char::is_ascii_alphanumeric).collect::>().len(), + 6 + ); + let file = unsafe { File::from_raw_fd(fd) }; + assert!(file.set_len(0).is_ok()); + + let invalid_templates = vec!["foo", "barXX", "XXXXXXbaz", "whatXXXXXXever", "X"]; + for t in invalid_templates { + let ptr = CString::new(t).unwrap().into_raw(); + let fd = unsafe { libc::mkstemp(ptr) }; + let _ = unsafe { CString::from_raw(ptr) }; + // "On error, -1 is returned, and errno is set to + // indicate the error" + assert_eq!(fd, -1); + let e = std::io::Error::last_os_error(); + assert_eq!(e.raw_os_error(), Some(libc::EINVAL)); + assert_eq!(e.kind(), std::io::ErrorKind::InvalidInput); + } +} diff --git a/tests/pass-dep/shims/libc-misc.rs b/tests/pass-dep/shims/libc-misc.rs index de1acb13cb..abb384b0a8 100644 --- a/tests/pass-dep/shims/libc-misc.rs +++ b/tests/pass-dep/shims/libc-misc.rs @@ -172,14 +172,13 @@ fn test_thread_local_errno() { } /// Tests whether clock support exists at all -#[cfg(not(target_os = "freebsd"))] fn test_clocks() { let mut tp = std::mem::MaybeUninit::::uninit(); let is_error = unsafe { libc::clock_gettime(libc::CLOCK_REALTIME, tp.as_mut_ptr()) }; assert_eq!(is_error, 0); let is_error = unsafe { libc::clock_gettime(libc::CLOCK_MONOTONIC, tp.as_mut_ptr()) }; assert_eq!(is_error, 0); - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "freebsd"))] { let is_error = unsafe { libc::clock_gettime(libc::CLOCK_REALTIME_COARSE, tp.as_mut_ptr()) }; assert_eq!(is_error, 0); @@ -238,51 +237,6 @@ fn test_isatty() { } } -#[cfg(not(target_os = "freebsd"))] -fn test_posix_mkstemp() { - use std::ffi::CString; - use std::ffi::OsStr; - use std::os::unix::ffi::OsStrExt; - use std::os::unix::io::FromRawFd; - use std::path::Path; - - let valid_template = "fooXXXXXX"; - // C needs to own this as `mkstemp(3)` says: - // "Since it will be modified, `template` must not be a string constant, but - // should be declared as a character array." - // There seems to be no `as_mut_ptr` on `CString` so we need to use `into_raw`. - let ptr = CString::new(valid_template).unwrap().into_raw(); - let fd = unsafe { libc::mkstemp(ptr) }; - // Take ownership back in Rust to not leak memory. - let slice = unsafe { CString::from_raw(ptr) }; - assert!(fd > 0); - let osstr = OsStr::from_bytes(slice.to_bytes()); - let path: &Path = osstr.as_ref(); - let name = path.file_name().unwrap().to_string_lossy(); - assert!(name.ne("fooXXXXXX")); - assert!(name.starts_with("foo")); - assert_eq!(name.len(), 9); - assert_eq!( - name.chars().skip(3).filter(char::is_ascii_alphanumeric).collect::>().len(), - 6 - ); - let file = unsafe { File::from_raw_fd(fd) }; - assert!(file.set_len(0).is_ok()); - - let invalid_templates = vec!["foo", "barXX", "XXXXXXbaz", "whatXXXXXXever", "X"]; - for t in invalid_templates { - let ptr = CString::new(t).unwrap().into_raw(); - let fd = unsafe { libc::mkstemp(ptr) }; - let _ = unsafe { CString::from_raw(ptr) }; - // "On error, -1 is returned, and errno is set to - // indicate the error" - assert_eq!(fd, -1); - let e = std::io::Error::last_os_error(); - assert_eq!(e.raw_os_error(), Some(libc::EINVAL)); - assert_eq!(e.kind(), std::io::ErrorKind::InvalidInput); - } -} - fn test_memcpy() { unsafe { let src = [1i8, 2, 3]; @@ -406,9 +360,6 @@ fn test_reallocarray() { fn main() { test_posix_gettimeofday(); - #[cfg(not(target_os = "freebsd"))] // FIXME we should support this on FreeBSD as well - test_posix_mkstemp(); - test_posix_realpath_alloc(); test_posix_realpath_noalloc(); test_posix_realpath_errors(); @@ -417,7 +368,6 @@ fn main() { test_isatty(); - #[cfg(not(target_os = "freebsd"))] // FIXME we should support this on FreeBSD as well test_clocks(); test_dlsym(); diff --git a/tests/pass/intrinsics-x86-aes-vaes.rs b/tests/pass/intrinsics-x86-aes-vaes.rs index 090b1db0af..55d1bacdf4 100644 --- a/tests/pass/intrinsics-x86-aes-vaes.rs +++ b/tests/pass/intrinsics-x86-aes-vaes.rs @@ -1,5 +1,5 @@ // Ignore everything except x86 and x86_64 -// Any additional target are added to CI should be ignored here +// Any new targets that are added to CI should be ignored here. // (We cannot use `cfg`-based tricks here since the `target-feature` flags below only work on x86.) //@ignore-target-aarch64 //@ignore-target-arm diff --git a/tests/pass/intrinsics-x86-avx.rs b/tests/pass/intrinsics-x86-avx.rs new file mode 100644 index 0000000000..933e3d4153 --- /dev/null +++ b/tests/pass/intrinsics-x86-avx.rs @@ -0,0 +1,162 @@ +// Ignore everything except x86 and x86_64 +// Any new targets that are added to CI should be ignored here. +// (We cannot use `cfg`-based tricks here since the `target-feature` flags below only work on x86.) +//@ignore-target-aarch64 +//@ignore-target-arm +//@ignore-target-avr +//@ignore-target-s390x +//@ignore-target-thumbv7em +//@ignore-target-wasm32 +//@compile-flags: -C target-feature=+avx + +#[cfg(target_arch = "x86")] +use std::arch::x86::*; +#[cfg(target_arch = "x86_64")] +use std::arch::x86_64::*; +use std::mem::transmute; + +fn main() { + assert!(is_x86_feature_detected!("avx")); + + unsafe { + test_avx(); + } +} + +#[target_feature(enable = "avx")] +unsafe fn test_avx() { + fn expected_cmp(imm: i32, lhs: F, rhs: F, if_t: F, if_f: F) -> F { + let res = match imm { + _CMP_EQ_OQ => lhs == rhs, + _CMP_LT_OS => lhs < rhs, + _CMP_LE_OS => lhs <= rhs, + _CMP_UNORD_Q => lhs.partial_cmp(&rhs).is_none(), + _CMP_NEQ_UQ => lhs != rhs, + _CMP_NLT_UQ => !(lhs < rhs), + _CMP_NLE_UQ => !(lhs <= rhs), + _CMP_ORD_Q => lhs.partial_cmp(&rhs).is_some(), + _CMP_EQ_UQ => lhs == rhs || lhs.partial_cmp(&rhs).is_none(), + _CMP_NGE_US => !(lhs >= rhs), + _CMP_NGT_US => !(lhs > rhs), + _CMP_FALSE_OQ => false, + _CMP_NEQ_OQ => lhs != rhs && lhs.partial_cmp(&rhs).is_some(), + _CMP_GE_OS => lhs >= rhs, + _CMP_GT_OS => lhs > rhs, + _CMP_TRUE_US => true, + _ => unreachable!(), + }; + if res { if_t } else { if_f } + } + fn expected_cmp_f32(imm: i32, lhs: f32, rhs: f32) -> f32 { + expected_cmp(imm, lhs, rhs, f32::from_bits(u32::MAX), 0.0) + } + fn expected_cmp_f64(imm: i32, lhs: f64, rhs: f64) -> f64 { + expected_cmp(imm, lhs, rhs, f64::from_bits(u64::MAX), 0.0) + } + + #[target_feature(enable = "avx")] + unsafe fn test_mm_cmp_ss() { + let values = [ + (1.0, 1.0), + (0.0, 1.0), + (1.0, 0.0), + (f32::NAN, 0.0), + (0.0, f32::NAN), + (f32::NAN, f32::NAN), + ]; + + for (lhs, rhs) in values { + let a = _mm_setr_ps(lhs, 2.0, 3.0, 4.0); + let b = _mm_setr_ps(rhs, 5.0, 6.0, 7.0); + let r: [u32; 4] = transmute(_mm_cmp_ss::(a, b)); + let e: [u32; 4] = + transmute(_mm_setr_ps(expected_cmp_f32(IMM, lhs, rhs), 2.0, 3.0, 4.0)); + assert_eq!(r, e); + } + } + + #[target_feature(enable = "avx")] + unsafe fn test_mm_cmp_ps() { + let values = [ + (1.0, 1.0), + (0.0, 1.0), + (1.0, 0.0), + (f32::NAN, 0.0), + (0.0, f32::NAN), + (f32::NAN, f32::NAN), + ]; + + for (lhs, rhs) in values { + let a = _mm_set1_ps(lhs); + let b = _mm_set1_ps(rhs); + let r: [u32; 4] = transmute(_mm_cmp_ps::(a, b)); + let e: [u32; 4] = transmute(_mm_set1_ps(expected_cmp_f32(IMM, lhs, rhs))); + assert_eq!(r, e); + } + } + + #[target_feature(enable = "avx")] + unsafe fn test_mm_cmp_sd() { + let values = [ + (1.0, 1.0), + (0.0, 1.0), + (1.0, 0.0), + (f64::NAN, 0.0), + (0.0, f64::NAN), + (f64::NAN, f64::NAN), + ]; + + for (lhs, rhs) in values { + let a = _mm_setr_pd(lhs, 2.0); + let b = _mm_setr_pd(rhs, 3.0); + let r: [u64; 2] = transmute(_mm_cmp_sd::(a, b)); + let e: [u64; 2] = transmute(_mm_setr_pd(expected_cmp_f64(IMM, lhs, rhs), 2.0)); + assert_eq!(r, e); + } + } + + #[target_feature(enable = "avx")] + unsafe fn test_mm_cmp_pd() { + let values = [ + (1.0, 1.0), + (0.0, 1.0), + (1.0, 0.0), + (f64::NAN, 0.0), + (0.0, f64::NAN), + (f64::NAN, f64::NAN), + ]; + + for (lhs, rhs) in values { + let a = _mm_set1_pd(lhs); + let b = _mm_set1_pd(rhs); + let r: [u64; 2] = transmute(_mm_cmp_pd::(a, b)); + let e: [u64; 2] = transmute(_mm_set1_pd(expected_cmp_f64(IMM, lhs, rhs))); + assert_eq!(r, e); + } + } + + #[target_feature(enable = "avx")] + unsafe fn test_cmp() { + test_mm_cmp_ss::(); + test_mm_cmp_ps::(); + test_mm_cmp_sd::(); + test_mm_cmp_pd::(); + } + + test_cmp::<_CMP_EQ_OQ>(); + test_cmp::<_CMP_LT_OS>(); + test_cmp::<_CMP_LE_OS>(); + test_cmp::<_CMP_UNORD_Q>(); + test_cmp::<_CMP_NEQ_UQ>(); + test_cmp::<_CMP_NLT_UQ>(); + test_cmp::<_CMP_NLE_UQ>(); + test_cmp::<_CMP_ORD_Q>(); + test_cmp::<_CMP_EQ_UQ>(); + test_cmp::<_CMP_NGE_US>(); + test_cmp::<_CMP_NGT_US>(); + test_cmp::<_CMP_FALSE_OQ>(); + test_cmp::<_CMP_NEQ_OQ>(); + test_cmp::<_CMP_GE_OS>(); + test_cmp::<_CMP_GT_OS>(); + test_cmp::<_CMP_TRUE_US>(); +} diff --git a/tests/pass/intrinsics-x86-avx512.rs b/tests/pass/intrinsics-x86-avx512.rs index c38158dc79..394412a235 100644 --- a/tests/pass/intrinsics-x86-avx512.rs +++ b/tests/pass/intrinsics-x86-avx512.rs @@ -1,5 +1,5 @@ // Ignore everything except x86 and x86_64 -// Any additional target are added to CI should be ignored here +// Any new targets that are added to CI should be ignored here. // (We cannot use `cfg`-based tricks here since the `target-feature` flags below only work on x86.) //@ignore-target-aarch64 //@ignore-target-arm diff --git a/tests/pass/intrinsics-x86-sse3-ssse3.rs b/tests/pass/intrinsics-x86-sse3-ssse3.rs index 0805d9bc30..7566be4431 100644 --- a/tests/pass/intrinsics-x86-sse3-ssse3.rs +++ b/tests/pass/intrinsics-x86-sse3-ssse3.rs @@ -1,5 +1,5 @@ // Ignore everything except x86 and x86_64 -// Any additional target are added to CI should be ignored here +// Any new targets that are added to CI should be ignored here. // (We cannot use `cfg`-based tricks here since the `target-feature` flags below only work on x86.) //@ignore-target-aarch64 //@ignore-target-arm diff --git a/tests/pass/intrinsics-x86-sse41.rs b/tests/pass/intrinsics-x86-sse41.rs index 8c565a2d6e..13856d29d3 100644 --- a/tests/pass/intrinsics-x86-sse41.rs +++ b/tests/pass/intrinsics-x86-sse41.rs @@ -1,5 +1,5 @@ // Ignore everything except x86 and x86_64 -// Any additional target are added to CI should be ignored here +// Any new targets that are added to CI should be ignored here. // (We cannot use `cfg`-based tricks here since the `target-feature` flags below only work on x86.) //@ignore-target-aarch64 //@ignore-target-arm diff --git a/tests/utils/mod.rs b/tests/utils/mod.rs index 6386162e09..cb9380f575 100644 --- a/tests/utils/mod.rs +++ b/tests/utils/mod.rs @@ -7,12 +7,10 @@ mod macros; mod fs; mod miri_extern; -pub use fs::*; -pub use miri_extern::*; +pub use self::fs::*; +pub use self::miri_extern::*; pub fn run_provenance_gc() { // SAFETY: No preconditions. The GC is fine to run at any time. - unsafe { - miri_run_provenance_gc() - } + unsafe { miri_run_provenance_gc() } }