Skip to content
Merged
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
e928185
support `f*_algebraic`
bend-n May 11, 2024
32b2238
Auto merge of #3596 - bend-n:support_fstar_algebraic, r=RalfJung
bors May 11, 2024
bf5906f
Add non-null pointer for posix_memalign
tiif May 12, 2024
fd5e037
Auto merge of #3600 - tiif:non-null-posix-memalign, r=RalfJung
bors May 12, 2024
9fc569d
organize float intrinsic implementations a bit
RalfJung May 12, 2024
5d76ec9
merge float tests into one
RalfJung May 12, 2024
01b5430
merge two integer tests
RalfJung May 12, 2024
14ced80
Auto merge of #3601 - RalfJung:intrinsics, r=RalfJung
bors May 12, 2024
7d565df
Preparing for merge from rustc
RalfJung May 12, 2024
1e7fba9
Merge from rustc
RalfJung May 12, 2024
75d531d
Auto merge of #3602 - RalfJung:rustup, r=RalfJung
bors May 12, 2024
cd7527a
Don't print unnecessary sysroot messages
saethlin May 11, 2024
e4f81d1
Auto merge of #3599 - saethlin:quiet-when-no-change, r=RalfJung
bors May 12, 2024
10acfd9
further illumos/solaris support.
devnexen May 5, 2024
d4937cb
Auto merge of #3575 - devnexen:illumos_part3, r=RalfJung
bors May 12, 2024
c6a0e2c
intrinsics: just panic when they get used incorrectly
RalfJung May 13, 2024
d0b2c66
Auto merge of #3604 - RalfJung:intrinsics, r=RalfJung
bors May 13, 2024
99c6b2e
Give `FileDescription::{read, write}` access to the `InterpCx`
Luv-Ray May 13, 2024
b1b278b
Preparing for merge from rustc
RalfJung May 16, 2024
0334bf8
Merge from rustc
RalfJung May 16, 2024
6d314f3
alloc now works on wasi (and some formatting)
RalfJung May 16, 2024
7e5b9e2
Auto merge of #3606 - RalfJung:rustup, r=RalfJung
bors May 16, 2024
983fb09
start implementing MiriAllocBytes
Strophox Apr 3, 2024
fffc8e9
Auto merge of #3526 - Strophox:miri-memory, r=RalfJung
bors May 17, 2024
5b2fdb6
Auto merge of #3603 - Luv-Ray:issue-3572, r=RalfJung
bors May 17, 2024
318a0fe
Ignore the Helix configuration directory
marc0246 May 17, 2024
421538c
Auto merge of #3611 - marc0246:helix-ignore, r=saethlin
bors May 17, 2024
5ea21ca
support aligned_alloc for unixes support.
devnexen May 7, 2024
c5c820e
Fix typos (taking into account review comments)
blyxyas May 18, 2024
1ba83f2
Preparing for merge from rustc
RalfJung May 19, 2024
3460853
Merge from rustc
RalfJung May 19, 2024
8d7c8ac
Auto merge of #3615 - RalfJung:rustup, r=RalfJung
bors May 19, 2024
430298c
a bit of refactoring and tweak the aligned-allocation tests
RalfJung May 19, 2024
844de64
make basic things work on Android
RalfJung May 19, 2024
3726afa
Auto merge of #3585 - devnexen:aligned_alloc, r=RalfJung
bors May 19, 2024
0e41a80
Auto merge of #3616 - RalfJung:android, r=RalfJung
bors May 19, 2024
2b9c1ca
properly print error in 'cargo miri setup --print-sysroot'
RalfJung May 19, 2024
3c15681
Auto merge of #3619 - RalfJung:print-sysroot, r=RalfJung
bors May 19, 2024
b8a7c73
test wasm32-wasip2 instead of the deprecated wasm32-wasi target
RalfJung May 19, 2024
006866f
make `Debug` impl for `Term` simpler
WaffleLapkin May 19, 2024
f7520e4
Auto merge of #3620 - RalfJung:wasi, r=RalfJung
bors May 19, 2024
9cba160
use a little arg-parsing helper for miri-script
RalfJung May 19, 2024
a32423c
Auto merge of #3621 - RalfJung:argparse, r=RalfJung
bors May 19, 2024
42cb1ff
Directly implement native exception raise methods in miri
bjorn3 Feb 24, 2024
5e41ff5
various small nits
RalfJung May 19, 2024
85ed056
Auto merge of #3319 - bjorn3:some_more_shims, r=RalfJung
bors May 19, 2024
0b6baf6
Add example to IsTerminal::is_terminal
foresterre May 10, 2024
e93268e
update lockfile
RalfJung May 19, 2024
d5bef41
Rollup merge of #124948 - blyxyas:remove-repeated-words, r=compiler-e…
matthiaskrgr May 19, 2024
c5b8c7c
Rollup merge of #124992 - foresterre:example/is-terminal, r=ChrisDenton
matthiaskrgr May 19, 2024
131d48f
Rollup merge of #125279 - WaffleLapkin:unpacktermindebug, r=aDotInThe…
matthiaskrgr May 19, 2024
7a45322
Rollup merge of #125286 - RalfJung:miri-sync, r=RalfJung
matthiaskrgr May 19, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Cargo.lock
Original file line number Diff line number Diff line change
@@ -3426,9 +3426,9 @@ dependencies = [

[[package]]
name = "rustc-build-sysroot"
version = "0.4.7"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab1dbbd1bdf65fdac44c885f6cca147ba179108ce284b60a08ccc04b1f1dbac0"
checksum = "fa3ca63cc537c1cb69e4c2c0afc5fda2ccd36ac84c97d5a4ae05e69b1c834afb"
dependencies = [
"anyhow",
"rustc_version",
2 changes: 1 addition & 1 deletion compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
Original file line number Diff line number Diff line change
@@ -652,7 +652,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
}

// FIXME: We make sure that this is a normal top-level binding,
// but we could suggest `todo!()` for all uninitialized bindings in the pattern pattern
// but we could suggest `todo!()` for all uninitialized bindings in the pattern
if let hir::StmtKind::Let(hir::LetStmt { span, ty, init: None, pat, .. }) =
&ex.kind
&& let hir::PatKind::Binding(..) = pat.kind
2 changes: 1 addition & 1 deletion compiler/rustc_hir_typeck/src/expr.rs
Original file line number Diff line number Diff line change
@@ -3112,7 +3112,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {

let true_errors = ocx.select_where_possible();

// Do a leak check -- we can't really report report a useful error here,
// Do a leak check -- we can't really report a useful error here,
// but it at least avoids an ICE when the error has to do with higher-ranked
// lifetimes.
self.leak_check(outer_universe, Some(snapshot))?;
2 changes: 1 addition & 1 deletion compiler/rustc_lint/src/non_local_def.rs
Original file line number Diff line number Diff line change
@@ -38,7 +38,7 @@ declare_lint! {
///
/// Creating non-local definitions go against expectation and can create discrepancies
/// in tooling. It should be avoided. It may become deny-by-default in edition 2024
/// and higher, see see the tracking issue <https://github.com/rust-lang/rust/issues/120363>.
/// and higher, see the tracking issue <https://github.com/rust-lang/rust/issues/120363>.
///
/// An `impl` definition is non-local if it is nested inside an item and neither
/// the type nor the trait are at the same nesting level as the `impl` block.
12 changes: 4 additions & 8 deletions compiler/rustc_middle/src/ty/mod.rs
Original file line number Diff line number Diff line change
@@ -536,14 +536,10 @@ unsafe impl<'tcx> Sync for Term<'tcx> where &'tcx (Ty<'tcx>, Const<'tcx>): Sync

impl Debug for Term<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let data = if let Some(ty) = self.ty() {
format!("Term::Ty({ty:?})")
} else if let Some(ct) = self.ct() {
format!("Term::Ct({ct:?})")
} else {
unreachable!()
};
f.write_str(&data)
match self.unpack() {
TermKind::Ty(ty) => write!(f, "Term::Ty({ty:?})"),
TermKind::Const(ct) => write!(f, "Term::Const({ct:?})"),
}
}
}

Original file line number Diff line number Diff line change
@@ -269,7 +269,7 @@ fn trait_object_ty<'tcx>(tcx: TyCtxt<'tcx>, poly_trait_ref: ty::PolyTraitRef<'tc
/// if a function is member of the group derived from this type id. Therefore, in the first call to
/// typeid_for_fnabi (when type ids are attached to functions and methods), it can only include at
/// most as much information that would be available in the second call (i.e., during code
/// generation at call sites); otherwise, the type ids would not not match.
/// generation at call sites); otherwise, the type ids would not match.
///
/// For this, it:
///
2 changes: 1 addition & 1 deletion library/portable-simd/crates/core_simd/src/ops.rs
Original file line number Diff line number Diff line change
@@ -122,7 +122,7 @@ macro_rules! for_base_types {
#[inline]
#[must_use = "operator returns a new vector without mutating the inputs"]
// TODO: only useful for int Div::div, but we hope that this
// will essentially always always get inlined anyway.
// will essentially always get inlined anyway.
#[track_caller]
fn $call(self, rhs: Self) -> Self::Output {
$macro_impl!(self, rhs, $inner, $scalar)
34 changes: 34 additions & 0 deletions library/std/src/io/stdio.rs
Original file line number Diff line number Diff line change
@@ -1161,7 +1161,41 @@ pub trait IsTerminal: crate::sealed::Sealed {
/// starting with `msys-` or `cygwin-` and ending in `-pty` will be considered terminals.
/// Note that this [may change in the future][changes].
///
/// # Examples
///
/// An example of a type for which `IsTerminal` is implemented is [`Stdin`]:
///
/// ```no_run
/// use std::io::{self, IsTerminal, Write};
///
/// fn main() -> io::Result<()> {
/// let stdin = io::stdin();
///
/// // Indicate that the user is prompted for input, if this is a terminal.
/// if stdin.is_terminal() {
/// print!("> ");
/// io::stdout().flush()?;
/// }
///
/// let mut name = String::new();
/// let _ = stdin.read_line(&mut name)?;
///
/// println!("Hello {}", name.trim_end());
///
/// Ok(())
/// }
/// ```
///
/// The example can be run in two ways:
///
/// - If you run this example by piping some text to it, e.g. `echo "foo" | path/to/executable`
/// it will print: `Hello foo`.
/// - If you instead run the example interactively by running the executable directly, it will
/// panic with the message "Expected input to be piped to the process".
///
///
/// [changes]: io#platform-specific-behavior
/// [`Stdin`]: crate::io::Stdin
#[stable(feature = "is_terminal", since = "1.70.0")]
fn is_terminal(&self) -> bool;
}
Original file line number Diff line number Diff line change
@@ -127,7 +127,7 @@ pub(super) fn check<'tcx>(
}
}

/// checks for for collecting into a (generic) method or function argument
/// checks for collecting into a (generic) method or function argument
/// taking an `IntoIterator`
fn check_collect_into_intoiterator<'tcx>(
cx: &LateContext<'tcx>,
2 changes: 1 addition & 1 deletion src/tools/clippy/tests/ui/ptr_arg.rs
Original file line number Diff line number Diff line change
@@ -282,7 +282,7 @@ mod issue_9218 {
todo!()
}

// These two's return types don't use use 'a so it's not okay
// These two's return types don't use 'a so it's not okay
fn cow_bad_ret_ty_1<'a>(input: &'a Cow<'a, str>) -> &'static str {
//~^ ERROR: using a reference to `Cow` is not recommended
todo!()
4 changes: 2 additions & 2 deletions src/tools/compiletest/src/common.rs
Original file line number Diff line number Diff line change
@@ -250,7 +250,7 @@ pub struct Config {
/// Only run tests that match these filters
pub filters: Vec<String>,

/// Skip tests tests matching these substrings. Corresponds to
/// Skip tests matching these substrings. Corresponds to
/// `test::TestOpts::skip`. `filter_exact` does not apply to these flags.
pub skip: Vec<String>,

@@ -381,7 +381,7 @@ pub struct Config {
/// Whether to rerun tests even if the inputs are unchanged.
pub force_rerun: bool,

/// Only rerun the tests that result has been modified accoring to Git status
/// Only rerun the tests that result has been modified according to Git status
pub only_modified: bool,

pub target_cfgs: OnceLock<TargetCfgs>,
2 changes: 1 addition & 1 deletion src/tools/compiletest/src/lib.rs
Original file line number Diff line number Diff line change
@@ -950,7 +950,7 @@ fn is_android_gdb_target(target: &str) -> bool {
)
}

/// Returns `true` if the given target is a MSVC target for the purpouses of CDB testing.
/// Returns `true` if the given target is a MSVC target for the purposes of CDB testing.
fn is_pc_windows_msvc_target(target: &str) -> bool {
target.ends_with("-pc-windows-msvc")
}
2 changes: 1 addition & 1 deletion src/tools/jsondoclint/src/validator.rs
Original file line number Diff line number Diff line change
@@ -21,7 +21,7 @@ const LOCAL_CRATE_ID: u32 = 0;
/// it is well formed. This involves calling `check_*` functions on
/// fields of that item, and `add_*` functions on [`Id`]s.
/// - `add_*`: These add an [`Id`] to the worklist, after validating it to check if
/// the `Id` is a kind expected in this suituation.
/// the `Id` is a kind expected in this situation.
#[derive(Debug)]
pub struct Validator<'a> {
pub(crate) errs: Vec<Error>,
2 changes: 1 addition & 1 deletion src/tools/lld-wrapper/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Script to invoke the bundled rust-lld with the correct flavor.
//!
//! lld supports multiple command line interfaces. If `-flavor <flavor>` are passed as the first
//! `lld` supports multiple command line interfaces. If `-flavor <flavor>` are passed as the first
//! two arguments the `<flavor>` command line interface is used to process the remaining arguments.
//! If no `-flavor` argument is present the flavor is determined by the executable name.
//!
1 change: 1 addition & 0 deletions src/tools/miri/.gitignore
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@ tex/*/out
*.out
*.rs.bk
.vscode
.helix
*.mm_profdata
perf.data
perf.data.old
4 changes: 2 additions & 2 deletions src/tools/miri/cargo-miri/Cargo.lock
Original file line number Diff line number Diff line change
@@ -178,9 +178,9 @@ dependencies = [

[[package]]
name = "rustc-build-sysroot"
version = "0.4.7"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab1dbbd1bdf65fdac44c885f6cca147ba179108ce284b60a08ccc04b1f1dbac0"
checksum = "fa3ca63cc537c1cb69e4c2c0afc5fda2ccd36ac84c97d5a4ae05e69b1c834afb"
dependencies = [
"anyhow",
"rustc_version",
2 changes: 1 addition & 1 deletion src/tools/miri/cargo-miri/Cargo.toml
Original file line number Diff line number Diff line change
@@ -18,7 +18,7 @@ directories = "5"
rustc_version = "0.4"
serde_json = "1.0.40"
cargo_metadata = "0.18.0"
rustc-build-sysroot = "0.4.6"
rustc-build-sysroot = "0.5.2"

# Enable some feature flags that dev-dependencies need but dependencies
# do not. This makes `./miri install` after `./miri build` faster.
87 changes: 44 additions & 43 deletions src/tools/miri/cargo-miri/src/setup.rs
Original file line number Diff line number Diff line change
@@ -2,11 +2,10 @@
use std::env;
use std::ffi::OsStr;
use std::fmt::Write;
use std::path::PathBuf;
use std::process::{self, Command};

use rustc_build_sysroot::{BuildMode, SysrootBuilder, SysrootConfig};
use rustc_build_sysroot::{BuildMode, SysrootBuilder, SysrootConfig, SysrootStatus};
use rustc_version::VersionMeta;

use crate::util::*;
@@ -24,6 +23,7 @@ pub fn setup(
let only_setup = matches!(subcommand, MiriCommand::Setup);
let ask_user = !only_setup;
let print_sysroot = only_setup && has_arg_flag("--print-sysroot"); // whether we just print the sysroot path
let show_setup = only_setup && !print_sysroot;
if !only_setup {
if let Some(sysroot) = std::env::var_os("MIRI_SYSROOT") {
// Skip setup step if MIRI_SYSROOT is explicitly set, *unless* we are `cargo miri setup`.
@@ -115,18 +115,16 @@ pub fn setup(
// `config.toml`.
command.env("RUSTC_WRAPPER", "");

if only_setup && !print_sysroot {
if show_setup {
// Forward output. Even make it verbose, if requested.
command.stdout(process::Stdio::inherit());
command.stderr(process::Stdio::inherit());
for _ in 0..verbose {
command.arg("-v");
}
if quiet {
command.arg("--quiet");
}
} else {
// Suppress output.
command.stdout(process::Stdio::null());
command.stderr(process::Stdio::null());
}

command
@@ -137,49 +135,52 @@ pub fn setup(
// not apply `RUSTFLAGS` to the sysroot either.
let rustflags = &["-Cdebug-assertions=off", "-Coverflow-checks=on"];

// Do the build.
if print_sysroot || quiet {
// Be silent.
} else {
let mut msg = String::new();
write!(msg, "Preparing a sysroot for Miri (target: {target})").unwrap();
if verbose > 0 {
write!(msg, " in {}", sysroot_dir.display()).unwrap();
}
write!(msg, "...").unwrap();
if only_setup {
// We want to be explicit.
eprintln!("{msg}");
} else {
// We want to be quiet, but still let the user know that something is happening.
eprint!("{msg} ");
let mut after_build_output = String::new(); // what should be printed when the build is done.
let notify = || {
if !quiet {
eprint!("Preparing a sysroot for Miri (target: {target})");
if verbose > 0 {
eprint!(" in {}", sysroot_dir.display());
}
if show_setup {
// Cargo will print things, so we need to finish this line.
eprintln!("...");
after_build_output = format!(
"A sysroot for Miri is now available in `{}`.\n",
sysroot_dir.display()
);
} else {
// Keep all output on a single line.
eprint!("... ");
after_build_output = format!("done\n");
}
}
}
SysrootBuilder::new(&sysroot_dir, target)
};

// Do the build.
let status = SysrootBuilder::new(&sysroot_dir, target)
.build_mode(BuildMode::Check)
.rustc_version(rustc_version.clone())
.sysroot_config(sysroot_config)
.rustflags(rustflags)
.cargo(cargo_cmd)
.build_from_source(&rust_src)
.unwrap_or_else(|err| {
if print_sysroot {
show_error!("failed to build sysroot")
} else if only_setup {
show_error!("failed to build sysroot: {err:?}")
} else {
show_error!(
"failed to build sysroot; run `cargo miri setup` to see the error details"
)
}
});
if print_sysroot || quiet {
// Be silent.
} else if only_setup {
eprintln!("A sysroot for Miri is now available in `{}`.", sysroot_dir.display());
} else {
eprintln!("done");
.when_build_required(notify)
.build_from_source(&rust_src);
match status {
Ok(SysrootStatus::AlreadyCached) =>
if !quiet && show_setup {
eprintln!(
"A sysroot for Miri is already available in `{}`.",
sysroot_dir.display()
);
},
Ok(SysrootStatus::SysrootBuilt) => {
// Print what `notify` prepared.
eprint!("{after_build_output}");
}
Err(err) => show_error!("failed to build sysroot: {err:?}"),
}

if print_sysroot {
// Print just the sysroot and nothing else to stdout; this way we do not need any escaping.
println!("{}", sysroot_dir.display());
2 changes: 1 addition & 1 deletion src/tools/miri/cargo-miri/src/util.rs
Original file line number Diff line number Diff line change
@@ -269,7 +269,7 @@ pub fn get_target_dir(meta: &Metadata) -> PathBuf {
output
}

/// Determines where the sysroot of this exeuction is
/// Determines where the sysroot of this execution is
///
/// Either in a user-specified spot by an envar, or in a default cache location.
pub fn get_sysroot_dir() -> PathBuf {
20 changes: 10 additions & 10 deletions src/tools/miri/ci/ci.sh
Original file line number Diff line number Diff line change
@@ -144,16 +144,16 @@ case $HOST_TARGET in
TEST_TARGET=arm-unknown-linux-gnueabi run_tests
TEST_TARGET=s390x-unknown-linux-gnu run_tests # big-endian architecture of choice
# Partially supported targets (tier 2)
VERY_BASIC="integer vec string btreemap" # common things we test on all of them (if they have std), requires no target-specific shims
BASIC="$VERY_BASIC hello hashmap alloc align" # ensures we have the shims for stdout and basic data structures
TEST_TARGET=x86_64-unknown-freebsd run_tests_minimal $BASIC panic/panic concurrency/simple atomic threadname libc-mem libc-misc libc-random libc-time fs env num_cpus
TEST_TARGET=i686-unknown-freebsd run_tests_minimal $BASIC panic/panic concurrency/simple atomic threadname libc-mem libc-misc libc-random libc-time fs env num_cpus
TEST_TARGET=x86_64-unknown-illumos run_tests_minimal $VERY_BASIC hello panic/panic concurrency/simple pthread-sync libc-mem libc-misc libc-random
TEST_TARGET=x86_64-pc-solaris run_tests_minimal $VERY_BASIC hello panic/panic concurrency/simple pthread-sync libc-mem libc-misc libc-random
TEST_TARGET=aarch64-linux-android run_tests_minimal $VERY_BASIC hello panic/panic
TEST_TARGET=wasm32-wasi run_tests_minimal $VERY_BASIC wasm
TEST_TARGET=wasm32-unknown-unknown run_tests_minimal $VERY_BASIC wasm
TEST_TARGET=thumbv7em-none-eabihf run_tests_minimal no_std
BASIC="empty_main integer vec string btreemap hello hashmap heap_alloc align" # ensures we have the basics: stdout/stderr, system allocator, randomness (for HashMap initialization)
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 pthread-sync
TEST_TARGET=x86_64-pc-solaris run_tests_minimal $BASIC $UNIX pthread-sync
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
TEST_TARGET=thumbv7em-none-eabihf run_tests_minimal no_std
# Custom target JSON file
TEST_TARGET=tests/avr.json MIRI_NO_STD=1 run_tests_minimal no_std
;;
136 changes: 136 additions & 0 deletions src/tools/miri/miri-script/src/args.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
use std::env;
use std::iter;

use anyhow::{bail, Result};

pub struct Args {
args: iter::Peekable<env::Args>,
/// Set to `true` once we saw a `--`.
terminated: bool,
}

impl Args {
pub fn new() -> Self {
let mut args = Args { args: env::args().peekable(), terminated: false };
args.args.next().unwrap(); // skip program name
args
}

/// Get the next argument without any interpretation.
pub fn next_raw(&mut self) -> Option<String> {
self.args.next()
}

/// Consume a `-$f` flag if present.
pub fn get_short_flag(&mut self, flag: char) -> Result<bool> {
if self.terminated {
return Ok(false);
}
if let Some(next) = self.args.peek() {
if let Some(next) = next.strip_prefix("-") {
if let Some(next) = next.strip_prefix(flag) {
if next.is_empty() {
self.args.next().unwrap(); // consume this argument
return Ok(true);
} else {
bail!("`-{flag}` followed by value");
}
}
}
}
Ok(false)
}

/// Consume a `--$name` flag if present.
pub fn get_long_flag(&mut self, name: &str) -> Result<bool> {
if self.terminated {
return Ok(false);
}
if let Some(next) = self.args.peek() {
if let Some(next) = next.strip_prefix("--") {
if next == name {
self.args.next().unwrap(); // consume this argument
return Ok(true);
}
}
}
Ok(false)
}

/// Consume a `--$name val` or `--$name=val` option if present.
pub fn get_long_opt(&mut self, name: &str) -> Result<Option<String>> {
assert!(!name.is_empty());
if self.terminated {
return Ok(None);
}
let Some(next) = self.args.peek() else { return Ok(None) };
let Some(next) = next.strip_prefix("--") else { return Ok(None) };
let Some(next) = next.strip_prefix(name) else { return Ok(None) };
// Starts with `--flag`.
Ok(if let Some(val) = next.strip_prefix("=") {
// `--flag=val` form
let val = val.into();
self.args.next().unwrap(); // consume this argument
Some(val)
} else if next.is_empty() {
// `--flag val` form
self.args.next().unwrap(); // consume this argument
let Some(val) = self.args.next() else { bail!("`--{name}` not followed by value") };
Some(val)
} else {
// Some unrelated flag, like `--flag-more` or so.
None
})
}

/// Consume a `--$name=val` or `--$name` option if present; the latter
/// produces a default value. (`--$name val` is *not* accepted for this form
/// of argument, it understands `val` already as the next argument!)
pub fn get_long_opt_with_default(
&mut self,
name: &str,
default: &str,
) -> Result<Option<String>> {
assert!(!name.is_empty());
if self.terminated {
return Ok(None);
}
let Some(next) = self.args.peek() else { return Ok(None) };
let Some(next) = next.strip_prefix("--") else { return Ok(None) };
let Some(next) = next.strip_prefix(name) else { return Ok(None) };
// Starts with `--flag`.
Ok(if let Some(val) = next.strip_prefix("=") {
// `--flag=val` form
let val = val.into();
self.args.next().unwrap(); // consume this argument
Some(val)
} else if next.is_empty() {
// `--flag` form
self.args.next().unwrap(); // consume this argument
Some(default.into())
} else {
// Some unrelated flag, like `--flag-more` or so.
None
})
}

/// Returns the next free argument or uninterpreted flag, or `None` if there are no more
/// arguments left. `--` is returned as well, but it is interpreted in the sense that no more
/// flags will be parsed after this.
pub fn get_other(&mut self) -> Option<String> {
if self.terminated {
return self.args.next();
}
let next = self.args.next()?;
if next == "--" {
self.terminated = true; // don't parse any more flags
// This is where our parser is special, we do yield the `--`.
}
Some(next)
}

/// Return the rest of the aguments entirely unparsed.
pub fn remainder(self) -> Vec<String> {
self.args.collect()
}
}
99 changes: 53 additions & 46 deletions src/tools/miri/miri-script/src/commands.rs
Original file line number Diff line number Diff line change
@@ -25,7 +25,11 @@ impl MiriEnv {
/// Returns the location of the sysroot.
///
/// If the target is None the sysroot will be built for the host machine.
fn build_miri_sysroot(&mut self, quiet: bool, target: Option<&OsStr>) -> Result<PathBuf> {
fn build_miri_sysroot(
&mut self,
quiet: bool,
target: Option<impl AsRef<OsStr>>,
) -> Result<PathBuf> {
if let Some(miri_sysroot) = self.sh.var_os("MIRI_SYSROOT") {
// Sysroot already set, use that.
return Ok(miri_sysroot.into());
@@ -37,33 +41,27 @@ impl MiriEnv {
self.build(path!(self.miri_dir / "Cargo.toml"), &[], quiet)?;
self.build(&manifest_path, &[], quiet)?;

let target_flag =
if let Some(target) = target { vec![OsStr::new("--target"), target] } else { vec![] };
let target_flag = if let Some(target) = &target {
vec![OsStr::new("--target"), target.as_ref()]
} else {
vec![]
};
let target_flag = &target_flag;

if !quiet {
if let Some(target) = target {
eprintln!("$ (building Miri sysroot for {})", target.to_string_lossy());
} else {
eprintln!("$ (building Miri sysroot)");
eprint!("$ cargo miri setup");
if let Some(target) = &target {
eprint!(" --target {target}", target = target.as_ref().to_string_lossy());
}
eprintln!();
}

let output = cmd!(self.sh,
let mut cmd = cmd!(self.sh,
"cargo +{toolchain} --quiet run {cargo_extra_flags...} --manifest-path {manifest_path} --
miri setup --print-sysroot {target_flag...}"
).read();
let Ok(output) = output else {
// Run it again (without `--print-sysroot` or `--quiet`) so the user can see the error.
cmd!(
self.sh,
"cargo +{toolchain} run {cargo_extra_flags...} --manifest-path {manifest_path} --
miri setup {target_flag...}"
)
.run()
.with_context(|| "`cargo miri setup` failed")?;
panic!("`cargo miri setup` didn't fail again the 2nd time?");
};
);
cmd.set_quiet(quiet);
let output = cmd.read()?;
self.sh.set_var("MIRI_SYSROOT", &output);
Ok(output.into())
}
@@ -166,8 +164,8 @@ impl Command {
Command::Build { flags } => Self::build(flags),
Command::Check { flags } => Self::check(flags),
Command::Test { bless, flags, target } => Self::test(bless, flags, target),
Command::Run { dep, verbose, many_seeds, flags } =>
Self::run(dep, verbose, many_seeds, flags),
Command::Run { dep, verbose, many_seeds, target, edition, flags } =>
Self::run(dep, verbose, many_seeds, target, edition, flags),
Command::Fmt { flags } => Self::fmt(flags),
Command::Clippy { flags } => Self::clippy(flags),
Command::Cargo { flags } => Self::cargo(flags),
@@ -178,7 +176,7 @@ impl Command {
}
}

fn toolchain(flags: Vec<OsString>) -> Result<()> {
fn toolchain(flags: Vec<String>) -> Result<()> {
// Make sure rustup-toolchain-install-master is installed.
which::which("rustup-toolchain-install-master")
.context("Please install rustup-toolchain-install-master by running 'cargo install rustup-toolchain-install-master'")?;
@@ -255,7 +253,7 @@ impl Command {
cmd!(sh, "git fetch http://localhost:{JOSH_PORT}/rust-lang/rust.git@{commit}{JOSH_FILTER}.git")
.run()
.map_err(|e| {
// Try to un-do the previous `git commit`, to leave the repo in the state we found it it.
// Try to un-do the previous `git commit`, to leave the repo in the state we found it.
cmd!(sh, "git reset --hard HEAD^")
.run()
.expect("FAILED to clean up again after failed `git fetch`, sorry for that");
@@ -373,7 +371,7 @@ impl Command {
Ok(())
}

fn bench(target: Option<OsString>, benches: Vec<OsString>) -> Result<()> {
fn bench(target: Option<String>, benches: Vec<String>) -> Result<()> {
// The hyperfine to use
let hyperfine = env::var("HYPERFINE");
let hyperfine = hyperfine.as_deref().unwrap_or("hyperfine -w 1 -m 5 --shell=none");
@@ -387,14 +385,14 @@ impl Command {
let sh = Shell::new()?;
sh.change_dir(miri_dir()?);
let benches_dir = "bench-cargo-miri";
let benches = if benches.is_empty() {
let benches: Vec<OsString> = if benches.is_empty() {
sh.read_dir(benches_dir)?
.into_iter()
.filter(|path| path.is_dir())
.map(Into::into)
.collect()
} else {
benches.to_owned()
benches.into_iter().map(Into::into).collect()
};
let target_flag = if let Some(target) = target {
let mut flag = OsString::from("--target=");
@@ -418,36 +416,36 @@ impl Command {
Ok(())
}

fn install(flags: Vec<OsString>) -> Result<()> {
fn install(flags: Vec<String>) -> Result<()> {
let e = MiriEnv::new()?;
e.install_to_sysroot(e.miri_dir.clone(), &flags)?;
e.install_to_sysroot(path!(e.miri_dir / "cargo-miri"), &flags)?;
Ok(())
}

fn build(flags: Vec<OsString>) -> Result<()> {
fn build(flags: Vec<String>) -> Result<()> {
let e = MiriEnv::new()?;
e.build(path!(e.miri_dir / "Cargo.toml"), &flags, /* quiet */ false)?;
e.build(path!(e.miri_dir / "cargo-miri" / "Cargo.toml"), &flags, /* quiet */ false)?;
Ok(())
}

fn check(flags: Vec<OsString>) -> Result<()> {
fn check(flags: Vec<String>) -> Result<()> {
let e = MiriEnv::new()?;
e.check(path!(e.miri_dir / "Cargo.toml"), &flags)?;
e.check(path!(e.miri_dir / "cargo-miri" / "Cargo.toml"), &flags)?;
Ok(())
}

fn clippy(flags: Vec<OsString>) -> Result<()> {
fn clippy(flags: Vec<String>) -> Result<()> {
let e = MiriEnv::new()?;
e.clippy(path!(e.miri_dir / "Cargo.toml"), &flags)?;
e.clippy(path!(e.miri_dir / "cargo-miri" / "Cargo.toml"), &flags)?;
e.clippy(path!(e.miri_dir / "miri-script" / "Cargo.toml"), &flags)?;
Ok(())
}

fn cargo(flags: Vec<OsString>) -> Result<()> {
fn cargo(flags: Vec<String>) -> Result<()> {
let e = MiriEnv::new()?;
let toolchain = &e.toolchain;
// We carefully kept the working dir intact, so this will run cargo *on the workspace in the
@@ -456,7 +454,7 @@ impl Command {
Ok(())
}

fn test(bless: bool, mut flags: Vec<OsString>, target: Option<OsString>) -> Result<()> {
fn test(bless: bool, mut flags: Vec<String>, target: Option<String>) -> Result<()> {
let mut e = MiriEnv::new()?;

// Prepare a sysroot.
@@ -484,21 +482,30 @@ impl Command {
dep: bool,
verbose: bool,
many_seeds: Option<Range<u32>>,
mut flags: Vec<OsString>,
target: Option<String>,
edition: Option<String>,
flags: Vec<String>,
) -> Result<()> {
let mut e = MiriEnv::new()?;
let target = arg_flag_value(&flags, "--target");

// Scan for "--edition", set one ourselves if that flag is not present.
let have_edition = arg_flag_value(&flags, "--edition").is_some();
if !have_edition {
flags.push("--edition=2021".into()); // keep in sync with `tests/ui.rs`.`
// More flags that we will pass before `flags`
// (because `flags` may contain `--`).
let mut early_flags = Vec::<OsString>::new();

// Add target, edition to flags.
if let Some(target) = &target {
early_flags.push("--target".into());
early_flags.push(target.into());
}
if verbose {
early_flags.push("--verbose".into());
}
early_flags.push("--edition".into());
early_flags.push(edition.as_deref().unwrap_or("2021").into());

// Prepare a sysroot, and add it to the flags.
// Prepare a sysroot, add it to the flags.
let miri_sysroot = e.build_miri_sysroot(/* quiet */ !verbose, target.as_deref())?;
flags.push("--sysroot".into());
flags.push(miri_sysroot.into());
early_flags.push("--sysroot".into());
early_flags.push(miri_sysroot.into());

// Compute everything needed to run the actual command. Also add MIRIFLAGS.
let miri_manifest = path!(e.miri_dir / "Cargo.toml");
@@ -524,7 +531,7 @@ impl Command {
};
cmd.set_quiet(!verbose);
// Add Miri flags
let cmd = cmd.args(&miri_flags).args(seed_flag).args(&flags);
let cmd = cmd.args(&miri_flags).args(&seed_flag).args(&early_flags).args(&flags);
// And run the thing.
Ok(cmd.run()?)
};
@@ -543,7 +550,7 @@ impl Command {
Ok(())
}

fn fmt(flags: Vec<OsString>) -> Result<()> {
fn fmt(flags: Vec<String>) -> Result<()> {
use itertools::Itertools;

let e = MiriEnv::new()?;
@@ -565,6 +572,6 @@ impl Command {
.filter_ok(|item| item.file_type().is_file())
.map_ok(|item| item.into_path());

e.format_files(files, &e.toolchain[..], &config_path, &flags[..])
e.format_files(files, &e.toolchain[..], &config_path, &flags)
}
}
158 changes: 71 additions & 87 deletions src/tools/miri/miri-script/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
#![allow(clippy::needless_question_mark)]

mod args;
mod commands;
mod util;

use std::ffi::OsString;
use std::{env, ops::Range};
use std::ops::Range;

use anyhow::{anyhow, bail, Context, Result};

@@ -16,60 +16,62 @@ pub enum Command {
/// sysroot, to prevent conflicts with other toolchains.
Install {
/// Flags that are passed through to `cargo install`.
flags: Vec<OsString>,
flags: Vec<String>,
},
/// Just build miri.
Build {
/// Flags that are passed through to `cargo build`.
flags: Vec<OsString>,
flags: Vec<String>,
},
/// Just check miri.
Check {
/// Flags that are passed through to `cargo check`.
flags: Vec<OsString>,
flags: Vec<String>,
},
/// Build miri, set up a sysroot and then run the test suite.
Test {
bless: bool,
/// The cross-interpretation target.
/// If none then the host is the target.
target: Option<OsString>,
target: Option<String>,
/// Flags that are passed through to the test harness.
flags: Vec<OsString>,
flags: Vec<String>,
},
/// Build miri, set up a sysroot and then run the driver with the given <flags>.
/// (Also respects MIRIFLAGS environment variable.)
Run {
dep: bool,
verbose: bool,
many_seeds: Option<Range<u32>>,
target: Option<String>,
edition: Option<String>,
/// Flags that are passed through to `miri`.
flags: Vec<OsString>,
flags: Vec<String>,
},
/// Format all sources and tests.
Fmt {
/// Flags that are passed through to `rustfmt`.
flags: Vec<OsString>,
flags: Vec<String>,
},
/// Runs clippy on all sources.
Clippy {
/// Flags that are passed through to `cargo clippy`.
flags: Vec<OsString>,
flags: Vec<String>,
},
/// Runs just `cargo <flags>` with the Miri-specific environment variables.
/// Mainly meant to be invoked by rust-analyzer.
Cargo { flags: Vec<OsString> },
Cargo { flags: Vec<String> },
/// Runs the benchmarks from bench-cargo-miri in hyperfine. hyperfine needs to be installed.
Bench {
target: Option<OsString>,
target: Option<String>,
/// List of benchmarks to run. By default all benchmarks are run.
benches: Vec<OsString>,
benches: Vec<String>,
},
/// Update and activate the rustup toolchain 'miri' to the commit given in the
/// `rust-version` file.
/// `rustup-toolchain-install-master` must be installed for this to work. Any extra
/// flags are passed to `rustup-toolchain-install-master`.
Toolchain { flags: Vec<OsString> },
Toolchain { flags: Vec<String> },
/// Pull and merge Miri changes from the rustc repo. Defaults to fetching the latest
/// rustc commit. The fetched commit is stored in the `rust-version` file, so the
/// next `./miri toolchain` will install the rustc that just got pulled.
@@ -145,113 +147,95 @@ Pass extra flags to all cargo invocations. (Ignored by `./miri cargo`.)"#;
fn main() -> Result<()> {
// We are hand-rolling our own argument parser, since `clap` can't express what we need
// (https://github.com/clap-rs/clap/issues/5055).
let mut args = env::args_os().peekable();
args.next().unwrap(); // skip program name
let command = match args.next().and_then(|s| s.into_string().ok()).as_deref() {
Some("build") => Command::Build { flags: args.collect() },
Some("check") => Command::Check { flags: args.collect() },
let mut args = args::Args::new();
let command = match args.next_raw().as_deref() {
Some("build") => Command::Build { flags: args.remainder() },
Some("check") => Command::Check { flags: args.remainder() },
Some("test") => {
let mut target = None;
let mut bless = false;

while let Some(arg) = args.peek().and_then(|s| s.to_str()) {
match arg {
"--bless" => bless = true,
"--target" => {
// Skip "--target"
args.next().unwrap();
// Next argument is the target triple.
let val = args.peek().ok_or_else(|| {
anyhow!("`--target` must be followed by target triple")
})?;
target = Some(val.to_owned());
}
// Only parse the leading flags.
_ => break,
let mut flags = Vec::new();
loop {
if args.get_long_flag("bless")? {
bless = true;
} else if let Some(val) = args.get_long_opt("target")? {
target = Some(val);
} else if let Some(flag) = args.get_other() {
flags.push(flag);
} else {
break;
}

// Consume the flag, look at the next one.
args.next().unwrap();
}

Command::Test { bless, flags: args.collect(), target }
Command::Test { bless, flags, target }
}
Some("run") => {
let mut dep = false;
let mut verbose = false;
let mut many_seeds = None;
while let Some(arg) = args.peek().and_then(|s| s.to_str()) {
if arg == "--dep" {
let mut target = None;
let mut edition = None;
let mut flags = Vec::new();
loop {
if args.get_long_flag("dep")? {
dep = true;
} else if arg == "-v" || arg == "--verbose" {
} else if args.get_long_flag("verbose")? || args.get_short_flag('v')? {
verbose = true;
} else if arg == "--many-seeds" {
many_seeds = Some(0..256);
} else if let Some(val) = arg.strip_prefix("--many-seeds=") {
} else if let Some(val) = args.get_long_opt_with_default("many-seeds", "0..256")? {
let (from, to) = val.split_once("..").ok_or_else(|| {
anyhow!("invalid format for `--many-seeds`: expected `from..to`")
anyhow!("invalid format for `--many-seeds-range`: expected `from..to`")
})?;
let from: u32 = if from.is_empty() {
0
} else {
from.parse().context("invalid `from` in `--many-seeds=from..to")?
from.parse().context("invalid `from` in `--many-seeds-range=from..to")?
};
let to: u32 = to.parse().context("invalid `to` in `--many-seeds=from..to")?;
let to: u32 =
to.parse().context("invalid `to` in `--many-seeds-range=from..to")?;
many_seeds = Some(from..to);
} else if let Some(val) = args.get_long_opt("target")? {
target = Some(val);
} else if let Some(val) = args.get_long_opt("edition")? {
edition = Some(val);
} else if let Some(flag) = args.get_other() {
flags.push(flag);
} else {
break; // not for us
break;
}
// Consume the flag, look at the next one.
args.next().unwrap();
}
Command::Run { dep, verbose, many_seeds, flags: args.collect() }
Command::Run { dep, verbose, many_seeds, target, edition, flags }
}
Some("fmt") => Command::Fmt { flags: args.collect() },
Some("clippy") => Command::Clippy { flags: args.collect() },
Some("cargo") => Command::Cargo { flags: args.collect() },
Some("install") => Command::Install { flags: args.collect() },
Some("fmt") => Command::Fmt { flags: args.remainder() },
Some("clippy") => Command::Clippy { flags: args.remainder() },
Some("cargo") => Command::Cargo { flags: args.remainder() },
Some("install") => Command::Install { flags: args.remainder() },
Some("bench") => {
let mut target = None;
while let Some(arg) = args.peek().and_then(|s| s.to_str()) {
match arg {
"--target" => {
// Skip "--target"
args.next().unwrap();
// Next argument is the target triple.
let val = args.peek().ok_or_else(|| {
anyhow!("`--target` must be followed by target triple")
})?;
target = Some(val.to_owned());
}
// Only parse the leading flags.
_ => break,
let mut benches = Vec::new();
loop {
if let Some(val) = args.get_long_opt("target")? {
target = Some(val);
} else if let Some(flag) = args.get_other() {
benches.push(flag);
} else {
break;
}

// Consume the flag, look at the next one.
args.next().unwrap();
}

Command::Bench { target, benches: args.collect() }
Command::Bench { target, benches }
}
Some("toolchain") => Command::Toolchain { flags: args.collect() },
Some("toolchain") => Command::Toolchain { flags: args.remainder() },
Some("rustc-pull") => {
let commit = args.next().map(|a| a.to_string_lossy().into_owned());
if args.next().is_some() {
let commit = args.next_raw();
if args.next_raw().is_some() {
bail!("Too many arguments for `./miri rustc-pull`");
}
Command::RustcPull { commit }
}
Some("rustc-push") => {
let github_user = args
.next()
.ok_or_else(|| {
anyhow!("Missing first argument for `./miri rustc-push GITHUB_USER [BRANCH]`")
})?
.to_string_lossy()
.into_owned();
let branch =
args.next().unwrap_or_else(|| "miri-sync".into()).to_string_lossy().into_owned();
if args.next().is_some() {
let github_user = args.next_raw().ok_or_else(|| {
anyhow!("Missing first argument for `./miri rustc-push GITHUB_USER [BRANCH]`")
})?;
let branch = args.next_raw().unwrap_or_else(|| "miri-sync".into());
if args.next_raw().is_some() {
bail!("Too many arguments for `./miri rustc-push GITHUB_USER BRANCH`");
}
Command::RustcPush { github_user, branch }
34 changes: 5 additions & 29 deletions src/tools/miri/miri-script/src/util.rs
Original file line number Diff line number Diff line change
@@ -27,30 +27,6 @@ pub fn flagsplit(flags: &str) -> Vec<String> {
flags.split(' ').map(str::trim).filter(|s| !s.is_empty()).map(str::to_string).collect()
}

pub fn arg_flag_value(
args: impl IntoIterator<Item = impl AsRef<OsStr>>,
flag: &str,
) -> Option<OsString> {
let mut args = args.into_iter();
while let Some(arg) = args.next() {
let arg = arg.as_ref();
if arg == "--" {
return None;
}
let Some(arg) = arg.to_str() else {
// Skip non-UTF-8 arguments.
continue;
};
if arg == flag {
// Next one is the value.
return Some(args.next()?.as_ref().to_owned());
} else if let Some(val) = arg.strip_prefix(flag).and_then(|s| s.strip_prefix("=")) {
return Some(val.to_owned().into());
}
}
None
}

/// Some extra state we track for building Miri, such as the right RUSTFLAGS.
pub struct MiriEnv {
/// miri_dir is the root of the miri repository checkout we are working in.
@@ -133,7 +109,7 @@ impl MiriEnv {
pub fn build(
&self,
manifest_path: impl AsRef<OsStr>,
args: &[OsString],
args: &[String],
quiet: bool,
) -> Result<()> {
let MiriEnv { toolchain, cargo_extra_flags, .. } = self;
@@ -149,21 +125,21 @@ impl MiriEnv {
Ok(())
}

pub fn check(&self, manifest_path: impl AsRef<OsStr>, args: &[OsString]) -> Result<()> {
pub fn check(&self, manifest_path: impl AsRef<OsStr>, args: &[String]) -> Result<()> {
let MiriEnv { toolchain, cargo_extra_flags, .. } = self;
cmd!(self.sh, "cargo +{toolchain} check {cargo_extra_flags...} --manifest-path {manifest_path} --all-targets {args...}")
.run()?;
Ok(())
}

pub fn clippy(&self, manifest_path: impl AsRef<OsStr>, args: &[OsString]) -> Result<()> {
pub fn clippy(&self, manifest_path: impl AsRef<OsStr>, args: &[String]) -> Result<()> {
let MiriEnv { toolchain, cargo_extra_flags, .. } = self;
cmd!(self.sh, "cargo +{toolchain} clippy {cargo_extra_flags...} --manifest-path {manifest_path} --all-targets {args...}")
.run()?;
Ok(())
}

pub fn test(&self, manifest_path: impl AsRef<OsStr>, args: &[OsString]) -> Result<()> {
pub fn test(&self, manifest_path: impl AsRef<OsStr>, args: &[String]) -> Result<()> {
let MiriEnv { toolchain, cargo_extra_flags, .. } = self;
cmd!(
self.sh,
@@ -181,7 +157,7 @@ impl MiriEnv {
files: impl Iterator<Item = Result<PathBuf, walkdir::Error>>,
toolchain: &str,
config_path: &Path,
flags: &[OsString],
flags: &[String],
) -> anyhow::Result<()> {
use itertools::Itertools;

2 changes: 1 addition & 1 deletion src/tools/miri/rust-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
ef15976387ad9c1cdceaabf469e0cf35f5852f6d
6579ed89f0fcc26da71afdd11d30d63f6f812a0a
109 changes: 109 additions & 0 deletions src/tools/miri/src/alloc_bytes.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
use std::alloc;
use std::alloc::Layout;
use std::borrow::Cow;
use std::slice;

use rustc_middle::mir::interpret::AllocBytes;
use rustc_target::abi::{Align, Size};

/// Allocation bytes that explicitly handle the layout of the data they're storing.
/// This is necessary to interface with native code that accesses the program store in Miri.
#[derive(Debug)]
pub struct MiriAllocBytes {
/// Stored layout information about the allocation.
layout: alloc::Layout,
/// Pointer to the allocation contents.
/// Invariant:
/// * If `self.layout.size() == 0`, then `self.ptr` is some suitably aligned pointer
/// without provenance (and no actual memory was allocated).
/// * Otherwise, `self.ptr` points to memory allocated with `self.layout`.
ptr: *mut u8,
}

impl Clone for MiriAllocBytes {
fn clone(&self) -> Self {
let bytes: Cow<'_, [u8]> = Cow::Borrowed(self);
let align = Align::from_bytes(self.layout.align().try_into().unwrap()).unwrap();
MiriAllocBytes::from_bytes(bytes, align)
}
}

impl Drop for MiriAllocBytes {
fn drop(&mut self) {
if self.layout.size() != 0 {
// SAFETY: Invariant, `self.ptr` points to memory allocated with `self.layout`.
unsafe { alloc::dealloc(self.ptr, self.layout) }
}
}
}

impl std::ops::Deref for MiriAllocBytes {
type Target = [u8];

fn deref(&self) -> &Self::Target {
// SAFETY: `ptr` is non-null, properly aligned, and valid for reading out `self.layout.size()`-many bytes.
// Note that due to the invariant this is true even if `self.layout.size() == 0`.
unsafe { slice::from_raw_parts(self.ptr, self.layout.size()) }
}
}

impl std::ops::DerefMut for MiriAllocBytes {
fn deref_mut(&mut self) -> &mut Self::Target {
// SAFETY: `ptr` is non-null, properly aligned, and valid for reading out `self.layout.size()`-many bytes.
// Note that due to the invariant this is true even if `self.layout.size() == 0`.
unsafe { slice::from_raw_parts_mut(self.ptr, self.layout.size()) }
}
}

impl MiriAllocBytes {
/// This method factors out how a `MiriAllocBytes` object is allocated,
/// specifically given an allocation function `alloc_fn`.
/// `alloc_fn` is only used if `size != 0`.
/// Returns `Err(layout)` if the allocation function returns a `ptr` that is `ptr.is_null()`.
fn alloc_with(
size: usize,
align: usize,
alloc_fn: impl FnOnce(Layout) -> *mut u8,
) -> Result<MiriAllocBytes, Layout> {
let layout = Layout::from_size_align(size, align).unwrap();
let ptr = if size == 0 {
std::ptr::without_provenance_mut(align)
} else {
let ptr = alloc_fn(layout);
if ptr.is_null() {
return Err(layout);
}
ptr
};
// SAFETY: All `MiriAllocBytes` invariants are fulfilled.
Ok(Self { ptr, layout })
}
}

impl AllocBytes for MiriAllocBytes {
fn from_bytes<'a>(slice: impl Into<Cow<'a, [u8]>>, align: Align) -> Self {
let slice = slice.into();
let size = slice.len();
let align = align.bytes_usize();
// SAFETY: `alloc_fn` will only be used if `size != 0`.
let alloc_fn = |layout| unsafe { alloc::alloc(layout) };
let alloc_bytes = MiriAllocBytes::alloc_with(size, align, alloc_fn)
.unwrap_or_else(|layout| alloc::handle_alloc_error(layout));
// SAFETY: `alloc_bytes.ptr` and `slice.as_ptr()` are non-null, properly aligned
// and valid for the `size`-many bytes to be copied.
unsafe { alloc_bytes.ptr.copy_from(slice.as_ptr(), size) };
alloc_bytes
}

fn zeroed(size: Size, align: Align) -> Option<Self> {
let size = size.bytes_usize();
let align = align.bytes_usize();
// SAFETY: `alloc_fn` will only be used if `size != 0`.
let alloc_fn = |layout| unsafe { alloc::alloc_zeroed(layout) };
MiriAllocBytes::alloc_with(size, align, alloc_fn).ok()
}

fn as_mut_ptr(&mut self) -> *mut u8 {
self.ptr
}
}
5 changes: 4 additions & 1 deletion src/tools/miri/src/bin/miri.rs
Original file line number Diff line number Diff line change
@@ -405,9 +405,12 @@ fn main() {

let mut rustc_args = vec![];
let mut after_dashdash = false;

// If user has explicitly enabled/disabled isolation
let mut isolation_enabled: Option<bool> = None;

// Note that we require values to be given with `=`, not with a space.
// This matches how rustc parses `-Z`.
// However, unlike rustc we do not accept a space after `-Z`.
for arg in args {
if rustc_args.is_empty() {
// Very first arg: binary name.
2 changes: 1 addition & 1 deletion src/tools/miri/src/borrow_tracker/stacked_borrows/stack.rs
Original file line number Diff line number Diff line change
@@ -9,7 +9,7 @@ use crate::borrow_tracker::{
};
use crate::ProvenanceExtra;

/// Exactly what cache size we should use is a difficult tradeoff. There will always be some
/// Exactly what cache size we should use is a difficult trade-off. There will always be some
/// workload which has a `BorTag` working set which exceeds the size of the cache, and ends up
/// falling back to linear searches of the borrow stack very often.
/// The cost of making this value too large is that the loop in `Stack::insert` which ensures the
6 changes: 3 additions & 3 deletions src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs
Original file line number Diff line number Diff line change
@@ -390,7 +390,7 @@ struct DisplayFmtWrapper {
warning_text: S,
}

/// Formating of the permissions on each range.
/// Formatting of the permissions on each range.
///
/// Example:
/// ```rust,ignore (private type)
@@ -422,7 +422,7 @@ struct DisplayFmtPermission {
range_sep: S,
}

/// Formating of the tree structure.
/// Formatting of the tree structure.
///
/// Example:
/// ```rust,ignore (private type)
@@ -487,7 +487,7 @@ struct DisplayFmtAccess {
meh: S,
}

/// All parameters to determine how the tree is formated.
/// All parameters to determine how the tree is formatted.
struct DisplayFmt {
wrapper: DisplayFmtWrapper,
perm: DisplayFmtPermission,
2 changes: 1 addition & 1 deletion src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs
Original file line number Diff line number Diff line change
@@ -202,7 +202,7 @@ impl Permission {
Self { inner: Frozen }
}

/// Default initial permission of the root of a new tre at out-of-bounds positions.
/// Default initial permission of the root of a new tree at out-of-bounds positions.
/// Must *only* be used for the root, this is not in general an "initial" permission!
pub fn new_disabled() -> Self {
Self { inner: Disabled }
Original file line number Diff line number Diff line change
@@ -483,7 +483,7 @@ mod spurious_read {
/// that causes UB in the target but not in the source.
/// This implementation simply explores the reachable space
/// by all sequences of `TestEvent`.
/// This function can be instanciated with `RetX` and `RetY`
/// This function can be instantiated with `RetX` and `RetY`
/// among `NoRet` or `AllowRet` to resp. forbid/allow `x`/`y` to lose their
/// protector.
fn distinguishable<RetX, RetY>(&self, other: &Self) -> bool
2 changes: 1 addition & 1 deletion src/tools/miri/src/concurrency/thread.rs
Original file line number Diff line number Diff line change
@@ -111,7 +111,7 @@ pub enum BlockReason {
Condvar(CondvarId),
/// Blocked on a reader-writer lock.
RwLock(RwLockId),
/// Blocled on a Futex variable.
/// Blocked on a Futex variable.
Futex { addr: u64 },
/// Blocked on an InitOnce.
InitOnce(InitOnceId),
2 changes: 1 addition & 1 deletion src/tools/miri/src/concurrency/weak_memory.rs
Original file line number Diff line number Diff line change
@@ -48,7 +48,7 @@
//! One consequence of this difference is that safe/sound Rust allows for more operations on atomic locations
//! than the C++20 atomic API was intended to allow, such as non-atomically accessing
//! a previously atomically accessed location, or accessing previously atomically accessed locations with a differently sized operation
//! (such as accessing the top 16 bits of an AtomicU32). These scenarios are generally undiscussed in formalisations of C++ memory model.
//! (such as accessing the top 16 bits of an AtomicU32). These scenarios are generally undiscussed in formalizations of C++ memory model.
//! In Rust, these operations can only be done through a `&mut AtomicFoo` reference or one derived from it, therefore these operations
//! can only happen after all previous accesses on the same locations. This implementation is adapted to allow these operations.
//! A mixed atomicity read that races with writes, or a write that races with reads or writes will still cause UBs to be thrown.
2 changes: 1 addition & 1 deletion src/tools/miri/src/diagnostics.rs
Original file line number Diff line number Diff line change
@@ -459,7 +459,7 @@ pub fn report_error<'tcx, 'mir>(

pub fn report_leaks<'mir, 'tcx>(
ecx: &InterpCx<'mir, 'tcx, MiriMachine<'mir, 'tcx>>,
leaks: Vec<(AllocId, MemoryKind, Allocation<Provenance, AllocExtra<'tcx>>)>,
leaks: Vec<(AllocId, MemoryKind, Allocation<Provenance, AllocExtra<'tcx>, MiriAllocBytes>)>,
) {
let mut any_pruned = false;
for (id, kind, mut alloc) in leaks {
68 changes: 34 additions & 34 deletions src/tools/miri/src/intrinsics/atomic.rs
Original file line number Diff line number Diff line change
@@ -25,98 +25,98 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {

let intrinsic_structure: Vec<_> = intrinsic_name.split('_').collect();

fn read_ord<'tcx>(ord: &str) -> InterpResult<'tcx, AtomicReadOrd> {
Ok(match ord {
fn read_ord(ord: &str) -> AtomicReadOrd {
match ord {
"seqcst" => AtomicReadOrd::SeqCst,
"acquire" => AtomicReadOrd::Acquire,
"relaxed" => AtomicReadOrd::Relaxed,
_ => throw_unsup_format!("unsupported read ordering `{ord}`"),
})
_ => panic!("invalid read ordering `{ord}`"),
}
}

fn write_ord<'tcx>(ord: &str) -> InterpResult<'tcx, AtomicWriteOrd> {
Ok(match ord {
fn write_ord(ord: &str) -> AtomicWriteOrd {
match ord {
"seqcst" => AtomicWriteOrd::SeqCst,
"release" => AtomicWriteOrd::Release,
"relaxed" => AtomicWriteOrd::Relaxed,
_ => throw_unsup_format!("unsupported write ordering `{ord}`"),
})
_ => panic!("invalid write ordering `{ord}`"),
}
}

fn rw_ord<'tcx>(ord: &str) -> InterpResult<'tcx, AtomicRwOrd> {
Ok(match ord {
fn rw_ord(ord: &str) -> AtomicRwOrd {
match ord {
"seqcst" => AtomicRwOrd::SeqCst,
"acqrel" => AtomicRwOrd::AcqRel,
"acquire" => AtomicRwOrd::Acquire,
"release" => AtomicRwOrd::Release,
"relaxed" => AtomicRwOrd::Relaxed,
_ => throw_unsup_format!("unsupported read-write ordering `{ord}`"),
})
_ => panic!("invalid read-write ordering `{ord}`"),
}
}

fn fence_ord<'tcx>(ord: &str) -> InterpResult<'tcx, AtomicFenceOrd> {
Ok(match ord {
fn fence_ord(ord: &str) -> AtomicFenceOrd {
match ord {
"seqcst" => AtomicFenceOrd::SeqCst,
"acqrel" => AtomicFenceOrd::AcqRel,
"acquire" => AtomicFenceOrd::Acquire,
"release" => AtomicFenceOrd::Release,
_ => throw_unsup_format!("unsupported fence ordering `{ord}`"),
})
_ => panic!("invalid fence ordering `{ord}`"),
}
}

match &*intrinsic_structure {
["load", ord] => this.atomic_load(args, dest, read_ord(ord)?)?,
["store", ord] => this.atomic_store(args, write_ord(ord)?)?,
["load", ord] => this.atomic_load(args, dest, read_ord(ord))?,
["store", ord] => this.atomic_store(args, write_ord(ord))?,

["fence", ord] => this.atomic_fence_intrinsic(args, fence_ord(ord)?)?,
["singlethreadfence", ord] => this.compiler_fence_intrinsic(args, fence_ord(ord)?)?,
["fence", ord] => this.atomic_fence_intrinsic(args, fence_ord(ord))?,
["singlethreadfence", ord] => this.compiler_fence_intrinsic(args, fence_ord(ord))?,

["xchg", ord] => this.atomic_exchange(args, dest, rw_ord(ord)?)?,
["xchg", ord] => this.atomic_exchange(args, dest, rw_ord(ord))?,
["cxchg", ord1, ord2] =>
this.atomic_compare_exchange(args, dest, rw_ord(ord1)?, read_ord(ord2)?)?,
this.atomic_compare_exchange(args, dest, rw_ord(ord1), read_ord(ord2))?,
["cxchgweak", ord1, ord2] =>
this.atomic_compare_exchange_weak(args, dest, rw_ord(ord1)?, read_ord(ord2)?)?,
this.atomic_compare_exchange_weak(args, dest, rw_ord(ord1), read_ord(ord2))?,

["or", ord] =>
this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::BitOr, false), rw_ord(ord)?)?,
this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::BitOr, false), rw_ord(ord))?,
["xor", ord] =>
this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::BitXor, false), rw_ord(ord)?)?,
this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::BitXor, false), rw_ord(ord))?,
["and", ord] =>
this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, false), rw_ord(ord)?)?,
this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, false), rw_ord(ord))?,
["nand", ord] =>
this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, true), rw_ord(ord)?)?,
this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, true), rw_ord(ord))?,
["xadd", ord] =>
this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::Add, false), rw_ord(ord)?)?,
this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::Add, false), rw_ord(ord))?,
["xsub", ord] =>
this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::Sub, false), rw_ord(ord)?)?,
this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::Sub, false), rw_ord(ord))?,
["min", ord] => {
// Later we will use the type to indicate signed vs unsigned,
// so make sure it matches the intrinsic name.
assert!(matches!(args[1].layout.ty.kind(), ty::Int(_)));
this.atomic_rmw_op(args, dest, AtomicOp::Min, rw_ord(ord)?)?;
this.atomic_rmw_op(args, dest, AtomicOp::Min, rw_ord(ord))?;
}
["umin", ord] => {
// Later we will use the type to indicate signed vs unsigned,
// so make sure it matches the intrinsic name.
assert!(matches!(args[1].layout.ty.kind(), ty::Uint(_)));
this.atomic_rmw_op(args, dest, AtomicOp::Min, rw_ord(ord)?)?;
this.atomic_rmw_op(args, dest, AtomicOp::Min, rw_ord(ord))?;
}
["max", ord] => {
// Later we will use the type to indicate signed vs unsigned,
// so make sure it matches the intrinsic name.
assert!(matches!(args[1].layout.ty.kind(), ty::Int(_)));
this.atomic_rmw_op(args, dest, AtomicOp::Max, rw_ord(ord)?)?;
this.atomic_rmw_op(args, dest, AtomicOp::Max, rw_ord(ord))?;
}
["umax", ord] => {
// Later we will use the type to indicate signed vs unsigned,
// so make sure it matches the intrinsic name.
assert!(matches!(args[1].layout.ty.kind(), ty::Uint(_)));
this.atomic_rmw_op(args, dest, AtomicOp::Max, rw_ord(ord)?)?;
this.atomic_rmw_op(args, dest, AtomicOp::Max, rw_ord(ord))?;
}

_ => return Ok(EmulateItemResult::NotSupported),
}
Ok(EmulateItemResult::NeedsJumping)
Ok(EmulateItemResult::NeedsReturn)
}
}

183 changes: 100 additions & 83 deletions src/tools/miri/src/intrinsics/mod.rs
Original file line number Diff line number Diff line change
@@ -26,7 +26,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
args: &[OpTy<'tcx, Provenance>],
dest: &MPlaceTy<'tcx, Provenance>,
ret: Option<mir::BasicBlock>,
_unwind: mir::UnwindAction,
unwind: mir::UnwindAction,
) -> InterpResult<'tcx, Option<ty::Instance<'tcx>>> {
let this = self.eval_context_mut();

@@ -62,11 +62,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
args: instance.args,
}))
}
EmulateItemResult::NeedsJumping => {
EmulateItemResult::NeedsReturn => {
trace!("{:?}", this.dump_place(&dest.clone().into()));
this.return_to_block(ret)?;
Ok(None)
}
EmulateItemResult::NeedsUnwind => {
// Jump to the unwind block to begin unwinding.
this.unwind_to_block(unwind)?;
Ok(None)
}
EmulateItemResult::AlreadyJumped => Ok(None),
}
}
@@ -167,6 +172,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
// This is a "bitwise" operation, so there's no NaN non-determinism.
this.write_scalar(Scalar::from_f64(f.abs()), dest)?;
}

"floorf32" | "ceilf32" | "truncf32" | "roundf32" | "rintf32" => {
let [f] = check_arg_count(args)?;
let f = this.read_scalar(f)?.to_f32()?;
@@ -182,6 +188,22 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
let res = this.adjust_nan(res, &[f]);
this.write_scalar(res, dest)?;
}
"floorf64" | "ceilf64" | "truncf64" | "roundf64" | "rintf64" => {
let [f] = check_arg_count(args)?;
let f = this.read_scalar(f)?.to_f64()?;
let mode = match intrinsic_name {
"floorf64" => Round::TowardNegative,
"ceilf64" => Round::TowardPositive,
"truncf64" => Round::TowardZero,
"roundf64" => Round::NearestTiesToAway,
"rintf64" => Round::NearestTiesToEven,
_ => bug!(),
};
let res = f.round_to_integral(mode).value;
let res = this.adjust_nan(res, &[f]);
this.write_scalar(res, dest)?;
}

#[rustfmt::skip]
| "sinf32"
| "cosf32"
@@ -211,22 +233,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
let res = this.adjust_nan(res, &[f]);
this.write_scalar(res, dest)?;
}

"floorf64" | "ceilf64" | "truncf64" | "roundf64" | "rintf64" => {
let [f] = check_arg_count(args)?;
let f = this.read_scalar(f)?.to_f64()?;
let mode = match intrinsic_name {
"floorf64" => Round::TowardNegative,
"ceilf64" => Round::TowardPositive,
"truncf64" => Round::TowardZero,
"roundf64" => Round::NearestTiesToAway,
"rintf64" => Round::NearestTiesToEven,
_ => bug!(),
};
let res = f.round_to_integral(mode).value;
let res = this.adjust_nan(res, &[f]);
this.write_scalar(res, dest)?;
}
#[rustfmt::skip]
| "sinf64"
| "cosf64"
@@ -257,61 +263,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
this.write_scalar(res, dest)?;
}

#[rustfmt::skip]
| "fadd_fast"
| "fsub_fast"
| "fmul_fast"
| "fdiv_fast"
| "frem_fast"
=> {
let [a, b] = check_arg_count(args)?;
let a = this.read_immediate(a)?;
let b = this.read_immediate(b)?;
let op = match intrinsic_name {
"fadd_fast" => mir::BinOp::Add,
"fsub_fast" => mir::BinOp::Sub,
"fmul_fast" => mir::BinOp::Mul,
"fdiv_fast" => mir::BinOp::Div,
"frem_fast" => mir::BinOp::Rem,
_ => bug!(),
};
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)
};
Ok(match fty {
FloatTy::F16 => unimplemented!("f16_f128"),
FloatTy::F32 => x.to_scalar().to_f32()?.is_finite(),
FloatTy::F64 => x.to_scalar().to_f64()?.is_finite(),
FloatTy::F128 => unimplemented!("f16_f128"),
})
};
match (float_finite(&a)?, float_finite(&b)?) {
(false, false) => throw_ub_format!(
"`{intrinsic_name}` intrinsic called with non-finite value as both parameters",
),
(false, _) => throw_ub_format!(
"`{intrinsic_name}` intrinsic called with non-finite value as first parameter",
),
(_, false) => throw_ub_format!(
"`{intrinsic_name}` intrinsic called with non-finite value as second parameter",
),
_ => {}
}
let res = this.wrapping_binary_op(op, &a, &b)?;
if !float_finite(&res)? {
throw_ub_format!("`{intrinsic_name}` intrinsic produced non-finite value as result");
}
// This cannot be a NaN so we also don't have to apply any non-determinism.
// (Also, `wrapping_binary_op` already called `generate_nan` if needed.)
this.write_immediate(*res, dest)?;
}

#[rustfmt::skip]
| "minnumf32"
| "maxnumf32"
| "copysignf32"
=> {
"minnumf32" | "maxnumf32" | "copysignf32" => {
let [a, b] = check_arg_count(args)?;
let a = this.read_scalar(a)?.to_f32()?;
let b = this.read_scalar(b)?.to_f32()?;
@@ -323,12 +275,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
};
this.write_scalar(Scalar::from_f32(res), dest)?;
}

#[rustfmt::skip]
| "minnumf64"
| "maxnumf64"
| "copysignf64"
=> {
"minnumf64" | "maxnumf64" | "copysignf64" => {
let [a, b] = check_arg_count(args)?;
let a = this.read_scalar(a)?.to_f64()?;
let b = this.read_scalar(b)?.to_f64()?;
@@ -351,7 +298,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
let res = this.adjust_nan(res, &[a, b, c]);
this.write_scalar(res, dest)?;
}

"fmaf64" => {
let [a, b, c] = check_arg_count(args)?;
let a = this.read_scalar(a)?.to_f64()?;
@@ -372,7 +318,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
let res = this.adjust_nan(res, &[f1, f2]);
this.write_scalar(res, dest)?;
}

"powf64" => {
let [f1, f2] = check_arg_count(args)?;
let f1 = this.read_scalar(f1)?.to_f64()?;
@@ -392,7 +337,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
let res = this.adjust_nan(res, &[f]);
this.write_scalar(res, dest)?;
}

"powif64" => {
let [f, i] = check_arg_count(args)?;
let f = this.read_scalar(f)?.to_f64()?;
@@ -403,6 +347,79 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
this.write_scalar(res, dest)?;
}

#[rustfmt::skip]
| "fadd_algebraic"
| "fsub_algebraic"
| "fmul_algebraic"
| "fdiv_algebraic"
| "frem_algebraic"
=> {
let [a, b] = check_arg_count(args)?;
let a = this.read_immediate(a)?;
let b = this.read_immediate(b)?;
let op = match intrinsic_name {
"fadd_algebraic" => mir::BinOp::Add,
"fsub_algebraic" => mir::BinOp::Sub,
"fmul_algebraic" => mir::BinOp::Mul,
"fdiv_algebraic" => mir::BinOp::Div,
"frem_algebraic" => mir::BinOp::Rem,
_ => bug!(),
};
let res = this.wrapping_binary_op(op, &a, &b)?;
// `wrapping_binary_op` already called `generate_nan` if necessary.
this.write_immediate(*res, dest)?;
}

#[rustfmt::skip]
| "fadd_fast"
| "fsub_fast"
| "fmul_fast"
| "fdiv_fast"
| "frem_fast"
=> {
let [a, b] = check_arg_count(args)?;
let a = this.read_immediate(a)?;
let b = this.read_immediate(b)?;
let op = match intrinsic_name {
"fadd_fast" => mir::BinOp::Add,
"fsub_fast" => mir::BinOp::Sub,
"fmul_fast" => mir::BinOp::Mul,
"fdiv_fast" => mir::BinOp::Div,
"frem_fast" => mir::BinOp::Rem,
_ => bug!(),
};
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)
};
Ok(match fty {
FloatTy::F16 => unimplemented!("f16_f128"),
FloatTy::F32 => x.to_scalar().to_f32()?.is_finite(),
FloatTy::F64 => x.to_scalar().to_f64()?.is_finite(),
FloatTy::F128 => unimplemented!("f16_f128"),
})
};
match (float_finite(&a)?, float_finite(&b)?) {
(false, false) => throw_ub_format!(
"`{intrinsic_name}` intrinsic called with non-finite value as both parameters",
),
(false, _) => throw_ub_format!(
"`{intrinsic_name}` intrinsic called with non-finite value as first parameter",
),
(_, false) => throw_ub_format!(
"`{intrinsic_name}` intrinsic called with non-finite value as second parameter",
),
_ => {}
}
let res = this.wrapping_binary_op(op, &a, &b)?;
if !float_finite(&res)? {
throw_ub_format!("`{intrinsic_name}` intrinsic produced non-finite value as result");
}
// This cannot be a NaN so we also don't have to apply any non-determinism.
// (Also, `wrapping_binary_op` already called `generate_nan` if needed.)
this.write_immediate(*res, dest)?;
}

"float_to_int_unchecked" => {
let [val] = check_arg_count(args)?;
let val = this.read_immediate(val)?;
@@ -429,6 +446,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
_ => return Ok(EmulateItemResult::NotSupported),
}

Ok(EmulateItemResult::NeedsJumping)
Ok(EmulateItemResult::NeedsReturn)
}
}
2 changes: 1 addition & 1 deletion src/tools/miri/src/intrinsics/simd.rs
Original file line number Diff line number Diff line change
@@ -746,7 +746,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {

_ => return Ok(EmulateItemResult::NotSupported),
}
Ok(EmulateItemResult::NeedsJumping)
Ok(EmulateItemResult::NeedsReturn)
}

fn fminmax_op(
3 changes: 3 additions & 0 deletions src/tools/miri/src/lib.rs
Original file line number Diff line number Diff line change
@@ -13,6 +13,7 @@
#![feature(lint_reasons)]
#![feature(trait_upcasting)]
#![feature(strict_overflow_ops)]
#![feature(strict_provenance)]
// Configure clippy and other lints
#![allow(
clippy::collapsible_else_if,
@@ -74,6 +75,7 @@ extern crate rustc_target;
extern crate rustc_driver;

mod alloc_addresses;
mod alloc_bytes;
mod borrow_tracker;
mod clock;
mod concurrency;
@@ -107,6 +109,7 @@ pub use crate::shims::tls::TlsData;
pub use crate::shims::EmulateItemResult;

pub use crate::alloc_addresses::{EvalContextExt as _, ProvenanceMode};
pub use crate::alloc_bytes::MiriAllocBytes;
pub use crate::borrow_tracker::stacked_borrows::{
EvalContextExt as _, Item, Permission, Stack, Stacks,
};
28 changes: 14 additions & 14 deletions src/tools/miri/src/machine.rs
Original file line number Diff line number Diff line change
@@ -30,14 +30,13 @@ use rustc_target::abi::{Align, Size};
use rustc_target::spec::abi::Abi;

use crate::{
concurrency::{data_race, weak_memory},
shims::unix,
concurrency::{
data_race::{self, NaReadType, NaWriteType},
weak_memory,
},
*,
};

use self::concurrency::data_race::NaReadType;
use self::concurrency::data_race::NaWriteType;

/// First real-time signal.
/// `signal(7)` says this must be between 32 and 64 and specifies 34 or 35
/// as typical values.
@@ -206,11 +205,11 @@ pub enum Provenance {
/// whether *some* exposed pointer could have done what we want to do, and if the answer is yes
/// then we allow the access. This allows too much code in two ways:
/// - The same wildcard pointer can "take the role" of multiple different exposed pointers on
/// subsequenct memory accesses.
/// subsequent memory accesses.
/// - In the aliasing model, we don't just have to know the borrow tag of the pointer used for
/// the access, we also have to update the aliasing state -- and that update can be very
/// different depending on which borrow tag we pick! Stacked Borrows has support for this by
/// switching to a stack that is only approximately known, i.e. we overapproximate the effect
/// switching to a stack that is only approximately known, i.e. we over-approximate the effect
/// of using *any* exposed pointer for this access, and only keep information about the borrow
/// stack that would be true with all possible choices.
Wildcard,
@@ -464,9 +463,9 @@ pub struct MiriMachine<'mir, 'tcx> {
pub(crate) validate: bool,

/// The table of file descriptors.
pub(crate) fds: unix::FdTable,
pub(crate) fds: shims::FdTable,
/// The table of directory descriptors.
pub(crate) dirs: unix::DirTable,
pub(crate) dirs: shims::DirTable,

/// This machine's monotone clock.
pub(crate) clock: Clock,
@@ -641,7 +640,7 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> {
tls: TlsData::default(),
isolated_op: config.isolated_op,
validate: config.validate,
fds: unix::FdTable::new(config.mute_stdout_stderr),
fds: shims::FdTable::new(config.mute_stdout_stderr),
dirs: Default::default(),
layouts,
threads: ThreadManager::default(),
@@ -862,7 +861,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> {

type Provenance = Provenance;
type ProvenanceExtra = ProvenanceExtra;
type Bytes = Box<[u8]>;
type Bytes = MiriAllocBytes;

type MemoryMap =
MonoHashMap<AllocId, (MemoryKind, Allocation<Provenance, Self::AllocExtra, Self::Bytes>)>;
@@ -1088,8 +1087,9 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> {
id: AllocId,
alloc: Cow<'b, Allocation>,
kind: Option<MemoryKind>,
) -> InterpResult<'tcx, Cow<'b, Allocation<Self::Provenance, Self::AllocExtra>>> {
let kind = kind.expect("we set our GLOBAL_KIND so this cannot be None");
) -> InterpResult<'tcx, Cow<'b, Allocation<Self::Provenance, Self::AllocExtra, Self::Bytes>>>
{
let kind = kind.expect("we set our STATIC_KIND so this cannot be None");
if ecx.machine.tracked_alloc_ids.contains(&id) {
ecx.emit_diagnostic(NonHaltingDiagnostic::CreatedAlloc(
id,
@@ -1126,7 +1126,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> {
Some(ecx.generate_stacktrace())
};

let alloc: Allocation<Provenance, Self::AllocExtra> = alloc.adjust_from_tcx(
let alloc: Allocation<Provenance, Self::AllocExtra, Self::Bytes> = alloc.adjust_from_tcx(
&ecx.tcx,
AllocExtra {
borrow_tracker,
2 changes: 1 addition & 1 deletion src/tools/miri/src/provenance_gc.rs
Original file line number Diff line number Diff line change
@@ -122,7 +122,7 @@ impl VisitProvenance for OpTy<'_, Provenance> {
}
}

impl VisitProvenance for Allocation<Provenance, AllocExtra<'_>> {
impl VisitProvenance for Allocation<Provenance, AllocExtra<'_>, MiriAllocBytes> {
fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
for prov in self.provenance().provenances() {
prov.visit_provenance(visit);
69 changes: 68 additions & 1 deletion src/tools/miri/src/shims/alloc.rs
Original file line number Diff line number Diff line change
@@ -87,7 +87,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
}
AllocatorKind::Default => {
default(this)?;
Ok(EmulateItemResult::NeedsJumping)
Ok(EmulateItemResult::NeedsReturn)
}
}
}
@@ -111,6 +111,32 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
Ok(ptr.into())
}

fn posix_memalign(
&mut self,
memptr: &OpTy<'tcx, Provenance>,
align: &OpTy<'tcx, Provenance>,
size: &OpTy<'tcx, Provenance>,
) -> InterpResult<'tcx, Scalar<Provenance>> {
let this = self.eval_context_mut();
let memptr = this.deref_pointer(memptr)?;
let align = this.read_target_usize(align)?;
let size = this.read_target_usize(size)?;

// Align must be power of 2, and also at least ptr-sized (POSIX rules).
// But failure to adhere to this is not UB, it's an error condition.
if !align.is_power_of_two() || align < this.pointer_size().bytes() {
Ok(this.eval_libc("EINVAL"))
} else {
let ptr = this.allocate_ptr(
Size::from_bytes(size),
Align::from_bytes(align).unwrap(),
MiriMemoryKind::C.into(),
)?;
this.write_pointer(ptr, &memptr)?;
Ok(Scalar::from_i32(0))
}
}

fn free(&mut self, ptr: Pointer<Option<Provenance>>) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
if !this.ptr_is_null(ptr)? {
@@ -146,4 +172,45 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
}
}
}

fn aligned_alloc(
&mut self,
align: &OpTy<'tcx, Provenance>,
size: &OpTy<'tcx, Provenance>,
) -> InterpResult<'tcx, Pointer<Option<Provenance>>> {
let this = self.eval_context_mut();
let align = this.read_target_usize(align)?;
let size = this.read_target_usize(size)?;

// Alignment must be a power of 2, and "supported by the implementation".
// We decide that "supported by the implementation" means that the
// size must be a multiple of the alignment. (This restriction seems common
// enough that it is stated on <https://en.cppreference.com/w/c/memory/aligned_alloc>
// as a general rule, but the actual standard has no such rule.)
// If any of these are violated, we have to return NULL.
// All fundamental alignments must be supported.
//
// macOS and Illumos are buggy in that they require the alignment
// to be at least the size of a pointer, so they do not support all fundamental
// alignments. We do not emulate those platform bugs.
//
// Linux also sets errno to EINVAL, but that's non-standard behavior that we do not
// emulate.
// FreeBSD says some of these cases are UB but that's violating the C standard.
// http://en.cppreference.com/w/cpp/memory/c/aligned_alloc
// Linux: https://linux.die.net/man/3/aligned_alloc
// FreeBSD: https://man.freebsd.org/cgi/man.cgi?query=aligned_alloc&apropos=0&sektion=3&manpath=FreeBSD+9-current&format=html
match size.checked_rem(align) {
Some(0) if align.is_power_of_two() => {
let align = align.max(this.malloc_align(size).bytes());
let ptr = this.allocate_ptr(
Size::from_bytes(size),
Align::from_bytes(align).unwrap(),
MiriMemoryKind::C.into(),
)?;
Ok(ptr.into())
}
_ => Ok(Pointer::null()),
}
}
}
14 changes: 7 additions & 7 deletions src/tools/miri/src/shims/extern_static.rs
Original file line number Diff line number Diff line change
@@ -55,26 +55,26 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> {
let val = ImmTy::from_int(val, this.machine.layouts.u8);
Self::alloc_extern_static(this, "__rust_alloc_error_handler_should_panic", val)?;

if this.target_os_is_unix() {
// "environ" is mandated by POSIX.
let environ = this.machine.env_vars.unix().environ();
Self::add_extern_static(this, "environ", environ);
}

match this.tcx.sess.target.os.as_ref() {
"linux" => {
Self::null_ptr_extern_statics(
this,
&["__cxa_thread_atexit_impl", "__clock_gettime64"],
)?;
Self::weak_symbol_extern_statics(this, &["getrandom", "statx"])?;
// "environ"
let environ = this.machine.env_vars.unix().environ();
Self::add_extern_static(this, "environ", environ);
}
"freebsd" => {
Self::null_ptr_extern_statics(this, &["__cxa_thread_atexit_impl"])?;
// "environ"
let environ = this.machine.env_vars.unix().environ();
Self::add_extern_static(this, "environ", environ);
}
"android" => {
Self::null_ptr_extern_statics(this, &["bsd_signal"])?;
Self::weak_symbol_extern_statics(this, &["signal"])?;
Self::weak_symbol_extern_statics(this, &["signal", "getrandom"])?;
}
"windows" => {
// "_tls_used"
28 changes: 18 additions & 10 deletions src/tools/miri/src/shims/foreign_items.rs
Original file line number Diff line number Diff line change
@@ -82,11 +82,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
}

// The rest either implements the logic, or falls back to `lookup_exported_symbol`.
match this.emulate_foreign_item_inner(link_name, abi, args, dest, unwind)? {
EmulateItemResult::NeedsJumping => {
match this.emulate_foreign_item_inner(link_name, abi, args, dest)? {
EmulateItemResult::NeedsReturn => {
trace!("{:?}", this.dump_place(&dest.clone().into()));
this.return_to_block(ret)?;
}
EmulateItemResult::NeedsUnwind => {
// Jump to the unwind block to begin unwinding.
this.unwind_to_block(unwind)?;
}
EmulateItemResult::AlreadyJumped => (),
EmulateItemResult::NotSupported => {
if let Some(body) = this.lookup_exported_symbol(link_name)? {
@@ -108,6 +112,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
let this = self.eval_context_ref();
match this.tcx.sess.target.os.as_ref() {
os if this.target_os_is_unix() => shims::unix::foreign_items::is_dyn_sym(name, os),
"wasi" => shims::wasi::foreign_items::is_dyn_sym(name),
"windows" => shims::windows::foreign_items::is_dyn_sym(name),
_ => false,
}
@@ -205,7 +210,6 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
abi: Abi,
args: &[OpTy<'tcx, Provenance>],
dest: &MPlaceTy<'tcx, Provenance>,
unwind: mir::UnwindAction,
) -> InterpResult<'tcx, EmulateItemResult> {
let this = self.eval_context_mut();

@@ -217,7 +221,7 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
// by the specified `.so` file; we should continue and check if it corresponds to
// a provided shim.
if this.call_native_fn(link_name, dest, args)? {
return Ok(EmulateItemResult::NeedsJumping);
return Ok(EmulateItemResult::NeedsReturn);
}
}

@@ -262,9 +266,9 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
match link_name.as_str() {
// Miri-specific extern functions
"miri_start_unwind" => {
// `check_shim` happens inside `handle_miri_start_unwind`.
this.handle_miri_start_unwind(abi, link_name, args, unwind)?;
return Ok(EmulateItemResult::AlreadyJumped);
let [payload] = this.check_shim(abi, Abi::Rust, link_name, args)?;
this.handle_miri_start_unwind(payload)?;
return Ok(EmulateItemResult::NeedsUnwind);
}
"miri_run_provenance_gc" => {
let [] = this.check_shim(abi, Abi::Rust, link_name, args)?;
@@ -479,7 +483,7 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
"__rust_alloc" => return this.emulate_allocator(default),
"miri_alloc" => {
default(this)?;
return Ok(EmulateItemResult::NeedsJumping);
return Ok(EmulateItemResult::NeedsReturn);
}
_ => unreachable!(),
}
@@ -539,7 +543,7 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
}
"miri_dealloc" => {
default(this)?;
return Ok(EmulateItemResult::NeedsJumping);
return Ok(EmulateItemResult::NeedsReturn);
}
_ => unreachable!(),
}
@@ -947,6 +951,10 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
shims::unix::foreign_items::EvalContextExt::emulate_foreign_item_inner(
this, link_name, abi, args, dest,
),
"wasi" =>
shims::wasi::foreign_items::EvalContextExt::emulate_foreign_item_inner(
this, link_name, abi, args, dest,
),
"windows" =>
shims::windows::foreign_items::EvalContextExt::emulate_foreign_item_inner(
this, link_name, abi, args, dest,
@@ -956,6 +964,6 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
};
// We only fall through to here if we did *not* hit the `_` arm above,
// i.e., if we actually emulated the function with one of the shims.
Ok(EmulateItemResult::NeedsJumping)
Ok(EmulateItemResult::NeedsReturn)
}
}
17 changes: 11 additions & 6 deletions src/tools/miri/src/shims/mod.rs
Original file line number Diff line number Diff line change
@@ -2,25 +2,30 @@

mod alloc;
mod backtrace;
pub mod foreign_items;
#[cfg(target_os = "linux")]
pub mod native_lib;
pub mod unix;
pub mod windows;
mod native_lib;
mod unix;
mod wasi;
mod windows;
mod x86;

pub mod env;
pub mod extern_static;
pub mod foreign_items;
pub mod os_str;
pub mod panic;
pub mod time;
pub mod tls;

pub use unix::{DirTable, FdTable};

/// What needs to be done after emulating an item (a shim or an intrinsic) is done.
pub enum EmulateItemResult {
/// The caller is expected to jump to the return block.
NeedsJumping,
/// Jumping has already been taken care of.
NeedsReturn,
/// The caller is expected to jump to the unwind block.
NeedsUnwind,
/// Jumping to the next block has already been taken care of.
AlreadyJumped,
/// The item is not supported.
NotSupported,
13 changes: 1 addition & 12 deletions src/tools/miri/src/shims/panic.rs
Original file line number Diff line number Diff line change
@@ -13,7 +13,6 @@
use rustc_ast::Mutability;
use rustc_middle::{mir, ty};
use rustc_span::Symbol;
use rustc_target::spec::abi::Abi;
use rustc_target::spec::PanicStrategy;

@@ -46,25 +45,15 @@ impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir,
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, '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,
abi: Abi,
link_name: Symbol,
args: &[OpTy<'tcx, Provenance>],
unwind: mir::UnwindAction,
) -> InterpResult<'tcx> {
fn handle_miri_start_unwind(&mut self, payload: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx> {
let this = self.eval_context_mut();

trace!("miri_start_unwind: {:?}", this.frame().instance);

// Get the raw pointer stored in arg[0] (the panic payload).
let [payload] = this.check_shim(abi, Abi::Rust, link_name, args)?;
let payload = this.read_scalar(payload)?;
let thread = this.active_thread_mut();
thread.panic_payloads.push(payload);

// Jump to the unwind block to begin unwinding.
this.unwind_to_block(unwind)?;
Ok(())
}

32 changes: 32 additions & 0 deletions src/tools/miri/src/shims/unix/android/foreign_items.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use rustc_span::Symbol;
use rustc_target::spec::abi::Abi;

use crate::*;

pub fn is_dyn_sym(_name: &str) -> bool {
false
}

impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {}
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
fn emulate_foreign_item_inner(
&mut self,
link_name: Symbol,
abi: Abi,
args: &[OpTy<'tcx, Provenance>],
dest: &MPlaceTy<'tcx, Provenance>,
) -> InterpResult<'tcx, EmulateItemResult> {
let this = self.eval_context_mut();
match link_name.as_str() {
// Miscellaneous
"__errno" => {
let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
let errno_place = this.last_error_place()?;
this.write_scalar(errno_place.to_ref(this).to_scalar(), dest)?;
}

_ => return Ok(EmulateItemResult::NotSupported),
}
Ok(EmulateItemResult::NeedsReturn)
}
}
1 change: 1 addition & 0 deletions src/tools/miri/src/shims/unix/android/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod foreign_items;
37 changes: 24 additions & 13 deletions src/tools/miri/src/shims/unix/fd.rs
Original file line number Diff line number Diff line change
@@ -7,7 +7,6 @@ use std::collections::BTreeMap;
use std::io::{self, ErrorKind, IsTerminal, Read, SeekFrom, Write};
use std::rc::Rc;

use rustc_middle::ty::TyCtxt;
use rustc_target::abi::Size;

use crate::shims::unix::*;
@@ -22,7 +21,7 @@ pub trait FileDescription: std::fmt::Debug + Any {
&mut self,
_communicate_allowed: bool,
_bytes: &mut [u8],
_tcx: TyCtxt<'tcx>,
_ecx: &mut MiriInterpCx<'_, 'tcx>,
) -> InterpResult<'tcx, io::Result<usize>> {
throw_unsup_format!("cannot read from {}", self.name());
}
@@ -32,7 +31,7 @@ pub trait FileDescription: std::fmt::Debug + Any {
&mut self,
_communicate_allowed: bool,
_bytes: &[u8],
_tcx: TyCtxt<'tcx>,
_ecx: &mut MiriInterpCx<'_, 'tcx>,
) -> InterpResult<'tcx, io::Result<usize>> {
throw_unsup_format!("cannot write to {}", self.name());
}
@@ -82,7 +81,7 @@ impl FileDescription for io::Stdin {
&mut self,
communicate_allowed: bool,
bytes: &mut [u8],
_tcx: TyCtxt<'tcx>,
_ecx: &mut MiriInterpCx<'_, 'tcx>,
) -> InterpResult<'tcx, io::Result<usize>> {
if !communicate_allowed {
// We want isolation mode to be deterministic, so we have to disallow all reads, even stdin.
@@ -105,7 +104,7 @@ impl FileDescription for io::Stdout {
&mut self,
_communicate_allowed: bool,
bytes: &[u8],
_tcx: TyCtxt<'tcx>,
_ecx: &mut MiriInterpCx<'_, 'tcx>,
) -> InterpResult<'tcx, io::Result<usize>> {
// We allow writing to stderr even with isolation enabled.
let result = Write::write(self, bytes);
@@ -133,7 +132,7 @@ impl FileDescription for io::Stderr {
&mut self,
_communicate_allowed: bool,
bytes: &[u8],
_tcx: TyCtxt<'tcx>,
_ecx: &mut MiriInterpCx<'_, 'tcx>,
) -> InterpResult<'tcx, io::Result<usize>> {
// We allow writing to stderr even with isolation enabled.
// No need to flush, stderr is not buffered.
@@ -158,7 +157,7 @@ impl FileDescription for NullOutput {
&mut self,
_communicate_allowed: bool,
bytes: &[u8],
_tcx: TyCtxt<'tcx>,
_ecx: &mut MiriInterpCx<'_, 'tcx>,
) -> InterpResult<'tcx, io::Result<usize>> {
// We just don't write anything, but report to the user that we did.
Ok(Ok(bytes.len()))
@@ -173,6 +172,14 @@ impl FileDescriptor {
FileDescriptor(Rc::new(RefCell::new(Box::new(fd))))
}

pub fn borrow(&self) -> Ref<'_, dyn FileDescription> {
Ref::map(self.0.borrow(), |fd| fd.as_ref())
}

pub fn borrow_mut(&self) -> RefMut<'_, dyn FileDescription> {
RefMut::map(self.0.borrow_mut(), |fd| fd.as_mut())
}

pub fn close<'ctx>(self, communicate_allowed: bool) -> InterpResult<'ctx, io::Result<()>> {
// Destroy this `Rc` using `into_inner` so we can call `close` instead of
// implicitly running the destructor of the file description.
@@ -242,12 +249,12 @@ impl FdTable {

pub fn get(&self, fd: i32) -> Option<Ref<'_, dyn FileDescription>> {
let fd = self.fds.get(&fd)?;
Some(Ref::map(fd.0.borrow(), |fd| fd.as_ref()))
Some(fd.borrow())
}

pub fn get_mut(&self, fd: i32) -> Option<RefMut<'_, dyn FileDescription>> {
let fd = self.fds.get(&fd)?;
Some(RefMut::map(fd.0.borrow_mut(), |fd| fd.as_mut()))
Some(fd.borrow_mut())
}

pub fn dup(&self, fd: i32) -> Option<FileDescriptor> {
@@ -370,7 +377,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
.min(u64::try_from(isize::MAX).unwrap());
let communicate = this.machine.communicate();

let Some(mut file_descriptor) = this.machine.fds.get_mut(fd) else {
// We temporarily dup the FD to be able to retain mutable access to `this`.
let Some(file_descriptor) = this.machine.fds.dup(fd) else {
trace!("read: FD not found");
return this.fd_not_found();
};
@@ -383,7 +391,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
// `File::read` never returns a value larger than `count`,
// so this cannot fail.
let result = file_descriptor
.read(communicate, &mut bytes, *this.tcx)?
.borrow_mut()
.read(communicate, &mut bytes, this)?
.map(|c| i64::try_from(c).unwrap());
drop(file_descriptor);

@@ -421,12 +430,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
let communicate = this.machine.communicate();

let bytes = this.read_bytes_ptr_strip_provenance(buf, Size::from_bytes(count))?.to_owned();
let Some(mut file_descriptor) = this.machine.fds.get_mut(fd) else {
// We temporarily dup the FD to be able to retain mutable access to `this`.
let Some(file_descriptor) = this.machine.fds.dup(fd) else {
return this.fd_not_found();
};

let result = file_descriptor
.write(communicate, &bytes, *this.tcx)?
.borrow_mut()
.write(communicate, &bytes, this)?
.map(|c| i64::try_from(c).unwrap());
drop(file_descriptor);

75 changes: 46 additions & 29 deletions src/tools/miri/src/shims/unix/foreign_items.rs
Original file line number Diff line number Diff line change
@@ -3,13 +3,13 @@ use std::str;

use rustc_middle::ty::layout::LayoutOf;
use rustc_span::Symbol;
use rustc_target::abi::{Align, Size};
use rustc_target::spec::abi::Abi;

use crate::shims::alloc::EvalContextExt as _;
use crate::shims::unix::*;
use crate::*;

use shims::unix::android::foreign_items as android;
use shims::unix::freebsd::foreign_items as freebsd;
use shims::unix::linux::foreign_items as linux;
use shims::unix::macos::foreign_items as macos;
@@ -27,6 +27,7 @@ pub fn is_dyn_sym(name: &str, target_os: &str) -> bool {
// Give specific OSes a chance to allow their symbols.
_ =>
match target_os {
"android" => android::is_dyn_sym(name),
"freebsd" => freebsd::is_dyn_sym(name),
"linux" => linux::is_dyn_sym(name),
"macos" => macos::is_dyn_sym(name),
@@ -249,28 +250,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {

// Allocation
"posix_memalign" => {
let [ret, align, size] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
let ret = this.deref_pointer(ret)?;
let align = this.read_target_usize(align)?;
let size = this.read_target_usize(size)?;
// Align must be power of 2, and also at least ptr-sized (POSIX rules).
// But failure to adhere to this is not UB, it's an error condition.
if !align.is_power_of_two() || align < this.pointer_size().bytes() {
let einval = this.eval_libc_i32("EINVAL");
this.write_int(einval, dest)?;
} else {
if size == 0 {
this.write_null(&ret)?;
} else {
let ptr = this.allocate_ptr(
Size::from_bytes(size),
Align::from_bytes(align).unwrap(),
MiriMemoryKind::C.into(),
)?;
this.write_pointer(ptr, &ret)?;
}
this.write_null(dest)?;
}
let [memptr, align, size] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
let result = this.posix_memalign(memptr, align, size)?;
this.write_scalar(result, dest)?;
}

"mmap" => {
@@ -287,7 +269,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {

"reallocarray" => {
// Currently this function does not exist on all Unixes, e.g. on macOS.
if !matches!(&*this.tcx.sess.target.os, "linux" | "freebsd") {
if !matches!(&*this.tcx.sess.target.os, "linux" | "freebsd" | "android") {
throw_unsup_format!(
"`reallocarray` is not supported on {}",
this.tcx.sess.target.os
@@ -315,6 +297,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
}
}
}
"aligned_alloc" => {
// This is a C11 function, we assume all Unixes have it.
// (MSVC explicitly does not support this.)
let [align, size] =
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
let res = this.aligned_alloc(align, size)?;
this.write_pointer(res, dest)?;
}

// Dynamic symbol loading
"dlsym" => {
@@ -605,7 +595,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
"getentropy" => {
// This function is non-standard but exists with the same signature and behavior on
// Linux, macOS, FreeBSD and Solaris/Illumos.
if !matches!(&*this.tcx.sess.target.os, "linux" | "macos" | "freebsd" | "illumos" | "solaris") {
if !matches!(&*this.tcx.sess.target.os, "linux" | "macos" | "freebsd" | "illumos" | "solaris" | "android") {
throw_unsup_format!(
"`getentropy` is not supported on {}",
this.tcx.sess.target.os
@@ -634,9 +624,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
"getrandom" => {
// This function is non-standard but exists with the same signature and behavior on
// Linux, FreeBSD and Solaris/Illumos.
if !matches!(&*this.tcx.sess.target.os, "linux" | "freebsd" | "illumos" | "solaris") {
if !matches!(&*this.tcx.sess.target.os, "linux" | "freebsd" | "illumos" | "solaris" | "android") {
throw_unsup_format!(
"`getentropy` is not supported on {}",
"`getrandom` is not supported on {}",
this.tcx.sess.target.os
);
}
@@ -649,6 +639,31 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
this.gen_random(ptr, len)?;
this.write_scalar(Scalar::from_target_usize(len, this), dest)?;
}
"_Unwind_RaiseException" => {
// This is not formally part of POSIX, but it is very wide-spread on POSIX systems.
// It was originally specified as part of the Itanium C++ ABI:
// https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html#base-throw.
// On Linux it is
// documented as part of the LSB:
// https://refspecs.linuxfoundation.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/baselib--unwind-raiseexception.html
// Basically every other UNIX uses the exact same api though. Arm also references
// back to the Itanium C++ ABI for the definition of `_Unwind_RaiseException` for
// arm64:
// https://github.com/ARM-software/abi-aa/blob/main/cppabi64/cppabi64.rst#toc-entry-35
// For arm32 they did something custom, but similar enough that the same
// `_Unwind_RaiseException` impl in miri should work:
// https://github.com/ARM-software/abi-aa/blob/main/ehabi32/ehabi32.rst
if !matches!(&*this.tcx.sess.target.os, "linux" | "freebsd" | "illumos" | "solaris" | "android" | "macos") {
throw_unsup_format!(
"`_Unwind_RaiseException` is not supported on {}",
this.tcx.sess.target.os
);
}
// This function looks and behaves excatly like miri_start_unwind.
let [payload] = this.check_shim(abi, Abi::C { unwind: true }, link_name, args)?;
this.handle_miri_start_unwind(payload)?;
return Ok(EmulateItemResult::NeedsUnwind);
}

// Incomplete shims that we "stub out" just to get pre-main initialization code to work.
// These shims are enabled only when the caller is in the standard library.
@@ -718,8 +733,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
this.write_int(super::UID, dest)?;
}

"getpwuid_r"
"getpwuid_r" | "__posix_getpwuid_r"
if this.frame_in_std() => {
// getpwuid_r is the standard name, __posix_getpwuid_r is used on solarish
let [uid, pwd, buf, buflen, result] =
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
this.check_no_isolation("`getpwuid_r`")?;
@@ -759,6 +775,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
_ => {
let target_os = &*this.tcx.sess.target.os;
return match target_os {
"android" => android::EvalContextExt::emulate_foreign_item_inner(this, link_name, abi, args, dest),
"freebsd" => freebsd::EvalContextExt::emulate_foreign_item_inner(this, link_name, abi, args, dest),
"linux" => linux::EvalContextExt::emulate_foreign_item_inner(this, link_name, abi, args, dest),
"macos" => macos::EvalContextExt::emulate_foreign_item_inner(this, link_name, abi, args, dest),
@@ -768,6 +785,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
}
};

Ok(EmulateItemResult::NeedsJumping)
Ok(EmulateItemResult::NeedsReturn)
}
}
2 changes: 1 addition & 1 deletion src/tools/miri/src/shims/unix/freebsd/foreign_items.rs
Original file line number Diff line number Diff line change
@@ -86,6 +86,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {

_ => return Ok(EmulateItemResult::NotSupported),
}
Ok(EmulateItemResult::NeedsJumping)
Ok(EmulateItemResult::NeedsReturn)
}
}
5 changes: 2 additions & 3 deletions src/tools/miri/src/shims/unix/fs.rs
Original file line number Diff line number Diff line change
@@ -9,7 +9,6 @@ use std::path::{Path, PathBuf};
use std::time::SystemTime;

use rustc_data_structures::fx::FxHashMap;
use rustc_middle::ty::TyCtxt;
use rustc_target::abi::Size;

use crate::shims::os_str::bytes_to_os_str;
@@ -34,7 +33,7 @@ impl FileDescription for FileHandle {
&mut self,
communicate_allowed: bool,
bytes: &mut [u8],
_tcx: TyCtxt<'tcx>,
_ecx: &mut MiriInterpCx<'_, 'tcx>,
) -> InterpResult<'tcx, io::Result<usize>> {
assert!(communicate_allowed, "isolation should have prevented even opening a file");
Ok(self.file.read(bytes))
@@ -44,7 +43,7 @@ impl FileDescription for FileHandle {
&mut self,
communicate_allowed: bool,
bytes: &[u8],
_tcx: TyCtxt<'tcx>,
_ecx: &mut MiriInterpCx<'_, 'tcx>,
) -> InterpResult<'tcx, io::Result<usize>> {
assert!(communicate_allowed, "isolation should have prevented even opening a file");
Ok(self.file.write(bytes))
5 changes: 2 additions & 3 deletions src/tools/miri/src/shims/unix/linux/eventfd.rs
Original file line number Diff line number Diff line change
@@ -2,7 +2,6 @@
//! Currently just a stub.
use std::io;

use rustc_middle::ty::TyCtxt;
use rustc_target::abi::Endian;

use crate::shims::unix::*;
@@ -52,11 +51,11 @@ impl FileDescription for Event {
&mut self,
_communicate_allowed: bool,
bytes: &[u8],
tcx: TyCtxt<'tcx>,
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 tcx.sess.target.endian {
let num = match ecx.tcx.sess.target.endian {
Endian::Little => u64::from_le_bytes(bytes),
Endian::Big => u64::from_be_bytes(bytes),
};
2 changes: 1 addition & 1 deletion src/tools/miri/src/shims/unix/linux/foreign_items.rs
Original file line number Diff line number Diff line change
@@ -203,6 +203,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
_ => return Ok(EmulateItemResult::NotSupported),
};

Ok(EmulateItemResult::NeedsJumping)
Ok(EmulateItemResult::NeedsReturn)
}
}
2 changes: 1 addition & 1 deletion src/tools/miri/src/shims/unix/macos/foreign_items.rs
Original file line number Diff line number Diff line change
@@ -177,6 +177,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
_ => return Ok(EmulateItemResult::NotSupported),
};

Ok(EmulateItemResult::NeedsJumping)
Ok(EmulateItemResult::NeedsReturn)
}
}
4 changes: 2 additions & 2 deletions src/tools/miri/src/shims/unix/mem.rs
Original file line number Diff line number Diff line change
@@ -11,7 +11,7 @@
//! calls to munmap, but for a very different reason. In principle, according to the man pages, it
//! is possible to unmap arbitrary regions of address space. But in a high-level language like Rust
//! this amounts to partial deallocation, which LLVM does not support. So any attempt to call our
//! munmap shim which would partily unmap a region of address space previously mapped by mmap will
//! munmap shim which would partially unmap a region of address space previously mapped by mmap will
//! report UB.
use crate::*;
@@ -78,7 +78,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
// * The implementation does not support the combination of accesses requested in the
// prot argument.
//
// Miri doesn't support MAP_FIXED or any any protections other than PROT_READ|PROT_WRITE.
// Miri doesn't support MAP_FIXED or any protections other than PROT_READ|PROT_WRITE.
if flags & map_fixed != 0 || prot != prot_read | prot_write {
this.set_last_error(this.eval_libc("ENOTSUP"))?;
return Ok(this.eval_libc("MAP_FAILED"));
1 change: 1 addition & 0 deletions src/tools/miri/src/shims/unix/mod.rs
Original file line number Diff line number Diff line change
@@ -8,6 +8,7 @@ mod socket;
mod sync;
mod thread;

mod android;
mod freebsd;
mod linux;
mod macos;
2 changes: 1 addition & 1 deletion src/tools/miri/src/shims/unix/solarish/foreign_items.rs
Original file line number Diff line number Diff line change
@@ -45,6 +45,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {

_ => return Ok(EmulateItemResult::NotSupported),
}
Ok(EmulateItemResult::NeedsJumping)
Ok(EmulateItemResult::NeedsReturn)
}
}
2 changes: 1 addition & 1 deletion src/tools/miri/src/shims/unix/sync.rs
Original file line number Diff line number Diff line change
@@ -390,7 +390,7 @@ fn reacquire_cond_mutex<'mir, 'tcx: 'mir>(
Ok(())
}

/// After a thread waiting on a condvar was signalled:
/// After a thread waiting on a condvar was signaled:
/// Reacquire the conditional variable and remove the timeout callback if any
/// was registered.
fn post_cond_signal<'mir, 'tcx: 'mir>(
40 changes: 40 additions & 0 deletions src/tools/miri/src/shims/wasi/foreign_items.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
use rustc_span::Symbol;
use rustc_target::spec::abi::Abi;

use crate::shims::alloc::EvalContextExt as _;
use crate::*;

pub fn is_dyn_sym(_name: &str) -> bool {
false
}

impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {}
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
fn emulate_foreign_item_inner(
&mut self,
link_name: Symbol,
abi: Abi,
args: &[OpTy<'tcx, Provenance>],
dest: &MPlaceTy<'tcx, Provenance>,
) -> InterpResult<'tcx, EmulateItemResult> {
let this = self.eval_context_mut();
match link_name.as_str() {
// Allocation
"posix_memalign" => {
let [memptr, align, size] =
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
let result = this.posix_memalign(memptr, align, size)?;
this.write_scalar(result, dest)?;
}
"aligned_alloc" => {
let [align, size] =
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
let res = this.aligned_alloc(align, size)?;
this.write_pointer(res, dest)?;
}

_ => return Ok(EmulateItemResult::NotSupported),
}
Ok(EmulateItemResult::NeedsReturn)
}
}
1 change: 1 addition & 0 deletions src/tools/miri/src/shims/wasi/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod foreign_items;
2 changes: 1 addition & 1 deletion src/tools/miri/src/shims/windows/foreign_items.rs
Original file line number Diff line number Diff line change
@@ -762,6 +762,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
_ => return Ok(EmulateItemResult::NotSupported),
}

Ok(EmulateItemResult::NeedsJumping)
Ok(EmulateItemResult::NeedsReturn)
}
}
2 changes: 1 addition & 1 deletion src/tools/miri/src/shims/x86/aesni.rs
Original file line number Diff line number Diff line change
@@ -127,7 +127,7 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>:
// with an external crate.
_ => return Ok(EmulateItemResult::NotSupported),
}
Ok(EmulateItemResult::NeedsJumping)
Ok(EmulateItemResult::NeedsReturn)
}
}

2 changes: 1 addition & 1 deletion src/tools/miri/src/shims/x86/avx.rs
Original file line number Diff line number Diff line change
@@ -344,6 +344,6 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>:
}
_ => return Ok(EmulateItemResult::NotSupported),
}
Ok(EmulateItemResult::NeedsJumping)
Ok(EmulateItemResult::NeedsReturn)
}
}
4 changes: 2 additions & 2 deletions src/tools/miri/src/shims/x86/avx2.rs
Original file line number Diff line number Diff line change
@@ -81,7 +81,7 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>:

let scale = this.read_scalar(scale)?.to_i8()?;
if !matches!(scale, 1 | 2 | 4 | 8) {
throw_unsup_format!("invalid gather scale {scale}");
panic!("invalid gather scale {scale}");
}
let scale = i64::from(scale);

@@ -440,6 +440,6 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>:
}
_ => return Ok(EmulateItemResult::NotSupported),
}
Ok(EmulateItemResult::NeedsJumping)
Ok(EmulateItemResult::NeedsReturn)
}
}
12 changes: 6 additions & 6 deletions src/tools/miri/src/shims/x86/mod.rs
Original file line number Diff line number Diff line change
@@ -144,7 +144,7 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>:

_ => return Ok(EmulateItemResult::NotSupported),
}
Ok(EmulateItemResult::NeedsJumping)
Ok(EmulateItemResult::NeedsReturn)
}
}

@@ -200,7 +200,7 @@ impl FloatBinOp {
) -> InterpResult<'tcx, Self> {
// Only bits 0..=4 are used, remaining should be zero.
if imm & !0b1_1111 != 0 {
throw_unsup_format!("invalid `imm` parameter of {intrinsic}: 0x{imm:x}");
panic!("invalid `imm` parameter of {intrinsic}: 0x{imm:x}");
}
// Bit 4 specifies whether the operation is quiet or signaling, which
// we do not care in Miri.
@@ -683,7 +683,7 @@ fn rounding_from_imm<'tcx>(rounding: i32) -> InterpResult<'tcx, rustc_apfloat::R
// SSE status register. Since we do not support modifying it from
// Miri (or Rust), we assume it to be at its default mode (round-to-nearest).
0b100..=0b111 => Ok(rustc_apfloat::Round::NearestTiesToEven),
rounding => throw_unsup_format!("unsupported rounding mode 0x{rounding:02x}"),
rounding => panic!("invalid rounding mode 0x{rounding:02x}"),
}
}

@@ -757,7 +757,7 @@ fn int_abs<'tcx>(
Ok(())
}

/// Splits `op` (which must be a SIMD vector) into 128-bit chuncks.
/// Splits `op` (which must be a SIMD vector) into 128-bit chunks.
///
/// Returns a tuple where:
/// * The first element is the number of 128-bit chunks (let's call it `N`).
@@ -788,7 +788,7 @@ fn split_simd_to_128bit_chunks<'tcx, P: Projectable<'tcx, Provenance>>(
Ok((num_chunks, items_per_chunk, chunked_op))
}

/// Horizontaly performs `which` operation on adjacent values of
/// Horizontally performs `which` operation on adjacent values of
/// `left` and `right` SIMD vectors and stores the result in `dest`.
/// "Horizontal" means that the i-th output element is calculated
/// from the elements 2*i and 2*i+1 of the concatenation of `left` and
@@ -1256,7 +1256,7 @@ fn packusdw<'tcx>(

/// Negates elements from `left` when the corresponding element in
/// `right` is negative. If an element from `right` is zero, zero
/// is writen to the corresponding output element.
/// is written to the corresponding output element.
/// In other words, multiplies `left` with `right.signum()`.
fn psign<'tcx>(
this: &mut crate::MiriInterpCx<'_, 'tcx>,
2 changes: 1 addition & 1 deletion src/tools/miri/src/shims/x86/sse.rs
Original file line number Diff line number Diff line change
@@ -212,6 +212,6 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>:
}
_ => return Ok(EmulateItemResult::NotSupported),
}
Ok(EmulateItemResult::NeedsJumping)
Ok(EmulateItemResult::NeedsReturn)
}
}
2 changes: 1 addition & 1 deletion src/tools/miri/src/shims/x86/sse2.rs
Original file line number Diff line number Diff line change
@@ -388,6 +388,6 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>:
}
_ => return Ok(EmulateItemResult::NotSupported),
}
Ok(EmulateItemResult::NeedsJumping)
Ok(EmulateItemResult::NeedsReturn)
}
}
2 changes: 1 addition & 1 deletion src/tools/miri/src/shims/x86/sse3.rs
Original file line number Diff line number Diff line change
@@ -51,6 +51,6 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>:
}
_ => return Ok(EmulateItemResult::NotSupported),
}
Ok(EmulateItemResult::NeedsJumping)
Ok(EmulateItemResult::NeedsReturn)
}
}
2 changes: 1 addition & 1 deletion src/tools/miri/src/shims/x86/sse41.rs
Original file line number Diff line number Diff line change
@@ -176,6 +176,6 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>:
}
_ => return Ok(EmulateItemResult::NotSupported),
}
Ok(EmulateItemResult::NeedsJumping)
Ok(EmulateItemResult::NeedsReturn)
}
}
2 changes: 1 addition & 1 deletion src/tools/miri/src/shims/x86/ssse3.rs
Original file line number Diff line number Diff line change
@@ -137,6 +137,6 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>:
}
_ => return Ok(EmulateItemResult::NotSupported),
}
Ok(EmulateItemResult::NeedsJumping)
Ok(EmulateItemResult::NeedsReturn)
}
}
2 changes: 1 addition & 1 deletion src/tools/miri/test_dependencies/Cargo.toml
Original file line number Diff line number Diff line change
@@ -11,12 +11,12 @@ edition = "2021"
# all dependencies (and their transitive ones) listed here can be used in `tests/`.
libc = "0.2"
num_cpus = "1.10.1"
tempfile = "3"

getrandom_01 = { package = "getrandom", version = "0.1" }
getrandom_02 = { package = "getrandom", version = "0.2", features = ["js"] }

[target.'cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))'.dependencies]
tempfile = "3"
page_size = "0.6"
tokio = { version = "1.24", features = ["macros", "rt-multi-thread", "time", "net"] }

Loading