diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs index d2fd51b099ad7..14ba69b898b11 100644 --- a/src/tools/miri/src/helpers.rs +++ b/src/tools/miri/src/helpers.rs @@ -6,6 +6,7 @@ use std::time::Duration; use log::trace; use rustc_apfloat::ieee::{Double, Single}; +use rustc_apfloat::Float; use rustc_hir::def::{DefKind, Namespace}; use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX}; use rustc_index::IndexVec; @@ -117,6 +118,50 @@ fn try_resolve_did(tcx: TyCtxt<'_>, path: &[&str], namespace: Option) } } +/// Convert a softfloat type to its corresponding hostfloat type. +pub trait ToHost { + type HostFloat; + fn to_host(self) -> Self::HostFloat; +} + +/// Convert a hostfloat type to its corresponding softfloat type. +pub trait ToSoft { + type SoftFloat; + fn to_soft(self) -> Self::SoftFloat; +} + +impl ToHost for rustc_apfloat::ieee::Double { + type HostFloat = f64; + + fn to_host(self) -> Self::HostFloat { + f64::from_bits(self.to_bits().try_into().unwrap()) + } +} + +impl ToSoft for f64 { + type SoftFloat = rustc_apfloat::ieee::Double; + + fn to_soft(self) -> Self::SoftFloat { + Float::from_bits(self.to_bits().into()) + } +} + +impl ToHost for rustc_apfloat::ieee::Single { + type HostFloat = f32; + + fn to_host(self) -> Self::HostFloat { + f32::from_bits(self.to_bits().try_into().unwrap()) + } +} + +impl ToSoft for f32 { + type SoftFloat = rustc_apfloat::ieee::Single; + + fn to_soft(self) -> Self::SoftFloat { + Float::from_bits(self.to_bits().into()) + } +} + impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { /// Checks if the given crate/module exists. @@ -1188,11 +1233,3 @@ pub(crate) fn simd_element_to_bool(elem: ImmTy<'_, Provenance>) -> InterpResult< _ => throw_ub_format!("each element of a SIMD mask must be all-0-bits or all-1-bits"), }) } - -// This looks like something that would be nice to have in the standard library... -pub(crate) fn round_to_next_multiple_of(x: u64, divisor: u64) -> u64 { - assert_ne!(divisor, 0); - // divisor is nonzero; multiplication cannot overflow since we just divided - #[allow(clippy::arithmetic_side_effects)] - return (x.checked_add(divisor - 1).unwrap() / divisor) * divisor; -} diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs index 4e9febf02050c..cacd02bbfaee1 100644 --- a/src/tools/miri/src/lib.rs +++ b/src/tools/miri/src/lib.rs @@ -11,6 +11,7 @@ #![feature(round_ties_even)] #![feature(let_chains)] #![feature(lint_reasons)] +#![feature(int_roundings)] // Configure clippy and other lints #![allow( clippy::collapsible_else_if, diff --git a/src/tools/miri/src/operator.rs b/src/tools/miri/src/operator.rs index e5a437f95f0ea..140764446969a 100644 --- a/src/tools/miri/src/operator.rs +++ b/src/tools/miri/src/operator.rs @@ -118,4 +118,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { nan } } + + fn adjust_nan, F2: Float>(&self, f: F2, inputs: &[F1]) -> F2 { + if f.is_nan() { self.generate_nan(inputs) } else { f } + } } diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index 8ddfc05dd300a..e7b2a6823ed6e 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -23,6 +23,7 @@ use rustc_target::{ use super::backtrace::EvalContextExt as _; use crate::*; +use helpers::{ToHost, ToSoft}; /// Type of dynamic symbols (for `dlsym` et al) #[derive(Debug, Copy, Clone)] @@ -886,23 +887,26 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { | "tgammaf" => { let [f] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + let f = this.read_scalar(f)?.to_f32()?; // FIXME: Using host floats. - let f = f32::from_bits(this.read_scalar(f)?.to_u32()?); + let f_host = f.to_host(); let res = match link_name.as_str() { - "cbrtf" => f.cbrt(), - "coshf" => f.cosh(), - "sinhf" => f.sinh(), - "tanf" => f.tan(), - "tanhf" => f.tanh(), - "acosf" => f.acos(), - "asinf" => f.asin(), - "atanf" => f.atan(), - "log1pf" => f.ln_1p(), - "expm1f" => f.exp_m1(), - "tgammaf" => f.gamma(), + "cbrtf" => f_host.cbrt(), + "coshf" => f_host.cosh(), + "sinhf" => f_host.sinh(), + "tanf" => f_host.tan(), + "tanhf" => f_host.tanh(), + "acosf" => f_host.acos(), + "asinf" => f_host.asin(), + "atanf" => f_host.atan(), + "log1pf" => f_host.ln_1p(), + "expm1f" => f_host.exp_m1(), + "tgammaf" => f_host.gamma(), _ => bug!(), }; - this.write_scalar(Scalar::from_u32(res.to_bits()), dest)?; + let res = res.to_soft(); + let res = this.adjust_nan(res, &[f]); + this.write_scalar(res, dest)?; } #[rustfmt::skip] | "_hypotf" @@ -911,19 +915,20 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { | "fdimf" => { let [f1, f2] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + let f1 = this.read_scalar(f1)?.to_f32()?; + let f2 = this.read_scalar(f2)?.to_f32()?; // underscore case for windows, here and below // (see https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/floating-point-primitives?view=vs-2019) // FIXME: Using host floats. - let f1 = f32::from_bits(this.read_scalar(f1)?.to_u32()?); - let f2 = f32::from_bits(this.read_scalar(f2)?.to_u32()?); let res = match link_name.as_str() { - "_hypotf" | "hypotf" => f1.hypot(f2), - "atan2f" => f1.atan2(f2), + "_hypotf" | "hypotf" => f1.to_host().hypot(f2.to_host()).to_soft(), + "atan2f" => f1.to_host().atan2(f2.to_host()).to_soft(), #[allow(deprecated)] - "fdimf" => f1.abs_sub(f2), + "fdimf" => f1.to_host().abs_sub(f2.to_host()).to_soft(), _ => bug!(), }; - this.write_scalar(Scalar::from_u32(res.to_bits()), dest)?; + let res = this.adjust_nan(res, &[f1, f2]); + this.write_scalar(res, dest)?; } #[rustfmt::skip] | "cbrt" @@ -939,23 +944,26 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { | "tgamma" => { let [f] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + let f = this.read_scalar(f)?.to_f64()?; // FIXME: Using host floats. - let f = f64::from_bits(this.read_scalar(f)?.to_u64()?); + let f_host = f.to_host(); let res = match link_name.as_str() { - "cbrt" => f.cbrt(), - "cosh" => f.cosh(), - "sinh" => f.sinh(), - "tan" => f.tan(), - "tanh" => f.tanh(), - "acos" => f.acos(), - "asin" => f.asin(), - "atan" => f.atan(), - "log1p" => f.ln_1p(), - "expm1" => f.exp_m1(), - "tgamma" => f.gamma(), + "cbrt" => f_host.cbrt(), + "cosh" => f_host.cosh(), + "sinh" => f_host.sinh(), + "tan" => f_host.tan(), + "tanh" => f_host.tanh(), + "acos" => f_host.acos(), + "asin" => f_host.asin(), + "atan" => f_host.atan(), + "log1p" => f_host.ln_1p(), + "expm1" => f_host.exp_m1(), + "tgamma" => f_host.gamma(), _ => bug!(), }; - this.write_scalar(Scalar::from_u64(res.to_bits()), dest)?; + let res = res.to_soft(); + let res = this.adjust_nan(res, &[f]); + this.write_scalar(res, dest)?; } #[rustfmt::skip] | "_hypot" @@ -964,17 +972,20 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { | "fdim" => { let [f1, f2] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + let f1 = this.read_scalar(f1)?.to_f64()?; + let f2 = this.read_scalar(f2)?.to_f64()?; + // underscore case for windows, here and below + // (see https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/floating-point-primitives?view=vs-2019) // FIXME: Using host floats. - let f1 = f64::from_bits(this.read_scalar(f1)?.to_u64()?); - let f2 = f64::from_bits(this.read_scalar(f2)?.to_u64()?); let res = match link_name.as_str() { - "_hypot" | "hypot" => f1.hypot(f2), - "atan2" => f1.atan2(f2), + "_hypot" | "hypot" => f1.to_host().hypot(f2.to_host()).to_soft(), + "atan2" => f1.to_host().atan2(f2.to_host()).to_soft(), #[allow(deprecated)] - "fdim" => f1.abs_sub(f2), + "fdim" => f1.to_host().abs_sub(f2.to_host()).to_soft(), _ => bug!(), }; - this.write_scalar(Scalar::from_u64(res.to_bits()), dest)?; + let res = this.adjust_nan(res, &[f1, f2]); + this.write_scalar(res, dest)?; } #[rustfmt::skip] | "_ldexp" @@ -987,27 +998,30 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let exp = this.read_scalar(exp)?.to_i32()?; let res = x.scalbn(exp); - this.write_scalar(Scalar::from_f64(res), dest)?; + let res = this.adjust_nan(res, &[x]); + this.write_scalar(res, dest)?; } "lgammaf_r" => { let [x, signp] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - // FIXME: Using host floats. - let x = f32::from_bits(this.read_scalar(x)?.to_u32()?); + let x = this.read_scalar(x)?.to_f32()?; let signp = this.deref_pointer(signp)?; - let (res, sign) = x.ln_gamma(); + // FIXME: Using host floats. + let (res, sign) = x.to_host().ln_gamma(); this.write_int(sign, &signp)?; - this.write_scalar(Scalar::from_u32(res.to_bits()), dest)?; + let res = this.adjust_nan(res.to_soft(), &[x]); + this.write_scalar(res, dest)?; } "lgamma_r" => { let [x, signp] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - // FIXME: Using host floats. - let x = f64::from_bits(this.read_scalar(x)?.to_u64()?); + let x = this.read_scalar(x)?.to_f64()?; let signp = this.deref_pointer(signp)?; - let (res, sign) = x.ln_gamma(); + // FIXME: Using host floats. + let (res, sign) = x.to_host().ln_gamma(); this.write_int(sign, &signp)?; - this.write_scalar(Scalar::from_u64(res.to_bits()), dest)?; + let res = this.adjust_nan(res.to_soft(), &[x]); + this.write_scalar(res, dest)?; } // LLVM intrinsics diff --git a/src/tools/miri/src/shims/intrinsics/mod.rs b/src/tools/miri/src/shims/intrinsics/mod.rs index 66918db995d03..cc81ef6e6c916 100644 --- a/src/tools/miri/src/shims/intrinsics/mod.rs +++ b/src/tools/miri/src/shims/intrinsics/mod.rs @@ -15,7 +15,7 @@ use rustc_target::abi::Size; use crate::*; use atomic::EvalContextExt as _; -use helpers::check_arg_count; +use helpers::{check_arg_count, ToHost, ToSoft}; use simd::EvalContextExt as _; impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} @@ -146,12 +146,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let [f] = check_arg_count(args)?; let f = this.read_scalar(f)?.to_f32()?; // Can be implemented in soft-floats. + // This is a "bitwise" operation, so there's no NaN non-determinism. this.write_scalar(Scalar::from_f32(f.abs()), dest)?; } "fabsf64" => { let [f] = check_arg_count(args)?; let f = this.read_scalar(f)?.to_f64()?; // Can be implemented in soft-floats. + // This is a "bitwise" operation, so there's no NaN non-determinism. this.write_scalar(Scalar::from_f64(f.abs()), dest)?; } #[rustfmt::skip] @@ -170,25 +172,28 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { | "rintf32" => { let [f] = check_arg_count(args)?; + let f = this.read_scalar(f)?.to_f32()?; // FIXME: Using host floats. - let f = f32::from_bits(this.read_scalar(f)?.to_u32()?); - let f = match intrinsic_name { - "sinf32" => f.sin(), - "cosf32" => f.cos(), - "sqrtf32" => f.sqrt(), - "expf32" => f.exp(), - "exp2f32" => f.exp2(), - "logf32" => f.ln(), - "log10f32" => f.log10(), - "log2f32" => f.log2(), - "floorf32" => f.floor(), - "ceilf32" => f.ceil(), - "truncf32" => f.trunc(), - "roundf32" => f.round(), - "rintf32" => f.round_ties_even(), + let f_host = f.to_host(); + let res = match intrinsic_name { + "sinf32" => f_host.sin(), + "cosf32" => f_host.cos(), + "sqrtf32" => f_host.sqrt(), + "expf32" => f_host.exp(), + "exp2f32" => f_host.exp2(), + "logf32" => f_host.ln(), + "log10f32" => f_host.log10(), + "log2f32" => f_host.log2(), + "floorf32" => f_host.floor(), + "ceilf32" => f_host.ceil(), + "truncf32" => f_host.trunc(), + "roundf32" => f_host.round(), + "rintf32" => f_host.round_ties_even(), _ => bug!(), }; - this.write_scalar(Scalar::from_u32(f.to_bits()), dest)?; + let res = res.to_soft(); + let res = this.adjust_nan(res, &[f]); + this.write_scalar(res, dest)?; } #[rustfmt::skip] @@ -207,25 +212,28 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { | "rintf64" => { let [f] = check_arg_count(args)?; + let f = this.read_scalar(f)?.to_f64()?; // FIXME: Using host floats. - let f = f64::from_bits(this.read_scalar(f)?.to_u64()?); - let f = match intrinsic_name { - "sinf64" => f.sin(), - "cosf64" => f.cos(), - "sqrtf64" => f.sqrt(), - "expf64" => f.exp(), - "exp2f64" => f.exp2(), - "logf64" => f.ln(), - "log10f64" => f.log10(), - "log2f64" => f.log2(), - "floorf64" => f.floor(), - "ceilf64" => f.ceil(), - "truncf64" => f.trunc(), - "roundf64" => f.round(), - "rintf64" => f.round_ties_even(), + let f_host = f.to_host(); + let res = match intrinsic_name { + "sinf64" => f_host.sin(), + "cosf64" => f_host.cos(), + "sqrtf64" => f_host.sqrt(), + "expf64" => f_host.exp(), + "exp2f64" => f_host.exp2(), + "logf64" => f_host.ln(), + "log10f64" => f_host.log10(), + "log2f64" => f_host.log2(), + "floorf64" => f_host.floor(), + "ceilf64" => f_host.ceil(), + "truncf64" => f_host.trunc(), + "roundf64" => f_host.round(), + "rintf64" => f_host.round_ties_even(), _ => bug!(), }; - this.write_scalar(Scalar::from_u64(f.to_bits()), dest)?; + let res = res.to_soft(); + let res = this.adjust_nan(res, &[f]); + this.write_scalar(res, dest)?; } #[rustfmt::skip] @@ -268,7 +276,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ), _ => {} } - this.binop_ignore_overflow(op, &a, &b, dest)?; + let res = this.wrapping_binary_op(op, &a, &b)?; + if !float_finite(&res)? { + throw_ub_format!("`{intrinsic_name}` intrinsic produced non-finite value as result"); + } + // This cannot be a NaN so we also don't have to apply any non-determinism. + // (Also, `wrapping_binary_op` already called `generate_nan` if needed.) + this.write_immediate(*res, dest)?; } #[rustfmt::skip] @@ -280,9 +294,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let a = this.read_scalar(a)?.to_f32()?; let b = this.read_scalar(b)?.to_f32()?; let res = match intrinsic_name { - "minnumf32" => a.min(b), - "maxnumf32" => a.max(b), - "copysignf32" => a.copy_sign(b), + "minnumf32" => this.adjust_nan(a.min(b), &[a, b]), + "maxnumf32" => this.adjust_nan(a.max(b), &[a, b]), + "copysignf32" => a.copy_sign(b), // bitwise, no NaN adjustments _ => bug!(), }; this.write_scalar(Scalar::from_f32(res), dest)?; @@ -297,68 +311,74 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let a = this.read_scalar(a)?.to_f64()?; let b = this.read_scalar(b)?.to_f64()?; let res = match intrinsic_name { - "minnumf64" => a.min(b), - "maxnumf64" => a.max(b), - "copysignf64" => a.copy_sign(b), + "minnumf64" => this.adjust_nan(a.min(b), &[a, b]), + "maxnumf64" => this.adjust_nan(a.max(b), &[a, b]), + "copysignf64" => a.copy_sign(b), // bitwise, no NaN adjustments _ => bug!(), }; this.write_scalar(Scalar::from_f64(res), dest)?; } - "powf32" => { - let [f, f2] = check_arg_count(args)?; - // FIXME: Using host floats. - let f = f32::from_bits(this.read_scalar(f)?.to_u32()?); - let f2 = f32::from_bits(this.read_scalar(f2)?.to_u32()?); - let res = f.powf(f2); - this.write_scalar(Scalar::from_u32(res.to_bits()), dest)?; - } - - "powf64" => { - let [f, f2] = check_arg_count(args)?; - // FIXME: Using host floats. - let f = f64::from_bits(this.read_scalar(f)?.to_u64()?); - let f2 = f64::from_bits(this.read_scalar(f2)?.to_u64()?); - let res = f.powf(f2); - this.write_scalar(Scalar::from_u64(res.to_bits()), dest)?; - } - "fmaf32" => { let [a, b, c] = check_arg_count(args)?; + let a = this.read_scalar(a)?.to_f32()?; + let b = this.read_scalar(b)?.to_f32()?; + let c = this.read_scalar(c)?.to_f32()?; // FIXME: Using host floats, to work around https://github.com/rust-lang/rustc_apfloat/issues/11 - let a = f32::from_bits(this.read_scalar(a)?.to_u32()?); - let b = f32::from_bits(this.read_scalar(b)?.to_u32()?); - let c = f32::from_bits(this.read_scalar(c)?.to_u32()?); - let res = a.mul_add(b, c); - this.write_scalar(Scalar::from_u32(res.to_bits()), dest)?; + let res = a.to_host().mul_add(b.to_host(), c.to_host()).to_soft(); + let res = this.adjust_nan(res, &[a, b, c]); + this.write_scalar(res, dest)?; } "fmaf64" => { let [a, b, c] = check_arg_count(args)?; + let a = this.read_scalar(a)?.to_f64()?; + let b = this.read_scalar(b)?.to_f64()?; + let c = this.read_scalar(c)?.to_f64()?; // FIXME: Using host floats, to work around https://github.com/rust-lang/rustc_apfloat/issues/11 - let a = f64::from_bits(this.read_scalar(a)?.to_u64()?); - let b = f64::from_bits(this.read_scalar(b)?.to_u64()?); - let c = f64::from_bits(this.read_scalar(c)?.to_u64()?); - let res = a.mul_add(b, c); - this.write_scalar(Scalar::from_u64(res.to_bits()), dest)?; + let res = a.to_host().mul_add(b.to_host(), c.to_host()).to_soft(); + let res = this.adjust_nan(res, &[a, b, c]); + this.write_scalar(res, dest)?; + } + + "powf32" => { + let [f1, f2] = check_arg_count(args)?; + let f1 = this.read_scalar(f1)?.to_f32()?; + let f2 = this.read_scalar(f2)?.to_f32()?; + // FIXME: Using host floats. + let res = f1.to_host().powf(f2.to_host()).to_soft(); + let res = this.adjust_nan(res, &[f1, f2]); + this.write_scalar(res, dest)?; + } + + "powf64" => { + let [f1, f2] = check_arg_count(args)?; + let f1 = this.read_scalar(f1)?.to_f64()?; + let f2 = this.read_scalar(f2)?.to_f64()?; + // FIXME: Using host floats. + let res = f1.to_host().powf(f2.to_host()).to_soft(); + let res = this.adjust_nan(res, &[f1, f2]); + this.write_scalar(res, dest)?; } "powif32" => { let [f, i] = check_arg_count(args)?; - // FIXME: Using host floats. - let f = f32::from_bits(this.read_scalar(f)?.to_u32()?); + let f = this.read_scalar(f)?.to_f32()?; let i = this.read_scalar(i)?.to_i32()?; - let res = f.powi(i); - this.write_scalar(Scalar::from_u32(res.to_bits()), dest)?; + // FIXME: Using host floats. + let res = f.to_host().powi(i).to_soft(); + let res = this.adjust_nan(res, &[f]); + this.write_scalar(res, dest)?; } "powif64" => { let [f, i] = check_arg_count(args)?; - // FIXME: Using host floats. - let f = f64::from_bits(this.read_scalar(f)?.to_u64()?); + let f = this.read_scalar(f)?.to_f64()?; let i = this.read_scalar(i)?.to_i32()?; - let res = f.powi(i); - this.write_scalar(Scalar::from_u64(res.to_bits()), dest)?; + // FIXME: Using host floats. + let res = f.to_host().powi(i).to_soft(); + let res = this.adjust_nan(res, &[f]); + this.write_scalar(res, dest)?; } "float_to_int_unchecked" => { diff --git a/src/tools/miri/src/shims/intrinsics/simd.rs b/src/tools/miri/src/shims/intrinsics/simd.rs index 2c8493d8aad1a..ea2d104694af7 100644 --- a/src/tools/miri/src/shims/intrinsics/simd.rs +++ b/src/tools/miri/src/shims/intrinsics/simd.rs @@ -4,11 +4,15 @@ use rustc_middle::{mir, ty, ty::FloatTy}; use rustc_span::{sym, Symbol}; use rustc_target::abi::{Endian, HasDataLayout}; -use crate::helpers::{ - bool_to_simd_element, check_arg_count, round_to_next_multiple_of, simd_element_to_bool, -}; +use crate::helpers::{bool_to_simd_element, check_arg_count, simd_element_to_bool, ToHost, ToSoft}; use crate::*; +#[derive(Copy, Clone)] +pub(crate) enum MinMax { + Min, + Max, +} + impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { /// Calls the simd intrinsic `intrinsic`; the `simd_` prefix has already been removed. @@ -67,13 +71,17 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let op = this.read_immediate(&this.project_index(&op, i)?)?; let dest = this.project_index(&dest, i)?; let val = match which { - Op::MirOp(mir_op) => this.wrapping_unary_op(mir_op, &op)?.to_scalar(), + Op::MirOp(mir_op) => { + // This already does NaN adjustments + this.wrapping_unary_op(mir_op, &op)?.to_scalar() + } Op::Abs => { // Works for f32 and f64. let ty::Float(float_ty) = op.layout.ty.kind() else { span_bug!(this.cur_span(), "{} operand is not a float", intrinsic_name) }; let op = op.to_scalar(); + // "Bitwise" operation, no NaN adjustments match float_ty { FloatTy::F32 => Scalar::from_f32(op.to_f32()?.abs()), FloatTy::F64 => Scalar::from_f64(op.to_f64()?.abs()), @@ -86,14 +94,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // FIXME using host floats match float_ty { FloatTy::F32 => { - let f = f32::from_bits(op.to_scalar().to_u32()?); - let res = f.sqrt(); - Scalar::from_u32(res.to_bits()) + let f = op.to_scalar().to_f32()?; + let res = f.to_host().sqrt().to_soft(); + let res = this.adjust_nan(res, &[f]); + Scalar::from(res) } FloatTy::F64 => { - let f = f64::from_bits(op.to_scalar().to_u64()?); - let res = f.sqrt(); - Scalar::from_u64(res.to_bits()) + let f = op.to_scalar().to_f64()?; + let res = f.to_host().sqrt().to_soft(); + let res = this.adjust_nan(res, &[f]); + Scalar::from(res) } } } @@ -105,11 +115,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { FloatTy::F32 => { let f = op.to_scalar().to_f32()?; let res = f.round_to_integral(rounding).value; + let res = this.adjust_nan(res, &[f]); Scalar::from_f32(res) } FloatTy::F64 => { let f = op.to_scalar().to_f64()?; let res = f.round_to_integral(rounding).value; + let res = this.adjust_nan(res, &[f]); Scalar::from_f64(res) } } @@ -157,8 +169,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { enum Op { MirOp(BinOp), SaturatingOp(BinOp), - FMax, - FMin, + FMinMax(MinMax), WrappingOffset, } let which = match intrinsic_name { @@ -178,8 +189,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { "le" => Op::MirOp(BinOp::Le), "gt" => Op::MirOp(BinOp::Gt), "ge" => Op::MirOp(BinOp::Ge), - "fmax" => Op::FMax, - "fmin" => Op::FMin, + "fmax" => Op::FMinMax(MinMax::Max), + "fmin" => Op::FMinMax(MinMax::Min), "saturating_add" => Op::SaturatingOp(BinOp::Add), "saturating_sub" => Op::SaturatingOp(BinOp::Sub), "arith_offset" => Op::WrappingOffset, @@ -192,6 +203,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let dest = this.project_index(&dest, i)?; let val = match which { Op::MirOp(mir_op) => { + // This does NaN adjustments. let (val, overflowed) = this.overflowing_binary_op(mir_op, &left, &right)?; if matches!(mir_op, BinOp::Shl | BinOp::Shr) { // Shifts have extra UB as SIMD operations that the MIR binop does not have. @@ -225,11 +237,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let offset_ptr = ptr.wrapping_signed_offset(offset_bytes, this); Scalar::from_maybe_pointer(offset_ptr, this) } - Op::FMax => { - fmax_op(&left, &right)? - } - Op::FMin => { - fmin_op(&left, &right)? + Op::FMinMax(op) => { + this.fminmax_op(op, &left, &right)? } }; this.write_scalar(val, &dest)?; @@ -259,18 +268,20 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { }; let val = match float_ty { FloatTy::F32 => { - let a = f32::from_bits(a.to_u32()?); - let b = f32::from_bits(b.to_u32()?); - let c = f32::from_bits(c.to_u32()?); - let res = a.mul_add(b, c); - Scalar::from_u32(res.to_bits()) + let a = a.to_f32()?; + let b = b.to_f32()?; + let c = c.to_f32()?; + let res = a.to_host().mul_add(b.to_host(), c.to_host()).to_soft(); + let res = this.adjust_nan(res, &[a, b, c]); + Scalar::from(res) } FloatTy::F64 => { - let a = f64::from_bits(a.to_u64()?); - let b = f64::from_bits(b.to_u64()?); - let c = f64::from_bits(c.to_u64()?); - let res = a.mul_add(b, c); - Scalar::from_u64(res.to_bits()) + let a = a.to_f64()?; + let b = b.to_f64()?; + let c = c.to_f64()?; + let res = a.to_host().mul_add(b.to_host(), c.to_host()).to_soft(); + let res = this.adjust_nan(res, &[a, b, c]); + Scalar::from(res) } }; this.write_scalar(val, &dest)?; @@ -295,8 +306,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { enum Op { MirOp(BinOp), MirOpBool(BinOp), - Max, - Min, + MinMax(MinMax), } let which = match intrinsic_name { "reduce_and" => Op::MirOp(BinOp::BitAnd), @@ -304,8 +314,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { "reduce_xor" => Op::MirOp(BinOp::BitXor), "reduce_any" => Op::MirOpBool(BinOp::BitOr), "reduce_all" => Op::MirOpBool(BinOp::BitAnd), - "reduce_max" => Op::Max, - "reduce_min" => Op::Min, + "reduce_max" => Op::MinMax(MinMax::Max), + "reduce_min" => Op::MinMax(MinMax::Min), _ => unreachable!(), }; @@ -325,24 +335,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let op = imm_from_bool(simd_element_to_bool(op)?); this.wrapping_binary_op(mir_op, &res, &op)? } - Op::Max => { - if matches!(res.layout.ty.kind(), ty::Float(_)) { - ImmTy::from_scalar(fmax_op(&res, &op)?, res.layout) - } else { - // Just boring integers, so NaNs to worry about - if this.wrapping_binary_op(BinOp::Ge, &res, &op)?.to_scalar().to_bool()? { - res - } else { - op - } - } - } - Op::Min => { + Op::MinMax(mmop) => { if matches!(res.layout.ty.kind(), ty::Float(_)) { - ImmTy::from_scalar(fmin_op(&res, &op)?, res.layout) + ImmTy::from_scalar(this.fminmax_op(mmop, &res, &op)?, res.layout) } else { // Just boring integers, so NaNs to worry about - if this.wrapping_binary_op(BinOp::Le, &res, &op)?.to_scalar().to_bool()? { + let mirop = match mmop { + MinMax::Min => BinOp::Le, + MinMax::Max => BinOp::Ge, + }; + if this.wrapping_binary_op(mirop, &res, &op)?.to_scalar().to_bool()? { res } else { op @@ -402,7 +404,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let (yes, yes_len) = this.operand_to_simd(yes)?; let (no, no_len) = this.operand_to_simd(no)?; let (dest, dest_len) = this.place_to_simd(dest)?; - let bitmask_len = round_to_next_multiple_of(dest_len, 8); + let bitmask_len = dest_len.next_multiple_of(8); // The mask must be an integer or an array. assert!( @@ -448,7 +450,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { "bitmask" => { let [op] = check_arg_count(args)?; let (op, op_len) = this.operand_to_simd(op)?; - let bitmask_len = round_to_next_multiple_of(op_len, 8); + let bitmask_len = op_len.next_multiple_of(8); // Returns either an unsigned integer or array of `u8`. assert!( @@ -709,6 +711,43 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } Ok(()) } + + fn fminmax_op( + &self, + op: MinMax, + left: &ImmTy<'tcx, Provenance>, + right: &ImmTy<'tcx, Provenance>, + ) -> InterpResult<'tcx, Scalar> { + let this = self.eval_context_ref(); + assert_eq!(left.layout.ty, right.layout.ty); + let ty::Float(float_ty) = left.layout.ty.kind() else { + bug!("fmax operand is not a float") + }; + let left = left.to_scalar(); + let right = right.to_scalar(); + Ok(match float_ty { + FloatTy::F32 => { + let left = left.to_f32()?; + let right = right.to_f32()?; + let res = match op { + MinMax::Min => left.min(right), + MinMax::Max => left.max(right), + }; + let res = this.adjust_nan(res, &[left, right]); + Scalar::from_f32(res) + } + FloatTy::F64 => { + let left = left.to_f64()?; + let right = right.to_f64()?; + let res = match op { + MinMax::Min => left.min(right), + MinMax::Max => left.max(right), + }; + let res = this.adjust_nan(res, &[left, right]); + Scalar::from_f64(res) + } + }) + } } fn simd_bitmask_index(idx: u32, vec_len: u32, endianness: Endian) -> u32 { @@ -719,31 +758,3 @@ fn simd_bitmask_index(idx: u32, vec_len: u32, endianness: Endian) -> u32 { Endian::Big => vec_len - 1 - idx, // reverse order of bits } } - -fn fmax_op<'tcx>( - left: &ImmTy<'tcx, Provenance>, - right: &ImmTy<'tcx, Provenance>, -) -> InterpResult<'tcx, Scalar> { - assert_eq!(left.layout.ty, right.layout.ty); - let ty::Float(float_ty) = left.layout.ty.kind() else { bug!("fmax operand is not a float") }; - let left = left.to_scalar(); - let right = right.to_scalar(); - Ok(match float_ty { - FloatTy::F32 => Scalar::from_f32(left.to_f32()?.max(right.to_f32()?)), - FloatTy::F64 => Scalar::from_f64(left.to_f64()?.max(right.to_f64()?)), - }) -} - -fn fmin_op<'tcx>( - left: &ImmTy<'tcx, Provenance>, - right: &ImmTy<'tcx, Provenance>, -) -> InterpResult<'tcx, Scalar> { - assert_eq!(left.layout.ty, right.layout.ty); - let ty::Float(float_ty) = left.layout.ty.kind() else { bug!("fmin operand is not a float") }; - let left = left.to_scalar(); - let right = right.to_scalar(); - Ok(match float_ty { - FloatTy::F32 => Scalar::from_f32(left.to_f32()?.min(right.to_f32()?)), - FloatTy::F64 => Scalar::from_f64(left.to_f64()?.min(right.to_f64()?)), - }) -} diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs index 1402f07dd38dc..99d8a3a2c91f4 100644 --- a/src/tools/miri/src/shims/unix/fs.rs +++ b/src/tools/miri/src/shims/unix/fs.rs @@ -559,8 +559,21 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ); }; - if mode != 0o666 { - throw_unsup_format!("non-default mode 0o{:o} is not supported", mode); + #[cfg(unix)] + { + // Support all modes on UNIX host + use std::os::unix::fs::OpenOptionsExt; + options.mode(mode); + } + #[cfg(not(unix))] + { + // Only support default mode for non-UNIX (i.e. Windows) host + if mode != 0o666 { + throw_unsup_format!( + "non-default mode 0o{:o} is not supported on non-Unix hosts", + mode + ); + } } mirror |= o_creat; diff --git a/src/tools/miri/src/shims/unix/mem.rs b/src/tools/miri/src/shims/unix/mem.rs index 5aa514715bc3a..d7dc17fa89f13 100644 --- a/src/tools/miri/src/shims/unix/mem.rs +++ b/src/tools/miri/src/shims/unix/mem.rs @@ -14,7 +14,7 @@ //! munmap shim which would partily unmap a region of address space previously mapped by mmap will //! report UB. -use crate::{helpers::round_to_next_multiple_of, *}; +use crate::*; use rustc_target::abi::Size; impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} @@ -96,7 +96,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } let align = this.machine.page_align(); - let map_length = round_to_next_multiple_of(length, this.machine.page_size); + let Some(map_length) = length.checked_next_multiple_of(this.machine.page_size) else { + this.set_last_error(Scalar::from_i32(this.eval_libc_i32("EINVAL")))?; + return Ok(this.eval_libc("MAP_FAILED")); + }; + if map_length > this.target_usize_max() { + this.set_last_error(Scalar::from_i32(this.eval_libc_i32("EINVAL")))?; + return Ok(this.eval_libc("MAP_FAILED")); + } let ptr = this.allocate_ptr(Size::from_bytes(map_length), align, MiriMemoryKind::Mmap.into())?; @@ -129,7 +136,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { return Ok(Scalar::from_i32(-1)); } - let length = Size::from_bytes(round_to_next_multiple_of(length, this.machine.page_size)); + let Some(length) = length.checked_next_multiple_of(this.machine.page_size) else { + this.set_last_error(Scalar::from_i32(this.eval_libc_i32("EINVAL")))?; + return Ok(Scalar::from_i32(-1)); + }; + if length > this.target_usize_max() { + this.set_last_error(Scalar::from_i32(this.eval_libc_i32("EINVAL")))?; + return Ok(this.eval_libc("MAP_FAILED")); + } + + let length = Size::from_bytes(length); this.deallocate_ptr( addr, Some((length, this.machine.page_align())), diff --git a/src/tools/miri/test_dependencies/Cargo.lock b/src/tools/miri/test_dependencies/Cargo.lock index 8d18d8535d8f5..aa3b25e338a7f 100644 --- a/src/tools/miri/test_dependencies/Cargo.lock +++ b/src/tools/miri/test_dependencies/Cargo.lock @@ -44,6 +44,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" + [[package]] name = "bumpalo" version = "3.14.0" @@ -71,6 +77,22 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "fastrand" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" + [[package]] name = "getrandom" version = "0.1.16" @@ -122,6 +144,12 @@ version = "0.2.148" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" +[[package]] +name = "linux-raw-sys" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" + [[package]] name = "lock_api" version = "0.4.10" @@ -161,7 +189,7 @@ checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" dependencies = [ "libc", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -174,7 +202,9 @@ dependencies = [ "num_cpus", "page_size", "rand", + "tempfile", "tokio", + "windows-sys 0.52.0", ] [[package]] @@ -230,9 +260,9 @@ checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.3.5", "smallvec", - "windows-targets", + "windows-targets 0.48.5", ] [[package]] @@ -301,7 +331,16 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ - "bitflags", + "bitflags 1.3.2", +] + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", ] [[package]] @@ -310,6 +349,19 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +[[package]] +name = "rustix" +version = "0.38.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3" +dependencies = [ + "bitflags 2.4.1", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.48.0", +] + [[package]] name = "scopeguard" version = "1.2.0" @@ -338,7 +390,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4031e820eb552adee9295814c0ced9e5cf38ddf1e8b7d566d6de8e2538ea989e" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -352,6 +404,19 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tempfile" +version = "3.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" +dependencies = [ + "cfg-if", + "fastrand", + "redox_syscall 0.4.1", + "rustix", + "windows-sys 0.48.0", +] + [[package]] name = "tokio" version = "1.32.0" @@ -368,7 +433,7 @@ dependencies = [ "signal-hook-registry", "socket2", "tokio-macros", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -482,7 +547,16 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets", + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.0", ] [[package]] @@ -491,13 +565,28 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", ] [[package]] @@ -506,38 +595,80 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + [[package]] name = "windows_aarch64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + [[package]] name = "windows_i686_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + [[package]] name = "windows_i686_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + [[package]] name = "windows_x86_64_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + [[package]] name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" diff --git a/src/tools/miri/test_dependencies/Cargo.toml b/src/tools/miri/test_dependencies/Cargo.toml index d54560608dc1e..9f28e4d169f4a 100644 --- a/src/tools/miri/test_dependencies/Cargo.toml +++ b/src/tools/miri/test_dependencies/Cargo.toml @@ -11,6 +11,7 @@ edition = "2021" # all dependencies (and their transitive ones) listed here can be used in `tests/`. libc = "0.2" num_cpus = "1.10.1" +tempfile = "3" getrandom_01 = { package = "getrandom", version = "0.1" } getrandom_02 = { package = "getrandom", version = "0.2", features = ["js"] } @@ -20,4 +21,7 @@ rand = { version = "0.8", features = ["small_rng"] } page_size = "0.6" tokio = { version = "1.24", features = ["full"] } +[target.'cfg(windows)'.dependencies] +windows-sys = { version = "0.52", features = [ "Win32_Foundation", "Win32_System_Threading" ] } + [workspace] diff --git a/src/tools/miri/tests/compiletest.rs b/src/tools/miri/tests/compiletest.rs index 3394c4a49f836..074808b11227f 100644 --- a/src/tools/miri/tests/compiletest.rs +++ b/src/tools/miri/tests/compiletest.rs @@ -111,6 +111,8 @@ fn test_config(target: &str, path: &str, mode: Mode, with_dependencies: bool) -> "run".into(), // There is no `cargo miri build` so we just use `cargo miri run`. ]); config.dependency_builder.args = builder_args.into_iter().map(Into::into).collect(); + // Reset `RUSTFLAGS` to work around . + config.dependency_builder.envs.push(("RUSTFLAGS".into(), None)); } config } diff --git a/src/tools/miri/tests/fail/concurrency/windows_join_detached.rs b/src/tools/miri/tests/fail-dep/concurrency/windows_join_detached.rs similarity index 62% rename from src/tools/miri/tests/fail/concurrency/windows_join_detached.rs rename to src/tools/miri/tests/fail-dep/concurrency/windows_join_detached.rs index b68a07797f8bd..0e9eb24459c3e 100644 --- a/src/tools/miri/tests/fail/concurrency/windows_join_detached.rs +++ b/src/tools/miri/tests/fail-dep/concurrency/windows_join_detached.rs @@ -3,18 +3,16 @@ // Joining a detached thread is undefined behavior. -use std::os::windows::io::{AsRawHandle, RawHandle}; +use std::os::windows::io::AsRawHandle; use std::thread; -extern "system" { - fn CloseHandle(handle: RawHandle) -> u32; -} +use windows_sys::Win32::Foundation::{CloseHandle, HANDLE}; fn main() { let thread = thread::spawn(|| ()); unsafe { - assert_ne!(CloseHandle(thread.as_raw_handle()), 0); + assert_ne!(CloseHandle(thread.as_raw_handle() as HANDLE), 0); } thread.join().unwrap(); diff --git a/src/tools/miri/tests/fail/concurrency/windows_join_detached.stderr b/src/tools/miri/tests/fail-dep/concurrency/windows_join_detached.stderr similarity index 100% rename from src/tools/miri/tests/fail/concurrency/windows_join_detached.stderr rename to src/tools/miri/tests/fail-dep/concurrency/windows_join_detached.stderr diff --git a/src/tools/miri/tests/fail/concurrency/windows_join_main.rs b/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.rs similarity index 70% rename from src/tools/miri/tests/fail/concurrency/windows_join_main.rs rename to src/tools/miri/tests/fail-dep/concurrency/windows_join_main.rs index cde6d19ef25bb..910e06222ee76 100644 --- a/src/tools/miri/tests/fail/concurrency/windows_join_main.rs +++ b/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.rs @@ -6,21 +6,18 @@ use std::thread; -extern "system" { - fn WaitForSingleObject(handle: isize, timeout: u32) -> u32; -} - -const INFINITE: u32 = u32::MAX; +use windows_sys::Win32::Foundation::{HANDLE, WAIT_OBJECT_0}; +use windows_sys::Win32::System::Threading::{WaitForSingleObject, INFINITE}; // XXX HACK: This is how miri represents the handle for thread 0. // This value can be "legitimately" obtained by using `GetCurrentThread` with `DuplicateHandle` // but miri does not implement `DuplicateHandle` yet. -const MAIN_THREAD: isize = (2i32 << 30) as isize; +const MAIN_THREAD: HANDLE = (2i32 << 30) as HANDLE; fn main() { thread::spawn(|| { unsafe { - assert_eq!(WaitForSingleObject(MAIN_THREAD, INFINITE), 0); //~ ERROR: deadlock: the evaluated program deadlocked + assert_eq!(WaitForSingleObject(MAIN_THREAD, INFINITE), WAIT_OBJECT_0); //~ ERROR: deadlock: the evaluated program deadlocked } }) .join() diff --git a/src/tools/miri/tests/fail/concurrency/windows_join_main.stderr b/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.stderr similarity index 87% rename from src/tools/miri/tests/fail/concurrency/windows_join_main.stderr rename to src/tools/miri/tests/fail-dep/concurrency/windows_join_main.stderr index 94fe95a0bdfab..cb51c7e0bd985 100644 --- a/src/tools/miri/tests/fail/concurrency/windows_join_main.stderr +++ b/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.stderr @@ -1,8 +1,8 @@ error: deadlock: the evaluated program deadlocked --> $DIR/windows_join_main.rs:LL:CC | -LL | assert_eq!(WaitForSingleObject(MAIN_THREAD, INFINITE), 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program deadlocked +LL | assert_eq!(WaitForSingleObject(MAIN_THREAD, INFINITE), WAIT_OBJECT_0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program deadlocked | = note: inside closure at RUSTLIB/core/src/macros/mod.rs:LL:CC = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/src/tools/miri/tests/fail/concurrency/windows_join_self.rs b/src/tools/miri/tests/fail-dep/concurrency/windows_join_self.rs similarity index 57% rename from src/tools/miri/tests/fail/concurrency/windows_join_self.rs rename to src/tools/miri/tests/fail-dep/concurrency/windows_join_self.rs index d9bbf66a7dca5..a7c8faf5a98ba 100644 --- a/src/tools/miri/tests/fail/concurrency/windows_join_self.rs +++ b/src/tools/miri/tests/fail-dep/concurrency/windows_join_self.rs @@ -6,18 +6,14 @@ use std::thread; -extern "system" { - fn GetCurrentThread() -> usize; - fn WaitForSingleObject(handle: usize, timeout: u32) -> u32; -} - -const INFINITE: u32 = u32::MAX; +use windows_sys::Win32::Foundation::WAIT_OBJECT_0; +use windows_sys::Win32::System::Threading::{GetCurrentThread, WaitForSingleObject, INFINITE}; fn main() { thread::spawn(|| { unsafe { let native = GetCurrentThread(); - assert_eq!(WaitForSingleObject(native, INFINITE), 0); //~ ERROR: deadlock: the evaluated program deadlocked + assert_eq!(WaitForSingleObject(native, INFINITE), WAIT_OBJECT_0); //~ ERROR: deadlock: the evaluated program deadlocked } }) .join() diff --git a/src/tools/miri/tests/fail/concurrency/windows_join_self.stderr b/src/tools/miri/tests/fail-dep/concurrency/windows_join_self.stderr similarity index 96% rename from src/tools/miri/tests/fail/concurrency/windows_join_self.stderr rename to src/tools/miri/tests/fail-dep/concurrency/windows_join_self.stderr index 0d2a22677c7ae..8b76e12421552 100644 --- a/src/tools/miri/tests/fail/concurrency/windows_join_self.stderr +++ b/src/tools/miri/tests/fail-dep/concurrency/windows_join_self.stderr @@ -1,7 +1,7 @@ error: deadlock: the evaluated program deadlocked --> $DIR/windows_join_self.rs:LL:CC | -LL | assert_eq!(WaitForSingleObject(native, INFINITE), 0); +LL | assert_eq!(WaitForSingleObject(native, INFINITE), WAIT_OBJECT_0); | ^ the evaluated program deadlocked | = note: inside closure at $DIR/windows_join_self.rs:LL:CC diff --git a/src/tools/miri/tests/fail/fast_math_both.rs b/src/tools/miri/tests/fail/intrinsics/fast_math_both.rs similarity index 100% rename from src/tools/miri/tests/fail/fast_math_both.rs rename to src/tools/miri/tests/fail/intrinsics/fast_math_both.rs diff --git a/src/tools/miri/tests/fail/fast_math_both.stderr b/src/tools/miri/tests/fail/intrinsics/fast_math_both.stderr similarity index 100% rename from src/tools/miri/tests/fail/fast_math_both.stderr rename to src/tools/miri/tests/fail/intrinsics/fast_math_both.stderr diff --git a/src/tools/miri/tests/fail/fast_math_first.rs b/src/tools/miri/tests/fail/intrinsics/fast_math_first.rs similarity index 100% rename from src/tools/miri/tests/fail/fast_math_first.rs rename to src/tools/miri/tests/fail/intrinsics/fast_math_first.rs diff --git a/src/tools/miri/tests/fail/fast_math_first.stderr b/src/tools/miri/tests/fail/intrinsics/fast_math_first.stderr similarity index 100% rename from src/tools/miri/tests/fail/fast_math_first.stderr rename to src/tools/miri/tests/fail/intrinsics/fast_math_first.stderr diff --git a/src/tools/miri/tests/fail/intrinsics/fast_math_result.rs b/src/tools/miri/tests/fail/intrinsics/fast_math_result.rs new file mode 100644 index 0000000000000..4a143da057556 --- /dev/null +++ b/src/tools/miri/tests/fail/intrinsics/fast_math_result.rs @@ -0,0 +1,7 @@ +#![feature(core_intrinsics)] + +fn main() { + unsafe { + let _x: f32 = core::intrinsics::fdiv_fast(1.0, 0.0); //~ ERROR: `fdiv_fast` intrinsic produced non-finite value as result + } +} diff --git a/src/tools/miri/tests/fail/intrinsics/fast_math_result.stderr b/src/tools/miri/tests/fail/intrinsics/fast_math_result.stderr new file mode 100644 index 0000000000000..5b24d2250266d --- /dev/null +++ b/src/tools/miri/tests/fail/intrinsics/fast_math_result.stderr @@ -0,0 +1,15 @@ +error: Undefined Behavior: `fdiv_fast` intrinsic produced non-finite value as result + --> $DIR/fast_math_result.rs:LL:CC + | +LL | let _x: f32 = core::intrinsics::fdiv_fast(1.0, 0.0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `fdiv_fast` intrinsic produced non-finite value as result + | + = 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/fast_math_result.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/src/tools/miri/tests/fail/fast_math_second.rs b/src/tools/miri/tests/fail/intrinsics/fast_math_second.rs similarity index 100% rename from src/tools/miri/tests/fail/fast_math_second.rs rename to src/tools/miri/tests/fail/intrinsics/fast_math_second.rs diff --git a/src/tools/miri/tests/fail/fast_math_second.stderr b/src/tools/miri/tests/fail/intrinsics/fast_math_second.stderr similarity index 100% rename from src/tools/miri/tests/fail/fast_math_second.stderr rename to src/tools/miri/tests/fail/intrinsics/fast_math_second.stderr diff --git a/src/tools/miri/tests/pass/concurrency/windows_condvar_shared.rs b/src/tools/miri/tests/pass-dep/concurrency/windows_condvar_shared.rs similarity index 88% rename from src/tools/miri/tests/pass/concurrency/windows_condvar_shared.rs rename to src/tools/miri/tests/pass-dep/concurrency/windows_condvar_shared.rs index 3b27af9094cc5..5bff9098a58bd 100644 --- a/src/tools/miri/tests/pass/concurrency/windows_condvar_shared.rs +++ b/src/tools/miri/tests/pass-dep/concurrency/windows_condvar_shared.rs @@ -2,39 +2,30 @@ // We are making scheduler assumptions here. //@compile-flags: -Zmiri-preemption-rate=0 -use std::ffi::c_void; use std::ptr::null_mut; use std::thread; +use windows_sys::Win32::System::Threading::{ + AcquireSRWLockExclusive, AcquireSRWLockShared, ReleaseSRWLockExclusive, ReleaseSRWLockShared, + SleepConditionVariableSRW, WakeAllConditionVariable, CONDITION_VARIABLE, + CONDITION_VARIABLE_LOCKMODE_SHARED, INFINITE, SRWLOCK, +}; + +// not in windows-sys +const SRWLOCK_INIT: SRWLOCK = SRWLOCK { Ptr: null_mut() }; +const CONDITION_VARIABLE_INIT: CONDITION_VARIABLE = CONDITION_VARIABLE { Ptr: null_mut() }; + #[derive(Copy, Clone)] struct SendPtr(*mut T); unsafe impl Send for SendPtr {} -extern "system" { - fn SleepConditionVariableSRW( - condvar: *mut *mut c_void, - lock: *mut *mut c_void, - timeout: u32, - flags: u32, - ) -> i32; - fn WakeAllConditionVariable(condvar: *mut *mut c_void); - - fn AcquireSRWLockExclusive(lock: *mut *mut c_void); - fn AcquireSRWLockShared(lock: *mut *mut c_void); - fn ReleaseSRWLockExclusive(lock: *mut *mut c_void); - fn ReleaseSRWLockShared(lock: *mut *mut c_void); -} - -const CONDITION_VARIABLE_LOCKMODE_SHARED: u32 = 1; -const INFINITE: u32 = u32::MAX; - /// threads should be able to reacquire the lock while it is locked by multiple other threads in shared mode fn all_shared() { println!("all_shared"); - let mut lock = null_mut(); - let mut condvar = null_mut(); + let mut lock = SRWLOCK_INIT; + let mut condvar = CONDITION_VARIABLE_INIT; let lock_ptr = SendPtr(&mut lock); let condvar_ptr = SendPtr(&mut condvar); @@ -105,8 +96,8 @@ fn all_shared() { fn shared_sleep_and_exclusive_lock() { println!("shared_sleep_and_exclusive_lock"); - let mut lock = null_mut(); - let mut condvar = null_mut(); + let mut lock = SRWLOCK_INIT; + let mut condvar = CONDITION_VARIABLE_INIT; let lock_ptr = SendPtr(&mut lock); let condvar_ptr = SendPtr(&mut condvar); @@ -166,8 +157,8 @@ fn shared_sleep_and_exclusive_lock() { fn exclusive_sleep_and_shared_lock() { println!("exclusive_sleep_and_shared_lock"); - let mut lock = null_mut(); - let mut condvar = null_mut(); + let mut lock = SRWLOCK_INIT; + let mut condvar = CONDITION_VARIABLE_INIT; let lock_ptr = SendPtr(&mut lock); let condvar_ptr = SendPtr(&mut condvar); diff --git a/src/tools/miri/tests/pass/concurrency/windows_condvar_shared.stdout b/src/tools/miri/tests/pass-dep/concurrency/windows_condvar_shared.stdout similarity index 100% rename from src/tools/miri/tests/pass/concurrency/windows_condvar_shared.stdout rename to src/tools/miri/tests/pass-dep/concurrency/windows_condvar_shared.stdout diff --git a/src/tools/miri/tests/pass/concurrency/windows_detach_terminated.rs b/src/tools/miri/tests/pass-dep/concurrency/windows_detach_terminated.rs similarity index 74% rename from src/tools/miri/tests/pass/concurrency/windows_detach_terminated.rs rename to src/tools/miri/tests/pass-dep/concurrency/windows_detach_terminated.rs index 91088ce6aef9b..3d4f8c5e1c063 100644 --- a/src/tools/miri/tests/pass/concurrency/windows_detach_terminated.rs +++ b/src/tools/miri/tests/pass-dep/concurrency/windows_detach_terminated.rs @@ -5,12 +5,10 @@ use std::os::windows::io::IntoRawHandle; use std::thread; -extern "system" { - fn CloseHandle(handle: usize) -> i32; -} +use windows_sys::Win32::Foundation::{CloseHandle, HANDLE}; fn main() { - let thread = thread::spawn(|| {}).into_raw_handle() as usize; + let thread = thread::spawn(|| {}).into_raw_handle() as HANDLE; // this yield ensures that `thread` is terminated by this point thread::yield_now(); diff --git a/src/tools/miri/tests/pass/concurrency/windows_init_once.rs b/src/tools/miri/tests/pass-dep/concurrency/windows_init_once.rs similarity index 88% rename from src/tools/miri/tests/pass/concurrency/windows_init_once.rs rename to src/tools/miri/tests/pass-dep/concurrency/windows_init_once.rs index b2412f7dbb0e6..5e33617f98ad1 100644 --- a/src/tools/miri/tests/pass/concurrency/windows_init_once.rs +++ b/src/tools/miri/tests/pass-dep/concurrency/windows_init_once.rs @@ -2,33 +2,24 @@ // We are making scheduler assumptions here. //@compile-flags: -Zmiri-preemption-rate=0 -use std::ffi::c_void; use std::ptr::null_mut; use std::thread; +use windows_sys::Win32::Foundation::{FALSE, TRUE}; +use windows_sys::Win32::System::Threading::{ + InitOnceBeginInitialize, InitOnceComplete, INIT_ONCE, INIT_ONCE_INIT_FAILED, +}; + +// not in windows-sys +const INIT_ONCE_STATIC_INIT: INIT_ONCE = INIT_ONCE { Ptr: null_mut() }; + #[derive(Copy, Clone)] struct SendPtr(*mut T); unsafe impl Send for SendPtr {} -extern "system" { - fn InitOnceBeginInitialize( - init: *mut *mut c_void, - flags: u32, - pending: *mut i32, - context: *mut c_void, - ) -> i32; - - fn InitOnceComplete(init: *mut *mut c_void, flags: u32, context: *mut c_void) -> i32; -} - -const TRUE: i32 = 1; -const FALSE: i32 = 0; - -const INIT_ONCE_INIT_FAILED: u32 = 4; - fn single_thread() { - let mut init_once = null_mut(); + let mut init_once = INIT_ONCE_STATIC_INIT; let mut pending = 0; unsafe { @@ -41,7 +32,7 @@ fn single_thread() { assert_eq!(pending, FALSE); } - let mut init_once = null_mut(); + let mut init_once = INIT_ONCE_STATIC_INIT; unsafe { assert_eq!(InitOnceBeginInitialize(&mut init_once, 0, &mut pending, null_mut()), TRUE); @@ -55,7 +46,7 @@ fn single_thread() { } fn block_until_complete() { - let mut init_once = null_mut(); + let mut init_once = INIT_ONCE_STATIC_INIT; let mut pending = 0; unsafe { @@ -92,7 +83,7 @@ fn block_until_complete() { } fn retry_on_fail() { - let mut init_once = null_mut(); + let mut init_once = INIT_ONCE_STATIC_INIT; let mut pending = 0; unsafe { @@ -134,7 +125,7 @@ fn retry_on_fail() { } fn no_data_race_after_complete() { - let mut init_once = null_mut(); + let mut init_once = INIT_ONCE_STATIC_INIT; let mut pending = 0; unsafe { diff --git a/src/tools/miri/tests/pass/concurrency/windows_init_once.stdout b/src/tools/miri/tests/pass-dep/concurrency/windows_init_once.stdout similarity index 100% rename from src/tools/miri/tests/pass/concurrency/windows_init_once.stdout rename to src/tools/miri/tests/pass-dep/concurrency/windows_init_once.stdout diff --git a/src/tools/miri/tests/pass/concurrency/windows_join_multiple.rs b/src/tools/miri/tests/pass-dep/concurrency/windows_join_multiple.rs similarity index 76% rename from src/tools/miri/tests/pass/concurrency/windows_join_multiple.rs rename to src/tools/miri/tests/pass-dep/concurrency/windows_join_multiple.rs index 5da5497f9829f..bff59591a94ad 100644 --- a/src/tools/miri/tests/pass/concurrency/windows_join_multiple.rs +++ b/src/tools/miri/tests/pass-dep/concurrency/windows_join_multiple.rs @@ -6,11 +6,8 @@ use std::os::windows::io::IntoRawHandle; use std::sync::atomic::{AtomicBool, Ordering}; use std::thread; -extern "system" { - fn WaitForSingleObject(handle: usize, timeout: u32) -> u32; -} - -const INFINITE: u32 = u32::MAX; +use windows_sys::Win32::Foundation::{HANDLE, WAIT_OBJECT_0}; +use windows_sys::Win32::System::Threading::{WaitForSingleObject, INFINITE}; fn main() { static FLAG: AtomicBool = AtomicBool::new(false); @@ -20,10 +17,10 @@ fn main() { thread::yield_now(); } }) - .into_raw_handle() as usize; + .into_raw_handle() as HANDLE; let waiter = move || unsafe { - assert_eq!(WaitForSingleObject(blocker, INFINITE), 0); + assert_eq!(WaitForSingleObject(blocker, INFINITE), WAIT_OBJECT_0); }; let waiter1 = thread::spawn(waiter); diff --git a/src/tools/miri/tests/pass-dep/shims/mmap.rs b/src/tools/miri/tests/pass-dep/shims/mmap.rs index 518f2ea3e6f1c..e19f54d0687df 100644 --- a/src/tools/miri/tests/pass-dep/shims/mmap.rs +++ b/src/tools/miri/tests/pass-dep/shims/mmap.rs @@ -90,9 +90,30 @@ fn test_mmap() { assert_eq!(Error::last_os_error().raw_os_error().unwrap(), libc::ENOTSUP); } + // We report an error for mappings whose length cannot be rounded up to a multiple of + // the page size. + let ptr = unsafe { + libc::mmap( + ptr::null_mut(), + usize::MAX - 1, + libc::PROT_READ | libc::PROT_WRITE, + libc::MAP_PRIVATE | libc::MAP_ANONYMOUS, + -1, + 0, + ) + }; + assert_eq!(ptr, libc::MAP_FAILED); + + // We report an error when trying to munmap an address which is not a multiple of the page size let res = unsafe { libc::munmap(ptr::invalid_mut(1), page_size) }; assert_eq!(res, -1); assert_eq!(Error::last_os_error().raw_os_error().unwrap(), libc::EINVAL); + + // We report an error when trying to munmap a length that cannot be rounded up to a multiple of + // the page size. + let res = unsafe { libc::munmap(ptr::invalid_mut(page_size), usize::MAX - 1) }; + assert_eq!(res, -1); + assert_eq!(Error::last_os_error().raw_os_error().unwrap(), libc::EINVAL); } #[cfg(target_os = "linux")] diff --git a/src/tools/miri/tests/pass-dep/tempfile.rs b/src/tools/miri/tests/pass-dep/tempfile.rs new file mode 100644 index 0000000000000..c4583ca3f47a4 --- /dev/null +++ b/src/tools/miri/tests/pass-dep/tempfile.rs @@ -0,0 +1,21 @@ +//@ignore-target-windows: File handling is not implemented yet +//@ignore-host-windows: Only supported for UNIX hosts +//@compile-flags: -Zmiri-disable-isolation + +#[path = "../utils/mod.rs"] +mod utils; + +/// Test that the [`tempfile`] crate is compatible with miri for UNIX hosts and targets +fn main() { + test_tempfile(); + test_tempfile_in(); +} + +fn test_tempfile() { + tempfile::tempfile().unwrap(); +} + +fn test_tempfile_in() { + let dir_path = utils::tmp(); + tempfile::tempfile_in(dir_path).unwrap(); +} diff --git a/src/tools/miri/tests/pass/float_nan.rs b/src/tools/miri/tests/pass/float_nan.rs index 6ea034e2cda9d..207ce70fb20e3 100644 --- a/src/tools/miri/tests/pass/float_nan.rs +++ b/src/tools/miri/tests/pass/float_nan.rs @@ -1,8 +1,16 @@ +#![feature(float_gamma, portable_simd, core_intrinsics, platform_intrinsics)] use std::collections::HashSet; use std::fmt; use std::hash::Hash; use std::hint::black_box; +fn ldexp(a: f64, b: i32) -> f64 { + extern "C" { + fn ldexp(x: f64, n: i32) -> f64; + } + unsafe { ldexp(a, b) } +} + #[derive(Clone, Copy, Debug, PartialEq, Eq)] enum Sign { Neg = 1, @@ -249,6 +257,58 @@ fn test_f32() { check_all_outcomes(HashSet::from_iter([F32::nan(Neg, Signaling, all1_payload)]), || { F32::from(-all1_snan) }); + + // Intrinsics + let nan = F32::nan(Neg, Quiet, 0).as_f32(); + check_all_outcomes( + HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]), + || F32::from(f32::min(nan, nan)), + ); + check_all_outcomes( + HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]), + || F32::from(nan.sin()), + ); + check_all_outcomes( + HashSet::from_iter([ + F32::nan(Pos, Quiet, 0), + F32::nan(Neg, Quiet, 0), + F32::nan(Pos, Quiet, 1), + F32::nan(Neg, Quiet, 1), + F32::nan(Pos, Quiet, 2), + F32::nan(Neg, Quiet, 2), + F32::nan(Pos, Quiet, all1_payload), + F32::nan(Neg, Quiet, all1_payload), + F32::nan(Pos, Signaling, all1_payload), + F32::nan(Neg, Signaling, all1_payload), + ]), + || F32::from(just1.mul_add(F32::nan(Neg, Quiet, 2).as_f32(), all1_snan)), + ); + check_all_outcomes( + HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]), + || F32::from(nan.powf(nan)), + ); + check_all_outcomes( + HashSet::from_iter([1.0f32.into()]), + || F32::from(1.0f32.powf(nan)), // special `pow` rule + ); + check_all_outcomes( + HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]), + || F32::from(nan.powi(1)), + ); + + // libm functions + check_all_outcomes( + HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]), + || F32::from(nan.sinh()), + ); + check_all_outcomes( + HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]), + || F32::from(nan.atan2(nan)), + ); + check_all_outcomes( + HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]), + || F32::from(nan.ln_gamma().0), + ); } fn test_f64() { @@ -309,6 +369,62 @@ fn test_f64() { ]), || F64::from(just1 % all1_snan), ); + + // Intrinsics + let nan = F64::nan(Neg, Quiet, 0).as_f64(); + check_all_outcomes( + HashSet::from_iter([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)]), + || F64::from(f64::min(nan, nan)), + ); + check_all_outcomes( + HashSet::from_iter([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)]), + || F64::from(nan.sin()), + ); + check_all_outcomes( + HashSet::from_iter([ + F64::nan(Pos, Quiet, 0), + F64::nan(Neg, Quiet, 0), + F64::nan(Pos, Quiet, 1), + F64::nan(Neg, Quiet, 1), + F64::nan(Pos, Quiet, 2), + F64::nan(Neg, Quiet, 2), + F64::nan(Pos, Quiet, all1_payload), + F64::nan(Neg, Quiet, all1_payload), + F64::nan(Pos, Signaling, all1_payload), + F64::nan(Neg, Signaling, all1_payload), + ]), + || F64::from(just1.mul_add(F64::nan(Neg, Quiet, 2).as_f64(), all1_snan)), + ); + check_all_outcomes( + HashSet::from_iter([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)]), + || F64::from(nan.powf(nan)), + ); + check_all_outcomes( + HashSet::from_iter([1.0f64.into()]), + || F64::from(1.0f64.powf(nan)), // special `pow` rule + ); + check_all_outcomes( + HashSet::from_iter([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)]), + || F64::from(nan.powi(1)), + ); + + // libm functions + check_all_outcomes( + HashSet::from_iter([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)]), + || F64::from(nan.sinh()), + ); + check_all_outcomes( + HashSet::from_iter([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)]), + || F64::from(nan.atan2(nan)), + ); + check_all_outcomes( + HashSet::from_iter([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)]), + || F64::from(ldexp(nan, 1)), + ); + check_all_outcomes( + HashSet::from_iter([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)]), + || F64::from(nan.ln_gamma().0), + ); } fn test_casts() { @@ -397,6 +513,61 @@ fn test_casts() { ); } +fn test_simd() { + use std::intrinsics::simd::*; + use std::simd::*; + + extern "platform-intrinsic" { + fn simd_fsqrt(x: T) -> T; + fn simd_ceil(x: T) -> T; + fn simd_fma(x: T, y: T, z: T) -> T; + } + + let nan = F32::nan(Neg, Quiet, 0).as_f32(); + check_all_outcomes( + HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]), + || F32::from(unsafe { simd_div(f32x4::splat(0.0), f32x4::splat(0.0)) }[0]), + ); + check_all_outcomes( + HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]), + || F32::from(unsafe { simd_fmin(f32x4::splat(nan), f32x4::splat(nan)) }[0]), + ); + check_all_outcomes( + HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]), + || F32::from(unsafe { simd_fmax(f32x4::splat(nan), f32x4::splat(nan)) }[0]), + ); + check_all_outcomes( + HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]), + || { + F32::from( + unsafe { simd_fma(f32x4::splat(nan), f32x4::splat(nan), f32x4::splat(nan)) }[0], + ) + }, + ); + check_all_outcomes( + HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]), + || F32::from(unsafe { simd_reduce_add_ordered::<_, f32>(f32x4::splat(nan), nan) }), + ); + check_all_outcomes( + HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]), + || F32::from(unsafe { simd_reduce_max::<_, f32>(f32x4::splat(nan)) }), + ); + check_all_outcomes( + HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]), + || F32::from(unsafe { simd_fsqrt(f32x4::splat(nan)) }[0]), + ); + check_all_outcomes( + HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]), + || F32::from(unsafe { simd_ceil(f32x4::splat(nan)) }[0]), + ); + + // Casts + check_all_outcomes( + HashSet::from_iter([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)]), + || F64::from(unsafe { simd_cast::(f32x4::splat(nan)) }[0]), + ); +} + fn main() { // Check our constants against std, just to be sure. // We add 1 since our numbers are the number of bits stored @@ -408,4 +579,5 @@ fn main() { test_f32(); test_f64(); test_casts(); + test_simd(); }