Skip to content

Commit d9284af

Browse files
committed
Auto merge of #127726 - RalfJung:miri-sync, r=RalfJung
Miri subtree update r? `@ghost`
2 parents f8e4ac0 + e90f047 commit d9284af

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+1531
-277
lines changed

src/tools/miri/cargo-miri/src/phases.rs

+18-8
Original file line numberDiff line numberDiff line change
@@ -104,9 +104,17 @@ pub fn phase_cargo_miri(mut args: impl Iterator<Item = String>) {
104104
miri_for_host()
105105
)
106106
});
107-
let host = &rustc_version.host;
108-
let target = get_arg_flag_value("--target");
109-
let target = target.as_ref().unwrap_or(host);
107+
let mut targets = get_arg_flag_values("--target").collect::<Vec<_>>();
108+
// If `targets` is empty, we need to add a `--target $HOST` flag ourselves, and also ensure
109+
// that the host target is indeed setup.
110+
let target_flag = if targets.is_empty() {
111+
let host = &rustc_version.host;
112+
targets.push(host.clone());
113+
Some(host)
114+
} else {
115+
// We don't need to add a `--target` flag, we just forward the user's flags.
116+
None
117+
};
110118

111119
// If cleaning the target directory & sysroot cache,
112120
// delete them then exit. There is no reason to setup a new
@@ -118,8 +126,11 @@ pub fn phase_cargo_miri(mut args: impl Iterator<Item = String>) {
118126
return;
119127
}
120128

121-
// We always setup.
122-
let miri_sysroot = setup(&subcommand, target, &rustc_version, verbose, quiet);
129+
for target in &targets {
130+
// We always setup.
131+
setup(&subcommand, target.as_str(), &rustc_version, verbose, quiet);
132+
}
133+
let miri_sysroot = get_sysroot_dir();
123134

124135
// Invoke actual cargo for the job, but with different flags.
125136
// We re-use `cargo test` and `cargo run`, which makes target and binary handling very easy but
@@ -155,10 +166,9 @@ pub fn phase_cargo_miri(mut args: impl Iterator<Item = String>) {
155166
// This is needed to make the `target.runner` settings do something,
156167
// and it later helps us detect which crates are proc-macro/build-script
157168
// (host crates) and which crates are needed for the program itself.
158-
if get_arg_flag_value("--target").is_none() {
159-
// No target given. Explicitly pick the host.
169+
if let Some(target_flag) = target_flag {
160170
cmd.arg("--target");
161-
cmd.arg(host);
171+
cmd.arg(target_flag);
162172
}
163173

164174
// Set ourselves as runner for al binaries invoked by cargo.

src/tools/miri/ci/ci.sh

+3-1
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,11 @@ function run_tests {
4141
if [ -n "${TEST_TARGET-}" ]; then
4242
begingroup "Testing foreign architecture $TEST_TARGET"
4343
TARGET_FLAG="--target $TEST_TARGET"
44+
MULTI_TARGET_FLAG=""
4445
else
4546
begingroup "Testing host architecture"
4647
TARGET_FLAG=""
48+
MULTI_TARGET_FLAG="--multi-target"
4749
fi
4850

4951
## ui test suite
@@ -93,7 +95,7 @@ function run_tests {
9395
echo 'build.rustc-wrapper = "thisdoesnotexist"' > .cargo/config.toml
9496
fi
9597
# Run the actual test
96-
time ${PYTHON} test-cargo-miri/run-test.py $TARGET_FLAG
98+
time ${PYTHON} test-cargo-miri/run-test.py $TARGET_FLAG $MULTI_TARGET_FLAG
9799
# Clean up
98100
unset RUSTC MIRI
99101
rm -rf .cargo

src/tools/miri/rust-version

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
66b4f0021bfb11a8c20d084c99a40f4a78ce1d38
1+
99b7134389e9766462601a2fc4013840b9d31745

src/tools/miri/src/bin/miri.rs

+3
Original file line numberDiff line numberDiff line change
@@ -592,6 +592,9 @@ fn main() {
592592
let num_cpus = param
593593
.parse::<u32>()
594594
.unwrap_or_else(|err| show_error!("-Zmiri-num-cpus requires a `u32`: {}", err));
595+
if !(1..=miri::MAX_CPUS).contains(&usize::try_from(num_cpus).unwrap()) {
596+
show_error!("-Zmiri-num-cpus must be in the range 1..={}", miri::MAX_CPUS);
597+
}
595598
miri_config.num_cpus = num_cpus;
596599
} else if let Some(param) = arg.strip_prefix("-Zmiri-force-page-size=") {
597600
let page_size = param.parse::<u64>().unwrap_or_else(|err| {

src/tools/miri/src/borrow_tracker/stacked_borrows/stack.rs

+10-2
Original file line numberDiff line numberDiff line change
@@ -136,8 +136,16 @@ impl StackCache {
136136

137137
impl PartialEq for Stack {
138138
fn eq(&self, other: &Self) -> bool {
139-
// All the semantics of Stack are in self.borrows, everything else is caching
140-
self.borrows == other.borrows
139+
let Stack {
140+
borrows,
141+
unknown_bottom,
142+
// The cache is ignored for comparison.
143+
#[cfg(feature = "stack-cache")]
144+
cache: _,
145+
#[cfg(feature = "stack-cache")]
146+
unique_range: _,
147+
} = self;
148+
*borrows == other.borrows && *unknown_bottom == other.unknown_bottom
141149
}
142150
}
143151

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
use rustc_middle::ty::layout::LayoutOf;
2+
use rustc_target::abi::Endian;
3+
4+
use crate::*;
5+
6+
/// The maximum number of CPUs supported by miri.
7+
///
8+
/// This value is compatible with the libc `CPU_SETSIZE` constant and corresponds to the number
9+
/// of CPUs that a `cpu_set_t` can contain.
10+
///
11+
/// Real machines can have more CPUs than this number, and there exist APIs to set their affinity,
12+
/// but this is not currently supported by miri.
13+
pub const MAX_CPUS: usize = 1024;
14+
15+
/// A thread's CPU affinity mask determines the set of CPUs on which it is eligible to run.
16+
// the actual representation depends on the target's endianness and pointer width.
17+
// See CpuAffinityMask::set for details
18+
#[derive(Clone)]
19+
pub(crate) struct CpuAffinityMask([u8; Self::CPU_MASK_BYTES]);
20+
21+
impl CpuAffinityMask {
22+
pub(crate) const CPU_MASK_BYTES: usize = MAX_CPUS / 8;
23+
24+
pub fn new<'tcx>(cx: &impl LayoutOf<'tcx>, cpu_count: u32) -> Self {
25+
let mut this = Self([0; Self::CPU_MASK_BYTES]);
26+
27+
// the default affinity mask includes only the available CPUs
28+
for i in 0..cpu_count as usize {
29+
this.set(cx, i);
30+
}
31+
32+
this
33+
}
34+
35+
pub fn chunk_size<'tcx>(cx: &impl LayoutOf<'tcx>) -> u64 {
36+
// The actual representation of the CpuAffinityMask is [c_ulong; _].
37+
let ulong = helpers::path_ty_layout(cx, &["core", "ffi", "c_ulong"]);
38+
ulong.size.bytes()
39+
}
40+
41+
fn set<'tcx>(&mut self, cx: &impl LayoutOf<'tcx>, cpu: usize) {
42+
// we silently ignore CPUs that are out of bounds. This matches the behavior of
43+
// `sched_setaffinity` with a mask that specifies more than `CPU_SETSIZE` CPUs.
44+
if cpu >= MAX_CPUS {
45+
return;
46+
}
47+
48+
// The actual representation of the CpuAffinityMask is [c_ulong; _].
49+
// Within the array elements, we need to use the endianness of the target.
50+
let target = &cx.tcx().sess.target;
51+
match Self::chunk_size(cx) {
52+
4 => {
53+
let start = cpu / 32 * 4; // first byte of the correct u32
54+
let chunk = self.0[start..].first_chunk_mut::<4>().unwrap();
55+
let offset = cpu % 32;
56+
*chunk = match target.options.endian {
57+
Endian::Little => (u32::from_le_bytes(*chunk) | 1 << offset).to_le_bytes(),
58+
Endian::Big => (u32::from_be_bytes(*chunk) | 1 << offset).to_be_bytes(),
59+
};
60+
}
61+
8 => {
62+
let start = cpu / 64 * 8; // first byte of the correct u64
63+
let chunk = self.0[start..].first_chunk_mut::<8>().unwrap();
64+
let offset = cpu % 64;
65+
*chunk = match target.options.endian {
66+
Endian::Little => (u64::from_le_bytes(*chunk) | 1 << offset).to_le_bytes(),
67+
Endian::Big => (u64::from_be_bytes(*chunk) | 1 << offset).to_be_bytes(),
68+
};
69+
}
70+
other => bug!("chunk size not supported: {other}"),
71+
};
72+
}
73+
74+
pub fn as_slice(&self) -> &[u8] {
75+
self.0.as_slice()
76+
}
77+
78+
pub fn from_array<'tcx>(
79+
cx: &impl LayoutOf<'tcx>,
80+
cpu_count: u32,
81+
bytes: [u8; Self::CPU_MASK_BYTES],
82+
) -> Option<Self> {
83+
// mask by what CPUs are actually available
84+
let default = Self::new(cx, cpu_count);
85+
let masked = std::array::from_fn(|i| bytes[i] & default.0[i]);
86+
87+
// at least one thread must be set for the input to be valid
88+
masked.iter().any(|b| *b != 0).then_some(Self(masked))
89+
}
90+
}

src/tools/miri/src/concurrency/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
pub mod cpu_affinity;
12
pub mod data_race;
23
pub mod init_once;
34
mod range_object_map;

src/tools/miri/src/concurrency/sync.rs

+15-6
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ pub(super) trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
269269
let this = self.eval_context_mut();
270270
if this.mutex_is_locked(mutex) {
271271
assert_ne!(this.mutex_get_owner(mutex), this.active_thread());
272-
this.mutex_enqueue_and_block(mutex, retval, dest);
272+
this.mutex_enqueue_and_block(mutex, Some((retval, dest)));
273273
} else {
274274
// We can have it right now!
275275
this.mutex_lock(mutex);
@@ -390,9 +390,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
390390
}
391391

392392
/// Put the thread into the queue waiting for the mutex.
393-
/// Once the Mutex becomes available, `retval` will be written to `dest`.
393+
///
394+
/// Once the Mutex becomes available and if it exists, `retval_dest.0` will
395+
/// be written to `retval_dest.1`.
394396
#[inline]
395-
fn mutex_enqueue_and_block(&mut self, id: MutexId, retval: Scalar, dest: MPlaceTy<'tcx>) {
397+
fn mutex_enqueue_and_block(
398+
&mut self,
399+
id: MutexId,
400+
retval_dest: Option<(Scalar, MPlaceTy<'tcx>)>,
401+
) {
396402
let this = self.eval_context_mut();
397403
assert!(this.mutex_is_locked(id), "queing on unlocked mutex");
398404
let thread = this.active_thread();
@@ -403,13 +409,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
403409
callback!(
404410
@capture<'tcx> {
405411
id: MutexId,
406-
retval: Scalar,
407-
dest: MPlaceTy<'tcx>,
412+
retval_dest: Option<(Scalar, MPlaceTy<'tcx>)>,
408413
}
409414
@unblock = |this| {
410415
assert!(!this.mutex_is_locked(id));
411416
this.mutex_lock(id);
412-
this.write_scalar(retval, &dest)?;
417+
418+
if let Some((retval, dest)) = retval_dest {
419+
this.write_scalar(retval, &dest)?;
420+
}
421+
413422
Ok(())
414423
}
415424
),

src/tools/miri/src/concurrency/thread.rs

+5
Original file line numberDiff line numberDiff line change
@@ -936,6 +936,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
936936
// After this all accesses will be treated as occurring in the new thread.
937937
let old_thread_id = this.machine.threads.set_active_thread_id(new_thread_id);
938938

939+
// The child inherits its parent's cpu affinity.
940+
if let Some(cpuset) = this.machine.thread_cpu_affinity.get(&old_thread_id).cloned() {
941+
this.machine.thread_cpu_affinity.insert(new_thread_id, cpuset);
942+
}
943+
939944
// Perform the function pointer load in the new thread frame.
940945
let instance = this.get_ptr_fn(start_routine)?.as_instance()?;
941946

src/tools/miri/src/eval.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,8 @@ pub fn create_ecx<'tcx>(
282282
})?;
283283

284284
// Make sure we have MIR. We check MIR for some stable monomorphic function in libcore.
285-
let sentinel = ecx.try_resolve_path(&["core", "ascii", "escape_default"], Namespace::ValueNS);
285+
let sentinel =
286+
helpers::try_resolve_path(tcx, &["core", "ascii", "escape_default"], Namespace::ValueNS);
286287
if !matches!(sentinel, Some(s) if tcx.is_mir_available(s.def.def_id())) {
287288
tcx.dcx().fatal(
288289
"the current sysroot was built without `-Zalways-encode-mir`, or libcore seems missing. \

src/tools/miri/src/helpers.rs

+33-22
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
1818
use rustc_middle::middle::dependency_format::Linkage;
1919
use rustc_middle::middle::exported_symbols::ExportedSymbol;
2020
use rustc_middle::mir;
21+
use rustc_middle::ty::layout::MaybeResult;
2122
use rustc_middle::ty::{
2223
self,
2324
layout::{LayoutOf, TyAndLayout},
@@ -159,6 +160,35 @@ fn try_resolve_did(tcx: TyCtxt<'_>, path: &[&str], namespace: Option<Namespace>)
159160
None
160161
}
161162

163+
/// Gets an instance for a path; fails gracefully if the path does not exist.
164+
pub fn try_resolve_path<'tcx>(
165+
tcx: TyCtxt<'tcx>,
166+
path: &[&str],
167+
namespace: Namespace,
168+
) -> Option<ty::Instance<'tcx>> {
169+
let did = try_resolve_did(tcx, path, Some(namespace))?;
170+
Some(ty::Instance::mono(tcx, did))
171+
}
172+
173+
/// Gets an instance for a path.
174+
#[track_caller]
175+
pub fn resolve_path<'tcx>(
176+
tcx: TyCtxt<'tcx>,
177+
path: &[&str],
178+
namespace: Namespace,
179+
) -> ty::Instance<'tcx> {
180+
try_resolve_path(tcx, path, namespace)
181+
.unwrap_or_else(|| panic!("failed to find required Rust item: {path:?}"))
182+
}
183+
184+
/// Gets the layout of a type at a path.
185+
#[track_caller]
186+
pub fn path_ty_layout<'tcx>(cx: &impl LayoutOf<'tcx>, path: &[&str]) -> TyAndLayout<'tcx> {
187+
let ty =
188+
resolve_path(cx.tcx(), path, Namespace::TypeNS).ty(cx.tcx(), ty::ParamEnv::reveal_all());
189+
cx.layout_of(ty).to_result().ok().unwrap()
190+
}
191+
162192
/// Call `f` for each exported symbol.
163193
pub fn iter_exported_symbols<'tcx>(
164194
tcx: TyCtxt<'tcx>,
@@ -259,23 +289,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
259289
try_resolve_did(*self.eval_context_ref().tcx, path, None).is_some()
260290
}
261291

262-
/// Gets an instance for a path; fails gracefully if the path does not exist.
263-
fn try_resolve_path(&self, path: &[&str], namespace: Namespace) -> Option<ty::Instance<'tcx>> {
264-
let tcx = self.eval_context_ref().tcx.tcx;
265-
let did = try_resolve_did(tcx, path, Some(namespace))?;
266-
Some(ty::Instance::mono(tcx, did))
267-
}
268-
269-
/// Gets an instance for a path.
270-
fn resolve_path(&self, path: &[&str], namespace: Namespace) -> ty::Instance<'tcx> {
271-
self.try_resolve_path(path, namespace)
272-
.unwrap_or_else(|| panic!("failed to find required Rust item: {path:?}"))
273-
}
274-
275292
/// Evaluates the scalar at the specified path.
276293
fn eval_path(&self, path: &[&str]) -> OpTy<'tcx> {
277294
let this = self.eval_context_ref();
278-
let instance = this.resolve_path(path, Namespace::ValueNS);
295+
let instance = resolve_path(*this.tcx, path, Namespace::ValueNS);
279296
// We don't give a span -- this isn't actually used directly by the program anyway.
280297
let const_val = this.eval_global(instance).unwrap_or_else(|err| {
281298
panic!("failed to evaluate required Rust item: {path:?}\n{err:?}")
@@ -344,19 +361,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
344361
"`libc` crate is not reliably available on Windows targets; Miri should not use it there"
345362
);
346363
}
347-
let ty = this
348-
.resolve_path(&["libc", name], Namespace::TypeNS)
349-
.ty(*this.tcx, ty::ParamEnv::reveal_all());
350-
this.layout_of(ty).unwrap()
364+
path_ty_layout(this, &["libc", name])
351365
}
352366

353367
/// Helper function to get the `TyAndLayout` of a `windows` type
354368
fn windows_ty_layout(&self, name: &str) -> TyAndLayout<'tcx> {
355369
let this = self.eval_context_ref();
356-
let ty = this
357-
.resolve_path(&["std", "sys", "pal", "windows", "c", name], Namespace::TypeNS)
358-
.ty(*this.tcx, ty::ParamEnv::reveal_all());
359-
this.layout_of(ty).unwrap()
370+
path_ty_layout(this, &["std", "sys", "pal", "windows", "c", name])
360371
}
361372

362373
/// Project to the given *named* field (which must be a struct or union type).

src/tools/miri/src/intrinsics/mod.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -392,10 +392,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
392392
bug!("float_finite: non-float input type {}", x.layout.ty)
393393
};
394394
Ok(match fty {
395-
FloatTy::F16 => unimplemented!("f16_f128"),
395+
FloatTy::F16 => x.to_scalar().to_f16()?.is_finite(),
396396
FloatTy::F32 => x.to_scalar().to_f32()?.is_finite(),
397397
FloatTy::F64 => x.to_scalar().to_f64()?.is_finite(),
398-
FloatTy::F128 => unimplemented!("f16_f128"),
398+
FloatTy::F128 => x.to_scalar().to_f128()?.is_finite(),
399399
})
400400
};
401401
match (float_finite(&a)?, float_finite(&b)?) {

src/tools/miri/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ pub use crate::borrow_tracker::{
129129
};
130130
pub use crate::clock::{Clock, Instant};
131131
pub use crate::concurrency::{
132+
cpu_affinity::MAX_CPUS,
132133
data_race::{AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, EvalContextExt as _},
133134
init_once::{EvalContextExt as _, InitOnceId},
134135
sync::{CondvarId, EvalContextExt as _, MutexId, RwLockId, SynchronizationObjects},

0 commit comments

Comments
 (0)