Skip to content

Commit fe5d1a9

Browse files
author
Ellen Arteca
committed
Merge branch 'master' into int-function-args-returns
2 parents f0072e7 + 530abac commit fe5d1a9

File tree

123 files changed

+918
-833
lines changed

Some content is hidden

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

123 files changed

+918
-833
lines changed

README.md

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,9 @@ to Miri failing to detect cases of undefined behavior in a program.
385385
happening and where in your code would be a good place to look for it.
386386
Specifying this argument multiple times does not overwrite the previous
387387
values, instead it appends its values to the list. Listing a tag multiple times has no effect.
388+
* `-Zmiri-track-weak-memory-loads` shows a backtrace when weak memory emulation returns an outdated
389+
value from a load. This can help diagnose problems that disappear under
390+
`-Zmiri-disable-weak-memory-emulation`.
388391

389392
[function ABI]: https://doc.rust-lang.org/reference/items/functions.html#extern-function-qualifier
390393

@@ -414,10 +417,9 @@ Moreover, Miri recognizes some environment variables:
414417
checkout. Note that changing files in that directory does not automatically
415418
trigger a re-build of the standard library; you have to clear the Miri build
416419
cache manually (on Linux, `rm -rf ~/.cache/miri`).
417-
* `MIRI_SYSROOT` (recognized by `cargo miri` and the test suite) indicates the
418-
sysroot to use. Only set this if you do not want to use the automatically
419-
created sysroot. (The `miri` driver sysroot is controlled via the `--sysroot`
420-
flag instead.)
420+
* `MIRI_SYSROOT` (recognized by `cargo miri` and the Miri driver) indicates the sysroot to use. When
421+
using `cargo miri`, only set this if you do not want to use the automatically created sysroot. For
422+
directly invoking the Miri driver, this variable (or a `--sysroot` flag) is mandatory.
421423
* `MIRI_TEST_TARGET` (recognized by the test suite and the `./miri` script) indicates which target
422424
architecture to test against. `miri` and `cargo miri` accept the `--target` flag for the same
423425
purpose.
@@ -453,6 +455,8 @@ binaries, and as such worth documenting:
453455
crate currently being compiled.
454456
* `MIRI_VERBOSE` when set to any value tells the various `cargo-miri` phases to
455457
perform verbose logging.
458+
* `MIRI_HOST_SYSROOT` is set by bootstrap to tell `cargo-miri` which sysroot to use for *host*
459+
operations.
456460

457461
[testing-miri]: CONTRIBUTING.md#testing-the-miri-driver
458462

cargo-miri/bin.rs

Lines changed: 224 additions & 195 deletions
Large diffs are not rendered by default.

ci.sh

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ function run_tests {
2121
echo "Testing host architecture"
2222
fi
2323

24+
## ui test suite
2425
./miri test --locked
2526
if [ -z "${MIRI_TEST_TARGET+exists}" ]; then
2627
# Only for host architecture: tests with optimizations (`-O` is what cargo passes, but crank MIR
@@ -30,17 +31,28 @@ function run_tests {
3031
MIRIFLAGS="-O -Zmir-opt-level=4" MIRI_SKIP_UI_CHECKS=1 ./miri test --locked -- tests/{pass,panic}
3132
fi
3233

34+
## test-cargo-miri
3335
# On Windows, there is always "python", not "python3" or "python2".
3436
if command -v python3 > /dev/null; then
3537
PYTHON=python3
3638
else
3739
PYTHON=python
3840
fi
39-
40-
# "miri test" has built the sysroot for us, now this should pass without
41-
# any interactive questions.
41+
# Some environment setup that attempts to confuse the heck out of cargo-miri.
42+
if [ "$HOST_TARGET" = x86_64-unknown-linux-gnu ]; then
43+
# These act up on Windows (`which miri` produces a filename that does not exist?!?),
44+
# so let's do this only on Linux. Also makes sure things work without these set.
45+
export RUSTC=$(which rustc)
46+
export MIRI=$(which miri)
47+
fi
48+
mkdir -p .cargo
49+
echo 'build.rustc-wrapper = "thisdoesnotexist"' > .cargo/config.toml
50+
# Run the actual test
4251
${PYTHON} test-cargo-miri/run-test.py
4352
echo
53+
# Clean up
54+
unset RUSTC MIRI
55+
rm -rf .cargo
4456

4557
# Ensure that our benchmarks all work, on the host at least.
4658
if [ -z "${MIRI_TEST_TARGET+exists}" ]; then

miri

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -131,10 +131,11 @@ export RUSTFLAGS="-C link-args=-Wl,-rpath,$LIBDIR $RUSTFLAGS"
131131

132132
# Build a sysroot and set MIRI_SYSROOT to use it. Arguments are passed to `cargo miri setup`.
133133
build_sysroot() {
134-
# Build once, for the user to see.
135-
$CARGO run $CARGO_EXTRA_FLAGS --manifest-path "$MIRIDIR"/cargo-miri/Cargo.toml -- miri setup "$@"
136-
# Call again, to just set env var.
137-
export MIRI_SYSROOT="$($CARGO run $CARGO_EXTRA_FLAGS --manifest-path "$MIRIDIR"/cargo-miri/Cargo.toml -q -- miri setup --print-sysroot "$@")"
134+
if ! MIRI_SYSROOT="$($CARGO run $CARGO_EXTRA_FLAGS --manifest-path "$MIRIDIR"/cargo-miri/Cargo.toml -q -- miri setup --print-sysroot "$@")"; then
135+
echo "'cargo miri setup' failed"
136+
exit 1
137+
fi
138+
export MIRI_SYSROOT
138139
}
139140

140141
# Prepare and set MIRI_SYSROOT. Respects `MIRI_TEST_TARGET` and takes into account
@@ -204,7 +205,7 @@ run)
204205
$CARGO build $CARGO_EXTRA_FLAGS --manifest-path "$MIRIDIR"/Cargo.toml
205206
find_sysroot
206207
# Then run the actual command.
207-
exec $CARGO run $CARGO_EXTRA_FLAGS --manifest-path "$MIRIDIR"/Cargo.toml -- --sysroot "$MIRI_SYSROOT" $MIRIFLAGS "$@"
208+
exec $CARGO run $CARGO_EXTRA_FLAGS --manifest-path "$MIRIDIR"/Cargo.toml -- $MIRIFLAGS "$@"
208209
;;
209210
fmt)
210211
find "$MIRIDIR" -not \( -name target -prune \) -name '*.rs' \

rust-version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
a7468c60f8dbf5feb23ad840b174d7e57113a846
1+
c11207ec89b856164bba03b8ecfe07b0b234ed21

src/bin/miri.rs

Lines changed: 37 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ use rustc_middle::{
2727
},
2828
ty::{query::ExternProviders, TyCtxt},
2929
};
30-
use rustc_session::{search_paths::PathKind, CtfeBacktrace};
30+
use rustc_session::{config::CrateType, search_paths::PathKind, CtfeBacktrace};
3131

3232
use miri::{BacktraceStyle, ProvenanceMode};
3333

@@ -60,6 +60,10 @@ impl rustc_driver::Callbacks for MiriCompilerCalls {
6060

6161
queries.global_ctxt().unwrap().peek_mut().enter(|tcx| {
6262
init_late_loggers(tcx);
63+
if !tcx.sess.crate_types().contains(&CrateType::Executable) {
64+
tcx.sess.fatal("miri only makes sense on bin crates");
65+
}
66+
6367
let (entry_def_id, entry_type) = if let Some(entry_def) = tcx.entry_fn(()) {
6468
entry_def
6569
} else {
@@ -204,9 +208,9 @@ fn init_late_loggers(tcx: TyCtxt<'_>) {
204208
}
205209
}
206210

207-
/// Returns the "default sysroot" that Miri will use if no `--sysroot` flag is set.
211+
/// Returns the "default sysroot" that Miri will use for host things if no `--sysroot` flag is set.
208212
/// Should be a compile-time constant.
209-
fn compile_time_sysroot() -> Option<String> {
213+
fn host_sysroot() -> Option<String> {
210214
if option_env!("RUSTC_STAGE").is_some() {
211215
// This is being built as part of rustc, and gets shipped with rustup.
212216
// We can rely on the sysroot computation in librustc_session.
@@ -227,7 +231,7 @@ fn compile_time_sysroot() -> Option<String> {
227231
if toolchain_runtime != toolchain {
228232
show_error(format!(
229233
"This Miri got built with local toolchain `{toolchain}`, but now is being run under a different toolchain. \n\
230-
Make sure to run Miri in the toolchain it got built with, e.g. via `cargo +{toolchain} miri`."
234+
Make sure to run Miri in the toolchain it got built with, e.g. via `cargo +{toolchain} miri`."
231235
));
232236
}
233237
}
@@ -246,25 +250,42 @@ fn compile_time_sysroot() -> Option<String> {
246250
/// Execute a compiler with the given CLI arguments and callbacks.
247251
fn run_compiler(
248252
mut args: Vec<String>,
253+
target_crate: bool,
249254
callbacks: &mut (dyn rustc_driver::Callbacks + Send),
250-
insert_default_args: bool,
251255
) -> ! {
252256
// Make sure we use the right default sysroot. The default sysroot is wrong,
253257
// because `get_or_default_sysroot` in `librustc_session` bases that on `current_exe`.
254258
//
255-
// Make sure we always call `compile_time_sysroot` as that also does some sanity-checks
256-
// of the environment we were built in.
257-
// FIXME: Ideally we'd turn a bad build env into a compile-time error via CTFE or so.
258-
if let Some(sysroot) = compile_time_sysroot() {
259-
let sysroot_flag = "--sysroot";
260-
if !args.iter().any(|e| e == sysroot_flag) {
259+
// Make sure we always call `host_sysroot` as that also does some sanity-checks
260+
// of the environment we were built in and whether it matches what we are running in.
261+
let host_default_sysroot = host_sysroot();
262+
// Now see if we even need to set something.
263+
let sysroot_flag = "--sysroot";
264+
if !args.iter().any(|e| e == sysroot_flag) {
265+
// No sysroot was set, let's see if we have a custom default we want to configure.
266+
let default_sysroot = if target_crate {
267+
// Using the built-in default here would be plain wrong, so we *require*
268+
// the env var to make sure things make sense.
269+
Some(env::var("MIRI_SYSROOT").unwrap_or_else(|_| {
270+
show_error(format!(
271+
"Miri was invoked in 'target' mode without `MIRI_SYSROOT` or `--sysroot` being set"
272+
))
273+
}))
274+
} else {
275+
host_default_sysroot
276+
};
277+
if let Some(sysroot) = default_sysroot {
261278
// We need to overwrite the default that librustc_session would compute.
262279
args.push(sysroot_flag.to_owned());
263280
args.push(sysroot);
264281
}
265282
}
266283

267-
if insert_default_args {
284+
// Don't insert `MIRI_DEFAULT_ARGS`, in particular, `--cfg=miri`, if we are building
285+
// a "host" crate. That may cause procedural macros (and probably build scripts) to
286+
// depend on Miri-only symbols, such as `miri_resolve_frame`:
287+
// https://github.com/rust-lang/miri/issues/1760
288+
if target_crate {
268289
// Some options have different defaults in Miri than in plain rustc; apply those by making
269290
// them the first arguments after the binary name (but later arguments can overwrite them).
270291
args.splice(1..1, miri::MIRI_DEFAULT_ARGS.iter().map(ToString::to_string));
@@ -302,13 +323,8 @@ fn main() {
302323
// We cannot use `rustc_driver::main` as we need to adjust the CLI arguments.
303324
run_compiler(
304325
env::args().collect(),
326+
target_crate,
305327
&mut MiriBeRustCompilerCalls { target_crate },
306-
// Don't insert `MIRI_DEFAULT_ARGS`, in particular, `--cfg=miri`, if we are building
307-
// a "host" crate. That may cause procedural macros (and probably build scripts) to
308-
// depend on Miri-only symbols, such as `miri_resolve_frame`:
309-
// https://github.com/rust-lang/miri/issues/1760
310-
#[rustfmt::skip]
311-
/* insert_default_args: */ target_crate,
312328
)
313329
}
314330

@@ -358,6 +374,8 @@ fn main() {
358374
miri_config.isolated_op = miri::IsolatedOp::Allow;
359375
} else if arg == "-Zmiri-disable-weak-memory-emulation" {
360376
miri_config.weak_memory_emulation = false;
377+
} else if arg == "-Zmiri-track-weak-memory-loads" {
378+
miri_config.track_outdated_loads = true;
361379
} else if let Some(param) = arg.strip_prefix("-Zmiri-isolation-error=") {
362380
if matches!(isolation_enabled, Some(false)) {
363381
panic!("-Zmiri-isolation-error cannot be used along with -Zmiri-disable-isolation");
@@ -513,9 +531,5 @@ fn main() {
513531

514532
debug!("rustc arguments: {:?}", rustc_args);
515533
debug!("crate arguments: {:?}", miri_config.args);
516-
run_compiler(
517-
rustc_args,
518-
&mut MiriCompilerCalls { miri_config },
519-
/* insert_default_args: */ true,
520-
)
534+
run_compiler(rustc_args, /* target_crate: */ true, &mut MiriCompilerCalls { miri_config })
521535
}

src/concurrency/data_race.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1187,12 +1187,15 @@ pub struct GlobalState {
11871187

11881188
/// The timestamp of last SC write performed by each thread
11891189
last_sc_write: RefCell<VClock>,
1190+
1191+
/// Track when an outdated (weak memory) load happens.
1192+
pub track_outdated_loads: bool,
11901193
}
11911194

11921195
impl GlobalState {
11931196
/// Create a new global state, setup with just thread-id=0
11941197
/// advanced to timestamp = 1.
1195-
pub fn new() -> Self {
1198+
pub fn new(config: &MiriConfig) -> Self {
11961199
let mut global_state = GlobalState {
11971200
multi_threaded: Cell::new(false),
11981201
ongoing_action_data_race_free: Cell::new(false),
@@ -1203,6 +1206,7 @@ impl GlobalState {
12031206
terminated_threads: RefCell::new(FxHashMap::default()),
12041207
last_sc_fence: RefCell::new(VClock::default()),
12051208
last_sc_write: RefCell::new(VClock::default()),
1209+
track_outdated_loads: config.track_outdated_loads,
12061210
};
12071211

12081212
// Setup the main-thread since it is not explicitly created:

src/concurrency/weak_memory.rs

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -82,10 +82,7 @@ use rustc_const_eval::interpret::{
8282
};
8383
use rustc_data_structures::fx::FxHashMap;
8484

85-
use crate::{
86-
AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, Provenance, ThreadManager, VClock, VTimestamp,
87-
VectorIdx,
88-
};
85+
use crate::*;
8986

9087
use super::{
9188
data_race::{GlobalState as DataRaceState, ThreadClockSet},
@@ -113,6 +110,13 @@ pub(super) struct StoreBuffer {
113110
buffer: VecDeque<StoreElement>,
114111
}
115112

113+
/// Whether a load returned the latest value or not.
114+
#[derive(PartialEq, Eq)]
115+
enum LoadRecency {
116+
Latest,
117+
Outdated,
118+
}
119+
116120
#[derive(Debug, Clone, PartialEq, Eq)]
117121
struct StoreElement {
118122
/// The identifier of the vector index, corresponding to a thread
@@ -254,11 +258,11 @@ impl<'mir, 'tcx: 'mir> StoreBuffer {
254258
is_seqcst: bool,
255259
rng: &mut (impl rand::Rng + ?Sized),
256260
validate: impl FnOnce() -> InterpResult<'tcx>,
257-
) -> InterpResult<'tcx, ScalarMaybeUninit<Provenance>> {
261+
) -> InterpResult<'tcx, (ScalarMaybeUninit<Provenance>, LoadRecency)> {
258262
// Having a live borrow to store_buffer while calling validate_atomic_load is fine
259263
// because the race detector doesn't touch store_buffer
260264

261-
let store_elem = {
265+
let (store_elem, recency) = {
262266
// The `clocks` we got here must be dropped before calling validate_atomic_load
263267
// as the race detector will update it
264268
let (.., clocks) = global.current_thread_state(thread_mgr);
@@ -274,7 +278,7 @@ impl<'mir, 'tcx: 'mir> StoreBuffer {
274278

275279
let (index, clocks) = global.current_thread_state(thread_mgr);
276280
let loaded = store_elem.load_impl(index, &clocks);
277-
Ok(loaded)
281+
Ok((loaded, recency))
278282
}
279283

280284
fn buffered_write(
@@ -296,7 +300,7 @@ impl<'mir, 'tcx: 'mir> StoreBuffer {
296300
is_seqcst: bool,
297301
clocks: &ThreadClockSet,
298302
rng: &mut R,
299-
) -> &StoreElement {
303+
) -> (&StoreElement, LoadRecency) {
300304
use rand::seq::IteratorRandom;
301305
let mut found_sc = false;
302306
// FIXME: we want an inclusive take_while (stops after a false predicate, but
@@ -359,9 +363,12 @@ impl<'mir, 'tcx: 'mir> StoreBuffer {
359363
}
360364
});
361365

362-
candidates
363-
.choose(rng)
364-
.expect("store buffer cannot be empty, an element is populated on construction")
366+
let chosen = candidates.choose(rng).expect("store buffer cannot be empty");
367+
if std::ptr::eq(chosen, self.buffer.back().expect("store buffer cannot be empty")) {
368+
(chosen, LoadRecency::Latest)
369+
} else {
370+
(chosen, LoadRecency::Outdated)
371+
}
365372
}
366373

367374
/// ATOMIC STORE IMPL in the paper (except we don't need the location's vector clock)
@@ -499,13 +506,16 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>:
499506
alloc_range(base_offset, place.layout.size),
500507
latest_in_mo,
501508
)?;
502-
let loaded = buffer.buffered_read(
509+
let (loaded, recency) = buffer.buffered_read(
503510
global,
504511
&this.machine.threads,
505512
atomic == AtomicReadOrd::SeqCst,
506513
&mut *rng,
507514
validate,
508515
)?;
516+
if global.track_outdated_loads && recency == LoadRecency::Outdated {
517+
register_diagnostic(NonHaltingDiagnostic::WeakMemoryOutdatedLoad);
518+
}
509519

510520
return Ok(loaded);
511521
}

src/diagnostics.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ pub enum NonHaltingDiagnostic {
7474
Int2Ptr {
7575
details: bool,
7676
},
77+
WeakMemoryOutdatedLoad,
7778
}
7879

7980
/// Level of Miri specific diagnostics
@@ -474,6 +475,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
474475
format!("progress report: current operation being executed is here"),
475476
Int2Ptr { .. } =>
476477
format!("integer-to-pointer cast"),
478+
WeakMemoryOutdatedLoad =>
479+
format!("weak memory emulation: outdated value returned from load"),
477480
};
478481

479482
let (title, diag_level) = match e {
@@ -485,7 +488,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
485488
| CreatedCallId(..)
486489
| CreatedAlloc(..)
487490
| FreedAlloc(..)
488-
| ProgressReport => ("tracking was triggered", DiagLevel::Note),
491+
| ProgressReport
492+
| WeakMemoryOutdatedLoad =>
493+
("tracking was triggered", DiagLevel::Note),
489494
};
490495

491496
let helps = match e {

src/eval.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,8 @@ pub struct MiriConfig {
103103
pub data_race_detector: bool,
104104
/// Determine if weak memory emulation should be enabled. Requires data race detection to be enabled
105105
pub weak_memory_emulation: bool,
106+
/// Track when an outdated (weak memory) load happens.
107+
pub track_outdated_loads: bool,
106108
/// Rate of spurious failures for compare_exchange_weak atomic operations,
107109
/// between 0.0 and 1.0, defaulting to 0.8 (80% chance of failure).
108110
pub cmpxchg_weak_failure_rate: f64,
@@ -147,6 +149,7 @@ impl Default for MiriConfig {
147149
tracked_alloc_ids: HashSet::default(),
148150
data_race_detector: true,
149151
weak_memory_emulation: true,
152+
track_outdated_loads: false,
150153
cmpxchg_weak_failure_rate: 0.8, // 80%
151154
measureme_out: None,
152155
panic_on_unsupported: false,

0 commit comments

Comments
 (0)