mirror of
https://github.com/rust-lang/rust.git
synced 2025-04-15 13:36:49 +00:00
Auto merge of #119039 - RalfJung:miri, r=RalfJung
Miri subtree update r? `@ghost`
This commit is contained in:
commit
5151b8c427
@ -109,7 +109,7 @@ to run the other checks while ignoring the ui output, use `MIRI_SKIP_UI_CHECKS=1
|
||||
|
||||
For more info on how to configure ui tests see [the documentation on the ui test crate][ui_test]
|
||||
|
||||
[ui_test]: ui_test/README.md
|
||||
[ui_test]: https://github.com/oli-obk/ui_test/blob/main/README.md
|
||||
|
||||
### Testing `cargo miri`
|
||||
|
||||
|
@ -200,9 +200,6 @@ pub fn ask_to_run(mut cmd: Command, ask: bool, text: &str) {
|
||||
// cargo invocation.
|
||||
fn cargo_extra_flags() -> Vec<String> {
|
||||
let mut flags = Vec::new();
|
||||
// `-Zunstable-options` is required by `--config`.
|
||||
flags.push("-Zunstable-options".to_string());
|
||||
|
||||
// Forward `--config` flags.
|
||||
let config_flag = "--config";
|
||||
for arg in get_arg_flag_values(config_flag) {
|
||||
|
@ -2,5 +2,6 @@
|
||||
set -e
|
||||
# Instead of doing just `cargo run --manifest-path .. $@`, we invoke miri-script binary directly. Invoking `cargo run` goes through
|
||||
# rustup (that sets it's own environmental variables), which is undesirable.
|
||||
cargo build $CARGO_EXTRA_FLAGS -q --manifest-path "$(dirname "$0")"/miri-script/Cargo.toml
|
||||
"$(dirname "$0")"/miri-script/target/debug/miri-script "$@"
|
||||
MIRI_SCRIPT_TARGET_DIR="$(dirname "$0")"/miri-script/target
|
||||
cargo build $CARGO_EXTRA_FLAGS -q --target-dir "$MIRI_SCRIPT_TARGET_DIR" --manifest-path "$(dirname "$0")"/miri-script/Cargo.toml
|
||||
"$MIRI_SCRIPT_TARGET_DIR"/debug/miri-script "$@"
|
||||
|
@ -73,7 +73,9 @@ impl MiriEnv {
|
||||
flags.push("-C link-args=-Wl,-rpath,");
|
||||
flags.push(libdir);
|
||||
// Enable rustc-specific lints (ignored without `-Zunstable-options`).
|
||||
flags.push(" -Zunstable-options -Wrustc::internal -Wrust_2018_idioms -Wunused_lifetimes -Wsemicolon_in_expressions_from_macros");
|
||||
flags.push(
|
||||
" -Zunstable-options -Wrustc::internal -Wrust_2018_idioms -Wunused_lifetimes",
|
||||
);
|
||||
// Add user-defined flags.
|
||||
if let Some(value) = std::env::var_os("RUSTFLAGS") {
|
||||
flags.push(" ");
|
||||
|
@ -1 +1 @@
|
||||
317d14a56cb8c748bf0e2f2afff89c2249ab4423
|
||||
604f185fae9a4b0edf7e28f616a0f53880f8f074
|
||||
|
@ -65,7 +65,7 @@ pub struct FrameState {
|
||||
/// incremental updates of the global list of protected tags stored in the
|
||||
/// `stacked_borrows::GlobalState` upon function return, and if we attempt to pop a protected
|
||||
/// tag, to identify which call is responsible for protecting the tag.
|
||||
/// See `Stack::item_popped` for more explanation.
|
||||
/// See `Stack::item_invalidated` for more explanation.
|
||||
/// Tree Borrows also needs to know which allocation these tags
|
||||
/// belong to so that it can perform a read through them immediately before
|
||||
/// the frame gets popped.
|
||||
@ -76,8 +76,10 @@ pub struct FrameState {
|
||||
}
|
||||
|
||||
impl VisitProvenance for FrameState {
|
||||
fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {
|
||||
// `protected_tags` are already recorded by `GlobalStateInner`.
|
||||
fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
|
||||
for (id, tag) in &self.protected_tags {
|
||||
visit(Some(*id), Some(*tag));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -98,7 +100,7 @@ pub struct GlobalStateInner {
|
||||
/// An item is protected if its tag is in this set, *and* it has the "protected" bit set.
|
||||
/// We add tags to this when they are created with a protector in `reborrow`, and
|
||||
/// we remove tags from this when the call which is protecting them returns, in
|
||||
/// `GlobalStateInner::end_call`. See `Stack::item_popped` for more details.
|
||||
/// `GlobalStateInner::end_call`. See `Stack::item_invalidated` for more details.
|
||||
protected_tags: FxHashMap<BorTag, ProtectorKind>,
|
||||
/// The pointer ids to trace
|
||||
tracked_pointer_tags: FxHashSet<BorTag>,
|
||||
@ -111,10 +113,8 @@ pub struct GlobalStateInner {
|
||||
}
|
||||
|
||||
impl VisitProvenance for GlobalStateInner {
|
||||
fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
|
||||
for &tag in self.protected_tags.keys() {
|
||||
visit(None, Some(tag));
|
||||
}
|
||||
fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {
|
||||
// All the provenance in protected_tags is also stored in FrameState, and visited there.
|
||||
// The only other candidate is base_ptr_tags, and that does not need visiting since we don't ever
|
||||
// GC the bottommost/root tag.
|
||||
}
|
||||
|
@ -5,9 +5,7 @@ use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_span::{Span, SpanData};
|
||||
use rustc_target::abi::Size;
|
||||
|
||||
use crate::borrow_tracker::{
|
||||
AccessKind, GlobalStateInner, ProtectorKind,
|
||||
};
|
||||
use crate::borrow_tracker::{AccessKind, GlobalStateInner, ProtectorKind};
|
||||
use crate::*;
|
||||
|
||||
/// Error reporting
|
||||
|
@ -18,7 +18,8 @@ use rustc_target::abi::{Abi, Size};
|
||||
|
||||
use crate::borrow_tracker::{
|
||||
stacked_borrows::diagnostics::{AllocHistory, DiagnosticCx, DiagnosticCxBuilder},
|
||||
AccessKind, GlobalStateInner, ProtectorKind,};
|
||||
AccessKind, GlobalStateInner, ProtectorKind,
|
||||
};
|
||||
use crate::*;
|
||||
|
||||
use diagnostics::{RetagCause, RetagInfo};
|
||||
|
@ -2,9 +2,7 @@ use log::trace;
|
||||
|
||||
use rustc_target::abi::{Abi, Size};
|
||||
|
||||
use crate::borrow_tracker::{
|
||||
AccessKind, GlobalState, GlobalStateInner, ProtectorKind,
|
||||
};
|
||||
use crate::borrow_tracker::{AccessKind, GlobalState, GlobalStateInner, ProtectorKind};
|
||||
use rustc_middle::{
|
||||
mir::{Mutability, RetagKind},
|
||||
ty::{
|
||||
|
@ -38,7 +38,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
||||
if flags & this.eval_libc_i32("MREMAP_MAYMOVE") == 0 {
|
||||
// We only support MREMAP_MAYMOVE, so not passing the flag is just a failure
|
||||
this.set_last_error(Scalar::from_i32(this.eval_libc_i32("EINVAL")))?;
|
||||
return Ok(Scalar::from_maybe_pointer(Pointer::null(), this));
|
||||
return Ok(this.eval_libc("MAP_FAILED"));
|
||||
}
|
||||
|
||||
let old_address = Machine::ptr_from_addr_cast(this, old_address)?;
|
||||
|
@ -48,11 +48,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
||||
// First, we do some basic argument validation as required by mmap
|
||||
if (flags & (map_private | map_shared)).count_ones() != 1 {
|
||||
this.set_last_error(Scalar::from_i32(this.eval_libc_i32("EINVAL")))?;
|
||||
return Ok(Scalar::from_maybe_pointer(Pointer::null(), this));
|
||||
return Ok(this.eval_libc("MAP_FAILED"));
|
||||
}
|
||||
if length == 0 {
|
||||
this.set_last_error(Scalar::from_i32(this.eval_libc_i32("EINVAL")))?;
|
||||
return Ok(Scalar::from_maybe_pointer(Pointer::null(), this));
|
||||
return Ok(this.eval_libc("MAP_FAILED"));
|
||||
}
|
||||
|
||||
// If a user tries to map a file, we want to loudly inform them that this is not going
|
||||
@ -72,7 +72,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
||||
// Miri doesn't support MAP_FIXED or any any protections other than PROT_READ|PROT_WRITE.
|
||||
if flags & map_fixed != 0 || prot != prot_read | prot_write {
|
||||
this.set_last_error(Scalar::from_i32(this.eval_libc_i32("ENOTSUP")))?;
|
||||
return Ok(Scalar::from_maybe_pointer(Pointer::null(), this));
|
||||
return Ok(this.eval_libc("MAP_FAILED"));
|
||||
}
|
||||
|
||||
// Miri does not support shared mappings, or any of the other extensions that for example
|
||||
|
@ -1,3 +1,6 @@
|
||||
use rand::Rng as _;
|
||||
|
||||
use rustc_apfloat::{ieee::Single, Float as _};
|
||||
use rustc_middle::{mir, ty};
|
||||
use rustc_span::Symbol;
|
||||
use rustc_target::abi::Size;
|
||||
@ -331,6 +334,210 @@ fn bin_op_simd_float_all<'tcx, F: rustc_apfloat::Float>(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
enum FloatUnaryOp {
|
||||
/// sqrt(x)
|
||||
///
|
||||
/// <https://www.felixcloutier.com/x86/sqrtss>
|
||||
/// <https://www.felixcloutier.com/x86/sqrtps>
|
||||
Sqrt,
|
||||
/// Approximation of 1/x
|
||||
///
|
||||
/// <https://www.felixcloutier.com/x86/rcpss>
|
||||
/// <https://www.felixcloutier.com/x86/rcpps>
|
||||
Rcp,
|
||||
/// Approximation of 1/sqrt(x)
|
||||
///
|
||||
/// <https://www.felixcloutier.com/x86/rsqrtss>
|
||||
/// <https://www.felixcloutier.com/x86/rsqrtps>
|
||||
Rsqrt,
|
||||
}
|
||||
|
||||
/// Performs `which` scalar operation on `op` and returns the result.
|
||||
#[allow(clippy::arithmetic_side_effects)] // floating point operations without side effects
|
||||
fn unary_op_f32<'tcx>(
|
||||
this: &mut crate::MiriInterpCx<'_, 'tcx>,
|
||||
which: FloatUnaryOp,
|
||||
op: &ImmTy<'tcx, Provenance>,
|
||||
) -> InterpResult<'tcx, Scalar<Provenance>> {
|
||||
match which {
|
||||
FloatUnaryOp::Sqrt => {
|
||||
let op = op.to_scalar();
|
||||
// FIXME using host floats
|
||||
Ok(Scalar::from_u32(f32::from_bits(op.to_u32()?).sqrt().to_bits()))
|
||||
}
|
||||
FloatUnaryOp::Rcp => {
|
||||
let op = op.to_scalar().to_f32()?;
|
||||
let div = (Single::from_u128(1).value / op).value;
|
||||
// Apply a relative error with a magnitude on the order of 2^-12 to simulate the
|
||||
// inaccuracy of RCP.
|
||||
let res = apply_random_float_error(this, div, -12);
|
||||
Ok(Scalar::from_f32(res))
|
||||
}
|
||||
FloatUnaryOp::Rsqrt => {
|
||||
let op = op.to_scalar().to_u32()?;
|
||||
// FIXME using host floats
|
||||
let sqrt = Single::from_bits(f32::from_bits(op).sqrt().to_bits().into());
|
||||
let rsqrt = (Single::from_u128(1).value / sqrt).value;
|
||||
// Apply a relative error with a magnitude on the order of 2^-12 to simulate the
|
||||
// inaccuracy of RSQRT.
|
||||
let res = apply_random_float_error(this, rsqrt, -12);
|
||||
Ok(Scalar::from_f32(res))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Disturbes a floating-point result by a relative error on the order of (-2^scale, 2^scale).
|
||||
#[allow(clippy::arithmetic_side_effects)] // floating point arithmetic cannot panic
|
||||
fn apply_random_float_error<F: rustc_apfloat::Float>(
|
||||
this: &mut crate::MiriInterpCx<'_, '_>,
|
||||
val: F,
|
||||
err_scale: i32,
|
||||
) -> F {
|
||||
let rng = this.machine.rng.get_mut();
|
||||
// generates rand(0, 2^64) * 2^(scale - 64) = rand(0, 1) * 2^scale
|
||||
let err =
|
||||
F::from_u128(rng.gen::<u64>().into()).value.scalbn(err_scale.checked_sub(64).unwrap());
|
||||
// give it a random sign
|
||||
let err = if rng.gen::<bool>() { -err } else { err };
|
||||
// multiple the value with (1+err)
|
||||
(val * (F::from_u128(1).value + err).value).value
|
||||
}
|
||||
|
||||
/// Performs `which` operation on the first component of `op` and copies
|
||||
/// the other components. The result is stored in `dest`.
|
||||
fn unary_op_ss<'tcx>(
|
||||
this: &mut crate::MiriInterpCx<'_, 'tcx>,
|
||||
which: FloatUnaryOp,
|
||||
op: &OpTy<'tcx, Provenance>,
|
||||
dest: &PlaceTy<'tcx, Provenance>,
|
||||
) -> InterpResult<'tcx, ()> {
|
||||
let (op, op_len) = this.operand_to_simd(op)?;
|
||||
let (dest, dest_len) = this.place_to_simd(dest)?;
|
||||
|
||||
assert_eq!(dest_len, op_len);
|
||||
|
||||
let res0 = unary_op_f32(this, which, &this.read_immediate(&this.project_index(&op, 0)?)?)?;
|
||||
this.write_scalar(res0, &this.project_index(&dest, 0)?)?;
|
||||
|
||||
for i in 1..dest_len {
|
||||
this.copy_op(
|
||||
&this.project_index(&op, i)?,
|
||||
&this.project_index(&dest, i)?,
|
||||
/*allow_transmute*/ false,
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Performs `which` operation on each component of `op`, storing the
|
||||
/// result is stored in `dest`.
|
||||
fn unary_op_ps<'tcx>(
|
||||
this: &mut crate::MiriInterpCx<'_, 'tcx>,
|
||||
which: FloatUnaryOp,
|
||||
op: &OpTy<'tcx, Provenance>,
|
||||
dest: &PlaceTy<'tcx, Provenance>,
|
||||
) -> InterpResult<'tcx, ()> {
|
||||
let (op, op_len) = this.operand_to_simd(op)?;
|
||||
let (dest, dest_len) = this.place_to_simd(dest)?;
|
||||
|
||||
assert_eq!(dest_len, op_len);
|
||||
|
||||
for i in 0..dest_len {
|
||||
let op = this.read_immediate(&this.project_index(&op, i)?)?;
|
||||
let dest = this.project_index(&dest, i)?;
|
||||
|
||||
let res = unary_op_f32(this, which, &op)?;
|
||||
this.write_scalar(res, &dest)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Rounds the first element of `right` according to `rounding`
|
||||
// and copies the remaining elements from `left`.
|
||||
fn round_first<'tcx, F: rustc_apfloat::Float>(
|
||||
this: &mut crate::MiriInterpCx<'_, 'tcx>,
|
||||
left: &OpTy<'tcx, Provenance>,
|
||||
right: &OpTy<'tcx, Provenance>,
|
||||
rounding: &OpTy<'tcx, Provenance>,
|
||||
dest: &PlaceTy<'tcx, Provenance>,
|
||||
) -> InterpResult<'tcx, ()> {
|
||||
let (left, left_len) = this.operand_to_simd(left)?;
|
||||
let (right, right_len) = this.operand_to_simd(right)?;
|
||||
let (dest, dest_len) = this.place_to_simd(dest)?;
|
||||
|
||||
assert_eq!(dest_len, left_len);
|
||||
assert_eq!(dest_len, right_len);
|
||||
|
||||
let rounding = rounding_from_imm(this.read_scalar(rounding)?.to_i32()?)?;
|
||||
|
||||
let op0: F = this.read_scalar(&this.project_index(&right, 0)?)?.to_float()?;
|
||||
let res = op0.round_to_integral(rounding).value;
|
||||
this.write_scalar(
|
||||
Scalar::from_uint(res.to_bits(), Size::from_bits(F::BITS)),
|
||||
&this.project_index(&dest, 0)?,
|
||||
)?;
|
||||
|
||||
for i in 1..dest_len {
|
||||
this.copy_op(
|
||||
&this.project_index(&left, i)?,
|
||||
&this.project_index(&dest, i)?,
|
||||
/*allow_transmute*/ false,
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Rounds all elements of `op` according to `rounding`.
|
||||
fn round_all<'tcx, F: rustc_apfloat::Float>(
|
||||
this: &mut crate::MiriInterpCx<'_, 'tcx>,
|
||||
op: &OpTy<'tcx, Provenance>,
|
||||
rounding: &OpTy<'tcx, Provenance>,
|
||||
dest: &PlaceTy<'tcx, Provenance>,
|
||||
) -> InterpResult<'tcx, ()> {
|
||||
let (op, op_len) = this.operand_to_simd(op)?;
|
||||
let (dest, dest_len) = this.place_to_simd(dest)?;
|
||||
|
||||
assert_eq!(dest_len, op_len);
|
||||
|
||||
let rounding = rounding_from_imm(this.read_scalar(rounding)?.to_i32()?)?;
|
||||
|
||||
for i in 0..dest_len {
|
||||
let op: F = this.read_scalar(&this.project_index(&op, i)?)?.to_float()?;
|
||||
let res = op.round_to_integral(rounding).value;
|
||||
this.write_scalar(
|
||||
Scalar::from_uint(res.to_bits(), Size::from_bits(F::BITS)),
|
||||
&this.project_index(&dest, i)?,
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Gets equivalent `rustc_apfloat::Round` from rounding mode immediate of
|
||||
/// `round.{ss,sd,ps,pd}` intrinsics.
|
||||
fn rounding_from_imm<'tcx>(rounding: i32) -> InterpResult<'tcx, rustc_apfloat::Round> {
|
||||
// The fourth bit of `rounding` only affects the SSE status
|
||||
// register, which cannot be accessed from Miri (or from Rust,
|
||||
// for that matter), so we can ignore it.
|
||||
match rounding & !0b1000 {
|
||||
// When the third bit is 0, the rounding mode is determined by the
|
||||
// first two bits.
|
||||
0b000 => Ok(rustc_apfloat::Round::NearestTiesToEven),
|
||||
0b001 => Ok(rustc_apfloat::Round::TowardNegative),
|
||||
0b010 => Ok(rustc_apfloat::Round::TowardPositive),
|
||||
0b011 => Ok(rustc_apfloat::Round::TowardZero),
|
||||
// When the third bit is 1, the rounding mode is determined by the
|
||||
// SSE status register. Since we do not support modifying it from
|
||||
// Miri (or Rust), we assume it to be at its default mode (round-to-nearest).
|
||||
0b100..=0b111 => Ok(rustc_apfloat::Round::NearestTiesToEven),
|
||||
rounding => throw_unsup_format!("unsupported rounding mode 0x{rounding:02x}"),
|
||||
}
|
||||
}
|
||||
|
||||
/// 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.
|
||||
@ -408,3 +615,84 @@ fn horizontal_bin_op<'tcx>(
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Conditionally multiplies the packed floating-point elements in
|
||||
/// `left` and `right` using the high 4 bits in `imm`, sums the calculated
|
||||
/// products (up to 4), and conditionally stores the sum in `dest` using
|
||||
/// the low 4 bits of `imm`.
|
||||
fn conditional_dot_product<'tcx>(
|
||||
this: &mut crate::MiriInterpCx<'_, 'tcx>,
|
||||
left: &OpTy<'tcx, Provenance>,
|
||||
right: &OpTy<'tcx, Provenance>,
|
||||
imm: &OpTy<'tcx, Provenance>,
|
||||
dest: &PlaceTy<'tcx, Provenance>,
|
||||
) -> InterpResult<'tcx, ()> {
|
||||
let (left, left_len) = this.operand_to_simd(left)?;
|
||||
let (right, right_len) = this.operand_to_simd(right)?;
|
||||
let (dest, dest_len) = this.place_to_simd(dest)?;
|
||||
|
||||
assert_eq!(left_len, right_len);
|
||||
assert!(dest_len <= 4);
|
||||
|
||||
let imm = this.read_scalar(imm)?.to_u8()?;
|
||||
|
||||
let element_layout = left.layout.field(this, 0);
|
||||
|
||||
// Calculate dot product
|
||||
// Elements are floating point numbers, but we can use `from_int`
|
||||
// because the representation of 0.0 is all zero bits.
|
||||
let mut sum = ImmTy::from_int(0u8, element_layout);
|
||||
for i in 0..left_len {
|
||||
if imm & (1 << i.checked_add(4).unwrap()) != 0 {
|
||||
let left = this.read_immediate(&this.project_index(&left, i)?)?;
|
||||
let right = this.read_immediate(&this.project_index(&right, i)?)?;
|
||||
|
||||
let mul = this.wrapping_binary_op(mir::BinOp::Mul, &left, &right)?;
|
||||
sum = this.wrapping_binary_op(mir::BinOp::Add, &sum, &mul)?;
|
||||
}
|
||||
}
|
||||
|
||||
// Write to destination (conditioned to imm)
|
||||
for i in 0..dest_len {
|
||||
let dest = this.project_index(&dest, i)?;
|
||||
|
||||
if imm & (1 << i) != 0 {
|
||||
this.write_immediate(*sum, &dest)?;
|
||||
} else {
|
||||
this.write_scalar(Scalar::from_int(0u8, element_layout.size), &dest)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Calculates two booleans.
|
||||
///
|
||||
/// The first is true when all the bits of `op & mask` are zero.
|
||||
/// The second is true when `(op & mask) == mask`
|
||||
fn test_bits_masked<'tcx>(
|
||||
this: &crate::MiriInterpCx<'_, 'tcx>,
|
||||
op: &OpTy<'tcx, Provenance>,
|
||||
mask: &OpTy<'tcx, Provenance>,
|
||||
) -> InterpResult<'tcx, (bool, bool)> {
|
||||
assert_eq!(op.layout, mask.layout);
|
||||
|
||||
let (op, op_len) = this.operand_to_simd(op)?;
|
||||
let (mask, mask_len) = this.operand_to_simd(mask)?;
|
||||
|
||||
assert_eq!(op_len, mask_len);
|
||||
|
||||
let mut all_zero = true;
|
||||
let mut masked_set = true;
|
||||
for i in 0..op_len {
|
||||
let op = this.project_index(&op, i)?;
|
||||
let mask = this.project_index(&mask, i)?;
|
||||
|
||||
let op = this.read_scalar(&op)?.to_uint(op.layout.size)?;
|
||||
let mask = this.read_scalar(&mask)?.to_uint(mask.layout.size)?;
|
||||
all_zero &= (op & mask) == 0;
|
||||
masked_set &= (op & mask) == mask;
|
||||
}
|
||||
|
||||
Ok((all_zero, masked_set))
|
||||
}
|
||||
|
@ -1,11 +1,12 @@
|
||||
use rustc_apfloat::{ieee::Single, Float as _};
|
||||
use rustc_apfloat::ieee::Single;
|
||||
use rustc_middle::mir;
|
||||
use rustc_span::Symbol;
|
||||
use rustc_target::spec::abi::Abi;
|
||||
|
||||
use rand::Rng as _;
|
||||
|
||||
use super::{bin_op_simd_float_all, bin_op_simd_float_first, FloatBinOp};
|
||||
use super::{
|
||||
bin_op_simd_float_all, bin_op_simd_float_first, unary_op_ps, unary_op_ss, FloatBinOp,
|
||||
FloatUnaryOp,
|
||||
};
|
||||
use crate::*;
|
||||
use shims::foreign_items::EmulateForeignItemResult;
|
||||
|
||||
@ -219,124 +220,3 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>:
|
||||
Ok(EmulateForeignItemResult::NeedsJumping)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
enum FloatUnaryOp {
|
||||
/// sqrt(x)
|
||||
///
|
||||
/// <https://www.felixcloutier.com/x86/sqrtss>
|
||||
/// <https://www.felixcloutier.com/x86/sqrtps>
|
||||
Sqrt,
|
||||
/// Approximation of 1/x
|
||||
///
|
||||
/// <https://www.felixcloutier.com/x86/rcpss>
|
||||
/// <https://www.felixcloutier.com/x86/rcpps>
|
||||
Rcp,
|
||||
/// Approximation of 1/sqrt(x)
|
||||
///
|
||||
/// <https://www.felixcloutier.com/x86/rsqrtss>
|
||||
/// <https://www.felixcloutier.com/x86/rsqrtps>
|
||||
Rsqrt,
|
||||
}
|
||||
|
||||
/// Performs `which` scalar operation on `op` and returns the result.
|
||||
#[allow(clippy::arithmetic_side_effects)] // floating point operations without side effects
|
||||
fn unary_op_f32<'tcx>(
|
||||
this: &mut crate::MiriInterpCx<'_, 'tcx>,
|
||||
which: FloatUnaryOp,
|
||||
op: &ImmTy<'tcx, Provenance>,
|
||||
) -> InterpResult<'tcx, Scalar<Provenance>> {
|
||||
match which {
|
||||
FloatUnaryOp::Sqrt => {
|
||||
let op = op.to_scalar();
|
||||
// FIXME using host floats
|
||||
Ok(Scalar::from_u32(f32::from_bits(op.to_u32()?).sqrt().to_bits()))
|
||||
}
|
||||
FloatUnaryOp::Rcp => {
|
||||
let op = op.to_scalar().to_f32()?;
|
||||
let div = (Single::from_u128(1).value / op).value;
|
||||
// Apply a relative error with a magnitude on the order of 2^-12 to simulate the
|
||||
// inaccuracy of RCP.
|
||||
let res = apply_random_float_error(this, div, -12);
|
||||
Ok(Scalar::from_f32(res))
|
||||
}
|
||||
FloatUnaryOp::Rsqrt => {
|
||||
let op = op.to_scalar().to_u32()?;
|
||||
// FIXME using host floats
|
||||
let sqrt = Single::from_bits(f32::from_bits(op).sqrt().to_bits().into());
|
||||
let rsqrt = (Single::from_u128(1).value / sqrt).value;
|
||||
// Apply a relative error with a magnitude on the order of 2^-12 to simulate the
|
||||
// inaccuracy of RSQRT.
|
||||
let res = apply_random_float_error(this, rsqrt, -12);
|
||||
Ok(Scalar::from_f32(res))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Disturbes a floating-point result by a relative error on the order of (-2^scale, 2^scale).
|
||||
#[allow(clippy::arithmetic_side_effects)] // floating point arithmetic cannot panic
|
||||
fn apply_random_float_error<F: rustc_apfloat::Float>(
|
||||
this: &mut crate::MiriInterpCx<'_, '_>,
|
||||
val: F,
|
||||
err_scale: i32,
|
||||
) -> F {
|
||||
let rng = this.machine.rng.get_mut();
|
||||
// generates rand(0, 2^64) * 2^(scale - 64) = rand(0, 1) * 2^scale
|
||||
let err =
|
||||
F::from_u128(rng.gen::<u64>().into()).value.scalbn(err_scale.checked_sub(64).unwrap());
|
||||
// give it a random sign
|
||||
let err = if rng.gen::<bool>() { -err } else { err };
|
||||
// multiple the value with (1+err)
|
||||
(val * (F::from_u128(1).value + err).value).value
|
||||
}
|
||||
|
||||
/// Performs `which` operation on the first component of `op` and copies
|
||||
/// the other components. The result is stored in `dest`.
|
||||
fn unary_op_ss<'tcx>(
|
||||
this: &mut crate::MiriInterpCx<'_, 'tcx>,
|
||||
which: FloatUnaryOp,
|
||||
op: &OpTy<'tcx, Provenance>,
|
||||
dest: &PlaceTy<'tcx, Provenance>,
|
||||
) -> InterpResult<'tcx, ()> {
|
||||
let (op, op_len) = this.operand_to_simd(op)?;
|
||||
let (dest, dest_len) = this.place_to_simd(dest)?;
|
||||
|
||||
assert_eq!(dest_len, op_len);
|
||||
|
||||
let res0 = unary_op_f32(this, which, &this.read_immediate(&this.project_index(&op, 0)?)?)?;
|
||||
this.write_scalar(res0, &this.project_index(&dest, 0)?)?;
|
||||
|
||||
for i in 1..dest_len {
|
||||
this.copy_op(
|
||||
&this.project_index(&op, i)?,
|
||||
&this.project_index(&dest, i)?,
|
||||
/*allow_transmute*/ false,
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Performs `which` operation on each component of `op`, storing the
|
||||
/// result is stored in `dest`.
|
||||
fn unary_op_ps<'tcx>(
|
||||
this: &mut crate::MiriInterpCx<'_, 'tcx>,
|
||||
which: FloatUnaryOp,
|
||||
op: &OpTy<'tcx, Provenance>,
|
||||
dest: &PlaceTy<'tcx, Provenance>,
|
||||
) -> InterpResult<'tcx, ()> {
|
||||
let (op, op_len) = this.operand_to_simd(op)?;
|
||||
let (dest, dest_len) = this.place_to_simd(dest)?;
|
||||
|
||||
assert_eq!(dest_len, op_len);
|
||||
|
||||
for i in 0..dest_len {
|
||||
let op = this.read_immediate(&this.project_index(&op, i)?)?;
|
||||
let dest = this.project_index(&dest, i)?;
|
||||
|
||||
let res = unary_op_f32(this, which, &op)?;
|
||||
this.write_scalar(res, &dest)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1,8 +1,7 @@
|
||||
use rustc_middle::mir;
|
||||
use rustc_span::Symbol;
|
||||
use rustc_target::abi::Size;
|
||||
use rustc_target::spec::abi::Abi;
|
||||
|
||||
use super::{conditional_dot_product, round_all, round_first, test_bits_masked};
|
||||
use crate::*;
|
||||
use shims::foreign_items::EmulateForeignItemResult;
|
||||
|
||||
@ -104,41 +103,7 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>:
|
||||
let [left, right, imm] =
|
||||
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
|
||||
|
||||
let (left, left_len) = this.operand_to_simd(left)?;
|
||||
let (right, right_len) = this.operand_to_simd(right)?;
|
||||
let (dest, dest_len) = this.place_to_simd(dest)?;
|
||||
|
||||
assert_eq!(left_len, right_len);
|
||||
assert!(dest_len <= 4);
|
||||
|
||||
let imm = this.read_scalar(imm)?.to_u8()?;
|
||||
|
||||
let element_layout = left.layout.field(this, 0);
|
||||
|
||||
// Calculate dot product
|
||||
// Elements are floating point numbers, but we can use `from_int`
|
||||
// because the representation of 0.0 is all zero bits.
|
||||
let mut sum = ImmTy::from_int(0u8, element_layout);
|
||||
for i in 0..left_len {
|
||||
if imm & (1 << i.checked_add(4).unwrap()) != 0 {
|
||||
let left = this.read_immediate(&this.project_index(&left, i)?)?;
|
||||
let right = this.read_immediate(&this.project_index(&right, i)?)?;
|
||||
|
||||
let mul = this.wrapping_binary_op(mir::BinOp::Mul, &left, &right)?;
|
||||
sum = this.wrapping_binary_op(mir::BinOp::Add, &sum, &mul)?;
|
||||
}
|
||||
}
|
||||
|
||||
// Write to destination (conditioned to imm)
|
||||
for i in 0..dest_len {
|
||||
let dest = this.project_index(&dest, i)?;
|
||||
|
||||
if imm & (1 << i) != 0 {
|
||||
this.write_immediate(*sum, &dest)?;
|
||||
} else {
|
||||
this.write_scalar(Scalar::from_int(0u8, element_layout.size), &dest)?;
|
||||
}
|
||||
}
|
||||
conditional_dot_product(this, left, right, imm, dest)?;
|
||||
}
|
||||
// Used to implement the _mm_floor_ss, _mm_ceil_ss and _mm_round_ss
|
||||
// functions. Rounds the first element of `right` according to `rounding`
|
||||
@ -252,117 +217,23 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>:
|
||||
}
|
||||
// Used to implement the _mm_testz_si128, _mm_testc_si128
|
||||
// and _mm_testnzc_si128 functions.
|
||||
// Tests `op & mask == 0`, `op & mask == mask` or
|
||||
// `op & mask != 0 && op & mask != mask`
|
||||
// Tests `(op & mask) == 0`, `(op & mask) == mask` or
|
||||
// `(op & mask) != 0 && (op & mask) != mask`
|
||||
"ptestz" | "ptestc" | "ptestnzc" => {
|
||||
let [op, mask] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
|
||||
|
||||
let (op, op_len) = this.operand_to_simd(op)?;
|
||||
let (mask, mask_len) = this.operand_to_simd(mask)?;
|
||||
|
||||
assert_eq!(op_len, mask_len);
|
||||
|
||||
let f = match unprefixed_name {
|
||||
"ptestz" => |op, mask| op & mask == 0,
|
||||
"ptestc" => |op, mask| op & mask == mask,
|
||||
"ptestnzc" => |op, mask| op & mask != 0 && op & mask != mask,
|
||||
let (all_zero, masked_set) = test_bits_masked(this, op, mask)?;
|
||||
let res = match unprefixed_name {
|
||||
"ptestz" => all_zero,
|
||||
"ptestc" => masked_set,
|
||||
"ptestnzc" => !all_zero && !masked_set,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let mut all_zero = true;
|
||||
for i in 0..op_len {
|
||||
let op = this.read_scalar(&this.project_index(&op, i)?)?.to_u64()?;
|
||||
let mask = this.read_scalar(&this.project_index(&mask, i)?)?.to_u64()?;
|
||||
all_zero &= f(op, mask);
|
||||
}
|
||||
|
||||
this.write_scalar(Scalar::from_i32(all_zero.into()), dest)?;
|
||||
this.write_scalar(Scalar::from_i32(res.into()), dest)?;
|
||||
}
|
||||
_ => return Ok(EmulateForeignItemResult::NotSupported),
|
||||
}
|
||||
Ok(EmulateForeignItemResult::NeedsJumping)
|
||||
}
|
||||
}
|
||||
|
||||
// Rounds the first element of `right` according to `rounding`
|
||||
// and copies the remaining elements from `left`.
|
||||
fn round_first<'tcx, F: rustc_apfloat::Float>(
|
||||
this: &mut crate::MiriInterpCx<'_, 'tcx>,
|
||||
left: &OpTy<'tcx, Provenance>,
|
||||
right: &OpTy<'tcx, Provenance>,
|
||||
rounding: &OpTy<'tcx, Provenance>,
|
||||
dest: &PlaceTy<'tcx, Provenance>,
|
||||
) -> InterpResult<'tcx, ()> {
|
||||
let (left, left_len) = this.operand_to_simd(left)?;
|
||||
let (right, right_len) = this.operand_to_simd(right)?;
|
||||
let (dest, dest_len) = this.place_to_simd(dest)?;
|
||||
|
||||
assert_eq!(dest_len, left_len);
|
||||
assert_eq!(dest_len, right_len);
|
||||
|
||||
let rounding = rounding_from_imm(this.read_scalar(rounding)?.to_i32()?)?;
|
||||
|
||||
let op0: F = this.read_scalar(&this.project_index(&right, 0)?)?.to_float()?;
|
||||
let res = op0.round_to_integral(rounding).value;
|
||||
this.write_scalar(
|
||||
Scalar::from_uint(res.to_bits(), Size::from_bits(F::BITS)),
|
||||
&this.project_index(&dest, 0)?,
|
||||
)?;
|
||||
|
||||
for i in 1..dest_len {
|
||||
this.copy_op(
|
||||
&this.project_index(&left, i)?,
|
||||
&this.project_index(&dest, i)?,
|
||||
/*allow_transmute*/ false,
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Rounds all elements of `op` according to `rounding`.
|
||||
fn round_all<'tcx, F: rustc_apfloat::Float>(
|
||||
this: &mut crate::MiriInterpCx<'_, 'tcx>,
|
||||
op: &OpTy<'tcx, Provenance>,
|
||||
rounding: &OpTy<'tcx, Provenance>,
|
||||
dest: &PlaceTy<'tcx, Provenance>,
|
||||
) -> InterpResult<'tcx, ()> {
|
||||
let (op, op_len) = this.operand_to_simd(op)?;
|
||||
let (dest, dest_len) = this.place_to_simd(dest)?;
|
||||
|
||||
assert_eq!(dest_len, op_len);
|
||||
|
||||
let rounding = rounding_from_imm(this.read_scalar(rounding)?.to_i32()?)?;
|
||||
|
||||
for i in 0..dest_len {
|
||||
let op: F = this.read_scalar(&this.project_index(&op, i)?)?.to_float()?;
|
||||
let res = op.round_to_integral(rounding).value;
|
||||
this.write_scalar(
|
||||
Scalar::from_uint(res.to_bits(), Size::from_bits(F::BITS)),
|
||||
&this.project_index(&dest, i)?,
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Gets equivalent `rustc_apfloat::Round` from rounding mode immediate of
|
||||
/// `round.{ss,sd,ps,pd}` intrinsics.
|
||||
fn rounding_from_imm<'tcx>(rounding: i32) -> InterpResult<'tcx, rustc_apfloat::Round> {
|
||||
// The fourth bit of `rounding` only affects the SSE status
|
||||
// register, which cannot be accessed from Miri (or from Rust,
|
||||
// for that matter), so we can ignore it.
|
||||
match rounding & !0b1000 {
|
||||
// When the third bit is 0, the rounding mode is determined by the
|
||||
// first two bits.
|
||||
0b000 => Ok(rustc_apfloat::Round::NearestTiesToEven),
|
||||
0b001 => Ok(rustc_apfloat::Round::TowardNegative),
|
||||
0b010 => Ok(rustc_apfloat::Round::TowardPositive),
|
||||
0b011 => Ok(rustc_apfloat::Round::TowardZero),
|
||||
// When the third bit is 1, the rounding mode is determined by the
|
||||
// SSE status register. Since we do not support modifying it from
|
||||
// Miri (or Rust), we assume it to be at its default mode (round-to-nearest).
|
||||
0b100..=0b111 => Ok(rustc_apfloat::Round::NearestTiesToEven),
|
||||
rounding => throw_unsup_format!("unsupported rounding mode 0x{rounding:02x}"),
|
||||
}
|
||||
}
|
||||
|
@ -181,7 +181,7 @@ def test_cargo_miri_test():
|
||||
)
|
||||
del os.environ["CARGO_TARGET_DIR"] # this overrides `build.target-dir` passed by `--config`, so unset it
|
||||
test("`cargo miri test` (config-cli)",
|
||||
cargo_miri("test") + ["--config=build.target-dir=\"config-cli\"", "-Zunstable-options"],
|
||||
cargo_miri("test") + ["--config=build.target-dir=\"config-cli\""],
|
||||
default_ref, "test.stderr-empty.ref",
|
||||
env={'MIRIFLAGS': "-Zmiri-permissive-provenance"},
|
||||
)
|
||||
|
@ -2,6 +2,7 @@
|
||||
//@compile-flags: -Zmiri-disable-isolation -Zmiri-permissive-provenance
|
||||
#![feature(strict_provenance)]
|
||||
|
||||
use std::io::Error;
|
||||
use std::{ptr, slice};
|
||||
|
||||
fn test_mmap() {
|
||||
@ -32,6 +33,67 @@ fn test_mmap() {
|
||||
let just_an_address = ptr::invalid_mut(ptr.addr());
|
||||
let res = unsafe { libc::munmap(just_an_address, page_size) };
|
||||
assert_eq!(res, 0i32);
|
||||
|
||||
// Test all of our error conditions
|
||||
let ptr = unsafe {
|
||||
libc::mmap(
|
||||
ptr::null_mut(),
|
||||
page_size,
|
||||
libc::PROT_READ | libc::PROT_WRITE,
|
||||
libc::MAP_PRIVATE | libc::MAP_SHARED, // Can't be both private and shared
|
||||
-1,
|
||||
0,
|
||||
)
|
||||
};
|
||||
assert_eq!(ptr, libc::MAP_FAILED);
|
||||
assert_eq!(Error::last_os_error().raw_os_error().unwrap(), libc::EINVAL);
|
||||
|
||||
let ptr = unsafe {
|
||||
libc::mmap(
|
||||
ptr::null_mut(),
|
||||
0, // Can't map no memory
|
||||
libc::PROT_READ | libc::PROT_WRITE,
|
||||
libc::MAP_PRIVATE | libc::MAP_ANONYMOUS,
|
||||
-1,
|
||||
0,
|
||||
)
|
||||
};
|
||||
assert_eq!(ptr, libc::MAP_FAILED);
|
||||
assert_eq!(Error::last_os_error().raw_os_error().unwrap(), libc::EINVAL);
|
||||
|
||||
let ptr = unsafe {
|
||||
libc::mmap(
|
||||
ptr::invalid_mut(page_size * 64),
|
||||
page_size,
|
||||
libc::PROT_READ | libc::PROT_WRITE,
|
||||
// We don't support MAP_FIXED
|
||||
libc::MAP_PRIVATE | libc::MAP_ANONYMOUS | libc::MAP_FIXED,
|
||||
-1,
|
||||
0,
|
||||
)
|
||||
};
|
||||
assert_eq!(ptr, libc::MAP_FAILED);
|
||||
assert_eq!(Error::last_os_error().raw_os_error().unwrap(), libc::ENOTSUP);
|
||||
|
||||
// We don't support protections other than read+write
|
||||
for prot in [libc::PROT_NONE, libc::PROT_EXEC, libc::PROT_READ, libc::PROT_WRITE] {
|
||||
let ptr = unsafe {
|
||||
libc::mmap(
|
||||
ptr::null_mut(),
|
||||
page_size,
|
||||
prot,
|
||||
libc::MAP_PRIVATE | libc::MAP_ANONYMOUS,
|
||||
-1,
|
||||
0,
|
||||
)
|
||||
};
|
||||
assert_eq!(ptr, libc::MAP_FAILED);
|
||||
assert_eq!(Error::last_os_error().raw_os_error().unwrap(), libc::ENOTSUP);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
@ -61,6 +123,23 @@ fn test_mremap() {
|
||||
|
||||
let res = unsafe { libc::munmap(ptr, page_size * 2) };
|
||||
assert_eq!(res, 0i32);
|
||||
|
||||
// Test all of our error conditions
|
||||
// Not aligned
|
||||
let ptr =
|
||||
unsafe { libc::mremap(ptr::invalid_mut(1), page_size, page_size, libc::MREMAP_MAYMOVE) };
|
||||
assert_eq!(ptr, libc::MAP_FAILED);
|
||||
assert_eq!(Error::last_os_error().raw_os_error().unwrap(), libc::EINVAL);
|
||||
|
||||
// Zero size
|
||||
let ptr = unsafe { libc::mremap(ptr::null_mut(), page_size, 0, libc::MREMAP_MAYMOVE) };
|
||||
assert_eq!(ptr, libc::MAP_FAILED);
|
||||
assert_eq!(Error::last_os_error().raw_os_error().unwrap(), libc::EINVAL);
|
||||
|
||||
// Not setting MREMAP_MAYMOVE
|
||||
let ptr = unsafe { libc::mremap(ptr::null_mut(), page_size, page_size, 0) };
|
||||
assert_eq!(ptr, libc::MAP_FAILED);
|
||||
assert_eq!(Error::last_os_error().raw_os_error().unwrap(), libc::EINVAL);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
@ -1,4 +1,5 @@
|
||||
#![feature(never_type)]
|
||||
#![feature(noop_waker)]
|
||||
|
||||
use std::future::Future;
|
||||
|
||||
@ -57,18 +58,25 @@ async fn hello_world() {
|
||||
read_exact(&mut reader, &mut marker).await.unwrap();
|
||||
}
|
||||
|
||||
fn run_fut<T>(fut: impl Future<Output = T>) -> T {
|
||||
use std::sync::Arc;
|
||||
use std::task::{Context, Poll, Wake, Waker};
|
||||
// This example comes from https://github.com/rust-lang/rust/issues/115145
|
||||
async fn uninhabited_variant() {
|
||||
async fn unreachable(_: Never) {}
|
||||
|
||||
struct MyWaker;
|
||||
impl Wake for MyWaker {
|
||||
fn wake(self: Arc<Self>) {
|
||||
unimplemented!()
|
||||
let c = async {};
|
||||
match None::<Never> {
|
||||
None => {
|
||||
c.await;
|
||||
}
|
||||
Some(r) => {
|
||||
unreachable(r).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let waker = Waker::from(Arc::new(MyWaker));
|
||||
fn run_fut<T>(fut: impl Future<Output = T>) -> T {
|
||||
use std::task::{Context, Poll, Waker};
|
||||
|
||||
let waker = Waker::noop();
|
||||
let mut context = Context::from_waker(&waker);
|
||||
|
||||
let mut pinned = Box::pin(fut);
|
||||
@ -87,4 +95,5 @@ fn main() {
|
||||
assert_eq!(run_fut(includes_never(false, 4)), 16);
|
||||
assert_eq!(run_fut(partial_init(4)), 8);
|
||||
run_fut(hello_world());
|
||||
run_fut(uninhabited_variant());
|
||||
}
|
||||
|
@ -220,7 +220,38 @@ fn smoke_resume_arg() {
|
||||
});
|
||||
}
|
||||
|
||||
fn uninit_fields() {
|
||||
// Test that uninhabited saved local doesn't make the entire variant uninhabited.
|
||||
// (https://github.com/rust-lang/rust/issues/115145, https://github.com/rust-lang/rust/pull/118871)
|
||||
fn conjure<T>() -> T {
|
||||
loop {}
|
||||
}
|
||||
|
||||
fn run<T>(x: bool, y: bool) {
|
||||
let mut c = || {
|
||||
if x {
|
||||
let _a: T;
|
||||
if y {
|
||||
_a = conjure::<T>();
|
||||
}
|
||||
yield ();
|
||||
} else {
|
||||
let _a: T;
|
||||
if y {
|
||||
_a = conjure::<T>();
|
||||
}
|
||||
yield ();
|
||||
}
|
||||
};
|
||||
assert!(matches!(Pin::new(&mut c).resume(()), CoroutineState::Yielded(())));
|
||||
assert!(matches!(Pin::new(&mut c).resume(()), CoroutineState::Complete(())));
|
||||
}
|
||||
|
||||
run::<!>(false, false);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
basic();
|
||||
smoke_resume_arg();
|
||||
uninit_fields();
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
#![feature(dyn_star)]
|
||||
#![allow(incomplete_features)]
|
||||
#![feature(custom_inner_attributes)]
|
||||
#![feature(noop_waker)]
|
||||
// rustfmt destroys `dyn* Trait` syntax
|
||||
#![rustfmt::skip]
|
||||
|
||||
@ -89,25 +90,10 @@ fn dispatch_on_pin_mut() {
|
||||
use std::pin::Pin;
|
||||
use std::task::*;
|
||||
|
||||
pub fn noop_waker() -> Waker {
|
||||
let raw = RawWaker::new(std::ptr::null(), &NOOP_WAKER_VTABLE);
|
||||
|
||||
// SAFETY: the contracts for RawWaker and RawWakerVTable are upheld
|
||||
unsafe { Waker::from_raw(raw) }
|
||||
}
|
||||
|
||||
const NOOP_WAKER_VTABLE: RawWakerVTable = RawWakerVTable::new(noop_clone, noop, noop, noop);
|
||||
|
||||
unsafe fn noop_clone(_p: *const ()) -> RawWaker {
|
||||
RawWaker::new(std::ptr::null(), &NOOP_WAKER_VTABLE)
|
||||
}
|
||||
|
||||
unsafe fn noop(_p: *const ()) {}
|
||||
|
||||
let mut fut = async_main();
|
||||
|
||||
// Poll loop, just to test the future...
|
||||
let waker = noop_waker();
|
||||
let waker = Waker::noop();
|
||||
let ctx = &mut Context::from_waker(&waker);
|
||||
|
||||
loop {
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
static C: Result<(), Box<isize>> = Ok(());
|
||||
|
||||
// This is because of yet another bad assertion (ICE) about the null side of a nullable enum.
|
||||
|
@ -1,5 +1,6 @@
|
||||
//@revisions: stack tree
|
||||
//@[tree]compile-flags: -Zmiri-tree-borrows
|
||||
#![feature(noop_waker)]
|
||||
|
||||
use std::future::*;
|
||||
use std::marker::PhantomPinned;
|
||||
@ -29,19 +30,6 @@ impl Future for Delay {
|
||||
}
|
||||
}
|
||||
|
||||
fn mk_waker() -> Waker {
|
||||
use std::sync::Arc;
|
||||
|
||||
struct MyWaker;
|
||||
impl Wake for MyWaker {
|
||||
fn wake(self: Arc<Self>) {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
Waker::from(Arc::new(MyWaker))
|
||||
}
|
||||
|
||||
async fn do_stuff() {
|
||||
(&mut Delay::new(1)).await;
|
||||
}
|
||||
@ -89,7 +77,7 @@ impl Future for DoStuff {
|
||||
}
|
||||
|
||||
fn run_fut<T>(fut: impl Future<Output = T>) -> T {
|
||||
let waker = mk_waker();
|
||||
let waker = Waker::noop();
|
||||
let mut context = Context::from_waker(&waker);
|
||||
|
||||
let mut pinned = pin!(fut);
|
||||
@ -102,7 +90,7 @@ fn run_fut<T>(fut: impl Future<Output = T>) -> T {
|
||||
}
|
||||
|
||||
fn self_referential_box() {
|
||||
let waker = mk_waker();
|
||||
let waker = Waker::noop();
|
||||
let cx = &mut Context::from_waker(&waker);
|
||||
|
||||
async fn my_fut() -> i32 {
|
||||
|
@ -515,6 +515,11 @@ unsafe fn test_sse41() {
|
||||
let mask = _mm_set1_epi8(0b101);
|
||||
let r = _mm_testnzc_si128(a, mask);
|
||||
assert_eq!(r, 0);
|
||||
|
||||
let a = _mm_setr_epi32(0b100, 0, 0, 0b010);
|
||||
let mask = _mm_setr_epi32(0b100, 0, 0, 0b110);
|
||||
let r = _mm_testnzc_si128(a, mask);
|
||||
assert_eq!(r, 1);
|
||||
}
|
||||
test_mm_testnzc_si128();
|
||||
}
|
||||
|
@ -1,19 +1,13 @@
|
||||
use core::future::Future;
|
||||
use core::pin::Pin;
|
||||
use core::task::{Context, Poll};
|
||||
#![feature(noop_waker)]
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
struct NopWaker;
|
||||
|
||||
impl std::task::Wake for NopWaker {
|
||||
fn wake(self: Arc<Self>) {}
|
||||
}
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll, Waker};
|
||||
|
||||
pub fn fuzzing_block_on<O, F: Future<Output = O>>(fut: F) -> O {
|
||||
let mut fut = std::pin::pin!(fut);
|
||||
let waker = std::task::Waker::from(Arc::new(NopWaker));
|
||||
let mut context = std::task::Context::from_waker(&waker);
|
||||
let waker = Waker::noop();
|
||||
let mut context = Context::from_waker(&waker);
|
||||
loop {
|
||||
match fut.as_mut().poll(&mut context) {
|
||||
Poll::Ready(v) => return v,
|
||||
|
@ -1,3 +1,4 @@
|
||||
#![feature(noop_waker)]
|
||||
use std::future::Future;
|
||||
use std::ptr;
|
||||
|
||||
@ -53,17 +54,9 @@ fn data_moved() {
|
||||
}
|
||||
|
||||
fn run_fut<T>(fut: impl Future<Output = T>) -> T {
|
||||
use std::sync::Arc;
|
||||
use std::task::{Context, Poll, Wake, Waker};
|
||||
use std::task::{Context, Poll, Waker};
|
||||
|
||||
struct MyWaker;
|
||||
impl Wake for MyWaker {
|
||||
fn wake(self: Arc<Self>) {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
let waker = Waker::from(Arc::new(MyWaker));
|
||||
let waker = Waker::noop();
|
||||
let mut context = Context::from_waker(&waker);
|
||||
|
||||
let mut pinned = Box::pin(fut);
|
||||
|
17
src/tools/miri/tests/pass/protector-gc.rs
Normal file
17
src/tools/miri/tests/pass/protector-gc.rs
Normal file
@ -0,0 +1,17 @@
|
||||
// When we pop a stack frame with weak protectors, we need to check if the protected pointer's
|
||||
// allocation is still live. If the provenance GC only knows about the BorTag that is protected,
|
||||
// we can ICE. This test checks that we don't.
|
||||
// See https://github.com/rust-lang/miri/issues/3228
|
||||
|
||||
#[path = "../utils/mod.rs"]
|
||||
mod utils;
|
||||
|
||||
#[allow(unused)]
|
||||
fn oof(mut b: Box<u8>) {
|
||||
b = Box::new(0u8);
|
||||
utils::run_provenance_gc();
|
||||
}
|
||||
|
||||
fn main() {
|
||||
oof(Box::new(0u8));
|
||||
}
|
Loading…
Reference in New Issue
Block a user