Rollup merge of #125286 - RalfJung:miri-sync, r=RalfJung
Miri subtree update r? `@ghost`
This commit is contained in:
commit
7a45322300
@ -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",
|
||||
|
1
src/tools/miri/.gitignore
vendored
1
src/tools/miri/.gitignore
vendored
@ -5,6 +5,7 @@ tex/*/out
|
||||
*.out
|
||||
*.rs.bk
|
||||
.vscode
|
||||
.helix
|
||||
*.mm_profdata
|
||||
perf.data
|
||||
perf.data.old
|
||||
|
@ -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",
|
||||
|
@ -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.
|
||||
|
@ -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"];
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 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} ");
|
||||
}
|
||||
}
|
||||
SysrootBuilder::new(&sysroot_dir, target)
|
||||
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());
|
||||
|
@ -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
src/tools/miri/miri-script/src/args.rs
Normal file
136
src/tools/miri/miri-script/src/args.rs
Normal file
@ -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()
|
||||
}
|
||||
}
|
@ -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 @@ fn build_miri_sysroot(&mut self, quiet: bool, target: Option<&OsStr>) -> Result<
|
||||
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 @@ pub fn exec(self) -> Result<()> {
|
||||
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 @@ pub fn exec(self) -> Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
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'")?;
|
||||
@ -373,7 +371,7 @@ fn rustc_push(github_user: String, branch: String) -> Result<()> {
|
||||
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 @@ fn bench(target: Option<OsString>, benches: Vec<OsString>) -> Result<()> {
|
||||
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,28 +416,28 @@ fn bench(target: Option<OsString>, benches: Vec<OsString>) -> Result<()> {
|
||||
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)?;
|
||||
@ -447,7 +445,7 @@ fn clippy(flags: Vec<OsString>) -> Result<()> {
|
||||
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 @@ fn cargo(flags: Vec<OsString>) -> Result<()> {
|
||||
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 @@ fn run(
|
||||
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");
|
||||
// More flags that we will pass before `flags`
|
||||
// (because `flags` may contain `--`).
|
||||
let mut early_flags = Vec::<OsString>::new();
|
||||
|
||||
// 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`.`
|
||||
// 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 @@ fn run(
|
||||
};
|
||||
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 @@ fn run(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn fmt(flags: Vec<OsString>) -> Result<()> {
|
||||
fn fmt(flags: Vec<String>) -> Result<()> {
|
||||
use itertools::Itertools;
|
||||
|
||||
let e = MiriEnv::new()?;
|
||||
@ -565,6 +572,6 @@ fn fmt(flags: Vec<OsString>) -> Result<()> {
|
||||
.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)
|
||||
}
|
||||
}
|
||||
|
@ -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,26 +16,26 @@ 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.)
|
||||
@ -43,33 +43,35 @@ pub enum Command {
|
||||
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 @@ pub enum Command {
|
||||
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 }
|
||||
|
@ -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 @@ pub fn install_to_sysroot(
|
||||
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 @@ pub fn build(
|
||||
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 @@ pub fn format_files(
|
||||
files: impl Iterator<Item = Result<PathBuf, walkdir::Error>>,
|
||||
toolchain: &str,
|
||||
config_path: &Path,
|
||||
flags: &[OsString],
|
||||
flags: &[String],
|
||||
) -> anyhow::Result<()> {
|
||||
use itertools::Itertools;
|
||||
|
||||
|
@ -1 +1 @@
|
||||
ef15976387ad9c1cdceaabf469e0cf35f5852f6d
|
||||
6579ed89f0fcc26da71afdd11d30d63f6f812a0a
|
||||
|
109
src/tools/miri/src/alloc_bytes.rs
Normal file
109
src/tools/miri/src/alloc_bytes.rs
Normal file
@ -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
|
||||
}
|
||||
}
|
@ -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.
|
||||
|
@ -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 {
|
||||
|
@ -25,98 +25,98 @@ fn emulate_atomic_intrinsic(
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -26,7 +26,7 @@ fn call_intrinsic(
|
||||
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 @@ fn call_intrinsic(
|
||||
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 @@ fn emulate_intrinsic_by_name(
|
||||
// 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 @@ fn emulate_intrinsic_by_name(
|
||||
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 @@ fn emulate_intrinsic_by_name(
|
||||
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,6 +263,113 @@ fn emulate_intrinsic_by_name(
|
||||
this.write_scalar(res, dest)?;
|
||||
}
|
||||
|
||||
"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()?;
|
||||
let res = match intrinsic_name {
|
||||
"minnumf32" => this.adjust_nan(a.min(b), &[a, b]),
|
||||
"maxnumf32" => this.adjust_nan(a.max(b), &[a, b]),
|
||||
"copysignf32" => a.copy_sign(b), // bitwise, no NaN adjustments
|
||||
_ => bug!(),
|
||||
};
|
||||
this.write_scalar(Scalar::from_f32(res), dest)?;
|
||||
}
|
||||
"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()?;
|
||||
let res = match intrinsic_name {
|
||||
"minnumf64" => this.adjust_nan(a.min(b), &[a, b]),
|
||||
"maxnumf64" => this.adjust_nan(a.max(b), &[a, b]),
|
||||
"copysignf64" => a.copy_sign(b), // bitwise, no NaN adjustments
|
||||
_ => bug!(),
|
||||
};
|
||||
this.write_scalar(Scalar::from_f64(res), dest)?;
|
||||
}
|
||||
|
||||
"fmaf32" => {
|
||||
let [a, b, c] = check_arg_count(args)?;
|
||||
let a = this.read_scalar(a)?.to_f32()?;
|
||||
let b = this.read_scalar(b)?.to_f32()?;
|
||||
let c = this.read_scalar(c)?.to_f32()?;
|
||||
// FIXME: Using host floats, to work around https://github.com/rust-lang/rustc_apfloat/issues/11
|
||||
let res = a.to_host().mul_add(b.to_host(), c.to_host()).to_soft();
|
||||
let res = this.adjust_nan(res, &[a, b, c]);
|
||||
this.write_scalar(res, dest)?;
|
||||
}
|
||||
"fmaf64" => {
|
||||
let [a, b, c] = check_arg_count(args)?;
|
||||
let a = this.read_scalar(a)?.to_f64()?;
|
||||
let b = this.read_scalar(b)?.to_f64()?;
|
||||
let c = this.read_scalar(c)?.to_f64()?;
|
||||
// FIXME: Using host floats, to work around https://github.com/rust-lang/rustc_apfloat/issues/11
|
||||
let res = a.to_host().mul_add(b.to_host(), c.to_host()).to_soft();
|
||||
let res = this.adjust_nan(res, &[a, b, c]);
|
||||
this.write_scalar(res, dest)?;
|
||||
}
|
||||
|
||||
"powf32" => {
|
||||
let [f1, f2] = check_arg_count(args)?;
|
||||
let f1 = this.read_scalar(f1)?.to_f32()?;
|
||||
let f2 = this.read_scalar(f2)?.to_f32()?;
|
||||
// Using host floats (but it's fine, this operation does not have guaranteed precision).
|
||||
let res = f1.to_host().powf(f2.to_host()).to_soft();
|
||||
let res = this.adjust_nan(res, &[f1, f2]);
|
||||
this.write_scalar(res, dest)?;
|
||||
}
|
||||
"powf64" => {
|
||||
let [f1, f2] = check_arg_count(args)?;
|
||||
let f1 = this.read_scalar(f1)?.to_f64()?;
|
||||
let f2 = this.read_scalar(f2)?.to_f64()?;
|
||||
// Using host floats (but it's fine, this operation does not have guaranteed precision).
|
||||
let res = f1.to_host().powf(f2.to_host()).to_soft();
|
||||
let res = this.adjust_nan(res, &[f1, f2]);
|
||||
this.write_scalar(res, dest)?;
|
||||
}
|
||||
|
||||
"powif32" => {
|
||||
let [f, i] = check_arg_count(args)?;
|
||||
let f = this.read_scalar(f)?.to_f32()?;
|
||||
let i = this.read_scalar(i)?.to_i32()?;
|
||||
// Using host floats (but it's fine, this operation does not have guaranteed precision).
|
||||
let res = f.to_host().powi(i).to_soft();
|
||||
let res = this.adjust_nan(res, &[f]);
|
||||
this.write_scalar(res, dest)?;
|
||||
}
|
||||
"powif64" => {
|
||||
let [f, i] = check_arg_count(args)?;
|
||||
let f = this.read_scalar(f)?.to_f64()?;
|
||||
let i = this.read_scalar(i)?.to_i32()?;
|
||||
// Using host floats (but it's fine, this operation does not have guaranteed precision).
|
||||
let res = f.to_host().powi(i).to_soft();
|
||||
let res = this.adjust_nan(res, &[f]);
|
||||
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"
|
||||
@ -307,102 +420,6 @@ fn emulate_intrinsic_by_name(
|
||||
this.write_immediate(*res, dest)?;
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
| "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()?;
|
||||
let res = match intrinsic_name {
|
||||
"minnumf32" => this.adjust_nan(a.min(b), &[a, b]),
|
||||
"maxnumf32" => this.adjust_nan(a.max(b), &[a, b]),
|
||||
"copysignf32" => a.copy_sign(b), // bitwise, no NaN adjustments
|
||||
_ => bug!(),
|
||||
};
|
||||
this.write_scalar(Scalar::from_f32(res), dest)?;
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
| "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()?;
|
||||
let res = match intrinsic_name {
|
||||
"minnumf64" => this.adjust_nan(a.min(b), &[a, b]),
|
||||
"maxnumf64" => this.adjust_nan(a.max(b), &[a, b]),
|
||||
"copysignf64" => a.copy_sign(b), // bitwise, no NaN adjustments
|
||||
_ => bug!(),
|
||||
};
|
||||
this.write_scalar(Scalar::from_f64(res), dest)?;
|
||||
}
|
||||
|
||||
"fmaf32" => {
|
||||
let [a, b, c] = check_arg_count(args)?;
|
||||
let a = this.read_scalar(a)?.to_f32()?;
|
||||
let b = this.read_scalar(b)?.to_f32()?;
|
||||
let c = this.read_scalar(c)?.to_f32()?;
|
||||
// FIXME: Using host floats, to work around https://github.com/rust-lang/rustc_apfloat/issues/11
|
||||
let res = a.to_host().mul_add(b.to_host(), c.to_host()).to_soft();
|
||||
let res = this.adjust_nan(res, &[a, b, c]);
|
||||
this.write_scalar(res, dest)?;
|
||||
}
|
||||
|
||||
"fmaf64" => {
|
||||
let [a, b, c] = check_arg_count(args)?;
|
||||
let a = this.read_scalar(a)?.to_f64()?;
|
||||
let b = this.read_scalar(b)?.to_f64()?;
|
||||
let c = this.read_scalar(c)?.to_f64()?;
|
||||
// FIXME: Using host floats, to work around https://github.com/rust-lang/rustc_apfloat/issues/11
|
||||
let res = a.to_host().mul_add(b.to_host(), c.to_host()).to_soft();
|
||||
let res = this.adjust_nan(res, &[a, b, c]);
|
||||
this.write_scalar(res, dest)?;
|
||||
}
|
||||
|
||||
"powf32" => {
|
||||
let [f1, f2] = check_arg_count(args)?;
|
||||
let f1 = this.read_scalar(f1)?.to_f32()?;
|
||||
let f2 = this.read_scalar(f2)?.to_f32()?;
|
||||
// Using host floats (but it's fine, this operation does not have guaranteed precision).
|
||||
let res = f1.to_host().powf(f2.to_host()).to_soft();
|
||||
let res = this.adjust_nan(res, &[f1, f2]);
|
||||
this.write_scalar(res, dest)?;
|
||||
}
|
||||
|
||||
"powf64" => {
|
||||
let [f1, f2] = check_arg_count(args)?;
|
||||
let f1 = this.read_scalar(f1)?.to_f64()?;
|
||||
let f2 = this.read_scalar(f2)?.to_f64()?;
|
||||
// Using host floats (but it's fine, this operation does not have guaranteed precision).
|
||||
let res = f1.to_host().powf(f2.to_host()).to_soft();
|
||||
let res = this.adjust_nan(res, &[f1, f2]);
|
||||
this.write_scalar(res, dest)?;
|
||||
}
|
||||
|
||||
"powif32" => {
|
||||
let [f, i] = check_arg_count(args)?;
|
||||
let f = this.read_scalar(f)?.to_f32()?;
|
||||
let i = this.read_scalar(i)?.to_i32()?;
|
||||
// Using host floats (but it's fine, this operation does not have guaranteed precision).
|
||||
let res = f.to_host().powi(i).to_soft();
|
||||
let res = this.adjust_nan(res, &[f]);
|
||||
this.write_scalar(res, dest)?;
|
||||
}
|
||||
|
||||
"powif64" => {
|
||||
let [f, i] = check_arg_count(args)?;
|
||||
let f = this.read_scalar(f)?.to_f64()?;
|
||||
let i = this.read_scalar(i)?.to_i32()?;
|
||||
// Using host floats (but it's fine, this operation does not have guaranteed precision).
|
||||
let res = f.to_host().powi(i).to_soft();
|
||||
let res = this.adjust_nan(res, &[f]);
|
||||
this.write_scalar(res, dest)?;
|
||||
}
|
||||
|
||||
"float_to_int_unchecked" => {
|
||||
let [val] = check_arg_count(args)?;
|
||||
let val = this.read_immediate(val)?;
|
||||
@ -429,6 +446,6 @@ fn emulate_intrinsic_by_name(
|
||||
_ => return Ok(EmulateItemResult::NotSupported),
|
||||
}
|
||||
|
||||
Ok(EmulateItemResult::NeedsJumping)
|
||||
Ok(EmulateItemResult::NeedsReturn)
|
||||
}
|
||||
}
|
||||
|
@ -746,7 +746,7 @@ enum Op {
|
||||
|
||||
_ => return Ok(EmulateItemResult::NotSupported),
|
||||
}
|
||||
Ok(EmulateItemResult::NeedsJumping)
|
||||
Ok(EmulateItemResult::NeedsReturn)
|
||||
}
|
||||
|
||||
fn fminmax_op(
|
||||
|
@ -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_driver;
|
||||
|
||||
mod alloc_addresses;
|
||||
mod alloc_bytes;
|
||||
mod borrow_tracker;
|
||||
mod clock;
|
||||
mod concurrency;
|
||||
@ -107,6 +109,7 @@
|
||||
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,
|
||||
};
|
||||
|
@ -30,14 +30,13 @@
|
||||
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.
|
||||
@ -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 @@ pub(crate) fn new(config: &MiriConfig, layout_cx: LayoutCx<'tcx, TyCtxt<'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 @@ fn adjust_allocation<'b>(
|
||||
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 @@ fn adjust_allocation<'b>(
|
||||
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,
|
||||
|
@ -122,7 +122,7 @@ fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
@ -87,7 +87,7 @@ fn emulate_allocator(
|
||||
}
|
||||
AllocatorKind::Default => {
|
||||
default(this)?;
|
||||
Ok(EmulateItemResult::NeedsJumping)
|
||||
Ok(EmulateItemResult::NeedsReturn)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -111,6 +111,32 @@ fn malloc(
|
||||
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 @@ fn realloc(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -55,6 +55,12 @@ pub fn init_extern_statics(this: &mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<
|
||||
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(
|
||||
@ -62,19 +68,13 @@ pub fn init_extern_statics(this: &mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<
|
||||
&["__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"
|
||||
|
@ -82,11 +82,15 @@ fn emulate_foreign_item(
|
||||
}
|
||||
|
||||
// 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 @@ fn is_dyn_sym(&self, name: &str) -> bool {
|
||||
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 @@ fn emulate_foreign_item_inner(
|
||||
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 @@ fn emulate_foreign_item_inner(
|
||||
// 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 @@ fn emulate_foreign_item_inner(
|
||||
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 @@ fn emulate_foreign_item_inner(
|
||||
"__rust_alloc" => return this.emulate_allocator(default),
|
||||
"miri_alloc" => {
|
||||
default(this)?;
|
||||
return Ok(EmulateItemResult::NeedsJumping);
|
||||
return Ok(EmulateItemResult::NeedsReturn);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
@ -539,7 +543,7 @@ fn emulate_foreign_item_inner(
|
||||
}
|
||||
"miri_dealloc" => {
|
||||
default(this)?;
|
||||
return Ok(EmulateItemResult::NeedsJumping);
|
||||
return Ok(EmulateItemResult::NeedsReturn);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
@ -947,6 +951,10 @@ fn emulate_foreign_item_inner(
|
||||
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 @@ fn emulate_foreign_item_inner(
|
||||
};
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
|
@ -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,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
src/tools/miri/src/shims/unix/android/foreign_items.rs
Normal file
32
src/tools/miri/src/shims/unix/android/foreign_items.rs
Normal file
@ -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
src/tools/miri/src/shims/unix/android/mod.rs
Normal file
1
src/tools/miri/src/shims/unix/android/mod.rs
Normal file
@ -0,0 +1 @@
|
||||
pub mod foreign_items;
|
@ -7,7 +7,6 @@
|
||||
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 @@ fn read<'tcx>(
|
||||
&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 @@ fn write<'tcx>(
|
||||
&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 @@ fn read<'tcx>(
|
||||
&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 @@ fn write<'tcx>(
|
||||
&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 @@ fn write<'tcx>(
|
||||
&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 @@ fn write<'tcx>(
|
||||
&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 @@ pub fn new<T: FileDescription>(fd: T) -> Self {
|
||||
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 @@ pub fn insert_fd_with_min_fd(&mut self, file_handle: FileDescriptor, min_fd: i32
|
||||
|
||||
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 @@ fn read(
|
||||
.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 @@ fn read(
|
||||
// `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 @@ fn write(
|
||||
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);
|
||||
|
||||
|
@ -3,13 +3,13 @@
|
||||
|
||||
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 @@ fn emulate_foreign_item_inner(
|
||||
|
||||
// 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 @@ fn emulate_foreign_item_inner(
|
||||
|
||||
"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 @@ fn emulate_foreign_item_inner(
|
||||
}
|
||||
}
|
||||
}
|
||||
"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 @@ fn emulate_foreign_item_inner(
|
||||
"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 @@ fn emulate_foreign_item_inner(
|
||||
"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 @@ fn emulate_foreign_item_inner(
|
||||
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 @@ fn emulate_foreign_item_inner(
|
||||
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 @@ fn emulate_foreign_item_inner(
|
||||
_ => {
|
||||
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 @@ fn emulate_foreign_item_inner(
|
||||
}
|
||||
};
|
||||
|
||||
Ok(EmulateItemResult::NeedsJumping)
|
||||
Ok(EmulateItemResult::NeedsReturn)
|
||||
}
|
||||
}
|
||||
|
@ -86,6 +86,6 @@ fn emulate_foreign_item_inner(
|
||||
|
||||
_ => return Ok(EmulateItemResult::NotSupported),
|
||||
}
|
||||
Ok(EmulateItemResult::NeedsJumping)
|
||||
Ok(EmulateItemResult::NeedsReturn)
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,6 @@
|
||||
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 @@ fn read<'tcx>(
|
||||
&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 @@ fn write<'tcx>(
|
||||
&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))
|
||||
|
@ -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 @@ fn write<'tcx>(
|
||||
&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),
|
||||
};
|
||||
|
@ -203,6 +203,6 @@ fn emulate_foreign_item_inner(
|
||||
_ => return Ok(EmulateItemResult::NotSupported),
|
||||
};
|
||||
|
||||
Ok(EmulateItemResult::NeedsJumping)
|
||||
Ok(EmulateItemResult::NeedsReturn)
|
||||
}
|
||||
}
|
||||
|
@ -177,6 +177,6 @@ fn emulate_foreign_item_inner(
|
||||
_ => return Ok(EmulateItemResult::NotSupported),
|
||||
};
|
||||
|
||||
Ok(EmulateItemResult::NeedsJumping)
|
||||
Ok(EmulateItemResult::NeedsReturn)
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,7 @@
|
||||
mod sync;
|
||||
mod thread;
|
||||
|
||||
mod android;
|
||||
mod freebsd;
|
||||
mod linux;
|
||||
mod macos;
|
||||
|
@ -45,6 +45,6 @@ fn emulate_foreign_item_inner(
|
||||
|
||||
_ => return Ok(EmulateItemResult::NotSupported),
|
||||
}
|
||||
Ok(EmulateItemResult::NeedsJumping)
|
||||
Ok(EmulateItemResult::NeedsReturn)
|
||||
}
|
||||
}
|
||||
|
40
src/tools/miri/src/shims/wasi/foreign_items.rs
Normal file
40
src/tools/miri/src/shims/wasi/foreign_items.rs
Normal file
@ -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
src/tools/miri/src/shims/wasi/mod.rs
Normal file
1
src/tools/miri/src/shims/wasi/mod.rs
Normal file
@ -0,0 +1 @@
|
||||
pub mod foreign_items;
|
@ -762,6 +762,6 @@ fn emulate_foreign_item_inner(
|
||||
_ => return Ok(EmulateItemResult::NotSupported),
|
||||
}
|
||||
|
||||
Ok(EmulateItemResult::NeedsJumping)
|
||||
Ok(EmulateItemResult::NeedsReturn)
|
||||
}
|
||||
}
|
||||
|
@ -127,7 +127,7 @@ fn emulate_x86_aesni_intrinsic(
|
||||
// with an external crate.
|
||||
_ => return Ok(EmulateItemResult::NotSupported),
|
||||
}
|
||||
Ok(EmulateItemResult::NeedsJumping)
|
||||
Ok(EmulateItemResult::NeedsReturn)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -344,6 +344,6 @@ fn emulate_x86_avx_intrinsic(
|
||||
}
|
||||
_ => return Ok(EmulateItemResult::NotSupported),
|
||||
}
|
||||
Ok(EmulateItemResult::NeedsJumping)
|
||||
Ok(EmulateItemResult::NeedsReturn)
|
||||
}
|
||||
}
|
||||
|
@ -81,7 +81,7 @@ fn emulate_x86_avx2_intrinsic(
|
||||
|
||||
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 @@ fn emulate_x86_avx2_intrinsic(
|
||||
}
|
||||
_ => return Ok(EmulateItemResult::NotSupported),
|
||||
}
|
||||
Ok(EmulateItemResult::NeedsJumping)
|
||||
Ok(EmulateItemResult::NeedsReturn)
|
||||
}
|
||||
}
|
||||
|
@ -144,7 +144,7 @@ fn emulate_x86_intrinsic(
|
||||
|
||||
_ => return Ok(EmulateItemResult::NotSupported),
|
||||
}
|
||||
Ok(EmulateItemResult::NeedsJumping)
|
||||
Ok(EmulateItemResult::NeedsReturn)
|
||||
}
|
||||
}
|
||||
|
||||
@ -200,7 +200,7 @@ fn cmp_from_imm<'tcx>(
|
||||
) -> 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}"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -212,6 +212,6 @@ fn emulate_x86_sse_intrinsic(
|
||||
}
|
||||
_ => return Ok(EmulateItemResult::NotSupported),
|
||||
}
|
||||
Ok(EmulateItemResult::NeedsJumping)
|
||||
Ok(EmulateItemResult::NeedsReturn)
|
||||
}
|
||||
}
|
||||
|
@ -388,6 +388,6 @@ fn emulate_x86_sse2_intrinsic(
|
||||
}
|
||||
_ => return Ok(EmulateItemResult::NotSupported),
|
||||
}
|
||||
Ok(EmulateItemResult::NeedsJumping)
|
||||
Ok(EmulateItemResult::NeedsReturn)
|
||||
}
|
||||
}
|
||||
|
@ -51,6 +51,6 @@ fn emulate_x86_sse3_intrinsic(
|
||||
}
|
||||
_ => return Ok(EmulateItemResult::NotSupported),
|
||||
}
|
||||
Ok(EmulateItemResult::NeedsJumping)
|
||||
Ok(EmulateItemResult::NeedsReturn)
|
||||
}
|
||||
}
|
||||
|
@ -176,6 +176,6 @@ fn emulate_x86_sse41_intrinsic(
|
||||
}
|
||||
_ => return Ok(EmulateItemResult::NotSupported),
|
||||
}
|
||||
Ok(EmulateItemResult::NeedsJumping)
|
||||
Ok(EmulateItemResult::NeedsReturn)
|
||||
}
|
||||
}
|
||||
|
@ -137,6 +137,6 @@ fn emulate_x86_ssse3_intrinsic(
|
||||
}
|
||||
_ => return Ok(EmulateItemResult::NotSupported),
|
||||
}
|
||||
Ok(EmulateItemResult::NeedsJumping)
|
||||
Ok(EmulateItemResult::NeedsReturn)
|
||||
}
|
||||
}
|
||||
|
@ -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"] }
|
||||
|
||||
|
@ -0,0 +1,15 @@
|
||||
//@ignore-target-windows: Windows does not support the standard C11 aligned_alloc.
|
||||
|
||||
fn main() {
|
||||
// libc doesn't have this function (https://github.com/rust-lang/libc/issues/3689),
|
||||
// so we declare it ourselves.
|
||||
extern "C" {
|
||||
fn aligned_alloc(alignment: libc::size_t, size: libc::size_t) -> *mut libc::c_void;
|
||||
}
|
||||
|
||||
// Make sure even zero-sized allocations need to be freed.
|
||||
|
||||
unsafe {
|
||||
aligned_alloc(2, 0); //~ERROR: memory leaked
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
error: memory leaked: ALLOC (C heap, size: 0, align: 2), allocated here:
|
||||
--> $DIR/aligned_alloc_size_zero_leak.rs:LL:CC
|
||||
|
|
||||
LL | aligned_alloc(2, 0);
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: BACKTRACE:
|
||||
= note: inside `main` at $DIR/aligned_alloc_size_zero_leak.rs:LL:CC
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||
|
||||
note: the evaluated program leaked memory, pass `-Zmiri-ignore-leaks` to disable this check
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
@ -0,0 +1,14 @@
|
||||
//@ignore-target-windows: No posix_memalign on Windows
|
||||
|
||||
use std::ptr;
|
||||
|
||||
fn main() {
|
||||
let mut ptr: *mut libc::c_void = ptr::null_mut();
|
||||
let align = 64;
|
||||
let size = 0;
|
||||
unsafe {
|
||||
let _ = libc::posix_memalign(&mut ptr, align, size);
|
||||
libc::free(ptr);
|
||||
libc::free(ptr); //~ERROR: dangling
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
error: Undefined Behavior: memory access failed: ALLOC has been freed, so this pointer is dangling
|
||||
--> $DIR/posix_memalign_size_zero_double_free.rs:LL:CC
|
||||
|
|
||||
LL | libc::free(ptr);
|
||||
| ^^^^^^^^^^^^^^^ memory access failed: ALLOC has been freed, so this pointer is dangling
|
||||
|
|
||||
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
|
||||
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
|
||||
help: ALLOC was allocated here:
|
||||
--> $DIR/posix_memalign_size_zero_double_free.rs:LL:CC
|
||||
|
|
||||
LL | let _ = libc::posix_memalign(&mut ptr, align, size);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
help: ALLOC was deallocated here:
|
||||
--> $DIR/posix_memalign_size_zero_double_free.rs:LL:CC
|
||||
|
|
||||
LL | libc::free(ptr);
|
||||
| ^^^^^^^^^^^^^^^
|
||||
= note: BACKTRACE (of the first span):
|
||||
= note: inside `main` at $DIR/posix_memalign_size_zero_double_free.rs:LL:CC
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
@ -0,0 +1,10 @@
|
||||
//@ignore-target-windows: No posix_memalign on Windows
|
||||
|
||||
use std::ptr;
|
||||
|
||||
fn main() {
|
||||
let mut ptr: *mut libc::c_void = ptr::null_mut();
|
||||
let align = 64;
|
||||
let size = 0;
|
||||
let _ = unsafe { libc::posix_memalign(&mut ptr, align, size) }; //~ERROR: memory leak
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
error: memory leaked: ALLOC (C heap, size: 0, align: 64), allocated here:
|
||||
--> $DIR/posix_memalign_size_zero_leak.rs:LL:CC
|
||||
|
|
||||
LL | let _ = unsafe { libc::posix_memalign(&mut ptr, align, size) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: BACKTRACE:
|
||||
= note: inside `main` at $DIR/posix_memalign_size_zero_leak.rs:LL:CC
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||
|
||||
note: the evaluated program leaked memory, pass `-Zmiri-ignore-leaks` to disable this check
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
@ -1,6 +1,5 @@
|
||||
//@ignore-target-windows: Windows does not have a global environ list that the program can access directly
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
|
||||
fn get_environ() -> *const *const u8 {
|
||||
extern "C" {
|
||||
static mut environ: *const *const u8;
|
||||
@ -8,14 +7,6 @@ fn get_environ() -> *const *const u8 {
|
||||
unsafe { environ }
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
fn get_environ() -> *const *const u8 {
|
||||
extern "C" {
|
||||
fn _NSGetEnviron() -> *mut *const *const u8;
|
||||
}
|
||||
unsafe { *_NSGetEnviron() }
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let pointer = get_environ();
|
||||
let _x = unsafe { *pointer };
|
||||
|
@ -148,54 +148,55 @@ fn test_calloc() {
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
fn test_memalign() {
|
||||
// A normal allocation.
|
||||
unsafe {
|
||||
let mut ptr: *mut libc::c_void = ptr::null_mut();
|
||||
let align = 8;
|
||||
let size = 64;
|
||||
assert_eq!(libc::posix_memalign(&mut ptr, align, size), 0);
|
||||
assert!(!ptr.is_null());
|
||||
assert!(ptr.is_aligned_to(align));
|
||||
ptr.cast::<u8>().write_bytes(1, size);
|
||||
libc::free(ptr);
|
||||
}
|
||||
for _ in 0..16 {
|
||||
// A normal allocation.
|
||||
unsafe {
|
||||
let mut ptr: *mut libc::c_void = ptr::null_mut();
|
||||
let align = 8;
|
||||
let size = 64;
|
||||
assert_eq!(libc::posix_memalign(&mut ptr, align, size), 0);
|
||||
assert!(!ptr.is_null());
|
||||
assert!(ptr.is_aligned_to(align));
|
||||
ptr.cast::<u8>().write_bytes(1, size);
|
||||
libc::free(ptr);
|
||||
}
|
||||
|
||||
// Align > size.
|
||||
unsafe {
|
||||
let mut ptr: *mut libc::c_void = ptr::null_mut();
|
||||
let align = 64;
|
||||
let size = 8;
|
||||
assert_eq!(libc::posix_memalign(&mut ptr, align, size), 0);
|
||||
assert!(!ptr.is_null());
|
||||
assert!(ptr.is_aligned_to(align));
|
||||
ptr.cast::<u8>().write_bytes(1, size);
|
||||
libc::free(ptr);
|
||||
}
|
||||
// Align > size.
|
||||
unsafe {
|
||||
let mut ptr: *mut libc::c_void = ptr::null_mut();
|
||||
let align = 64;
|
||||
let size = 8;
|
||||
assert_eq!(libc::posix_memalign(&mut ptr, align, size), 0);
|
||||
assert!(!ptr.is_null());
|
||||
assert!(ptr.is_aligned_to(align));
|
||||
ptr.cast::<u8>().write_bytes(1, size);
|
||||
libc::free(ptr);
|
||||
}
|
||||
|
||||
// Size not multiple of align
|
||||
unsafe {
|
||||
let mut ptr: *mut libc::c_void = ptr::null_mut();
|
||||
let align = 16;
|
||||
let size = 31;
|
||||
assert_eq!(libc::posix_memalign(&mut ptr, align, size), 0);
|
||||
assert!(!ptr.is_null());
|
||||
assert!(ptr.is_aligned_to(align));
|
||||
ptr.cast::<u8>().write_bytes(1, size);
|
||||
libc::free(ptr);
|
||||
}
|
||||
// Size not multiple of align
|
||||
unsafe {
|
||||
let mut ptr: *mut libc::c_void = ptr::null_mut();
|
||||
let align = 16;
|
||||
let size = 31;
|
||||
assert_eq!(libc::posix_memalign(&mut ptr, align, size), 0);
|
||||
assert!(!ptr.is_null());
|
||||
assert!(ptr.is_aligned_to(align));
|
||||
ptr.cast::<u8>().write_bytes(1, size);
|
||||
libc::free(ptr);
|
||||
}
|
||||
|
||||
// Size == 0
|
||||
unsafe {
|
||||
let mut ptr: *mut libc::c_void = ptr::null_mut();
|
||||
let align = 64;
|
||||
let size = 0;
|
||||
assert_eq!(libc::posix_memalign(&mut ptr, align, size), 0);
|
||||
// We are not required to return null if size == 0, but we currently do.
|
||||
// It's fine to remove this assert if we start returning non-null pointers.
|
||||
assert!(ptr.is_null());
|
||||
assert!(ptr.is_aligned_to(align));
|
||||
// Regardless of what we return, it must be `free`able.
|
||||
libc::free(ptr);
|
||||
// Size == 0
|
||||
unsafe {
|
||||
let mut ptr: *mut libc::c_void = ptr::null_mut();
|
||||
let align = 64;
|
||||
let size = 0;
|
||||
assert_eq!(libc::posix_memalign(&mut ptr, align, size), 0);
|
||||
// Non-null pointer is returned if size == 0.
|
||||
// (This is not a guarantee, it just reflects our current behavior.)
|
||||
assert!(!ptr.is_null());
|
||||
assert!(ptr.is_aligned_to(align));
|
||||
libc::free(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
// Non-power of 2 align
|
||||
@ -227,7 +228,8 @@ fn test_memalign() {
|
||||
target_os = "windows",
|
||||
target_os = "macos",
|
||||
target_os = "illumos",
|
||||
target_os = "solaris"
|
||||
target_os = "solaris",
|
||||
target_os = "wasi",
|
||||
)))]
|
||||
fn test_reallocarray() {
|
||||
unsafe {
|
||||
@ -241,6 +243,44 @@ fn test_reallocarray() {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
fn test_aligned_alloc() {
|
||||
// libc doesn't have this function (https://github.com/rust-lang/libc/issues/3689),
|
||||
// so we declare it ourselves.
|
||||
extern "C" {
|
||||
fn aligned_alloc(alignment: libc::size_t, size: libc::size_t) -> *mut libc::c_void;
|
||||
}
|
||||
// size not a multiple of the alignment
|
||||
unsafe {
|
||||
let p = aligned_alloc(16, 3);
|
||||
assert_eq!(p, ptr::null_mut());
|
||||
}
|
||||
|
||||
// alignment not power of 2
|
||||
unsafe {
|
||||
let p = aligned_alloc(63, 8);
|
||||
assert_eq!(p, ptr::null_mut());
|
||||
}
|
||||
|
||||
// repeated tests on correct alignment/size
|
||||
for _ in 0..16 {
|
||||
// alignment 1, size 4 should succeed and actually must align to 4 (because C says so...)
|
||||
unsafe {
|
||||
let p = aligned_alloc(1, 4);
|
||||
assert!(!p.is_null());
|
||||
assert!(p.is_aligned_to(4));
|
||||
libc::free(p);
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let p = aligned_alloc(64, 64);
|
||||
assert!(!p.is_null());
|
||||
assert!(p.is_aligned_to(64));
|
||||
libc::free(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
test_malloc();
|
||||
test_calloc();
|
||||
@ -250,9 +290,12 @@ fn main() {
|
||||
target_os = "windows",
|
||||
target_os = "macos",
|
||||
target_os = "illumos",
|
||||
target_os = "solaris"
|
||||
target_os = "solaris",
|
||||
target_os = "wasi",
|
||||
)))]
|
||||
test_reallocarray();
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
test_aligned_alloc();
|
||||
|
||||
test_memcpy();
|
||||
test_strcpy();
|
||||
|
@ -10,9 +10,11 @@
|
||||
fn test_thread_local_errno() {
|
||||
#[cfg(any(target_os = "illumos", target_os = "solaris"))]
|
||||
use libc::___errno as __errno_location;
|
||||
#[cfg(target_os = "android")]
|
||||
use libc::__errno as __errno_location;
|
||||
#[cfg(target_os = "linux")]
|
||||
use libc::__errno_location;
|
||||
#[cfg(any(target_os = "macos", target_os = "freebsd"))]
|
||||
#[cfg(any(target_os = "freebsd", target_os = "macos"))]
|
||||
use libc::__error as __errno_location;
|
||||
|
||||
unsafe {
|
||||
@ -28,6 +30,21 @@ fn test_thread_local_errno() {
|
||||
}
|
||||
}
|
||||
|
||||
fn test_environ() {
|
||||
// Just a smoke test for now, checking that the extern static exists.
|
||||
extern "C" {
|
||||
static mut environ: *const *const libc::c_char;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let mut e = environ;
|
||||
// Iterate to the end.
|
||||
while !(*e).is_null() {
|
||||
e = e.add(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn test_sigrt() {
|
||||
let min = libc::SIGRTMIN();
|
||||
@ -60,6 +77,7 @@ fn test_dlsym() {
|
||||
|
||||
fn main() {
|
||||
test_thread_local_errno();
|
||||
test_environ();
|
||||
|
||||
test_dlsym();
|
||||
|
||||
|
3
src/tools/miri/tests/pass/empty_main.rs
Normal file
3
src/tools/miri/tests/pass/empty_main.rs
Normal file
@ -0,0 +1,3 @@
|
||||
// This may look trivial, but a bunch of code runs in std before
|
||||
// `main` is called, so we are ensuring that that all works.
|
||||
fn main() {}
|
@ -1,5 +1,6 @@
|
||||
#![feature(stmt_expr_attributes)]
|
||||
#![feature(float_gamma)]
|
||||
#![feature(core_intrinsics)]
|
||||
#![allow(arithmetic_overflow)]
|
||||
|
||||
use std::fmt::Debug;
|
||||
@ -22,6 +23,8 @@ fn main() {
|
||||
rounding();
|
||||
mul_add();
|
||||
libm();
|
||||
test_fast();
|
||||
test_algebraic();
|
||||
}
|
||||
|
||||
// Helper function to avoid promotion so that this tests "run-time" casts, not CTFE.
|
||||
@ -751,3 +754,67 @@ fn ldexp(a: f64, b: i32) -> f64 {
|
||||
assert_approx_eq!(val, (2.0 * f64::consts::PI.sqrt()).ln());
|
||||
assert_eq!(sign, -1);
|
||||
}
|
||||
|
||||
fn test_fast() {
|
||||
use std::intrinsics::{fadd_fast, fdiv_fast, fmul_fast, frem_fast, fsub_fast};
|
||||
|
||||
#[inline(never)]
|
||||
pub fn test_operations_f64(a: f64, b: f64) {
|
||||
// make sure they all map to the correct operation
|
||||
unsafe {
|
||||
assert_eq!(fadd_fast(a, b), a + b);
|
||||
assert_eq!(fsub_fast(a, b), a - b);
|
||||
assert_eq!(fmul_fast(a, b), a * b);
|
||||
assert_eq!(fdiv_fast(a, b), a / b);
|
||||
assert_eq!(frem_fast(a, b), a % b);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub fn test_operations_f32(a: f32, b: f32) {
|
||||
// make sure they all map to the correct operation
|
||||
unsafe {
|
||||
assert_eq!(fadd_fast(a, b), a + b);
|
||||
assert_eq!(fsub_fast(a, b), a - b);
|
||||
assert_eq!(fmul_fast(a, b), a * b);
|
||||
assert_eq!(fdiv_fast(a, b), a / b);
|
||||
assert_eq!(frem_fast(a, b), a % b);
|
||||
}
|
||||
}
|
||||
|
||||
test_operations_f64(1., 2.);
|
||||
test_operations_f64(10., 5.);
|
||||
test_operations_f32(11., 2.);
|
||||
test_operations_f32(10., 15.);
|
||||
}
|
||||
|
||||
fn test_algebraic() {
|
||||
use std::intrinsics::{
|
||||
fadd_algebraic, fdiv_algebraic, fmul_algebraic, frem_algebraic, fsub_algebraic,
|
||||
};
|
||||
|
||||
#[inline(never)]
|
||||
pub fn test_operations_f64(a: f64, b: f64) {
|
||||
// make sure they all map to the correct operation
|
||||
assert_eq!(fadd_algebraic(a, b), a + b);
|
||||
assert_eq!(fsub_algebraic(a, b), a - b);
|
||||
assert_eq!(fmul_algebraic(a, b), a * b);
|
||||
assert_eq!(fdiv_algebraic(a, b), a / b);
|
||||
assert_eq!(frem_algebraic(a, b), a % b);
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub fn test_operations_f32(a: f32, b: f32) {
|
||||
// make sure they all map to the correct operation
|
||||
assert_eq!(fadd_algebraic(a, b), a + b);
|
||||
assert_eq!(fsub_algebraic(a, b), a - b);
|
||||
assert_eq!(fmul_algebraic(a, b), a * b);
|
||||
assert_eq!(fdiv_algebraic(a, b), a / b);
|
||||
assert_eq!(frem_algebraic(a, b), a % b);
|
||||
}
|
||||
|
||||
test_operations_f64(1., 2.);
|
||||
test_operations_f64(10., 5.);
|
||||
test_operations_f32(11., 2.);
|
||||
test_operations_f32(10., 15.);
|
||||
}
|
||||
|
@ -1,34 +0,0 @@
|
||||
#![feature(core_intrinsics)]
|
||||
|
||||
use std::intrinsics::{fadd_fast, fdiv_fast, fmul_fast, frem_fast, fsub_fast};
|
||||
|
||||
#[inline(never)]
|
||||
pub fn test_operations_f64(a: f64, b: f64) {
|
||||
// make sure they all map to the correct operation
|
||||
unsafe {
|
||||
assert_eq!(fadd_fast(a, b), a + b);
|
||||
assert_eq!(fsub_fast(a, b), a - b);
|
||||
assert_eq!(fmul_fast(a, b), a * b);
|
||||
assert_eq!(fdiv_fast(a, b), a / b);
|
||||
assert_eq!(frem_fast(a, b), a % b);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub fn test_operations_f32(a: f32, b: f32) {
|
||||
// make sure they all map to the correct operation
|
||||
unsafe {
|
||||
assert_eq!(fadd_fast(a, b), a + b);
|
||||
assert_eq!(fsub_fast(a, b), a - b);
|
||||
assert_eq!(fmul_fast(a, b), a * b);
|
||||
assert_eq!(fdiv_fast(a, b), a / b);
|
||||
assert_eq!(frem_fast(a, b), a % b);
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
test_operations_f64(1., 2.);
|
||||
test_operations_f64(10., 5.);
|
||||
test_operations_f32(11., 2.);
|
||||
test_operations_f32(10., 15.);
|
||||
}
|
@ -1,7 +1,64 @@
|
||||
//@compile-flags: -Coverflow-checks=off
|
||||
#![allow(arithmetic_overflow)]
|
||||
|
||||
fn basic() {
|
||||
fn ret() -> i64 {
|
||||
1
|
||||
}
|
||||
|
||||
fn neg() -> i64 {
|
||||
-1
|
||||
}
|
||||
|
||||
fn add() -> i64 {
|
||||
1 + 2
|
||||
}
|
||||
|
||||
fn indirect_add() -> i64 {
|
||||
let x = 1;
|
||||
let y = 2;
|
||||
x + y
|
||||
}
|
||||
|
||||
fn arith() -> i32 {
|
||||
3 * 3 + 4 * 4
|
||||
}
|
||||
|
||||
fn match_int() -> i16 {
|
||||
let n = 2;
|
||||
match n {
|
||||
0 => 0,
|
||||
1 => 10,
|
||||
2 => 20,
|
||||
3 => 30,
|
||||
_ => 100,
|
||||
}
|
||||
}
|
||||
|
||||
fn match_int_range() -> i64 {
|
||||
let n = 42;
|
||||
match n {
|
||||
0..=9 => 0,
|
||||
10..=19 => 1,
|
||||
20..=29 => 2,
|
||||
30..=39 => 3,
|
||||
40..=42 => 4,
|
||||
_ => 5,
|
||||
}
|
||||
}
|
||||
|
||||
assert_eq!(ret(), 1);
|
||||
assert_eq!(neg(), -1);
|
||||
assert_eq!(add(), 3);
|
||||
assert_eq!(indirect_add(), 3);
|
||||
assert_eq!(arith(), 5 * 5);
|
||||
assert_eq!(match_int(), 20);
|
||||
assert_eq!(match_int_range(), 4);
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
basic();
|
||||
|
||||
// This tests that we do (not) do sign extension properly when loading integers
|
||||
assert_eq!(u32::MAX as i64, 4294967295);
|
||||
assert_eq!(i32::MIN as i64, -2147483648);
|
||||
@ -152,6 +209,10 @@ pub fn main() {
|
||||
|
||||
assert_eq!(5i32.overflowing_mul(2), (10, false));
|
||||
assert_eq!(1_000_000_000i32.overflowing_mul(10), (1410065408, true));
|
||||
assert_eq!(i64::MIN.overflowing_mul(-1), (i64::MIN, true));
|
||||
assert_eq!(i32::MIN.overflowing_mul(-1), (i32::MIN, true));
|
||||
assert_eq!(i16::MIN.overflowing_mul(-1), (i16::MIN, true));
|
||||
assert_eq!(i8::MIN.overflowing_mul(-1), (i8::MIN, true));
|
||||
|
||||
assert_eq!(5i32.overflowing_div(2), (2, false));
|
||||
assert_eq!(i32::MIN.overflowing_div(-1), (i32::MIN, true));
|
||||
|
@ -1,58 +0,0 @@
|
||||
fn ret() -> i64 {
|
||||
1
|
||||
}
|
||||
|
||||
fn neg() -> i64 {
|
||||
-1
|
||||
}
|
||||
|
||||
fn add() -> i64 {
|
||||
1 + 2
|
||||
}
|
||||
|
||||
fn indirect_add() -> i64 {
|
||||
let x = 1;
|
||||
let y = 2;
|
||||
x + y
|
||||
}
|
||||
|
||||
fn arith() -> i32 {
|
||||
3 * 3 + 4 * 4
|
||||
}
|
||||
|
||||
fn match_int() -> i16 {
|
||||
let n = 2;
|
||||
match n {
|
||||
0 => 0,
|
||||
1 => 10,
|
||||
2 => 20,
|
||||
3 => 30,
|
||||
_ => 100,
|
||||
}
|
||||
}
|
||||
|
||||
fn match_int_range() -> i64 {
|
||||
let n = 42;
|
||||
match n {
|
||||
0..=9 => 0,
|
||||
10..=19 => 1,
|
||||
20..=29 => 2,
|
||||
30..=39 => 3,
|
||||
40..=42 => 4,
|
||||
_ => 5,
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
assert_eq!(ret(), 1);
|
||||
assert_eq!(neg(), -1);
|
||||
assert_eq!(add(), 3);
|
||||
assert_eq!(indirect_add(), 3);
|
||||
assert_eq!(arith(), 5 * 5);
|
||||
assert_eq!(match_int(), 20);
|
||||
assert_eq!(match_int_range(), 4);
|
||||
assert_eq!(i64::MIN.overflowing_mul(-1), (i64::MIN, true));
|
||||
assert_eq!(i32::MIN.overflowing_mul(-1), (i32::MIN, true));
|
||||
assert_eq!(i16::MIN.overflowing_mul(-1), (i16::MIN, true));
|
||||
assert_eq!(i8::MIN.overflowing_mul(-1), (i8::MIN, true));
|
||||
}
|
96
src/tools/miri/tests/pass/panic/unwind_dwarf.rs
Normal file
96
src/tools/miri/tests/pass/panic/unwind_dwarf.rs
Normal file
@ -0,0 +1,96 @@
|
||||
//@ignore-target-windows: Windows uses a different unwinding mechanism
|
||||
#![feature(core_intrinsics, panic_unwind, rustc_attrs)]
|
||||
#![allow(internal_features)]
|
||||
|
||||
//! Unwinding using `_Unwind_RaiseException`
|
||||
|
||||
extern crate unwind as uw;
|
||||
|
||||
use std::any::Any;
|
||||
use std::ptr;
|
||||
|
||||
#[repr(C)]
|
||||
struct Exception {
|
||||
_uwe: uw::_Unwind_Exception,
|
||||
cause: Box<dyn Any + Send>,
|
||||
}
|
||||
|
||||
pub fn panic(data: Box<dyn Any + Send>) -> u32 {
|
||||
extern "C" fn exception_cleanup(
|
||||
_unwind_code: uw::_Unwind_Reason_Code,
|
||||
_exception: *mut uw::_Unwind_Exception,
|
||||
) {
|
||||
std::process::abort();
|
||||
}
|
||||
|
||||
let exception = Box::new(Exception {
|
||||
_uwe: uw::_Unwind_Exception {
|
||||
exception_class: miri_exception_class(),
|
||||
exception_cleanup: Some(exception_cleanup),
|
||||
private: [core::ptr::null(); uw::unwinder_private_data_size],
|
||||
},
|
||||
cause: data,
|
||||
});
|
||||
let exception_param = Box::into_raw(exception) as *mut uw::_Unwind_Exception;
|
||||
return unsafe { uw::_Unwind_RaiseException(exception_param) as u32 };
|
||||
}
|
||||
|
||||
pub unsafe fn rust_panic_cleanup(ptr: *mut u8) -> Box<dyn Any + Send> {
|
||||
let exception = ptr as *mut uw::_Unwind_Exception;
|
||||
if (*exception).exception_class != miri_exception_class() {
|
||||
std::process::abort();
|
||||
}
|
||||
|
||||
let exception = exception.cast::<Exception>();
|
||||
|
||||
let exception = Box::from_raw(exception as *mut Exception);
|
||||
exception.cause
|
||||
}
|
||||
|
||||
fn miri_exception_class() -> uw::_Unwind_Exception_Class {
|
||||
// M O Z \0 M I R I -- vendor, language
|
||||
// (Miri's own exception class is just used for testing)
|
||||
0x4d4f5a_00_4d495249
|
||||
}
|
||||
|
||||
pub fn catch_unwind<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>> {
|
||||
struct Data<F, R> {
|
||||
f: Option<F>,
|
||||
r: Option<R>,
|
||||
p: Option<Box<dyn Any + Send>>,
|
||||
}
|
||||
|
||||
let mut data = Data { f: Some(f), r: None, p: None };
|
||||
|
||||
let data_ptr = ptr::addr_of_mut!(data) as *mut u8;
|
||||
unsafe {
|
||||
return if std::intrinsics::catch_unwind(do_call::<F, R>, data_ptr, do_catch::<F, R>) == 0 {
|
||||
Ok(data.r.take().unwrap())
|
||||
} else {
|
||||
Err(data.p.take().unwrap())
|
||||
};
|
||||
}
|
||||
|
||||
fn do_call<F: FnOnce() -> R, R>(data: *mut u8) {
|
||||
unsafe {
|
||||
let data = &mut *data.cast::<Data<F, R>>();
|
||||
let f = data.f.take().unwrap();
|
||||
data.r = Some(f());
|
||||
}
|
||||
}
|
||||
|
||||
#[rustc_nounwind]
|
||||
fn do_catch<F: FnOnce() -> R, R>(data: *mut u8, payload: *mut u8) {
|
||||
unsafe {
|
||||
let obj = rust_panic_cleanup(payload);
|
||||
(*data.cast::<Data<F, R>>()).p = Some(obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
assert_eq!(
|
||||
catch_unwind(|| panic(Box::new(42))).unwrap_err().downcast::<i32>().unwrap(),
|
||||
Box::new(42)
|
||||
);
|
||||
}
|
8
src/tools/miri/tests/pass/shims/env/home.rs
vendored
8
src/tools/miri/tests/pass/shims/env/home.rs
vendored
@ -2,8 +2,12 @@
|
||||
use std::env;
|
||||
|
||||
fn main() {
|
||||
env::remove_var("HOME"); // make sure we enter the interesting codepath
|
||||
env::remove_var("USERPROFILE"); // Windows also looks as this env var
|
||||
// Remove the env vars to hit the underlying shim -- except
|
||||
// on android where the env var is all we have.
|
||||
#[cfg(not(target_os = "android"))]
|
||||
env::remove_var("HOME");
|
||||
env::remove_var("USERPROFILE");
|
||||
|
||||
#[allow(deprecated)]
|
||||
env::home_dir().unwrap();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user