Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,15 +162,22 @@ endian-sensitive code.
### Controlling target features

Controlling target features works similar to regular rustc invocations:
`RUSTFLAGS="-Ctarget-feature=+avx512f" cargo miri test` runs the tests with AVX512 enabled. (Miri
only supports very few AVX512 intrinsics at the moment.) `-Ctarget-cpu` also works. If target
features are also relevant for doctests, you have to also set `RUSTDOCFLAGS`.
`RUSTFLAGS="-Ctarget-feature=+avx2" cargo miri test` runs the tests with AVX2 enabled.
`-Ctarget-cpu` also works. If target features are also relevant for doctests, you have to also set
`RUSTDOCFLAGS`.

Unlike regular rustc, this flag has *two* effects: it builds the code with that target feature
available (which affects `cfg(target_feature)`), and it tells Miri to consider the "virtual CPU"
that the interpreted program runs on as having the feature available (meaning the code is allowed to
invoke the corresponding intrinsics).

By default, Miri will interpret `-Ctarget-feature` and `-Ctarget-cpu` the same way rustc does.
However, not all these target features are actually supported by Miri. You can instruct Miri to
automatically disable unsupported target features by setting
`MIRI_DISABLE_UNSUPPORTED_TARGET_FEATURES=1`. (You have to `cargo clean` when changing this setting
as it affects dependencies as well.) Note that this can disable even some target features that are
usually available by default on the current target, which could cause surprising behavior.

### Nextest integration

Miri can be combined with [`cargo-nextest`](https://nexte.st):
Expand Down
2 changes: 2 additions & 0 deletions cargo-miri/src/setup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,8 @@ pub fn setup(
// `RUSTC_WRAPPER` to the empty string overwrites `build.rustc-wrapper` set via
// `bootstrap.toml`.
command.env("RUSTC_WRAPPER", "");
// Don't let `MIRI_DISABLE_UNSUPPORTED_TARGET_FEATURES` affect the sysroot build.
command.env_remove("MIRI_DISABLE_UNSUPPORTED_TARGET_FEATURES");

if show_setup {
// Forward output. Even make it verbose, if requested.
Expand Down
6 changes: 4 additions & 2 deletions src/bin/miri.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,9 +216,11 @@ impl CodegenBackend for MiriCodegenBackend {
}

fn target_config(&self, sess: &Session) -> TargetConfig {
let native_target_config = self.native.target_config(sess);
let mut internal_target_features = self.native.target_config(sess).internal_target_features;
miri::remove_unsupported_target_features(sess, &mut internal_target_features);

TargetConfig {
internal_target_features: native_target_config.internal_target_features,
internal_target_features,

// The basic types and ABI always work.
has_reliable_f16: true,
Expand Down
22 changes: 15 additions & 7 deletions src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,10 @@ pub enum TerminationInfo {
extra: Option<&'static str>,
retag_explain: bool,
},
UnsupportedForeignItem(String),
UnsupportedForeignItem {
msg: String,
help: Option<String>,
},
}

pub struct RacingOp {
Expand Down Expand Up @@ -99,7 +102,7 @@ impl fmt::Display for TerminationInfo {
op2.action,
op2.thread_info
),
UnsupportedForeignItem(msg) => write!(f, "{msg}"),
UnsupportedForeignItem { msg, .. } => write!(f, "{msg}"),
}
}
}
Expand Down Expand Up @@ -254,8 +257,9 @@ pub fn report_result<'tcx>(
&Exit { code, leak_check } => return Some((code, leak_check)),
Abort(_) => Some("abnormal termination"),
Interrupted => None,
UnsupportedInIsolation(_) | Int2PtrWithStrictProvenance | UnsupportedForeignItem(_) =>
Some("unsupported operation"),
UnsupportedInIsolation(_)
| Int2PtrWithStrictProvenance
| UnsupportedForeignItem { .. } => Some("unsupported operation"),
StackedBorrowsUb { .. } | TreeBorrowsUb { .. } | DataRace { .. } =>
Some("Undefined Behavior"),
GenmcMoot => {
Expand Down Expand Up @@ -301,10 +305,14 @@ pub fn report_result<'tcx>(
note!("set `MIRIFLAGS=-Zmiri-disable-isolation` to disable isolation;"),
note!("or set `MIRIFLAGS=-Zmiri-isolation-error=warn` to make Miri return an error code from isolated operations (if supported for that operation) and continue with a warning"),
],
UnsupportedForeignItem(_) => {
vec![
UnsupportedForeignItem { help, .. } => {
let mut helps = vec![
note!("this means the program tried to do something Miri does not support; it does not indicate a bug in the program"),
]
];
if let Some(help) = help {
helps.push(note!("{help}"))
}
helps
}
StackedBorrowsUb { help, history, .. } => {
labels.extend(help.clone());
Expand Down
4 changes: 4 additions & 0 deletions src/eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,9 @@ pub struct MiriConfig {
pub short_fd_operations: bool,
/// A list of crates that are considered user-relevant.
pub user_relevant_crates: Vec<String>,
/// Whether target features that Miri does not support should be removed from
/// `cfg(target_feature)`.
pub disable_unsupported_target_features: Option<bool>,
}

impl Default for MiriConfig {
Expand Down Expand Up @@ -212,6 +215,7 @@ impl Default for MiriConfig {
float_rounding_error: FloatRoundingErrorMode::Random,
short_fd_operations: true,
user_relevant_crates: vec![],
disable_unsupported_target_features: None,
}
}
}
Expand Down
61 changes: 56 additions & 5 deletions src/intrinsics/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,14 @@ mod math;
mod simd;
mod x86;

use std::mem;

#[rustfmt::skip] // prevent `use` reordering
use rand::RngExt;
use rustc_abi::{Endian, Size};
use rustc_data_structures::unord::UnordSet;
use rustc_middle::{mir, ty};
use rustc_session::Session;
use rustc_span::{Symbol, sym};
use rustc_target::spec::Arch;

Expand All @@ -34,6 +38,39 @@ where
)
}

fn should_remove_unsupported_target_features() -> bool {
// We cannot use `MiriConfig` to decide this as that is not available in dependencies.
std::env::var("MIRI_DISABLE_UNSUPPORTED_TARGET_FEATURES").is_ok_and(|s| s == "1")
}

pub fn remove_unsupported_target_features(sess: &Session, features: &mut UnordSet<Symbol>) {
if !should_remove_unsupported_target_features() {
return;
}

// While an allowlist may feel more natural here, we don't have a very good idea of what exactly
// we do support. So instead we list things that we know we don't support.
let is_unsupported = match sess.target.arch {
Arch::X86_64 | Arch::X86 =>
|feature: Symbol| {
// AVX512 is only partially supported.
feature.as_str().starts_with("avx512")
},
Arch::AArch64 =>
|feature| {
// We don't fully support `neon` but we cannot remove that as it is ABI-required.
matches!(feature, sym::aes)
},
// Fallback: we don't know so we just don't touch anything.
_ => |_feature| true,
};

// Apply the filter. This is currently annoying to do because there's no `retain` on `UnordSet`.
let mut features_vec = mem::take(features).into_sorted_stable_ord();
features_vec.retain(|f| !is_unsupported(*f));
*features = UnordSet::from_iter(features_vec);
}

impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn call_intrinsic(
Expand Down Expand Up @@ -245,12 +282,26 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
EmulateItemResult::NeedsReturn
};

// The rest either implements the logic, or falls back to `lookup_exported_symbol`.
// The rest either implements the logic, or emits an error. These are not real symbols so we
// do not invoke `lookup_exported_symbol`.
res.jump_to_next_block(this, &dest.clone().into(), ret, None, |this| {
throw_machine_stop!(TerminationInfo::UnsupportedForeignItem(format!(
"can't call LLVM intrinsic `{link_name}` on architecture `{arch}`",
arch = this.tcx.sess.target.arch,
)));
// Only show a hint on targets where we have decent support.
let help = if matches!(this.tcx.sess.target.arch, Arch::X86_64 | Arch::X86 | Arch::AArch64) {
Some(if should_remove_unsupported_target_features() {
format!("This likely means that Miri did not remove enough target features from `cfg(target_feature)`; \
please file an issue at <https://github.com/rust-lang/miri/issues>.")
} else {
format!("Try setting `MIRI_DISABLE_UNSUPPORTED_TARGET_FEATURES=1` (needs full rebuild via `cargo clean`) \
to steer this code towards a fallback path that may work in Miri.")
})
} else { None };
throw_machine_stop!(TerminationInfo::UnsupportedForeignItem {
msg: format!(
"can't call LLVM intrinsic `{link_name}` on architecture `{arch}`",
arch = this.tcx.sess.target.arch,
),
help
});
})
}
}
3 changes: 2 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
// Configure clippy and other lints
#![allow(
clippy::collapsible_if,
clippy::collapsible_match,
clippy::enum_variant_names,
clippy::manual_map,
clippy::neg_cmp_op_on_partial_ord,
Expand Down Expand Up @@ -156,7 +157,7 @@ pub use crate::diagnostics::{
};
pub use crate::eval::{MiriConfig, MiriEntryFnType, create_ecx, entry_fn, eval_entry};
pub use crate::helpers::{EvalContextExt as _, ToU64 as _, ToUsize as _};
pub use crate::intrinsics::EvalContextExt as _;
pub use crate::intrinsics::{EvalContextExt as _, remove_unsupported_target_features};
pub use crate::machine::{
AlignmentCheck, AllocExtra, BacktraceStyle, DynMachineCallback, FloatRoundingErrorMode,
FrameExtra, IsolatedOp, MachineCallback, MemoryKind, MiriInterpCx, MiriInterpCxExt,
Expand Down
11 changes: 7 additions & 4 deletions src/shims/foreign_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
return interp_ok(Some(body));
}

throw_machine_stop!(TerminationInfo::UnsupportedForeignItem(format!(
"can't call foreign function `{link_name}` on OS `{os}`",
os = this.tcx.sess.target.os,
)));
throw_machine_stop!(TerminationInfo::UnsupportedForeignItem {
msg: format!(
"can't call foreign function `{link_name}` on OS `{os}`",
os = this.tcx.sess.target.os,
),
help: None
});
})
}

Expand Down
9 changes: 9 additions & 0 deletions tests/pass/no-unsupported-target-features.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
//@only-target: aarch64
//@rustc-env: MIRI_DISABLE_UNSUPPORTED_TARGET_FEATURES=1

use std::arch::is_aarch64_feature_detected;

fn main() {
assert!(!cfg!(target_feature = "aes"));
assert!(!is_aarch64_feature_detected!("aes"));
}
Loading