Auto merge of #126193 - RalfJung:miri-sync, r=RalfJung

Miri subtree update

r? `@ghost`
This commit is contained in:
bors 2024-06-09 14:37:00 +00:00
commit 7bb0ef4902
89 changed files with 1504 additions and 1091 deletions

View file

@ -223,7 +223,7 @@ will eventually sync those changes back into this repository.
When working on Miri in the rustc tree, here's how you can run tests:
```
./x.py test miri --stage 0
./x.py test miri
```
`--bless` will work, too.
@ -231,7 +231,7 @@ When working on Miri in the rustc tree, here's how you can run tests:
You can also directly run Miri on a Rust source file:
```
./x.py run miri --stage 0 --args src/tools/miri/tests/pass/hello.rs
./x.py run miri --stage 1 --args src/tools/miri/tests/pass/hello.rs
```
## Advanced topic: Syncing with the rustc repo
@ -287,7 +287,22 @@ https. Add the following to your `.gitconfig`:
pushInsteadOf = https://github.com/
```
## Internal environment variables
## Further environment variables
The following environment variables are relevant to `./miri`:
* `MIRI_AUTO_OPS` indicates whether the automatic execution of rustfmt, clippy and toolchain setup
(as controlled by the `./auto-*` files) should be skipped. If it is set to `no`, they are skipped.
This is used to allow automated IDE actions to avoid the auto ops.
* `MIRI_LOG`, `MIRI_BACKTRACE` control logging and backtrace printing during Miri executions.
* `MIRI_TEST_THREADS` (recognized by `./miri test`) sets the number of threads to use for running
tests. By default, the number of cores is used.
* `MIRI_SKIP_UI_CHECKS` (recognized by `./miri test`) disables checking that the `stderr` or
`stdout` files match the actual output.
Furthermore, the usual environment variables recognized by `cargo miri` also work for `./miri`, e.g.
`MIRI_LIB_SRC`. Note that `MIRIFLAGS` is ignored by `./miri test` as each test controls the flags it
is run with.
The following environment variables are *internal* and must not be used by
anyone but Miri itself. They are used to communicate between different Miri

View file

@ -448,28 +448,19 @@ Some native rustc `-Z` flags are also very relevant for Miri:
* `-Zmir-emit-retag` controls whether `Retag` statements are emitted. Miri
enables this per default because it is needed for [Stacked Borrows] and [Tree Borrows].
Moreover, Miri recognizes some environment variables (unless noted otherwise, these are supported
by all intended entry points, i.e. `cargo miri` and `./miri {test,run}`):
Moreover, Miri recognizes some environment variables:
* `MIRI_AUTO_OPS` indicates whether the automatic execution of rustfmt, clippy and toolchain setup
should be skipped. If it is set to `no`, they are skipped. This is used to allow automated IDE
actions to avoid the auto ops.
* `MIRI_LOG`, `MIRI_BACKTRACE` control logging and backtrace printing during
Miri executions, also [see "Testing the Miri driver" in `CONTRIBUTING.md`][testing-miri].
* `MIRIFLAGS` defines extra flags to be passed to Miri.
* `MIRI_LIB_SRC` defines the directory where Miri expects the sources of the standard library that
it will build and use for interpretation. This directory must point to the `library` subdirectory
of a `rust-lang/rust` repository checkout.
* `MIRI_SYSROOT` indicates the sysroot to use. When using `cargo miri`, this skips the automatic
* `MIRI_SYSROOT` indicates the sysroot to use. When using `cargo miri test`/`cargo miri run`, this skips the automatic
setup -- only set this if you do not want to use the automatically created sysroot. When invoking
`cargo miri setup`, this indicates where the sysroot will be put.
* `MIRI_TEST_THREADS` (recognized by `./miri test`): set the number of threads to use for running tests.
By default, the number of cores is used.
* `MIRI_NO_STD` makes sure that the target's sysroot is built without libstd. This allows testing
and running no_std programs. (Miri has a heuristic to detect no-std targets based on the target
name; this environment variable is only needed when that heuristic fails.)
* `MIRI_SKIP_UI_CHECKS` (recognized by `./miri test`): don't check whether the
`stderr` or `stdout` files match the actual output.
and running no_std programs. This should *not usually be used*; Miri has a heuristic to detect
no-std targets based on the target name. Setting this on a target that does support libstd can
lead to confusing results.
[testing-miri]: CONTRIBUTING.md#testing-the-miri-driver

View file

@ -0,0 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "big-allocs"
version = "0.1.0"

View file

@ -0,0 +1,8 @@
[package]
name = "big-allocs"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

View file

@ -0,0 +1,13 @@
//! This is a regression test for https://github.com/rust-lang/miri/issues/3637.
//! `Allocation`s are backed by a `Box<[u8]>`, which we create using `alloc_zeroed`, which should
//! make very large allocations cheap. But then we also need to not clone those `Allocation`s, or
//! we end up slow anyway.
fn main() {
// We can't use too big of an allocation or this code will encounter an allocation failure in
// CI. Since the allocation can't be huge, we need to do a few iterations so that the effect
// we're trying to measure is clearly visible above the interpreter's startup time.
for _ in 0..10 {
drop(Vec::<u8>::with_capacity(512 * 1024 * 1024));
}
}

View file

@ -1,15 +1,14 @@
#![allow(clippy::useless_format, clippy::derive_partial_eq_without_eq, rustc::internal)]
#[macro_use]
mod util;
mod arg;
mod phases;
mod setup;
mod util;
use std::{env, iter};
use crate::phases::*;
use crate::util::show_error;
/// Returns `true` if our flags look like they may be for rustdoc, i.e., this is cargo calling us to
/// be rustdoc. It's hard to be sure as cargo does not have a RUSTDOC_WRAPPER or an env var that

View file

@ -1,3 +1,4 @@
use std::collections::HashMap;
use std::env;
use std::ffi::OsString;
use std::fs::File;
@ -11,14 +12,15 @@ use serde::{Deserialize, Serialize};
pub use crate::arg::*;
pub fn show_error(msg: &impl std::fmt::Display) -> ! {
pub fn show_error_(msg: &impl std::fmt::Display) -> ! {
eprintln!("fatal error: {msg}");
std::process::exit(1)
}
macro_rules! show_error {
($($tt:tt)*) => { crate::util::show_error(&format_args!($($tt)*)) };
($($tt:tt)*) => { crate::util::show_error_(&format_args!($($tt)*)) };
}
pub(crate) use show_error;
/// The information to run a crate with the given environment.
#[derive(Clone, Serialize, Deserialize)]
@ -232,21 +234,18 @@ pub fn get_cargo_metadata() -> Metadata {
}
/// Pulls all the crates in this workspace from the cargo metadata.
/// Workspace members are emitted like "miri 0.1.0 (path+file:///path/to/miri)"
/// Additionally, somewhere between cargo metadata and TyCtxt, '-' gets replaced with '_' so we
/// make that same transformation here.
pub fn local_crates(metadata: &Metadata) -> String {
assert!(!metadata.workspace_members.is_empty());
let mut local_crates = String::new();
for member in &metadata.workspace_members {
let name = member.repr.split(' ').next().unwrap();
let name = name.replace('-', "_");
local_crates.push_str(&name);
local_crates.push(',');
}
local_crates.pop(); // Remove the trailing ','
local_crates
let package_name_by_id: HashMap<_, _> =
metadata.packages.iter().map(|package| (&package.id, package.name.as_str())).collect();
metadata
.workspace_members
.iter()
.map(|id| package_name_by_id[id].replace('-', "_"))
.collect::<Vec<_>>()
.join(",")
}
/// Debug-print a command that is going to be run.

View file

@ -148,8 +148,8 @@ case $HOST_TARGET in
UNIX="panic/panic panic/unwind concurrency/simple atomic libc-mem libc-misc libc-random env num_cpus" # the things that are very similar across all Unixes, and hence easily supported there
TEST_TARGET=x86_64-unknown-freebsd run_tests_minimal $BASIC $UNIX threadname libc-time fs
TEST_TARGET=i686-unknown-freebsd run_tests_minimal $BASIC $UNIX threadname libc-time fs
TEST_TARGET=x86_64-unknown-illumos run_tests_minimal $BASIC $UNIX threadname pthread-sync libc-time
TEST_TARGET=x86_64-pc-solaris run_tests_minimal $BASIC $UNIX threadname pthread-sync libc-time
TEST_TARGET=x86_64-unknown-illumos run_tests_minimal $BASIC $UNIX threadname pthread-sync available-parallelism libc-time
TEST_TARGET=x86_64-pc-solaris run_tests_minimal $BASIC $UNIX threadname pthread-sync available-parallelism libc-time
TEST_TARGET=aarch64-linux-android run_tests_minimal $BASIC $UNIX
TEST_TARGET=wasm32-wasip2 run_tests_minimal empty_main wasm heap_alloc libc-mem
TEST_TARGET=wasm32-unknown-unknown run_tests_minimal empty_main wasm

View file

@ -1 +1 @@
21e6de7eb64c09102de3f100420a09edc1a2a8d7
565cadb514d35e7b851540edbc172af0f606014f

View file

@ -257,7 +257,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
Ok(())
}
fn ptr_from_addr_cast(&self, addr: u64) -> InterpResult<'tcx, Pointer<Option<Provenance>>> {
fn ptr_from_addr_cast(&self, addr: u64) -> InterpResult<'tcx, Pointer> {
trace!("Casting {:#x} to a pointer", addr);
let ecx = self.eval_context_ref();
@ -297,10 +297,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
/// Convert a relative (tcx) pointer to a Miri pointer.
fn adjust_alloc_root_pointer(
&self,
ptr: Pointer<CtfeProvenance>,
ptr: interpret::Pointer<CtfeProvenance>,
tag: BorTag,
kind: MemoryKind,
) -> InterpResult<'tcx, Pointer<Provenance>> {
) -> InterpResult<'tcx, interpret::Pointer<Provenance>> {
let ecx = self.eval_context_ref();
let (prov, offset) = ptr.into_parts(); // offset is relative (AllocId provenance)
@ -310,12 +310,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Add offset with the right kind of pointer-overflowing arithmetic.
let dl = ecx.data_layout();
let absolute_addr = dl.overflowing_offset(base_addr, offset.bytes()).0;
Ok(Pointer::new(Provenance::Concrete { alloc_id, tag }, Size::from_bytes(absolute_addr)))
Ok(interpret::Pointer::new(
Provenance::Concrete { alloc_id, tag },
Size::from_bytes(absolute_addr),
))
}
/// When a pointer is used for a memory access, this computes where in which allocation the
/// access is going.
fn ptr_get_alloc(&self, ptr: Pointer<Provenance>) -> Option<(AllocId, Size)> {
fn ptr_get_alloc(&self, ptr: interpret::Pointer<Provenance>) -> Option<(AllocId, Size)> {
let ecx = self.eval_context_ref();
let (tag, addr) = ptr.into_parts(); // addr is absolute (Tag provenance)

View file

@ -8,7 +8,6 @@
)]
// Some "regular" crates we want to share with rustc
#[macro_use]
extern crate tracing;
// The rustc crates we need
@ -26,6 +25,8 @@ use std::num::NonZero;
use std::path::PathBuf;
use std::str::FromStr;
use tracing::debug;
use rustc_data_structures::sync::Lrc;
use rustc_driver::Compilation;
use rustc_hir::{self as hir, Node};

View file

@ -281,8 +281,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn retag_ptr_value(
&mut self,
kind: RetagKind,
val: &ImmTy<'tcx, Provenance>,
) -> InterpResult<'tcx, ImmTy<'tcx, Provenance>> {
val: &ImmTy<'tcx>,
) -> InterpResult<'tcx, ImmTy<'tcx>> {
let this = self.eval_context_mut();
let method = this.machine.borrow_tracker.as_ref().unwrap().borrow().borrow_tracker_method;
match method {
@ -294,7 +294,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn retag_place_contents(
&mut self,
kind: RetagKind,
place: &PlaceTy<'tcx, Provenance>,
place: &PlaceTy<'tcx>,
) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
let method = this.machine.borrow_tracker.as_ref().unwrap().borrow().borrow_tracker_method;
@ -304,10 +304,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
}
fn protect_place(
&mut self,
place: &MPlaceTy<'tcx, Provenance>,
) -> InterpResult<'tcx, MPlaceTy<'tcx, Provenance>> {
fn protect_place(&mut self, place: &MPlaceTy<'tcx>) -> InterpResult<'tcx, MPlaceTy<'tcx>> {
let this = self.eval_context_mut();
let method = this.machine.borrow_tracker.as_ref().unwrap().borrow().borrow_tracker_method;
match method {
@ -327,7 +324,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn give_pointer_debug_name(
&mut self,
ptr: Pointer<Option<Provenance>>,
ptr: Pointer,
nth_parent: u8,
name: &str,
) -> InterpResult<'tcx> {

View file

@ -149,11 +149,7 @@ impl<'ecx, 'tcx> DiagnosticCxBuilder<'ecx, 'tcx> {
DiagnosticCxBuilder { machine, operation }
}
pub fn read(
machine: &'ecx MiriMachine<'tcx>,
tag: ProvenanceExtra,
range: AllocRange,
) -> Self {
pub fn read(machine: &'ecx MiriMachine<'tcx>, tag: ProvenanceExtra, range: AllocRange) -> Self {
let operation = Operation::Access(AccessOp { kind: AccessKind::Read, tag, range });
DiagnosticCxBuilder { machine, operation }
}

View file

@ -531,7 +531,7 @@ impl Stacks {
trace!(
"read access with tag {:?}: {:?}, size {}",
tag,
Pointer::new(alloc_id, range.start),
interpret::Pointer::new(alloc_id, range.start),
range.size.bytes()
);
let dcx = DiagnosticCxBuilder::read(machine, tag, range);
@ -552,7 +552,7 @@ impl Stacks {
trace!(
"write access with tag {:?}: {:?}, size {}",
tag,
Pointer::new(alloc_id, range.start),
interpret::Pointer::new(alloc_id, range.start),
range.size.bytes()
);
let dcx = DiagnosticCxBuilder::write(machine, tag, range);
@ -587,7 +587,7 @@ trait EvalContextPrivExt<'tcx, 'ecx>: crate::MiriInterpCxExt<'tcx> {
/// Returns the provenance that should be used henceforth.
fn sb_reborrow(
&mut self,
place: &MPlaceTy<'tcx, Provenance>,
place: &MPlaceTy<'tcx>,
size: Size,
new_perm: NewPermission,
new_tag: BorTag,
@ -692,7 +692,7 @@ trait EvalContextPrivExt<'tcx, 'ecx>: crate::MiriInterpCxExt<'tcx> {
new_tag,
orig_tag,
place.layout.ty,
Pointer::new(alloc_id, base_offset),
interpret::Pointer::new(alloc_id, base_offset),
size.bytes()
);
@ -809,10 +809,10 @@ trait EvalContextPrivExt<'tcx, 'ecx>: crate::MiriInterpCxExt<'tcx> {
fn sb_retag_place(
&mut self,
place: &MPlaceTy<'tcx, Provenance>,
place: &MPlaceTy<'tcx>,
new_perm: NewPermission,
info: RetagInfo, // diagnostics info about this retag
) -> InterpResult<'tcx, MPlaceTy<'tcx, Provenance>> {
) -> InterpResult<'tcx, MPlaceTy<'tcx>> {
let this = self.eval_context_mut();
let size = this.size_and_align_of_mplace(place)?.map(|(size, _)| size);
// FIXME: If we cannot determine the size (because the unsized tail is an `extern type`),
@ -839,10 +839,10 @@ trait EvalContextPrivExt<'tcx, 'ecx>: crate::MiriInterpCxExt<'tcx> {
/// `kind` indicates what kind of reference is being created.
fn sb_retag_reference(
&mut self,
val: &ImmTy<'tcx, Provenance>,
val: &ImmTy<'tcx>,
new_perm: NewPermission,
info: RetagInfo, // diagnostics info about this retag
) -> InterpResult<'tcx, ImmTy<'tcx, Provenance>> {
) -> InterpResult<'tcx, ImmTy<'tcx>> {
let this = self.eval_context_mut();
let place = this.ref_to_mplace(val)?;
let new_place = this.sb_retag_place(&place, new_perm, info)?;
@ -855,8 +855,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn sb_retag_ptr_value(
&mut self,
kind: RetagKind,
val: &ImmTy<'tcx, Provenance>,
) -> InterpResult<'tcx, ImmTy<'tcx, Provenance>> {
val: &ImmTy<'tcx>,
) -> InterpResult<'tcx, ImmTy<'tcx>> {
let this = self.eval_context_mut();
let new_perm = NewPermission::from_ref_ty(val.layout.ty, kind, this);
let cause = match kind {
@ -870,7 +870,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn sb_retag_place_contents(
&mut self,
kind: RetagKind,
place: &PlaceTy<'tcx, Provenance>,
place: &PlaceTy<'tcx>,
) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
let retag_fields = this.machine.borrow_tracker.as_mut().unwrap().get_mut().retag_fields;
@ -895,7 +895,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
#[inline(always)] // yes this helps in our benchmarks
fn retag_ptr_inplace(
&mut self,
place: &PlaceTy<'tcx, Provenance>,
place: &PlaceTy<'tcx>,
new_perm: NewPermission,
) -> InterpResult<'tcx> {
let val = self.ecx.read_immediate(&self.ecx.place_to_op(place)?)?;
@ -909,18 +909,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
}
impl<'ecx, 'tcx> ValueVisitor<'tcx, MiriMachine<'tcx>> for RetagVisitor<'ecx, 'tcx> {
type V = PlaceTy<'tcx, Provenance>;
type V = PlaceTy<'tcx>;
#[inline(always)]
fn ecx(&self) -> &MiriInterpCx<'tcx> {
self.ecx
}
fn visit_box(
&mut self,
box_ty: Ty<'tcx>,
place: &PlaceTy<'tcx, Provenance>,
) -> InterpResult<'tcx> {
fn visit_box(&mut self, box_ty: Ty<'tcx>, place: &PlaceTy<'tcx>) -> InterpResult<'tcx> {
// Only boxes for the global allocator get any special treatment.
if box_ty.is_box_global(*self.ecx.tcx) {
// Boxes get a weak protectors, since they may be deallocated.
@ -930,7 +926,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
Ok(())
}
fn visit_value(&mut self, place: &PlaceTy<'tcx, Provenance>) -> InterpResult<'tcx> {
fn visit_value(&mut self, place: &PlaceTy<'tcx>) -> InterpResult<'tcx> {
// If this place is smaller than a pointer, we know that it can't contain any
// pointers we need to retag, so we can stop recursion early.
// This optimization is crucial for ZSTs, because they can contain way more fields
@ -984,10 +980,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
/// call.
///
/// This is used to ensure soundness of in-place function argument/return passing.
fn sb_protect_place(
&mut self,
place: &MPlaceTy<'tcx, Provenance>,
) -> InterpResult<'tcx, MPlaceTy<'tcx, Provenance>> {
fn sb_protect_place(&mut self, place: &MPlaceTy<'tcx>) -> InterpResult<'tcx, MPlaceTy<'tcx>> {
let this = self.eval_context_mut();
// Retag it. With protection! That is the entire point.

View file

@ -2,6 +2,7 @@
use std::ops::Range;
use rustc_data_structures::fx::FxHashSet;
use tracing::trace;
use crate::borrow_tracker::{
stacked_borrows::{Item, Permission},

View file

@ -56,7 +56,7 @@ impl<'tcx> Tree {
"{} with tag {:?}: {:?}, size {}",
access_kind,
prov,
Pointer::new(alloc_id, range.start),
interpret::Pointer::new(alloc_id, range.start),
range.size.bytes(),
);
// TODO: for now we bail out on wildcard pointers. Eventually we should
@ -195,7 +195,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
/// Returns the provenance that should be used henceforth.
fn tb_reborrow(
&mut self,
place: &MPlaceTy<'tcx, Provenance>, // parent tag extracted from here
place: &MPlaceTy<'tcx>, // parent tag extracted from here
ptr_size: Size,
new_perm: NewPermission,
new_tag: BorTag,
@ -258,7 +258,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
new_tag,
orig_tag,
place.layout.ty,
Pointer::new(alloc_id, base_offset),
interpret::Pointer::new(alloc_id, base_offset),
ptr_size.bytes()
);
@ -327,9 +327,9 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn tb_retag_place(
&mut self,
place: &MPlaceTy<'tcx, Provenance>,
place: &MPlaceTy<'tcx>,
new_perm: NewPermission,
) -> InterpResult<'tcx, MPlaceTy<'tcx, Provenance>> {
) -> InterpResult<'tcx, MPlaceTy<'tcx>> {
let this = self.eval_context_mut();
// Determine the size of the reborrow.
@ -366,9 +366,9 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
/// Retags an individual pointer, returning the retagged version.
fn tb_retag_reference(
&mut self,
val: &ImmTy<'tcx, Provenance>,
val: &ImmTy<'tcx>,
new_perm: NewPermission,
) -> InterpResult<'tcx, ImmTy<'tcx, Provenance>> {
) -> InterpResult<'tcx, ImmTy<'tcx>> {
let this = self.eval_context_mut();
let place = this.ref_to_mplace(val)?;
let new_place = this.tb_retag_place(&place, new_perm)?;
@ -383,8 +383,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn tb_retag_ptr_value(
&mut self,
kind: RetagKind,
val: &ImmTy<'tcx, Provenance>,
) -> InterpResult<'tcx, ImmTy<'tcx, Provenance>> {
val: &ImmTy<'tcx>,
) -> InterpResult<'tcx, ImmTy<'tcx>> {
let this = self.eval_context_mut();
let new_perm = match val.layout.ty.kind() {
&ty::Ref(_, pointee, mutability) =>
@ -402,7 +402,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn tb_retag_place_contents(
&mut self,
kind: RetagKind,
place: &PlaceTy<'tcx, Provenance>,
place: &PlaceTy<'tcx>,
) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
let options = this.machine.borrow_tracker.as_mut().unwrap().get_mut();
@ -423,7 +423,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
#[inline(always)] // yes this helps in our benchmarks
fn retag_ptr_inplace(
&mut self,
place: &PlaceTy<'tcx, Provenance>,
place: &PlaceTy<'tcx>,
new_perm: Option<NewPermission>,
) -> InterpResult<'tcx> {
if let Some(new_perm) = new_perm {
@ -435,7 +435,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
}
impl<'ecx, 'tcx> ValueVisitor<'tcx, MiriMachine<'tcx>> for RetagVisitor<'ecx, 'tcx> {
type V = PlaceTy<'tcx, Provenance>;
type V = PlaceTy<'tcx>;
#[inline(always)]
fn ecx(&self) -> &MiriInterpCx<'tcx> {
@ -445,11 +445,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
/// Regardless of how `Unique` is handled, Boxes are always reborrowed.
/// When `Unique` is also reborrowed, then it behaves exactly like `Box`
/// except for the fact that `Box` has a non-zero-sized reborrow.
fn visit_box(
&mut self,
box_ty: Ty<'tcx>,
place: &PlaceTy<'tcx, Provenance>,
) -> InterpResult<'tcx> {
fn visit_box(&mut self, box_ty: Ty<'tcx>, place: &PlaceTy<'tcx>) -> InterpResult<'tcx> {
// Only boxes for the global allocator get any special treatment.
if box_ty.is_box_global(*self.ecx.tcx) {
let new_perm = NewPermission::from_unique_ty(
@ -463,7 +459,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
Ok(())
}
fn visit_value(&mut self, place: &PlaceTy<'tcx, Provenance>) -> InterpResult<'tcx> {
fn visit_value(&mut self, place: &PlaceTy<'tcx>) -> InterpResult<'tcx> {
// If this place is smaller than a pointer, we know that it can't contain any
// pointers we need to retag, so we can stop recursion early.
// This optimization is crucial for ZSTs, because they can contain way more fields
@ -526,10 +522,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
/// call.
///
/// This is used to ensure soundness of in-place function argument/return passing.
fn tb_protect_place(
&mut self,
place: &MPlaceTy<'tcx, Provenance>,
) -> InterpResult<'tcx, MPlaceTy<'tcx, Provenance>> {
fn tb_protect_place(&mut self, place: &MPlaceTy<'tcx>) -> InterpResult<'tcx, MPlaceTy<'tcx>> {
let this = self.eval_context_mut();
// Retag it. With protection! That is the entire point.
@ -581,7 +574,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
/// of `ptr` (with 0 representing `ptr` itself)
fn tb_give_pointer_debug_name(
&mut self,
ptr: Pointer<Option<Provenance>>,
ptr: Pointer,
nth_parent: u8,
name: &str,
) -> InterpResult<'tcx> {
@ -604,8 +597,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
/// and output can be used by `retag_ptr_inplace`.
fn inner_ptr_of_unique<'tcx>(
ecx: &MiriInterpCx<'tcx>,
place: &PlaceTy<'tcx, Provenance>,
) -> InterpResult<'tcx, PlaceTy<'tcx, Provenance>> {
place: &PlaceTy<'tcx>,
) -> InterpResult<'tcx, PlaceTy<'tcx>> {
// Follows the same layout as `interpret/visitor.rs:walk_value` for `Box` in
// `rustc_const_eval`, just with one fewer layer.
// Here we have a `Unique(NonNull(*mut), PhantomData)`

View file

@ -6,7 +6,7 @@ use std::time::{Duration, Instant as StdInstant};
/// This number is pretty random, but it has been shown to approximately cause
/// some sample programs to run within an order of magnitude of real time on desktop CPUs.
/// (See `tests/pass/shims/time-with-isolation*.rs`.)
const NANOSECONDS_PER_BASIC_BLOCK: u64 = 5000;
const NANOSECONDS_PER_BASIC_BLOCK: u128 = 5000;
#[derive(Debug)]
pub struct Instant {
@ -16,19 +16,24 @@ pub struct Instant {
#[derive(Debug)]
enum InstantKind {
Host(StdInstant),
Virtual { nanoseconds: u64 },
Virtual { nanoseconds: u128 },
}
impl Instant {
pub fn checked_add(&self, duration: Duration) -> Option<Instant> {
/// Will try to add `duration`, but if that overflows it may add less.
pub fn add_lossy(&self, duration: Duration) -> Instant {
match self.kind {
InstantKind::Host(instant) =>
instant.checked_add(duration).map(|i| Instant { kind: InstantKind::Host(i) }),
InstantKind::Virtual { nanoseconds } =>
u128::from(nanoseconds)
.checked_add(duration.as_nanos())
.and_then(|n| u64::try_from(n).ok())
.map(|nanoseconds| Instant { kind: InstantKind::Virtual { nanoseconds } }),
InstantKind::Host(instant) => {
// If this overflows, try adding just 1h and assume that will not overflow.
let i = instant
.checked_add(duration)
.unwrap_or_else(|| instant.checked_add(Duration::from_secs(3600)).unwrap());
Instant { kind: InstantKind::Host(i) }
}
InstantKind::Virtual { nanoseconds } => {
let n = nanoseconds.saturating_add(duration.as_nanos());
Instant { kind: InstantKind::Virtual { nanoseconds: n } }
}
}
}
@ -39,7 +44,17 @@ impl Instant {
(
InstantKind::Virtual { nanoseconds },
InstantKind::Virtual { nanoseconds: earlier },
) => Duration::from_nanos(nanoseconds.saturating_sub(earlier)),
) => {
let duration = nanoseconds.saturating_sub(earlier);
// `Duration` does not provide a nice constructor from a `u128` of nanoseconds,
// so we have to implement this ourselves.
// It is possible for second to overflow because u64::MAX < (u128::MAX / 1e9).
// It will be saturated to u64::MAX seconds if the value after division exceeds u64::MAX.
let seconds = u64::try_from(duration / 1_000_000_000).unwrap_or(u64::MAX);
// It is impossible for nanosecond to overflow because u32::MAX > 1e9.
let nanosecond = u32::try_from(duration.wrapping_rem(1_000_000_000)).unwrap();
Duration::new(seconds, nanosecond)
}
_ => panic!("all `Instant` must be of the same kind"),
}
}
@ -54,12 +69,13 @@ pub struct Clock {
#[derive(Debug)]
enum ClockKind {
Host {
/// The "time anchor" for this machine's monotone clock.
time_anchor: StdInstant,
/// The "epoch" for this machine's monotone clock:
/// the moment we consider to be time = 0.
epoch: StdInstant,
},
Virtual {
/// The "current virtual time".
nanoseconds: Cell<u64>,
nanoseconds: Cell<u128>,
},
}
@ -67,7 +83,7 @@ impl Clock {
/// Create a new clock based on the availability of communication with the host.
pub fn new(communicate: bool) -> Self {
let kind = if communicate {
ClockKind::Host { time_anchor: StdInstant::now() }
ClockKind::Host { epoch: StdInstant::now() }
} else {
ClockKind::Virtual { nanoseconds: 0.into() }
};
@ -93,16 +109,19 @@ impl Clock {
ClockKind::Host { .. } => std::thread::sleep(duration),
ClockKind::Virtual { nanoseconds } => {
// Just pretend that we have slept for some time.
let nanos: u64 = duration.as_nanos().try_into().unwrap();
nanoseconds.update(|x| x + nanos);
let nanos: u128 = duration.as_nanos();
nanoseconds.update(|x| {
x.checked_add(nanos)
.expect("Miri's virtual clock cannot represent an execution this long")
});
}
}
}
/// Return the `anchor` instant, to convert between monotone instants and durations relative to the anchor.
pub fn anchor(&self) -> Instant {
/// Return the `epoch` instant (time = 0), to convert between monotone instants and absolute durations.
pub fn epoch(&self) -> Instant {
match &self.kind {
ClockKind::Host { time_anchor } => Instant { kind: InstantKind::Host(*time_anchor) },
ClockKind::Host { epoch } => Instant { kind: InstantKind::Host(*epoch) },
ClockKind::Virtual { .. } => Instant { kind: InstantKind::Virtual { nanoseconds: 0 } },
}
}

View file

@ -606,9 +606,9 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> {
/// Perform an atomic read operation at the memory location.
fn read_scalar_atomic(
&self,
place: &MPlaceTy<'tcx, Provenance>,
place: &MPlaceTy<'tcx>,
atomic: AtomicReadOrd,
) -> InterpResult<'tcx, Scalar<Provenance>> {
) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_ref();
this.atomic_access_check(place, AtomicAccessType::Load(atomic))?;
// This will read from the last store in the modification order of this location. In case
@ -625,8 +625,8 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> {
/// Perform an atomic write operation at the memory location.
fn write_scalar_atomic(
&mut self,
val: Scalar<Provenance>,
dest: &MPlaceTy<'tcx, Provenance>,
val: Scalar,
dest: &MPlaceTy<'tcx>,
atomic: AtomicWriteOrd,
) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
@ -645,12 +645,12 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> {
/// Perform an atomic RMW operation on a memory location.
fn atomic_rmw_op_immediate(
&mut self,
place: &MPlaceTy<'tcx, Provenance>,
rhs: &ImmTy<'tcx, Provenance>,
place: &MPlaceTy<'tcx>,
rhs: &ImmTy<'tcx>,
op: mir::BinOp,
not: bool,
atomic: AtomicRwOrd,
) -> InterpResult<'tcx, ImmTy<'tcx, Provenance>> {
) -> InterpResult<'tcx, ImmTy<'tcx>> {
let this = self.eval_context_mut();
this.atomic_access_check(place, AtomicAccessType::Rmw)?;
@ -670,10 +670,10 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> {
/// scalar value, the old value is returned.
fn atomic_exchange_scalar(
&mut self,
place: &MPlaceTy<'tcx, Provenance>,
new: Scalar<Provenance>,
place: &MPlaceTy<'tcx>,
new: Scalar,
atomic: AtomicRwOrd,
) -> InterpResult<'tcx, Scalar<Provenance>> {
) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();
this.atomic_access_check(place, AtomicAccessType::Rmw)?;
@ -690,11 +690,11 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> {
/// scalar value, the old value is returned.
fn atomic_min_max_scalar(
&mut self,
place: &MPlaceTy<'tcx, Provenance>,
rhs: ImmTy<'tcx, Provenance>,
place: &MPlaceTy<'tcx>,
rhs: ImmTy<'tcx>,
min: bool,
atomic: AtomicRwOrd,
) -> InterpResult<'tcx, ImmTy<'tcx, Provenance>> {
) -> InterpResult<'tcx, ImmTy<'tcx>> {
let this = self.eval_context_mut();
this.atomic_access_check(place, AtomicAccessType::Rmw)?;
@ -726,9 +726,9 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> {
/// identical.
fn atomic_compare_exchange_scalar(
&mut self,
place: &MPlaceTy<'tcx, Provenance>,
expect_old: &ImmTy<'tcx, Provenance>,
new: Scalar<Provenance>,
place: &MPlaceTy<'tcx>,
expect_old: &ImmTy<'tcx>,
new: Scalar,
success: AtomicRwOrd,
fail: AtomicReadOrd,
can_fail_spuriously: bool,
@ -948,7 +948,7 @@ impl VClockAlloc {
mem_clocks: &MemoryCellClocks,
access: AccessType,
access_size: Size,
ptr_dbg: Pointer<AllocId>,
ptr_dbg: interpret::Pointer<AllocId>,
ty: Option<Ty<'_>>,
) -> InterpResult<'tcx> {
let (active_index, active_clocks) = global.active_thread_state(thread_mgr);
@ -1063,7 +1063,7 @@ impl VClockAlloc {
mem_clocks,
AccessType::NaRead(read_type),
access_range.size,
Pointer::new(alloc_id, Size::from_bytes(mem_clocks_range.start)),
interpret::Pointer::new(alloc_id, Size::from_bytes(mem_clocks_range.start)),
ty,
);
}
@ -1108,7 +1108,7 @@ impl VClockAlloc {
mem_clocks,
AccessType::NaWrite(write_type),
access_range.size,
Pointer::new(alloc_id, Size::from_bytes(mem_clocks_range.start)),
interpret::Pointer::new(alloc_id, Size::from_bytes(mem_clocks_range.start)),
ty,
);
}
@ -1163,7 +1163,7 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> {
/// Checks that an atomic access is legal at the given place.
fn atomic_access_check(
&self,
place: &MPlaceTy<'tcx, Provenance>,
place: &MPlaceTy<'tcx>,
access_type: AtomicAccessType,
) -> InterpResult<'tcx> {
let this = self.eval_context_ref();
@ -1219,7 +1219,7 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> {
/// associated memory-place and on the current thread.
fn validate_atomic_load(
&self,
place: &MPlaceTy<'tcx, Provenance>,
place: &MPlaceTy<'tcx>,
atomic: AtomicReadOrd,
) -> InterpResult<'tcx> {
let this = self.eval_context_ref();
@ -1241,7 +1241,7 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> {
/// associated memory-place and on the current thread.
fn validate_atomic_store(
&mut self,
place: &MPlaceTy<'tcx, Provenance>,
place: &MPlaceTy<'tcx>,
atomic: AtomicWriteOrd,
) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
@ -1263,7 +1263,7 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> {
/// at the associated memory place and on the current thread.
fn validate_atomic_rmw(
&mut self,
place: &MPlaceTy<'tcx, Provenance>,
place: &MPlaceTy<'tcx>,
atomic: AtomicRwOrd,
) -> InterpResult<'tcx> {
use AtomicRwOrd::*;
@ -1292,7 +1292,7 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> {
/// Generic atomic operation implementation
fn validate_atomic_op<A: Debug + Copy>(
&self,
place: &MPlaceTy<'tcx, Provenance>,
place: &MPlaceTy<'tcx>,
atomic: A,
access: AccessType,
mut op: impl FnMut(
@ -1337,7 +1337,7 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> {
mem_clocks,
access,
place.layout.size,
Pointer::new(
interpret::Pointer::new(
alloc_id,
Size::from_bytes(mem_clocks_range.start),
),

View file

@ -7,7 +7,7 @@ use super::sync::EvalContextExtPriv as _;
use super::vector_clock::VClock;
use crate::*;
declare_id!(InitOnceId);
super::sync::declare_id!(InitOnceId);
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
/// The current status of a one time initialization.
@ -33,10 +33,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
#[inline]
fn init_once_get_or_create<F>(&mut self, existing: F) -> InterpResult<'tcx, InitOnceId>
where
F: FnOnce(
&mut MiriInterpCx<'tcx>,
InitOnceId,
) -> InterpResult<'tcx, Option<InitOnceId>>,
F: FnOnce(&mut MiriInterpCx<'tcx>, InitOnceId) -> InterpResult<'tcx, Option<InitOnceId>>,
{
let this = self.eval_context_mut();
let next_index = this.machine.sync.init_onces.next_index();
@ -54,7 +51,7 @@ impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn init_once_get_or_create_id(
&mut self,
lock_op: &OpTy<'tcx, Provenance>,
lock_op: &OpTy<'tcx>,
lock_layout: TyAndLayout<'tcx>,
offset: u64,
) -> InterpResult<'tcx, InitOnceId> {

View file

@ -1,8 +1,7 @@
pub mod data_race;
mod range_object_map;
#[macro_use]
pub mod sync;
pub mod init_once;
mod range_object_map;
pub mod sync;
pub mod thread;
mod vector_clock;
pub mod weak_memory;

View file

@ -1,5 +1,6 @@
use std::collections::{hash_map::Entry, VecDeque};
use std::ops::Not;
use std::time::Duration;
use rustc_data_structures::fx::FxHashMap;
use rustc_index::{Idx, IndexVec};
@ -55,12 +56,13 @@ macro_rules! declare_id {
}
impl $name {
pub fn to_u32_scalar(&self) -> Scalar<Provenance> {
pub fn to_u32_scalar(&self) -> Scalar {
Scalar::from_u32(self.0.get())
}
}
};
}
pub(super) use declare_id;
declare_id!(MutexId);
@ -160,16 +162,14 @@ pub struct SynchronizationObjects {
// Private extension trait for local helper methods
impl<'tcx> EvalContextExtPriv<'tcx> for crate::MiriInterpCx<'tcx> {}
pub(super) trait EvalContextExtPriv<'tcx>:
crate::MiriInterpCxExt<'tcx>
{
pub(super) trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
/// Lazily initialize the ID of this Miri sync structure.
/// ('0' indicates uninit.)
#[inline]
fn get_or_create_id<Id: SyncId>(
&mut self,
next_id: Id,
lock_op: &OpTy<'tcx, Provenance>,
lock_op: &OpTy<'tcx>,
lock_layout: TyAndLayout<'tcx>,
offset: u64,
) -> InterpResult<'tcx, Option<Id>> {
@ -244,10 +244,7 @@ pub(super) trait EvalContextExtPriv<'tcx>:
#[inline]
fn condvar_get_or_create<F>(&mut self, existing: F) -> InterpResult<'tcx, CondvarId>
where
F: FnOnce(
&mut MiriInterpCx<'tcx>,
CondvarId,
) -> InterpResult<'tcx, Option<CondvarId>>,
F: FnOnce(&mut MiriInterpCx<'tcx>, CondvarId) -> InterpResult<'tcx, Option<CondvarId>>,
{
let this = self.eval_context_mut();
let next_index = this.machine.sync.condvars.next_index();
@ -266,8 +263,8 @@ pub(super) trait EvalContextExtPriv<'tcx>:
fn condvar_reacquire_mutex(
&mut self,
mutex: MutexId,
retval: Scalar<Provenance>,
dest: MPlaceTy<'tcx, Provenance>,
retval: Scalar,
dest: MPlaceTy<'tcx>,
) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
if this.mutex_is_locked(mutex) {
@ -291,7 +288,7 @@ impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn mutex_get_or_create_id(
&mut self,
lock_op: &OpTy<'tcx, Provenance>,
lock_op: &OpTy<'tcx>,
lock_layout: TyAndLayout<'tcx>,
offset: u64,
) -> InterpResult<'tcx, MutexId> {
@ -303,7 +300,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn rwlock_get_or_create_id(
&mut self,
lock_op: &OpTy<'tcx, Provenance>,
lock_op: &OpTy<'tcx>,
lock_layout: TyAndLayout<'tcx>,
offset: u64,
) -> InterpResult<'tcx, RwLockId> {
@ -315,7 +312,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn condvar_get_or_create_id(
&mut self,
lock_op: &OpTy<'tcx, Provenance>,
lock_op: &OpTy<'tcx>,
lock_layout: TyAndLayout<'tcx>,
offset: u64,
) -> InterpResult<'tcx, CondvarId> {
@ -397,12 +394,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
/// Put the thread into the queue waiting for the mutex.
/// Once the Mutex becomes available, `retval` will be written to `dest`.
#[inline]
fn mutex_enqueue_and_block(
&mut self,
id: MutexId,
retval: Scalar<Provenance>,
dest: MPlaceTy<'tcx, Provenance>,
) {
fn mutex_enqueue_and_block(&mut self, id: MutexId, retval: Scalar, dest: MPlaceTy<'tcx>) {
let this = self.eval_context_mut();
assert!(this.mutex_is_locked(id), "queing on unlocked mutex");
let thread = this.active_thread();
@ -413,8 +405,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
callback!(
@capture<'tcx> {
id: MutexId,
retval: Scalar<Provenance>,
dest: MPlaceTy<'tcx, Provenance>,
retval: Scalar,
dest: MPlaceTy<'tcx>,
}
@unblock = |this| {
assert!(!this.mutex_is_locked(id));
@ -510,8 +502,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn rwlock_enqueue_and_block_reader(
&mut self,
id: RwLockId,
retval: Scalar<Provenance>,
dest: MPlaceTy<'tcx, Provenance>,
retval: Scalar,
dest: MPlaceTy<'tcx>,
) {
let this = self.eval_context_mut();
let thread = this.active_thread();
@ -523,8 +515,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
callback!(
@capture<'tcx> {
id: RwLockId,
retval: Scalar<Provenance>,
dest: MPlaceTy<'tcx, Provenance>,
retval: Scalar,
dest: MPlaceTy<'tcx>,
}
@unblock = |this| {
this.rwlock_reader_lock(id);
@ -593,8 +585,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn rwlock_enqueue_and_block_writer(
&mut self,
id: RwLockId,
retval: Scalar<Provenance>,
dest: MPlaceTy<'tcx, Provenance>,
retval: Scalar,
dest: MPlaceTy<'tcx>,
) {
let this = self.eval_context_mut();
assert!(this.rwlock_is_locked(id), "write-queueing on unlocked rwlock");
@ -606,8 +598,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
callback!(
@capture<'tcx> {
id: RwLockId,
retval: Scalar<Provenance>,
dest: MPlaceTy<'tcx, Provenance>,
retval: Scalar,
dest: MPlaceTy<'tcx>,
}
@unblock = |this| {
this.rwlock_writer_lock(id);
@ -632,10 +624,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
&mut self,
condvar: CondvarId,
mutex: MutexId,
timeout: Option<Timeout>,
retval_succ: Scalar<Provenance>,
retval_timeout: Scalar<Provenance>,
dest: MPlaceTy<'tcx, Provenance>,
timeout: Option<(TimeoutClock, TimeoutAnchor, Duration)>,
retval_succ: Scalar,
retval_timeout: Scalar,
dest: MPlaceTy<'tcx>,
) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
if let Some(old_locked_count) = this.mutex_unlock(mutex)? {
@ -659,9 +651,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
@capture<'tcx> {
condvar: CondvarId,
mutex: MutexId,
retval_succ: Scalar<Provenance>,
retval_timeout: Scalar<Provenance>,
dest: MPlaceTy<'tcx, Provenance>,
retval_succ: Scalar,
retval_timeout: Scalar,
dest: MPlaceTy<'tcx>,
}
@unblock = |this| {
// The condvar was signaled. Make sure we get the clock for that.
@ -713,11 +705,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
&mut self,
addr: u64,
bitset: u32,
timeout: Option<Timeout>,
retval_succ: Scalar<Provenance>,
retval_timeout: Scalar<Provenance>,
dest: MPlaceTy<'tcx, Provenance>,
errno_timeout: Scalar<Provenance>,
timeout: Option<(TimeoutClock, TimeoutAnchor, Duration)>,
retval_succ: Scalar,
retval_timeout: Scalar,
dest: MPlaceTy<'tcx>,
errno_timeout: Scalar,
) {
let this = self.eval_context_mut();
let thread = this.active_thread();
@ -731,10 +723,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
callback!(
@capture<'tcx> {
addr: u64,
retval_succ: Scalar<Provenance>,
retval_timeout: Scalar<Provenance>,
dest: MPlaceTy<'tcx, Provenance>,
errno_timeout: Scalar<Provenance>,
retval_succ: Scalar,
retval_timeout: Scalar,
dest: MPlaceTy<'tcx>,
errno_timeout: Scalar,
}
@unblock = |this| {
let futex = this.machine.sync.futexes.get(&addr).unwrap();

View file

@ -256,10 +256,10 @@ pub struct Thread<'tcx> {
/// which then forwards it to 'Resume'. However this argument is implicit in MIR,
/// so we have to store it out-of-band. When there are multiple active unwinds,
/// the innermost one is always caught first, so we can store them as a stack.
pub(crate) panic_payloads: Vec<Scalar<Provenance>>,
pub(crate) panic_payloads: Vec<Scalar>,
/// Last OS error location in memory. It is a 32-bit integer.
pub(crate) last_error: Option<MPlaceTy<'tcx, Provenance>>,
pub(crate) last_error: Option<MPlaceTy<'tcx>>,
}
pub type StackEmptyCallback<'tcx> =
@ -407,7 +407,7 @@ impl VisitProvenance for Frame<'_, Provenance, FrameExtra<'_>> {
/// The moment in time when a blocked thread should be woken up.
#[derive(Debug)]
pub enum Timeout {
enum Timeout {
Monotonic(Instant),
RealTime(SystemTime),
}
@ -421,6 +421,34 @@ impl Timeout {
time.duration_since(SystemTime::now()).unwrap_or(Duration::ZERO),
}
}
/// Will try to add `duration`, but if that overflows it may add less.
fn add_lossy(&self, duration: Duration) -> Self {
match self {
Timeout::Monotonic(i) => Timeout::Monotonic(i.add_lossy(duration)),
Timeout::RealTime(s) => {
// If this overflows, try adding just 1h and assume that will not overflow.
Timeout::RealTime(
s.checked_add(duration)
.unwrap_or_else(|| s.checked_add(Duration::from_secs(3600)).unwrap()),
)
}
}
}
}
/// The clock to use for the timeout you are asking for.
#[derive(Debug, Copy, Clone)]
pub enum TimeoutClock {
Monotonic,
RealTime,
}
/// Whether the timeout is relative or absolute.
#[derive(Debug, Copy, Clone)]
pub enum TimeoutAnchor {
Relative,
Absolute,
}
/// A set of threads.
@ -432,9 +460,8 @@ pub struct ThreadManager<'tcx> {
///
/// Note that this vector also contains terminated threads.
threads: IndexVec<ThreadId, Thread<'tcx>>,
/// A mapping from a thread-local static to an allocation id of a thread
/// specific allocation.
thread_local_alloc_ids: FxHashMap<(DefId, ThreadId), Pointer<Provenance>>,
/// A mapping from a thread-local static to the thread specific allocation.
thread_local_allocs: FxHashMap<(DefId, ThreadId), StrictPointer>,
/// A flag that indicates that we should change the active thread.
yield_active_thread: bool,
}
@ -443,7 +470,7 @@ impl VisitProvenance for ThreadManager<'_> {
fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
let ThreadManager {
threads,
thread_local_alloc_ids,
thread_local_allocs,
active_thread: _,
yield_active_thread: _,
} = self;
@ -451,7 +478,7 @@ impl VisitProvenance for ThreadManager<'_> {
for thread in threads {
thread.visit_provenance(visit);
}
for ptr in thread_local_alloc_ids.values() {
for ptr in thread_local_allocs.values() {
ptr.visit_provenance(visit);
}
}
@ -465,7 +492,7 @@ impl<'tcx> Default for ThreadManager<'tcx> {
Self {
active_thread: ThreadId::MAIN_THREAD,
threads,
thread_local_alloc_ids: Default::default(),
thread_local_allocs: Default::default(),
yield_active_thread: false,
}
}
@ -487,16 +514,16 @@ impl<'tcx> ThreadManager<'tcx> {
/// Check if we have an allocation for the given thread local static for the
/// active thread.
fn get_thread_local_alloc_id(&self, def_id: DefId) -> Option<Pointer<Provenance>> {
self.thread_local_alloc_ids.get(&(def_id, self.active_thread)).cloned()
fn get_thread_local_alloc_id(&self, def_id: DefId) -> Option<StrictPointer> {
self.thread_local_allocs.get(&(def_id, self.active_thread)).cloned()
}
/// Set the pointer for the allocation of the given thread local
/// static for the active thread.
///
/// Panics if a thread local is initialized twice for the same thread.
fn set_thread_local_alloc(&mut self, def_id: DefId, ptr: Pointer<Provenance>) {
self.thread_local_alloc_ids.try_insert((def_id, self.active_thread), ptr).unwrap();
fn set_thread_local_alloc(&mut self, def_id: DefId, ptr: StrictPointer) {
self.thread_local_allocs.try_insert((def_id, self.active_thread), ptr).unwrap();
}
/// Borrow the stack of the active thread.
@ -848,7 +875,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn get_or_create_thread_local_alloc(
&mut self,
def_id: DefId,
) -> InterpResult<'tcx, Pointer<Provenance>> {
) -> InterpResult<'tcx, StrictPointer> {
let this = self.eval_context_mut();
let tcx = this.tcx;
if let Some(old_alloc) = this.machine.threads.get_thread_local_alloc_id(def_id) {
@ -864,7 +891,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
let alloc = this.ctfe_query(|tcx| tcx.eval_static_initializer(def_id))?;
// We make a full copy of this allocation.
let mut alloc = alloc.inner().adjust_from_tcx(&this.tcx, |ptr| this.global_root_pointer(ptr))?;
let mut alloc =
alloc.inner().adjust_from_tcx(&this.tcx, |ptr| this.global_root_pointer(ptr))?;
// This allocation will be deallocated when the thread dies, so it is not in read-only memory.
alloc.mutability = Mutability::Mut;
// Create a fresh allocation with this content.
@ -878,10 +906,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
#[inline]
fn start_regular_thread(
&mut self,
thread: Option<MPlaceTy<'tcx, Provenance>>,
start_routine: Pointer<Option<Provenance>>,
thread: Option<MPlaceTy<'tcx>>,
start_routine: Pointer,
start_abi: Abi,
func_arg: ImmTy<'tcx, Provenance>,
func_arg: ImmTy<'tcx>,
ret_layout: TyAndLayout<'tcx>,
) -> InterpResult<'tcx, ThreadId> {
let this = self.eval_context_mut();
@ -948,18 +976,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let gone_thread = this.active_thread();
{
let mut free_tls_statics = Vec::new();
this.machine.threads.thread_local_alloc_ids.retain(
|&(_def_id, thread), &mut alloc_id| {
if thread != gone_thread {
// A different thread, keep this static around.
return true;
}
// Delete this static from the map and from memory.
// We cannot free directly here as we cannot use `?` in this context.
free_tls_statics.push(alloc_id);
false
},
);
this.machine.threads.thread_local_allocs.retain(|&(_def_id, thread), &mut alloc_id| {
if thread != gone_thread {
// A different thread, keep this static around.
return true;
}
// Delete this static from the map and from memory.
// We cannot free directly here as we cannot use `?` in this context.
free_tls_statics.push(alloc_id);
false
});
// Now free the TLS statics.
for ptr in free_tls_statics {
match tls_alloc_action {
@ -997,13 +1023,30 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn block_thread(
&mut self,
reason: BlockReason,
timeout: Option<Timeout>,
timeout: Option<(TimeoutClock, TimeoutAnchor, Duration)>,
callback: impl UnblockCallback<'tcx> + 'tcx,
) {
let this = self.eval_context_mut();
if !this.machine.communicate() && matches!(timeout, Some(Timeout::RealTime(..))) {
panic!("cannot have `RealTime` callback with isolation enabled!")
}
let timeout = timeout.map(|(clock, anchor, duration)| {
let anchor = match clock {
TimeoutClock::RealTime => {
assert!(
this.machine.communicate(),
"cannot have `RealTime` timeout with isolation enabled!"
);
Timeout::RealTime(match anchor {
TimeoutAnchor::Absolute => SystemTime::UNIX_EPOCH,
TimeoutAnchor::Relative => SystemTime::now(),
})
}
TimeoutClock::Monotonic =>
Timeout::Monotonic(match anchor {
TimeoutAnchor::Absolute => this.machine.clock.epoch(),
TimeoutAnchor::Relative => this.machine.clock.now(),
}),
};
anchor.add_lossy(duration)
});
this.machine.threads.block_thread(reason, timeout, callback);
}

View file

@ -148,7 +148,7 @@ struct StoreElement {
// FIXME: this means the store must be fully initialized;
// we will have to change this if we want to support atomics on
// (partially) uninitialized data.
val: Scalar<Provenance>,
val: Scalar,
/// Metadata about loads from this store element,
/// behind a RefCell to keep load op take &self
@ -197,7 +197,7 @@ impl StoreBufferAlloc {
fn get_or_create_store_buffer<'tcx>(
&self,
range: AllocRange,
init: Scalar<Provenance>,
init: Scalar,
) -> InterpResult<'tcx, Ref<'_, StoreBuffer>> {
let access_type = self.store_buffers.borrow().access_type(range);
let pos = match access_type {
@ -222,7 +222,7 @@ impl StoreBufferAlloc {
fn get_or_create_store_buffer_mut<'tcx>(
&mut self,
range: AllocRange,
init: Scalar<Provenance>,
init: Scalar,
) -> InterpResult<'tcx, &mut StoreBuffer> {
let buffers = self.store_buffers.get_mut();
let access_type = buffers.access_type(range);
@ -244,7 +244,7 @@ impl StoreBufferAlloc {
}
impl<'tcx> StoreBuffer {
fn new(init: Scalar<Provenance>) -> Self {
fn new(init: Scalar) -> Self {
let mut buffer = VecDeque::new();
buffer.reserve(STORE_BUFFER_LIMIT);
let mut ret = Self { buffer };
@ -282,7 +282,7 @@ impl<'tcx> StoreBuffer {
is_seqcst: bool,
rng: &mut (impl rand::Rng + ?Sized),
validate: impl FnOnce() -> InterpResult<'tcx>,
) -> InterpResult<'tcx, (Scalar<Provenance>, LoadRecency)> {
) -> InterpResult<'tcx, (Scalar, LoadRecency)> {
// Having a live borrow to store_buffer while calling validate_atomic_load is fine
// because the race detector doesn't touch store_buffer
@ -307,7 +307,7 @@ impl<'tcx> StoreBuffer {
fn buffered_write(
&mut self,
val: Scalar<Provenance>,
val: Scalar,
global: &DataRaceState,
thread_mgr: &ThreadManager<'_>,
is_seqcst: bool,
@ -408,7 +408,7 @@ impl<'tcx> StoreBuffer {
/// ATOMIC STORE IMPL in the paper (except we don't need the location's vector clock)
fn store_impl(
&mut self,
val: Scalar<Provenance>,
val: Scalar,
index: VectorIdx,
thread_clock: &VClock,
is_seqcst: bool,
@ -450,12 +450,7 @@ impl StoreElement {
/// buffer regardless of subsequent loads by the same thread; if the earliest load of another
/// thread doesn't happen before the current one, then no subsequent load by the other thread
/// can happen before the current one.
fn load_impl(
&self,
index: VectorIdx,
clocks: &ThreadClockSet,
is_seqcst: bool,
) -> Scalar<Provenance> {
fn load_impl(&self, index: VectorIdx, clocks: &ThreadClockSet, is_seqcst: bool) -> Scalar {
let mut load_info = self.load_info.borrow_mut();
load_info.sc_loaded |= is_seqcst;
let _ = load_info.timestamps.try_insert(index, clocks.clock[index]);
@ -467,10 +462,10 @@ impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn buffered_atomic_rmw(
&mut self,
new_val: Scalar<Provenance>,
place: &MPlaceTy<'tcx, Provenance>,
new_val: Scalar,
place: &MPlaceTy<'tcx>,
atomic: AtomicRwOrd,
init: Scalar<Provenance>,
init: Scalar,
) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
let (alloc_id, base_offset, ..) = this.ptr_get_alloc_id(place.ptr())?;
@ -493,11 +488,11 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn buffered_atomic_read(
&self,
place: &MPlaceTy<'tcx, Provenance>,
place: &MPlaceTy<'tcx>,
atomic: AtomicReadOrd,
latest_in_mo: Scalar<Provenance>,
latest_in_mo: Scalar,
validate: impl FnOnce() -> InterpResult<'tcx>,
) -> InterpResult<'tcx, Scalar<Provenance>> {
) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_ref();
if let Some(global) = &this.machine.data_race {
let (alloc_id, base_offset, ..) = this.ptr_get_alloc_id(place.ptr())?;
@ -534,10 +529,10 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn buffered_atomic_write(
&mut self,
val: Scalar<Provenance>,
dest: &MPlaceTy<'tcx, Provenance>,
val: Scalar,
dest: &MPlaceTy<'tcx>,
atomic: AtomicWriteOrd,
init: Scalar<Provenance>,
init: Scalar,
) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
let (alloc_id, base_offset, ..) = this.ptr_get_alloc_id(dest.ptr())?;
@ -579,9 +574,9 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
/// to perform load_impl on the latest store element
fn perform_read_on_buffered_latest(
&self,
place: &MPlaceTy<'tcx, Provenance>,
place: &MPlaceTy<'tcx>,
atomic: AtomicReadOrd,
init: Scalar<Provenance>,
init: Scalar,
) -> InterpResult<'tcx> {
let this = self.eval_context_ref();

View file

@ -42,7 +42,7 @@ pub enum TerminationInfo {
},
DataRace {
involves_non_atomic: bool,
ptr: Pointer<AllocId>,
ptr: interpret::Pointer<AllocId>,
op1: RacingOp,
op2: RacingOp,
extra: Option<&'static str>,
@ -128,7 +128,7 @@ pub enum NonHaltingDiagnostic {
details: bool,
},
WeakMemoryOutdatedLoad {
ptr: Pointer<Option<Provenance>>,
ptr: Pointer,
},
}

View file

@ -255,7 +255,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
/// Evaluates the scalar at the specified path.
fn eval_path(&self, path: &[&str]) -> OpTy<'tcx, Provenance> {
fn eval_path(&self, path: &[&str]) -> OpTy<'tcx> {
let this = self.eval_context_ref();
let instance = this.resolve_path(path, Namespace::ValueNS);
// We don't give a span -- this isn't actually used directly by the program anyway.
@ -264,7 +264,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
});
const_val.into()
}
fn eval_path_scalar(&self, path: &[&str]) -> Scalar<Provenance> {
fn eval_path_scalar(&self, path: &[&str]) -> Scalar {
let this = self.eval_context_ref();
let val = this.eval_path(path);
this.read_scalar(&val)
@ -272,7 +272,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
/// Helper function to get a `libc` constant as a `Scalar`.
fn eval_libc(&self, name: &str) -> Scalar<Provenance> {
fn eval_libc(&self, name: &str) -> Scalar {
self.eval_path_scalar(&["libc", name])
}
@ -293,7 +293,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
/// Helper function to get a `windows` constant as a `Scalar`.
fn eval_windows(&self, module: &str, name: &str) -> Scalar<Provenance> {
fn eval_windows(&self, module: &str, name: &str) -> Scalar {
self.eval_context_ref().eval_path_scalar(&["std", "sys", "pal", "windows", module, name])
}
@ -374,7 +374,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let val = if dest.layout().abi.is_signed() {
Scalar::from_int(i, dest.layout().size)
} else {
Scalar::from_uint(u64::try_from(i.into()).unwrap(), dest.layout().size)
// `unwrap` can only fail here if `i` is negative
Scalar::from_uint(u128::try_from(i.into()).unwrap(), dest.layout().size)
};
self.eval_context_mut().write_scalar(val, dest)
}
@ -413,12 +414,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
/// Test if this pointer equals 0.
fn ptr_is_null(&self, ptr: Pointer<Option<Provenance>>) -> InterpResult<'tcx, bool> {
fn ptr_is_null(&self, ptr: Pointer) -> InterpResult<'tcx, bool> {
Ok(ptr.addr().bytes() == 0)
}
/// Generate some random bytes, and write them to `dest`.
fn gen_random(&mut self, ptr: Pointer<Option<Provenance>>, len: u64) -> InterpResult<'tcx> {
fn gen_random(&mut self, ptr: Pointer, len: u64) -> InterpResult<'tcx> {
// Some programs pass in a null pointer and a length of 0
// to their platform's random-generation function (e.g. getrandom())
// on Linux. For compatibility with these programs, we don't perform
@ -453,7 +454,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
f: ty::Instance<'tcx>,
caller_abi: Abi,
args: &[Immediate<Provenance>],
dest: Option<&MPlaceTy<'tcx, Provenance>>,
dest: Option<&MPlaceTy<'tcx>>,
stack_pop: StackPopCleanup,
) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
@ -501,7 +502,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
/// The range is relative to `place`.
fn visit_freeze_sensitive(
&self,
place: &MPlaceTy<'tcx, Provenance>,
place: &MPlaceTy<'tcx>,
size: Size,
mut action: impl FnMut(AllocRange, bool) -> InterpResult<'tcx>,
) -> InterpResult<'tcx> {
@ -520,8 +521,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let mut cur_addr = start_addr;
// Called when we detected an `UnsafeCell` at the given offset and size.
// Calls `action` and advances `cur_ptr`.
let mut unsafe_cell_action = |unsafe_cell_ptr: &Pointer<Option<Provenance>>,
unsafe_cell_size: Size| {
let mut unsafe_cell_action = |unsafe_cell_ptr: &Pointer, unsafe_cell_size: Size| {
// We assume that we are given the fields in increasing offset order,
// and nothing else changes.
let unsafe_cell_addr = unsafe_cell_ptr.addr();
@ -575,7 +575,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
/// whether we are inside an `UnsafeCell` or not.
struct UnsafeCellVisitor<'ecx, 'tcx, F>
where
F: FnMut(&MPlaceTy<'tcx, Provenance>) -> InterpResult<'tcx>,
F: FnMut(&MPlaceTy<'tcx>) -> InterpResult<'tcx>,
{
ecx: &'ecx MiriInterpCx<'tcx>,
unsafe_cell_action: F,
@ -583,9 +583,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
impl<'ecx, 'tcx, F> ValueVisitor<'tcx, MiriMachine<'tcx>> for UnsafeCellVisitor<'ecx, 'tcx, F>
where
F: FnMut(&MPlaceTy<'tcx, Provenance>) -> InterpResult<'tcx>,
F: FnMut(&MPlaceTy<'tcx>) -> InterpResult<'tcx>,
{
type V = MPlaceTy<'tcx, Provenance>;
type V = MPlaceTy<'tcx>;
#[inline(always)]
fn ecx(&self) -> &MiriInterpCx<'tcx> {
@ -603,7 +603,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
// Hook to detect `UnsafeCell`.
fn visit_value(&mut self, v: &MPlaceTy<'tcx, Provenance>) -> InterpResult<'tcx> {
fn visit_value(&mut self, v: &MPlaceTy<'tcx>) -> InterpResult<'tcx> {
trace!("UnsafeCellVisitor: {:?} {:?}", *v, v.layout.ty);
let is_unsafe_cell = match v.layout.ty.kind() {
ty::Adt(adt, _) =>
@ -649,7 +649,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn visit_union(
&mut self,
_v: &MPlaceTy<'tcx, Provenance>,
_v: &MPlaceTy<'tcx>,
_fields: NonZero<usize>,
) -> InterpResult<'tcx> {
bug!("we should have already handled unions in `visit_value`")
@ -720,7 +720,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
/// Get last error variable as a place, lazily allocating thread-local storage for it if
/// necessary.
fn last_error_place(&mut self) -> InterpResult<'tcx, MPlaceTy<'tcx, Provenance>> {
fn last_error_place(&mut self) -> InterpResult<'tcx, MPlaceTy<'tcx>> {
let this = self.eval_context_mut();
if let Some(errno_place) = this.active_thread_ref().last_error.as_ref() {
Ok(errno_place.clone())
@ -735,14 +735,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
/// Sets the last error variable.
fn set_last_error(&mut self, scalar: Scalar<Provenance>) -> InterpResult<'tcx> {
fn set_last_error(&mut self, scalar: Scalar) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
let errno_place = this.last_error_place()?;
this.write_scalar(scalar, &errno_place)
}
/// Gets the last error variable.
fn get_last_error(&mut self) -> InterpResult<'tcx, Scalar<Provenance>> {
fn get_last_error(&mut self) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();
let errno_place = this.last_error_place()?;
this.read_scalar(&errno_place)
@ -750,7 +750,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
/// This function tries to produce the most similar OS error from the `std::io::ErrorKind`
/// as a platform-specific errnum.
fn io_error_to_errnum(&self, err: std::io::Error) -> InterpResult<'tcx, Scalar<Provenance>> {
fn io_error_to_errnum(&self, err: std::io::Error) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_ref();
let target = &this.tcx.sess.target;
if target.families.iter().any(|f| f == "unix") {
@ -779,7 +779,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
#[allow(clippy::needless_return)]
fn try_errnum_to_io_error(
&self,
errnum: Scalar<Provenance>,
errnum: Scalar,
) -> InterpResult<'tcx, Option<std::io::ErrorKind>> {
let this = self.eval_context_ref();
let target = &this.tcx.sess.target;
@ -836,7 +836,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
&self,
op: &impl Readable<'tcx, Provenance>,
layout: TyAndLayout<'tcx>,
) -> InterpResult<'tcx, MPlaceTy<'tcx, Provenance>> {
) -> InterpResult<'tcx, MPlaceTy<'tcx>> {
let this = self.eval_context_ref();
let ptr = this.read_pointer(op)?;
Ok(this.ptr_to_mplace(ptr, layout))
@ -849,7 +849,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
offset: u64,
base_layout: TyAndLayout<'tcx>,
value_layout: TyAndLayout<'tcx>,
) -> InterpResult<'tcx, MPlaceTy<'tcx, Provenance>> {
) -> InterpResult<'tcx, MPlaceTy<'tcx>> {
let this = self.eval_context_ref();
let op_place = this.deref_pointer_as(op, base_layout)?;
let offset = Size::from_bytes(offset);
@ -866,7 +866,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
offset: u64,
base_layout: TyAndLayout<'tcx>,
value_layout: TyAndLayout<'tcx>,
) -> InterpResult<'tcx, Scalar<Provenance>> {
) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_ref();
let value_place = this.deref_pointer_and_offset(op, offset, base_layout, value_layout)?;
this.read_scalar(&value_place)
@ -876,7 +876,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
&mut self,
op: &impl Readable<'tcx, Provenance>,
offset: u64,
value: impl Into<Scalar<Provenance>>,
value: impl Into<Scalar>,
base_layout: TyAndLayout<'tcx>,
value_layout: TyAndLayout<'tcx>,
) -> InterpResult<'tcx, ()> {
@ -888,10 +888,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
/// Parse a `timespec` struct and return it as a `std::time::Duration`. It returns `None`
/// if the value in the `timespec` struct is invalid. Some libc functions will return
/// `EINVAL` in this case.
fn read_timespec(
&mut self,
tp: &MPlaceTy<'tcx, Provenance>,
) -> InterpResult<'tcx, Option<Duration>> {
fn read_timespec(&mut self, tp: &MPlaceTy<'tcx>) -> InterpResult<'tcx, Option<Duration>> {
let this = self.eval_context_mut();
let seconds_place = this.project_field(tp, 0)?;
let seconds_scalar = this.read_scalar(&seconds_place)?;
@ -914,10 +911,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
/// Read bytes from a byte slice.
fn read_byte_slice<'a>(
&'a self,
slice: &ImmTy<'tcx, Provenance>,
) -> InterpResult<'tcx, &'a [u8]>
fn read_byte_slice<'a>(&'a self, slice: &ImmTy<'tcx>) -> InterpResult<'tcx, &'a [u8]>
where
'tcx: 'a,
{
@ -930,7 +924,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
/// Read a sequence of bytes until the first null terminator.
fn read_c_str<'a>(&'a self, ptr: Pointer<Option<Provenance>>) -> InterpResult<'tcx, &'a [u8]>
fn read_c_str<'a>(&'a self, ptr: Pointer) -> InterpResult<'tcx, &'a [u8]>
where
'tcx: 'a,
{
@ -963,7 +957,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn write_c_str(
&mut self,
c_str: &[u8],
ptr: Pointer<Option<Provenance>>,
ptr: Pointer,
size: u64,
) -> InterpResult<'tcx, (bool, u64)> {
// If `size` is smaller or equal than `bytes.len()`, writing `bytes` plus the required null
@ -982,7 +976,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
/// until the first null terminator.
fn read_c_str_with_char_size<T>(
&self,
mut ptr: Pointer<Option<Provenance>>,
mut ptr: Pointer,
size: Size,
align: Align,
) -> InterpResult<'tcx, Vec<T>>
@ -1014,7 +1008,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
/// Read a sequence of u16 until the first null terminator.
fn read_wide_str(&self, ptr: Pointer<Option<Provenance>>) -> InterpResult<'tcx, Vec<u16>> {
fn read_wide_str(&self, ptr: Pointer) -> InterpResult<'tcx, Vec<u16>> {
self.read_c_str_with_char_size(ptr, Size::from_bytes(2), Align::from_bytes(2).unwrap())
}
@ -1027,7 +1021,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn write_wide_str(
&mut self,
wide_str: &[u16],
ptr: Pointer<Option<Provenance>>,
ptr: Pointer,
size: u64,
) -> InterpResult<'tcx, (bool, u64)> {
// If `size` is smaller or equal than `bytes.len()`, writing `bytes` plus the required
@ -1052,7 +1046,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
/// Read a sequence of wchar_t until the first null terminator.
/// Always returns a `Vec<u32>` no matter the size of `wchar_t`.
fn read_wchar_t_str(&self, ptr: Pointer<Option<Provenance>>) -> InterpResult<'tcx, Vec<u32>> {
fn read_wchar_t_str(&self, ptr: Pointer) -> InterpResult<'tcx, Vec<u32>> {
let this = self.eval_context_ref();
let wchar_t = this.libc_ty_layout("wchar_t");
self.read_c_str_with_char_size(ptr, wchar_t.size, wchar_t.align.abi)
@ -1138,17 +1132,17 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
abi: Abi,
exp_abi: Abi,
link_name: Symbol,
args: &'a [OpTy<'tcx, Provenance>],
) -> InterpResult<'tcx, &'a [OpTy<'tcx, Provenance>; N]>
args: &'a [OpTy<'tcx>],
) -> InterpResult<'tcx, &'a [OpTy<'tcx>; N]>
where
&'a [OpTy<'tcx, Provenance>; N]: TryFrom<&'a [OpTy<'tcx, Provenance>]>,
&'a [OpTy<'tcx>; N]: TryFrom<&'a [OpTy<'tcx>]>,
{
self.check_abi_and_shim_symbol_clash(abi, exp_abi, link_name)?;
check_arg_count(args)
}
/// Mark a machine allocation that was just created as immutable.
fn mark_immutable(&mut self, mplace: &MPlaceTy<'tcx, Provenance>) {
fn mark_immutable(&mut self, mplace: &MPlaceTy<'tcx>) {
let this = self.eval_context_mut();
// This got just allocated, so there definitely is a pointer here.
let provenance = mplace.ptr().into_pointer_or_addr().unwrap().provenance;
@ -1168,10 +1162,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
/// Returns `None` if `f` is NaN or out of range.
fn float_to_int_checked(
&self,
src: &ImmTy<'tcx, Provenance>,
src: &ImmTy<'tcx>,
cast_to: TyAndLayout<'tcx>,
round: rustc_apfloat::Round,
) -> InterpResult<'tcx, Option<ImmTy<'tcx, Provenance>>> {
) -> InterpResult<'tcx, Option<ImmTy<'tcx>>> {
let this = self.eval_context_ref();
fn float_to_int_inner<'tcx, F: rustc_apfloat::Float>(
@ -1179,7 +1173,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
src: F,
cast_to: TyAndLayout<'tcx>,
round: rustc_apfloat::Round,
) -> (Scalar<Provenance>, rustc_apfloat::Status) {
) -> (Scalar, rustc_apfloat::Status) {
let int_size = cast_to.layout.size;
match cast_to.ty.kind() {
// Unsigned
@ -1267,10 +1261,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
/// Lookup an array of immediates stored as a linker section of name `name`.
fn lookup_link_section(
&mut self,
name: &str,
) -> InterpResult<'tcx, Vec<ImmTy<'tcx, Provenance>>> {
fn lookup_link_section(&mut self, name: &str) -> InterpResult<'tcx, Vec<ImmTy<'tcx>>> {
let this = self.eval_context_mut();
let tcx = this.tcx.tcx;
@ -1338,10 +1329,10 @@ impl<'tcx> MiriMachine<'tcx> {
/// Check that the number of args is what we expect.
pub fn check_arg_count<'a, 'tcx, const N: usize>(
args: &'a [OpTy<'tcx, Provenance>],
) -> InterpResult<'tcx, &'a [OpTy<'tcx, Provenance>; N]>
args: &'a [OpTy<'tcx>],
) -> InterpResult<'tcx, &'a [OpTy<'tcx>; N]>
where
&'a [OpTy<'tcx, Provenance>; N]: TryFrom<&'a [OpTy<'tcx, Provenance>]>,
&'a [OpTy<'tcx>; N]: TryFrom<&'a [OpTy<'tcx>]>,
{
if let Ok(ops) = args.try_into() {
return Ok(ops);
@ -1374,7 +1365,7 @@ pub fn get_local_crates(tcx: TyCtxt<'_>) -> Vec<CrateNum> {
local_crates
}
pub(crate) fn bool_to_simd_element(b: bool, size: Size) -> Scalar<Provenance> {
pub(crate) fn bool_to_simd_element(b: bool, size: Size) -> Scalar {
// SIMD uses all-1 as pattern for "true". In two's complement,
// -1 has all its bits set to one and `from_int` will truncate or
// sign-extend it to `size` as required.
@ -1382,7 +1373,7 @@ pub(crate) fn bool_to_simd_element(b: bool, size: Size) -> Scalar<Provenance> {
Scalar::from_int(val, size)
}
pub(crate) fn simd_element_to_bool(elem: ImmTy<'_, Provenance>) -> InterpResult<'_, bool> {
pub(crate) fn simd_element_to_bool(elem: ImmTy<'_>) -> InterpResult<'_, bool> {
let val = elem.to_scalar().to_int(elem.layout.size)?;
Ok(match val {
0 => false,

View file

@ -18,8 +18,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn emulate_atomic_intrinsic(
&mut self,
intrinsic_name: &str,
args: &[OpTy<'tcx, Provenance>],
dest: &MPlaceTy<'tcx, Provenance>,
args: &[OpTy<'tcx>],
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx, EmulateItemResult> {
let this = self.eval_context_mut();
@ -124,8 +124,8 @@ impl<'tcx> EvalContextPrivExt<'tcx> for MiriInterpCx<'tcx> {}
trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> {
fn atomic_load(
&mut self,
args: &[OpTy<'tcx, Provenance>],
dest: &MPlaceTy<'tcx, Provenance>,
args: &[OpTy<'tcx>],
dest: &MPlaceTy<'tcx>,
atomic: AtomicReadOrd,
) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
@ -140,11 +140,7 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> {
Ok(())
}
fn atomic_store(
&mut self,
args: &[OpTy<'tcx, Provenance>],
atomic: AtomicWriteOrd,
) -> InterpResult<'tcx> {
fn atomic_store(&mut self, args: &[OpTy<'tcx>], atomic: AtomicWriteOrd) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
let [place, val] = check_arg_count(args)?;
@ -159,7 +155,7 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> {
fn compiler_fence_intrinsic(
&mut self,
args: &[OpTy<'tcx, Provenance>],
args: &[OpTy<'tcx>],
atomic: AtomicFenceOrd,
) -> InterpResult<'tcx> {
let [] = check_arg_count(args)?;
@ -170,7 +166,7 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> {
fn atomic_fence_intrinsic(
&mut self,
args: &[OpTy<'tcx, Provenance>],
args: &[OpTy<'tcx>],
atomic: AtomicFenceOrd,
) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
@ -181,8 +177,8 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> {
fn atomic_rmw_op(
&mut self,
args: &[OpTy<'tcx, Provenance>],
dest: &MPlaceTy<'tcx, Provenance>,
args: &[OpTy<'tcx>],
dest: &MPlaceTy<'tcx>,
atomic_op: AtomicOp,
atomic: AtomicRwOrd,
) -> InterpResult<'tcx> {
@ -223,8 +219,8 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> {
fn atomic_exchange(
&mut self,
args: &[OpTy<'tcx, Provenance>],
dest: &MPlaceTy<'tcx, Provenance>,
args: &[OpTy<'tcx>],
dest: &MPlaceTy<'tcx>,
atomic: AtomicRwOrd,
) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
@ -240,8 +236,8 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> {
fn atomic_compare_exchange_impl(
&mut self,
args: &[OpTy<'tcx, Provenance>],
dest: &MPlaceTy<'tcx, Provenance>,
args: &[OpTy<'tcx>],
dest: &MPlaceTy<'tcx>,
success: AtomicRwOrd,
fail: AtomicReadOrd,
can_fail_spuriously: bool,
@ -269,8 +265,8 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> {
fn atomic_compare_exchange(
&mut self,
args: &[OpTy<'tcx, Provenance>],
dest: &MPlaceTy<'tcx, Provenance>,
args: &[OpTy<'tcx>],
dest: &MPlaceTy<'tcx>,
success: AtomicRwOrd,
fail: AtomicReadOrd,
) -> InterpResult<'tcx> {
@ -279,8 +275,8 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> {
fn atomic_compare_exchange_weak(
&mut self,
args: &[OpTy<'tcx, Provenance>],
dest: &MPlaceTy<'tcx, Provenance>,
args: &[OpTy<'tcx>],
dest: &MPlaceTy<'tcx>,
success: AtomicRwOrd,
fail: AtomicReadOrd,
) -> InterpResult<'tcx> {

View file

@ -1,3 +1,5 @@
#![warn(clippy::arithmetic_side_effects)]
mod atomic;
mod simd;
@ -23,8 +25,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn call_intrinsic(
&mut self,
instance: ty::Instance<'tcx>,
args: &[OpTy<'tcx, Provenance>],
dest: &MPlaceTy<'tcx, Provenance>,
args: &[OpTy<'tcx>],
dest: &MPlaceTy<'tcx>,
ret: Option<mir::BasicBlock>,
unwind: mir::UnwindAction,
) -> InterpResult<'tcx, Option<ty::Instance<'tcx>>> {
@ -79,8 +81,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
&mut self,
intrinsic_name: &str,
generic_args: ty::GenericArgsRef<'tcx>,
args: &[OpTy<'tcx, Provenance>],
dest: &MPlaceTy<'tcx, Provenance>,
args: &[OpTy<'tcx>],
dest: &MPlaceTy<'tcx>,
ret: Option<mir::BasicBlock>,
) -> InterpResult<'tcx, EmulateItemResult> {
let this = self.eval_context_mut();
@ -385,7 +387,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
"frem_fast" => mir::BinOp::Rem,
_ => bug!(),
};
let float_finite = |x: &ImmTy<'tcx, _>| -> InterpResult<'tcx, bool> {
let float_finite = |x: &ImmTy<'tcx>| -> InterpResult<'tcx, bool> {
let ty::Float(fty) = x.layout.ty.kind() else {
bug!("float_finite: non-float input type {}", x.layout.ty)
};

View file

@ -23,8 +23,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
&mut self,
intrinsic_name: &str,
generic_args: ty::GenericArgsRef<'tcx>,
args: &[OpTy<'tcx, Provenance>],
dest: &MPlaceTy<'tcx, Provenance>,
args: &[OpTy<'tcx>],
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx, EmulateItemResult> {
let this = self.eval_context_mut();
match intrinsic_name {
@ -452,28 +452,54 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let (no, no_len) = this.operand_to_simd(no)?;
let (dest, dest_len) = this.mplace_to_simd(dest)?;
let bitmask_len = dest_len.next_multiple_of(8);
if bitmask_len > 64 {
throw_unsup_format!(
"simd_select_bitmask: vectors larger than 64 elements are currently not supported"
);
}
// The mask must be an integer or an array.
assert!(
mask.layout.ty.is_integral()
|| matches!(mask.layout.ty.kind(), ty::Array(elemty, _) if elemty == &this.tcx.types.u8)
);
assert!(bitmask_len <= 64);
assert_eq!(bitmask_len, mask.layout.size.bits());
assert_eq!(dest_len, yes_len);
assert_eq!(dest_len, no_len);
// Read the mask, either as an integer or as an array.
let mask: u64 = match mask.layout.ty.kind() {
ty::Uint(_) => {
// Any larger integer type is fine.
assert!(mask.layout.size.bits() >= bitmask_len);
this.read_scalar(mask)?.to_bits(mask.layout.size)?.try_into().unwrap()
}
ty::Array(elem, _len) if elem == &this.tcx.types.u8 => {
// The array must have exactly the right size.
assert_eq!(mask.layout.size.bits(), bitmask_len);
// Read the raw bytes.
let mask = mask.assert_mem_place(); // arrays cannot be immediate
let mask_bytes =
this.read_bytes_ptr_strip_provenance(mask.ptr(), mask.layout.size)?;
// Turn them into a `u64` in the right way.
let mask_size = mask.layout.size.bytes_usize();
let mut mask_arr = [0u8; 8];
match this.data_layout().endian {
Endian::Little => {
// Fill the first N bytes.
mask_arr[..mask_size].copy_from_slice(mask_bytes);
u64::from_le_bytes(mask_arr)
}
Endian::Big => {
// Fill the last N bytes.
let i = mask_arr.len().strict_sub(mask_size);
mask_arr[i..].copy_from_slice(mask_bytes);
u64::from_be_bytes(mask_arr)
}
}
}
_ => bug!("simd_select_bitmask: invalid mask type {}", mask.layout.ty),
};
let dest_len = u32::try_from(dest_len).unwrap();
let bitmask_len = u32::try_from(bitmask_len).unwrap();
// To read the mask, we transmute it to an integer.
// That does the right thing wrt endianness.
let mask_ty = this.machine.layouts.uint(mask.layout.size).unwrap();
let mask = mask.transmute(mask_ty, this)?;
let mask: u64 = this.read_scalar(&mask)?.to_bits(mask_ty.size)?.try_into().unwrap();
for i in 0..dest_len {
let bit_i = simd_bitmask_index(i, dest_len, this.data_layout().endian);
let mask = mask & 1u64.checked_shl(bit_i).unwrap();
let mask = mask & 1u64.strict_shl(bit_i);
let yes = this.read_immediate(&this.project_index(&yes, i.into())?)?;
let no = this.read_immediate(&this.project_index(&no, i.into())?)?;
let dest = this.project_index(&dest, i.into())?;
@ -485,7 +511,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// If the mask is "padded", ensure that padding is all-zero.
// This deliberately does not use `simd_bitmask_index`; these bits are outside
// the bitmask. It does not matter in which order we check them.
let mask = mask & 1u64.checked_shl(i).unwrap();
let mask = mask & 1u64.strict_shl(i);
if mask != 0 {
throw_ub_format!(
"a SIMD bitmask less than 8 bits long must be filled with 0s for the remaining bits"
@ -498,30 +524,49 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let [op] = check_arg_count(args)?;
let (op, op_len) = this.operand_to_simd(op)?;
let bitmask_len = op_len.next_multiple_of(8);
if bitmask_len > 64 {
throw_unsup_format!(
"simd_bitmask: vectors larger than 64 elements are currently not supported"
);
}
// Returns either an unsigned integer or array of `u8`.
assert!(
dest.layout.ty.is_integral()
|| matches!(dest.layout.ty.kind(), ty::Array(elemty, _) if elemty == &this.tcx.types.u8)
);
assert!(bitmask_len <= 64);
assert_eq!(bitmask_len, dest.layout.size.bits());
let op_len = u32::try_from(op_len).unwrap();
let mut res = 0u64;
for i in 0..op_len {
let op = this.read_immediate(&this.project_index(&op, i.into())?)?;
if simd_element_to_bool(op)? {
res |= 1u64
.checked_shl(simd_bitmask_index(i, op_len, this.data_layout().endian))
.unwrap();
let bit_i = simd_bitmask_index(i, op_len, this.data_layout().endian);
res |= 1u64.strict_shl(bit_i);
}
}
// We have to change the type of the place to be able to write `res` into it. This
// transmutes the integer to an array, which does the right thing wrt endianness.
let dest =
dest.transmute(this.machine.layouts.uint(dest.layout.size).unwrap(), this)?;
this.write_int(res, &dest)?;
// Write the result, depending on the `dest` type.
// Returns either an unsigned integer or array of `u8`.
match dest.layout.ty.kind() {
ty::Uint(_) => {
// Any larger integer type is fine, it will be zero-extended.
assert!(dest.layout.size.bits() >= bitmask_len);
this.write_int(res, dest)?;
}
ty::Array(elem, _len) if elem == &this.tcx.types.u8 => {
// The array must have exactly the right size.
assert_eq!(dest.layout.size.bits(), bitmask_len);
// We have to write the result byte-for-byte.
let res_size = dest.layout.size.bytes_usize();
let res_bytes;
let res_bytes_slice = match this.data_layout().endian {
Endian::Little => {
res_bytes = res.to_le_bytes();
&res_bytes[..res_size] // take the first N bytes
}
Endian::Big => {
res_bytes = res.to_be_bytes();
&res_bytes[res_bytes.len().strict_sub(res_size)..] // take the last N bytes
}
};
this.write_bytes_ptr(dest.ptr(), res_bytes_slice.iter().cloned())?;
}
_ => bug!("simd_bitmask: invalid return type {}", dest.layout.ty),
}
}
"cast" | "as" | "cast_ptr" | "expose_provenance" | "with_exposed_provenance" => {
let [op] = check_arg_count(args)?;
@ -607,8 +652,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let val = if src_index < left_len {
this.read_immediate(&this.project_index(&left, src_index)?)?
} else if src_index < left_len.checked_add(right_len).unwrap() {
let right_idx = src_index.checked_sub(left_len).unwrap();
} else if src_index < left_len.strict_add(right_len) {
let right_idx = src_index.strict_sub(left_len);
this.read_immediate(&this.project_index(&right, right_idx)?)?
} else {
throw_ub_format!(
@ -647,8 +692,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let val = if src_index < left_len {
this.read_immediate(&this.project_index(&left, src_index)?)?
} else if src_index < left_len.checked_add(right_len).unwrap() {
let right_idx = src_index.checked_sub(left_len).unwrap();
} else if src_index < left_len.strict_add(right_len) {
let right_idx = src_index.strict_sub(left_len);
this.read_immediate(&this.project_index(&right, right_idx)?)?
} else {
throw_ub_format!(
@ -761,9 +806,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn fminmax_op(
&self,
op: MinMax,
left: &ImmTy<'tcx, Provenance>,
right: &ImmTy<'tcx, Provenance>,
) -> InterpResult<'tcx, Scalar<Provenance>> {
left: &ImmTy<'tcx>,
right: &ImmTy<'tcx>,
) -> 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 {

View file

@ -53,7 +53,6 @@
// Some "regular" crates we want to share with rustc
extern crate either;
#[macro_use]
extern crate tracing;
// The rustc crates we need
@ -64,7 +63,6 @@ extern crate rustc_data_structures;
extern crate rustc_errors;
extern crate rustc_hir;
extern crate rustc_index;
#[macro_use]
extern crate rustc_middle;
extern crate rustc_session;
extern crate rustc_span;
@ -91,13 +89,24 @@ mod range_map;
mod shims;
// Establish a "crate-wide prelude": we often import `crate::*`.
use rustc_middle::{bug, span_bug};
use tracing::{info, trace};
// Make all those symbols available in the same place as our own.
#[doc(no_inline)]
pub use rustc_const_eval::interpret::*;
// Resolve ambiguity.
#[doc(no_inline)]
pub use rustc_const_eval::interpret::{self, AllocMap, PlaceTy, Provenance as _};
pub use rustc_const_eval::interpret::{self, AllocMap, Provenance as _};
// Type aliases that set the provenance parameter.
pub type Pointer = interpret::Pointer<Option<machine::Provenance>>;
pub type StrictPointer = interpret::Pointer<machine::Provenance>;
pub type Scalar = interpret::Scalar<machine::Provenance>;
pub type ImmTy<'tcx> = interpret::ImmTy<'tcx, machine::Provenance>;
pub type OpTy<'tcx> = interpret::OpTy<'tcx, machine::Provenance>;
pub type PlaceTy<'tcx> = interpret::PlaceTy<'tcx, machine::Provenance>;
pub type MPlaceTy<'tcx> = interpret::MPlaceTy<'tcx, machine::Provenance>;
pub use crate::intrinsics::EvalContextExt as _;
pub use crate::shims::env::{EnvVars, EvalContextExt as _};
@ -123,8 +132,8 @@ pub use crate::concurrency::{
init_once::{EvalContextExt as _, InitOnceId},
sync::{CondvarId, EvalContextExt as _, MutexId, RwLockId, SynchronizationObjects},
thread::{
BlockReason, EvalContextExt as _, StackEmptyCallback, ThreadId, ThreadManager, Timeout,
UnblockCallback,
BlockReason, EvalContextExt as _, StackEmptyCallback, ThreadId, ThreadManager,
TimeoutAnchor, TimeoutClock, UnblockCallback,
},
};
pub use crate::diagnostics::{

View file

@ -240,12 +240,12 @@ pub enum ProvenanceExtra {
}
#[cfg(target_pointer_width = "64")]
static_assert_size!(Pointer<Provenance>, 24);
static_assert_size!(StrictPointer, 24);
// FIXME: this would with in 24bytes but layout optimizations are not smart enough
// #[cfg(target_pointer_width = "64")]
//static_assert_size!(Pointer<Option<Provenance>>, 24);
//static_assert_size!(Pointer, 24);
#[cfg(target_pointer_width = "64")]
static_assert_size!(Scalar<Provenance>, 32);
static_assert_size!(Scalar, 32);
impl fmt::Debug for Provenance {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
@ -269,7 +269,7 @@ impl fmt::Debug for Provenance {
}
impl interpret::Provenance for Provenance {
/// We use absolute addresses in the `offset` of a `Pointer<Provenance>`.
/// We use absolute addresses in the `offset` of a `StrictPointer`.
const OFFSET_IS_ADDR: bool = true;
fn get_alloc_id(self) -> Option<AllocId> {
@ -279,7 +279,7 @@ impl interpret::Provenance for Provenance {
}
}
fn fmt(ptr: &Pointer<Self>, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fn fmt(ptr: &interpret::Pointer<Self>, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let (prov, addr) = ptr.into_parts(); // address is absolute
write!(f, "{:#x}", addr.bytes())?;
if f.alternate() {
@ -441,14 +441,14 @@ pub struct MiriMachine<'tcx> {
pub(crate) env_vars: EnvVars<'tcx>,
/// Return place of the main function.
pub(crate) main_fn_ret_place: Option<MPlaceTy<'tcx, Provenance>>,
pub(crate) main_fn_ret_place: Option<MPlaceTy<'tcx>>,
/// Program arguments (`Option` because we can only initialize them after creating the ecx).
/// These are *pointers* to argc/argv because macOS.
/// We also need the full command line as one string because of Windows.
pub(crate) argc: Option<Pointer<Option<Provenance>>>,
pub(crate) argv: Option<Pointer<Option<Provenance>>>,
pub(crate) cmd_line: Option<Pointer<Option<Provenance>>>,
pub(crate) argc: Option<Pointer>,
pub(crate) argv: Option<Pointer>,
pub(crate) cmd_line: Option<Pointer>,
/// TLS state.
pub(crate) tls: TlsData<'tcx>,
@ -503,7 +503,7 @@ pub struct MiriMachine<'tcx> {
pub(crate) local_crates: Vec<CrateNum>,
/// Mapping extern static names to their pointer.
extern_statics: FxHashMap<Symbol, Pointer<Provenance>>,
extern_statics: FxHashMap<Symbol, StrictPointer>,
/// The random number generator used for resolving non-determinism.
/// Needs to be queried by ptr_to_int, hence needs interior mutability.
@ -564,7 +564,7 @@ pub struct MiriMachine<'tcx> {
/// Maps MIR consts to their evaluated result. We combine the const with a "salt" (`usize`)
/// that is fixed per stack frame; this lets us have sometimes different results for the
/// same const while ensuring consistent results within a single call.
const_cache: RefCell<FxHashMap<(mir::Const<'tcx>, usize), OpTy<'tcx, Provenance>>>,
const_cache: RefCell<FxHashMap<(mir::Const<'tcx>, usize), OpTy<'tcx>>>,
/// For each allocation, an offset inside that allocation that was deemed aligned even for
/// symbolic alignment checks. This cannot be stored in `AllocExtra` since it needs to be
@ -715,11 +715,7 @@ impl<'tcx> MiriMachine<'tcx> {
Ok(())
}
pub(crate) fn add_extern_static(
this: &mut MiriInterpCx<'tcx>,
name: &str,
ptr: Pointer<Option<Provenance>>,
) {
pub(crate) fn add_extern_static(this: &mut MiriInterpCx<'tcx>, name: &str, ptr: Pointer) {
// This got just allocated, so there definitely is a pointer here.
let ptr = ptr.into_pointer_or_addr().unwrap();
this.machine.extern_statics.try_insert(Symbol::intern(name), ptr).unwrap();
@ -945,7 +941,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
instance: ty::Instance<'tcx>,
abi: Abi,
args: &[FnArg<'tcx, Provenance>],
dest: &MPlaceTy<'tcx, Provenance>,
dest: &MPlaceTy<'tcx>,
ret: Option<mir::BasicBlock>,
unwind: mir::UnwindAction,
) -> InterpResult<'tcx, Option<(&'tcx mir::Body<'tcx>, ty::Instance<'tcx>)>> {
@ -972,7 +968,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
fn_val: DynSym,
abi: Abi,
args: &[FnArg<'tcx, Provenance>],
dest: &MPlaceTy<'tcx, Provenance>,
dest: &MPlaceTy<'tcx>,
ret: Option<mir::BasicBlock>,
unwind: mir::UnwindAction,
) -> InterpResult<'tcx> {
@ -984,8 +980,8 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
fn call_intrinsic(
ecx: &mut MiriInterpCx<'tcx>,
instance: ty::Instance<'tcx>,
args: &[OpTy<'tcx, Provenance>],
dest: &MPlaceTy<'tcx, Provenance>,
args: &[OpTy<'tcx>],
dest: &MPlaceTy<'tcx>,
ret: Option<mir::BasicBlock>,
unwind: mir::UnwindAction,
) -> InterpResult<'tcx, Option<ty::Instance<'tcx>>> {
@ -1026,9 +1022,9 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
fn binary_ptr_op(
ecx: &MiriInterpCx<'tcx>,
bin_op: mir::BinOp,
left: &ImmTy<'tcx, Provenance>,
right: &ImmTy<'tcx, Provenance>,
) -> InterpResult<'tcx, ImmTy<'tcx, Provenance>> {
left: &ImmTy<'tcx>,
right: &ImmTy<'tcx>,
) -> InterpResult<'tcx, ImmTy<'tcx>> {
ecx.binary_ptr_op(bin_op, left, right)
}
@ -1046,14 +1042,14 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
fn thread_local_static_pointer(
ecx: &mut MiriInterpCx<'tcx>,
def_id: DefId,
) -> InterpResult<'tcx, Pointer<Provenance>> {
) -> InterpResult<'tcx, StrictPointer> {
ecx.get_or_create_thread_local_alloc(def_id)
}
fn extern_static_pointer(
ecx: &MiriInterpCx<'tcx>,
def_id: DefId,
) -> InterpResult<'tcx, Pointer<Provenance>> {
) -> InterpResult<'tcx, StrictPointer> {
let link_name = ecx.item_link_name(def_id);
if let Some(&ptr) = ecx.machine.extern_statics.get(&link_name) {
// Various parts of the engine rely on `get_alloc_info` for size and alignment
@ -1134,9 +1130,9 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
fn adjust_alloc_root_pointer(
ecx: &MiriInterpCx<'tcx>,
ptr: Pointer<CtfeProvenance>,
ptr: interpret::Pointer<CtfeProvenance>,
kind: Option<MemoryKind>,
) -> InterpResult<'tcx, Pointer<Provenance>> {
) -> InterpResult<'tcx, interpret::Pointer<Provenance>> {
let kind = kind.expect("we set our GLOBAL_KIND so this cannot be None");
let alloc_id = ptr.provenance.alloc_id();
if cfg!(debug_assertions) {
@ -1163,20 +1159,14 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
/// Called on `usize as ptr` casts.
#[inline(always)]
fn ptr_from_addr_cast(
ecx: &MiriInterpCx<'tcx>,
addr: u64,
) -> InterpResult<'tcx, Pointer<Option<Self::Provenance>>> {
fn ptr_from_addr_cast(ecx: &MiriInterpCx<'tcx>, addr: u64) -> InterpResult<'tcx, Pointer> {
ecx.ptr_from_addr_cast(addr)
}
/// Called on `ptr as usize` casts.
/// (Actually computing the resulting `usize` doesn't need machine help,
/// that's just `Scalar::try_to_int`.)
fn expose_ptr(
ecx: &mut InterpCx<'tcx, Self>,
ptr: Pointer<Self::Provenance>,
) -> InterpResult<'tcx> {
fn expose_ptr(ecx: &mut InterpCx<'tcx, Self>, ptr: StrictPointer) -> InterpResult<'tcx> {
match ptr.provenance {
Provenance::Concrete { alloc_id, tag } => ecx.expose_ptr(alloc_id, tag),
Provenance::Wildcard => {
@ -1197,7 +1187,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
/// stored in machine state).
fn ptr_get_alloc(
ecx: &MiriInterpCx<'tcx>,
ptr: Pointer<Self::Provenance>,
ptr: StrictPointer,
) -> Option<(AllocId, Size, Self::ProvenanceExtra)> {
let rel = ecx.ptr_get_alloc(ptr);
@ -1295,8 +1285,8 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
fn retag_ptr_value(
ecx: &mut InterpCx<'tcx, Self>,
kind: mir::RetagKind,
val: &ImmTy<'tcx, Provenance>,
) -> InterpResult<'tcx, ImmTy<'tcx, Provenance>> {
val: &ImmTy<'tcx>,
) -> InterpResult<'tcx, ImmTy<'tcx>> {
if ecx.machine.borrow_tracker.is_some() {
ecx.retag_ptr_value(kind, val)
} else {
@ -1308,7 +1298,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
fn retag_place_contents(
ecx: &mut InterpCx<'tcx, Self>,
kind: mir::RetagKind,
place: &PlaceTy<'tcx, Provenance>,
place: &PlaceTy<'tcx>,
) -> InterpResult<'tcx> {
if ecx.machine.borrow_tracker.is_some() {
ecx.retag_place_contents(kind, place)?;
@ -1318,7 +1308,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
fn protect_in_place_function_argument(
ecx: &mut InterpCx<'tcx, Self>,
place: &MPlaceTy<'tcx, Provenance>,
place: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx> {
// If we have a borrow tracker, we also have it set up protection so that all reads *and
// writes* during this call are insta-UB.
@ -1473,7 +1463,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
fn after_local_allocated(
ecx: &mut InterpCx<'tcx, Self>,
local: mir::Local,
mplace: &MPlaceTy<'tcx, Provenance>,
mplace: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx> {
let Some(Provenance::Concrete { alloc_id, .. }) = mplace.ptr().provenance else {
panic!("after_local_allocated should only be called on fresh allocations");
@ -1490,14 +1480,14 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
span: Span,
layout: Option<TyAndLayout<'tcx>>,
eval: F,
) -> InterpResult<'tcx, OpTy<'tcx, Self::Provenance>>
) -> InterpResult<'tcx, OpTy<'tcx>>
where
F: Fn(
&InterpCx<'tcx, Self>,
mir::Const<'tcx>,
Span,
Option<TyAndLayout<'tcx>>,
) -> InterpResult<'tcx, OpTy<'tcx, Self::Provenance>>,
) -> InterpResult<'tcx, OpTy<'tcx>>,
{
let frame = ecx.active_thread_stack().last().unwrap();
let mut cache = ecx.machine.const_cache.borrow_mut();

View file

@ -12,9 +12,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn binary_ptr_op(
&self,
bin_op: mir::BinOp,
left: &ImmTy<'tcx, Provenance>,
right: &ImmTy<'tcx, Provenance>,
) -> InterpResult<'tcx, ImmTy<'tcx, Provenance>> {
left: &ImmTy<'tcx>,
right: &ImmTy<'tcx>,
) -> InterpResult<'tcx, ImmTy<'tcx>> {
use rustc_middle::mir::BinOp::*;
let this = self.eval_context_ref();

View file

@ -56,21 +56,21 @@ impl VisitProvenance for Provenance {
}
}
impl VisitProvenance for Pointer<Provenance> {
impl VisitProvenance for StrictPointer {
fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
let (prov, _offset) = self.into_parts();
prov.visit_provenance(visit);
}
}
impl VisitProvenance for Pointer<Option<Provenance>> {
impl VisitProvenance for Pointer {
fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
let (prov, _offset) = self.into_parts();
prov.visit_provenance(visit);
}
}
impl VisitProvenance for Scalar<Provenance> {
impl VisitProvenance for Scalar {
fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
match self {
Scalar::Ptr(ptr, _) => ptr.visit_provenance(visit),
@ -103,20 +103,20 @@ impl VisitProvenance for MemPlaceMeta<Provenance> {
}
}
impl VisitProvenance for ImmTy<'_, Provenance> {
impl VisitProvenance for ImmTy<'_> {
fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
(**self).visit_provenance(visit)
}
}
impl VisitProvenance for MPlaceTy<'_, Provenance> {
impl VisitProvenance for MPlaceTy<'_> {
fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
self.ptr().visit_provenance(visit);
self.meta().visit_provenance(visit);
}
}
impl VisitProvenance for PlaceTy<'_, Provenance> {
impl VisitProvenance for PlaceTy<'_> {
fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
match self.as_mplace_or_local() {
Either::Left(mplace) => mplace.visit_provenance(visit),
@ -125,7 +125,7 @@ impl VisitProvenance for PlaceTy<'_, Provenance> {
}
}
impl VisitProvenance for OpTy<'_, Provenance> {
impl VisitProvenance for OpTy<'_> {
fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
match self.as_mplace_or_imm() {
Either::Left(mplace) => mplace.visit_provenance(visit),

View file

@ -92,11 +92,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
}
fn malloc(
&mut self,
size: u64,
zero_init: bool,
) -> InterpResult<'tcx, Pointer<Option<Provenance>>> {
fn malloc(&mut self, size: u64, zero_init: bool) -> InterpResult<'tcx, Pointer> {
let this = self.eval_context_mut();
let align = this.malloc_align(size);
let ptr = this.allocate_ptr(Size::from_bytes(size), align, MiriMemoryKind::C.into())?;
@ -113,10 +109,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn posix_memalign(
&mut self,
memptr: &OpTy<'tcx, Provenance>,
align: &OpTy<'tcx, Provenance>,
size: &OpTy<'tcx, Provenance>,
) -> InterpResult<'tcx, Scalar<Provenance>> {
memptr: &OpTy<'tcx>,
align: &OpTy<'tcx>,
size: &OpTy<'tcx>,
) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();
let memptr = this.deref_pointer(memptr)?;
let align = this.read_target_usize(align)?;
@ -137,7 +133,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
}
fn free(&mut self, ptr: Pointer<Option<Provenance>>) -> InterpResult<'tcx> {
fn free(&mut self, ptr: Pointer) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
if !this.ptr_is_null(ptr)? {
this.deallocate_ptr(ptr, None, MiriMemoryKind::C.into())?;
@ -145,11 +141,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
Ok(())
}
fn realloc(
&mut self,
old_ptr: Pointer<Option<Provenance>>,
new_size: u64,
) -> InterpResult<'tcx, Pointer<Option<Provenance>>> {
fn realloc(&mut self, old_ptr: Pointer, new_size: u64) -> InterpResult<'tcx, Pointer> {
let this = self.eval_context_mut();
let new_align = this.malloc_align(new_size);
if this.ptr_is_null(old_ptr)? {
@ -175,9 +167,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn aligned_alloc(
&mut self,
align: &OpTy<'tcx, Provenance>,
size: &OpTy<'tcx, Provenance>,
) -> InterpResult<'tcx, Pointer<Option<Provenance>>> {
align: &OpTy<'tcx>,
size: &OpTy<'tcx>,
) -> InterpResult<'tcx, Pointer> {
let this = self.eval_context_mut();
let align = this.read_target_usize(align)?;
let size = this.read_target_usize(size)?;

View file

@ -11,8 +11,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
&mut self,
abi: Abi,
link_name: Symbol,
args: &[OpTy<'tcx, Provenance>],
dest: &MPlaceTy<'tcx, Provenance>,
args: &[OpTy<'tcx>],
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
let [flags] = this.check_shim(abi, Abi::Rust, link_name, args)?;
@ -31,8 +31,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
&mut self,
abi: Abi,
link_name: Symbol,
args: &[OpTy<'tcx, Provenance>],
dest: &MPlaceTy<'tcx, Provenance>,
args: &[OpTy<'tcx>],
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
let tcx = this.tcx;
@ -110,7 +110,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn resolve_frame_pointer(
&mut self,
ptr: &OpTy<'tcx, Provenance>,
ptr: &OpTy<'tcx>,
) -> InterpResult<'tcx, (Instance<'tcx>, Loc, String, String)> {
let this = self.eval_context_mut();
@ -140,8 +140,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
&mut self,
abi: Abi,
link_name: Symbol,
args: &[OpTy<'tcx, Provenance>],
dest: &MPlaceTy<'tcx, Provenance>,
args: &[OpTy<'tcx>],
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
let [ptr, flags] = this.check_shim(abi, Abi::Rust, link_name, args)?;
@ -218,7 +218,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
&mut self,
abi: Abi,
link_name: Symbol,
args: &[OpTy<'tcx, Provenance>],
args: &[OpTy<'tcx>],
) -> InterpResult<'tcx> {
let this = self.eval_context_mut();

View file

@ -6,7 +6,7 @@ impl<'tcx> MiriMachine<'tcx> {
fn alloc_extern_static(
this: &mut MiriInterpCx<'tcx>,
name: &str,
val: ImmTy<'tcx, Provenance>,
val: ImmTy<'tcx>,
) -> InterpResult<'tcx> {
let place = this.allocate(val.layout, MiriMemoryKind::ExternStatic.into())?;
this.write_immediate(*val, &place)?;

View file

@ -40,8 +40,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
&mut self,
link_name: Symbol,
abi: Abi,
args: &[OpTy<'tcx, Provenance>],
dest: &MPlaceTy<'tcx, Provenance>,
args: &[OpTy<'tcx>],
dest: &MPlaceTy<'tcx>,
ret: Option<mir::BasicBlock>,
unwind: mir::UnwindAction,
) -> InterpResult<'tcx, Option<(&'tcx mir::Body<'tcx>, ty::Instance<'tcx>)>> {
@ -123,8 +123,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
&mut self,
sym: DynSym,
abi: Abi,
args: &[OpTy<'tcx, Provenance>],
dest: &MPlaceTy<'tcx, Provenance>,
args: &[OpTy<'tcx>],
dest: &MPlaceTy<'tcx>,
ret: Option<mir::BasicBlock>,
unwind: mir::UnwindAction,
) -> InterpResult<'tcx> {
@ -208,8 +208,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
&mut self,
link_name: Symbol,
abi: Abi,
args: &[OpTy<'tcx, Provenance>],
dest: &MPlaceTy<'tcx, Provenance>,
args: &[OpTy<'tcx>],
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx, EmulateItemResult> {
let this = self.eval_context_mut();
@ -238,11 +238,11 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
// ```
// fn shim_name(
// &mut self,
// arg1: &OpTy<'tcx, Provenance>,
// arg2: &OpTy<'tcx, Provenance>,
// arg3: &OpTy<'tcx, Provenance>,
// arg4: &OpTy<'tcx, Provenance>)
// -> InterpResult<'tcx, Scalar<Provenance>> {
// arg1: &OpTy<'tcx>,
// arg2: &OpTy<'tcx>,
// arg3: &OpTy<'tcx>,
// arg4: &OpTy<'tcx>)
// -> InterpResult<'tcx, Scalar> {
// let this = self.eval_context_mut();
//
// // First thing: load all the arguments. Details depend on the shim.

View file

@ -14,10 +14,10 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn call_native_with_args<'a>(
&mut self,
link_name: Symbol,
dest: &MPlaceTy<'tcx, Provenance>,
dest: &MPlaceTy<'tcx>,
ptr: CodePtr,
libffi_args: Vec<libffi::high::Arg<'a>>,
) -> InterpResult<'tcx, ImmTy<'tcx, Provenance>> {
) -> InterpResult<'tcx, ImmTy<'tcx>> {
let this = self.eval_context_mut();
// Call the function (`ptr`) with arguments `libffi_args`, and obtain the return value
@ -132,8 +132,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn call_native_fn(
&mut self,
link_name: Symbol,
dest: &MPlaceTy<'tcx, Provenance>,
args: &[OpTy<'tcx, Provenance>],
dest: &MPlaceTy<'tcx>,
args: &[OpTy<'tcx>],
) -> InterpResult<'tcx, bool> {
let this = self.eval_context_mut();
// Get the pointer to the function in the shared object file if it exists.
@ -216,10 +216,7 @@ impl<'a> CArg {
/// Extract the scalar value from the result of reading a scalar from the machine,
/// and convert it to a `CArg`.
fn imm_to_carg<'tcx>(
v: ImmTy<'tcx, Provenance>,
cx: &impl HasDataLayout,
) -> InterpResult<'tcx, CArg> {
fn imm_to_carg<'tcx>(v: ImmTy<'tcx>, cx: &impl HasDataLayout) -> InterpResult<'tcx, CArg> {
Ok(match v.layout.ty.kind() {
// If the primitive provided can be converted to a type matching the type pattern
// then create a `CArg` of this primitive value with the corresponding `CArg` constructor.

View file

@ -34,10 +34,7 @@ impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
/// Helper function to read an OsString from a null-terminated sequence of bytes, which is what
/// the Unix APIs usually handle.
fn read_os_str_from_c_str<'a>(
&'a self,
ptr: Pointer<Option<Provenance>>,
) -> InterpResult<'tcx, &'a OsStr>
fn read_os_str_from_c_str<'a>(&'a self, ptr: Pointer) -> InterpResult<'tcx, &'a OsStr>
where
'tcx: 'a,
{
@ -48,10 +45,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
/// Helper function to read an OsString from a 0x0000-terminated sequence of u16,
/// which is what the Windows APIs usually handle.
fn read_os_str_from_wide_str<'a>(
&'a self,
ptr: Pointer<Option<Provenance>>,
) -> InterpResult<'tcx, OsString>
fn read_os_str_from_wide_str<'a>(&'a self, ptr: Pointer) -> InterpResult<'tcx, OsString>
where
'tcx: 'a,
{
@ -76,7 +70,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn write_os_str_to_c_str(
&mut self,
os_str: &OsStr,
ptr: Pointer<Option<Provenance>>,
ptr: Pointer,
size: u64,
) -> InterpResult<'tcx, (bool, u64)> {
let bytes = os_str.as_encoded_bytes();
@ -88,7 +82,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn write_os_str_to_wide_str_helper(
&mut self,
os_str: &OsStr,
ptr: Pointer<Option<Provenance>>,
ptr: Pointer,
size: u64,
truncate: bool,
) -> InterpResult<'tcx, (bool, u64)> {
@ -125,7 +119,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn write_os_str_to_wide_str(
&mut self,
os_str: &OsStr,
ptr: Pointer<Option<Provenance>>,
ptr: Pointer,
size: u64,
) -> InterpResult<'tcx, (bool, u64)> {
self.write_os_str_to_wide_str_helper(os_str, ptr, size, /*truncate*/ false)
@ -136,7 +130,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn write_os_str_to_wide_str_truncated(
&mut self,
os_str: &OsStr,
ptr: Pointer<Option<Provenance>>,
ptr: Pointer,
size: u64,
) -> InterpResult<'tcx, (bool, u64)> {
self.write_os_str_to_wide_str_helper(os_str, ptr, size, /*truncate*/ true)
@ -147,7 +141,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
&mut self,
os_str: &OsStr,
memkind: MemoryKind,
) -> InterpResult<'tcx, Pointer<Option<Provenance>>> {
) -> InterpResult<'tcx, Pointer> {
let size = u64::try_from(os_str.len()).unwrap().checked_add(1).unwrap(); // Make space for `0` terminator.
let this = self.eval_context_mut();
@ -163,7 +157,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
&mut self,
os_str: &OsStr,
memkind: MemoryKind,
) -> InterpResult<'tcx, Pointer<Option<Provenance>>> {
) -> InterpResult<'tcx, Pointer> {
let size = u64::try_from(os_str.len()).unwrap().checked_add(1).unwrap(); // Make space for `0x0000` terminator.
let this = self.eval_context_mut();
@ -175,10 +169,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
/// Read a null-terminated sequence of bytes, and perform path separator conversion if needed.
fn read_path_from_c_str<'a>(
&'a self,
ptr: Pointer<Option<Provenance>>,
) -> InterpResult<'tcx, Cow<'a, Path>>
fn read_path_from_c_str<'a>(&'a self, ptr: Pointer) -> InterpResult<'tcx, Cow<'a, Path>>
where
'tcx: 'a,
{
@ -192,10 +183,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
/// Read a null-terminated sequence of `u16`s, and perform path separator conversion if needed.
fn read_path_from_wide_str(
&self,
ptr: Pointer<Option<Provenance>>,
) -> InterpResult<'tcx, PathBuf> {
fn read_path_from_wide_str(&self, ptr: Pointer) -> InterpResult<'tcx, PathBuf> {
let this = self.eval_context_ref();
let os_str = this.read_os_str_from_wide_str(ptr)?;
@ -207,7 +195,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn write_path_to_c_str(
&mut self,
path: &Path,
ptr: Pointer<Option<Provenance>>,
ptr: Pointer,
size: u64,
) -> InterpResult<'tcx, (bool, u64)> {
let this = self.eval_context_mut();
@ -221,7 +209,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn write_path_to_wide_str(
&mut self,
path: &Path,
ptr: Pointer<Option<Provenance>>,
ptr: Pointer,
size: u64,
) -> InterpResult<'tcx, (bool, u64)> {
let this = self.eval_context_mut();
@ -235,7 +223,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn write_path_to_wide_str_truncated(
&mut self,
path: &Path,
ptr: Pointer<Option<Provenance>>,
ptr: Pointer,
size: u64,
) -> InterpResult<'tcx, (bool, u64)> {
let this = self.eval_context_mut();
@ -250,7 +238,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
&mut self,
path: &Path,
memkind: MemoryKind,
) -> InterpResult<'tcx, Pointer<Option<Provenance>>> {
) -> InterpResult<'tcx, Pointer> {
let this = self.eval_context_mut();
let os_str =
this.convert_path(Cow::Borrowed(path.as_os_str()), PathConversion::HostToTarget);
@ -263,7 +251,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
&mut self,
path: &Path,
memkind: MemoryKind,
) -> InterpResult<'tcx, Pointer<Option<Provenance>>> {
) -> InterpResult<'tcx, Pointer> {
let this = self.eval_context_mut();
let os_str =
this.convert_path(Cow::Borrowed(path.as_os_str()), PathConversion::HostToTarget);

View file

@ -23,11 +23,11 @@ use helpers::check_arg_count;
#[derive(Debug)]
pub struct CatchUnwindData<'tcx> {
/// The `catch_fn` callback to call in case of a panic.
catch_fn: Pointer<Option<Provenance>>,
catch_fn: Pointer,
/// The `data` argument for that callback.
data: Scalar<Provenance>,
data: Scalar,
/// The return place from the original call to `try`.
dest: MPlaceTy<'tcx, Provenance>,
dest: MPlaceTy<'tcx>,
/// The return block from the original call to `try`.
ret: Option<mir::BasicBlock>,
}
@ -45,7 +45,7 @@ impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
/// Handles the special `miri_start_unwind` intrinsic, which is called
/// by libpanic_unwind to delegate the actual unwinding process to Miri.
fn handle_miri_start_unwind(&mut self, payload: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx> {
fn handle_miri_start_unwind(&mut self, payload: &OpTy<'tcx>) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
trace!("miri_start_unwind: {:?}", this.frame().instance);
@ -60,8 +60,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
/// Handles the `try` intrinsic, the underlying implementation of `std::panicking::try`.
fn handle_catch_unwind(
&mut self,
args: &[OpTy<'tcx, Provenance>],
dest: &MPlaceTy<'tcx, Provenance>,
args: &[OpTy<'tcx>],
dest: &MPlaceTy<'tcx>,
ret: Option<mir::BasicBlock>,
) -> InterpResult<'tcx> {
let this = self.eval_context_mut();

View file

@ -18,9 +18,9 @@ impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn clock_gettime(
&mut self,
clk_id_op: &OpTy<'tcx, Provenance>,
tp_op: &OpTy<'tcx, Provenance>,
) -> InterpResult<'tcx, Scalar<Provenance>> {
clk_id_op: &OpTy<'tcx>,
tp_op: &OpTy<'tcx>,
) -> InterpResult<'tcx, Scalar> {
// This clock support is deliberately minimal because a lot of clock types have fiddly
// properties (is it possible for Miri to be suspended independently of the host?). If you
// have a use for another clock type, please open an issue.
@ -78,7 +78,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.check_no_isolation("`clock_gettime` with `REALTIME` clocks")?;
system_time_to_duration(&SystemTime::now())?
} else if relative_clocks.contains(&clk_id) {
this.machine.clock.now().duration_since(this.machine.clock.anchor())
this.machine.clock.now().duration_since(this.machine.clock.epoch())
} else {
let einval = this.eval_libc("EINVAL");
this.set_last_error(einval)?;
@ -93,11 +93,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
Ok(Scalar::from_i32(0))
}
fn gettimeofday(
&mut self,
tv_op: &OpTy<'tcx, Provenance>,
tz_op: &OpTy<'tcx, Provenance>,
) -> InterpResult<'tcx, i32> {
fn gettimeofday(&mut self, tv_op: &OpTy<'tcx>, tz_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();
this.assert_target_os_is_unix("gettimeofday");
@ -127,9 +123,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// https://linux.die.net/man/3/localtime_r
fn localtime_r(
&mut self,
timep: &OpTy<'tcx, Provenance>,
result_op: &OpTy<'tcx, Provenance>,
) -> InterpResult<'tcx, Pointer<Option<Provenance>>> {
timep: &OpTy<'tcx>,
result_op: &OpTy<'tcx>,
) -> InterpResult<'tcx, Pointer> {
let this = self.eval_context_mut();
this.assert_target_os_is_unix("localtime_r");
@ -212,7 +208,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn GetSystemTimeAsFileTime(
&mut self,
shim_name: &str,
LPFILETIME_op: &OpTy<'tcx, Provenance>,
LPFILETIME_op: &OpTy<'tcx>,
) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
@ -242,15 +238,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
#[allow(non_snake_case)]
fn QueryPerformanceCounter(
&mut self,
lpPerformanceCount_op: &OpTy<'tcx, Provenance>,
) -> InterpResult<'tcx, Scalar<Provenance>> {
lpPerformanceCount_op: &OpTy<'tcx>,
) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();
this.assert_target_os("windows", "QueryPerformanceCounter");
// QueryPerformanceCounter uses a hardware counter as its basis.
// Miri will emulate a counter with a resolution of 1 nanosecond.
let duration = this.machine.clock.now().duration_since(this.machine.clock.anchor());
let duration = this.machine.clock.now().duration_since(this.machine.clock.epoch());
let qpc = i64::try_from(duration.as_nanos()).map_err(|_| {
err_unsup_format!("programs running longer than 2^63 nanoseconds are not supported")
})?;
@ -261,8 +257,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
#[allow(non_snake_case)]
fn QueryPerformanceFrequency(
&mut self,
lpFrequency_op: &OpTy<'tcx, Provenance>,
) -> InterpResult<'tcx, Scalar<Provenance>> {
lpFrequency_op: &OpTy<'tcx>,
) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();
this.assert_target_os("windows", "QueryPerformanceFrequency");
@ -279,24 +275,21 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
Ok(Scalar::from_i32(-1)) // Return non-zero on success
}
fn mach_absolute_time(&self) -> InterpResult<'tcx, Scalar<Provenance>> {
fn mach_absolute_time(&self) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_ref();
this.assert_target_os("macos", "mach_absolute_time");
// This returns a u64, with time units determined dynamically by `mach_timebase_info`.
// We return plain nanoseconds.
let duration = this.machine.clock.now().duration_since(this.machine.clock.anchor());
let duration = this.machine.clock.now().duration_since(this.machine.clock.epoch());
let res = u64::try_from(duration.as_nanos()).map_err(|_| {
err_unsup_format!("programs running longer than 2^64 nanoseconds are not supported")
})?;
Ok(Scalar::from_u64(res))
}
fn mach_timebase_info(
&mut self,
info_op: &OpTy<'tcx, Provenance>,
) -> InterpResult<'tcx, Scalar<Provenance>> {
fn mach_timebase_info(&mut self, info_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();
this.assert_target_os("macos", "mach_timebase_info");
@ -313,8 +306,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn nanosleep(
&mut self,
req_op: &OpTy<'tcx, Provenance>,
_rem: &OpTy<'tcx, Provenance>, // Signal handlers are not supported, so rem will never be written to.
req_op: &OpTy<'tcx>,
_rem: &OpTy<'tcx>, // Signal handlers are not supported, so rem will never be written to.
) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();
@ -330,16 +323,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
return Ok(-1);
}
};
// If adding the duration overflows, let's just sleep for an hour. Waking up early is always acceptable.
let now = this.machine.clock.now();
let timeout_time = now
.checked_add(duration)
.unwrap_or_else(|| now.checked_add(Duration::from_secs(3600)).unwrap());
let timeout_time = Timeout::Monotonic(timeout_time);
this.block_thread(
BlockReason::Sleep,
Some(timeout_time),
Some((TimeoutClock::Monotonic, TimeoutAnchor::Relative, duration)),
callback!(
@capture<'tcx> {}
@unblock = |_this| { panic!("sleeping thread unblocked before time is up") }
@ -350,7 +337,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
#[allow(non_snake_case)]
fn Sleep(&mut self, timeout: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx> {
fn Sleep(&mut self, timeout: &OpTy<'tcx>) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
this.assert_target_os("windows", "Sleep");
@ -358,12 +345,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let timeout_ms = this.read_scalar(timeout)?.to_u32()?;
let duration = Duration::from_millis(timeout_ms.into());
let timeout_time = this.machine.clock.now().checked_add(duration).unwrap();
let timeout_time = Timeout::Monotonic(timeout_time);
this.block_thread(
BlockReason::Sleep,
Some(timeout_time),
Some((TimeoutClock::Monotonic, TimeoutAnchor::Relative, duration)),
callback!(
@capture<'tcx> {}
@unblock = |_this| { panic!("sleeping thread unblocked before time is up") }

View file

@ -16,7 +16,7 @@ pub type TlsKey = u128;
pub struct TlsEntry<'tcx> {
/// The data for this key. None is used to represent NULL.
/// (We normalize this early to avoid having to do a NULL-ptr-test each time we access the data.)
data: BTreeMap<ThreadId, Scalar<Provenance>>,
data: BTreeMap<ThreadId, Scalar>,
dtor: Option<ty::Instance<'tcx>>,
}
@ -38,7 +38,7 @@ pub struct TlsData<'tcx> {
/// A single per thread destructor of the thread local storage (that's how
/// things work on macOS) with a data argument.
macos_thread_dtors: BTreeMap<ThreadId, (ty::Instance<'tcx>, Scalar<Provenance>)>,
macos_thread_dtors: BTreeMap<ThreadId, (ty::Instance<'tcx>, Scalar)>,
}
impl<'tcx> Default for TlsData<'tcx> {
@ -86,7 +86,7 @@ impl<'tcx> TlsData<'tcx> {
key: TlsKey,
thread_id: ThreadId,
cx: &impl HasDataLayout,
) -> InterpResult<'tcx, Scalar<Provenance>> {
) -> InterpResult<'tcx, Scalar> {
match self.keys.get(&key) {
Some(TlsEntry { data, .. }) => {
let value = data.get(&thread_id).copied();
@ -101,7 +101,7 @@ impl<'tcx> TlsData<'tcx> {
&mut self,
key: TlsKey,
thread_id: ThreadId,
new_data: Scalar<Provenance>,
new_data: Scalar,
cx: &impl HasDataLayout,
) -> InterpResult<'tcx> {
match self.keys.get_mut(&key) {
@ -132,7 +132,7 @@ impl<'tcx> TlsData<'tcx> {
&mut self,
thread: ThreadId,
dtor: ty::Instance<'tcx>,
data: Scalar<Provenance>,
data: Scalar,
) -> InterpResult<'tcx> {
if self.macos_thread_dtors.insert(thread, (dtor, data)).is_some() {
throw_unsup_format!(
@ -165,7 +165,7 @@ impl<'tcx> TlsData<'tcx> {
&mut self,
key: Option<TlsKey>,
thread_id: ThreadId,
) -> Option<(ty::Instance<'tcx>, Scalar<Provenance>, TlsKey)> {
) -> Option<(ty::Instance<'tcx>, Scalar, TlsKey)> {
use std::ops::Bound::*;
let thread_local = &mut self.keys;
@ -228,7 +228,7 @@ enum TlsDtorsStatePriv<'tcx> {
PthreadDtors(RunningDtorState),
/// For Windows Dtors, we store the list of functions that we still have to call.
/// These are functions from the magic `.CRT$XLB` linker section.
WindowsDtors(Vec<ImmTy<'tcx, Provenance>>),
WindowsDtors(Vec<ImmTy<'tcx>>),
Done,
}
@ -297,7 +297,7 @@ impl<'tcx> EvalContextPrivExt<'tcx> for crate::MiriInterpCx<'tcx> {}
trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
/// Schedule TLS destructors for Windows.
/// On windows, TLS destructors are managed by std.
fn lookup_windows_tls_dtors(&mut self) -> InterpResult<'tcx, Vec<ImmTy<'tcx, Provenance>>> {
fn lookup_windows_tls_dtors(&mut self) -> InterpResult<'tcx, Vec<ImmTy<'tcx>>> {
let this = self.eval_context_mut();
// Windows has a special magic linker section that is run on certain events.
@ -305,7 +305,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
Ok(this.lookup_link_section(".CRT$XLB")?)
}
fn schedule_windows_tls_dtor(&mut self, dtor: ImmTy<'tcx, Provenance>) -> InterpResult<'tcx> {
fn schedule_windows_tls_dtor(&mut self, dtor: ImmTy<'tcx>) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
let dtor = dtor.to_scalar().to_pointer(this)?;

View file

@ -13,8 +13,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
&mut self,
link_name: Symbol,
abi: Abi,
args: &[OpTy<'tcx, Provenance>],
dest: &MPlaceTy<'tcx, Provenance>,
args: &[OpTy<'tcx>],
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx, EmulateItemResult> {
let this = self.eval_context_mut();
match link_name.as_str() {

View file

@ -13,10 +13,10 @@ use crate::*;
pub struct UnixEnvVars<'tcx> {
/// Stores pointers to the environment variables. These variables must be stored as
/// null-terminated target strings (c_str or wide_str) with the `"{name}={value}"` format.
map: FxHashMap<OsString, Pointer<Option<Provenance>>>,
map: FxHashMap<OsString, Pointer>,
/// Place where the `environ` static is stored. Lazily initialized, but then never changes.
environ: MPlaceTy<'tcx, Provenance>,
environ: MPlaceTy<'tcx>,
}
impl VisitProvenance for UnixEnvVars<'_> {
@ -65,7 +65,7 @@ impl<'tcx> UnixEnvVars<'tcx> {
Ok(())
}
pub(crate) fn environ(&self) -> Pointer<Option<Provenance>> {
pub(crate) fn environ(&self) -> Pointer {
self.environ.ptr()
}
@ -73,7 +73,7 @@ impl<'tcx> UnixEnvVars<'tcx> {
&self,
ecx: &InterpCx<'tcx, MiriMachine<'tcx>>,
name: &OsStr,
) -> InterpResult<'tcx, Option<Pointer<Option<Provenance>>>> {
) -> InterpResult<'tcx, Option<Pointer>> {
// We don't care about the value as we have the `map` to keep track of everything,
// but we do want to do this read so it shows up as a data race.
let _vars_ptr = ecx.read_pointer(&self.environ)?;
@ -109,7 +109,7 @@ fn alloc_env_var<'tcx>(
ecx: &mut InterpCx<'tcx, MiriMachine<'tcx>>,
name: &OsStr,
value: &OsStr,
) -> InterpResult<'tcx, Pointer<Option<Provenance>>> {
) -> InterpResult<'tcx, Pointer> {
let mut name_osstring = name.to_os_string();
name_osstring.push("=");
name_osstring.push(value);
@ -119,8 +119,8 @@ fn alloc_env_var<'tcx>(
/// Allocates an `environ` block with the given list of pointers.
fn alloc_environ_block<'tcx>(
ecx: &mut InterpCx<'tcx, MiriMachine<'tcx>>,
mut vars: Vec<Pointer<Option<Provenance>>>,
) -> InterpResult<'tcx, Pointer<Option<Provenance>>> {
mut vars: Vec<Pointer>,
) -> InterpResult<'tcx, Pointer> {
// Add trailing null.
vars.push(Pointer::null());
// Make an array with all these pointers inside Miri.
@ -139,10 +139,7 @@ fn alloc_environ_block<'tcx>(
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn getenv(
&mut self,
name_op: &OpTy<'tcx, Provenance>,
) -> InterpResult<'tcx, Pointer<Option<Provenance>>> {
fn getenv(&mut self, name_op: &OpTy<'tcx>) -> InterpResult<'tcx, Pointer> {
let this = self.eval_context_mut();
this.assert_target_os_is_unix("getenv");
@ -153,11 +150,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
Ok(var_ptr.unwrap_or_else(Pointer::null))
}
fn setenv(
&mut self,
name_op: &OpTy<'tcx, Provenance>,
value_op: &OpTy<'tcx, Provenance>,
) -> InterpResult<'tcx, i32> {
fn setenv(&mut self, name_op: &OpTy<'tcx>, value_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();
this.assert_target_os_is_unix("setenv");
@ -187,7 +180,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
}
fn unsetenv(&mut self, name_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx, i32> {
fn unsetenv(&mut self, name_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();
this.assert_target_os_is_unix("unsetenv");
@ -213,11 +206,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
}
fn getcwd(
&mut self,
buf_op: &OpTy<'tcx, Provenance>,
size_op: &OpTy<'tcx, Provenance>,
) -> InterpResult<'tcx, Pointer<Option<Provenance>>> {
fn getcwd(&mut self, buf_op: &OpTy<'tcx>, size_op: &OpTy<'tcx>) -> InterpResult<'tcx, Pointer> {
let this = self.eval_context_mut();
this.assert_target_os_is_unix("getcwd");
@ -245,7 +234,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
Ok(Pointer::null())
}
fn chdir(&mut self, path_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx, i32> {
fn chdir(&mut self, path_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();
this.assert_target_os_is_unix("chdir");

View file

@ -273,7 +273,7 @@ impl FdTable {
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn fcntl(&mut self, args: &[OpTy<'tcx, Provenance>]) -> InterpResult<'tcx, i32> {
fn fcntl(&mut self, args: &[OpTy<'tcx>]) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();
if args.len() < 2 {
@ -329,7 +329,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
}
fn close(&mut self, fd_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx, Scalar<Provenance>> {
fn close(&mut self, fd_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();
let fd = this.read_scalar(fd_op)?.to_i32()?;
@ -355,12 +355,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
Ok((-1).into())
}
fn read(
&mut self,
fd: i32,
buf: Pointer<Option<Provenance>>,
count: u64,
) -> InterpResult<'tcx, i64> {
fn read(&mut self, fd: i32, buf: Pointer, count: u64) -> InterpResult<'tcx, i64> {
let this = self.eval_context_mut();
// Isolation check is done via `FileDescriptor` trait.
@ -409,12 +404,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
}
fn write(
&mut self,
fd: i32,
buf: Pointer<Option<Provenance>>,
count: u64,
) -> InterpResult<'tcx, i64> {
fn write(&mut self, fd: i32, buf: Pointer, count: u64) -> InterpResult<'tcx, i64> {
let this = self.eval_context_mut();
// Isolation check is done via `FileDescriptor` trait.

View file

@ -43,8 +43,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
&mut self,
link_name: Symbol,
abi: Abi,
args: &[OpTy<'tcx, Provenance>],
dest: &MPlaceTy<'tcx, Provenance>,
args: &[OpTy<'tcx>],
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx, EmulateItemResult> {
let this = self.eval_context_mut();
@ -326,7 +326,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let name = this.read_scalar(name)?.to_i32()?;
// FIXME: Which of these are POSIX, and which are GNU/Linux?
// At least the names seem to all also exist on macOS.
let sysconfs: &[(&str, fn(&MiriInterpCx<'_>) -> Scalar<Provenance>)] = &[
let sysconfs: &[(&str, fn(&MiriInterpCx<'_>) -> Scalar)] = &[
("_SC_PAGESIZE", |this| Scalar::from_int(this.machine.page_size, this.pointer_size())),
("_SC_NPROCESSORS_CONF", |this| Scalar::from_int(this.machine.num_cpus, this.pointer_size())),
("_SC_NPROCESSORS_ONLN", |this| Scalar::from_int(this.machine.num_cpus, this.pointer_size())),

View file

@ -14,8 +14,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
&mut self,
link_name: Symbol,
abi: Abi,
args: &[OpTy<'tcx, Provenance>],
dest: &MPlaceTy<'tcx, Provenance>,
args: &[OpTy<'tcx>],
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx, EmulateItemResult> {
let this = self.eval_context_mut();
match link_name.as_str() {

View file

@ -91,7 +91,7 @@ trait EvalContextExtPrivate<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn macos_stat_write_buf(
&mut self,
metadata: FileMetadata,
buf_op: &OpTy<'tcx, Provenance>,
buf_op: &OpTy<'tcx>,
) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();
@ -180,7 +180,7 @@ struct OpenDir {
read_dir: ReadDir,
/// The most recent entry returned by readdir().
/// Will be freed by the next call.
entry: Option<Pointer<Option<Provenance>>>,
entry: Option<Pointer>,
}
impl OpenDir {
@ -256,7 +256,7 @@ fn maybe_sync_file(
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn open(&mut self, args: &[OpTy<'tcx, Provenance>]) -> InterpResult<'tcx, i32> {
fn open(&mut self, args: &[OpTy<'tcx>]) -> InterpResult<'tcx, i32> {
if args.len() < 2 {
throw_ub_format!(
"incorrect number of arguments for `open`: got {}, expected at least 2",
@ -389,12 +389,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.try_unwrap_io_result(fd)
}
fn lseek64(
&mut self,
fd: i32,
offset: i128,
whence: i32,
) -> InterpResult<'tcx, Scalar<Provenance>> {
fn lseek64(&mut self, fd: i32, offset: i128, whence: i32) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();
// Isolation check is done via `FileDescriptor` trait.
@ -425,7 +420,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
Ok(Scalar::from_i64(result))
}
fn unlink(&mut self, path_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx, i32> {
fn unlink(&mut self, path_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();
let path = this.read_path_from_c_str(this.read_pointer(path_op)?)?;
@ -443,8 +438,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn symlink(
&mut self,
target_op: &OpTy<'tcx, Provenance>,
linkpath_op: &OpTy<'tcx, Provenance>,
target_op: &OpTy<'tcx>,
linkpath_op: &OpTy<'tcx>,
) -> InterpResult<'tcx, i32> {
#[cfg(unix)]
fn create_link(src: &Path, dst: &Path) -> std::io::Result<()> {
@ -474,9 +469,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn macos_fbsd_stat(
&mut self,
path_op: &OpTy<'tcx, Provenance>,
buf_op: &OpTy<'tcx, Provenance>,
) -> InterpResult<'tcx, Scalar<Provenance>> {
path_op: &OpTy<'tcx>,
buf_op: &OpTy<'tcx>,
) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();
if !matches!(&*this.tcx.sess.target.os, "macos" | "freebsd") {
@ -506,9 +501,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// `lstat` is used to get symlink metadata.
fn macos_fbsd_lstat(
&mut self,
path_op: &OpTy<'tcx, Provenance>,
buf_op: &OpTy<'tcx, Provenance>,
) -> InterpResult<'tcx, Scalar<Provenance>> {
path_op: &OpTy<'tcx>,
buf_op: &OpTy<'tcx>,
) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();
if !matches!(&*this.tcx.sess.target.os, "macos" | "freebsd") {
@ -536,9 +531,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn macos_fbsd_fstat(
&mut self,
fd_op: &OpTy<'tcx, Provenance>,
buf_op: &OpTy<'tcx, Provenance>,
) -> InterpResult<'tcx, Scalar<Provenance>> {
fd_op: &OpTy<'tcx>,
buf_op: &OpTy<'tcx>,
) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();
if !matches!(&*this.tcx.sess.target.os, "macos" | "freebsd") {
@ -563,11 +558,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn linux_statx(
&mut self,
dirfd_op: &OpTy<'tcx, Provenance>, // Should be an `int`
pathname_op: &OpTy<'tcx, Provenance>, // Should be a `const char *`
flags_op: &OpTy<'tcx, Provenance>, // Should be an `int`
mask_op: &OpTy<'tcx, Provenance>, // Should be an `unsigned int`
statxbuf_op: &OpTy<'tcx, Provenance>, // Should be a `struct statx *`
dirfd_op: &OpTy<'tcx>, // Should be an `int`
pathname_op: &OpTy<'tcx>, // Should be a `const char *`
flags_op: &OpTy<'tcx>, // Should be an `int`
mask_op: &OpTy<'tcx>, // Should be an `unsigned int`
statxbuf_op: &OpTy<'tcx>, // Should be a `struct statx *`
) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();
@ -745,8 +740,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn rename(
&mut self,
oldpath_op: &OpTy<'tcx, Provenance>,
newpath_op: &OpTy<'tcx, Provenance>,
oldpath_op: &OpTy<'tcx>,
newpath_op: &OpTy<'tcx>,
) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();
@ -774,11 +769,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.try_unwrap_io_result(result)
}
fn mkdir(
&mut self,
path_op: &OpTy<'tcx, Provenance>,
mode_op: &OpTy<'tcx, Provenance>,
) -> InterpResult<'tcx, i32> {
fn mkdir(&mut self, path_op: &OpTy<'tcx>, mode_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();
#[cfg_attr(not(unix), allow(unused_variables))]
@ -813,7 +804,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.try_unwrap_io_result(result)
}
fn rmdir(&mut self, path_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx, i32> {
fn rmdir(&mut self, path_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();
let path = this.read_path_from_c_str(this.read_pointer(path_op)?)?;
@ -830,10 +821,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.try_unwrap_io_result(result)
}
fn opendir(
&mut self,
name_op: &OpTy<'tcx, Provenance>,
) -> InterpResult<'tcx, Scalar<Provenance>> {
fn opendir(&mut self, name_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();
let name = this.read_path_from_c_str(this.read_pointer(name_op)?)?;
@ -864,10 +852,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
}
fn linux_readdir64(
&mut self,
dirp_op: &OpTy<'tcx, Provenance>,
) -> InterpResult<'tcx, Scalar<Provenance>> {
fn linux_readdir64(&mut self, dirp_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();
this.assert_target_os("linux", "readdir64");
@ -915,7 +900,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
dirent64_layout.align.abi,
MiriMemoryKind::Runtime.into(),
)?;
let entry: Pointer<Option<Provenance>> = entry.into();
let entry: Pointer = entry.into();
// If the host is a Unix system, fill in the inode number with its real value.
// If not, use 0 as a fallback value.
@ -962,10 +947,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn macos_fbsd_readdir_r(
&mut self,
dirp_op: &OpTy<'tcx, Provenance>,
entry_op: &OpTy<'tcx, Provenance>,
result_op: &OpTy<'tcx, Provenance>,
) -> InterpResult<'tcx, Scalar<Provenance>> {
dirp_op: &OpTy<'tcx>,
entry_op: &OpTy<'tcx>,
result_op: &OpTy<'tcx>,
) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();
if !matches!(&*this.tcx.sess.target.os, "macos" | "freebsd") {
@ -1083,7 +1068,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}))
}
fn closedir(&mut self, dirp_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx, i32> {
fn closedir(&mut self, dirp_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();
let dirp = this.read_target_usize(dirp_op)?;
@ -1106,7 +1091,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
}
fn ftruncate64(&mut self, fd: i32, length: i128) -> InterpResult<'tcx, Scalar<Provenance>> {
fn ftruncate64(&mut self, fd: i32, length: i128) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();
// Reject if isolation is enabled.
@ -1147,7 +1132,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
}
fn fsync(&mut self, fd_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx, i32> {
fn fsync(&mut self, fd_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> {
// On macOS, `fsync` (unlike `fcntl(F_FULLFSYNC)`) does not wait for the
// underlying disk to finish writing. In the interest of host compatibility,
// we conservatively implement this with `sync_all`, which
@ -1182,7 +1167,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.try_unwrap_io_result(io_result)
}
fn fdatasync(&mut self, fd_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx, i32> {
fn fdatasync(&mut self, fd_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();
let fd = this.read_scalar(fd_op)?.to_i32()?;
@ -1209,11 +1194,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn sync_file_range(
&mut self,
fd_op: &OpTy<'tcx, Provenance>,
offset_op: &OpTy<'tcx, Provenance>,
nbytes_op: &OpTy<'tcx, Provenance>,
flags_op: &OpTy<'tcx, Provenance>,
) -> InterpResult<'tcx, Scalar<Provenance>> {
fd_op: &OpTy<'tcx>,
offset_op: &OpTy<'tcx>,
nbytes_op: &OpTy<'tcx>,
flags_op: &OpTy<'tcx>,
) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();
let fd = this.read_scalar(fd_op)?.to_i32()?;
@ -1259,9 +1244,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn readlink(
&mut self,
pathname_op: &OpTy<'tcx, Provenance>,
buf_op: &OpTy<'tcx, Provenance>,
bufsize_op: &OpTy<'tcx, Provenance>,
pathname_op: &OpTy<'tcx>,
buf_op: &OpTy<'tcx>,
bufsize_op: &OpTy<'tcx>,
) -> InterpResult<'tcx, i64> {
let this = self.eval_context_mut();
@ -1302,10 +1287,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
}
fn isatty(
&mut self,
miri_fd: &OpTy<'tcx, Provenance>,
) -> InterpResult<'tcx, Scalar<Provenance>> {
fn isatty(&mut self, miri_fd: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();
// "returns 1 if fd is an open file descriptor referring to a terminal;
// otherwise 0 is returned, and errno is set to indicate the error"
@ -1326,9 +1308,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn realpath(
&mut self,
path_op: &OpTy<'tcx, Provenance>,
processed_path_op: &OpTy<'tcx, Provenance>,
) -> InterpResult<'tcx, Scalar<Provenance>> {
path_op: &OpTy<'tcx>,
processed_path_op: &OpTy<'tcx>,
) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();
this.assert_target_os_is_unix("realpath");
@ -1384,7 +1366,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
}
}
fn mkstemp(&mut self, template_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx, i32> {
fn mkstemp(&mut self, template_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> {
use rand::seq::SliceRandom;
// POSIX defines the template string.
@ -1531,7 +1513,7 @@ fn extract_sec_and_nsec<'tcx>(
/// Stores a file's metadata in order to avoid code duplication in the different metadata related
/// shims.
struct FileMetadata {
mode: Scalar<Provenance>,
mode: Scalar,
size: u64,
created: Option<(u64, u32)>,
accessed: Option<(u64, u32)>,

View file

@ -25,10 +25,10 @@ struct Epoll {
struct EpollEvent {
#[allow(dead_code)]
events: u32,
/// `Scalar<Provenance>` is used to represent the
/// `Scalar` is used to represent the
/// `epoll_data` type union.
#[allow(dead_code)]
data: Scalar<Provenance>,
data: Scalar,
}
impl FileDescription for Epoll {
@ -51,10 +51,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
/// is 0, then this function is the same as `epoll_create()`.
///
/// <https://linux.die.net/man/2/epoll_create1>
fn epoll_create1(
&mut self,
flags: &OpTy<'tcx, Provenance>,
) -> InterpResult<'tcx, Scalar<Provenance>> {
fn epoll_create1(&mut self, flags: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();
let flags = this.read_scalar(flags)?.to_i32()?;
@ -85,11 +82,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
/// <https://linux.die.net/man/2/epoll_ctl>
fn epoll_ctl(
&mut self,
epfd: &OpTy<'tcx, Provenance>,
op: &OpTy<'tcx, Provenance>,
fd: &OpTy<'tcx, Provenance>,
event: &OpTy<'tcx, Provenance>,
) -> InterpResult<'tcx, Scalar<Provenance>> {
epfd: &OpTy<'tcx>,
op: &OpTy<'tcx>,
fd: &OpTy<'tcx>,
event: &OpTy<'tcx>,
) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();
let epfd = this.read_scalar(epfd)?.to_i32()?;
@ -167,11 +164,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
/// <https://man7.org/linux/man-pages/man2/epoll_wait.2.html>
fn epoll_wait(
&mut self,
epfd: &OpTy<'tcx, Provenance>,
events: &OpTy<'tcx, Provenance>,
maxevents: &OpTy<'tcx, Provenance>,
timeout: &OpTy<'tcx, Provenance>,
) -> InterpResult<'tcx, Scalar<Provenance>> {
epfd: &OpTy<'tcx>,
events: &OpTy<'tcx>,
maxevents: &OpTy<'tcx>,
timeout: &OpTy<'tcx>,
) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();
let epfd = this.read_scalar(epfd)?.to_i32()?;

View file

@ -1,14 +1,21 @@
//! Linux `eventfd` implementation.
//! Currently just a stub.
use std::io;
use std::io::{Error, ErrorKind};
use std::mem;
use rustc_target::abi::Endian;
use crate::shims::unix::*;
use crate::*;
use crate::{concurrency::VClock, *};
use self::shims::unix::fd::FileDescriptor;
// We'll only do reads and writes in chunks of size u64.
const U64_ARRAY_SIZE: usize = mem::size_of::<u64>();
/// Maximum value that the eventfd counter can hold.
const MAX_COUNTER: u64 = u64::MAX - 1;
/// A kind of file descriptor created by `eventfd`.
/// The `Event` type isn't currently written to by `eventfd`.
/// The interface is meant to keep track of objects associated
@ -20,7 +27,9 @@ use self::shims::unix::fd::FileDescriptor;
struct Event {
/// The object contains an unsigned 64-bit integer (uint64_t) counter that is maintained by the
/// kernel. This counter is initialized with the value specified in the argument initval.
val: u64,
counter: u64,
is_nonblock: bool,
clock: VClock,
}
impl FileDescription for Event {
@ -35,6 +44,38 @@ impl FileDescription for Event {
Ok(Ok(()))
}
/// Read the counter in the buffer and return the counter if succeeded.
fn read<'tcx>(
&mut self,
_communicate_allowed: bool,
bytes: &mut [u8],
ecx: &mut MiriInterpCx<'tcx>,
) -> InterpResult<'tcx, io::Result<usize>> {
// Check the size of slice, and return error only if the size of the slice < 8.
let Some(bytes) = bytes.first_chunk_mut::<U64_ARRAY_SIZE>() else {
return Ok(Err(Error::from(ErrorKind::InvalidInput)));
};
// Block when counter == 0.
if self.counter == 0 {
if self.is_nonblock {
return Ok(Err(Error::from(ErrorKind::WouldBlock)));
} else {
//FIXME: blocking is not supported
throw_unsup_format!("eventfd: blocking is unsupported");
}
} else {
// Synchronize with all prior `write` calls to this FD.
ecx.acquire_clock(&self.clock);
// Return the counter in the host endianness using the buffer provided by caller.
*bytes = match ecx.tcx.sess.target.endian {
Endian::Little => self.counter.to_le_bytes(),
Endian::Big => self.counter.to_be_bytes(),
};
self.counter = 0;
return Ok(Ok(U64_ARRAY_SIZE));
}
}
/// A write call adds the 8-byte integer value supplied in
/// its buffer (in native endianness) to the counter. The maximum value that may be
/// stored in the counter is the largest unsigned 64-bit value
@ -53,16 +94,39 @@ impl FileDescription for Event {
bytes: &[u8],
ecx: &mut MiriInterpCx<'tcx>,
) -> InterpResult<'tcx, io::Result<usize>> {
let bytes: [u8; 8] = bytes.try_into().unwrap(); // FIXME fail gracefully when this has the wrong size
// Convert from target endianness to host endianness.
let num = match ecx.tcx.sess.target.endian {
Endian::Little => u64::from_le_bytes(bytes),
Endian::Big => u64::from_be_bytes(bytes),
// Check the size of slice, and return error only if the size of the slice < 8.
let Some(bytes) = bytes.first_chunk::<U64_ARRAY_SIZE>() else {
return Ok(Err(Error::from(ErrorKind::InvalidInput)));
};
// FIXME handle blocking when addition results in exceeding the max u64 value
// or fail with EAGAIN if the file descriptor is nonblocking.
self.val = self.val.checked_add(num).unwrap();
Ok(Ok(8))
// Convert from bytes to int according to host endianness.
let num = match ecx.tcx.sess.target.endian {
Endian::Little => u64::from_le_bytes(*bytes),
Endian::Big => u64::from_be_bytes(*bytes),
};
// u64::MAX as input is invalid because the maximum value of counter is u64::MAX - 1.
if num == u64::MAX {
return Ok(Err(Error::from(ErrorKind::InvalidInput)));
}
// If the addition does not let the counter to exceed the maximum value, update the counter.
// Else, block.
match self.counter.checked_add(num) {
Some(new_count @ 0..=MAX_COUNTER) => {
// Future `read` calls will synchronize with this write, so update the FD clock.
if let Some(clock) = &ecx.release_clock() {
self.clock.join(clock);
}
self.counter = new_count;
}
None | Some(u64::MAX) => {
if self.is_nonblock {
return Ok(Err(Error::from(ErrorKind::WouldBlock)));
} else {
//FIXME: blocking is not supported
throw_unsup_format!("eventfd: blocking is unsupported");
}
}
};
Ok(Ok(U64_ARRAY_SIZE))
}
}
@ -84,34 +148,43 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
/// `EFD_SEMAPHORE` - miri does not support semaphore-like semantics.
///
/// <https://linux.die.net/man/2/eventfd>
fn eventfd(
&mut self,
val: &OpTy<'tcx, Provenance>,
flags: &OpTy<'tcx, Provenance>,
) -> InterpResult<'tcx, Scalar<Provenance>> {
fn eventfd(&mut self, val: &OpTy<'tcx>, flags: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();
// eventfd is Linux specific.
this.assert_target_os("linux", "eventfd");
let val = this.read_scalar(val)?.to_u32()?;
let flags = this.read_scalar(flags)?.to_i32()?;
let mut flags = this.read_scalar(flags)?.to_i32()?;
let efd_cloexec = this.eval_libc_i32("EFD_CLOEXEC");
let efd_nonblock = this.eval_libc_i32("EFD_NONBLOCK");
let efd_semaphore = this.eval_libc_i32("EFD_SEMAPHORE");
if flags & (efd_cloexec | efd_nonblock | efd_semaphore) != flags {
throw_unsup_format!("eventfd: flag {flags:#x} is unsupported");
}
if flags & efd_cloexec == efd_cloexec {
// cloexec does nothing as we don't support `exec`
}
if flags & efd_nonblock == efd_nonblock {
// FIXME remember the nonblock flag
}
if flags & efd_semaphore == efd_semaphore {
throw_unsup_format!("eventfd: EFD_SEMAPHORE is unsupported");
}
let fd = this.machine.fds.insert_fd(FileDescriptor::new(Event { val: val.into() }));
let mut is_nonblock = false;
// Unset the flag that we support.
// After unloading, flags != 0 means other flags are used.
if flags & efd_cloexec == efd_cloexec {
// cloexec is ignored because Miri does not support exec.
flags &= !efd_cloexec;
}
if flags & efd_nonblock == efd_nonblock {
flags &= !efd_nonblock;
is_nonblock = true;
}
if flags != 0 {
throw_unsup_format!("eventfd: encountered unknown unsupported flags {:#x}", flags);
}
let fd = this.machine.fds.insert_fd(FileDescriptor::new(Event {
counter: val.into(),
is_nonblock,
clock: VClock::default(),
}));
Ok(Scalar::from_i32(fd))
}
}

View file

@ -20,8 +20,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
&mut self,
link_name: Symbol,
abi: Abi,
args: &[OpTy<'tcx, Provenance>],
dest: &MPlaceTy<'tcx, Provenance>,
args: &[OpTy<'tcx>],
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx, EmulateItemResult> {
let this = self.eval_context_mut();

View file

@ -8,11 +8,11 @@ impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn mremap(
&mut self,
old_address: &OpTy<'tcx, Provenance>,
old_size: &OpTy<'tcx, Provenance>,
new_size: &OpTy<'tcx, Provenance>,
flags: &OpTy<'tcx, Provenance>,
) -> InterpResult<'tcx, Scalar<Provenance>> {
old_address: &OpTy<'tcx>,
old_size: &OpTy<'tcx>,
new_size: &OpTy<'tcx>,
flags: &OpTy<'tcx>,
) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();
let old_address = this.read_pointer(old_address)?;

View file

@ -1,13 +1,11 @@
use std::time::SystemTime;
use crate::*;
/// Implementation of the SYS_futex syscall.
/// `args` is the arguments *after* the syscall number.
pub fn futex<'tcx>(
this: &mut MiriInterpCx<'tcx>,
args: &[OpTy<'tcx, Provenance>],
dest: &MPlaceTy<'tcx, Provenance>,
args: &[OpTy<'tcx>],
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx> {
// The amount of arguments used depends on the type of futex operation.
// The full futex syscall takes six arguments (excluding the syscall
@ -84,15 +82,9 @@ pub fn futex<'tcx>(
}
let timeout = this.deref_pointer_as(&args[3], this.libc_ty_layout("timespec"))?;
let timeout_time = if this.ptr_is_null(timeout.ptr())? {
let timeout = if this.ptr_is_null(timeout.ptr())? {
None
} else {
let realtime = op & futex_realtime == futex_realtime;
if realtime {
this.check_no_isolation(
"`futex` syscall with `op=FUTEX_WAIT` and non-null timeout with `FUTEX_CLOCK_REALTIME`",
)?;
}
let duration = match this.read_timespec(&timeout)? {
Some(duration) => duration,
None => {
@ -102,23 +94,22 @@ pub fn futex<'tcx>(
return Ok(());
}
};
Some(if wait_bitset {
let timeout_clock = if op & futex_realtime == futex_realtime {
this.check_no_isolation(
"`futex` syscall with `op=FUTEX_WAIT` and non-null timeout with `FUTEX_CLOCK_REALTIME`",
)?;
TimeoutClock::RealTime
} else {
TimeoutClock::Monotonic
};
let timeout_anchor = if wait_bitset {
// FUTEX_WAIT_BITSET uses an absolute timestamp.
if realtime {
Timeout::RealTime(SystemTime::UNIX_EPOCH.checked_add(duration).unwrap())
} else {
Timeout::Monotonic(
this.machine.clock.anchor().checked_add(duration).unwrap(),
)
}
TimeoutAnchor::Absolute
} else {
// FUTEX_WAIT uses a relative timestamp.
if realtime {
Timeout::RealTime(SystemTime::now().checked_add(duration).unwrap())
} else {
Timeout::Monotonic(this.machine.clock.now().checked_add(duration).unwrap())
}
})
TimeoutAnchor::Relative
};
Some((timeout_clock, timeout_anchor, duration))
};
// There may be a concurrent thread changing the value of addr
// and then invoking the FUTEX_WAKE syscall. It is critical that the
@ -172,7 +163,7 @@ pub fn futex<'tcx>(
this.futex_wait(
addr_usize,
bitset,
timeout_time,
timeout,
Scalar::from_target_isize(0, this), // retval_succ
Scalar::from_target_isize(-1, this), // retval_timeout
dest.clone(),

View file

@ -14,8 +14,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
&mut self,
link_name: Symbol,
abi: Abi,
args: &[OpTy<'tcx, Provenance>],
dest: &MPlaceTy<'tcx, Provenance>,
args: &[OpTy<'tcx>],
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx, EmulateItemResult> {
let this = self.eval_context_mut();

View file

@ -21,13 +21,13 @@ impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn mmap(
&mut self,
addr: &OpTy<'tcx, Provenance>,
length: &OpTy<'tcx, Provenance>,
prot: &OpTy<'tcx, Provenance>,
flags: &OpTy<'tcx, Provenance>,
fd: &OpTy<'tcx, Provenance>,
addr: &OpTy<'tcx>,
length: &OpTy<'tcx>,
prot: &OpTy<'tcx>,
flags: &OpTy<'tcx>,
fd: &OpTy<'tcx>,
offset: i128,
) -> InterpResult<'tcx, Scalar<Provenance>> {
) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();
// We do not support MAP_FIXED, so the addr argument is always ignored (except for the MacOS hack)
@ -123,11 +123,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
Ok(Scalar::from_pointer(ptr, this))
}
fn munmap(
&mut self,
addr: &OpTy<'tcx, Provenance>,
length: &OpTy<'tcx, Provenance>,
) -> InterpResult<'tcx, Scalar<Provenance>> {
fn munmap(&mut self, addr: &OpTy<'tcx>, length: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();
let addr = this.read_pointer(addr)?;

View file

@ -35,11 +35,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
/// <https://linux.die.net/man/2/socketpair>
fn socketpair(
&mut self,
domain: &OpTy<'tcx, Provenance>,
type_: &OpTy<'tcx, Provenance>,
protocol: &OpTy<'tcx, Provenance>,
sv: &OpTy<'tcx, Provenance>,
) -> InterpResult<'tcx, Scalar<Provenance>> {
domain: &OpTy<'tcx>,
type_: &OpTy<'tcx>,
protocol: &OpTy<'tcx>,
sv: &OpTy<'tcx>,
) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();
let _domain = this.read_scalar(domain)?.to_i32()?;

View file

@ -14,8 +14,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
&mut self,
link_name: Symbol,
abi: Abi,
args: &[OpTy<'tcx, Provenance>],
dest: &MPlaceTy<'tcx, Provenance>,
args: &[OpTy<'tcx>],
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx, EmulateItemResult> {
let this = self.eval_context_mut();
match link_name.as_str() {
@ -69,6 +69,36 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_null(dest)?;
}
"pset_info" => {
let [pset, tpe, cpus, list] =
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
// We do not need to handle the current process cpu mask, available_parallelism
// implementation pass null anyway. We only care for the number of
// cpus.
// https://docs.oracle.com/cd/E88353_01/html/E37841/pset-info-2.html
let pset = this.read_scalar(pset)?.to_i32()?;
let tpe = this.read_pointer(tpe)?;
let list = this.read_pointer(list)?;
let ps_myid = this.eval_libc_i32("PS_MYID");
if ps_myid != pset {
throw_unsup_format!("pset_info is only supported with pset==PS_MYID");
}
if !this.ptr_is_null(tpe)? {
throw_unsup_format!("pset_info is only supported with type==NULL");
}
if !this.ptr_is_null(list)? {
throw_unsup_format!("pset_info is only supported with list==NULL");
}
let cpus = this.deref_pointer(cpus)?;
this.write_scalar(Scalar::from_u32(this.machine.num_cpus), &cpus)?;
this.write_null(dest)?;
}
_ => return Ok(EmulateItemResult::NotSupported),
}
Ok(EmulateItemResult::NeedsReturn)

View file

@ -1,5 +1,4 @@
use std::sync::atomic::{AtomicBool, Ordering};
use std::time::SystemTime;
use rustc_target::abi::Size;
@ -10,9 +9,7 @@ use crate::*;
// - kind: i32
#[inline]
fn mutexattr_kind_offset<'tcx>(
ecx: &MiriInterpCx<'tcx>,
) -> InterpResult<'tcx, u64> {
fn mutexattr_kind_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> {
Ok(match &*ecx.tcx.sess.target.os {
"linux" | "illumos" | "solaris" | "macos" => 0,
os => throw_unsup_format!("`pthread_mutexattr` is not supported on {os}"),
@ -21,7 +18,7 @@ fn mutexattr_kind_offset<'tcx>(
fn mutexattr_get_kind<'tcx>(
ecx: &MiriInterpCx<'tcx>,
attr_op: &OpTy<'tcx, Provenance>,
attr_op: &OpTy<'tcx>,
) -> InterpResult<'tcx, i32> {
ecx.deref_pointer_and_read(
attr_op,
@ -34,7 +31,7 @@ fn mutexattr_get_kind<'tcx>(
fn mutexattr_set_kind<'tcx>(
ecx: &mut MiriInterpCx<'tcx>,
attr_op: &OpTy<'tcx, Provenance>,
attr_op: &OpTy<'tcx>,
kind: i32,
) -> InterpResult<'tcx, ()> {
ecx.deref_pointer_and_write(
@ -53,17 +50,11 @@ fn mutexattr_set_kind<'tcx>(
/// in `pthread_mutexattr_settype` function.
const PTHREAD_MUTEX_NORMAL_FLAG: i32 = 0x8000000;
fn is_mutex_kind_default<'tcx>(
ecx: &MiriInterpCx<'tcx>,
kind: i32,
) -> InterpResult<'tcx, bool> {
fn is_mutex_kind_default<'tcx>(ecx: &MiriInterpCx<'tcx>, kind: i32) -> InterpResult<'tcx, bool> {
Ok(kind == ecx.eval_libc_i32("PTHREAD_MUTEX_DEFAULT"))
}
fn is_mutex_kind_normal<'tcx>(
ecx: &MiriInterpCx<'tcx>,
kind: i32,
) -> InterpResult<'tcx, bool> {
fn is_mutex_kind_normal<'tcx>(ecx: &MiriInterpCx<'tcx>, kind: i32) -> InterpResult<'tcx, bool> {
let mutex_normal_kind = ecx.eval_libc_i32("PTHREAD_MUTEX_NORMAL");
Ok(kind == (mutex_normal_kind | PTHREAD_MUTEX_NORMAL_FLAG))
}
@ -125,7 +116,7 @@ fn mutex_kind_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> u64 {
fn mutex_get_id<'tcx>(
ecx: &mut MiriInterpCx<'tcx>,
mutex_op: &OpTy<'tcx, Provenance>,
mutex_op: &OpTy<'tcx>,
) -> InterpResult<'tcx, MutexId> {
ecx.mutex_get_or_create_id(
mutex_op,
@ -136,7 +127,7 @@ fn mutex_get_id<'tcx>(
fn mutex_reset_id<'tcx>(
ecx: &mut MiriInterpCx<'tcx>,
mutex_op: &OpTy<'tcx, Provenance>,
mutex_op: &OpTy<'tcx>,
) -> InterpResult<'tcx, ()> {
ecx.deref_pointer_and_write(
mutex_op,
@ -149,7 +140,7 @@ fn mutex_reset_id<'tcx>(
fn mutex_get_kind<'tcx>(
ecx: &MiriInterpCx<'tcx>,
mutex_op: &OpTy<'tcx, Provenance>,
mutex_op: &OpTy<'tcx>,
) -> InterpResult<'tcx, i32> {
ecx.deref_pointer_and_read(
mutex_op,
@ -162,7 +153,7 @@ fn mutex_get_kind<'tcx>(
fn mutex_set_kind<'tcx>(
ecx: &mut MiriInterpCx<'tcx>,
mutex_op: &OpTy<'tcx, Provenance>,
mutex_op: &OpTy<'tcx>,
kind: i32,
) -> InterpResult<'tcx, ()> {
ecx.deref_pointer_and_write(
@ -206,7 +197,7 @@ fn rwlock_id_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> {
fn rwlock_get_id<'tcx>(
ecx: &mut MiriInterpCx<'tcx>,
rwlock_op: &OpTy<'tcx, Provenance>,
rwlock_op: &OpTy<'tcx>,
) -> InterpResult<'tcx, RwLockId> {
ecx.rwlock_get_or_create_id(
rwlock_op,
@ -220,9 +211,7 @@ fn rwlock_get_id<'tcx>(
// - clock: i32
#[inline]
fn condattr_clock_offset<'tcx>(
ecx: &MiriInterpCx<'tcx>,
) -> InterpResult<'tcx, u64> {
fn condattr_clock_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> {
Ok(match &*ecx.tcx.sess.target.os {
"linux" | "illumos" | "solaris" => 0,
// macOS does not have a clock attribute.
@ -232,7 +221,7 @@ fn condattr_clock_offset<'tcx>(
fn condattr_get_clock_id<'tcx>(
ecx: &MiriInterpCx<'tcx>,
attr_op: &OpTy<'tcx, Provenance>,
attr_op: &OpTy<'tcx>,
) -> InterpResult<'tcx, i32> {
ecx.deref_pointer_and_read(
attr_op,
@ -245,7 +234,7 @@ fn condattr_get_clock_id<'tcx>(
fn condattr_set_clock_id<'tcx>(
ecx: &mut MiriInterpCx<'tcx>,
attr_op: &OpTy<'tcx, Provenance>,
attr_op: &OpTy<'tcx>,
clock_id: i32,
) -> InterpResult<'tcx, ()> {
ecx.deref_pointer_and_write(
@ -323,7 +312,7 @@ fn cond_clock_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> u64 {
fn cond_get_id<'tcx>(
ecx: &mut MiriInterpCx<'tcx>,
cond_op: &OpTy<'tcx, Provenance>,
cond_op: &OpTy<'tcx>,
) -> InterpResult<'tcx, CondvarId> {
ecx.condvar_get_or_create_id(
cond_op,
@ -334,7 +323,7 @@ fn cond_get_id<'tcx>(
fn cond_reset_id<'tcx>(
ecx: &mut MiriInterpCx<'tcx>,
cond_op: &OpTy<'tcx, Provenance>,
cond_op: &OpTy<'tcx>,
) -> InterpResult<'tcx, ()> {
ecx.deref_pointer_and_write(
cond_op,
@ -347,7 +336,7 @@ fn cond_reset_id<'tcx>(
fn cond_get_clock_id<'tcx>(
ecx: &MiriInterpCx<'tcx>,
cond_op: &OpTy<'tcx, Provenance>,
cond_op: &OpTy<'tcx>,
) -> InterpResult<'tcx, i32> {
ecx.deref_pointer_and_read(
cond_op,
@ -360,7 +349,7 @@ fn cond_get_clock_id<'tcx>(
fn cond_set_clock_id<'tcx>(
ecx: &mut MiriInterpCx<'tcx>,
cond_op: &OpTy<'tcx, Provenance>,
cond_op: &OpTy<'tcx>,
clock_id: i32,
) -> InterpResult<'tcx, ()> {
ecx.deref_pointer_and_write(
@ -374,10 +363,7 @@ fn cond_set_clock_id<'tcx>(
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn pthread_mutexattr_init(
&mut self,
attr_op: &OpTy<'tcx, Provenance>,
) -> InterpResult<'tcx, i32> {
fn pthread_mutexattr_init(&mut self, attr_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();
let default_kind = this.eval_libc_i32("PTHREAD_MUTEX_DEFAULT");
@ -388,8 +374,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn pthread_mutexattr_settype(
&mut self,
attr_op: &OpTy<'tcx, Provenance>,
kind_op: &OpTy<'tcx, Provenance>,
attr_op: &OpTy<'tcx>,
kind_op: &OpTy<'tcx>,
) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();
@ -427,10 +413,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
Ok(0)
}
fn pthread_mutexattr_destroy(
&mut self,
attr_op: &OpTy<'tcx, Provenance>,
) -> InterpResult<'tcx, i32> {
fn pthread_mutexattr_destroy(&mut self, attr_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();
// Destroying an uninit pthread_mutexattr is UB, so check to make sure it's not uninit.
@ -457,8 +440,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn pthread_mutex_init(
&mut self,
mutex_op: &OpTy<'tcx, Provenance>,
attr_op: &OpTy<'tcx, Provenance>,
mutex_op: &OpTy<'tcx>,
attr_op: &OpTy<'tcx>,
) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();
@ -479,8 +462,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn pthread_mutex_lock(
&mut self,
mutex_op: &OpTy<'tcx, Provenance>,
dest: &MPlaceTy<'tcx, Provenance>,
mutex_op: &OpTy<'tcx>,
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
@ -518,10 +501,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
Ok(())
}
fn pthread_mutex_trylock(
&mut self,
mutex_op: &OpTy<'tcx, Provenance>,
) -> InterpResult<'tcx, i32> {
fn pthread_mutex_trylock(&mut self, mutex_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();
let kind = mutex_get_kind(this, mutex_op)?;
@ -553,10 +533,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
}
fn pthread_mutex_unlock(
&mut self,
mutex_op: &OpTy<'tcx, Provenance>,
) -> InterpResult<'tcx, i32> {
fn pthread_mutex_unlock(&mut self, mutex_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();
let kind = mutex_get_kind(this, mutex_op)?;
@ -587,10 +564,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
}
fn pthread_mutex_destroy(
&mut self,
mutex_op: &OpTy<'tcx, Provenance>,
) -> InterpResult<'tcx, i32> {
fn pthread_mutex_destroy(&mut self, mutex_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();
let id = mutex_get_id(this, mutex_op)?;
@ -614,8 +588,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn pthread_rwlock_rdlock(
&mut self,
rwlock_op: &OpTy<'tcx, Provenance>,
dest: &MPlaceTy<'tcx, Provenance>,
rwlock_op: &OpTy<'tcx>,
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
@ -631,10 +605,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
Ok(())
}
fn pthread_rwlock_tryrdlock(
&mut self,
rwlock_op: &OpTy<'tcx, Provenance>,
) -> InterpResult<'tcx, i32> {
fn pthread_rwlock_tryrdlock(&mut self, rwlock_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();
let id = rwlock_get_id(this, rwlock_op)?;
@ -649,8 +620,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn pthread_rwlock_wrlock(
&mut self,
rwlock_op: &OpTy<'tcx, Provenance>,
dest: &MPlaceTy<'tcx, Provenance>,
rwlock_op: &OpTy<'tcx>,
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
@ -678,10 +649,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
Ok(())
}
fn pthread_rwlock_trywrlock(
&mut self,
rwlock_op: &OpTy<'tcx, Provenance>,
) -> InterpResult<'tcx, i32> {
fn pthread_rwlock_trywrlock(&mut self, rwlock_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();
let id = rwlock_get_id(this, rwlock_op)?;
@ -694,10 +662,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
}
fn pthread_rwlock_unlock(
&mut self,
rwlock_op: &OpTy<'tcx, Provenance>,
) -> InterpResult<'tcx, i32> {
fn pthread_rwlock_unlock(&mut self, rwlock_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();
let id = rwlock_get_id(this, rwlock_op)?;
@ -712,10 +677,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
}
fn pthread_rwlock_destroy(
&mut self,
rwlock_op: &OpTy<'tcx, Provenance>,
) -> InterpResult<'tcx, i32> {
fn pthread_rwlock_destroy(&mut self, rwlock_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();
let id = rwlock_get_id(this, rwlock_op)?;
@ -736,10 +698,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
Ok(0)
}
fn pthread_condattr_init(
&mut self,
attr_op: &OpTy<'tcx, Provenance>,
) -> InterpResult<'tcx, i32> {
fn pthread_condattr_init(&mut self, attr_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();
// no clock attribute on macOS
@ -756,9 +715,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn pthread_condattr_setclock(
&mut self,
attr_op: &OpTy<'tcx, Provenance>,
clock_id_op: &OpTy<'tcx, Provenance>,
) -> InterpResult<'tcx, Scalar<Provenance>> {
attr_op: &OpTy<'tcx>,
clock_id_op: &OpTy<'tcx>,
) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();
let clock_id = this.read_scalar(clock_id_op)?.to_i32()?;
@ -776,9 +735,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn pthread_condattr_getclock(
&mut self,
attr_op: &OpTy<'tcx, Provenance>,
clk_id_op: &OpTy<'tcx, Provenance>,
) -> InterpResult<'tcx, Scalar<Provenance>> {
attr_op: &OpTy<'tcx>,
clk_id_op: &OpTy<'tcx>,
) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();
let clock_id = condattr_get_clock_id(this, attr_op)?;
@ -787,10 +746,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
Ok(Scalar::from_i32(0))
}
fn pthread_condattr_destroy(
&mut self,
attr_op: &OpTy<'tcx, Provenance>,
) -> InterpResult<'tcx, i32> {
fn pthread_condattr_destroy(&mut self, attr_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();
// Destroying an uninit pthread_condattr is UB, so check to make sure it's not uninit.
@ -810,8 +766,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn pthread_cond_init(
&mut self,
cond_op: &OpTy<'tcx, Provenance>,
attr_op: &OpTy<'tcx, Provenance>,
cond_op: &OpTy<'tcx>,
attr_op: &OpTy<'tcx>,
) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();
@ -831,17 +787,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
Ok(0)
}
fn pthread_cond_signal(&mut self, cond_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx, i32> {
fn pthread_cond_signal(&mut self, cond_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();
let id = cond_get_id(this, cond_op)?;
this.condvar_signal(id)?;
Ok(0)
}
fn pthread_cond_broadcast(
&mut self,
cond_op: &OpTy<'tcx, Provenance>,
) -> InterpResult<'tcx, i32> {
fn pthread_cond_broadcast(&mut self, cond_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();
let id = cond_get_id(this, cond_op)?;
while this.condvar_signal(id)? {}
@ -850,9 +803,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn pthread_cond_wait(
&mut self,
cond_op: &OpTy<'tcx, Provenance>,
mutex_op: &OpTy<'tcx, Provenance>,
dest: &MPlaceTy<'tcx, Provenance>,
cond_op: &OpTy<'tcx>,
mutex_op: &OpTy<'tcx>,
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
@ -873,10 +826,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn pthread_cond_timedwait(
&mut self,
cond_op: &OpTy<'tcx, Provenance>,
mutex_op: &OpTy<'tcx, Provenance>,
abstime_op: &OpTy<'tcx, Provenance>,
dest: &MPlaceTy<'tcx, Provenance>,
cond_op: &OpTy<'tcx>,
mutex_op: &OpTy<'tcx>,
abstime_op: &OpTy<'tcx>,
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
@ -895,11 +848,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
return Ok(());
}
};
let timeout_time = if is_cond_clock_realtime(this, clock_id) {
let timeout_clock = if is_cond_clock_realtime(this, clock_id) {
this.check_no_isolation("`pthread_cond_timedwait` with `CLOCK_REALTIME`")?;
Timeout::RealTime(SystemTime::UNIX_EPOCH.checked_add(duration).unwrap())
TimeoutClock::RealTime
} else if clock_id == this.eval_libc_i32("CLOCK_MONOTONIC") {
Timeout::Monotonic(this.machine.clock.anchor().checked_add(duration).unwrap())
TimeoutClock::Monotonic
} else {
throw_unsup_format!("unsupported clock id: {}", clock_id);
};
@ -907,7 +860,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.condvar_wait(
id,
mutex_id,
Some(timeout_time),
Some((timeout_clock, TimeoutAnchor::Absolute, duration)),
Scalar::from_i32(0),
this.eval_libc("ETIMEDOUT"), // retval_timeout
dest.clone(),
@ -916,10 +869,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
Ok(())
}
fn pthread_cond_destroy(
&mut self,
cond_op: &OpTy<'tcx, Provenance>,
) -> InterpResult<'tcx, i32> {
fn pthread_cond_destroy(&mut self, cond_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();
let id = cond_get_id(this, cond_op)?;

View file

@ -6,10 +6,10 @@ impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn pthread_create(
&mut self,
thread: &OpTy<'tcx, Provenance>,
_attr: &OpTy<'tcx, Provenance>,
start_routine: &OpTy<'tcx, Provenance>,
arg: &OpTy<'tcx, Provenance>,
thread: &OpTy<'tcx>,
_attr: &OpTy<'tcx>,
start_routine: &OpTy<'tcx>,
arg: &OpTy<'tcx>,
) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();
@ -32,8 +32,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn pthread_join(
&mut self,
thread: &OpTy<'tcx, Provenance>,
retval: &OpTy<'tcx, Provenance>,
thread: &OpTy<'tcx>,
retval: &OpTy<'tcx>,
) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();
@ -48,7 +48,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
Ok(0)
}
fn pthread_detach(&mut self, thread: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx, i32> {
fn pthread_detach(&mut self, thread: &OpTy<'tcx>) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();
let thread_id = this.read_scalar(thread)?.to_int(this.libc_ty_layout("pthread_t").size)?;
@ -60,7 +60,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
Ok(0)
}
fn pthread_self(&mut self) -> InterpResult<'tcx, Scalar<Provenance>> {
fn pthread_self(&mut self) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();
let thread_id = this.active_thread();
@ -71,10 +71,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
/// including the null terminator.
fn pthread_setname_np(
&mut self,
thread: Scalar<Provenance>,
name: Scalar<Provenance>,
thread: Scalar,
name: Scalar,
max_name_len: usize,
) -> InterpResult<'tcx, Scalar<Provenance>> {
) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();
let thread = thread.to_int(this.libc_ty_layout("pthread_t").size)?;
@ -95,10 +95,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn pthread_getname_np(
&mut self,
thread: Scalar<Provenance>,
name_out: Scalar<Provenance>,
len: Scalar<Provenance>,
) -> InterpResult<'tcx, Scalar<Provenance>> {
thread: Scalar,
name_out: Scalar,
len: Scalar,
) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();
let thread = thread.to_int(this.libc_ty_layout("pthread_t").size)?;

View file

@ -14,8 +14,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
&mut self,
link_name: Symbol,
abi: Abi,
args: &[OpTy<'tcx, Provenance>],
dest: &MPlaceTy<'tcx, Provenance>,
args: &[OpTy<'tcx>],
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx, EmulateItemResult> {
let this = self.eval_context_mut();
match link_name.as_str() {

View file

@ -38,10 +38,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
#[allow(non_snake_case)]
fn GetEnvironmentVariableW(
&mut self,
name_op: &OpTy<'tcx, Provenance>, // LPCWSTR
buf_op: &OpTy<'tcx, Provenance>, // LPWSTR
size_op: &OpTy<'tcx, Provenance>, // DWORD
) -> InterpResult<'tcx, Scalar<Provenance>> {
name_op: &OpTy<'tcx>, // LPCWSTR
buf_op: &OpTy<'tcx>, // LPWSTR
size_op: &OpTy<'tcx>, // DWORD
) -> InterpResult<'tcx, Scalar> {
// ^ Returns DWORD (u32 on Windows)
let this = self.eval_context_mut();
@ -71,7 +71,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
#[allow(non_snake_case)]
fn GetEnvironmentStringsW(&mut self) -> InterpResult<'tcx, Pointer<Option<Provenance>>> {
fn GetEnvironmentStringsW(&mut self) -> InterpResult<'tcx, Pointer> {
let this = self.eval_context_mut();
this.assert_target_os("windows", "GetEnvironmentStringsW");
@ -93,10 +93,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
#[allow(non_snake_case)]
fn FreeEnvironmentStringsW(
&mut self,
env_block_op: &OpTy<'tcx, Provenance>,
) -> InterpResult<'tcx, Scalar<Provenance>> {
fn FreeEnvironmentStringsW(&mut self, env_block_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();
this.assert_target_os("windows", "FreeEnvironmentStringsW");
@ -109,9 +106,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
#[allow(non_snake_case)]
fn SetEnvironmentVariableW(
&mut self,
name_op: &OpTy<'tcx, Provenance>, // LPCWSTR
value_op: &OpTy<'tcx, Provenance>, // LPCWSTR
) -> InterpResult<'tcx, Scalar<Provenance>> {
name_op: &OpTy<'tcx>, // LPCWSTR
value_op: &OpTy<'tcx>, // LPCWSTR
) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();
this.assert_target_os("windows", "SetEnvironmentVariableW");
@ -142,9 +139,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
#[allow(non_snake_case)]
fn GetCurrentDirectoryW(
&mut self,
size_op: &OpTy<'tcx, Provenance>, // DWORD
buf_op: &OpTy<'tcx, Provenance>, // LPTSTR
) -> InterpResult<'tcx, Scalar<Provenance>> {
size_op: &OpTy<'tcx>, // DWORD
buf_op: &OpTy<'tcx>, // LPTSTR
) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();
this.assert_target_os("windows", "GetCurrentDirectoryW");
@ -174,8 +171,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
#[allow(non_snake_case)]
fn SetCurrentDirectoryW(
&mut self,
path_op: &OpTy<'tcx, Provenance>, // LPCTSTR
) -> InterpResult<'tcx, Scalar<Provenance>> {
path_op: &OpTy<'tcx>, // LPCTSTR
) -> InterpResult<'tcx, Scalar> {
// ^ Returns BOOL (i32 on Windows)
let this = self.eval_context_mut();
@ -211,10 +208,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
#[allow(non_snake_case)]
fn GetUserProfileDirectoryW(
&mut self,
token: &OpTy<'tcx, Provenance>, // HANDLE
buf: &OpTy<'tcx, Provenance>, // LPWSTR
size: &OpTy<'tcx, Provenance>, // LPDWORD
) -> InterpResult<'tcx, Scalar<Provenance>> // returns BOOL
token: &OpTy<'tcx>, // HANDLE
buf: &OpTy<'tcx>, // LPWSTR
size: &OpTy<'tcx>, // LPDWORD
) -> InterpResult<'tcx, Scalar> // returns BOOL
{
let this = self.eval_context_mut();
this.assert_target_os("windows", "GetUserProfileDirectoryW");

View file

@ -82,8 +82,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
&mut self,
link_name: Symbol,
abi: Abi,
args: &[OpTy<'tcx, Provenance>],
dest: &MPlaceTy<'tcx, Provenance>,
args: &[OpTy<'tcx>],
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx, EmulateItemResult> {
let this = self.eval_context_mut();

View file

@ -119,7 +119,7 @@ impl Handle {
Self::new(discriminant, data)
}
pub fn to_scalar(self, cx: &impl HasDataLayout) -> Scalar<Provenance> {
pub fn to_scalar(self, cx: &impl HasDataLayout) -> Scalar {
// 64-bit handles are sign extended 32-bit handles
// see https://docs.microsoft.com/en-us/windows/win32/winprog64/interprocess-communication
#[allow(clippy::cast_possible_wrap)] // we want it to wrap
@ -128,7 +128,7 @@ impl Handle {
}
pub fn from_scalar<'tcx>(
handle: Scalar<Provenance>,
handle: Scalar,
cx: &impl HasDataLayout,
) -> InterpResult<'tcx, Option<Self>> {
let sign_extended_handle = handle.to_target_isize(cx)?;
@ -155,7 +155,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
)))
}
fn CloseHandle(&mut self, handle_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx> {
fn CloseHandle(&mut self, handle_op: &OpTy<'tcx>) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
let handle = this.read_scalar(handle_op)?;

View file

@ -10,10 +10,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Windows sync primitives are pointer sized.
// We only use the first 4 bytes for the id.
fn init_once_get_id(
&mut self,
init_once_op: &OpTy<'tcx, Provenance>,
) -> InterpResult<'tcx, InitOnceId> {
fn init_once_get_id(&mut self, init_once_op: &OpTy<'tcx>) -> InterpResult<'tcx, InitOnceId> {
let this = self.eval_context_mut();
this.init_once_get_or_create_id(init_once_op, this.windows_ty_layout("INIT_ONCE"), 0)
}
@ -22,8 +19,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn init_once_try_begin(
&mut self,
id: InitOnceId,
pending_place: &MPlaceTy<'tcx, Provenance>,
dest: &MPlaceTy<'tcx, Provenance>,
pending_place: &MPlaceTy<'tcx>,
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx, bool> {
let this = self.eval_context_mut();
Ok(match this.init_once_status(id) {
@ -49,11 +46,11 @@ impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn InitOnceBeginInitialize(
&mut self,
init_once_op: &OpTy<'tcx, Provenance>,
flags_op: &OpTy<'tcx, Provenance>,
pending_op: &OpTy<'tcx, Provenance>,
context_op: &OpTy<'tcx, Provenance>,
dest: &MPlaceTy<'tcx, Provenance>,
init_once_op: &OpTy<'tcx>,
flags_op: &OpTy<'tcx>,
pending_op: &OpTy<'tcx>,
context_op: &OpTy<'tcx>,
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
@ -82,8 +79,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
callback!(
@capture<'tcx> {
id: InitOnceId,
pending_place: MPlaceTy<'tcx, Provenance>,
dest: MPlaceTy<'tcx, Provenance>,
pending_place: MPlaceTy<'tcx>,
dest: MPlaceTy<'tcx>,
}
@unblock = |this| {
let ret = this.init_once_try_begin(id, &pending_place, &dest)?;
@ -97,10 +94,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn InitOnceComplete(
&mut self,
init_once_op: &OpTy<'tcx, Provenance>,
flags_op: &OpTy<'tcx, Provenance>,
context_op: &OpTy<'tcx, Provenance>,
) -> InterpResult<'tcx, Scalar<Provenance>> {
init_once_op: &OpTy<'tcx>,
flags_op: &OpTy<'tcx>,
context_op: &OpTy<'tcx>,
) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();
let id = this.init_once_get_id(init_once_op)?;
@ -137,11 +134,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn WaitOnAddress(
&mut self,
ptr_op: &OpTy<'tcx, Provenance>,
compare_op: &OpTy<'tcx, Provenance>,
size_op: &OpTy<'tcx, Provenance>,
timeout_op: &OpTy<'tcx, Provenance>,
dest: &MPlaceTy<'tcx, Provenance>,
ptr_op: &OpTy<'tcx>,
compare_op: &OpTy<'tcx>,
size_op: &OpTy<'tcx>,
timeout_op: &OpTy<'tcx>,
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
@ -160,11 +157,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
};
let size = Size::from_bytes(size);
let timeout_time = if timeout_ms == this.eval_windows_u32("c", "INFINITE") {
let timeout = if timeout_ms == this.eval_windows_u32("c", "INFINITE") {
None
} else {
let duration = Duration::from_millis(timeout_ms.into());
Some(Timeout::Monotonic(this.machine.clock.now().checked_add(duration).unwrap()))
Some((TimeoutClock::Monotonic, TimeoutAnchor::Relative, duration))
};
// See the Linux futex implementation for why this fence exists.
@ -180,7 +177,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.futex_wait(
addr,
u32::MAX, // bitset
timeout_time,
timeout,
Scalar::from_i32(1), // retval_succ
Scalar::from_i32(0), // retval_timeout
dest.clone(),
@ -193,7 +190,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
Ok(())
}
fn WakeByAddressSingle(&mut self, ptr_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx> {
fn WakeByAddressSingle(&mut self, ptr_op: &OpTy<'tcx>) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
let ptr = this.read_pointer(ptr_op)?;
@ -206,7 +203,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
Ok(())
}
fn WakeByAddressAll(&mut self, ptr_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx> {
fn WakeByAddressAll(&mut self, ptr_op: &OpTy<'tcx>) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
let ptr = this.read_pointer(ptr_op)?;

View file

@ -10,12 +10,12 @@ impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn CreateThread(
&mut self,
security_op: &OpTy<'tcx, Provenance>,
stacksize_op: &OpTy<'tcx, Provenance>,
start_op: &OpTy<'tcx, Provenance>,
arg_op: &OpTy<'tcx, Provenance>,
flags_op: &OpTy<'tcx, Provenance>,
thread_op: &OpTy<'tcx, Provenance>,
security_op: &OpTy<'tcx>,
stacksize_op: &OpTy<'tcx>,
start_op: &OpTy<'tcx>,
arg_op: &OpTy<'tcx>,
flags_op: &OpTy<'tcx>,
thread_op: &OpTy<'tcx>,
) -> InterpResult<'tcx, ThreadId> {
let this = self.eval_context_mut();
@ -57,8 +57,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn WaitForSingleObject(
&mut self,
handle_op: &OpTy<'tcx, Provenance>,
timeout_op: &OpTy<'tcx, Provenance>,
handle_op: &OpTy<'tcx>,
timeout_op: &OpTy<'tcx>,
) -> InterpResult<'tcx, u32> {
let this = self.eval_context_mut();

View file

@ -6,15 +6,13 @@ use rustc_target::spec::abi::Abi;
use crate::*;
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
pub(super) trait EvalContextExt<'tcx>:
crate::MiriInterpCxExt<'tcx>
{
pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn emulate_x86_aesni_intrinsic(
&mut self,
link_name: Symbol,
abi: Abi,
args: &[OpTy<'tcx, Provenance>],
dest: &MPlaceTy<'tcx, Provenance>,
args: &[OpTy<'tcx>],
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx, EmulateItemResult> {
let this = self.eval_context_mut();
this.expect_target_feature_for_intrinsic(link_name, "aes")?;
@ -135,9 +133,9 @@ pub(super) trait EvalContextExt<'tcx>:
// `state` with the corresponding 128-bit key of `key`.
fn aes_round<'tcx>(
this: &mut crate::MiriInterpCx<'tcx>,
state: &OpTy<'tcx, Provenance>,
key: &OpTy<'tcx, Provenance>,
dest: &MPlaceTy<'tcx, Provenance>,
state: &OpTy<'tcx>,
key: &OpTy<'tcx>,
dest: &MPlaceTy<'tcx>,
f: impl Fn(u128, u128) -> u128,
) -> InterpResult<'tcx, ()> {
assert_eq!(dest.layout.size, state.layout.size);

View file

@ -13,15 +13,13 @@ use super::{
use crate::*;
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
pub(super) trait EvalContextExt<'tcx>:
crate::MiriInterpCxExt<'tcx>
{
pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn emulate_x86_avx_intrinsic(
&mut self,
link_name: Symbol,
abi: Abi,
args: &[OpTy<'tcx, Provenance>],
dest: &MPlaceTy<'tcx, Provenance>,
args: &[OpTy<'tcx>],
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx, EmulateItemResult> {
let this = self.eval_context_mut();
this.expect_target_feature_for_intrinsic(link_name, "avx")?;

View file

@ -11,15 +11,13 @@ use super::{
use crate::*;
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
pub(super) trait EvalContextExt<'tcx>:
crate::MiriInterpCxExt<'tcx>
{
pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn emulate_x86_avx2_intrinsic(
&mut self,
link_name: Symbol,
abi: Abi,
args: &[OpTy<'tcx, Provenance>],
dest: &MPlaceTy<'tcx, Provenance>,
args: &[OpTy<'tcx>],
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx, EmulateItemResult> {
let this = self.eval_context_mut();
this.expect_target_feature_for_intrinsic(link_name, "avx2")?;

View file

@ -26,8 +26,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
&mut self,
link_name: Symbol,
abi: Abi,
args: &[OpTy<'tcx, Provenance>],
dest: &MPlaceTy<'tcx, Provenance>,
args: &[OpTy<'tcx>],
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx, EmulateItemResult> {
let this = self.eval_context_mut();
// Prefix should have already been checked.
@ -105,6 +105,13 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
}
"pclmulqdq" => {
let [left, right, imm] =
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
pclmulqdq(this, left, right, imm, dest)?;
}
name if name.starts_with("sse.") => {
return sse::EvalContextExt::emulate_x86_sse_intrinsic(
this, link_name, abi, args, dest,
@ -244,9 +251,9 @@ impl FloatBinOp {
fn bin_op_float<'tcx, F: rustc_apfloat::Float>(
this: &crate::MiriInterpCx<'tcx>,
which: FloatBinOp,
left: &ImmTy<'tcx, Provenance>,
right: &ImmTy<'tcx, Provenance>,
) -> InterpResult<'tcx, Scalar<Provenance>> {
left: &ImmTy<'tcx>,
right: &ImmTy<'tcx>,
) -> InterpResult<'tcx, Scalar> {
match which {
FloatBinOp::Arith(which) => {
let res = this.binary_op(which, left, right)?;
@ -306,9 +313,9 @@ fn bin_op_float<'tcx, F: rustc_apfloat::Float>(
fn bin_op_simd_float_first<'tcx, F: rustc_apfloat::Float>(
this: &mut crate::MiriInterpCx<'tcx>,
which: FloatBinOp,
left: &OpTy<'tcx, Provenance>,
right: &OpTy<'tcx, Provenance>,
dest: &MPlaceTy<'tcx, Provenance>,
left: &OpTy<'tcx>,
right: &OpTy<'tcx>,
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx, ()> {
let (left, left_len) = this.operand_to_simd(left)?;
let (right, right_len) = this.operand_to_simd(right)?;
@ -337,9 +344,9 @@ fn bin_op_simd_float_first<'tcx, F: rustc_apfloat::Float>(
fn bin_op_simd_float_all<'tcx, F: rustc_apfloat::Float>(
this: &mut crate::MiriInterpCx<'tcx>,
which: FloatBinOp,
left: &OpTy<'tcx, Provenance>,
right: &OpTy<'tcx, Provenance>,
dest: &MPlaceTy<'tcx, Provenance>,
left: &OpTy<'tcx>,
right: &OpTy<'tcx>,
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx, ()> {
let (left, left_len) = this.operand_to_simd(left)?;
let (right, right_len) = this.operand_to_simd(right)?;
@ -384,8 +391,8 @@ enum FloatUnaryOp {
fn unary_op_f32<'tcx>(
this: &mut crate::MiriInterpCx<'tcx>,
which: FloatUnaryOp,
op: &ImmTy<'tcx, Provenance>,
) -> InterpResult<'tcx, Scalar<Provenance>> {
op: &ImmTy<'tcx>,
) -> InterpResult<'tcx, Scalar> {
match which {
FloatUnaryOp::Sqrt => {
let op = op.to_scalar();
@ -435,8 +442,8 @@ fn apply_random_float_error<F: rustc_apfloat::Float>(
fn unary_op_ss<'tcx>(
this: &mut crate::MiriInterpCx<'tcx>,
which: FloatUnaryOp,
op: &OpTy<'tcx, Provenance>,
dest: &MPlaceTy<'tcx, Provenance>,
op: &OpTy<'tcx>,
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx, ()> {
let (op, op_len) = this.operand_to_simd(op)?;
let (dest, dest_len) = this.mplace_to_simd(dest)?;
@ -458,8 +465,8 @@ fn unary_op_ss<'tcx>(
fn unary_op_ps<'tcx>(
this: &mut crate::MiriInterpCx<'tcx>,
which: FloatUnaryOp,
op: &OpTy<'tcx, Provenance>,
dest: &MPlaceTy<'tcx, Provenance>,
op: &OpTy<'tcx>,
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx, ()> {
let (op, op_len) = this.operand_to_simd(op)?;
let (dest, dest_len) = this.mplace_to_simd(dest)?;
@ -494,10 +501,10 @@ enum ShiftOp {
/// bit is copied to all bits.
fn shift_simd_by_scalar<'tcx>(
this: &mut crate::MiriInterpCx<'tcx>,
left: &OpTy<'tcx, Provenance>,
right: &OpTy<'tcx, Provenance>,
left: &OpTy<'tcx>,
right: &OpTy<'tcx>,
which: ShiftOp,
dest: &MPlaceTy<'tcx, Provenance>,
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx, ()> {
let (left, left_len) = this.operand_to_simd(left)?;
let (dest, dest_len) = this.mplace_to_simd(dest)?;
@ -550,10 +557,10 @@ fn shift_simd_by_scalar<'tcx>(
/// bit is copied to all bits.
fn shift_simd_by_simd<'tcx>(
this: &mut crate::MiriInterpCx<'tcx>,
left: &OpTy<'tcx, Provenance>,
right: &OpTy<'tcx, Provenance>,
left: &OpTy<'tcx>,
right: &OpTy<'tcx>,
which: ShiftOp,
dest: &MPlaceTy<'tcx, Provenance>,
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx, ()> {
let (left, left_len) = this.operand_to_simd(left)?;
let (right, right_len) = this.operand_to_simd(right)?;
@ -602,7 +609,7 @@ fn shift_simd_by_simd<'tcx>(
/// the first value.
fn extract_first_u64<'tcx>(
this: &crate::MiriInterpCx<'tcx>,
op: &OpTy<'tcx, Provenance>,
op: &OpTy<'tcx>,
) -> InterpResult<'tcx, u64> {
// Transmute vector to `[u64; 2]`
let array_layout = this.layout_of(Ty::new_array(this.tcx.tcx, this.tcx.types.u64, 2))?;
@ -616,10 +623,10 @@ fn extract_first_u64<'tcx>(
// 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: &MPlaceTy<'tcx, Provenance>,
left: &OpTy<'tcx>,
right: &OpTy<'tcx>,
rounding: &OpTy<'tcx>,
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx, ()> {
let (left, left_len) = this.operand_to_simd(left)?;
let (right, right_len) = this.operand_to_simd(right)?;
@ -647,9 +654,9 @@ fn round_first<'tcx, F: rustc_apfloat::Float>(
// 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: &MPlaceTy<'tcx, Provenance>,
op: &OpTy<'tcx>,
rounding: &OpTy<'tcx>,
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx, ()> {
let (op, op_len) = this.operand_to_simd(op)?;
let (dest, dest_len) = this.mplace_to_simd(dest)?;
@ -699,9 +706,9 @@ fn rounding_from_imm<'tcx>(rounding: i32) -> InterpResult<'tcx, rustc_apfloat::R
/// has less elements than `dest`, the rest is filled with zeros.
fn convert_float_to_int<'tcx>(
this: &mut crate::MiriInterpCx<'tcx>,
op: &OpTy<'tcx, Provenance>,
op: &OpTy<'tcx>,
rnd: rustc_apfloat::Round,
dest: &MPlaceTy<'tcx, Provenance>,
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx, ()> {
let (op, op_len) = this.operand_to_simd(op)?;
let (dest, dest_len) = this.mplace_to_simd(dest)?;
@ -734,8 +741,8 @@ fn convert_float_to_int<'tcx>(
/// will wrap around.
fn int_abs<'tcx>(
this: &mut crate::MiriInterpCx<'tcx>,
op: &OpTy<'tcx, Provenance>,
dest: &MPlaceTy<'tcx, Provenance>,
op: &OpTy<'tcx>,
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx, ()> {
let (op, op_len) = this.operand_to_simd(op)?;
let (dest, dest_len) = this.mplace_to_simd(dest)?;
@ -802,9 +809,9 @@ fn horizontal_bin_op<'tcx>(
this: &mut crate::MiriInterpCx<'tcx>,
which: mir::BinOp,
saturating: bool,
left: &OpTy<'tcx, Provenance>,
right: &OpTy<'tcx, Provenance>,
dest: &MPlaceTy<'tcx, Provenance>,
left: &OpTy<'tcx>,
right: &OpTy<'tcx>,
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx, ()> {
assert_eq!(left.layout, dest.layout);
assert_eq!(right.layout, dest.layout);
@ -853,10 +860,10 @@ fn horizontal_bin_op<'tcx>(
/// 128-bit blocks of `left` and `right`).
fn conditional_dot_product<'tcx>(
this: &mut crate::MiriInterpCx<'tcx>,
left: &OpTy<'tcx, Provenance>,
right: &OpTy<'tcx, Provenance>,
imm: &OpTy<'tcx, Provenance>,
dest: &MPlaceTy<'tcx, Provenance>,
left: &OpTy<'tcx>,
right: &OpTy<'tcx>,
imm: &OpTy<'tcx>,
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx, ()> {
assert_eq!(left.layout, dest.layout);
assert_eq!(right.layout, dest.layout);
@ -911,8 +918,8 @@ fn conditional_dot_product<'tcx>(
/// 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>,
op: &OpTy<'tcx>,
mask: &OpTy<'tcx>,
) -> InterpResult<'tcx, (bool, bool)> {
assert_eq!(op.layout, mask.layout);
@ -942,8 +949,8 @@ fn test_bits_masked<'tcx>(
/// The second is true when the highest bit of each element of `!op & mask` is zero.
fn test_high_bits_masked<'tcx>(
this: &crate::MiriInterpCx<'tcx>,
op: &OpTy<'tcx, Provenance>,
mask: &OpTy<'tcx, Provenance>,
op: &OpTy<'tcx>,
mask: &OpTy<'tcx>,
) -> InterpResult<'tcx, (bool, bool)> {
assert_eq!(op.layout, mask.layout);
@ -973,9 +980,9 @@ fn test_high_bits_masked<'tcx>(
/// element of `mask`. `ptr` does not need to be aligned.
fn mask_load<'tcx>(
this: &mut crate::MiriInterpCx<'tcx>,
ptr: &OpTy<'tcx, Provenance>,
mask: &OpTy<'tcx, Provenance>,
dest: &MPlaceTy<'tcx, Provenance>,
ptr: &OpTy<'tcx>,
mask: &OpTy<'tcx>,
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx, ()> {
let (mask, mask_len) = this.operand_to_simd(mask)?;
let (dest, dest_len) = this.mplace_to_simd(dest)?;
@ -1006,9 +1013,9 @@ fn mask_load<'tcx>(
/// element of `mask`. `ptr` does not need to be aligned.
fn mask_store<'tcx>(
this: &mut crate::MiriInterpCx<'tcx>,
ptr: &OpTy<'tcx, Provenance>,
mask: &OpTy<'tcx, Provenance>,
value: &OpTy<'tcx, Provenance>,
ptr: &OpTy<'tcx>,
mask: &OpTy<'tcx>,
value: &OpTy<'tcx>,
) -> InterpResult<'tcx, ()> {
let (mask, mask_len) = this.operand_to_simd(mask)?;
let (value, value_len) = this.operand_to_simd(value)?;
@ -1046,10 +1053,10 @@ fn mask_store<'tcx>(
/// 128-bit chunks of `left` and `right`).
fn mpsadbw<'tcx>(
this: &mut crate::MiriInterpCx<'tcx>,
left: &OpTy<'tcx, Provenance>,
right: &OpTy<'tcx, Provenance>,
imm: &OpTy<'tcx, Provenance>,
dest: &MPlaceTy<'tcx, Provenance>,
left: &OpTy<'tcx>,
right: &OpTy<'tcx>,
imm: &OpTy<'tcx>,
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx, ()> {
assert_eq!(left.layout, right.layout);
assert_eq!(left.layout.size, dest.layout.size);
@ -1103,9 +1110,9 @@ fn mpsadbw<'tcx>(
/// <https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_mulhrs_epi16>
fn pmulhrsw<'tcx>(
this: &mut crate::MiriInterpCx<'tcx>,
left: &OpTy<'tcx, Provenance>,
right: &OpTy<'tcx, Provenance>,
dest: &MPlaceTy<'tcx, Provenance>,
left: &OpTy<'tcx>,
right: &OpTy<'tcx>,
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx, ()> {
let (left, left_len) = this.operand_to_simd(left)?;
let (right, right_len) = this.operand_to_simd(right)?;
@ -1133,6 +1140,68 @@ fn pmulhrsw<'tcx>(
Ok(())
}
/// Perform a carry-less multiplication of two 64-bit integers, selected from `left` and `right` according to `imm8`,
/// and store the results in `dst`.
///
/// `left` and `right` are both vectors of type 2 x i64. Only bits 0 and 4 of `imm8` matter;
/// they select the element of `left` and `right`, respectively.
///
/// <https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_clmulepi64_si128>
fn pclmulqdq<'tcx>(
this: &mut MiriInterpCx<'tcx>,
left: &OpTy<'tcx>,
right: &OpTy<'tcx>,
imm8: &OpTy<'tcx>,
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx, ()> {
assert_eq!(left.layout, right.layout);
assert_eq!(left.layout.size, dest.layout.size);
// Transmute to `[u64; 2]`
let array_layout = this.layout_of(Ty::new_array(this.tcx.tcx, this.tcx.types.u64, 2))?;
let left = left.transmute(array_layout, this)?;
let right = right.transmute(array_layout, this)?;
let dest = dest.transmute(array_layout, this)?;
let imm8 = this.read_scalar(imm8)?.to_u8()?;
// select the 64-bit integer from left that the user specified (low or high)
let index = if (imm8 & 0x01) == 0 { 0 } else { 1 };
let left = this.read_scalar(&this.project_index(&left, index)?)?.to_u64()?;
// select the 64-bit integer from right that the user specified (low or high)
let index = if (imm8 & 0x10) == 0 { 0 } else { 1 };
let right = this.read_scalar(&this.project_index(&right, index)?)?.to_u64()?;
// Perform carry-less multiplication
//
// This operation is like long multiplication, but ignores all carries.
// That idea corresponds to the xor operator, which is used in the implementation.
//
// Wikipedia has an example https://en.wikipedia.org/wiki/Carry-less_product#Example
let mut result: u128 = 0;
for i in 0..64 {
// if the i-th bit in right is set
if (right & (1 << i)) != 0 {
// xor result with `left` shifted to the left by i positions
result ^= (left as u128) << i;
}
}
let result_low = (result & 0xFFFF_FFFF_FFFF_FFFF) as u64;
let result_high = (result >> 64) as u64;
let dest_low = this.project_index(&dest, 0)?;
this.write_scalar(Scalar::from_u64(result_low), &dest_low)?;
let dest_high = this.project_index(&dest, 1)?;
this.write_scalar(Scalar::from_u64(result_high), &dest_high)?;
Ok(())
}
/// Packs two N-bit integer vectors to a single N/2-bit integers.
///
/// The conversion from N-bit to N/2-bit should be provided by `f`.
@ -1142,10 +1211,10 @@ fn pmulhrsw<'tcx>(
/// 128-bit chunks of `left` and `right`).
fn pack_generic<'tcx>(
this: &mut crate::MiriInterpCx<'tcx>,
left: &OpTy<'tcx, Provenance>,
right: &OpTy<'tcx, Provenance>,
dest: &MPlaceTy<'tcx, Provenance>,
f: impl Fn(Scalar<Provenance>) -> InterpResult<'tcx, Scalar<Provenance>>,
left: &OpTy<'tcx>,
right: &OpTy<'tcx>,
dest: &MPlaceTy<'tcx>,
f: impl Fn(Scalar) -> InterpResult<'tcx, Scalar>,
) -> InterpResult<'tcx, ()> {
assert_eq!(left.layout, right.layout);
assert_eq!(left.layout.size, dest.layout.size);
@ -1187,9 +1256,9 @@ fn pack_generic<'tcx>(
/// 128-bit chunks of `left` and `right`).
fn packsswb<'tcx>(
this: &mut crate::MiriInterpCx<'tcx>,
left: &OpTy<'tcx, Provenance>,
right: &OpTy<'tcx, Provenance>,
dest: &MPlaceTy<'tcx, Provenance>,
left: &OpTy<'tcx>,
right: &OpTy<'tcx>,
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx, ()> {
pack_generic(this, left, right, dest, |op| {
let op = op.to_i16()?;
@ -1206,9 +1275,9 @@ fn packsswb<'tcx>(
/// 128-bit chunks of `left` and `right`).
fn packuswb<'tcx>(
this: &mut crate::MiriInterpCx<'tcx>,
left: &OpTy<'tcx, Provenance>,
right: &OpTy<'tcx, Provenance>,
dest: &MPlaceTy<'tcx, Provenance>,
left: &OpTy<'tcx>,
right: &OpTy<'tcx>,
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx, ()> {
pack_generic(this, left, right, dest, |op| {
let op = op.to_i16()?;
@ -1225,9 +1294,9 @@ fn packuswb<'tcx>(
/// 128-bit chunks of `left` and `right`).
fn packssdw<'tcx>(
this: &mut crate::MiriInterpCx<'tcx>,
left: &OpTy<'tcx, Provenance>,
right: &OpTy<'tcx, Provenance>,
dest: &MPlaceTy<'tcx, Provenance>,
left: &OpTy<'tcx>,
right: &OpTy<'tcx>,
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx, ()> {
pack_generic(this, left, right, dest, |op| {
let op = op.to_i32()?;
@ -1244,9 +1313,9 @@ fn packssdw<'tcx>(
/// 128-bit chunks of `left` and `right`).
fn packusdw<'tcx>(
this: &mut crate::MiriInterpCx<'tcx>,
left: &OpTy<'tcx, Provenance>,
right: &OpTy<'tcx, Provenance>,
dest: &MPlaceTy<'tcx, Provenance>,
left: &OpTy<'tcx>,
right: &OpTy<'tcx>,
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx, ()> {
pack_generic(this, left, right, dest, |op| {
let op = op.to_i32()?;
@ -1261,9 +1330,9 @@ fn packusdw<'tcx>(
/// In other words, multiplies `left` with `right.signum()`.
fn psign<'tcx>(
this: &mut crate::MiriInterpCx<'tcx>,
left: &OpTy<'tcx, Provenance>,
right: &OpTy<'tcx, Provenance>,
dest: &MPlaceTy<'tcx, Provenance>,
left: &OpTy<'tcx>,
right: &OpTy<'tcx>,
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx, ()> {
let (left, left_len) = this.operand_to_simd(left)?;
let (right, right_len) = this.operand_to_simd(right)?;

View file

@ -10,15 +10,13 @@ use super::{
use crate::*;
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
pub(super) trait EvalContextExt<'tcx>:
crate::MiriInterpCxExt<'tcx>
{
pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn emulate_x86_sse_intrinsic(
&mut self,
link_name: Symbol,
abi: Abi,
args: &[OpTy<'tcx, Provenance>],
dest: &MPlaceTy<'tcx, Provenance>,
args: &[OpTy<'tcx>],
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx, EmulateItemResult> {
let this = self.eval_context_mut();
this.expect_target_feature_for_intrinsic(link_name, "sse")?;

View file

@ -9,15 +9,13 @@ use super::{
use crate::*;
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
pub(super) trait EvalContextExt<'tcx>:
crate::MiriInterpCxExt<'tcx>
{
pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn emulate_x86_sse2_intrinsic(
&mut self,
link_name: Symbol,
abi: Abi,
args: &[OpTy<'tcx, Provenance>],
dest: &MPlaceTy<'tcx, Provenance>,
args: &[OpTy<'tcx>],
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx, EmulateItemResult> {
let this = self.eval_context_mut();
this.expect_target_feature_for_intrinsic(link_name, "sse2")?;

View file

@ -6,15 +6,13 @@ use super::horizontal_bin_op;
use crate::*;
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
pub(super) trait EvalContextExt<'tcx>:
crate::MiriInterpCxExt<'tcx>
{
pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn emulate_x86_sse3_intrinsic(
&mut self,
link_name: Symbol,
abi: Abi,
args: &[OpTy<'tcx, Provenance>],
dest: &MPlaceTy<'tcx, Provenance>,
args: &[OpTy<'tcx>],
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx, EmulateItemResult> {
let this = self.eval_context_mut();
this.expect_target_feature_for_intrinsic(link_name, "sse3")?;

View file

@ -5,15 +5,13 @@ use super::{conditional_dot_product, mpsadbw, packusdw, round_all, round_first,
use crate::*;
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
pub(super) trait EvalContextExt<'tcx>:
crate::MiriInterpCxExt<'tcx>
{
pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn emulate_x86_sse41_intrinsic(
&mut self,
link_name: Symbol,
abi: Abi,
args: &[OpTy<'tcx, Provenance>],
dest: &MPlaceTy<'tcx, Provenance>,
args: &[OpTy<'tcx>],
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx, EmulateItemResult> {
let this = self.eval_context_mut();
this.expect_target_feature_for_intrinsic(link_name, "sse4.1")?;

View file

@ -6,15 +6,13 @@ use super::{horizontal_bin_op, int_abs, pmulhrsw, psign};
use crate::*;
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
pub(super) trait EvalContextExt<'tcx>:
crate::MiriInterpCxExt<'tcx>
{
pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn emulate_x86_ssse3_intrinsic(
&mut self,
link_name: Symbol,
abi: Abi,
args: &[OpTy<'tcx, Provenance>],
dest: &MPlaceTy<'tcx, Provenance>,
args: &[OpTy<'tcx>],
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx, EmulateItemResult> {
let this = self.eval_context_mut();
this.expect_target_feature_for_intrinsic(link_name, "ssse3")?;

View file

@ -123,6 +123,10 @@ dependencies = [
"byteorder 1.5.0",
]
[[package]]
name = "test-local-crate-detection"
version = "0.1.0"
[[package]]
name = "unicode-ident"
version = "1.0.12"

View file

@ -1,5 +1,5 @@
[workspace]
members = ["subcrate", "issue-1567", "exported-symbol-dep"]
members = ["subcrate", "issue-1567", "exported-symbol-dep", "test-local-crate-detection"]
exclude = ["no-std-smoke"] # it wants to be panic="abort"
[package]

View file

@ -131,6 +131,10 @@ def test_cargo_miri_run():
cargo_miri("run") + ["--target-dir=custom-run", "--", "--target-dir=target/custom-run"],
"run.args.stdout.ref", "run.custom-target-dir.stderr.ref",
)
test("`cargo miri run --package=test-local-crate-detection` (test local crate detection)",
cargo_miri("run") + ["--package=test-local-crate-detection"],
"run.local_crate.stdout.ref", "run.local_crate.stderr.ref",
)
def test_cargo_miri_test():
# rustdoc is not run on foreign targets

View file

@ -0,0 +1 @@
subcrate,issue_1567,exported_symbol_dep,test_local_crate_detection,cargo_miri_test,cdylib,exported_symbol,issue_1691,issue_1705,issue_rust_86261,proc_macro_crate

View file

@ -0,0 +1,4 @@
[package]
name = "test-local-crate-detection"
version = "0.1.0"
edition = "2021"

View file

@ -0,0 +1,5 @@
fn main() {
// Make sure we detect all crates from this workspace as "local".
// The env var is set during the "build" so we can use `env!` to access it directly.
println!("{}", env!("MIRI_LOCAL_CRATES"));
}

View file

@ -0,0 +1,11 @@
//@only-target-linux
fn main() {
// eventfd read will block when EFD_NONBLOCK flag is clear and counter = 0.
// This will pass when blocking is implemented.
let flags = libc::EFD_CLOEXEC;
let fd = unsafe { libc::eventfd(0, flags) };
let mut buf: [u8; 8] = [0; 8];
let _res: i32 = unsafe {
libc::read(fd, buf.as_mut_ptr().cast(), buf.len() as libc::size_t).try_into().unwrap() //~ERROR: blocking is unsupported
};
}

View file

@ -0,0 +1,14 @@
error: unsupported operation: eventfd: blocking is unsupported
--> $DIR/libc_eventfd_read_block.rs:LL:CC
|
LL | libc::read(fd, buf.as_mut_ptr().cast(), buf.len() as libc::size_t).try_into().unwrap()
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ eventfd: blocking is unsupported
|
= help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support
= note: BACKTRACE:
= note: inside `main` at $DIR/libc_eventfd_read_block.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

View file

@ -0,0 +1,21 @@
//@only-target-linux
fn main() {
// eventfd write will block when EFD_NONBLOCK flag is clear
// and the addition caused counter to exceed u64::MAX - 1.
// This will pass when blocking is implemented.
let flags = libc::EFD_CLOEXEC;
let fd = unsafe { libc::eventfd(0, flags) };
// Write u64 - 1.
let mut sized_8_data: [u8; 8] = (u64::MAX - 1).to_ne_bytes();
let res: i64 = unsafe {
libc::write(fd, sized_8_data.as_ptr() as *const libc::c_void, 8).try_into().unwrap()
};
assert_eq!(res, 8);
// Write 1.
sized_8_data = 1_u64.to_ne_bytes();
// Write 1 to the counter.
let _res: i64 = unsafe {
libc::write(fd, sized_8_data.as_ptr() as *const libc::c_void, 8).try_into().unwrap() //~ERROR: blocking is unsupported
};
}

View file

@ -0,0 +1,14 @@
error: unsupported operation: eventfd: blocking is unsupported
--> $DIR/libc_eventfd_write_block.rs:LL:CC
|
LL | libc::write(fd, sized_8_data.as_ptr() as *const libc::c_void, 8).try_into().unwrap()
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ eventfd: blocking is unsupported
|
= help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support
= note: BACKTRACE:
= note: inside `main` at $DIR/libc_eventfd_write_block.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

View file

@ -0,0 +1,108 @@
//@only-target-linux
// test_race depends on a deterministic schedule.
//@compile-flags: -Zmiri-preemption-rate=0
use std::thread;
fn main() {
test_read_write();
test_race();
}
fn read_bytes<const N: usize>(fd: i32, buf: &mut [u8; N]) -> i32 {
let res: i32 = unsafe { libc::read(fd, buf.as_mut_ptr().cast(), N).try_into().unwrap() };
return res;
}
fn write_bytes<const N: usize>(fd: i32, data: [u8; N]) -> i32 {
let res: i32 =
unsafe { libc::write(fd, data.as_ptr() as *const libc::c_void, N).try_into().unwrap() };
return res;
}
fn test_read_write() {
let flags = libc::EFD_NONBLOCK | libc::EFD_CLOEXEC;
let fd = unsafe { libc::eventfd(0, flags) };
let sized_8_data: [u8; 8] = 1_u64.to_ne_bytes();
// Write 1 to the counter.
let res = write_bytes(fd, sized_8_data);
assert_eq!(res, 8);
// Read 1 from the counter.
let mut buf: [u8; 8] = [0; 8];
let res = read_bytes(fd, &mut buf);
// Read returns number of bytes has been read, which is always 8.
assert_eq!(res, 8);
// Check the value of counter read.
let counter = u64::from_ne_bytes(buf);
assert_eq!(counter, 1);
// After read, the counter is currently 0, read counter 0 should fail with return
// value -1.
let mut buf: [u8; 8] = [0; 8];
let res = read_bytes(fd, &mut buf);
let e = std::io::Error::last_os_error();
assert_eq!(e.raw_os_error(), Some(libc::EAGAIN));
assert_eq!(res, -1);
// Write with supplied buffer bigger than 8 bytes should be allowed.
let sized_9_data: [u8; 9];
if cfg!(target_endian = "big") {
// Adjust the data based on the endianness of host system.
sized_9_data = [0, 0, 0, 0, 0, 0, 0, 1, 0];
} else {
sized_9_data = [1, 0, 0, 0, 0, 0, 0, 0, 0];
}
let res = write_bytes(fd, sized_9_data);
assert_eq!(res, 8);
// Read with supplied buffer smaller than 8 bytes should fail with return
// value -1.
let mut buf: [u8; 7] = [1; 7];
let res = read_bytes(fd, &mut buf);
let e = std::io::Error::last_os_error();
assert_eq!(e.raw_os_error(), Some(libc::EINVAL));
assert_eq!(res, -1);
// Write with supplied buffer smaller than 8 bytes should fail with return
// value -1.
let size_7_data: [u8; 7] = [1; 7];
let res = write_bytes(fd, size_7_data);
let e = std::io::Error::last_os_error();
assert_eq!(e.raw_os_error(), Some(libc::EINVAL));
assert_eq!(res, -1);
// Read with supplied buffer bigger than 8 bytes should be allowed.
let mut buf: [u8; 9] = [1; 9];
let res = read_bytes(fd, &mut buf);
assert_eq!(res, 8);
// Write u64::MAX should fail.
let u64_max_bytes: [u8; 8] = [255; 8];
let res = write_bytes(fd, u64_max_bytes);
let e = std::io::Error::last_os_error();
assert_eq!(e.raw_os_error(), Some(libc::EINVAL));
assert_eq!(res, -1);
}
fn test_race() {
static mut VAL: u8 = 0;
let flags = libc::EFD_NONBLOCK | libc::EFD_CLOEXEC;
let fd = unsafe { libc::eventfd(0, flags) };
let thread1 = thread::spawn(move || {
let mut buf: [u8; 8] = [0; 8];
let res = read_bytes(fd, &mut buf);
// read returns number of bytes has been read, which is always 8.
assert_eq!(res, 8);
let counter = u64::from_ne_bytes(buf);
assert_eq!(counter, 1);
unsafe { assert_eq!(VAL, 1) };
});
unsafe { VAL = 1 };
let data: [u8; 8] = 1_u64.to_ne_bytes();
let res = write_bytes(fd, data);
// write returns number of bytes written, which is always 8.
assert_eq!(res, 8);
thread::yield_now();
thread1.join().unwrap();
}

View file

@ -6,7 +6,5 @@ use std::ptr;
fn main() {
let mut not_a_bool = 13u8;
unsafe {
ptr::drop_in_place(&mut not_a_bool as *mut u8 as *mut bool)
};
unsafe { ptr::drop_in_place(&mut not_a_bool as *mut u8 as *mut bool) };
}

View file

@ -1,5 +1,5 @@
//@compile-flags: -Zmiri-strict-provenance
#![feature(portable_simd, adt_const_params, core_intrinsics)]
#![feature(portable_simd, adt_const_params, core_intrinsics, repr_simd)]
#![allow(incomplete_features, internal_features)]
use std::intrinsics::simd as intrinsics;
use std::ptr;
@ -318,6 +318,83 @@ fn simd_mask() {
assert_eq!(selected1, i32x4::from_array([0, 0, 0, 1]));
assert_eq!(selected2, selected1);
}
// Non-power-of-2 multi-byte mask.
#[repr(simd, packed)]
#[allow(non_camel_case_types)]
#[derive(Copy, Clone, Debug, PartialEq)]
struct i32x10([i32; 10]);
impl i32x10 {
fn splat(x: i32) -> Self {
Self([x; 10])
}
}
unsafe {
let mask = i32x10([!0, !0, 0, !0, 0, 0, !0, 0, !0, 0]);
let mask_bits = if cfg!(target_endian = "little") { 0b0101001011 } else { 0b1101001010 };
let mask_bytes =
if cfg!(target_endian = "little") { [0b01001011, 0b01] } else { [0b11, 0b01001010] };
let bitmask1: u16 = simd_bitmask(mask);
let bitmask2: [u8; 2] = simd_bitmask(mask);
assert_eq!(bitmask1, mask_bits);
assert_eq!(bitmask2, mask_bytes);
let selected1 = simd_select_bitmask::<u16, _>(
mask_bits,
i32x10::splat(!0), // yes
i32x10::splat(0), // no
);
let selected2 = simd_select_bitmask::<[u8; 2], _>(
mask_bytes,
i32x10::splat(!0), // yes
i32x10::splat(0), // no
);
assert_eq!(selected1, mask);
assert_eq!(selected2, mask);
}
// Test for a mask where the next multiple of 8 is not a power of two.
#[repr(simd, packed)]
#[allow(non_camel_case_types)]
#[derive(Copy, Clone, Debug, PartialEq)]
struct i32x20([i32; 20]);
impl i32x20 {
fn splat(x: i32) -> Self {
Self([x; 20])
}
}
unsafe {
let mask = i32x20([!0, !0, 0, !0, 0, 0, !0, 0, !0, 0, 0, 0, 0, !0, !0, !0, !0, !0, !0, !0]);
let mask_bits = if cfg!(target_endian = "little") {
0b11111110000101001011
} else {
0b11010010100001111111
};
let mask_bytes = if cfg!(target_endian = "little") {
[0b01001011, 0b11100001, 0b1111]
} else {
[0b1101, 0b00101000, 0b01111111]
};
let bitmask1: u32 = simd_bitmask(mask);
let bitmask2: [u8; 3] = simd_bitmask(mask);
assert_eq!(bitmask1, mask_bits);
assert_eq!(bitmask2, mask_bytes);
let selected1 = simd_select_bitmask::<u32, _>(
mask_bits,
i32x20::splat(!0), // yes
i32x20::splat(0), // no
);
let selected2 = simd_select_bitmask::<[u8; 3], _>(
mask_bytes,
i32x20::splat(!0), // yes
i32x20::splat(0), // no
);
assert_eq!(selected1, mask);
assert_eq!(selected2, mask);
}
}
fn simd_cast() {

View file

@ -1,3 +1,4 @@
//@compile-flags: -Zmiri-preemption-rate=0
use std::env;
use std::thread;
@ -26,6 +27,8 @@ fn main() {
println!("{:#?}", env::vars().collect::<Vec<_>>());
// Do things concurrently, to make sure there's no data race.
// We disable preemption to make sure the lock is not contended;
// that means we don't hit e.g. the futex codepath on Android (which we don't support).
let t = thread::spawn(|| {
env::set_var("MIRI_TEST", "42");
});

View file

@ -0,0 +1,48 @@
// Ignore everything except x86 and x86_64
// Any new targets that are added to CI should be ignored here.
// (We cannot use `cfg`-based tricks here since the `target-feature` flags below only work on x86.)
//@ignore-target-aarch64
//@ignore-target-arm
//@ignore-target-avr
//@ignore-target-s390x
//@ignore-target-thumbv7em
//@ignore-target-wasm32
//@compile-flags: -C target-feature=+pclmulqdq
#[cfg(target_arch = "x86")]
use std::arch::x86::*;
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::*;
fn main() {
assert!(is_x86_feature_detected!("pclmulqdq"));
let a = (0x7fffffffffffffff, 0x4317e40ab4ddcf05);
let b = (0xdd358416f52ecd34, 0x633d11cc638ca16b);
unsafe {
assert_eq!(clmulepi64_si128::<0x00>(a, b), (13036940098130298092, 2704901987789626761));
assert_eq!(clmulepi64_si128::<0x01>(a, b), (6707488474444649956, 3901733953304450635));
assert_eq!(clmulepi64_si128::<0x10>(a, b), (11607166829323378905, 1191897396234301548));
assert_eq!(clmulepi64_si128::<0x11>(a, b), (7731954893213347271, 1760130762532070957));
}
}
#[target_feature(enable = "pclmulqdq")]
unsafe fn clmulepi64_si128<const IMM8: i32>(
(a1, a2): (u64, u64),
(b1, b2): (u64, u64),
) -> (u64, u64) {
// SAFETY: There are no safety requirements for calling `_mm_clmulepi64_si128`.
// It's just unsafe for API consistency with other intrinsics.
unsafe {
let a = core::mem::transmute::<_, __m128i>([a1, a2]);
let b = core::mem::transmute::<_, __m128i>([b1, b2]);
let out = _mm_clmulepi64_si128::<IMM8>(a, b);
let [c1, c2] = core::mem::transmute::<_, [u64; 2]>(out);
(c1, c2)
}
}