Auto merge of #126193 - RalfJung:miri-sync, r=RalfJung
Miri subtree update r? `@ghost`
This commit is contained in:
commit
7bb0ef4902
89 changed files with 1504 additions and 1091 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
7
src/tools/miri/bench-cargo-miri/big-allocs/Cargo.lock
Normal file
7
src/tools/miri/bench-cargo-miri/big-allocs/Cargo.lock
Normal 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"
|
||||
8
src/tools/miri/bench-cargo-miri/big-allocs/Cargo.toml
Normal file
8
src/tools/miri/bench-cargo-miri/big-allocs/Cargo.toml
Normal 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]
|
||||
13
src/tools/miri/bench-cargo-miri/big-allocs/src/main.rs
Normal file
13
src/tools/miri/bench-cargo-miri/big-allocs/src/main.rs
Normal 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));
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
21e6de7eb64c09102de3f100420a09edc1a2a8d7
|
||||
565cadb514d35e7b851540edbc172af0f606014f
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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};
|
||||
|
|
|
|||
|
|
@ -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> {
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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},
|
||||
|
|
|
|||
|
|
@ -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)`
|
||||
|
|
|
|||
|
|
@ -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 } },
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -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> {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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> {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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::{
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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)?;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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)?;
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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") }
|
||||
|
|
|
|||
|
|
@ -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)?;
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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())),
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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)>,
|
||||
|
|
|
|||
|
|
@ -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()?;
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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)?;
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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)?;
|
||||
|
|
|
|||
|
|
@ -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()?;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)?;
|
||||
|
|
|
|||
|
|
@ -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)?;
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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)?;
|
||||
|
|
|
|||
|
|
@ -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)?;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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")?;
|
||||
|
|
|
|||
|
|
@ -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")?;
|
||||
|
|
|
|||
|
|
@ -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)?;
|
||||
|
|
|
|||
|
|
@ -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")?;
|
||||
|
|
|
|||
|
|
@ -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")?;
|
||||
|
|
|
|||
|
|
@ -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")?;
|
||||
|
|
|
|||
|
|
@ -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")?;
|
||||
|
|
|
|||
|
|
@ -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")?;
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
[package]
|
||||
name = "test-local-crate-detection"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
|
@ -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"));
|
||||
}
|
||||
|
|
@ -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
|
||||
};
|
||||
}
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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
|
||||
};
|
||||
}
|
||||
|
|
@ -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
|
||||
|
||||
108
src/tools/miri/tests/pass-dep/libc/libc-eventfd.rs
Normal file
108
src/tools/miri/tests/pass-dep/libc/libc-eventfd.rs
Normal 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();
|
||||
}
|
||||
|
|
@ -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) };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
3
src/tools/miri/tests/pass/shims/env/var.rs
vendored
3
src/tools/miri/tests/pass/shims/env/var.rs
vendored
|
|
@ -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");
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue