From 3fc854d5619d315c20afc7e949dcb0223787dc2f Mon Sep 17 00:00:00 2001 From: Patrick-6 Date: Mon, 14 Apr 2025 08:53:50 +0200 Subject: [PATCH 01/41] WIP: Add initial support for running Miri flamegraph (no flamegraph options yet) --- Cargo.toml | 3 +++ miri-script/src/commands.rs | 18 +++++++++++------- miri-script/src/main.rs | 3 +++ tests/ui.rs | 22 +++++++++++++++++----- 4 files changed, 34 insertions(+), 12 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 91dadf78a2..38ceaf2688 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,9 @@ name = "miri" test = false # we have no unit tests doctest = false # and no doc tests +[profile.bench] +debug = true + [dependencies] getrandom = { version = "0.3", features = ["std"] } rand = "0.9" diff --git a/miri-script/src/commands.rs b/miri-script/src/commands.rs index ee09b9b4b7..74f1f4a6e8 100644 --- a/miri-script/src/commands.rs +++ b/miri-script/src/commands.rs @@ -112,8 +112,8 @@ impl Command { Command::Check { features, flags } => Self::check(features, flags), Command::Test { bless, target, coverage, features, flags } => Self::test(bless, target, coverage, features, flags), - Command::Run { dep, verbose, target, edition, features, flags } => - Self::run(dep, verbose, target, edition, features, flags), + Command::Run { dep, verbose, target, edition, features, flags, flamegraph } => + Self::run(dep, verbose, target, edition, features, flags, flamegraph), Command::Doc { features, flags } => Self::doc(features, flags), Command::Fmt { flags } => Self::fmt(flags), Command::Clippy { features, flags } => Self::clippy(features, flags), @@ -463,6 +463,7 @@ impl Command { edition: Option, features: Vec, flags: Vec, + flamegraph: bool, ) -> Result<()> { let mut e = MiriEnv::new()?; @@ -498,11 +499,14 @@ impl Command { // The basic command that executes the Miri driver. let mut cmd = if dep { // We invoke the test suite as that has all the logic for running with dependencies. - e.cargo_cmd(".", "test", &features) - .args(&["--test", "ui"]) - .args(quiet_flag) - .arg("--") - .args(&["--miri-run-dep-mode"]) + let cmd = e.cargo_cmd(".", "test", &features).args(&["--test", "ui"]).args(quiet_flag).arg("--"); + if flamegraph { + cmd.args(&["--miri-run-dep-mode-flamegraph"]) + } else { + cmd.args(&["--miri-run-dep-mode"]) + } + } else if flamegraph { + cmd!(e.sh, "flamegraph -- {miri_bin}") } else { cmd!(e.sh, "{miri_bin}") }; diff --git a/miri-script/src/main.rs b/miri-script/src/main.rs index 761ec5979f..f0d850df35 100644 --- a/miri-script/src/main.rs +++ b/miri-script/src/main.rs @@ -96,6 +96,9 @@ pub enum Command { /// The flags set in `MIRIFLAGS` are added in front of these flags. #[arg(trailing_var_arg = true, allow_hyphen_values = true)] flags: Vec, + /// Run with flamegraph (requires cargo-flamegraph to be installed) + #[arg(long)] + flamegraph: bool, }, /// Build documentation. Doc { diff --git a/tests/ui.rs b/tests/ui.rs index 73fbe2cc02..a790557938 100644 --- a/tests/ui.rs +++ b/tests/ui.rs @@ -322,10 +322,12 @@ fn main() -> Result<()> { let mut args = std::env::args_os(); // Skip the program name and check whether this is a `./miri run-dep` invocation - if let Some(first) = args.nth(1) - && first == "--miri-run-dep-mode" - { - return run_dep_mode(target, args); + if let Some(first) = args.nth(1) { + if first == "--miri-run-dep-mode" { + return run_dep_mode(target, args, /* flamegraph */ false); + } else if first == "--miri-run-dep-mode-flamegraph" { + return run_dep_mode(target, args, /* flamegraph */ true); + } } ui(Mode::Pass, "tests/pass", &target, WithoutDependencies, tmpdir.path())?; @@ -355,7 +357,11 @@ fn main() -> Result<()> { Ok(()) } -fn run_dep_mode(target: String, args: impl Iterator) -> Result<()> { +fn run_dep_mode( + target: String, + args: impl Iterator, + flamegraph: bool, +) -> Result<()> { let mut config = miri_config(&target, "", Mode::RunDep, Some(WithDependencies { bless: false })); config.comment_defaults.base().custom.remove("edition"); // `./miri` adds an `--edition` in `args`, so don't set it twice @@ -370,5 +376,11 @@ fn run_dep_mode(target: String, args: impl Iterator) -> Result< // Build dependencies test_config.apply_custom(&mut cmd, &build_manager).unwrap(); + if flamegraph { + let mut flamegraph_cmd = Command::new("flamegraph"); + flamegraph_cmd.arg("--").arg(cmd.get_program()).args(cmd.get_args()); + cmd = flamegraph_cmd; + } + if cmd.spawn()?.wait()?.success() { Ok(()) } else { std::process::exit(1) } } From 7fcd1731192d81eba684cdae11b14790f5d5ed1a Mon Sep 17 00:00:00 2001 From: Patrick-6 Date: Fri, 14 Mar 2025 09:58:46 +0100 Subject: [PATCH 02/41] WIP: Start working on Miri-GenMC interop. - Add genmc-sys crate for building GenMC C++ code. - Implemented program features: - Handling of atomic loads, stores, allocations and deallocations. - Implement consistent address allocations for globals - Non-global addresses allocated by GenMC. - Limitation: No address randomization. - Limitation: No address reuse. - Handling of non-atomic memory accesses. - Limitation: need to split up NA reads and stores into max 8 byte chunks. - Limitation: no mixed size access support. - Limitation: Incomplete (unsound) handling of uninitialized memory. - Implement Pointer conversions to and from GenMC - Limitation: Aliasing model checking must be disabled. - Handling of Read-Modify-Write, Compare-Exchange and Atomic Exchange operations. - Limitation: Compare-Exchange currently ignores possibility of spurious failures. - Limitation: Compare-Exchange failure memory ordering is ignored. - Handling of thread creation and finishing. - Added Miri to GenMC thread id mapping. - Implement mutex lock, try_lock and unlock. - Make use of annotations to reduce number of blocked executions for programs with mutexes. - Add estimation mode for Miri-GenMC. - Limitation: Printing currently handled on C++ side (Should be moved once VerificationResult is available to Rust code) - Thread scheduling in Miri done through GenMC, add loop over eval_entry function. - Rebase GenMC to use new scheduler. - Added GenMC `__VERIFIER_assume` equivalent for Miri (`miri_genmc_verifier_assume`) - Add warnings for GenMC mode for unsupported features. - Add a lot of test, including translation of GenMC litmus tests. - Only run tests if 'genmc' feature enabled. - WIP: try implementing NonNullUniquePtr wrapper for CXX. - WIP: work on mixed atomic/non-atomics. - Partially working, some issues with globals - FIX: Make naming consistent with GenMC for blocked execution Squashed changes: - Add git support to build, move C++ files into genmc-sys crate - Implement building GenMC-Miri after GenMC codebase split. - Cleanup genmc-sys build, remove walkdir dependency. - Fix printing after LLVM IO removal. - WIP: temporarily point GenMC repo to private branch. - WIP: Work on building without LLVM dependency. - Fix build for Ubuntu 22 - Disable stacked borrows for GenMC tests - Get Miri-GenMC building again after rebasing onto GenMC release 0.12.0 - Translate some Loom tests for Miri-GenMC. - Add error for using native-lib with GenMC mode. - Remove on_stack_empty handling except for main thread. - Rename 'stuck' to 'blocked' to be in line with GenMC terminology. - Enable tests that were blocked on rust-lang #116707 - Remove redundant file in compilation step. - Clean up InterpResult<'tcx, ()> - Improve program exit handling, remove 'thread_stack_empty' hack, clean up terminator cache leftovers. --- Cargo.toml | 3 +- genmc-sys/.gitignore | 1 + genmc-sys/build.rs | 1 - genmc-sys/src/cxx_extra.rs | 48 + genmc-sys/src/lib.rs | 288 ++++- genmc-sys/src_cpp/MiriInterface.cpp | 484 +++++++- genmc-sys/src_cpp/MiriInterface.hpp | 233 +++- miri-script/src/commands.rs | 6 +- src/alloc_addresses/mod.rs | 32 +- src/bin/miri.rs | 37 +- src/concurrency/data_race.rs | 73 +- src/concurrency/genmc/config.rs | 48 +- src/concurrency/genmc/dummy.rs | 133 ++- src/concurrency/genmc/global_allocations.rs | 145 +++ src/concurrency/genmc/helper.rs | 199 ++++ src/concurrency/genmc/mapping.rs | 83 ++ src/concurrency/genmc/miri_genmc.rs | 72 ++ src/concurrency/genmc/mod.rs | 1047 +++++++++++++++-- src/concurrency/genmc/thread_info_manager.rs | 81 ++ src/concurrency/genmc/warnings.rs | 66 ++ src/concurrency/mod.rs | 2 +- src/concurrency/thread.rs | 84 +- src/diagnostics.rs | 31 +- src/eval.rs | 13 +- src/lib.rs | 2 +- src/machine.rs | 28 +- src/shims/foreign_items.rs | 24 +- .../_disabled/fail/weak_memory/weak_uninit.rs | 57 + tests/genmc/fail/loom/buggy-inc/buggy_inc.rs | 56 + .../fail/loom/buggy-inc/buggy_inc.stderr | 15 + .../store_buffering.genmc.stderr | 15 + .../store_buffering.genmc_std.stderr | 21 + .../store_buffering.non_genmc.stderr | 15 + .../store_buffering.non_genmc_std.stderr | 15 + .../loom/store-buffering/store_buffering.rs | 83 ++ .../fail/simple/2w2w_weak.acq_rel.stderr | 15 + .../fail/simple/2w2w_weak.relaxed.stderr | 15 + tests/genmc/fail/simple/2w2w_weak.rs | 52 + .../read_global_init.rs | 20 + .../read_global_init.stderr | 15 + .../wna_wrlx_rrlx.return1234.stderr | 15 + .../wna_wrlx_rrlx.return42.stderr | 15 + .../mixed-atomic-non-atomic/wna_wrlx_rrlx.rs | 58 + .../basics/mutex/TODO_mutex_get_mut.rs.txt | 0 tests/genmc/pass/basics/mutex/mutex_simple.c | 72 ++ .../mutex/mutex_simple.order12reps1.stderr | 3 + .../mutex/mutex_simple.order12reps2.stderr | 3 + .../mutex/mutex_simple.order21reps1.stderr | 3 + .../mutex/mutex_simple.order21reps2.stderr | 3 + tests/genmc/pass/basics/mutex/mutex_simple.rs | 73 ++ .../basics/rmw/rmw_edge_cases.i16_.stderr | 3 + .../basics/rmw/rmw_edge_cases.i32_.stderr | 3 + .../basics/rmw/rmw_edge_cases.i64_.stderr | 3 + .../pass/basics/rmw/rmw_edge_cases.i8_.stderr | 3 + .../basics/rmw/rmw_edge_cases.isize_.stderr | 3 + tests/genmc/pass/basics/rmw/rmw_edge_cases.rs | 88 ++ .../basics/rmw/rmw_edge_cases.u16_.stderr | 3 + .../basics/rmw/rmw_edge_cases.u32_.stderr | 3 + .../basics/rmw/rmw_edge_cases.u64_.stderr | 3 + .../pass/basics/rmw/rmw_edge_cases.u8_.stderr | 3 + .../basics/rmw/rmw_edge_cases.usize_.stderr | 3 + tests/genmc/pass/basics/rmw/rmw_simple.rs | 29 + tests/genmc/pass/basics/rmw/rmw_simple.stderr | 3 + .../pass/data-structures/ms_queue_dynamic.rs | 224 ++++ .../data-structures/ms_queue_dynamic.stderr | 3 + .../data-structures/treiber_stack_dynamic.rs | 211 ++++ .../treiber_stack_dynamic.stderr | 3 + .../2+2W+2sc+scf/2w2w_2sc_scf.order12.stderr | 3 + .../2+2W+2sc+scf/2w2w_2sc_scf.order21.stderr | 3 + .../pass/litmus/2+2W+2sc+scf/2w2w_2sc_scf.rs | 43 + .../litmus/2+2W+3sc+rel1/2_2w_3sc_rel1.rs | 33 + .../litmus/2+2W+3sc+rel1/2_2w_3sc_rel1.stderr | 3 + tests/genmc/pass/litmus/2+2W+4sc/2_2w_4sc.rs | 33 + .../pass/litmus/2+2W+4sc/2_2w_4sc.stderr | 3 + .../genmc/pass/litmus/2+2W+scfs/2_2w_scfs.rs | 35 + .../pass/litmus/2+2W+scfs/2_2w_scfs.stderr | 3 + .../pass/litmus/2+2w/2W2W.order12.stderr | 3 + .../pass/litmus/2+2w/2W2W.order21.stderr | 3 + tests/genmc/pass/litmus/2+2w/2W2W.rs | 42 + .../pass/litmus/2CoWR/2cowr.order1234.stderr | 3 + .../pass/litmus/2CoWR/2cowr.order2341.stderr | 3 + .../pass/litmus/2CoWR/2cowr.order3412.stderr | 3 + .../pass/litmus/2CoWR/2cowr.order4123.stderr | 3 + .../pass/litmus/2CoWR/2cowr.order4321.stderr | 3 + tests/genmc/pass/litmus/2CoWR/2cowr.rs | 57 + .../pass/litmus/CoRR/corr.order12.stderr | 3 + .../pass/litmus/CoRR/corr.order21.stderr | 3 + tests/genmc/pass/litmus/CoRR/corr.rs | 41 + .../pass/litmus/CoRR0/corr0.order123.stderr | 3 + .../pass/litmus/CoRR0/corr0.order231.stderr | 3 + .../pass/litmus/CoRR0/corr0.order312.stderr | 3 + .../pass/litmus/CoRR0/corr0.order321.stderr | 3 + tests/genmc/pass/litmus/CoRR0/corr0.rs | 48 + .../pass/litmus/CoRR1/corr1.order1234.stderr | 3 + .../pass/litmus/CoRR1/corr1.order2341.stderr | 3 + .../pass/litmus/CoRR1/corr1.order3412.stderr | 3 + .../pass/litmus/CoRR1/corr1.order4123.stderr | 3 + .../pass/litmus/CoRR1/corr1.order4321.stderr | 3 + tests/genmc/pass/litmus/CoRR1/corr1.rs | 57 + .../pass/litmus/CoRR2/corr2.order1234.stderr | 3 + .../pass/litmus/CoRR2/corr2.order2341.stderr | 3 + .../pass/litmus/CoRR2/corr2.order3412.stderr | 3 + .../pass/litmus/CoRR2/corr2.order4123.stderr | 3 + .../pass/litmus/CoRR2/corr2.order4321.stderr | 3 + tests/genmc/pass/litmus/CoRR2/corr2.rs | 57 + .../pass/litmus/CoRW/corw.order12.stderr | 3 + .../pass/litmus/CoRW/corw.order21.stderr | 3 + tests/genmc/pass/litmus/CoRW/corw.rs | 40 + .../pass/litmus/CoWR/cowr.order12.stderr | 3 + .../pass/litmus/CoWR/cowr.order21.stderr | 3 + tests/genmc/pass/litmus/CoWR/cowr.rs | 40 + .../pass/litmus/IRIW-acq-sc/IRIW-acq-sc.rs | 45 + .../litmus/IRIW-acq-sc/IRIW-acq-sc.stderr | 3 + tests/genmc/pass/litmus/IRIWish/IRIWish.rs | 48 + .../genmc/pass/litmus/IRIWish/IRIWish.stderr | 3 + .../genmc/pass/litmus/LB+incMPs/LB_incMPs.rs | 48 + .../pass/litmus/LB+incMPs/LB_incMPs.stderr | 3 + tests/genmc/pass/litmus/LB/LB.order12.stderr | 3 + tests/genmc/pass/litmus/LB/LB.order21.stderr | 3 + tests/genmc/pass/litmus/LB/LB.rs | 42 + .../genmc/pass/litmus/MP+incMPs/MP_incMPs.rs | 41 + .../pass/litmus/MP+incMPs/MP_incMPs.stderr | 3 + .../pass/litmus/MP+rels+acqf/MP_rels_acqf.rs | 40 + .../litmus/MP+rels+acqf/MP_rels_acqf.stderr | 3 + tests/genmc/pass/litmus/MP/MP.order12.stderr | 3 + tests/genmc/pass/litmus/MP/MP.order21.stderr | 3 + tests/genmc/pass/litmus/MP/MP.rs | 42 + .../pass/litmus/MPU+rels+acq/MPU_rels_acq.rs | 43 + .../litmus/MPU+rels+acq/MPU_rels_acq.stderr | 3 + .../litmus/MPU2+rels+acqf/MPU2_rels_acqf.rs | 51 + .../MPU2+rels+acqf/MPU2_rels_acqf.stderr | 3 + .../pass/litmus/SB+2sc+scf/SB_2sc_scf.rs | 35 + .../pass/litmus/SB+2sc+scf/SB_2sc_scf.stderr | 3 + tests/genmc/pass/litmus/SB/SB.order12.stderr | 3 + tests/genmc/pass/litmus/SB/SB.order21.stderr | 3 + tests/genmc/pass/litmus/SB/SB.rs | 42 + .../pass/litmus/Z6+acq/Z6_acq.order123.stderr | 3 + .../pass/litmus/Z6+acq/Z6_acq.order321.stderr | 3 + tests/genmc/pass/litmus/Z6+acq/Z6_acq.rs | 51 + tests/genmc/pass/litmus/Z6.U0/Z6_U0.rs | 40 + tests/genmc/pass/litmus/Z6.U0/Z6_U0.stderr | 3 + .../pass/litmus/assume-ctrl/assume-ctrl.rs | 42 + .../litmus/assume-ctrl/assume-ctrl.stderr | 4 + tests/genmc/pass/litmus/atomicpo/atomicpo.rs | 34 + .../pass/litmus/atomicpo/atomicpo.stderr | 3 + tests/genmc/pass/litmus/casdep/casdep.rs | 34 + tests/genmc/pass/litmus/casdep/casdep.stderr | 3 + tests/genmc/pass/litmus/ccr/ccr.rs | 33 + tests/genmc/pass/litmus/ccr/ccr.stderr | 3 + tests/genmc/pass/litmus/cii/cii.rs | 36 + tests/genmc/pass/litmus/cii/cii.stderr | 3 + .../litmus/cumul-release/cumul-release.rs | 42 + .../litmus/cumul-release/cumul-release.stderr | 3 + .../litmus/default/default.order123.stderr | 3 + .../litmus/default/default.order231.stderr | 3 + .../litmus/default/default.order312.stderr | 3 + .../litmus/default/default.order321.stderr | 3 + tests/genmc/pass/litmus/default/default.rs | 49 + tests/genmc/pass/litmus/detour/detour.rs | 42 + tests/genmc/pass/litmus/detour/detour.stderr | 3 + .../litmus/fr_w_w_w_reads/fr_w_w_w_reads.rs | 45 + .../fr_w_w_w_reads/fr_w_w_w_reads.stderr | 3 + tests/genmc/pass/litmus/inc2w/inc2w.rs | 37 + tests/genmc/pass/litmus/inc2w/inc2w.stderr | 3 + .../litmus/inc_inc_RR_W_RR/inc_inc_RR_W_RR.rs | 63 + .../inc_inc_RR_W_RR/inc_inc_RR_W_RR.stderr | 3 + tests/genmc/pass/litmus/riwi/riwi.rs | 34 + tests/genmc/pass/litmus/riwi/riwi.stderr | 3 + .../litmus/viktor-relseq/viktor-relseq.rs | 43 + .../litmus/viktor-relseq/viktor-relseq.stderr | 3 + .../load_buffering.genmc.stderr | 3 + .../load_buffering.genmc_std.stderr | 9 + .../loom/load-buffering/load_buffering.rs | 80 ++ .../simple/2+2W+3sc+rel1/2_2w_3sc_rel1.rs | 39 + .../simple/2+2W+3sc+rel1/2_2w_3sc_rel1.stderr | 3 + tests/genmc/pass/simple/2w2w_seqcst.rs | 42 + tests/genmc/pass/simple/2w2w_seqcst.stderr | 3 + tests/genmc/pass/simple/atomic_ptr.rs | 83 ++ tests/genmc/pass/simple/atomic_ptr.stderr | 3 + tests/genmc/pass/simple/cas_simple.rs | 47 + tests/genmc/pass/simple/cas_simple.stderr | 3 + tests/genmc/pass/simple/simple_main.rs | 3 + tests/genmc/pass/simple/simple_main.stderr | 7 + .../pass/simple/simple_main_spawn_threads.rs | 10 + .../simple/simple_main_spawn_threads.stderr | 9 + .../simple/simple_miri_main_spawn_pthreads.rs | 35 + .../simple_miri_main_spawn_pthreads.stderr | 3 + .../simple/simple_miri_main_spawn_threads.rs | 15 + .../simple_miri_main_spawn_threads.stderr | 9 + tests/genmc/pass/simple/stack_alloc_atomic.rs | 18 + .../pass/simple/stack_alloc_atomic.stderr | 3 + tests/genmc/pass/simple/thread_locals.rs | 54 + tests/genmc/pass/simple/thread_locals.stderr | 3 + tests/genmc/pass/simple/thread_simple.rs | 34 + tests/genmc/pass/simple/thread_simple.stderr | 3 + tests/genmc/pass/test_cxx_build.rs | 8 - tests/genmc/pass/test_cxx_build.stderr | 5 - tests/utils-dep/genmc.rs | 38 + tests/utils-dep/miri_extern.rs | 2 + tests/utils-dep/mod.rs | 8 + tests/utils/miri_extern.rs | 3 + 201 files changed, 6652 insertions(+), 251 deletions(-) create mode 100644 genmc-sys/src/cxx_extra.rs create mode 100644 src/concurrency/genmc/global_allocations.rs create mode 100644 src/concurrency/genmc/helper.rs create mode 100644 src/concurrency/genmc/mapping.rs create mode 100644 src/concurrency/genmc/miri_genmc.rs create mode 100644 src/concurrency/genmc/thread_info_manager.rs create mode 100644 src/concurrency/genmc/warnings.rs create mode 100644 tests/genmc/_disabled/fail/weak_memory/weak_uninit.rs create mode 100644 tests/genmc/fail/loom/buggy-inc/buggy_inc.rs create mode 100644 tests/genmc/fail/loom/buggy-inc/buggy_inc.stderr create mode 100644 tests/genmc/fail/loom/store-buffering/store_buffering.genmc.stderr create mode 100644 tests/genmc/fail/loom/store-buffering/store_buffering.genmc_std.stderr create mode 100644 tests/genmc/fail/loom/store-buffering/store_buffering.non_genmc.stderr create mode 100644 tests/genmc/fail/loom/store-buffering/store_buffering.non_genmc_std.stderr create mode 100644 tests/genmc/fail/loom/store-buffering/store_buffering.rs create mode 100644 tests/genmc/fail/simple/2w2w_weak.acq_rel.stderr create mode 100644 tests/genmc/fail/simple/2w2w_weak.relaxed.stderr create mode 100644 tests/genmc/fail/simple/2w2w_weak.rs create mode 100644 tests/genmc/fail/simple/mixed-atomic-non-atomic/read_global_init.rs create mode 100644 tests/genmc/fail/simple/mixed-atomic-non-atomic/read_global_init.stderr create mode 100644 tests/genmc/fail/simple/mixed-atomic-non-atomic/wna_wrlx_rrlx.return1234.stderr create mode 100644 tests/genmc/fail/simple/mixed-atomic-non-atomic/wna_wrlx_rrlx.return42.stderr create mode 100644 tests/genmc/fail/simple/mixed-atomic-non-atomic/wna_wrlx_rrlx.rs create mode 100644 tests/genmc/pass/basics/mutex/TODO_mutex_get_mut.rs.txt create mode 100644 tests/genmc/pass/basics/mutex/mutex_simple.c create mode 100644 tests/genmc/pass/basics/mutex/mutex_simple.order12reps1.stderr create mode 100644 tests/genmc/pass/basics/mutex/mutex_simple.order12reps2.stderr create mode 100644 tests/genmc/pass/basics/mutex/mutex_simple.order21reps1.stderr create mode 100644 tests/genmc/pass/basics/mutex/mutex_simple.order21reps2.stderr create mode 100644 tests/genmc/pass/basics/mutex/mutex_simple.rs create mode 100644 tests/genmc/pass/basics/rmw/rmw_edge_cases.i16_.stderr create mode 100644 tests/genmc/pass/basics/rmw/rmw_edge_cases.i32_.stderr create mode 100644 tests/genmc/pass/basics/rmw/rmw_edge_cases.i64_.stderr create mode 100644 tests/genmc/pass/basics/rmw/rmw_edge_cases.i8_.stderr create mode 100644 tests/genmc/pass/basics/rmw/rmw_edge_cases.isize_.stderr create mode 100644 tests/genmc/pass/basics/rmw/rmw_edge_cases.rs create mode 100644 tests/genmc/pass/basics/rmw/rmw_edge_cases.u16_.stderr create mode 100644 tests/genmc/pass/basics/rmw/rmw_edge_cases.u32_.stderr create mode 100644 tests/genmc/pass/basics/rmw/rmw_edge_cases.u64_.stderr create mode 100644 tests/genmc/pass/basics/rmw/rmw_edge_cases.u8_.stderr create mode 100644 tests/genmc/pass/basics/rmw/rmw_edge_cases.usize_.stderr create mode 100644 tests/genmc/pass/basics/rmw/rmw_simple.rs create mode 100644 tests/genmc/pass/basics/rmw/rmw_simple.stderr create mode 100644 tests/genmc/pass/data-structures/ms_queue_dynamic.rs create mode 100644 tests/genmc/pass/data-structures/ms_queue_dynamic.stderr create mode 100644 tests/genmc/pass/data-structures/treiber_stack_dynamic.rs create mode 100644 tests/genmc/pass/data-structures/treiber_stack_dynamic.stderr create mode 100644 tests/genmc/pass/litmus/2+2W+2sc+scf/2w2w_2sc_scf.order12.stderr create mode 100644 tests/genmc/pass/litmus/2+2W+2sc+scf/2w2w_2sc_scf.order21.stderr create mode 100644 tests/genmc/pass/litmus/2+2W+2sc+scf/2w2w_2sc_scf.rs create mode 100644 tests/genmc/pass/litmus/2+2W+3sc+rel1/2_2w_3sc_rel1.rs create mode 100644 tests/genmc/pass/litmus/2+2W+3sc+rel1/2_2w_3sc_rel1.stderr create mode 100644 tests/genmc/pass/litmus/2+2W+4sc/2_2w_4sc.rs create mode 100644 tests/genmc/pass/litmus/2+2W+4sc/2_2w_4sc.stderr create mode 100644 tests/genmc/pass/litmus/2+2W+scfs/2_2w_scfs.rs create mode 100644 tests/genmc/pass/litmus/2+2W+scfs/2_2w_scfs.stderr create mode 100644 tests/genmc/pass/litmus/2+2w/2W2W.order12.stderr create mode 100644 tests/genmc/pass/litmus/2+2w/2W2W.order21.stderr create mode 100644 tests/genmc/pass/litmus/2+2w/2W2W.rs create mode 100644 tests/genmc/pass/litmus/2CoWR/2cowr.order1234.stderr create mode 100644 tests/genmc/pass/litmus/2CoWR/2cowr.order2341.stderr create mode 100644 tests/genmc/pass/litmus/2CoWR/2cowr.order3412.stderr create mode 100644 tests/genmc/pass/litmus/2CoWR/2cowr.order4123.stderr create mode 100644 tests/genmc/pass/litmus/2CoWR/2cowr.order4321.stderr create mode 100644 tests/genmc/pass/litmus/2CoWR/2cowr.rs create mode 100644 tests/genmc/pass/litmus/CoRR/corr.order12.stderr create mode 100644 tests/genmc/pass/litmus/CoRR/corr.order21.stderr create mode 100644 tests/genmc/pass/litmus/CoRR/corr.rs create mode 100644 tests/genmc/pass/litmus/CoRR0/corr0.order123.stderr create mode 100644 tests/genmc/pass/litmus/CoRR0/corr0.order231.stderr create mode 100644 tests/genmc/pass/litmus/CoRR0/corr0.order312.stderr create mode 100644 tests/genmc/pass/litmus/CoRR0/corr0.order321.stderr create mode 100644 tests/genmc/pass/litmus/CoRR0/corr0.rs create mode 100644 tests/genmc/pass/litmus/CoRR1/corr1.order1234.stderr create mode 100644 tests/genmc/pass/litmus/CoRR1/corr1.order2341.stderr create mode 100644 tests/genmc/pass/litmus/CoRR1/corr1.order3412.stderr create mode 100644 tests/genmc/pass/litmus/CoRR1/corr1.order4123.stderr create mode 100644 tests/genmc/pass/litmus/CoRR1/corr1.order4321.stderr create mode 100644 tests/genmc/pass/litmus/CoRR1/corr1.rs create mode 100644 tests/genmc/pass/litmus/CoRR2/corr2.order1234.stderr create mode 100644 tests/genmc/pass/litmus/CoRR2/corr2.order2341.stderr create mode 100644 tests/genmc/pass/litmus/CoRR2/corr2.order3412.stderr create mode 100644 tests/genmc/pass/litmus/CoRR2/corr2.order4123.stderr create mode 100644 tests/genmc/pass/litmus/CoRR2/corr2.order4321.stderr create mode 100644 tests/genmc/pass/litmus/CoRR2/corr2.rs create mode 100644 tests/genmc/pass/litmus/CoRW/corw.order12.stderr create mode 100644 tests/genmc/pass/litmus/CoRW/corw.order21.stderr create mode 100644 tests/genmc/pass/litmus/CoRW/corw.rs create mode 100644 tests/genmc/pass/litmus/CoWR/cowr.order12.stderr create mode 100644 tests/genmc/pass/litmus/CoWR/cowr.order21.stderr create mode 100644 tests/genmc/pass/litmus/CoWR/cowr.rs create mode 100644 tests/genmc/pass/litmus/IRIW-acq-sc/IRIW-acq-sc.rs create mode 100644 tests/genmc/pass/litmus/IRIW-acq-sc/IRIW-acq-sc.stderr create mode 100644 tests/genmc/pass/litmus/IRIWish/IRIWish.rs create mode 100644 tests/genmc/pass/litmus/IRIWish/IRIWish.stderr create mode 100644 tests/genmc/pass/litmus/LB+incMPs/LB_incMPs.rs create mode 100644 tests/genmc/pass/litmus/LB+incMPs/LB_incMPs.stderr create mode 100644 tests/genmc/pass/litmus/LB/LB.order12.stderr create mode 100644 tests/genmc/pass/litmus/LB/LB.order21.stderr create mode 100644 tests/genmc/pass/litmus/LB/LB.rs create mode 100644 tests/genmc/pass/litmus/MP+incMPs/MP_incMPs.rs create mode 100644 tests/genmc/pass/litmus/MP+incMPs/MP_incMPs.stderr create mode 100644 tests/genmc/pass/litmus/MP+rels+acqf/MP_rels_acqf.rs create mode 100644 tests/genmc/pass/litmus/MP+rels+acqf/MP_rels_acqf.stderr create mode 100644 tests/genmc/pass/litmus/MP/MP.order12.stderr create mode 100644 tests/genmc/pass/litmus/MP/MP.order21.stderr create mode 100644 tests/genmc/pass/litmus/MP/MP.rs create mode 100644 tests/genmc/pass/litmus/MPU+rels+acq/MPU_rels_acq.rs create mode 100644 tests/genmc/pass/litmus/MPU+rels+acq/MPU_rels_acq.stderr create mode 100644 tests/genmc/pass/litmus/MPU2+rels+acqf/MPU2_rels_acqf.rs create mode 100644 tests/genmc/pass/litmus/MPU2+rels+acqf/MPU2_rels_acqf.stderr create mode 100644 tests/genmc/pass/litmus/SB+2sc+scf/SB_2sc_scf.rs create mode 100644 tests/genmc/pass/litmus/SB+2sc+scf/SB_2sc_scf.stderr create mode 100644 tests/genmc/pass/litmus/SB/SB.order12.stderr create mode 100644 tests/genmc/pass/litmus/SB/SB.order21.stderr create mode 100644 tests/genmc/pass/litmus/SB/SB.rs create mode 100644 tests/genmc/pass/litmus/Z6+acq/Z6_acq.order123.stderr create mode 100644 tests/genmc/pass/litmus/Z6+acq/Z6_acq.order321.stderr create mode 100644 tests/genmc/pass/litmus/Z6+acq/Z6_acq.rs create mode 100644 tests/genmc/pass/litmus/Z6.U0/Z6_U0.rs create mode 100644 tests/genmc/pass/litmus/Z6.U0/Z6_U0.stderr create mode 100644 tests/genmc/pass/litmus/assume-ctrl/assume-ctrl.rs create mode 100644 tests/genmc/pass/litmus/assume-ctrl/assume-ctrl.stderr create mode 100644 tests/genmc/pass/litmus/atomicpo/atomicpo.rs create mode 100644 tests/genmc/pass/litmus/atomicpo/atomicpo.stderr create mode 100644 tests/genmc/pass/litmus/casdep/casdep.rs create mode 100644 tests/genmc/pass/litmus/casdep/casdep.stderr create mode 100644 tests/genmc/pass/litmus/ccr/ccr.rs create mode 100644 tests/genmc/pass/litmus/ccr/ccr.stderr create mode 100644 tests/genmc/pass/litmus/cii/cii.rs create mode 100644 tests/genmc/pass/litmus/cii/cii.stderr create mode 100644 tests/genmc/pass/litmus/cumul-release/cumul-release.rs create mode 100644 tests/genmc/pass/litmus/cumul-release/cumul-release.stderr create mode 100644 tests/genmc/pass/litmus/default/default.order123.stderr create mode 100644 tests/genmc/pass/litmus/default/default.order231.stderr create mode 100644 tests/genmc/pass/litmus/default/default.order312.stderr create mode 100644 tests/genmc/pass/litmus/default/default.order321.stderr create mode 100644 tests/genmc/pass/litmus/default/default.rs create mode 100644 tests/genmc/pass/litmus/detour/detour.rs create mode 100644 tests/genmc/pass/litmus/detour/detour.stderr create mode 100644 tests/genmc/pass/litmus/fr_w_w_w_reads/fr_w_w_w_reads.rs create mode 100644 tests/genmc/pass/litmus/fr_w_w_w_reads/fr_w_w_w_reads.stderr create mode 100644 tests/genmc/pass/litmus/inc2w/inc2w.rs create mode 100644 tests/genmc/pass/litmus/inc2w/inc2w.stderr create mode 100644 tests/genmc/pass/litmus/inc_inc_RR_W_RR/inc_inc_RR_W_RR.rs create mode 100644 tests/genmc/pass/litmus/inc_inc_RR_W_RR/inc_inc_RR_W_RR.stderr create mode 100644 tests/genmc/pass/litmus/riwi/riwi.rs create mode 100644 tests/genmc/pass/litmus/riwi/riwi.stderr create mode 100644 tests/genmc/pass/litmus/viktor-relseq/viktor-relseq.rs create mode 100644 tests/genmc/pass/litmus/viktor-relseq/viktor-relseq.stderr create mode 100644 tests/genmc/pass/loom/load-buffering/load_buffering.genmc.stderr create mode 100644 tests/genmc/pass/loom/load-buffering/load_buffering.genmc_std.stderr create mode 100644 tests/genmc/pass/loom/load-buffering/load_buffering.rs create mode 100644 tests/genmc/pass/simple/2+2W+3sc+rel1/2_2w_3sc_rel1.rs create mode 100644 tests/genmc/pass/simple/2+2W+3sc+rel1/2_2w_3sc_rel1.stderr create mode 100644 tests/genmc/pass/simple/2w2w_seqcst.rs create mode 100644 tests/genmc/pass/simple/2w2w_seqcst.stderr create mode 100644 tests/genmc/pass/simple/atomic_ptr.rs create mode 100644 tests/genmc/pass/simple/atomic_ptr.stderr create mode 100644 tests/genmc/pass/simple/cas_simple.rs create mode 100644 tests/genmc/pass/simple/cas_simple.stderr create mode 100644 tests/genmc/pass/simple/simple_main.rs create mode 100644 tests/genmc/pass/simple/simple_main.stderr create mode 100644 tests/genmc/pass/simple/simple_main_spawn_threads.rs create mode 100644 tests/genmc/pass/simple/simple_main_spawn_threads.stderr create mode 100644 tests/genmc/pass/simple/simple_miri_main_spawn_pthreads.rs create mode 100644 tests/genmc/pass/simple/simple_miri_main_spawn_pthreads.stderr create mode 100644 tests/genmc/pass/simple/simple_miri_main_spawn_threads.rs create mode 100644 tests/genmc/pass/simple/simple_miri_main_spawn_threads.stderr create mode 100644 tests/genmc/pass/simple/stack_alloc_atomic.rs create mode 100644 tests/genmc/pass/simple/stack_alloc_atomic.stderr create mode 100644 tests/genmc/pass/simple/thread_locals.rs create mode 100644 tests/genmc/pass/simple/thread_locals.stderr create mode 100644 tests/genmc/pass/simple/thread_simple.rs create mode 100644 tests/genmc/pass/simple/thread_simple.stderr delete mode 100644 tests/genmc/pass/test_cxx_build.rs delete mode 100644 tests/genmc/pass/test_cxx_build.stderr create mode 100644 tests/utils-dep/genmc.rs create mode 100644 tests/utils-dep/miri_extern.rs create mode 100644 tests/utils-dep/mod.rs diff --git a/Cargo.toml b/Cargo.toml index 38ceaf2688..de3d467c1b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -72,7 +72,8 @@ name = "ui" harness = false [features] -default = ["stack-cache", "native-lib"] +# TODO GENMC (DEBUGGING): `genmc` feature should be turned off in upstream repo for now +default = ["stack-cache", "native-lib", "genmc"] genmc = ["dep:genmc-sys"] # this enables a GPL dependency! stack-cache = [] stack-cache-consistency-check = ["stack-cache"] diff --git a/genmc-sys/.gitignore b/genmc-sys/.gitignore index 276a053cd0..1ba3f669fe 100644 --- a/genmc-sys/.gitignore +++ b/genmc-sys/.gitignore @@ -1 +1,2 @@ genmc-src*/ +genmc*/ diff --git a/genmc-sys/build.rs b/genmc-sys/build.rs index f20e0e8d52..fa0729e1a5 100644 --- a/genmc-sys/build.rs +++ b/genmc-sys/build.rs @@ -224,7 +224,6 @@ fn compile_cpp_dependencies(genmc_path: &Path) { .include(genmc_include_dir) .include(llvm_include_dirs) .include("./src_cpp") - .file("./src_cpp/MiriInterface.hpp") .file("./src_cpp/MiriInterface.cpp") .compile("genmc_interop"); diff --git a/genmc-sys/src/cxx_extra.rs b/genmc-sys/src/cxx_extra.rs new file mode 100644 index 0000000000..55cfee57ea --- /dev/null +++ b/genmc-sys/src/cxx_extra.rs @@ -0,0 +1,48 @@ +#![allow(unused)] // TODO GENMC + +use std::pin::Pin; + +use cxx::UniquePtr; +use cxx::memory::UniquePtrTarget; + +#[repr(transparent)] +pub struct NonNullUniquePtr { + /// SAFETY: `inner` is never `null` + inner: UniquePtr, +} + +impl NonNullUniquePtr { + pub fn new(input: UniquePtr) -> Option { + if input.is_null() { + None + } else { + // SAFETY: `input` is not `null` + Some(unsafe { Self::new_unchecked(input) }) + } + } + + /// SAFETY: caller must ensure that `input` is not `null` + pub unsafe fn new_unchecked(input: UniquePtr) -> Self { + Self { inner: input } + } + + pub fn into_inner(self) -> UniquePtr { + self.inner + } + + pub fn as_mut(&mut self) -> Pin<&mut T> { + let ptr = self.inner.as_mut_ptr(); + + // SAFETY: `inner` is not `null` (type invariant) + let mut_reference = unsafe { ptr.as_mut().unwrap_unchecked() }; + // SAFETY: TODO GENMC (should be the same reason as in CXX crate, but there is no safety comment there) + unsafe { Pin::new_unchecked(mut_reference) } + } +} + +impl AsRef for NonNullUniquePtr { + fn as_ref(&self) -> &T { + // SAFETY: `inner` is not `null` (type invariant) + unsafe { self.inner.as_ref().unwrap_unchecked() } + } +} diff --git a/genmc-sys/src/lib.rs b/genmc-sys/src/lib.rs index ab46d729ea..3df5f62a15 100644 --- a/genmc-sys/src/lib.rs +++ b/genmc-sys/src/lib.rs @@ -1,11 +1,38 @@ pub use self::ffi::*; +pub mod cxx_extra; + +/// Defined in "genmc/src/Support/SAddr.hpp" +/// FIXME: currently we use `getGlobalAllocStaticMask()` to ensure the constant is consistent between Miri and GenMC, +/// but if https://github.com/dtolnay/cxx/issues/1051 is fixed we could share the constant directly. +pub const GENMC_GLOBAL_ADDRESSES_MASK: u64 = 1 << 63; + +#[repr(transparent)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct GenmcThreadId(pub i32); + +pub const GENMC_MAIN_THREAD_ID: GenmcThreadId = GenmcThreadId(0); + +impl GenmcScalar { + pub const UNINIT: Self = Self { value: 0, extra: 0, is_init: false }; + pub const DUMMY: Self = Self::from_u64(0xDEADBEEF); + + pub const MUTEX_LOCKED_STATE: Self = Self::from_u64(1); + pub const MUTEX_UNLOCKED_STATE: Self = Self::from_u64(0); + + pub const fn from_u64(value: u64) -> Self { + Self { value, extra: 0, is_init: true } + } +} + impl Default for GenmcParams { fn default() -> Self { Self { print_random_schedule_seed: false, - do_symmetry_reduction: false, - // FIXME(GenMC): Add defaults for remaining parameters + quiet: true, + log_level_trace: false, + do_symmetry_reduction: false, // TODO GENMC (PERFORMANCE): maybe make this default `true` + estimation_max: 1000, } } } @@ -16,15 +43,268 @@ mod ffi { /// (The fields of this struct are visible to both Rust and C++) #[derive(Clone, Debug)] struct GenmcParams { + // pub genmc_seed: u64; // OR: Option pub print_random_schedule_seed: bool, + pub quiet: bool, // TODO GENMC: maybe make log-level more fine grained + pub log_level_trace: bool, pub do_symmetry_reduction: bool, - // FIXME(GenMC): Add remaining parameters. + pub estimation_max: u32, + } + + #[derive(Debug)] + enum ActionKind { + /// Any Mir terminator that's atomic and has load semantics. + Load, + /// Anything that's not a `Load`. + NonLoad, + } + + #[derive(Debug)] + enum MemOrdering { + NotAtomic = 0, + Relaxed = 1, + // In case we support consume + Acquire = 3, + Release = 4, + AcquireRelease = 5, + SequentiallyConsistent = 6, + } + + #[derive(Debug)] + enum RMWBinOp { + Xchg = 0, + Add = 1, + Sub = 2, + And = 3, + Nand = 4, + Or = 5, + Xor = 6, + Max = 7, + Min = 8, + UMax = 9, + UMin = 10, + } + + // TODO GENMC: do these have to be shared with the Rust side? + #[derive(Debug)] + enum StoreEventType { + Normal, + ReadModifyWrite, + CompareExchange, + MutexUnlockWrite, + } + + #[derive(Debug, Clone, Copy)] + struct GenmcScalar { + value: u64, + extra: u64, + is_init: bool, + } + + /**** \/ Result & Error types \/ ****/ + + #[must_use] + #[derive(Debug)] + struct ReadModifyWriteResult { + old_value: GenmcScalar, + new_value: GenmcScalar, + isCoMaxWrite: bool, + error: UniquePtr, // TODO GENMC: pass more error info here + } + + #[must_use] + #[derive(Debug)] + struct MutexLockResult { + is_lock_acquired: bool, + error: UniquePtr, // TODO GENMC: pass more error info here + } + + #[must_use] + #[derive(Debug)] + struct CompareExchangeResult { + old_value: GenmcScalar, // TODO GENMC: handle bigger values + is_success: bool, + isCoMaxWrite: bool, + error: UniquePtr, // TODO GENMC: pass more error info here } + + #[must_use] + #[derive(Debug)] + struct LoadResult { + is_read_opt: bool, + read_value: GenmcScalar, // TODO GENMC: handle bigger values + error: UniquePtr, // TODO GENMC: pass more error info here + } + + #[must_use] + #[derive(Debug)] + struct StoreResult { + error: UniquePtr, // TODO GENMC: pass more error info here + isCoMaxWrite: bool, + } + + #[must_use] + #[derive(Debug)] + enum VerificationError { + VE_NonErrorBegin, + VE_OK, + VE_WWRace, + VE_UnfreedMemory, + VE_NonErrorLast, + + VE_Safety, + VE_Recovery, + VE_Liveness, + VE_RaceNotAtomic, + VE_RaceFreeMalloc, + VE_FreeNonMalloc, + VE_DoubleFree, + VE_Allocation, + + VE_InvalidAccessBegin, + VE_UninitializedMem, + VE_AccessNonMalloc, + VE_AccessFreed, + VE_InvalidAccessEnd, + + VE_InvalidCreate, + VE_InvalidJoin, + VE_InvalidUnlock, + VE_InvalidBInit, + VE_BarrierWellFormedness, + VE_Annotation, + VE_MixedSize, + VE_LinearizabilityError, + VE_SystemError, + } + + /**** /\ Result & Error types /\ ****/ + unsafe extern "C++" { include!("MiriInterface.hpp"); + type MemOrdering; + type RMWBinOp; + type StoreEventType; + + // Types for Scheduling queries: + type ActionKind; + + // Result / Error types: + type LoadResult; + type StoreResult; + type ReadModifyWriteResult; + type CompareExchangeResult; + type MutexLockResult; + type VerificationError; + + type GenmcScalar; + + // type OperatingMode; // Estimation(budget) or Verification + type MiriGenMCShim; - fn createGenmcHandle(config: &GenmcParams) -> UniquePtr; + fn createGenmcHandle(config: &GenmcParams, do_estimation: bool) + -> UniquePtr; + fn getGlobalAllocStaticMask() -> u64; + + fn handleExecutionStart(self: Pin<&mut MiriGenMCShim>); + fn handleExecutionEnd(self: Pin<&mut MiriGenMCShim>) -> UniquePtr; + + fn handleLoad( + self: Pin<&mut MiriGenMCShim>, + thread_id: i32, + address: u64, + size: u64, + memory_ordering: MemOrdering, + old_value: GenmcScalar, + ) -> LoadResult; + fn handleReadModifyWrite( + self: Pin<&mut MiriGenMCShim>, + thread_id: i32, + address: u64, + size: u64, + load_ordering: MemOrdering, + store_ordering: MemOrdering, + rmw_op: RMWBinOp, + rhs_value: GenmcScalar, + old_value: GenmcScalar, + ) -> ReadModifyWriteResult; + fn handleCompareExchange( + self: Pin<&mut MiriGenMCShim>, + thread_id: i32, + address: u64, + size: u64, + expected_value: GenmcScalar, + new_value: GenmcScalar, + old_value: GenmcScalar, + success_load_ordering: MemOrdering, + success_store_ordering: MemOrdering, + fail_load_ordering: MemOrdering, + can_fail_spuriously: bool, + ) -> CompareExchangeResult; + fn handleStore( + self: Pin<&mut MiriGenMCShim>, + thread_id: i32, + address: u64, + size: u64, + value: GenmcScalar, + old_value: GenmcScalar, + memory_ordering: MemOrdering, + store_event_type: StoreEventType, + ) -> StoreResult; + fn handleFence(self: Pin<&mut MiriGenMCShim>, thread_id: i32, memory_ordering: MemOrdering); + + fn handleMalloc( + self: Pin<&mut MiriGenMCShim>, + thread_id: i32, + size: u64, + alignment: u64, + ) -> u64; + fn handleFree(self: Pin<&mut MiriGenMCShim>, thread_id: i32, address: u64, size: u64); + + fn handleThreadCreate(self: Pin<&mut MiriGenMCShim>, thread_id: i32, parent_id: i32); + fn handleThreadJoin(self: Pin<&mut MiriGenMCShim>, thread_id: i32, child_id: i32); + fn handleThreadFinish(self: Pin<&mut MiriGenMCShim>, thread_id: i32, ret_val: u64); + + /**** Blocking instructions ****/ + fn handleUserBlock(self: Pin<&mut MiriGenMCShim>, thread_id: i32); + + /**** Mutex handling ****/ + fn handleMutexLock( + self: Pin<&mut MiriGenMCShim>, + thread_id: i32, + address: u64, + size: u64, + ) -> MutexLockResult; + fn handleMutexTryLock( + self: Pin<&mut MiriGenMCShim>, + thread_id: i32, + address: u64, + size: u64, + ) -> MutexLockResult; + fn handleMutexUnlock( + self: Pin<&mut MiriGenMCShim>, + thread_id: i32, + address: u64, + size: u64, + ) -> StoreResult; + + /**** Scheduling ****/ + fn scheduleNext( + self: Pin<&mut MiriGenMCShim>, + curr_thread_id: i32, + curr_thread_next_instr_kind: ActionKind, + ) -> i64; + + fn getBlockedExecutionCount(self: &MiriGenMCShim) -> u64; + fn getExploredExecutionCount(self: &MiriGenMCShim) -> u64; + + /// Check whether there are more executions to explore. + /// If there are more executions, this method prepares for the next execution and returns `true`. + fn isExplorationDone(self: Pin<&mut MiriGenMCShim>) -> bool; + + fn printGraph(self: Pin<&mut MiriGenMCShim>); + fn printEstimationResults(self: &MiriGenMCShim, elapsed_time_sec: f64); } } diff --git a/genmc-sys/src_cpp/MiriInterface.cpp b/genmc-sys/src_cpp/MiriInterface.cpp index 0827bb3d40..0e13cef514 100644 --- a/genmc-sys/src_cpp/MiriInterface.cpp +++ b/genmc-sys/src_cpp/MiriInterface.cpp @@ -2,49 +2,507 @@ #include "genmc-sys/src/lib.rs.h" -auto MiriGenMCShim::createHandle(const GenmcParams &config) +#include "ADT/value_ptr.hpp" +#include "Config/MemoryModel.hpp" +#include "Config/Verbosity.hpp" +#include "ExecutionGraph/EventLabel.hpp" +#include "ExecutionGraph/LoadAnnotation.hpp" +#include "Runtime/InterpreterEnumAPI.hpp" +#include "Static/ModuleID.hpp" +#include "Support/ASize.hpp" +#include "Support/Error.hpp" +#include "Support/Logger.hpp" +#include "Support/MemAccess.hpp" +#include "Support/RMWOps.hpp" +#include "Support/SAddr.hpp" +#include "Support/SVal.hpp" +#include "Support/ThreadInfo.hpp" +#include "Verification/GenMCDriver.hpp" + +#include +#include +#include +#include + +using AnnotID = ModuleID::ID; +using AnnotT = SExpr; + +// Return -1 when no thread can/should be scheduled, or the thread id of the next thread +// NOTE: this is safe because ThreadId is 32 bit, and we return a 64 bit integer +// TODO GENMC: could directly return std::optional if CXX ever supports this +auto MiriGenMCShim::scheduleNext(const int curr_thread_id, + const ActionKind curr_thread_next_instr_kind) -> int64_t +{ + // The current thread is the only one where the `kind` could have changed since we last made + // a scheduling decision. + globalInstructions[curr_thread_id].kind = curr_thread_next_instr_kind; + + auto result = GenMCDriver::scheduleNext(globalInstructions); + if (result.has_value()) { + return static_cast(result.value()); + } + return -1; +} + +/**** Functions available to Miri ****/ + +// NOLINTNEXTLINE(readability-convert-member-functions-to-static) +auto MiriGenMCShim::createHandle(const GenmcParams &config, bool estimation_mode) -> std::unique_ptr { auto conf = std::make_shared(); + // TODO GENMC: Can we get some default values somehow? + // Config::saveConfigOptions(*conf); + + // NOTE: Miri already initialization checks, so we can disable them in GenMC + conf->skipNonAtomicInitializedCheck = true; // Miri needs all threads to be replayed, even fully completed ones. conf->replayCompletedThreads = true; + // TODO GENMC: make sure this doesn't affect any tests, and maybe make it changeable from + // Miri: + constexpr unsigned int DEFAULT_WARN_ON_GRAPH_SIZE = 16 * 1024; + conf->warnOnGraphSize = DEFAULT_WARN_ON_GRAPH_SIZE; + // We only support the RC11 memory model for Rust. conf->model = ModelType::RC11; + conf->randomScheduleSeed = + "42"; // TODO GENMC: only for random exploration/scheduling mode in GenMC conf->printRandomScheduleSeed = config.print_random_schedule_seed; + if (config.quiet) { + // logLevel = VerbosityLevel::Quiet; + // TODO GENMC: error might be better (or new level for `BUG`) + // logLevel = VerbosityLevel::Quiet; + logLevel = VerbosityLevel::Error; + } else if (config.log_level_trace) { + logLevel = VerbosityLevel::Trace; + } else { + logLevel = VerbosityLevel::Tip; + } - // FIXME(genmc): disable any options we don't support currently: + // TODO GENMC (EXTRA): check if we can enable IPR: conf->ipr = false; + // TODO GENMC (EXTRA): check if we can enable BAM: conf->disableBAM = true; + // TODO GENMC (EXTRA): check if we can do instruction caching (probably not) conf->instructionCaching = false; - ERROR_ON(config.do_symmetry_reduction, "Symmetry reduction is currently unsupported in GenMC mode."); + // TODO GENMC (EXTRA): check if we can enable Symmetry Reduction: + ERROR_ON(config.do_symmetry_reduction, + "Symmetry reduction is currently unsupported in GenMC mode."); conf->symmetryReduction = config.do_symmetry_reduction; - // FIXME(genmc): Should there be a way to change this option from Miri? + // TODO GENMC: Should there be a way to change this option from Miri? conf->schedulePolicy = SchedulePolicy::WF; - // FIXME(genmc): implement estimation mode: - conf->estimate = false; - conf->estimationMax = 1000; + conf->estimate = estimation_mode; + conf->estimationMax = config.estimation_max; const auto mode = conf->estimate ? GenMCDriver::Mode(GenMCDriver::EstimationMode{}) - : GenMCDriver::Mode(GenMCDriver::VerificationMode{}); + : GenMCDriver::Mode(GenMCDriver::VerificationMode{}); // Running Miri-GenMC without race detection is not supported. - // Disabling this option also changes the behavior of the replay scheduler to only schedule at atomic operations, which is required with Miri. - // This happens because Miri can generate multiple GenMC events for a single MIR terminator. Without this option, - // the scheduler might incorrectly schedule an atomic MIR terminator because the first event it creates is a non-atomic (e.g., `StorageLive`). + // Disabling this option also changes the behavior of the replay scheduler to only schedule + // at atomic operations, which is required with Miri. This happens because Miri can generate + // multiple GenMC events for a single MIR terminator. Without this option, the scheduler + // might incorrectly schedule an atomic MIR terminator because the first event it creates is + // a non-atomic (e.g., `StorageLive`). conf->disableRaceDetection = false; // Miri can already check for unfreed memory. Also, GenMC cannot distinguish between memory // that is allowed to leak and memory that is not. conf->warnUnfreedMemory = false; - // FIXME(genmc): check config: - // checkConfigOptions(*conf); + checkConfigOptions(*conf, true); auto driver = std::make_unique(std::move(conf), mode); + + auto *driverPtr = driver.get(); + auto initValGetter = [driverPtr](const AAccess &access) { + const auto addr = access.getAddr(); + if (!driverPtr->initVals_.contains(addr)) { + MIRI_LOG() << "WARNING: TODO GENMC: requested initial value for address " + << addr << ", but there is none.\n"; + return SVal(0xCC00CC00); + // BUG_ON(!driverPtr->initVals_.contains(addr)); + } + auto result = driverPtr->initVals_[addr]; + if (!result.is_init) { + MIRI_LOG() << "WARNING: TODO GENMC: requested initial value for address " + << addr << ", but the memory is uninitialized.\n"; + return SVal(0xFF00FF00); + } + MIRI_LOG() << "MiriGenMCShim: requested initial value for address " << addr + << " == " << addr.get() << ", returning: " << result << "\n"; + return result.toSVal(); + }; + driver->getExec().getGraph().setInitValGetter(initValGetter); + return driver; } + +// This needs to be available to Miri, but clang-tidy wants it static +// NOLINTNEXTLINE(misc-use-internal-linkage) +auto createGenmcHandle(const GenmcParams &config, bool estimation_mode) + -> std::unique_ptr +{ + return MiriGenMCShim::createHandle(config, estimation_mode); +} + +/**** Execution start/end handling ****/ + +void MiriGenMCShim::handleExecutionStart() +{ + globalInstructions.clear(); + globalInstructions.push_back(Action(ActionKind::Load, Event::getInit())); + GenMCDriver::handleExecutionStart(); +} + +auto MiriGenMCShim::handleExecutionEnd() -> std::unique_ptr +{ + return GenMCDriver::handleExecutionEnd(globalInstructions); +} + +/**** Thread management ****/ + +void MiriGenMCShim::handleThreadCreate(ThreadId thread_id, ThreadId parent_id) +{ + // NOTE: The threadCreate event happens in the parent: + auto pos = incPos(parent_id); + + // FIXME(genmc): for supporting symmetry reduction, these will need to be properly set: + const unsigned funId = 0; + const SVal arg = SVal(0); + const ThreadInfo childInfo = ThreadInfo{thread_id, parent_id, funId, arg}; + + // NOTE: Default memory ordering (`Release`) used here. + auto tcLab = std::make_unique(pos, childInfo); + auto createLab = GenMCDriver::handleThreadCreate(std::move(tcLab)); + auto genmcTid = createLab->getChildId(); + + BUG_ON(genmcTid != thread_id || genmcTid == -1 || genmcTid != globalInstructions.size()); + globalInstructions.push_back(Action(ActionKind::Load, Event(genmcTid, 0))); +} + +void MiriGenMCShim::handleThreadJoin(ThreadId thread_id, ThreadId child_id) +{ + auto parentTid = thread_id; + auto childTid = child_id; + + // The thread join event happens in the parent. + auto pos = incPos(parentTid); + + // NOTE: Default memory ordering (`Acquire`) used here. + auto lab = std::make_unique(pos, childTid); + auto res = GenMCDriver::handleThreadJoin(std::move(lab)); + // If the join failed, decrease the event index again: + if (!res.has_value()) + decPos(parentTid); + + // NOTE: Thread return value is ignored, since Miri doesn't need it. +} + +void MiriGenMCShim::handleThreadFinish(ThreadId thread_id, uint64_t ret_val) +{ + MIRI_LOG() << "GenMC: handleThreadFinish: thread id: " << thread_id << "\n"; + + auto pos = incPos(thread_id); + auto retVal = SVal(ret_val); + + // NOTE: Default memory ordering (`Release`) used here. + auto eLab = std::make_unique(pos, retVal); + + GenMCDriver::handleThreadFinish(std::move(eLab)); +} + +/**** Blocking instructions ****/ + +void MiriGenMCShim::handleUserBlock(ThreadId thread_id) +{ + auto pos = incPos(thread_id); + auto bLab = UserBlockLabel::create(pos); + GenMCDriver::handleBlock(std::move(bLab)); +} + +/**** Memory access handling ****/ + +[[nodiscard]] auto MiriGenMCShim::handleLoad(ThreadId thread_id, uint64_t address, uint64_t size, + MemOrdering ord, GenmcScalar old_val) -> LoadResult +{ + auto pos = incPos(thread_id); + MIRI_LOG() << "Received Load from Miri at address: " << address << ", size " << size + << " with ordering " << ord << ", event: " << pos << "\n"; + + auto loc = SAddr(address); + auto aSize = ASize(size); + auto type = AType::Unsigned; // TODO GENMC: get correct type from Miri + + auto newLab = std::make_unique(pos, ord, loc, aSize, type); + + auto oldValSetter = [this, old_val](SAddr loc) { this->handleOldVal(loc, old_val); }; + auto result = GenMCDriver::handleLoad(std::move(newLab), oldValSetter); + return result; +} + +[[nodiscard]] auto MiriGenMCShim::handleReadModifyWrite(ThreadId thread_id, uint64_t address, + uint64_t size, MemOrdering loadOrd, + MemOrdering store_ordering, RMWBinOp rmw_op, + GenmcScalar rhs_value, GenmcScalar old_val) + -> ReadModifyWriteResult +{ + MIRI_LOG() << "Received Read-Modify-Write from Miri at address: " << address << ", size " + << size << " with orderings (" << loadOrd << ", " << store_ordering + << "), rmw op: " << static_cast(rmw_op) << "\n"; + + auto pos = incPos(thread_id); + + auto loc = SAddr(address); + auto aSize = ASize(size); + auto type = AType::Unsigned; + + auto rhsVal = rhs_value.toSVal(); + auto newLab = + std::make_unique(pos, loadOrd, loc, aSize, type, rmw_op, rhsVal); + + auto oldValSetter = [this, old_val](SAddr loc) { this->handleOldVal(loc, old_val); }; + auto result = GenMCDriver::handleLoad(std::move(newLab), oldValSetter); + if (const auto *error = result.error.get()) { + return ReadModifyWriteResult::fromError(*error); + } + + auto oldVal = result.scalar.toSVal(); // TODO GENMC: u128 handling + auto newVal = executeRMWBinOp(oldVal, rhsVal, size, rmw_op); + + auto store_result = handleStore(thread_id, address, size, GenmcScalar(newVal), old_val, + store_ordering, StoreEventType::ReadModifyWrite); + + if (store_result.is_error()) + return ReadModifyWriteResult::fromError(*store_result.error.get()); + return ReadModifyWriteResult(oldVal, newVal, store_result.isCoMaxWrite); +} + +[[nodiscard]] auto MiriGenMCShim::handleCompareExchange( + ThreadId thread_id, uint64_t address, uint64_t size, GenmcScalar expected_value, + GenmcScalar new_value, GenmcScalar old_val, MemOrdering success_load_ordering, + MemOrdering success_store_ordering, MemOrdering fail_load_ordering, + bool can_fail_spuriously) -> CompareExchangeResult +{ + + MIRI_LOG() << "Received Compare-Exchange from Miri (value: " << expected_value << " --> " + << new_value << ", old value: " << old_val << ") at address: " << address + << ", size " << size << " with success orderings (" << success_load_ordering + << ", " << success_store_ordering + << "), fail load ordering: " << fail_load_ordering + << ", is weak (can fail spuriously): " << can_fail_spuriously << "\n"; + + auto pos = incPos(thread_id); + + auto loc = SAddr(address); + auto aSize = ASize(size); + auto type = AType::Unsigned; + + auto expectedVal = expected_value.toSVal(); + auto newVal = new_value.toSVal(); + + // FIXME(GenMC): properly handle failure memory ordering. + + auto newLab = std::make_unique(pos, success_load_ordering, loc, aSize, type, + expectedVal, newVal); + + auto oldValSetter = [this, old_val](SAddr loc) { this->handleOldVal(loc, old_val); }; + auto result = GenMCDriver::handleLoad(std::move(newLab), oldValSetter); + if (const auto *error = result.error.get()) { + return CompareExchangeResult::fromError(*error); + } + + auto oldVal = result.scalar.toSVal(); + if (oldVal != expectedVal) + return CompareExchangeResult::failure(oldVal); + + auto store_result = handleStore(thread_id, address, size, GenmcScalar(newVal), old_val, + success_store_ordering, StoreEventType::CompareExchange); + + if (store_result.is_error()) + return CompareExchangeResult::fromError(*store_result.error); + return CompareExchangeResult::success(oldVal, store_result.isCoMaxWrite); +} + +[[nodiscard]] auto MiriGenMCShim::handleStore(ThreadId thread_id, uint64_t address, uint64_t size, + GenmcScalar value, GenmcScalar old_val, + MemOrdering ord, StoreEventType store_event_type) + -> StoreResult +{ + MIRI_LOG() << "Received Store from Miri at address " << address << ", size " << size + << " with ordering " << ord << ", is part of rmw: (" + << static_cast(store_event_type) << ")\n"; + + auto pos = incPos(thread_id); + + auto loc = SAddr(address); // TODO GENMC: called addr for write, loc for read? + auto aSize = ASize(size); + auto type = AType::Unsigned; // TODO GENMC: get from Miri + + // TODO GENMC: u128 support + auto val = value.toSVal(); + + std::unique_ptr wLab; + switch (store_event_type) { + case StoreEventType::Normal: + wLab = std::make_unique(pos, ord, loc, aSize, type, val); + break; + case StoreEventType::ReadModifyWrite: + wLab = std::make_unique(pos, ord, loc, aSize, type, val); + break; + case StoreEventType::CompareExchange: + wLab = std::make_unique(pos, ord, loc, aSize, type, val); + break; + case StoreEventType::MutexUnlockWrite: + wLab = UnlockWriteLabel::create(pos, ord, loc, aSize, AType::Signed, val); + break; + default: + ERROR("Unsupported Store Event Type"); + } + + auto oldValSetter = [this, old_val](SAddr loc) { + this->handleOldVal(loc, + old_val); // TODO GENMC(HACK): is this the correct way to do it? + }; + + return GenMCDriver::handleStore(std::move(wLab), oldValSetter); +} + +void MiriGenMCShim::handleFence(ThreadId thread_id, MemOrdering ord) +{ + MIRI_LOG() << "Received fence operation from Miri with ordering " << ord << "\n"; + + auto pos = incPos(thread_id); + + auto fLab = std::make_unique(pos, ord); + GenMCDriver::handleFence(std::move(fLab)); +} + +/**** Memory (de)allocation ****/ + +auto MiriGenMCShim::handleMalloc(ThreadId thread_id, uint64_t size, uint64_t alignment) -> uintptr_t +{ + auto pos = incPos(thread_id); + + auto sd = StorageDuration::SD_Heap; // TODO GENMC: get from Miri + auto stype = StorageType::ST_Durable; // TODO GENMC + auto spc = AddressSpace::AS_User; // TODO GENMC + + auto deps = EventDeps(); // TODO GENMC: without this, constructor is ambiguous + + // TODO GENMC (types): size_t vs unsigned int + auto aLab = std::make_unique(pos, size, alignment, sd, stype, spc, deps); + + SAddr retVal = GenMCDriver::handleMalloc(std::move(aLab)); + + BUG_ON(retVal.get() == 0); + + auto address = retVal.get(); + return address; +} + +void MiriGenMCShim::handleFree(ThreadId thread_id, uint64_t address, uint64_t size) +{ + auto addr = SAddr(address); + auto alloc_size = SAddr(size); + + auto pos = incPos(thread_id); + + auto dLab = std::make_unique(pos, addr, size); + GenMCDriver::handleFree(std::move(dLab)); +} + +/**** Mutex handling ****/ + +auto MiriGenMCShim::handleMutexLock(ThreadId thread_id, uint64_t address, uint64_t size) + -> MutexLockResult +{ + // TODO GENMC: this needs to be identical even in multithreading + ModuleID::ID annot_id; + if (annotation_id.contains(address)) { + annot_id = annotation_id.at(address); + } else { + annot_id = annotation_id_counter++; + annotation_id.insert(std::make_pair(address, annot_id)); + } + const auto aSize = ASize(size); + auto annot = std::move(Annotation( + AssumeType::Spinloop, + Annotation::ExprVP(NeExpr::create( + RegisterExpr::create(aSize.getBits(), annot_id), + ConcreteExpr::create(aSize.getBits(), SVal(1))) + .release()))); + + auto &currPos = globalInstructions[thread_id].event; + // auto rLab = LockCasReadLabel::create(++currPos, address, size); + auto rLab = LockCasReadLabel::create(++currPos, address, size, annot); + + // Mutex starts out unlocked, so we always say the previous value is "unlocked". + auto oldValSetter = [this](SAddr loc) { this->handleOldVal(loc, SVal(0)); }; + LoadResult loadResult = GenMCDriver::handleLoad(std::move(rLab), oldValSetter); + if (loadResult.is_error()) { + --currPos; + return MutexLockResult::fromError(*loadResult.error); + } else if (loadResult.is_read_opt) { + --currPos; + // TODO GENMC: is_read_opt == Mutex is acquired + // None --> Someone else has lock, this thread will be rescheduled later (currently + // block) 0 --> Got the lock 1 --> Someone else has lock, this thread will + // not be rescheduled later (block on Miri side) + return MutexLockResult(false); + } + // TODO GENMC(QUESTION): is the `isBlocked` even needed? + // if (!loadResult.has_value() || getCurThr().isBlocked()) + // return; + + const bool is_lock_acquired = loadResult.value() == SVal(0); + if (is_lock_acquired) { + auto wLab = LockCasWriteLabel::create(++currPos, address, size); + StoreResult storeResult = GenMCDriver::handleStore(std::move(wLab), oldValSetter); + if (storeResult.is_error()) + return MutexLockResult::fromError(*storeResult.error); + + } else { + auto bLab = LockNotAcqBlockLabel::create(++currPos); + GenMCDriver::handleBlock(std::move(bLab)); + } + + return MutexLockResult(is_lock_acquired); +} + +auto MiriGenMCShim::handleMutexTryLock(ThreadId thread_id, uint64_t address, uint64_t size) + -> MutexLockResult +{ + auto &currPos = globalInstructions[thread_id].event; + auto rLab = TrylockCasReadLabel::create(++currPos, address, size); + // Mutex starts out unlocked, so we always say the previous value is "unlocked". + auto oldValSetter = [this](SAddr loc) { this->handleOldVal(loc, SVal(0)); }; + LoadResult loadResult = GenMCDriver::handleLoad(std::move(rLab), oldValSetter); + if (!loadResult.has_value()) { + --currPos; + // TODO GENMC: maybe use std move and make it take a unique_ptr ? + return MutexLockResult::fromError(*loadResult.error); + } + + const bool is_lock_acquired = loadResult.value() == SVal(0); + if (!is_lock_acquired) + return MutexLockResult(false); /* Lock already held. */ + + auto wLab = TrylockCasWriteLabel::create(++currPos, address, size); + StoreResult storeResult = GenMCDriver::handleStore(std::move(wLab), oldValSetter); + if (storeResult.is_error()) + return MutexLockResult::fromError(*storeResult.error); + + return MutexLockResult(true); +} + +auto MiriGenMCShim::handleMutexUnlock(ThreadId thread_id, uint64_t address, uint64_t size) + -> StoreResult +{ + return handleStore(thread_id, address, size, SVal(0), SVal(0xDEADBEEF), + MemOrdering::Release, StoreEventType::MutexUnlockWrite); +} diff --git a/genmc-sys/src_cpp/MiriInterface.hpp b/genmc-sys/src_cpp/MiriInterface.hpp index e55522ef41..74e07c78ee 100644 --- a/genmc-sys/src_cpp/MiriInterface.hpp +++ b/genmc-sys/src_cpp/MiriInterface.hpp @@ -6,39 +6,246 @@ #include "config.h" #include "Config/Config.hpp" +#include "ExecutionGraph/EventLabel.hpp" +#include "Static/ModuleID.hpp" +#include "Support/MemOrdering.hpp" +#include "Support/RMWOps.hpp" +#include "Support/ResultHandling.hpp" #include "Verification/GenMCDriver.hpp" -#include +#include +#include +#include +#include /**** Types available to Miri ****/ -// Config struct defined on the Rust side and translated to C++ by cxx.rs: struct GenmcParams; -struct MiriGenMCShim : private GenMCDriver -{ +using ThreadId = int; + +enum class StoreEventType : uint8_t { + Normal, + ReadModifyWrite, + CompareExchange, + MutexUnlockWrite, +}; + +struct MutexLockResult { + bool is_lock_acquired; + std::unique_ptr error; // TODO GENMC: pass more error info here + + MutexLockResult(bool is_lock_acquired) : is_lock_acquired(is_lock_acquired), error(nullptr) + {} + + static auto fromError(std::string msg) -> MutexLockResult + { + auto res = MutexLockResult(false); + res.error = std::make_unique(msg); + return res; + } +}; + +// TODO GENMC: fix naming conventions + +struct MiriGenMCShim : private GenMCDriver { public: MiriGenMCShim(std::shared_ptr conf, Mode mode /* = VerificationMode{} */) : GenMCDriver(std::move(conf), nullptr, mode) { - std::cerr << "C++: GenMC handle created!" << std::endl; + globalInstructions.reserve(8); + globalInstructions.push_back(Action(ActionKind::Load, Event::getInit())); + } + + virtual ~MiriGenMCShim() {} + + /**** Execution start/end handling ****/ + + void handleExecutionStart(); + std::unique_ptr handleExecutionEnd(); + + /**** Memory access handling ****/ + + /////////////////// + [[nodiscard]] LoadResult handleLoad(ThreadId thread_id, uint64_t address, uint64_t size, + MemOrdering ord, GenmcScalar old_val); + [[nodiscard]] ReadModifyWriteResult + handleReadModifyWrite(ThreadId thread_id, uint64_t address, uint64_t size, + MemOrdering loadOrd, MemOrdering store_ordering, RMWBinOp rmw_op, + GenmcScalar rhs_value, GenmcScalar old_val); + [[nodiscard]] CompareExchangeResult + handleCompareExchange(ThreadId thread_id, uint64_t address, uint64_t size, + GenmcScalar expected_value, GenmcScalar new_value, + GenmcScalar old_val, MemOrdering success_load_ordering, + MemOrdering success_store_ordering, MemOrdering fail_load_ordering, + bool can_fail_spuriously); + [[nodiscard]] StoreResult handleStore(ThreadId thread_id, uint64_t address, uint64_t size, + GenmcScalar value, GenmcScalar old_val, + MemOrdering ord, StoreEventType store_event_type); + + void handleFence(ThreadId thread_id, MemOrdering ord); + + /**** Memory (de)allocation ****/ + + uintptr_t handleMalloc(ThreadId thread_id, uint64_t size, uint64_t alignment); + void handleFree(ThreadId thread_id, uint64_t address, uint64_t size); + + /**** Thread management ****/ + + void handleThreadCreate(ThreadId thread_id, ThreadId parent_id); + void handleThreadJoin(ThreadId thread_id, ThreadId child_id); + void handleThreadFinish(ThreadId thread_id, uint64_t ret_val); + + /**** Blocking instructions ****/ + + void handleUserBlock(ThreadId thread_id); + + /**** Mutex handling ****/ + auto handleMutexLock(ThreadId thread_id, uint64_t address, uint64_t size) + -> MutexLockResult; + auto handleMutexTryLock(ThreadId thread_id, uint64_t address, uint64_t size) + -> MutexLockResult; + auto handleMutexUnlock(ThreadId thread_id, uint64_t address, uint64_t size) -> StoreResult; + + /**** Scheduling queries ****/ + + // TODO GENMC: implement + + auto scheduleNext(const int curr_thread_id, const ActionKind curr_thread_next_instr_kind) + -> int64_t; + + /**** TODO GENMC: Other stuff: ****/ + + auto getBlockedExecutionCount() const -> uint64_t + { + return static_cast(getResult().exploredBlocked); } - virtual ~MiriGenMCShim() + auto getExploredExecutionCount() const -> uint64_t { - std::cerr << "C++: GenMC handle destroyed!" << std::endl; + return static_cast(getResult().explored); } - static std::unique_ptr createHandle(const GenmcParams &config); + bool isExplorationDone() { return GenMCDriver::done(); } + + /**** OTHER ****/ + + auto incPos(ThreadId tid) -> Event + { + ERROR_ON(tid >= globalInstructions.size(), "ThreadId out of bounds"); + return ++globalInstructions[tid].event; + } + auto decPos(ThreadId tid) -> Event + { + ERROR_ON(tid >= globalInstructions.size(), "ThreadId out of bounds"); + return --globalInstructions[tid].event; + } + + void printGraph() { GenMCDriver::debugPrintGraph(); } + + void printEstimationResults(const double elapsed_time_sec) const + { + // TODO GENMC(CLEANUP): should this happen on the Rust side? + const auto &res = getResult(); + const auto *conf = getConf(); + + auto mean = std::llround(res.estimationMean); + auto sd = std::llround(std::sqrt(res.estimationVariance)); + auto meanTimeSecs = + (long double)elapsed_time_sec / (res.explored + res.exploredBlocked); + // FIXME(io): restore the old precision after the print? + // PRINT(VerbosityLevel::Error) + // << "Finished estimation in " << std::setprecision(2) << elapsed_time_sec + // << " seconds.\n\n" + // << "Total executions estimate: " << mean << " (+- " << sd << ")\n" + // << "Time to completion estimate: " << std::setprecision(2) + // << (meanTimeSecs * mean) << "s\n"; + PRINT(VerbosityLevel::Error) + << "Finished estimation in " << std::format("%.2f", elapsed_time_sec) + << " seconds.\n\n" + << "Total executions estimate: " << mean << " (+- " << sd << ")\n" + << "Time to completion estimate: " + << std::format("%.2f", meanTimeSecs * mean) << "s\n"; + GENMC_DEBUG(if (conf->printEstimationStats) PRINT(VerbosityLevel::Error) + << "Estimation moot: " << res.exploredMoot << "\n" + << "Estimation blocked: " << res.exploredBlocked << "\n" + << "Estimation complete: " << res.explored << "\n";); + } + + static std::unique_ptr createHandle(const GenmcParams &config, + bool estimation_mode); + +private: + /** + * @brief Try to insert the initial value of a memory location. + * @param addr + * @param value + * */ + void handleOldVal(const SAddr addr, GenmcScalar value) + { + MIRI_LOG() << "handleOldVal: " << addr << ", " << value.value << ", " << value.extra + << ", " << value.is_init << "\n"; + // if (!value.is_init) { + // // // TODO GENMC(uninit value handling) + // // MIRI_LOG() << "WARNING: got uninitialized old value, ignoring ...\n"; + // // return; + // MIRI_LOG() << "WARNING: got uninitialized old value, converting to dummy " + // "value ...\n"; + // value.is_init = true; + // value.value = 0xAAFFAAFF; + // } + + // TODO GENMC(CLEANUP): Pass this as a parameter: + auto &g = getExec().getGraph(); + auto *coLab = g.co_max(addr); + MIRI_LOG() << "handleOldVal: coLab: " << *coLab << "\n"; + if (auto *wLab = llvm::dyn_cast(coLab)) { + MIRI_LOG() + << "handleOldVal: got WriteLabel, atomic: " << !wLab->isNotAtomic() + << "\n"; + if (!value.is_init) + MIRI_LOG() << "WARNING: TODO GENMC: handleOldVal tried to " + "overwrite value of NA " + "reads-from label, but old value is `uninit`\n"; + else if (wLab->isNotAtomic()) + wLab->setVal(value.toSVal()); + } else if (const auto *wLab = llvm::dyn_cast(coLab)) { + if (value.is_init) { + auto result = initVals_.insert(std::make_pair(addr, value)); + MIRI_LOG() << "handleOldVal: got InitLabel, insertion result: " + << result.first->second << ", " << result.second << "\n"; + BUG_ON(result.second && + (*result.first).second != + value); /* Attempt to replace initial value */ + } else { + // LOG(VerbosityLevel::Error) << + MIRI_LOG() << "WARNING: TODO GENMC: handleOldVal tried set initial " + "value, but old " + "value is `uninit`\n"; + } + } else { + BUG(); /* Invalid label */ + } + // either initLabel ==> update initValGetter + // or WriteLabel ==> Update its value in place (only if non-atomic) + } + + // TODO GENMC(mixed-size accesses): + std::unordered_map initVals_{}; + + std::vector globalInstructions; + + std::unordered_map annotation_id{}; + ModuleID::ID annotation_id_counter = 0; }; /**** Functions available to Miri ****/ -// NOTE: CXX doesn't support exposing static methods to Rust currently, so we expose this function instead. -static inline auto createGenmcHandle(const GenmcParams &config) -> std::unique_ptr -{ - return MiriGenMCShim::createHandle(config); -} +// NOTE: CXX doesn't seem to support exposing static methods to Rust, so we expose this +// function instead +std::unique_ptr createGenmcHandle(const GenmcParams &config, bool estimation_mode); + +constexpr auto getGlobalAllocStaticMask() -> uint64_t { return SAddr::staticMask; } #endif /* GENMC_MIRI_INTERFACE_HPP */ diff --git a/miri-script/src/commands.rs b/miri-script/src/commands.rs index 74f1f4a6e8..c1aad7cadf 100644 --- a/miri-script/src/commands.rs +++ b/miri-script/src/commands.rs @@ -499,7 +499,11 @@ impl Command { // The basic command that executes the Miri driver. let mut cmd = if dep { // We invoke the test suite as that has all the logic for running with dependencies. - let cmd = e.cargo_cmd(".", "test", &features).args(&["--test", "ui"]).args(quiet_flag).arg("--"); + let cmd = e + .cargo_cmd(".", "test", &features) + .args(&["--test", "ui"]) + .args(quiet_flag) + .arg("--"); if flamegraph { cmd.args(&["--miri-run-dep-mode-flamegraph"]) } else { diff --git a/src/alloc_addresses/mod.rs b/src/alloc_addresses/mod.rs index 334503d299..d28db3bb8a 100644 --- a/src/alloc_addresses/mod.rs +++ b/src/alloc_addresses/mod.rs @@ -33,6 +33,7 @@ pub struct GlobalStateInner { /// sorted by address. We cannot use a `HashMap` since we can be given an address that is offset /// from the base address, and we need to find the `AllocId` it belongs to. This is not the /// *full* inverse of `base_addr`; dead allocations have been removed. + /// TODO GENMC: keep dead allocations in GenMC mode? int_to_ptr_map: Vec<(u64, AllocId)>, /// The base address for each allocation. We cannot put that into /// `AllocExtra` because function pointers also have a base address, and @@ -98,7 +99,8 @@ impl GlobalStateInner { /// Shifts `addr` to make it aligned with `align` by rounding `addr` to the smallest multiple /// of `align` that is larger or equal to `addr` -fn align_addr(addr: u64, align: u64) -> u64 { +/// FIXME(GenMC): is it ok to make this public? +pub(crate) fn align_addr(addr: u64, align: u64) -> u64 { match addr % align { 0 => addr, rem => addr.strict_add(align) - rem, @@ -116,6 +118,12 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { let this = self.eval_context_ref(); let info = this.get_alloc_info(alloc_id); + // Miri's address assignment leaks state across thread boundaries, which is incompatible + // with GenMC execution. So we instead let GenMC assign addresses to allocations. + if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() { + return genmc_ctx.handle_alloc(this, alloc_id, info.size, info.align, memory_kind); + } + // This is either called immediately after allocation (and then cached), or when // adjusting `tcx` pointers (which never get freed). So assert that we are looking // at a live allocation. This also ensures that we never re-assign an address to an @@ -132,7 +140,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { // Miri's address assignment leaks state across thread boundaries, which is incompatible // with GenMC execution. So we instead let GenMC assign addresses to allocations. if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() { - let addr = genmc_ctx.handle_alloc(&this.machine, info.size, info.align, memory_kind)?; + let addr = + genmc_ctx.handle_alloc(this, alloc_id, info.size, info.align, memory_kind)?; return interp_ok(addr); } @@ -268,7 +277,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // We only use this provenance if it has been exposed, or if the caller requested also non-exposed allocations if !only_exposed_allocations || global_state.exposed.contains(&alloc_id) { // This must still be live, since we remove allocations from `int_to_ptr_map` when they get freed. - debug_assert!(this.is_alloc_live(alloc_id)); + // In GenMC mode, we keep all allocations, so this check doesn't apply there. + debug_assert!( + this.machine.data_race.as_genmc_ref().is_some() || this.is_alloc_live(alloc_id) + ); Some(alloc_id) } else { None @@ -503,11 +515,15 @@ impl<'tcx> MiriMachine<'tcx> { let addr = *global_state.base_addr.get(&dead_id).unwrap(); let pos = global_state.int_to_ptr_map.binary_search_by_key(&addr, |(addr, _)| *addr).unwrap(); - let removed = global_state.int_to_ptr_map.remove(pos); - assert_eq!(removed, (addr, dead_id)); // double-check that we removed the right thing - // We can also remove it from `exposed`, since this allocation can anyway not be returned by - // `alloc_id_from_addr` any more. - global_state.exposed.remove(&dead_id); + + // TODO GENMC(DOCUMENTATION): + if self.data_race.as_genmc_ref().is_none() { + let removed = global_state.int_to_ptr_map.remove(pos); + assert_eq!(removed, (addr, dead_id)); // double-check that we removed the right thing + // We can also remove it from `exposed`, since this allocation can anyway not be returned by + // `alloc_id_from_addr` any more. + global_state.exposed.remove(&dead_id); + } // Also remember this address for future reuse. let thread = self.threads.active_thread(); global_state.reuse.add_addr(rng, addr, size, align, kind, thread, || { diff --git a/src/bin/miri.rs b/src/bin/miri.rs index ae1b25f885..ef4e46b459 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -39,7 +39,7 @@ use std::sync::atomic::{AtomicI32, AtomicU32, Ordering}; use miri::{ BacktraceStyle, BorrowTrackerMethod, GenmcConfig, GenmcCtx, MiriConfig, MiriEntryFnType, - ProvenanceMode, RetagFields, TreeBorrowsParams, ValidationMode, + ProvenanceMode, RetagFields, TreeBorrowsParams, ValidationMode, miri_genmc, }; use rustc_abi::ExternAbi; use rustc_data_structures::sync; @@ -186,10 +186,35 @@ impl rustc_driver::Callbacks for MiriCompilerCalls { optimizations is usually marginal at best."); } - if let Some(_genmc_config) = &config.genmc_config { - let _genmc_ctx = Rc::new(GenmcCtx::new(&config)); + if let Some(genmc_config) = &config.genmc_config { + let eval_entry_once = |genmc_ctx: Rc| { + miri::eval_entry(tcx, entry_def_id, entry_type, &config, Some(genmc_ctx)) + }; + + if genmc_config.do_estimation() + && miri_genmc::run_genmc_mode( + &config, + genmc_config, + eval_entry_once, + miri_genmc::Mode::Estimation, + ) + .is_some() + { + tcx.dcx().abort_if_errors(); + } + + let return_code = miri_genmc::run_genmc_mode( + &config, + genmc_config, + eval_entry_once, + miri_genmc::Mode::Verification, + ) + .unwrap_or_else(|| { + tcx.dcx().abort_if_errors(); + rustc_driver::EXIT_FAILURE + }); - todo!("GenMC mode not yet implemented"); + exit(return_code); }; if let Some(many_seeds) = self.many_seeds.take() { @@ -735,9 +760,11 @@ fn main() { // Validate settings for data race detection and GenMC mode. if miri_config.genmc_config.is_some() { if !miri_config.data_race_detector { - fatal_error!("Cannot disable data race detection in GenMC mode (currently)"); + fatal_error!("Cannot disable data race detection in GenMC mode"); } else if !miri_config.weak_memory_emulation { fatal_error!("Cannot disable weak memory emulation in GenMC mode"); + } else if !miri_config.native_lib.is_empty() { + fatal_error!("native-lib not supported in GenMC mode."); } if miri_config.borrow_tracker.is_some() { eprintln!( diff --git a/src/concurrency/data_race.rs b/src/concurrency/data_race.rs index 38d76f5cf7..c9112cda08 100644 --- a/src/concurrency/data_race.rs +++ b/src/concurrency/data_race.rs @@ -71,7 +71,7 @@ pub enum AtomicRwOrd { } /// Valid atomic read orderings, subset of atomic::Ordering. -#[derive(Copy, Clone, PartialEq, Eq, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] pub enum AtomicReadOrd { Relaxed, Acquire, @@ -719,8 +719,7 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { // Only metadata on the location itself is used. if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() { - // FIXME(GenMC): Inform GenMC what a non-atomic read here would return, to support mixed atomics/non-atomics - let old_val = None; + let old_val = this.run_for_validation_ref(|this| this.read_scalar(place)).discard_err(); return genmc_ctx.atomic_load( this, place.ptr().addr(), @@ -752,10 +751,21 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { // This is also a very special exception where we just ignore an error -- if this read // was UB e.g. because the memory is uninitialized, we don't want to know! let old_val = this.run_for_validation_mut(|this| this.read_scalar(dest)).discard_err(); + // Inform GenMC about the atomic store. if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() { - // FIXME(GenMC): Inform GenMC what a non-atomic read here would return, to support mixed atomics/non-atomics - genmc_ctx.atomic_store(this, dest.ptr().addr(), dest.layout.size, val, atomic)?; + if genmc_ctx.atomic_store( + this, + dest.ptr().addr(), + dest.layout.size, + val, + old_val, + atomic, + )? { + // The store might be the latest store in coherence order (determined by GenMC). + // If it is, we need to update the value in Miri's memory: + this.allow_data_races_mut(|this| this.write_scalar(val, dest))?; + } return interp_ok(()); } this.allow_data_races_mut(move |this| this.write_scalar(val, dest))?; @@ -779,7 +789,6 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { // Inform GenMC about the atomic rmw operation. if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() { - // FIXME(GenMC): Inform GenMC what a non-atomic read here would return, to support mixed atomics/non-atomics let (old_val, new_val) = genmc_ctx.atomic_rmw_op( this, place.ptr().addr(), @@ -787,8 +796,11 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { atomic, (op, not), rhs.to_scalar(), + old.to_scalar(), )?; - this.allow_data_races_mut(|this| this.write_scalar(new_val, place))?; + if let Some(new_val) = new_val { + this.allow_data_races_mut(|this| this.write_scalar(new_val, place))?; + } return interp_ok(ImmTy::from_scalar(old_val, old.layout)); } @@ -818,14 +830,19 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { // Inform GenMC about the atomic atomic exchange. if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() { - // FIXME(GenMC): Inform GenMC what a non-atomic read here would return, to support mixed atomics/non-atomics - let (old_val, _is_success) = genmc_ctx.atomic_exchange( + let (old_val, new_val) = genmc_ctx.atomic_exchange( this, place.ptr().addr(), place.layout.size, new, atomic, + old, )?; + // The store might be the latest store in coherence order (determined by GenMC). + // If it is, we need to update the value in Miri's memory: + if let Some(new_val) = new_val { + this.allow_data_races_mut(|this| this.write_scalar(new_val, place))?; + } return interp_ok(old_val); } @@ -851,7 +868,6 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { // Inform GenMC about the atomic min/max operation. if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() { - // FIXME(GenMC): Inform GenMC what a non-atomic read here would return, to support mixed atomics/non-atomics let (old_val, new_val) = genmc_ctx.atomic_min_max_op( this, place.ptr().addr(), @@ -860,8 +876,13 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { min, old.layout.backend_repr.is_signed(), rhs.to_scalar(), + old.to_scalar(), )?; - this.allow_data_races_mut(|this| this.write_scalar(new_val, place))?; + // The store might be the latest store in coherence order (determined by GenMC). + // If it is, we need to update the value in Miri's memory: + if let Some(new_val) = new_val { + this.allow_data_races_mut(|this| this.write_scalar(new_val, place))?; + } return interp_ok(ImmTy::from_scalar(old_val, old.layout)); } @@ -903,6 +924,7 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { let this = self.eval_context_mut(); this.atomic_access_check(place, AtomicAccessType::Rmw)?; + // // FIXME(GenMC): this comment is wrong: // Failure ordering cannot be stronger than success ordering, therefore first attempt // to read with the failure ordering and if successful then try again with the success // read ordering and write in the success case. @@ -911,20 +933,24 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { // Inform GenMC about the atomic atomic compare exchange. if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() { - let (old, cmpxchg_success) = genmc_ctx.atomic_compare_exchange( - this, - place.ptr().addr(), - place.layout.size, - this.read_scalar(expect_old)?, - new, - success, - fail, - can_fail_spuriously, - )?; - if cmpxchg_success { + let (old_value, is_co_maximal_write, cmpxchg_success) = genmc_ctx + .atomic_compare_exchange( + this, + place.ptr().addr(), + place.layout.size, + this.read_scalar(expect_old)?, + new, + success, + fail, + can_fail_spuriously, + old.to_scalar(), + )?; + // The store might be the latest store in coherence order (determined by GenMC). + // If it is, we need to update the value in Miri's memory: + if is_co_maximal_write { this.allow_data_races_mut(|this| this.write_scalar(new, place))?; } - return interp_ok(Immediate::ScalarPair(old, Scalar::from_bool(cmpxchg_success))); + return interp_ok(Immediate::ScalarPair(old_value, Scalar::from_bool(cmpxchg_success))); } // `binary_op` will bail if either of them is not a scalar. @@ -990,6 +1016,7 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { if let Some(data_race) = this.machine.data_race.as_vclocks_ref() { data_race.acquire_clock(clock, &this.machine.threads); } + // TODO GENMC: does GenMC need to be informed about this? } } diff --git a/src/concurrency/genmc/config.rs b/src/concurrency/genmc/config.rs index c56adab90f..2a7c0e9454 100644 --- a/src/concurrency/genmc/config.rs +++ b/src/concurrency/genmc/config.rs @@ -6,11 +6,24 @@ use super::GenmcParams; #[derive(Debug, Default, Clone)] pub struct GenmcConfig { pub(super) params: GenmcParams, + print_exec_graphs: bool, do_estimation: bool, - // FIXME(GenMC): add remaining options. } impl GenmcConfig { + fn set_log_level_trace(&mut self) { + self.params.quiet = false; + self.params.log_level_trace = true; + } + + pub fn print_exec_graphs(&self) -> bool { + self.print_exec_graphs + } + + pub fn do_estimation(&self) -> bool { + self.do_estimation + } + /// Function for parsing command line options for GenMC mode. /// /// All GenMC arguments start with the string "-Zmiri-genmc". @@ -29,7 +42,36 @@ impl GenmcConfig { if trimmed_arg.is_empty() { return Ok(()); // this corresponds to "-Zmiri-genmc" } - // FIXME(GenMC): implement remaining parameters. - todo!(); + let genmc_config = genmc_config.as_mut().unwrap(); + let Some(trimmed_arg) = trimmed_arg.strip_prefix("-") else { + return Err(format!("Invalid GenMC argument \"-Zmiri-genmc{trimmed_arg}\"")); + }; + if trimmed_arg == "log-trace" { + // TODO GENMC: maybe expand to allow more control over log level? + genmc_config.set_log_level_trace(); + } else if trimmed_arg == "print-graphs" { + // TODO GENMC (DOCUMENTATION) + genmc_config.print_exec_graphs = true; + } else if trimmed_arg == "estimate" { + // TODO GENMC (DOCUMENTATION): naming, off/on by default? + genmc_config.do_estimation = true; + } else if let Some(estimation_max_str) = trimmed_arg.strip_prefix("estimation-max=") { + // TODO GENMC (DOCUMENTATION) + let Some(estimation_max) = + estimation_max_str.parse().ok().filter(|estimation_max| *estimation_max > 0) + else { + return Err(format!( + "-Zmiri-genmc-estimation-max expects a positive integer argument, but got '{estimation_max_str}'" + )); + }; + genmc_config.params.estimation_max = estimation_max; + } else if trimmed_arg == "symmetry-reduction" { + // TODO GENMC (PERFORMANCE): maybe make this the default, have an option to turn it off instead + genmc_config.params.do_symmetry_reduction = true; + } else { + // TODO GENMC: how to properly handle this? + return Err(format!("Invalid GenMC argument: \"-Zmiri-genmc-{trimmed_arg}\"")); + } + Ok(()) } } diff --git a/src/concurrency/genmc/dummy.rs b/src/concurrency/genmc/dummy.rs index 79d27c4be1..74f054c4ce 100644 --- a/src/concurrency/genmc/dummy.rs +++ b/src/concurrency/genmc/dummy.rs @@ -1,12 +1,10 @@ -#![allow(unused)] - use rustc_abi::{Align, Size}; -use rustc_const_eval::interpret::{InterpCx, InterpResult}; +use rustc_const_eval::interpret::{AllocId, InterpCx, InterpResult}; use rustc_middle::mir; use crate::{ AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, MemoryKind, MiriConfig, - MiriMachine, Scalar, ThreadId, ThreadManager, VisitProvenance, VisitWith, + MiriMachine, OpTy, Scalar, ThreadId, ThreadManager, VisitProvenance, VisitWith, }; #[derive(Debug)] @@ -15,12 +13,41 @@ pub struct GenmcCtx {} #[derive(Debug, Default, Clone)] pub struct GenmcConfig {} +pub mod miri_genmc { + use std::rc::Rc; + + use crate::{GenmcConfig, GenmcCtx, MiriConfig}; + + #[derive(Clone, Copy, PartialEq, Eq)] + pub enum Mode { + Estimation, + Verification, + } + + pub fn run_genmc_mode( + _config: &MiriConfig, + _genmc_config: &GenmcConfig, + _eval_entry: impl Fn(Rc) -> Option, + _mode: Mode, + ) -> Option { + unreachable!(); + } +} + impl GenmcCtx { pub fn new(_miri_config: &MiriConfig) -> Self { unreachable!() } - pub fn get_stuck_execution_count(&self) -> usize { + pub fn print_estimation_result(&self) { + unreachable!() + } + + pub fn get_blocked_execution_count(&self) -> usize { + unreachable!() + } + + pub fn get_explored_execution_count(&self) -> usize { unreachable!() } @@ -32,6 +59,10 @@ impl GenmcCtx { unreachable!() } + pub fn get_exit_status(&self) -> Option<(i32, bool)> { + unreachable!() + } + /**** Memory access handling ****/ pub(crate) fn handle_execution_start(&self) { @@ -67,8 +98,9 @@ impl GenmcCtx { _address: Size, _size: Size, _value: Scalar, + _old_value: Option, _ordering: AtomicWriteOrd, - ) -> InterpResult<'tcx, ()> { + ) -> InterpResult<'tcx, bool> { unreachable!() } @@ -76,7 +108,7 @@ impl GenmcCtx { &self, _machine: &MiriMachine<'tcx>, _ordering: AtomicFenceOrd, - ) -> InterpResult<'tcx, ()> { + ) -> InterpResult<'tcx> { unreachable!() } @@ -86,22 +118,24 @@ impl GenmcCtx { _address: Size, _size: Size, _ordering: AtomicRwOrd, - (rmw_op, not): (mir::BinOp, bool), + (_rmw_op, _not): (mir::BinOp, bool), _rhs_scalar: Scalar, - ) -> InterpResult<'tcx, (Scalar, Scalar)> { + _old_value: Scalar, + ) -> InterpResult<'tcx, (Scalar, Option)> { unreachable!() } pub(crate) fn atomic_min_max_op<'tcx>( &self, - ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, - address: Size, - size: Size, - ordering: AtomicRwOrd, - min: bool, - is_signed: bool, - rhs_scalar: Scalar, - ) -> InterpResult<'tcx, (Scalar, Scalar)> { + _ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, + _address: Size, + _size: Size, + _ordering: AtomicRwOrd, + _min: bool, + _is_signed: bool, + _rhs_scalar: Scalar, + _old_value: Scalar, + ) -> InterpResult<'tcx, (Scalar, Option)> { unreachable!() } @@ -112,7 +146,8 @@ impl GenmcCtx { _size: Size, _rhs_scalar: Scalar, _ordering: AtomicRwOrd, - ) -> InterpResult<'tcx, (Scalar, bool)> { + _old_value: Scalar, + ) -> InterpResult<'tcx, (Scalar, Option)> { unreachable!() } @@ -126,7 +161,8 @@ impl GenmcCtx { _success: AtomicRwOrd, _fail: AtomicReadOrd, _can_fail_spuriously: bool, - ) -> InterpResult<'tcx, (Scalar, bool)> { + _old_value: Scalar, + ) -> InterpResult<'tcx, (Scalar, bool, bool)> { unreachable!() } @@ -135,7 +171,7 @@ impl GenmcCtx { _machine: &MiriMachine<'tcx>, _address: Size, _size: Size, - ) -> InterpResult<'tcx, ()> { + ) -> InterpResult<'tcx> { unreachable!() } @@ -144,7 +180,7 @@ impl GenmcCtx { _machine: &MiriMachine<'tcx>, _address: Size, _size: Size, - ) -> InterpResult<'tcx, ()> { + ) -> InterpResult<'tcx> { unreachable!() } @@ -152,7 +188,8 @@ impl GenmcCtx { pub(crate) fn handle_alloc<'tcx>( &self, - _machine: &MiriMachine<'tcx>, + _ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, + _alloc_id: AllocId, _size: Size, _alignment: Align, _memory_kind: MemoryKind, @@ -163,11 +200,12 @@ impl GenmcCtx { pub(crate) fn handle_dealloc<'tcx>( &self, _machine: &MiriMachine<'tcx>, + _alloc_id: AllocId, _address: Size, _size: Size, _align: Align, _kind: MemoryKind, - ) -> InterpResult<'tcx, ()> { + ) -> InterpResult<'tcx> { unreachable!() } @@ -176,8 +214,10 @@ impl GenmcCtx { pub(crate) fn handle_thread_create<'tcx>( &self, _threads: &ThreadManager<'tcx>, + _start_routine: crate::Pointer, + _func_arg: &crate::ImmTy<'tcx>, _new_thread_id: ThreadId, - ) -> InterpResult<'tcx, ()> { + ) -> InterpResult<'tcx> { unreachable!() } @@ -185,37 +225,46 @@ impl GenmcCtx { &self, _active_thread_id: ThreadId, _child_thread_id: ThreadId, - ) -> InterpResult<'tcx, ()> { + ) -> InterpResult<'tcx> { unreachable!() } - pub(crate) fn handle_thread_stack_empty(&self, _thread_id: ThreadId) { + pub(crate) fn handle_thread_finish<'tcx>(&self, _threads: &ThreadManager<'tcx>) { unreachable!() } - pub(crate) fn handle_thread_finish<'tcx>( + pub(crate) fn handle_exit<'tcx>( &self, - _threads: &ThreadManager<'tcx>, - ) -> InterpResult<'tcx, ()> { + _thread: ThreadId, + _exit_code: i32, + _is_exit_call: bool, + ) -> InterpResult<'tcx> { + unreachable!() + } +} + +/// Other functionality not directly related to event handling +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { + fn check_genmc_intercept_function( + &mut self, + _instance: rustc_middle::ty::Instance<'tcx>, + _args: &[rustc_const_eval::interpret::FnArg<'tcx, crate::Provenance>], + _dest: &crate::PlaceTy<'tcx>, + _ret: Option, + ) -> InterpResult<'tcx, bool> { unreachable!() } /**** Scheduling functionality ****/ - pub(crate) fn schedule_thread<'tcx>( - &self, - _ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, - ) -> InterpResult<'tcx, ThreadId> { + fn genmc_schedule_thread(&mut self) -> InterpResult<'tcx, ThreadId> { unreachable!() } /**** Blocking instructions ****/ - pub(crate) fn handle_verifier_assume<'tcx>( - &self, - _machine: &MiriMachine<'tcx>, - _condition: bool, - ) -> InterpResult<'tcx, ()> { + fn handle_genmc_verifier_assume(&mut self, _condition: &OpTy<'tcx>) -> InterpResult<'tcx> { unreachable!() } } @@ -229,7 +278,7 @@ impl VisitProvenance for GenmcCtx { impl GenmcConfig { pub fn parse_arg( _genmc_config: &mut Option, - trimmed_arg: &str, + _trimmed_arg: &str, ) -> Result<(), String> { if cfg!(feature = "genmc") { Err(format!("GenMC is disabled in this build of Miri")) @@ -238,7 +287,11 @@ impl GenmcConfig { } } - pub fn should_print_graph(&self, _rep: usize) -> bool { + pub fn print_exec_graphs(&self) -> bool { + unreachable!() + } + + pub fn do_estimation(&self) -> bool { unreachable!() } } diff --git a/src/concurrency/genmc/global_allocations.rs b/src/concurrency/genmc/global_allocations.rs new file mode 100644 index 0000000000..e2fafba9cc --- /dev/null +++ b/src/concurrency/genmc/global_allocations.rs @@ -0,0 +1,145 @@ +use std::cmp::max; +use std::collections::hash_map::Entry; +use std::sync::RwLock; + +use genmc_sys::{GENMC_GLOBAL_ADDRESSES_MASK, getGlobalAllocStaticMask}; +use rand::rngs::StdRng; +use rand::{Rng, SeedableRng}; +use rustc_const_eval::interpret::{ + AllocId, AllocInfo, AllocKind, InterpResult, PointerArithmetic, interp_ok, +}; +use rustc_data_structures::fx::FxHashMap; +use rustc_middle::{err_exhaust, throw_exhaust}; +use tracing::info; + +use crate::alloc_addresses::align_addr; + +#[derive(Debug, Default)] +pub struct GlobalAllocationHandler { + inner: RwLock, +} + +/// This contains more or less a subset of the functionality of `struct GlobalStateInner` in `alloc_addresses`. +#[derive(Clone, Debug)] +struct GlobalStateInner { + /// This is used as a map between the address of each allocation and its `AllocId`. It is always + /// sorted by address. We cannot use a `HashMap` since we can be given an address that is offset + /// from the base address, and we need to find the `AllocId` it belongs to. This is not the + /// *full* inverse of `base_addr`; dead allocations have been removed. + #[allow(unused)] // FIXME(GenMC): do we need this? + int_to_ptr_map: Vec<(u64, AllocId)>, + /// The base address for each allocation. + /// This is the inverse of `int_to_ptr_map`. + base_addr: FxHashMap, + /// This is used as a memory address when a new pointer is casted to an integer. It + /// is always larger than any address that was previously made part of a block. + next_base_addr: u64, + /// To add some randomness to the allocations + /// FIXME(GenMC): maybe seed this from the rng in MiriMachine? + rng: StdRng, +} + +impl Default for GlobalStateInner { + fn default() -> Self { + Self::new() + } +} + +impl GlobalStateInner { + pub fn new() -> Self { + assert_eq!(GENMC_GLOBAL_ADDRESSES_MASK, getGlobalAllocStaticMask()); + assert_ne!(GENMC_GLOBAL_ADDRESSES_MASK, 0); + Self { + int_to_ptr_map: Vec::default(), + base_addr: FxHashMap::default(), + next_base_addr: GENMC_GLOBAL_ADDRESSES_MASK, + rng: StdRng::seed_from_u64(0), + } + } + + fn global_allocate_addr<'tcx>( + &mut self, + alloc_id: AllocId, + info: AllocInfo, + ) -> InterpResult<'tcx, u64> { + let entry = match self.base_addr.entry(alloc_id) { + Entry::Occupied(occupied_entry) => { + // Looks like some other thread allocated this for us + // between when we released the read lock and aquired the write lock, + // so we just return that value. + return interp_ok(*occupied_entry.get()); + } + Entry::Vacant(vacant_entry) => vacant_entry, + }; + + // This is either called immediately after allocation (and then cached), or when + // adjusting `tcx` pointers (which never get freed). So assert that we are looking + // at a live allocation. This also ensures that we never re-assign an address to an + // allocation that previously had an address, but then was freed and the address + // information was removed. + assert!(!matches!(info.kind, AllocKind::Dead)); + + // This allocation does not have a base address yet, pick or reuse one. + + // We are not in native lib mode, so we control the addresses ourselves. + + // We have to pick a fresh address. + // Leave some space to the previous allocation, to give it some chance to be less aligned. + // We ensure that `(global_state.next_base_addr + slack) % 16` is uniformly distributed. + let slack = self.rng.random_range(0..16); + // From next_base_addr + slack, round up to adjust for alignment. + let base_addr = + self.next_base_addr.checked_add(slack).ok_or_else(|| err_exhaust!(AddressSpaceFull))?; + let base_addr = align_addr(base_addr, info.align.bytes()); + + // Remember next base address. If this allocation is zero-sized, leave a gap of at + // least 1 to avoid two allocations having the same base address. (The logic in + // `alloc_id_from_addr` assumes unique addresses, and different function/vtable pointers + // need to be distinguishable!) + self.next_base_addr = base_addr + .checked_add(max(info.size.bytes(), 1)) + .ok_or_else(|| err_exhaust!(AddressSpaceFull))?; + + assert_ne!(0, base_addr & GENMC_GLOBAL_ADDRESSES_MASK); + assert_ne!(0, self.next_base_addr & GENMC_GLOBAL_ADDRESSES_MASK); + // Cache the address for future use. + entry.insert(base_addr); + + interp_ok(base_addr) + } +} + +// FIXME(GenMC): "ExtPriv" or "PrivExt"? +impl<'tcx> EvalContextExtPriv<'tcx> for crate::MiriInterpCx<'tcx> {} +pub(super) trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { + fn get_global_allocation_address( + &self, + global_allocation_handler: &GlobalAllocationHandler, + alloc_id: AllocId, + ) -> InterpResult<'tcx, u64> { + let this = self.eval_context_ref(); + let info = this.get_alloc_info(alloc_id); + + let global_state = global_allocation_handler.inner.read().unwrap(); + if let Some(base_addr) = global_state.base_addr.get(&alloc_id) { + info!( + "GenMC: address for global with alloc id {alloc_id:?} was cached: {base_addr} == {base_addr:#x}" + ); + return interp_ok(*base_addr); + } + + drop(global_state); + // We need to upgrade to a write lock. std::sync::RwLock doesn't support this, so we drop the guard and lock again + // Note that another thread might run in between and allocate the address, but we handle this case in the allocation function. + let mut global_state = global_allocation_handler.inner.write().unwrap(); + let base_addr = global_state.global_allocate_addr(alloc_id, info)?; + // Even if `Size` didn't overflow, we might still have filled up the address space. + if global_state.next_base_addr > this.target_usize_max() { + throw_exhaust!(AddressSpaceFull); + } + info!( + "GenMC: global with alloc id {alloc_id:?} got address: {base_addr} == {base_addr:#x}" + ); + interp_ok(base_addr) + } +} diff --git a/src/concurrency/genmc/helper.rs b/src/concurrency/genmc/helper.rs new file mode 100644 index 0000000000..4d083196e7 --- /dev/null +++ b/src/concurrency/genmc/helper.rs @@ -0,0 +1,199 @@ +use rustc_abi::Size; +use rustc_const_eval::interpret::{InterpCx, InterpResult, interp_ok}; +use rustc_middle::mir::{Terminator, TerminatorKind}; +use rustc_middle::ty::{self, ScalarInt, Ty}; +use tracing::info; + +use super::GenmcScalar; +use crate::alloc_addresses::EvalContextExt as _; +use crate::{ + BorTag, MiriInterpCx, MiriMachine, Pointer, Provenance, Scalar, ThreadId, throw_unsup_format, +}; + +pub fn split_access(address: Size, size: Size) -> impl Iterator { + /// Maximum size memory access in bytes that GenMC supports. + const MAX_SIZE: u64 = 8; + + let size_bytes = size.bytes(); + + let start_address = address.bytes(); + let end_address = start_address + size_bytes; + let start_missing = (MAX_SIZE - (start_address % MAX_SIZE)) % MAX_SIZE; + let end_missing = end_address % MAX_SIZE; + + let start_address_aligned = start_address + start_missing; + let end_address_aligned = end_address - end_missing; + + info!( + "GenMC: splitting NA memory access into {MAX_SIZE} byte chunks: {start_missing}B + {} * {MAX_SIZE}B + {end_missing}B = {size:?}", + (end_address_aligned - start_address_aligned) / MAX_SIZE + ); + debug_assert_eq!( + 0, + start_address_aligned % MAX_SIZE, + "Incorrectly aligned start address: {start_address_aligned} % {MAX_SIZE} != 0, {start_address} + {start_missing}" + ); + debug_assert_eq!( + 0, + end_address_aligned % MAX_SIZE, + "Incorrectly aligned end address: {end_address_aligned} % {MAX_SIZE} != 0, {end_address} - {end_missing}" + ); + debug_assert!(start_missing < MAX_SIZE && end_missing < MAX_SIZE); + + // FIXME(genmc): could make remaining accesses powers-of-2, instead of 1 byte. + let start_chunks = (start_address..start_address_aligned).map(|address| (address, 1)); + let aligned_chunks = (start_address_aligned..end_address_aligned) + .step_by(MAX_SIZE.try_into().unwrap()) + .map(|address| (address, MAX_SIZE)); + let end_chunks = (end_address_aligned..end_address).map(|address| (address, 1)); + + start_chunks.chain(aligned_chunks).chain(end_chunks) +} + +/// Like `scalar_to_genmc_scalar`, but returns an error if the scalar is not an integer +pub fn rhs_scalar_to_genmc_scalar<'tcx>( + ecx: &MiriInterpCx<'tcx>, + scalar: Scalar, +) -> InterpResult<'tcx, GenmcScalar> { + if matches!(scalar, Scalar::Ptr(..)) { + throw_unsup_format!("Right hand side of atomic operation cannot be a pointer"); + } + scalar_to_genmc_scalar(ecx, scalar) +} + +pub fn option_scalar_to_genmc_scalar<'tcx>( + ecx: &MiriInterpCx<'tcx>, + maybe_scalar: Option, +) -> InterpResult<'tcx, GenmcScalar> { + if let Some(scalar) = maybe_scalar { + scalar_to_genmc_scalar(ecx, scalar) + } else { + interp_ok(GenmcScalar::UNINIT) + } +} + +pub fn scalar_to_genmc_scalar<'tcx>( + ecx: &MiriInterpCx<'tcx>, + scalar: Scalar, +) -> InterpResult<'tcx, GenmcScalar> { + interp_ok(match scalar { + rustc_const_eval::interpret::Scalar::Int(scalar_int) => { + // TODO GENMC: u128 support + let value: u64 = scalar_int.to_uint(scalar_int.size()).try_into().unwrap(); // TODO GENMC: doesn't work for size != 8 + GenmcScalar { value, extra: 0, is_init: true } + } + rustc_const_eval::interpret::Scalar::Ptr(pointer, size) => { + let addr = Pointer::from(pointer).addr(); + if let Provenance::Wildcard = pointer.provenance { + throw_unsup_format!("Pointers with wildcard provenance not allowed in GenMC mode"); + } + let (alloc_id, _size, _prov_extra) = + rustc_const_eval::interpret::Machine::ptr_get_alloc(ecx, pointer, size.into()) + .unwrap(); + let base_addr = ecx.addr_from_alloc_id(alloc_id, None)?; + GenmcScalar { value: addr.bytes(), extra: base_addr, is_init: true } + } + }) +} + +pub fn genmc_scalar_to_scalar<'tcx>( + ecx: &MiriInterpCx<'tcx>, + scalar: GenmcScalar, + size: Size, +) -> InterpResult<'tcx, Scalar> { + // TODO GENMC: proper handling of large integers + // TODO GENMC: proper handling of pointers (currently assumes all integers) + + if scalar.extra != 0 { + // We have a pointer! + + let addr = Size::from_bytes(scalar.value); + let base_addr = scalar.extra; + + let alloc_size = 0; // TODO GENMC: what is the correct size here? Is 0 ok? + let only_exposed_allocations = false; + let Some(alloc_id) = + ecx.alloc_id_from_addr(base_addr, alloc_size, only_exposed_allocations) + else { + // TODO GENMC: what is the correct error in this case? + throw_unsup_format!( + "Cannot get allocation id of pointer received from GenMC (base address: 0x{base_addr:x}, pointer address: 0x{:x})", + addr.bytes() + ); + }; + + // TODO GENMC: is using `size: Size` ok here? Can we ever have `size != sizeof pointer`? + + // FIXME: Currently GenMC mode incompatible with aliasing model checking + let tag = BorTag::default(); + let provenance = crate::machine::Provenance::Concrete { alloc_id, tag }; + let offset = addr; + let ptr = rustc_middle::mir::interpret::Pointer::new(provenance, offset); + + let size = size.bytes().try_into().unwrap(); + return interp_ok(Scalar::Ptr(ptr, size)); + } + + // NOTE: GenMC always returns 64 bit values, and the upper bits are not yet truncated. + // FIXME(genmc): Rework if 128bit support is added to GenMC. + let trunc_value = if size.bits() >= 64 { + scalar.value + } else { + // Zero out the upper bits. + let mask = (1u64 << size.bits()) - 1; + scalar.value & mask + }; + + let value_scalar_int = ScalarInt::try_from_uint(trunc_value, size).unwrap(); + interp_ok(Scalar::Int(value_scalar_int)) +} + +pub fn is_terminator_atomic<'tcx>( + ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, + terminator: &Terminator<'tcx>, + thread_id: ThreadId, +) -> InterpResult<'tcx, bool> { + match &terminator.kind { + // All atomics are modeled as function calls to intrinsic functions. + // The one exception is thread joining, but those are also calls. + TerminatorKind::Call { func, .. } | TerminatorKind::TailCall { func, .. } => { + let frame = ecx.machine.threads.get_thread_stack(thread_id).last().unwrap(); + let func_ty = func.ty(&frame.body().local_decls, *ecx.tcx); + info!("GenMC: terminator is a call with operand: {func:?}, ty of operand: {func_ty:?}"); + + is_function_atomic(ecx, func_ty) + } + _ => interp_ok(false), + } +} + +fn is_function_atomic<'tcx>( + ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, + func_ty: Ty<'tcx>, + // func: &Operand<'tcx>, +) -> InterpResult<'tcx, bool> { + let callee_def_id = match func_ty.kind() { + ty::FnDef(def_id, _args) => def_id, + _ => return interp_ok(true), // we don't know the callee, might be an intrinsic or pthread_join + }; + if ecx.tcx.is_foreign_item(*callee_def_id) { + // Some shims, like pthread_join, must be considered loads. So just consider them all loads, + // these calls are not *that* common. + return interp_ok(true); + } + + let Some(intrinsic_def) = ecx.tcx.intrinsic(callee_def_id) else { + // TODO GENMC: Make this work for other platforms? + let item_name = ecx.tcx.item_name(*callee_def_id); + info!("GenMC: function DefId: {callee_def_id:?}, item name: {item_name:?}"); + if matches!(item_name.as_str(), "pthread_join" | "WaitForSingleObject") { + info!("GenMC: found a 'join' terminator: '{}'", item_name.as_str(),); + return interp_ok(true); + } + return interp_ok(false); + }; + let intrinsice_name = intrinsic_def.name.as_str(); + info!("GenMC: intrinsic name: '{intrinsice_name}'"); + // TODO GENMC(ENHANCEMENT): make this more precise (only loads). How can we make this maintainable? + interp_ok(intrinsice_name.starts_with("atomic_")) +} diff --git a/src/concurrency/genmc/mapping.rs b/src/concurrency/genmc/mapping.rs new file mode 100644 index 0000000000..5a64eebef9 --- /dev/null +++ b/src/concurrency/genmc/mapping.rs @@ -0,0 +1,83 @@ +use genmc_sys::{MemOrdering, RMWBinOp}; + +use crate::{AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd}; + +impl AtomicReadOrd { + pub(super) fn convert(self) -> MemOrdering { + match self { + AtomicReadOrd::Relaxed => MemOrdering::Relaxed, + AtomicReadOrd::Acquire => MemOrdering::Acquire, + AtomicReadOrd::SeqCst => MemOrdering::SequentiallyConsistent, + } + } +} + +impl AtomicWriteOrd { + pub(super) fn convert(self) -> MemOrdering { + match self { + AtomicWriteOrd::Relaxed => MemOrdering::Relaxed, + AtomicWriteOrd::Release => MemOrdering::Release, + AtomicWriteOrd::SeqCst => MemOrdering::SequentiallyConsistent, + } + } +} + +impl AtomicFenceOrd { + pub(super) fn convert(self) -> MemOrdering { + match self { + AtomicFenceOrd::Acquire => MemOrdering::Acquire, + AtomicFenceOrd::Release => MemOrdering::Release, + AtomicFenceOrd::AcqRel => MemOrdering::AcquireRelease, + AtomicFenceOrd::SeqCst => MemOrdering::SequentiallyConsistent, + } + } +} + +impl AtomicRwOrd { + /// Split up an atomic read-write memory ordering into a separate read and write ordering. + pub(super) fn split_memory_orderings(self) -> (AtomicReadOrd, AtomicWriteOrd) { + match self { + AtomicRwOrd::Relaxed => (AtomicReadOrd::Relaxed, AtomicWriteOrd::Relaxed), + AtomicRwOrd::Acquire => (AtomicReadOrd::Acquire, AtomicWriteOrd::Relaxed), + AtomicRwOrd::Release => (AtomicReadOrd::Relaxed, AtomicWriteOrd::Release), + AtomicRwOrd::AcqRel => (AtomicReadOrd::Acquire, AtomicWriteOrd::Release), + AtomicRwOrd::SeqCst => (AtomicReadOrd::SeqCst, AtomicWriteOrd::SeqCst), + } + } + + /// Split up the atomic success ordering of a read-modify-write operation into GenMC's representation. + /// Note that both returned orderings are currently identical, because this is what GenMC expects. + pub(super) fn to_genmc_memory_orderings(self) -> (MemOrdering, MemOrdering) { + match self { + AtomicRwOrd::Relaxed => (MemOrdering::Relaxed, MemOrdering::Relaxed), + AtomicRwOrd::Acquire => (MemOrdering::Acquire, MemOrdering::Acquire), + AtomicRwOrd::Release => (MemOrdering::Release, MemOrdering::Release), + AtomicRwOrd::AcqRel => (MemOrdering::AcquireRelease, MemOrdering::AcquireRelease), + AtomicRwOrd::SeqCst => + (MemOrdering::SequentiallyConsistent, MemOrdering::SequentiallyConsistent), + } + } +} + +pub(super) fn min_max_to_genmc_rmw_op(min: bool, is_signed: bool) -> RMWBinOp { + match (min, is_signed) { + (true, true) => RMWBinOp::Min, // TODO GENMC: is there a use for FMin? (Min, UMin, FMin) + (false, true) => RMWBinOp::Max, + (true, false) => RMWBinOp::UMin, + (false, false) => RMWBinOp::UMax, + } +} + +pub(super) fn to_genmc_rmw_op(bin_op: rustc_middle::mir::BinOp, negate: bool) -> RMWBinOp { + match bin_op { + rustc_middle::mir::BinOp::Add => RMWBinOp::Add, + rustc_middle::mir::BinOp::Sub => RMWBinOp::Sub, + rustc_middle::mir::BinOp::BitOr if !negate => RMWBinOp::Or, + rustc_middle::mir::BinOp::BitXor if !negate => RMWBinOp::Xor, + rustc_middle::mir::BinOp::BitAnd if negate => RMWBinOp::Nand, + rustc_middle::mir::BinOp::BitAnd => RMWBinOp::And, + _ => { + panic!("unsupported atomic operation: bin_op: {bin_op:?}, negate: {negate}"); + } + } +} diff --git a/src/concurrency/genmc/miri_genmc.rs b/src/concurrency/genmc/miri_genmc.rs new file mode 100644 index 0000000000..75408df4cd --- /dev/null +++ b/src/concurrency/genmc/miri_genmc.rs @@ -0,0 +1,72 @@ +use std::fmt::Display; +use std::rc::Rc; +use std::time::Instant; + +use crate::{GenmcConfig, GenmcCtx, MiriConfig}; + +#[derive(Clone, Copy, PartialEq, Eq)] +pub enum Mode { + Estimation, + Verification, +} + +impl Display for Mode { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(match self { + Mode::Estimation => "Estimation", + Mode::Verification => "Verification", + }) + } +} + +pub fn run_genmc_mode( + config: &MiriConfig, + genmc_config: &GenmcConfig, + eval_entry: impl Fn(Rc) -> Option, + mode: Mode, +) -> Option { + let time_start = Instant::now(); + let genmc_ctx = Rc::new(GenmcCtx::new(config, mode)); + + for rep in 0u64.. { + tracing::info!("Miri-GenMC loop {}", rep + 1); + let result = eval_entry(genmc_ctx.clone()); + + if genmc_config.print_exec_graphs() { + genmc_ctx.print_genmc_graph(); + } + + // TODO GENMC (ERROR REPORTING): we currently do this here, so we can still print the GenMC graph above + let return_code = result?; + + let is_exploration_done = genmc_ctx.is_exploration_done(); + + tracing::info!( + "(GenMC Mode) Execution done (return code: {return_code}), is_exploration_done: {is_exploration_done}", + ); + + if is_exploration_done { + eprintln!(); + eprintln!("(GenMC) {mode} complete. No errors were detected.",); + + if mode == Mode::Estimation && return_code == 0 { + let elapsed_time = Instant::now().duration_since(time_start); + genmc_ctx.print_estimation_result(elapsed_time); + return Some(0); + } + + let explored_execution_count = genmc_ctx.get_explored_execution_count(); + let blocked_execution_count = genmc_ctx.get_blocked_execution_count(); + + eprintln!("Number of complete executions explored: {explored_execution_count}"); + if blocked_execution_count > 0 { + eprintln!("Number of blocked executions seen: {blocked_execution_count}"); + } + + // TODO GENMC: what is an appropriate return code? (since there are possibly many) + return Some(return_code); + } + } + tracing::error!("GenMC mode did not finish in 2^64 iterations!"); + None +} diff --git a/src/concurrency/genmc/mod.rs b/src/concurrency/genmc/mod.rs index 3617775e27..70d0200556 100644 --- a/src/concurrency/genmc/mod.rs +++ b/src/concurrency/genmc/mod.rs @@ -1,74 +1,169 @@ -#![allow(unused)] // FIXME(GenMC): remove this +use std::cell::{Cell, RefCell}; +use std::sync::Arc; +use std::time::Duration; -use std::cell::Cell; - -use genmc_sys::{GenmcParams, createGenmcHandle}; +use genmc_sys::cxx_extra::NonNullUniquePtr; +use genmc_sys::{ + ActionKind, GENMC_GLOBAL_ADDRESSES_MASK, GenmcScalar, GenmcThreadId, MemOrdering, + MiriGenMCShim, RMWBinOp, StoreEventType, createGenmcHandle, +}; use rustc_abi::{Align, Size}; -use rustc_const_eval::interpret::{InterpCx, InterpResult, interp_ok}; -use rustc_middle::mir; +use rustc_const_eval::interpret::{AllocId, InterpCx, InterpResult, interp_ok}; +use rustc_middle::{mir, throw_machine_stop, throw_ub_format, throw_unsup_format}; +use tracing::info; +use self::global_allocations::{EvalContextExtPriv as _, GlobalAllocationHandler}; +use self::helper::{ + genmc_scalar_to_scalar, option_scalar_to_genmc_scalar, rhs_scalar_to_genmc_scalar, + scalar_to_genmc_scalar, +}; +use self::mapping::{min_max_to_genmc_rmw_op, to_genmc_rmw_op}; +use self::thread_info_manager::ThreadInfoManager; +use crate::concurrency::genmc::helper::{is_terminator_atomic, split_access}; +use crate::concurrency::genmc::warnings::WarningsCache; +use crate::concurrency::thread::{EvalContextExt as _, ThreadState}; use crate::{ - AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, MemoryKind, MiriConfig, - MiriMachine, Scalar, ThreadId, ThreadManager, VisitProvenance, VisitWith, + AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, BlockReason, MachineCallback, + MemoryKind, MiriConfig, MiriInterpCx, MiriMachine, MiriMemoryKind, OpTy, Scalar, + TerminationInfo, ThreadId, ThreadManager, UnblockKind, VisitProvenance, VisitWith, callback, }; mod config; +mod global_allocations; +mod helper; +mod mapping; +pub mod miri_genmc; +mod thread_info_manager; +mod warnings; + +pub use genmc_sys::GenmcParams; pub use self::config::GenmcConfig; -// FIXME(GenMC): add fields +const UNSUPPORTED_ATOMICS_SIZE_MSG: &str = + "GenMC mode currently does not support atomics larger than 8 bytes."; + +#[derive(Clone, Copy)] +struct ExitStatus { + exit_code: i32, + leak_check: bool, +} + pub struct GenmcCtx { + handle: RefCell>, + + // TODO GENMC (PERFORMANCE): could use one RefCell for all internals instead of multiple + thread_infos: RefCell, + /// Some actions Miri does are allowed to cause data races. /// GenMC will not be informed about certain actions (e.g. non-atomic loads) when this flag is set. allow_data_races: Cell, + + /// Keep track of global allocations, to ensure they keep the same address across different executions, even if the order of allocations changes. + /// The `AllocId` for globals is stable across executions, so we can use it as an identifier. + global_allocations: Arc, + // TODO GENMC: maybe make this a (base, size), maybe BTreeMap/sorted vector for reverse lookups + // GenMC needs to have access to that + // TODO: look at code of "pub struct GlobalStateInner" + warnings_cache: RefCell, + // terminator_cache: RefCell, bool>>, + exit_status: Cell>, } +/// GenMC Context creation and administrative / query actions impl GenmcCtx { /// Create a new `GenmcCtx` from a given config. - pub fn new(miri_config: &MiriConfig) -> Self { + pub fn new(miri_config: &MiriConfig, mode: miri_genmc::Mode) -> Self { let genmc_config = miri_config.genmc_config.as_ref().unwrap(); + info!("GenMC: Creating new GenMC Context"); - let handle = createGenmcHandle(&genmc_config.params); - assert!(!handle.is_null()); + let handle = createGenmcHandle(&genmc_config.params, mode == miri_genmc::Mode::Estimation); + let non_null_handle = NonNullUniquePtr::new(handle).expect("GenMC should not return null"); + let non_null_handle = RefCell::new(non_null_handle); - eprintln!("Miri: GenMC handle creation successful!"); + Self { + handle: non_null_handle, + thread_infos: Default::default(), + allow_data_races: Cell::new(false), + global_allocations: Default::default(), + warnings_cache: Default::default(), + exit_status: Cell::new(None), + } + } - drop(handle); - eprintln!("Miri: Dropping GenMC handle successful!"); + pub fn print_estimation_result(&self, elapsed_time: Duration) { + let elapsed_time_sec = elapsed_time.as_secs_f64(); + let mc = self.handle.borrow(); + mc.as_ref().printEstimationResults(elapsed_time_sec); + } - // FIXME(GenMC): implement - std::process::exit(0); + pub fn get_blocked_execution_count(&self) -> u64 { + let mc = self.handle.borrow(); + mc.as_ref().getBlockedExecutionCount() } - pub fn get_stuck_execution_count(&self) -> usize { - todo!() + pub fn get_explored_execution_count(&self) -> u64 { + let mc = self.handle.borrow(); + mc.as_ref().getExploredExecutionCount() } pub fn print_genmc_graph(&self) { - todo!() + info!("GenMC: print the Execution graph"); + let mut mc = self.handle.borrow_mut(); + let pinned_mc = mc.as_mut(); + pinned_mc.printGraph(); } /// This function determines if we should continue exploring executions or if we are done. /// /// In GenMC mode, the input program should be repeatedly executed until this function returns `true` or an error is found. pub fn is_exploration_done(&self) -> bool { - todo!() + let mut mc = self.handle.borrow_mut(); + mc.as_mut().isExplorationDone() } + pub fn get_exit_status(&self) -> Option<(i32, bool)> { + let ExitStatus { exit_code, leak_check } = self.exit_status.get()?; + Some((exit_code, leak_check)) + } + + fn set_exit_status(&self, exit_code: i32, leak_check: bool) { + self.exit_status.set(Some(ExitStatus { exit_code, leak_check })); + } +} + +/// GenMC event handling. These methods are used to inform GenMC about events happening in the program, and to handle scheduling decisions. +impl GenmcCtx { + /**** Memory access handling ****/ + /// Inform GenMC that a new program execution has started. /// This function should be called at the start of every execution. pub(crate) fn handle_execution_start(&self) { - todo!() + self.allow_data_races.replace(false); + self.thread_infos.borrow_mut().reset(); + self.exit_status.set(None); + + let mut mc = self.handle.borrow_mut(); + mc.as_mut().handleExecutionStart(); } /// Inform GenMC that the program's execution has ended. /// - /// This function must be called even when the execution got stuck (i.e., it returned a `InterpErrorKind::MachineStop` with error kind `TerminationInfo::GenmcStuckExecution`). + /// This function must be called even when the execution is blocked + /// (i.e., it returned a `InterpErrorKind::MachineStop` with error kind `TerminationInfo::GenmcBlockedExecution`). pub(crate) fn handle_execution_end<'tcx>( &self, - ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, + _ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, ) -> Result<(), String> { - todo!() + let mut mc = self.handle.borrow_mut(); + let result = mc.as_mut().handleExecutionEnd(); + if let Some(msg) = result.as_ref() { + let msg = msg.to_string_lossy().to_string(); + info!("GenMC: execution ended with error \"{msg}\""); + Err(msg) // TODO GENMC: add more error info here, and possibly handle this without requiring to clone the CxxString + } else { + Ok(()) + } } /**** Memory access handling ****/ @@ -83,20 +178,29 @@ impl GenmcCtx { /// # Panics /// If data race free is attempted to be set more than once (i.e., no nesting allowed). pub(super) fn set_ongoing_action_data_race_free(&self, enable: bool) { + info!("GenMC: set_ongoing_action_data_race_free ({enable})"); let old = self.allow_data_races.replace(enable); assert_ne!(old, enable, "cannot nest allow_data_races"); } + //* might fails if there's a race, load might also not read anything (returns None) */ pub(crate) fn atomic_load<'tcx>( &self, ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, address: Size, size: Size, ordering: AtomicReadOrd, + // The value that we would get, if we were to do a non-atomic load here. old_val: Option, ) -> InterpResult<'tcx, Scalar> { - assert!(!self.allow_data_races.get()); - todo!() + info!("GenMC: atomic_load: old_val: {old_val:?}"); + assert!(!self.allow_data_races.get()); // TODO GENMC: handle this properly + let ordering = ordering.convert(); + let genmc_old_value = option_scalar_to_genmc_scalar(ecx, old_val)?; + let read_value = + self.atomic_load_impl(&ecx.machine, address, size, ordering, genmc_old_value)?; + info!("GenMC: atomic_load: received value from GenMC: {read_value:?}"); + genmc_scalar_to_scalar(ecx, read_value, size) } pub(crate) fn atomic_store<'tcx>( @@ -105,24 +209,42 @@ impl GenmcCtx { address: Size, size: Size, value: Scalar, + // The value that we would get, if we were to do a non-atomic load here. + old_value: Option, ordering: AtomicWriteOrd, - ) -> InterpResult<'tcx, ()> { - assert!(!self.allow_data_races.get()); - todo!() + ) -> InterpResult<'tcx, bool> { + assert!(!self.allow_data_races.get()); // TODO GENMC: handle this properly + let ordering = ordering.convert(); + let genmc_value = scalar_to_genmc_scalar(ecx, value)?; + let genmc_old_value = option_scalar_to_genmc_scalar(ecx, old_value)?; + self.atomic_store_impl(&ecx.machine, address, size, genmc_value, genmc_old_value, ordering) } pub(crate) fn atomic_fence<'tcx>( &self, machine: &MiriMachine<'tcx>, ordering: AtomicFenceOrd, - ) -> InterpResult<'tcx, ()> { - assert!(!self.allow_data_races.get()); - todo!() + ) -> InterpResult<'tcx> { + assert!(!self.allow_data_races.get()); // TODO GENMC: handle this properly + info!("GenMC: atomic_fence with ordering: {ordering:?}"); + + let ordering = ordering.convert(); + + let thread_infos = self.thread_infos.borrow(); + let curr_thread = machine.threads.active_thread(); + let genmc_tid = thread_infos.get_info(curr_thread).genmc_tid; + + let mut mc = self.handle.borrow_mut(); + let pinned_mc = mc.as_mut(); + pinned_mc.handleFence(genmc_tid.0, ordering); + + // TODO GENMC: can this operation ever fail? + interp_ok(()) } /// Inform GenMC about an atomic read-modify-write operation. /// - /// Returns `(old_val, new_val)`. + /// Returns `(old_val, Option)`. `new_val` might not be the latest write in coherence order, which is indicated by `None`. pub(crate) fn atomic_rmw_op<'tcx>( &self, ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, @@ -131,14 +253,32 @@ impl GenmcCtx { ordering: AtomicRwOrd, (rmw_op, not): (mir::BinOp, bool), rhs_scalar: Scalar, - ) -> InterpResult<'tcx, (Scalar, Scalar)> { - assert!(!self.allow_data_races.get()); - todo!() + // The value that we would get, if we were to do a non-atomic load here. + old_value: Scalar, + ) -> InterpResult<'tcx, (Scalar, Option)> { + assert!(!self.allow_data_races.get()); // TODO GENMC: handle this properly + let (load_ordering, store_ordering) = ordering.to_genmc_memory_orderings(); + let genmc_rmw_op = to_genmc_rmw_op(rmw_op, not); + tracing::info!( + "GenMC: atomic_rmw_op (op: {rmw_op:?}, not: {not}, genmc_rmw_op: {genmc_rmw_op:?}): rhs value: {rhs_scalar:?}, orderings ({load_ordering:?}, {store_ordering:?})" + ); + let genmc_rhs_scalar = rhs_scalar_to_genmc_scalar(ecx, rhs_scalar)?; + let genmc_old_value = scalar_to_genmc_scalar(ecx, old_value)?; + self.atomic_rmw_op_impl( + ecx, + address, + size, + load_ordering, + store_ordering, + genmc_rmw_op, + genmc_rhs_scalar, + genmc_old_value, + ) } /// Inform GenMC about an atomic `min` or `max` operation. /// - /// Returns `(old_val, new_val)`. + /// Returns `(old_val, Option)`. `new_val` might not be the latest write in coherence order, which is indicated by `None`. pub(crate) fn atomic_min_max_op<'tcx>( &self, ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, @@ -148,11 +288,30 @@ impl GenmcCtx { min: bool, is_signed: bool, rhs_scalar: Scalar, - ) -> InterpResult<'tcx, (Scalar, Scalar)> { - assert!(!self.allow_data_races.get()); - todo!() + // The value that we would get, if we were to do a non-atomic load here. + old_value: Scalar, + ) -> InterpResult<'tcx, (Scalar, Option)> { + assert!(!self.allow_data_races.get()); // TODO GENMC: handle this properly + let (load_ordering, store_ordering) = ordering.to_genmc_memory_orderings(); + let genmc_rmw_op = min_max_to_genmc_rmw_op(min, is_signed); + tracing::info!( + "GenMC: atomic_min_max_op (min: {min}, signed: {is_signed}, genmc_rmw_op: {genmc_rmw_op:?}): rhs value: {rhs_scalar:?}, orderings ({load_ordering:?}, {store_ordering:?})" + ); + let genmc_rhs_scalar = rhs_scalar_to_genmc_scalar(ecx, rhs_scalar)?; + let genmc_old_value = scalar_to_genmc_scalar(ecx, old_value)?; + self.atomic_rmw_op_impl( + ecx, + address, + size, + load_ordering, + store_ordering, + genmc_rmw_op, + genmc_rhs_scalar, + genmc_old_value, + ) } + /// Returns `(old_val, Option)`. `new_val` might not be the latest write in coherence order, which is indicated by `None`. pub(crate) fn atomic_exchange<'tcx>( &self, ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, @@ -160,9 +319,29 @@ impl GenmcCtx { size: Size, rhs_scalar: Scalar, ordering: AtomicRwOrd, - ) -> InterpResult<'tcx, (Scalar, bool)> { - assert!(!self.allow_data_races.get()); - todo!() + // The value that we would get, if we were to do a non-atomic load here. + old_value: Scalar, + ) -> InterpResult<'tcx, (Scalar, Option)> { + assert!(!self.allow_data_races.get()); // TODO GENMC: handle this properly + // TODO GENMC: could maybe merge this with atomic_rmw? + + let (load_ordering, store_ordering) = ordering.to_genmc_memory_orderings(); + let genmc_rmw_op = RMWBinOp::Xchg; + tracing::info!( + "GenMC: atomic_exchange (op: {genmc_rmw_op:?}): new value: {rhs_scalar:?}, orderings ({load_ordering:?}, {store_ordering:?})" + ); + let genmc_rhs_scalar = scalar_to_genmc_scalar(ecx, rhs_scalar)?; + let genmc_old_value = scalar_to_genmc_scalar(ecx, old_value)?; + self.atomic_rmw_op_impl( + ecx, + address, + size, + load_ordering, + store_ordering, + genmc_rmw_op, + genmc_rhs_scalar, + genmc_old_value, + ) } pub(crate) fn atomic_compare_exchange<'tcx>( @@ -175,9 +354,68 @@ impl GenmcCtx { success: AtomicRwOrd, fail: AtomicReadOrd, can_fail_spuriously: bool, - ) -> InterpResult<'tcx, (Scalar, bool)> { - assert!(!self.allow_data_races.get()); - todo!() + // The value that we would get, if we were to do a non-atomic load here. + old_value: Scalar, + ) -> InterpResult<'tcx, (Scalar, bool, bool)> { + assert!(!self.allow_data_races.get()); // TODO GENMC: handle this properly + + // FIXME(genmc): remove once GenMC supports failure memory ordering in `compare_exchange`. + self.warnings_cache.borrow_mut().warn_once_rmw_failure_ordering(&ecx.tcx, success, fail); + // FIXME(genmc): remove once GenMC implements spurious failures for `compare_exchange_weak`. + if can_fail_spuriously { + self.warnings_cache.borrow_mut().warn_once_compare_exchange_weak(&ecx.tcx); + } + + let machine = &ecx.machine; + let (success_load_ordering, success_store_ordering) = success.to_genmc_memory_orderings(); + let fail_load_ordering = fail.convert(); + + info!( + "GenMC: atomic_compare_exchange, address: {address:?}, size: {size:?} (expect: {expected_old_value:?}, new: {new_value:?}, old_value: {old_value:?}, {success:?}, {fail:?}), can fail spuriously: {can_fail_spuriously}" + ); + info!( + "GenMC: atomic_compare_exchange orderings: success: ({success_load_ordering:?}, {success_store_ordering:?}), failure load ordering: {fail_load_ordering:?}" + ); + + let thread_infos = self.thread_infos.borrow(); + let curr_thread = machine.threads.active_thread(); + let genmc_tid = thread_infos.get_info(curr_thread).genmc_tid; + + let genmc_address = address.bytes(); + let genmc_size = size.bytes(); + + let genmc_expected_value = scalar_to_genmc_scalar(ecx, expected_old_value)?; + let genmc_new_value = scalar_to_genmc_scalar(ecx, new_value)?; + let genmc_old_value = scalar_to_genmc_scalar(ecx, old_value)?; + + let mut mc = self.handle.borrow_mut(); + let pinned_mc = mc.as_mut(); + let cas_result = pinned_mc.handleCompareExchange( + genmc_tid.0, + genmc_address, + genmc_size, + genmc_expected_value, + genmc_new_value, + genmc_old_value, + success_load_ordering, + success_store_ordering, + fail_load_ordering, + can_fail_spuriously, + ); + + if let Some(error) = cas_result.error.as_ref() { + let msg = error.to_string_lossy().to_string(); + info!("GenMC: RMW operation returned an error: \"{msg}\""); + throw_ub_format!("{}", msg); // TODO GENMC: proper error handling: find correct error here + } + + let return_scalar = genmc_scalar_to_scalar(ecx, cas_result.old_value, size)?; + info!( + "GenMC: atomic_compare_exchange: result: {cas_result:?}, returning scalar: {return_scalar:?}" + ); + // The write can only be a co-maximal write if the CAS succeeded. + assert!(cas_result.is_success || !cas_result.isCoMaxWrite); + interp_ok((return_scalar, cas_result.isCoMaxWrite, cas_result.is_success)) } /// Inform GenMC about a non-atomic memory load @@ -188,8 +426,45 @@ impl GenmcCtx { machine: &MiriMachine<'tcx>, address: Size, size: Size, - ) -> InterpResult<'tcx, ()> { - todo!() + ) -> InterpResult<'tcx> { + info!( + "GenMC: received memory_load (non-atomic): address: {:#x}, size: {}", + address.bytes(), + size.bytes() + ); + if self.allow_data_races.get() { + info!("GenMC: skipping `memory_load` on address"); + return interp_ok(()); + } + // GenMC doesn't like ZSTs, and they can't have any data races, so we skip them + if size.bytes() == 0 { + return interp_ok(()); + } + + if size.bytes() <= 8 { + // NOTE: Values loaded non-atomically are still handled by Miri, so we discard whatever we get from GenMC + let _read_value = self.atomic_load_impl( + machine, + address, + size, + MemOrdering::NotAtomic, + GenmcScalar::UNINIT, // Don't use DUMMY here, since that might have it stored as the initial value + )?; + return interp_ok(()); + } + + for (address, size) in split_access(address, size) { + let chunk_addr = Size::from_bytes(address); + let chunk_size = Size::from_bytes(size); + let _read_value = self.atomic_load_impl( + machine, + chunk_addr, + chunk_size, + MemOrdering::NotAtomic, + GenmcScalar::UNINIT, // Don't use DUMMY here, since that might have it stored as the initial value + )?; + } + interp_ok(()) } pub(crate) fn memory_store<'tcx>( @@ -197,31 +472,145 @@ impl GenmcCtx { machine: &MiriMachine<'tcx>, address: Size, size: Size, - ) -> InterpResult<'tcx, ()> { - todo!() + // old_value: Option, // TODO GENMC(mixed atomic-non-atomic): is this needed? + ) -> InterpResult<'tcx> { + info!( + "GenMC: received memory_store (non-atomic): address: {:#x}, size: {}", + address.bytes(), + size.bytes() + ); + if self.allow_data_races.get() { + info!("GenMC: skipping `memory_store`"); + return interp_ok(()); + } + // GenMC doesn't like ZSTs, and they can't have any data races, so we skip them + if size.bytes() == 0 { + return interp_ok(()); + } + + if size.bytes() <= 8 { + // TODO GENMC(mixed atomic-non-atomics): anything to do here? + let _is_co_max_write = self.atomic_store_impl( + machine, + address, + size, + GenmcScalar::DUMMY, + GenmcScalar::UNINIT, // Don't use DUMMY here, since that might have it stored as the initial value + MemOrdering::NotAtomic, + )?; + return interp_ok(()); + } + + for (address, size) in split_access(address, size) { + let chunk_addr = Size::from_bytes(address); + let chunk_size = Size::from_bytes(size); + let _is_co_max_write = self.atomic_store_impl( + machine, + chunk_addr, + chunk_size, + GenmcScalar::DUMMY, + GenmcScalar::UNINIT, // Don't use DUMMY here, since that might have it stored as the initial value + MemOrdering::NotAtomic, + )?; + } + interp_ok(()) } /**** Memory (de)allocation ****/ pub(crate) fn handle_alloc<'tcx>( &self, - machine: &MiriMachine<'tcx>, + ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, + alloc_id: AllocId, size: Size, alignment: Align, memory_kind: MemoryKind, ) -> InterpResult<'tcx, u64> { - todo!() + let machine = &ecx.machine; + let chosen_address = if memory_kind == MiriMemoryKind::Global.into() { + info!("GenMC: global memory allocation: {alloc_id:?}"); + ecx.get_global_allocation_address(&self.global_allocations, alloc_id)? + } else { + // TODO GENMC: Does GenMC need to know about the kind of Memory? + + // eprintln!( + // "handle_alloc ({memory_kind:?}): Custom backtrace: {}", + // std::backtrace::Backtrace::force_capture() + // ); + // TODO GENMC: should we put this before the special handling for globals? + if self.allow_data_races.get() { + unreachable!(); // FIXME(genmc): can this happen and if yes, how should this be handled? + } + let thread_infos = self.thread_infos.borrow(); + let curr_thread = machine.threads.active_thread(); + let genmc_tid = thread_infos.get_info(curr_thread).genmc_tid; + // GenMC doesn't support ZSTs, so we set the minimum size to 1 byte + let genmc_size = size.bytes().max(1); + info!( + "GenMC: handle_alloc (thread: {curr_thread:?} ({genmc_tid:?}), size: {}, alignment: {alignment:?}, memory_kind: {memory_kind:?})", + size.bytes() + ); + + let alignment = alignment.bytes(); + + let mut mc = self.handle.borrow_mut(); + let pinned_mc = mc.as_mut(); + let chosen_address = pinned_mc.handleMalloc(genmc_tid.0, genmc_size, alignment); + info!("GenMC: handle_alloc: got address '{chosen_address}' ({chosen_address:#x})"); + + // TODO GENMC: + if chosen_address == 0 { + throw_unsup_format!("TODO GENMC: we got address '0' from malloc"); + } + assert_eq!(0, chosen_address & GENMC_GLOBAL_ADDRESSES_MASK); + chosen_address + }; + // Sanity check the address alignment: + assert_eq!( + 0, + chosen_address % alignment.bytes(), + "GenMC returned address {chosen_address} == {chosen_address:#x} with lower alignment than requested ({:})!", + alignment.bytes() + ); + + interp_ok(chosen_address) } pub(crate) fn handle_dealloc<'tcx>( &self, machine: &MiriMachine<'tcx>, + alloc_id: AllocId, address: Size, size: Size, align: Align, kind: MemoryKind, - ) -> InterpResult<'tcx, ()> { - todo!() + ) -> InterpResult<'tcx> { + assert_ne!( + kind, + MiriMemoryKind::Global.into(), + "we probably shouldn't try to deallocate global allocations (alloc_id: {alloc_id:?})" + ); + if self.allow_data_races.get() { + unreachable!(); // FIXME(genmc): can this happen and if yes, how should this be handled? + } + let thread_infos = self.thread_infos.borrow(); + let curr_thread = machine.threads.active_thread(); + let genmc_tid = thread_infos.get_info(curr_thread).genmc_tid; + info!( + "GenMC: memory deallocation, thread: {curr_thread:?} ({genmc_tid:?}), address: {addr} == {addr:#x}, size: {size:?}, align: {align:?}, memory_kind: {kind:?}", + addr = address.bytes() + ); + + let genmc_address = address.bytes(); + // GenMC doesn't support ZSTs, so we set the minimum size to 1 byte + let genmc_size = size.bytes().max(1); + + let mut mc = self.handle.borrow_mut(); + let pinned_mc = mc.as_mut(); + pinned_mc.handleFree(genmc_tid.0, genmc_address, genmc_size); + + // TODO GENMC (ERROR HANDLING): can this ever fail? + interp_ok(()) } /**** Thread management ****/ @@ -229,56 +618,543 @@ impl GenmcCtx { pub(crate) fn handle_thread_create<'tcx>( &self, threads: &ThreadManager<'tcx>, + _start_routine: crate::Pointer, // TODO GENMC: pass info to GenMC + _func_arg: &crate::ImmTy<'tcx>, new_thread_id: ThreadId, - ) -> InterpResult<'tcx, ()> { - assert!(!self.allow_data_races.get()); - todo!() + ) -> InterpResult<'tcx> { + assert!(!self.allow_data_races.get()); // TODO GENMC: handle this properly + let mut thread_infos = self.thread_infos.borrow_mut(); + + let curr_thread_id = threads.active_thread(); + let genmc_parent_tid = thread_infos.get_info(curr_thread_id).genmc_tid; + let genmc_new_tid = thread_infos.add_thread(new_thread_id); + + info!( + "GenMC: handling thread creation (thread {curr_thread_id:?} ({genmc_parent_tid:?}) spawned thread {new_thread_id:?} ({genmc_new_tid:?}))" + ); + + let mut mc = self.handle.borrow_mut(); + mc.as_mut().handleThreadCreate(genmc_new_tid.0, genmc_parent_tid.0); + + // TODO GENMC (ERROR HANDLING): can this ever fail? + interp_ok(()) } pub(crate) fn handle_thread_join<'tcx>( &self, active_thread_id: ThreadId, child_thread_id: ThreadId, - ) -> InterpResult<'tcx, ()> { - assert!(!self.allow_data_races.get()); - todo!() + ) -> InterpResult<'tcx> { + assert!(!self.allow_data_races.get()); // TODO GENMC: handle this properly + let thread_infos = self.thread_infos.borrow(); + + let genmc_curr_tid = thread_infos.get_info(active_thread_id).genmc_tid; + let genmc_child_tid = thread_infos.get_info(child_thread_id).genmc_tid; + + info!( + "GenMC: handling thread joining (thread {active_thread_id:?} ({genmc_curr_tid:?}) joining thread {child_thread_id:?} ({genmc_child_tid:?}))" + ); + + let mut mc = self.handle.borrow_mut(); + mc.as_mut().handleThreadJoin(genmc_curr_tid.0, genmc_child_tid.0); + + interp_ok(()) } - pub(crate) fn handle_thread_stack_empty(&self, thread_id: ThreadId) { - todo!() + pub(crate) fn handle_thread_finish<'tcx>(&self, threads: &ThreadManager<'tcx>) { + assert!(!self.allow_data_races.get()); // TODO GENMC: handle this properly + let curr_thread_id = threads.active_thread(); + + let thread_infos = self.thread_infos.borrow(); + let genmc_tid = thread_infos.get_info(curr_thread_id).genmc_tid; + + // NOTE: Miri doesn't support return values for threads, but GenMC expects one, so we return 0 + let ret_val = 0; + + info!( + "GenMC: handling thread finish (thread {curr_thread_id:?} ({genmc_tid:?}) returns with dummy value 0)" + ); + + let mut mc = self.handle.borrow_mut(); + let pinned_mc = mc.as_mut(); + pinned_mc.handleThreadFinish(genmc_tid.0, ret_val); } - pub(crate) fn handle_thread_finish<'tcx>( + /// Handle a call to `libc::exit`. Returns `true` if the program should continue executing (in a different thread). + pub(crate) fn handle_exit<'tcx>( &self, - threads: &ThreadManager<'tcx>, - ) -> InterpResult<'tcx, ()> { - assert!(!self.allow_data_races.get()); - todo!() + thread: ThreadId, + exit_code: i32, + is_exit_call: bool, + ) -> InterpResult<'tcx> { + // Calling `libc::exit` doesn't do cleanup, so we skip the leak check in that case. + let leak_check = !is_exit_call; + self.set_exit_status(exit_code, leak_check); + if is_exit_call { + // FIXME(genmc): handle exit call (run other threads to completion) + todo!("Inform GenMC about killed thread {thread:?}."); + } + interp_ok(()) + } +} + +impl GenmcCtx { + //* might fails if there's a race, load might also not read anything (returns None) */ + fn atomic_load_impl<'tcx>( + &self, + machine: &MiriMachine<'tcx>, + address: Size, + size: Size, + memory_ordering: MemOrdering, + genmc_old_value: GenmcScalar, + ) -> InterpResult<'tcx, GenmcScalar> { + assert!( + size.bytes() != 0 + && (memory_ordering == MemOrdering::NotAtomic || size.bytes().is_power_of_two()) + ); + if size.bytes() > 8 { + throw_unsup_format!("{UNSUPPORTED_ATOMICS_SIZE_MSG}"); + } + assert!(!self.allow_data_races.get()); // TODO GENMC: handle this properly + let thread_infos = self.thread_infos.borrow(); + let curr_thread_id = machine.threads.active_thread(); + let genmc_tid = thread_infos.get_info(curr_thread_id).genmc_tid; + + info!( + "GenMC: load, thread: {curr_thread_id:?} ({genmc_tid:?}), address: {addr} == {addr:#x}, size: {size:?}, ordering: {memory_ordering:?}, old_value: {genmc_old_value:x?}", + addr = address.bytes() + ); + let genmc_address = address.bytes(); + let genmc_size = size.bytes(); + + let mut mc = self.handle.borrow_mut(); + let pinned_mc = mc.as_mut(); + let load_result = pinned_mc.handleLoad( + genmc_tid.0, + genmc_address, + genmc_size, + memory_ordering, + genmc_old_value, + ); + + if load_result.is_read_opt { + todo!(); + } + + if let Some(error) = load_result.error.as_ref() { + let msg = error.to_string_lossy().to_string(); + info!("GenMC: load operation returned an error: \"{msg}\""); + throw_ub_format!("{}", msg); // TODO GENMC: proper error handling: find correct error here + } + + info!("GenMC: load returned value: {:?}", load_result.read_value); + + interp_ok(load_result.read_value) + } + + fn atomic_store_impl<'tcx>( + &self, + machine: &MiriMachine<'tcx>, + address: Size, + size: Size, + genmc_value: GenmcScalar, + genmc_old_value: GenmcScalar, + memory_ordering: MemOrdering, + ) -> InterpResult<'tcx, bool> { + assert!( + size.bytes() != 0 + && (memory_ordering == MemOrdering::NotAtomic || size.bytes().is_power_of_two()) + ); + if size.bytes() > 8 { + throw_unsup_format!("{UNSUPPORTED_ATOMICS_SIZE_MSG}"); + } + let thread_infos = self.thread_infos.borrow(); + let curr_thread_id = machine.threads.active_thread(); + let genmc_tid = thread_infos.get_info(curr_thread_id).genmc_tid; + + let genmc_address = address.bytes(); + let genmc_size = size.bytes(); + + info!( + "GenMC: store, thread: {curr_thread_id:?} ({genmc_tid:?}), address: {addr} = {addr:#x}, size: {size:?}, ordering {memory_ordering:?}, value: {genmc_value:?}", + addr = address.bytes() + ); + + let mut mc = self.handle.borrow_mut(); + let pinned_mc = mc.as_mut(); + let store_result = pinned_mc.handleStore( + genmc_tid.0, + genmc_address, + genmc_size, + genmc_value, + genmc_old_value, + memory_ordering, + StoreEventType::Normal, + ); + + if let Some(error) = store_result.error.as_ref() { + let msg = error.to_string_lossy().to_string(); + info!("GenMC: store operation returned an error: \"{msg}\""); + throw_ub_format!("{}", msg); // TODO GENMC: proper error handling: find correct error here + } + + interp_ok(store_result.isCoMaxWrite) + } + + fn atomic_rmw_op_impl<'tcx>( + &self, + ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, + address: Size, + size: Size, + load_ordering: MemOrdering, + store_ordering: MemOrdering, + genmc_rmw_op: RMWBinOp, + genmc_rhs_scalar: GenmcScalar, + genmc_old_value: GenmcScalar, + ) -> InterpResult<'tcx, (Scalar, Option)> { + assert!( + size.bytes() <= 8, + "TODO GENMC: no support for accesses larger than 8 bytes (got {} bytes)", + size.bytes() + ); + let machine = &ecx.machine; + assert_ne!(0, size.bytes()); + let thread_infos = self.thread_infos.borrow(); + let curr_thread_id = machine.threads.active_thread(); + let genmc_tid = thread_infos.get_info(curr_thread_id).genmc_tid; + + let genmc_address = address.bytes(); + let genmc_size = size.bytes(); + + info!( + "GenMC: atomic_rmw_op, thread: {curr_thread_id:?} ({genmc_tid:?}) (op: {genmc_rmw_op:?}, rhs value: {genmc_rhs_scalar:?}), address: {address:?}, size: {size:?}, orderings: ({load_ordering:?}, {store_ordering:?})", + ); + + let mut mc = self.handle.borrow_mut(); + let pinned_mc = mc.as_mut(); + let rmw_result = pinned_mc.handleReadModifyWrite( + genmc_tid.0, + genmc_address, + genmc_size, + load_ordering, + store_ordering, + genmc_rmw_op, + genmc_rhs_scalar, + genmc_old_value, + ); + + if let Some(error) = rmw_result.error.as_ref() { + let msg = error.to_string_lossy().to_string(); + info!("GenMC: RMW operation returned an error: \"{msg}\""); + throw_ub_format!("{}", msg); // TODO GENMC: proper error handling: find correct error here + } + + let old_value_scalar = genmc_scalar_to_scalar(ecx, rmw_result.old_value, size)?; + + let new_value_scalar = if rmw_result.isCoMaxWrite { + Some(genmc_scalar_to_scalar(ecx, rmw_result.new_value, size)?) + } else { + None + }; + interp_ok((old_value_scalar, new_value_scalar)) + } + + /**** Scheduling functionality ****/ + + fn schedule_thread<'tcx>( + &self, + ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, + ) -> InterpResult<'tcx, ThreadId> { + assert!(!self.allow_data_races.get()); // TODO GENMC: handle this properly + let thread_manager = &ecx.machine.threads; + let active_thread_id = thread_manager.active_thread(); + + let curr_thread_next_instr_kind = + if !thread_manager.active_thread_ref().get_state().is_enabled() { + // The current thread can get blocked (e.g., due to a thread join, assume statement, ...), then we need to ask GenMC for another thread to schedule. + // `Load` is a safe default for the next instruction type, since we may not know what the next instruction is. + ActionKind::Load + } else { + let Some(frame) = thread_manager.get_thread_stack(active_thread_id).last() else { + return interp_ok(active_thread_id); + }; + let either::Either::Left(loc) = frame.current_loc() else { + // We are unwinding. + return interp_ok(active_thread_id); + }; + let basic_block = &frame.body().basic_blocks[loc.block]; + if let Some(_statement) = basic_block.statements.get(loc.statement_index) { + return interp_ok(active_thread_id); + } + + if is_terminator_atomic(ecx, basic_block.terminator(), active_thread_id)? { + ActionKind::Load + } else { + ActionKind::NonLoad + } + }; + + info!( + "GenMC: schedule_thread, active thread: {active_thread_id:?}, next instr.: '{curr_thread_next_instr_kind:?}'" + ); + + // let curr_thread_user_block = self.curr_thread_user_block.replace(false); + let thread_infos = self.thread_infos.borrow(); + let curr_thread_info = thread_infos.get_info(active_thread_id); + + let mut mc = self.handle.borrow_mut(); + let pinned_mc = mc.as_mut(); + let result = + pinned_mc.scheduleNext(curr_thread_info.genmc_tid.0, curr_thread_next_instr_kind); + if result >= 0 { + // TODO GENMC: can we ensure this thread_id is valid? + let genmc_next_thread_id = result.try_into().unwrap(); + let genmc_next_thread_id = GenmcThreadId(genmc_next_thread_id); + let thread_infos = self.thread_infos.borrow(); + let next_thread_id = thread_infos.get_info_genmc(genmc_next_thread_id).miri_tid; + + return interp_ok(next_thread_id); + } + + // Negative result means that GenMC has no next thread to schedule. + info!("GenMC: scheduleNext returned no thread to schedule, execution is finished."); + throw_machine_stop!(TerminationInfo::GenmcFinishedExecution); + } + + /**** Blocking functionality ****/ + + fn handle_user_block<'tcx>(&self, machine: &MiriMachine<'tcx>) -> InterpResult<'tcx> { + let thread_infos = self.thread_infos.borrow(); + let curr_thread = machine.threads.active_thread(); + let genmc_curr_thread = thread_infos.get_info(curr_thread).genmc_tid; + info!("GenMC: handle_user_block, blocking thread {curr_thread:?} ({genmc_curr_thread:?})"); + + let mut mc = self.handle.borrow_mut(); + let pinned_mc = mc.as_mut(); + pinned_mc.handleUserBlock(genmc_curr_thread.0); + + interp_ok(()) + } +} + +/// Other functionality not directly related to event handling +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { + /// Given a `ty::Instance<'tcx>`, do any required special handling. Returns true if this `instance` should be skipped (i.e., no Mir should be executed for it). + fn check_genmc_intercept_function( + &mut self, + instance: rustc_middle::ty::Instance<'tcx>, + args: &[rustc_const_eval::interpret::FnArg<'tcx, crate::Provenance>], + dest: &crate::PlaceTy<'tcx>, + ret: Option, + ) -> InterpResult<'tcx, bool> { + let this = self.eval_context_mut(); + let genmc_ctx = this + .machine + .data_race + .as_genmc_ref() + .expect("This function should only be called in GenMC mode."); + + let get_mutex_call_infos = || { + // assert!(!args.is_empty()); + assert_eq!(args.len(), 1); + let arg = this.copy_fn_arg(&args[0]); + let addr = this.read_target_usize(&arg)?; + // FIXME(genmc): assert that we have at least 1 byte. + // FIXME(genmc): maybe use actual size of mutex here?. + + let thread_infos = genmc_ctx.thread_infos.borrow(); + let curr_thread = this.machine.threads.active_thread(); + let genmc_curr_thread = thread_infos.get_info(curr_thread).genmc_tid; + interp_ok((genmc_curr_thread, addr, 1)) + }; + + use rustc_span::sym; + if this.tcx.is_diagnostic_item(sym::sys_mutex_lock, instance.def_id()) { + info!("GenMC: handling Mutex::lock()"); + let (genmc_curr_thread, addr, size) = get_mutex_call_infos()?; + + let result = { + let mut mc = genmc_ctx.handle.borrow_mut(); + let pinned_mc = mc.as_mut(); + pinned_mc.handleMutexLock(genmc_curr_thread.0, addr, size) + }; + if let Some(error) = result.error.as_ref() { + let msg = error.to_string_lossy().to_string(); + info!("GenMC: handling Mutex::lock: error: {msg:?}"); + throw_ub_format!("{msg}"); + } + // TODO GENMC(HACK): for determining if the Mutex lock blocks this thread. + if !result.is_lock_acquired { + fn create_callback<'tcx>( + genmc_curr_thread: i32, + addr: u64, + size: u64, + ) -> crate::DynUnblockCallback<'tcx> { + crate::callback!( + @capture<'tcx> { + // mutex_ref: MutexRef, + // retval_dest: Option<(Scalar, MPlaceTy<'tcx>)>, + genmc_curr_thread: i32, + addr: u64, + size: u64, + } + |this, unblock: crate::UnblockKind| { + assert_eq!(unblock, crate::UnblockKind::Ready); + let genmc_ctx = this.machine.data_race.as_genmc_ref().unwrap(); + + info!("GenMC: handling Mutex::lock: unblocking callback called!"); + let result = { + let mut mc = genmc_ctx.handle.borrow_mut(); + let pinned_mc = mc.as_mut(); + pinned_mc.handleMutexLock(genmc_curr_thread, addr, size) + }; + if let Some(error) = result.error.as_ref() { + let msg = error.to_string_lossy().to_string(); + info!("GenMC: handling Mutex::lock: error: {msg:?}"); + throw_ub_format!("{msg}"); + } + // TODO GENMC(HACK): for determining if the Mutex lock blocks this thread. + if !result.is_lock_acquired { + // If this thread gets woken up without the mutex being made available, block the thread again. + this.block_thread( crate::BlockReason::Mutex, None, create_callback(genmc_curr_thread, addr, size)); + // panic!( + // "Somehow, Mutex is still locked after waiting thread was unblocked?!" + // ); + } + interp_ok(()) + } + ) + } + + info!("GenMC: handling Mutex::lock failed, blocking thread"); + // NOTE: We don't write anything back to Miri's memory, the Mutex state is handled only by GenMC. + + info!("GenMC: blocking thread due to intercepted call."); + let genmc_curr_thread = genmc_curr_thread.0; + this.block_thread( + crate::BlockReason::Mutex, + None, + create_callback(genmc_curr_thread, addr, size), + ); + } else { + info!("GenMC: handling Mutex::lock: success: lock acquired."); + } + } else if this.tcx.is_diagnostic_item(sym::sys_mutex_try_lock, instance.def_id()) { + info!("GenMC: handling Mutex::try_lock()"); + let (genmc_curr_thread, addr, size) = get_mutex_call_infos()?; + + let result = { + let mut mc = genmc_ctx.handle.borrow_mut(); + let pinned_mc = mc.as_mut(); + pinned_mc.handleMutexTryLock(genmc_curr_thread.0, addr, size) + }; + if let Some(error) = result.error.as_ref() { + let msg = error.to_string_lossy().to_string(); + info!("GenMC: handling Mutex::try_lock: error: {msg:?}"); + throw_ub_format!("{msg}"); + } + info!( + "GenMC: Mutex::try_lock(): writing resulting bool is_lock_acquired ({}) to place: {dest:?}", + result.is_lock_acquired + ); + + this.write_scalar(Scalar::from_bool(result.is_lock_acquired), dest)?; + // todo!("return whether lock was successful or not"); + } else if this.tcx.is_diagnostic_item(sym::sys_mutex_unlock, instance.def_id()) { + info!("GenMC: handling Mutex::unlock()"); + let (genmc_curr_thread, addr, size) = get_mutex_call_infos()?; + + let mut mc = genmc_ctx.handle.borrow_mut(); + let pinned_mc = mc.as_mut(); + let result = pinned_mc.handleMutexUnlock(genmc_curr_thread.0, addr, size); + if let Some(error) = result.error.as_ref() { + let msg = error.to_string_lossy().to_string(); + throw_ub_format!("{msg}"); + } + // NOTE: We don't write anything back to Miri's memory, the Mutex state is handled only by GenMC. + + // this.unblock_thread(, crate::BlockReason::Mutex)?; + } else { + return interp_ok(false); + }; + + this.return_to_block(ret)?; + + interp_ok(true) } /**** Scheduling functionality ****/ /// Ask for a scheduling decision. This should be called before every MIR instruction. /// - /// GenMC may realize that the execution got stuck, then this function will return a `InterpErrorKind::MachineStop` with error kind `TerminationInfo::GenmcStuckExecution`). + /// GenMC may realize that the execution is blocked, then this function will return a `InterpErrorKind::MachineStop` with error kind `TerminationInfo::GenmcBlockedExecution`). /// /// This is **not** an error by iself! Treat this as if the program ended normally: `handle_execution_end` should be called next, which will determine if were are any actual errors. - pub(crate) fn schedule_thread<'tcx>( - &self, - ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, - ) -> InterpResult<'tcx, ThreadId> { - assert!(!self.allow_data_races.get()); - todo!() + fn genmc_schedule_thread(&mut self) -> InterpResult<'tcx, ThreadId> { + let this = self.eval_context_mut(); + loop { + let genmc_ctx = this.machine.data_race.as_genmc_ref().unwrap(); + let next_thread_id = genmc_ctx.schedule_thread(this)?; + + match this.machine.threads.threads_ref()[next_thread_id].get_state() { + ThreadState::Blocked { + reason: block_reason @ (BlockReason::Mutex | BlockReason::GenmcAssume), + .. + } => { + info!( + "GenMC: schedule returned thread {next_thread_id:?}, which is blocked, so we unblock it now." + ); + this.unblock_thread(next_thread_id, *block_reason)?; + + // In some cases, like waiting on a Mutex::lock, the thread might still be blocked here: + if this.machine.threads.threads_ref()[next_thread_id] + .get_state() + .is_blocked_on(crate::BlockReason::Mutex) + { + info!("GenMC: Unblocked thread is blocked on a Mutex again!"); + continue; + } + } + _ => {} + } + + return interp_ok(next_thread_id); + } } /**** Blocking instructions ****/ - pub(crate) fn handle_verifier_assume<'tcx>( - &self, - machine: &MiriMachine<'tcx>, - condition: bool, - ) -> InterpResult<'tcx, ()> { - if condition { interp_ok(()) } else { self.handle_user_block(machine) } + /// Handle an `assume` statement. This will tell GenMC to block the current thread if the `condition` is false. + /// Returns `true` if the current thread should be blocked in Miri too. + fn handle_genmc_verifier_assume(&mut self, condition: &OpTy<'tcx>) -> InterpResult<'tcx> { + let this = self.eval_context_mut(); + let condition_bool = this.read_scalar(condition)?.to_bool()?; + info!("GenMC: handle_genmc_verifier_assume, condition: {condition:?} = {condition_bool}"); + if condition_bool { + return interp_ok(()); + } + let genmc_ctx = this.machine.data_race.as_genmc_ref().unwrap(); + genmc_ctx.handle_user_block(&this.machine)?; + let condition = condition.clone(); + this.block_thread( + BlockReason::GenmcAssume, + None, + callback!( + @capture<'tcx> { + condition: OpTy<'tcx>, + } + |this, unblock: UnblockKind| { + assert_eq!(unblock, UnblockKind::Ready); + + let condition = this.run_for_validation_ref(|this| this.read_scalar(&condition))?.to_bool()?; + assert!(condition); + + interp_ok(()) + } + ), + ); + interp_ok(()) } } @@ -288,8 +1164,11 @@ impl VisitProvenance for GenmcCtx { } } -impl GenmcCtx { - fn handle_user_block<'tcx>(&self, machine: &MiriMachine<'tcx>) -> InterpResult<'tcx, ()> { - todo!() +impl std::fmt::Debug for GenmcCtx { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("GenmcCtx") + // .field("mc", &self.mc) + .field("thread_infos", &self.thread_infos) + .finish_non_exhaustive() } } diff --git a/src/concurrency/genmc/thread_info_manager.rs b/src/concurrency/genmc/thread_info_manager.rs new file mode 100644 index 0000000000..3a8a6906fb --- /dev/null +++ b/src/concurrency/genmc/thread_info_manager.rs @@ -0,0 +1,81 @@ +use genmc_sys::{GENMC_MAIN_THREAD_ID, GenmcThreadId}; +use rustc_data_structures::fx::FxHashMap; + +use crate::ThreadId; + +#[derive(Debug)] +pub struct ThreadInfo { + pub miri_tid: ThreadId, + pub genmc_tid: GenmcThreadId, +} + +impl ThreadInfo { + const MAIN_THREAD_INFO: Self = Self::new(ThreadId::MAIN_THREAD, GENMC_MAIN_THREAD_ID); + + #[must_use] + pub const fn new(miri_tid: ThreadId, genmc_tid: GenmcThreadId) -> Self { + Self { miri_tid, genmc_tid } + } +} + +#[derive(Debug)] +pub struct ThreadInfoManager { + tid_map: FxHashMap, + thread_infos: Vec, +} + +impl Default for ThreadInfoManager { + fn default() -> Self { + Self::new() + } +} + +impl ThreadInfoManager { + #[must_use] + pub fn new() -> Self { + let mut tid_map = FxHashMap::default(); + tid_map.insert(ThreadId::MAIN_THREAD, GENMC_MAIN_THREAD_ID); + let thread_infos = vec![ThreadInfo::MAIN_THREAD_INFO]; + Self { tid_map, thread_infos } + } + + pub fn reset(&mut self) { + self.tid_map.clear(); + self.tid_map.insert(ThreadId::MAIN_THREAD, GENMC_MAIN_THREAD_ID); + self.thread_infos.clear(); + self.thread_infos.push(ThreadInfo::MAIN_THREAD_INFO); + } + + #[must_use] + #[allow(unused)] + pub fn thread_count(&self) -> usize { + self.thread_infos.len() + } + + pub fn add_thread(&mut self, thread_id: ThreadId) -> GenmcThreadId { + // NOTE: GenMC thread ids are integers incremented by one every time + let index = self.thread_infos.len(); + let genmc_tid = GenmcThreadId(index.try_into().unwrap()); + let thread_info = ThreadInfo::new(thread_id, genmc_tid); + // TODO GENMC: Document this in place where ThreadIds are created + assert!( + self.tid_map.insert(thread_id, genmc_tid).is_none(), + "Cannot reuse thread ids: thread id {thread_id:?} already inserted" + ); + self.thread_infos.push(thread_info); + + genmc_tid + } + + #[must_use] + pub fn get_info(&self, thread_id: ThreadId) -> &ThreadInfo { + let genmc_tid = *self.tid_map.get(&thread_id).unwrap(); + self.get_info_genmc(genmc_tid) + } + + #[must_use] + pub fn get_info_genmc(&self, genmc_tid: GenmcThreadId) -> &ThreadInfo { + let index: usize = genmc_tid.0.try_into().unwrap(); + &self.thread_infos[index] + } +} diff --git a/src/concurrency/genmc/warnings.rs b/src/concurrency/genmc/warnings.rs new file mode 100644 index 0000000000..e8d3b2a922 --- /dev/null +++ b/src/concurrency/genmc/warnings.rs @@ -0,0 +1,66 @@ +use rustc_data_structures::fx::FxHashSet; +use rustc_middle::query::TyCtxtAt; +use rustc_span::Span; + +use crate::{AtomicReadOrd, AtomicRwOrd}; + +#[derive(Default)] +pub struct WarningsCache { + emitted_compare_exchange_weak: FxHashSet, + emitted_compare_exchange_failure_ordering: FxHashSet<(Span, AtomicReadOrd, AtomicReadOrd)>, +} + +impl WarningsCache { + /// Warn about unsupported spurious failures of `compare_exchange_weak`, once per span, returning `true` if the warning was printed. + pub fn warn_once_compare_exchange_weak<'tcx>(&mut self, tcx: &TyCtxtAt<'tcx>) -> bool { + if self.emitted_compare_exchange_weak.insert(tcx.span) { + tcx.dcx().span_warn(tcx.span, "GenMC mode currently does not model spurious failures of `compare_exchange_weak`. This may lead to missed bugs (possible unsoundness)!"); + return true; + } + false + } + + /// Check if the given failure ordering is unsupported by GenMC. + /// Warning is printed only once per span and ordering combination. + /// Returns `true` if the warning was printed. + pub fn warn_once_rmw_failure_ordering<'tcx>( + &mut self, + tcx: &TyCtxtAt<'tcx>, + success_ordering: AtomicRwOrd, + failure_load_ordering: AtomicReadOrd, + ) -> bool { + let (success_load_ordering, _success_store_ordering) = + success_ordering.split_memory_orderings(); + let is_failure_ordering_weaker = match (success_load_ordering, failure_load_ordering) { + // Unsound: failure ordering is weaker than success ordering, but GenMC treats them as equally strong. + // Actual program execution might have behavior not modelled by GenMC: + (AtomicReadOrd::Acquire, AtomicReadOrd::Relaxed) + | (AtomicReadOrd::SeqCst, AtomicReadOrd::Relaxed) + | (AtomicReadOrd::SeqCst, AtomicReadOrd::Acquire) => true, + // Possible false positives: failure ordering is stronger than success ordering, but GenMC treats them as equally strong. + // We might explore executions that are not allowed by the program. + (AtomicReadOrd::Relaxed, AtomicReadOrd::Acquire) + | (AtomicReadOrd::Relaxed, AtomicReadOrd::SeqCst) + | (AtomicReadOrd::Acquire, AtomicReadOrd::SeqCst) => false, + // Correct: failure ordering is equally strong as success ordering: + (AtomicReadOrd::Relaxed, AtomicReadOrd::Relaxed) + | (AtomicReadOrd::Acquire, AtomicReadOrd::Acquire) + | (AtomicReadOrd::SeqCst, AtomicReadOrd::SeqCst) => return false, + }; + let key = (tcx.span, success_load_ordering, failure_load_ordering); + if self.emitted_compare_exchange_failure_ordering.insert(key) { + let error = if is_failure_ordering_weaker { + "miss bugs related to this memory access (possible unsoundness)!" + } else { + "incorrectly detect errors related to this memory access (possible false positives)." + }; + let msg = format!( + "GenMC currently does not model the atomic failure ordering for `compare_exchange`. Failure ordering '{failure_load_ordering:?}' is treated like '{success_load_ordering:?}', which means that Miri might {error}", + ); + // FIXME(genmc): this doesn't print a span: + tcx.dcx().span_warn(tcx.span, msg); + return true; + } + false + } +} diff --git a/src/concurrency/mod.rs b/src/concurrency/mod.rs index 435615efd9..7c4e262492 100644 --- a/src/concurrency/mod.rs +++ b/src/concurrency/mod.rs @@ -22,5 +22,5 @@ pub mod weak_memory; mod genmc; pub use self::data_race_handler::{AllocDataRaceHandler, GlobalDataRaceHandler}; -pub use self::genmc::{GenmcConfig, GenmcCtx}; +pub use self::genmc::{EvalContextExt as GenmcEvalContextExt, GenmcConfig, GenmcCtx, miri_genmc}; pub use self::vector_clock::VClock; diff --git a/src/concurrency/thread.rs b/src/concurrency/thread.rs index fe1ef86ccd..509f9b5b9e 100644 --- a/src/concurrency/thread.rs +++ b/src/concurrency/thread.rs @@ -16,7 +16,7 @@ use rustc_middle::mir::Mutability; use rustc_middle::ty::layout::TyAndLayout; use rustc_span::Span; -use crate::concurrency::GlobalDataRaceHandler; +use crate::concurrency::{GenmcEvalContextExt as _, GlobalDataRaceHandler}; use crate::shims::tls; use crate::*; @@ -110,10 +110,13 @@ pub enum BlockReason { Eventfd, /// Blocked on unnamed_socket. UnnamedSocket, + /// Blocked on a GenMC `assume` statement (GenMC mode only). + GenmcAssume, } /// The state of a thread. -enum ThreadState<'tcx> { +// TODO GENMC: is this ok to be pub? +pub enum ThreadState<'tcx> { /// The thread is enabled and can be executed. Enabled, /// The thread is blocked on something. @@ -135,15 +138,16 @@ impl<'tcx> std::fmt::Debug for ThreadState<'tcx> { } impl<'tcx> ThreadState<'tcx> { - fn is_enabled(&self) -> bool { + // TODO GENMC: is it ok if these are pub? + pub fn is_enabled(&self) -> bool { matches!(self, ThreadState::Enabled) } - fn is_terminated(&self) -> bool { + pub fn is_terminated(&self) -> bool { matches!(self, ThreadState::Terminated) } - fn is_blocked_on(&self, reason: BlockReason) -> bool { + pub fn is_blocked_on(&self, reason: BlockReason) -> bool { matches!(*self, ThreadState::Blocked { reason: actual_reason, .. } if actual_reason == reason) } } @@ -209,6 +213,11 @@ impl<'tcx> Thread<'tcx> { self.thread_name.as_deref() } + pub fn get_state(&self) -> &ThreadState<'tcx> { + // TODO GENMC: should this implementation detail be exposed? + &self.state + } + /// Get the name of the current thread for display purposes; will include thread ID if not set. fn thread_display_name(&self, id: ThreadId) -> String { if let Some(ref thread_name) = self.thread_name { @@ -343,8 +352,9 @@ impl VisitProvenance for Frame<'_, Provenance, FrameExtra<'_>> { } /// The moment in time when a blocked thread should be woken up. +// TODO GENMC: is this ok to be pub? #[derive(Debug)] -enum Timeout { +pub enum Timeout { Monotonic(Instant), RealTime(SystemTime), } @@ -491,6 +501,11 @@ impl<'tcx> ThreadManager<'tcx> { &mut self.threads[self.active_thread].stack } + /// TODO GENMC: this function can probably be removed once the GenmcCtx code is finished: + pub fn get_thread_stack(&self, id: ThreadId) -> &[Frame<'tcx, Provenance, FrameExtra<'tcx>>] { + &self.threads[id].stack + } + pub fn all_stacks( &self, ) -> impl Iterator>])> { @@ -520,11 +535,21 @@ impl<'tcx> ThreadManager<'tcx> { self.active_thread } + pub fn threads_ref(&self) -> &IndexVec> { + // TODO GENMC: should this implementation detail be exposed? + &self.threads + } + /// Get the total number of threads that were ever spawn by this program. pub fn get_total_thread_count(&self) -> usize { self.threads.len() } + /// Get the total of threads that are currently enabled, i.e., could continue executing. + pub fn get_enabled_thread_count(&self) -> usize { + self.threads.iter().filter(|t| t.state.is_enabled()).count() + } + /// Get the total of threads that are currently live, i.e., not yet terminated. /// (They might be blocked.) pub fn get_live_thread_count(&self) -> usize { @@ -568,6 +593,8 @@ impl<'tcx> ThreadManager<'tcx> { fn detach_thread(&mut self, id: ThreadId, allow_terminated_joined: bool) -> InterpResult<'tcx> { trace!("detaching {:?}", id); + tracing::info!("GenMC: TODO GENMC: does GenMC need special handling for detached threads?"); + let is_ub = if allow_terminated_joined && self.threads[id].state.is_terminated() { // "Detached" in particular means "not yet joined". Redundant detaching is still UB. self.threads[id].join_status == ThreadJoinStatus::Detached @@ -676,13 +703,6 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { #[inline] fn run_on_stack_empty(&mut self) -> InterpResult<'tcx, Poll<()>> { let this = self.eval_context_mut(); - // Inform GenMC that a thread has finished all user code. GenMC needs to know this for scheduling. - // FIXME(GenMC): Thread-local destructors *are* user code, so this is odd. Also now that we - // support pre-main constructors, it can get called there as well. - if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() { - let thread_id = this.active_thread(); - genmc_ctx.handle_thread_stack_empty(thread_id); - } let mut callback = this .active_thread_mut() .on_stack_empty @@ -703,9 +723,10 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { /// If GenMC mode is active, the scheduling is instead handled by GenMC. fn schedule(&mut self) -> InterpResult<'tcx, SchedulingAction> { let this = self.eval_context_mut(); - // In GenMC mode, we let GenMC do the scheduling - if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() { - let next_thread_id = genmc_ctx.schedule_thread(this)?; + + // In GenMC mode, we let GenMC do the scheduling. + if this.machine.data_race.as_genmc_ref().is_some() { + let next_thread_id = this.genmc_schedule_thread()?; let thread_manager = &mut this.machine.threads; thread_manager.active_thread = next_thread_id; @@ -715,7 +736,7 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { return interp_ok(SchedulingAction::ExecuteStep); } - // We are not in GenMC mode, so we control the schedule + // We are not in GenMC mode, so we control the scheduling. let thread_manager = &mut this.machine.threads; let clock = &this.machine.monotonic_clock; let rng = this.machine.rng.get_mut(); @@ -863,7 +884,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { GlobalDataRaceHandler::Vclocks(data_race) => data_race.thread_created(&this.machine.threads, new_thread_id, current_span), GlobalDataRaceHandler::Genmc(genmc_ctx) => - genmc_ctx.handle_thread_create(&this.machine.threads, new_thread_id)?, + genmc_ctx.handle_thread_create( + &this.machine.threads, + start_routine, + &func_arg, + new_thread_id, + )?, } // Write the current thread-id, switch to the next thread later // to treat this write operation as occurring on the current thread. @@ -916,12 +942,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let thread = this.active_thread_mut(); assert!(thread.stack.is_empty(), "only threads with an empty stack can be terminated"); thread.state = ThreadState::Terminated; - match &mut this.machine.data_race { - GlobalDataRaceHandler::None => {} - GlobalDataRaceHandler::Vclocks(data_race) => - data_race.thread_terminated(&this.machine.threads), - GlobalDataRaceHandler::Genmc(genmc_ctx) => - genmc_ctx.handle_thread_finish(&this.machine.threads)?, + + // TODO GENMC (QUESTION): Can we move this down to where the GenmcCtx is? + if let Some(data_race) = this.machine.data_race.as_vclocks_mut() { + data_race.thread_terminated(&this.machine.threads); } // Deallocate TLS. let gone_thread = this.active_thread(); @@ -953,6 +977,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } } } + + // Inform GenMC that the thread finished. + // This needs to happen once all accesses to the thread are done, including freeing any TLS statics. + if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() { + genmc_ctx.handle_thread_finish(&this.machine.threads); + } + // Unblock joining threads. let unblock_reason = BlockReason::Join(gone_thread); let threads = &this.machine.threads.threads; @@ -1076,6 +1107,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "{:?} blocked on {:?} when trying to join", thread_mgr.active_thread, joined_thread_id ); + if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() { + genmc_ctx.handle_thread_join(thread_mgr.active_thread, joined_thread_id)?; + } + // The joined thread is still running, we need to wait for it. // Once we get unblocked, perform the appropriate synchronization and write the return value. let dest = return_dest.clone(); @@ -1236,6 +1271,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { SchedulingAction::ExecuteTimeoutCallback => { this.run_timeout_callback()?; } + // FIXME(genmc): properly handle sleep in GenMC mode. SchedulingAction::Sleep(duration) => { this.machine.monotonic_clock.sleep(duration); } diff --git a/src/diagnostics.rs b/src/diagnostics.rs index 9ecbd31c5b..e5ab282ff4 100644 --- a/src/diagnostics.rs +++ b/src/diagnostics.rs @@ -31,8 +31,10 @@ pub enum TerminationInfo { }, Int2PtrWithStrictProvenance, Deadlock, - /// In GenMC mode, an execution can get stuck in certain cases. This is not an error. - GenmcStuckExecution, + /// Program exit is handled differently in GenMC mode. + /// Executions can get blocked or continue running after the main thread is finished or `exit` is called. + /// The `Exit` variant of this enum should not be used in GenMC mode. + GenmcFinishedExecution, MultipleSymbolDefinitions { link_name: Symbol, first: SpanData, @@ -77,7 +79,11 @@ impl fmt::Display for TerminationInfo { StackedBorrowsUb { msg, .. } => write!(f, "{msg}"), TreeBorrowsUb { title, .. } => write!(f, "{title}"), Deadlock => write!(f, "the evaluated program deadlocked"), - GenmcStuckExecution => write!(f, "GenMC determined that the execution got stuck"), + GenmcFinishedExecution => + write!( + f, + "GenMC determined that the execution is finished (either the program exited or got blocked)" + ), MultipleSymbolDefinitions { link_name, .. } => write!(f, "multiple definitions of symbol `{link_name}`"), SymbolShimClashing { link_name, .. } => @@ -231,6 +237,10 @@ pub fn report_error<'tcx>( let (title, helps) = if let MachineStop(info) = e.kind() { let info = info.downcast_ref::().expect("invalid MachineStop payload"); use TerminationInfo::*; + assert!( + ecx.machine.data_race.as_genmc_ref().is_none() || !matches!(info, Exit { .. }), + "Program exit in GenMC mode should use `GenmcFinishedExecution`." + ); let title = match info { &Exit { code, leak_check } => return Some((code, leak_check)), Abort(_) => Some("abnormal termination"), @@ -243,11 +253,16 @@ pub fn report_error<'tcx>( labels.push(format!("this thread got stuck here")); None } - GenmcStuckExecution => { - // This case should only happen in GenMC mode. We treat it like a normal program exit. - assert!(ecx.machine.data_race.as_genmc_ref().is_some()); - tracing::info!("GenMC: found stuck execution"); - return Some((0, true)); + GenmcFinishedExecution => { + // This case should only happen in GenMC mode. + let genmc_ctx = ecx.machine.data_race.as_genmc_ref().unwrap(); + // Check whether the execution got blocked or the program exited. + if let Some(exit_status) = genmc_ctx.get_exit_status() { + // We have an exit status (from `exit(x)` or main thread return). + return Some(exit_status); + } + // The program got blocked by GenMC without ever exiting, so we don't do any leak checks. + return Some((0, false)); } MultipleSymbolDefinitions { .. } | SymbolShimClashing { .. } => None, }; diff --git a/src/eval.rs b/src/eval.rs index 4c531a8d1f..d9c46f40e8 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -303,8 +303,17 @@ impl<'tcx> MainThreadState<'tcx> { // to be like a global `static`, so that all memory reached by it is considered to "not leak". this.terminate_active_thread(TlsAllocAction::Leak)?; - // Stop interpreter loop. - throw_machine_stop!(TerminationInfo::Exit { code: exit_code, leak_check: true }); + // In GenMC mode, we let GenMC decide what happens on main thread exit. + if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() { + // If there's no error, execution will continue (on another thread). + genmc_ctx.handle_exit(ThreadId::MAIN_THREAD, exit_code, false)?; + } else { + // Stop interpreter loop. + throw_machine_stop!(TerminationInfo::Exit { + code: exit_code, + leak_check: true + }); + } } } interp_ok(Poll::Pending) diff --git a/src/lib.rs b/src/lib.rs index 507d4f7b42..04c50b5be4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -133,7 +133,7 @@ pub use crate::concurrency::thread::{ BlockReason, DynUnblockCallback, EvalContextExt as _, StackEmptyCallback, ThreadId, ThreadManager, TimeoutAnchor, TimeoutClock, UnblockKind, }; -pub use crate::concurrency::{GenmcConfig, GenmcCtx}; +pub use crate::concurrency::{GenmcConfig, GenmcCtx, miri_genmc}; pub use crate::data_structures::dedup_range_map::DedupRangeMap; pub use crate::data_structures::mono_hash_map::MonoHashMap; pub use crate::diagnostics::{ diff --git a/src/machine.rs b/src/machine.rs index 8f0814a070..6c0d498781 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -30,7 +30,9 @@ use rustc_target::callconv::FnAbi; use crate::alloc_addresses::EvalContextExt; use crate::concurrency::cpu_affinity::{self, CpuAffinityMask}; use crate::concurrency::data_race::{self, NaReadType, NaWriteType}; -use crate::concurrency::{AllocDataRaceHandler, GenmcCtx, GlobalDataRaceHandler, weak_memory}; +use crate::concurrency::{ + AllocDataRaceHandler, GenmcCtx, GenmcEvalContextExt as _, GlobalDataRaceHandler, weak_memory, +}; use crate::*; /// First real-time signal. @@ -1122,6 +1124,12 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { return ecx.emulate_foreign_item(link_name, abi, &args, dest, ret, unwind); } + if ecx.machine.data_race.as_genmc_ref().is_some() + && ecx.check_genmc_intercept_function(instance, args, dest, ret)? + { + return interp_ok(None); + } + // Otherwise, load the MIR. interp_ok(Some((ecx.load_mir(instance.def, None)?, instance))) } @@ -1274,6 +1282,10 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { size: Size, align: Align, ) -> InterpResult<'tcx, Self::AllocExtra> { + info!( + "GenMC: TODO GENMC: init_local_allocation: id: {id:?}, kind: {kind:?}, size: {size:?}, align: {align:?}" + ); + assert!(kind != MiriMemoryKind::Global.into()); MiriMachine::init_allocation(ecx, id, kind, size, align) } @@ -1365,6 +1377,10 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { alloc: &'b Allocation, ) -> InterpResult<'tcx, Cow<'b, Allocation>> { + info!( + "GenMC: adjust_global_allocation (TODO GENMC): id: {id:?} ==> Maybe tell GenMC about initial value here?" + ); + let alloc = alloc.adjust_from_tcx( &ecx.tcx, |bytes, align| ecx.get_global_alloc_bytes(id, bytes, align), @@ -1425,9 +1441,8 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { } match &machine.data_race { GlobalDataRaceHandler::None => {} - GlobalDataRaceHandler::Genmc(genmc_ctx) => { - genmc_ctx.memory_store(machine, ptr.addr(), range.size)?; - } + GlobalDataRaceHandler::Genmc(genmc_ctx) => + genmc_ctx.memory_store(machine, ptr.addr(), range.size)?, GlobalDataRaceHandler::Vclocks(_global_state) => { let AllocDataRaceHandler::Vclocks(data_race, weak_memory) = &mut alloc_extra.data_race @@ -1463,7 +1478,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { match &machine.data_race { GlobalDataRaceHandler::None => {} GlobalDataRaceHandler::Genmc(genmc_ctx) => - genmc_ctx.handle_dealloc(machine, ptr.addr(), size, align, kind)?, + genmc_ctx.handle_dealloc(machine, alloc_id, ptr.addr(), size, align, kind)?, GlobalDataRaceHandler::Vclocks(_global_state) => { let data_race = alloc_extra.data_race.as_vclocks_mut().unwrap(); data_race.write( @@ -1674,6 +1689,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { frame: &Frame<'tcx, Provenance, FrameExtra<'tcx>>, local: mir::Local, ) -> InterpResult<'tcx> { + // TODO GENMC: does GenMC care about local reads/writes? if let Some(data_race) = &frame.extra.data_race { data_race.local_read(local, &ecx.machine); } @@ -1685,6 +1701,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { local: mir::Local, storage_live: bool, ) -> InterpResult<'tcx> { + // TODO GENMC: does GenMC care about local reads/writes? if let Some(data_race) = &ecx.frame().extra.data_race { data_race.local_write(local, storage_live, &ecx.machine); } @@ -1714,6 +1731,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { machine, ); } + // TODO GENMC: how to handle this (if at all)? interp_ok(()) } diff --git a/src/shims/foreign_items.rs b/src/shims/foreign_items.rs index 21545b6802..578f66fb3d 100644 --- a/src/shims/foreign_items.rs +++ b/src/shims/foreign_items.rs @@ -17,6 +17,7 @@ use rustc_target::callconv::FnAbi; use self::helpers::{ToHost, ToSoft}; use super::alloc::EvalContextExt as _; use super::backtrace::EvalContextExt as _; +use crate::concurrency::GenmcEvalContextExt as _; use crate::*; /// Type of dynamic symbols (for `dlsym` et al) @@ -439,17 +440,38 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { } } + /*** \/ GENMC VERIFIER CALLS \/ ****/ + "miri_genmc_verifier_assume" => { + let [condition] = + this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; + if this.machine.data_race.as_genmc_ref().is_some() { + this.handle_genmc_verifier_assume(condition)?; + } else { + tracing::warn!( + "GenMC: function `miri_genmc_verifier_assume` used, but GenMC mode is not active, skip ..." + ); + } + } + + // TODO GENMC: add other genmc functions + + /*** /\ GENMC VERIFIER CALLS /\ ****/ // Aborting the process. "exit" => { let [code] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let code = this.read_scalar(code)?.to_i32()?; + if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() { + // If there is no error, execution will continue (on another thread). + genmc_ctx.handle_exit(this.machine.threads.active_thread(), code, true)?; + return interp_ok(EmulateItemResult::AlreadyJumped); + } throw_machine_stop!(TerminationInfo::Exit { code, leak_check: false }); } "abort" => { let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; throw_machine_stop!(TerminationInfo::Abort( "the program aborted execution".to_owned() - )) + )); } // Standard C allocation diff --git a/tests/genmc/_disabled/fail/weak_memory/weak_uninit.rs b/tests/genmc/_disabled/fail/weak_memory/weak_uninit.rs new file mode 100644 index 0000000000..b72b0a169c --- /dev/null +++ b/tests/genmc/_disabled/fail/weak_memory/weak_uninit.rs @@ -0,0 +1,57 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// NOTE: Disabled due to incomplete uninitialized memory support in Miri-GenMC mode. + +// Tests showing weak memory behaviours are exhibited. All tests +// return true when the desired behaviour is seen. +// This is scheduler and pseudo-RNG dependent, so each test is +// run multiple times until one try returns true. +// Spurious failure is possible, if you are really unlucky with +// the RNG and always read the latest value from the store buffer. + +#![no_main] + +#[path = "../../../utils-dep/mod.rs"] +mod utils_dep; + +use std::ffi::c_void; +use std::mem::MaybeUninit; +use std::sync::atomic::*; + +use crate::utils_dep::*; + +#[allow(dead_code)] +#[derive(Copy, Clone)] +struct EvilSend(pub T); + +unsafe impl Send for EvilSend {} +unsafe impl Sync for EvilSend {} + +static mut F: MaybeUninit = MaybeUninit::uninit(); + +extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { + unsafe { + let x = AtomicUsize::from_ptr(&raw mut F as *mut usize); + x.store(1, Ordering::Relaxed); + std::ptr::null_mut() + } +} + +extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { + unsafe { + let x = AtomicUsize::from_ptr(&raw mut F as *mut usize); + x.load(Ordering::Relaxed); //~ERROR: using uninitialized data + std::ptr::null_mut() + } +} + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // Unlike with the non-GenMC version of this test, we should only need 1 iteration to detect the bug: + unsafe { + let ids = create_pthreads_no_params([thread_1, thread_2]); + join_pthreads(ids); + } + + 0 +} diff --git a/tests/genmc/fail/loom/buggy-inc/buggy_inc.rs b/tests/genmc/fail/loom/buggy-inc/buggy_inc.rs new file mode 100644 index 0000000000..fefefa6b39 --- /dev/null +++ b/tests/genmc/fail/loom/buggy-inc/buggy_inc.rs @@ -0,0 +1,56 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// This tests is the test `checks_fail` from loom/test/smoke.rs adapted for Miri-GenMC. +// https://github.com/tokio-rs/loom/blob/dbf32b04bae821c64be44405a0bb72ca08741558/tests/smoke.rs + +// This test checks that an incorrect implementation of an incrementing counter is detected. +// The counter behaves wrong if two threads try to increment at the same time (increments can be lost). + +#![no_main] + +#[cfg(not(any(non_genmc_std, genmc_std)))] +#[path = "../../../../utils-dep/mod.rs"] +mod utils_dep; + +use std::ffi::c_void; +use std::sync::atomic::AtomicUsize; +use std::sync::atomic::Ordering::{Acquire, Relaxed, Release}; + +use crate::utils_dep::genmc::{create_pthreads_no_params, join_pthreads}; + +struct BuggyInc { + num: AtomicUsize, +} + +impl BuggyInc { + const fn new() -> BuggyInc { + BuggyInc { num: AtomicUsize::new(0) } + } + + fn inc(&self) { + // The bug is here: + // Another thread can increment `self.num` between the next two lines, which is then overridden by this thread. + let curr = self.num.load(Acquire); + self.num.store(curr + 1, Release); + } +} + +static BUGGY_INC: BuggyInc = BuggyInc::new(); + +extern "C" fn thread_func(_value: *mut c_void) -> *mut c_void { + BUGGY_INC.inc(); + std::ptr::null_mut() +} + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + let thread_ids = unsafe { create_pthreads_no_params([thread_func; 2]) }; + + unsafe { join_pthreads(thread_ids) }; + + if 2 != BUGGY_INC.num.load(Relaxed) { + unsafe { std::hint::unreachable_unchecked() }; //~ ERROR: entering unreachable code + } + + 0 +} diff --git a/tests/genmc/fail/loom/buggy-inc/buggy_inc.stderr b/tests/genmc/fail/loom/buggy-inc/buggy_inc.stderr new file mode 100644 index 0000000000..6b87d1c736 --- /dev/null +++ b/tests/genmc/fail/loom/buggy-inc/buggy_inc.stderr @@ -0,0 +1,15 @@ +error: Undefined Behavior: entering unreachable code + --> tests/genmc/fail/loom/buggy-inc/buggy_inc.rs:LL:CC + | +LL | unsafe { std::hint::unreachable_unchecked() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = 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 + = note: BACKTRACE: + = note: inside `miri_start` at tests/genmc/fail/loom/buggy-inc/buggy_inc.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 + diff --git a/tests/genmc/fail/loom/store-buffering/store_buffering.genmc.stderr b/tests/genmc/fail/loom/store-buffering/store_buffering.genmc.stderr new file mode 100644 index 0000000000..9fa6ca9b1d --- /dev/null +++ b/tests/genmc/fail/loom/store-buffering/store_buffering.genmc.stderr @@ -0,0 +1,15 @@ +error: Undefined Behavior: entering unreachable code + --> tests/genmc/fail/loom/store-buffering/store_buffering.rs:LL:CC + | +LL | unsafe { std::hint::unreachable_unchecked() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = 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 + = note: BACKTRACE: + = note: inside `miri_start` at tests/genmc/fail/loom/store-buffering/store_buffering.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 + diff --git a/tests/genmc/fail/loom/store-buffering/store_buffering.genmc_std.stderr b/tests/genmc/fail/loom/store-buffering/store_buffering.genmc_std.stderr new file mode 100644 index 0000000000..61babf0493 --- /dev/null +++ b/tests/genmc/fail/loom/store-buffering/store_buffering.genmc_std.stderr @@ -0,0 +1,21 @@ +warning: GenMC currently does not model the atomic failure ordering for `compare_exchange`. Failure ordering 'Relaxed' is treated like 'Acquire', which means that Miri might miss bugs related to this memory access (possible unsoundness)! + +warning: GenMC mode currently does not model spurious failures of `compare_exchange_weak`. This may lead to missed bugs (possible unsoundness)! + +warning: GenMC currently does not model the atomic failure ordering for `compare_exchange`. Failure ordering 'Acquire' is treated like 'Relaxed', which means that Miri might incorrectly detect errors related to this memory access (possible false positives). + +error: Undefined Behavior: entering unreachable code + --> tests/genmc/fail/loom/store-buffering/store_buffering.rs:LL:CC + | +LL | unsafe { std::hint::unreachable_unchecked() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = 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 + = note: BACKTRACE: + = note: inside `miri_start` at tests/genmc/fail/loom/store-buffering/store_buffering.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; 3 warnings emitted + diff --git a/tests/genmc/fail/loom/store-buffering/store_buffering.non_genmc.stderr b/tests/genmc/fail/loom/store-buffering/store_buffering.non_genmc.stderr new file mode 100644 index 0000000000..9fa6ca9b1d --- /dev/null +++ b/tests/genmc/fail/loom/store-buffering/store_buffering.non_genmc.stderr @@ -0,0 +1,15 @@ +error: Undefined Behavior: entering unreachable code + --> tests/genmc/fail/loom/store-buffering/store_buffering.rs:LL:CC + | +LL | unsafe { std::hint::unreachable_unchecked() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = 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 + = note: BACKTRACE: + = note: inside `miri_start` at tests/genmc/fail/loom/store-buffering/store_buffering.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 + diff --git a/tests/genmc/fail/loom/store-buffering/store_buffering.non_genmc_std.stderr b/tests/genmc/fail/loom/store-buffering/store_buffering.non_genmc_std.stderr new file mode 100644 index 0000000000..9fa6ca9b1d --- /dev/null +++ b/tests/genmc/fail/loom/store-buffering/store_buffering.non_genmc_std.stderr @@ -0,0 +1,15 @@ +error: Undefined Behavior: entering unreachable code + --> tests/genmc/fail/loom/store-buffering/store_buffering.rs:LL:CC + | +LL | unsafe { std::hint::unreachable_unchecked() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = 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 + = note: BACKTRACE: + = note: inside `miri_start` at tests/genmc/fail/loom/store-buffering/store_buffering.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 + diff --git a/tests/genmc/fail/loom/store-buffering/store_buffering.rs b/tests/genmc/fail/loom/store-buffering/store_buffering.rs new file mode 100644 index 0000000000..14ecf6b308 --- /dev/null +++ b/tests/genmc/fail/loom/store-buffering/store_buffering.rs @@ -0,0 +1,83 @@ +//@ revisions: non_genmc non_genmc_std genmc genmc_std +//@[non_genmc,non_genmc_std] compile-flags: +//@[genmc,genmc_std] compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// This is the test `store_buffering` from `loom/test/litmus.rs`, adapted for Miri-GenMC. +// https://github.com/tokio-rs/loom/blob/dbf32b04bae821c64be44405a0bb72ca08741558/tests/litmus.rs + +// This test doubles as a comparison between using std threads and pthreads, and normal Miri vs Miri-GenMC. + +#![no_main] + +#[cfg(not(any(non_genmc_std, genmc_std)))] +#[path = "../../../../utils-dep/mod.rs"] +mod utils_dep; + +use std::sync::atomic::AtomicUsize; +use std::sync::atomic::Ordering::{Relaxed, SeqCst}; + +static X: AtomicUsize = AtomicUsize::new(0); +static Y: AtomicUsize = AtomicUsize::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // For normal Miri, we need multiple repetitions, but GenMC should find the bug with only 1. + const REPS: usize = if cfg!(any(non_genmc, non_genmc_std)) { 128 } else { 1 }; + + for _ in 0..REPS { + X.store(0, SeqCst); + Y.store(0, SeqCst); + + if test() == (0, 0) { + unsafe { std::hint::unreachable_unchecked() }; //~ ERROR: entering unreachable code + } + } + + 0 +} + +#[cfg(any(non_genmc_std, genmc_std))] +fn test() -> (usize, usize) { + let thread = std::thread::spawn(thread_0); + + let b = thread_1(); + + let a = thread.join().unwrap(); + + (a, b) +} + +#[cfg(not(any(non_genmc_std, genmc_std)))] +fn test() -> (usize, usize) { + use std::ffi::c_void; + + use crate::utils_dep::genmc::{join_pthread, spawn_pthread}; + + extern "C" fn thread_func(value: *mut c_void) -> *mut c_void { + let a_ptr = value as *mut usize; + let a = thread_0(); + unsafe { *a_ptr = a }; + std::ptr::null_mut() + } + + let mut a: usize = 0; + let thread_id = unsafe { spawn_pthread(thread_func, &raw mut a as *mut c_void) }; + + let b = thread_1(); + + unsafe { join_pthread(thread_id) }; + + (a, b) +} + +/// Returns the value for `a` +fn thread_0() -> usize { + X.store(1, Relaxed); + Y.load(Relaxed) +} + +/// Returns the value for `b` +fn thread_1() -> usize { + Y.store(1, Relaxed); + X.load(Relaxed) +} diff --git a/tests/genmc/fail/simple/2w2w_weak.acq_rel.stderr b/tests/genmc/fail/simple/2w2w_weak.acq_rel.stderr new file mode 100644 index 0000000000..5567f4fc7a --- /dev/null +++ b/tests/genmc/fail/simple/2w2w_weak.acq_rel.stderr @@ -0,0 +1,15 @@ +error: Undefined Behavior: entering unreachable code + --> tests/genmc/fail/simple/2w2w_weak.rs:LL:CC + | +LL | unsafe { std::hint::unreachable_unchecked() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = 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 + = note: BACKTRACE: + = note: inside `miri_start` at tests/genmc/fail/simple/2w2w_weak.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 + diff --git a/tests/genmc/fail/simple/2w2w_weak.relaxed.stderr b/tests/genmc/fail/simple/2w2w_weak.relaxed.stderr new file mode 100644 index 0000000000..5567f4fc7a --- /dev/null +++ b/tests/genmc/fail/simple/2w2w_weak.relaxed.stderr @@ -0,0 +1,15 @@ +error: Undefined Behavior: entering unreachable code + --> tests/genmc/fail/simple/2w2w_weak.rs:LL:CC + | +LL | unsafe { std::hint::unreachable_unchecked() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = 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 + = note: BACKTRACE: + = note: inside `miri_start` at tests/genmc/fail/simple/2w2w_weak.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 + diff --git a/tests/genmc/fail/simple/2w2w_weak.rs b/tests/genmc/fail/simple/2w2w_weak.rs new file mode 100644 index 0000000000..a8b94aa584 --- /dev/null +++ b/tests/genmc/fail/simple/2w2w_weak.rs @@ -0,0 +1,52 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@revisions: acq_rel relaxed + +// This test is the equivalent to the `2w2w_seqcst.rs` "pass" test. +// Here we use weaker atomic memory orderings to test if we can encounter +// an execution where (X == 1 && Y == 1). + +#![no_main] + +#[path = "../../../utils-dep/mod.rs"] +mod utils_dep; + +use std::ffi::c_void; +use std::sync::atomic::{AtomicU64, Ordering}; + +use crate::utils_dep::genmc::{join_pthread, spawn_pthread}; + +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); + +#[cfg(acq_rel)] +const LOAD_ORD: Ordering = Ordering::Acquire; +#[cfg(acq_rel)] +const STORE_ORD: Ordering = Ordering::Release; + +#[cfg(not(acq_rel))] +const LOAD_ORD: Ordering = Ordering::Relaxed; +#[cfg(not(acq_rel))] +const STORE_ORD: Ordering = Ordering::Relaxed; + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + let thread_id = unsafe { spawn_pthread(thread_func, std::ptr::null_mut()) }; + + X.store(1, STORE_ORD); + Y.store(2, STORE_ORD); + + unsafe { join_pthread(thread_id) }; + + let x = X.load(LOAD_ORD); + let y = Y.load(LOAD_ORD); + if x == 1 && y == 1 { + unsafe { std::hint::unreachable_unchecked() }; //~ ERROR: entering unreachable code + } + 0 +} + +extern "C" fn thread_func(_value: *mut c_void) -> *mut c_void { + Y.store(1, STORE_ORD); + X.store(2, STORE_ORD); + std::ptr::null_mut() +} diff --git a/tests/genmc/fail/simple/mixed-atomic-non-atomic/read_global_init.rs b/tests/genmc/fail/simple/mixed-atomic-non-atomic/read_global_init.rs new file mode 100644 index 0000000000..bac8293469 --- /dev/null +++ b/tests/genmc/fail/simple/mixed-atomic-non-atomic/read_global_init.rs @@ -0,0 +1,20 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +#![no_main] + +use std::sync::atomic::{AtomicU64, Ordering}; + +static X: AtomicU64 = AtomicU64::new(1234); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // TODO GENMC: make this a "pass" test + if 1234 != unsafe { *X.as_ptr() } { + unsafe { std::hint::unreachable_unchecked() }; + } + if 1234 == X.load(Ordering::SeqCst) { + unsafe { std::hint::unreachable_unchecked() }; //~ ERROR: entering unreachable code + } + + 0 +} diff --git a/tests/genmc/fail/simple/mixed-atomic-non-atomic/read_global_init.stderr b/tests/genmc/fail/simple/mixed-atomic-non-atomic/read_global_init.stderr new file mode 100644 index 0000000000..ec779aa7c4 --- /dev/null +++ b/tests/genmc/fail/simple/mixed-atomic-non-atomic/read_global_init.stderr @@ -0,0 +1,15 @@ +error: Undefined Behavior: entering unreachable code + --> tests/genmc/fail/simple/mixed-atomic-non-atomic/read_global_init.rs:LL:CC + | +LL | unsafe { std::hint::unreachable_unchecked() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = 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 + = note: BACKTRACE: + = note: inside `miri_start` at tests/genmc/fail/simple/mixed-atomic-non-atomic/read_global_init.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 + diff --git a/tests/genmc/fail/simple/mixed-atomic-non-atomic/wna_wrlx_rrlx.return1234.stderr b/tests/genmc/fail/simple/mixed-atomic-non-atomic/wna_wrlx_rrlx.return1234.stderr new file mode 100644 index 0000000000..28a8b74c86 --- /dev/null +++ b/tests/genmc/fail/simple/mixed-atomic-non-atomic/wna_wrlx_rrlx.return1234.stderr @@ -0,0 +1,15 @@ +error: Undefined Behavior: entering unreachable code + --> tests/genmc/fail/simple/mixed-atomic-non-atomic/wna_wrlx_rrlx.rs:LL:CC + | +LL | std::hint::unreachable_unchecked(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = 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 + = note: BACKTRACE on thread `unnamed-ID`: + = note: inside `read_relaxed` at tests/genmc/fail/simple/mixed-atomic-non-atomic/wna_wrlx_rrlx.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 + diff --git a/tests/genmc/fail/simple/mixed-atomic-non-atomic/wna_wrlx_rrlx.return42.stderr b/tests/genmc/fail/simple/mixed-atomic-non-atomic/wna_wrlx_rrlx.return42.stderr new file mode 100644 index 0000000000..28a8b74c86 --- /dev/null +++ b/tests/genmc/fail/simple/mixed-atomic-non-atomic/wna_wrlx_rrlx.return42.stderr @@ -0,0 +1,15 @@ +error: Undefined Behavior: entering unreachable code + --> tests/genmc/fail/simple/mixed-atomic-non-atomic/wna_wrlx_rrlx.rs:LL:CC + | +LL | std::hint::unreachable_unchecked(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = 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 + = note: BACKTRACE on thread `unnamed-ID`: + = note: inside `read_relaxed` at tests/genmc/fail/simple/mixed-atomic-non-atomic/wna_wrlx_rrlx.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 + diff --git a/tests/genmc/fail/simple/mixed-atomic-non-atomic/wna_wrlx_rrlx.rs b/tests/genmc/fail/simple/mixed-atomic-non-atomic/wna_wrlx_rrlx.rs new file mode 100644 index 0000000000..2b4f803c6a --- /dev/null +++ b/tests/genmc/fail/simple/mixed-atomic-non-atomic/wna_wrlx_rrlx.rs @@ -0,0 +1,58 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@revisions: return1234 return42 + +#![no_main] + +use std::ffi::c_void; +use std::sync::atomic::{AtomicU64, Ordering}; + +use libc::{self, pthread_attr_t, pthread_t}; + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + let mut t0: pthread_t = 0; + let mut t1: pthread_t = 0; + + let attr: *const pthread_attr_t = std::ptr::null(); + + let mut x: AtomicU64 = AtomicU64::new(1); + *x.get_mut() = 42; + // x.store(42, STORE_ORD); + + let value: *mut c_void = x.as_ptr() as *mut c_void; + + assert!(0 == unsafe { libc::pthread_create(&raw mut t0, attr, read_relaxed, value) }); + assert!(0 == unsafe { libc::pthread_create(&raw mut t1, attr, write_relaxed, value) }); + + assert!(0 == unsafe { libc::pthread_join(t0, std::ptr::null_mut()) }); + assert!(0 == unsafe { libc::pthread_join(t1, std::ptr::null_mut()) }); + + 0 +} + +extern "C" fn read_relaxed(value: *mut c_void) -> *mut c_void { + unsafe { + let x = (value as *const AtomicU64).as_ref().unwrap(); + let val = x.load(Ordering::Relaxed); + + let mut flag = false; + if cfg!(return1234) && 1234 == val { + flag = true; + } + if cfg!(return42) && 42 == val { + flag = true; + } + if flag { + std::hint::unreachable_unchecked(); //~ ERROR: entering unreachable code + } + std::ptr::null_mut() + } +} + +extern "C" fn write_relaxed(value: *mut c_void) -> *mut c_void { + unsafe { + let x = (value as *const AtomicU64).as_ref().unwrap(); + x.store(1234, Ordering::Relaxed); + std::ptr::null_mut() + } +} diff --git a/tests/genmc/pass/basics/mutex/TODO_mutex_get_mut.rs.txt b/tests/genmc/pass/basics/mutex/TODO_mutex_get_mut.rs.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/genmc/pass/basics/mutex/mutex_simple.c b/tests/genmc/pass/basics/mutex/mutex_simple.c new file mode 100644 index 0000000000..eb9c87aa82 --- /dev/null +++ b/tests/genmc/pass/basics/mutex/mutex_simple.c @@ -0,0 +1,72 @@ +#include +#include +#include +#include +#include + +#define REPS 1 + +static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; +static uint64_t data[32]; + +void* thread_1(void* arg) { + for (uint64_t i = 0; i < REPS; i++) { + pthread_mutex_lock(&lock); + data[0] += 2; + pthread_mutex_unlock(&lock); + } + return NULL; +} + +void* thread_2(void* arg) { + for (uint64_t i = 0; i < REPS; i++) { + pthread_mutex_lock(&lock); + data[0] += 4; + pthread_mutex_unlock(&lock); + } + return NULL; +} + +int main(int argc, char** argv) { + // Initialize data + for (int i = 0; i < 32; i++) { + data[i] = 1234; + } + + pthread_mutex_lock(&lock); + for (int i = 0; i < 32; i++) { + assert(data[i] == 1234); + } + data[0] = 0; + data[1] = 10; + assert(data[0] == 0 && data[1] == 10); + pthread_mutex_unlock(&lock); + + // Thread order: can be changed for different test orders +#ifdef ORDER21 + void* (*thread_order[2])(void*) = {thread_2, thread_1}; +#else + void* (*thread_order[2])(void*) = {thread_1, thread_2}; +#endif + + pthread_t ids[2]; + for (int i = 0; i < 2; i++) { + int ret = pthread_create(&ids[i], NULL, thread_order[i], NULL); + assert(ret == 0); + } + + for (int i = 0; i < 2; i++) { + int ret = pthread_join(ids[i], NULL); + assert(ret == 0); + } + + pthread_mutex_lock(&lock); + // assert(data[0] == REPS * 6); // Not checked, but can be enabled + assert(data[1] == 10); + for (int i = 2; i < 32; i++) { + assert(data[i] == 1234); + } + pthread_mutex_unlock(&lock); + + return 0; +} diff --git a/tests/genmc/pass/basics/mutex/mutex_simple.order12reps1.stderr b/tests/genmc/pass/basics/mutex/mutex_simple.order12reps1.stderr new file mode 100644 index 0000000000..edecd38278 --- /dev/null +++ b/tests/genmc/pass/basics/mutex/mutex_simple.order12reps1.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 2 diff --git a/tests/genmc/pass/basics/mutex/mutex_simple.order12reps2.stderr b/tests/genmc/pass/basics/mutex/mutex_simple.order12reps2.stderr new file mode 100644 index 0000000000..e4151d346e --- /dev/null +++ b/tests/genmc/pass/basics/mutex/mutex_simple.order12reps2.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 6 diff --git a/tests/genmc/pass/basics/mutex/mutex_simple.order21reps1.stderr b/tests/genmc/pass/basics/mutex/mutex_simple.order21reps1.stderr new file mode 100644 index 0000000000..edecd38278 --- /dev/null +++ b/tests/genmc/pass/basics/mutex/mutex_simple.order21reps1.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 2 diff --git a/tests/genmc/pass/basics/mutex/mutex_simple.order21reps2.stderr b/tests/genmc/pass/basics/mutex/mutex_simple.order21reps2.stderr new file mode 100644 index 0000000000..e4151d346e --- /dev/null +++ b/tests/genmc/pass/basics/mutex/mutex_simple.order21reps2.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 6 diff --git a/tests/genmc/pass/basics/mutex/mutex_simple.rs b/tests/genmc/pass/basics/mutex/mutex_simple.rs new file mode 100644 index 0000000000..63349b03be --- /dev/null +++ b/tests/genmc/pass/basics/mutex/mutex_simple.rs @@ -0,0 +1,73 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@revisions: order12reps1 order21reps1 order12reps2 order21reps2 + +#![no_main] +#![feature(abort_unwind)] + +#[path = "../../../../utils-dep/mod.rs"] +mod utils_dep; + +use std::ffi::c_void; +use std::sync::Mutex; + +use crate::utils_dep::*; + +#[cfg(not(any(order12reps2, order21reps2)))] +const REPS: u64 = 1; +#[cfg(any(order12reps2, order21reps2))] +const REPS: u64 = 2; + +static LOCK: Mutex<[u64; 32]> = Mutex::new([1234; 32]); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + std::panic::abort_unwind(main_); + 0 +} + +fn main_() { + let mut guard = LOCK.lock().unwrap(); + for &v in guard.iter() { + assert!(v == 1234); // Check that mutex values are initialized correctly + } + guard[0] = 0; + guard[1] = 10; + assert!(guard[0] == 0 && guard[1] == 10); // Check if changes are accepted + + assert!(LOCK.try_lock().is_err()); // Trying to lock should fail if the lock is already held + + drop(guard); // Dropping the guard should unlock the mutex correctly. + { + assert!(LOCK.try_lock().is_ok()); // Trying to lock now should *not* fail since the lock is not held. + } + + // Thread spawning order should not matter for the result + let thread_order = if cfg!(order21) { [thread_2, thread_1] } else { [thread_1, thread_2] }; + // let thread_order = [thread_1 as extern "C" fn(*mut libc::c_void) -> *mut libc::c_void]; + let ids = unsafe { create_pthreads_no_params(thread_order) }; + unsafe { join_pthreads(ids) }; + + let guard = LOCK.lock().unwrap(); + assert!(guard[0] == REPS * 6); // Due to locking, no weird values should be here + assert!(guard[1] == 10); // Rest should be unchanged + for &v in guard.iter().skip(2) { + assert!(v == 1234); + } + drop(guard); +} + +extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { + for _ in 0..REPS { + let mut guard = LOCK.lock().unwrap(); + guard[0] += 2; + } + std::ptr::null_mut() +} + +extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { + for _ in 0..REPS { + let mut guard = LOCK.lock().unwrap(); + guard[0] += 4; + } + std::ptr::null_mut() +} diff --git a/tests/genmc/pass/basics/rmw/rmw_edge_cases.i16_.stderr b/tests/genmc/pass/basics/rmw/rmw_edge_cases.i16_.stderr new file mode 100644 index 0000000000..2382cc5a78 --- /dev/null +++ b/tests/genmc/pass/basics/rmw/rmw_edge_cases.i16_.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 1 diff --git a/tests/genmc/pass/basics/rmw/rmw_edge_cases.i32_.stderr b/tests/genmc/pass/basics/rmw/rmw_edge_cases.i32_.stderr new file mode 100644 index 0000000000..2382cc5a78 --- /dev/null +++ b/tests/genmc/pass/basics/rmw/rmw_edge_cases.i32_.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 1 diff --git a/tests/genmc/pass/basics/rmw/rmw_edge_cases.i64_.stderr b/tests/genmc/pass/basics/rmw/rmw_edge_cases.i64_.stderr new file mode 100644 index 0000000000..2382cc5a78 --- /dev/null +++ b/tests/genmc/pass/basics/rmw/rmw_edge_cases.i64_.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 1 diff --git a/tests/genmc/pass/basics/rmw/rmw_edge_cases.i8_.stderr b/tests/genmc/pass/basics/rmw/rmw_edge_cases.i8_.stderr new file mode 100644 index 0000000000..2382cc5a78 --- /dev/null +++ b/tests/genmc/pass/basics/rmw/rmw_edge_cases.i8_.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 1 diff --git a/tests/genmc/pass/basics/rmw/rmw_edge_cases.isize_.stderr b/tests/genmc/pass/basics/rmw/rmw_edge_cases.isize_.stderr new file mode 100644 index 0000000000..2382cc5a78 --- /dev/null +++ b/tests/genmc/pass/basics/rmw/rmw_edge_cases.isize_.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 1 diff --git a/tests/genmc/pass/basics/rmw/rmw_edge_cases.rs b/tests/genmc/pass/basics/rmw/rmw_edge_cases.rs new file mode 100644 index 0000000000..dbdef25071 --- /dev/null +++ b/tests/genmc/pass/basics/rmw/rmw_edge_cases.rs @@ -0,0 +1,88 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@revisions: u8_ u16_ u32_ u64_ usize_ i8_ i16_ i32_ i64_ isize_ + +// FIXME(genmc): ensure that 64 bit tests don't run on platforms without 64 bit atomics +// FIXME(genmc): add 128 bit tests for platforms that support it, once GenMC gets 128 bit atomic support + +// This test check for correct handling of some edge cases with atomic read-modify-write operations for all integer sizes. +// Atomic max and min should return the previous value, and store the result in the atomic. +// Atomic addition and subtraction should have wrapping semantics. + +#![no_main] + +#[cfg(u8_)] +type Int = u8; +#[cfg(u8_)] +type AtomicInt = AtomicU8; + +#[cfg(u16_)] +type Int = u16; +#[cfg(u16_)] +type AtomicInt = AtomicU16; + +#[cfg(u32_)] +type Int = u32; +#[cfg(u32_)] +type AtomicInt = AtomicU32; + +#[cfg(u64_)] +type Int = u64; +#[cfg(u64_)] +type AtomicInt = AtomicU64; + +#[cfg(usize_)] +type Int = usize; +#[cfg(usize_)] +type AtomicInt = AtomicUsize; + +#[cfg(i8_)] +type Int = i8; +#[cfg(i8_)] +type AtomicInt = AtomicI8; + +#[cfg(i16_)] +type Int = i16; +#[cfg(i16_)] +type AtomicInt = AtomicI16; + +#[cfg(i32_)] +type Int = i32; +#[cfg(i32_)] +type AtomicInt = AtomicI32; + +#[cfg(i64_)] +type Int = i64; +#[cfg(i64_)] +type AtomicInt = AtomicI64; + +#[cfg(isize_)] +type Int = isize; +#[cfg(isize_)] +type AtomicInt = AtomicIsize; + +use std::sync::atomic::*; + +const ORD: Ordering = Ordering::SeqCst; + +fn assert_eq(x: T, y: T) { + if x != y { + std::process::abort(); + } +} + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + let x = AtomicInt::new(123); + assert_eq(123, x.fetch_max(0, ORD)); // `max` keeps existing value + assert_eq(123, x.fetch_max(Int::MAX, ORD)); // `max` stores the new value + assert_eq(Int::MAX, x.fetch_add(10, ORD)); // `fetch_add` should be wrapping + assert_eq(Int::MAX.wrapping_add(10), x.load(ORD)); + + x.store(42, ORD); + assert_eq(42, x.fetch_min(Int::MAX, ORD)); // `max` keeps existing value + assert_eq(42, x.fetch_min(Int::MIN, ORD)); // `max` stores the new value + assert_eq(Int::MIN, x.fetch_sub(10, ORD)); // `fetch_sub` should be wrapping + assert_eq(Int::MIN.wrapping_sub(10), x.load(ORD)); + + 0 +} diff --git a/tests/genmc/pass/basics/rmw/rmw_edge_cases.u16_.stderr b/tests/genmc/pass/basics/rmw/rmw_edge_cases.u16_.stderr new file mode 100644 index 0000000000..2382cc5a78 --- /dev/null +++ b/tests/genmc/pass/basics/rmw/rmw_edge_cases.u16_.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 1 diff --git a/tests/genmc/pass/basics/rmw/rmw_edge_cases.u32_.stderr b/tests/genmc/pass/basics/rmw/rmw_edge_cases.u32_.stderr new file mode 100644 index 0000000000..2382cc5a78 --- /dev/null +++ b/tests/genmc/pass/basics/rmw/rmw_edge_cases.u32_.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 1 diff --git a/tests/genmc/pass/basics/rmw/rmw_edge_cases.u64_.stderr b/tests/genmc/pass/basics/rmw/rmw_edge_cases.u64_.stderr new file mode 100644 index 0000000000..2382cc5a78 --- /dev/null +++ b/tests/genmc/pass/basics/rmw/rmw_edge_cases.u64_.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 1 diff --git a/tests/genmc/pass/basics/rmw/rmw_edge_cases.u8_.stderr b/tests/genmc/pass/basics/rmw/rmw_edge_cases.u8_.stderr new file mode 100644 index 0000000000..2382cc5a78 --- /dev/null +++ b/tests/genmc/pass/basics/rmw/rmw_edge_cases.u8_.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 1 diff --git a/tests/genmc/pass/basics/rmw/rmw_edge_cases.usize_.stderr b/tests/genmc/pass/basics/rmw/rmw_edge_cases.usize_.stderr new file mode 100644 index 0000000000..2382cc5a78 --- /dev/null +++ b/tests/genmc/pass/basics/rmw/rmw_edge_cases.usize_.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 1 diff --git a/tests/genmc/pass/basics/rmw/rmw_simple.rs b/tests/genmc/pass/basics/rmw/rmw_simple.rs new file mode 100644 index 0000000000..c272549ea9 --- /dev/null +++ b/tests/genmc/pass/basics/rmw/rmw_simple.rs @@ -0,0 +1,29 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +#![no_main] + +use std::sync::atomic::*; + +static VALUE: AtomicUsize = AtomicUsize::new(0); + +const ORD: Ordering = Ordering::SeqCst; + +fn assert_eq(x: usize, y: usize) { + if x != y { + std::process::abort(); + } +} + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + VALUE.store(1, ORD); + + assert_eq(1, VALUE.fetch_add(7, ORD)); + assert_eq(8, VALUE.fetch_sub(2, ORD)); + assert_eq(6, VALUE.fetch_max(16, ORD)); + assert_eq(16, VALUE.fetch_min(4, ORD)); + assert_eq(4, VALUE.swap(42, ORD)); + + assert_eq(42, VALUE.load(ORD)); + 0 +} diff --git a/tests/genmc/pass/basics/rmw/rmw_simple.stderr b/tests/genmc/pass/basics/rmw/rmw_simple.stderr new file mode 100644 index 0000000000..2382cc5a78 --- /dev/null +++ b/tests/genmc/pass/basics/rmw/rmw_simple.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 1 diff --git a/tests/genmc/pass/data-structures/ms_queue_dynamic.rs b/tests/genmc/pass/data-structures/ms_queue_dynamic.rs new file mode 100644 index 0000000000..bc52eba423 --- /dev/null +++ b/tests/genmc/pass/data-structures/ms_queue_dynamic.rs @@ -0,0 +1,224 @@ +//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// TODO GENMC: maybe use `-Zmiri-genmc-symmetry-reduction`? +// TODO GENMC: investigate why `-Zmiri-ignore-leaks ` is required + +#![no_main] +#![allow(static_mut_refs)] +#![allow(unused)] + +use std::alloc::{Layout, alloc, dealloc}; +use std::ffi::c_void; +use std::sync::atomic::Ordering::*; +use std::sync::atomic::{AtomicPtr, AtomicU64}; + +use libc::{self, pthread_attr_t, pthread_t}; + +const MAX_THREADS: usize = 32; + +const POISON_IDX: u64 = 0xAAAABBBBBBBBAAAA; + +static mut QUEUE: MyStack = MyStack::new(); +static mut PARAMS: [u64; MAX_THREADS] = [POISON_IDX; MAX_THREADS]; +static mut INPUT: [u64; MAX_THREADS] = [POISON_IDX; MAX_THREADS]; +static mut OUTPUT: [Option; MAX_THREADS] = [None; MAX_THREADS]; +static mut THREADS: [pthread_t; MAX_THREADS] = [0; MAX_THREADS]; + +#[repr(C)] +struct Node { + value: u64, + next: AtomicPtr, +} + +struct MyStack { + head: AtomicPtr, + tail: AtomicPtr, +} + +impl Node { + pub unsafe fn new_alloc() -> *mut Self { + alloc(Layout::new::()) as *mut Self + } + + pub unsafe fn free(node: *mut Self) { + dealloc(node as *mut u8, Layout::new::()) + } + + pub unsafe fn reclaim(_node: *mut Self) { + // __VERIFIER_hp_retire(node); + } +} + +impl MyStack { + pub const fn new() -> Self { + let head = AtomicPtr::new(std::ptr::null_mut()); + let tail = AtomicPtr::new(std::ptr::null_mut()); + Self { head, tail } + } + + pub unsafe fn init_queue(&mut self, _num_threads: usize) { + /* initialize queue */ + let mut dummy = Node::new_alloc(); + + (*dummy).next = AtomicPtr::new(std::ptr::null_mut()); + self.head = AtomicPtr::new(dummy); + self.tail = AtomicPtr::new(dummy); + } + + pub unsafe fn clear_queue(&mut self, _num_threads: usize) { + let mut next; + let mut head = *self.head.get_mut(); + while !head.is_null() { + next = *(*head).next.get_mut(); + Node::free(head); + head = next; + } + } + + pub unsafe fn enqueue(&self, value: u64) { + let mut tail; + let node = Node::new_alloc(); + (*node).value = value; + (*node).next = AtomicPtr::new(std::ptr::null_mut()); + + loop { + tail = self.tail.load(Acquire); + let next = (*tail).next.load(Acquire); + if tail != self.tail.load(Acquire) { + continue; + } + + if next.is_null() { + // TODO GENMC: what if anything has to be done for `__VERIFIER_final_CAS`? + if (*tail).next.compare_exchange(next, node, Release, Relaxed).is_ok() { + break; + } + } else { + // TODO GENMC: what if anything has to be done for `__VERIFIER_helping_CAS`? + let _ = self.tail.compare_exchange(tail, next, Release, Relaxed); + } + } + + // TODO GENMC: what if anything has to be done for `__VERIFIER_helped_CAS`? + let _ = self.tail.compare_exchange(tail, node, Release, Relaxed); + } + + pub unsafe fn dequeue(&self) -> Option { + loop { + let head = self.head.load(Acquire); + let tail = self.tail.load(Acquire); + + let next_ref = &(*head).next; + let next = next_ref.load(Acquire); + if self.head.load(Acquire) != head { + continue; + } + if head == tail { + if next.is_null() { + return None; + } + let _ = self.tail.compare_exchange(tail, next, Release, Relaxed); + } else { + let ret_val = (*next).value; + if self.head.compare_exchange(head, next, Release, Relaxed).is_ok() { + // reclaim(head); + // __VERIFIER_hp_free(hp_head); + // __VERIFIER_hp_free(hp_next); + return Some(ret_val); + } + } + } + } +} + +extern "C" fn thread_w(value: *mut c_void) -> *mut c_void { + unsafe { + let pid = *(value as *mut u64); + + INPUT[pid as usize] = pid * 10; + QUEUE.enqueue(INPUT[pid as usize]); + + std::ptr::null_mut() + } +} + +extern "C" fn thread_r(value: *mut c_void) -> *mut c_void { + unsafe { + let pid = *(value as *mut u64); + + OUTPUT[pid as usize] = QUEUE.dequeue(); + + std::ptr::null_mut() + } +} + +extern "C" fn thread_rw(value: *mut c_void) -> *mut c_void { + unsafe { + let pid = *(value as *mut u64); + + INPUT[pid as usize] = pid * 10; + QUEUE.enqueue(INPUT[pid as usize]); + + OUTPUT[pid as usize] = QUEUE.dequeue(); + + std::ptr::null_mut() + } +} + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + let attr: *const pthread_attr_t = std::ptr::null(); + + // TODO GENMC (TESTS): make different tests: + let readers = 0; + let writers = 0; + let rdwr = 2; + + let num_threads = readers + writers + rdwr; + + if num_threads > MAX_THREADS { + std::process::abort(); + } + + let mut i = 0; + unsafe { + MyStack::init_queue(&mut QUEUE, num_threads); + + for j in 0..num_threads { + PARAMS[j] = j as u64; + } + + /* Spawn threads */ + for _ in 0..writers { + let value: *mut c_void = (&raw mut PARAMS[i]) as *mut c_void; + if 0 != libc::pthread_create(&raw mut THREADS[i], attr, thread_w, value) { + std::process::abort(); + } + i += 1; + } + for _ in 0..readers { + let value: *mut c_void = (&raw mut PARAMS[i]) as *mut c_void; + if 0 != libc::pthread_create(&raw mut THREADS[i], attr, thread_r, value) { + std::process::abort(); + } + i += 1; + } + for _ in 0..rdwr { + let value: *mut c_void = (&raw mut PARAMS[i]) as *mut c_void; + if 0 != libc::pthread_create(&raw mut THREADS[i], attr, thread_rw, value) { + std::process::abort(); + } + i += 1; + } + + for i in 0..num_threads { + if 0 != libc::pthread_join(THREADS[i], std::ptr::null_mut()) { + std::process::abort(); + } + } + + MyStack::clear_queue(&mut QUEUE, num_threads); + } + + 0 +} diff --git a/tests/genmc/pass/data-structures/ms_queue_dynamic.stderr b/tests/genmc/pass/data-structures/ms_queue_dynamic.stderr new file mode 100644 index 0000000000..2ed05b58e8 --- /dev/null +++ b/tests/genmc/pass/data-structures/ms_queue_dynamic.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 222 diff --git a/tests/genmc/pass/data-structures/treiber_stack_dynamic.rs b/tests/genmc/pass/data-structures/treiber_stack_dynamic.rs new file mode 100644 index 0000000000..1c5fa9d27b --- /dev/null +++ b/tests/genmc/pass/data-structures/treiber_stack_dynamic.rs @@ -0,0 +1,211 @@ +//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// TODO GENMC: maybe use `-Zmiri-genmc -Zmiri-disable-stacked-borrows-symmetry-reduction`? + +#![no_main] +#![allow(static_mut_refs)] +#![allow(unused)] + +use std::alloc::{Layout, alloc, dealloc}; +use std::ffi::c_void; +use std::sync::atomic::{AtomicPtr, AtomicU64, Ordering}; + +use libc::{self, pthread_attr_t, pthread_t}; + +const MAX_THREADS: usize = 32; + +const MAX_NODES: usize = 0xFF; + +const POISON_IDX: u64 = 0xDEADBEEF; + +// TODO GENMC: thread local (for GenMC hazard pointer API) +// static mut TID: u64 = POISON_IDX; + +static mut STACK: MyStack = MyStack::new(); +static mut THREADS: [pthread_t; MAX_THREADS] = [0; MAX_THREADS]; +static mut PARAMS: [u64; MAX_THREADS] = [POISON_IDX; MAX_THREADS]; + +unsafe fn set_thread_num(_i: u64) { + // TID = i; +} + +#[allow(unused)] // TODO GENMC: what is the purpose of this in the GenMC version? +unsafe fn get_thread_num() -> u64 { + // TID + todo!() +} + +#[repr(C)] +struct Node { + value: u64, + next: AtomicPtr, +} + +struct MyStack { + top: AtomicPtr, +} + +impl Node { + pub unsafe fn new_alloc() -> *mut Self { + alloc(Layout::new::()) as *mut Self + } + + pub unsafe fn free(node: *mut Self) { + dealloc(node as *mut u8, Layout::new::()) + } + + pub unsafe fn reclaim(_node: *mut Self) { + // __VERIFIER_hp_retire(node); + } +} + +impl MyStack { + pub const fn new() -> Self { + Self { top: AtomicPtr::new(std::ptr::null_mut()) } + } + + pub unsafe fn init_stack(&mut self, _num_threads: usize) { + self.top = AtomicPtr::new(std::ptr::null_mut()); + } + + pub unsafe fn clear_stack(&mut self, _num_threads: usize) { + let mut next; + let mut top = *self.top.get_mut(); + while !top.is_null() { + next = *(*top).next.get_mut(); + Node::free(top); + top = next; + } + } + + pub unsafe fn push(&self, value: u64) { + let node = Node::new_alloc(); + (*node).value = value; + + loop { + let top = self.top.load(Ordering::Acquire); + (*node).next.store(top, Ordering::Relaxed); + if self.top.compare_exchange(top, node, Ordering::Release, Ordering::Relaxed).is_ok() { + break; + } + } + } + + pub unsafe fn pop(&self) -> u64 { + let mut top; + + // TODO GENMC: enable if GenMC hazard pointer API is implemented in MIRI + // __VERIFIER_hp_t *hp = __VERIFIER_hp_alloc(); + loop { + top = STACK.top.load(Ordering::Acquire); + // top = __VERIFIER_hp_protect(hp, &s->top); + if top.is_null() { + // __VERIFIER_hp_free(hp); + return 0; + } + + let next = (*top).next.load(Ordering::Relaxed); + if self.top.compare_exchange(top, next, Ordering::Release, Ordering::Relaxed).is_ok() { + break; + } + } + + let value = (*top).value; + /* Reclaim the used slot */ + // Node::reclaim(top); + // Node::free(top); + // __VERIFIER_hp_free(hp); + return value; + } +} + +extern "C" fn thread_w(value: *mut c_void) -> *mut c_void { + unsafe { + let pid = *(value as *mut u64); + set_thread_num(pid); + + STACK.push(pid); + + std::ptr::null_mut() + } +} + +extern "C" fn thread_r(value: *mut c_void) -> *mut c_void { + unsafe { + let pid = *(value as *mut u64); + set_thread_num(pid); + + let _idx = STACK.pop(); + + std::ptr::null_mut() + } +} + +extern "C" fn thread_rw(value: *mut c_void) -> *mut c_void { + unsafe { + let pid = *(value as *mut u64); + set_thread_num(pid); + + STACK.push(pid); + + let _idx = STACK.pop(); + + std::ptr::null_mut() + } +} + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + let attr: *const pthread_attr_t = std::ptr::null(); + + // TODO GENMC: make different tests: + let readers = 1; + let writers = 2; + let rdwr = 0; + + let num_threads = readers + writers + rdwr; + + if num_threads > MAX_THREADS { + std::process::abort(); + } + + let mut i = 0; + unsafe { + MyStack::init_stack(&mut STACK, num_threads); + + for j in 0..num_threads { + PARAMS[j] = j as u64; + } + for _ in 0..readers { + let value: *mut c_void = (&raw mut PARAMS[i]) as *mut c_void; + if 0 != libc::pthread_create(&raw mut THREADS[i], attr, thread_r, value) { + std::process::abort(); + } + i += 1; + } + for _ in 0..writers { + let value: *mut c_void = (&raw mut PARAMS[i]) as *mut c_void; + if 0 != libc::pthread_create(&raw mut THREADS[i], attr, thread_w, value) { + std::process::abort(); + } + i += 1; + } + for _ in 0..rdwr { + let value: *mut c_void = (&raw mut PARAMS[i]) as *mut c_void; + if 0 != libc::pthread_create(&raw mut THREADS[i], attr, thread_rw, value) { + std::process::abort(); + } + i += 1; + } + + for i in 0..num_threads { + if 0 != libc::pthread_join(THREADS[i], std::ptr::null_mut()) { + std::process::abort(); + } + } + + MyStack::clear_stack(&mut STACK, num_threads); + } + + 0 +} diff --git a/tests/genmc/pass/data-structures/treiber_stack_dynamic.stderr b/tests/genmc/pass/data-structures/treiber_stack_dynamic.stderr new file mode 100644 index 0000000000..fbd9adb20b --- /dev/null +++ b/tests/genmc/pass/data-structures/treiber_stack_dynamic.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 22 diff --git a/tests/genmc/pass/litmus/2+2W+2sc+scf/2w2w_2sc_scf.order12.stderr b/tests/genmc/pass/litmus/2+2W+2sc+scf/2w2w_2sc_scf.order12.stderr new file mode 100644 index 0000000000..f2f3f904fb --- /dev/null +++ b/tests/genmc/pass/litmus/2+2W+2sc+scf/2w2w_2sc_scf.order12.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 3 diff --git a/tests/genmc/pass/litmus/2+2W+2sc+scf/2w2w_2sc_scf.order21.stderr b/tests/genmc/pass/litmus/2+2W+2sc+scf/2w2w_2sc_scf.order21.stderr new file mode 100644 index 0000000000..f2f3f904fb --- /dev/null +++ b/tests/genmc/pass/litmus/2+2W+2sc+scf/2w2w_2sc_scf.order21.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 3 diff --git a/tests/genmc/pass/litmus/2+2W+2sc+scf/2w2w_2sc_scf.rs b/tests/genmc/pass/litmus/2+2W+2sc+scf/2w2w_2sc_scf.rs new file mode 100644 index 0000000000..eca9494b81 --- /dev/null +++ b/tests/genmc/pass/litmus/2+2W+2sc+scf/2w2w_2sc_scf.rs @@ -0,0 +1,43 @@ +//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@revisions: order12 order21 + +#![no_main] + +#[path = "../../../../utils-dep/mod.rs"] +mod utils_dep; + +use std::ffi::c_void; +use std::sync::atomic::{AtomicU64, Ordering}; + +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); + +use crate::utils_dep::*; + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + let thread_order = if cfg!(order12) { + [thread_1, thread_2] + } else if cfg!(order21) { + [thread_2, thread_1] + } else { + unimplemented!(); + }; + + let _ids = unsafe { create_pthreads_no_params(thread_order) }; + + 0 +} + +extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { + X.store(1, Ordering::SeqCst); + Y.store(2, Ordering::SeqCst); + std::ptr::null_mut() +} + +extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { + Y.store(1, Ordering::Relaxed); + std::sync::atomic::fence(Ordering::SeqCst); + X.store(2, Ordering::Relaxed); + std::ptr::null_mut() +} diff --git a/tests/genmc/pass/litmus/2+2W+3sc+rel1/2_2w_3sc_rel1.rs b/tests/genmc/pass/litmus/2+2W+3sc+rel1/2_2w_3sc_rel1.rs new file mode 100644 index 0000000000..941879fa70 --- /dev/null +++ b/tests/genmc/pass/litmus/2+2W+3sc+rel1/2_2w_3sc_rel1.rs @@ -0,0 +1,33 @@ +//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows + +#![no_main] + +#[path = "../../../../utils-dep/mod.rs"] +mod utils_dep; + +use std::ffi::c_void; +use std::sync::atomic::{AtomicU64, Ordering}; + +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); + +use crate::utils_dep::*; + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + let _ids = unsafe { create_pthreads_no_params([thread_1, thread_2]) }; + + 0 +} + +extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { + X.store(1, Ordering::SeqCst); + Y.store(2, Ordering::SeqCst); + std::ptr::null_mut() +} + +extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { + Y.store(1, Ordering::Release); + X.store(2, Ordering::SeqCst); + std::ptr::null_mut() +} diff --git a/tests/genmc/pass/litmus/2+2W+3sc+rel1/2_2w_3sc_rel1.stderr b/tests/genmc/pass/litmus/2+2W+3sc+rel1/2_2w_3sc_rel1.stderr new file mode 100644 index 0000000000..61dc3f60ba --- /dev/null +++ b/tests/genmc/pass/litmus/2+2W+3sc+rel1/2_2w_3sc_rel1.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 4 diff --git a/tests/genmc/pass/litmus/2+2W+4sc/2_2w_4sc.rs b/tests/genmc/pass/litmus/2+2W+4sc/2_2w_4sc.rs new file mode 100644 index 0000000000..f2c4b422df --- /dev/null +++ b/tests/genmc/pass/litmus/2+2W+4sc/2_2w_4sc.rs @@ -0,0 +1,33 @@ +//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows + +#![no_main] + +#[path = "../../../../utils-dep/mod.rs"] +mod utils_dep; + +use std::ffi::c_void; +use std::sync::atomic::{AtomicU64, Ordering}; + +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); + +use crate::utils_dep::*; + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + let _ids = unsafe { create_pthreads_no_params([thread_1, thread_2]) }; + + 0 +} + +extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { + X.store(1, Ordering::SeqCst); + Y.store(2, Ordering::SeqCst); + std::ptr::null_mut() +} + +extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { + Y.store(1, Ordering::SeqCst); + X.store(2, Ordering::SeqCst); + std::ptr::null_mut() +} diff --git a/tests/genmc/pass/litmus/2+2W+4sc/2_2w_4sc.stderr b/tests/genmc/pass/litmus/2+2W+4sc/2_2w_4sc.stderr new file mode 100644 index 0000000000..f2f3f904fb --- /dev/null +++ b/tests/genmc/pass/litmus/2+2W+4sc/2_2w_4sc.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 3 diff --git a/tests/genmc/pass/litmus/2+2W+scfs/2_2w_scfs.rs b/tests/genmc/pass/litmus/2+2W+scfs/2_2w_scfs.rs new file mode 100644 index 0000000000..b819038c18 --- /dev/null +++ b/tests/genmc/pass/litmus/2+2W+scfs/2_2w_scfs.rs @@ -0,0 +1,35 @@ +//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows + +#![no_main] + +#[path = "../../../../utils-dep/mod.rs"] +mod utils_dep; + +use std::ffi::c_void; +use std::sync::atomic::{AtomicU64, Ordering}; + +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); + +use crate::utils_dep::*; + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + let _ids = unsafe { create_pthreads_no_params([thread_1, thread_2]) }; + + 0 +} + +extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { + X.store(1, Ordering::Relaxed); + std::sync::atomic::fence(Ordering::SeqCst); + Y.store(2, Ordering::Relaxed); + std::ptr::null_mut() +} + +extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { + Y.store(1, Ordering::Relaxed); + std::sync::atomic::fence(Ordering::SeqCst); + X.store(2, Ordering::Relaxed); + std::ptr::null_mut() +} diff --git a/tests/genmc/pass/litmus/2+2W+scfs/2_2w_scfs.stderr b/tests/genmc/pass/litmus/2+2W+scfs/2_2w_scfs.stderr new file mode 100644 index 0000000000..f2f3f904fb --- /dev/null +++ b/tests/genmc/pass/litmus/2+2W+scfs/2_2w_scfs.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 3 diff --git a/tests/genmc/pass/litmus/2+2w/2W2W.order12.stderr b/tests/genmc/pass/litmus/2+2w/2W2W.order12.stderr new file mode 100644 index 0000000000..61dc3f60ba --- /dev/null +++ b/tests/genmc/pass/litmus/2+2w/2W2W.order12.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 4 diff --git a/tests/genmc/pass/litmus/2+2w/2W2W.order21.stderr b/tests/genmc/pass/litmus/2+2w/2W2W.order21.stderr new file mode 100644 index 0000000000..61dc3f60ba --- /dev/null +++ b/tests/genmc/pass/litmus/2+2w/2W2W.order21.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 4 diff --git a/tests/genmc/pass/litmus/2+2w/2W2W.rs b/tests/genmc/pass/litmus/2+2w/2W2W.rs new file mode 100644 index 0000000000..cd70ede2fd --- /dev/null +++ b/tests/genmc/pass/litmus/2+2w/2W2W.rs @@ -0,0 +1,42 @@ +//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@revisions: order12 order21 + +#![no_main] + +#[path = "../../../../utils-dep/mod.rs"] +mod utils_dep; + +use std::ffi::c_void; +use std::sync::atomic::{AtomicU64, Ordering}; + +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); + +use crate::utils_dep::*; + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + let thread_order = if cfg!(order12) { + [thread_1, thread_2] + } else if cfg!(order21) { + [thread_2, thread_1] + } else { + unimplemented!(); + }; + + let _ids = unsafe { create_pthreads_no_params(thread_order) }; + + 0 +} + +extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { + Y.store(1, Ordering::Release); + X.store(2, Ordering::Release); + std::ptr::null_mut() +} + +extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { + Y.store(1, Ordering::Release); + X.store(2, Ordering::Release); + std::ptr::null_mut() +} diff --git a/tests/genmc/pass/litmus/2CoWR/2cowr.order1234.stderr b/tests/genmc/pass/litmus/2CoWR/2cowr.order1234.stderr new file mode 100644 index 0000000000..61dc3f60ba --- /dev/null +++ b/tests/genmc/pass/litmus/2CoWR/2cowr.order1234.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 4 diff --git a/tests/genmc/pass/litmus/2CoWR/2cowr.order2341.stderr b/tests/genmc/pass/litmus/2CoWR/2cowr.order2341.stderr new file mode 100644 index 0000000000..61dc3f60ba --- /dev/null +++ b/tests/genmc/pass/litmus/2CoWR/2cowr.order2341.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 4 diff --git a/tests/genmc/pass/litmus/2CoWR/2cowr.order3412.stderr b/tests/genmc/pass/litmus/2CoWR/2cowr.order3412.stderr new file mode 100644 index 0000000000..61dc3f60ba --- /dev/null +++ b/tests/genmc/pass/litmus/2CoWR/2cowr.order3412.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 4 diff --git a/tests/genmc/pass/litmus/2CoWR/2cowr.order4123.stderr b/tests/genmc/pass/litmus/2CoWR/2cowr.order4123.stderr new file mode 100644 index 0000000000..61dc3f60ba --- /dev/null +++ b/tests/genmc/pass/litmus/2CoWR/2cowr.order4123.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 4 diff --git a/tests/genmc/pass/litmus/2CoWR/2cowr.order4321.stderr b/tests/genmc/pass/litmus/2CoWR/2cowr.order4321.stderr new file mode 100644 index 0000000000..61dc3f60ba --- /dev/null +++ b/tests/genmc/pass/litmus/2CoWR/2cowr.order4321.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 4 diff --git a/tests/genmc/pass/litmus/2CoWR/2cowr.rs b/tests/genmc/pass/litmus/2CoWR/2cowr.rs new file mode 100644 index 0000000000..eabed7aaec --- /dev/null +++ b/tests/genmc/pass/litmus/2CoWR/2cowr.rs @@ -0,0 +1,57 @@ +//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@revisions: order1234 order2341 order3412 order4123 order4321 + +#![no_main] + +#[path = "../../../../utils-dep/mod.rs"] +mod utils_dep; + +use std::ffi::c_void; +use std::ptr::null_mut; +use std::sync::atomic::{AtomicU64, Ordering}; + +use crate::utils_dep::*; + +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + let thread_order = if cfg!(order1234) { + [thread_1, thread_2, thread_3, thread_4] + } else if cfg!(order2341) { + [thread_2, thread_3, thread_4, thread_1] + } else if cfg!(order3412) { + [thread_3, thread_4, thread_1, thread_2] + } else if cfg!(order4123) { + [thread_4, thread_1, thread_2, thread_3] + } else if cfg!(order4321) { + [thread_4, thread_3, thread_2, thread_1] + } else { + unimplemented!(); + }; + + let _ids = unsafe { create_pthreads_no_params(thread_order) }; + + 0 +} + +pub extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { + Y.load(Ordering::Acquire); + null_mut() +} + +pub extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { + X.load(Ordering::Acquire); + null_mut() +} + +pub extern "C" fn thread_3(_value: *mut c_void) -> *mut c_void { + X.store(1, Ordering::Release); + null_mut() +} + +pub extern "C" fn thread_4(_value: *mut c_void) -> *mut c_void { + Y.store(1, Ordering::Release); + null_mut() +} diff --git a/tests/genmc/pass/litmus/CoRR/corr.order12.stderr b/tests/genmc/pass/litmus/CoRR/corr.order12.stderr new file mode 100644 index 0000000000..e4151d346e --- /dev/null +++ b/tests/genmc/pass/litmus/CoRR/corr.order12.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 6 diff --git a/tests/genmc/pass/litmus/CoRR/corr.order21.stderr b/tests/genmc/pass/litmus/CoRR/corr.order21.stderr new file mode 100644 index 0000000000..e4151d346e --- /dev/null +++ b/tests/genmc/pass/litmus/CoRR/corr.order21.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 6 diff --git a/tests/genmc/pass/litmus/CoRR/corr.rs b/tests/genmc/pass/litmus/CoRR/corr.rs new file mode 100644 index 0000000000..ad8b75294e --- /dev/null +++ b/tests/genmc/pass/litmus/CoRR/corr.rs @@ -0,0 +1,41 @@ +//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@revisions: order12 order21 + +#![no_main] + +#[path = "../../../../utils-dep/mod.rs"] +mod utils_dep; + +use std::ffi::c_void; +use std::sync::atomic::{AtomicU64, Ordering}; + +static X: AtomicU64 = AtomicU64::new(0); + +use crate::utils_dep::*; + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + let thread_order = if cfg!(order12) { + [thread_1, thread_2] + } else if cfg!(order21) { + [thread_2, thread_1] + } else { + unimplemented!(); + }; + + let _ids = unsafe { create_pthreads_no_params(thread_order) }; + + 0 +} + +extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { + X.store(1, Ordering::Release); + X.store(2, Ordering::Release); + std::ptr::null_mut() +} + +extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { + X.load(Ordering::Acquire); + X.load(Ordering::Acquire); + std::ptr::null_mut() +} diff --git a/tests/genmc/pass/litmus/CoRR0/corr0.order123.stderr b/tests/genmc/pass/litmus/CoRR0/corr0.order123.stderr new file mode 100644 index 0000000000..61dc3f60ba --- /dev/null +++ b/tests/genmc/pass/litmus/CoRR0/corr0.order123.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 4 diff --git a/tests/genmc/pass/litmus/CoRR0/corr0.order231.stderr b/tests/genmc/pass/litmus/CoRR0/corr0.order231.stderr new file mode 100644 index 0000000000..61dc3f60ba --- /dev/null +++ b/tests/genmc/pass/litmus/CoRR0/corr0.order231.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 4 diff --git a/tests/genmc/pass/litmus/CoRR0/corr0.order312.stderr b/tests/genmc/pass/litmus/CoRR0/corr0.order312.stderr new file mode 100644 index 0000000000..61dc3f60ba --- /dev/null +++ b/tests/genmc/pass/litmus/CoRR0/corr0.order312.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 4 diff --git a/tests/genmc/pass/litmus/CoRR0/corr0.order321.stderr b/tests/genmc/pass/litmus/CoRR0/corr0.order321.stderr new file mode 100644 index 0000000000..61dc3f60ba --- /dev/null +++ b/tests/genmc/pass/litmus/CoRR0/corr0.order321.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 4 diff --git a/tests/genmc/pass/litmus/CoRR0/corr0.rs b/tests/genmc/pass/litmus/CoRR0/corr0.rs new file mode 100644 index 0000000000..2c802c8faa --- /dev/null +++ b/tests/genmc/pass/litmus/CoRR0/corr0.rs @@ -0,0 +1,48 @@ +//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@revisions: order123 order321 order312 order231 + +#![no_main] + +#[path = "../../../../utils-dep/mod.rs"] +mod utils_dep; + +use std::ffi::c_void; +use std::sync::atomic::{AtomicU64, Ordering}; + +static X: AtomicU64 = AtomicU64::new(0); + +use crate::utils_dep::*; + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + let thread_order = if cfg!(order123) { + [thread_1, thread_2, thread_3] + } else if cfg!(order321) { + [thread_3, thread_2, thread_1] + } else if cfg!(order312) { + [thread_3, thread_1, thread_2] + } else if cfg!(order231) { + [thread_2, thread_3, thread_1] + } else { + unimplemented!(); + }; + + let _ids = unsafe { create_pthreads_no_params(thread_order) }; + + 0 +} + +extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { + X.store(1, Ordering::Release); + std::ptr::null_mut() +} + +extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { + X.load(Ordering::Acquire); + std::ptr::null_mut() +} + +extern "C" fn thread_3(_value: *mut c_void) -> *mut c_void { + X.load(Ordering::Acquire); + std::ptr::null_mut() +} diff --git a/tests/genmc/pass/litmus/CoRR1/corr1.order1234.stderr b/tests/genmc/pass/litmus/CoRR1/corr1.order1234.stderr new file mode 100644 index 0000000000..c2ffa8ec3c --- /dev/null +++ b/tests/genmc/pass/litmus/CoRR1/corr1.order1234.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 36 diff --git a/tests/genmc/pass/litmus/CoRR1/corr1.order2341.stderr b/tests/genmc/pass/litmus/CoRR1/corr1.order2341.stderr new file mode 100644 index 0000000000..c2ffa8ec3c --- /dev/null +++ b/tests/genmc/pass/litmus/CoRR1/corr1.order2341.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 36 diff --git a/tests/genmc/pass/litmus/CoRR1/corr1.order3412.stderr b/tests/genmc/pass/litmus/CoRR1/corr1.order3412.stderr new file mode 100644 index 0000000000..c2ffa8ec3c --- /dev/null +++ b/tests/genmc/pass/litmus/CoRR1/corr1.order3412.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 36 diff --git a/tests/genmc/pass/litmus/CoRR1/corr1.order4123.stderr b/tests/genmc/pass/litmus/CoRR1/corr1.order4123.stderr new file mode 100644 index 0000000000..c2ffa8ec3c --- /dev/null +++ b/tests/genmc/pass/litmus/CoRR1/corr1.order4123.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 36 diff --git a/tests/genmc/pass/litmus/CoRR1/corr1.order4321.stderr b/tests/genmc/pass/litmus/CoRR1/corr1.order4321.stderr new file mode 100644 index 0000000000..c2ffa8ec3c --- /dev/null +++ b/tests/genmc/pass/litmus/CoRR1/corr1.order4321.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 36 diff --git a/tests/genmc/pass/litmus/CoRR1/corr1.rs b/tests/genmc/pass/litmus/CoRR1/corr1.rs new file mode 100644 index 0000000000..a31a9fd23a --- /dev/null +++ b/tests/genmc/pass/litmus/CoRR1/corr1.rs @@ -0,0 +1,57 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@revisions: order1234 order4321 order4123 order3412 order2341 + +#![no_main] + +#[path = "../../../../utils-dep/mod.rs"] +mod utils_dep; + +use std::ffi::c_void; +use std::sync::atomic::{AtomicU64, Ordering}; + +static X: AtomicU64 = AtomicU64::new(0); + +use crate::utils_dep::*; + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + let thread_order = if cfg!(order1234) { + [thread_1, thread_2, thread_3, thread_4] + } else if cfg!(order4321) { + [thread_4, thread_3, thread_2, thread_1] + } else if cfg!(order4123) { + [thread_4, thread_1, thread_2, thread_3] + } else if cfg!(order3412) { + [thread_3, thread_4, thread_1, thread_2] + } else if cfg!(order2341) { + [thread_2, thread_3, thread_4, thread_1] + } else { + unimplemented!(); + }; + + let ids = unsafe { create_pthreads_no_params(thread_order) }; + unsafe { join_pthreads(ids) }; + + 0 +} + +extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { + X.store(1, Ordering::Release); + std::ptr::null_mut() +} + +extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { + X.store(2, Ordering::Release); + std::ptr::null_mut() +} + +extern "C" fn thread_3(_value: *mut c_void) -> *mut c_void { + X.load(Ordering::Acquire); + X.load(Ordering::Acquire); + std::ptr::null_mut() +} + +extern "C" fn thread_4(_value: *mut c_void) -> *mut c_void { + X.load(Ordering::Acquire); + std::ptr::null_mut() +} diff --git a/tests/genmc/pass/litmus/CoRR2/corr2.order1234.stderr b/tests/genmc/pass/litmus/CoRR2/corr2.order1234.stderr new file mode 100644 index 0000000000..c8cd87ff6b --- /dev/null +++ b/tests/genmc/pass/litmus/CoRR2/corr2.order1234.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 72 diff --git a/tests/genmc/pass/litmus/CoRR2/corr2.order2341.stderr b/tests/genmc/pass/litmus/CoRR2/corr2.order2341.stderr new file mode 100644 index 0000000000..c8cd87ff6b --- /dev/null +++ b/tests/genmc/pass/litmus/CoRR2/corr2.order2341.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 72 diff --git a/tests/genmc/pass/litmus/CoRR2/corr2.order3412.stderr b/tests/genmc/pass/litmus/CoRR2/corr2.order3412.stderr new file mode 100644 index 0000000000..c8cd87ff6b --- /dev/null +++ b/tests/genmc/pass/litmus/CoRR2/corr2.order3412.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 72 diff --git a/tests/genmc/pass/litmus/CoRR2/corr2.order4123.stderr b/tests/genmc/pass/litmus/CoRR2/corr2.order4123.stderr new file mode 100644 index 0000000000..c8cd87ff6b --- /dev/null +++ b/tests/genmc/pass/litmus/CoRR2/corr2.order4123.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 72 diff --git a/tests/genmc/pass/litmus/CoRR2/corr2.order4321.stderr b/tests/genmc/pass/litmus/CoRR2/corr2.order4321.stderr new file mode 100644 index 0000000000..c8cd87ff6b --- /dev/null +++ b/tests/genmc/pass/litmus/CoRR2/corr2.order4321.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 72 diff --git a/tests/genmc/pass/litmus/CoRR2/corr2.rs b/tests/genmc/pass/litmus/CoRR2/corr2.rs new file mode 100644 index 0000000000..8f59836939 --- /dev/null +++ b/tests/genmc/pass/litmus/CoRR2/corr2.rs @@ -0,0 +1,57 @@ +//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@revisions: order1234 order4321 order4123 order3412 order2341 + +#![no_main] + +#[path = "../../../../utils-dep/mod.rs"] +mod utils_dep; + +use std::ffi::c_void; +use std::sync::atomic::{AtomicU64, Ordering}; + +static X: AtomicU64 = AtomicU64::new(0); + +use crate::utils_dep::*; + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + let thread_order = if cfg!(order1234) { + [thread_1, thread_2, thread_3, thread_4] + } else if cfg!(order4321) { + [thread_4, thread_3, thread_2, thread_1] + } else if cfg!(order4123) { + [thread_4, thread_1, thread_2, thread_3] + } else if cfg!(order3412) { + [thread_3, thread_4, thread_1, thread_2] + } else if cfg!(order2341) { + [thread_2, thread_3, thread_4, thread_1] + } else { + unimplemented!(); + }; + + let _ids = unsafe { create_pthreads_no_params(thread_order) }; + + 0 +} + +extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { + X.store(1, Ordering::Release); + std::ptr::null_mut() +} + +extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { + X.store(2, Ordering::Release); + std::ptr::null_mut() +} + +extern "C" fn thread_3(_value: *mut c_void) -> *mut c_void { + X.load(Ordering::Acquire); + X.load(Ordering::Acquire); + std::ptr::null_mut() +} + +extern "C" fn thread_4(_value: *mut c_void) -> *mut c_void { + X.load(Ordering::Acquire); + X.load(Ordering::Acquire); + std::ptr::null_mut() +} diff --git a/tests/genmc/pass/litmus/CoRW/corw.order12.stderr b/tests/genmc/pass/litmus/CoRW/corw.order12.stderr new file mode 100644 index 0000000000..f2f3f904fb --- /dev/null +++ b/tests/genmc/pass/litmus/CoRW/corw.order12.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 3 diff --git a/tests/genmc/pass/litmus/CoRW/corw.order21.stderr b/tests/genmc/pass/litmus/CoRW/corw.order21.stderr new file mode 100644 index 0000000000..f2f3f904fb --- /dev/null +++ b/tests/genmc/pass/litmus/CoRW/corw.order21.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 3 diff --git a/tests/genmc/pass/litmus/CoRW/corw.rs b/tests/genmc/pass/litmus/CoRW/corw.rs new file mode 100644 index 0000000000..7c99b60340 --- /dev/null +++ b/tests/genmc/pass/litmus/CoRW/corw.rs @@ -0,0 +1,40 @@ +//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@revisions: order12 order21 + +#![no_main] + +#[path = "../../../../utils-dep/mod.rs"] +mod utils_dep; + +use std::ffi::c_void; +use std::sync::atomic::{AtomicU64, Ordering}; + +static X: AtomicU64 = AtomicU64::new(0); + +use crate::utils_dep::*; + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + let thread_order = if cfg!(order12) { + [thread_1, thread_2] + } else if cfg!(order21) { + [thread_2, thread_1] + } else { + unimplemented!(); + }; + + let _ids = unsafe { create_pthreads_no_params(thread_order) }; + + 0 +} + +extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { + X.load(Ordering::Acquire); + X.store(1, Ordering::Release); + std::ptr::null_mut() +} + +extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { + X.store(2, Ordering::Release); + std::ptr::null_mut() +} diff --git a/tests/genmc/pass/litmus/CoWR/cowr.order12.stderr b/tests/genmc/pass/litmus/CoWR/cowr.order12.stderr new file mode 100644 index 0000000000..f2f3f904fb --- /dev/null +++ b/tests/genmc/pass/litmus/CoWR/cowr.order12.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 3 diff --git a/tests/genmc/pass/litmus/CoWR/cowr.order21.stderr b/tests/genmc/pass/litmus/CoWR/cowr.order21.stderr new file mode 100644 index 0000000000..f2f3f904fb --- /dev/null +++ b/tests/genmc/pass/litmus/CoWR/cowr.order21.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 3 diff --git a/tests/genmc/pass/litmus/CoWR/cowr.rs b/tests/genmc/pass/litmus/CoWR/cowr.rs new file mode 100644 index 0000000000..881f933910 --- /dev/null +++ b/tests/genmc/pass/litmus/CoWR/cowr.rs @@ -0,0 +1,40 @@ +//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@revisions: order12 order21 + +#![no_main] + +#[path = "../../../../utils-dep/mod.rs"] +mod utils_dep; + +use std::ffi::c_void; +use std::sync::atomic::{AtomicU64, Ordering}; + +static X: AtomicU64 = AtomicU64::new(0); + +use crate::utils_dep::*; + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + let thread_order = if cfg!(order12) { + [thread_1, thread_2] + } else if cfg!(order21) { + [thread_2, thread_1] + } else { + unimplemented!(); + }; + + let _ids = unsafe { create_pthreads_no_params(thread_order) }; + + 0 +} + +extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { + X.store(1, Ordering::Release); + X.load(Ordering::Acquire); + std::ptr::null_mut() +} + +extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { + X.store(2, Ordering::Release); + std::ptr::null_mut() +} diff --git a/tests/genmc/pass/litmus/IRIW-acq-sc/IRIW-acq-sc.rs b/tests/genmc/pass/litmus/IRIW-acq-sc/IRIW-acq-sc.rs new file mode 100644 index 0000000000..e4a8763790 --- /dev/null +++ b/tests/genmc/pass/litmus/IRIW-acq-sc/IRIW-acq-sc.rs @@ -0,0 +1,45 @@ +//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows + +#![no_main] + +#[path = "../../../../utils-dep/mod.rs"] +mod utils_dep; + +use std::ffi::c_void; +use std::ptr::null_mut; +use std::sync::atomic::{AtomicU64, Ordering}; + +use crate::utils_dep::*; + +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + let thread_order = [thread_1, thread_2, thread_3, thread_4]; + let _ids = unsafe { create_pthreads_no_params(thread_order) }; + + 0 +} + +pub extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { + X.store(1, Ordering::SeqCst); + null_mut() +} + +pub extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { + X.load(Ordering::Acquire); + Y.load(Ordering::SeqCst); + null_mut() +} + +pub extern "C" fn thread_3(_value: *mut c_void) -> *mut c_void { + Y.load(Ordering::Acquire); + X.load(Ordering::SeqCst); + null_mut() +} + +pub extern "C" fn thread_4(_value: *mut c_void) -> *mut c_void { + Y.store(1, Ordering::SeqCst); + null_mut() +} diff --git a/tests/genmc/pass/litmus/IRIW-acq-sc/IRIW-acq-sc.stderr b/tests/genmc/pass/litmus/IRIW-acq-sc/IRIW-acq-sc.stderr new file mode 100644 index 0000000000..f5e120ea47 --- /dev/null +++ b/tests/genmc/pass/litmus/IRIW-acq-sc/IRIW-acq-sc.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 16 diff --git a/tests/genmc/pass/litmus/IRIWish/IRIWish.rs b/tests/genmc/pass/litmus/IRIWish/IRIWish.rs new file mode 100644 index 0000000000..03e0e4c1e8 --- /dev/null +++ b/tests/genmc/pass/litmus/IRIWish/IRIWish.rs @@ -0,0 +1,48 @@ +//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows + +#![no_main] + +#[path = "../../../../utils-dep/mod.rs"] +mod utils_dep; + +use std::ffi::c_void; +use std::ptr::null_mut; +use std::sync::atomic::{AtomicU64, Ordering}; + +use crate::utils_dep::*; + +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + let thread_order = [thread_1, thread_2, thread_3, thread_4]; + let _ids = unsafe { create_pthreads_no_params(thread_order) }; + + 0 +} + +pub extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { + X.store(1, Ordering::Relaxed); + null_mut() +} + +pub extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { + let r1 = X.load(Ordering::Relaxed); + Y.store(r1, Ordering::Release); + null_mut() +} + +pub extern "C" fn thread_3(_value: *mut c_void) -> *mut c_void { + let _r1 = X.load(Ordering::Relaxed); + std::sync::atomic::fence(Ordering::AcqRel); + let _r2 = Y.load(Ordering::Relaxed); + null_mut() +} + +pub extern "C" fn thread_4(_value: *mut c_void) -> *mut c_void { + let _r1 = Y.load(Ordering::Relaxed); + std::sync::atomic::fence(Ordering::AcqRel); + let _r2 = X.load(Ordering::Relaxed); + null_mut() +} diff --git a/tests/genmc/pass/litmus/IRIWish/IRIWish.stderr b/tests/genmc/pass/litmus/IRIWish/IRIWish.stderr new file mode 100644 index 0000000000..452fca4856 --- /dev/null +++ b/tests/genmc/pass/litmus/IRIWish/IRIWish.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 28 diff --git a/tests/genmc/pass/litmus/LB+incMPs/LB_incMPs.rs b/tests/genmc/pass/litmus/LB+incMPs/LB_incMPs.rs new file mode 100644 index 0000000000..4e63c25f92 --- /dev/null +++ b/tests/genmc/pass/litmus/LB+incMPs/LB_incMPs.rs @@ -0,0 +1,48 @@ +//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows + +#![no_main] + +#[path = "../../../../utils-dep/mod.rs"] +mod utils_dep; + +use std::ffi::c_void; +use std::sync::atomic::{AtomicU64, Ordering}; + +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); +static W: AtomicU64 = AtomicU64::new(0); +static Z: AtomicU64 = AtomicU64::new(0); + +use crate::utils_dep::*; + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + let thread_order = [thread_1, thread_2, thread_3, thread_4]; + let _ids = unsafe { create_pthreads_no_params(thread_order) }; + + 0 +} + +extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { + X.load(Ordering::Acquire); + Z.fetch_add(1, Ordering::AcqRel); + std::ptr::null_mut() +} + +extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { + Z.fetch_add(1, Ordering::AcqRel); + Y.store(1, Ordering::Release); + std::ptr::null_mut() +} + +extern "C" fn thread_3(_value: *mut c_void) -> *mut c_void { + Y.load(Ordering::Acquire); + W.fetch_add(1, Ordering::AcqRel); + std::ptr::null_mut() +} + +extern "C" fn thread_4(_value: *mut c_void) -> *mut c_void { + W.fetch_add(1, Ordering::AcqRel); + X.store(1, Ordering::Release); + std::ptr::null_mut() +} diff --git a/tests/genmc/pass/litmus/LB+incMPs/LB_incMPs.stderr b/tests/genmc/pass/litmus/LB+incMPs/LB_incMPs.stderr new file mode 100644 index 0000000000..978d7c1ff3 --- /dev/null +++ b/tests/genmc/pass/litmus/LB+incMPs/LB_incMPs.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 15 diff --git a/tests/genmc/pass/litmus/LB/LB.order12.stderr b/tests/genmc/pass/litmus/LB/LB.order12.stderr new file mode 100644 index 0000000000..f2f3f904fb --- /dev/null +++ b/tests/genmc/pass/litmus/LB/LB.order12.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 3 diff --git a/tests/genmc/pass/litmus/LB/LB.order21.stderr b/tests/genmc/pass/litmus/LB/LB.order21.stderr new file mode 100644 index 0000000000..f2f3f904fb --- /dev/null +++ b/tests/genmc/pass/litmus/LB/LB.order21.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 3 diff --git a/tests/genmc/pass/litmus/LB/LB.rs b/tests/genmc/pass/litmus/LB/LB.rs new file mode 100644 index 0000000000..10f08a9fda --- /dev/null +++ b/tests/genmc/pass/litmus/LB/LB.rs @@ -0,0 +1,42 @@ +//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@revisions: order12 order21 + +#![no_main] + +#[path = "../../../../utils-dep/mod.rs"] +mod utils_dep; + +use std::ffi::c_void; +use std::sync::atomic::{AtomicU64, Ordering}; + +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); + +use crate::utils_dep::*; + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + let thread_order = if cfg!(order12) { + [thread_1, thread_2] + } else if cfg!(order21) { + [thread_2, thread_1] + } else { + unimplemented!(); + }; + + let _ids = unsafe { create_pthreads_no_params(thread_order) }; + + 0 +} + +extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { + Y.load(Ordering::Acquire); + X.store(2, Ordering::Release); + std::ptr::null_mut() +} + +extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { + X.load(Ordering::Acquire); + Y.store(1, Ordering::Release); + std::ptr::null_mut() +} diff --git a/tests/genmc/pass/litmus/MP+incMPs/MP_incMPs.rs b/tests/genmc/pass/litmus/MP+incMPs/MP_incMPs.rs new file mode 100644 index 0000000000..445bc17442 --- /dev/null +++ b/tests/genmc/pass/litmus/MP+incMPs/MP_incMPs.rs @@ -0,0 +1,41 @@ +//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows + +#![no_main] + +#[path = "../../../../utils-dep/mod.rs"] +mod utils_dep; + +use std::ffi::c_void; +use std::sync::atomic::{AtomicU64, Ordering}; + +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); +static Z: AtomicU64 = AtomicU64::new(0); + +use crate::utils_dep::*; + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + let thread_order = [thread_1, thread_2, thread_3]; + let _ids = unsafe { create_pthreads_no_params(thread_order) }; + + 0 +} + +extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { + Y.load(Ordering::Acquire); + Z.fetch_add(1, Ordering::AcqRel); + std::ptr::null_mut() +} + +extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { + Z.fetch_add(1, Ordering::AcqRel); + X.load(Ordering::Acquire); + std::ptr::null_mut() +} + +extern "C" fn thread_3(_value: *mut c_void) -> *mut c_void { + X.store(1, Ordering::Release); + Y.store(1, Ordering::Release); + std::ptr::null_mut() +} diff --git a/tests/genmc/pass/litmus/MP+incMPs/MP_incMPs.stderr b/tests/genmc/pass/litmus/MP+incMPs/MP_incMPs.stderr new file mode 100644 index 0000000000..708f4aab1b --- /dev/null +++ b/tests/genmc/pass/litmus/MP+incMPs/MP_incMPs.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 7 diff --git a/tests/genmc/pass/litmus/MP+rels+acqf/MP_rels_acqf.rs b/tests/genmc/pass/litmus/MP+rels+acqf/MP_rels_acqf.rs new file mode 100644 index 0000000000..5b70b2c4c5 --- /dev/null +++ b/tests/genmc/pass/litmus/MP+rels+acqf/MP_rels_acqf.rs @@ -0,0 +1,40 @@ +//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows +// -Zmiri-disable-data-race-detector + +#![no_main] + +#[path = "../../../../utils-dep/mod.rs"] +mod utils_dep; + +use std::ffi::c_void; +use std::sync::atomic::{AtomicU64, Ordering}; + +// Note: the GenMC equivalent of this test (genmc/tests/correct/litmus/MP+rels+acqf/mp+rels+acqf.c) uses non-atomic accesses for `X` with disabled race detection. +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); + +use crate::utils_dep::*; + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + let thread_order = [thread_1, thread_2]; + let _ids = unsafe { create_pthreads_no_params(thread_order) }; + + 0 +} + +extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { + X.store(1, Ordering::Relaxed); + + Y.store(0, Ordering::Release); + Y.store(1, Ordering::Relaxed); + std::ptr::null_mut() +} + +extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { + if Y.load(Ordering::Relaxed) != 0 { + std::sync::atomic::fence(Ordering::Acquire); + let _x = X.load(Ordering::Relaxed); + } + std::ptr::null_mut() +} diff --git a/tests/genmc/pass/litmus/MP+rels+acqf/MP_rels_acqf.stderr b/tests/genmc/pass/litmus/MP+rels+acqf/MP_rels_acqf.stderr new file mode 100644 index 0000000000..61dc3f60ba --- /dev/null +++ b/tests/genmc/pass/litmus/MP+rels+acqf/MP_rels_acqf.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 4 diff --git a/tests/genmc/pass/litmus/MP/MP.order12.stderr b/tests/genmc/pass/litmus/MP/MP.order12.stderr new file mode 100644 index 0000000000..f2f3f904fb --- /dev/null +++ b/tests/genmc/pass/litmus/MP/MP.order12.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 3 diff --git a/tests/genmc/pass/litmus/MP/MP.order21.stderr b/tests/genmc/pass/litmus/MP/MP.order21.stderr new file mode 100644 index 0000000000..f2f3f904fb --- /dev/null +++ b/tests/genmc/pass/litmus/MP/MP.order21.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 3 diff --git a/tests/genmc/pass/litmus/MP/MP.rs b/tests/genmc/pass/litmus/MP/MP.rs new file mode 100644 index 0000000000..6157fa7013 --- /dev/null +++ b/tests/genmc/pass/litmus/MP/MP.rs @@ -0,0 +1,42 @@ +//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@revisions: order12 order21 + +#![no_main] + +#[path = "../../../../utils-dep/mod.rs"] +mod utils_dep; + +use std::ffi::c_void; +use std::sync::atomic::{AtomicU64, Ordering}; + +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); + +use crate::utils_dep::*; + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + let thread_order = if cfg!(order12) { + [thread_1, thread_2] + } else if cfg!(order21) { + [thread_2, thread_1] + } else { + unimplemented!(); + }; + + let _ids = unsafe { create_pthreads_no_params(thread_order) }; + + 0 +} + +extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { + X.store(1, Ordering::Release); + Y.store(1, Ordering::Release); + std::ptr::null_mut() +} + +extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { + Y.load(Ordering::Acquire); + X.load(Ordering::Acquire); + std::ptr::null_mut() +} diff --git a/tests/genmc/pass/litmus/MPU+rels+acq/MPU_rels_acq.rs b/tests/genmc/pass/litmus/MPU+rels+acq/MPU_rels_acq.rs new file mode 100644 index 0000000000..e8715c51ff --- /dev/null +++ b/tests/genmc/pass/litmus/MPU+rels+acq/MPU_rels_acq.rs @@ -0,0 +1,43 @@ +//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows + +#![no_main] + +#[path = "../../../../utils-dep/mod.rs"] +mod utils_dep; + +use std::ffi::c_void; +use std::sync::atomic::{AtomicU64, Ordering}; + +// Note: the GenMC equivalent of this test (genmc/tests/correct/litmus/MPU+rels+acq/mpu+rels+acq.c) uses non-atomic accesses for `X` with disabled race detection. +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); + +use crate::utils_dep::*; + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + let thread_order = [thread_1, thread_2, thread_3]; + let _ids = unsafe { create_pthreads_no_params(thread_order) }; + + 0 +} + +extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { + X.store(1, Ordering::Relaxed); + + Y.store(0, Ordering::Release); + Y.store(1, Ordering::Relaxed); + std::ptr::null_mut() +} + +extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { + Y.fetch_add(1, Ordering::Relaxed); + std::ptr::null_mut() +} + +extern "C" fn thread_3(_value: *mut c_void) -> *mut c_void { + if Y.load(Ordering::Acquire) > 1 { + X.store(2, Ordering::Relaxed); + } + std::ptr::null_mut() +} diff --git a/tests/genmc/pass/litmus/MPU+rels+acq/MPU_rels_acq.stderr b/tests/genmc/pass/litmus/MPU+rels+acq/MPU_rels_acq.stderr new file mode 100644 index 0000000000..89aa5eb69c --- /dev/null +++ b/tests/genmc/pass/litmus/MPU+rels+acq/MPU_rels_acq.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 13 diff --git a/tests/genmc/pass/litmus/MPU2+rels+acqf/MPU2_rels_acqf.rs b/tests/genmc/pass/litmus/MPU2+rels+acqf/MPU2_rels_acqf.rs new file mode 100644 index 0000000000..94b14de7cf --- /dev/null +++ b/tests/genmc/pass/litmus/MPU2+rels+acqf/MPU2_rels_acqf.rs @@ -0,0 +1,51 @@ +//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows + +#![no_main] + +#[path = "../../../../utils-dep/mod.rs"] +mod utils_dep; + +use std::ffi::c_void; +use std::sync::atomic::{AtomicU64, Ordering}; + +// Note: the GenMC equivalent of this test (genmc/tests/correct/litmus/MPU2+rels+acqf/mpu2+rels+acqf.c) uses non-atomic accesses for `X` with disabled race detection. +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); + +use crate::utils_dep::*; + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + let thread_order = [thread_1, thread_2, thread_3, thread_4]; + let _ids = unsafe { create_pthreads_no_params(thread_order) }; + + 0 +} + +extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { + X.store(1, Ordering::Relaxed); + + Y.store(0, Ordering::Release); + Y.store(1, Ordering::Relaxed); + std::ptr::null_mut() +} + +extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { + let expected = 2; + let _ = Y.compare_exchange(expected, 3, Ordering::Relaxed, Ordering::Relaxed); + std::ptr::null_mut() +} + +extern "C" fn thread_3(_value: *mut c_void) -> *mut c_void { + let expected = 1; + let _ = Y.compare_exchange(expected, 2, Ordering::Relaxed, Ordering::Relaxed); + std::ptr::null_mut() +} + +extern "C" fn thread_4(_value: *mut c_void) -> *mut c_void { + if Y.load(Ordering::Acquire) > 2 { + std::sync::atomic::fence(Ordering::Acquire); + X.store(2, Ordering::Relaxed); + } + std::ptr::null_mut() +} diff --git a/tests/genmc/pass/litmus/MPU2+rels+acqf/MPU2_rels_acqf.stderr b/tests/genmc/pass/litmus/MPU2+rels+acqf/MPU2_rels_acqf.stderr new file mode 100644 index 0000000000..c2ffa8ec3c --- /dev/null +++ b/tests/genmc/pass/litmus/MPU2+rels+acqf/MPU2_rels_acqf.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 36 diff --git a/tests/genmc/pass/litmus/SB+2sc+scf/SB_2sc_scf.rs b/tests/genmc/pass/litmus/SB+2sc+scf/SB_2sc_scf.rs new file mode 100644 index 0000000000..f9855a85f5 --- /dev/null +++ b/tests/genmc/pass/litmus/SB+2sc+scf/SB_2sc_scf.rs @@ -0,0 +1,35 @@ +//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows + +#![no_main] + +#[path = "../../../../utils-dep/mod.rs"] +mod utils_dep; + +use std::ffi::c_void; +use std::sync::atomic::{AtomicU64, Ordering}; + +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); + +use crate::utils_dep::*; + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + let thread_order = [thread_1, thread_2]; + let _ids = unsafe { create_pthreads_no_params(thread_order) }; + + 0 +} + +extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { + X.store(1, Ordering::SeqCst); + Y.load(Ordering::SeqCst); + std::ptr::null_mut() +} + +extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { + Y.store(1, Ordering::Relaxed); + std::sync::atomic::fence(Ordering::SeqCst); + X.load(Ordering::Relaxed); + std::ptr::null_mut() +} diff --git a/tests/genmc/pass/litmus/SB+2sc+scf/SB_2sc_scf.stderr b/tests/genmc/pass/litmus/SB+2sc+scf/SB_2sc_scf.stderr new file mode 100644 index 0000000000..f2f3f904fb --- /dev/null +++ b/tests/genmc/pass/litmus/SB+2sc+scf/SB_2sc_scf.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 3 diff --git a/tests/genmc/pass/litmus/SB/SB.order12.stderr b/tests/genmc/pass/litmus/SB/SB.order12.stderr new file mode 100644 index 0000000000..61dc3f60ba --- /dev/null +++ b/tests/genmc/pass/litmus/SB/SB.order12.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 4 diff --git a/tests/genmc/pass/litmus/SB/SB.order21.stderr b/tests/genmc/pass/litmus/SB/SB.order21.stderr new file mode 100644 index 0000000000..61dc3f60ba --- /dev/null +++ b/tests/genmc/pass/litmus/SB/SB.order21.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 4 diff --git a/tests/genmc/pass/litmus/SB/SB.rs b/tests/genmc/pass/litmus/SB/SB.rs new file mode 100644 index 0000000000..d5360faf14 --- /dev/null +++ b/tests/genmc/pass/litmus/SB/SB.rs @@ -0,0 +1,42 @@ +//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@revisions: order12 order21 + +#![no_main] + +#[path = "../../../../utils-dep/mod.rs"] +mod utils_dep; + +use std::ffi::c_void; +use std::sync::atomic::{AtomicU64, Ordering}; + +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); + +use crate::utils_dep::*; + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + let thread_order = if cfg!(order12) { + [thread_1, thread_2] + } else if cfg!(order21) { + [thread_2, thread_1] + } else { + unimplemented!(); + }; + + let _ids = unsafe { create_pthreads_no_params(thread_order) }; + + 0 +} + +extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { + X.store(1, Ordering::Release); + Y.load(Ordering::Acquire); + std::ptr::null_mut() +} + +extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { + Y.store(1, Ordering::Release); + X.load(Ordering::Acquire); + std::ptr::null_mut() +} diff --git a/tests/genmc/pass/litmus/Z6+acq/Z6_acq.order123.stderr b/tests/genmc/pass/litmus/Z6+acq/Z6_acq.order123.stderr new file mode 100644 index 0000000000..708f4aab1b --- /dev/null +++ b/tests/genmc/pass/litmus/Z6+acq/Z6_acq.order123.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 7 diff --git a/tests/genmc/pass/litmus/Z6+acq/Z6_acq.order321.stderr b/tests/genmc/pass/litmus/Z6+acq/Z6_acq.order321.stderr new file mode 100644 index 0000000000..708f4aab1b --- /dev/null +++ b/tests/genmc/pass/litmus/Z6+acq/Z6_acq.order321.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 7 diff --git a/tests/genmc/pass/litmus/Z6+acq/Z6_acq.rs b/tests/genmc/pass/litmus/Z6+acq/Z6_acq.rs new file mode 100644 index 0000000000..255e856d89 --- /dev/null +++ b/tests/genmc/pass/litmus/Z6+acq/Z6_acq.rs @@ -0,0 +1,51 @@ +//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@revisions: order123 order321 + +#![no_main] + +#[path = "../../../../utils-dep/mod.rs"] +mod utils_dep; + +use std::ffi::c_void; +use std::sync::atomic::{AtomicU64, Ordering}; + +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); +static Z: AtomicU64 = AtomicU64::new(0); + +use crate::utils_dep::*; + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + let thread_order = if cfg!(order123) { + [thread_1, thread_2, thread_3] + } else if cfg!(order321) { + [thread_3, thread_2, thread_1] + } else { + unimplemented!(); + }; + + let _ids = unsafe { create_pthreads_no_params(thread_order) }; + + 0 +} + +extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { + X.store(1, Ordering::Relaxed); + std::sync::atomic::fence(Ordering::SeqCst); + Y.store(1, Ordering::Relaxed); + std::ptr::null_mut() +} + +extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { + Y.load(Ordering::Acquire); + Z.store(1, Ordering::Relaxed); + std::ptr::null_mut() +} + +extern "C" fn thread_3(_value: *mut c_void) -> *mut c_void { + Z.store(2, Ordering::Relaxed); + std::sync::atomic::fence(Ordering::SeqCst); + X.load(Ordering::Relaxed); + std::ptr::null_mut() +} diff --git a/tests/genmc/pass/litmus/Z6.U0/Z6_U0.rs b/tests/genmc/pass/litmus/Z6.U0/Z6_U0.rs new file mode 100644 index 0000000000..ad65049b3b --- /dev/null +++ b/tests/genmc/pass/litmus/Z6.U0/Z6_U0.rs @@ -0,0 +1,40 @@ +//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows + +#![no_main] + +#[path = "../../../../utils-dep/mod.rs"] +mod utils_dep; + +use std::ffi::c_void; +use std::sync::atomic::{AtomicU64, Ordering}; + +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); + +use crate::utils_dep::*; + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + let thread_order = [thread_1, thread_2, thread_3]; + let _ids = unsafe { create_pthreads_no_params(thread_order) }; + + 0 +} + +extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { + X.store(1, Ordering::SeqCst); + Y.store(1, Ordering::Release); + std::ptr::null_mut() +} + +extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { + Y.fetch_add(1, Ordering::SeqCst); + Y.load(Ordering::Relaxed); + std::ptr::null_mut() +} + +extern "C" fn thread_3(_value: *mut c_void) -> *mut c_void { + Y.store(3, Ordering::SeqCst); + X.load(Ordering::SeqCst); + std::ptr::null_mut() +} diff --git a/tests/genmc/pass/litmus/Z6.U0/Z6_U0.stderr b/tests/genmc/pass/litmus/Z6.U0/Z6_U0.stderr new file mode 100644 index 0000000000..fbd9adb20b --- /dev/null +++ b/tests/genmc/pass/litmus/Z6.U0/Z6_U0.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 22 diff --git a/tests/genmc/pass/litmus/assume-ctrl/assume-ctrl.rs b/tests/genmc/pass/litmus/assume-ctrl/assume-ctrl.rs new file mode 100644 index 0000000000..7d6e0680db --- /dev/null +++ b/tests/genmc/pass/litmus/assume-ctrl/assume-ctrl.rs @@ -0,0 +1,42 @@ +//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows + +#![no_main] + +#[path = "../../../../utils-dep/mod.rs"] +mod utils_dep; + +use std::ffi::c_void; +use std::sync::atomic::{AtomicU64, Ordering}; + +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); + +use crate::utils_dep::*; + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + let _ids = unsafe { create_pthreads_no_params([thread_1, thread_2]) }; + + 0 +} + +extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { + // TODO GENMC: do these have to be unsafe? + unsafe { + miri_genmc_verifier_assume(2 > Y.load(Ordering::Relaxed) || Y.load(Ordering::Relaxed) > 3); + } + X.store(1, Ordering::Relaxed); + std::ptr::null_mut() +} + +extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { + // TODO GENMC: do these have to be unsafe? + unsafe { + miri_genmc_verifier_assume(X.load(Ordering::Relaxed) < 3); + } + + Y.store(3, Ordering::Relaxed); + std::sync::atomic::fence(Ordering::SeqCst); + Y.store(4, Ordering::Relaxed); + std::ptr::null_mut() +} diff --git a/tests/genmc/pass/litmus/assume-ctrl/assume-ctrl.stderr b/tests/genmc/pass/litmus/assume-ctrl/assume-ctrl.stderr new file mode 100644 index 0000000000..bcd0ece9c7 --- /dev/null +++ b/tests/genmc/pass/litmus/assume-ctrl/assume-ctrl.stderr @@ -0,0 +1,4 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 4 +Number of blocked executions seen: 1 diff --git a/tests/genmc/pass/litmus/atomicpo/atomicpo.rs b/tests/genmc/pass/litmus/atomicpo/atomicpo.rs new file mode 100644 index 0000000000..37c5b036c8 --- /dev/null +++ b/tests/genmc/pass/litmus/atomicpo/atomicpo.rs @@ -0,0 +1,34 @@ +//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows + +#![no_main] + +#[path = "../../../../utils-dep/mod.rs"] +mod utils_dep; + +use std::ffi::c_void; +use std::sync::atomic::{AtomicU64, Ordering}; + +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); + +use crate::utils_dep::*; + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + let _ids = unsafe { create_pthreads_no_params([thread_1, thread_2]) }; + + 0 +} + +extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { + X.store(1, Ordering::Relaxed); + std::sync::atomic::fence(Ordering::AcqRel); + Y.store(1, Ordering::Relaxed); + std::ptr::null_mut() +} + +extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { + Y.swap(1, Ordering::Relaxed); + X.swap(1, Ordering::Relaxed); + std::ptr::null_mut() +} diff --git a/tests/genmc/pass/litmus/atomicpo/atomicpo.stderr b/tests/genmc/pass/litmus/atomicpo/atomicpo.stderr new file mode 100644 index 0000000000..61dc3f60ba --- /dev/null +++ b/tests/genmc/pass/litmus/atomicpo/atomicpo.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 4 diff --git a/tests/genmc/pass/litmus/casdep/casdep.rs b/tests/genmc/pass/litmus/casdep/casdep.rs new file mode 100644 index 0000000000..e448e0842e --- /dev/null +++ b/tests/genmc/pass/litmus/casdep/casdep.rs @@ -0,0 +1,34 @@ +//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows + +#![no_main] + +#[path = "../../../../utils-dep/mod.rs"] +mod utils_dep; + +use std::ffi::c_void; +use std::sync::atomic::{AtomicU64, Ordering}; + +use crate::utils_dep::*; + +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); +static Z: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + let _ids = unsafe { create_pthreads_no_params([thread_1, thread_2]) }; + + 0 +} + +extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { + let a = X.load(Ordering::Relaxed); + let _b = Y.compare_exchange(a, 1, Ordering::Relaxed, Ordering::Relaxed); + Z.store(a, Ordering::Relaxed); + std::ptr::null_mut() +} + +extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { + Y.store(2, Ordering::Relaxed); + std::ptr::null_mut() +} diff --git a/tests/genmc/pass/litmus/casdep/casdep.stderr b/tests/genmc/pass/litmus/casdep/casdep.stderr new file mode 100644 index 0000000000..edecd38278 --- /dev/null +++ b/tests/genmc/pass/litmus/casdep/casdep.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 2 diff --git a/tests/genmc/pass/litmus/ccr/ccr.rs b/tests/genmc/pass/litmus/ccr/ccr.rs new file mode 100644 index 0000000000..181f7d0ef1 --- /dev/null +++ b/tests/genmc/pass/litmus/ccr/ccr.rs @@ -0,0 +1,33 @@ +//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows + +#![no_main] + +#[path = "../../../../utils-dep/mod.rs"] +mod utils_dep; + +use std::ffi::c_void; +use std::sync::atomic::{AtomicU64, Ordering}; + +static X: AtomicU64 = AtomicU64::new(0); + +use crate::utils_dep::*; + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + let _ids = unsafe { create_pthreads_no_params([thread_1, thread_2]) }; + + 0 +} + +extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { + let expected = 0; + let _ = X.compare_exchange(expected, 42, Ordering::Relaxed, Ordering::Relaxed); + std::ptr::null_mut() +} + +extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { + let expected = 0; + let _ = X.compare_exchange(expected, 17, Ordering::Relaxed, Ordering::Relaxed); + X.load(Ordering::Relaxed); + std::ptr::null_mut() +} diff --git a/tests/genmc/pass/litmus/ccr/ccr.stderr b/tests/genmc/pass/litmus/ccr/ccr.stderr new file mode 100644 index 0000000000..edecd38278 --- /dev/null +++ b/tests/genmc/pass/litmus/ccr/ccr.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 2 diff --git a/tests/genmc/pass/litmus/cii/cii.rs b/tests/genmc/pass/litmus/cii/cii.rs new file mode 100644 index 0000000000..77548fc8fd --- /dev/null +++ b/tests/genmc/pass/litmus/cii/cii.rs @@ -0,0 +1,36 @@ +//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows + +#![no_main] + +#[path = "../../../../utils-dep/mod.rs"] +mod utils_dep; + +use std::ffi::c_void; +use std::sync::atomic::{AtomicU64, Ordering}; + +static X: AtomicU64 = AtomicU64::new(0); + +use crate::utils_dep::*; + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + let _ids = unsafe { create_pthreads_no_params([thread_1, thread_2, thread_3]) }; + + 0 +} + +extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { + let expected = 1; + let _ = X.compare_exchange(expected, 2, Ordering::Relaxed, Ordering::Relaxed); + std::ptr::null_mut() +} + +extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { + X.fetch_add(1, Ordering::Relaxed); + std::ptr::null_mut() +} + +extern "C" fn thread_3(_value: *mut c_void) -> *mut c_void { + X.fetch_add(1, Ordering::Relaxed); + std::ptr::null_mut() +} diff --git a/tests/genmc/pass/litmus/cii/cii.stderr b/tests/genmc/pass/litmus/cii/cii.stderr new file mode 100644 index 0000000000..e4151d346e --- /dev/null +++ b/tests/genmc/pass/litmus/cii/cii.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 6 diff --git a/tests/genmc/pass/litmus/cumul-release/cumul-release.rs b/tests/genmc/pass/litmus/cumul-release/cumul-release.rs new file mode 100644 index 0000000000..0abbee98b4 --- /dev/null +++ b/tests/genmc/pass/litmus/cumul-release/cumul-release.rs @@ -0,0 +1,42 @@ +//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows + +#![no_main] + +#[path = "../../../../utils-dep/mod.rs"] +mod utils_dep; + +use std::ffi::c_void; +use std::sync::atomic::{AtomicU64, Ordering}; + +use crate::utils_dep::*; + +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); +static Z: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + let thread_order = [thread_1, thread_2, thread_3]; + let _ids = unsafe { create_pthreads_no_params(thread_order) }; + + 0 +} + +extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { + X.store(1, Ordering::Relaxed); + Y.store(1, Ordering::Release); + std::ptr::null_mut() +} + +extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { + let r1 = Y.load(Ordering::Relaxed); + Z.store(r1, Ordering::Relaxed); + std::ptr::null_mut() +} + +extern "C" fn thread_3(_value: *mut c_void) -> *mut c_void { + let _r2 = Z.load(Ordering::Relaxed); + std::sync::atomic::fence(Ordering::AcqRel); + let _r3 = X.load(Ordering::Relaxed); + std::ptr::null_mut() +} diff --git a/tests/genmc/pass/litmus/cumul-release/cumul-release.stderr b/tests/genmc/pass/litmus/cumul-release/cumul-release.stderr new file mode 100644 index 0000000000..c2e069848f --- /dev/null +++ b/tests/genmc/pass/litmus/cumul-release/cumul-release.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 8 diff --git a/tests/genmc/pass/litmus/default/default.order123.stderr b/tests/genmc/pass/litmus/default/default.order123.stderr new file mode 100644 index 0000000000..1ec3beefbf --- /dev/null +++ b/tests/genmc/pass/litmus/default/default.order123.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 12 diff --git a/tests/genmc/pass/litmus/default/default.order231.stderr b/tests/genmc/pass/litmus/default/default.order231.stderr new file mode 100644 index 0000000000..1ec3beefbf --- /dev/null +++ b/tests/genmc/pass/litmus/default/default.order231.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 12 diff --git a/tests/genmc/pass/litmus/default/default.order312.stderr b/tests/genmc/pass/litmus/default/default.order312.stderr new file mode 100644 index 0000000000..1ec3beefbf --- /dev/null +++ b/tests/genmc/pass/litmus/default/default.order312.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 12 diff --git a/tests/genmc/pass/litmus/default/default.order321.stderr b/tests/genmc/pass/litmus/default/default.order321.stderr new file mode 100644 index 0000000000..1ec3beefbf --- /dev/null +++ b/tests/genmc/pass/litmus/default/default.order321.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 12 diff --git a/tests/genmc/pass/litmus/default/default.rs b/tests/genmc/pass/litmus/default/default.rs new file mode 100644 index 0000000000..6bd9e42a76 --- /dev/null +++ b/tests/genmc/pass/litmus/default/default.rs @@ -0,0 +1,49 @@ +//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@revisions: order123 order321 order312 order231 + +#![no_main] + +#[path = "../../../../utils-dep/mod.rs"] +mod utils_dep; + +use std::ffi::c_void; +use std::sync::atomic::{AtomicU64, Ordering}; + +use crate::utils_dep::*; + +static X: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + let thread_order = if cfg!(order123) { + [thread_1, thread_2, thread_3] + } else if cfg!(order321) { + [thread_3, thread_2, thread_1] + } else if cfg!(order312) { + [thread_3, thread_1, thread_2] + } else if cfg!(order231) { + [thread_2, thread_3, thread_1] + } else { + unimplemented!(); + }; + + let _ids = unsafe { create_pthreads_no_params(thread_order) }; + + 0 +} + +extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { + X.load(Ordering::Acquire); + X.load(Ordering::Acquire); + std::ptr::null_mut() +} + +extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { + X.store(1, Ordering::Release); + std::ptr::null_mut() +} + +extern "C" fn thread_3(_value: *mut c_void) -> *mut c_void { + X.store(2, Ordering::Release); + std::ptr::null_mut() +} diff --git a/tests/genmc/pass/litmus/detour/detour.rs b/tests/genmc/pass/litmus/detour/detour.rs new file mode 100644 index 0000000000..d721c2edc4 --- /dev/null +++ b/tests/genmc/pass/litmus/detour/detour.rs @@ -0,0 +1,42 @@ +//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows + +#![no_main] + +#[path = "../../../../utils-dep/mod.rs"] +mod utils_dep; + +use std::ffi::c_void; +use std::sync::atomic::{AtomicI64, Ordering}; + +use crate::utils_dep::*; + +static X: AtomicI64 = AtomicI64::new(0); +static Y: AtomicI64 = AtomicI64::new(0); +static Z: AtomicI64 = AtomicI64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + let thread_order = [thread_1, thread_2, thread_3]; + let _ids = unsafe { create_pthreads_no_params(thread_order) }; + + 0 +} + +extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { + X.store(1, Ordering::Relaxed); + std::ptr::null_mut() +} + +extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { + let a = Z.load(Ordering::Relaxed); + X.store(a.wrapping_sub(1), Ordering::Relaxed); + let b = X.load(Ordering::Relaxed); + Y.store(b, Ordering::Relaxed); + std::ptr::null_mut() +} + +extern "C" fn thread_3(_value: *mut c_void) -> *mut c_void { + let c = Y.load(Ordering::Relaxed); + Z.store(c, Ordering::Relaxed); + std::ptr::null_mut() +} diff --git a/tests/genmc/pass/litmus/detour/detour.stderr b/tests/genmc/pass/litmus/detour/detour.stderr new file mode 100644 index 0000000000..7e0204914a --- /dev/null +++ b/tests/genmc/pass/litmus/detour/detour.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 9 diff --git a/tests/genmc/pass/litmus/fr_w_w_w_reads/fr_w_w_w_reads.rs b/tests/genmc/pass/litmus/fr_w_w_w_reads/fr_w_w_w_reads.rs new file mode 100644 index 0000000000..458f8df1cd --- /dev/null +++ b/tests/genmc/pass/litmus/fr_w_w_w_reads/fr_w_w_w_reads.rs @@ -0,0 +1,45 @@ +//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows + +#![no_main] + +#[path = "../../../../utils-dep/mod.rs"] +mod utils_dep; + +use std::ffi::c_void; +use std::ptr::null_mut; +use std::sync::atomic::{AtomicU64, Ordering}; + +use crate::utils_dep::*; + +static X: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + let thread_order = [thread_1, thread_2, thread_3, thread_4]; + let _ids = unsafe { create_pthreads_no_params(thread_order) }; + + 0 +} + +pub extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { + X.store(1, Ordering::Relaxed); + null_mut() +} + +pub extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { + X.store(2, Ordering::Relaxed); + null_mut() +} + +pub extern "C" fn thread_3(_value: *mut c_void) -> *mut c_void { + X.store(3, Ordering::Relaxed); + null_mut() +} + +pub extern "C" fn thread_4(_value: *mut c_void) -> *mut c_void { + let _r1 = X.load(Ordering::Relaxed); + let _r2 = X.load(Ordering::Relaxed); + let _r3 = X.load(Ordering::Relaxed); + let _r4 = X.load(Ordering::Relaxed); + null_mut() +} diff --git a/tests/genmc/pass/litmus/fr_w_w_w_reads/fr_w_w_w_reads.stderr b/tests/genmc/pass/litmus/fr_w_w_w_reads/fr_w_w_w_reads.stderr new file mode 100644 index 0000000000..528ebdfd2b --- /dev/null +++ b/tests/genmc/pass/litmus/fr_w_w_w_reads/fr_w_w_w_reads.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 210 diff --git a/tests/genmc/pass/litmus/inc2w/inc2w.rs b/tests/genmc/pass/litmus/inc2w/inc2w.rs new file mode 100644 index 0000000000..785137efd4 --- /dev/null +++ b/tests/genmc/pass/litmus/inc2w/inc2w.rs @@ -0,0 +1,37 @@ +//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows + +#![no_main] + +#[path = "../../../../utils-dep/mod.rs"] +mod utils_dep; + +use std::ffi::c_void; +use std::ptr::null_mut; +use std::sync::atomic::{AtomicU64, Ordering}; + +use crate::utils_dep::*; + +static X: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + let thread_order = [thread_1, thread_2, thread_3]; + let _ids = unsafe { create_pthreads_no_params(thread_order) }; + + 0 +} + +pub extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { + X.fetch_add(1, Ordering::Relaxed); + null_mut() +} + +pub extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { + X.store(4, Ordering::Release); + null_mut() +} + +pub extern "C" fn thread_3(_value: *mut c_void) -> *mut c_void { + X.fetch_add(2, Ordering::Relaxed); + null_mut() +} diff --git a/tests/genmc/pass/litmus/inc2w/inc2w.stderr b/tests/genmc/pass/litmus/inc2w/inc2w.stderr new file mode 100644 index 0000000000..e4151d346e --- /dev/null +++ b/tests/genmc/pass/litmus/inc2w/inc2w.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 6 diff --git a/tests/genmc/pass/litmus/inc_inc_RR_W_RR/inc_inc_RR_W_RR.rs b/tests/genmc/pass/litmus/inc_inc_RR_W_RR/inc_inc_RR_W_RR.rs new file mode 100644 index 0000000000..ab1a70123e --- /dev/null +++ b/tests/genmc/pass/litmus/inc_inc_RR_W_RR/inc_inc_RR_W_RR.rs @@ -0,0 +1,63 @@ +//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows + +#![no_main] + +#[path = "../../../../utils-dep/mod.rs"] +mod utils_dep; + +use std::ffi::c_void; +use std::ptr::null_mut; +use std::sync::atomic::{AtomicU64, Ordering}; + +use crate::utils_dep::*; + +static X: AtomicU64 = AtomicU64::new(0); + +static mut A: u64 = 0; +static mut B: u64 = 0; +static mut C: u64 = 0; +static mut D: u64 = 0; + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + let thread_order = [thread_1, thread_2, thread_3, thread_4, thread_5]; + let ids = unsafe { create_pthreads_no_params(thread_order) }; + unsafe { join_pthreads(ids) }; + + if unsafe { A == 42 && B == 2 && C == 1 && D == 42 } { + std::process::abort(); + } + + 0 +} + +pub extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { + X.fetch_add(1, Ordering::Relaxed); + null_mut() +} + +pub extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { + X.fetch_add(1, Ordering::Relaxed); + null_mut() +} + +pub extern "C" fn thread_3(_value: *mut c_void) -> *mut c_void { + unsafe { + A = X.load(Ordering::Relaxed); + B = X.load(Ordering::Relaxed); + } + null_mut() +} + +pub extern "C" fn thread_4(_value: *mut c_void) -> *mut c_void { + X.store(42, Ordering::Relaxed); + null_mut() +} + +pub extern "C" fn thread_5(_value: *mut c_void) -> *mut c_void { + unsafe { + C = X.load(Ordering::Relaxed); + D = X.load(Ordering::Relaxed); + } + null_mut() +} diff --git a/tests/genmc/pass/litmus/inc_inc_RR_W_RR/inc_inc_RR_W_RR.stderr b/tests/genmc/pass/litmus/inc_inc_RR_W_RR/inc_inc_RR_W_RR.stderr new file mode 100644 index 0000000000..ad9e4b3a6e --- /dev/null +++ b/tests/genmc/pass/litmus/inc_inc_RR_W_RR/inc_inc_RR_W_RR.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 600 diff --git a/tests/genmc/pass/litmus/riwi/riwi.rs b/tests/genmc/pass/litmus/riwi/riwi.rs new file mode 100644 index 0000000000..1a15846836 --- /dev/null +++ b/tests/genmc/pass/litmus/riwi/riwi.rs @@ -0,0 +1,34 @@ +//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows + +#![no_main] + +#[path = "../../../../utils-dep/mod.rs"] +mod utils_dep; + +use std::ffi::c_void; +use std::sync::atomic::{AtomicU64, Ordering}; + +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); + +use crate::utils_dep::*; + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + let thread_order = [thread_1, thread_2]; + let _ids = unsafe { create_pthreads_no_params(thread_order) }; + + 0 +} + +extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { + Y.load(Ordering::Relaxed); + X.fetch_add(1, Ordering::Relaxed); + std::ptr::null_mut() +} + +extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { + X.fetch_add(1, Ordering::Relaxed); + Y.store(1, Ordering::Release); + std::ptr::null_mut() +} diff --git a/tests/genmc/pass/litmus/riwi/riwi.stderr b/tests/genmc/pass/litmus/riwi/riwi.stderr new file mode 100644 index 0000000000..f2f3f904fb --- /dev/null +++ b/tests/genmc/pass/litmus/riwi/riwi.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 3 diff --git a/tests/genmc/pass/litmus/viktor-relseq/viktor-relseq.rs b/tests/genmc/pass/litmus/viktor-relseq/viktor-relseq.rs new file mode 100644 index 0000000000..243849113c --- /dev/null +++ b/tests/genmc/pass/litmus/viktor-relseq/viktor-relseq.rs @@ -0,0 +1,43 @@ +//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows + +#![no_main] + +#[path = "../../../../utils-dep/mod.rs"] +mod utils_dep; + +use std::ffi::c_void; +use std::sync::atomic::{AtomicU64, Ordering}; + +static LOCK: AtomicU64 = AtomicU64::new(0); + +use crate::utils_dep::*; + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + let thread_order = [thread_ra, thread_r, thread_rr, thread_rs]; + let _ids = unsafe { create_pthreads_no_params(thread_order) }; + + 0 +} + +extern "C" fn thread_ra(_value: *mut c_void) -> *mut c_void { + LOCK.fetch_add(1, Ordering::Acquire); + LOCK.fetch_add(1, Ordering::Relaxed); + std::ptr::null_mut() +} + +extern "C" fn thread_r(_value: *mut c_void) -> *mut c_void { + LOCK.fetch_add(1, Ordering::Relaxed); + LOCK.fetch_add(1, Ordering::Relaxed); + std::ptr::null_mut() +} + +extern "C" fn thread_rr(_value: *mut c_void) -> *mut c_void { + LOCK.fetch_add(1, Ordering::Release); + std::ptr::null_mut() +} + +extern "C" fn thread_rs(_value: *mut c_void) -> *mut c_void { + LOCK.fetch_add(1, Ordering::Relaxed); + std::ptr::null_mut() +} diff --git a/tests/genmc/pass/litmus/viktor-relseq/viktor-relseq.stderr b/tests/genmc/pass/litmus/viktor-relseq/viktor-relseq.stderr new file mode 100644 index 0000000000..5b01ae0789 --- /dev/null +++ b/tests/genmc/pass/litmus/viktor-relseq/viktor-relseq.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 180 diff --git a/tests/genmc/pass/loom/load-buffering/load_buffering.genmc.stderr b/tests/genmc/pass/loom/load-buffering/load_buffering.genmc.stderr new file mode 100644 index 0000000000..f2f3f904fb --- /dev/null +++ b/tests/genmc/pass/loom/load-buffering/load_buffering.genmc.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 3 diff --git a/tests/genmc/pass/loom/load-buffering/load_buffering.genmc_std.stderr b/tests/genmc/pass/loom/load-buffering/load_buffering.genmc_std.stderr new file mode 100644 index 0000000000..40f2279166 --- /dev/null +++ b/tests/genmc/pass/loom/load-buffering/load_buffering.genmc_std.stderr @@ -0,0 +1,9 @@ +warning: GenMC currently does not model the atomic failure ordering for `compare_exchange`. Failure ordering 'Relaxed' is treated like 'Acquire', which means that Miri might miss bugs related to this memory access (possible unsoundness)! + +warning: GenMC mode currently does not model spurious failures of `compare_exchange_weak`. This may lead to missed bugs (possible unsoundness)! + +warning: GenMC currently does not model the atomic failure ordering for `compare_exchange`. Failure ordering 'Acquire' is treated like 'Relaxed', which means that Miri might incorrectly detect errors related to this memory access (possible false positives). + + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 3 diff --git a/tests/genmc/pass/loom/load-buffering/load_buffering.rs b/tests/genmc/pass/loom/load-buffering/load_buffering.rs new file mode 100644 index 0000000000..bb896cdc17 --- /dev/null +++ b/tests/genmc/pass/loom/load-buffering/load_buffering.rs @@ -0,0 +1,80 @@ +//@ revisions: non_genmc non_genmc_std genmc genmc_std +//@[non_genmc,non_genmc_std] compile-flags: +//@[genmc,genmc_std] compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// This is the test `load_buffering` from `loom/test/litmus.rs`, adapted for Miri-GenMC. +// https://github.com/tokio-rs/loom/blob/dbf32b04bae821c64be44405a0bb72ca08741558/tests/litmus.rs + +// Loom uses a memory model like C++11's, which allowed for the `test` function to return `1`. +// This is not allowed in the RC11 memory model, which is what Miri-GenMC uses. + +// This test doubles as a comparison between using std threads and pthreads, and normal Miri vs Miri-GenMC. + +#![no_main] + +#[cfg(not(any(non_genmc_std, genmc_std)))] +#[path = "../../../../utils-dep/mod.rs"] +mod utils_dep; + +use std::sync::atomic::AtomicUsize; +use std::sync::atomic::Ordering::{Relaxed, SeqCst}; + +static X: AtomicUsize = AtomicUsize::new(0); +static Y: AtomicUsize = AtomicUsize::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // For normal Miri, we need multiple repetitions, but 1 is enough for GenMC. + const REPS: usize = if cfg!(any(non_genmc, non_genmc_std)) { 128 } else { 1 }; + + for _ in 0..REPS { + X.store(0, SeqCst); + Y.store(0, SeqCst); + + if test() == 1 { + unsafe { std::hint::unreachable_unchecked() }; + } + } + + 0 +} + +#[cfg(any(non_genmc_std, genmc_std))] +fn test() -> usize { + let thread = std::thread::spawn(thread_0); + let a = thread_1(); + thread.join().unwrap(); + + a +} + +#[cfg(not(any(non_genmc_std, genmc_std)))] +fn test() -> usize { + use std::ffi::c_void; + + use crate::utils_dep::genmc::{join_pthread, spawn_pthread}; + + extern "C" fn thread_func(_value: *mut c_void) -> *mut c_void { + thread_0(); + std::ptr::null_mut() + } + + let thread_id = unsafe { spawn_pthread(thread_func, std::ptr::null_mut()) }; + + let a = thread_1(); + + unsafe { join_pthread(thread_id) }; + + a +} + +fn thread_0() { + X.store(Y.load(Relaxed), Relaxed); +} + +/// Returns the value for `a` +fn thread_1() -> usize { + let a = X.load(Relaxed); + Y.store(1, Relaxed); + a +} diff --git a/tests/genmc/pass/simple/2+2W+3sc+rel1/2_2w_3sc_rel1.rs b/tests/genmc/pass/simple/2+2W+3sc+rel1/2_2w_3sc_rel1.rs new file mode 100644 index 0000000000..940d6ba580 --- /dev/null +++ b/tests/genmc/pass/simple/2+2W+3sc+rel1/2_2w_3sc_rel1.rs @@ -0,0 +1,39 @@ +//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows + +#![no_main] + +#[path = "../../../../utils-dep/mod.rs"] +mod utils_dep; + +use std::ffi::c_void; +use std::sync::atomic::{AtomicU64, Ordering}; + +use crate::utils_dep::genmc::{join_pthread, spawn_pthread}; + +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + unsafe { + let value: *mut c_void = std::ptr::null_mut(); + let t1 = spawn_pthread(thread_1, value); + let t2 = spawn_pthread(thread_2, value); + + join_pthread(t1); + join_pthread(t2); + } + 0 +} + +extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { + X.store(1, Ordering::SeqCst); + Y.store(2, Ordering::SeqCst); + std::ptr::null_mut() +} + +extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { + Y.store(1, Ordering::Release); + X.store(2, Ordering::SeqCst); + std::ptr::null_mut() +} diff --git a/tests/genmc/pass/simple/2+2W+3sc+rel1/2_2w_3sc_rel1.stderr b/tests/genmc/pass/simple/2+2W+3sc+rel1/2_2w_3sc_rel1.stderr new file mode 100644 index 0000000000..61dc3f60ba --- /dev/null +++ b/tests/genmc/pass/simple/2+2W+3sc+rel1/2_2w_3sc_rel1.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 4 diff --git a/tests/genmc/pass/simple/2w2w_seqcst.rs b/tests/genmc/pass/simple/2w2w_seqcst.rs new file mode 100644 index 0000000000..c3890da4c9 --- /dev/null +++ b/tests/genmc/pass/simple/2w2w_seqcst.rs @@ -0,0 +1,42 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// TODO GENMC: this test currently takes 3 iterations, it this correct? + +#![no_main] + +#[path = "../../../utils-dep/mod.rs"] +mod utils_dep; + +use std::ffi::c_void; +use std::sync::atomic::{AtomicU64, Ordering}; + +use crate::utils_dep::{join_pthread, spawn_pthread}; + +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); + +const LOAD_ORD: Ordering = Ordering::SeqCst; +const STORE_ORD: Ordering = Ordering::SeqCst; + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + let thread_id = unsafe { spawn_pthread(thread_func, std::ptr::null_mut()) }; + + X.store(1, STORE_ORD); + Y.store(2, STORE_ORD); + + unsafe { join_pthread(thread_id) }; + + let x = X.load(LOAD_ORD); + let y = Y.load(LOAD_ORD); + if x == 1 && y == 1 { + unsafe { std::hint::unreachable_unchecked() }; + } + 0 +} + +extern "C" fn thread_func(_value: *mut c_void) -> *mut c_void { + Y.store(1, STORE_ORD); + X.store(2, STORE_ORD); + std::ptr::null_mut() +} diff --git a/tests/genmc/pass/simple/2w2w_seqcst.stderr b/tests/genmc/pass/simple/2w2w_seqcst.stderr new file mode 100644 index 0000000000..f2f3f904fb --- /dev/null +++ b/tests/genmc/pass/simple/2w2w_seqcst.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 3 diff --git a/tests/genmc/pass/simple/atomic_ptr.rs b/tests/genmc/pass/simple/atomic_ptr.rs new file mode 100644 index 0000000000..a13b517d4a --- /dev/null +++ b/tests/genmc/pass/simple/atomic_ptr.rs @@ -0,0 +1,83 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +#![no_main] + +use std::sync::atomic::*; + +static mut X: u64 = 0; +static mut Y: u64 = 0; + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + unsafe { + let atomic_ptr: AtomicPtr = AtomicPtr::new(&raw mut X); + + let x_ptr = atomic_ptr.load(Ordering::SeqCst); + *x_ptr = 10; + if X != 10 { + std::process::abort(); + } + atomic_ptr.store(&raw mut Y, Ordering::SeqCst); + Y = 42; + let y_ptr = atomic_ptr.load(Ordering::SeqCst); + if *y_ptr != 42 { + std::process::abort(); + } + *y_ptr = 1234; + if Y != 1234 { + std::process::abort(); + } else if X != 10 { + std::process::abort(); + } + let y_ptr_ = atomic_ptr.swap(&raw mut X, Ordering::SeqCst); + if y_ptr_ != y_ptr { + std::process::abort(); + } + // To make sure also the provenance info is correctly restored, we need to use the pointers: + if *y_ptr_ != *y_ptr { + std::process::abort(); + } + *y_ptr_ = *y_ptr; + + match atomic_ptr.compare_exchange( + y_ptr, // wrong, it should be `x_ptr`, so this should never succeed + std::ptr::dangling_mut(), + Ordering::SeqCst, + Ordering::SeqCst, + ) { + Ok(_ptr) => std::process::abort(), + Err(ptr) => + if ptr != x_ptr { + std::process::abort(); + } else if *ptr != *x_ptr { + std::process::abort(); + } else { + *ptr = *ptr; + }, + } + + let mut array: [u64; 10] = [0xAAAA; 10]; + match atomic_ptr.compare_exchange( + x_ptr, + &raw mut array[2], + Ordering::SeqCst, + Ordering::SeqCst, + ) { + Ok(ptr) => + if ptr != x_ptr { + std::process::abort(); + }, + Err(_ptr) => std::process::abort(), + } + let ptr = atomic_ptr.load(Ordering::SeqCst); + *ptr = 0xB; + if array[2] != 0xB { + std::process::abort(); + } + array[2] = 0xC; + if *ptr != 0xC { + std::process::abort(); + } + } + 0 +} diff --git a/tests/genmc/pass/simple/atomic_ptr.stderr b/tests/genmc/pass/simple/atomic_ptr.stderr new file mode 100644 index 0000000000..2382cc5a78 --- /dev/null +++ b/tests/genmc/pass/simple/atomic_ptr.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 1 diff --git a/tests/genmc/pass/simple/cas_simple.rs b/tests/genmc/pass/simple/cas_simple.rs new file mode 100644 index 0000000000..e9893ffe1b --- /dev/null +++ b/tests/genmc/pass/simple/cas_simple.rs @@ -0,0 +1,47 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +#![no_main] + +use std::sync::atomic::*; + +static VALUE: AtomicUsize = AtomicUsize::new(0); + +const SUCCESS_ORD: Ordering = Ordering::SeqCst; +const FAILURE_ORD: Ordering = Ordering::SeqCst; + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + VALUE.store(1, SUCCESS_ORD); + + let current = 1; + let new_value = 2; + // Expect success: + match VALUE.compare_exchange(current, new_value, SUCCESS_ORD, FAILURE_ORD) { + Ok(old_value) => + if old_value != current { + std::process::abort(); + }, + Err(_value) => std::process::abort(), + } + + if new_value != VALUE.load(SUCCESS_ORD) { + std::process::abort() + } + + let dummy_value = 42; + let wrong_value = 1234; + + // Expect failure: + match VALUE.compare_exchange(wrong_value, dummy_value, SUCCESS_ORD, FAILURE_ORD) { + Ok(_old_value) => std::process::abort(), + Err(old_value) => + if old_value != new_value { + std::process::abort(); + }, + } + + if new_value != VALUE.load(SUCCESS_ORD) { + std::process::abort() + } + 0 +} diff --git a/tests/genmc/pass/simple/cas_simple.stderr b/tests/genmc/pass/simple/cas_simple.stderr new file mode 100644 index 0000000000..2382cc5a78 --- /dev/null +++ b/tests/genmc/pass/simple/cas_simple.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 1 diff --git a/tests/genmc/pass/simple/simple_main.rs b/tests/genmc/pass/simple/simple_main.rs new file mode 100644 index 0000000000..e2d7c3b562 --- /dev/null +++ b/tests/genmc/pass/simple/simple_main.rs @@ -0,0 +1,3 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +fn main() {} diff --git a/tests/genmc/pass/simple/simple_main.stderr b/tests/genmc/pass/simple/simple_main.stderr new file mode 100644 index 0000000000..2184c16ce9 --- /dev/null +++ b/tests/genmc/pass/simple/simple_main.stderr @@ -0,0 +1,7 @@ +warning: GenMC mode currently does not model spurious failures of `compare_exchange_weak`. This may lead to missed bugs (possible unsoundness)! + +warning: GenMC currently does not model the atomic failure ordering for `compare_exchange`. Failure ordering 'Relaxed' is treated like 'Acquire', which means that Miri might miss bugs related to this memory access (possible unsoundness)! + + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 1 diff --git a/tests/genmc/pass/simple/simple_main_spawn_threads.rs b/tests/genmc/pass/simple/simple_main_spawn_threads.rs new file mode 100644 index 0000000000..46e37fd5bc --- /dev/null +++ b/tests/genmc/pass/simple/simple_main_spawn_threads.rs @@ -0,0 +1,10 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +const N: usize = 1; + +fn main() { + let handles: Vec<_> = (0..N).map(|_| std::thread::spawn(thread_func)).collect(); + handles.into_iter().for_each(|handle| handle.join().unwrap()); +} + +fn thread_func() {} diff --git a/tests/genmc/pass/simple/simple_main_spawn_threads.stderr b/tests/genmc/pass/simple/simple_main_spawn_threads.stderr new file mode 100644 index 0000000000..c6bd55a4f8 --- /dev/null +++ b/tests/genmc/pass/simple/simple_main_spawn_threads.stderr @@ -0,0 +1,9 @@ +warning: GenMC mode currently does not model spurious failures of `compare_exchange_weak`. This may lead to missed bugs (possible unsoundness)! + +warning: GenMC currently does not model the atomic failure ordering for `compare_exchange`. Failure ordering 'Relaxed' is treated like 'Acquire', which means that Miri might miss bugs related to this memory access (possible unsoundness)! + +warning: GenMC currently does not model the atomic failure ordering for `compare_exchange`. Failure ordering 'Acquire' is treated like 'Relaxed', which means that Miri might incorrectly detect errors related to this memory access (possible false positives). + + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 1 diff --git a/tests/genmc/pass/simple/simple_miri_main_spawn_pthreads.rs b/tests/genmc/pass/simple/simple_miri_main_spawn_pthreads.rs new file mode 100644 index 0000000000..6d2269794b --- /dev/null +++ b/tests/genmc/pass/simple/simple_miri_main_spawn_pthreads.rs @@ -0,0 +1,35 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +#![no_main] + +use std::ffi::c_void; + +use libc::{self, pthread_attr_t, pthread_t}; + +const N: usize = 1; + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + let mut handles: Vec = vec![0; N]; + + let attr: *const pthread_attr_t = std::ptr::null(); + let value: *mut c_void = std::ptr::null_mut(); + + handles.iter_mut().for_each(|thread_id| { + if 0 != unsafe { libc::pthread_create(thread_id, attr, thread_func, value) } { + std::process::abort(); + } + }); + + handles.into_iter().for_each(|thread_id| { + if 0 != unsafe { libc::pthread_join(thread_id, std::ptr::null_mut()) } { + std::process::abort(); + } + }); + + 0 +} + +extern "C" fn thread_func(_value: *mut c_void) -> *mut c_void { + std::ptr::null_mut() +} diff --git a/tests/genmc/pass/simple/simple_miri_main_spawn_pthreads.stderr b/tests/genmc/pass/simple/simple_miri_main_spawn_pthreads.stderr new file mode 100644 index 0000000000..2382cc5a78 --- /dev/null +++ b/tests/genmc/pass/simple/simple_miri_main_spawn_pthreads.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 1 diff --git a/tests/genmc/pass/simple/simple_miri_main_spawn_threads.rs b/tests/genmc/pass/simple/simple_miri_main_spawn_threads.rs new file mode 100644 index 0000000000..b7203b4697 --- /dev/null +++ b/tests/genmc/pass/simple/simple_miri_main_spawn_threads.rs @@ -0,0 +1,15 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +#![no_main] + +const N: usize = 1; + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + let handles: Vec<_> = (0..N).map(|_| std::thread::spawn(thread_func)).collect(); + handles.into_iter().for_each(|handle| handle.join().unwrap()); + + 0 +} + +fn thread_func() {} diff --git a/tests/genmc/pass/simple/simple_miri_main_spawn_threads.stderr b/tests/genmc/pass/simple/simple_miri_main_spawn_threads.stderr new file mode 100644 index 0000000000..8ac3e9f5cf --- /dev/null +++ b/tests/genmc/pass/simple/simple_miri_main_spawn_threads.stderr @@ -0,0 +1,9 @@ +warning: GenMC currently does not model the atomic failure ordering for `compare_exchange`. Failure ordering 'Relaxed' is treated like 'Acquire', which means that Miri might miss bugs related to this memory access (possible unsoundness)! + +warning: GenMC mode currently does not model spurious failures of `compare_exchange_weak`. This may lead to missed bugs (possible unsoundness)! + +warning: GenMC currently does not model the atomic failure ordering for `compare_exchange`. Failure ordering 'Acquire' is treated like 'Relaxed', which means that Miri might incorrectly detect errors related to this memory access (possible false positives). + + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 1 diff --git a/tests/genmc/pass/simple/stack_alloc_atomic.rs b/tests/genmc/pass/simple/stack_alloc_atomic.rs new file mode 100644 index 0000000000..d4c9968f43 --- /dev/null +++ b/tests/genmc/pass/simple/stack_alloc_atomic.rs @@ -0,0 +1,18 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +#![no_main] + +use std::sync::atomic::*; + +const ORD: Ordering = Ordering::SeqCst; + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + let x = AtomicU64::new(1234); + let a = x.load(ORD); + if a != 1234 { + std::process::abort(); + } + + 0 +} diff --git a/tests/genmc/pass/simple/stack_alloc_atomic.stderr b/tests/genmc/pass/simple/stack_alloc_atomic.stderr new file mode 100644 index 0000000000..2382cc5a78 --- /dev/null +++ b/tests/genmc/pass/simple/stack_alloc_atomic.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 1 diff --git a/tests/genmc/pass/simple/thread_locals.rs b/tests/genmc/pass/simple/thread_locals.rs new file mode 100644 index 0000000000..f12f6bbad6 --- /dev/null +++ b/tests/genmc/pass/simple/thread_locals.rs @@ -0,0 +1,54 @@ +//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows + +#![no_main] + +#[path = "../../../utils-dep/mod.rs"] +mod utils_dep; + +use std::cell::Cell; +use std::ffi::c_void; +use std::sync::atomic::{AtomicPtr, Ordering}; + +use crate::utils_dep::*; + +static X: AtomicPtr = AtomicPtr::new(std::ptr::null_mut()); + +thread_local! { + static R: Cell<*mut u64> = Cell::new(std::ptr::null_mut()); +} + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + let _ids = unsafe { create_pthreads_no_params([thread_1, thread_2, thread_3]) }; + + 0 +} + +pub unsafe fn malloc() -> *mut u64 { + Box::into_raw(Box::::new_uninit()) as *mut u64 +} + +extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { + unsafe { + R.set(malloc()); + let r_ptr = R.get(); + let _ = X.compare_exchange(std::ptr::null_mut(), r_ptr, Ordering::SeqCst, Ordering::SeqCst); + std::ptr::null_mut() + } +} + +extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { + unsafe { + R.set(malloc()); + std::ptr::null_mut() + } +} + +extern "C" fn thread_3(_value: *mut c_void) -> *mut c_void { + unsafe { + R.set(malloc()); + let r_ptr = R.get(); + let _ = X.compare_exchange(std::ptr::null_mut(), r_ptr, Ordering::SeqCst, Ordering::SeqCst); + std::ptr::null_mut() + } +} diff --git a/tests/genmc/pass/simple/thread_locals.stderr b/tests/genmc/pass/simple/thread_locals.stderr new file mode 100644 index 0000000000..edecd38278 --- /dev/null +++ b/tests/genmc/pass/simple/thread_locals.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 2 diff --git a/tests/genmc/pass/simple/thread_simple.rs b/tests/genmc/pass/simple/thread_simple.rs new file mode 100644 index 0000000000..abb3adbb03 --- /dev/null +++ b/tests/genmc/pass/simple/thread_simple.rs @@ -0,0 +1,34 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +#![no_main] + +use std::ffi::c_void; +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::SeqCst; + +use libc::{self, pthread_attr_t, pthread_t}; + +static FLAG: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + let mut thread_id: pthread_t = 0; + + let attr: *const pthread_attr_t = std::ptr::null(); + let value: *mut c_void = std::ptr::null_mut(); + + assert!(0 == unsafe { libc::pthread_create(&raw mut thread_id, attr, thread_func, value) }); + + FLAG.store(1, SeqCst); + + assert!(0 == unsafe { libc::pthread_join(thread_id, std::ptr::null_mut()) }); + + let flag = FLAG.load(SeqCst); + assert!(flag == 1 || flag == 2); + return 0; +} + +extern "C" fn thread_func(_value: *mut c_void) -> *mut c_void { + FLAG.store(2, SeqCst); + std::ptr::null_mut() +} diff --git a/tests/genmc/pass/simple/thread_simple.stderr b/tests/genmc/pass/simple/thread_simple.stderr new file mode 100644 index 0000000000..edecd38278 --- /dev/null +++ b/tests/genmc/pass/simple/thread_simple.stderr @@ -0,0 +1,3 @@ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 2 diff --git a/tests/genmc/pass/test_cxx_build.rs b/tests/genmc/pass/test_cxx_build.rs deleted file mode 100644 index f621bd9114..0000000000 --- a/tests/genmc/pass/test_cxx_build.rs +++ /dev/null @@ -1,8 +0,0 @@ -//@compile-flags: -Zmiri-genmc - -#![no_main] - -#[unsafe(no_mangle)] -fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - 0 -} diff --git a/tests/genmc/pass/test_cxx_build.stderr b/tests/genmc/pass/test_cxx_build.stderr deleted file mode 100644 index 4b7aa824bd..0000000000 --- a/tests/genmc/pass/test_cxx_build.stderr +++ /dev/null @@ -1,5 +0,0 @@ -warning: borrow tracking has been disabled, it is not (yet) supported in GenMC mode. -C++: GenMC handle created! -Miri: GenMC handle creation successful! -C++: GenMC handle destroyed! -Miri: Dropping GenMC handle successful! diff --git a/tests/utils-dep/genmc.rs b/tests/utils-dep/genmc.rs new file mode 100644 index 0000000000..e1fd070813 --- /dev/null +++ b/tests/utils-dep/genmc.rs @@ -0,0 +1,38 @@ +use std::ffi::c_void; + +use libc::{self, pthread_attr_t, pthread_t}; + +/// Spawn 1 thread using `pthread_create`, abort the process on any errors. +pub unsafe fn spawn_pthread( + f: extern "C" fn(*mut c_void) -> *mut c_void, + value: *mut c_void, +) -> pthread_t { + let mut thread_id: pthread_t = 0; + + let attr: *const pthread_attr_t = std::ptr::null(); + + if 0 != unsafe { libc::pthread_create(&raw mut thread_id, attr, f, value) } { + std::process::abort(); + } + thread_id +} + +// Join the given pthread, abort the process on any errors. +pub unsafe fn join_pthread(thread_id: pthread_t) { + if 0 != unsafe { libc::pthread_join(thread_id, std::ptr::null_mut()) } { + std::process::abort(); + } +} + +/// Spawn `N` threads using `pthread_create` without any arguments, abort the process on any errors. +pub unsafe fn create_pthreads_no_params( + functions: [extern "C" fn(*mut c_void) -> *mut c_void; N], +) -> [pthread_t; N] { + let value = std::ptr::null_mut(); + functions.map(|func| spawn_pthread(func, value)) +} + +// Join the `N` given pthreads, abort the process on any errors. +pub unsafe fn join_pthreads(thread_ids: [pthread_t; N]) { + let _ = thread_ids.map(|id| join_pthread(id)); +} diff --git a/tests/utils-dep/miri_extern.rs b/tests/utils-dep/miri_extern.rs new file mode 100644 index 0000000000..dbd4cdb48d --- /dev/null +++ b/tests/utils-dep/miri_extern.rs @@ -0,0 +1,2 @@ +// TODO GENMC: is this ok or is there a better solution? +include!("../utils/miri_extern.rs"); diff --git a/tests/utils-dep/mod.rs b/tests/utils-dep/mod.rs new file mode 100644 index 0000000000..ab35b8e3c4 --- /dev/null +++ b/tests/utils-dep/mod.rs @@ -0,0 +1,8 @@ +#![allow(dead_code)] +#![allow(unused_imports)] + +pub mod genmc; +mod miri_extern; + +pub use self::genmc::*; +pub use self::miri_extern::*; diff --git a/tests/utils/miri_extern.rs b/tests/utils/miri_extern.rs index d6c43b1882..4ce7d27539 100644 --- a/tests/utils/miri_extern.rs +++ b/tests/utils/miri_extern.rs @@ -147,4 +147,7 @@ extern "Rust" { /// "symbolic" alignment checks. Will fail if the pointer is not actually aligned or `align` is /// not a power of two. Has no effect when alignment checks are concrete (which is the default). pub fn miri_promise_symbolic_alignment(ptr: *const (), align: usize); + + /// Blocks the current execution if the argument is false + pub fn miri_genmc_verifier_assume(condition: bool); } From af9d8468b6f97287374d69348fb65a929cf2766a Mon Sep 17 00:00:00 2001 From: Patrick-6 Date: Tue, 29 Jul 2025 16:43:06 +0200 Subject: [PATCH 03/41] Remove duplicate tests --- .../simple/2+2W+3sc+rel1/2_2w_3sc_rel1.rs | 39 ----------------- .../simple/2+2W+3sc+rel1/2_2w_3sc_rel1.stderr | 3 -- tests/genmc/pass/simple/2w2w_seqcst.rs | 42 ------------------- tests/genmc/pass/simple/2w2w_seqcst.stderr | 3 -- ...threads.rs => miri_main_spawn_pthreads.rs} | 0 ...stderr => miri_main_spawn_pthreads.stderr} | 0 6 files changed, 87 deletions(-) delete mode 100644 tests/genmc/pass/simple/2+2W+3sc+rel1/2_2w_3sc_rel1.rs delete mode 100644 tests/genmc/pass/simple/2+2W+3sc+rel1/2_2w_3sc_rel1.stderr delete mode 100644 tests/genmc/pass/simple/2w2w_seqcst.rs delete mode 100644 tests/genmc/pass/simple/2w2w_seqcst.stderr rename tests/genmc/pass/simple/{simple_miri_main_spawn_pthreads.rs => miri_main_spawn_pthreads.rs} (100%) rename tests/genmc/pass/simple/{simple_miri_main_spawn_pthreads.stderr => miri_main_spawn_pthreads.stderr} (100%) diff --git a/tests/genmc/pass/simple/2+2W+3sc+rel1/2_2w_3sc_rel1.rs b/tests/genmc/pass/simple/2+2W+3sc+rel1/2_2w_3sc_rel1.rs deleted file mode 100644 index 940d6ba580..0000000000 --- a/tests/genmc/pass/simple/2+2W+3sc+rel1/2_2w_3sc_rel1.rs +++ /dev/null @@ -1,39 +0,0 @@ -//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows - -#![no_main] - -#[path = "../../../../utils-dep/mod.rs"] -mod utils_dep; - -use std::ffi::c_void; -use std::sync::atomic::{AtomicU64, Ordering}; - -use crate::utils_dep::genmc::{join_pthread, spawn_pthread}; - -static X: AtomicU64 = AtomicU64::new(0); -static Y: AtomicU64 = AtomicU64::new(0); - -#[unsafe(no_mangle)] -fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - unsafe { - let value: *mut c_void = std::ptr::null_mut(); - let t1 = spawn_pthread(thread_1, value); - let t2 = spawn_pthread(thread_2, value); - - join_pthread(t1); - join_pthread(t2); - } - 0 -} - -extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { - X.store(1, Ordering::SeqCst); - Y.store(2, Ordering::SeqCst); - std::ptr::null_mut() -} - -extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { - Y.store(1, Ordering::Release); - X.store(2, Ordering::SeqCst); - std::ptr::null_mut() -} diff --git a/tests/genmc/pass/simple/2+2W+3sc+rel1/2_2w_3sc_rel1.stderr b/tests/genmc/pass/simple/2+2W+3sc+rel1/2_2w_3sc_rel1.stderr deleted file mode 100644 index 61dc3f60ba..0000000000 --- a/tests/genmc/pass/simple/2+2W+3sc+rel1/2_2w_3sc_rel1.stderr +++ /dev/null @@ -1,3 +0,0 @@ - -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 4 diff --git a/tests/genmc/pass/simple/2w2w_seqcst.rs b/tests/genmc/pass/simple/2w2w_seqcst.rs deleted file mode 100644 index c3890da4c9..0000000000 --- a/tests/genmc/pass/simple/2w2w_seqcst.rs +++ /dev/null @@ -1,42 +0,0 @@ -//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows - -// TODO GENMC: this test currently takes 3 iterations, it this correct? - -#![no_main] - -#[path = "../../../utils-dep/mod.rs"] -mod utils_dep; - -use std::ffi::c_void; -use std::sync::atomic::{AtomicU64, Ordering}; - -use crate::utils_dep::{join_pthread, spawn_pthread}; - -static X: AtomicU64 = AtomicU64::new(0); -static Y: AtomicU64 = AtomicU64::new(0); - -const LOAD_ORD: Ordering = Ordering::SeqCst; -const STORE_ORD: Ordering = Ordering::SeqCst; - -#[unsafe(no_mangle)] -fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - let thread_id = unsafe { spawn_pthread(thread_func, std::ptr::null_mut()) }; - - X.store(1, STORE_ORD); - Y.store(2, STORE_ORD); - - unsafe { join_pthread(thread_id) }; - - let x = X.load(LOAD_ORD); - let y = Y.load(LOAD_ORD); - if x == 1 && y == 1 { - unsafe { std::hint::unreachable_unchecked() }; - } - 0 -} - -extern "C" fn thread_func(_value: *mut c_void) -> *mut c_void { - Y.store(1, STORE_ORD); - X.store(2, STORE_ORD); - std::ptr::null_mut() -} diff --git a/tests/genmc/pass/simple/2w2w_seqcst.stderr b/tests/genmc/pass/simple/2w2w_seqcst.stderr deleted file mode 100644 index f2f3f904fb..0000000000 --- a/tests/genmc/pass/simple/2w2w_seqcst.stderr +++ /dev/null @@ -1,3 +0,0 @@ - -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 3 diff --git a/tests/genmc/pass/simple/simple_miri_main_spawn_pthreads.rs b/tests/genmc/pass/simple/miri_main_spawn_pthreads.rs similarity index 100% rename from tests/genmc/pass/simple/simple_miri_main_spawn_pthreads.rs rename to tests/genmc/pass/simple/miri_main_spawn_pthreads.rs diff --git a/tests/genmc/pass/simple/simple_miri_main_spawn_pthreads.stderr b/tests/genmc/pass/simple/miri_main_spawn_pthreads.stderr similarity index 100% rename from tests/genmc/pass/simple/simple_miri_main_spawn_pthreads.stderr rename to tests/genmc/pass/simple/miri_main_spawn_pthreads.stderr From 9d7e9648ffddff2e39b7f8c64267d403ecd0dd42 Mon Sep 17 00:00:00 2001 From: Patrick-6 Date: Tue, 29 Jul 2025 16:52:48 +0200 Subject: [PATCH 04/41] Clean up comments. --- genmc-sys/src_cpp/MiriInterface.cpp | 35 +++++++++++++---------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/genmc-sys/src_cpp/MiriInterface.cpp b/genmc-sys/src_cpp/MiriInterface.cpp index 0e13cef514..8e2f982c6f 100644 --- a/genmc-sys/src_cpp/MiriInterface.cpp +++ b/genmc-sys/src_cpp/MiriInterface.cpp @@ -60,16 +60,15 @@ auto MiriGenMCShim::createHandle(const GenmcParams &config, bool estimation_mode // Miri needs all threads to be replayed, even fully completed ones. conf->replayCompletedThreads = true; - // TODO GENMC: make sure this doesn't affect any tests, and maybe make it changeable from - // Miri: + // FIXME(genmc): make sure this doesn't affect any tests, and maybe make it changeable from Miri: constexpr unsigned int DEFAULT_WARN_ON_GRAPH_SIZE = 16 * 1024; conf->warnOnGraphSize = DEFAULT_WARN_ON_GRAPH_SIZE; // We only support the RC11 memory model for Rust. conf->model = ModelType::RC11; - conf->randomScheduleSeed = - "42"; // TODO GENMC: only for random exploration/scheduling mode in GenMC + // FIXME(genmc): expose this setting to Miri + conf->randomScheduleSeed = "42"; conf->printRandomScheduleSeed = config.print_random_schedule_seed; if (config.quiet) { // logLevel = VerbosityLevel::Quiet; @@ -82,19 +81,19 @@ auto MiriGenMCShim::createHandle(const GenmcParams &config, bool estimation_mode logLevel = VerbosityLevel::Tip; } - // TODO GENMC (EXTRA): check if we can enable IPR: + // FIXME(genmc): check if we can enable IPR: conf->ipr = false; - // TODO GENMC (EXTRA): check if we can enable BAM: + // FIXME(genmc): check if we can enable BAM: conf->disableBAM = true; - // TODO GENMC (EXTRA): check if we can do instruction caching (probably not) + // FIXME(genmc): check if we can do instruction caching (probably not) conf->instructionCaching = false; - // TODO GENMC (EXTRA): check if we can enable Symmetry Reduction: + // FIXME(genmc): implement symmetry reduction. ERROR_ON(config.do_symmetry_reduction, "Symmetry reduction is currently unsupported in GenMC mode."); conf->symmetryReduction = config.do_symmetry_reduction; - // TODO GENMC: Should there be a way to change this option from Miri? + // FIXME(genmc): expose this setting to Miri (useful for testing Miri-GenMC). conf->schedulePolicy = SchedulePolicy::WF; conf->estimate = estimation_mode; @@ -236,7 +235,7 @@ void MiriGenMCShim::handleUserBlock(ThreadId thread_id) auto loc = SAddr(address); auto aSize = ASize(size); - auto type = AType::Unsigned; // TODO GENMC: get correct type from Miri + auto type = AType::Unsigned; // FIXME(genmc): get correct type from Miri(?) auto newLab = std::make_unique(pos, ord, loc, aSize, type); @@ -339,9 +338,9 @@ void MiriGenMCShim::handleUserBlock(ThreadId thread_id) auto pos = incPos(thread_id); - auto loc = SAddr(address); // TODO GENMC: called addr for write, loc for read? + auto loc = SAddr(address); auto aSize = ASize(size); - auto type = AType::Unsigned; // TODO GENMC: get from Miri + auto type = AType::Unsigned; // FIXME(genmc): get correct type from Miri(?) // TODO GENMC: u128 support auto val = value.toSVal(); @@ -388,14 +387,12 @@ auto MiriGenMCShim::handleMalloc(ThreadId thread_id, uint64_t size, uint64_t ali { auto pos = incPos(thread_id); - auto sd = StorageDuration::SD_Heap; // TODO GENMC: get from Miri - auto stype = StorageType::ST_Durable; // TODO GENMC - auto spc = AddressSpace::AS_User; // TODO GENMC - - auto deps = EventDeps(); // TODO GENMC: without this, constructor is ambiguous + // FIXME(genmc): get correct values from Miri + auto sd = StorageDuration::SD_Heap; + auto stype = StorageType::ST_Durable; + auto spc = AddressSpace::AS_User; - // TODO GENMC (types): size_t vs unsigned int - auto aLab = std::make_unique(pos, size, alignment, sd, stype, spc, deps); + auto aLab = std::make_unique(pos, size, alignment, sd, stype, spc, EventDeps()); SAddr retVal = GenMCDriver::handleMalloc(std::move(aLab)); From 1631c9d489200874c2a074c8270063f237976c80 Mon Sep 17 00:00:00 2001 From: Patrick-6 Date: Wed, 30 Jul 2025 10:50:36 +0200 Subject: [PATCH 05/41] Remove leak checks from tests where possible, fix leak check for blocked executions. --- src/diagnostics.rs | 10 ++++++++-- tests/genmc/pass/data-structures/ms_queue_dynamic.rs | 3 --- tests/genmc/pass/litmus/2+2W+2sc+scf/2w2w_2sc_scf.rs | 2 +- tests/genmc/pass/litmus/2+2W+3sc+rel1/2_2w_3sc_rel1.rs | 2 +- tests/genmc/pass/litmus/2+2W+4sc/2_2w_4sc.rs | 2 +- tests/genmc/pass/litmus/2+2W+scfs/2_2w_scfs.rs | 2 +- tests/genmc/pass/litmus/2+2w/2W2W.rs | 2 +- tests/genmc/pass/litmus/2CoWR/2cowr.rs | 2 +- tests/genmc/pass/litmus/CoRR/corr.rs | 2 +- tests/genmc/pass/litmus/CoRR0/corr0.rs | 2 +- tests/genmc/pass/litmus/CoRR2/corr2.rs | 2 +- tests/genmc/pass/litmus/CoRW/corw.rs | 2 +- tests/genmc/pass/litmus/CoWR/cowr.rs | 2 +- tests/genmc/pass/litmus/IRIW-acq-sc/IRIW-acq-sc.rs | 2 +- tests/genmc/pass/litmus/IRIWish/IRIWish.rs | 2 +- tests/genmc/pass/litmus/LB+incMPs/LB_incMPs.rs | 2 +- tests/genmc/pass/litmus/LB/LB.rs | 2 +- tests/genmc/pass/litmus/MP+incMPs/MP_incMPs.rs | 2 +- tests/genmc/pass/litmus/MP+rels+acqf/MP_rels_acqf.rs | 2 +- tests/genmc/pass/litmus/MP/MP.rs | 2 +- tests/genmc/pass/litmus/MPU+rels+acq/MPU_rels_acq.rs | 2 +- .../genmc/pass/litmus/MPU2+rels+acqf/MPU2_rels_acqf.rs | 2 +- tests/genmc/pass/litmus/SB+2sc+scf/SB_2sc_scf.rs | 2 +- tests/genmc/pass/litmus/SB/SB.rs | 2 +- tests/genmc/pass/litmus/Z6+acq/Z6_acq.rs | 2 +- tests/genmc/pass/litmus/Z6.U0/Z6_U0.rs | 2 +- tests/genmc/pass/litmus/assume-ctrl/assume-ctrl.rs | 2 +- tests/genmc/pass/litmus/atomicpo/atomicpo.rs | 2 +- tests/genmc/pass/litmus/casdep/casdep.rs | 2 +- tests/genmc/pass/litmus/ccr/ccr.rs | 2 +- tests/genmc/pass/litmus/cii/cii.rs | 2 +- tests/genmc/pass/litmus/cumul-release/cumul-release.rs | 2 +- tests/genmc/pass/litmus/default/default.rs | 2 +- tests/genmc/pass/litmus/detour/detour.rs | 2 +- .../genmc/pass/litmus/fr_w_w_w_reads/fr_w_w_w_reads.rs | 2 +- tests/genmc/pass/litmus/inc2w/inc2w.rs | 2 +- .../pass/litmus/inc_inc_RR_W_RR/inc_inc_RR_W_RR.rs | 2 +- tests/genmc/pass/litmus/riwi/riwi.rs | 2 +- tests/genmc/pass/litmus/viktor-relseq/viktor-relseq.rs | 2 +- tests/genmc/pass/simple/thread_locals.rs | 3 ++- 40 files changed, 47 insertions(+), 43 deletions(-) diff --git a/src/diagnostics.rs b/src/diagnostics.rs index e5ab282ff4..f07c564813 100644 --- a/src/diagnostics.rs +++ b/src/diagnostics.rs @@ -257,9 +257,15 @@ pub fn report_error<'tcx>( // This case should only happen in GenMC mode. let genmc_ctx = ecx.machine.data_race.as_genmc_ref().unwrap(); // Check whether the execution got blocked or the program exited. - if let Some(exit_status) = genmc_ctx.get_exit_status() { + if let Some((exit_code, leak_check)) = genmc_ctx.get_exit_status() { + let is_blocked_execution = + ecx.machine.threads.threads_ref().iter().any(|thread| { + thread.get_state().is_blocked_on(BlockReason::GenmcAssume) + }); + // We have an exit status (from `exit(x)` or main thread return). - return Some(exit_status); + // Skip leak checks if the execution was blocked. + return Some((exit_code, leak_check && !is_blocked_execution)); } // The program got blocked by GenMC without ever exiting, so we don't do any leak checks. return Some((0, false)); diff --git a/tests/genmc/pass/data-structures/ms_queue_dynamic.rs b/tests/genmc/pass/data-structures/ms_queue_dynamic.rs index bc52eba423..a2af4db269 100644 --- a/tests/genmc/pass/data-structures/ms_queue_dynamic.rs +++ b/tests/genmc/pass/data-structures/ms_queue_dynamic.rs @@ -1,8 +1,5 @@ //@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows -// TODO GENMC: maybe use `-Zmiri-genmc-symmetry-reduction`? -// TODO GENMC: investigate why `-Zmiri-ignore-leaks ` is required - #![no_main] #![allow(static_mut_refs)] #![allow(unused)] diff --git a/tests/genmc/pass/litmus/2+2W+2sc+scf/2w2w_2sc_scf.rs b/tests/genmc/pass/litmus/2+2W+2sc+scf/2w2w_2sc_scf.rs index eca9494b81..313d2d48de 100644 --- a/tests/genmc/pass/litmus/2+2W+2sc+scf/2w2w_2sc_scf.rs +++ b/tests/genmc/pass/litmus/2+2W+2sc+scf/2w2w_2sc_scf.rs @@ -1,4 +1,4 @@ -//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows //@revisions: order12 order21 #![no_main] diff --git a/tests/genmc/pass/litmus/2+2W+3sc+rel1/2_2w_3sc_rel1.rs b/tests/genmc/pass/litmus/2+2W+3sc+rel1/2_2w_3sc_rel1.rs index 941879fa70..2747e764ae 100644 --- a/tests/genmc/pass/litmus/2+2W+3sc+rel1/2_2w_3sc_rel1.rs +++ b/tests/genmc/pass/litmus/2+2W+3sc+rel1/2_2w_3sc_rel1.rs @@ -1,4 +1,4 @@ -//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows #![no_main] diff --git a/tests/genmc/pass/litmus/2+2W+4sc/2_2w_4sc.rs b/tests/genmc/pass/litmus/2+2W+4sc/2_2w_4sc.rs index f2c4b422df..024959a6ba 100644 --- a/tests/genmc/pass/litmus/2+2W+4sc/2_2w_4sc.rs +++ b/tests/genmc/pass/litmus/2+2W+4sc/2_2w_4sc.rs @@ -1,4 +1,4 @@ -//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows #![no_main] diff --git a/tests/genmc/pass/litmus/2+2W+scfs/2_2w_scfs.rs b/tests/genmc/pass/litmus/2+2W+scfs/2_2w_scfs.rs index b819038c18..98533fa0c5 100644 --- a/tests/genmc/pass/litmus/2+2W+scfs/2_2w_scfs.rs +++ b/tests/genmc/pass/litmus/2+2W+scfs/2_2w_scfs.rs @@ -1,4 +1,4 @@ -//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows #![no_main] diff --git a/tests/genmc/pass/litmus/2+2w/2W2W.rs b/tests/genmc/pass/litmus/2+2w/2W2W.rs index cd70ede2fd..6c72d6366c 100644 --- a/tests/genmc/pass/litmus/2+2w/2W2W.rs +++ b/tests/genmc/pass/litmus/2+2w/2W2W.rs @@ -1,4 +1,4 @@ -//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows //@revisions: order12 order21 #![no_main] diff --git a/tests/genmc/pass/litmus/2CoWR/2cowr.rs b/tests/genmc/pass/litmus/2CoWR/2cowr.rs index eabed7aaec..cb54cf386f 100644 --- a/tests/genmc/pass/litmus/2CoWR/2cowr.rs +++ b/tests/genmc/pass/litmus/2CoWR/2cowr.rs @@ -1,4 +1,4 @@ -//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows //@revisions: order1234 order2341 order3412 order4123 order4321 #![no_main] diff --git a/tests/genmc/pass/litmus/CoRR/corr.rs b/tests/genmc/pass/litmus/CoRR/corr.rs index ad8b75294e..99ca0c2646 100644 --- a/tests/genmc/pass/litmus/CoRR/corr.rs +++ b/tests/genmc/pass/litmus/CoRR/corr.rs @@ -1,4 +1,4 @@ -//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows //@revisions: order12 order21 #![no_main] diff --git a/tests/genmc/pass/litmus/CoRR0/corr0.rs b/tests/genmc/pass/litmus/CoRR0/corr0.rs index 2c802c8faa..cd5bb2e756 100644 --- a/tests/genmc/pass/litmus/CoRR0/corr0.rs +++ b/tests/genmc/pass/litmus/CoRR0/corr0.rs @@ -1,4 +1,4 @@ -//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows //@revisions: order123 order321 order312 order231 #![no_main] diff --git a/tests/genmc/pass/litmus/CoRR2/corr2.rs b/tests/genmc/pass/litmus/CoRR2/corr2.rs index 8f59836939..f64ba401fb 100644 --- a/tests/genmc/pass/litmus/CoRR2/corr2.rs +++ b/tests/genmc/pass/litmus/CoRR2/corr2.rs @@ -1,4 +1,4 @@ -//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows //@revisions: order1234 order4321 order4123 order3412 order2341 #![no_main] diff --git a/tests/genmc/pass/litmus/CoRW/corw.rs b/tests/genmc/pass/litmus/CoRW/corw.rs index 7c99b60340..ca2c97908e 100644 --- a/tests/genmc/pass/litmus/CoRW/corw.rs +++ b/tests/genmc/pass/litmus/CoRW/corw.rs @@ -1,4 +1,4 @@ -//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows //@revisions: order12 order21 #![no_main] diff --git a/tests/genmc/pass/litmus/CoWR/cowr.rs b/tests/genmc/pass/litmus/CoWR/cowr.rs index 881f933910..af81b9da8e 100644 --- a/tests/genmc/pass/litmus/CoWR/cowr.rs +++ b/tests/genmc/pass/litmus/CoWR/cowr.rs @@ -1,4 +1,4 @@ -//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows //@revisions: order12 order21 #![no_main] diff --git a/tests/genmc/pass/litmus/IRIW-acq-sc/IRIW-acq-sc.rs b/tests/genmc/pass/litmus/IRIW-acq-sc/IRIW-acq-sc.rs index e4a8763790..7658f0519f 100644 --- a/tests/genmc/pass/litmus/IRIW-acq-sc/IRIW-acq-sc.rs +++ b/tests/genmc/pass/litmus/IRIW-acq-sc/IRIW-acq-sc.rs @@ -1,4 +1,4 @@ -//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows #![no_main] diff --git a/tests/genmc/pass/litmus/IRIWish/IRIWish.rs b/tests/genmc/pass/litmus/IRIWish/IRIWish.rs index 03e0e4c1e8..eee41cbc55 100644 --- a/tests/genmc/pass/litmus/IRIWish/IRIWish.rs +++ b/tests/genmc/pass/litmus/IRIWish/IRIWish.rs @@ -1,4 +1,4 @@ -//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows #![no_main] diff --git a/tests/genmc/pass/litmus/LB+incMPs/LB_incMPs.rs b/tests/genmc/pass/litmus/LB+incMPs/LB_incMPs.rs index 4e63c25f92..8be7d959d0 100644 --- a/tests/genmc/pass/litmus/LB+incMPs/LB_incMPs.rs +++ b/tests/genmc/pass/litmus/LB+incMPs/LB_incMPs.rs @@ -1,4 +1,4 @@ -//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows #![no_main] diff --git a/tests/genmc/pass/litmus/LB/LB.rs b/tests/genmc/pass/litmus/LB/LB.rs index 10f08a9fda..0957644399 100644 --- a/tests/genmc/pass/litmus/LB/LB.rs +++ b/tests/genmc/pass/litmus/LB/LB.rs @@ -1,4 +1,4 @@ -//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows //@revisions: order12 order21 #![no_main] diff --git a/tests/genmc/pass/litmus/MP+incMPs/MP_incMPs.rs b/tests/genmc/pass/litmus/MP+incMPs/MP_incMPs.rs index 445bc17442..26fadd7b04 100644 --- a/tests/genmc/pass/litmus/MP+incMPs/MP_incMPs.rs +++ b/tests/genmc/pass/litmus/MP+incMPs/MP_incMPs.rs @@ -1,4 +1,4 @@ -//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows #![no_main] diff --git a/tests/genmc/pass/litmus/MP+rels+acqf/MP_rels_acqf.rs b/tests/genmc/pass/litmus/MP+rels+acqf/MP_rels_acqf.rs index 5b70b2c4c5..21d9e7cb29 100644 --- a/tests/genmc/pass/litmus/MP+rels+acqf/MP_rels_acqf.rs +++ b/tests/genmc/pass/litmus/MP+rels+acqf/MP_rels_acqf.rs @@ -1,4 +1,4 @@ -//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows // -Zmiri-disable-data-race-detector #![no_main] diff --git a/tests/genmc/pass/litmus/MP/MP.rs b/tests/genmc/pass/litmus/MP/MP.rs index 6157fa7013..1a9fb34cd0 100644 --- a/tests/genmc/pass/litmus/MP/MP.rs +++ b/tests/genmc/pass/litmus/MP/MP.rs @@ -1,4 +1,4 @@ -//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows //@revisions: order12 order21 #![no_main] diff --git a/tests/genmc/pass/litmus/MPU+rels+acq/MPU_rels_acq.rs b/tests/genmc/pass/litmus/MPU+rels+acq/MPU_rels_acq.rs index e8715c51ff..a36a6c4439 100644 --- a/tests/genmc/pass/litmus/MPU+rels+acq/MPU_rels_acq.rs +++ b/tests/genmc/pass/litmus/MPU+rels+acq/MPU_rels_acq.rs @@ -1,4 +1,4 @@ -//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows #![no_main] diff --git a/tests/genmc/pass/litmus/MPU2+rels+acqf/MPU2_rels_acqf.rs b/tests/genmc/pass/litmus/MPU2+rels+acqf/MPU2_rels_acqf.rs index 94b14de7cf..8bc2e3c6af 100644 --- a/tests/genmc/pass/litmus/MPU2+rels+acqf/MPU2_rels_acqf.rs +++ b/tests/genmc/pass/litmus/MPU2+rels+acqf/MPU2_rels_acqf.rs @@ -1,4 +1,4 @@ -//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows #![no_main] diff --git a/tests/genmc/pass/litmus/SB+2sc+scf/SB_2sc_scf.rs b/tests/genmc/pass/litmus/SB+2sc+scf/SB_2sc_scf.rs index f9855a85f5..c843206edb 100644 --- a/tests/genmc/pass/litmus/SB+2sc+scf/SB_2sc_scf.rs +++ b/tests/genmc/pass/litmus/SB+2sc+scf/SB_2sc_scf.rs @@ -1,4 +1,4 @@ -//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows #![no_main] diff --git a/tests/genmc/pass/litmus/SB/SB.rs b/tests/genmc/pass/litmus/SB/SB.rs index d5360faf14..4d609df507 100644 --- a/tests/genmc/pass/litmus/SB/SB.rs +++ b/tests/genmc/pass/litmus/SB/SB.rs @@ -1,4 +1,4 @@ -//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows //@revisions: order12 order21 #![no_main] diff --git a/tests/genmc/pass/litmus/Z6+acq/Z6_acq.rs b/tests/genmc/pass/litmus/Z6+acq/Z6_acq.rs index 255e856d89..a7e43686a4 100644 --- a/tests/genmc/pass/litmus/Z6+acq/Z6_acq.rs +++ b/tests/genmc/pass/litmus/Z6+acq/Z6_acq.rs @@ -1,4 +1,4 @@ -//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows //@revisions: order123 order321 #![no_main] diff --git a/tests/genmc/pass/litmus/Z6.U0/Z6_U0.rs b/tests/genmc/pass/litmus/Z6.U0/Z6_U0.rs index ad65049b3b..ec4ff0f5be 100644 --- a/tests/genmc/pass/litmus/Z6.U0/Z6_U0.rs +++ b/tests/genmc/pass/litmus/Z6.U0/Z6_U0.rs @@ -1,4 +1,4 @@ -//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows #![no_main] diff --git a/tests/genmc/pass/litmus/assume-ctrl/assume-ctrl.rs b/tests/genmc/pass/litmus/assume-ctrl/assume-ctrl.rs index 7d6e0680db..a5af1db572 100644 --- a/tests/genmc/pass/litmus/assume-ctrl/assume-ctrl.rs +++ b/tests/genmc/pass/litmus/assume-ctrl/assume-ctrl.rs @@ -1,4 +1,4 @@ -//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows #![no_main] diff --git a/tests/genmc/pass/litmus/atomicpo/atomicpo.rs b/tests/genmc/pass/litmus/atomicpo/atomicpo.rs index 37c5b036c8..8621e80f12 100644 --- a/tests/genmc/pass/litmus/atomicpo/atomicpo.rs +++ b/tests/genmc/pass/litmus/atomicpo/atomicpo.rs @@ -1,4 +1,4 @@ -//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows #![no_main] diff --git a/tests/genmc/pass/litmus/casdep/casdep.rs b/tests/genmc/pass/litmus/casdep/casdep.rs index e448e0842e..e09961e509 100644 --- a/tests/genmc/pass/litmus/casdep/casdep.rs +++ b/tests/genmc/pass/litmus/casdep/casdep.rs @@ -1,4 +1,4 @@ -//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows #![no_main] diff --git a/tests/genmc/pass/litmus/ccr/ccr.rs b/tests/genmc/pass/litmus/ccr/ccr.rs index 181f7d0ef1..4f86311658 100644 --- a/tests/genmc/pass/litmus/ccr/ccr.rs +++ b/tests/genmc/pass/litmus/ccr/ccr.rs @@ -1,4 +1,4 @@ -//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows #![no_main] diff --git a/tests/genmc/pass/litmus/cii/cii.rs b/tests/genmc/pass/litmus/cii/cii.rs index 77548fc8fd..dda6caff5e 100644 --- a/tests/genmc/pass/litmus/cii/cii.rs +++ b/tests/genmc/pass/litmus/cii/cii.rs @@ -1,4 +1,4 @@ -//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows #![no_main] diff --git a/tests/genmc/pass/litmus/cumul-release/cumul-release.rs b/tests/genmc/pass/litmus/cumul-release/cumul-release.rs index 0abbee98b4..3f2aaec9dd 100644 --- a/tests/genmc/pass/litmus/cumul-release/cumul-release.rs +++ b/tests/genmc/pass/litmus/cumul-release/cumul-release.rs @@ -1,4 +1,4 @@ -//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows #![no_main] diff --git a/tests/genmc/pass/litmus/default/default.rs b/tests/genmc/pass/litmus/default/default.rs index 6bd9e42a76..d1602f6338 100644 --- a/tests/genmc/pass/litmus/default/default.rs +++ b/tests/genmc/pass/litmus/default/default.rs @@ -1,4 +1,4 @@ -//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows //@revisions: order123 order321 order312 order231 #![no_main] diff --git a/tests/genmc/pass/litmus/detour/detour.rs b/tests/genmc/pass/litmus/detour/detour.rs index d721c2edc4..e1a2220841 100644 --- a/tests/genmc/pass/litmus/detour/detour.rs +++ b/tests/genmc/pass/litmus/detour/detour.rs @@ -1,4 +1,4 @@ -//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows #![no_main] diff --git a/tests/genmc/pass/litmus/fr_w_w_w_reads/fr_w_w_w_reads.rs b/tests/genmc/pass/litmus/fr_w_w_w_reads/fr_w_w_w_reads.rs index 458f8df1cd..f318eb6479 100644 --- a/tests/genmc/pass/litmus/fr_w_w_w_reads/fr_w_w_w_reads.rs +++ b/tests/genmc/pass/litmus/fr_w_w_w_reads/fr_w_w_w_reads.rs @@ -1,4 +1,4 @@ -//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows #![no_main] diff --git a/tests/genmc/pass/litmus/inc2w/inc2w.rs b/tests/genmc/pass/litmus/inc2w/inc2w.rs index 785137efd4..bb4f5c45f3 100644 --- a/tests/genmc/pass/litmus/inc2w/inc2w.rs +++ b/tests/genmc/pass/litmus/inc2w/inc2w.rs @@ -1,4 +1,4 @@ -//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows #![no_main] diff --git a/tests/genmc/pass/litmus/inc_inc_RR_W_RR/inc_inc_RR_W_RR.rs b/tests/genmc/pass/litmus/inc_inc_RR_W_RR/inc_inc_RR_W_RR.rs index ab1a70123e..fcc964d211 100644 --- a/tests/genmc/pass/litmus/inc_inc_RR_W_RR/inc_inc_RR_W_RR.rs +++ b/tests/genmc/pass/litmus/inc_inc_RR_W_RR/inc_inc_RR_W_RR.rs @@ -1,4 +1,4 @@ -//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows #![no_main] diff --git a/tests/genmc/pass/litmus/riwi/riwi.rs b/tests/genmc/pass/litmus/riwi/riwi.rs index 1a15846836..51de24d2ca 100644 --- a/tests/genmc/pass/litmus/riwi/riwi.rs +++ b/tests/genmc/pass/litmus/riwi/riwi.rs @@ -1,4 +1,4 @@ -//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows #![no_main] diff --git a/tests/genmc/pass/litmus/viktor-relseq/viktor-relseq.rs b/tests/genmc/pass/litmus/viktor-relseq/viktor-relseq.rs index 243849113c..d576c6894c 100644 --- a/tests/genmc/pass/litmus/viktor-relseq/viktor-relseq.rs +++ b/tests/genmc/pass/litmus/viktor-relseq/viktor-relseq.rs @@ -1,4 +1,4 @@ -//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows #![no_main] diff --git a/tests/genmc/pass/simple/thread_locals.rs b/tests/genmc/pass/simple/thread_locals.rs index f12f6bbad6..9639d44cf5 100644 --- a/tests/genmc/pass/simple/thread_locals.rs +++ b/tests/genmc/pass/simple/thread_locals.rs @@ -5,6 +5,7 @@ #[path = "../../../utils-dep/mod.rs"] mod utils_dep; +use std::alloc::{Layout, alloc}; use std::cell::Cell; use std::ffi::c_void; use std::sync::atomic::{AtomicPtr, Ordering}; @@ -25,7 +26,7 @@ fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { } pub unsafe fn malloc() -> *mut u64 { - Box::into_raw(Box::::new_uninit()) as *mut u64 + alloc(Layout::new::()) as *mut u64 } extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { From d9a66299f5e1c9aa75804a0f863a403fa433e569 Mon Sep 17 00:00:00 2001 From: Patrick-6 Date: Wed, 30 Jul 2025 14:00:00 +0200 Subject: [PATCH 06/41] Clean up exit handling. --- genmc-sys/src/lib.rs | 1 + genmc-sys/src_cpp/MiriInterface.cpp | 7 +++++++ genmc-sys/src_cpp/MiriInterface.hpp | 1 + src/concurrency/genmc/mod.rs | 20 +++++++++++++++----- src/eval.rs | 2 +- src/shims/foreign_items.rs | 10 +++++++--- 6 files changed, 32 insertions(+), 9 deletions(-) diff --git a/genmc-sys/src/lib.rs b/genmc-sys/src/lib.rs index 3df5f62a15..8c6a28a6be 100644 --- a/genmc-sys/src/lib.rs +++ b/genmc-sys/src/lib.rs @@ -266,6 +266,7 @@ mod ffi { fn handleThreadCreate(self: Pin<&mut MiriGenMCShim>, thread_id: i32, parent_id: i32); fn handleThreadJoin(self: Pin<&mut MiriGenMCShim>, thread_id: i32, child_id: i32); fn handleThreadFinish(self: Pin<&mut MiriGenMCShim>, thread_id: i32, ret_val: u64); + fn handleThreadKill(self: Pin<&mut MiriGenMCShim>, thread_id: i32); /**** Blocking instructions ****/ fn handleUserBlock(self: Pin<&mut MiriGenMCShim>, thread_id: i32); diff --git a/genmc-sys/src_cpp/MiriInterface.cpp b/genmc-sys/src_cpp/MiriInterface.cpp index 8e2f982c6f..791ee4465a 100644 --- a/genmc-sys/src_cpp/MiriInterface.cpp +++ b/genmc-sys/src_cpp/MiriInterface.cpp @@ -215,6 +215,13 @@ void MiriGenMCShim::handleThreadFinish(ThreadId thread_id, uint64_t ret_val) GenMCDriver::handleThreadFinish(std::move(eLab)); } +void MiriGenMCShim::handleThreadKill(ThreadId thread_id) { + auto pos = incPos(thread_id); + auto kLab = std::make_unique(pos); + + GenMCDriver::handleThreadKill(std::move(kLab)); +} + /**** Blocking instructions ****/ void MiriGenMCShim::handleUserBlock(ThreadId thread_id) diff --git a/genmc-sys/src_cpp/MiriInterface.hpp b/genmc-sys/src_cpp/MiriInterface.hpp index 74e07c78ee..1927cc74d1 100644 --- a/genmc-sys/src_cpp/MiriInterface.hpp +++ b/genmc-sys/src_cpp/MiriInterface.hpp @@ -96,6 +96,7 @@ struct MiriGenMCShim : private GenMCDriver { void handleThreadCreate(ThreadId thread_id, ThreadId parent_id); void handleThreadJoin(ThreadId thread_id, ThreadId child_id); void handleThreadFinish(ThreadId thread_id, uint64_t ret_val); + void handleThreadKill(ThreadId thread_id); /**** Blocking instructions ****/ diff --git a/src/concurrency/genmc/mod.rs b/src/concurrency/genmc/mod.rs index 70d0200556..f92e1c8176 100644 --- a/src/concurrency/genmc/mod.rs +++ b/src/concurrency/genmc/mod.rs @@ -676,11 +676,11 @@ impl GenmcCtx { ); let mut mc = self.handle.borrow_mut(); - let pinned_mc = mc.as_mut(); - pinned_mc.handleThreadFinish(genmc_tid.0, ret_val); + mc.as_mut().handleThreadFinish(genmc_tid.0, ret_val); } - /// Handle a call to `libc::exit`. Returns `true` if the program should continue executing (in a different thread). + /// Handle a call to `libc::exit` or the exit of the main thread. + /// Unless an error is returned, the program should continue executing (in a different thread, chosen by the next scheduling call). pub(crate) fn handle_exit<'tcx>( &self, thread: ThreadId, @@ -690,9 +690,19 @@ impl GenmcCtx { // Calling `libc::exit` doesn't do cleanup, so we skip the leak check in that case. let leak_check = !is_exit_call; self.set_exit_status(exit_code, leak_check); + + // FIXME(genmc): Add a flag to continue exploration even when the program exits with a non-zero exit code. + if exit_code != 0 { + info!("GenMC: 'exit' called with non-zero argument, aborting execution."); + throw_machine_stop!(TerminationInfo::GenmcFinishedExecution); + } + if is_exit_call { - // FIXME(genmc): handle exit call (run other threads to completion) - todo!("Inform GenMC about killed thread {thread:?}."); + let thread_infos = self.thread_infos.borrow(); + let genmc_tid = thread_infos.get_info(thread).genmc_tid; + + let mut mc = self.handle.borrow_mut(); + mc.as_mut().handleThreadKill(genmc_tid.0); } interp_ok(()) } diff --git a/src/eval.rs b/src/eval.rs index d9c46f40e8..c4f9fcbb4f 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -306,7 +306,7 @@ impl<'tcx> MainThreadState<'tcx> { // In GenMC mode, we let GenMC decide what happens on main thread exit. if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() { // If there's no error, execution will continue (on another thread). - genmc_ctx.handle_exit(ThreadId::MAIN_THREAD, exit_code, false)?; + genmc_ctx.handle_exit(ThreadId::MAIN_THREAD, exit_code, /* is_exit_call */ false)?; } else { // Stop interpreter loop. throw_machine_stop!(TerminationInfo::Exit { diff --git a/src/shims/foreign_items.rs b/src/shims/foreign_items.rs index 578f66fb3d..2883541920 100644 --- a/src/shims/foreign_items.rs +++ b/src/shims/foreign_items.rs @@ -461,9 +461,13 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { let [code] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let code = this.read_scalar(code)?.to_i32()?; if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() { - // If there is no error, execution will continue (on another thread). - genmc_ctx.handle_exit(this.machine.threads.active_thread(), code, true)?; - return interp_ok(EmulateItemResult::AlreadyJumped); + // If there is no error, execution should continue (on a different thread). + genmc_ctx.handle_exit( + this.machine.threads.active_thread(), + code, + /* is_exit_call */ true, + )?; + todo!(); // FIXME(genmc): Add a way to return here that is allowed to not do progress (can't use existing EmulateItemResult variants). } throw_machine_stop!(TerminationInfo::Exit { code, leak_check: false }); } From c968c94de9e5c0f8246cfa85d8676992c9d40958 Mon Sep 17 00:00:00 2001 From: Patrick-6 Date: Tue, 29 Jul 2025 20:48:10 +0200 Subject: [PATCH 07/41] Remove unused VerificationError enum. --- genmc-sys/src/lib.rs | 36 ------------------------------------ 1 file changed, 36 deletions(-) diff --git a/genmc-sys/src/lib.rs b/genmc-sys/src/lib.rs index 8c6a28a6be..4dfabaefe0 100644 --- a/genmc-sys/src/lib.rs +++ b/genmc-sys/src/lib.rs @@ -143,41 +143,6 @@ mod ffi { isCoMaxWrite: bool, } - #[must_use] - #[derive(Debug)] - enum VerificationError { - VE_NonErrorBegin, - VE_OK, - VE_WWRace, - VE_UnfreedMemory, - VE_NonErrorLast, - - VE_Safety, - VE_Recovery, - VE_Liveness, - VE_RaceNotAtomic, - VE_RaceFreeMalloc, - VE_FreeNonMalloc, - VE_DoubleFree, - VE_Allocation, - - VE_InvalidAccessBegin, - VE_UninitializedMem, - VE_AccessNonMalloc, - VE_AccessFreed, - VE_InvalidAccessEnd, - - VE_InvalidCreate, - VE_InvalidJoin, - VE_InvalidUnlock, - VE_InvalidBInit, - VE_BarrierWellFormedness, - VE_Annotation, - VE_MixedSize, - VE_LinearizabilityError, - VE_SystemError, - } - /**** /\ Result & Error types /\ ****/ unsafe extern "C++" { @@ -196,7 +161,6 @@ mod ffi { type ReadModifyWriteResult; type CompareExchangeResult; type MutexLockResult; - type VerificationError; type GenmcScalar; From 24e42d0a14d25c6adff919ac17b05ee52453d88d Mon Sep 17 00:00:00 2001 From: Patrick-6 Date: Wed, 30 Jul 2025 14:27:49 +0200 Subject: [PATCH 08/41] Simplify GenMC test includes. --- .../_disabled/fail/weak_memory/weak_uninit.rs | 6 +- tests/genmc/fail/loom/buggy-inc/buggy_inc.rs | 6 +- .../loom/store-buffering/store_buffering.rs | 6 +- tests/genmc/fail/simple/2w2w_weak.rs | 6 +- .../basics/libc-exit/libc_exit.rs.disabled | 59 +++++++++++++++++++ tests/genmc/pass/basics/mutex/mutex_simple.rs | 6 +- .../pass/litmus/2+2W+2sc+scf/2w2w_2sc_scf.rs | 6 +- .../litmus/2+2W+3sc+rel1/2_2w_3sc_rel1.rs | 6 +- tests/genmc/pass/litmus/2+2W+4sc/2_2w_4sc.rs | 6 +- .../genmc/pass/litmus/2+2W+scfs/2_2w_scfs.rs | 6 +- tests/genmc/pass/litmus/2+2w/2W2W.rs | 6 +- tests/genmc/pass/litmus/2CoWR/2cowr.rs | 6 +- tests/genmc/pass/litmus/CoRR/corr.rs | 6 +- tests/genmc/pass/litmus/CoRR0/corr0.rs | 6 +- tests/genmc/pass/litmus/CoRR1/corr1.rs | 6 +- tests/genmc/pass/litmus/CoRR2/corr2.rs | 6 +- tests/genmc/pass/litmus/CoRW/corw.rs | 6 +- tests/genmc/pass/litmus/CoWR/cowr.rs | 6 +- .../pass/litmus/IRIW-acq-sc/IRIW-acq-sc.rs | 6 +- tests/genmc/pass/litmus/IRIWish/IRIWish.rs | 6 +- .../genmc/pass/litmus/LB+incMPs/LB_incMPs.rs | 6 +- tests/genmc/pass/litmus/LB/LB.rs | 6 +- .../genmc/pass/litmus/MP+incMPs/MP_incMPs.rs | 6 +- .../pass/litmus/MP+rels+acqf/MP_rels_acqf.rs | 6 +- tests/genmc/pass/litmus/MP/MP.rs | 6 +- .../pass/litmus/MPU+rels+acq/MPU_rels_acq.rs | 6 +- .../litmus/MPU2+rels+acqf/MPU2_rels_acqf.rs | 6 +- .../pass/litmus/SB+2sc+scf/SB_2sc_scf.rs | 6 +- tests/genmc/pass/litmus/SB/SB.rs | 6 +- tests/genmc/pass/litmus/Z6+acq/Z6_acq.rs | 6 +- tests/genmc/pass/litmus/Z6.U0/Z6_U0.rs | 6 +- .../pass/litmus/assume-ctrl/assume-ctrl.rs | 10 +++- tests/genmc/pass/litmus/atomicpo/atomicpo.rs | 6 +- tests/genmc/pass/litmus/casdep/casdep.rs | 6 +- tests/genmc/pass/litmus/ccr/ccr.rs | 6 +- tests/genmc/pass/litmus/cii/cii.rs | 6 +- .../litmus/cumul-release/cumul-release.rs | 6 +- tests/genmc/pass/litmus/default/default.rs | 6 +- tests/genmc/pass/litmus/detour/detour.rs | 6 +- .../litmus/fr_w_w_w_reads/fr_w_w_w_reads.rs | 6 +- tests/genmc/pass/litmus/inc2w/inc2w.rs | 6 +- .../litmus/inc_inc_RR_W_RR/inc_inc_RR_W_RR.rs | 6 +- tests/genmc/pass/litmus/riwi/riwi.rs | 6 +- .../litmus/viktor-relseq/viktor-relseq.rs | 6 +- .../loom/load-buffering/load_buffering.rs | 6 +- tests/genmc/pass/simple/thread_locals.rs | 6 +- tests/utils-dep/miri_extern.rs | 2 - tests/utils-dep/mod.rs | 8 --- tests/{utils-dep => utils}/genmc.rs | 2 + 49 files changed, 200 insertions(+), 145 deletions(-) create mode 100644 tests/genmc/pass/basics/libc-exit/libc_exit.rs.disabled delete mode 100644 tests/utils-dep/miri_extern.rs delete mode 100644 tests/utils-dep/mod.rs rename tests/{utils-dep => utils}/genmc.rs (98%) diff --git a/tests/genmc/_disabled/fail/weak_memory/weak_uninit.rs b/tests/genmc/_disabled/fail/weak_memory/weak_uninit.rs index b72b0a169c..1c12bbde6e 100644 --- a/tests/genmc/_disabled/fail/weak_memory/weak_uninit.rs +++ b/tests/genmc/_disabled/fail/weak_memory/weak_uninit.rs @@ -11,14 +11,14 @@ #![no_main] -#[path = "../../../utils-dep/mod.rs"] -mod utils_dep; +#[path = "../../../utils/genmc.rs"] +mod genmc; use std::ffi::c_void; use std::mem::MaybeUninit; use std::sync::atomic::*; -use crate::utils_dep::*; +use crate::genmc::*; #[allow(dead_code)] #[derive(Copy, Clone)] diff --git a/tests/genmc/fail/loom/buggy-inc/buggy_inc.rs b/tests/genmc/fail/loom/buggy-inc/buggy_inc.rs index fefefa6b39..8ed18412b7 100644 --- a/tests/genmc/fail/loom/buggy-inc/buggy_inc.rs +++ b/tests/genmc/fail/loom/buggy-inc/buggy_inc.rs @@ -9,14 +9,14 @@ #![no_main] #[cfg(not(any(non_genmc_std, genmc_std)))] -#[path = "../../../../utils-dep/mod.rs"] -mod utils_dep; +#[path = "../../../../utils/genmc.rs"] +mod genmc; use std::ffi::c_void; use std::sync::atomic::AtomicUsize; use std::sync::atomic::Ordering::{Acquire, Relaxed, Release}; -use crate::utils_dep::genmc::{create_pthreads_no_params, join_pthreads}; +use crate::genmc::{create_pthreads_no_params, join_pthreads}; struct BuggyInc { num: AtomicUsize, diff --git a/tests/genmc/fail/loom/store-buffering/store_buffering.rs b/tests/genmc/fail/loom/store-buffering/store_buffering.rs index 14ecf6b308..174ac8e080 100644 --- a/tests/genmc/fail/loom/store-buffering/store_buffering.rs +++ b/tests/genmc/fail/loom/store-buffering/store_buffering.rs @@ -10,8 +10,8 @@ #![no_main] #[cfg(not(any(non_genmc_std, genmc_std)))] -#[path = "../../../../utils-dep/mod.rs"] -mod utils_dep; +#[path = "../../../../utils/genmc.rs"] +mod genmc; use std::sync::atomic::AtomicUsize; use std::sync::atomic::Ordering::{Relaxed, SeqCst}; @@ -51,7 +51,7 @@ fn test() -> (usize, usize) { fn test() -> (usize, usize) { use std::ffi::c_void; - use crate::utils_dep::genmc::{join_pthread, spawn_pthread}; + use crate::genmc::{join_pthread, spawn_pthread}; extern "C" fn thread_func(value: *mut c_void) -> *mut c_void { let a_ptr = value as *mut usize; diff --git a/tests/genmc/fail/simple/2w2w_weak.rs b/tests/genmc/fail/simple/2w2w_weak.rs index a8b94aa584..f98ae48ccf 100644 --- a/tests/genmc/fail/simple/2w2w_weak.rs +++ b/tests/genmc/fail/simple/2w2w_weak.rs @@ -7,13 +7,13 @@ #![no_main] -#[path = "../../../utils-dep/mod.rs"] -mod utils_dep; +#[path = "../../../utils/genmc.rs"] +mod genmc; use std::ffi::c_void; use std::sync::atomic::{AtomicU64, Ordering}; -use crate::utils_dep::genmc::{join_pthread, spawn_pthread}; +use crate::genmc::{join_pthread, spawn_pthread}; static X: AtomicU64 = AtomicU64::new(0); static Y: AtomicU64 = AtomicU64::new(0); diff --git a/tests/genmc/pass/basics/libc-exit/libc_exit.rs.disabled b/tests/genmc/pass/basics/libc-exit/libc_exit.rs.disabled new file mode 100644 index 0000000000..7493d16a20 --- /dev/null +++ b/tests/genmc/pass/basics/libc-exit/libc_exit.rs.disabled @@ -0,0 +1,59 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@revisions: order0123 order3012 order2301 order1230 order3210 order1032 + +#![no_main] + +// Copied from `tests/genmc/pass/litmus/inc2w/inc2w.rs` + +#[path = "../../../../utils/genmc.rs"] +mod genmc; + +use std::ffi::c_void; +use std::ptr::null_mut; +use std::sync::atomic::{AtomicU64, Ordering}; + +use crate::genmc::*; + +static X: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + let thread_order = if cfg!(order0123) { + [thread_0_exit, thread_1, thread_2, thread_3] + } else if cfg!(order3012) { + [thread_3, thread_0_exit, thread_1, thread_2] + } else if cfg!(order2301) { + [thread_2, thread_3, thread_0_exit, thread_1] + } else if cfg!(order1230) { + [thread_1, thread_2, thread_3, thread_0_exit] + } else if cfg!(order3210) { + [thread_3, thread_2, thread_1, thread_0_exit] + } else if cfg!(order1032) { + [thread_1, thread_0_exit, thread_3, thread_2] + } else { + unimplemented!(); + }; + + let _ids = unsafe { create_pthreads_no_params(thread_order) }; + + 0 +} + +pub extern "C" fn thread_0_exit(_value: *mut c_void) -> *mut c_void { + unsafe { libc::exit(0) } +} + +pub extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { + X.fetch_add(1, Ordering::Relaxed); + null_mut() +} + +pub extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { + X.store(4, Ordering::Release); + null_mut() +} + +pub extern "C" fn thread_3(_value: *mut c_void) -> *mut c_void { + X.fetch_add(2, Ordering::Relaxed); + null_mut() +} diff --git a/tests/genmc/pass/basics/mutex/mutex_simple.rs b/tests/genmc/pass/basics/mutex/mutex_simple.rs index 63349b03be..7decc23b9b 100644 --- a/tests/genmc/pass/basics/mutex/mutex_simple.rs +++ b/tests/genmc/pass/basics/mutex/mutex_simple.rs @@ -4,13 +4,13 @@ #![no_main] #![feature(abort_unwind)] -#[path = "../../../../utils-dep/mod.rs"] -mod utils_dep; +#[path = "../../../../utils/genmc.rs"] +mod genmc; use std::ffi::c_void; use std::sync::Mutex; -use crate::utils_dep::*; +use crate::genmc::*; #[cfg(not(any(order12reps2, order21reps2)))] const REPS: u64 = 1; diff --git a/tests/genmc/pass/litmus/2+2W+2sc+scf/2w2w_2sc_scf.rs b/tests/genmc/pass/litmus/2+2W+2sc+scf/2w2w_2sc_scf.rs index 313d2d48de..b480ba415b 100644 --- a/tests/genmc/pass/litmus/2+2W+2sc+scf/2w2w_2sc_scf.rs +++ b/tests/genmc/pass/litmus/2+2W+2sc+scf/2w2w_2sc_scf.rs @@ -3,8 +3,8 @@ #![no_main] -#[path = "../../../../utils-dep/mod.rs"] -mod utils_dep; +#[path = "../../../../utils/genmc.rs"] +mod genmc; use std::ffi::c_void; use std::sync::atomic::{AtomicU64, Ordering}; @@ -12,7 +12,7 @@ use std::sync::atomic::{AtomicU64, Ordering}; static X: AtomicU64 = AtomicU64::new(0); static Y: AtomicU64 = AtomicU64::new(0); -use crate::utils_dep::*; +use crate::genmc::*; #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { diff --git a/tests/genmc/pass/litmus/2+2W+3sc+rel1/2_2w_3sc_rel1.rs b/tests/genmc/pass/litmus/2+2W+3sc+rel1/2_2w_3sc_rel1.rs index 2747e764ae..6cc9d47f3a 100644 --- a/tests/genmc/pass/litmus/2+2W+3sc+rel1/2_2w_3sc_rel1.rs +++ b/tests/genmc/pass/litmus/2+2W+3sc+rel1/2_2w_3sc_rel1.rs @@ -2,8 +2,8 @@ #![no_main] -#[path = "../../../../utils-dep/mod.rs"] -mod utils_dep; +#[path = "../../../../utils/genmc.rs"] +mod genmc; use std::ffi::c_void; use std::sync::atomic::{AtomicU64, Ordering}; @@ -11,7 +11,7 @@ use std::sync::atomic::{AtomicU64, Ordering}; static X: AtomicU64 = AtomicU64::new(0); static Y: AtomicU64 = AtomicU64::new(0); -use crate::utils_dep::*; +use crate::genmc::*; #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { diff --git a/tests/genmc/pass/litmus/2+2W+4sc/2_2w_4sc.rs b/tests/genmc/pass/litmus/2+2W+4sc/2_2w_4sc.rs index 024959a6ba..13a08652c2 100644 --- a/tests/genmc/pass/litmus/2+2W+4sc/2_2w_4sc.rs +++ b/tests/genmc/pass/litmus/2+2W+4sc/2_2w_4sc.rs @@ -2,8 +2,8 @@ #![no_main] -#[path = "../../../../utils-dep/mod.rs"] -mod utils_dep; +#[path = "../../../../utils/genmc.rs"] +mod genmc; use std::ffi::c_void; use std::sync::atomic::{AtomicU64, Ordering}; @@ -11,7 +11,7 @@ use std::sync::atomic::{AtomicU64, Ordering}; static X: AtomicU64 = AtomicU64::new(0); static Y: AtomicU64 = AtomicU64::new(0); -use crate::utils_dep::*; +use crate::genmc::*; #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { diff --git a/tests/genmc/pass/litmus/2+2W+scfs/2_2w_scfs.rs b/tests/genmc/pass/litmus/2+2W+scfs/2_2w_scfs.rs index 98533fa0c5..dd4dabd823 100644 --- a/tests/genmc/pass/litmus/2+2W+scfs/2_2w_scfs.rs +++ b/tests/genmc/pass/litmus/2+2W+scfs/2_2w_scfs.rs @@ -2,8 +2,8 @@ #![no_main] -#[path = "../../../../utils-dep/mod.rs"] -mod utils_dep; +#[path = "../../../../utils/genmc.rs"] +mod genmc; use std::ffi::c_void; use std::sync::atomic::{AtomicU64, Ordering}; @@ -11,7 +11,7 @@ use std::sync::atomic::{AtomicU64, Ordering}; static X: AtomicU64 = AtomicU64::new(0); static Y: AtomicU64 = AtomicU64::new(0); -use crate::utils_dep::*; +use crate::genmc::*; #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { diff --git a/tests/genmc/pass/litmus/2+2w/2W2W.rs b/tests/genmc/pass/litmus/2+2w/2W2W.rs index 6c72d6366c..f52916d332 100644 --- a/tests/genmc/pass/litmus/2+2w/2W2W.rs +++ b/tests/genmc/pass/litmus/2+2w/2W2W.rs @@ -3,8 +3,8 @@ #![no_main] -#[path = "../../../../utils-dep/mod.rs"] -mod utils_dep; +#[path = "../../../../utils/genmc.rs"] +mod genmc; use std::ffi::c_void; use std::sync::atomic::{AtomicU64, Ordering}; @@ -12,7 +12,7 @@ use std::sync::atomic::{AtomicU64, Ordering}; static X: AtomicU64 = AtomicU64::new(0); static Y: AtomicU64 = AtomicU64::new(0); -use crate::utils_dep::*; +use crate::genmc::*; #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { diff --git a/tests/genmc/pass/litmus/2CoWR/2cowr.rs b/tests/genmc/pass/litmus/2CoWR/2cowr.rs index cb54cf386f..c5604fd57c 100644 --- a/tests/genmc/pass/litmus/2CoWR/2cowr.rs +++ b/tests/genmc/pass/litmus/2CoWR/2cowr.rs @@ -3,14 +3,14 @@ #![no_main] -#[path = "../../../../utils-dep/mod.rs"] -mod utils_dep; +#[path = "../../../../utils/genmc.rs"] +mod genmc; use std::ffi::c_void; use std::ptr::null_mut; use std::sync::atomic::{AtomicU64, Ordering}; -use crate::utils_dep::*; +use crate::genmc::*; static X: AtomicU64 = AtomicU64::new(0); static Y: AtomicU64 = AtomicU64::new(0); diff --git a/tests/genmc/pass/litmus/CoRR/corr.rs b/tests/genmc/pass/litmus/CoRR/corr.rs index 99ca0c2646..f630fadf56 100644 --- a/tests/genmc/pass/litmus/CoRR/corr.rs +++ b/tests/genmc/pass/litmus/CoRR/corr.rs @@ -3,15 +3,15 @@ #![no_main] -#[path = "../../../../utils-dep/mod.rs"] -mod utils_dep; +#[path = "../../../../utils/genmc.rs"] +mod genmc; use std::ffi::c_void; use std::sync::atomic::{AtomicU64, Ordering}; static X: AtomicU64 = AtomicU64::new(0); -use crate::utils_dep::*; +use crate::genmc::*; #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { diff --git a/tests/genmc/pass/litmus/CoRR0/corr0.rs b/tests/genmc/pass/litmus/CoRR0/corr0.rs index cd5bb2e756..4d47175560 100644 --- a/tests/genmc/pass/litmus/CoRR0/corr0.rs +++ b/tests/genmc/pass/litmus/CoRR0/corr0.rs @@ -3,15 +3,15 @@ #![no_main] -#[path = "../../../../utils-dep/mod.rs"] -mod utils_dep; +#[path = "../../../../utils/genmc.rs"] +mod genmc; use std::ffi::c_void; use std::sync::atomic::{AtomicU64, Ordering}; static X: AtomicU64 = AtomicU64::new(0); -use crate::utils_dep::*; +use crate::genmc::*; #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { diff --git a/tests/genmc/pass/litmus/CoRR1/corr1.rs b/tests/genmc/pass/litmus/CoRR1/corr1.rs index a31a9fd23a..58ef3c7691 100644 --- a/tests/genmc/pass/litmus/CoRR1/corr1.rs +++ b/tests/genmc/pass/litmus/CoRR1/corr1.rs @@ -3,15 +3,15 @@ #![no_main] -#[path = "../../../../utils-dep/mod.rs"] -mod utils_dep; +#[path = "../../../../utils/genmc.rs"] +mod genmc; use std::ffi::c_void; use std::sync::atomic::{AtomicU64, Ordering}; static X: AtomicU64 = AtomicU64::new(0); -use crate::utils_dep::*; +use crate::genmc::*; #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { diff --git a/tests/genmc/pass/litmus/CoRR2/corr2.rs b/tests/genmc/pass/litmus/CoRR2/corr2.rs index f64ba401fb..16ebc0fe28 100644 --- a/tests/genmc/pass/litmus/CoRR2/corr2.rs +++ b/tests/genmc/pass/litmus/CoRR2/corr2.rs @@ -3,15 +3,15 @@ #![no_main] -#[path = "../../../../utils-dep/mod.rs"] -mod utils_dep; +#[path = "../../../../utils/genmc.rs"] +mod genmc; use std::ffi::c_void; use std::sync::atomic::{AtomicU64, Ordering}; static X: AtomicU64 = AtomicU64::new(0); -use crate::utils_dep::*; +use crate::genmc::*; #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { diff --git a/tests/genmc/pass/litmus/CoRW/corw.rs b/tests/genmc/pass/litmus/CoRW/corw.rs index ca2c97908e..104a4c93dd 100644 --- a/tests/genmc/pass/litmus/CoRW/corw.rs +++ b/tests/genmc/pass/litmus/CoRW/corw.rs @@ -3,15 +3,15 @@ #![no_main] -#[path = "../../../../utils-dep/mod.rs"] -mod utils_dep; +#[path = "../../../../utils/genmc.rs"] +mod genmc; use std::ffi::c_void; use std::sync::atomic::{AtomicU64, Ordering}; static X: AtomicU64 = AtomicU64::new(0); -use crate::utils_dep::*; +use crate::genmc::*; #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { diff --git a/tests/genmc/pass/litmus/CoWR/cowr.rs b/tests/genmc/pass/litmus/CoWR/cowr.rs index af81b9da8e..f9f1f55c0a 100644 --- a/tests/genmc/pass/litmus/CoWR/cowr.rs +++ b/tests/genmc/pass/litmus/CoWR/cowr.rs @@ -3,15 +3,15 @@ #![no_main] -#[path = "../../../../utils-dep/mod.rs"] -mod utils_dep; +#[path = "../../../../utils/genmc.rs"] +mod genmc; use std::ffi::c_void; use std::sync::atomic::{AtomicU64, Ordering}; static X: AtomicU64 = AtomicU64::new(0); -use crate::utils_dep::*; +use crate::genmc::*; #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { diff --git a/tests/genmc/pass/litmus/IRIW-acq-sc/IRIW-acq-sc.rs b/tests/genmc/pass/litmus/IRIW-acq-sc/IRIW-acq-sc.rs index 7658f0519f..5172effdd2 100644 --- a/tests/genmc/pass/litmus/IRIW-acq-sc/IRIW-acq-sc.rs +++ b/tests/genmc/pass/litmus/IRIW-acq-sc/IRIW-acq-sc.rs @@ -2,14 +2,14 @@ #![no_main] -#[path = "../../../../utils-dep/mod.rs"] -mod utils_dep; +#[path = "../../../../utils/genmc.rs"] +mod genmc; use std::ffi::c_void; use std::ptr::null_mut; use std::sync::atomic::{AtomicU64, Ordering}; -use crate::utils_dep::*; +use crate::genmc::*; static X: AtomicU64 = AtomicU64::new(0); static Y: AtomicU64 = AtomicU64::new(0); diff --git a/tests/genmc/pass/litmus/IRIWish/IRIWish.rs b/tests/genmc/pass/litmus/IRIWish/IRIWish.rs index eee41cbc55..17bff3311c 100644 --- a/tests/genmc/pass/litmus/IRIWish/IRIWish.rs +++ b/tests/genmc/pass/litmus/IRIWish/IRIWish.rs @@ -2,14 +2,14 @@ #![no_main] -#[path = "../../../../utils-dep/mod.rs"] -mod utils_dep; +#[path = "../../../../utils/genmc.rs"] +mod genmc; use std::ffi::c_void; use std::ptr::null_mut; use std::sync::atomic::{AtomicU64, Ordering}; -use crate::utils_dep::*; +use crate::genmc::*; static X: AtomicU64 = AtomicU64::new(0); static Y: AtomicU64 = AtomicU64::new(0); diff --git a/tests/genmc/pass/litmus/LB+incMPs/LB_incMPs.rs b/tests/genmc/pass/litmus/LB+incMPs/LB_incMPs.rs index 8be7d959d0..4b037d1413 100644 --- a/tests/genmc/pass/litmus/LB+incMPs/LB_incMPs.rs +++ b/tests/genmc/pass/litmus/LB+incMPs/LB_incMPs.rs @@ -2,8 +2,8 @@ #![no_main] -#[path = "../../../../utils-dep/mod.rs"] -mod utils_dep; +#[path = "../../../../utils/genmc.rs"] +mod genmc; use std::ffi::c_void; use std::sync::atomic::{AtomicU64, Ordering}; @@ -13,7 +13,7 @@ static Y: AtomicU64 = AtomicU64::new(0); static W: AtomicU64 = AtomicU64::new(0); static Z: AtomicU64 = AtomicU64::new(0); -use crate::utils_dep::*; +use crate::genmc::*; #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { diff --git a/tests/genmc/pass/litmus/LB/LB.rs b/tests/genmc/pass/litmus/LB/LB.rs index 0957644399..bab3bfe03e 100644 --- a/tests/genmc/pass/litmus/LB/LB.rs +++ b/tests/genmc/pass/litmus/LB/LB.rs @@ -3,8 +3,8 @@ #![no_main] -#[path = "../../../../utils-dep/mod.rs"] -mod utils_dep; +#[path = "../../../../utils/genmc.rs"] +mod genmc; use std::ffi::c_void; use std::sync::atomic::{AtomicU64, Ordering}; @@ -12,7 +12,7 @@ use std::sync::atomic::{AtomicU64, Ordering}; static X: AtomicU64 = AtomicU64::new(0); static Y: AtomicU64 = AtomicU64::new(0); -use crate::utils_dep::*; +use crate::genmc::*; #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { diff --git a/tests/genmc/pass/litmus/MP+incMPs/MP_incMPs.rs b/tests/genmc/pass/litmus/MP+incMPs/MP_incMPs.rs index 26fadd7b04..ca6ab43c24 100644 --- a/tests/genmc/pass/litmus/MP+incMPs/MP_incMPs.rs +++ b/tests/genmc/pass/litmus/MP+incMPs/MP_incMPs.rs @@ -2,8 +2,8 @@ #![no_main] -#[path = "../../../../utils-dep/mod.rs"] -mod utils_dep; +#[path = "../../../../utils/genmc.rs"] +mod genmc; use std::ffi::c_void; use std::sync::atomic::{AtomicU64, Ordering}; @@ -12,7 +12,7 @@ static X: AtomicU64 = AtomicU64::new(0); static Y: AtomicU64 = AtomicU64::new(0); static Z: AtomicU64 = AtomicU64::new(0); -use crate::utils_dep::*; +use crate::genmc::*; #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { diff --git a/tests/genmc/pass/litmus/MP+rels+acqf/MP_rels_acqf.rs b/tests/genmc/pass/litmus/MP+rels+acqf/MP_rels_acqf.rs index 21d9e7cb29..5c84189800 100644 --- a/tests/genmc/pass/litmus/MP+rels+acqf/MP_rels_acqf.rs +++ b/tests/genmc/pass/litmus/MP+rels+acqf/MP_rels_acqf.rs @@ -3,8 +3,8 @@ #![no_main] -#[path = "../../../../utils-dep/mod.rs"] -mod utils_dep; +#[path = "../../../../utils/genmc.rs"] +mod genmc; use std::ffi::c_void; use std::sync::atomic::{AtomicU64, Ordering}; @@ -13,7 +13,7 @@ use std::sync::atomic::{AtomicU64, Ordering}; static X: AtomicU64 = AtomicU64::new(0); static Y: AtomicU64 = AtomicU64::new(0); -use crate::utils_dep::*; +use crate::genmc::*; #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { diff --git a/tests/genmc/pass/litmus/MP/MP.rs b/tests/genmc/pass/litmus/MP/MP.rs index 1a9fb34cd0..ac3347fade 100644 --- a/tests/genmc/pass/litmus/MP/MP.rs +++ b/tests/genmc/pass/litmus/MP/MP.rs @@ -3,8 +3,8 @@ #![no_main] -#[path = "../../../../utils-dep/mod.rs"] -mod utils_dep; +#[path = "../../../../utils/genmc.rs"] +mod genmc; use std::ffi::c_void; use std::sync::atomic::{AtomicU64, Ordering}; @@ -12,7 +12,7 @@ use std::sync::atomic::{AtomicU64, Ordering}; static X: AtomicU64 = AtomicU64::new(0); static Y: AtomicU64 = AtomicU64::new(0); -use crate::utils_dep::*; +use crate::genmc::*; #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { diff --git a/tests/genmc/pass/litmus/MPU+rels+acq/MPU_rels_acq.rs b/tests/genmc/pass/litmus/MPU+rels+acq/MPU_rels_acq.rs index a36a6c4439..b13c368170 100644 --- a/tests/genmc/pass/litmus/MPU+rels+acq/MPU_rels_acq.rs +++ b/tests/genmc/pass/litmus/MPU+rels+acq/MPU_rels_acq.rs @@ -2,8 +2,8 @@ #![no_main] -#[path = "../../../../utils-dep/mod.rs"] -mod utils_dep; +#[path = "../../../../utils/genmc.rs"] +mod genmc; use std::ffi::c_void; use std::sync::atomic::{AtomicU64, Ordering}; @@ -12,7 +12,7 @@ use std::sync::atomic::{AtomicU64, Ordering}; static X: AtomicU64 = AtomicU64::new(0); static Y: AtomicU64 = AtomicU64::new(0); -use crate::utils_dep::*; +use crate::genmc::*; #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { diff --git a/tests/genmc/pass/litmus/MPU2+rels+acqf/MPU2_rels_acqf.rs b/tests/genmc/pass/litmus/MPU2+rels+acqf/MPU2_rels_acqf.rs index 8bc2e3c6af..b84704e705 100644 --- a/tests/genmc/pass/litmus/MPU2+rels+acqf/MPU2_rels_acqf.rs +++ b/tests/genmc/pass/litmus/MPU2+rels+acqf/MPU2_rels_acqf.rs @@ -2,8 +2,8 @@ #![no_main] -#[path = "../../../../utils-dep/mod.rs"] -mod utils_dep; +#[path = "../../../../utils/genmc.rs"] +mod genmc; use std::ffi::c_void; use std::sync::atomic::{AtomicU64, Ordering}; @@ -12,7 +12,7 @@ use std::sync::atomic::{AtomicU64, Ordering}; static X: AtomicU64 = AtomicU64::new(0); static Y: AtomicU64 = AtomicU64::new(0); -use crate::utils_dep::*; +use crate::genmc::*; #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { diff --git a/tests/genmc/pass/litmus/SB+2sc+scf/SB_2sc_scf.rs b/tests/genmc/pass/litmus/SB+2sc+scf/SB_2sc_scf.rs index c843206edb..50cdc6a9f1 100644 --- a/tests/genmc/pass/litmus/SB+2sc+scf/SB_2sc_scf.rs +++ b/tests/genmc/pass/litmus/SB+2sc+scf/SB_2sc_scf.rs @@ -2,8 +2,8 @@ #![no_main] -#[path = "../../../../utils-dep/mod.rs"] -mod utils_dep; +#[path = "../../../../utils/genmc.rs"] +mod genmc; use std::ffi::c_void; use std::sync::atomic::{AtomicU64, Ordering}; @@ -11,7 +11,7 @@ use std::sync::atomic::{AtomicU64, Ordering}; static X: AtomicU64 = AtomicU64::new(0); static Y: AtomicU64 = AtomicU64::new(0); -use crate::utils_dep::*; +use crate::genmc::*; #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { diff --git a/tests/genmc/pass/litmus/SB/SB.rs b/tests/genmc/pass/litmus/SB/SB.rs index 4d609df507..b99f2fe67a 100644 --- a/tests/genmc/pass/litmus/SB/SB.rs +++ b/tests/genmc/pass/litmus/SB/SB.rs @@ -3,8 +3,8 @@ #![no_main] -#[path = "../../../../utils-dep/mod.rs"] -mod utils_dep; +#[path = "../../../../utils/genmc.rs"] +mod genmc; use std::ffi::c_void; use std::sync::atomic::{AtomicU64, Ordering}; @@ -12,7 +12,7 @@ use std::sync::atomic::{AtomicU64, Ordering}; static X: AtomicU64 = AtomicU64::new(0); static Y: AtomicU64 = AtomicU64::new(0); -use crate::utils_dep::*; +use crate::genmc::*; #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { diff --git a/tests/genmc/pass/litmus/Z6+acq/Z6_acq.rs b/tests/genmc/pass/litmus/Z6+acq/Z6_acq.rs index a7e43686a4..79d7e7f225 100644 --- a/tests/genmc/pass/litmus/Z6+acq/Z6_acq.rs +++ b/tests/genmc/pass/litmus/Z6+acq/Z6_acq.rs @@ -3,8 +3,8 @@ #![no_main] -#[path = "../../../../utils-dep/mod.rs"] -mod utils_dep; +#[path = "../../../../utils/genmc.rs"] +mod genmc; use std::ffi::c_void; use std::sync::atomic::{AtomicU64, Ordering}; @@ -13,7 +13,7 @@ static X: AtomicU64 = AtomicU64::new(0); static Y: AtomicU64 = AtomicU64::new(0); static Z: AtomicU64 = AtomicU64::new(0); -use crate::utils_dep::*; +use crate::genmc::*; #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { diff --git a/tests/genmc/pass/litmus/Z6.U0/Z6_U0.rs b/tests/genmc/pass/litmus/Z6.U0/Z6_U0.rs index ec4ff0f5be..b39e86ab64 100644 --- a/tests/genmc/pass/litmus/Z6.U0/Z6_U0.rs +++ b/tests/genmc/pass/litmus/Z6.U0/Z6_U0.rs @@ -2,8 +2,8 @@ #![no_main] -#[path = "../../../../utils-dep/mod.rs"] -mod utils_dep; +#[path = "../../../../utils/genmc.rs"] +mod genmc; use std::ffi::c_void; use std::sync::atomic::{AtomicU64, Ordering}; @@ -11,7 +11,7 @@ use std::sync::atomic::{AtomicU64, Ordering}; static X: AtomicU64 = AtomicU64::new(0); static Y: AtomicU64 = AtomicU64::new(0); -use crate::utils_dep::*; +use crate::genmc::*; #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { diff --git a/tests/genmc/pass/litmus/assume-ctrl/assume-ctrl.rs b/tests/genmc/pass/litmus/assume-ctrl/assume-ctrl.rs index a5af1db572..d369ccf97d 100644 --- a/tests/genmc/pass/litmus/assume-ctrl/assume-ctrl.rs +++ b/tests/genmc/pass/litmus/assume-ctrl/assume-ctrl.rs @@ -2,8 +2,11 @@ #![no_main] -#[path = "../../../../utils-dep/mod.rs"] -mod utils_dep; +#[path = "../../../../utils/genmc.rs"] +mod genmc; + +#[path = "../../../../utils/mod.rs"] +mod utils; use std::ffi::c_void; use std::sync::atomic::{AtomicU64, Ordering}; @@ -11,7 +14,8 @@ use std::sync::atomic::{AtomicU64, Ordering}; static X: AtomicU64 = AtomicU64::new(0); static Y: AtomicU64 = AtomicU64::new(0); -use crate::utils_dep::*; +use crate::genmc::*; +use crate::utils::miri_genmc_verifier_assume; #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { diff --git a/tests/genmc/pass/litmus/atomicpo/atomicpo.rs b/tests/genmc/pass/litmus/atomicpo/atomicpo.rs index 8621e80f12..6769e715eb 100644 --- a/tests/genmc/pass/litmus/atomicpo/atomicpo.rs +++ b/tests/genmc/pass/litmus/atomicpo/atomicpo.rs @@ -2,8 +2,8 @@ #![no_main] -#[path = "../../../../utils-dep/mod.rs"] -mod utils_dep; +#[path = "../../../../utils/genmc.rs"] +mod genmc; use std::ffi::c_void; use std::sync::atomic::{AtomicU64, Ordering}; @@ -11,7 +11,7 @@ use std::sync::atomic::{AtomicU64, Ordering}; static X: AtomicU64 = AtomicU64::new(0); static Y: AtomicU64 = AtomicU64::new(0); -use crate::utils_dep::*; +use crate::genmc::*; #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { diff --git a/tests/genmc/pass/litmus/casdep/casdep.rs b/tests/genmc/pass/litmus/casdep/casdep.rs index e09961e509..90ab45fff7 100644 --- a/tests/genmc/pass/litmus/casdep/casdep.rs +++ b/tests/genmc/pass/litmus/casdep/casdep.rs @@ -2,13 +2,13 @@ #![no_main] -#[path = "../../../../utils-dep/mod.rs"] -mod utils_dep; +#[path = "../../../../utils/genmc.rs"] +mod genmc; use std::ffi::c_void; use std::sync::atomic::{AtomicU64, Ordering}; -use crate::utils_dep::*; +use crate::genmc::*; static X: AtomicU64 = AtomicU64::new(0); static Y: AtomicU64 = AtomicU64::new(0); diff --git a/tests/genmc/pass/litmus/ccr/ccr.rs b/tests/genmc/pass/litmus/ccr/ccr.rs index 4f86311658..fc5b9168bc 100644 --- a/tests/genmc/pass/litmus/ccr/ccr.rs +++ b/tests/genmc/pass/litmus/ccr/ccr.rs @@ -2,15 +2,15 @@ #![no_main] -#[path = "../../../../utils-dep/mod.rs"] -mod utils_dep; +#[path = "../../../../utils/genmc.rs"] +mod genmc; use std::ffi::c_void; use std::sync::atomic::{AtomicU64, Ordering}; static X: AtomicU64 = AtomicU64::new(0); -use crate::utils_dep::*; +use crate::genmc::*; #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { diff --git a/tests/genmc/pass/litmus/cii/cii.rs b/tests/genmc/pass/litmus/cii/cii.rs index dda6caff5e..199e525522 100644 --- a/tests/genmc/pass/litmus/cii/cii.rs +++ b/tests/genmc/pass/litmus/cii/cii.rs @@ -2,15 +2,15 @@ #![no_main] -#[path = "../../../../utils-dep/mod.rs"] -mod utils_dep; +#[path = "../../../../utils/genmc.rs"] +mod genmc; use std::ffi::c_void; use std::sync::atomic::{AtomicU64, Ordering}; static X: AtomicU64 = AtomicU64::new(0); -use crate::utils_dep::*; +use crate::genmc::*; #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { diff --git a/tests/genmc/pass/litmus/cumul-release/cumul-release.rs b/tests/genmc/pass/litmus/cumul-release/cumul-release.rs index 3f2aaec9dd..65672e0429 100644 --- a/tests/genmc/pass/litmus/cumul-release/cumul-release.rs +++ b/tests/genmc/pass/litmus/cumul-release/cumul-release.rs @@ -2,13 +2,13 @@ #![no_main] -#[path = "../../../../utils-dep/mod.rs"] -mod utils_dep; +#[path = "../../../../utils/genmc.rs"] +mod genmc; use std::ffi::c_void; use std::sync::atomic::{AtomicU64, Ordering}; -use crate::utils_dep::*; +use crate::genmc::*; static X: AtomicU64 = AtomicU64::new(0); static Y: AtomicU64 = AtomicU64::new(0); diff --git a/tests/genmc/pass/litmus/default/default.rs b/tests/genmc/pass/litmus/default/default.rs index d1602f6338..76d2688324 100644 --- a/tests/genmc/pass/litmus/default/default.rs +++ b/tests/genmc/pass/litmus/default/default.rs @@ -3,13 +3,13 @@ #![no_main] -#[path = "../../../../utils-dep/mod.rs"] -mod utils_dep; +#[path = "../../../../utils/genmc.rs"] +mod genmc; use std::ffi::c_void; use std::sync::atomic::{AtomicU64, Ordering}; -use crate::utils_dep::*; +use crate::genmc::*; static X: AtomicU64 = AtomicU64::new(0); diff --git a/tests/genmc/pass/litmus/detour/detour.rs b/tests/genmc/pass/litmus/detour/detour.rs index e1a2220841..1d444b9095 100644 --- a/tests/genmc/pass/litmus/detour/detour.rs +++ b/tests/genmc/pass/litmus/detour/detour.rs @@ -2,13 +2,13 @@ #![no_main] -#[path = "../../../../utils-dep/mod.rs"] -mod utils_dep; +#[path = "../../../../utils/genmc.rs"] +mod genmc; use std::ffi::c_void; use std::sync::atomic::{AtomicI64, Ordering}; -use crate::utils_dep::*; +use crate::genmc::*; static X: AtomicI64 = AtomicI64::new(0); static Y: AtomicI64 = AtomicI64::new(0); diff --git a/tests/genmc/pass/litmus/fr_w_w_w_reads/fr_w_w_w_reads.rs b/tests/genmc/pass/litmus/fr_w_w_w_reads/fr_w_w_w_reads.rs index f318eb6479..ecdf441203 100644 --- a/tests/genmc/pass/litmus/fr_w_w_w_reads/fr_w_w_w_reads.rs +++ b/tests/genmc/pass/litmus/fr_w_w_w_reads/fr_w_w_w_reads.rs @@ -2,14 +2,14 @@ #![no_main] -#[path = "../../../../utils-dep/mod.rs"] -mod utils_dep; +#[path = "../../../../utils/genmc.rs"] +mod genmc; use std::ffi::c_void; use std::ptr::null_mut; use std::sync::atomic::{AtomicU64, Ordering}; -use crate::utils_dep::*; +use crate::genmc::*; static X: AtomicU64 = AtomicU64::new(0); diff --git a/tests/genmc/pass/litmus/inc2w/inc2w.rs b/tests/genmc/pass/litmus/inc2w/inc2w.rs index bb4f5c45f3..0a19ca3a4e 100644 --- a/tests/genmc/pass/litmus/inc2w/inc2w.rs +++ b/tests/genmc/pass/litmus/inc2w/inc2w.rs @@ -2,14 +2,14 @@ #![no_main] -#[path = "../../../../utils-dep/mod.rs"] -mod utils_dep; +#[path = "../../../../utils/genmc.rs"] +mod genmc; use std::ffi::c_void; use std::ptr::null_mut; use std::sync::atomic::{AtomicU64, Ordering}; -use crate::utils_dep::*; +use crate::genmc::*; static X: AtomicU64 = AtomicU64::new(0); diff --git a/tests/genmc/pass/litmus/inc_inc_RR_W_RR/inc_inc_RR_W_RR.rs b/tests/genmc/pass/litmus/inc_inc_RR_W_RR/inc_inc_RR_W_RR.rs index fcc964d211..0e81d2eb5d 100644 --- a/tests/genmc/pass/litmus/inc_inc_RR_W_RR/inc_inc_RR_W_RR.rs +++ b/tests/genmc/pass/litmus/inc_inc_RR_W_RR/inc_inc_RR_W_RR.rs @@ -2,14 +2,14 @@ #![no_main] -#[path = "../../../../utils-dep/mod.rs"] -mod utils_dep; +#[path = "../../../../utils/genmc.rs"] +mod genmc; use std::ffi::c_void; use std::ptr::null_mut; use std::sync::atomic::{AtomicU64, Ordering}; -use crate::utils_dep::*; +use crate::genmc::*; static X: AtomicU64 = AtomicU64::new(0); diff --git a/tests/genmc/pass/litmus/riwi/riwi.rs b/tests/genmc/pass/litmus/riwi/riwi.rs index 51de24d2ca..9383f79c1f 100644 --- a/tests/genmc/pass/litmus/riwi/riwi.rs +++ b/tests/genmc/pass/litmus/riwi/riwi.rs @@ -2,8 +2,8 @@ #![no_main] -#[path = "../../../../utils-dep/mod.rs"] -mod utils_dep; +#[path = "../../../../utils/genmc.rs"] +mod genmc; use std::ffi::c_void; use std::sync::atomic::{AtomicU64, Ordering}; @@ -11,7 +11,7 @@ use std::sync::atomic::{AtomicU64, Ordering}; static X: AtomicU64 = AtomicU64::new(0); static Y: AtomicU64 = AtomicU64::new(0); -use crate::utils_dep::*; +use crate::genmc::*; #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { diff --git a/tests/genmc/pass/litmus/viktor-relseq/viktor-relseq.rs b/tests/genmc/pass/litmus/viktor-relseq/viktor-relseq.rs index d576c6894c..bea3af911a 100644 --- a/tests/genmc/pass/litmus/viktor-relseq/viktor-relseq.rs +++ b/tests/genmc/pass/litmus/viktor-relseq/viktor-relseq.rs @@ -2,15 +2,15 @@ #![no_main] -#[path = "../../../../utils-dep/mod.rs"] -mod utils_dep; +#[path = "../../../../utils/genmc.rs"] +mod genmc; use std::ffi::c_void; use std::sync::atomic::{AtomicU64, Ordering}; static LOCK: AtomicU64 = AtomicU64::new(0); -use crate::utils_dep::*; +use crate::genmc::*; #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { diff --git a/tests/genmc/pass/loom/load-buffering/load_buffering.rs b/tests/genmc/pass/loom/load-buffering/load_buffering.rs index bb896cdc17..df8b58e36e 100644 --- a/tests/genmc/pass/loom/load-buffering/load_buffering.rs +++ b/tests/genmc/pass/loom/load-buffering/load_buffering.rs @@ -13,8 +13,8 @@ #![no_main] #[cfg(not(any(non_genmc_std, genmc_std)))] -#[path = "../../../../utils-dep/mod.rs"] -mod utils_dep; +#[path = "../../../../utils/genmc.rs"] +mod genmc; use std::sync::atomic::AtomicUsize; use std::sync::atomic::Ordering::{Relaxed, SeqCst}; @@ -52,7 +52,7 @@ fn test() -> usize { fn test() -> usize { use std::ffi::c_void; - use crate::utils_dep::genmc::{join_pthread, spawn_pthread}; + use crate::genmc::{join_pthread, spawn_pthread}; extern "C" fn thread_func(_value: *mut c_void) -> *mut c_void { thread_0(); diff --git a/tests/genmc/pass/simple/thread_locals.rs b/tests/genmc/pass/simple/thread_locals.rs index 9639d44cf5..bb650c2076 100644 --- a/tests/genmc/pass/simple/thread_locals.rs +++ b/tests/genmc/pass/simple/thread_locals.rs @@ -2,15 +2,15 @@ #![no_main] -#[path = "../../../utils-dep/mod.rs"] -mod utils_dep; +#[path = "../../../utils/genmc.rs"] +mod genmc; use std::alloc::{Layout, alloc}; use std::cell::Cell; use std::ffi::c_void; use std::sync::atomic::{AtomicPtr, Ordering}; -use crate::utils_dep::*; +use crate::genmc::*; static X: AtomicPtr = AtomicPtr::new(std::ptr::null_mut()); diff --git a/tests/utils-dep/miri_extern.rs b/tests/utils-dep/miri_extern.rs deleted file mode 100644 index dbd4cdb48d..0000000000 --- a/tests/utils-dep/miri_extern.rs +++ /dev/null @@ -1,2 +0,0 @@ -// TODO GENMC: is this ok or is there a better solution? -include!("../utils/miri_extern.rs"); diff --git a/tests/utils-dep/mod.rs b/tests/utils-dep/mod.rs deleted file mode 100644 index ab35b8e3c4..0000000000 --- a/tests/utils-dep/mod.rs +++ /dev/null @@ -1,8 +0,0 @@ -#![allow(dead_code)] -#![allow(unused_imports)] - -pub mod genmc; -mod miri_extern; - -pub use self::genmc::*; -pub use self::miri_extern::*; diff --git a/tests/utils-dep/genmc.rs b/tests/utils/genmc.rs similarity index 98% rename from tests/utils-dep/genmc.rs rename to tests/utils/genmc.rs index e1fd070813..376f296b3b 100644 --- a/tests/utils-dep/genmc.rs +++ b/tests/utils/genmc.rs @@ -1,3 +1,5 @@ +#![allow(unused)] + use std::ffi::c_void; use libc::{self, pthread_attr_t, pthread_t}; From 50afd6f72a32443f449d9250e7007c1d5d97564f Mon Sep 17 00:00:00 2001 From: Patrick-6 Date: Wed, 30 Jul 2025 14:51:42 +0200 Subject: [PATCH 09/41] Inline test folders, move tests, remove extra test variants. --- src/eval.rs | 6 +- .../fail/loom/{buggy-inc => }/buggy_inc.rs | 2 +- .../loom/{buggy-inc => }/buggy_inc.stderr | 4 +- .../store_buffering.genmc.stderr | 4 +- .../store_buffering.genmc_std.stderr | 4 +- .../store_buffering.non_genmc.stderr | 4 +- .../store_buffering.non_genmc_std.stderr | 4 +- .../{store-buffering => }/store_buffering.rs | 2 +- .../pass/{simple => atomics}/atomic_ptr.rs | 0 .../{simple => atomics}/atomic_ptr.stderr | 0 .../pass/{simple => atomics}/cas_simple.rs | 0 .../{simple => atomics}/cas_simple.stderr | 0 .../rmw_edge_cases.i16_.stderr | 0 .../rmw_edge_cases.i32_.stderr | 0 .../rmw_edge_cases.i64_.stderr | 0 .../rmw => atomics}/rmw_edge_cases.i8_.stderr | 0 .../rmw_edge_cases.isize_.stderr | 0 .../{basics/rmw => atomics}/rmw_edge_cases.rs | 0 .../rmw_edge_cases.u16_.stderr | 0 .../rmw_edge_cases.u32_.stderr | 0 .../rmw_edge_cases.u64_.stderr | 0 .../rmw => atomics}/rmw_edge_cases.u8_.stderr | 0 .../rmw_edge_cases.usize_.stderr | 0 .../{basics/rmw => atomics}/rmw_simple.rs | 0 .../{basics/rmw => atomics}/rmw_simple.stderr | 0 .../{simple => atomics}/stack_alloc_atomic.rs | 0 .../stack_alloc_atomic.stderr | 0 .../basics/mutex/TODO_mutex_get_mut.rs.txt | 0 tests/genmc/pass/basics/mutex/mutex_simple.c | 72 ------------------- .../mutex/mutex_simple.order21reps1.stderr | 3 - .../pass/litmus/2CoWR/2cowr.order3412.stderr | 3 - .../pass/litmus/2CoWR/2cowr.order4123.stderr | 3 - .../pass/litmus/2CoWR/2cowr.order4321.stderr | 3 - .../{2+2W+3sc+rel1 => }/2_2w_3sc_rel1.rs | 2 +- .../{2+2W+3sc+rel1 => }/2_2w_3sc_rel1.stderr | 0 .../pass/litmus/{2+2W+4sc => }/2_2w_4sc.rs | 2 +- .../litmus/{2+2W+4sc => }/2_2w_4sc.stderr | 0 .../pass/litmus/{2+2W+scfs => }/2_2w_scfs.rs | 2 +- .../litmus/{2+2W+scfs => }/2_2w_scfs.stderr | 0 tests/genmc/pass/litmus/{2CoWR => }/2cowr.rs | 19 +---- .../2W2W.order12.stderr => 2cowr.stderr} | 0 .../pass/litmus/{2+2w/2W2W.rs => 2w2w.rs} | 13 +--- .../{2+2w/2W2W.order21.stderr => 2w2w.stderr} | 0 .../litmus/{2+2W+2sc+scf => }/2w2w_2sc_scf.rs | 13 +--- ...scf.order12.stderr => 2w2w_2sc_scf.stderr} | 0 .../pass/litmus/CoRR/corr.order12.stderr | 3 - .../pass/litmus/CoRR/corr.order21.stderr | 3 - .../pass/litmus/CoRR0/corr0.order123.stderr | 3 - .../pass/litmus/CoRR0/corr0.order231.stderr | 3 - .../pass/litmus/CoRR0/corr0.order312.stderr | 3 - .../pass/litmus/CoRR0/corr0.order321.stderr | 3 - .../pass/litmus/CoRR1/corr1.order2341.stderr | 3 - .../pass/litmus/CoRR1/corr1.order3412.stderr | 3 - .../pass/litmus/CoRR1/corr1.order4123.stderr | 3 - .../pass/litmus/CoRR1/corr1.order4321.stderr | 3 - tests/genmc/pass/litmus/CoRR1/corr1.rs | 57 --------------- .../pass/litmus/CoRR2/corr2.order2341.stderr | 3 - .../pass/litmus/CoRR2/corr2.order3412.stderr | 3 - .../pass/litmus/CoRR2/corr2.order4123.stderr | 3 - .../pass/litmus/CoRR2/corr2.order4321.stderr | 3 - tests/genmc/pass/litmus/CoRR2/corr2.rs | 57 --------------- .../pass/litmus/CoWR/cowr.order21.stderr | 3 - .../litmus/{IRIW-acq-sc => }/IRIW-acq-sc.rs | 11 ++- .../{IRIW-acq-sc => }/IRIW-acq-sc.stderr | 0 .../pass/litmus/{IRIWish => }/IRIWish.rs | 11 ++- .../pass/litmus/{IRIWish => }/IRIWish.stderr | 0 tests/genmc/pass/litmus/{LB => }/LB.rs | 13 +--- .../2w2w_2sc_scf.order21.stderr => LB.stderr} | 0 tests/genmc/pass/litmus/LB/LB.order12.stderr | 3 - tests/genmc/pass/litmus/LB/LB.order21.stderr | 3 - .../pass/litmus/{LB+incMPs => }/LB_incMPs.rs | 2 +- .../litmus/{LB+incMPs => }/LB_incMPs.stderr | 0 tests/genmc/pass/litmus/{MP => }/MP.rs | 13 +--- .../{CoRW/corw.order12.stderr => MP.stderr} | 0 tests/genmc/pass/litmus/MP/MP.order12.stderr | 3 - tests/genmc/pass/litmus/MP/MP.order21.stderr | 3 - .../{MPU2+rels+acqf => }/MPU2_rels_acqf.rs | 2 +- .../MPU2_rels_acqf.stderr | 0 .../litmus/{MPU+rels+acq => }/MPU_rels_acq.rs | 2 +- .../{MPU+rels+acq => }/MPU_rels_acq.stderr | 0 .../pass/litmus/{MP+incMPs => }/MP_incMPs.rs | 2 +- .../litmus/{MP+incMPs => }/MP_incMPs.stderr | 0 .../litmus/{MP+rels+acqf => }/MP_rels_acqf.rs | 2 +- .../{MP+rels+acqf => }/MP_rels_acqf.stderr | 0 tests/genmc/pass/litmus/{SB => }/SB.rs | 13 +--- .../2cowr.order1234.stderr => SB.stderr} | 0 tests/genmc/pass/litmus/SB/SB.order12.stderr | 3 - tests/genmc/pass/litmus/SB/SB.order21.stderr | 3 - .../litmus/{SB+2sc+scf => }/SB_2sc_scf.rs | 2 +- .../litmus/{SB+2sc+scf => }/SB_2sc_scf.stderr | 0 .../pass/litmus/Z6+acq/Z6_acq.order321.stderr | 3 - tests/genmc/pass/litmus/{Z6.U0 => }/Z6_U0.rs | 2 +- .../pass/litmus/{Z6.U0 => }/Z6_U0.stderr | 0 .../genmc/pass/litmus/{Z6+acq => }/Z6_acq.rs | 13 +--- .../Z6_acq.order123.stderr => Z6_acq.stderr} | 0 .../litmus/{assume-ctrl => }/assume-ctrl.rs | 4 +- .../{assume-ctrl => }/assume-ctrl.stderr | 0 .../pass/litmus/{atomicpo => }/atomicpo.rs | 2 +- .../litmus/{atomicpo => }/atomicpo.stderr | 0 .../genmc/pass/litmus/{casdep => }/casdep.rs | 2 +- .../pass/litmus/{casdep => }/casdep.stderr | 0 tests/genmc/pass/litmus/{ccr => }/ccr.rs | 2 +- tests/genmc/pass/litmus/{ccr => }/ccr.stderr | 0 tests/genmc/pass/litmus/{cii => }/cii.rs | 2 +- tests/genmc/pass/litmus/{cii => }/cii.stderr | 0 tests/genmc/pass/litmus/corr.rs | 32 +++++++++ .../corr.stderr} | 0 tests/genmc/pass/litmus/{CoRR0 => }/corr0.rs | 17 +---- .../2cowr.order2341.stderr => corr0.stderr} | 0 tests/genmc/pass/litmus/corr1.rs | 43 +++++++++++ .../corr1.order1234.stderr => corr1.stderr} | 0 .../litmus/{default/default.rs => corr2.rs} | 35 ++++----- .../corr2.order1234.stderr => corr2.stderr} | 0 tests/genmc/pass/litmus/{CoRW => }/corw.rs | 13 +--- .../{CoRW/corw.order21.stderr => corw.stderr} | 0 tests/genmc/pass/litmus/{CoWR => }/cowr.rs | 13 +--- .../{CoWR/cowr.order12.stderr => cowr.stderr} | 0 .../{cumul-release => }/cumul-release.rs | 2 +- .../{cumul-release => }/cumul-release.stderr | 0 .../pass/litmus/{CoRR/corr.rs => default.rs} | 29 ++++---- ...default.order123.stderr => default.stderr} | 0 .../litmus/default/default.order231.stderr | 3 - .../litmus/default/default.order312.stderr | 3 - .../litmus/default/default.order321.stderr | 3 - .../genmc/pass/litmus/{detour => }/detour.rs | 2 +- .../pass/litmus/{detour => }/detour.stderr | 0 .../{fr_w_w_w_reads => }/fr_w_w_w_reads.rs | 11 ++- .../fr_w_w_w_reads.stderr | 0 tests/genmc/pass/litmus/{inc2w => }/inc2w.rs | 9 ++- .../pass/litmus/{inc2w => }/inc2w.stderr | 0 .../{inc_inc_RR_W_RR => }/inc_inc_RR_W_RR.rs | 13 ++-- .../inc_inc_RR_W_RR.stderr | 0 tests/genmc/pass/litmus/{riwi => }/riwi.rs | 2 +- .../genmc/pass/litmus/{riwi => }/riwi.stderr | 0 .../{viktor-relseq => }/viktor-relseq.rs | 2 +- .../{viktor-relseq => }/viktor-relseq.stderr | 0 .../load_buffering.genmc.stderr | 0 .../load_buffering.genmc_std.stderr | 0 .../{load-buffering => }/load_buffering.rs | 2 +- .../mutex_simple.reps1.stderr} | 0 .../mutex_simple.reps2.stderr} | 0 .../{basics/mutex => std}/mutex_simple.rs | 8 +-- .../simple_main.rs => thread/empty_main.rs} | 0 .../empty_main.stderr} | 0 .../libc_exit.rs.disabled | 2 +- .../miri_main_spawn_pthreads.rs | 2 +- .../miri_main_spawn_pthreads.stderr | 0 .../miri_main_spawn_threads.rs} | 2 +- .../miri_main_spawn_threads.stderr} | 0 .../std_main_spawn_threads.rs} | 2 +- .../std_main_spawn_threads.stderr} | 0 .../pass/{simple => thread}/thread_locals.rs | 0 .../{simple => thread}/thread_locals.stderr | 0 .../pass/{simple => thread}/thread_simple.rs | 0 .../{simple => thread}/thread_simple.stderr | 0 155 files changed, 192 insertions(+), 503 deletions(-) rename tests/genmc/fail/loom/{buggy-inc => }/buggy_inc.rs (97%) rename tests/genmc/fail/loom/{buggy-inc => }/buggy_inc.stderr (81%) rename tests/genmc/fail/loom/{store-buffering => }/store_buffering.genmc.stderr (78%) rename tests/genmc/fail/loom/{store-buffering => }/store_buffering.genmc_std.stderr (88%) rename tests/genmc/fail/loom/{store-buffering => }/store_buffering.non_genmc.stderr (78%) rename tests/genmc/fail/loom/{store-buffering => }/store_buffering.non_genmc_std.stderr (78%) rename tests/genmc/fail/loom/{store-buffering => }/store_buffering.rs (98%) rename tests/genmc/pass/{simple => atomics}/atomic_ptr.rs (100%) rename tests/genmc/pass/{simple => atomics}/atomic_ptr.stderr (100%) rename tests/genmc/pass/{simple => atomics}/cas_simple.rs (100%) rename tests/genmc/pass/{simple => atomics}/cas_simple.stderr (100%) rename tests/genmc/pass/{basics/rmw => atomics}/rmw_edge_cases.i16_.stderr (100%) rename tests/genmc/pass/{basics/rmw => atomics}/rmw_edge_cases.i32_.stderr (100%) rename tests/genmc/pass/{basics/rmw => atomics}/rmw_edge_cases.i64_.stderr (100%) rename tests/genmc/pass/{basics/rmw => atomics}/rmw_edge_cases.i8_.stderr (100%) rename tests/genmc/pass/{basics/rmw => atomics}/rmw_edge_cases.isize_.stderr (100%) rename tests/genmc/pass/{basics/rmw => atomics}/rmw_edge_cases.rs (100%) rename tests/genmc/pass/{basics/rmw => atomics}/rmw_edge_cases.u16_.stderr (100%) rename tests/genmc/pass/{basics/rmw => atomics}/rmw_edge_cases.u32_.stderr (100%) rename tests/genmc/pass/{basics/rmw => atomics}/rmw_edge_cases.u64_.stderr (100%) rename tests/genmc/pass/{basics/rmw => atomics}/rmw_edge_cases.u8_.stderr (100%) rename tests/genmc/pass/{basics/rmw => atomics}/rmw_edge_cases.usize_.stderr (100%) rename tests/genmc/pass/{basics/rmw => atomics}/rmw_simple.rs (100%) rename tests/genmc/pass/{basics/rmw => atomics}/rmw_simple.stderr (100%) rename tests/genmc/pass/{simple => atomics}/stack_alloc_atomic.rs (100%) rename tests/genmc/pass/{simple => atomics}/stack_alloc_atomic.stderr (100%) delete mode 100644 tests/genmc/pass/basics/mutex/TODO_mutex_get_mut.rs.txt delete mode 100644 tests/genmc/pass/basics/mutex/mutex_simple.c delete mode 100644 tests/genmc/pass/basics/mutex/mutex_simple.order21reps1.stderr delete mode 100644 tests/genmc/pass/litmus/2CoWR/2cowr.order3412.stderr delete mode 100644 tests/genmc/pass/litmus/2CoWR/2cowr.order4123.stderr delete mode 100644 tests/genmc/pass/litmus/2CoWR/2cowr.order4321.stderr rename tests/genmc/pass/litmus/{2+2W+3sc+rel1 => }/2_2w_3sc_rel1.rs (95%) rename tests/genmc/pass/litmus/{2+2W+3sc+rel1 => }/2_2w_3sc_rel1.stderr (100%) rename tests/genmc/pass/litmus/{2+2W+4sc => }/2_2w_4sc.rs (95%) rename tests/genmc/pass/litmus/{2+2W+4sc => }/2_2w_4sc.stderr (100%) rename tests/genmc/pass/litmus/{2+2W+scfs => }/2_2w_scfs.rs (95%) rename tests/genmc/pass/litmus/{2+2W+scfs => }/2_2w_scfs.stderr (100%) rename tests/genmc/pass/litmus/{2CoWR => }/2cowr.rs (57%) rename tests/genmc/pass/litmus/{2+2w/2W2W.order12.stderr => 2cowr.stderr} (100%) rename tests/genmc/pass/litmus/{2+2w/2W2W.rs => 2w2w.rs} (68%) rename tests/genmc/pass/litmus/{2+2w/2W2W.order21.stderr => 2w2w.stderr} (100%) rename tests/genmc/pass/litmus/{2+2W+2sc+scf => }/2w2w_2sc_scf.rs (69%) rename tests/genmc/pass/litmus/{2+2W+2sc+scf/2w2w_2sc_scf.order12.stderr => 2w2w_2sc_scf.stderr} (100%) delete mode 100644 tests/genmc/pass/litmus/CoRR/corr.order12.stderr delete mode 100644 tests/genmc/pass/litmus/CoRR/corr.order21.stderr delete mode 100644 tests/genmc/pass/litmus/CoRR0/corr0.order123.stderr delete mode 100644 tests/genmc/pass/litmus/CoRR0/corr0.order231.stderr delete mode 100644 tests/genmc/pass/litmus/CoRR0/corr0.order312.stderr delete mode 100644 tests/genmc/pass/litmus/CoRR0/corr0.order321.stderr delete mode 100644 tests/genmc/pass/litmus/CoRR1/corr1.order2341.stderr delete mode 100644 tests/genmc/pass/litmus/CoRR1/corr1.order3412.stderr delete mode 100644 tests/genmc/pass/litmus/CoRR1/corr1.order4123.stderr delete mode 100644 tests/genmc/pass/litmus/CoRR1/corr1.order4321.stderr delete mode 100644 tests/genmc/pass/litmus/CoRR1/corr1.rs delete mode 100644 tests/genmc/pass/litmus/CoRR2/corr2.order2341.stderr delete mode 100644 tests/genmc/pass/litmus/CoRR2/corr2.order3412.stderr delete mode 100644 tests/genmc/pass/litmus/CoRR2/corr2.order4123.stderr delete mode 100644 tests/genmc/pass/litmus/CoRR2/corr2.order4321.stderr delete mode 100644 tests/genmc/pass/litmus/CoRR2/corr2.rs delete mode 100644 tests/genmc/pass/litmus/CoWR/cowr.order21.stderr rename tests/genmc/pass/litmus/{IRIW-acq-sc => }/IRIW-acq-sc.rs (87%) rename tests/genmc/pass/litmus/{IRIW-acq-sc => }/IRIW-acq-sc.stderr (100%) rename tests/genmc/pass/litmus/{IRIWish => }/IRIWish.rs (89%) rename tests/genmc/pass/litmus/{IRIWish => }/IRIWish.stderr (100%) rename tests/genmc/pass/litmus/{LB => }/LB.rs (68%) rename tests/genmc/pass/litmus/{2+2W+2sc+scf/2w2w_2sc_scf.order21.stderr => LB.stderr} (100%) delete mode 100644 tests/genmc/pass/litmus/LB/LB.order12.stderr delete mode 100644 tests/genmc/pass/litmus/LB/LB.order21.stderr rename tests/genmc/pass/litmus/{LB+incMPs => }/LB_incMPs.rs (96%) rename tests/genmc/pass/litmus/{LB+incMPs => }/LB_incMPs.stderr (100%) rename tests/genmc/pass/litmus/{MP => }/MP.rs (68%) rename tests/genmc/pass/litmus/{CoRW/corw.order12.stderr => MP.stderr} (100%) delete mode 100644 tests/genmc/pass/litmus/MP/MP.order12.stderr delete mode 100644 tests/genmc/pass/litmus/MP/MP.order21.stderr rename tests/genmc/pass/litmus/{MPU2+rels+acqf => }/MPU2_rels_acqf.rs (97%) rename tests/genmc/pass/litmus/{MPU2+rels+acqf => }/MPU2_rels_acqf.stderr (100%) rename tests/genmc/pass/litmus/{MPU+rels+acq => }/MPU_rels_acq.rs (96%) rename tests/genmc/pass/litmus/{MPU+rels+acq => }/MPU_rels_acq.stderr (100%) rename tests/genmc/pass/litmus/{MP+incMPs => }/MP_incMPs.rs (96%) rename tests/genmc/pass/litmus/{MP+incMPs => }/MP_incMPs.stderr (100%) rename tests/genmc/pass/litmus/{MP+rels+acqf => }/MP_rels_acqf.rs (96%) rename tests/genmc/pass/litmus/{MP+rels+acqf => }/MP_rels_acqf.stderr (100%) rename tests/genmc/pass/litmus/{SB => }/SB.rs (68%) rename tests/genmc/pass/litmus/{2CoWR/2cowr.order1234.stderr => SB.stderr} (100%) delete mode 100644 tests/genmc/pass/litmus/SB/SB.order12.stderr delete mode 100644 tests/genmc/pass/litmus/SB/SB.order21.stderr rename tests/genmc/pass/litmus/{SB+2sc+scf => }/SB_2sc_scf.rs (95%) rename tests/genmc/pass/litmus/{SB+2sc+scf => }/SB_2sc_scf.stderr (100%) delete mode 100644 tests/genmc/pass/litmus/Z6+acq/Z6_acq.order321.stderr rename tests/genmc/pass/litmus/{Z6.U0 => }/Z6_U0.rs (96%) rename tests/genmc/pass/litmus/{Z6.U0 => }/Z6_U0.stderr (100%) rename tests/genmc/pass/litmus/{Z6+acq => }/Z6_acq.rs (74%) rename tests/genmc/pass/litmus/{Z6+acq/Z6_acq.order123.stderr => Z6_acq.stderr} (100%) rename tests/genmc/pass/litmus/{assume-ctrl => }/assume-ctrl.rs (93%) rename tests/genmc/pass/litmus/{assume-ctrl => }/assume-ctrl.stderr (100%) rename tests/genmc/pass/litmus/{atomicpo => }/atomicpo.rs (95%) rename tests/genmc/pass/litmus/{atomicpo => }/atomicpo.stderr (100%) rename tests/genmc/pass/litmus/{casdep => }/casdep.rs (95%) rename tests/genmc/pass/litmus/{casdep => }/casdep.stderr (100%) rename tests/genmc/pass/litmus/{ccr => }/ccr.rs (95%) rename tests/genmc/pass/litmus/{ccr => }/ccr.stderr (100%) rename tests/genmc/pass/litmus/{cii => }/cii.rs (95%) rename tests/genmc/pass/litmus/{cii => }/cii.stderr (100%) create mode 100644 tests/genmc/pass/litmus/corr.rs rename tests/genmc/pass/{basics/mutex/mutex_simple.order12reps2.stderr => litmus/corr.stderr} (100%) rename tests/genmc/pass/litmus/{CoRR0 => }/corr0.rs (57%) rename tests/genmc/pass/litmus/{2CoWR/2cowr.order2341.stderr => corr0.stderr} (100%) create mode 100644 tests/genmc/pass/litmus/corr1.rs rename tests/genmc/pass/litmus/{CoRR1/corr1.order1234.stderr => corr1.stderr} (100%) rename tests/genmc/pass/litmus/{default/default.rs => corr2.rs} (59%) rename tests/genmc/pass/litmus/{CoRR2/corr2.order1234.stderr => corr2.stderr} (100%) rename tests/genmc/pass/litmus/{CoRW => }/corw.rs (65%) rename tests/genmc/pass/litmus/{CoRW/corw.order21.stderr => corw.stderr} (100%) rename tests/genmc/pass/litmus/{CoWR => }/cowr.rs (65%) rename tests/genmc/pass/litmus/{CoWR/cowr.order12.stderr => cowr.stderr} (100%) rename tests/genmc/pass/litmus/{cumul-release => }/cumul-release.rs (96%) rename tests/genmc/pass/litmus/{cumul-release => }/cumul-release.stderr (100%) rename tests/genmc/pass/litmus/{CoRR/corr.rs => default.rs} (66%) rename tests/genmc/pass/litmus/{default/default.order123.stderr => default.stderr} (100%) delete mode 100644 tests/genmc/pass/litmus/default/default.order231.stderr delete mode 100644 tests/genmc/pass/litmus/default/default.order312.stderr delete mode 100644 tests/genmc/pass/litmus/default/default.order321.stderr rename tests/genmc/pass/litmus/{detour => }/detour.rs (96%) rename tests/genmc/pass/litmus/{detour => }/detour.stderr (100%) rename tests/genmc/pass/litmus/{fr_w_w_w_reads => }/fr_w_w_w_reads.rs (87%) rename tests/genmc/pass/litmus/{fr_w_w_w_reads => }/fr_w_w_w_reads.stderr (100%) rename tests/genmc/pass/litmus/{inc2w => }/inc2w.rs (87%) rename tests/genmc/pass/litmus/{inc2w => }/inc2w.stderr (100%) rename tests/genmc/pass/litmus/{inc_inc_RR_W_RR => }/inc_inc_RR_W_RR.rs (89%) rename tests/genmc/pass/litmus/{inc_inc_RR_W_RR => }/inc_inc_RR_W_RR.stderr (100%) rename tests/genmc/pass/litmus/{riwi => }/riwi.rs (95%) rename tests/genmc/pass/litmus/{riwi => }/riwi.stderr (100%) rename tests/genmc/pass/litmus/{viktor-relseq => }/viktor-relseq.rs (96%) rename tests/genmc/pass/litmus/{viktor-relseq => }/viktor-relseq.stderr (100%) rename tests/genmc/pass/loom/{load-buffering => }/load_buffering.genmc.stderr (100%) rename tests/genmc/pass/loom/{load-buffering => }/load_buffering.genmc_std.stderr (100%) rename tests/genmc/pass/loom/{load-buffering => }/load_buffering.rs (98%) rename tests/genmc/pass/{basics/mutex/mutex_simple.order12reps1.stderr => std/mutex_simple.reps1.stderr} (100%) rename tests/genmc/pass/{basics/mutex/mutex_simple.order21reps2.stderr => std/mutex_simple.reps2.stderr} (100%) rename tests/genmc/pass/{basics/mutex => std}/mutex_simple.rs (91%) rename tests/genmc/pass/{simple/simple_main.rs => thread/empty_main.rs} (100%) rename tests/genmc/pass/{simple/simple_main.stderr => thread/empty_main.stderr} (100%) rename tests/genmc/pass/{basics/libc-exit => thread}/libc_exit.rs.disabled (96%) rename tests/genmc/pass/{simple => thread}/miri_main_spawn_pthreads.rs (97%) rename tests/genmc/pass/{simple => thread}/miri_main_spawn_pthreads.stderr (100%) rename tests/genmc/pass/{simple/simple_miri_main_spawn_threads.rs => thread/miri_main_spawn_threads.rs} (94%) rename tests/genmc/pass/{simple/simple_miri_main_spawn_threads.stderr => thread/miri_main_spawn_threads.stderr} (100%) rename tests/genmc/pass/{simple/simple_main_spawn_threads.rs => thread/std_main_spawn_threads.rs} (92%) rename tests/genmc/pass/{simple/simple_main_spawn_threads.stderr => thread/std_main_spawn_threads.stderr} (100%) rename tests/genmc/pass/{simple => thread}/thread_locals.rs (100%) rename tests/genmc/pass/{simple => thread}/thread_locals.stderr (100%) rename tests/genmc/pass/{simple => thread}/thread_simple.rs (100%) rename tests/genmc/pass/{simple => thread}/thread_simple.stderr (100%) diff --git a/src/eval.rs b/src/eval.rs index c4f9fcbb4f..a19eecb83c 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -306,7 +306,11 @@ impl<'tcx> MainThreadState<'tcx> { // In GenMC mode, we let GenMC decide what happens on main thread exit. if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() { // If there's no error, execution will continue (on another thread). - genmc_ctx.handle_exit(ThreadId::MAIN_THREAD, exit_code, /* is_exit_call */ false)?; + genmc_ctx.handle_exit( + ThreadId::MAIN_THREAD, + exit_code, + /* is_exit_call */ false, + )?; } else { // Stop interpreter loop. throw_machine_stop!(TerminationInfo::Exit { diff --git a/tests/genmc/fail/loom/buggy-inc/buggy_inc.rs b/tests/genmc/fail/loom/buggy_inc.rs similarity index 97% rename from tests/genmc/fail/loom/buggy-inc/buggy_inc.rs rename to tests/genmc/fail/loom/buggy_inc.rs index 8ed18412b7..7208f4e85b 100644 --- a/tests/genmc/fail/loom/buggy-inc/buggy_inc.rs +++ b/tests/genmc/fail/loom/buggy_inc.rs @@ -9,7 +9,7 @@ #![no_main] #[cfg(not(any(non_genmc_std, genmc_std)))] -#[path = "../../../../utils/genmc.rs"] +#[path = "../../../utils/genmc.rs"] mod genmc; use std::ffi::c_void; diff --git a/tests/genmc/fail/loom/buggy-inc/buggy_inc.stderr b/tests/genmc/fail/loom/buggy_inc.stderr similarity index 81% rename from tests/genmc/fail/loom/buggy-inc/buggy_inc.stderr rename to tests/genmc/fail/loom/buggy_inc.stderr index 6b87d1c736..7617864a42 100644 --- a/tests/genmc/fail/loom/buggy-inc/buggy_inc.stderr +++ b/tests/genmc/fail/loom/buggy_inc.stderr @@ -1,5 +1,5 @@ error: Undefined Behavior: entering unreachable code - --> tests/genmc/fail/loom/buggy-inc/buggy_inc.rs:LL:CC + --> tests/genmc/fail/loom/buggy_inc.rs:LL:CC | LL | unsafe { std::hint::unreachable_unchecked() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here @@ -7,7 +7,7 @@ LL | unsafe { std::hint::unreachable_unchecked() }; = 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 = note: BACKTRACE: - = note: inside `miri_start` at tests/genmc/fail/loom/buggy-inc/buggy_inc.rs:LL:CC + = note: inside `miri_start` at tests/genmc/fail/loom/buggy_inc.rs:LL:CC note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/tests/genmc/fail/loom/store-buffering/store_buffering.genmc.stderr b/tests/genmc/fail/loom/store_buffering.genmc.stderr similarity index 78% rename from tests/genmc/fail/loom/store-buffering/store_buffering.genmc.stderr rename to tests/genmc/fail/loom/store_buffering.genmc.stderr index 9fa6ca9b1d..33c65f3ce6 100644 --- a/tests/genmc/fail/loom/store-buffering/store_buffering.genmc.stderr +++ b/tests/genmc/fail/loom/store_buffering.genmc.stderr @@ -1,5 +1,5 @@ error: Undefined Behavior: entering unreachable code - --> tests/genmc/fail/loom/store-buffering/store_buffering.rs:LL:CC + --> tests/genmc/fail/loom/store_buffering.rs:LL:CC | LL | unsafe { std::hint::unreachable_unchecked() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here @@ -7,7 +7,7 @@ LL | unsafe { std::hint::unreachable_unchecked() }; = 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 = note: BACKTRACE: - = note: inside `miri_start` at tests/genmc/fail/loom/store-buffering/store_buffering.rs:LL:CC + = note: inside `miri_start` at tests/genmc/fail/loom/store_buffering.rs:LL:CC note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/tests/genmc/fail/loom/store-buffering/store_buffering.genmc_std.stderr b/tests/genmc/fail/loom/store_buffering.genmc_std.stderr similarity index 88% rename from tests/genmc/fail/loom/store-buffering/store_buffering.genmc_std.stderr rename to tests/genmc/fail/loom/store_buffering.genmc_std.stderr index 61babf0493..d23bc640a7 100644 --- a/tests/genmc/fail/loom/store-buffering/store_buffering.genmc_std.stderr +++ b/tests/genmc/fail/loom/store_buffering.genmc_std.stderr @@ -5,7 +5,7 @@ warning: GenMC mode currently does not model spurious failures of `compare_excha warning: GenMC currently does not model the atomic failure ordering for `compare_exchange`. Failure ordering 'Acquire' is treated like 'Relaxed', which means that Miri might incorrectly detect errors related to this memory access (possible false positives). error: Undefined Behavior: entering unreachable code - --> tests/genmc/fail/loom/store-buffering/store_buffering.rs:LL:CC + --> tests/genmc/fail/loom/store_buffering.rs:LL:CC | LL | unsafe { std::hint::unreachable_unchecked() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here @@ -13,7 +13,7 @@ LL | unsafe { std::hint::unreachable_unchecked() }; = 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 = note: BACKTRACE: - = note: inside `miri_start` at tests/genmc/fail/loom/store-buffering/store_buffering.rs:LL:CC + = note: inside `miri_start` at tests/genmc/fail/loom/store_buffering.rs:LL:CC note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/tests/genmc/fail/loom/store-buffering/store_buffering.non_genmc.stderr b/tests/genmc/fail/loom/store_buffering.non_genmc.stderr similarity index 78% rename from tests/genmc/fail/loom/store-buffering/store_buffering.non_genmc.stderr rename to tests/genmc/fail/loom/store_buffering.non_genmc.stderr index 9fa6ca9b1d..33c65f3ce6 100644 --- a/tests/genmc/fail/loom/store-buffering/store_buffering.non_genmc.stderr +++ b/tests/genmc/fail/loom/store_buffering.non_genmc.stderr @@ -1,5 +1,5 @@ error: Undefined Behavior: entering unreachable code - --> tests/genmc/fail/loom/store-buffering/store_buffering.rs:LL:CC + --> tests/genmc/fail/loom/store_buffering.rs:LL:CC | LL | unsafe { std::hint::unreachable_unchecked() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here @@ -7,7 +7,7 @@ LL | unsafe { std::hint::unreachable_unchecked() }; = 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 = note: BACKTRACE: - = note: inside `miri_start` at tests/genmc/fail/loom/store-buffering/store_buffering.rs:LL:CC + = note: inside `miri_start` at tests/genmc/fail/loom/store_buffering.rs:LL:CC note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/tests/genmc/fail/loom/store-buffering/store_buffering.non_genmc_std.stderr b/tests/genmc/fail/loom/store_buffering.non_genmc_std.stderr similarity index 78% rename from tests/genmc/fail/loom/store-buffering/store_buffering.non_genmc_std.stderr rename to tests/genmc/fail/loom/store_buffering.non_genmc_std.stderr index 9fa6ca9b1d..33c65f3ce6 100644 --- a/tests/genmc/fail/loom/store-buffering/store_buffering.non_genmc_std.stderr +++ b/tests/genmc/fail/loom/store_buffering.non_genmc_std.stderr @@ -1,5 +1,5 @@ error: Undefined Behavior: entering unreachable code - --> tests/genmc/fail/loom/store-buffering/store_buffering.rs:LL:CC + --> tests/genmc/fail/loom/store_buffering.rs:LL:CC | LL | unsafe { std::hint::unreachable_unchecked() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here @@ -7,7 +7,7 @@ LL | unsafe { std::hint::unreachable_unchecked() }; = 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 = note: BACKTRACE: - = note: inside `miri_start` at tests/genmc/fail/loom/store-buffering/store_buffering.rs:LL:CC + = note: inside `miri_start` at tests/genmc/fail/loom/store_buffering.rs:LL:CC note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/tests/genmc/fail/loom/store-buffering/store_buffering.rs b/tests/genmc/fail/loom/store_buffering.rs similarity index 98% rename from tests/genmc/fail/loom/store-buffering/store_buffering.rs rename to tests/genmc/fail/loom/store_buffering.rs index 174ac8e080..eeeeeefeb5 100644 --- a/tests/genmc/fail/loom/store-buffering/store_buffering.rs +++ b/tests/genmc/fail/loom/store_buffering.rs @@ -10,7 +10,7 @@ #![no_main] #[cfg(not(any(non_genmc_std, genmc_std)))] -#[path = "../../../../utils/genmc.rs"] +#[path = "../../../utils/genmc.rs"] mod genmc; use std::sync::atomic::AtomicUsize; diff --git a/tests/genmc/pass/simple/atomic_ptr.rs b/tests/genmc/pass/atomics/atomic_ptr.rs similarity index 100% rename from tests/genmc/pass/simple/atomic_ptr.rs rename to tests/genmc/pass/atomics/atomic_ptr.rs diff --git a/tests/genmc/pass/simple/atomic_ptr.stderr b/tests/genmc/pass/atomics/atomic_ptr.stderr similarity index 100% rename from tests/genmc/pass/simple/atomic_ptr.stderr rename to tests/genmc/pass/atomics/atomic_ptr.stderr diff --git a/tests/genmc/pass/simple/cas_simple.rs b/tests/genmc/pass/atomics/cas_simple.rs similarity index 100% rename from tests/genmc/pass/simple/cas_simple.rs rename to tests/genmc/pass/atomics/cas_simple.rs diff --git a/tests/genmc/pass/simple/cas_simple.stderr b/tests/genmc/pass/atomics/cas_simple.stderr similarity index 100% rename from tests/genmc/pass/simple/cas_simple.stderr rename to tests/genmc/pass/atomics/cas_simple.stderr diff --git a/tests/genmc/pass/basics/rmw/rmw_edge_cases.i16_.stderr b/tests/genmc/pass/atomics/rmw_edge_cases.i16_.stderr similarity index 100% rename from tests/genmc/pass/basics/rmw/rmw_edge_cases.i16_.stderr rename to tests/genmc/pass/atomics/rmw_edge_cases.i16_.stderr diff --git a/tests/genmc/pass/basics/rmw/rmw_edge_cases.i32_.stderr b/tests/genmc/pass/atomics/rmw_edge_cases.i32_.stderr similarity index 100% rename from tests/genmc/pass/basics/rmw/rmw_edge_cases.i32_.stderr rename to tests/genmc/pass/atomics/rmw_edge_cases.i32_.stderr diff --git a/tests/genmc/pass/basics/rmw/rmw_edge_cases.i64_.stderr b/tests/genmc/pass/atomics/rmw_edge_cases.i64_.stderr similarity index 100% rename from tests/genmc/pass/basics/rmw/rmw_edge_cases.i64_.stderr rename to tests/genmc/pass/atomics/rmw_edge_cases.i64_.stderr diff --git a/tests/genmc/pass/basics/rmw/rmw_edge_cases.i8_.stderr b/tests/genmc/pass/atomics/rmw_edge_cases.i8_.stderr similarity index 100% rename from tests/genmc/pass/basics/rmw/rmw_edge_cases.i8_.stderr rename to tests/genmc/pass/atomics/rmw_edge_cases.i8_.stderr diff --git a/tests/genmc/pass/basics/rmw/rmw_edge_cases.isize_.stderr b/tests/genmc/pass/atomics/rmw_edge_cases.isize_.stderr similarity index 100% rename from tests/genmc/pass/basics/rmw/rmw_edge_cases.isize_.stderr rename to tests/genmc/pass/atomics/rmw_edge_cases.isize_.stderr diff --git a/tests/genmc/pass/basics/rmw/rmw_edge_cases.rs b/tests/genmc/pass/atomics/rmw_edge_cases.rs similarity index 100% rename from tests/genmc/pass/basics/rmw/rmw_edge_cases.rs rename to tests/genmc/pass/atomics/rmw_edge_cases.rs diff --git a/tests/genmc/pass/basics/rmw/rmw_edge_cases.u16_.stderr b/tests/genmc/pass/atomics/rmw_edge_cases.u16_.stderr similarity index 100% rename from tests/genmc/pass/basics/rmw/rmw_edge_cases.u16_.stderr rename to tests/genmc/pass/atomics/rmw_edge_cases.u16_.stderr diff --git a/tests/genmc/pass/basics/rmw/rmw_edge_cases.u32_.stderr b/tests/genmc/pass/atomics/rmw_edge_cases.u32_.stderr similarity index 100% rename from tests/genmc/pass/basics/rmw/rmw_edge_cases.u32_.stderr rename to tests/genmc/pass/atomics/rmw_edge_cases.u32_.stderr diff --git a/tests/genmc/pass/basics/rmw/rmw_edge_cases.u64_.stderr b/tests/genmc/pass/atomics/rmw_edge_cases.u64_.stderr similarity index 100% rename from tests/genmc/pass/basics/rmw/rmw_edge_cases.u64_.stderr rename to tests/genmc/pass/atomics/rmw_edge_cases.u64_.stderr diff --git a/tests/genmc/pass/basics/rmw/rmw_edge_cases.u8_.stderr b/tests/genmc/pass/atomics/rmw_edge_cases.u8_.stderr similarity index 100% rename from tests/genmc/pass/basics/rmw/rmw_edge_cases.u8_.stderr rename to tests/genmc/pass/atomics/rmw_edge_cases.u8_.stderr diff --git a/tests/genmc/pass/basics/rmw/rmw_edge_cases.usize_.stderr b/tests/genmc/pass/atomics/rmw_edge_cases.usize_.stderr similarity index 100% rename from tests/genmc/pass/basics/rmw/rmw_edge_cases.usize_.stderr rename to tests/genmc/pass/atomics/rmw_edge_cases.usize_.stderr diff --git a/tests/genmc/pass/basics/rmw/rmw_simple.rs b/tests/genmc/pass/atomics/rmw_simple.rs similarity index 100% rename from tests/genmc/pass/basics/rmw/rmw_simple.rs rename to tests/genmc/pass/atomics/rmw_simple.rs diff --git a/tests/genmc/pass/basics/rmw/rmw_simple.stderr b/tests/genmc/pass/atomics/rmw_simple.stderr similarity index 100% rename from tests/genmc/pass/basics/rmw/rmw_simple.stderr rename to tests/genmc/pass/atomics/rmw_simple.stderr diff --git a/tests/genmc/pass/simple/stack_alloc_atomic.rs b/tests/genmc/pass/atomics/stack_alloc_atomic.rs similarity index 100% rename from tests/genmc/pass/simple/stack_alloc_atomic.rs rename to tests/genmc/pass/atomics/stack_alloc_atomic.rs diff --git a/tests/genmc/pass/simple/stack_alloc_atomic.stderr b/tests/genmc/pass/atomics/stack_alloc_atomic.stderr similarity index 100% rename from tests/genmc/pass/simple/stack_alloc_atomic.stderr rename to tests/genmc/pass/atomics/stack_alloc_atomic.stderr diff --git a/tests/genmc/pass/basics/mutex/TODO_mutex_get_mut.rs.txt b/tests/genmc/pass/basics/mutex/TODO_mutex_get_mut.rs.txt deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/tests/genmc/pass/basics/mutex/mutex_simple.c b/tests/genmc/pass/basics/mutex/mutex_simple.c deleted file mode 100644 index eb9c87aa82..0000000000 --- a/tests/genmc/pass/basics/mutex/mutex_simple.c +++ /dev/null @@ -1,72 +0,0 @@ -#include -#include -#include -#include -#include - -#define REPS 1 - -static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; -static uint64_t data[32]; - -void* thread_1(void* arg) { - for (uint64_t i = 0; i < REPS; i++) { - pthread_mutex_lock(&lock); - data[0] += 2; - pthread_mutex_unlock(&lock); - } - return NULL; -} - -void* thread_2(void* arg) { - for (uint64_t i = 0; i < REPS; i++) { - pthread_mutex_lock(&lock); - data[0] += 4; - pthread_mutex_unlock(&lock); - } - return NULL; -} - -int main(int argc, char** argv) { - // Initialize data - for (int i = 0; i < 32; i++) { - data[i] = 1234; - } - - pthread_mutex_lock(&lock); - for (int i = 0; i < 32; i++) { - assert(data[i] == 1234); - } - data[0] = 0; - data[1] = 10; - assert(data[0] == 0 && data[1] == 10); - pthread_mutex_unlock(&lock); - - // Thread order: can be changed for different test orders -#ifdef ORDER21 - void* (*thread_order[2])(void*) = {thread_2, thread_1}; -#else - void* (*thread_order[2])(void*) = {thread_1, thread_2}; -#endif - - pthread_t ids[2]; - for (int i = 0; i < 2; i++) { - int ret = pthread_create(&ids[i], NULL, thread_order[i], NULL); - assert(ret == 0); - } - - for (int i = 0; i < 2; i++) { - int ret = pthread_join(ids[i], NULL); - assert(ret == 0); - } - - pthread_mutex_lock(&lock); - // assert(data[0] == REPS * 6); // Not checked, but can be enabled - assert(data[1] == 10); - for (int i = 2; i < 32; i++) { - assert(data[i] == 1234); - } - pthread_mutex_unlock(&lock); - - return 0; -} diff --git a/tests/genmc/pass/basics/mutex/mutex_simple.order21reps1.stderr b/tests/genmc/pass/basics/mutex/mutex_simple.order21reps1.stderr deleted file mode 100644 index edecd38278..0000000000 --- a/tests/genmc/pass/basics/mutex/mutex_simple.order21reps1.stderr +++ /dev/null @@ -1,3 +0,0 @@ - -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 2 diff --git a/tests/genmc/pass/litmus/2CoWR/2cowr.order3412.stderr b/tests/genmc/pass/litmus/2CoWR/2cowr.order3412.stderr deleted file mode 100644 index 61dc3f60ba..0000000000 --- a/tests/genmc/pass/litmus/2CoWR/2cowr.order3412.stderr +++ /dev/null @@ -1,3 +0,0 @@ - -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 4 diff --git a/tests/genmc/pass/litmus/2CoWR/2cowr.order4123.stderr b/tests/genmc/pass/litmus/2CoWR/2cowr.order4123.stderr deleted file mode 100644 index 61dc3f60ba..0000000000 --- a/tests/genmc/pass/litmus/2CoWR/2cowr.order4123.stderr +++ /dev/null @@ -1,3 +0,0 @@ - -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 4 diff --git a/tests/genmc/pass/litmus/2CoWR/2cowr.order4321.stderr b/tests/genmc/pass/litmus/2CoWR/2cowr.order4321.stderr deleted file mode 100644 index 61dc3f60ba..0000000000 --- a/tests/genmc/pass/litmus/2CoWR/2cowr.order4321.stderr +++ /dev/null @@ -1,3 +0,0 @@ - -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 4 diff --git a/tests/genmc/pass/litmus/2+2W+3sc+rel1/2_2w_3sc_rel1.rs b/tests/genmc/pass/litmus/2_2w_3sc_rel1.rs similarity index 95% rename from tests/genmc/pass/litmus/2+2W+3sc+rel1/2_2w_3sc_rel1.rs rename to tests/genmc/pass/litmus/2_2w_3sc_rel1.rs index 6cc9d47f3a..df219021da 100644 --- a/tests/genmc/pass/litmus/2+2W+3sc+rel1/2_2w_3sc_rel1.rs +++ b/tests/genmc/pass/litmus/2_2w_3sc_rel1.rs @@ -2,7 +2,7 @@ #![no_main] -#[path = "../../../../utils/genmc.rs"] +#[path = "../../../utils/genmc.rs"] mod genmc; use std::ffi::c_void; diff --git a/tests/genmc/pass/litmus/2+2W+3sc+rel1/2_2w_3sc_rel1.stderr b/tests/genmc/pass/litmus/2_2w_3sc_rel1.stderr similarity index 100% rename from tests/genmc/pass/litmus/2+2W+3sc+rel1/2_2w_3sc_rel1.stderr rename to tests/genmc/pass/litmus/2_2w_3sc_rel1.stderr diff --git a/tests/genmc/pass/litmus/2+2W+4sc/2_2w_4sc.rs b/tests/genmc/pass/litmus/2_2w_4sc.rs similarity index 95% rename from tests/genmc/pass/litmus/2+2W+4sc/2_2w_4sc.rs rename to tests/genmc/pass/litmus/2_2w_4sc.rs index 13a08652c2..9c500e5012 100644 --- a/tests/genmc/pass/litmus/2+2W+4sc/2_2w_4sc.rs +++ b/tests/genmc/pass/litmus/2_2w_4sc.rs @@ -2,7 +2,7 @@ #![no_main] -#[path = "../../../../utils/genmc.rs"] +#[path = "../../../utils/genmc.rs"] mod genmc; use std::ffi::c_void; diff --git a/tests/genmc/pass/litmus/2+2W+4sc/2_2w_4sc.stderr b/tests/genmc/pass/litmus/2_2w_4sc.stderr similarity index 100% rename from tests/genmc/pass/litmus/2+2W+4sc/2_2w_4sc.stderr rename to tests/genmc/pass/litmus/2_2w_4sc.stderr diff --git a/tests/genmc/pass/litmus/2+2W+scfs/2_2w_scfs.rs b/tests/genmc/pass/litmus/2_2w_scfs.rs similarity index 95% rename from tests/genmc/pass/litmus/2+2W+scfs/2_2w_scfs.rs rename to tests/genmc/pass/litmus/2_2w_scfs.rs index dd4dabd823..d318b4f4cb 100644 --- a/tests/genmc/pass/litmus/2+2W+scfs/2_2w_scfs.rs +++ b/tests/genmc/pass/litmus/2_2w_scfs.rs @@ -2,7 +2,7 @@ #![no_main] -#[path = "../../../../utils/genmc.rs"] +#[path = "../../../utils/genmc.rs"] mod genmc; use std::ffi::c_void; diff --git a/tests/genmc/pass/litmus/2+2W+scfs/2_2w_scfs.stderr b/tests/genmc/pass/litmus/2_2w_scfs.stderr similarity index 100% rename from tests/genmc/pass/litmus/2+2W+scfs/2_2w_scfs.stderr rename to tests/genmc/pass/litmus/2_2w_scfs.stderr diff --git a/tests/genmc/pass/litmus/2CoWR/2cowr.rs b/tests/genmc/pass/litmus/2cowr.rs similarity index 57% rename from tests/genmc/pass/litmus/2CoWR/2cowr.rs rename to tests/genmc/pass/litmus/2cowr.rs index c5604fd57c..9078539e1d 100644 --- a/tests/genmc/pass/litmus/2CoWR/2cowr.rs +++ b/tests/genmc/pass/litmus/2cowr.rs @@ -1,9 +1,8 @@ //@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows -//@revisions: order1234 order2341 order3412 order4123 order4321 #![no_main] -#[path = "../../../../utils/genmc.rs"] +#[path = "../../../utils/genmc.rs"] mod genmc; use std::ffi::c_void; @@ -17,21 +16,7 @@ static Y: AtomicU64 = AtomicU64::new(0); #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - let thread_order = if cfg!(order1234) { - [thread_1, thread_2, thread_3, thread_4] - } else if cfg!(order2341) { - [thread_2, thread_3, thread_4, thread_1] - } else if cfg!(order3412) { - [thread_3, thread_4, thread_1, thread_2] - } else if cfg!(order4123) { - [thread_4, thread_1, thread_2, thread_3] - } else if cfg!(order4321) { - [thread_4, thread_3, thread_2, thread_1] - } else { - unimplemented!(); - }; - - let _ids = unsafe { create_pthreads_no_params(thread_order) }; + let _ids = unsafe { create_pthreads_no_params([thread_1, thread_2, thread_3, thread_4]) }; 0 } diff --git a/tests/genmc/pass/litmus/2+2w/2W2W.order12.stderr b/tests/genmc/pass/litmus/2cowr.stderr similarity index 100% rename from tests/genmc/pass/litmus/2+2w/2W2W.order12.stderr rename to tests/genmc/pass/litmus/2cowr.stderr diff --git a/tests/genmc/pass/litmus/2+2w/2W2W.rs b/tests/genmc/pass/litmus/2w2w.rs similarity index 68% rename from tests/genmc/pass/litmus/2+2w/2W2W.rs rename to tests/genmc/pass/litmus/2w2w.rs index f52916d332..629634af09 100644 --- a/tests/genmc/pass/litmus/2+2w/2W2W.rs +++ b/tests/genmc/pass/litmus/2w2w.rs @@ -1,9 +1,8 @@ //@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows -//@revisions: order12 order21 #![no_main] -#[path = "../../../../utils/genmc.rs"] +#[path = "../../../utils/genmc.rs"] mod genmc; use std::ffi::c_void; @@ -16,15 +15,7 @@ use crate::genmc::*; #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - let thread_order = if cfg!(order12) { - [thread_1, thread_2] - } else if cfg!(order21) { - [thread_2, thread_1] - } else { - unimplemented!(); - }; - - let _ids = unsafe { create_pthreads_no_params(thread_order) }; + let _ids = unsafe { create_pthreads_no_params([thread_1, thread_2]) }; 0 } diff --git a/tests/genmc/pass/litmus/2+2w/2W2W.order21.stderr b/tests/genmc/pass/litmus/2w2w.stderr similarity index 100% rename from tests/genmc/pass/litmus/2+2w/2W2W.order21.stderr rename to tests/genmc/pass/litmus/2w2w.stderr diff --git a/tests/genmc/pass/litmus/2+2W+2sc+scf/2w2w_2sc_scf.rs b/tests/genmc/pass/litmus/2w2w_2sc_scf.rs similarity index 69% rename from tests/genmc/pass/litmus/2+2W+2sc+scf/2w2w_2sc_scf.rs rename to tests/genmc/pass/litmus/2w2w_2sc_scf.rs index b480ba415b..7e833b1b6c 100644 --- a/tests/genmc/pass/litmus/2+2W+2sc+scf/2w2w_2sc_scf.rs +++ b/tests/genmc/pass/litmus/2w2w_2sc_scf.rs @@ -1,9 +1,8 @@ //@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows -//@revisions: order12 order21 #![no_main] -#[path = "../../../../utils/genmc.rs"] +#[path = "../../../utils/genmc.rs"] mod genmc; use std::ffi::c_void; @@ -16,15 +15,7 @@ use crate::genmc::*; #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - let thread_order = if cfg!(order12) { - [thread_1, thread_2] - } else if cfg!(order21) { - [thread_2, thread_1] - } else { - unimplemented!(); - }; - - let _ids = unsafe { create_pthreads_no_params(thread_order) }; + let _ids = unsafe { create_pthreads_no_params([thread_1, thread_2]) }; 0 } diff --git a/tests/genmc/pass/litmus/2+2W+2sc+scf/2w2w_2sc_scf.order12.stderr b/tests/genmc/pass/litmus/2w2w_2sc_scf.stderr similarity index 100% rename from tests/genmc/pass/litmus/2+2W+2sc+scf/2w2w_2sc_scf.order12.stderr rename to tests/genmc/pass/litmus/2w2w_2sc_scf.stderr diff --git a/tests/genmc/pass/litmus/CoRR/corr.order12.stderr b/tests/genmc/pass/litmus/CoRR/corr.order12.stderr deleted file mode 100644 index e4151d346e..0000000000 --- a/tests/genmc/pass/litmus/CoRR/corr.order12.stderr +++ /dev/null @@ -1,3 +0,0 @@ - -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 6 diff --git a/tests/genmc/pass/litmus/CoRR/corr.order21.stderr b/tests/genmc/pass/litmus/CoRR/corr.order21.stderr deleted file mode 100644 index e4151d346e..0000000000 --- a/tests/genmc/pass/litmus/CoRR/corr.order21.stderr +++ /dev/null @@ -1,3 +0,0 @@ - -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 6 diff --git a/tests/genmc/pass/litmus/CoRR0/corr0.order123.stderr b/tests/genmc/pass/litmus/CoRR0/corr0.order123.stderr deleted file mode 100644 index 61dc3f60ba..0000000000 --- a/tests/genmc/pass/litmus/CoRR0/corr0.order123.stderr +++ /dev/null @@ -1,3 +0,0 @@ - -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 4 diff --git a/tests/genmc/pass/litmus/CoRR0/corr0.order231.stderr b/tests/genmc/pass/litmus/CoRR0/corr0.order231.stderr deleted file mode 100644 index 61dc3f60ba..0000000000 --- a/tests/genmc/pass/litmus/CoRR0/corr0.order231.stderr +++ /dev/null @@ -1,3 +0,0 @@ - -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 4 diff --git a/tests/genmc/pass/litmus/CoRR0/corr0.order312.stderr b/tests/genmc/pass/litmus/CoRR0/corr0.order312.stderr deleted file mode 100644 index 61dc3f60ba..0000000000 --- a/tests/genmc/pass/litmus/CoRR0/corr0.order312.stderr +++ /dev/null @@ -1,3 +0,0 @@ - -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 4 diff --git a/tests/genmc/pass/litmus/CoRR0/corr0.order321.stderr b/tests/genmc/pass/litmus/CoRR0/corr0.order321.stderr deleted file mode 100644 index 61dc3f60ba..0000000000 --- a/tests/genmc/pass/litmus/CoRR0/corr0.order321.stderr +++ /dev/null @@ -1,3 +0,0 @@ - -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 4 diff --git a/tests/genmc/pass/litmus/CoRR1/corr1.order2341.stderr b/tests/genmc/pass/litmus/CoRR1/corr1.order2341.stderr deleted file mode 100644 index c2ffa8ec3c..0000000000 --- a/tests/genmc/pass/litmus/CoRR1/corr1.order2341.stderr +++ /dev/null @@ -1,3 +0,0 @@ - -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 36 diff --git a/tests/genmc/pass/litmus/CoRR1/corr1.order3412.stderr b/tests/genmc/pass/litmus/CoRR1/corr1.order3412.stderr deleted file mode 100644 index c2ffa8ec3c..0000000000 --- a/tests/genmc/pass/litmus/CoRR1/corr1.order3412.stderr +++ /dev/null @@ -1,3 +0,0 @@ - -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 36 diff --git a/tests/genmc/pass/litmus/CoRR1/corr1.order4123.stderr b/tests/genmc/pass/litmus/CoRR1/corr1.order4123.stderr deleted file mode 100644 index c2ffa8ec3c..0000000000 --- a/tests/genmc/pass/litmus/CoRR1/corr1.order4123.stderr +++ /dev/null @@ -1,3 +0,0 @@ - -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 36 diff --git a/tests/genmc/pass/litmus/CoRR1/corr1.order4321.stderr b/tests/genmc/pass/litmus/CoRR1/corr1.order4321.stderr deleted file mode 100644 index c2ffa8ec3c..0000000000 --- a/tests/genmc/pass/litmus/CoRR1/corr1.order4321.stderr +++ /dev/null @@ -1,3 +0,0 @@ - -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 36 diff --git a/tests/genmc/pass/litmus/CoRR1/corr1.rs b/tests/genmc/pass/litmus/CoRR1/corr1.rs deleted file mode 100644 index 58ef3c7691..0000000000 --- a/tests/genmc/pass/litmus/CoRR1/corr1.rs +++ /dev/null @@ -1,57 +0,0 @@ -//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows -//@revisions: order1234 order4321 order4123 order3412 order2341 - -#![no_main] - -#[path = "../../../../utils/genmc.rs"] -mod genmc; - -use std::ffi::c_void; -use std::sync::atomic::{AtomicU64, Ordering}; - -static X: AtomicU64 = AtomicU64::new(0); - -use crate::genmc::*; - -#[unsafe(no_mangle)] -fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - let thread_order = if cfg!(order1234) { - [thread_1, thread_2, thread_3, thread_4] - } else if cfg!(order4321) { - [thread_4, thread_3, thread_2, thread_1] - } else if cfg!(order4123) { - [thread_4, thread_1, thread_2, thread_3] - } else if cfg!(order3412) { - [thread_3, thread_4, thread_1, thread_2] - } else if cfg!(order2341) { - [thread_2, thread_3, thread_4, thread_1] - } else { - unimplemented!(); - }; - - let ids = unsafe { create_pthreads_no_params(thread_order) }; - unsafe { join_pthreads(ids) }; - - 0 -} - -extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { - X.store(1, Ordering::Release); - std::ptr::null_mut() -} - -extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { - X.store(2, Ordering::Release); - std::ptr::null_mut() -} - -extern "C" fn thread_3(_value: *mut c_void) -> *mut c_void { - X.load(Ordering::Acquire); - X.load(Ordering::Acquire); - std::ptr::null_mut() -} - -extern "C" fn thread_4(_value: *mut c_void) -> *mut c_void { - X.load(Ordering::Acquire); - std::ptr::null_mut() -} diff --git a/tests/genmc/pass/litmus/CoRR2/corr2.order2341.stderr b/tests/genmc/pass/litmus/CoRR2/corr2.order2341.stderr deleted file mode 100644 index c8cd87ff6b..0000000000 --- a/tests/genmc/pass/litmus/CoRR2/corr2.order2341.stderr +++ /dev/null @@ -1,3 +0,0 @@ - -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 72 diff --git a/tests/genmc/pass/litmus/CoRR2/corr2.order3412.stderr b/tests/genmc/pass/litmus/CoRR2/corr2.order3412.stderr deleted file mode 100644 index c8cd87ff6b..0000000000 --- a/tests/genmc/pass/litmus/CoRR2/corr2.order3412.stderr +++ /dev/null @@ -1,3 +0,0 @@ - -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 72 diff --git a/tests/genmc/pass/litmus/CoRR2/corr2.order4123.stderr b/tests/genmc/pass/litmus/CoRR2/corr2.order4123.stderr deleted file mode 100644 index c8cd87ff6b..0000000000 --- a/tests/genmc/pass/litmus/CoRR2/corr2.order4123.stderr +++ /dev/null @@ -1,3 +0,0 @@ - -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 72 diff --git a/tests/genmc/pass/litmus/CoRR2/corr2.order4321.stderr b/tests/genmc/pass/litmus/CoRR2/corr2.order4321.stderr deleted file mode 100644 index c8cd87ff6b..0000000000 --- a/tests/genmc/pass/litmus/CoRR2/corr2.order4321.stderr +++ /dev/null @@ -1,3 +0,0 @@ - -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 72 diff --git a/tests/genmc/pass/litmus/CoRR2/corr2.rs b/tests/genmc/pass/litmus/CoRR2/corr2.rs deleted file mode 100644 index 16ebc0fe28..0000000000 --- a/tests/genmc/pass/litmus/CoRR2/corr2.rs +++ /dev/null @@ -1,57 +0,0 @@ -//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows -//@revisions: order1234 order4321 order4123 order3412 order2341 - -#![no_main] - -#[path = "../../../../utils/genmc.rs"] -mod genmc; - -use std::ffi::c_void; -use std::sync::atomic::{AtomicU64, Ordering}; - -static X: AtomicU64 = AtomicU64::new(0); - -use crate::genmc::*; - -#[unsafe(no_mangle)] -fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - let thread_order = if cfg!(order1234) { - [thread_1, thread_2, thread_3, thread_4] - } else if cfg!(order4321) { - [thread_4, thread_3, thread_2, thread_1] - } else if cfg!(order4123) { - [thread_4, thread_1, thread_2, thread_3] - } else if cfg!(order3412) { - [thread_3, thread_4, thread_1, thread_2] - } else if cfg!(order2341) { - [thread_2, thread_3, thread_4, thread_1] - } else { - unimplemented!(); - }; - - let _ids = unsafe { create_pthreads_no_params(thread_order) }; - - 0 -} - -extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { - X.store(1, Ordering::Release); - std::ptr::null_mut() -} - -extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { - X.store(2, Ordering::Release); - std::ptr::null_mut() -} - -extern "C" fn thread_3(_value: *mut c_void) -> *mut c_void { - X.load(Ordering::Acquire); - X.load(Ordering::Acquire); - std::ptr::null_mut() -} - -extern "C" fn thread_4(_value: *mut c_void) -> *mut c_void { - X.load(Ordering::Acquire); - X.load(Ordering::Acquire); - std::ptr::null_mut() -} diff --git a/tests/genmc/pass/litmus/CoWR/cowr.order21.stderr b/tests/genmc/pass/litmus/CoWR/cowr.order21.stderr deleted file mode 100644 index f2f3f904fb..0000000000 --- a/tests/genmc/pass/litmus/CoWR/cowr.order21.stderr +++ /dev/null @@ -1,3 +0,0 @@ - -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 3 diff --git a/tests/genmc/pass/litmus/IRIW-acq-sc/IRIW-acq-sc.rs b/tests/genmc/pass/litmus/IRIW-acq-sc.rs similarity index 87% rename from tests/genmc/pass/litmus/IRIW-acq-sc/IRIW-acq-sc.rs rename to tests/genmc/pass/litmus/IRIW-acq-sc.rs index 5172effdd2..af63b6f2ba 100644 --- a/tests/genmc/pass/litmus/IRIW-acq-sc/IRIW-acq-sc.rs +++ b/tests/genmc/pass/litmus/IRIW-acq-sc.rs @@ -2,11 +2,10 @@ #![no_main] -#[path = "../../../../utils/genmc.rs"] +#[path = "../../../utils/genmc.rs"] mod genmc; use std::ffi::c_void; -use std::ptr::null_mut; use std::sync::atomic::{AtomicU64, Ordering}; use crate::genmc::*; @@ -24,22 +23,22 @@ fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { pub extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { X.store(1, Ordering::SeqCst); - null_mut() + std::ptr::null_mut() } pub extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { X.load(Ordering::Acquire); Y.load(Ordering::SeqCst); - null_mut() + std::ptr::null_mut() } pub extern "C" fn thread_3(_value: *mut c_void) -> *mut c_void { Y.load(Ordering::Acquire); X.load(Ordering::SeqCst); - null_mut() + std::ptr::null_mut() } pub extern "C" fn thread_4(_value: *mut c_void) -> *mut c_void { Y.store(1, Ordering::SeqCst); - null_mut() + std::ptr::null_mut() } diff --git a/tests/genmc/pass/litmus/IRIW-acq-sc/IRIW-acq-sc.stderr b/tests/genmc/pass/litmus/IRIW-acq-sc.stderr similarity index 100% rename from tests/genmc/pass/litmus/IRIW-acq-sc/IRIW-acq-sc.stderr rename to tests/genmc/pass/litmus/IRIW-acq-sc.stderr diff --git a/tests/genmc/pass/litmus/IRIWish/IRIWish.rs b/tests/genmc/pass/litmus/IRIWish.rs similarity index 89% rename from tests/genmc/pass/litmus/IRIWish/IRIWish.rs rename to tests/genmc/pass/litmus/IRIWish.rs index 17bff3311c..14197ccd23 100644 --- a/tests/genmc/pass/litmus/IRIWish/IRIWish.rs +++ b/tests/genmc/pass/litmus/IRIWish.rs @@ -2,11 +2,10 @@ #![no_main] -#[path = "../../../../utils/genmc.rs"] +#[path = "../../../utils/genmc.rs"] mod genmc; use std::ffi::c_void; -use std::ptr::null_mut; use std::sync::atomic::{AtomicU64, Ordering}; use crate::genmc::*; @@ -24,25 +23,25 @@ fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { pub extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { X.store(1, Ordering::Relaxed); - null_mut() + std::ptr::null_mut() } pub extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { let r1 = X.load(Ordering::Relaxed); Y.store(r1, Ordering::Release); - null_mut() + std::ptr::null_mut() } pub extern "C" fn thread_3(_value: *mut c_void) -> *mut c_void { let _r1 = X.load(Ordering::Relaxed); std::sync::atomic::fence(Ordering::AcqRel); let _r2 = Y.load(Ordering::Relaxed); - null_mut() + std::ptr::null_mut() } pub extern "C" fn thread_4(_value: *mut c_void) -> *mut c_void { let _r1 = Y.load(Ordering::Relaxed); std::sync::atomic::fence(Ordering::AcqRel); let _r2 = X.load(Ordering::Relaxed); - null_mut() + std::ptr::null_mut() } diff --git a/tests/genmc/pass/litmus/IRIWish/IRIWish.stderr b/tests/genmc/pass/litmus/IRIWish.stderr similarity index 100% rename from tests/genmc/pass/litmus/IRIWish/IRIWish.stderr rename to tests/genmc/pass/litmus/IRIWish.stderr diff --git a/tests/genmc/pass/litmus/LB/LB.rs b/tests/genmc/pass/litmus/LB.rs similarity index 68% rename from tests/genmc/pass/litmus/LB/LB.rs rename to tests/genmc/pass/litmus/LB.rs index bab3bfe03e..686ea37748 100644 --- a/tests/genmc/pass/litmus/LB/LB.rs +++ b/tests/genmc/pass/litmus/LB.rs @@ -1,9 +1,8 @@ //@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows -//@revisions: order12 order21 #![no_main] -#[path = "../../../../utils/genmc.rs"] +#[path = "../../../utils/genmc.rs"] mod genmc; use std::ffi::c_void; @@ -16,15 +15,7 @@ use crate::genmc::*; #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - let thread_order = if cfg!(order12) { - [thread_1, thread_2] - } else if cfg!(order21) { - [thread_2, thread_1] - } else { - unimplemented!(); - }; - - let _ids = unsafe { create_pthreads_no_params(thread_order) }; + let _ids = unsafe { create_pthreads_no_params([thread_1, thread_2]) }; 0 } diff --git a/tests/genmc/pass/litmus/2+2W+2sc+scf/2w2w_2sc_scf.order21.stderr b/tests/genmc/pass/litmus/LB.stderr similarity index 100% rename from tests/genmc/pass/litmus/2+2W+2sc+scf/2w2w_2sc_scf.order21.stderr rename to tests/genmc/pass/litmus/LB.stderr diff --git a/tests/genmc/pass/litmus/LB/LB.order12.stderr b/tests/genmc/pass/litmus/LB/LB.order12.stderr deleted file mode 100644 index f2f3f904fb..0000000000 --- a/tests/genmc/pass/litmus/LB/LB.order12.stderr +++ /dev/null @@ -1,3 +0,0 @@ - -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 3 diff --git a/tests/genmc/pass/litmus/LB/LB.order21.stderr b/tests/genmc/pass/litmus/LB/LB.order21.stderr deleted file mode 100644 index f2f3f904fb..0000000000 --- a/tests/genmc/pass/litmus/LB/LB.order21.stderr +++ /dev/null @@ -1,3 +0,0 @@ - -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 3 diff --git a/tests/genmc/pass/litmus/LB+incMPs/LB_incMPs.rs b/tests/genmc/pass/litmus/LB_incMPs.rs similarity index 96% rename from tests/genmc/pass/litmus/LB+incMPs/LB_incMPs.rs rename to tests/genmc/pass/litmus/LB_incMPs.rs index 4b037d1413..10022bd201 100644 --- a/tests/genmc/pass/litmus/LB+incMPs/LB_incMPs.rs +++ b/tests/genmc/pass/litmus/LB_incMPs.rs @@ -2,7 +2,7 @@ #![no_main] -#[path = "../../../../utils/genmc.rs"] +#[path = "../../../utils/genmc.rs"] mod genmc; use std::ffi::c_void; diff --git a/tests/genmc/pass/litmus/LB+incMPs/LB_incMPs.stderr b/tests/genmc/pass/litmus/LB_incMPs.stderr similarity index 100% rename from tests/genmc/pass/litmus/LB+incMPs/LB_incMPs.stderr rename to tests/genmc/pass/litmus/LB_incMPs.stderr diff --git a/tests/genmc/pass/litmus/MP/MP.rs b/tests/genmc/pass/litmus/MP.rs similarity index 68% rename from tests/genmc/pass/litmus/MP/MP.rs rename to tests/genmc/pass/litmus/MP.rs index ac3347fade..b360f97b8b 100644 --- a/tests/genmc/pass/litmus/MP/MP.rs +++ b/tests/genmc/pass/litmus/MP.rs @@ -1,9 +1,8 @@ //@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows -//@revisions: order12 order21 #![no_main] -#[path = "../../../../utils/genmc.rs"] +#[path = "../../../utils/genmc.rs"] mod genmc; use std::ffi::c_void; @@ -16,15 +15,7 @@ use crate::genmc::*; #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - let thread_order = if cfg!(order12) { - [thread_1, thread_2] - } else if cfg!(order21) { - [thread_2, thread_1] - } else { - unimplemented!(); - }; - - let _ids = unsafe { create_pthreads_no_params(thread_order) }; + let _ids = unsafe { create_pthreads_no_params([thread_1, thread_2]) }; 0 } diff --git a/tests/genmc/pass/litmus/CoRW/corw.order12.stderr b/tests/genmc/pass/litmus/MP.stderr similarity index 100% rename from tests/genmc/pass/litmus/CoRW/corw.order12.stderr rename to tests/genmc/pass/litmus/MP.stderr diff --git a/tests/genmc/pass/litmus/MP/MP.order12.stderr b/tests/genmc/pass/litmus/MP/MP.order12.stderr deleted file mode 100644 index f2f3f904fb..0000000000 --- a/tests/genmc/pass/litmus/MP/MP.order12.stderr +++ /dev/null @@ -1,3 +0,0 @@ - -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 3 diff --git a/tests/genmc/pass/litmus/MP/MP.order21.stderr b/tests/genmc/pass/litmus/MP/MP.order21.stderr deleted file mode 100644 index f2f3f904fb..0000000000 --- a/tests/genmc/pass/litmus/MP/MP.order21.stderr +++ /dev/null @@ -1,3 +0,0 @@ - -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 3 diff --git a/tests/genmc/pass/litmus/MPU2+rels+acqf/MPU2_rels_acqf.rs b/tests/genmc/pass/litmus/MPU2_rels_acqf.rs similarity index 97% rename from tests/genmc/pass/litmus/MPU2+rels+acqf/MPU2_rels_acqf.rs rename to tests/genmc/pass/litmus/MPU2_rels_acqf.rs index b84704e705..d7fe499ff3 100644 --- a/tests/genmc/pass/litmus/MPU2+rels+acqf/MPU2_rels_acqf.rs +++ b/tests/genmc/pass/litmus/MPU2_rels_acqf.rs @@ -2,7 +2,7 @@ #![no_main] -#[path = "../../../../utils/genmc.rs"] +#[path = "../../../utils/genmc.rs"] mod genmc; use std::ffi::c_void; diff --git a/tests/genmc/pass/litmus/MPU2+rels+acqf/MPU2_rels_acqf.stderr b/tests/genmc/pass/litmus/MPU2_rels_acqf.stderr similarity index 100% rename from tests/genmc/pass/litmus/MPU2+rels+acqf/MPU2_rels_acqf.stderr rename to tests/genmc/pass/litmus/MPU2_rels_acqf.stderr diff --git a/tests/genmc/pass/litmus/MPU+rels+acq/MPU_rels_acq.rs b/tests/genmc/pass/litmus/MPU_rels_acq.rs similarity index 96% rename from tests/genmc/pass/litmus/MPU+rels+acq/MPU_rels_acq.rs rename to tests/genmc/pass/litmus/MPU_rels_acq.rs index b13c368170..fa825b83ea 100644 --- a/tests/genmc/pass/litmus/MPU+rels+acq/MPU_rels_acq.rs +++ b/tests/genmc/pass/litmus/MPU_rels_acq.rs @@ -2,7 +2,7 @@ #![no_main] -#[path = "../../../../utils/genmc.rs"] +#[path = "../../../utils/genmc.rs"] mod genmc; use std::ffi::c_void; diff --git a/tests/genmc/pass/litmus/MPU+rels+acq/MPU_rels_acq.stderr b/tests/genmc/pass/litmus/MPU_rels_acq.stderr similarity index 100% rename from tests/genmc/pass/litmus/MPU+rels+acq/MPU_rels_acq.stderr rename to tests/genmc/pass/litmus/MPU_rels_acq.stderr diff --git a/tests/genmc/pass/litmus/MP+incMPs/MP_incMPs.rs b/tests/genmc/pass/litmus/MP_incMPs.rs similarity index 96% rename from tests/genmc/pass/litmus/MP+incMPs/MP_incMPs.rs rename to tests/genmc/pass/litmus/MP_incMPs.rs index ca6ab43c24..1fddb67eb3 100644 --- a/tests/genmc/pass/litmus/MP+incMPs/MP_incMPs.rs +++ b/tests/genmc/pass/litmus/MP_incMPs.rs @@ -2,7 +2,7 @@ #![no_main] -#[path = "../../../../utils/genmc.rs"] +#[path = "../../../utils/genmc.rs"] mod genmc; use std::ffi::c_void; diff --git a/tests/genmc/pass/litmus/MP+incMPs/MP_incMPs.stderr b/tests/genmc/pass/litmus/MP_incMPs.stderr similarity index 100% rename from tests/genmc/pass/litmus/MP+incMPs/MP_incMPs.stderr rename to tests/genmc/pass/litmus/MP_incMPs.stderr diff --git a/tests/genmc/pass/litmus/MP+rels+acqf/MP_rels_acqf.rs b/tests/genmc/pass/litmus/MP_rels_acqf.rs similarity index 96% rename from tests/genmc/pass/litmus/MP+rels+acqf/MP_rels_acqf.rs rename to tests/genmc/pass/litmus/MP_rels_acqf.rs index 5c84189800..5777a7196c 100644 --- a/tests/genmc/pass/litmus/MP+rels+acqf/MP_rels_acqf.rs +++ b/tests/genmc/pass/litmus/MP_rels_acqf.rs @@ -3,7 +3,7 @@ #![no_main] -#[path = "../../../../utils/genmc.rs"] +#[path = "../../../utils/genmc.rs"] mod genmc; use std::ffi::c_void; diff --git a/tests/genmc/pass/litmus/MP+rels+acqf/MP_rels_acqf.stderr b/tests/genmc/pass/litmus/MP_rels_acqf.stderr similarity index 100% rename from tests/genmc/pass/litmus/MP+rels+acqf/MP_rels_acqf.stderr rename to tests/genmc/pass/litmus/MP_rels_acqf.stderr diff --git a/tests/genmc/pass/litmus/SB/SB.rs b/tests/genmc/pass/litmus/SB.rs similarity index 68% rename from tests/genmc/pass/litmus/SB/SB.rs rename to tests/genmc/pass/litmus/SB.rs index b99f2fe67a..b090a7b098 100644 --- a/tests/genmc/pass/litmus/SB/SB.rs +++ b/tests/genmc/pass/litmus/SB.rs @@ -1,9 +1,8 @@ //@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows -//@revisions: order12 order21 #![no_main] -#[path = "../../../../utils/genmc.rs"] +#[path = "../../../utils/genmc.rs"] mod genmc; use std::ffi::c_void; @@ -16,15 +15,7 @@ use crate::genmc::*; #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - let thread_order = if cfg!(order12) { - [thread_1, thread_2] - } else if cfg!(order21) { - [thread_2, thread_1] - } else { - unimplemented!(); - }; - - let _ids = unsafe { create_pthreads_no_params(thread_order) }; + let _ids = unsafe { create_pthreads_no_params([thread_1, thread_2]) }; 0 } diff --git a/tests/genmc/pass/litmus/2CoWR/2cowr.order1234.stderr b/tests/genmc/pass/litmus/SB.stderr similarity index 100% rename from tests/genmc/pass/litmus/2CoWR/2cowr.order1234.stderr rename to tests/genmc/pass/litmus/SB.stderr diff --git a/tests/genmc/pass/litmus/SB/SB.order12.stderr b/tests/genmc/pass/litmus/SB/SB.order12.stderr deleted file mode 100644 index 61dc3f60ba..0000000000 --- a/tests/genmc/pass/litmus/SB/SB.order12.stderr +++ /dev/null @@ -1,3 +0,0 @@ - -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 4 diff --git a/tests/genmc/pass/litmus/SB/SB.order21.stderr b/tests/genmc/pass/litmus/SB/SB.order21.stderr deleted file mode 100644 index 61dc3f60ba..0000000000 --- a/tests/genmc/pass/litmus/SB/SB.order21.stderr +++ /dev/null @@ -1,3 +0,0 @@ - -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 4 diff --git a/tests/genmc/pass/litmus/SB+2sc+scf/SB_2sc_scf.rs b/tests/genmc/pass/litmus/SB_2sc_scf.rs similarity index 95% rename from tests/genmc/pass/litmus/SB+2sc+scf/SB_2sc_scf.rs rename to tests/genmc/pass/litmus/SB_2sc_scf.rs index 50cdc6a9f1..be65dffcb0 100644 --- a/tests/genmc/pass/litmus/SB+2sc+scf/SB_2sc_scf.rs +++ b/tests/genmc/pass/litmus/SB_2sc_scf.rs @@ -2,7 +2,7 @@ #![no_main] -#[path = "../../../../utils/genmc.rs"] +#[path = "../../../utils/genmc.rs"] mod genmc; use std::ffi::c_void; diff --git a/tests/genmc/pass/litmus/SB+2sc+scf/SB_2sc_scf.stderr b/tests/genmc/pass/litmus/SB_2sc_scf.stderr similarity index 100% rename from tests/genmc/pass/litmus/SB+2sc+scf/SB_2sc_scf.stderr rename to tests/genmc/pass/litmus/SB_2sc_scf.stderr diff --git a/tests/genmc/pass/litmus/Z6+acq/Z6_acq.order321.stderr b/tests/genmc/pass/litmus/Z6+acq/Z6_acq.order321.stderr deleted file mode 100644 index 708f4aab1b..0000000000 --- a/tests/genmc/pass/litmus/Z6+acq/Z6_acq.order321.stderr +++ /dev/null @@ -1,3 +0,0 @@ - -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 7 diff --git a/tests/genmc/pass/litmus/Z6.U0/Z6_U0.rs b/tests/genmc/pass/litmus/Z6_U0.rs similarity index 96% rename from tests/genmc/pass/litmus/Z6.U0/Z6_U0.rs rename to tests/genmc/pass/litmus/Z6_U0.rs index b39e86ab64..a7c9b93016 100644 --- a/tests/genmc/pass/litmus/Z6.U0/Z6_U0.rs +++ b/tests/genmc/pass/litmus/Z6_U0.rs @@ -2,7 +2,7 @@ #![no_main] -#[path = "../../../../utils/genmc.rs"] +#[path = "../../../utils/genmc.rs"] mod genmc; use std::ffi::c_void; diff --git a/tests/genmc/pass/litmus/Z6.U0/Z6_U0.stderr b/tests/genmc/pass/litmus/Z6_U0.stderr similarity index 100% rename from tests/genmc/pass/litmus/Z6.U0/Z6_U0.stderr rename to tests/genmc/pass/litmus/Z6_U0.stderr diff --git a/tests/genmc/pass/litmus/Z6+acq/Z6_acq.rs b/tests/genmc/pass/litmus/Z6_acq.rs similarity index 74% rename from tests/genmc/pass/litmus/Z6+acq/Z6_acq.rs rename to tests/genmc/pass/litmus/Z6_acq.rs index 79d7e7f225..34c398bbb0 100644 --- a/tests/genmc/pass/litmus/Z6+acq/Z6_acq.rs +++ b/tests/genmc/pass/litmus/Z6_acq.rs @@ -1,9 +1,8 @@ //@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows -//@revisions: order123 order321 #![no_main] -#[path = "../../../../utils/genmc.rs"] +#[path = "../../../utils/genmc.rs"] mod genmc; use std::ffi::c_void; @@ -17,15 +16,7 @@ use crate::genmc::*; #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - let thread_order = if cfg!(order123) { - [thread_1, thread_2, thread_3] - } else if cfg!(order321) { - [thread_3, thread_2, thread_1] - } else { - unimplemented!(); - }; - - let _ids = unsafe { create_pthreads_no_params(thread_order) }; + let _ids = unsafe { create_pthreads_no_params([thread_1, thread_2, thread_3]) }; 0 } diff --git a/tests/genmc/pass/litmus/Z6+acq/Z6_acq.order123.stderr b/tests/genmc/pass/litmus/Z6_acq.stderr similarity index 100% rename from tests/genmc/pass/litmus/Z6+acq/Z6_acq.order123.stderr rename to tests/genmc/pass/litmus/Z6_acq.stderr diff --git a/tests/genmc/pass/litmus/assume-ctrl/assume-ctrl.rs b/tests/genmc/pass/litmus/assume-ctrl.rs similarity index 93% rename from tests/genmc/pass/litmus/assume-ctrl/assume-ctrl.rs rename to tests/genmc/pass/litmus/assume-ctrl.rs index d369ccf97d..eb3ac511d2 100644 --- a/tests/genmc/pass/litmus/assume-ctrl/assume-ctrl.rs +++ b/tests/genmc/pass/litmus/assume-ctrl.rs @@ -2,10 +2,10 @@ #![no_main] -#[path = "../../../../utils/genmc.rs"] +#[path = "../../../utils/genmc.rs"] mod genmc; -#[path = "../../../../utils/mod.rs"] +#[path = "../../../utils/mod.rs"] mod utils; use std::ffi::c_void; diff --git a/tests/genmc/pass/litmus/assume-ctrl/assume-ctrl.stderr b/tests/genmc/pass/litmus/assume-ctrl.stderr similarity index 100% rename from tests/genmc/pass/litmus/assume-ctrl/assume-ctrl.stderr rename to tests/genmc/pass/litmus/assume-ctrl.stderr diff --git a/tests/genmc/pass/litmus/atomicpo/atomicpo.rs b/tests/genmc/pass/litmus/atomicpo.rs similarity index 95% rename from tests/genmc/pass/litmus/atomicpo/atomicpo.rs rename to tests/genmc/pass/litmus/atomicpo.rs index 6769e715eb..374c2aa099 100644 --- a/tests/genmc/pass/litmus/atomicpo/atomicpo.rs +++ b/tests/genmc/pass/litmus/atomicpo.rs @@ -2,7 +2,7 @@ #![no_main] -#[path = "../../../../utils/genmc.rs"] +#[path = "../../../utils/genmc.rs"] mod genmc; use std::ffi::c_void; diff --git a/tests/genmc/pass/litmus/atomicpo/atomicpo.stderr b/tests/genmc/pass/litmus/atomicpo.stderr similarity index 100% rename from tests/genmc/pass/litmus/atomicpo/atomicpo.stderr rename to tests/genmc/pass/litmus/atomicpo.stderr diff --git a/tests/genmc/pass/litmus/casdep/casdep.rs b/tests/genmc/pass/litmus/casdep.rs similarity index 95% rename from tests/genmc/pass/litmus/casdep/casdep.rs rename to tests/genmc/pass/litmus/casdep.rs index 90ab45fff7..b5793f004d 100644 --- a/tests/genmc/pass/litmus/casdep/casdep.rs +++ b/tests/genmc/pass/litmus/casdep.rs @@ -2,7 +2,7 @@ #![no_main] -#[path = "../../../../utils/genmc.rs"] +#[path = "../../../utils/genmc.rs"] mod genmc; use std::ffi::c_void; diff --git a/tests/genmc/pass/litmus/casdep/casdep.stderr b/tests/genmc/pass/litmus/casdep.stderr similarity index 100% rename from tests/genmc/pass/litmus/casdep/casdep.stderr rename to tests/genmc/pass/litmus/casdep.stderr diff --git a/tests/genmc/pass/litmus/ccr/ccr.rs b/tests/genmc/pass/litmus/ccr.rs similarity index 95% rename from tests/genmc/pass/litmus/ccr/ccr.rs rename to tests/genmc/pass/litmus/ccr.rs index fc5b9168bc..75bb654d88 100644 --- a/tests/genmc/pass/litmus/ccr/ccr.rs +++ b/tests/genmc/pass/litmus/ccr.rs @@ -2,7 +2,7 @@ #![no_main] -#[path = "../../../../utils/genmc.rs"] +#[path = "../../../utils/genmc.rs"] mod genmc; use std::ffi::c_void; diff --git a/tests/genmc/pass/litmus/ccr/ccr.stderr b/tests/genmc/pass/litmus/ccr.stderr similarity index 100% rename from tests/genmc/pass/litmus/ccr/ccr.stderr rename to tests/genmc/pass/litmus/ccr.stderr diff --git a/tests/genmc/pass/litmus/cii/cii.rs b/tests/genmc/pass/litmus/cii.rs similarity index 95% rename from tests/genmc/pass/litmus/cii/cii.rs rename to tests/genmc/pass/litmus/cii.rs index 199e525522..a34d809c1a 100644 --- a/tests/genmc/pass/litmus/cii/cii.rs +++ b/tests/genmc/pass/litmus/cii.rs @@ -2,7 +2,7 @@ #![no_main] -#[path = "../../../../utils/genmc.rs"] +#[path = "../../../utils/genmc.rs"] mod genmc; use std::ffi::c_void; diff --git a/tests/genmc/pass/litmus/cii/cii.stderr b/tests/genmc/pass/litmus/cii.stderr similarity index 100% rename from tests/genmc/pass/litmus/cii/cii.stderr rename to tests/genmc/pass/litmus/cii.stderr diff --git a/tests/genmc/pass/litmus/corr.rs b/tests/genmc/pass/litmus/corr.rs new file mode 100644 index 0000000000..1de6e708da --- /dev/null +++ b/tests/genmc/pass/litmus/corr.rs @@ -0,0 +1,32 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::ffi::c_void; +use std::sync::atomic::{AtomicU64, Ordering}; + +static X: AtomicU64 = AtomicU64::new(0); + +use crate::genmc::*; + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + let _ids = unsafe { create_pthreads_no_params([thread_1, thread_2]) }; + + 0 +} + +extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { + X.store(1, Ordering::Release); + X.store(2, Ordering::Release); + std::ptr::null_mut() +} + +extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { + X.load(Ordering::Acquire); + X.load(Ordering::Acquire); + std::ptr::null_mut() +} diff --git a/tests/genmc/pass/basics/mutex/mutex_simple.order12reps2.stderr b/tests/genmc/pass/litmus/corr.stderr similarity index 100% rename from tests/genmc/pass/basics/mutex/mutex_simple.order12reps2.stderr rename to tests/genmc/pass/litmus/corr.stderr diff --git a/tests/genmc/pass/litmus/CoRR0/corr0.rs b/tests/genmc/pass/litmus/corr0.rs similarity index 57% rename from tests/genmc/pass/litmus/CoRR0/corr0.rs rename to tests/genmc/pass/litmus/corr0.rs index 4d47175560..73d0a7318b 100644 --- a/tests/genmc/pass/litmus/CoRR0/corr0.rs +++ b/tests/genmc/pass/litmus/corr0.rs @@ -1,9 +1,8 @@ //@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows -//@revisions: order123 order321 order312 order231 #![no_main] -#[path = "../../../../utils/genmc.rs"] +#[path = "../../../utils/genmc.rs"] mod genmc; use std::ffi::c_void; @@ -15,19 +14,7 @@ use crate::genmc::*; #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - let thread_order = if cfg!(order123) { - [thread_1, thread_2, thread_3] - } else if cfg!(order321) { - [thread_3, thread_2, thread_1] - } else if cfg!(order312) { - [thread_3, thread_1, thread_2] - } else if cfg!(order231) { - [thread_2, thread_3, thread_1] - } else { - unimplemented!(); - }; - - let _ids = unsafe { create_pthreads_no_params(thread_order) }; + let _ids = unsafe { create_pthreads_no_params([thread_1, thread_2, thread_3]) }; 0 } diff --git a/tests/genmc/pass/litmus/2CoWR/2cowr.order2341.stderr b/tests/genmc/pass/litmus/corr0.stderr similarity index 100% rename from tests/genmc/pass/litmus/2CoWR/2cowr.order2341.stderr rename to tests/genmc/pass/litmus/corr0.stderr diff --git a/tests/genmc/pass/litmus/corr1.rs b/tests/genmc/pass/litmus/corr1.rs new file mode 100644 index 0000000000..802005154d --- /dev/null +++ b/tests/genmc/pass/litmus/corr1.rs @@ -0,0 +1,43 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::ffi::c_void; +use std::sync::atomic::{AtomicU64, Ordering}; + +static X: AtomicU64 = AtomicU64::new(0); + +use crate::genmc::*; + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + unsafe { + let ids = create_pthreads_no_params([thread_1, thread_2, thread_3, thread_4]); + join_pthreads(ids); + 0 + } +} + +extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { + X.store(1, Ordering::Release); + std::ptr::null_mut() +} + +extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { + X.store(2, Ordering::Release); + std::ptr::null_mut() +} + +extern "C" fn thread_3(_value: *mut c_void) -> *mut c_void { + X.load(Ordering::Acquire); + X.load(Ordering::Acquire); + std::ptr::null_mut() +} + +extern "C" fn thread_4(_value: *mut c_void) -> *mut c_void { + X.load(Ordering::Acquire); + std::ptr::null_mut() +} diff --git a/tests/genmc/pass/litmus/CoRR1/corr1.order1234.stderr b/tests/genmc/pass/litmus/corr1.stderr similarity index 100% rename from tests/genmc/pass/litmus/CoRR1/corr1.order1234.stderr rename to tests/genmc/pass/litmus/corr1.stderr diff --git a/tests/genmc/pass/litmus/default/default.rs b/tests/genmc/pass/litmus/corr2.rs similarity index 59% rename from tests/genmc/pass/litmus/default/default.rs rename to tests/genmc/pass/litmus/corr2.rs index 76d2688324..8b61508fc7 100644 --- a/tests/genmc/pass/litmus/default/default.rs +++ b/tests/genmc/pass/litmus/corr2.rs @@ -1,49 +1,42 @@ //@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows -//@revisions: order123 order321 order312 order231 #![no_main] -#[path = "../../../../utils/genmc.rs"] +#[path = "../../../utils/genmc.rs"] mod genmc; use std::ffi::c_void; use std::sync::atomic::{AtomicU64, Ordering}; -use crate::genmc::*; - static X: AtomicU64 = AtomicU64::new(0); +use crate::genmc::*; + #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - let thread_order = if cfg!(order123) { - [thread_1, thread_2, thread_3] - } else if cfg!(order321) { - [thread_3, thread_2, thread_1] - } else if cfg!(order312) { - [thread_3, thread_1, thread_2] - } else if cfg!(order231) { - [thread_2, thread_3, thread_1] - } else { - unimplemented!(); - }; - - let _ids = unsafe { create_pthreads_no_params(thread_order) }; + let _ids = unsafe { create_pthreads_no_params([thread_1, thread_2, thread_3, thread_4]) }; 0 } extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { - X.load(Ordering::Acquire); - X.load(Ordering::Acquire); + X.store(1, Ordering::Release); std::ptr::null_mut() } extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { - X.store(1, Ordering::Release); + X.store(2, Ordering::Release); std::ptr::null_mut() } extern "C" fn thread_3(_value: *mut c_void) -> *mut c_void { - X.store(2, Ordering::Release); + X.load(Ordering::Acquire); + X.load(Ordering::Acquire); + std::ptr::null_mut() +} + +extern "C" fn thread_4(_value: *mut c_void) -> *mut c_void { + X.load(Ordering::Acquire); + X.load(Ordering::Acquire); std::ptr::null_mut() } diff --git a/tests/genmc/pass/litmus/CoRR2/corr2.order1234.stderr b/tests/genmc/pass/litmus/corr2.stderr similarity index 100% rename from tests/genmc/pass/litmus/CoRR2/corr2.order1234.stderr rename to tests/genmc/pass/litmus/corr2.stderr diff --git a/tests/genmc/pass/litmus/CoRW/corw.rs b/tests/genmc/pass/litmus/corw.rs similarity index 65% rename from tests/genmc/pass/litmus/CoRW/corw.rs rename to tests/genmc/pass/litmus/corw.rs index 104a4c93dd..d19fd5c131 100644 --- a/tests/genmc/pass/litmus/CoRW/corw.rs +++ b/tests/genmc/pass/litmus/corw.rs @@ -1,9 +1,8 @@ //@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows -//@revisions: order12 order21 #![no_main] -#[path = "../../../../utils/genmc.rs"] +#[path = "../../../utils/genmc.rs"] mod genmc; use std::ffi::c_void; @@ -15,15 +14,7 @@ use crate::genmc::*; #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - let thread_order = if cfg!(order12) { - [thread_1, thread_2] - } else if cfg!(order21) { - [thread_2, thread_1] - } else { - unimplemented!(); - }; - - let _ids = unsafe { create_pthreads_no_params(thread_order) }; + let _ids = unsafe { create_pthreads_no_params([thread_1, thread_2]) }; 0 } diff --git a/tests/genmc/pass/litmus/CoRW/corw.order21.stderr b/tests/genmc/pass/litmus/corw.stderr similarity index 100% rename from tests/genmc/pass/litmus/CoRW/corw.order21.stderr rename to tests/genmc/pass/litmus/corw.stderr diff --git a/tests/genmc/pass/litmus/CoWR/cowr.rs b/tests/genmc/pass/litmus/cowr.rs similarity index 65% rename from tests/genmc/pass/litmus/CoWR/cowr.rs rename to tests/genmc/pass/litmus/cowr.rs index f9f1f55c0a..b1333c69c3 100644 --- a/tests/genmc/pass/litmus/CoWR/cowr.rs +++ b/tests/genmc/pass/litmus/cowr.rs @@ -1,9 +1,8 @@ //@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows -//@revisions: order12 order21 #![no_main] -#[path = "../../../../utils/genmc.rs"] +#[path = "../../../utils/genmc.rs"] mod genmc; use std::ffi::c_void; @@ -15,15 +14,7 @@ use crate::genmc::*; #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - let thread_order = if cfg!(order12) { - [thread_1, thread_2] - } else if cfg!(order21) { - [thread_2, thread_1] - } else { - unimplemented!(); - }; - - let _ids = unsafe { create_pthreads_no_params(thread_order) }; + let _ids = unsafe { create_pthreads_no_params([thread_1, thread_2]) }; 0 } diff --git a/tests/genmc/pass/litmus/CoWR/cowr.order12.stderr b/tests/genmc/pass/litmus/cowr.stderr similarity index 100% rename from tests/genmc/pass/litmus/CoWR/cowr.order12.stderr rename to tests/genmc/pass/litmus/cowr.stderr diff --git a/tests/genmc/pass/litmus/cumul-release/cumul-release.rs b/tests/genmc/pass/litmus/cumul-release.rs similarity index 96% rename from tests/genmc/pass/litmus/cumul-release/cumul-release.rs rename to tests/genmc/pass/litmus/cumul-release.rs index 65672e0429..fffb3437fe 100644 --- a/tests/genmc/pass/litmus/cumul-release/cumul-release.rs +++ b/tests/genmc/pass/litmus/cumul-release.rs @@ -2,7 +2,7 @@ #![no_main] -#[path = "../../../../utils/genmc.rs"] +#[path = "../../../utils/genmc.rs"] mod genmc; use std::ffi::c_void; diff --git a/tests/genmc/pass/litmus/cumul-release/cumul-release.stderr b/tests/genmc/pass/litmus/cumul-release.stderr similarity index 100% rename from tests/genmc/pass/litmus/cumul-release/cumul-release.stderr rename to tests/genmc/pass/litmus/cumul-release.stderr diff --git a/tests/genmc/pass/litmus/CoRR/corr.rs b/tests/genmc/pass/litmus/default.rs similarity index 66% rename from tests/genmc/pass/litmus/CoRR/corr.rs rename to tests/genmc/pass/litmus/default.rs index f630fadf56..10538ca3af 100644 --- a/tests/genmc/pass/litmus/CoRR/corr.rs +++ b/tests/genmc/pass/litmus/default.rs @@ -1,41 +1,36 @@ //@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows -//@revisions: order12 order21 #![no_main] -#[path = "../../../../utils/genmc.rs"] +#[path = "../../../utils/genmc.rs"] mod genmc; use std::ffi::c_void; use std::sync::atomic::{AtomicU64, Ordering}; -static X: AtomicU64 = AtomicU64::new(0); - use crate::genmc::*; +static X: AtomicU64 = AtomicU64::new(0); + #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - let thread_order = if cfg!(order12) { - [thread_1, thread_2] - } else if cfg!(order21) { - [thread_2, thread_1] - } else { - unimplemented!(); - }; - - let _ids = unsafe { create_pthreads_no_params(thread_order) }; + let _ids = unsafe { create_pthreads_no_params([thread_1, thread_2, thread_3]) }; 0 } extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { - X.store(1, Ordering::Release); - X.store(2, Ordering::Release); + X.load(Ordering::Acquire); + X.load(Ordering::Acquire); std::ptr::null_mut() } extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { - X.load(Ordering::Acquire); - X.load(Ordering::Acquire); + X.store(1, Ordering::Release); + std::ptr::null_mut() +} + +extern "C" fn thread_3(_value: *mut c_void) -> *mut c_void { + X.store(2, Ordering::Release); std::ptr::null_mut() } diff --git a/tests/genmc/pass/litmus/default/default.order123.stderr b/tests/genmc/pass/litmus/default.stderr similarity index 100% rename from tests/genmc/pass/litmus/default/default.order123.stderr rename to tests/genmc/pass/litmus/default.stderr diff --git a/tests/genmc/pass/litmus/default/default.order231.stderr b/tests/genmc/pass/litmus/default/default.order231.stderr deleted file mode 100644 index 1ec3beefbf..0000000000 --- a/tests/genmc/pass/litmus/default/default.order231.stderr +++ /dev/null @@ -1,3 +0,0 @@ - -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 12 diff --git a/tests/genmc/pass/litmus/default/default.order312.stderr b/tests/genmc/pass/litmus/default/default.order312.stderr deleted file mode 100644 index 1ec3beefbf..0000000000 --- a/tests/genmc/pass/litmus/default/default.order312.stderr +++ /dev/null @@ -1,3 +0,0 @@ - -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 12 diff --git a/tests/genmc/pass/litmus/default/default.order321.stderr b/tests/genmc/pass/litmus/default/default.order321.stderr deleted file mode 100644 index 1ec3beefbf..0000000000 --- a/tests/genmc/pass/litmus/default/default.order321.stderr +++ /dev/null @@ -1,3 +0,0 @@ - -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 12 diff --git a/tests/genmc/pass/litmus/detour/detour.rs b/tests/genmc/pass/litmus/detour.rs similarity index 96% rename from tests/genmc/pass/litmus/detour/detour.rs rename to tests/genmc/pass/litmus/detour.rs index 1d444b9095..55a081415e 100644 --- a/tests/genmc/pass/litmus/detour/detour.rs +++ b/tests/genmc/pass/litmus/detour.rs @@ -2,7 +2,7 @@ #![no_main] -#[path = "../../../../utils/genmc.rs"] +#[path = "../../../utils/genmc.rs"] mod genmc; use std::ffi::c_void; diff --git a/tests/genmc/pass/litmus/detour/detour.stderr b/tests/genmc/pass/litmus/detour.stderr similarity index 100% rename from tests/genmc/pass/litmus/detour/detour.stderr rename to tests/genmc/pass/litmus/detour.stderr diff --git a/tests/genmc/pass/litmus/fr_w_w_w_reads/fr_w_w_w_reads.rs b/tests/genmc/pass/litmus/fr_w_w_w_reads.rs similarity index 87% rename from tests/genmc/pass/litmus/fr_w_w_w_reads/fr_w_w_w_reads.rs rename to tests/genmc/pass/litmus/fr_w_w_w_reads.rs index ecdf441203..595cfd16c0 100644 --- a/tests/genmc/pass/litmus/fr_w_w_w_reads/fr_w_w_w_reads.rs +++ b/tests/genmc/pass/litmus/fr_w_w_w_reads.rs @@ -2,11 +2,10 @@ #![no_main] -#[path = "../../../../utils/genmc.rs"] +#[path = "../../../utils/genmc.rs"] mod genmc; use std::ffi::c_void; -use std::ptr::null_mut; use std::sync::atomic::{AtomicU64, Ordering}; use crate::genmc::*; @@ -23,17 +22,17 @@ fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { pub extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { X.store(1, Ordering::Relaxed); - null_mut() + std::ptr::null_mut() } pub extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { X.store(2, Ordering::Relaxed); - null_mut() + std::ptr::null_mut() } pub extern "C" fn thread_3(_value: *mut c_void) -> *mut c_void { X.store(3, Ordering::Relaxed); - null_mut() + std::ptr::null_mut() } pub extern "C" fn thread_4(_value: *mut c_void) -> *mut c_void { @@ -41,5 +40,5 @@ pub extern "C" fn thread_4(_value: *mut c_void) -> *mut c_void { let _r2 = X.load(Ordering::Relaxed); let _r3 = X.load(Ordering::Relaxed); let _r4 = X.load(Ordering::Relaxed); - null_mut() + std::ptr::null_mut() } diff --git a/tests/genmc/pass/litmus/fr_w_w_w_reads/fr_w_w_w_reads.stderr b/tests/genmc/pass/litmus/fr_w_w_w_reads.stderr similarity index 100% rename from tests/genmc/pass/litmus/fr_w_w_w_reads/fr_w_w_w_reads.stderr rename to tests/genmc/pass/litmus/fr_w_w_w_reads.stderr diff --git a/tests/genmc/pass/litmus/inc2w/inc2w.rs b/tests/genmc/pass/litmus/inc2w.rs similarity index 87% rename from tests/genmc/pass/litmus/inc2w/inc2w.rs rename to tests/genmc/pass/litmus/inc2w.rs index 0a19ca3a4e..dbdbbffe1e 100644 --- a/tests/genmc/pass/litmus/inc2w/inc2w.rs +++ b/tests/genmc/pass/litmus/inc2w.rs @@ -2,11 +2,10 @@ #![no_main] -#[path = "../../../../utils/genmc.rs"] +#[path = "../../../utils/genmc.rs"] mod genmc; use std::ffi::c_void; -use std::ptr::null_mut; use std::sync::atomic::{AtomicU64, Ordering}; use crate::genmc::*; @@ -23,15 +22,15 @@ fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { pub extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { X.fetch_add(1, Ordering::Relaxed); - null_mut() + std::ptr::null_mut() } pub extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { X.store(4, Ordering::Release); - null_mut() + std::ptr::null_mut() } pub extern "C" fn thread_3(_value: *mut c_void) -> *mut c_void { X.fetch_add(2, Ordering::Relaxed); - null_mut() + std::ptr::null_mut() } diff --git a/tests/genmc/pass/litmus/inc2w/inc2w.stderr b/tests/genmc/pass/litmus/inc2w.stderr similarity index 100% rename from tests/genmc/pass/litmus/inc2w/inc2w.stderr rename to tests/genmc/pass/litmus/inc2w.stderr diff --git a/tests/genmc/pass/litmus/inc_inc_RR_W_RR/inc_inc_RR_W_RR.rs b/tests/genmc/pass/litmus/inc_inc_RR_W_RR.rs similarity index 89% rename from tests/genmc/pass/litmus/inc_inc_RR_W_RR/inc_inc_RR_W_RR.rs rename to tests/genmc/pass/litmus/inc_inc_RR_W_RR.rs index 0e81d2eb5d..00f00b5850 100644 --- a/tests/genmc/pass/litmus/inc_inc_RR_W_RR/inc_inc_RR_W_RR.rs +++ b/tests/genmc/pass/litmus/inc_inc_RR_W_RR.rs @@ -2,11 +2,10 @@ #![no_main] -#[path = "../../../../utils/genmc.rs"] +#[path = "../../../utils/genmc.rs"] mod genmc; use std::ffi::c_void; -use std::ptr::null_mut; use std::sync::atomic::{AtomicU64, Ordering}; use crate::genmc::*; @@ -33,12 +32,12 @@ fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { pub extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { X.fetch_add(1, Ordering::Relaxed); - null_mut() + std::ptr::null_mut() } pub extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { X.fetch_add(1, Ordering::Relaxed); - null_mut() + std::ptr::null_mut() } pub extern "C" fn thread_3(_value: *mut c_void) -> *mut c_void { @@ -46,12 +45,12 @@ pub extern "C" fn thread_3(_value: *mut c_void) -> *mut c_void { A = X.load(Ordering::Relaxed); B = X.load(Ordering::Relaxed); } - null_mut() + std::ptr::null_mut() } pub extern "C" fn thread_4(_value: *mut c_void) -> *mut c_void { X.store(42, Ordering::Relaxed); - null_mut() + std::ptr::null_mut() } pub extern "C" fn thread_5(_value: *mut c_void) -> *mut c_void { @@ -59,5 +58,5 @@ pub extern "C" fn thread_5(_value: *mut c_void) -> *mut c_void { C = X.load(Ordering::Relaxed); D = X.load(Ordering::Relaxed); } - null_mut() + std::ptr::null_mut() } diff --git a/tests/genmc/pass/litmus/inc_inc_RR_W_RR/inc_inc_RR_W_RR.stderr b/tests/genmc/pass/litmus/inc_inc_RR_W_RR.stderr similarity index 100% rename from tests/genmc/pass/litmus/inc_inc_RR_W_RR/inc_inc_RR_W_RR.stderr rename to tests/genmc/pass/litmus/inc_inc_RR_W_RR.stderr diff --git a/tests/genmc/pass/litmus/riwi/riwi.rs b/tests/genmc/pass/litmus/riwi.rs similarity index 95% rename from tests/genmc/pass/litmus/riwi/riwi.rs rename to tests/genmc/pass/litmus/riwi.rs index 9383f79c1f..9f158d6fea 100644 --- a/tests/genmc/pass/litmus/riwi/riwi.rs +++ b/tests/genmc/pass/litmus/riwi.rs @@ -2,7 +2,7 @@ #![no_main] -#[path = "../../../../utils/genmc.rs"] +#[path = "../../../utils/genmc.rs"] mod genmc; use std::ffi::c_void; diff --git a/tests/genmc/pass/litmus/riwi/riwi.stderr b/tests/genmc/pass/litmus/riwi.stderr similarity index 100% rename from tests/genmc/pass/litmus/riwi/riwi.stderr rename to tests/genmc/pass/litmus/riwi.stderr diff --git a/tests/genmc/pass/litmus/viktor-relseq/viktor-relseq.rs b/tests/genmc/pass/litmus/viktor-relseq.rs similarity index 96% rename from tests/genmc/pass/litmus/viktor-relseq/viktor-relseq.rs rename to tests/genmc/pass/litmus/viktor-relseq.rs index bea3af911a..5c10d39ba4 100644 --- a/tests/genmc/pass/litmus/viktor-relseq/viktor-relseq.rs +++ b/tests/genmc/pass/litmus/viktor-relseq.rs @@ -2,7 +2,7 @@ #![no_main] -#[path = "../../../../utils/genmc.rs"] +#[path = "../../../utils/genmc.rs"] mod genmc; use std::ffi::c_void; diff --git a/tests/genmc/pass/litmus/viktor-relseq/viktor-relseq.stderr b/tests/genmc/pass/litmus/viktor-relseq.stderr similarity index 100% rename from tests/genmc/pass/litmus/viktor-relseq/viktor-relseq.stderr rename to tests/genmc/pass/litmus/viktor-relseq.stderr diff --git a/tests/genmc/pass/loom/load-buffering/load_buffering.genmc.stderr b/tests/genmc/pass/loom/load_buffering.genmc.stderr similarity index 100% rename from tests/genmc/pass/loom/load-buffering/load_buffering.genmc.stderr rename to tests/genmc/pass/loom/load_buffering.genmc.stderr diff --git a/tests/genmc/pass/loom/load-buffering/load_buffering.genmc_std.stderr b/tests/genmc/pass/loom/load_buffering.genmc_std.stderr similarity index 100% rename from tests/genmc/pass/loom/load-buffering/load_buffering.genmc_std.stderr rename to tests/genmc/pass/loom/load_buffering.genmc_std.stderr diff --git a/tests/genmc/pass/loom/load-buffering/load_buffering.rs b/tests/genmc/pass/loom/load_buffering.rs similarity index 98% rename from tests/genmc/pass/loom/load-buffering/load_buffering.rs rename to tests/genmc/pass/loom/load_buffering.rs index df8b58e36e..46205e154e 100644 --- a/tests/genmc/pass/loom/load-buffering/load_buffering.rs +++ b/tests/genmc/pass/loom/load_buffering.rs @@ -13,7 +13,7 @@ #![no_main] #[cfg(not(any(non_genmc_std, genmc_std)))] -#[path = "../../../../utils/genmc.rs"] +#[path = "../../../utils/genmc.rs"] mod genmc; use std::sync::atomic::AtomicUsize; diff --git a/tests/genmc/pass/basics/mutex/mutex_simple.order12reps1.stderr b/tests/genmc/pass/std/mutex_simple.reps1.stderr similarity index 100% rename from tests/genmc/pass/basics/mutex/mutex_simple.order12reps1.stderr rename to tests/genmc/pass/std/mutex_simple.reps1.stderr diff --git a/tests/genmc/pass/basics/mutex/mutex_simple.order21reps2.stderr b/tests/genmc/pass/std/mutex_simple.reps2.stderr similarity index 100% rename from tests/genmc/pass/basics/mutex/mutex_simple.order21reps2.stderr rename to tests/genmc/pass/std/mutex_simple.reps2.stderr diff --git a/tests/genmc/pass/basics/mutex/mutex_simple.rs b/tests/genmc/pass/std/mutex_simple.rs similarity index 91% rename from tests/genmc/pass/basics/mutex/mutex_simple.rs rename to tests/genmc/pass/std/mutex_simple.rs index 7decc23b9b..abddced3fb 100644 --- a/tests/genmc/pass/basics/mutex/mutex_simple.rs +++ b/tests/genmc/pass/std/mutex_simple.rs @@ -1,10 +1,10 @@ //@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows -//@revisions: order12reps1 order21reps1 order12reps2 order21reps2 +//@revisions: reps1 reps2 #![no_main] #![feature(abort_unwind)] -#[path = "../../../../utils/genmc.rs"] +#[path = "../../../utils/genmc.rs"] mod genmc; use std::ffi::c_void; @@ -12,9 +12,9 @@ use std::sync::Mutex; use crate::genmc::*; -#[cfg(not(any(order12reps2, order21reps2)))] +#[cfg(reps1)] const REPS: u64 = 1; -#[cfg(any(order12reps2, order21reps2))] +#[cfg(reps2)] const REPS: u64 = 2; static LOCK: Mutex<[u64; 32]> = Mutex::new([1234; 32]); diff --git a/tests/genmc/pass/simple/simple_main.rs b/tests/genmc/pass/thread/empty_main.rs similarity index 100% rename from tests/genmc/pass/simple/simple_main.rs rename to tests/genmc/pass/thread/empty_main.rs diff --git a/tests/genmc/pass/simple/simple_main.stderr b/tests/genmc/pass/thread/empty_main.stderr similarity index 100% rename from tests/genmc/pass/simple/simple_main.stderr rename to tests/genmc/pass/thread/empty_main.stderr diff --git a/tests/genmc/pass/basics/libc-exit/libc_exit.rs.disabled b/tests/genmc/pass/thread/libc_exit.rs.disabled similarity index 96% rename from tests/genmc/pass/basics/libc-exit/libc_exit.rs.disabled rename to tests/genmc/pass/thread/libc_exit.rs.disabled index 7493d16a20..96a72790d0 100644 --- a/tests/genmc/pass/basics/libc-exit/libc_exit.rs.disabled +++ b/tests/genmc/pass/thread/libc_exit.rs.disabled @@ -3,7 +3,7 @@ #![no_main] -// Copied from `tests/genmc/pass/litmus/inc2w/inc2w.rs` +// Copied from `tests/genmc/pass/litmus/inc2w.rs` #[path = "../../../../utils/genmc.rs"] mod genmc; diff --git a/tests/genmc/pass/simple/miri_main_spawn_pthreads.rs b/tests/genmc/pass/thread/miri_main_spawn_pthreads.rs similarity index 97% rename from tests/genmc/pass/simple/miri_main_spawn_pthreads.rs rename to tests/genmc/pass/thread/miri_main_spawn_pthreads.rs index 6d2269794b..59df0e766f 100644 --- a/tests/genmc/pass/simple/miri_main_spawn_pthreads.rs +++ b/tests/genmc/pass/thread/miri_main_spawn_pthreads.rs @@ -6,7 +6,7 @@ use std::ffi::c_void; use libc::{self, pthread_attr_t, pthread_t}; -const N: usize = 1; +const N: usize = 2; #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { diff --git a/tests/genmc/pass/simple/miri_main_spawn_pthreads.stderr b/tests/genmc/pass/thread/miri_main_spawn_pthreads.stderr similarity index 100% rename from tests/genmc/pass/simple/miri_main_spawn_pthreads.stderr rename to tests/genmc/pass/thread/miri_main_spawn_pthreads.stderr diff --git a/tests/genmc/pass/simple/simple_miri_main_spawn_threads.rs b/tests/genmc/pass/thread/miri_main_spawn_threads.rs similarity index 94% rename from tests/genmc/pass/simple/simple_miri_main_spawn_threads.rs rename to tests/genmc/pass/thread/miri_main_spawn_threads.rs index b7203b4697..c7fd6dff28 100644 --- a/tests/genmc/pass/simple/simple_miri_main_spawn_threads.rs +++ b/tests/genmc/pass/thread/miri_main_spawn_threads.rs @@ -2,7 +2,7 @@ #![no_main] -const N: usize = 1; +const N: usize = 2; #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { diff --git a/tests/genmc/pass/simple/simple_miri_main_spawn_threads.stderr b/tests/genmc/pass/thread/miri_main_spawn_threads.stderr similarity index 100% rename from tests/genmc/pass/simple/simple_miri_main_spawn_threads.stderr rename to tests/genmc/pass/thread/miri_main_spawn_threads.stderr diff --git a/tests/genmc/pass/simple/simple_main_spawn_threads.rs b/tests/genmc/pass/thread/std_main_spawn_threads.rs similarity index 92% rename from tests/genmc/pass/simple/simple_main_spawn_threads.rs rename to tests/genmc/pass/thread/std_main_spawn_threads.rs index 46e37fd5bc..6477ac74ab 100644 --- a/tests/genmc/pass/simple/simple_main_spawn_threads.rs +++ b/tests/genmc/pass/thread/std_main_spawn_threads.rs @@ -1,6 +1,6 @@ //@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows -const N: usize = 1; +const N: usize = 2; fn main() { let handles: Vec<_> = (0..N).map(|_| std::thread::spawn(thread_func)).collect(); diff --git a/tests/genmc/pass/simple/simple_main_spawn_threads.stderr b/tests/genmc/pass/thread/std_main_spawn_threads.stderr similarity index 100% rename from tests/genmc/pass/simple/simple_main_spawn_threads.stderr rename to tests/genmc/pass/thread/std_main_spawn_threads.stderr diff --git a/tests/genmc/pass/simple/thread_locals.rs b/tests/genmc/pass/thread/thread_locals.rs similarity index 100% rename from tests/genmc/pass/simple/thread_locals.rs rename to tests/genmc/pass/thread/thread_locals.rs diff --git a/tests/genmc/pass/simple/thread_locals.stderr b/tests/genmc/pass/thread/thread_locals.stderr similarity index 100% rename from tests/genmc/pass/simple/thread_locals.stderr rename to tests/genmc/pass/thread/thread_locals.stderr diff --git a/tests/genmc/pass/simple/thread_simple.rs b/tests/genmc/pass/thread/thread_simple.rs similarity index 100% rename from tests/genmc/pass/simple/thread_simple.rs rename to tests/genmc/pass/thread/thread_simple.rs diff --git a/tests/genmc/pass/simple/thread_simple.stderr b/tests/genmc/pass/thread/thread_simple.stderr similarity index 100% rename from tests/genmc/pass/simple/thread_simple.stderr rename to tests/genmc/pass/thread/thread_simple.stderr From e194cc8a92b5a161a191adf4a68ed96e1b82007b Mon Sep 17 00:00:00 2001 From: Patrick-6 Date: Wed, 30 Jul 2025 16:08:05 +0200 Subject: [PATCH 10/41] Remove extra newline in program output --- src/concurrency/genmc/miri_genmc.rs | 1 - tests/genmc/pass/atomics/atomic_ptr.stderr | 1 - tests/genmc/pass/atomics/cas_simple.stderr | 1 - tests/genmc/pass/atomics/rmw_edge_cases.i16_.stderr | 1 - tests/genmc/pass/atomics/rmw_edge_cases.i32_.stderr | 1 - tests/genmc/pass/atomics/rmw_edge_cases.i64_.stderr | 1 - tests/genmc/pass/atomics/rmw_edge_cases.i8_.stderr | 1 - tests/genmc/pass/atomics/rmw_edge_cases.isize_.stderr | 1 - tests/genmc/pass/atomics/rmw_edge_cases.u16_.stderr | 1 - tests/genmc/pass/atomics/rmw_edge_cases.u32_.stderr | 1 - tests/genmc/pass/atomics/rmw_edge_cases.u64_.stderr | 1 - tests/genmc/pass/atomics/rmw_edge_cases.u8_.stderr | 1 - tests/genmc/pass/atomics/rmw_edge_cases.usize_.stderr | 1 - tests/genmc/pass/atomics/rmw_simple.stderr | 1 - tests/genmc/pass/atomics/stack_alloc_atomic.stderr | 1 - tests/genmc/pass/data-structures/ms_queue_dynamic.stderr | 1 - tests/genmc/pass/data-structures/treiber_stack_dynamic.stderr | 1 - tests/genmc/pass/litmus/2_2w_3sc_rel1.stderr | 1 - tests/genmc/pass/litmus/2_2w_4sc.stderr | 1 - tests/genmc/pass/litmus/2_2w_scfs.stderr | 1 - tests/genmc/pass/litmus/2cowr.stderr | 1 - tests/genmc/pass/litmus/2w2w.stderr | 1 - tests/genmc/pass/litmus/2w2w_2sc_scf.stderr | 1 - tests/genmc/pass/litmus/IRIW-acq-sc.stderr | 1 - tests/genmc/pass/litmus/IRIWish.stderr | 1 - tests/genmc/pass/litmus/LB.stderr | 1 - tests/genmc/pass/litmus/LB_incMPs.stderr | 1 - tests/genmc/pass/litmus/MP.stderr | 1 - tests/genmc/pass/litmus/MPU2_rels_acqf.stderr | 1 - tests/genmc/pass/litmus/MPU_rels_acq.stderr | 1 - tests/genmc/pass/litmus/MP_incMPs.stderr | 1 - tests/genmc/pass/litmus/MP_rels_acqf.stderr | 1 - tests/genmc/pass/litmus/SB.stderr | 1 - tests/genmc/pass/litmus/SB_2sc_scf.stderr | 1 - tests/genmc/pass/litmus/Z6_U0.stderr | 1 - tests/genmc/pass/litmus/Z6_acq.stderr | 1 - tests/genmc/pass/litmus/assume-ctrl.stderr | 1 - tests/genmc/pass/litmus/atomicpo.stderr | 1 - tests/genmc/pass/litmus/casdep.stderr | 1 - tests/genmc/pass/litmus/ccr.stderr | 1 - tests/genmc/pass/litmus/cii.stderr | 1 - tests/genmc/pass/litmus/corr.stderr | 1 - tests/genmc/pass/litmus/corr0.stderr | 1 - tests/genmc/pass/litmus/corr1.stderr | 1 - tests/genmc/pass/litmus/corr2.stderr | 1 - tests/genmc/pass/litmus/corw.stderr | 1 - tests/genmc/pass/litmus/cowr.stderr | 1 - tests/genmc/pass/litmus/cumul-release.stderr | 1 - tests/genmc/pass/litmus/default.stderr | 1 - tests/genmc/pass/litmus/detour.stderr | 1 - tests/genmc/pass/litmus/fr_w_w_w_reads.stderr | 1 - tests/genmc/pass/litmus/inc2w.stderr | 1 - tests/genmc/pass/litmus/inc_inc_RR_W_RR.stderr | 1 - tests/genmc/pass/litmus/riwi.stderr | 1 - tests/genmc/pass/litmus/viktor-relseq.stderr | 1 - tests/genmc/pass/loom/load_buffering.genmc.stderr | 1 - tests/genmc/pass/loom/load_buffering.genmc_std.stderr | 1 - tests/genmc/pass/std/mutex_simple.reps1.stderr | 1 - tests/genmc/pass/std/mutex_simple.reps2.stderr | 1 - tests/genmc/pass/thread/empty_main.stderr | 1 - tests/genmc/pass/thread/miri_main_spawn_pthreads.stderr | 1 - tests/genmc/pass/thread/miri_main_spawn_threads.stderr | 1 - tests/genmc/pass/thread/std_main_spawn_threads.stderr | 1 - tests/genmc/pass/thread/thread_locals.stderr | 1 - tests/genmc/pass/thread/thread_simple.stderr | 1 - 65 files changed, 65 deletions(-) diff --git a/src/concurrency/genmc/miri_genmc.rs b/src/concurrency/genmc/miri_genmc.rs index 75408df4cd..5afca626d2 100644 --- a/src/concurrency/genmc/miri_genmc.rs +++ b/src/concurrency/genmc/miri_genmc.rs @@ -46,7 +46,6 @@ pub fn run_genmc_mode( ); if is_exploration_done { - eprintln!(); eprintln!("(GenMC) {mode} complete. No errors were detected.",); if mode == Mode::Estimation && return_code == 0 { diff --git a/tests/genmc/pass/atomics/atomic_ptr.stderr b/tests/genmc/pass/atomics/atomic_ptr.stderr index 2382cc5a78..3b22247ee4 100644 --- a/tests/genmc/pass/atomics/atomic_ptr.stderr +++ b/tests/genmc/pass/atomics/atomic_ptr.stderr @@ -1,3 +1,2 @@ - (GenMC) Verification complete. No errors were detected. Number of complete executions explored: 1 diff --git a/tests/genmc/pass/atomics/cas_simple.stderr b/tests/genmc/pass/atomics/cas_simple.stderr index 2382cc5a78..3b22247ee4 100644 --- a/tests/genmc/pass/atomics/cas_simple.stderr +++ b/tests/genmc/pass/atomics/cas_simple.stderr @@ -1,3 +1,2 @@ - (GenMC) Verification complete. No errors were detected. Number of complete executions explored: 1 diff --git a/tests/genmc/pass/atomics/rmw_edge_cases.i16_.stderr b/tests/genmc/pass/atomics/rmw_edge_cases.i16_.stderr index 2382cc5a78..3b22247ee4 100644 --- a/tests/genmc/pass/atomics/rmw_edge_cases.i16_.stderr +++ b/tests/genmc/pass/atomics/rmw_edge_cases.i16_.stderr @@ -1,3 +1,2 @@ - (GenMC) Verification complete. No errors were detected. Number of complete executions explored: 1 diff --git a/tests/genmc/pass/atomics/rmw_edge_cases.i32_.stderr b/tests/genmc/pass/atomics/rmw_edge_cases.i32_.stderr index 2382cc5a78..3b22247ee4 100644 --- a/tests/genmc/pass/atomics/rmw_edge_cases.i32_.stderr +++ b/tests/genmc/pass/atomics/rmw_edge_cases.i32_.stderr @@ -1,3 +1,2 @@ - (GenMC) Verification complete. No errors were detected. Number of complete executions explored: 1 diff --git a/tests/genmc/pass/atomics/rmw_edge_cases.i64_.stderr b/tests/genmc/pass/atomics/rmw_edge_cases.i64_.stderr index 2382cc5a78..3b22247ee4 100644 --- a/tests/genmc/pass/atomics/rmw_edge_cases.i64_.stderr +++ b/tests/genmc/pass/atomics/rmw_edge_cases.i64_.stderr @@ -1,3 +1,2 @@ - (GenMC) Verification complete. No errors were detected. Number of complete executions explored: 1 diff --git a/tests/genmc/pass/atomics/rmw_edge_cases.i8_.stderr b/tests/genmc/pass/atomics/rmw_edge_cases.i8_.stderr index 2382cc5a78..3b22247ee4 100644 --- a/tests/genmc/pass/atomics/rmw_edge_cases.i8_.stderr +++ b/tests/genmc/pass/atomics/rmw_edge_cases.i8_.stderr @@ -1,3 +1,2 @@ - (GenMC) Verification complete. No errors were detected. Number of complete executions explored: 1 diff --git a/tests/genmc/pass/atomics/rmw_edge_cases.isize_.stderr b/tests/genmc/pass/atomics/rmw_edge_cases.isize_.stderr index 2382cc5a78..3b22247ee4 100644 --- a/tests/genmc/pass/atomics/rmw_edge_cases.isize_.stderr +++ b/tests/genmc/pass/atomics/rmw_edge_cases.isize_.stderr @@ -1,3 +1,2 @@ - (GenMC) Verification complete. No errors were detected. Number of complete executions explored: 1 diff --git a/tests/genmc/pass/atomics/rmw_edge_cases.u16_.stderr b/tests/genmc/pass/atomics/rmw_edge_cases.u16_.stderr index 2382cc5a78..3b22247ee4 100644 --- a/tests/genmc/pass/atomics/rmw_edge_cases.u16_.stderr +++ b/tests/genmc/pass/atomics/rmw_edge_cases.u16_.stderr @@ -1,3 +1,2 @@ - (GenMC) Verification complete. No errors were detected. Number of complete executions explored: 1 diff --git a/tests/genmc/pass/atomics/rmw_edge_cases.u32_.stderr b/tests/genmc/pass/atomics/rmw_edge_cases.u32_.stderr index 2382cc5a78..3b22247ee4 100644 --- a/tests/genmc/pass/atomics/rmw_edge_cases.u32_.stderr +++ b/tests/genmc/pass/atomics/rmw_edge_cases.u32_.stderr @@ -1,3 +1,2 @@ - (GenMC) Verification complete. No errors were detected. Number of complete executions explored: 1 diff --git a/tests/genmc/pass/atomics/rmw_edge_cases.u64_.stderr b/tests/genmc/pass/atomics/rmw_edge_cases.u64_.stderr index 2382cc5a78..3b22247ee4 100644 --- a/tests/genmc/pass/atomics/rmw_edge_cases.u64_.stderr +++ b/tests/genmc/pass/atomics/rmw_edge_cases.u64_.stderr @@ -1,3 +1,2 @@ - (GenMC) Verification complete. No errors were detected. Number of complete executions explored: 1 diff --git a/tests/genmc/pass/atomics/rmw_edge_cases.u8_.stderr b/tests/genmc/pass/atomics/rmw_edge_cases.u8_.stderr index 2382cc5a78..3b22247ee4 100644 --- a/tests/genmc/pass/atomics/rmw_edge_cases.u8_.stderr +++ b/tests/genmc/pass/atomics/rmw_edge_cases.u8_.stderr @@ -1,3 +1,2 @@ - (GenMC) Verification complete. No errors were detected. Number of complete executions explored: 1 diff --git a/tests/genmc/pass/atomics/rmw_edge_cases.usize_.stderr b/tests/genmc/pass/atomics/rmw_edge_cases.usize_.stderr index 2382cc5a78..3b22247ee4 100644 --- a/tests/genmc/pass/atomics/rmw_edge_cases.usize_.stderr +++ b/tests/genmc/pass/atomics/rmw_edge_cases.usize_.stderr @@ -1,3 +1,2 @@ - (GenMC) Verification complete. No errors were detected. Number of complete executions explored: 1 diff --git a/tests/genmc/pass/atomics/rmw_simple.stderr b/tests/genmc/pass/atomics/rmw_simple.stderr index 2382cc5a78..3b22247ee4 100644 --- a/tests/genmc/pass/atomics/rmw_simple.stderr +++ b/tests/genmc/pass/atomics/rmw_simple.stderr @@ -1,3 +1,2 @@ - (GenMC) Verification complete. No errors were detected. Number of complete executions explored: 1 diff --git a/tests/genmc/pass/atomics/stack_alloc_atomic.stderr b/tests/genmc/pass/atomics/stack_alloc_atomic.stderr index 2382cc5a78..3b22247ee4 100644 --- a/tests/genmc/pass/atomics/stack_alloc_atomic.stderr +++ b/tests/genmc/pass/atomics/stack_alloc_atomic.stderr @@ -1,3 +1,2 @@ - (GenMC) Verification complete. No errors were detected. Number of complete executions explored: 1 diff --git a/tests/genmc/pass/data-structures/ms_queue_dynamic.stderr b/tests/genmc/pass/data-structures/ms_queue_dynamic.stderr index 2ed05b58e8..0513e8df10 100644 --- a/tests/genmc/pass/data-structures/ms_queue_dynamic.stderr +++ b/tests/genmc/pass/data-structures/ms_queue_dynamic.stderr @@ -1,3 +1,2 @@ - (GenMC) Verification complete. No errors were detected. Number of complete executions explored: 222 diff --git a/tests/genmc/pass/data-structures/treiber_stack_dynamic.stderr b/tests/genmc/pass/data-structures/treiber_stack_dynamic.stderr index fbd9adb20b..15aa1e752a 100644 --- a/tests/genmc/pass/data-structures/treiber_stack_dynamic.stderr +++ b/tests/genmc/pass/data-structures/treiber_stack_dynamic.stderr @@ -1,3 +1,2 @@ - (GenMC) Verification complete. No errors were detected. Number of complete executions explored: 22 diff --git a/tests/genmc/pass/litmus/2_2w_3sc_rel1.stderr b/tests/genmc/pass/litmus/2_2w_3sc_rel1.stderr index 61dc3f60ba..0667962f99 100644 --- a/tests/genmc/pass/litmus/2_2w_3sc_rel1.stderr +++ b/tests/genmc/pass/litmus/2_2w_3sc_rel1.stderr @@ -1,3 +1,2 @@ - (GenMC) Verification complete. No errors were detected. Number of complete executions explored: 4 diff --git a/tests/genmc/pass/litmus/2_2w_4sc.stderr b/tests/genmc/pass/litmus/2_2w_4sc.stderr index f2f3f904fb..115b1986ce 100644 --- a/tests/genmc/pass/litmus/2_2w_4sc.stderr +++ b/tests/genmc/pass/litmus/2_2w_4sc.stderr @@ -1,3 +1,2 @@ - (GenMC) Verification complete. No errors were detected. Number of complete executions explored: 3 diff --git a/tests/genmc/pass/litmus/2_2w_scfs.stderr b/tests/genmc/pass/litmus/2_2w_scfs.stderr index f2f3f904fb..115b1986ce 100644 --- a/tests/genmc/pass/litmus/2_2w_scfs.stderr +++ b/tests/genmc/pass/litmus/2_2w_scfs.stderr @@ -1,3 +1,2 @@ - (GenMC) Verification complete. No errors were detected. Number of complete executions explored: 3 diff --git a/tests/genmc/pass/litmus/2cowr.stderr b/tests/genmc/pass/litmus/2cowr.stderr index 61dc3f60ba..0667962f99 100644 --- a/tests/genmc/pass/litmus/2cowr.stderr +++ b/tests/genmc/pass/litmus/2cowr.stderr @@ -1,3 +1,2 @@ - (GenMC) Verification complete. No errors were detected. Number of complete executions explored: 4 diff --git a/tests/genmc/pass/litmus/2w2w.stderr b/tests/genmc/pass/litmus/2w2w.stderr index 61dc3f60ba..0667962f99 100644 --- a/tests/genmc/pass/litmus/2w2w.stderr +++ b/tests/genmc/pass/litmus/2w2w.stderr @@ -1,3 +1,2 @@ - (GenMC) Verification complete. No errors were detected. Number of complete executions explored: 4 diff --git a/tests/genmc/pass/litmus/2w2w_2sc_scf.stderr b/tests/genmc/pass/litmus/2w2w_2sc_scf.stderr index f2f3f904fb..115b1986ce 100644 --- a/tests/genmc/pass/litmus/2w2w_2sc_scf.stderr +++ b/tests/genmc/pass/litmus/2w2w_2sc_scf.stderr @@ -1,3 +1,2 @@ - (GenMC) Verification complete. No errors were detected. Number of complete executions explored: 3 diff --git a/tests/genmc/pass/litmus/IRIW-acq-sc.stderr b/tests/genmc/pass/litmus/IRIW-acq-sc.stderr index f5e120ea47..c760b44605 100644 --- a/tests/genmc/pass/litmus/IRIW-acq-sc.stderr +++ b/tests/genmc/pass/litmus/IRIW-acq-sc.stderr @@ -1,3 +1,2 @@ - (GenMC) Verification complete. No errors were detected. Number of complete executions explored: 16 diff --git a/tests/genmc/pass/litmus/IRIWish.stderr b/tests/genmc/pass/litmus/IRIWish.stderr index 452fca4856..bce94be916 100644 --- a/tests/genmc/pass/litmus/IRIWish.stderr +++ b/tests/genmc/pass/litmus/IRIWish.stderr @@ -1,3 +1,2 @@ - (GenMC) Verification complete. No errors were detected. Number of complete executions explored: 28 diff --git a/tests/genmc/pass/litmus/LB.stderr b/tests/genmc/pass/litmus/LB.stderr index f2f3f904fb..115b1986ce 100644 --- a/tests/genmc/pass/litmus/LB.stderr +++ b/tests/genmc/pass/litmus/LB.stderr @@ -1,3 +1,2 @@ - (GenMC) Verification complete. No errors were detected. Number of complete executions explored: 3 diff --git a/tests/genmc/pass/litmus/LB_incMPs.stderr b/tests/genmc/pass/litmus/LB_incMPs.stderr index 978d7c1ff3..c879e95a17 100644 --- a/tests/genmc/pass/litmus/LB_incMPs.stderr +++ b/tests/genmc/pass/litmus/LB_incMPs.stderr @@ -1,3 +1,2 @@ - (GenMC) Verification complete. No errors were detected. Number of complete executions explored: 15 diff --git a/tests/genmc/pass/litmus/MP.stderr b/tests/genmc/pass/litmus/MP.stderr index f2f3f904fb..115b1986ce 100644 --- a/tests/genmc/pass/litmus/MP.stderr +++ b/tests/genmc/pass/litmus/MP.stderr @@ -1,3 +1,2 @@ - (GenMC) Verification complete. No errors were detected. Number of complete executions explored: 3 diff --git a/tests/genmc/pass/litmus/MPU2_rels_acqf.stderr b/tests/genmc/pass/litmus/MPU2_rels_acqf.stderr index c2ffa8ec3c..f6d07e9c77 100644 --- a/tests/genmc/pass/litmus/MPU2_rels_acqf.stderr +++ b/tests/genmc/pass/litmus/MPU2_rels_acqf.stderr @@ -1,3 +1,2 @@ - (GenMC) Verification complete. No errors were detected. Number of complete executions explored: 36 diff --git a/tests/genmc/pass/litmus/MPU_rels_acq.stderr b/tests/genmc/pass/litmus/MPU_rels_acq.stderr index 89aa5eb69c..c5e34b0064 100644 --- a/tests/genmc/pass/litmus/MPU_rels_acq.stderr +++ b/tests/genmc/pass/litmus/MPU_rels_acq.stderr @@ -1,3 +1,2 @@ - (GenMC) Verification complete. No errors were detected. Number of complete executions explored: 13 diff --git a/tests/genmc/pass/litmus/MP_incMPs.stderr b/tests/genmc/pass/litmus/MP_incMPs.stderr index 708f4aab1b..2be9a6c7fb 100644 --- a/tests/genmc/pass/litmus/MP_incMPs.stderr +++ b/tests/genmc/pass/litmus/MP_incMPs.stderr @@ -1,3 +1,2 @@ - (GenMC) Verification complete. No errors were detected. Number of complete executions explored: 7 diff --git a/tests/genmc/pass/litmus/MP_rels_acqf.stderr b/tests/genmc/pass/litmus/MP_rels_acqf.stderr index 61dc3f60ba..0667962f99 100644 --- a/tests/genmc/pass/litmus/MP_rels_acqf.stderr +++ b/tests/genmc/pass/litmus/MP_rels_acqf.stderr @@ -1,3 +1,2 @@ - (GenMC) Verification complete. No errors were detected. Number of complete executions explored: 4 diff --git a/tests/genmc/pass/litmus/SB.stderr b/tests/genmc/pass/litmus/SB.stderr index 61dc3f60ba..0667962f99 100644 --- a/tests/genmc/pass/litmus/SB.stderr +++ b/tests/genmc/pass/litmus/SB.stderr @@ -1,3 +1,2 @@ - (GenMC) Verification complete. No errors were detected. Number of complete executions explored: 4 diff --git a/tests/genmc/pass/litmus/SB_2sc_scf.stderr b/tests/genmc/pass/litmus/SB_2sc_scf.stderr index f2f3f904fb..115b1986ce 100644 --- a/tests/genmc/pass/litmus/SB_2sc_scf.stderr +++ b/tests/genmc/pass/litmus/SB_2sc_scf.stderr @@ -1,3 +1,2 @@ - (GenMC) Verification complete. No errors were detected. Number of complete executions explored: 3 diff --git a/tests/genmc/pass/litmus/Z6_U0.stderr b/tests/genmc/pass/litmus/Z6_U0.stderr index fbd9adb20b..15aa1e752a 100644 --- a/tests/genmc/pass/litmus/Z6_U0.stderr +++ b/tests/genmc/pass/litmus/Z6_U0.stderr @@ -1,3 +1,2 @@ - (GenMC) Verification complete. No errors were detected. Number of complete executions explored: 22 diff --git a/tests/genmc/pass/litmus/Z6_acq.stderr b/tests/genmc/pass/litmus/Z6_acq.stderr index 708f4aab1b..2be9a6c7fb 100644 --- a/tests/genmc/pass/litmus/Z6_acq.stderr +++ b/tests/genmc/pass/litmus/Z6_acq.stderr @@ -1,3 +1,2 @@ - (GenMC) Verification complete. No errors were detected. Number of complete executions explored: 7 diff --git a/tests/genmc/pass/litmus/assume-ctrl.stderr b/tests/genmc/pass/litmus/assume-ctrl.stderr index bcd0ece9c7..86a4c37e30 100644 --- a/tests/genmc/pass/litmus/assume-ctrl.stderr +++ b/tests/genmc/pass/litmus/assume-ctrl.stderr @@ -1,4 +1,3 @@ - (GenMC) Verification complete. No errors were detected. Number of complete executions explored: 4 Number of blocked executions seen: 1 diff --git a/tests/genmc/pass/litmus/atomicpo.stderr b/tests/genmc/pass/litmus/atomicpo.stderr index 61dc3f60ba..0667962f99 100644 --- a/tests/genmc/pass/litmus/atomicpo.stderr +++ b/tests/genmc/pass/litmus/atomicpo.stderr @@ -1,3 +1,2 @@ - (GenMC) Verification complete. No errors were detected. Number of complete executions explored: 4 diff --git a/tests/genmc/pass/litmus/casdep.stderr b/tests/genmc/pass/litmus/casdep.stderr index edecd38278..01701dfe69 100644 --- a/tests/genmc/pass/litmus/casdep.stderr +++ b/tests/genmc/pass/litmus/casdep.stderr @@ -1,3 +1,2 @@ - (GenMC) Verification complete. No errors were detected. Number of complete executions explored: 2 diff --git a/tests/genmc/pass/litmus/ccr.stderr b/tests/genmc/pass/litmus/ccr.stderr index edecd38278..01701dfe69 100644 --- a/tests/genmc/pass/litmus/ccr.stderr +++ b/tests/genmc/pass/litmus/ccr.stderr @@ -1,3 +1,2 @@ - (GenMC) Verification complete. No errors were detected. Number of complete executions explored: 2 diff --git a/tests/genmc/pass/litmus/cii.stderr b/tests/genmc/pass/litmus/cii.stderr index e4151d346e..be75e68fde 100644 --- a/tests/genmc/pass/litmus/cii.stderr +++ b/tests/genmc/pass/litmus/cii.stderr @@ -1,3 +1,2 @@ - (GenMC) Verification complete. No errors were detected. Number of complete executions explored: 6 diff --git a/tests/genmc/pass/litmus/corr.stderr b/tests/genmc/pass/litmus/corr.stderr index e4151d346e..be75e68fde 100644 --- a/tests/genmc/pass/litmus/corr.stderr +++ b/tests/genmc/pass/litmus/corr.stderr @@ -1,3 +1,2 @@ - (GenMC) Verification complete. No errors were detected. Number of complete executions explored: 6 diff --git a/tests/genmc/pass/litmus/corr0.stderr b/tests/genmc/pass/litmus/corr0.stderr index 61dc3f60ba..0667962f99 100644 --- a/tests/genmc/pass/litmus/corr0.stderr +++ b/tests/genmc/pass/litmus/corr0.stderr @@ -1,3 +1,2 @@ - (GenMC) Verification complete. No errors were detected. Number of complete executions explored: 4 diff --git a/tests/genmc/pass/litmus/corr1.stderr b/tests/genmc/pass/litmus/corr1.stderr index c2ffa8ec3c..f6d07e9c77 100644 --- a/tests/genmc/pass/litmus/corr1.stderr +++ b/tests/genmc/pass/litmus/corr1.stderr @@ -1,3 +1,2 @@ - (GenMC) Verification complete. No errors were detected. Number of complete executions explored: 36 diff --git a/tests/genmc/pass/litmus/corr2.stderr b/tests/genmc/pass/litmus/corr2.stderr index c8cd87ff6b..78a90b63fe 100644 --- a/tests/genmc/pass/litmus/corr2.stderr +++ b/tests/genmc/pass/litmus/corr2.stderr @@ -1,3 +1,2 @@ - (GenMC) Verification complete. No errors were detected. Number of complete executions explored: 72 diff --git a/tests/genmc/pass/litmus/corw.stderr b/tests/genmc/pass/litmus/corw.stderr index f2f3f904fb..115b1986ce 100644 --- a/tests/genmc/pass/litmus/corw.stderr +++ b/tests/genmc/pass/litmus/corw.stderr @@ -1,3 +1,2 @@ - (GenMC) Verification complete. No errors were detected. Number of complete executions explored: 3 diff --git a/tests/genmc/pass/litmus/cowr.stderr b/tests/genmc/pass/litmus/cowr.stderr index f2f3f904fb..115b1986ce 100644 --- a/tests/genmc/pass/litmus/cowr.stderr +++ b/tests/genmc/pass/litmus/cowr.stderr @@ -1,3 +1,2 @@ - (GenMC) Verification complete. No errors were detected. Number of complete executions explored: 3 diff --git a/tests/genmc/pass/litmus/cumul-release.stderr b/tests/genmc/pass/litmus/cumul-release.stderr index c2e069848f..00394048ec 100644 --- a/tests/genmc/pass/litmus/cumul-release.stderr +++ b/tests/genmc/pass/litmus/cumul-release.stderr @@ -1,3 +1,2 @@ - (GenMC) Verification complete. No errors were detected. Number of complete executions explored: 8 diff --git a/tests/genmc/pass/litmus/default.stderr b/tests/genmc/pass/litmus/default.stderr index 1ec3beefbf..e031393028 100644 --- a/tests/genmc/pass/litmus/default.stderr +++ b/tests/genmc/pass/litmus/default.stderr @@ -1,3 +1,2 @@ - (GenMC) Verification complete. No errors were detected. Number of complete executions explored: 12 diff --git a/tests/genmc/pass/litmus/detour.stderr b/tests/genmc/pass/litmus/detour.stderr index 7e0204914a..15017249dc 100644 --- a/tests/genmc/pass/litmus/detour.stderr +++ b/tests/genmc/pass/litmus/detour.stderr @@ -1,3 +1,2 @@ - (GenMC) Verification complete. No errors were detected. Number of complete executions explored: 9 diff --git a/tests/genmc/pass/litmus/fr_w_w_w_reads.stderr b/tests/genmc/pass/litmus/fr_w_w_w_reads.stderr index 528ebdfd2b..3b6ba238f5 100644 --- a/tests/genmc/pass/litmus/fr_w_w_w_reads.stderr +++ b/tests/genmc/pass/litmus/fr_w_w_w_reads.stderr @@ -1,3 +1,2 @@ - (GenMC) Verification complete. No errors were detected. Number of complete executions explored: 210 diff --git a/tests/genmc/pass/litmus/inc2w.stderr b/tests/genmc/pass/litmus/inc2w.stderr index e4151d346e..be75e68fde 100644 --- a/tests/genmc/pass/litmus/inc2w.stderr +++ b/tests/genmc/pass/litmus/inc2w.stderr @@ -1,3 +1,2 @@ - (GenMC) Verification complete. No errors were detected. Number of complete executions explored: 6 diff --git a/tests/genmc/pass/litmus/inc_inc_RR_W_RR.stderr b/tests/genmc/pass/litmus/inc_inc_RR_W_RR.stderr index ad9e4b3a6e..b5f8cd15b6 100644 --- a/tests/genmc/pass/litmus/inc_inc_RR_W_RR.stderr +++ b/tests/genmc/pass/litmus/inc_inc_RR_W_RR.stderr @@ -1,3 +1,2 @@ - (GenMC) Verification complete. No errors were detected. Number of complete executions explored: 600 diff --git a/tests/genmc/pass/litmus/riwi.stderr b/tests/genmc/pass/litmus/riwi.stderr index f2f3f904fb..115b1986ce 100644 --- a/tests/genmc/pass/litmus/riwi.stderr +++ b/tests/genmc/pass/litmus/riwi.stderr @@ -1,3 +1,2 @@ - (GenMC) Verification complete. No errors were detected. Number of complete executions explored: 3 diff --git a/tests/genmc/pass/litmus/viktor-relseq.stderr b/tests/genmc/pass/litmus/viktor-relseq.stderr index 5b01ae0789..d63ac5199d 100644 --- a/tests/genmc/pass/litmus/viktor-relseq.stderr +++ b/tests/genmc/pass/litmus/viktor-relseq.stderr @@ -1,3 +1,2 @@ - (GenMC) Verification complete. No errors were detected. Number of complete executions explored: 180 diff --git a/tests/genmc/pass/loom/load_buffering.genmc.stderr b/tests/genmc/pass/loom/load_buffering.genmc.stderr index f2f3f904fb..115b1986ce 100644 --- a/tests/genmc/pass/loom/load_buffering.genmc.stderr +++ b/tests/genmc/pass/loom/load_buffering.genmc.stderr @@ -1,3 +1,2 @@ - (GenMC) Verification complete. No errors were detected. Number of complete executions explored: 3 diff --git a/tests/genmc/pass/loom/load_buffering.genmc_std.stderr b/tests/genmc/pass/loom/load_buffering.genmc_std.stderr index 40f2279166..76b0302eda 100644 --- a/tests/genmc/pass/loom/load_buffering.genmc_std.stderr +++ b/tests/genmc/pass/loom/load_buffering.genmc_std.stderr @@ -4,6 +4,5 @@ warning: GenMC mode currently does not model spurious failures of `compare_excha warning: GenMC currently does not model the atomic failure ordering for `compare_exchange`. Failure ordering 'Acquire' is treated like 'Relaxed', which means that Miri might incorrectly detect errors related to this memory access (possible false positives). - (GenMC) Verification complete. No errors were detected. Number of complete executions explored: 3 diff --git a/tests/genmc/pass/std/mutex_simple.reps1.stderr b/tests/genmc/pass/std/mutex_simple.reps1.stderr index edecd38278..01701dfe69 100644 --- a/tests/genmc/pass/std/mutex_simple.reps1.stderr +++ b/tests/genmc/pass/std/mutex_simple.reps1.stderr @@ -1,3 +1,2 @@ - (GenMC) Verification complete. No errors were detected. Number of complete executions explored: 2 diff --git a/tests/genmc/pass/std/mutex_simple.reps2.stderr b/tests/genmc/pass/std/mutex_simple.reps2.stderr index e4151d346e..be75e68fde 100644 --- a/tests/genmc/pass/std/mutex_simple.reps2.stderr +++ b/tests/genmc/pass/std/mutex_simple.reps2.stderr @@ -1,3 +1,2 @@ - (GenMC) Verification complete. No errors were detected. Number of complete executions explored: 6 diff --git a/tests/genmc/pass/thread/empty_main.stderr b/tests/genmc/pass/thread/empty_main.stderr index 2184c16ce9..1660e2431b 100644 --- a/tests/genmc/pass/thread/empty_main.stderr +++ b/tests/genmc/pass/thread/empty_main.stderr @@ -2,6 +2,5 @@ warning: GenMC mode currently does not model spurious failures of `compare_excha warning: GenMC currently does not model the atomic failure ordering for `compare_exchange`. Failure ordering 'Relaxed' is treated like 'Acquire', which means that Miri might miss bugs related to this memory access (possible unsoundness)! - (GenMC) Verification complete. No errors were detected. Number of complete executions explored: 1 diff --git a/tests/genmc/pass/thread/miri_main_spawn_pthreads.stderr b/tests/genmc/pass/thread/miri_main_spawn_pthreads.stderr index 2382cc5a78..3b22247ee4 100644 --- a/tests/genmc/pass/thread/miri_main_spawn_pthreads.stderr +++ b/tests/genmc/pass/thread/miri_main_spawn_pthreads.stderr @@ -1,3 +1,2 @@ - (GenMC) Verification complete. No errors were detected. Number of complete executions explored: 1 diff --git a/tests/genmc/pass/thread/miri_main_spawn_threads.stderr b/tests/genmc/pass/thread/miri_main_spawn_threads.stderr index 8ac3e9f5cf..8e6a8b4056 100644 --- a/tests/genmc/pass/thread/miri_main_spawn_threads.stderr +++ b/tests/genmc/pass/thread/miri_main_spawn_threads.stderr @@ -4,6 +4,5 @@ warning: GenMC mode currently does not model spurious failures of `compare_excha warning: GenMC currently does not model the atomic failure ordering for `compare_exchange`. Failure ordering 'Acquire' is treated like 'Relaxed', which means that Miri might incorrectly detect errors related to this memory access (possible false positives). - (GenMC) Verification complete. No errors were detected. Number of complete executions explored: 1 diff --git a/tests/genmc/pass/thread/std_main_spawn_threads.stderr b/tests/genmc/pass/thread/std_main_spawn_threads.stderr index c6bd55a4f8..ecfee88d60 100644 --- a/tests/genmc/pass/thread/std_main_spawn_threads.stderr +++ b/tests/genmc/pass/thread/std_main_spawn_threads.stderr @@ -4,6 +4,5 @@ warning: GenMC currently does not model the atomic failure ordering for `compare warning: GenMC currently does not model the atomic failure ordering for `compare_exchange`. Failure ordering 'Acquire' is treated like 'Relaxed', which means that Miri might incorrectly detect errors related to this memory access (possible false positives). - (GenMC) Verification complete. No errors were detected. Number of complete executions explored: 1 diff --git a/tests/genmc/pass/thread/thread_locals.stderr b/tests/genmc/pass/thread/thread_locals.stderr index edecd38278..01701dfe69 100644 --- a/tests/genmc/pass/thread/thread_locals.stderr +++ b/tests/genmc/pass/thread/thread_locals.stderr @@ -1,3 +1,2 @@ - (GenMC) Verification complete. No errors were detected. Number of complete executions explored: 2 diff --git a/tests/genmc/pass/thread/thread_simple.stderr b/tests/genmc/pass/thread/thread_simple.stderr index edecd38278..01701dfe69 100644 --- a/tests/genmc/pass/thread/thread_simple.stderr +++ b/tests/genmc/pass/thread/thread_simple.stderr @@ -1,3 +1,2 @@ - (GenMC) Verification complete. No errors were detected. Number of complete executions explored: 2 From c0f9987f18007668819820054e5093b604e29f84 Mon Sep 17 00:00:00 2001 From: Patrick-6 Date: Wed, 30 Jul 2025 16:12:58 +0200 Subject: [PATCH 11/41] Remove duplicate call to handleAlloc. --- src/alloc_addresses/mod.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/alloc_addresses/mod.rs b/src/alloc_addresses/mod.rs index d28db3bb8a..95a1d9c0ed 100644 --- a/src/alloc_addresses/mod.rs +++ b/src/alloc_addresses/mod.rs @@ -118,12 +118,6 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { let this = self.eval_context_ref(); let info = this.get_alloc_info(alloc_id); - // Miri's address assignment leaks state across thread boundaries, which is incompatible - // with GenMC execution. So we instead let GenMC assign addresses to allocations. - if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() { - return genmc_ctx.handle_alloc(this, alloc_id, info.size, info.align, memory_kind); - } - // This is either called immediately after allocation (and then cached), or when // adjusting `tcx` pointers (which never get freed). So assert that we are looking // at a live allocation. This also ensures that we never re-assign an address to an From cd5be0fd38c338cb22cdbc9f62b385f841b37251 Mon Sep 17 00:00:00 2001 From: Patrick-6 Date: Wed, 30 Jul 2025 16:16:54 +0200 Subject: [PATCH 12/41] Improve comments --- genmc-sys/src/lib.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/genmc-sys/src/lib.rs b/genmc-sys/src/lib.rs index 4dfabaefe0..78b8baa4be 100644 --- a/genmc-sys/src/lib.rs +++ b/genmc-sys/src/lib.rs @@ -15,6 +15,8 @@ pub const GENMC_MAIN_THREAD_ID: GenmcThreadId = GenmcThreadId(0); impl GenmcScalar { pub const UNINIT: Self = Self { value: 0, extra: 0, is_init: false }; + /// GenMC expects a value for all stores, but we cannot always provide one (e.g., non-atomic writes). + /// FIXME(genmc): remove this if a permanent fix is ever found. pub const DUMMY: Self = Self::from_u64(0xDEADBEEF); pub const MUTEX_LOCKED_STATE: Self = Self::from_u64(1); @@ -63,7 +65,7 @@ mod ffi { enum MemOrdering { NotAtomic = 0, Relaxed = 1, - // In case we support consume + // We skip 2 in case we support consume. Acquire = 3, Release = 4, AcquireRelease = 5, From 8d49e293047bcd3ef2f88ed942f0741f2eb59c85 Mon Sep 17 00:00:00 2001 From: Patrick-6 Date: Wed, 30 Jul 2025 16:17:09 +0200 Subject: [PATCH 13/41] Cleanup C++ code. --- genmc-sys/src_cpp/MiriInterface.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/genmc-sys/src_cpp/MiriInterface.cpp b/genmc-sys/src_cpp/MiriInterface.cpp index 791ee4465a..57eb50b06c 100644 --- a/genmc-sys/src_cpp/MiriInterface.cpp +++ b/genmc-sys/src_cpp/MiriInterface.cpp @@ -29,7 +29,7 @@ using AnnotT = SExpr; // Return -1 when no thread can/should be scheduled, or the thread id of the next thread // NOTE: this is safe because ThreadId is 32 bit, and we return a 64 bit integer -// TODO GENMC: could directly return std::optional if CXX ever supports this +// FIXME(genmc,cxx): could directly return std::optional if CXX ever supports sharing it (see https://github.com/dtolnay/cxx/issues/87). auto MiriGenMCShim::scheduleNext(const int curr_thread_id, const ActionKind curr_thread_next_instr_kind) -> int64_t { @@ -186,18 +186,15 @@ void MiriGenMCShim::handleThreadCreate(ThreadId thread_id, ThreadId parent_id) void MiriGenMCShim::handleThreadJoin(ThreadId thread_id, ThreadId child_id) { - auto parentTid = thread_id; - auto childTid = child_id; - // The thread join event happens in the parent. - auto pos = incPos(parentTid); + auto pos = incPos(thread_id); // NOTE: Default memory ordering (`Acquire`) used here. - auto lab = std::make_unique(pos, childTid); + auto lab = std::make_unique(pos, child_id); auto res = GenMCDriver::handleThreadJoin(std::move(lab)); // If the join failed, decrease the event index again: if (!res.has_value()) - decPos(parentTid); + decPos(thread_id); // NOTE: Thread return value is ignored, since Miri doesn't need it. } @@ -242,7 +239,8 @@ void MiriGenMCShim::handleUserBlock(ThreadId thread_id) auto loc = SAddr(address); auto aSize = ASize(size); - auto type = AType::Unsigned; // FIXME(genmc): get correct type from Miri(?) + // `type` is only used for printing. + auto type = AType::Unsigned; auto newLab = std::make_unique(pos, ord, loc, aSize, type); @@ -265,6 +263,7 @@ void MiriGenMCShim::handleUserBlock(ThreadId thread_id) auto loc = SAddr(address); auto aSize = ASize(size); + // `type` is only used for printing. auto type = AType::Unsigned; auto rhsVal = rhs_value.toSVal(); @@ -306,6 +305,7 @@ void MiriGenMCShim::handleUserBlock(ThreadId thread_id) auto loc = SAddr(address); auto aSize = ASize(size); + // `type` is only used for printing. auto type = AType::Unsigned; auto expectedVal = expected_value.toSVal(); @@ -347,9 +347,9 @@ void MiriGenMCShim::handleUserBlock(ThreadId thread_id) auto loc = SAddr(address); auto aSize = ASize(size); - auto type = AType::Unsigned; // FIXME(genmc): get correct type from Miri(?) + // `type` is only used for printing. + auto type = AType::Unsigned; - // TODO GENMC: u128 support auto val = value.toSVal(); std::unique_ptr wLab; @@ -394,9 +394,9 @@ auto MiriGenMCShim::handleMalloc(ThreadId thread_id, uint64_t size, uint64_t ali { auto pos = incPos(thread_id); - // FIXME(genmc): get correct values from Miri + // These are only used for printing and features Miri-GenMC doesn't support (yet). auto sd = StorageDuration::SD_Heap; - auto stype = StorageType::ST_Durable; + auto stype = StorageType::ST_Volatile; auto spc = AddressSpace::AS_User; auto aLab = std::make_unique(pos, size, alignment, sd, stype, spc, EventDeps()); From 1dba4cdde875dbc42506a25c0972fc4272c61581 Mon Sep 17 00:00:00 2001 From: Patrick-6 Date: Wed, 30 Jul 2025 16:17:34 +0200 Subject: [PATCH 14/41] Use existing truncation function. --- src/concurrency/genmc/helper.rs | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/concurrency/genmc/helper.rs b/src/concurrency/genmc/helper.rs index 4d083196e7..f5b6f26b3e 100644 --- a/src/concurrency/genmc/helper.rs +++ b/src/concurrency/genmc/helper.rs @@ -135,16 +135,8 @@ pub fn genmc_scalar_to_scalar<'tcx>( } // NOTE: GenMC always returns 64 bit values, and the upper bits are not yet truncated. - // FIXME(genmc): Rework if 128bit support is added to GenMC. - let trunc_value = if size.bits() >= 64 { - scalar.value - } else { - // Zero out the upper bits. - let mask = (1u64 << size.bits()) - 1; - scalar.value & mask - }; - - let value_scalar_int = ScalarInt::try_from_uint(trunc_value, size).unwrap(); + // FIXME(genmc): GenMC should be doing the truncation, not Miri. + let (value_scalar_int, _got_truncated) = ScalarInt::truncate_from_uint(scalar.value, size); interp_ok(Scalar::Int(value_scalar_int)) } From 69f7689437c90878f9087b0bd89734b092c52ffb Mon Sep 17 00:00:00 2001 From: Patrick-6 Date: Wed, 30 Jul 2025 23:24:39 +0200 Subject: [PATCH 15/41] Clean up error handling and logs. --- src/concurrency/genmc/mod.rs | 46 ++++++++---------------------------- 1 file changed, 10 insertions(+), 36 deletions(-) diff --git a/src/concurrency/genmc/mod.rs b/src/concurrency/genmc/mod.rs index f92e1c8176..86c9229bb9 100644 --- a/src/concurrency/genmc/mod.rs +++ b/src/concurrency/genmc/mod.rs @@ -108,10 +108,8 @@ impl GenmcCtx { } pub fn print_genmc_graph(&self) { - info!("GenMC: print the Execution graph"); let mut mc = self.handle.borrow_mut(); - let pinned_mc = mc.as_mut(); - pinned_mc.printGraph(); + mc.as_mut().printGraph(); } /// This function determines if we should continue exploring executions or if we are done. @@ -158,9 +156,7 @@ impl GenmcCtx { let mut mc = self.handle.borrow_mut(); let result = mc.as_mut().handleExecutionEnd(); if let Some(msg) = result.as_ref() { - let msg = msg.to_string_lossy().to_string(); - info!("GenMC: execution ended with error \"{msg}\""); - Err(msg) // TODO GENMC: add more error info here, and possibly handle this without requiring to clone the CxxString + Err(msg.to_string_lossy().to_string()) } else { Ok(()) } @@ -193,13 +189,11 @@ impl GenmcCtx { // The value that we would get, if we were to do a non-atomic load here. old_val: Option, ) -> InterpResult<'tcx, Scalar> { - info!("GenMC: atomic_load: old_val: {old_val:?}"); assert!(!self.allow_data_races.get()); // TODO GENMC: handle this properly let ordering = ordering.convert(); let genmc_old_value = option_scalar_to_genmc_scalar(ecx, old_val)?; let read_value = self.atomic_load_impl(&ecx.machine, address, size, ordering, genmc_old_value)?; - info!("GenMC: atomic_load: received value from GenMC: {read_value:?}"); genmc_scalar_to_scalar(ecx, read_value, size) } @@ -226,7 +220,6 @@ impl GenmcCtx { ordering: AtomicFenceOrd, ) -> InterpResult<'tcx> { assert!(!self.allow_data_races.get()); // TODO GENMC: handle this properly - info!("GenMC: atomic_fence with ordering: {ordering:?}"); let ordering = ordering.convert(); @@ -404,9 +397,7 @@ impl GenmcCtx { ); if let Some(error) = cas_result.error.as_ref() { - let msg = error.to_string_lossy().to_string(); - info!("GenMC: RMW operation returned an error: \"{msg}\""); - throw_ub_format!("{}", msg); // TODO GENMC: proper error handling: find correct error here + throw_ub_format!("{}", error.to_string_lossy()); // TODO GENMC: proper error handling: find correct error here } let return_scalar = genmc_scalar_to_scalar(ecx, cas_result.old_value, size)?; @@ -752,9 +743,7 @@ impl GenmcCtx { } if let Some(error) = load_result.error.as_ref() { - let msg = error.to_string_lossy().to_string(); - info!("GenMC: load operation returned an error: \"{msg}\""); - throw_ub_format!("{}", msg); // TODO GENMC: proper error handling: find correct error here + throw_ub_format!("{}", error.to_string_lossy()); // TODO GENMC: proper error handling: find correct error here } info!("GenMC: load returned value: {:?}", load_result.read_value); @@ -803,9 +792,7 @@ impl GenmcCtx { ); if let Some(error) = store_result.error.as_ref() { - let msg = error.to_string_lossy().to_string(); - info!("GenMC: store operation returned an error: \"{msg}\""); - throw_ub_format!("{}", msg); // TODO GENMC: proper error handling: find correct error here + throw_ub_format!("{}", error.to_string_lossy()); // TODO GENMC: proper error handling: find correct error here } interp_ok(store_result.isCoMaxWrite) @@ -854,9 +841,7 @@ impl GenmcCtx { ); if let Some(error) = rmw_result.error.as_ref() { - let msg = error.to_string_lossy().to_string(); - info!("GenMC: RMW operation returned an error: \"{msg}\""); - throw_ub_format!("{}", msg); // TODO GENMC: proper error handling: find correct error here + throw_ub_format!("{}", error.to_string_lossy()); // TODO GENMC: proper error handling: find correct error here } let old_value_scalar = genmc_scalar_to_scalar(ecx, rmw_result.old_value, size)?; @@ -908,7 +893,6 @@ impl GenmcCtx { "GenMC: schedule_thread, active thread: {active_thread_id:?}, next instr.: '{curr_thread_next_instr_kind:?}'" ); - // let curr_thread_user_block = self.curr_thread_user_block.replace(false); let thread_infos = self.thread_infos.borrow(); let curr_thread_info = thread_infos.get_info(active_thread_id); @@ -990,9 +974,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { pinned_mc.handleMutexLock(genmc_curr_thread.0, addr, size) }; if let Some(error) = result.error.as_ref() { - let msg = error.to_string_lossy().to_string(); - info!("GenMC: handling Mutex::lock: error: {msg:?}"); - throw_ub_format!("{msg}"); + throw_ub_format!("{}", error.to_string_lossy()); } // TODO GENMC(HACK): for determining if the Mutex lock blocks this thread. if !result.is_lock_acquired { @@ -1020,17 +1002,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { pinned_mc.handleMutexLock(genmc_curr_thread, addr, size) }; if let Some(error) = result.error.as_ref() { - let msg = error.to_string_lossy().to_string(); - info!("GenMC: handling Mutex::lock: error: {msg:?}"); - throw_ub_format!("{msg}"); + throw_ub_format!("{}", error.to_string_lossy()); } // TODO GENMC(HACK): for determining if the Mutex lock blocks this thread. if !result.is_lock_acquired { // If this thread gets woken up without the mutex being made available, block the thread again. this.block_thread( crate::BlockReason::Mutex, None, create_callback(genmc_curr_thread, addr, size)); - // panic!( - // "Somehow, Mutex is still locked after waiting thread was unblocked?!" - // ); } interp_ok(()) } @@ -1060,9 +1037,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { pinned_mc.handleMutexTryLock(genmc_curr_thread.0, addr, size) }; if let Some(error) = result.error.as_ref() { - let msg = error.to_string_lossy().to_string(); - info!("GenMC: handling Mutex::try_lock: error: {msg:?}"); - throw_ub_format!("{msg}"); + throw_ub_format!("{}", error.to_string_lossy()); } info!( "GenMC: Mutex::try_lock(): writing resulting bool is_lock_acquired ({}) to place: {dest:?}", @@ -1079,8 +1054,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let pinned_mc = mc.as_mut(); let result = pinned_mc.handleMutexUnlock(genmc_curr_thread.0, addr, size); if let Some(error) = result.error.as_ref() { - let msg = error.to_string_lossy().to_string(); - throw_ub_format!("{msg}"); + throw_ub_format!("{}", error.to_string_lossy()); } // NOTE: We don't write anything back to Miri's memory, the Mutex state is handled only by GenMC. From 92978f09cce262a7adb1754465330a6c94078ef1 Mon Sep 17 00:00:00 2001 From: Patrick-6 Date: Thu, 31 Jul 2025 09:31:49 +0200 Subject: [PATCH 16/41] Clean up thread id mapping code. --- genmc-sys/src/lib.rs | 8 +- src/concurrency/genmc/mod.rs | 119 +++++++++---------- src/concurrency/genmc/thread_id_map.rs | 61 ++++++++++ src/concurrency/genmc/thread_info_manager.rs | 81 ------------- 4 files changed, 121 insertions(+), 148 deletions(-) create mode 100644 src/concurrency/genmc/thread_id_map.rs delete mode 100644 src/concurrency/genmc/thread_info_manager.rs diff --git a/genmc-sys/src/lib.rs b/genmc-sys/src/lib.rs index 78b8baa4be..df6c60019f 100644 --- a/genmc-sys/src/lib.rs +++ b/genmc-sys/src/lib.rs @@ -7,11 +7,9 @@ pub mod cxx_extra; /// but if https://github.com/dtolnay/cxx/issues/1051 is fixed we could share the constant directly. pub const GENMC_GLOBAL_ADDRESSES_MASK: u64 = 1 << 63; -#[repr(transparent)] -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct GenmcThreadId(pub i32); - -pub const GENMC_MAIN_THREAD_ID: GenmcThreadId = GenmcThreadId(0); +/// GenMC thread ids are C++ type `int`, which is equivalent to Rust's `i32` on most platforms. +/// The main thread always has thread id 0. +pub const GENMC_MAIN_THREAD_ID: i32 = 0; impl GenmcScalar { pub const UNINIT: Self = Self { value: 0, extra: 0, is_init: false }; diff --git a/src/concurrency/genmc/mod.rs b/src/concurrency/genmc/mod.rs index 86c9229bb9..441d23272f 100644 --- a/src/concurrency/genmc/mod.rs +++ b/src/concurrency/genmc/mod.rs @@ -4,8 +4,8 @@ use std::time::Duration; use genmc_sys::cxx_extra::NonNullUniquePtr; use genmc_sys::{ - ActionKind, GENMC_GLOBAL_ADDRESSES_MASK, GenmcScalar, GenmcThreadId, MemOrdering, - MiriGenMCShim, RMWBinOp, StoreEventType, createGenmcHandle, + ActionKind, GENMC_GLOBAL_ADDRESSES_MASK, GenmcScalar, MemOrdering, MiriGenMCShim, RMWBinOp, + StoreEventType, createGenmcHandle, }; use rustc_abi::{Align, Size}; use rustc_const_eval::interpret::{AllocId, InterpCx, InterpResult, interp_ok}; @@ -18,7 +18,7 @@ use self::helper::{ scalar_to_genmc_scalar, }; use self::mapping::{min_max_to_genmc_rmw_op, to_genmc_rmw_op}; -use self::thread_info_manager::ThreadInfoManager; +use self::thread_id_map::ThreadIdMap; use crate::concurrency::genmc::helper::{is_terminator_atomic, split_access}; use crate::concurrency::genmc::warnings::WarningsCache; use crate::concurrency::thread::{EvalContextExt as _, ThreadState}; @@ -33,7 +33,7 @@ mod global_allocations; mod helper; mod mapping; pub mod miri_genmc; -mod thread_info_manager; +mod thread_id_map; mod warnings; pub use genmc_sys::GenmcParams; @@ -53,7 +53,7 @@ pub struct GenmcCtx { handle: RefCell>, // TODO GENMC (PERFORMANCE): could use one RefCell for all internals instead of multiple - thread_infos: RefCell, + thread_id_manager: RefCell, /// Some actions Miri does are allowed to cause data races. /// GenMC will not be informed about certain actions (e.g. non-atomic loads) when this flag is set. @@ -66,7 +66,7 @@ pub struct GenmcCtx { // GenMC needs to have access to that // TODO: look at code of "pub struct GlobalStateInner" warnings_cache: RefCell, - // terminator_cache: RefCell, bool>>, + exit_status: Cell>, } @@ -83,7 +83,7 @@ impl GenmcCtx { Self { handle: non_null_handle, - thread_infos: Default::default(), + thread_id_manager: Default::default(), allow_data_races: Cell::new(false), global_allocations: Default::default(), warnings_cache: Default::default(), @@ -138,7 +138,7 @@ impl GenmcCtx { /// This function should be called at the start of every execution. pub(crate) fn handle_execution_start(&self) { self.allow_data_races.replace(false); - self.thread_infos.borrow_mut().reset(); + self.thread_id_manager.borrow_mut().reset(); self.exit_status.set(None); let mut mc = self.handle.borrow_mut(); @@ -223,13 +223,13 @@ impl GenmcCtx { let ordering = ordering.convert(); - let thread_infos = self.thread_infos.borrow(); + let thread_infos = self.thread_id_manager.borrow(); let curr_thread = machine.threads.active_thread(); - let genmc_tid = thread_infos.get_info(curr_thread).genmc_tid; + let genmc_tid = thread_infos.get_genmc_tid(curr_thread); let mut mc = self.handle.borrow_mut(); let pinned_mc = mc.as_mut(); - pinned_mc.handleFence(genmc_tid.0, ordering); + pinned_mc.handleFence(genmc_tid, ordering); // TODO GENMC: can this operation ever fail? interp_ok(()) @@ -370,9 +370,9 @@ impl GenmcCtx { "GenMC: atomic_compare_exchange orderings: success: ({success_load_ordering:?}, {success_store_ordering:?}), failure load ordering: {fail_load_ordering:?}" ); - let thread_infos = self.thread_infos.borrow(); + let thread_infos = self.thread_id_manager.borrow(); let curr_thread = machine.threads.active_thread(); - let genmc_tid = thread_infos.get_info(curr_thread).genmc_tid; + let genmc_tid = thread_infos.get_genmc_tid(curr_thread); let genmc_address = address.bytes(); let genmc_size = size.bytes(); @@ -384,7 +384,7 @@ impl GenmcCtx { let mut mc = self.handle.borrow_mut(); let pinned_mc = mc.as_mut(); let cas_result = pinned_mc.handleCompareExchange( - genmc_tid.0, + genmc_tid, genmc_address, genmc_size, genmc_expected_value, @@ -532,9 +532,9 @@ impl GenmcCtx { if self.allow_data_races.get() { unreachable!(); // FIXME(genmc): can this happen and if yes, how should this be handled? } - let thread_infos = self.thread_infos.borrow(); + let thread_infos = self.thread_id_manager.borrow(); let curr_thread = machine.threads.active_thread(); - let genmc_tid = thread_infos.get_info(curr_thread).genmc_tid; + let genmc_tid = thread_infos.get_genmc_tid(curr_thread); // GenMC doesn't support ZSTs, so we set the minimum size to 1 byte let genmc_size = size.bytes().max(1); info!( @@ -546,7 +546,7 @@ impl GenmcCtx { let mut mc = self.handle.borrow_mut(); let pinned_mc = mc.as_mut(); - let chosen_address = pinned_mc.handleMalloc(genmc_tid.0, genmc_size, alignment); + let chosen_address = pinned_mc.handleMalloc(genmc_tid, genmc_size, alignment); info!("GenMC: handle_alloc: got address '{chosen_address}' ({chosen_address:#x})"); // TODO GENMC: @@ -584,9 +584,9 @@ impl GenmcCtx { if self.allow_data_races.get() { unreachable!(); // FIXME(genmc): can this happen and if yes, how should this be handled? } - let thread_infos = self.thread_infos.borrow(); + let thread_infos = self.thread_id_manager.borrow(); let curr_thread = machine.threads.active_thread(); - let genmc_tid = thread_infos.get_info(curr_thread).genmc_tid; + let genmc_tid = thread_infos.get_genmc_tid(curr_thread); info!( "GenMC: memory deallocation, thread: {curr_thread:?} ({genmc_tid:?}), address: {addr} == {addr:#x}, size: {size:?}, align: {align:?}, memory_kind: {kind:?}", addr = address.bytes() @@ -598,7 +598,7 @@ impl GenmcCtx { let mut mc = self.handle.borrow_mut(); let pinned_mc = mc.as_mut(); - pinned_mc.handleFree(genmc_tid.0, genmc_address, genmc_size); + pinned_mc.handleFree(genmc_tid, genmc_address, genmc_size); // TODO GENMC (ERROR HANDLING): can this ever fail? interp_ok(()) @@ -614,10 +614,10 @@ impl GenmcCtx { new_thread_id: ThreadId, ) -> InterpResult<'tcx> { assert!(!self.allow_data_races.get()); // TODO GENMC: handle this properly - let mut thread_infos = self.thread_infos.borrow_mut(); + let mut thread_infos = self.thread_id_manager.borrow_mut(); let curr_thread_id = threads.active_thread(); - let genmc_parent_tid = thread_infos.get_info(curr_thread_id).genmc_tid; + let genmc_parent_tid = thread_infos.get_genmc_tid(curr_thread_id); let genmc_new_tid = thread_infos.add_thread(new_thread_id); info!( @@ -625,7 +625,7 @@ impl GenmcCtx { ); let mut mc = self.handle.borrow_mut(); - mc.as_mut().handleThreadCreate(genmc_new_tid.0, genmc_parent_tid.0); + mc.as_mut().handleThreadCreate(genmc_new_tid, genmc_parent_tid); // TODO GENMC (ERROR HANDLING): can this ever fail? interp_ok(()) @@ -637,17 +637,17 @@ impl GenmcCtx { child_thread_id: ThreadId, ) -> InterpResult<'tcx> { assert!(!self.allow_data_races.get()); // TODO GENMC: handle this properly - let thread_infos = self.thread_infos.borrow(); + let thread_infos = self.thread_id_manager.borrow(); - let genmc_curr_tid = thread_infos.get_info(active_thread_id).genmc_tid; - let genmc_child_tid = thread_infos.get_info(child_thread_id).genmc_tid; + let genmc_curr_tid = thread_infos.get_genmc_tid(active_thread_id); + let genmc_child_tid = thread_infos.get_genmc_tid(child_thread_id); info!( "GenMC: handling thread joining (thread {active_thread_id:?} ({genmc_curr_tid:?}) joining thread {child_thread_id:?} ({genmc_child_tid:?}))" ); let mut mc = self.handle.borrow_mut(); - mc.as_mut().handleThreadJoin(genmc_curr_tid.0, genmc_child_tid.0); + mc.as_mut().handleThreadJoin(genmc_curr_tid, genmc_child_tid); interp_ok(()) } @@ -656,8 +656,8 @@ impl GenmcCtx { assert!(!self.allow_data_races.get()); // TODO GENMC: handle this properly let curr_thread_id = threads.active_thread(); - let thread_infos = self.thread_infos.borrow(); - let genmc_tid = thread_infos.get_info(curr_thread_id).genmc_tid; + let thread_infos = self.thread_id_manager.borrow(); + let genmc_tid = thread_infos.get_genmc_tid(curr_thread_id); // NOTE: Miri doesn't support return values for threads, but GenMC expects one, so we return 0 let ret_val = 0; @@ -667,7 +667,7 @@ impl GenmcCtx { ); let mut mc = self.handle.borrow_mut(); - mc.as_mut().handleThreadFinish(genmc_tid.0, ret_val); + mc.as_mut().handleThreadFinish(genmc_tid, ret_val); } /// Handle a call to `libc::exit` or the exit of the main thread. @@ -689,11 +689,11 @@ impl GenmcCtx { } if is_exit_call { - let thread_infos = self.thread_infos.borrow(); - let genmc_tid = thread_infos.get_info(thread).genmc_tid; + let thread_infos = self.thread_id_manager.borrow(); + let genmc_tid = thread_infos.get_genmc_tid(thread); let mut mc = self.handle.borrow_mut(); - mc.as_mut().handleThreadKill(genmc_tid.0); + mc.as_mut().handleThreadKill(genmc_tid); } interp_ok(()) } @@ -717,9 +717,9 @@ impl GenmcCtx { throw_unsup_format!("{UNSUPPORTED_ATOMICS_SIZE_MSG}"); } assert!(!self.allow_data_races.get()); // TODO GENMC: handle this properly - let thread_infos = self.thread_infos.borrow(); + let thread_infos = self.thread_id_manager.borrow(); let curr_thread_id = machine.threads.active_thread(); - let genmc_tid = thread_infos.get_info(curr_thread_id).genmc_tid; + let genmc_tid = thread_infos.get_genmc_tid(curr_thread_id); info!( "GenMC: load, thread: {curr_thread_id:?} ({genmc_tid:?}), address: {addr} == {addr:#x}, size: {size:?}, ordering: {memory_ordering:?}, old_value: {genmc_old_value:x?}", @@ -731,7 +731,7 @@ impl GenmcCtx { let mut mc = self.handle.borrow_mut(); let pinned_mc = mc.as_mut(); let load_result = pinned_mc.handleLoad( - genmc_tid.0, + genmc_tid, genmc_address, genmc_size, memory_ordering, @@ -767,9 +767,9 @@ impl GenmcCtx { if size.bytes() > 8 { throw_unsup_format!("{UNSUPPORTED_ATOMICS_SIZE_MSG}"); } - let thread_infos = self.thread_infos.borrow(); + let thread_infos = self.thread_id_manager.borrow(); let curr_thread_id = machine.threads.active_thread(); - let genmc_tid = thread_infos.get_info(curr_thread_id).genmc_tid; + let genmc_tid = thread_infos.get_genmc_tid(curr_thread_id); let genmc_address = address.bytes(); let genmc_size = size.bytes(); @@ -782,7 +782,7 @@ impl GenmcCtx { let mut mc = self.handle.borrow_mut(); let pinned_mc = mc.as_mut(); let store_result = pinned_mc.handleStore( - genmc_tid.0, + genmc_tid, genmc_address, genmc_size, genmc_value, @@ -816,9 +816,9 @@ impl GenmcCtx { ); let machine = &ecx.machine; assert_ne!(0, size.bytes()); - let thread_infos = self.thread_infos.borrow(); + let thread_infos = self.thread_id_manager.borrow(); let curr_thread_id = machine.threads.active_thread(); - let genmc_tid = thread_infos.get_info(curr_thread_id).genmc_tid; + let genmc_tid = thread_infos.get_genmc_tid(curr_thread_id); let genmc_address = address.bytes(); let genmc_size = size.bytes(); @@ -830,7 +830,7 @@ impl GenmcCtx { let mut mc = self.handle.borrow_mut(); let pinned_mc = mc.as_mut(); let rmw_result = pinned_mc.handleReadModifyWrite( - genmc_tid.0, + genmc_tid, genmc_address, genmc_size, load_ordering, @@ -893,19 +893,16 @@ impl GenmcCtx { "GenMC: schedule_thread, active thread: {active_thread_id:?}, next instr.: '{curr_thread_next_instr_kind:?}'" ); - let thread_infos = self.thread_infos.borrow(); - let curr_thread_info = thread_infos.get_info(active_thread_id); + let thread_infos = self.thread_id_manager.borrow(); + let curr_thread_info = thread_infos.get_genmc_tid(active_thread_id); let mut mc = self.handle.borrow_mut(); let pinned_mc = mc.as_mut(); - let result = - pinned_mc.scheduleNext(curr_thread_info.genmc_tid.0, curr_thread_next_instr_kind); + let result = pinned_mc.scheduleNext(curr_thread_info, curr_thread_next_instr_kind); if result >= 0 { - // TODO GENMC: can we ensure this thread_id is valid? - let genmc_next_thread_id = result.try_into().unwrap(); - let genmc_next_thread_id = GenmcThreadId(genmc_next_thread_id); - let thread_infos = self.thread_infos.borrow(); - let next_thread_id = thread_infos.get_info_genmc(genmc_next_thread_id).miri_tid; + let next_thread_id = thread_infos + .try_get_miri_tid(result) + .expect("A thread id returned from GenMC should exist."); return interp_ok(next_thread_id); } @@ -918,14 +915,14 @@ impl GenmcCtx { /**** Blocking functionality ****/ fn handle_user_block<'tcx>(&self, machine: &MiriMachine<'tcx>) -> InterpResult<'tcx> { - let thread_infos = self.thread_infos.borrow(); + let thread_infos = self.thread_id_manager.borrow(); let curr_thread = machine.threads.active_thread(); - let genmc_curr_thread = thread_infos.get_info(curr_thread).genmc_tid; + let genmc_curr_thread = thread_infos.get_genmc_tid(curr_thread); info!("GenMC: handle_user_block, blocking thread {curr_thread:?} ({genmc_curr_thread:?})"); let mut mc = self.handle.borrow_mut(); let pinned_mc = mc.as_mut(); - pinned_mc.handleUserBlock(genmc_curr_thread.0); + pinned_mc.handleUserBlock(genmc_curr_thread); interp_ok(()) } @@ -957,9 +954,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // FIXME(genmc): assert that we have at least 1 byte. // FIXME(genmc): maybe use actual size of mutex here?. - let thread_infos = genmc_ctx.thread_infos.borrow(); + let thread_infos = genmc_ctx.thread_id_manager.borrow(); let curr_thread = this.machine.threads.active_thread(); - let genmc_curr_thread = thread_infos.get_info(curr_thread).genmc_tid; + let genmc_curr_thread = thread_infos.get_genmc_tid(curr_thread); interp_ok((genmc_curr_thread, addr, 1)) }; @@ -971,7 +968,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let result = { let mut mc = genmc_ctx.handle.borrow_mut(); let pinned_mc = mc.as_mut(); - pinned_mc.handleMutexLock(genmc_curr_thread.0, addr, size) + pinned_mc.handleMutexLock(genmc_curr_thread, addr, size) }; if let Some(error) = result.error.as_ref() { throw_ub_format!("{}", error.to_string_lossy()); @@ -1017,8 +1014,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { info!("GenMC: handling Mutex::lock failed, blocking thread"); // NOTE: We don't write anything back to Miri's memory, the Mutex state is handled only by GenMC. - info!("GenMC: blocking thread due to intercepted call."); - let genmc_curr_thread = genmc_curr_thread.0; this.block_thread( crate::BlockReason::Mutex, None, @@ -1034,7 +1029,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let result = { let mut mc = genmc_ctx.handle.borrow_mut(); let pinned_mc = mc.as_mut(); - pinned_mc.handleMutexTryLock(genmc_curr_thread.0, addr, size) + pinned_mc.handleMutexTryLock(genmc_curr_thread, addr, size) }; if let Some(error) = result.error.as_ref() { throw_ub_format!("{}", error.to_string_lossy()); @@ -1052,7 +1047,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let mut mc = genmc_ctx.handle.borrow_mut(); let pinned_mc = mc.as_mut(); - let result = pinned_mc.handleMutexUnlock(genmc_curr_thread.0, addr, size); + let result = pinned_mc.handleMutexUnlock(genmc_curr_thread, addr, size); if let Some(error) = result.error.as_ref() { throw_ub_format!("{}", error.to_string_lossy()); } @@ -1152,7 +1147,7 @@ impl std::fmt::Debug for GenmcCtx { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("GenmcCtx") // .field("mc", &self.mc) - .field("thread_infos", &self.thread_infos) + .field("thread_infos", &self.thread_id_manager) .finish_non_exhaustive() } } diff --git a/src/concurrency/genmc/thread_id_map.rs b/src/concurrency/genmc/thread_id_map.rs new file mode 100644 index 0000000000..0b2247ca87 --- /dev/null +++ b/src/concurrency/genmc/thread_id_map.rs @@ -0,0 +1,61 @@ +use genmc_sys::GENMC_MAIN_THREAD_ID; +use rustc_data_structures::fx::FxHashMap; + +use crate::ThreadId; + +#[derive(Debug)] +pub struct ThreadIdMap { + /// Map from Miri thread IDs to GenMC thread IDs. + /// We assume as little as possible about Miri thread IDs, so we use a map. + miri_to_genmc: FxHashMap, + /// Map from GenMC thread IDs to Miri thread IDs. + /// We control which thread IDs are used, so we choose them in as an incrementing counter. + genmc_to_miri: Vec, // FIXME(genmc): check if this assumption is (and will stay) correct. +} + +impl Default for ThreadIdMap { + fn default() -> Self { + let miri_to_genmc = [(ThreadId::MAIN_THREAD, GENMC_MAIN_THREAD_ID)].into_iter().collect(); + let genmc_to_miri = vec![ThreadId::MAIN_THREAD]; + Self { miri_to_genmc, genmc_to_miri } + } +} + +impl ThreadIdMap { + pub fn reset(&mut self) { + self.miri_to_genmc.clear(); + self.miri_to_genmc.insert(ThreadId::MAIN_THREAD, GENMC_MAIN_THREAD_ID); + self.genmc_to_miri.clear(); + self.genmc_to_miri.push(ThreadId::MAIN_THREAD); + } + + #[must_use] + /// Add a new Miri thread to the mapping and dispense a new thread ID for GenMC to use. + pub fn add_thread(&mut self, thread_id: ThreadId) -> i32 { + // NOTE: We select the new thread ids as integers incremented by one (we use the length as the counter). + let next_thread_id = self.genmc_to_miri.len(); + let genmc_tid = next_thread_id.try_into().unwrap(); + // FIXME(genmc): Fix this, or document this where ThreadIds are created (where is this?) + if self.miri_to_genmc.insert(thread_id, genmc_tid).is_some() { + panic!("Cannot reuse thread ids: thread id {thread_id:?} already inserted."); + } + self.genmc_to_miri.push(thread_id); + + genmc_tid + } + + #[must_use] + /// Try to get the GenMC thread ID corresponding to a given Miri `ThreadId`. + /// Panics if there is no mapping for the given `ThreadId`. + pub fn get_genmc_tid(&self, thread_id: ThreadId) -> i32 { + *self.miri_to_genmc.get(&thread_id).unwrap() + } + + #[must_use] + /// Try to get the Miri `ThreadId` corresponding to a given GenMC thread id. + pub fn try_get_miri_tid(&self, genmc_tid: impl TryInto) -> Option { + let genmc_tid: i32 = genmc_tid.try_into().ok()?; + let index: usize = genmc_tid.try_into().ok()?; + self.genmc_to_miri.get(index).cloned() + } +} diff --git a/src/concurrency/genmc/thread_info_manager.rs b/src/concurrency/genmc/thread_info_manager.rs deleted file mode 100644 index 3a8a6906fb..0000000000 --- a/src/concurrency/genmc/thread_info_manager.rs +++ /dev/null @@ -1,81 +0,0 @@ -use genmc_sys::{GENMC_MAIN_THREAD_ID, GenmcThreadId}; -use rustc_data_structures::fx::FxHashMap; - -use crate::ThreadId; - -#[derive(Debug)] -pub struct ThreadInfo { - pub miri_tid: ThreadId, - pub genmc_tid: GenmcThreadId, -} - -impl ThreadInfo { - const MAIN_THREAD_INFO: Self = Self::new(ThreadId::MAIN_THREAD, GENMC_MAIN_THREAD_ID); - - #[must_use] - pub const fn new(miri_tid: ThreadId, genmc_tid: GenmcThreadId) -> Self { - Self { miri_tid, genmc_tid } - } -} - -#[derive(Debug)] -pub struct ThreadInfoManager { - tid_map: FxHashMap, - thread_infos: Vec, -} - -impl Default for ThreadInfoManager { - fn default() -> Self { - Self::new() - } -} - -impl ThreadInfoManager { - #[must_use] - pub fn new() -> Self { - let mut tid_map = FxHashMap::default(); - tid_map.insert(ThreadId::MAIN_THREAD, GENMC_MAIN_THREAD_ID); - let thread_infos = vec![ThreadInfo::MAIN_THREAD_INFO]; - Self { tid_map, thread_infos } - } - - pub fn reset(&mut self) { - self.tid_map.clear(); - self.tid_map.insert(ThreadId::MAIN_THREAD, GENMC_MAIN_THREAD_ID); - self.thread_infos.clear(); - self.thread_infos.push(ThreadInfo::MAIN_THREAD_INFO); - } - - #[must_use] - #[allow(unused)] - pub fn thread_count(&self) -> usize { - self.thread_infos.len() - } - - pub fn add_thread(&mut self, thread_id: ThreadId) -> GenmcThreadId { - // NOTE: GenMC thread ids are integers incremented by one every time - let index = self.thread_infos.len(); - let genmc_tid = GenmcThreadId(index.try_into().unwrap()); - let thread_info = ThreadInfo::new(thread_id, genmc_tid); - // TODO GENMC: Document this in place where ThreadIds are created - assert!( - self.tid_map.insert(thread_id, genmc_tid).is_none(), - "Cannot reuse thread ids: thread id {thread_id:?} already inserted" - ); - self.thread_infos.push(thread_info); - - genmc_tid - } - - #[must_use] - pub fn get_info(&self, thread_id: ThreadId) -> &ThreadInfo { - let genmc_tid = *self.tid_map.get(&thread_id).unwrap(); - self.get_info_genmc(genmc_tid) - } - - #[must_use] - pub fn get_info_genmc(&self, genmc_tid: GenmcThreadId) -> &ThreadInfo { - let index: usize = genmc_tid.0.try_into().unwrap(); - &self.thread_infos[index] - } -} From a9dbdf8ad9d47af00059528097f8a7a8f58cd9b2 Mon Sep 17 00:00:00 2001 From: Patrick-6 Date: Fri, 1 Aug 2025 14:59:30 +0200 Subject: [PATCH 17/41] Extract address generator struct, clean up GenMC global allocator code. --- genmc-sys/src/lib.rs | 8 +- src/alloc_addresses/address_generator.rs | 77 ++++++++++++ src/alloc_addresses/mod.rs | 65 ++-------- src/bin/miri.rs | 5 +- src/concurrency/genmc/global_allocations.rs | 131 ++++++++------------ src/concurrency/genmc/miri_genmc.rs | 3 +- src/concurrency/genmc/mod.rs | 8 +- src/machine.rs | 7 +- 8 files changed, 165 insertions(+), 139 deletions(-) create mode 100644 src/alloc_addresses/address_generator.rs diff --git a/genmc-sys/src/lib.rs b/genmc-sys/src/lib.rs index df6c60019f..aadd81091f 100644 --- a/genmc-sys/src/lib.rs +++ b/genmc-sys/src/lib.rs @@ -2,8 +2,12 @@ pub use self::ffi::*; pub mod cxx_extra; -/// Defined in "genmc/src/Support/SAddr.hpp" -/// FIXME: currently we use `getGlobalAllocStaticMask()` to ensure the constant is consistent between Miri and GenMC, +/// Defined in "genmc/src/Support/SAddr.hpp". +/// The first bit of all global addresses must be set to `1`, the rest are the actual address. +/// This means the mask, interpreted as an address, is the lower bound of where the global address space starts. +/// +/// FIXME(genmc): rework this if non-64bit support is added to GenMC (the current allocation scheme only allows for 64bit addresses). +/// FIXME(genmc): currently we use `getGlobalAllocStaticMask()` to ensure the constant is consistent between Miri and GenMC, /// but if https://github.com/dtolnay/cxx/issues/1051 is fixed we could share the constant directly. pub const GENMC_GLOBAL_ADDRESSES_MASK: u64 = 1 << 63; diff --git a/src/alloc_addresses/address_generator.rs b/src/alloc_addresses/address_generator.rs new file mode 100644 index 0000000000..2a0f4291fc --- /dev/null +++ b/src/alloc_addresses/address_generator.rs @@ -0,0 +1,77 @@ +use std::ops::Range; + +use rand::Rng; +use rustc_abi::{Align, Size}; +use rustc_const_eval::interpret::{InterpResult, interp_ok}; +use rustc_middle::{err_exhaust, throw_exhaust}; + +/// Shifts `addr` to make it aligned with `align` by rounding `addr` to the smallest multiple +/// of `align` that is larger or equal to `addr` +fn align_addr(addr: u64, align: u64) -> u64 { + match addr % align { + 0 => addr, + rem => addr.strict_add(align) - rem, + } +} + +#[derive(Debug)] +pub struct AddressGenerator { + /// This is used as a memory address when a new pointer is casted to an integer. It + /// is always larger than any address that was previously made part of a block. + next_base_addr: u64, + /// This is the last address that can be allocated. + end: u64, +} + +impl AddressGenerator { + pub fn new(addr_range: Range) -> Self { + Self { next_base_addr: addr_range.start, end: addr_range.end } + } + + /// Get the remaining range where this `AddressGenerator` can still allocate addresses. + pub fn get_remaining(&self) -> Range { + self.next_base_addr..self.end + } + + /// Generate a new address with the specified size and alignment, using the given Rng to add some randomness. + /// The returned allocation is guaranteed not to overlap with any address ranges given out by the generator before. + /// Returns an error if the allocation request cannot be fulfilled. + pub fn generate<'tcx, R: Rng>( + &mut self, + size: Size, + align: Align, + rng: &mut R, + ) -> InterpResult<'tcx, u64> { + // Leave some space to the previous allocation, to give it some chance to be less aligned. + // We ensure that `(self.next_base_addr + slack) % 16` is uniformly distributed. + let slack = rng.random_range(0..16); + // From next_base_addr + slack, round up to adjust for alignment. + let base_addr = + self.next_base_addr.checked_add(slack).ok_or_else(|| err_exhaust!(AddressSpaceFull))?; + let base_addr = align_addr(base_addr, align.bytes()); + + // Remember next base address. If this allocation is zero-sized, leave a gap of at + // least 1 to avoid two allocations having the same base address. (The logic in + // `alloc_id_from_addr` assumes unique addresses, and different function/vtable pointers + // need to be distinguishable!) + self.next_base_addr = base_addr + .checked_add(size.bytes().max(1)) + .ok_or_else(|| err_exhaust!(AddressSpaceFull))?; + // Even if `Size` didn't overflow, we might still have filled up the address space. + if self.next_base_addr > self.end { + throw_exhaust!(AddressSpaceFull); + } + interp_ok(base_addr) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_align_addr() { + assert_eq!(align_addr(37, 4), 40); + assert_eq!(align_addr(44, 4), 44); + } +} diff --git a/src/alloc_addresses/mod.rs b/src/alloc_addresses/mod.rs index 95a1d9c0ed..402fd7cebb 100644 --- a/src/alloc_addresses/mod.rs +++ b/src/alloc_addresses/mod.rs @@ -1,15 +1,15 @@ //! This module is responsible for managing the absolute addresses that allocations are located at, //! and for casting between pointers and integers based on those addresses. +mod address_generator; mod reuse_pool; use std::cell::RefCell; -use std::cmp::max; -use rand::Rng; use rustc_abi::{Align, Size}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +pub use self::address_generator::AddressGenerator; use self::reuse_pool::ReusePool; use crate::concurrency::VClock; use crate::*; @@ -50,9 +50,8 @@ pub struct GlobalStateInner { /// Whether an allocation has been exposed or not. This cannot be put /// into `AllocExtra` for the same reason as `base_addr`. exposed: FxHashSet, - /// This is used as a memory address when a new pointer is casted to an integer. It - /// is always larger than any address that was previously made part of a block. - next_base_addr: u64, + /// The generator for new addresses in a given range. + address_generator: AddressGenerator, /// The provenance to use for int2ptr casts provenance_mode: ProvenanceMode, } @@ -65,7 +64,7 @@ impl VisitProvenance for GlobalStateInner { prepared_alloc_bytes: _, reuse: _, exposed: _, - next_base_addr: _, + address_generator: _, provenance_mode: _, } = self; // Though base_addr, int_to_ptr_map, and exposed contain AllocIds, we do not want to visit them. @@ -78,14 +77,14 @@ impl VisitProvenance for GlobalStateInner { } impl GlobalStateInner { - pub fn new(config: &MiriConfig, stack_addr: u64) -> Self { + pub fn new(config: &MiriConfig, stack_addr: u64, last_addr: u64) -> Self { GlobalStateInner { int_to_ptr_map: Vec::default(), base_addr: FxHashMap::default(), prepared_alloc_bytes: FxHashMap::default(), reuse: ReusePool::new(config), exposed: FxHashSet::default(), - next_base_addr: stack_addr, + address_generator: AddressGenerator::new(stack_addr..last_addr), provenance_mode: config.provenance_mode, } } @@ -97,16 +96,6 @@ impl GlobalStateInner { } } -/// Shifts `addr` to make it aligned with `align` by rounding `addr` to the smallest multiple -/// of `align` that is larger or equal to `addr` -/// FIXME(GenMC): is it ok to make this public? -pub(crate) fn align_addr(addr: u64, align: u64) -> u64 { - match addr % align { - 0 => addr, - rem => addr.strict_add(align) - rem, - } -} - impl<'tcx> EvalContextExtPriv<'tcx> for crate::MiriInterpCx<'tcx> {} trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { fn addr_from_alloc_id_uncached( @@ -197,34 +186,17 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { interp_ok(reuse_addr) } else { // We have to pick a fresh address. - // Leave some space to the previous allocation, to give it some chance to be less aligned. - // We ensure that `(global_state.next_base_addr + slack) % 16` is uniformly distributed. - let slack = rng.random_range(0..16); - // From next_base_addr + slack, round up to adjust for alignment. - let base_addr = global_state - .next_base_addr - .checked_add(slack) - .ok_or_else(|| err_exhaust!(AddressSpaceFull))?; - let base_addr = align_addr(base_addr, info.align.bytes()); - - // Remember next base address. If this allocation is zero-sized, leave a gap of at - // least 1 to avoid two allocations having the same base address. (The logic in - // `alloc_id_from_addr` assumes unique addresses, and different function/vtable pointers - // need to be distinguishable!) - global_state.next_base_addr = base_addr - .checked_add(max(info.size.bytes(), 1)) - .ok_or_else(|| err_exhaust!(AddressSpaceFull))?; - // Even if `Size` didn't overflow, we might still have filled up the address space. - if global_state.next_base_addr > this.target_usize_max() { - throw_exhaust!(AddressSpaceFull); - } + let new_addr = + global_state.address_generator.generate(info.size, info.align, &mut rng)?; + // If we filled up more than half the address space, start aggressively reusing // addresses to avoid running out. - if global_state.next_base_addr > u64::try_from(this.target_isize_max()).unwrap() { + let remaining_range = global_state.address_generator.get_remaining(); + if remaining_range.start > remaining_range.end / 2 { global_state.reuse.address_space_shortage(); } - interp_ok(base_addr) + interp_ok(new_addr) } } } @@ -529,14 +501,3 @@ impl<'tcx> MiriMachine<'tcx> { }) } } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_align_addr() { - assert_eq!(align_addr(37, 4), 40); - assert_eq!(align_addr(44, 4), 44); - } -} diff --git a/src/bin/miri.rs b/src/bin/miri.rs index ef4e46b459..e8f8c7ac45 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -39,7 +39,7 @@ use std::sync::atomic::{AtomicI32, AtomicU32, Ordering}; use miri::{ BacktraceStyle, BorrowTrackerMethod, GenmcConfig, GenmcCtx, MiriConfig, MiriEntryFnType, - ProvenanceMode, RetagFields, TreeBorrowsParams, ValidationMode, miri_genmc, + PointerArithmetic, ProvenanceMode, RetagFields, TreeBorrowsParams, ValidationMode, miri_genmc, }; use rustc_abi::ExternAbi; use rustc_data_structures::sync; @@ -187,6 +187,7 @@ impl rustc_driver::Callbacks for MiriCompilerCalls { } if let Some(genmc_config) = &config.genmc_config { + let target_usize_max = tcx.target_usize_max(); let eval_entry_once = |genmc_ctx: Rc| { miri::eval_entry(tcx, entry_def_id, entry_type, &config, Some(genmc_ctx)) }; @@ -196,6 +197,7 @@ impl rustc_driver::Callbacks for MiriCompilerCalls { &config, genmc_config, eval_entry_once, + target_usize_max, miri_genmc::Mode::Estimation, ) .is_some() @@ -207,6 +209,7 @@ impl rustc_driver::Callbacks for MiriCompilerCalls { &config, genmc_config, eval_entry_once, + target_usize_max, miri_genmc::Mode::Verification, ) .unwrap_or_else(|| { diff --git a/src/concurrency/genmc/global_allocations.rs b/src/concurrency/genmc/global_allocations.rs index e2fafba9cc..f557aa976d 100644 --- a/src/concurrency/genmc/global_allocations.rs +++ b/src/concurrency/genmc/global_allocations.rs @@ -1,58 +1,59 @@ -use std::cmp::max; use std::collections::hash_map::Entry; use std::sync::RwLock; use genmc_sys::{GENMC_GLOBAL_ADDRESSES_MASK, getGlobalAllocStaticMask}; +use rand::SeedableRng; use rand::rngs::StdRng; -use rand::{Rng, SeedableRng}; -use rustc_const_eval::interpret::{ - AllocId, AllocInfo, AllocKind, InterpResult, PointerArithmetic, interp_ok, -}; +use rustc_const_eval::interpret::{AllocId, AllocInfo, InterpResult, interp_ok}; use rustc_data_structures::fx::FxHashMap; -use rustc_middle::{err_exhaust, throw_exhaust}; use tracing::info; -use crate::alloc_addresses::align_addr; - -#[derive(Debug, Default)] -pub struct GlobalAllocationHandler { - inner: RwLock, +use crate::alloc_addresses::AddressGenerator; + +/// Allocator for global memory in GenMC mode. +/// Miri doesn't discover all global allocations statically like LLI does for GenMC. +/// The existing global memory allocator in GenMC doesn't support this, so we take over these allocations. +/// Global allocations need to be in a specific address range, with the lower limit given by the `GENMC_GLOBAL_ADDRESSES_MASK` constant. +/// +/// Every global allocation must have the same addresses across all executions of a single program. +/// Therefore there is only 1 global allocator, and it syncs new globals across executions, even if they are explored in parallel. +#[derive(Debug)] +pub struct GlobalAllocationHandler(RwLock); + +impl GlobalAllocationHandler { + pub fn new(last_addr: u64) -> GlobalAllocationHandler { + Self(RwLock::new(GlobalStateInner::new(last_addr))) + } } -/// This contains more or less a subset of the functionality of `struct GlobalStateInner` in `alloc_addresses`. -#[derive(Clone, Debug)] +#[derive(Debug)] struct GlobalStateInner { - /// This is used as a map between the address of each allocation and its `AllocId`. It is always - /// sorted by address. We cannot use a `HashMap` since we can be given an address that is offset - /// from the base address, and we need to find the `AllocId` it belongs to. This is not the - /// *full* inverse of `base_addr`; dead allocations have been removed. - #[allow(unused)] // FIXME(GenMC): do we need this? - int_to_ptr_map: Vec<(u64, AllocId)>, /// The base address for each allocation. /// This is the inverse of `int_to_ptr_map`. base_addr: FxHashMap, - /// This is used as a memory address when a new pointer is casted to an integer. It - /// is always larger than any address that was previously made part of a block. - next_base_addr: u64, - /// To add some randomness to the allocations - /// FIXME(GenMC): maybe seed this from the rng in MiriMachine? + /// We use the same address generator that Miri uses in normal operation. + address_generator: AddressGenerator, + /// The address generator needs an Rng to randomize the offsets between allocations. + /// We don't use the `MiriMachine` Rng, since we don't want it to be reset after every execution. rng: StdRng, } -impl Default for GlobalStateInner { - fn default() -> Self { - Self::new() - } -} - impl GlobalStateInner { - pub fn new() -> Self { + /// Create a new global address generator with a given max address (corresponding to the highest address available on the target platform, unless another limit exists). + /// No addresses higher than this will be allocated. + /// Will return an error if the given address limit is too small to allocate any addresses. + fn new(last_addr: u64) -> Self { assert_eq!(GENMC_GLOBAL_ADDRESSES_MASK, getGlobalAllocStaticMask()); assert_ne!(GENMC_GLOBAL_ADDRESSES_MASK, 0); + // FIXME(genmc): Remove if non-64bit targets are supported. + assert!( + GENMC_GLOBAL_ADDRESSES_MASK < last_addr, + "only 64bit platforms are currently supported (highest address {last_addr:#x} <= minimum global address {GENMC_GLOBAL_ADDRESSES_MASK:#x})." + ); Self { - int_to_ptr_map: Vec::default(), base_addr: FxHashMap::default(), - next_base_addr: GENMC_GLOBAL_ADDRESSES_MASK, + address_generator: AddressGenerator::new(GENMC_GLOBAL_ADDRESSES_MASK..last_addr), + // FIXME(genmc): We could provide a way to changes this seed, to allow for different global addresses. rng: StdRng::seed_from_u64(0), } } @@ -72,46 +73,19 @@ impl GlobalStateInner { Entry::Vacant(vacant_entry) => vacant_entry, }; - // This is either called immediately after allocation (and then cached), or when - // adjusting `tcx` pointers (which never get freed). So assert that we are looking - // at a live allocation. This also ensures that we never re-assign an address to an - // allocation that previously had an address, but then was freed and the address - // information was removed. - assert!(!matches!(info.kind, AllocKind::Dead)); - // This allocation does not have a base address yet, pick or reuse one. + // We are not in native lib mode (incompatible with GenMC mode), so we control the addresses ourselves. + let new_addr = self.address_generator.generate(info.size, info.align, &mut self.rng)?; - // We are not in native lib mode, so we control the addresses ourselves. - - // We have to pick a fresh address. - // Leave some space to the previous allocation, to give it some chance to be less aligned. - // We ensure that `(global_state.next_base_addr + slack) % 16` is uniformly distributed. - let slack = self.rng.random_range(0..16); - // From next_base_addr + slack, round up to adjust for alignment. - let base_addr = - self.next_base_addr.checked_add(slack).ok_or_else(|| err_exhaust!(AddressSpaceFull))?; - let base_addr = align_addr(base_addr, info.align.bytes()); - - // Remember next base address. If this allocation is zero-sized, leave a gap of at - // least 1 to avoid two allocations having the same base address. (The logic in - // `alloc_id_from_addr` assumes unique addresses, and different function/vtable pointers - // need to be distinguishable!) - self.next_base_addr = base_addr - .checked_add(max(info.size.bytes(), 1)) - .ok_or_else(|| err_exhaust!(AddressSpaceFull))?; - - assert_ne!(0, base_addr & GENMC_GLOBAL_ADDRESSES_MASK); - assert_ne!(0, self.next_base_addr & GENMC_GLOBAL_ADDRESSES_MASK); // Cache the address for future use. - entry.insert(base_addr); + entry.insert(new_addr); - interp_ok(base_addr) + interp_ok(new_addr) } } -// FIXME(GenMC): "ExtPriv" or "PrivExt"? -impl<'tcx> EvalContextExtPriv<'tcx> for crate::MiriInterpCx<'tcx> {} -pub(super) trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn get_global_allocation_address( &self, global_allocation_handler: &GlobalAllocationHandler, @@ -120,7 +94,7 @@ pub(super) trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { let this = self.eval_context_ref(); let info = this.get_alloc_info(alloc_id); - let global_state = global_allocation_handler.inner.read().unwrap(); + let global_state = global_allocation_handler.0.read().unwrap(); if let Some(base_addr) = global_state.base_addr.get(&alloc_id) { info!( "GenMC: address for global with alloc id {alloc_id:?} was cached: {base_addr} == {base_addr:#x}" @@ -128,18 +102,19 @@ pub(super) trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { return interp_ok(*base_addr); } + // We need to upgrade to a write lock. `std::sync::RwLock` doesn't support this, so we drop the guard and lock again + // Note that another thread might allocate the address while the `RwLock` is unlocked, but we handle this case in the allocation function. drop(global_state); - // We need to upgrade to a write lock. std::sync::RwLock doesn't support this, so we drop the guard and lock again - // Note that another thread might run in between and allocate the address, but we handle this case in the allocation function. - let mut global_state = global_allocation_handler.inner.write().unwrap(); - let base_addr = global_state.global_allocate_addr(alloc_id, info)?; - // Even if `Size` didn't overflow, we might still have filled up the address space. - if global_state.next_base_addr > this.target_usize_max() { - throw_exhaust!(AddressSpaceFull); - } - info!( - "GenMC: global with alloc id {alloc_id:?} got address: {base_addr} == {base_addr:#x}" + let mut global_state = global_allocation_handler.0.write().unwrap(); + // With the write lock, we can safely allocate an address only once per `alloc_id`. + let new_addr = global_state.global_allocate_addr(alloc_id, info)?; + info!("GenMC: global with alloc id {alloc_id:?} got address: {new_addr} == {new_addr:#x}"); + assert_eq!( + GENMC_GLOBAL_ADDRESSES_MASK, + new_addr & GENMC_GLOBAL_ADDRESSES_MASK, + "Global address allocated outside global address space." ); - interp_ok(base_addr) + + interp_ok(new_addr) } } diff --git a/src/concurrency/genmc/miri_genmc.rs b/src/concurrency/genmc/miri_genmc.rs index 5afca626d2..3928e7dc2f 100644 --- a/src/concurrency/genmc/miri_genmc.rs +++ b/src/concurrency/genmc/miri_genmc.rs @@ -23,10 +23,11 @@ pub fn run_genmc_mode( config: &MiriConfig, genmc_config: &GenmcConfig, eval_entry: impl Fn(Rc) -> Option, + target_usize_max: u64, mode: Mode, ) -> Option { let time_start = Instant::now(); - let genmc_ctx = Rc::new(GenmcCtx::new(config, mode)); + let genmc_ctx = Rc::new(GenmcCtx::new(config, target_usize_max, mode)); for rep in 0u64.. { tracing::info!("Miri-GenMC loop {}", rep + 1); diff --git a/src/concurrency/genmc/mod.rs b/src/concurrency/genmc/mod.rs index 441d23272f..54899b710d 100644 --- a/src/concurrency/genmc/mod.rs +++ b/src/concurrency/genmc/mod.rs @@ -12,7 +12,7 @@ use rustc_const_eval::interpret::{AllocId, InterpCx, InterpResult, interp_ok}; use rustc_middle::{mir, throw_machine_stop, throw_ub_format, throw_unsup_format}; use tracing::info; -use self::global_allocations::{EvalContextExtPriv as _, GlobalAllocationHandler}; +use self::global_allocations::{EvalContextExt as _, GlobalAllocationHandler}; use self::helper::{ genmc_scalar_to_scalar, option_scalar_to_genmc_scalar, rhs_scalar_to_genmc_scalar, scalar_to_genmc_scalar, @@ -73,19 +73,19 @@ pub struct GenmcCtx { /// GenMC Context creation and administrative / query actions impl GenmcCtx { /// Create a new `GenmcCtx` from a given config. - pub fn new(miri_config: &MiriConfig, mode: miri_genmc::Mode) -> Self { + pub fn new(miri_config: &MiriConfig, target_usize_max: u64, mode: miri_genmc::Mode) -> Self { let genmc_config = miri_config.genmc_config.as_ref().unwrap(); info!("GenMC: Creating new GenMC Context"); let handle = createGenmcHandle(&genmc_config.params, mode == miri_genmc::Mode::Estimation); let non_null_handle = NonNullUniquePtr::new(handle).expect("GenMC should not return null"); let non_null_handle = RefCell::new(non_null_handle); - + let global_allocations = Arc::new(GlobalAllocationHandler::new(target_usize_max)); Self { handle: non_null_handle, thread_id_manager: Default::default(), allow_data_races: Cell::new(false), - global_allocations: Default::default(), + global_allocations, warnings_cache: Default::default(), exit_status: Cell::new(None), } diff --git a/src/machine.rs b/src/machine.rs index 6c0d498781..6711d057b2 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -676,11 +676,16 @@ impl<'tcx> MiriMachine<'tcx> { thread_cpu_affinity .insert(threads.active_thread(), CpuAffinityMask::new(&layout_cx, config.num_cpus)); } + let alloc_addresses = RefCell::new(alloc_addresses::GlobalStateInner::new( + config, + stack_addr, + /* last_addr */ tcx.target_usize_max(), + )); MiriMachine { tcx, borrow_tracker, data_race, - alloc_addresses: RefCell::new(alloc_addresses::GlobalStateInner::new(config, stack_addr)), + alloc_addresses, // `env_vars` depends on a full interpreter so we cannot properly initialize it yet. env_vars: EnvVars::default(), main_fn_ret_place: None, From 78208d819b48afd71777a5bfe536a3ba3a3975b8 Mon Sep 17 00:00:00 2001 From: Patrick-6 Date: Fri, 1 Aug 2025 15:02:09 +0200 Subject: [PATCH 18/41] Remove redundant test --- tests/genmc/pass/thread/thread_simple.rs | 34 -------------------- tests/genmc/pass/thread/thread_simple.stderr | 2 -- 2 files changed, 36 deletions(-) delete mode 100644 tests/genmc/pass/thread/thread_simple.rs delete mode 100644 tests/genmc/pass/thread/thread_simple.stderr diff --git a/tests/genmc/pass/thread/thread_simple.rs b/tests/genmc/pass/thread/thread_simple.rs deleted file mode 100644 index abb3adbb03..0000000000 --- a/tests/genmc/pass/thread/thread_simple.rs +++ /dev/null @@ -1,34 +0,0 @@ -//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows - -#![no_main] - -use std::ffi::c_void; -use std::sync::atomic::AtomicU64; -use std::sync::atomic::Ordering::SeqCst; - -use libc::{self, pthread_attr_t, pthread_t}; - -static FLAG: AtomicU64 = AtomicU64::new(0); - -#[unsafe(no_mangle)] -fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - let mut thread_id: pthread_t = 0; - - let attr: *const pthread_attr_t = std::ptr::null(); - let value: *mut c_void = std::ptr::null_mut(); - - assert!(0 == unsafe { libc::pthread_create(&raw mut thread_id, attr, thread_func, value) }); - - FLAG.store(1, SeqCst); - - assert!(0 == unsafe { libc::pthread_join(thread_id, std::ptr::null_mut()) }); - - let flag = FLAG.load(SeqCst); - assert!(flag == 1 || flag == 2); - return 0; -} - -extern "C" fn thread_func(_value: *mut c_void) -> *mut c_void { - FLAG.store(2, SeqCst); - std::ptr::null_mut() -} diff --git a/tests/genmc/pass/thread/thread_simple.stderr b/tests/genmc/pass/thread/thread_simple.stderr deleted file mode 100644 index 01701dfe69..0000000000 --- a/tests/genmc/pass/thread/thread_simple.stderr +++ /dev/null @@ -1,2 +0,0 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 2 From 001d04dc88db551f08034f23200873c6fc3e352b Mon Sep 17 00:00:00 2001 From: Patrick-6 Date: Fri, 1 Aug 2025 15:17:31 +0200 Subject: [PATCH 19/41] Apply suggestions from code review to C++ code. --- genmc-sys/src_cpp/MiriInterface.cpp | 18 +++++++++--------- genmc-sys/src_cpp/MiriInterface.hpp | 22 +++++++++++++--------- 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/genmc-sys/src_cpp/MiriInterface.cpp b/genmc-sys/src_cpp/MiriInterface.cpp index 57eb50b06c..c8d74111fe 100644 --- a/genmc-sys/src_cpp/MiriInterface.cpp +++ b/genmc-sys/src_cpp/MiriInterface.cpp @@ -35,9 +35,9 @@ auto MiriGenMCShim::scheduleNext(const int curr_thread_id, { // The current thread is the only one where the `kind` could have changed since we last made // a scheduling decision. - globalInstructions[curr_thread_id].kind = curr_thread_next_instr_kind; + threadsAction[curr_thread_id].kind = curr_thread_next_instr_kind; - auto result = GenMCDriver::scheduleNext(globalInstructions); + auto result = GenMCDriver::scheduleNext(threadsAction); if (result.has_value()) { return static_cast(result.value()); } @@ -153,14 +153,14 @@ auto createGenmcHandle(const GenmcParams &config, bool estimation_mode) void MiriGenMCShim::handleExecutionStart() { - globalInstructions.clear(); - globalInstructions.push_back(Action(ActionKind::Load, Event::getInit())); + threadsAction.clear(); + threadsAction.push_back(Action(ActionKind::Load, Event::getInit())); GenMCDriver::handleExecutionStart(); } auto MiriGenMCShim::handleExecutionEnd() -> std::unique_ptr { - return GenMCDriver::handleExecutionEnd(globalInstructions); + return GenMCDriver::handleExecutionEnd(threadsAction); } /**** Thread management ****/ @@ -180,8 +180,8 @@ void MiriGenMCShim::handleThreadCreate(ThreadId thread_id, ThreadId parent_id) auto createLab = GenMCDriver::handleThreadCreate(std::move(tcLab)); auto genmcTid = createLab->getChildId(); - BUG_ON(genmcTid != thread_id || genmcTid == -1 || genmcTid != globalInstructions.size()); - globalInstructions.push_back(Action(ActionKind::Load, Event(genmcTid, 0))); + BUG_ON(genmcTid != thread_id || genmcTid == -1 || genmcTid != threadsAction.size()); + threadsAction.push_back(Action(ActionKind::Load, Event(genmcTid, 0))); } void MiriGenMCShim::handleThreadJoin(ThreadId thread_id, ThreadId child_id) @@ -441,7 +441,7 @@ auto MiriGenMCShim::handleMutexLock(ThreadId thread_id, uint64_t address, uint64 ConcreteExpr::create(aSize.getBits(), SVal(1))) .release()))); - auto &currPos = globalInstructions[thread_id].event; + auto &currPos = threadsAction[thread_id].event; // auto rLab = LockCasReadLabel::create(++currPos, address, size); auto rLab = LockCasReadLabel::create(++currPos, address, size, annot); @@ -481,7 +481,7 @@ auto MiriGenMCShim::handleMutexLock(ThreadId thread_id, uint64_t address, uint64 auto MiriGenMCShim::handleMutexTryLock(ThreadId thread_id, uint64_t address, uint64_t size) -> MutexLockResult { - auto &currPos = globalInstructions[thread_id].event; + auto &currPos = threadsAction[thread_id].event; auto rLab = TrylockCasReadLabel::create(++currPos, address, size); // Mutex starts out unlocked, so we always say the previous value is "unlocked". auto oldValSetter = [this](SAddr loc) { this->handleOldVal(loc, SVal(0)); }; diff --git a/genmc-sys/src_cpp/MiriInterface.hpp b/genmc-sys/src_cpp/MiriInterface.hpp index 1927cc74d1..64a94054bf 100644 --- a/genmc-sys/src_cpp/MiriInterface.hpp +++ b/genmc-sys/src_cpp/MiriInterface.hpp @@ -53,10 +53,7 @@ struct MiriGenMCShim : private GenMCDriver { public: MiriGenMCShim(std::shared_ptr conf, Mode mode /* = VerificationMode{} */) : GenMCDriver(std::move(conf), nullptr, mode) - { - globalInstructions.reserve(8); - globalInstructions.push_back(Action(ActionKind::Load, Event::getInit())); - } + {} virtual ~MiriGenMCShim() {} @@ -134,13 +131,13 @@ struct MiriGenMCShim : private GenMCDriver { auto incPos(ThreadId tid) -> Event { - ERROR_ON(tid >= globalInstructions.size(), "ThreadId out of bounds"); - return ++globalInstructions[tid].event; + ERROR_ON(tid >= threadsAction.size(), "ThreadId out of bounds"); + return ++threadsAction[tid].event; } auto decPos(ThreadId tid) -> Event { - ERROR_ON(tid >= globalInstructions.size(), "ThreadId out of bounds"); - return --globalInstructions[tid].event; + ERROR_ON(tid >= threadsAction.size(), "ThreadId out of bounds"); + return --threadsAction[tid].event; } void printGraph() { GenMCDriver::debugPrintGraph(); } @@ -235,7 +232,14 @@ struct MiriGenMCShim : private GenMCDriver { // TODO GENMC(mixed-size accesses): std::unordered_map initVals_{}; - std::vector globalInstructions; + /** + * Currently, the interpreter is responsible for maintaining `ExecutionGraph` event indices. + * The interpreter is also responsible for informing GenMC about the `ActionKind` of the next instruction in each thread. + * + * This vector contains this data in the expected format `Action`, which consists of the `ActionKind` of the next instruction + * and the last event index added to the ExecutionGraph in a given thread. + */ + std::vector threadsAction; std::unordered_map annotation_id{}; ModuleID::ID annotation_id_counter = 0; From 5d4a0271b5436b2bc79ed732b7590ea4f671d908 Mon Sep 17 00:00:00 2001 From: Patrick-6 Date: Fri, 1 Aug 2025 15:18:02 +0200 Subject: [PATCH 20/41] Apply suggestions from code review to memory allocation code. --- src/alloc_addresses/mod.rs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/alloc_addresses/mod.rs b/src/alloc_addresses/mod.rs index 402fd7cebb..2ac1041d71 100644 --- a/src/alloc_addresses/mod.rs +++ b/src/alloc_addresses/mod.rs @@ -33,7 +33,8 @@ pub struct GlobalStateInner { /// sorted by address. We cannot use a `HashMap` since we can be given an address that is offset /// from the base address, and we need to find the `AllocId` it belongs to. This is not the /// *full* inverse of `base_addr`; dead allocations have been removed. - /// TODO GENMC: keep dead allocations in GenMC mode? + /// Note that in GenMC mode, dead allocations are *not* removed -- and also, addresses are never + /// reused. This lets us use the address as a cross-execution-stable identifier for an allocation. int_to_ptr_map: Vec<(u64, AllocId)>, /// The base address for each allocation. We cannot put that into /// `AllocExtra` because function pointers also have a base address, and @@ -244,9 +245,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if !only_exposed_allocations || global_state.exposed.contains(&alloc_id) { // This must still be live, since we remove allocations from `int_to_ptr_map` when they get freed. // In GenMC mode, we keep all allocations, so this check doesn't apply there. - debug_assert!( - this.machine.data_race.as_genmc_ref().is_some() || this.is_alloc_live(alloc_id) - ); + if this.machine.data_race.as_genmc_ref().is_none() { + debug_assert!(this.is_alloc_live(alloc_id)); + } Some(alloc_id) } else { None @@ -482,14 +483,14 @@ impl<'tcx> MiriMachine<'tcx> { let pos = global_state.int_to_ptr_map.binary_search_by_key(&addr, |(addr, _)| *addr).unwrap(); - // TODO GENMC(DOCUMENTATION): + // In GenMC mode, we don't remove the allocation, so we can provide better errors for pointers sent to GenMC and back. if self.data_race.as_genmc_ref().is_none() { let removed = global_state.int_to_ptr_map.remove(pos); assert_eq!(removed, (addr, dead_id)); // double-check that we removed the right thing - // We can also remove it from `exposed`, since this allocation can anyway not be returned by - // `alloc_id_from_addr` any more. - global_state.exposed.remove(&dead_id); } + // We can also remove it from `exposed`, since this allocation can anyway not be returned by + // `alloc_id_from_addr` any more. + global_state.exposed.remove(&dead_id); // Also remember this address for future reuse. let thread = self.threads.active_thread(); global_state.reuse.add_addr(rng, addr, size, align, kind, thread, || { From 5c486c404522e6bb94e28d2924ea0f453400d28b Mon Sep 17 00:00:00 2001 From: Patrick-6 Date: Fri, 1 Aug 2025 15:22:04 +0200 Subject: [PATCH 21/41] Clean up code, tests, docs. Remove old debugging code. Add fixes for correctly rebuilding on changes. --- doc/genmc.md | 18 ++++ genmc-sys/build.rs | 5 +- genmc-sys/src/lib.rs | 59 ++++++++++++- genmc-sys/src_cpp/LogLevel.hpp | 50 +++++++++++ genmc-sys/src_cpp/MiriInterface.cpp | 82 ++++++++----------- genmc-sys/src_cpp/MiriInterface.hpp | 51 ++++++------ src/bin/miri.rs | 5 +- src/borrow_tracker/stacked_borrows/mod.rs | 1 + src/concurrency/genmc/config.rs | 10 +-- src/concurrency/genmc/dummy.rs | 2 +- src/concurrency/genmc/mod.rs | 24 ++---- src/machine.rs | 2 +- .../_disabled/pass/atomic-primitives/mpsc.rs | 41 ++++++++++ .../pass/libc_exit.rs} | 2 + .../atomic-primitives/arc.check_count.stderr | 8 ++ tests/genmc/pass/atomic-primitives/arc.rs | 55 +++++++++++++ .../atomic-primitives/arc.try_upgrade.stderr | 8 ++ .../mutex_simple.reps1.stderr | 0 .../mutex_simple.reps2.stderr | 0 .../mutex_simple.rs | 5 +- .../pass/data-structures/ms_queue_dynamic.rs | 37 ++++----- .../data-structures/treiber_stack_dynamic.rs | 77 ++++------------- .../pass/thread/miri_main_spawn_pthreads.rs | 4 +- tests/utils/genmc.rs | 4 +- 24 files changed, 350 insertions(+), 200 deletions(-) create mode 100644 genmc-sys/src_cpp/LogLevel.hpp create mode 100644 tests/genmc/_disabled/pass/atomic-primitives/mpsc.rs rename tests/genmc/{pass/thread/libc_exit.rs.disabled => _disabled/pass/libc_exit.rs} (96%) create mode 100644 tests/genmc/pass/atomic-primitives/arc.check_count.stderr create mode 100644 tests/genmc/pass/atomic-primitives/arc.rs create mode 100644 tests/genmc/pass/atomic-primitives/arc.try_upgrade.stderr rename tests/genmc/pass/{std => atomic-primitives}/mutex_simple.reps1.stderr (100%) rename tests/genmc/pass/{std => atomic-primitives}/mutex_simple.reps2.stderr (100%) rename tests/genmc/pass/{std => atomic-primitives}/mutex_simple.rs (84%) diff --git a/doc/genmc.md b/doc/genmc.md index 5aabe90b5d..f7a7463cd2 100644 --- a/doc/genmc.md +++ b/doc/genmc.md @@ -21,6 +21,24 @@ Basic usage: MIRIFLAGS="-Zmiri-genmc" cargo miri run ``` +### Supported Parameters +- `-Zmiri-genmc`: Enable GenMC mode (not required if any other GenMC options are used). +- `-Zmiri-genmc-log=LOG_LEVEL`: Change the log level for GenMC. Default: `warning`. + +#### Available log levels +Every log level includes all previous log levels. +- `quiet`: Disable logging. +- `error`: Print errors. +- `warning`: Print errors and warnings. +- `tip`: Print errors, warnings and tips. +#### Extra log levels for debugging +These are downgraded to `tip` if GenMC is compiled without debug assertions (e.g., with Miri in release mode). +These options are *very* verbose. +- `debug1`: Print revisits considered by GenMC. +- `debug2`: Print the execution graph after every memory access. +- `debug3`: Print reads-from values considered by GenMC. + + diff --git a/genmc-sys/build.rs b/genmc-sys/build.rs index fa0729e1a5..0b5e9645bc 100644 --- a/genmc-sys/build.rs +++ b/genmc-sys/build.rs @@ -240,7 +240,7 @@ fn main() { } // Select which path to use for the GenMC repo: - let genmc_path = if let Ok(genmc_src_path) = std::env::var("GENMC_SRC_PATH") { + let genmc_path = if let Some(genmc_src_path) = option_env!("GENMC_SRC_PATH") { let genmc_src_path = PathBuf::from_str(&genmc_src_path).expect("GENMC_SRC_PATH should contain a valid path"); assert!( @@ -248,6 +248,9 @@ fn main() { "GENMC_SRC_PATH={} does not exist!", genmc_src_path.display() ); + // Rebuild files in the given path change. + // FIXME(genmc): using the full directory instead of `./src` causes spurious rebuilds for some reason. + println!("cargo::rerun-if-changed={}", genmc_src_path.join("src").display()); genmc_src_path } else { downloading::download_genmc() diff --git a/genmc-sys/src/lib.rs b/genmc-sys/src/lib.rs index aadd81091f..abcd2a66b5 100644 --- a/genmc-sys/src/lib.rs +++ b/genmc-sys/src/lib.rs @@ -1,3 +1,5 @@ +use std::str::FromStr; + pub use self::ffi::*; pub mod cxx_extra; @@ -33,14 +35,38 @@ impl Default for GenmcParams { fn default() -> Self { Self { print_random_schedule_seed: false, - quiet: true, - log_level_trace: false, + log_level: Default::default(), do_symmetry_reduction: false, // TODO GENMC (PERFORMANCE): maybe make this default `true` estimation_max: 1000, } } } +impl Default for LogLevel { + fn default() -> Self { + // FIXME(genmc): set `Warning` by default once changes to GenMC are upstreamed. + // FIXME(genmc): set `Tip` by default once the GenMC tips are relevant to Miri. + Self::Error + } +} + +impl FromStr for LogLevel { + type Err = &'static str; + + fn from_str(s: &str) -> Result { + Ok(match s { + "quiet" => LogLevel::Quiet, + "error" => LogLevel::Error, + "warning" => LogLevel::Warning, + "tip" => LogLevel::Tip, + "debug1" => LogLevel::Debug1Revisits, + "debug2" => LogLevel::Debug2MemoryAccesses, + "debug3" => LogLevel::Debug3ReadsFrom, + _ => return Err("invalid log level"), + }) + } +} + #[cxx::bridge] mod ffi { /// Parameters that will be given to GenMC for setting up the model checker. @@ -49,12 +75,37 @@ mod ffi { struct GenmcParams { // pub genmc_seed: u64; // OR: Option pub print_random_schedule_seed: bool, - pub quiet: bool, // TODO GENMC: maybe make log-level more fine grained - pub log_level_trace: bool, + pub log_level: LogLevel, pub do_symmetry_reduction: bool, pub estimation_max: u32, } + /// This is mostly equivalent to GenMC `VerbosityLevel`, but the debug log levels are always present (not conditionally compiled based on `ENABLE_GENMC_DEBUG`). + /// We add this intermediate type to prevent changes to the GenMC log-level from breaking the Miri + /// build, and to have a stable type for the C++-Rust interface, independent of `ENABLE_GENMC_DEBUG`. + #[derive(Debug)] + enum LogLevel { + /// Disable *all* logging (including error messages on a crash). + Quiet, + /// Log errors. + Error, + /// Log errors and warnings. + Warning, + /// Log errors, warnings and tips. + Tip, + /// Debug print considered revisits. + /// Downgraded to `Tip` if `GENMC_DEBUG` is not enabled. + Debug1Revisits, + /// Print the execution graph after every memory access. + /// Also includes the previous debug log level. + /// Downgraded to `Tip` if `GENMC_DEBUG` is not enabled. + Debug2MemoryAccesses, + /// Print reads-from values considered by GenMC. + /// Also includes the previous debug log level. + /// Downgraded to `Tip` if `GENMC_DEBUG` is not enabled. + Debug3ReadsFrom, + } + #[derive(Debug)] enum ActionKind { /// Any Mir terminator that's atomic and has load semantics. diff --git a/genmc-sys/src_cpp/LogLevel.hpp b/genmc-sys/src_cpp/LogLevel.hpp new file mode 100644 index 0000000000..89b1b989a4 --- /dev/null +++ b/genmc-sys/src_cpp/LogLevel.hpp @@ -0,0 +1,50 @@ +#ifndef GENMC_MIRI_LOG_LEVEL_HPP +#define GENMC_MIRI_LOG_LEVEL_HPP + +// CXX.rs generated headers: +#include "genmc-sys/src/lib.rs.h" + +// GenMC headers: +#include "Config/Verbosity.hpp" +#include "Support/Error.hpp" + +// C++ headers: +#include + +/** + * Translate the Miri-GenMC `LogLevel` to the GenMC `VerbosityLevel`. + * Downgrade any debug options to `Tip` if `ENABLE_GENMC_DEBUG` is not enabled. + */ +auto to_genmc_verbosity_level(const LogLevel log_level) -> VerbosityLevel +{ + switch (log_level) { + case LogLevel::Quiet: + return VerbosityLevel::Quiet; + case LogLevel::Error: + return VerbosityLevel::Error; + case LogLevel::Warning: + return VerbosityLevel::Warning; + case LogLevel::Tip: + return VerbosityLevel::Tip; +#ifdef ENABLE_GENMC_DEBUG + case LogLevel::Debug1Revisits: + return VerbosityLevel::Debug1; + case LogLevel::Debug2MemoryAccesses: + return VerbosityLevel::Debug2; + case LogLevel::Debug3ReadsFrom: + return VerbosityLevel::Debug3; +#else + // Downgrade to `Tip` if the debug levels are not available. + case LogLevel::Debug1Revisits: + case LogLevel::Debug2MemoryAccesses: + case LogLevel::Debug3ReadsFrom: + return VerbosityLevel::Tip; +#endif + default: + WARN_ONCE("unknown-log-level", + "Unknown `LogLevel`, defaulting to `VerbosityLevel::Tip`."); + return VerbosityLevel::Tip; + } +} + +#endif /* GENMC_MIRI_LOG_LEVEL_HPP */ diff --git a/genmc-sys/src_cpp/MiriInterface.cpp b/genmc-sys/src_cpp/MiriInterface.cpp index c8d74111fe..c9e10916e5 100644 --- a/genmc-sys/src_cpp/MiriInterface.cpp +++ b/genmc-sys/src_cpp/MiriInterface.cpp @@ -1,7 +1,11 @@ +// `genmc-sys/src_cpp` headers: #include "MiriInterface.hpp" +#include "LogLevel.hpp" +// CXX.rs generated headers: #include "genmc-sys/src/lib.rs.h" +// GenMC headers: #include "ADT/value_ptr.hpp" #include "Config/MemoryModel.hpp" #include "Config/Verbosity.hpp" @@ -19,6 +23,7 @@ #include "Support/ThreadInfo.hpp" #include "Verification/GenMCDriver.hpp" +// C++ headers: #include #include #include @@ -29,7 +34,8 @@ using AnnotT = SExpr; // Return -1 when no thread can/should be scheduled, or the thread id of the next thread // NOTE: this is safe because ThreadId is 32 bit, and we return a 64 bit integer -// FIXME(genmc,cxx): could directly return std::optional if CXX ever supports sharing it (see https://github.com/dtolnay/cxx/issues/87). +// FIXME(genmc,cxx): could directly return std::optional if CXX ever supports sharing it (see +// https://github.com/dtolnay/cxx/issues/87). auto MiriGenMCShim::scheduleNext(const int curr_thread_id, const ActionKind curr_thread_next_instr_kind) -> int64_t { @@ -37,10 +43,8 @@ auto MiriGenMCShim::scheduleNext(const int curr_thread_id, // a scheduling decision. threadsAction[curr_thread_id].kind = curr_thread_next_instr_kind; - auto result = GenMCDriver::scheduleNext(threadsAction); - if (result.has_value()) { + if (const auto result = GenMCDriver::scheduleNext(threadsAction)) return static_cast(result.value()); - } return -1; } @@ -54,32 +58,34 @@ auto MiriGenMCShim::createHandle(const GenmcParams &config, bool estimation_mode // TODO GENMC: Can we get some default values somehow? // Config::saveConfigOptions(*conf); - // NOTE: Miri already initialization checks, so we can disable them in GenMC - conf->skipNonAtomicInitializedCheck = true; + // NOTE: Miri already does validity checks, so we can disable them in GenMC. + conf->skipAccessValidityChecks = true; + // Miri handles non-atomic accesses, so we skip the check for those in GenMC. + // Mixed atomic-non-atomic mixed-size checks are still enabled. + conf->allowNonAtomicMixedSizeAccesses = true; // Miri needs all threads to be replayed, even fully completed ones. conf->replayCompletedThreads = true; - // FIXME(genmc): make sure this doesn't affect any tests, and maybe make it changeable from Miri: - constexpr unsigned int DEFAULT_WARN_ON_GRAPH_SIZE = 16 * 1024; - conf->warnOnGraphSize = DEFAULT_WARN_ON_GRAPH_SIZE; + // `1024` is the default value that GenMC uses. + // If any thread has at least this many events, a warning/tip will be printed. + // + // Miri produces a lot more events than GenMC, so the graph size warning triggers on almost + // all programs. The current value is large enough so the warning is not be triggered by any + // reasonable programs. + // FIXME(genmc): The emitted warning mentions features not supported by Miri ('--unroll' + // parameter). + // FIXME(genmc): A more appropriate limit should be chosen once the warning is useful for + // Miri. + conf->warnOnGraphSize = 1024 * 1024; + + // The `logLevel` is not part of the config struct, but the static variable `logLevel`. + logLevel = to_genmc_verbosity_level(config.log_level); // We only support the RC11 memory model for Rust. conf->model = ModelType::RC11; - // FIXME(genmc): expose this setting to Miri - conf->randomScheduleSeed = "42"; conf->printRandomScheduleSeed = config.print_random_schedule_seed; - if (config.quiet) { - // logLevel = VerbosityLevel::Quiet; - // TODO GENMC: error might be better (or new level for `BUG`) - // logLevel = VerbosityLevel::Quiet; - logLevel = VerbosityLevel::Error; - } else if (config.log_level_trace) { - logLevel = VerbosityLevel::Trace; - } else { - logLevel = VerbosityLevel::Tip; - } // FIXME(genmc): check if we can enable IPR: conf->ipr = false; @@ -121,18 +127,18 @@ auto MiriGenMCShim::createHandle(const GenmcParams &config, bool estimation_mode auto initValGetter = [driverPtr](const AAccess &access) { const auto addr = access.getAddr(); if (!driverPtr->initVals_.contains(addr)) { - MIRI_LOG() << "WARNING: TODO GENMC: requested initial value for address " + LOG(VerbosityLevel::Warning) << "WARNING: TODO GENMC: requested initial value for address " << addr << ", but there is none.\n"; return SVal(0xCC00CC00); // BUG_ON(!driverPtr->initVals_.contains(addr)); } auto result = driverPtr->initVals_[addr]; if (!result.is_init) { - MIRI_LOG() << "WARNING: TODO GENMC: requested initial value for address " + LOG(VerbosityLevel::Warning) << "WARNING: TODO GENMC: requested initial value for address " << addr << ", but the memory is uninitialized.\n"; return SVal(0xFF00FF00); } - MIRI_LOG() << "MiriGenMCShim: requested initial value for address " << addr + LOG(VerbosityLevel::Warning) << "MiriGenMCShim: requested initial value for address " << addr << " == " << addr.get() << ", returning: " << result << "\n"; return result.toSVal(); }; @@ -201,8 +207,6 @@ void MiriGenMCShim::handleThreadJoin(ThreadId thread_id, ThreadId child_id) void MiriGenMCShim::handleThreadFinish(ThreadId thread_id, uint64_t ret_val) { - MIRI_LOG() << "GenMC: handleThreadFinish: thread id: " << thread_id << "\n"; - auto pos = incPos(thread_id); auto retVal = SVal(ret_val); @@ -212,7 +216,8 @@ void MiriGenMCShim::handleThreadFinish(ThreadId thread_id, uint64_t ret_val) GenMCDriver::handleThreadFinish(std::move(eLab)); } -void MiriGenMCShim::handleThreadKill(ThreadId thread_id) { +void MiriGenMCShim::handleThreadKill(ThreadId thread_id) +{ auto pos = incPos(thread_id); auto kLab = std::make_unique(pos); @@ -234,8 +239,6 @@ void MiriGenMCShim::handleUserBlock(ThreadId thread_id) MemOrdering ord, GenmcScalar old_val) -> LoadResult { auto pos = incPos(thread_id); - MIRI_LOG() << "Received Load from Miri at address: " << address << ", size " << size - << " with ordering " << ord << ", event: " << pos << "\n"; auto loc = SAddr(address); auto aSize = ASize(size); @@ -255,10 +258,6 @@ void MiriGenMCShim::handleUserBlock(ThreadId thread_id) GenmcScalar rhs_value, GenmcScalar old_val) -> ReadModifyWriteResult { - MIRI_LOG() << "Received Read-Modify-Write from Miri at address: " << address << ", size " - << size << " with orderings (" << loadOrd << ", " << store_ordering - << "), rmw op: " << static_cast(rmw_op) << "\n"; - auto pos = incPos(thread_id); auto loc = SAddr(address); @@ -293,14 +292,6 @@ void MiriGenMCShim::handleUserBlock(ThreadId thread_id) MemOrdering success_store_ordering, MemOrdering fail_load_ordering, bool can_fail_spuriously) -> CompareExchangeResult { - - MIRI_LOG() << "Received Compare-Exchange from Miri (value: " << expected_value << " --> " - << new_value << ", old value: " << old_val << ") at address: " << address - << ", size " << size << " with success orderings (" << success_load_ordering - << ", " << success_store_ordering - << "), fail load ordering: " << fail_load_ordering - << ", is weak (can fail spuriously): " << can_fail_spuriously << "\n"; - auto pos = incPos(thread_id); auto loc = SAddr(address); @@ -339,10 +330,6 @@ void MiriGenMCShim::handleUserBlock(ThreadId thread_id) MemOrdering ord, StoreEventType store_event_type) -> StoreResult { - MIRI_LOG() << "Received Store from Miri at address " << address << ", size " << size - << " with ordering " << ord << ", is part of rmw: (" - << static_cast(store_event_type) << ")\n"; - auto pos = incPos(thread_id); auto loc = SAddr(address); @@ -380,8 +367,6 @@ void MiriGenMCShim::handleUserBlock(ThreadId thread_id) void MiriGenMCShim::handleFence(ThreadId thread_id, MemOrdering ord) { - MIRI_LOG() << "Received fence operation from Miri with ordering " << ord << "\n"; - auto pos = incPos(thread_id); auto fLab = std::make_unique(pos, ord); @@ -399,7 +384,8 @@ auto MiriGenMCShim::handleMalloc(ThreadId thread_id, uint64_t size, uint64_t ali auto stype = StorageType::ST_Volatile; auto spc = AddressSpace::AS_User; - auto aLab = std::make_unique(pos, size, alignment, sd, stype, spc, EventDeps()); + auto aLab = + std::make_unique(pos, size, alignment, sd, stype, spc, EventDeps()); SAddr retVal = GenMCDriver::handleMalloc(std::move(aLab)); diff --git a/genmc-sys/src_cpp/MiriInterface.hpp b/genmc-sys/src_cpp/MiriInterface.hpp index 64a94054bf..183e51ad52 100644 --- a/genmc-sys/src_cpp/MiriInterface.hpp +++ b/genmc-sys/src_cpp/MiriInterface.hpp @@ -1,10 +1,13 @@ #ifndef GENMC_MIRI_INTERFACE_HPP #define GENMC_MIRI_INTERFACE_HPP +// CXX.rs generated headers: #include "rust/cxx.h" +// GenMC generated headers: #include "config.h" +// GenMC headers: #include "Config/Config.hpp" #include "ExecutionGraph/EventLabel.hpp" #include "Static/ModuleID.hpp" @@ -13,6 +16,7 @@ #include "Support/ResultHandling.hpp" #include "Verification/GenMCDriver.hpp" +// C++ headers: #include #include #include @@ -182,45 +186,38 @@ struct MiriGenMCShim : private GenMCDriver { * */ void handleOldVal(const SAddr addr, GenmcScalar value) { - MIRI_LOG() << "handleOldVal: " << addr << ", " << value.value << ", " << value.extra - << ", " << value.is_init << "\n"; - // if (!value.is_init) { - // // // TODO GENMC(uninit value handling) - // // MIRI_LOG() << "WARNING: got uninitialized old value, ignoring ...\n"; - // // return; - // MIRI_LOG() << "WARNING: got uninitialized old value, converting to dummy " - // "value ...\n"; - // value.is_init = true; - // value.value = 0xAAFFAAFF; - // } + LOG(VerbosityLevel::Warning) + << "handleOldVal: " << addr << ", " << value.value << ", " << value.extra + << ", " << value.is_init << "\n"; // TODO GENMC(CLEANUP): Pass this as a parameter: auto &g = getExec().getGraph(); auto *coLab = g.co_max(addr); - MIRI_LOG() << "handleOldVal: coLab: " << *coLab << "\n"; if (auto *wLab = llvm::dyn_cast(coLab)) { - MIRI_LOG() + LOG(VerbosityLevel::Warning) << "handleOldVal: got WriteLabel, atomic: " << !wLab->isNotAtomic() << "\n"; if (!value.is_init) - MIRI_LOG() << "WARNING: TODO GENMC: handleOldVal tried to " - "overwrite value of NA " - "reads-from label, but old value is `uninit`\n"; + LOG(VerbosityLevel::Warning) + << "WARNING: TODO GENMC: handleOldVal tried to " + "overwrite value of NA " + "reads-from label, but old value is `uninit`\n"; else if (wLab->isNotAtomic()) wLab->setVal(value.toSVal()); } else if (const auto *wLab = llvm::dyn_cast(coLab)) { if (value.is_init) { auto result = initVals_.insert(std::make_pair(addr, value)); - MIRI_LOG() << "handleOldVal: got InitLabel, insertion result: " - << result.first->second << ", " << result.second << "\n"; + LOG(VerbosityLevel::Warning) + << "handleOldVal: got InitLabel, insertion result: " + << result.first->second << ", " << result.second << "\n"; BUG_ON(result.second && (*result.first).second != value); /* Attempt to replace initial value */ } else { - // LOG(VerbosityLevel::Error) << - MIRI_LOG() << "WARNING: TODO GENMC: handleOldVal tried set initial " - "value, but old " - "value is `uninit`\n"; + LOG(VerbosityLevel::Warning) + << "WARNING: TODO GENMC: handleOldVal tried set initial " + "value, but old " + "value is `uninit`\n"; } } else { BUG(); /* Invalid label */ @@ -234,10 +231,12 @@ struct MiriGenMCShim : private GenMCDriver { /** * Currently, the interpreter is responsible for maintaining `ExecutionGraph` event indices. - * The interpreter is also responsible for informing GenMC about the `ActionKind` of the next instruction in each thread. - * - * This vector contains this data in the expected format `Action`, which consists of the `ActionKind` of the next instruction - * and the last event index added to the ExecutionGraph in a given thread. + * The interpreter is also responsible for informing GenMC about the `ActionKind` of the + * next instruction in each thread. + * + * This vector contains this data in the expected format `Action`, which consists of the + * `ActionKind` of the next instruction and the last event index added to the ExecutionGraph + * in a given thread. */ std::vector threadsAction; diff --git a/src/bin/miri.rs b/src/bin/miri.rs index e8f8c7ac45..b1422f31fb 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -192,6 +192,7 @@ impl rustc_driver::Callbacks for MiriCompilerCalls { miri::eval_entry(tcx, entry_def_id, entry_type, &config, Some(genmc_ctx)) }; + // Estimate the execution space and runtime, if enabled. if genmc_config.do_estimation() && miri_genmc::run_genmc_mode( &config, @@ -200,9 +201,11 @@ impl rustc_driver::Callbacks for MiriCompilerCalls { target_usize_max, miri_genmc::Mode::Estimation, ) - .is_some() + .is_none() { + // We might already find an error during estimation, then we should stop here. tcx.dcx().abort_if_errors(); + exit(rustc_driver::EXIT_FAILURE); } let return_code = miri_genmc::run_genmc_mode( diff --git a/src/borrow_tracker/stacked_borrows/mod.rs b/src/borrow_tracker/stacked_borrows/mod.rs index 2977efaae0..5fe00ab02c 100644 --- a/src/borrow_tracker/stacked_borrows/mod.rs +++ b/src/borrow_tracker/stacked_borrows/mod.rs @@ -740,6 +740,7 @@ trait EvalContextPrivExt<'tcx, 'ecx>: crate::MiriInterpCxExt<'tcx> { if let Some(access) = access { assert_eq!(access, AccessKind::Write); // Make sure the data race model also knows about this. + // FIXME(genmc): Ensure this is still done in GenMC mode. Check for other places where GenMC may need to be informed. if let Some(data_race) = alloc_extra.data_race.as_vclocks_mut() { data_race.write( alloc_id, diff --git a/src/concurrency/genmc/config.rs b/src/concurrency/genmc/config.rs index 2a7c0e9454..dd1dacdcfa 100644 --- a/src/concurrency/genmc/config.rs +++ b/src/concurrency/genmc/config.rs @@ -11,11 +11,6 @@ pub struct GenmcConfig { } impl GenmcConfig { - fn set_log_level_trace(&mut self) { - self.params.quiet = false; - self.params.log_level_trace = true; - } - pub fn print_exec_graphs(&self) -> bool { self.print_exec_graphs } @@ -46,9 +41,8 @@ impl GenmcConfig { let Some(trimmed_arg) = trimmed_arg.strip_prefix("-") else { return Err(format!("Invalid GenMC argument \"-Zmiri-genmc{trimmed_arg}\"")); }; - if trimmed_arg == "log-trace" { - // TODO GENMC: maybe expand to allow more control over log level? - genmc_config.set_log_level_trace(); + if let Some(log_level) = trimmed_arg.strip_prefix("log=") { + genmc_config.params.log_level = log_level.parse()?; } else if trimmed_arg == "print-graphs" { // TODO GENMC (DOCUMENTATION) genmc_config.print_exec_graphs = true; diff --git a/src/concurrency/genmc/dummy.rs b/src/concurrency/genmc/dummy.rs index 74f054c4ce..4fa8b3aa19 100644 --- a/src/concurrency/genmc/dummy.rs +++ b/src/concurrency/genmc/dummy.rs @@ -28,6 +28,7 @@ pub mod miri_genmc { _config: &MiriConfig, _genmc_config: &GenmcConfig, _eval_entry: impl Fn(Rc) -> Option, + _target_usize_max: u64, _mode: Mode, ) -> Option { unreachable!(); @@ -203,7 +204,6 @@ impl GenmcCtx { _alloc_id: AllocId, _address: Size, _size: Size, - _align: Align, _kind: MemoryKind, ) -> InterpResult<'tcx> { unreachable!() diff --git a/src/concurrency/genmc/mod.rs b/src/concurrency/genmc/mod.rs index 54899b710d..0102a4355e 100644 --- a/src/concurrency/genmc/mod.rs +++ b/src/concurrency/genmc/mod.rs @@ -547,12 +547,9 @@ impl GenmcCtx { let mut mc = self.handle.borrow_mut(); let pinned_mc = mc.as_mut(); let chosen_address = pinned_mc.handleMalloc(genmc_tid, genmc_size, alignment); - info!("GenMC: handle_alloc: got address '{chosen_address}' ({chosen_address:#x})"); - // TODO GENMC: - if chosen_address == 0 { - throw_unsup_format!("TODO GENMC: we got address '0' from malloc"); - } + // Non-global addresses should not be in the global address space or null. + assert_ne!(0, chosen_address, "GenMC malloc returned nullptr."); assert_eq!(0, chosen_address & GENMC_GLOBAL_ADDRESSES_MASK); chosen_address }; @@ -573,7 +570,6 @@ impl GenmcCtx { alloc_id: AllocId, address: Size, size: Size, - align: Align, kind: MemoryKind, ) -> InterpResult<'tcx> { assert_ne!( @@ -587,10 +583,6 @@ impl GenmcCtx { let thread_infos = self.thread_id_manager.borrow(); let curr_thread = machine.threads.active_thread(); let genmc_tid = thread_infos.get_genmc_tid(curr_thread); - info!( - "GenMC: memory deallocation, thread: {curr_thread:?} ({genmc_tid:?}), address: {addr} == {addr:#x}, size: {size:?}, align: {align:?}, memory_kind: {kind:?}", - addr = address.bytes() - ); let genmc_address = address.bytes(); // GenMC doesn't support ZSTs, so we set the minimum size to 1 byte @@ -642,10 +634,6 @@ impl GenmcCtx { let genmc_curr_tid = thread_infos.get_genmc_tid(active_thread_id); let genmc_child_tid = thread_infos.get_genmc_tid(child_thread_id); - info!( - "GenMC: handling thread joining (thread {active_thread_id:?} ({genmc_curr_tid:?}) joining thread {child_thread_id:?} ({genmc_child_tid:?}))" - ); - let mut mc = self.handle.borrow_mut(); mc.as_mut().handleThreadJoin(genmc_curr_tid, genmc_child_tid); @@ -738,14 +726,14 @@ impl GenmcCtx { genmc_old_value, ); - if load_result.is_read_opt { - todo!(); - } - if let Some(error) = load_result.error.as_ref() { throw_ub_format!("{}", error.to_string_lossy()); // TODO GENMC: proper error handling: find correct error here } + if load_result.is_read_opt { + todo!(); + } + info!("GenMC: load returned value: {:?}", load_result.read_value); interp_ok(load_result.read_value) diff --git a/src/machine.rs b/src/machine.rs index 6711d057b2..20fb33e82a 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -1483,7 +1483,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { match &machine.data_race { GlobalDataRaceHandler::None => {} GlobalDataRaceHandler::Genmc(genmc_ctx) => - genmc_ctx.handle_dealloc(machine, alloc_id, ptr.addr(), size, align, kind)?, + genmc_ctx.handle_dealloc(machine, alloc_id, ptr.addr(), size, kind)?, GlobalDataRaceHandler::Vclocks(_global_state) => { let data_race = alloc_extra.data_race.as_vclocks_mut().unwrap(); data_race.write( diff --git a/tests/genmc/_disabled/pass/atomic-primitives/mpsc.rs b/tests/genmc/_disabled/pass/atomic-primitives/mpsc.rs new file mode 100644 index 0000000000..e6a2ddc5c3 --- /dev/null +++ b/tests/genmc/_disabled/pass/atomic-primitives/mpsc.rs @@ -0,0 +1,41 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@revisions: threads1 threads2 threads3 + +// NOTE: Test disabled due to missing mixed-size access support in GenMC. + +// Check that various operations on `std::sync::mpsc` are handled properly in GenMC mode. +// This test is a slightly changed version of the "Shared usage" example in the `mpsc` documentation. + +#![no_main] + +use std::sync::mpsc::channel; +use std::thread; + +const NUM_THREADS: usize = if cfg!(threads3) { + 3 +} else if cfg!(threads2) { + 2 +} else { + 1 +}; + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // Create a shared channel that can be sent along from many threads + // where tx is the sending half (tx for transmission), and rx is the receiving + // half (rx for receiving). + let (tx, rx) = channel(); + for i in 0..NUM_THREADS { + let tx = tx.clone(); + thread::spawn(move || { + tx.send(i).unwrap(); + }); + } + + for _ in 0..NUM_THREADS { + let j = rx.recv().unwrap(); + assert!(j < NUM_THREADS); + } + + 0 +} diff --git a/tests/genmc/pass/thread/libc_exit.rs.disabled b/tests/genmc/_disabled/pass/libc_exit.rs similarity index 96% rename from tests/genmc/pass/thread/libc_exit.rs.disabled rename to tests/genmc/_disabled/pass/libc_exit.rs index 96a72790d0..3e727f62b3 100644 --- a/tests/genmc/pass/thread/libc_exit.rs.disabled +++ b/tests/genmc/_disabled/pass/libc_exit.rs @@ -3,6 +3,8 @@ #![no_main] +// NOTE: Disabled due to incomplete support for `libc::exit`. + // Copied from `tests/genmc/pass/litmus/inc2w.rs` #[path = "../../../../utils/genmc.rs"] diff --git a/tests/genmc/pass/atomic-primitives/arc.check_count.stderr b/tests/genmc/pass/atomic-primitives/arc.check_count.stderr new file mode 100644 index 0000000000..f7aacf6cda --- /dev/null +++ b/tests/genmc/pass/atomic-primitives/arc.check_count.stderr @@ -0,0 +1,8 @@ +warning: GenMC currently does not model the atomic failure ordering for `compare_exchange`. Failure ordering 'Relaxed' is treated like 'Acquire', which means that Miri might miss bugs related to this memory access (possible unsoundness)! + +warning: GenMC mode currently does not model spurious failures of `compare_exchange_weak`. This may lead to missed bugs (possible unsoundness)! + +warning: GenMC currently does not model the atomic failure ordering for `compare_exchange`. Failure ordering 'Acquire' is treated like 'Relaxed', which means that Miri might incorrectly detect errors related to this memory access (possible false positives). + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 4 diff --git a/tests/genmc/pass/atomic-primitives/arc.rs b/tests/genmc/pass/atomic-primitives/arc.rs new file mode 100644 index 0000000000..4ffe458775 --- /dev/null +++ b/tests/genmc/pass/atomic-primitives/arc.rs @@ -0,0 +1,55 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@revisions: check_count try_upgrade + +// Check that various operations on `std::sync::Arc` are handled properly in GenMC mode. +// +// The number of explored executions in the expected output of this test may change if +// the implementation of `Arc` is ever changed, or additional optimizations are added to GenMC mode. +// +// The revision that tries to upgrade the `Weak` should never explore fewer executions compared to the revision that just accesses the `strong_count`, +// since `upgrade` needs to access the `strong_count` internally. +// There should always be more than 1 execution for both, since there is always the possibilility that the `Arc` has already been dropped, or it hasn't. + +#![no_main] + +use std::sync::Arc; +use std::thread; + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + let data = Arc::new(42); + + // Clone the Arc, drop the original, check that memory still valid. + let data_clone = Arc::clone(&data); + drop(data); + assert!(*data_clone == 42); + + // Create a Weak reference. + let weak = Arc::downgrade(&data_clone); + + // Spawn a thread that uses the Arc. + let weak_ = weak.clone(); + let handle = thread::spawn(move || { + // Try to upgrade weak reference. + // Depending on execution schedule, this may fail or succeed depending on whether this runs before or after the `drop` in the main thread. + + #[cfg(check_count)] + let _strong_count = weak_.strong_count(); + + #[cfg(try_upgrade)] + if let Some(strong) = weak_.upgrade() { + assert_eq!(*strong, 42); + } + }); + + // Drop the last strong reference to the data. + drop(data_clone); + + // Wait for the thread to finish. + handle.join().unwrap(); + + // The upgrade should fail now (all Arcs dropped). + assert!(weak.upgrade().is_none()); + + 0 +} diff --git a/tests/genmc/pass/atomic-primitives/arc.try_upgrade.stderr b/tests/genmc/pass/atomic-primitives/arc.try_upgrade.stderr new file mode 100644 index 0000000000..89b4da095c --- /dev/null +++ b/tests/genmc/pass/atomic-primitives/arc.try_upgrade.stderr @@ -0,0 +1,8 @@ +warning: GenMC currently does not model the atomic failure ordering for `compare_exchange`. Failure ordering 'Relaxed' is treated like 'Acquire', which means that Miri might miss bugs related to this memory access (possible unsoundness)! + +warning: GenMC mode currently does not model spurious failures of `compare_exchange_weak`. This may lead to missed bugs (possible unsoundness)! + +warning: GenMC currently does not model the atomic failure ordering for `compare_exchange`. Failure ordering 'Acquire' is treated like 'Relaxed', which means that Miri might incorrectly detect errors related to this memory access (possible false positives). + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 7 diff --git a/tests/genmc/pass/std/mutex_simple.reps1.stderr b/tests/genmc/pass/atomic-primitives/mutex_simple.reps1.stderr similarity index 100% rename from tests/genmc/pass/std/mutex_simple.reps1.stderr rename to tests/genmc/pass/atomic-primitives/mutex_simple.reps1.stderr diff --git a/tests/genmc/pass/std/mutex_simple.reps2.stderr b/tests/genmc/pass/atomic-primitives/mutex_simple.reps2.stderr similarity index 100% rename from tests/genmc/pass/std/mutex_simple.reps2.stderr rename to tests/genmc/pass/atomic-primitives/mutex_simple.reps2.stderr diff --git a/tests/genmc/pass/std/mutex_simple.rs b/tests/genmc/pass/atomic-primitives/mutex_simple.rs similarity index 84% rename from tests/genmc/pass/std/mutex_simple.rs rename to tests/genmc/pass/atomic-primitives/mutex_simple.rs index abddced3fb..aed8e92c47 100644 --- a/tests/genmc/pass/std/mutex_simple.rs +++ b/tests/genmc/pass/atomic-primitives/mutex_simple.rs @@ -41,10 +41,7 @@ fn main_() { assert!(LOCK.try_lock().is_ok()); // Trying to lock now should *not* fail since the lock is not held. } - // Thread spawning order should not matter for the result - let thread_order = if cfg!(order21) { [thread_2, thread_1] } else { [thread_1, thread_2] }; - // let thread_order = [thread_1 as extern "C" fn(*mut libc::c_void) -> *mut libc::c_void]; - let ids = unsafe { create_pthreads_no_params(thread_order) }; + let ids = unsafe { create_pthreads_no_params([thread_1, thread_2]) }; unsafe { join_pthreads(ids) }; let guard = LOCK.lock().unwrap(); diff --git a/tests/genmc/pass/data-structures/ms_queue_dynamic.rs b/tests/genmc/pass/data-structures/ms_queue_dynamic.rs index a2af4db269..efd0df3a9f 100644 --- a/tests/genmc/pass/data-structures/ms_queue_dynamic.rs +++ b/tests/genmc/pass/data-structures/ms_queue_dynamic.rs @@ -1,25 +1,24 @@ //@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows +// This test is a translations of the GenMC test `ms-queue-dynamic`, but with all code related to GenMC's hazard pointer API removed. +// The test leaks memory, so leak checks are disabled. + #![no_main] #![allow(static_mut_refs)] -#![allow(unused)] use std::alloc::{Layout, alloc, dealloc}; use std::ffi::c_void; use std::sync::atomic::Ordering::*; -use std::sync::atomic::{AtomicPtr, AtomicU64}; +use std::sync::atomic::{AtomicPtr}; use libc::{self, pthread_attr_t, pthread_t}; const MAX_THREADS: usize = 32; -const POISON_IDX: u64 = 0xAAAABBBBBBBBAAAA; - static mut QUEUE: MyStack = MyStack::new(); -static mut PARAMS: [u64; MAX_THREADS] = [POISON_IDX; MAX_THREADS]; -static mut INPUT: [u64; MAX_THREADS] = [POISON_IDX; MAX_THREADS]; +static mut PARAMS: [u64; MAX_THREADS] = [0; MAX_THREADS]; +static mut INPUT: [u64; MAX_THREADS] = [0; MAX_THREADS]; static mut OUTPUT: [Option; MAX_THREADS] = [None; MAX_THREADS]; -static mut THREADS: [pthread_t; MAX_THREADS] = [0; MAX_THREADS]; #[repr(C)] struct Node { @@ -40,10 +39,6 @@ impl Node { pub unsafe fn free(node: *mut Self) { dealloc(node as *mut u8, Layout::new::()) } - - pub unsafe fn reclaim(_node: *mut Self) { - // __VERIFIER_hp_retire(node); - } } impl MyStack { @@ -54,8 +49,7 @@ impl MyStack { } pub unsafe fn init_queue(&mut self, _num_threads: usize) { - /* initialize queue */ - let mut dummy = Node::new_alloc(); + let dummy = Node::new_alloc(); (*dummy).next = AtomicPtr::new(std::ptr::null_mut()); self.head = AtomicPtr::new(dummy); @@ -86,17 +80,14 @@ impl MyStack { } if next.is_null() { - // TODO GENMC: what if anything has to be done for `__VERIFIER_final_CAS`? if (*tail).next.compare_exchange(next, node, Release, Relaxed).is_ok() { break; } } else { - // TODO GENMC: what if anything has to be done for `__VERIFIER_helping_CAS`? let _ = self.tail.compare_exchange(tail, next, Release, Relaxed); } } - // TODO GENMC: what if anything has to be done for `__VERIFIER_helped_CAS`? let _ = self.tail.compare_exchange(tail, node, Release, Relaxed); } @@ -118,9 +109,7 @@ impl MyStack { } else { let ret_val = (*next).value; if self.head.compare_exchange(head, next, Release, Relaxed).is_ok() { - // reclaim(head); - // __VERIFIER_hp_free(hp_head); - // __VERIFIER_hp_free(hp_next); + // NOTE: The popped `Node` is leaked. return Some(ret_val); } } @@ -164,6 +153,8 @@ extern "C" fn thread_rw(value: *mut c_void) -> *mut c_void { #[unsafe(no_mangle)] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + let mut thread_ids: [pthread_t; MAX_THREADS] = [0; MAX_THREADS]; + let attr: *const pthread_attr_t = std::ptr::null(); // TODO GENMC (TESTS): make different tests: @@ -188,28 +179,28 @@ fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { /* Spawn threads */ for _ in 0..writers { let value: *mut c_void = (&raw mut PARAMS[i]) as *mut c_void; - if 0 != libc::pthread_create(&raw mut THREADS[i], attr, thread_w, value) { + if libc::pthread_create(&raw mut thread_ids[i], attr, thread_w, value) != 0 { std::process::abort(); } i += 1; } for _ in 0..readers { let value: *mut c_void = (&raw mut PARAMS[i]) as *mut c_void; - if 0 != libc::pthread_create(&raw mut THREADS[i], attr, thread_r, value) { + if libc::pthread_create(&raw mut thread_ids[i], attr, thread_r, value) != 0 { std::process::abort(); } i += 1; } for _ in 0..rdwr { let value: *mut c_void = (&raw mut PARAMS[i]) as *mut c_void; - if 0 != libc::pthread_create(&raw mut THREADS[i], attr, thread_rw, value) { + if libc::pthread_create(&raw mut thread_ids[i], attr, thread_rw, value) != 0 { std::process::abort(); } i += 1; } for i in 0..num_threads { - if 0 != libc::pthread_join(THREADS[i], std::ptr::null_mut()) { + if libc::pthread_join(thread_ids[i], std::ptr::null_mut()) != 0 { std::process::abort(); } } diff --git a/tests/genmc/pass/data-structures/treiber_stack_dynamic.rs b/tests/genmc/pass/data-structures/treiber_stack_dynamic.rs index 1c5fa9d27b..04850b85a6 100644 --- a/tests/genmc/pass/data-structures/treiber_stack_dynamic.rs +++ b/tests/genmc/pass/data-structures/treiber_stack_dynamic.rs @@ -1,39 +1,22 @@ //@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows -// TODO GENMC: maybe use `-Zmiri-genmc -Zmiri-disable-stacked-borrows-symmetry-reduction`? +// This test is a translations of the GenMC test `treiber-stack-dynamic`, but with all code related to GenMC's hazard pointer API removed. +// The test leaks memory, so leak checks are disabled. #![no_main] #![allow(static_mut_refs)] -#![allow(unused)] use std::alloc::{Layout, alloc, dealloc}; use std::ffi::c_void; -use std::sync::atomic::{AtomicPtr, AtomicU64, Ordering}; +use std::sync::atomic::{AtomicPtr, Ordering}; use libc::{self, pthread_attr_t, pthread_t}; const MAX_THREADS: usize = 32; -const MAX_NODES: usize = 0xFF; - -const POISON_IDX: u64 = 0xDEADBEEF; - -// TODO GENMC: thread local (for GenMC hazard pointer API) -// static mut TID: u64 = POISON_IDX; - static mut STACK: MyStack = MyStack::new(); static mut THREADS: [pthread_t; MAX_THREADS] = [0; MAX_THREADS]; -static mut PARAMS: [u64; MAX_THREADS] = [POISON_IDX; MAX_THREADS]; - -unsafe fn set_thread_num(_i: u64) { - // TID = i; -} - -#[allow(unused)] // TODO GENMC: what is the purpose of this in the GenMC version? -unsafe fn get_thread_num() -> u64 { - // TID - todo!() -} +static mut PARAMS: [u64; MAX_THREADS] = [0; MAX_THREADS]; #[repr(C)] struct Node { @@ -53,10 +36,6 @@ impl Node { pub unsafe fn free(node: *mut Self) { dealloc(node as *mut u8, Layout::new::()) } - - pub unsafe fn reclaim(_node: *mut Self) { - // __VERIFIER_hp_retire(node); - } } impl MyStack { @@ -92,66 +71,42 @@ impl MyStack { } pub unsafe fn pop(&self) -> u64 { - let mut top; - - // TODO GENMC: enable if GenMC hazard pointer API is implemented in MIRI - // __VERIFIER_hp_t *hp = __VERIFIER_hp_alloc(); loop { - top = STACK.top.load(Ordering::Acquire); - // top = __VERIFIER_hp_protect(hp, &s->top); + let top = self.top.load(Ordering::Acquire); if top.is_null() { - // __VERIFIER_hp_free(hp); return 0; } let next = (*top).next.load(Ordering::Relaxed); if self.top.compare_exchange(top, next, Ordering::Release, Ordering::Relaxed).is_ok() { - break; + // NOTE: The popped `Node` is leaked. + return (*top).value; } } - - let value = (*top).value; - /* Reclaim the used slot */ - // Node::reclaim(top); - // Node::free(top); - // __VERIFIER_hp_free(hp); - return value; } } extern "C" fn thread_w(value: *mut c_void) -> *mut c_void { unsafe { let pid = *(value as *mut u64); - set_thread_num(pid); STACK.push(pid); - - std::ptr::null_mut() } + std::ptr::null_mut() } -extern "C" fn thread_r(value: *mut c_void) -> *mut c_void { - unsafe { - let pid = *(value as *mut u64); - set_thread_num(pid); - - let _idx = STACK.pop(); - - std::ptr::null_mut() - } +extern "C" fn thread_r(_value: *mut c_void) -> *mut c_void { + let _idx = unsafe { STACK.pop() }; + std::ptr::null_mut() } extern "C" fn thread_rw(value: *mut c_void) -> *mut c_void { unsafe { let pid = *(value as *mut u64); - set_thread_num(pid); - STACK.push(pid); - let _idx = STACK.pop(); - - std::ptr::null_mut() } + std::ptr::null_mut() } #[unsafe(no_mangle)] @@ -178,28 +133,28 @@ fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { } for _ in 0..readers { let value: *mut c_void = (&raw mut PARAMS[i]) as *mut c_void; - if 0 != libc::pthread_create(&raw mut THREADS[i], attr, thread_r, value) { + if libc::pthread_create(&raw mut THREADS[i], attr, thread_r, value) != 0 { std::process::abort(); } i += 1; } for _ in 0..writers { let value: *mut c_void = (&raw mut PARAMS[i]) as *mut c_void; - if 0 != libc::pthread_create(&raw mut THREADS[i], attr, thread_w, value) { + if libc::pthread_create(&raw mut THREADS[i], attr, thread_w, value) != 0 { std::process::abort(); } i += 1; } for _ in 0..rdwr { let value: *mut c_void = (&raw mut PARAMS[i]) as *mut c_void; - if 0 != libc::pthread_create(&raw mut THREADS[i], attr, thread_rw, value) { + if libc::pthread_create(&raw mut THREADS[i], attr, thread_rw, value) != 0 { std::process::abort(); } i += 1; } for i in 0..num_threads { - if 0 != libc::pthread_join(THREADS[i], std::ptr::null_mut()) { + if libc::pthread_join(THREADS[i], std::ptr::null_mut()) != 0 { std::process::abort(); } } diff --git a/tests/genmc/pass/thread/miri_main_spawn_pthreads.rs b/tests/genmc/pass/thread/miri_main_spawn_pthreads.rs index 59df0e766f..9e281747e5 100644 --- a/tests/genmc/pass/thread/miri_main_spawn_pthreads.rs +++ b/tests/genmc/pass/thread/miri_main_spawn_pthreads.rs @@ -16,13 +16,13 @@ fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { let value: *mut c_void = std::ptr::null_mut(); handles.iter_mut().for_each(|thread_id| { - if 0 != unsafe { libc::pthread_create(thread_id, attr, thread_func, value) } { + if unsafe { libc::pthread_create(thread_id, attr, thread_func, value) } != 0 { std::process::abort(); } }); handles.into_iter().for_each(|thread_id| { - if 0 != unsafe { libc::pthread_join(thread_id, std::ptr::null_mut()) } { + if unsafe { libc::pthread_join(thread_id, std::ptr::null_mut()) } != 0 { std::process::abort(); } }); diff --git a/tests/utils/genmc.rs b/tests/utils/genmc.rs index 376f296b3b..9608c7ecb8 100644 --- a/tests/utils/genmc.rs +++ b/tests/utils/genmc.rs @@ -13,7 +13,7 @@ pub unsafe fn spawn_pthread( let attr: *const pthread_attr_t = std::ptr::null(); - if 0 != unsafe { libc::pthread_create(&raw mut thread_id, attr, f, value) } { + if unsafe { libc::pthread_create(&raw mut thread_id, attr, f, value) } != 0 { std::process::abort(); } thread_id @@ -21,7 +21,7 @@ pub unsafe fn spawn_pthread( // Join the given pthread, abort the process on any errors. pub unsafe fn join_pthread(thread_id: pthread_t) { - if 0 != unsafe { libc::pthread_join(thread_id, std::ptr::null_mut()) } { + if unsafe { libc::pthread_join(thread_id, std::ptr::null_mut()) } != 0 { std::process::abort(); } } From 1c3b63920d213e2ac868766d5fcce9e0d8b8a082 Mon Sep 17 00:00:00 2001 From: Patrick-6 Date: Sun, 17 Aug 2025 22:09:28 +0200 Subject: [PATCH 22/41] Rebase GenMC onto config-move branch, cleanup, add documentation. --- genmc-sys/build.rs | 8 +- genmc-sys/src/lib.rs | 70 +-- genmc-sys/src_cpp/Helper.hpp | 12 + genmc-sys/src_cpp/LogLevel.hpp | 2 +- genmc-sys/src_cpp/MiriInterface.cpp | 498 ------------------ genmc-sys/src_cpp/MiriInterface.hpp | 98 ++-- .../src_cpp/MiriInterface/MiriInterface.cpp | 400 ++++++++++++++ genmc-sys/src_cpp/MiriInterface/Mutex.cpp | 133 +++++ genmc-sys/src_cpp/ResultHandling.cpp | 11 + genmc-sys/src_cpp/ResultHandling.hpp | 186 +++++++ src/concurrency/genmc/dummy.rs | 1 - src/concurrency/genmc/mod.rs | 96 ++-- src/machine.rs | 13 +- tests/genmc/_disabled/mutex_poison.rs | 76 +++ tests/genmc/par-len.rs | 48 ++ .../pass/atomic-primitives/mutex_simple.rs | 2 +- .../pass/data-structures/ms_queue_dynamic.rs | 2 +- 17 files changed, 1011 insertions(+), 645 deletions(-) create mode 100644 genmc-sys/src_cpp/Helper.hpp delete mode 100644 genmc-sys/src_cpp/MiriInterface.cpp create mode 100644 genmc-sys/src_cpp/MiriInterface/MiriInterface.cpp create mode 100644 genmc-sys/src_cpp/MiriInterface/Mutex.cpp create mode 100644 genmc-sys/src_cpp/ResultHandling.cpp create mode 100644 genmc-sys/src_cpp/ResultHandling.hpp create mode 100644 tests/genmc/_disabled/mutex_poison.rs create mode 100644 tests/genmc/par-len.rs diff --git a/genmc-sys/build.rs b/genmc-sys/build.rs index 0b5e9645bc..f286b1962a 100644 --- a/genmc-sys/build.rs +++ b/genmc-sys/build.rs @@ -210,6 +210,12 @@ fn compile_cpp_dependencies(genmc_path: &Path) { // These definitions are parsed into a cmake list and then printed to the config.h file, so they are ';' separated. let definitions = llvm_definitions.split(";"); + let cpp_files = [ + "./src_cpp/MiriInterface/MiriInterface.cpp", + "./src_cpp/MiriInterface/Mutex.cpp", + "./src_cpp/ResultHandling.cpp", + ]; + let mut bridge = cxx_build::bridge("src/lib.rs"); // FIXME(genmc,cmake): Remove once the GenMC debug setting is available in the config.h file. if enable_genmc_debug { @@ -224,7 +230,7 @@ fn compile_cpp_dependencies(genmc_path: &Path) { .include(genmc_include_dir) .include(llvm_include_dirs) .include("./src_cpp") - .file("./src_cpp/MiriInterface.cpp") + .files(&cpp_files) .compile("genmc_interop"); // Link the Rust-C++ interface library generated by cxx_build: diff --git a/genmc-sys/src/lib.rs b/genmc-sys/src/lib.rs index abcd2a66b5..4d6eb645fa 100644 --- a/genmc-sys/src/lib.rs +++ b/genmc-sys/src/lib.rs @@ -29,6 +29,10 @@ impl GenmcScalar { pub const fn from_u64(value: u64) -> Self { Self { value, extra: 0, is_init: true } } + + pub const fn has_provenance(&self) -> bool { + self.extra != 0 + } } impl Default for GenmcParams { @@ -140,15 +144,6 @@ mod ffi { UMin = 10, } - // TODO GENMC: do these have to be shared with the Rust side? - #[derive(Debug)] - enum StoreEventType { - Normal, - ReadModifyWrite, - CompareExchange, - MutexUnlockWrite, - } - #[derive(Debug, Clone, Copy)] struct GenmcScalar { value: u64, @@ -160,42 +155,57 @@ mod ffi { #[must_use] #[derive(Debug)] - struct ReadModifyWriteResult { - old_value: GenmcScalar, - new_value: GenmcScalar, - isCoMaxWrite: bool, - error: UniquePtr, // TODO GENMC: pass more error info here + struct LoadResult { + /// If there was an error, it will be stored in `error`, otherwise it is `None`. + error: UniquePtr, + /// Indicates whether a value was read or not. + has_value: bool, + /// The value that was read. Should not be used if `has_value` is `false`. + read_value: GenmcScalar, } #[must_use] #[derive(Debug)] - struct MutexLockResult { - is_lock_acquired: bool, - error: UniquePtr, // TODO GENMC: pass more error info here + struct StoreResult { + /// If there was an error, it will be stored in `error`, otherwise it is `None`. + error: UniquePtr, + /// `true` if the write should also be reflected in Miri's memory representation. + isCoMaxWrite: bool, } #[must_use] #[derive(Debug)] - struct CompareExchangeResult { - old_value: GenmcScalar, // TODO GENMC: handle bigger values - is_success: bool, + struct ReadModifyWriteResult { + /// If there was an error, it will be stored in `error`, otherwise it is `None`. + error: UniquePtr, + /// The value that was read by the RMW operation as the left operand. + old_value: GenmcScalar, + /// The value that was produced by the RMW operation. + new_value: GenmcScalar, + /// `true` if the write should also be reflected in Miri's memory representation. isCoMaxWrite: bool, - error: UniquePtr, // TODO GENMC: pass more error info here } #[must_use] #[derive(Debug)] - struct LoadResult { - is_read_opt: bool, - read_value: GenmcScalar, // TODO GENMC: handle bigger values - error: UniquePtr, // TODO GENMC: pass more error info here + struct CompareExchangeResult { + /// If there was an error, it will be stored in `error`, otherwise it is `None`. + error: UniquePtr, + /// The value that was read by the compare-exchange. + old_value: GenmcScalar, + /// `true` if compare_exchange op was successful. + is_success: bool, + /// `true` if the write should also be reflected in Miri's memory representation. + isCoMaxWrite: bool, } #[must_use] #[derive(Debug)] - struct StoreResult { - error: UniquePtr, // TODO GENMC: pass more error info here - isCoMaxWrite: bool, + struct MutexLockResult { + /// If there was an error, it will be stored in `error`, otherwise it is `None`. + error: UniquePtr, + /// Indicate whether the lock was acquired by this thread. + is_lock_acquired: bool, } /**** /\ Result & Error types /\ ****/ @@ -205,7 +215,6 @@ mod ffi { type MemOrdering; type RMWBinOp; - type StoreEventType; // Types for Scheduling queries: type ActionKind; @@ -270,7 +279,6 @@ mod ffi { value: GenmcScalar, old_value: GenmcScalar, memory_ordering: MemOrdering, - store_event_type: StoreEventType, ) -> StoreResult; fn handleFence(self: Pin<&mut MiriGenMCShim>, thread_id: i32, memory_ordering: MemOrdering); @@ -280,7 +288,7 @@ mod ffi { size: u64, alignment: u64, ) -> u64; - fn handleFree(self: Pin<&mut MiriGenMCShim>, thread_id: i32, address: u64, size: u64); + fn handleFree(self: Pin<&mut MiriGenMCShim>, thread_id: i32, address: u64); fn handleThreadCreate(self: Pin<&mut MiriGenMCShim>, thread_id: i32, parent_id: i32); fn handleThreadJoin(self: Pin<&mut MiriGenMCShim>, thread_id: i32, child_id: i32); diff --git a/genmc-sys/src_cpp/Helper.hpp b/genmc-sys/src_cpp/Helper.hpp new file mode 100644 index 0000000000..c052c758f4 --- /dev/null +++ b/genmc-sys/src_cpp/Helper.hpp @@ -0,0 +1,12 @@ +#ifndef GENMC_MIRI_HELPER +#define GENMC_MIRI_HELPER + +/** If `ret` contains a `VerificationError`, return an `ErrorType::fromError(err)`. */ +#define RETURN_IF_ERROR(ret, ErrorType) \ + do { \ + const auto *err = std::get_if(&ret); \ + if (nullptr != err) \ + return ErrorType::fromError("FIXME(GenMC): show actual error here."); \ + } while (0) + +#endif /* GENMC_MIRI_HELPER */ \ No newline at end of file diff --git a/genmc-sys/src_cpp/LogLevel.hpp b/genmc-sys/src_cpp/LogLevel.hpp index 89b1b989a4..f58aa7eb10 100644 --- a/genmc-sys/src_cpp/LogLevel.hpp +++ b/genmc-sys/src_cpp/LogLevel.hpp @@ -5,8 +5,8 @@ #include "genmc-sys/src/lib.rs.h" // GenMC headers: -#include "Config/Verbosity.hpp" #include "Support/Error.hpp" +#include "Support/Verbosity.hpp" // C++ headers: #include diff --git a/genmc-sys/src_cpp/MiriInterface.cpp b/genmc-sys/src_cpp/MiriInterface.cpp deleted file mode 100644 index c9e10916e5..0000000000 --- a/genmc-sys/src_cpp/MiriInterface.cpp +++ /dev/null @@ -1,498 +0,0 @@ -// `genmc-sys/src_cpp` headers: -#include "MiriInterface.hpp" -#include "LogLevel.hpp" - -// CXX.rs generated headers: -#include "genmc-sys/src/lib.rs.h" - -// GenMC headers: -#include "ADT/value_ptr.hpp" -#include "Config/MemoryModel.hpp" -#include "Config/Verbosity.hpp" -#include "ExecutionGraph/EventLabel.hpp" -#include "ExecutionGraph/LoadAnnotation.hpp" -#include "Runtime/InterpreterEnumAPI.hpp" -#include "Static/ModuleID.hpp" -#include "Support/ASize.hpp" -#include "Support/Error.hpp" -#include "Support/Logger.hpp" -#include "Support/MemAccess.hpp" -#include "Support/RMWOps.hpp" -#include "Support/SAddr.hpp" -#include "Support/SVal.hpp" -#include "Support/ThreadInfo.hpp" -#include "Verification/GenMCDriver.hpp" - -// C++ headers: -#include -#include -#include -#include - -using AnnotID = ModuleID::ID; -using AnnotT = SExpr; - -// Return -1 when no thread can/should be scheduled, or the thread id of the next thread -// NOTE: this is safe because ThreadId is 32 bit, and we return a 64 bit integer -// FIXME(genmc,cxx): could directly return std::optional if CXX ever supports sharing it (see -// https://github.com/dtolnay/cxx/issues/87). -auto MiriGenMCShim::scheduleNext(const int curr_thread_id, - const ActionKind curr_thread_next_instr_kind) -> int64_t -{ - // The current thread is the only one where the `kind` could have changed since we last made - // a scheduling decision. - threadsAction[curr_thread_id].kind = curr_thread_next_instr_kind; - - if (const auto result = GenMCDriver::scheduleNext(threadsAction)) - return static_cast(result.value()); - return -1; -} - -/**** Functions available to Miri ****/ - -// NOLINTNEXTLINE(readability-convert-member-functions-to-static) -auto MiriGenMCShim::createHandle(const GenmcParams &config, bool estimation_mode) - -> std::unique_ptr -{ - auto conf = std::make_shared(); - // TODO GENMC: Can we get some default values somehow? - // Config::saveConfigOptions(*conf); - - // NOTE: Miri already does validity checks, so we can disable them in GenMC. - conf->skipAccessValidityChecks = true; - // Miri handles non-atomic accesses, so we skip the check for those in GenMC. - // Mixed atomic-non-atomic mixed-size checks are still enabled. - conf->allowNonAtomicMixedSizeAccesses = true; - - // Miri needs all threads to be replayed, even fully completed ones. - conf->replayCompletedThreads = true; - - // `1024` is the default value that GenMC uses. - // If any thread has at least this many events, a warning/tip will be printed. - // - // Miri produces a lot more events than GenMC, so the graph size warning triggers on almost - // all programs. The current value is large enough so the warning is not be triggered by any - // reasonable programs. - // FIXME(genmc): The emitted warning mentions features not supported by Miri ('--unroll' - // parameter). - // FIXME(genmc): A more appropriate limit should be chosen once the warning is useful for - // Miri. - conf->warnOnGraphSize = 1024 * 1024; - - // The `logLevel` is not part of the config struct, but the static variable `logLevel`. - logLevel = to_genmc_verbosity_level(config.log_level); - - // We only support the RC11 memory model for Rust. - conf->model = ModelType::RC11; - - conf->printRandomScheduleSeed = config.print_random_schedule_seed; - - // FIXME(genmc): check if we can enable IPR: - conf->ipr = false; - // FIXME(genmc): check if we can enable BAM: - conf->disableBAM = true; - // FIXME(genmc): check if we can do instruction caching (probably not) - conf->instructionCaching = false; - - // FIXME(genmc): implement symmetry reduction. - ERROR_ON(config.do_symmetry_reduction, - "Symmetry reduction is currently unsupported in GenMC mode."); - conf->symmetryReduction = config.do_symmetry_reduction; - - // FIXME(genmc): expose this setting to Miri (useful for testing Miri-GenMC). - conf->schedulePolicy = SchedulePolicy::WF; - - conf->estimate = estimation_mode; - conf->estimationMax = config.estimation_max; - const auto mode = conf->estimate ? GenMCDriver::Mode(GenMCDriver::EstimationMode{}) - : GenMCDriver::Mode(GenMCDriver::VerificationMode{}); - - // Running Miri-GenMC without race detection is not supported. - // Disabling this option also changes the behavior of the replay scheduler to only schedule - // at atomic operations, which is required with Miri. This happens because Miri can generate - // multiple GenMC events for a single MIR terminator. Without this option, the scheduler - // might incorrectly schedule an atomic MIR terminator because the first event it creates is - // a non-atomic (e.g., `StorageLive`). - conf->disableRaceDetection = false; - - // Miri can already check for unfreed memory. Also, GenMC cannot distinguish between memory - // that is allowed to leak and memory that is not. - conf->warnUnfreedMemory = false; - - checkConfigOptions(*conf, true); - - auto driver = std::make_unique(std::move(conf), mode); - - auto *driverPtr = driver.get(); - auto initValGetter = [driverPtr](const AAccess &access) { - const auto addr = access.getAddr(); - if (!driverPtr->initVals_.contains(addr)) { - LOG(VerbosityLevel::Warning) << "WARNING: TODO GENMC: requested initial value for address " - << addr << ", but there is none.\n"; - return SVal(0xCC00CC00); - // BUG_ON(!driverPtr->initVals_.contains(addr)); - } - auto result = driverPtr->initVals_[addr]; - if (!result.is_init) { - LOG(VerbosityLevel::Warning) << "WARNING: TODO GENMC: requested initial value for address " - << addr << ", but the memory is uninitialized.\n"; - return SVal(0xFF00FF00); - } - LOG(VerbosityLevel::Warning) << "MiriGenMCShim: requested initial value for address " << addr - << " == " << addr.get() << ", returning: " << result << "\n"; - return result.toSVal(); - }; - driver->getExec().getGraph().setInitValGetter(initValGetter); - - return driver; -} - -// This needs to be available to Miri, but clang-tidy wants it static -// NOLINTNEXTLINE(misc-use-internal-linkage) -auto createGenmcHandle(const GenmcParams &config, bool estimation_mode) - -> std::unique_ptr -{ - return MiriGenMCShim::createHandle(config, estimation_mode); -} - -/**** Execution start/end handling ****/ - -void MiriGenMCShim::handleExecutionStart() -{ - threadsAction.clear(); - threadsAction.push_back(Action(ActionKind::Load, Event::getInit())); - GenMCDriver::handleExecutionStart(); -} - -auto MiriGenMCShim::handleExecutionEnd() -> std::unique_ptr -{ - return GenMCDriver::handleExecutionEnd(threadsAction); -} - -/**** Thread management ****/ - -void MiriGenMCShim::handleThreadCreate(ThreadId thread_id, ThreadId parent_id) -{ - // NOTE: The threadCreate event happens in the parent: - auto pos = incPos(parent_id); - - // FIXME(genmc): for supporting symmetry reduction, these will need to be properly set: - const unsigned funId = 0; - const SVal arg = SVal(0); - const ThreadInfo childInfo = ThreadInfo{thread_id, parent_id, funId, arg}; - - // NOTE: Default memory ordering (`Release`) used here. - auto tcLab = std::make_unique(pos, childInfo); - auto createLab = GenMCDriver::handleThreadCreate(std::move(tcLab)); - auto genmcTid = createLab->getChildId(); - - BUG_ON(genmcTid != thread_id || genmcTid == -1 || genmcTid != threadsAction.size()); - threadsAction.push_back(Action(ActionKind::Load, Event(genmcTid, 0))); -} - -void MiriGenMCShim::handleThreadJoin(ThreadId thread_id, ThreadId child_id) -{ - // The thread join event happens in the parent. - auto pos = incPos(thread_id); - - // NOTE: Default memory ordering (`Acquire`) used here. - auto lab = std::make_unique(pos, child_id); - auto res = GenMCDriver::handleThreadJoin(std::move(lab)); - // If the join failed, decrease the event index again: - if (!res.has_value()) - decPos(thread_id); - - // NOTE: Thread return value is ignored, since Miri doesn't need it. -} - -void MiriGenMCShim::handleThreadFinish(ThreadId thread_id, uint64_t ret_val) -{ - auto pos = incPos(thread_id); - auto retVal = SVal(ret_val); - - // NOTE: Default memory ordering (`Release`) used here. - auto eLab = std::make_unique(pos, retVal); - - GenMCDriver::handleThreadFinish(std::move(eLab)); -} - -void MiriGenMCShim::handleThreadKill(ThreadId thread_id) -{ - auto pos = incPos(thread_id); - auto kLab = std::make_unique(pos); - - GenMCDriver::handleThreadKill(std::move(kLab)); -} - -/**** Blocking instructions ****/ - -void MiriGenMCShim::handleUserBlock(ThreadId thread_id) -{ - auto pos = incPos(thread_id); - auto bLab = UserBlockLabel::create(pos); - GenMCDriver::handleBlock(std::move(bLab)); -} - -/**** Memory access handling ****/ - -[[nodiscard]] auto MiriGenMCShim::handleLoad(ThreadId thread_id, uint64_t address, uint64_t size, - MemOrdering ord, GenmcScalar old_val) -> LoadResult -{ - auto pos = incPos(thread_id); - - auto loc = SAddr(address); - auto aSize = ASize(size); - // `type` is only used for printing. - auto type = AType::Unsigned; - - auto newLab = std::make_unique(pos, ord, loc, aSize, type); - - auto oldValSetter = [this, old_val](SAddr loc) { this->handleOldVal(loc, old_val); }; - auto result = GenMCDriver::handleLoad(std::move(newLab), oldValSetter); - return result; -} - -[[nodiscard]] auto MiriGenMCShim::handleReadModifyWrite(ThreadId thread_id, uint64_t address, - uint64_t size, MemOrdering loadOrd, - MemOrdering store_ordering, RMWBinOp rmw_op, - GenmcScalar rhs_value, GenmcScalar old_val) - -> ReadModifyWriteResult -{ - auto pos = incPos(thread_id); - - auto loc = SAddr(address); - auto aSize = ASize(size); - // `type` is only used for printing. - auto type = AType::Unsigned; - - auto rhsVal = rhs_value.toSVal(); - auto newLab = - std::make_unique(pos, loadOrd, loc, aSize, type, rmw_op, rhsVal); - - auto oldValSetter = [this, old_val](SAddr loc) { this->handleOldVal(loc, old_val); }; - auto result = GenMCDriver::handleLoad(std::move(newLab), oldValSetter); - if (const auto *error = result.error.get()) { - return ReadModifyWriteResult::fromError(*error); - } - - auto oldVal = result.scalar.toSVal(); // TODO GENMC: u128 handling - auto newVal = executeRMWBinOp(oldVal, rhsVal, size, rmw_op); - - auto store_result = handleStore(thread_id, address, size, GenmcScalar(newVal), old_val, - store_ordering, StoreEventType::ReadModifyWrite); - - if (store_result.is_error()) - return ReadModifyWriteResult::fromError(*store_result.error.get()); - return ReadModifyWriteResult(oldVal, newVal, store_result.isCoMaxWrite); -} - -[[nodiscard]] auto MiriGenMCShim::handleCompareExchange( - ThreadId thread_id, uint64_t address, uint64_t size, GenmcScalar expected_value, - GenmcScalar new_value, GenmcScalar old_val, MemOrdering success_load_ordering, - MemOrdering success_store_ordering, MemOrdering fail_load_ordering, - bool can_fail_spuriously) -> CompareExchangeResult -{ - auto pos = incPos(thread_id); - - auto loc = SAddr(address); - auto aSize = ASize(size); - // `type` is only used for printing. - auto type = AType::Unsigned; - - auto expectedVal = expected_value.toSVal(); - auto newVal = new_value.toSVal(); - - // FIXME(GenMC): properly handle failure memory ordering. - - auto newLab = std::make_unique(pos, success_load_ordering, loc, aSize, type, - expectedVal, newVal); - - auto oldValSetter = [this, old_val](SAddr loc) { this->handleOldVal(loc, old_val); }; - auto result = GenMCDriver::handleLoad(std::move(newLab), oldValSetter); - if (const auto *error = result.error.get()) { - return CompareExchangeResult::fromError(*error); - } - - auto oldVal = result.scalar.toSVal(); - if (oldVal != expectedVal) - return CompareExchangeResult::failure(oldVal); - - auto store_result = handleStore(thread_id, address, size, GenmcScalar(newVal), old_val, - success_store_ordering, StoreEventType::CompareExchange); - - if (store_result.is_error()) - return CompareExchangeResult::fromError(*store_result.error); - return CompareExchangeResult::success(oldVal, store_result.isCoMaxWrite); -} - -[[nodiscard]] auto MiriGenMCShim::handleStore(ThreadId thread_id, uint64_t address, uint64_t size, - GenmcScalar value, GenmcScalar old_val, - MemOrdering ord, StoreEventType store_event_type) - -> StoreResult -{ - auto pos = incPos(thread_id); - - auto loc = SAddr(address); - auto aSize = ASize(size); - // `type` is only used for printing. - auto type = AType::Unsigned; - - auto val = value.toSVal(); - - std::unique_ptr wLab; - switch (store_event_type) { - case StoreEventType::Normal: - wLab = std::make_unique(pos, ord, loc, aSize, type, val); - break; - case StoreEventType::ReadModifyWrite: - wLab = std::make_unique(pos, ord, loc, aSize, type, val); - break; - case StoreEventType::CompareExchange: - wLab = std::make_unique(pos, ord, loc, aSize, type, val); - break; - case StoreEventType::MutexUnlockWrite: - wLab = UnlockWriteLabel::create(pos, ord, loc, aSize, AType::Signed, val); - break; - default: - ERROR("Unsupported Store Event Type"); - } - - auto oldValSetter = [this, old_val](SAddr loc) { - this->handleOldVal(loc, - old_val); // TODO GENMC(HACK): is this the correct way to do it? - }; - - return GenMCDriver::handleStore(std::move(wLab), oldValSetter); -} - -void MiriGenMCShim::handleFence(ThreadId thread_id, MemOrdering ord) -{ - auto pos = incPos(thread_id); - - auto fLab = std::make_unique(pos, ord); - GenMCDriver::handleFence(std::move(fLab)); -} - -/**** Memory (de)allocation ****/ - -auto MiriGenMCShim::handleMalloc(ThreadId thread_id, uint64_t size, uint64_t alignment) -> uintptr_t -{ - auto pos = incPos(thread_id); - - // These are only used for printing and features Miri-GenMC doesn't support (yet). - auto sd = StorageDuration::SD_Heap; - auto stype = StorageType::ST_Volatile; - auto spc = AddressSpace::AS_User; - - auto aLab = - std::make_unique(pos, size, alignment, sd, stype, spc, EventDeps()); - - SAddr retVal = GenMCDriver::handleMalloc(std::move(aLab)); - - BUG_ON(retVal.get() == 0); - - auto address = retVal.get(); - return address; -} - -void MiriGenMCShim::handleFree(ThreadId thread_id, uint64_t address, uint64_t size) -{ - auto addr = SAddr(address); - auto alloc_size = SAddr(size); - - auto pos = incPos(thread_id); - - auto dLab = std::make_unique(pos, addr, size); - GenMCDriver::handleFree(std::move(dLab)); -} - -/**** Mutex handling ****/ - -auto MiriGenMCShim::handleMutexLock(ThreadId thread_id, uint64_t address, uint64_t size) - -> MutexLockResult -{ - // TODO GENMC: this needs to be identical even in multithreading - ModuleID::ID annot_id; - if (annotation_id.contains(address)) { - annot_id = annotation_id.at(address); - } else { - annot_id = annotation_id_counter++; - annotation_id.insert(std::make_pair(address, annot_id)); - } - const auto aSize = ASize(size); - auto annot = std::move(Annotation( - AssumeType::Spinloop, - Annotation::ExprVP(NeExpr::create( - RegisterExpr::create(aSize.getBits(), annot_id), - ConcreteExpr::create(aSize.getBits(), SVal(1))) - .release()))); - - auto &currPos = threadsAction[thread_id].event; - // auto rLab = LockCasReadLabel::create(++currPos, address, size); - auto rLab = LockCasReadLabel::create(++currPos, address, size, annot); - - // Mutex starts out unlocked, so we always say the previous value is "unlocked". - auto oldValSetter = [this](SAddr loc) { this->handleOldVal(loc, SVal(0)); }; - LoadResult loadResult = GenMCDriver::handleLoad(std::move(rLab), oldValSetter); - if (loadResult.is_error()) { - --currPos; - return MutexLockResult::fromError(*loadResult.error); - } else if (loadResult.is_read_opt) { - --currPos; - // TODO GENMC: is_read_opt == Mutex is acquired - // None --> Someone else has lock, this thread will be rescheduled later (currently - // block) 0 --> Got the lock 1 --> Someone else has lock, this thread will - // not be rescheduled later (block on Miri side) - return MutexLockResult(false); - } - // TODO GENMC(QUESTION): is the `isBlocked` even needed? - // if (!loadResult.has_value() || getCurThr().isBlocked()) - // return; - - const bool is_lock_acquired = loadResult.value() == SVal(0); - if (is_lock_acquired) { - auto wLab = LockCasWriteLabel::create(++currPos, address, size); - StoreResult storeResult = GenMCDriver::handleStore(std::move(wLab), oldValSetter); - if (storeResult.is_error()) - return MutexLockResult::fromError(*storeResult.error); - - } else { - auto bLab = LockNotAcqBlockLabel::create(++currPos); - GenMCDriver::handleBlock(std::move(bLab)); - } - - return MutexLockResult(is_lock_acquired); -} - -auto MiriGenMCShim::handleMutexTryLock(ThreadId thread_id, uint64_t address, uint64_t size) - -> MutexLockResult -{ - auto &currPos = threadsAction[thread_id].event; - auto rLab = TrylockCasReadLabel::create(++currPos, address, size); - // Mutex starts out unlocked, so we always say the previous value is "unlocked". - auto oldValSetter = [this](SAddr loc) { this->handleOldVal(loc, SVal(0)); }; - LoadResult loadResult = GenMCDriver::handleLoad(std::move(rLab), oldValSetter); - if (!loadResult.has_value()) { - --currPos; - // TODO GENMC: maybe use std move and make it take a unique_ptr ? - return MutexLockResult::fromError(*loadResult.error); - } - - const bool is_lock_acquired = loadResult.value() == SVal(0); - if (!is_lock_acquired) - return MutexLockResult(false); /* Lock already held. */ - - auto wLab = TrylockCasWriteLabel::create(++currPos, address, size); - StoreResult storeResult = GenMCDriver::handleStore(std::move(wLab), oldValSetter); - if (storeResult.is_error()) - return MutexLockResult::fromError(*storeResult.error); - - return MutexLockResult(true); -} - -auto MiriGenMCShim::handleMutexUnlock(ThreadId thread_id, uint64_t address, uint64_t size) - -> StoreResult -{ - return handleStore(thread_id, address, size, SVal(0), SVal(0xDEADBEEF), - MemOrdering::Release, StoreEventType::MutexUnlockWrite); -} diff --git a/genmc-sys/src_cpp/MiriInterface.hpp b/genmc-sys/src_cpp/MiriInterface.hpp index 183e51ad52..0f38b13b0d 100644 --- a/genmc-sys/src_cpp/MiriInterface.hpp +++ b/genmc-sys/src_cpp/MiriInterface.hpp @@ -7,13 +7,15 @@ // GenMC generated headers: #include "config.h" +// Miri `genmc-sys/src_cpp` headers: +#include "ResultHandling.hpp" + // GenMC headers: -#include "Config/Config.hpp" #include "ExecutionGraph/EventLabel.hpp" #include "Static/ModuleID.hpp" #include "Support/MemOrdering.hpp" #include "Support/RMWOps.hpp" -#include "Support/ResultHandling.hpp" +#include "Verification/Config.hpp" #include "Verification/GenMCDriver.hpp" // C++ headers: @@ -28,27 +30,8 @@ struct GenmcParams; using ThreadId = int; -enum class StoreEventType : uint8_t { - Normal, - ReadModifyWrite, - CompareExchange, - MutexUnlockWrite, -}; - -struct MutexLockResult { - bool is_lock_acquired; - std::unique_ptr error; // TODO GENMC: pass more error info here - - MutexLockResult(bool is_lock_acquired) : is_lock_acquired(is_lock_acquired), error(nullptr) - {} - - static auto fromError(std::string msg) -> MutexLockResult - { - auto res = MutexLockResult(false); - res.error = std::make_unique(msg); - return res; - } -}; +using AnnotID = ModuleID::ID; +using AnnotT = SExpr; // TODO GENMC: fix naming conventions @@ -83,14 +66,14 @@ struct MiriGenMCShim : private GenMCDriver { bool can_fail_spuriously); [[nodiscard]] StoreResult handleStore(ThreadId thread_id, uint64_t address, uint64_t size, GenmcScalar value, GenmcScalar old_val, - MemOrdering ord, StoreEventType store_event_type); + MemOrdering ord); void handleFence(ThreadId thread_id, MemOrdering ord); /**** Memory (de)allocation ****/ uintptr_t handleMalloc(ThreadId thread_id, uint64_t size, uint64_t alignment); - void handleFree(ThreadId thread_id, uint64_t address, uint64_t size); + void handleFree(ThreadId thread_id, uint64_t address); /**** Thread management ****/ @@ -110,63 +93,54 @@ struct MiriGenMCShim : private GenMCDriver { -> MutexLockResult; auto handleMutexUnlock(ThreadId thread_id, uint64_t address, uint64_t size) -> StoreResult; - /**** Scheduling queries ****/ - - // TODO GENMC: implement + /**** Exploration related functionality ****/ auto scheduleNext(const int curr_thread_id, const ActionKind curr_thread_next_instr_kind) -> int64_t; - /**** TODO GENMC: Other stuff: ****/ + bool isExplorationDone() { return GenMCDriver::done(); } - auto getBlockedExecutionCount() const -> uint64_t + /** Increment the event index in the given thread by 1 and return the new event. */ + [[nodiscard]] inline auto incPos(ThreadId tid) -> Event { - return static_cast(getResult().exploredBlocked); + ERROR_ON(tid >= threadsAction.size(), "ThreadId out of bounds"); + return ++threadsAction[tid].event; } - - auto getExploredExecutionCount() const -> uint64_t + /** Decrement the event index in the given thread by 1 and return the new event. */ + inline auto decPos(ThreadId tid) -> Event { - return static_cast(getResult().explored); + ERROR_ON(tid >= threadsAction.size(), "ThreadId out of bounds"); + return --threadsAction[tid].event; } - bool isExplorationDone() { return GenMCDriver::done(); } + /**** Printing and result querying ****/ - /**** OTHER ****/ - - auto incPos(ThreadId tid) -> Event + auto getBlockedExecutionCount() const -> uint64_t { - ERROR_ON(tid >= threadsAction.size(), "ThreadId out of bounds"); - return ++threadsAction[tid].event; + return static_cast(getResult().exploredBlocked); } - auto decPos(ThreadId tid) -> Event + + auto getExploredExecutionCount() const -> uint64_t { - ERROR_ON(tid >= threadsAction.size(), "ThreadId out of bounds"); - return --threadsAction[tid].event; + return static_cast(getResult().explored); } void printGraph() { GenMCDriver::debugPrintGraph(); } void printEstimationResults(const double elapsed_time_sec) const { - // TODO GENMC(CLEANUP): should this happen on the Rust side? + // FIXME(GenMC,cleanup): should this happen on the Rust side? const auto &res = getResult(); const auto *conf = getConf(); - auto mean = std::llround(res.estimationMean); - auto sd = std::llround(std::sqrt(res.estimationVariance)); - auto meanTimeSecs = + long long mean = std::llround(res.estimationMean); + long long sd = std::llround(std::sqrt(res.estimationVariance)); + long double meanTimeSecs = (long double)elapsed_time_sec / (res.explored + res.exploredBlocked); - // FIXME(io): restore the old precision after the print? - // PRINT(VerbosityLevel::Error) - // << "Finished estimation in " << std::setprecision(2) << elapsed_time_sec - // << " seconds.\n\n" - // << "Total executions estimate: " << mean << " (+- " << sd << ")\n" - // << "Time to completion estimate: " << std::setprecision(2) - // << (meanTimeSecs * mean) << "s\n"; PRINT(VerbosityLevel::Error) << "Finished estimation in " << std::format("%.2f", elapsed_time_sec) << " seconds.\n\n" - << "Total executions estimate: " << mean << " (+- " << sd << ")\n" + << std::format("Total executions estimate: {} (+- {})\n", mean, sd) << "Time to completion estimate: " << std::format("%.2f", meanTimeSecs * mean) << "s\n"; GENMC_DEBUG(if (conf->printEstimationStats) PRINT(VerbosityLevel::Error) @@ -226,6 +200,20 @@ struct MiriGenMCShim : private GenMCDriver { // or WriteLabel ==> Update its value in place (only if non-atomic) } + template + HandleResult handleLoadResetIfNone(std::function oldValSetter, + ThreadId tid, Ts &&...params) + { + const auto pos = incPos(tid); + const auto ret = + GenMCDriver::handleLoad(oldValSetter, pos, std::forward(params)...); + // If we didn't get a value, we reset the index of the current thread. + if (!std::holds_alternative(ret)) { + decPos(tid); + } + return ret; + } + // TODO GENMC(mixed-size accesses): std::unordered_map initVals_{}; diff --git a/genmc-sys/src_cpp/MiriInterface/MiriInterface.cpp b/genmc-sys/src_cpp/MiriInterface/MiriInterface.cpp new file mode 100644 index 0000000000..eeb4433800 --- /dev/null +++ b/genmc-sys/src_cpp/MiriInterface/MiriInterface.cpp @@ -0,0 +1,400 @@ +#include "MiriInterface.hpp" + +// Miri C++ helpers: +#include "Helper.hpp" +#include "LogLevel.hpp" + +// CXX.rs generated headers: +#include "genmc-sys/src/lib.rs.h" + +// GenMC headers: +#include "ADT/value_ptr.hpp" +#include "ExecutionGraph/EventLabel.hpp" +#include "ExecutionGraph/LoadAnnotation.hpp" +#include "Runtime/InterpreterEnumAPI.hpp" +#include "Static/ModuleID.hpp" +#include "Support/ASize.hpp" +#include "Support/Error.hpp" +#include "Support/Logger.hpp" +#include "Support/MemAccess.hpp" +#include "Support/RMWOps.hpp" +#include "Support/SAddr.hpp" +#include "Support/SVal.hpp" +#include "Support/ThreadInfo.hpp" +#include "Support/Verbosity.hpp" +#include "Verification/GenMCDriver.hpp" +#include "Verification/MemoryModel.hpp" + +// C++ headers: +#include +#include +#include +#include + +// Return -1 when no thread can/should be scheduled, or the thread id of the next thread +// NOTE: this is safe because ThreadId is 32 bit, and we return a 64 bit integer +// FIXME(genmc,cxx): could directly return std::optional if CXX ever supports sharing it (see +// https://github.com/dtolnay/cxx/issues/87). +auto MiriGenMCShim::scheduleNext(const int curr_thread_id, + const ActionKind curr_thread_next_instr_kind) -> int64_t +{ + // The current thread is the only one where the `kind` could have changed since we last made + // a scheduling decision. + threadsAction[curr_thread_id].kind = curr_thread_next_instr_kind; + + if (const auto result = GenMCDriver::scheduleNext(threadsAction)) + return static_cast(result.value()); + return -1; +} + +/**** Functions available to Miri ****/ + +// NOLINTNEXTLINE(readability-convert-member-functions-to-static) +auto MiriGenMCShim::createHandle(const GenmcParams &config, bool estimation_mode) + -> std::unique_ptr +{ + auto conf = std::make_shared(); + + conf->skipNonAtomicInitializedCheck = true; + + // Miri needs all threads to be replayed, even fully completed ones. + conf->replayCompletedThreads = true; + + // `1024` is the default value that GenMC uses. + // If any thread has at least this many events, a warning/tip will be printed. + // + // Miri produces a lot more events than GenMC, so the graph size warning triggers on almost + // all programs. The current value is large enough so the warning is not be triggered by any + // reasonable programs. + // FIXME(genmc): The emitted warning mentions features not supported by Miri ('--unroll' + // parameter). + // FIXME(genmc): A more appropriate limit should be chosen once the warning is useful for + // Miri. + conf->warnOnGraphSize = 1024 * 1024; + + // The `logLevel` is not part of the config struct, but the static variable `logLevel`. + logLevel = to_genmc_verbosity_level(config.log_level); + + // We only support the RC11 memory model for Rust. + conf->model = ModelType::RC11; + + conf->printRandomScheduleSeed = config.print_random_schedule_seed; + + // FIXME(genmc): check if we can enable IPR: + conf->ipr = false; + // FIXME(genmc): check if we can enable BAM: + conf->disableBAM = true; + // FIXME(genmc): check if we can do instruction caching (probably not) + conf->instructionCaching = false; + + // FIXME(genmc): implement symmetry reduction. + ERROR_ON(config.do_symmetry_reduction, + "Symmetry reduction is currently unsupported in GenMC mode."); + conf->symmetryReduction = config.do_symmetry_reduction; + + // FIXME(genmc): expose this setting to Miri (useful for testing Miri-GenMC). + conf->schedulePolicy = SchedulePolicy::WF; + + conf->estimate = estimation_mode; + conf->estimationMax = config.estimation_max; + const auto mode = conf->estimate ? GenMCDriver::Mode(GenMCDriver::EstimationMode{}) + : GenMCDriver::Mode(GenMCDriver::VerificationMode{}); + + // Running Miri-GenMC without race detection is not supported. + // Disabling this option also changes the behavior of the replay scheduler to only schedule + // at atomic operations, which is required with Miri. This happens because Miri can generate + // multiple GenMC events for a single MIR terminator. Without this option, the scheduler + // might incorrectly schedule an atomic MIR terminator because the first event it creates is + // a non-atomic (e.g., `StorageLive`). + conf->disableRaceDetection = false; + + // Miri can already check for unfreed memory. Also, GenMC cannot distinguish between memory + // that is allowed to leak and memory that is not. + conf->warnUnfreedMemory = false; + + // FIXME(GenMC): This function currently exits on error, but will return an error value in + // the future. + checkConfig(*conf); + + auto driver = std::make_unique(std::move(conf), mode); + + auto *driverPtr = driver.get(); + auto initValGetter = [driverPtr](const AAccess &access) { + const auto addr = access.getAddr(); + if (!driverPtr->initVals_.contains(addr)) { + LOG(VerbosityLevel::Warning) + << "WARNING: TODO GENMC: requested initial value for address " + << addr << ", but there is none.\n"; + return SVal(0xCC00CC00); + // BUG_ON(!driverPtr->initVals_.contains(addr)); + } + auto result = driverPtr->initVals_[addr]; + if (!result.is_init) { + LOG(VerbosityLevel::Warning) + << "WARNING: TODO GENMC: requested initial value for address " + << addr << ", but the memory is uninitialized.\n"; + return SVal(0xFF00FF00); + } + LOG(VerbosityLevel::Warning) + << "MiriGenMCShim: requested initial value for address " << addr + << " == " << addr.get() << ", returning: " << result << "\n"; + return result.toSVal(); + }; + driver->getExec().getGraph().setInitValGetter(initValGetter); + + return driver; +} + +// This needs to be available to Miri, but clang-tidy wants it static +// NOLINTNEXTLINE(misc-use-internal-linkage) +auto createGenmcHandle(const GenmcParams &config, bool estimation_mode) + -> std::unique_ptr +{ + return MiriGenMCShim::createHandle(config, estimation_mode); +} + +/**** Execution start/end handling ****/ + +void MiriGenMCShim::handleExecutionStart() +{ + threadsAction.clear(); + threadsAction.push_back(Action(ActionKind::Load, Event::getInit())); + GenMCDriver::handleExecutionStart(); +} + +auto MiriGenMCShim::handleExecutionEnd() -> std::unique_ptr +{ + GenMCDriver::handleExecutionEnd(threadsAction); + // TODO GENMC: check if an error happened here? + // ERROR_ON(isHalting(), + // "handleExecutionEnd found an error, but there is no error handling for that yet."); + return {}; +} + +/**** Thread management ****/ + +void MiriGenMCShim::handleThreadCreate(ThreadId thread_id, ThreadId parent_id) +{ + // NOTE: The threadCreate event happens in the parent: + auto pos = incPos(parent_id); + + // FIXME(genmc): for supporting symmetry reduction, these will need to be properly set: + const unsigned fun_id = 0; + const SVal arg = SVal(0); + const ThreadInfo childInfo = ThreadInfo{thread_id, parent_id, fun_id, arg}; + + // NOTE: Default memory ordering (`Release`) used here. + auto childTid = GenMCDriver::handleThreadCreate(pos, childInfo, EventDeps()); + + BUG_ON(childTid != thread_id || childTid <= 0 || childTid != threadsAction.size()); + threadsAction.push_back(Action(ActionKind::Load, Event(childTid, 0))); +} + +void MiriGenMCShim::handleThreadJoin(ThreadId thread_id, ThreadId child_id) +{ + // The thread join event happens in the parent. + auto pos = incPos(thread_id); + + // NOTE: Default memory ordering (`Acquire`) used here. + auto ret = GenMCDriver::handleThreadJoin(pos, child_id, EventDeps()); + // If the join failed, decrease the event index again: + if (!std::holds_alternative(ret)) { + decPos(thread_id); + } + + // NOTE: Thread return value is ignored, since Miri doesn't need it. +} + +void MiriGenMCShim::handleThreadFinish(ThreadId thread_id, uint64_t ret_val) +{ + const auto pos = incPos(thread_id); + // NOTE: Default memory ordering (`Release`) used here. + GenMCDriver::handleThreadFinish(pos, SVal(ret_val)); +} + +void MiriGenMCShim::handleThreadKill(ThreadId thread_id) +{ + const auto pos = incPos(thread_id); + GenMCDriver::handleThreadKill(pos); +} + +/**** Blocking instructions ****/ + +void MiriGenMCShim::handleUserBlock(ThreadId thread_id) +{ + GenMCDriver::handleAssume(incPos(thread_id), AssumeType::User); +} + +/**** Memory access handling ****/ + +[[nodiscard]] auto MiriGenMCShim::handleLoad(ThreadId thread_id, uint64_t address, uint64_t size, + MemOrdering ord, GenmcScalar old_val) -> LoadResult +{ + const auto addr = SAddr(address); + const auto aSize = ASize(size); + // `type` is only used for printing. + const auto type = AType::Unsigned; + + const auto oldValSetter = [this, old_val](SAddr addr) { + this->handleOldVal(addr, old_val); + }; + const auto ret = handleLoadResetIfNone( + oldValSetter, thread_id, ord, addr, aSize, type); + RETURN_IF_ERROR(ret, LoadResult); + + const auto *retVal = std::get_if(&ret); + if (retVal != nullptr) + return LoadResult::fromValue(*retVal); + + ERROR("Unimplemented: load returned unexpected result."); +} + +[[nodiscard]] auto MiriGenMCShim::handleReadModifyWrite(ThreadId thread_id, uint64_t address, + uint64_t size, MemOrdering loadOrd, + MemOrdering store_ordering, RMWBinOp rmw_op, + GenmcScalar rhs_value, GenmcScalar old_val) + -> ReadModifyWriteResult +{ + const auto addr = SAddr(address); + const auto aSize = ASize(size); + // `type` is only used for printing. + const auto type = AType::Unsigned; + + const auto rhsVal = rhs_value.toSVal(); + + const auto oldValSetter = [this, old_val](SAddr addr) { + this->handleOldVal(addr, old_val); + }; + const auto ret = handleLoadResetIfNone( + oldValSetter, thread_id, loadOrd, addr, aSize, type, rmw_op, rhsVal, EventDeps()); + RETURN_IF_ERROR(ret, ReadModifyWriteResult); + + const auto *retVal = std::get_if(&ret); + if (nullptr == retVal) { + ERROR("Unimplemented: read-modify-write returned unhandled result."); + } + const auto oldVal = *retVal; + const auto newVal = executeRMWBinOp(oldVal, rhsVal, size, rmw_op); + + const auto storePos = incPos(thread_id); + const auto storeRet = GenMCDriver::handleStore( + oldValSetter, storePos, store_ordering, addr, aSize, type, newVal); + RETURN_IF_ERROR(storeRet, ReadModifyWriteResult); + + const auto *storeRetVal = std::get_if(&ret); + ERROR_ON(nullptr == storeRetVal, "Unimplemented: load returned unexpected result."); + + // TODO GENMC(mixed-accesses): calculate this value + const auto &g = getExec().getGraph(); + const bool isCoMaxWrite = g.co_max(addr)->getPos() == storePos; + LOG(VerbosityLevel::Tip) << "TODO GENMC: calcuate isCoMaxWrite!!!\n"; + return ReadModifyWriteResult(oldVal, newVal, isCoMaxWrite); +} + +[[nodiscard]] auto MiriGenMCShim::handleCompareExchange( + ThreadId thread_id, uint64_t address, uint64_t size, GenmcScalar expected_value, + GenmcScalar new_value, GenmcScalar old_val, MemOrdering success_load_ordering, + MemOrdering success_store_ordering, MemOrdering fail_load_ordering, + bool can_fail_spuriously) -> CompareExchangeResult +{ + auto addr = SAddr(address); + auto aSize = ASize(size); + // `type` is only used for printing. + auto type = AType::Unsigned; + + auto expectedVal = expected_value.toSVal(); + auto newVal = new_value.toSVal(); + + // FIXME(GenMC): properly handle failure memory ordering. + + auto oldValSetter = [this, old_val](SAddr addr) { this->handleOldVal(addr, old_val); }; + const auto ret = handleLoadResetIfNone( + oldValSetter, thread_id, success_load_ordering, addr, aSize, type, expectedVal, + newVal); + RETURN_IF_ERROR(ret, CompareExchangeResult); + + const auto *retVal = std::get_if(&ret); + ERROR_ON(nullptr == retVal, "Unimplemented: load returned unexpected result."); + + const auto oldVal = *retVal; + if (oldVal != expectedVal) + return CompareExchangeResult::failure(oldVal); + + // FIXME(GenMC): Add support for modelling spurious failures. + + const auto storePos = incPos(thread_id); + const auto storeRet = GenMCDriver::handleStore( + oldValSetter, storePos, success_store_ordering, addr, aSize, type, newVal); + RETURN_IF_ERROR(storeRet, CompareExchangeResult); + + const auto *storeRetVal = std::get_if(&ret); + ERROR_ON(nullptr == storeRetVal, "Unimplemented: load returned unexpected result."); + + // TODO GENMC(mixed-accesses): calculate this value + const auto &g = getExec().getGraph(); + const bool isCoMaxWrite = g.co_max(addr)->getPos() == storePos; + LOG(VerbosityLevel::Tip) << "TODO GENMC: calcuate isCoMaxWrite!!!\n"; + return CompareExchangeResult::success(oldVal, isCoMaxWrite); +} + +[[nodiscard]] auto MiriGenMCShim::handleStore(ThreadId thread_id, uint64_t address, uint64_t size, + GenmcScalar value, GenmcScalar old_val, + MemOrdering ord) -> StoreResult +{ + auto pos = incPos(thread_id); + + auto addr = SAddr(address); + auto aSize = ASize(size); + // `type` is only used for printing. + auto type = AType::Unsigned; + + auto val = value.toSVal(); + + auto oldValSetter = [this, old_val](SAddr addr) { + this->handleOldVal(addr, + old_val); // TODO GENMC(HACK): is this the correct way to do it? + }; + + const auto ret = GenMCDriver::handleStore( + oldValSetter, pos, ord, addr, aSize, type, val, EventDeps()); + + RETURN_IF_ERROR(ret, StoreResult); + + if (!std::holds_alternative(ret)) { + ERROR("store returned unexpected result"); + } + + // TODO GENMC(mixed-accesses): calculate this value + const auto &g = getExec().getGraph(); + const bool isCoMaxWrite = g.co_max(addr)->getPos() == pos; + return StoreResult::ok(isCoMaxWrite); +} + +void MiriGenMCShim::handleFence(ThreadId thread_id, MemOrdering ord) +{ + const auto pos = incPos(thread_id); + GenMCDriver::handleFence(pos, ord, EventDeps()); +} + +/**** Memory (de)allocation ****/ + +auto MiriGenMCShim::handleMalloc(ThreadId thread_id, uint64_t size, uint64_t alignment) -> uint64_t +{ + auto pos = incPos(thread_id); + + // These are only used for printing and features Miri-GenMC doesn't support (yet). + auto sd = StorageDuration::SD_Heap; + auto stype = StorageType::ST_Volatile; + auto spc = AddressSpace::AS_User; + + const SVal retVal = + GenMCDriver::handleMalloc(pos, size, alignment, sd, stype, spc, EventDeps()); + return retVal.get(); +} + +void MiriGenMCShim::handleFree(ThreadId thread_id, uint64_t address) +{ + const auto pos = incPos(thread_id); + const auto addr = SAddr(address); + GenMCDriver::handleFree(pos, addr, EventDeps()); +} diff --git a/genmc-sys/src_cpp/MiriInterface/Mutex.cpp b/genmc-sys/src_cpp/MiriInterface/Mutex.cpp new file mode 100644 index 0000000000..5b58db71c9 --- /dev/null +++ b/genmc-sys/src_cpp/MiriInterface/Mutex.cpp @@ -0,0 +1,133 @@ +#include "MiriInterface.hpp" + +// Miri C++ helpers: +#include "Helper.hpp" + +// CXX.rs generated headers: +#include "genmc-sys/src/lib.rs.h" + +auto MiriGenMCShim::handleMutexLock(ThreadId thread_id, uint64_t address, uint64_t size) + -> MutexLockResult +{ + // TODO GENMC: this needs to be identical even in multithreading + ModuleID::ID annot_id; + if (annotation_id.contains(address)) { + annot_id = annotation_id.at(address); + } else { + annot_id = annotation_id_counter++; + annotation_id.insert(std::make_pair(address, annot_id)); + } + const auto aSize = ASize(size); + auto annot = std::move(Annotation( + AssumeType::Spinloop, + Annotation::ExprVP(NeExpr::create( + RegisterExpr::create(aSize.getBits(), annot_id), + ConcreteExpr::create(aSize.getBits(), SVal(1))) + .release()))); + + // Mutex starts out unlocked, so we always say the previous value is "unlocked". + auto oldValSetter = [this](SAddr addr) { this->handleOldVal(addr, GenmcScalar(0, 0)); }; + const auto ret = handleLoadResetIfNone( + oldValSetter, thread_id, address, size, annot, EventDeps()); + RETURN_IF_ERROR(ret, MutexLockResult); + + const auto *retVal = std::get_if(&ret); + if (!retVal) { + if (std::holds_alternative(ret)) { + // TODO TODO GENMC: what did I mean with this comment? + // TODO GENMC: is_read_opt == Mutex is acquired + // None --> Someone else has lock, this thread will be rescheduled later + // (currently block) 0 --> Got the lock 1 --> Someone else has lock, + // this thread will not be rescheduled later (block on Miri side) + return MutexLockResult(false); + } + ERROR("Unimplemented: mutex lock returned unexpected result."); + } + + const bool is_lock_acquired = *retVal == SVal(0); + if (is_lock_acquired) { + const auto ret = GenMCDriver::handleStore( + oldValSetter, incPos(thread_id), address, size, EventDeps()); + + const auto *err = std::get_if(&ret); + if (err != nullptr) { + return MutexLockResult::fromError( + "TODO GENMC: format error once std::format change is merged"); + } + ERROR_ON(!std::holds_alternative(ret), + "Unsupported: mutex lock store returned unexpected result."); + } else { + GenMCDriver::handleAssume(incPos(thread_id), AssumeType::Spinloop); + } + + return MutexLockResult(is_lock_acquired); +} + +auto MiriGenMCShim::handleMutexTryLock(ThreadId thread_id, uint64_t address, uint64_t size) + -> MutexLockResult +{ + const auto addr = SAddr(address); + const auto aSize = ASize(size); + + auto &currPos = threadsAction[thread_id].event; + // Mutex starts out unlocked, so we always say the previous value is "unlocked". + auto oldValSetter = [this](SAddr addr) { this->handleOldVal(addr, GenmcScalar(0, 0)); }; + const auto ret0 = GenMCDriver::handleLoad( + oldValSetter, ++currPos, addr, aSize); + RETURN_IF_ERROR(ret0, MutexLockResult); + + const auto *retVal = std::get_if(&ret0); + if (nullptr == retVal) { + // if (std::holds_alternative(ret0)) { + // // TODO TODO GENMC: what did I mean with this comment? + // // TODO GENMC: is_read_opt == Mutex is acquired + // // None --> Someone else has lock, this thread will be rescheduled later + // // (currently block) 0 --> Got the lock 1 --> Someone else has lock, + // // this thread will not be rescheduled later (block on Miri side) + // return MutexLockResult(false); + // } + ERROR("Unimplemented: mutex trylock load returned unexpected result."); + } + + const bool is_lock_acquired = *retVal == SVal(0); + if (!is_lock_acquired) + return MutexLockResult(false); /* Lock already held. */ + + const auto ret1 = GenMCDriver::handleStore( + oldValSetter, ++currPos, addr, aSize); + RETURN_IF_ERROR(ret1, MutexLockResult); + + if (!std::holds_alternative(ret1)) { + ERROR("Unimplemented: mutex trylock store returned unexpected result."); + } + // No error or unexpected result: lock is acquired. + return MutexLockResult(true); +} + +auto MiriGenMCShim::handleMutexUnlock(ThreadId thread_id, uint64_t address, uint64_t size) + -> StoreResult +{ + const auto pos = incPos(thread_id); + const auto addr = SAddr(address); + const auto aSize = ASize(size); + + const auto oldValSetter = [this](SAddr addr) { + // TODO GENMC(HACK): is this the best way to do it? + this->handleOldVal(addr, GenmcScalar(0xDEADBEEF, 0)); + }; + const auto ret = GenMCDriver::handleStore( + oldValSetter, pos, MemOrdering::Release, addr, aSize, AType::Signed, SVal(0), + EventDeps()); + RETURN_IF_ERROR(ret, StoreResult); + + if (!std::holds_alternative(ret)) { + ERROR("Unimplemented: mutex unlock store returned unexpected result."); + } + + // TODO GENMC: Mixed-accesses (`false` should be fine, since we never want to update Miri's + // memory for mutexes anyway) + // const bool isCoMaxWrite = false; + const auto &g = getExec().getGraph(); + const bool isCoMaxWrite = g.co_max(addr)->getPos() == pos; + return StoreResult::ok(isCoMaxWrite); +} diff --git a/genmc-sys/src_cpp/ResultHandling.cpp b/genmc-sys/src_cpp/ResultHandling.cpp new file mode 100644 index 0000000000..f5f306bfe1 --- /dev/null +++ b/genmc-sys/src_cpp/ResultHandling.cpp @@ -0,0 +1,11 @@ +#include "ResultHandling.hpp" + +auto operator<<(llvm::raw_ostream &s, const GenmcScalar &v) -> llvm::raw_ostream & +{ + if (v.is_init) { + s << "{" << v.value << ", " << v.extra << "}"; + } else { + s << "{UNINITIALIZED}"; + } + return s; +} diff --git a/genmc-sys/src_cpp/ResultHandling.hpp b/genmc-sys/src_cpp/ResultHandling.hpp new file mode 100644 index 0000000000..146b7afd09 --- /dev/null +++ b/genmc-sys/src_cpp/ResultHandling.hpp @@ -0,0 +1,186 @@ +#ifndef GENMC_RESULT_HANDLING_HPP +#define GENMC_RESULT_HANDLING_HPP + +#include "Support/SVal.hpp" + +#include +#include +#include +#include + +/** Information about an error, formatted as a string to avoid having to share an error enum and + * printing functionality with the Rust side. */ +using ModelCheckerError = std::string; + +/** + * This type is the Miri equivalent to GenMC's `SVal`, but with the addition of a field to mark the + * value as uninitialized. + */ +struct GenmcScalar { + uint64_t value; + uint64_t extra; + bool is_init; + + explicit GenmcScalar() : value(0), extra(0), is_init(false) {} + explicit GenmcScalar(uint64_t value, uint64_t extra) + : value(value), extra(extra), is_init(true) + {} + explicit GenmcScalar(SVal val) : value(val.get()), extra(val.getExtra()), is_init(true) {} + + /** Convert to a GenMC SVal. Panics if the value is uninitialized. */ + auto toSVal() const -> SVal + { + ERROR_ON(!is_init, "attempt to convert uninitialized GenmcScalar to an SVal\n"); + return SVal(value, extra); + } + + bool operator==(const GenmcScalar &other) const + { + // Treat uninitialized values as being equal. + if (!is_init && !other.is_init) + return true; + + // An initialized scalar is never equal to an uninitialized one. + if (is_init != other.is_init) + return false; + + // Compare the actual values. + return value == other.value && extra == other.extra; + } + + friend auto operator<<(llvm::raw_ostream &rhs, const GenmcScalar &v) -> llvm::raw_ostream &; +}; + +struct LoadResult { + /// If there is an error, it will be stored in `error`, otherwise it is `None` + std::unique_ptr error; + /// Indicates whether a value was read or not. + bool has_value; + /// The value that was read. Should not be used if `has_value` is `false`. + GenmcScalar read_value; + +private: + explicit LoadResult(bool has_value, SVal value) + : has_value(true), read_value(GenmcScalar(value)), error(nullptr) + {} + explicit LoadResult(bool has_value) + : has_value(has_value), read_value(GenmcScalar()), error(nullptr) + {} + explicit LoadResult(std::string error) + : has_value(false), read_value(GenmcScalar()), + error(std::make_unique(error)) + {} + +public: + /**** Construction functions: ****/ + + static LoadResult noValue() { return LoadResult(false); } + static LoadResult fromValue(SVal value) { return LoadResult(true, value); } + static LoadResult fromError(std::string msg) { return LoadResult(msg); } + + /**** Operators: ****/ + + LoadResult &operator=(const LoadResult &rhs) + { + has_value = rhs.has_value; + read_value = rhs.read_value; + if (rhs.error.get() != nullptr) { + error = std::make_unique(*rhs.error); + } else { + error = nullptr; + } + return *this; + } +}; + +struct StoreResult { + /// if not `nullptr`, it contains an error encountered during the handling of the store. + std::unique_ptr error; + /// `true` if the write should also be reflected in Miri's memory representation. + bool isCoMaxWrite; + + static StoreResult ok(bool isCoMaxWrite) { return StoreResult{nullptr, isCoMaxWrite}; } + + static StoreResult fromError(std::string msg) + { + auto store_result = StoreResult{}; + store_result.error = std::make_unique(msg); + return store_result; + } +}; + +struct ReadModifyWriteResult { + /// if not `nullptr`, it contains an error encountered during the handling of the RMW. + std::unique_ptr error; + /// The value that was read by the RMW operation as the left operand. + GenmcScalar old_value; + /// The value that was produced by the RMW operation. + GenmcScalar new_value; + /// `true` if the write should also be reflected in Miri's memory representation. + bool isCoMaxWrite; + +private: + ReadModifyWriteResult(std::string msg) : error(std::make_unique(msg)) + { + old_value = GenmcScalar(); + new_value = GenmcScalar(); + } + +public: + ReadModifyWriteResult(SVal old_value, SVal new_value, bool isCoMaxWrite) + : old_value(GenmcScalar(old_value)), new_value(GenmcScalar(new_value)), + isCoMaxWrite(isCoMaxWrite), error(nullptr) + {} + + static ReadModifyWriteResult fromError(std::string msg) + { + return ReadModifyWriteResult(msg); + } +}; + +struct CompareExchangeResult { + /// if not `nullptr`, it contains an error encountered during the handling of the RMW op. + std::unique_ptr error; + /// The value that was read by the compare-exchange. + GenmcScalar old_value; + /// `true` if compare_exchange op was successful. + bool is_success; + /// `true` if the write should also be reflected in Miri's memory representation. + bool isCoMaxWrite; + + static CompareExchangeResult success(SVal old_value, bool isCoMaxWrite) + { + return CompareExchangeResult{nullptr, GenmcScalar(old_value), true, isCoMaxWrite}; + } + + static CompareExchangeResult failure(SVal old_value) + { + return CompareExchangeResult{nullptr, GenmcScalar(old_value), false, false}; + } + + static CompareExchangeResult fromError(std::string msg) + { + const auto dummy_scalar = GenmcScalar(); + return CompareExchangeResult{std::make_unique(msg), dummy_scalar, + false, false}; + } +}; + +struct MutexLockResult { + /// if not `nullptr`, it contains an error encountered during the handling of the mutex op. + std::unique_ptr error; + /// Indicate whether the lock was acquired by this thread. + bool is_lock_acquired; + + MutexLockResult(bool is_lock_acquired) : is_lock_acquired(is_lock_acquired), error(nullptr) + {} + + static auto fromError(std::string msg) -> MutexLockResult + { + auto res = MutexLockResult(false); + res.error = std::make_unique(msg); + return res; + } +}; + +#endif /* GENMC_RESULT_HANDLING_HPP */ diff --git a/src/concurrency/genmc/dummy.rs b/src/concurrency/genmc/dummy.rs index 4fa8b3aa19..2378dc7e46 100644 --- a/src/concurrency/genmc/dummy.rs +++ b/src/concurrency/genmc/dummy.rs @@ -203,7 +203,6 @@ impl GenmcCtx { _machine: &MiriMachine<'tcx>, _alloc_id: AllocId, _address: Size, - _size: Size, _kind: MemoryKind, ) -> InterpResult<'tcx> { unreachable!() diff --git a/src/concurrency/genmc/mod.rs b/src/concurrency/genmc/mod.rs index 0102a4355e..9c1d05835d 100644 --- a/src/concurrency/genmc/mod.rs +++ b/src/concurrency/genmc/mod.rs @@ -5,7 +5,7 @@ use std::time::Duration; use genmc_sys::cxx_extra::NonNullUniquePtr; use genmc_sys::{ ActionKind, GENMC_GLOBAL_ADDRESSES_MASK, GenmcScalar, MemOrdering, MiriGenMCShim, RMWBinOp, - StoreEventType, createGenmcHandle, + createGenmcHandle, }; use rustc_abi::{Align, Size}; use rustc_const_eval::interpret::{AllocId, InterpCx, InterpResult, interp_ok}; @@ -517,48 +517,37 @@ impl GenmcCtx { alignment: Align, memory_kind: MemoryKind, ) -> InterpResult<'tcx, u64> { + if self.allow_data_races.get() { + unreachable!(); // FIXME(genmc): can this happen and if yes, how should this be handled? + } let machine = &ecx.machine; - let chosen_address = if memory_kind == MiriMemoryKind::Global.into() { + if memory_kind == MiriMemoryKind::Global.into() { info!("GenMC: global memory allocation: {alloc_id:?}"); - ecx.get_global_allocation_address(&self.global_allocations, alloc_id)? - } else { - // TODO GENMC: Does GenMC need to know about the kind of Memory? - - // eprintln!( - // "handle_alloc ({memory_kind:?}): Custom backtrace: {}", - // std::backtrace::Backtrace::force_capture() - // ); - // TODO GENMC: should we put this before the special handling for globals? - if self.allow_data_races.get() { - unreachable!(); // FIXME(genmc): can this happen and if yes, how should this be handled? - } - let thread_infos = self.thread_id_manager.borrow(); - let curr_thread = machine.threads.active_thread(); - let genmc_tid = thread_infos.get_genmc_tid(curr_thread); - // GenMC doesn't support ZSTs, so we set the minimum size to 1 byte - let genmc_size = size.bytes().max(1); - info!( - "GenMC: handle_alloc (thread: {curr_thread:?} ({genmc_tid:?}), size: {}, alignment: {alignment:?}, memory_kind: {memory_kind:?})", - size.bytes() - ); + return ecx.get_global_allocation_address(&self.global_allocations, alloc_id); + } + let thread_infos = self.thread_id_manager.borrow(); + let curr_thread = machine.threads.active_thread(); + let genmc_tid = thread_infos.get_genmc_tid(curr_thread); + // GenMC doesn't support ZSTs, so we set the minimum size to 1 byte + let genmc_size = size.bytes().max(1); + info!( + "GenMC: handle_alloc (thread: {curr_thread:?} ({genmc_tid:?}), size: {}, alignment: {alignment:?}, memory_kind: {memory_kind:?})", + size.bytes() + ); - let alignment = alignment.bytes(); + let alignment = alignment.bytes(); - let mut mc = self.handle.borrow_mut(); - let pinned_mc = mc.as_mut(); - let chosen_address = pinned_mc.handleMalloc(genmc_tid, genmc_size, alignment); + let mut mc = self.handle.borrow_mut(); + let pinned_mc = mc.as_mut(); + let chosen_address = pinned_mc.handleMalloc(genmc_tid, genmc_size, alignment); - // Non-global addresses should not be in the global address space or null. - assert_ne!(0, chosen_address, "GenMC malloc returned nullptr."); - assert_eq!(0, chosen_address & GENMC_GLOBAL_ADDRESSES_MASK); - chosen_address - }; + // Non-global addresses should not be in the global address space or null. + assert_ne!(0, chosen_address, "GenMC malloc returned nullptr."); + assert_eq!(0, chosen_address & GENMC_GLOBAL_ADDRESSES_MASK); // Sanity check the address alignment: - assert_eq!( - 0, - chosen_address % alignment.bytes(), - "GenMC returned address {chosen_address} == {chosen_address:#x} with lower alignment than requested ({:})!", - alignment.bytes() + assert!( + chosen_address.is_multiple_of(alignment), + "GenMC returned address {chosen_address:#x} with lower alignment than requested ({alignment}).", ); interp_ok(chosen_address) @@ -569,7 +558,6 @@ impl GenmcCtx { machine: &MiriMachine<'tcx>, alloc_id: AllocId, address: Size, - size: Size, kind: MemoryKind, ) -> InterpResult<'tcx> { assert_ne!( @@ -585,12 +573,10 @@ impl GenmcCtx { let genmc_tid = thread_infos.get_genmc_tid(curr_thread); let genmc_address = address.bytes(); - // GenMC doesn't support ZSTs, so we set the minimum size to 1 byte - let genmc_size = size.bytes().max(1); let mut mc = self.handle.borrow_mut(); let pinned_mc = mc.as_mut(); - pinned_mc.handleFree(genmc_tid, genmc_address, genmc_size); + pinned_mc.handleFree(genmc_tid, genmc_address); // TODO GENMC (ERROR HANDLING): can this ever fail? interp_ok(()) @@ -730,8 +716,9 @@ impl GenmcCtx { throw_ub_format!("{}", error.to_string_lossy()); // TODO GENMC: proper error handling: find correct error here } - if load_result.is_read_opt { - todo!(); + if !load_result.has_value { + // FIXME(GenMC): Implementing certain GenMC optimizations will lead to this. + unimplemented!("GenMC: load returned no value."); } info!("GenMC: load returned value: {:?}", load_result.read_value); @@ -776,7 +763,6 @@ impl GenmcCtx { genmc_value, genmc_old_value, memory_ordering, - StoreEventType::Normal, ); if let Some(error) = store_result.error.as_ref() { @@ -832,6 +818,28 @@ impl GenmcCtx { throw_ub_format!("{}", error.to_string_lossy()); // TODO GENMC: proper error handling: find correct error here } + // Check that both RMW arguments have sane provenance. + match ( + genmc_rmw_op, + rmw_result.old_value.has_provenance(), + genmc_rhs_scalar.has_provenance(), + ) { + // compare-exchange should not swap a pointer and an integer. + // FIXME(GenMC): is this correct? + (RMWBinOp::Xchg, left, right) => + if left != right { + throw_ub_format!( + "atomic compare-exchange arguments should either both have pointer provenance ({left} != {right}). Both arguments should be pointers, or both integers." + ); + }, + // All other read-modify-write operations should never have a right-side argument that's a pointer. + (_, _, true) => + throw_ub_format!( + "atomic read-modify-write operation right argument has pointer provenance." + ), + _ => {} + } + let old_value_scalar = genmc_scalar_to_scalar(ecx, rmw_result.old_value, size)?; let new_value_scalar = if rmw_result.isCoMaxWrite { diff --git a/src/machine.rs b/src/machine.rs index 20fb33e82a..43869b8351 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -1287,10 +1287,6 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { size: Size, align: Align, ) -> InterpResult<'tcx, Self::AllocExtra> { - info!( - "GenMC: TODO GENMC: init_local_allocation: id: {id:?}, kind: {kind:?}, size: {size:?}, align: {align:?}" - ); - assert!(kind != MiriMemoryKind::Global.into()); MiriMachine::init_allocation(ecx, id, kind, size, align) } @@ -1382,10 +1378,6 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { alloc: &'b Allocation, ) -> InterpResult<'tcx, Cow<'b, Allocation>> { - info!( - "GenMC: adjust_global_allocation (TODO GENMC): id: {id:?} ==> Maybe tell GenMC about initial value here?" - ); - let alloc = alloc.adjust_from_tcx( &ecx.tcx, |bytes, align| ecx.get_global_alloc_bytes(id, bytes, align), @@ -1483,7 +1475,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { match &machine.data_race { GlobalDataRaceHandler::None => {} GlobalDataRaceHandler::Genmc(genmc_ctx) => - genmc_ctx.handle_dealloc(machine, alloc_id, ptr.addr(), size, kind)?, + genmc_ctx.handle_dealloc(machine, alloc_id, ptr.addr(), kind)?, GlobalDataRaceHandler::Vclocks(_global_state) => { let data_race = alloc_extra.data_race.as_vclocks_mut().unwrap(); data_race.write( @@ -1694,7 +1686,6 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { frame: &Frame<'tcx, Provenance, FrameExtra<'tcx>>, local: mir::Local, ) -> InterpResult<'tcx> { - // TODO GENMC: does GenMC care about local reads/writes? if let Some(data_race) = &frame.extra.data_race { data_race.local_read(local, &ecx.machine); } @@ -1706,7 +1697,6 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { local: mir::Local, storage_live: bool, ) -> InterpResult<'tcx> { - // TODO GENMC: does GenMC care about local reads/writes? if let Some(data_race) = &ecx.frame().extra.data_race { data_race.local_write(local, storage_live, &ecx.machine); } @@ -1736,7 +1726,6 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { machine, ); } - // TODO GENMC: how to handle this (if at all)? interp_ok(()) } diff --git a/tests/genmc/_disabled/mutex_poison.rs b/tests/genmc/_disabled/mutex_poison.rs new file mode 100644 index 0000000000..64fe5c6db6 --- /dev/null +++ b/tests/genmc/_disabled/mutex_poison.rs @@ -0,0 +1,76 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +#![no_main] +#![feature(abort_unwind)] + +// #[path = "../../../utils/genmc.rs"] +// mod genmc; + +// use std::ffi::c_void; +use std::sync::{LockResult, Mutex}; + +// use crate::genmc::*; + +static LOCK: Mutex = Mutex::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + std::panic::abort_unwind(main_); + 0 +} + +fn main_() { + // let _ids = unsafe { create_pthreads_no_params( [thread_1, thread_2]) }; + // unsafe { join_pthreads(ids) }; + + let handle1 = std::thread::spawn(|| { + // let _err = std::panic::catch_unwind(|| { + let mut guard = LOCK.lock().unwrap(); + *guard = 0xDEADBEEF; + panic!(); + // }); + // drop(_err); + }); + + let handle2 = std::thread::spawn(|| { + // std::thread::sleep(std::time::Duration::from_millis(10)); // Let thread1 run first + match LOCK.lock() { + LockResult::Ok(mut value) => *value = 1234, + LockResult::Err(mut poison) => { + **poison.get_mut() = 42; + } + } + }); + + handle1.join().unwrap(); + handle2.join().unwrap(); + + // // Depending on the thread interleaving, the mutex might be poisoned. + // match LOCK.lock() { + // LockResult::Ok(value) => assert!(*value == 1234 || *value == 42), + // LockResult::Err(_poison) => {} + // } +} + +// // extern "C" fn thread_1(_: *mut c_void) -> *mut c_void { +// fn thread_1() { +// let _err = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { +// let mut guard = LOCK.lock().unwrap(); +// // Pretend whatever causes the crash fills the mutex with garbage values. +// *guard = 0xDEADBEEF; +// panic!(); +// })); +// Box::leak(Box::new(_err)); +// std::ptr::null_mut() +// } + +// // extern "C" fn thread_2(_: *mut c_void) -> *mut c_void { +// fn thread_2() { +// match LOCK.lock() { +// LockResult::Ok(mut value) => *value = 1234, +// LockResult::Err(mut poison) => { +// **poison.get_mut() = 42; +// } +// } +// std::ptr::null_mut() +// } diff --git a/tests/genmc/par-len.rs b/tests/genmc/par-len.rs new file mode 100644 index 0000000000..7022756b57 --- /dev/null +++ b/tests/genmc/par-len.rs @@ -0,0 +1,48 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +#![no_main] + +#[path = "../utils/genmc.rs"] +mod genmc; + +use std::ffi::c_void; +use std::sync::atomic::AtomicI32; +use std::sync::atomic::Ordering::SeqCst; + +use crate::genmc::*; + +const T: usize = 2; +const N: usize = 2000; + +static V: AtomicI32 = AtomicI32::new(0); +static LOCAL: [AtomicI32; T] = [const { AtomicI32::new(0) }; T]; + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + for i in 0..T { + let arg = i as *mut c_void; + let _id = unsafe { spawn_pthread(thread_func, arg) }; + } + + // let arg = 0usize as *mut c_void; + // let _id0 = unsafe { spawn_pthread(thread_func, arg) }; + // let arg = 1usize as *mut c_void; + // let _id1 = unsafe { spawn_pthread(thread_func, arg) }; + // unsafe { join_pthreads([_id0, _id1])}; + + 0 +} + +extern "C" fn thread_func(value: *mut c_void) -> *mut c_void { + let tid = value as usize; + + for i in 0..N { + LOCAL[tid].store(i.try_into().unwrap(), SeqCst); + let _ = LOCAL[tid].load(SeqCst); + } + + V.store(tid.try_into().unwrap(), SeqCst); + let _ = V.load(SeqCst); + + std::ptr::null_mut() +} diff --git a/tests/genmc/pass/atomic-primitives/mutex_simple.rs b/tests/genmc/pass/atomic-primitives/mutex_simple.rs index aed8e92c47..04030b90c2 100644 --- a/tests/genmc/pass/atomic-primitives/mutex_simple.rs +++ b/tests/genmc/pass/atomic-primitives/mutex_simple.rs @@ -12,7 +12,7 @@ use std::sync::Mutex; use crate::genmc::*; -#[cfg(reps1)] +#[cfg(not(reps2))] const REPS: u64 = 1; #[cfg(reps2)] const REPS: u64 = 2; diff --git a/tests/genmc/pass/data-structures/ms_queue_dynamic.rs b/tests/genmc/pass/data-structures/ms_queue_dynamic.rs index efd0df3a9f..dd02df5936 100644 --- a/tests/genmc/pass/data-structures/ms_queue_dynamic.rs +++ b/tests/genmc/pass/data-structures/ms_queue_dynamic.rs @@ -8,8 +8,8 @@ use std::alloc::{Layout, alloc, dealloc}; use std::ffi::c_void; +use std::sync::atomic::AtomicPtr; use std::sync::atomic::Ordering::*; -use std::sync::atomic::{AtomicPtr}; use libc::{self, pthread_attr_t, pthread_t}; From 503597899cbf7ff40d1d6375d7d5eb9824cf8bed Mon Sep 17 00:00:00 2001 From: Patrick-6 Date: Mon, 18 Aug 2025 16:51:50 +0200 Subject: [PATCH 23/41] Apply suggestions from code review, cleanup, improve error reporting, improve assertions. --- genmc-sys/src/lib.rs | 12 ++ genmc-sys/src_cpp/MiriInterface.hpp | 39 +++++- src/concurrency/genmc/global_allocations.rs | 2 + src/concurrency/genmc/helper.rs | 72 +++++----- src/concurrency/genmc/mapping.rs | 7 +- src/concurrency/genmc/miri_genmc.rs | 62 +++++---- src/concurrency/genmc/mod.rs | 137 +++++++++++++------- src/concurrency/thread.rs | 10 +- 8 files changed, 221 insertions(+), 120 deletions(-) diff --git a/genmc-sys/src/lib.rs b/genmc-sys/src/lib.rs index 4d6eb645fa..f8bb5ea649 100644 --- a/genmc-sys/src/lib.rs +++ b/genmc-sys/src/lib.rs @@ -325,8 +325,20 @@ mod ffi { curr_thread_next_instr_kind: ActionKind, ) -> i64; + /**** Result querying functionality. ****/ + // NOTE: We don't want to share the `VerificationResult` type with the Rust side, since it + // is very large, uses features that CXX.rs doesn't support and may change as GenMC changes. + // Instead, we only use the result on the C++ side, and only expose these getter function to + // the Rust side. + + /// Get the number of blocked executions encountered by GenMC (cast into a fixed with integer) fn getBlockedExecutionCount(self: &MiriGenMCShim) -> u64; + /// Get the number of executions explored by GenMC (cast into a fixed with integer) fn getExploredExecutionCount(self: &MiriGenMCShim) -> u64; + /// Get all messages that GenMC produced (errors, warnings). + fn getResultMessage(self: &MiriGenMCShim) -> UniquePtr; + /// If an error occurred, return a string describing the error, otherwise, return `nullptr`. + fn getErrorString(self: &MiriGenMCShim) -> UniquePtr; /// Check whether there are more executions to explore. /// If there are more executions, this method prepares for the next execution and returns `true`. diff --git a/genmc-sys/src_cpp/MiriInterface.hpp b/genmc-sys/src_cpp/MiriInterface.hpp index 0f38b13b0d..808fb852ea 100644 --- a/genmc-sys/src_cpp/MiriInterface.hpp +++ b/genmc-sys/src_cpp/MiriInterface.hpp @@ -113,18 +113,45 @@ struct MiriGenMCShim : private GenMCDriver { return --threadsAction[tid].event; } - /**** Printing and result querying ****/ + /**** Result querying functionality. ****/ + // NOTE: We don't want to share the `VerificationResult` type with the Rust side, since it + // is very large, uses features that CXX.rs doesn't support and may change as GenMC changes. + // Instead, we only use the result on the C++ side, and only expose these getter function to + // the Rust side. + + /// Get the number of blocked executions encountered by GenMC (cast into a fixed with integer) auto getBlockedExecutionCount() const -> uint64_t { return static_cast(getResult().exploredBlocked); } + /// Get the number of executions explored by GenMC (cast into a fixed with integer) auto getExploredExecutionCount() const -> uint64_t { return static_cast(getResult().explored); } + /// Get all messages that GenMC produced (errors, warnings). + auto getResultMessage() const -> std::unique_ptr + { + return std::make_unique(getResult().message); + } + + /// If an error occurred, return a string describing the error, otherwise, return `nullptr`. + auto getErrorString() const -> std::unique_ptr + { + const auto &result = GenMCDriver::getResult(); + if (result.status.has_value()) { + // FIXME(genmc): format the error once std::format changes are merged into + // GenMC. + return std::make_unique("FIXME(genmc): show error string"); + } + return nullptr; + } + + /**** Printing and estimation mode functionality. ****/ + void printGraph() { GenMCDriver::debugPrintGraph(); } void printEstimationResults(const double elapsed_time_sec) const @@ -200,6 +227,11 @@ struct MiriGenMCShim : private GenMCDriver { // or WriteLabel ==> Update its value in place (only if non-atomic) } + /** + * Helper function for loads that need to reset the event counter when no value is returned. + * Same syntax as `GenMCDriver::handleLoad`, but this takes a thread id instead of an Event. + * Automatically calls `incPos` and `decPos` where needed for the given thread. + */ template HandleResult handleLoadResetIfNone(std::function oldValSetter, ThreadId tid, Ts &&...params) @@ -228,6 +260,11 @@ struct MiriGenMCShim : private GenMCDriver { */ std::vector threadsAction; + /** + * Map of already used annotation ids (e.g., for mutexes). + * FIXME(GenMC): For multithreading support, this map and the counter below need to be + * synchronized across threads. + */ std::unordered_map annotation_id{}; ModuleID::ID annotation_id_counter = 0; }; diff --git a/src/concurrency/genmc/global_allocations.rs b/src/concurrency/genmc/global_allocations.rs index f557aa976d..74766f9815 100644 --- a/src/concurrency/genmc/global_allocations.rs +++ b/src/concurrency/genmc/global_allocations.rs @@ -86,6 +86,8 @@ impl GlobalStateInner { impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { + /// Allocate a new address for the given alloc id, or return the cached address. + /// Each alloc id is assigned one unique allocation which will not change if this function is called again with the same alloc id. fn get_global_allocation_address( &self, global_allocation_handler: &GlobalAllocationHandler, diff --git a/src/concurrency/genmc/helper.rs b/src/concurrency/genmc/helper.rs index f5b6f26b3e..7e3ce1cf30 100644 --- a/src/concurrency/genmc/helper.rs +++ b/src/concurrency/genmc/helper.rs @@ -10,6 +10,8 @@ use crate::{ BorTag, MiriInterpCx, MiriMachine, Pointer, Provenance, Scalar, ThreadId, throw_unsup_format, }; +/// This function is used to split up a large memory access into aligned, non-overlapping chunks of a limited size. +/// Returns an iterator over the chunks, yielding `(base address, size)` of each chunk, ordered by address. pub fn split_access(address: Size, size: Size) -> impl Iterator { /// Maximum size memory access in bytes that GenMC supports. const MAX_SIZE: u64 = 8; @@ -50,36 +52,20 @@ pub fn split_access(address: Size, size: Size) -> impl Iterator( - ecx: &MiriInterpCx<'tcx>, - scalar: Scalar, -) -> InterpResult<'tcx, GenmcScalar> { - if matches!(scalar, Scalar::Ptr(..)) { - throw_unsup_format!("Right hand side of atomic operation cannot be a pointer"); - } - scalar_to_genmc_scalar(ecx, scalar) -} - -pub fn option_scalar_to_genmc_scalar<'tcx>( - ecx: &MiriInterpCx<'tcx>, - maybe_scalar: Option, -) -> InterpResult<'tcx, GenmcScalar> { - if let Some(scalar) = maybe_scalar { - scalar_to_genmc_scalar(ecx, scalar) - } else { - interp_ok(GenmcScalar::UNINIT) - } -} - +/// Inverse function to `scalar_to_genmc_scalar`. +/// +/// Convert a Miri `Scalar` to a `GenmcScalar`. +/// To be able to restore pointer provenance from a `GenmcScalar`, the base address of the allocation of the pointer is also stored in the `GenmcScalar`. +/// We cannot use the `AllocId` instead of the base address, since Miri has no control over the `AllocId`, and it may change across executions. +/// Pointers with `Wildcard` provenance are not supported. pub fn scalar_to_genmc_scalar<'tcx>( ecx: &MiriInterpCx<'tcx>, scalar: Scalar, ) -> InterpResult<'tcx, GenmcScalar> { interp_ok(match scalar { rustc_const_eval::interpret::Scalar::Int(scalar_int) => { - // TODO GENMC: u128 support - let value: u64 = scalar_int.to_uint(scalar_int.size()).try_into().unwrap(); // TODO GENMC: doesn't work for size != 8 + // FIXME(genmc): Add u128 support once GenMC supports it. + let value: u64 = scalar_int.to_uint(scalar_int.size()).try_into().unwrap(); GenmcScalar { value, extra: 0, is_init: true } } rustc_const_eval::interpret::Scalar::Ptr(pointer, size) => { @@ -96,14 +82,15 @@ pub fn scalar_to_genmc_scalar<'tcx>( }) } +/// Inverse function to `scalar_to_genmc_scalar`. +/// +/// Convert a `GenmcScalar` back into a Miri `Scalar`. +/// For pointers, attempt to convert the stored base address of their allocation back into an `AllocId`. pub fn genmc_scalar_to_scalar<'tcx>( ecx: &MiriInterpCx<'tcx>, scalar: GenmcScalar, size: Size, ) -> InterpResult<'tcx, Scalar> { - // TODO GENMC: proper handling of large integers - // TODO GENMC: proper handling of pointers (currently assumes all integers) - if scalar.extra != 0 { // We have a pointer! @@ -115,7 +102,7 @@ pub fn genmc_scalar_to_scalar<'tcx>( let Some(alloc_id) = ecx.alloc_id_from_addr(base_addr, alloc_size, only_exposed_allocations) else { - // TODO GENMC: what is the correct error in this case? + // TODO GENMC: what is the correct error in this case? Maybe give the pointer wildcard provenance instead? throw_unsup_format!( "Cannot get allocation id of pointer received from GenMC (base address: 0x{base_addr:x}, pointer address: 0x{:x})", addr.bytes() @@ -140,29 +127,37 @@ pub fn genmc_scalar_to_scalar<'tcx>( interp_ok(Scalar::Int(value_scalar_int)) } -pub fn is_terminator_atomic<'tcx>( +/// Check if a MIR terminator could be an atomic load operation. +/// Currently this check is very conservative; all atomics are seen as possibly being loads. +/// NOTE: This function panics if called with a thread that is not currently the active one. +pub fn is_terminator_atomic_load<'tcx>( ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, terminator: &Terminator<'tcx>, thread_id: ThreadId, ) -> InterpResult<'tcx, bool> { + assert_eq!( + thread_id, + ecx.machine.threads.active_thread(), + "Can only call this function on the active thread." + ); match &terminator.kind { // All atomics are modeled as function calls to intrinsic functions. // The one exception is thread joining, but those are also calls. TerminatorKind::Call { func, .. } | TerminatorKind::TailCall { func, .. } => { - let frame = ecx.machine.threads.get_thread_stack(thread_id).last().unwrap(); + let frame = ecx.machine.threads.active_thread_stack().last().unwrap(); let func_ty = func.ty(&frame.body().local_decls, *ecx.tcx); info!("GenMC: terminator is a call with operand: {func:?}, ty of operand: {func_ty:?}"); - is_function_atomic(ecx, func_ty) + has_function_atomic_load_semantics(ecx, func_ty) } _ => interp_ok(false), } } -fn is_function_atomic<'tcx>( +/// Check if a call or tail-call could have atomic load semantics. +fn has_function_atomic_load_semantics<'tcx>( ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, func_ty: Ty<'tcx>, - // func: &Operand<'tcx>, ) -> InterpResult<'tcx, bool> { let callee_def_id = match func_ty.kind() { ty::FnDef(def_id, _args) => def_id, @@ -175,17 +170,12 @@ fn is_function_atomic<'tcx>( } let Some(intrinsic_def) = ecx.tcx.intrinsic(callee_def_id) else { - // TODO GENMC: Make this work for other platforms? + // FIXME(genmc): Make this work for other platforms. let item_name = ecx.tcx.item_name(*callee_def_id); - info!("GenMC: function DefId: {callee_def_id:?}, item name: {item_name:?}"); - if matches!(item_name.as_str(), "pthread_join" | "WaitForSingleObject") { - info!("GenMC: found a 'join' terminator: '{}'", item_name.as_str(),); - return interp_ok(true); - } - return interp_ok(false); + return interp_ok(matches!(item_name.as_str(), "pthread_join" | "WaitForSingleObject")); }; let intrinsice_name = intrinsic_def.name.as_str(); info!("GenMC: intrinsic name: '{intrinsice_name}'"); - // TODO GENMC(ENHANCEMENT): make this more precise (only loads). How can we make this maintainable? + // FIXME(genmc): make this more precise (only loads). How can we make this maintainable? interp_ok(intrinsice_name.starts_with("atomic_")) } diff --git a/src/concurrency/genmc/mapping.rs b/src/concurrency/genmc/mapping.rs index 5a64eebef9..714227488e 100644 --- a/src/concurrency/genmc/mapping.rs +++ b/src/concurrency/genmc/mapping.rs @@ -2,6 +2,8 @@ use genmc_sys::{MemOrdering, RMWBinOp}; use crate::{AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd}; +// This file contains functionality to convert between Miri enums and their GenMC counterparts. + impl AtomicReadOrd { pub(super) fn convert(self) -> MemOrdering { match self { @@ -59,15 +61,18 @@ impl AtomicRwOrd { } } +/// Convert a possibly signed Miri min/max operation to its GenMC counterpart. pub(super) fn min_max_to_genmc_rmw_op(min: bool, is_signed: bool) -> RMWBinOp { + // FIXME(genmc): is there a use for FMin/FMax? GenMC has (Min, UMin, FMin) match (min, is_signed) { - (true, true) => RMWBinOp::Min, // TODO GENMC: is there a use for FMin? (Min, UMin, FMin) + (true, true) => RMWBinOp::Min, (false, true) => RMWBinOp::Max, (true, false) => RMWBinOp::UMin, (false, false) => RMWBinOp::UMax, } } +/// Convert a possibly negated binary operation to its GenMC counterpart. pub(super) fn to_genmc_rmw_op(bin_op: rustc_middle::mir::BinOp, negate: bool) -> RMWBinOp { match bin_op { rustc_middle::mir::BinOp::Add => RMWBinOp::Add, diff --git a/src/concurrency/genmc/miri_genmc.rs b/src/concurrency/genmc/miri_genmc.rs index 3928e7dc2f..eb769c5fd1 100644 --- a/src/concurrency/genmc/miri_genmc.rs +++ b/src/concurrency/genmc/miri_genmc.rs @@ -19,6 +19,13 @@ impl Display for Mode { } } +/// Do a complete run of the program in GenMC mode. +/// This will call `eval_entry` multiple times, until either: +/// - An error is detected +/// - All possible executions are explored (in `Mode::Verification`) +/// - Enough executions are explored to estimated the total number of executions (in `Mode::Estimation`) +/// +/// Returns `None` is an error is detected, or `Some(return_value)` with the return value of the last run of the program. pub fn run_genmc_mode( config: &MiriConfig, genmc_config: &GenmcConfig, @@ -31,41 +38,50 @@ pub fn run_genmc_mode( for rep in 0u64.. { tracing::info!("Miri-GenMC loop {}", rep + 1); - let result = eval_entry(genmc_ctx.clone()); + // Execute the program until completion or an error happens: + let result = eval_entry(genmc_ctx.clone()); + // We always print the graph when requested, even on errors. + // This may not be needed if Miri makes use of GenMC's error message at some point, since it already includes the graph. + // FIXME(genmc): Currently GenMC is missing some info from Miri to be able to fully print the execution graph. if genmc_config.print_exec_graphs() { genmc_ctx.print_genmc_graph(); } - - // TODO GENMC (ERROR REPORTING): we currently do this here, so we can still print the GenMC graph above + // Return if there was an error, or get the return code of the program. let return_code = result?; - let is_exploration_done = genmc_ctx.is_exploration_done(); - - tracing::info!( - "(GenMC Mode) Execution done (return code: {return_code}), is_exploration_done: {is_exploration_done}", - ); + // Some errors are not returned immediately during execution, so check for them here: + if let Some(error) = genmc_ctx.try_get_error() { + eprintln!("(GenMC) Error detected: {error}"); + eprintln!(); + // FIXME(genmc): we may want to print this message every time we finish the verification/find an error. + eprintln!("{}", genmc_ctx.get_result_message()); + return None; + } - if is_exploration_done { - eprintln!("(GenMC) {mode} complete. No errors were detected.",); + // Check if we've explored enough executions: + if !genmc_ctx.is_exploration_done() { + continue; + } - if mode == Mode::Estimation && return_code == 0 { - let elapsed_time = Instant::now().duration_since(time_start); - genmc_ctx.print_estimation_result(elapsed_time); - return Some(0); - } + eprintln!("(GenMC) {mode} complete. No errors were detected.",); - let explored_execution_count = genmc_ctx.get_explored_execution_count(); - let blocked_execution_count = genmc_ctx.get_blocked_execution_count(); + if mode == Mode::Estimation && return_code == 0 { + let elapsed_time = Instant::now().duration_since(time_start); + genmc_ctx.print_estimation_result(elapsed_time); + return Some(0); + } - eprintln!("Number of complete executions explored: {explored_execution_count}"); - if blocked_execution_count > 0 { - eprintln!("Number of blocked executions seen: {blocked_execution_count}"); - } + let explored_execution_count = genmc_ctx.get_explored_execution_count(); + let blocked_execution_count = genmc_ctx.get_blocked_execution_count(); - // TODO GENMC: what is an appropriate return code? (since there are possibly many) - return Some(return_code); + eprintln!("Number of complete executions explored: {explored_execution_count}"); + if blocked_execution_count > 0 { + eprintln!("Number of blocked executions seen: {blocked_execution_count}"); } + + // TODO GENMC: what is an appropriate return code? (since there are possibly many) + return Some(return_code); } tracing::error!("GenMC mode did not finish in 2^64 iterations!"); None diff --git a/src/concurrency/genmc/mod.rs b/src/concurrency/genmc/mod.rs index 9c1d05835d..5f38c3caf6 100644 --- a/src/concurrency/genmc/mod.rs +++ b/src/concurrency/genmc/mod.rs @@ -13,13 +13,10 @@ use rustc_middle::{mir, throw_machine_stop, throw_ub_format, throw_unsup_format} use tracing::info; use self::global_allocations::{EvalContextExt as _, GlobalAllocationHandler}; -use self::helper::{ - genmc_scalar_to_scalar, option_scalar_to_genmc_scalar, rhs_scalar_to_genmc_scalar, - scalar_to_genmc_scalar, -}; +use self::helper::{genmc_scalar_to_scalar, scalar_to_genmc_scalar}; use self::mapping::{min_max_to_genmc_rmw_op, to_genmc_rmw_op}; use self::thread_id_map::ThreadIdMap; -use crate::concurrency::genmc::helper::{is_terminator_atomic, split_access}; +use crate::concurrency::genmc::helper::{is_terminator_atomic_load, split_access}; use crate::concurrency::genmc::warnings::WarningsCache; use crate::concurrency::thread::{EvalContextExt as _, ThreadState}; use crate::{ @@ -107,6 +104,24 @@ impl GenmcCtx { mc.as_ref().getExploredExecutionCount() } + /// Check if GenMC encountered an error that wasn't immediately returned during execution. + /// Returns a string representation of the error if one occurred. + pub fn try_get_error(&self) -> Option { + let mc = self.handle.borrow(); + mc.as_ref().getErrorString().as_ref().map(|error| error.to_string_lossy().to_string()) + } + + /// Check if GenMC encountered an error that wasn't immediately returned during execution. + /// Returns a string representation of the error if one occurred. + pub fn get_result_message(&self) -> String { + let mc = self.handle.borrow(); + mc.as_ref() + .getResultMessage() + .as_ref() + .map(|error| error.to_string_lossy().to_string()) + .expect("there should always be a message") + } + pub fn print_genmc_graph(&self) { let mut mc = self.handle.borrow_mut(); mc.as_mut().printGraph(); @@ -189,9 +204,13 @@ impl GenmcCtx { // The value that we would get, if we were to do a non-atomic load here. old_val: Option, ) -> InterpResult<'tcx, Scalar> { - assert!(!self.allow_data_races.get()); // TODO GENMC: handle this properly + assert!(!self.allow_data_races.get(), "atomic load with data race checking disabled."); let ordering = ordering.convert(); - let genmc_old_value = option_scalar_to_genmc_scalar(ecx, old_val)?; + let genmc_old_value = if let Some(scalar) = old_val { + scalar_to_genmc_scalar(ecx, scalar)? + } else { + GenmcScalar::UNINIT + }; let read_value = self.atomic_load_impl(&ecx.machine, address, size, ordering, genmc_old_value)?; genmc_scalar_to_scalar(ecx, read_value, size) @@ -207,10 +226,14 @@ impl GenmcCtx { old_value: Option, ordering: AtomicWriteOrd, ) -> InterpResult<'tcx, bool> { - assert!(!self.allow_data_races.get()); // TODO GENMC: handle this properly + assert!(!self.allow_data_races.get(), "atomic store with data race checking disabled."); let ordering = ordering.convert(); let genmc_value = scalar_to_genmc_scalar(ecx, value)?; - let genmc_old_value = option_scalar_to_genmc_scalar(ecx, old_value)?; + let genmc_old_value = if let Some(scalar) = old_value { + scalar_to_genmc_scalar(ecx, scalar)? + } else { + GenmcScalar::UNINIT + }; self.atomic_store_impl(&ecx.machine, address, size, genmc_value, genmc_old_value, ordering) } @@ -219,7 +242,7 @@ impl GenmcCtx { machine: &MiriMachine<'tcx>, ordering: AtomicFenceOrd, ) -> InterpResult<'tcx> { - assert!(!self.allow_data_races.get()); // TODO GENMC: handle this properly + assert!(!self.allow_data_races.get(), "atomic fence with data race checking disabled."); let ordering = ordering.convert(); @@ -249,13 +272,22 @@ impl GenmcCtx { // The value that we would get, if we were to do a non-atomic load here. old_value: Scalar, ) -> InterpResult<'tcx, (Scalar, Option)> { - assert!(!self.allow_data_races.get()); // TODO GENMC: handle this properly + assert!( + !self.allow_data_races.get(), + "atomic read-modify-write operation with data race checking disabled." + ); let (load_ordering, store_ordering) = ordering.to_genmc_memory_orderings(); let genmc_rmw_op = to_genmc_rmw_op(rmw_op, not); tracing::info!( "GenMC: atomic_rmw_op (op: {rmw_op:?}, not: {not}, genmc_rmw_op: {genmc_rmw_op:?}): rhs value: {rhs_scalar:?}, orderings ({load_ordering:?}, {store_ordering:?})" ); - let genmc_rhs_scalar = rhs_scalar_to_genmc_scalar(ecx, rhs_scalar)?; + + if matches!(rhs_scalar, Scalar::Ptr(..)) { + throw_unsup_format!( + "Right hand side of atomic read-modify-write operation cannot be a pointer" + ); + } + let genmc_rhs_scalar = scalar_to_genmc_scalar(ecx, rhs_scalar)?; let genmc_old_value = scalar_to_genmc_scalar(ecx, old_value)?; self.atomic_rmw_op_impl( ecx, @@ -284,13 +316,18 @@ impl GenmcCtx { // The value that we would get, if we were to do a non-atomic load here. old_value: Scalar, ) -> InterpResult<'tcx, (Scalar, Option)> { - assert!(!self.allow_data_races.get()); // TODO GENMC: handle this properly + assert!( + !self.allow_data_races.get(), + "atomic min/max operation with data race checking disabled." + ); let (load_ordering, store_ordering) = ordering.to_genmc_memory_orderings(); let genmc_rmw_op = min_max_to_genmc_rmw_op(min, is_signed); tracing::info!( "GenMC: atomic_min_max_op (min: {min}, signed: {is_signed}, genmc_rmw_op: {genmc_rmw_op:?}): rhs value: {rhs_scalar:?}, orderings ({load_ordering:?}, {store_ordering:?})" ); - let genmc_rhs_scalar = rhs_scalar_to_genmc_scalar(ecx, rhs_scalar)?; + + // FIXME(genmc): can `rhs_scalar` be a pointer? Should this be allowed? + let genmc_rhs_scalar = scalar_to_genmc_scalar(ecx, rhs_scalar)?; let genmc_old_value = scalar_to_genmc_scalar(ecx, old_value)?; self.atomic_rmw_op_impl( ecx, @@ -315,8 +352,10 @@ impl GenmcCtx { // The value that we would get, if we were to do a non-atomic load here. old_value: Scalar, ) -> InterpResult<'tcx, (Scalar, Option)> { - assert!(!self.allow_data_races.get()); // TODO GENMC: handle this properly - // TODO GENMC: could maybe merge this with atomic_rmw? + assert!( + !self.allow_data_races.get(), + "atomic swap operation with data race checking disabled." + ); let (load_ordering, store_ordering) = ordering.to_genmc_memory_orderings(); let genmc_rmw_op = RMWBinOp::Xchg; @@ -350,7 +389,10 @@ impl GenmcCtx { // The value that we would get, if we were to do a non-atomic load here. old_value: Scalar, ) -> InterpResult<'tcx, (Scalar, bool, bool)> { - assert!(!self.allow_data_races.get()); // TODO GENMC: handle this properly + assert!( + !self.allow_data_races.get(), + "atomic compare-exchange with data race checking disabled." + ); // FIXME(genmc): remove once GenMC supports failure memory ordering in `compare_exchange`. self.warnings_cache.borrow_mut().warn_once_rmw_failure_ordering(&ecx.tcx, success, fail); @@ -424,7 +466,7 @@ impl GenmcCtx { size.bytes() ); if self.allow_data_races.get() { - info!("GenMC: skipping `memory_load` on address"); + info!("GenMC: data race checking disabled, ignoring non-atomic load."); return interp_ok(()); } // GenMC doesn't like ZSTs, and they can't have any data races, so we skip them @@ -439,7 +481,8 @@ impl GenmcCtx { address, size, MemOrdering::NotAtomic, - GenmcScalar::UNINIT, // Don't use DUMMY here, since that might have it stored as the initial value + // Don't use DUMMY here, since that might have it stored as the initial value of the chunk. + GenmcScalar::UNINIT, )?; return interp_ok(()); } @@ -452,7 +495,8 @@ impl GenmcCtx { chunk_addr, chunk_size, MemOrdering::NotAtomic, - GenmcScalar::UNINIT, // Don't use DUMMY here, since that might have it stored as the initial value + // Don't use DUMMY here, since that might have it stored as the initial value of the chunk. + GenmcScalar::UNINIT, )?; } interp_ok(()) @@ -463,7 +507,6 @@ impl GenmcCtx { machine: &MiriMachine<'tcx>, address: Size, size: Size, - // old_value: Option, // TODO GENMC(mixed atomic-non-atomic): is this needed? ) -> InterpResult<'tcx> { info!( "GenMC: received memory_store (non-atomic): address: {:#x}, size: {}", @@ -471,7 +514,7 @@ impl GenmcCtx { size.bytes() ); if self.allow_data_races.get() { - info!("GenMC: skipping `memory_store`"); + info!("GenMC: data race checking disabled, ignoring non-atomic store."); return interp_ok(()); } // GenMC doesn't like ZSTs, and they can't have any data races, so we skip them @@ -485,8 +528,10 @@ impl GenmcCtx { machine, address, size, + // We use DUMMY, since we don't know the actual value, but GenMC expects something. GenmcScalar::DUMMY, - GenmcScalar::UNINIT, // Don't use DUMMY here, since that might have it stored as the initial value + // Don't use DUMMY here, since that might have it stored as the initial value of the chunk. + GenmcScalar::UNINIT, MemOrdering::NotAtomic, )?; return interp_ok(()); @@ -499,8 +544,10 @@ impl GenmcCtx { machine, chunk_addr, chunk_size, + // We use DUMMY, since we don't know the actual value, but GenMC expects something. GenmcScalar::DUMMY, - GenmcScalar::UNINIT, // Don't use DUMMY here, since that might have it stored as the initial value + // Don't use DUMMY here, since that might have it stored as the initial value of the chunk. + GenmcScalar::UNINIT, MemOrdering::NotAtomic, )?; } @@ -517,9 +564,10 @@ impl GenmcCtx { alignment: Align, memory_kind: MemoryKind, ) -> InterpResult<'tcx, u64> { - if self.allow_data_races.get() { - unreachable!(); // FIXME(genmc): can this happen and if yes, how should this be handled? - } + assert!( + !self.allow_data_races.get(), + "memory allocation with data race checking disabled." + ); let machine = &ecx.machine; if memory_kind == MiriMemoryKind::Global.into() { info!("GenMC: global memory allocation: {alloc_id:?}"); @@ -565,9 +613,10 @@ impl GenmcCtx { MiriMemoryKind::Global.into(), "we probably shouldn't try to deallocate global allocations (alloc_id: {alloc_id:?})" ); - if self.allow_data_races.get() { - unreachable!(); // FIXME(genmc): can this happen and if yes, how should this be handled? - } + assert!( + !self.allow_data_races.get(), + "memory deallocation with data race checking disabled." + ); let thread_infos = self.thread_id_manager.borrow(); let curr_thread = machine.threads.active_thread(); let genmc_tid = thread_infos.get_genmc_tid(curr_thread); @@ -587,11 +636,12 @@ impl GenmcCtx { pub(crate) fn handle_thread_create<'tcx>( &self, threads: &ThreadManager<'tcx>, - _start_routine: crate::Pointer, // TODO GENMC: pass info to GenMC + // FIXME(genmc,symmetry reduction): pass info to GenMC + _start_routine: crate::Pointer, _func_arg: &crate::ImmTy<'tcx>, new_thread_id: ThreadId, ) -> InterpResult<'tcx> { - assert!(!self.allow_data_races.get()); // TODO GENMC: handle this properly + assert!(!self.allow_data_races.get(), "thread creation with data race checking disabled."); let mut thread_infos = self.thread_id_manager.borrow_mut(); let curr_thread_id = threads.active_thread(); @@ -614,7 +664,7 @@ impl GenmcCtx { active_thread_id: ThreadId, child_thread_id: ThreadId, ) -> InterpResult<'tcx> { - assert!(!self.allow_data_races.get()); // TODO GENMC: handle this properly + assert!(!self.allow_data_races.get(), "thread join with data race checking disabled."); let thread_infos = self.thread_id_manager.borrow(); let genmc_curr_tid = thread_infos.get_genmc_tid(active_thread_id); @@ -627,7 +677,7 @@ impl GenmcCtx { } pub(crate) fn handle_thread_finish<'tcx>(&self, threads: &ThreadManager<'tcx>) { - assert!(!self.allow_data_races.get()); // TODO GENMC: handle this properly + assert!(!self.allow_data_races.get(), "thread finish with data race checking disabled."); let curr_thread_id = threads.active_thread(); let thread_infos = self.thread_id_manager.borrow(); @@ -690,7 +740,6 @@ impl GenmcCtx { if size.bytes() > 8 { throw_unsup_format!("{UNSUPPORTED_ATOMICS_SIZE_MSG}"); } - assert!(!self.allow_data_races.get()); // TODO GENMC: handle this properly let thread_infos = self.thread_id_manager.borrow(); let curr_thread_id = machine.threads.active_thread(); let genmc_tid = thread_infos.get_genmc_tid(curr_thread_id); @@ -856,29 +905,30 @@ impl GenmcCtx { &self, ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, ) -> InterpResult<'tcx, ThreadId> { - assert!(!self.allow_data_races.get()); // TODO GENMC: handle this properly let thread_manager = &ecx.machine.threads; let active_thread_id = thread_manager.active_thread(); + // Determine whether the next instruction in the current thread might be a load. let curr_thread_next_instr_kind = if !thread_manager.active_thread_ref().get_state().is_enabled() { // The current thread can get blocked (e.g., due to a thread join, assume statement, ...), then we need to ask GenMC for another thread to schedule. // `Load` is a safe default for the next instruction type, since we may not know what the next instruction is. ActionKind::Load } else { - let Some(frame) = thread_manager.get_thread_stack(active_thread_id).last() else { + let Some(frame) = thread_manager.active_thread_stack().last() else { return interp_ok(active_thread_id); }; let either::Either::Left(loc) = frame.current_loc() else { - // We are unwinding. + // We are unwinding, so the next step is definitely not atomic. return interp_ok(active_thread_id); }; let basic_block = &frame.body().basic_blocks[loc.block]; if let Some(_statement) = basic_block.statements.get(loc.statement_index) { + // Statements can't be atomic. return interp_ok(active_thread_id); } - if is_terminator_atomic(ecx, basic_block.terminator(), active_thread_id)? { + if is_terminator_atomic_load(ecx, basic_block.terminator(), active_thread_id)? { ActionKind::Load } else { ActionKind::NonLoad @@ -1138,12 +1188,3 @@ impl VisitProvenance for GenmcCtx { // We don't have any tags. } } - -impl std::fmt::Debug for GenmcCtx { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("GenmcCtx") - // .field("mc", &self.mc) - .field("thread_infos", &self.thread_id_manager) - .finish_non_exhaustive() - } -} diff --git a/src/concurrency/thread.rs b/src/concurrency/thread.rs index 509f9b5b9e..d9a1ea2e82 100644 --- a/src/concurrency/thread.rs +++ b/src/concurrency/thread.rs @@ -501,11 +501,6 @@ impl<'tcx> ThreadManager<'tcx> { &mut self.threads[self.active_thread].stack } - /// TODO GENMC: this function can probably be removed once the GenmcCtx code is finished: - pub fn get_thread_stack(&self, id: ThreadId) -> &[Frame<'tcx, Provenance, FrameExtra<'tcx>>] { - &self.threads[id].stack - } - pub fn all_stacks( &self, ) -> impl Iterator>])> { @@ -1271,8 +1266,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { SchedulingAction::ExecuteTimeoutCallback => { this.run_timeout_callback()?; } - // FIXME(genmc): properly handle sleep in GenMC mode. SchedulingAction::Sleep(duration) => { + // FIXME(genmc): properly handle sleep in GenMC mode. + if this.machine.data_race.as_genmc_ref().is_some() { + throw_unsup_format!("Sleeping is not yet supported in GenMC mode"); + } this.machine.monotonic_clock.sleep(duration); } } From 65a60ebf32fedc762a7101c9e49c5c37c27b2f96 Mon Sep 17 00:00:00 2001 From: Patrick-6 Date: Sun, 27 Jul 2025 19:31:05 +0200 Subject: [PATCH 24/41] Remove loom tests, remove disabled tests. --- .../_disabled/fail/weak_memory/weak_uninit.rs | 57 ------------- .../_disabled/pass/atomic-primitives/mpsc.rs | 41 --------- tests/genmc/_disabled/pass/libc_exit.rs | 61 -------------- tests/genmc/fail/loom/buggy_inc.rs | 56 ------------- tests/genmc/fail/loom/buggy_inc.stderr | 15 ---- .../fail/loom/store_buffering.genmc.stderr | 15 ---- .../loom/store_buffering.genmc_std.stderr | 21 ----- .../loom/store_buffering.non_genmc.stderr | 15 ---- .../loom/store_buffering.non_genmc_std.stderr | 15 ---- tests/genmc/fail/loom/store_buffering.rs | 83 ------------------- tests/genmc/par-len.rs | 48 ----------- .../pass/loom/load_buffering.genmc.stderr | 2 - .../pass/loom/load_buffering.genmc_std.stderr | 8 -- tests/genmc/pass/loom/load_buffering.rs | 80 ------------------ 14 files changed, 517 deletions(-) delete mode 100644 tests/genmc/_disabled/fail/weak_memory/weak_uninit.rs delete mode 100644 tests/genmc/_disabled/pass/atomic-primitives/mpsc.rs delete mode 100644 tests/genmc/_disabled/pass/libc_exit.rs delete mode 100644 tests/genmc/fail/loom/buggy_inc.rs delete mode 100644 tests/genmc/fail/loom/buggy_inc.stderr delete mode 100644 tests/genmc/fail/loom/store_buffering.genmc.stderr delete mode 100644 tests/genmc/fail/loom/store_buffering.genmc_std.stderr delete mode 100644 tests/genmc/fail/loom/store_buffering.non_genmc.stderr delete mode 100644 tests/genmc/fail/loom/store_buffering.non_genmc_std.stderr delete mode 100644 tests/genmc/fail/loom/store_buffering.rs delete mode 100644 tests/genmc/par-len.rs delete mode 100644 tests/genmc/pass/loom/load_buffering.genmc.stderr delete mode 100644 tests/genmc/pass/loom/load_buffering.genmc_std.stderr delete mode 100644 tests/genmc/pass/loom/load_buffering.rs diff --git a/tests/genmc/_disabled/fail/weak_memory/weak_uninit.rs b/tests/genmc/_disabled/fail/weak_memory/weak_uninit.rs deleted file mode 100644 index 1c12bbde6e..0000000000 --- a/tests/genmc/_disabled/fail/weak_memory/weak_uninit.rs +++ /dev/null @@ -1,57 +0,0 @@ -//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows - -// NOTE: Disabled due to incomplete uninitialized memory support in Miri-GenMC mode. - -// Tests showing weak memory behaviours are exhibited. All tests -// return true when the desired behaviour is seen. -// This is scheduler and pseudo-RNG dependent, so each test is -// run multiple times until one try returns true. -// Spurious failure is possible, if you are really unlucky with -// the RNG and always read the latest value from the store buffer. - -#![no_main] - -#[path = "../../../utils/genmc.rs"] -mod genmc; - -use std::ffi::c_void; -use std::mem::MaybeUninit; -use std::sync::atomic::*; - -use crate::genmc::*; - -#[allow(dead_code)] -#[derive(Copy, Clone)] -struct EvilSend(pub T); - -unsafe impl Send for EvilSend {} -unsafe impl Sync for EvilSend {} - -static mut F: MaybeUninit = MaybeUninit::uninit(); - -extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { - unsafe { - let x = AtomicUsize::from_ptr(&raw mut F as *mut usize); - x.store(1, Ordering::Relaxed); - std::ptr::null_mut() - } -} - -extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { - unsafe { - let x = AtomicUsize::from_ptr(&raw mut F as *mut usize); - x.load(Ordering::Relaxed); //~ERROR: using uninitialized data - std::ptr::null_mut() - } -} - -#[unsafe(no_mangle)] -fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - // Unlike with the non-GenMC version of this test, we should only need 1 iteration to detect the bug: - unsafe { - let ids = create_pthreads_no_params([thread_1, thread_2]); - join_pthreads(ids); - } - - 0 -} diff --git a/tests/genmc/_disabled/pass/atomic-primitives/mpsc.rs b/tests/genmc/_disabled/pass/atomic-primitives/mpsc.rs deleted file mode 100644 index e6a2ddc5c3..0000000000 --- a/tests/genmc/_disabled/pass/atomic-primitives/mpsc.rs +++ /dev/null @@ -1,41 +0,0 @@ -//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows -//@revisions: threads1 threads2 threads3 - -// NOTE: Test disabled due to missing mixed-size access support in GenMC. - -// Check that various operations on `std::sync::mpsc` are handled properly in GenMC mode. -// This test is a slightly changed version of the "Shared usage" example in the `mpsc` documentation. - -#![no_main] - -use std::sync::mpsc::channel; -use std::thread; - -const NUM_THREADS: usize = if cfg!(threads3) { - 3 -} else if cfg!(threads2) { - 2 -} else { - 1 -}; - -#[unsafe(no_mangle)] -fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - // Create a shared channel that can be sent along from many threads - // where tx is the sending half (tx for transmission), and rx is the receiving - // half (rx for receiving). - let (tx, rx) = channel(); - for i in 0..NUM_THREADS { - let tx = tx.clone(); - thread::spawn(move || { - tx.send(i).unwrap(); - }); - } - - for _ in 0..NUM_THREADS { - let j = rx.recv().unwrap(); - assert!(j < NUM_THREADS); - } - - 0 -} diff --git a/tests/genmc/_disabled/pass/libc_exit.rs b/tests/genmc/_disabled/pass/libc_exit.rs deleted file mode 100644 index 3e727f62b3..0000000000 --- a/tests/genmc/_disabled/pass/libc_exit.rs +++ /dev/null @@ -1,61 +0,0 @@ -//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows -//@revisions: order0123 order3012 order2301 order1230 order3210 order1032 - -#![no_main] - -// NOTE: Disabled due to incomplete support for `libc::exit`. - -// Copied from `tests/genmc/pass/litmus/inc2w.rs` - -#[path = "../../../../utils/genmc.rs"] -mod genmc; - -use std::ffi::c_void; -use std::ptr::null_mut; -use std::sync::atomic::{AtomicU64, Ordering}; - -use crate::genmc::*; - -static X: AtomicU64 = AtomicU64::new(0); - -#[unsafe(no_mangle)] -fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - let thread_order = if cfg!(order0123) { - [thread_0_exit, thread_1, thread_2, thread_3] - } else if cfg!(order3012) { - [thread_3, thread_0_exit, thread_1, thread_2] - } else if cfg!(order2301) { - [thread_2, thread_3, thread_0_exit, thread_1] - } else if cfg!(order1230) { - [thread_1, thread_2, thread_3, thread_0_exit] - } else if cfg!(order3210) { - [thread_3, thread_2, thread_1, thread_0_exit] - } else if cfg!(order1032) { - [thread_1, thread_0_exit, thread_3, thread_2] - } else { - unimplemented!(); - }; - - let _ids = unsafe { create_pthreads_no_params(thread_order) }; - - 0 -} - -pub extern "C" fn thread_0_exit(_value: *mut c_void) -> *mut c_void { - unsafe { libc::exit(0) } -} - -pub extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { - X.fetch_add(1, Ordering::Relaxed); - null_mut() -} - -pub extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { - X.store(4, Ordering::Release); - null_mut() -} - -pub extern "C" fn thread_3(_value: *mut c_void) -> *mut c_void { - X.fetch_add(2, Ordering::Relaxed); - null_mut() -} diff --git a/tests/genmc/fail/loom/buggy_inc.rs b/tests/genmc/fail/loom/buggy_inc.rs deleted file mode 100644 index 7208f4e85b..0000000000 --- a/tests/genmc/fail/loom/buggy_inc.rs +++ /dev/null @@ -1,56 +0,0 @@ -//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows - -// This tests is the test `checks_fail` from loom/test/smoke.rs adapted for Miri-GenMC. -// https://github.com/tokio-rs/loom/blob/dbf32b04bae821c64be44405a0bb72ca08741558/tests/smoke.rs - -// This test checks that an incorrect implementation of an incrementing counter is detected. -// The counter behaves wrong if two threads try to increment at the same time (increments can be lost). - -#![no_main] - -#[cfg(not(any(non_genmc_std, genmc_std)))] -#[path = "../../../utils/genmc.rs"] -mod genmc; - -use std::ffi::c_void; -use std::sync::atomic::AtomicUsize; -use std::sync::atomic::Ordering::{Acquire, Relaxed, Release}; - -use crate::genmc::{create_pthreads_no_params, join_pthreads}; - -struct BuggyInc { - num: AtomicUsize, -} - -impl BuggyInc { - const fn new() -> BuggyInc { - BuggyInc { num: AtomicUsize::new(0) } - } - - fn inc(&self) { - // The bug is here: - // Another thread can increment `self.num` between the next two lines, which is then overridden by this thread. - let curr = self.num.load(Acquire); - self.num.store(curr + 1, Release); - } -} - -static BUGGY_INC: BuggyInc = BuggyInc::new(); - -extern "C" fn thread_func(_value: *mut c_void) -> *mut c_void { - BUGGY_INC.inc(); - std::ptr::null_mut() -} - -#[unsafe(no_mangle)] -fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - let thread_ids = unsafe { create_pthreads_no_params([thread_func; 2]) }; - - unsafe { join_pthreads(thread_ids) }; - - if 2 != BUGGY_INC.num.load(Relaxed) { - unsafe { std::hint::unreachable_unchecked() }; //~ ERROR: entering unreachable code - } - - 0 -} diff --git a/tests/genmc/fail/loom/buggy_inc.stderr b/tests/genmc/fail/loom/buggy_inc.stderr deleted file mode 100644 index 7617864a42..0000000000 --- a/tests/genmc/fail/loom/buggy_inc.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error: Undefined Behavior: entering unreachable code - --> tests/genmc/fail/loom/buggy_inc.rs:LL:CC - | -LL | unsafe { std::hint::unreachable_unchecked() }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here - | - = 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 - = note: BACKTRACE: - = note: inside `miri_start` at tests/genmc/fail/loom/buggy_inc.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 - diff --git a/tests/genmc/fail/loom/store_buffering.genmc.stderr b/tests/genmc/fail/loom/store_buffering.genmc.stderr deleted file mode 100644 index 33c65f3ce6..0000000000 --- a/tests/genmc/fail/loom/store_buffering.genmc.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error: Undefined Behavior: entering unreachable code - --> tests/genmc/fail/loom/store_buffering.rs:LL:CC - | -LL | unsafe { std::hint::unreachable_unchecked() }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here - | - = 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 - = note: BACKTRACE: - = note: inside `miri_start` at tests/genmc/fail/loom/store_buffering.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 - diff --git a/tests/genmc/fail/loom/store_buffering.genmc_std.stderr b/tests/genmc/fail/loom/store_buffering.genmc_std.stderr deleted file mode 100644 index d23bc640a7..0000000000 --- a/tests/genmc/fail/loom/store_buffering.genmc_std.stderr +++ /dev/null @@ -1,21 +0,0 @@ -warning: GenMC currently does not model the atomic failure ordering for `compare_exchange`. Failure ordering 'Relaxed' is treated like 'Acquire', which means that Miri might miss bugs related to this memory access (possible unsoundness)! - -warning: GenMC mode currently does not model spurious failures of `compare_exchange_weak`. This may lead to missed bugs (possible unsoundness)! - -warning: GenMC currently does not model the atomic failure ordering for `compare_exchange`. Failure ordering 'Acquire' is treated like 'Relaxed', which means that Miri might incorrectly detect errors related to this memory access (possible false positives). - -error: Undefined Behavior: entering unreachable code - --> tests/genmc/fail/loom/store_buffering.rs:LL:CC - | -LL | unsafe { std::hint::unreachable_unchecked() }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here - | - = 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 - = note: BACKTRACE: - = note: inside `miri_start` at tests/genmc/fail/loom/store_buffering.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; 3 warnings emitted - diff --git a/tests/genmc/fail/loom/store_buffering.non_genmc.stderr b/tests/genmc/fail/loom/store_buffering.non_genmc.stderr deleted file mode 100644 index 33c65f3ce6..0000000000 --- a/tests/genmc/fail/loom/store_buffering.non_genmc.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error: Undefined Behavior: entering unreachable code - --> tests/genmc/fail/loom/store_buffering.rs:LL:CC - | -LL | unsafe { std::hint::unreachable_unchecked() }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here - | - = 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 - = note: BACKTRACE: - = note: inside `miri_start` at tests/genmc/fail/loom/store_buffering.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 - diff --git a/tests/genmc/fail/loom/store_buffering.non_genmc_std.stderr b/tests/genmc/fail/loom/store_buffering.non_genmc_std.stderr deleted file mode 100644 index 33c65f3ce6..0000000000 --- a/tests/genmc/fail/loom/store_buffering.non_genmc_std.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error: Undefined Behavior: entering unreachable code - --> tests/genmc/fail/loom/store_buffering.rs:LL:CC - | -LL | unsafe { std::hint::unreachable_unchecked() }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here - | - = 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 - = note: BACKTRACE: - = note: inside `miri_start` at tests/genmc/fail/loom/store_buffering.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 - diff --git a/tests/genmc/fail/loom/store_buffering.rs b/tests/genmc/fail/loom/store_buffering.rs deleted file mode 100644 index eeeeeefeb5..0000000000 --- a/tests/genmc/fail/loom/store_buffering.rs +++ /dev/null @@ -1,83 +0,0 @@ -//@ revisions: non_genmc non_genmc_std genmc genmc_std -//@[non_genmc,non_genmc_std] compile-flags: -//@[genmc,genmc_std] compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows - -// This is the test `store_buffering` from `loom/test/litmus.rs`, adapted for Miri-GenMC. -// https://github.com/tokio-rs/loom/blob/dbf32b04bae821c64be44405a0bb72ca08741558/tests/litmus.rs - -// This test doubles as a comparison between using std threads and pthreads, and normal Miri vs Miri-GenMC. - -#![no_main] - -#[cfg(not(any(non_genmc_std, genmc_std)))] -#[path = "../../../utils/genmc.rs"] -mod genmc; - -use std::sync::atomic::AtomicUsize; -use std::sync::atomic::Ordering::{Relaxed, SeqCst}; - -static X: AtomicUsize = AtomicUsize::new(0); -static Y: AtomicUsize = AtomicUsize::new(0); - -#[unsafe(no_mangle)] -fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - // For normal Miri, we need multiple repetitions, but GenMC should find the bug with only 1. - const REPS: usize = if cfg!(any(non_genmc, non_genmc_std)) { 128 } else { 1 }; - - for _ in 0..REPS { - X.store(0, SeqCst); - Y.store(0, SeqCst); - - if test() == (0, 0) { - unsafe { std::hint::unreachable_unchecked() }; //~ ERROR: entering unreachable code - } - } - - 0 -} - -#[cfg(any(non_genmc_std, genmc_std))] -fn test() -> (usize, usize) { - let thread = std::thread::spawn(thread_0); - - let b = thread_1(); - - let a = thread.join().unwrap(); - - (a, b) -} - -#[cfg(not(any(non_genmc_std, genmc_std)))] -fn test() -> (usize, usize) { - use std::ffi::c_void; - - use crate::genmc::{join_pthread, spawn_pthread}; - - extern "C" fn thread_func(value: *mut c_void) -> *mut c_void { - let a_ptr = value as *mut usize; - let a = thread_0(); - unsafe { *a_ptr = a }; - std::ptr::null_mut() - } - - let mut a: usize = 0; - let thread_id = unsafe { spawn_pthread(thread_func, &raw mut a as *mut c_void) }; - - let b = thread_1(); - - unsafe { join_pthread(thread_id) }; - - (a, b) -} - -/// Returns the value for `a` -fn thread_0() -> usize { - X.store(1, Relaxed); - Y.load(Relaxed) -} - -/// Returns the value for `b` -fn thread_1() -> usize { - Y.store(1, Relaxed); - X.load(Relaxed) -} diff --git a/tests/genmc/par-len.rs b/tests/genmc/par-len.rs deleted file mode 100644 index 7022756b57..0000000000 --- a/tests/genmc/par-len.rs +++ /dev/null @@ -1,48 +0,0 @@ -//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows - -#![no_main] - -#[path = "../utils/genmc.rs"] -mod genmc; - -use std::ffi::c_void; -use std::sync::atomic::AtomicI32; -use std::sync::atomic::Ordering::SeqCst; - -use crate::genmc::*; - -const T: usize = 2; -const N: usize = 2000; - -static V: AtomicI32 = AtomicI32::new(0); -static LOCAL: [AtomicI32; T] = [const { AtomicI32::new(0) }; T]; - -#[unsafe(no_mangle)] -fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - for i in 0..T { - let arg = i as *mut c_void; - let _id = unsafe { spawn_pthread(thread_func, arg) }; - } - - // let arg = 0usize as *mut c_void; - // let _id0 = unsafe { spawn_pthread(thread_func, arg) }; - // let arg = 1usize as *mut c_void; - // let _id1 = unsafe { spawn_pthread(thread_func, arg) }; - // unsafe { join_pthreads([_id0, _id1])}; - - 0 -} - -extern "C" fn thread_func(value: *mut c_void) -> *mut c_void { - let tid = value as usize; - - for i in 0..N { - LOCAL[tid].store(i.try_into().unwrap(), SeqCst); - let _ = LOCAL[tid].load(SeqCst); - } - - V.store(tid.try_into().unwrap(), SeqCst); - let _ = V.load(SeqCst); - - std::ptr::null_mut() -} diff --git a/tests/genmc/pass/loom/load_buffering.genmc.stderr b/tests/genmc/pass/loom/load_buffering.genmc.stderr deleted file mode 100644 index 115b1986ce..0000000000 --- a/tests/genmc/pass/loom/load_buffering.genmc.stderr +++ /dev/null @@ -1,2 +0,0 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 3 diff --git a/tests/genmc/pass/loom/load_buffering.genmc_std.stderr b/tests/genmc/pass/loom/load_buffering.genmc_std.stderr deleted file mode 100644 index 76b0302eda..0000000000 --- a/tests/genmc/pass/loom/load_buffering.genmc_std.stderr +++ /dev/null @@ -1,8 +0,0 @@ -warning: GenMC currently does not model the atomic failure ordering for `compare_exchange`. Failure ordering 'Relaxed' is treated like 'Acquire', which means that Miri might miss bugs related to this memory access (possible unsoundness)! - -warning: GenMC mode currently does not model spurious failures of `compare_exchange_weak`. This may lead to missed bugs (possible unsoundness)! - -warning: GenMC currently does not model the atomic failure ordering for `compare_exchange`. Failure ordering 'Acquire' is treated like 'Relaxed', which means that Miri might incorrectly detect errors related to this memory access (possible false positives). - -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 3 diff --git a/tests/genmc/pass/loom/load_buffering.rs b/tests/genmc/pass/loom/load_buffering.rs deleted file mode 100644 index 46205e154e..0000000000 --- a/tests/genmc/pass/loom/load_buffering.rs +++ /dev/null @@ -1,80 +0,0 @@ -//@ revisions: non_genmc non_genmc_std genmc genmc_std -//@[non_genmc,non_genmc_std] compile-flags: -//@[genmc,genmc_std] compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows - -// This is the test `load_buffering` from `loom/test/litmus.rs`, adapted for Miri-GenMC. -// https://github.com/tokio-rs/loom/blob/dbf32b04bae821c64be44405a0bb72ca08741558/tests/litmus.rs - -// Loom uses a memory model like C++11's, which allowed for the `test` function to return `1`. -// This is not allowed in the RC11 memory model, which is what Miri-GenMC uses. - -// This test doubles as a comparison between using std threads and pthreads, and normal Miri vs Miri-GenMC. - -#![no_main] - -#[cfg(not(any(non_genmc_std, genmc_std)))] -#[path = "../../../utils/genmc.rs"] -mod genmc; - -use std::sync::atomic::AtomicUsize; -use std::sync::atomic::Ordering::{Relaxed, SeqCst}; - -static X: AtomicUsize = AtomicUsize::new(0); -static Y: AtomicUsize = AtomicUsize::new(0); - -#[unsafe(no_mangle)] -fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - // For normal Miri, we need multiple repetitions, but 1 is enough for GenMC. - const REPS: usize = if cfg!(any(non_genmc, non_genmc_std)) { 128 } else { 1 }; - - for _ in 0..REPS { - X.store(0, SeqCst); - Y.store(0, SeqCst); - - if test() == 1 { - unsafe { std::hint::unreachable_unchecked() }; - } - } - - 0 -} - -#[cfg(any(non_genmc_std, genmc_std))] -fn test() -> usize { - let thread = std::thread::spawn(thread_0); - let a = thread_1(); - thread.join().unwrap(); - - a -} - -#[cfg(not(any(non_genmc_std, genmc_std)))] -fn test() -> usize { - use std::ffi::c_void; - - use crate::genmc::{join_pthread, spawn_pthread}; - - extern "C" fn thread_func(_value: *mut c_void) -> *mut c_void { - thread_0(); - std::ptr::null_mut() - } - - let thread_id = unsafe { spawn_pthread(thread_func, std::ptr::null_mut()) }; - - let a = thread_1(); - - unsafe { join_pthread(thread_id) }; - - a -} - -fn thread_0() { - X.store(Y.load(Relaxed), Relaxed); -} - -/// Returns the value for `a` -fn thread_1() -> usize { - let a = X.load(Relaxed); - Y.store(1, Relaxed); - a -} From ecf24bc8ebd78895e31711bfb91c7ee2de5c6012 Mon Sep 17 00:00:00 2001 From: Patrick-6 Date: Sat, 26 Jul 2025 13:42:47 +0200 Subject: [PATCH 25/41] Remove __VERIFIER_assume --- genmc-sys/src/lib.rs | 3 - genmc-sys/src_cpp/MiriInterface.hpp | 4 -- .../src_cpp/MiriInterface/MiriInterface.cpp | 7 --- src/concurrency/genmc/dummy.rs | 17 ++++-- src/concurrency/genmc/mod.rs | 58 +++++-------------- src/diagnostics.rs | 7 +-- src/shims/foreign_items.rs | 17 ------ tests/genmc/pass/litmus/assume-ctrl.rs | 46 --------------- tests/genmc/pass/litmus/assume-ctrl.stderr | 3 - tests/utils/miri_extern.rs | 3 - 10 files changed, 25 insertions(+), 140 deletions(-) delete mode 100644 tests/genmc/pass/litmus/assume-ctrl.rs delete mode 100644 tests/genmc/pass/litmus/assume-ctrl.stderr diff --git a/genmc-sys/src/lib.rs b/genmc-sys/src/lib.rs index f8bb5ea649..8ff61141bf 100644 --- a/genmc-sys/src/lib.rs +++ b/genmc-sys/src/lib.rs @@ -295,9 +295,6 @@ mod ffi { fn handleThreadFinish(self: Pin<&mut MiriGenMCShim>, thread_id: i32, ret_val: u64); fn handleThreadKill(self: Pin<&mut MiriGenMCShim>, thread_id: i32); - /**** Blocking instructions ****/ - fn handleUserBlock(self: Pin<&mut MiriGenMCShim>, thread_id: i32); - /**** Mutex handling ****/ fn handleMutexLock( self: Pin<&mut MiriGenMCShim>, diff --git a/genmc-sys/src_cpp/MiriInterface.hpp b/genmc-sys/src_cpp/MiriInterface.hpp index 808fb852ea..dabf205624 100644 --- a/genmc-sys/src_cpp/MiriInterface.hpp +++ b/genmc-sys/src_cpp/MiriInterface.hpp @@ -82,10 +82,6 @@ struct MiriGenMCShim : private GenMCDriver { void handleThreadFinish(ThreadId thread_id, uint64_t ret_val); void handleThreadKill(ThreadId thread_id); - /**** Blocking instructions ****/ - - void handleUserBlock(ThreadId thread_id); - /**** Mutex handling ****/ auto handleMutexLock(ThreadId thread_id, uint64_t address, uint64_t size) -> MutexLockResult; diff --git a/genmc-sys/src_cpp/MiriInterface/MiriInterface.cpp b/genmc-sys/src_cpp/MiriInterface/MiriInterface.cpp index eeb4433800..ee08f5b6ef 100644 --- a/genmc-sys/src_cpp/MiriInterface/MiriInterface.cpp +++ b/genmc-sys/src_cpp/MiriInterface/MiriInterface.cpp @@ -218,13 +218,6 @@ void MiriGenMCShim::handleThreadKill(ThreadId thread_id) GenMCDriver::handleThreadKill(pos); } -/**** Blocking instructions ****/ - -void MiriGenMCShim::handleUserBlock(ThreadId thread_id) -{ - GenMCDriver::handleAssume(incPos(thread_id), AssumeType::User); -} - /**** Memory access handling ****/ [[nodiscard]] auto MiriGenMCShim::handleLoad(ThreadId thread_id, uint64_t address, uint64_t size, diff --git a/src/concurrency/genmc/dummy.rs b/src/concurrency/genmc/dummy.rs index 2378dc7e46..6f01321c1a 100644 --- a/src/concurrency/genmc/dummy.rs +++ b/src/concurrency/genmc/dummy.rs @@ -240,6 +240,17 @@ impl GenmcCtx { ) -> InterpResult<'tcx> { unreachable!() } + + /**** Blocking instructions ****/ + + #[allow(unused)] + pub(crate) fn handle_verifier_assume<'tcx>( + &self, + _machine: &MiriMachine<'tcx>, + _condition: bool, + ) -> InterpResult<'tcx, ()> { + unreachable!() + } } /// Other functionality not directly related to event handling @@ -260,12 +271,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn genmc_schedule_thread(&mut self) -> InterpResult<'tcx, ThreadId> { unreachable!() } - - /**** Blocking instructions ****/ - - fn handle_genmc_verifier_assume(&mut self, _condition: &OpTy<'tcx>) -> InterpResult<'tcx> { - unreachable!() - } } impl VisitProvenance for GenmcCtx { diff --git a/src/concurrency/genmc/mod.rs b/src/concurrency/genmc/mod.rs index 5f38c3caf6..0263482230 100644 --- a/src/concurrency/genmc/mod.rs +++ b/src/concurrency/genmc/mod.rs @@ -721,6 +721,17 @@ impl GenmcCtx { } interp_ok(()) } + + /**** Blocking instructions ****/ + + #[allow(unused)] + pub(crate) fn handle_verifier_assume<'tcx>( + &self, + machine: &MiriMachine<'tcx>, + condition: bool, + ) -> InterpResult<'tcx, ()> { + if condition { interp_ok(()) } else { self.handle_user_block(machine) } + } } impl GenmcCtx { @@ -960,17 +971,8 @@ impl GenmcCtx { /**** Blocking functionality ****/ - fn handle_user_block<'tcx>(&self, machine: &MiriMachine<'tcx>) -> InterpResult<'tcx> { - let thread_infos = self.thread_id_manager.borrow(); - let curr_thread = machine.threads.active_thread(); - let genmc_curr_thread = thread_infos.get_genmc_tid(curr_thread); - info!("GenMC: handle_user_block, blocking thread {curr_thread:?} ({genmc_curr_thread:?})"); - - let mut mc = self.handle.borrow_mut(); - let pinned_mc = mc.as_mut(); - pinned_mc.handleUserBlock(genmc_curr_thread); - - interp_ok(()) + fn handle_user_block<'tcx>(&self, _machine: &MiriMachine<'tcx>) -> InterpResult<'tcx> { + todo!() } } @@ -1147,40 +1149,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { return interp_ok(next_thread_id); } } - - /**** Blocking instructions ****/ - - /// Handle an `assume` statement. This will tell GenMC to block the current thread if the `condition` is false. - /// Returns `true` if the current thread should be blocked in Miri too. - fn handle_genmc_verifier_assume(&mut self, condition: &OpTy<'tcx>) -> InterpResult<'tcx> { - let this = self.eval_context_mut(); - let condition_bool = this.read_scalar(condition)?.to_bool()?; - info!("GenMC: handle_genmc_verifier_assume, condition: {condition:?} = {condition_bool}"); - if condition_bool { - return interp_ok(()); - } - let genmc_ctx = this.machine.data_race.as_genmc_ref().unwrap(); - genmc_ctx.handle_user_block(&this.machine)?; - let condition = condition.clone(); - this.block_thread( - BlockReason::GenmcAssume, - None, - callback!( - @capture<'tcx> { - condition: OpTy<'tcx>, - } - |this, unblock: UnblockKind| { - assert_eq!(unblock, UnblockKind::Ready); - - let condition = this.run_for_validation_ref(|this| this.read_scalar(&condition))?.to_bool()?; - assert!(condition); - - interp_ok(()) - } - ), - ); - interp_ok(()) - } } impl VisitProvenance for GenmcCtx { diff --git a/src/diagnostics.rs b/src/diagnostics.rs index f07c564813..56bcedc74b 100644 --- a/src/diagnostics.rs +++ b/src/diagnostics.rs @@ -258,14 +258,9 @@ pub fn report_error<'tcx>( let genmc_ctx = ecx.machine.data_race.as_genmc_ref().unwrap(); // Check whether the execution got blocked or the program exited. if let Some((exit_code, leak_check)) = genmc_ctx.get_exit_status() { - let is_blocked_execution = - ecx.machine.threads.threads_ref().iter().any(|thread| { - thread.get_state().is_blocked_on(BlockReason::GenmcAssume) - }); - // We have an exit status (from `exit(x)` or main thread return). // Skip leak checks if the execution was blocked. - return Some((exit_code, leak_check && !is_blocked_execution)); + return Some((exit_code, leak_check)); } // The program got blocked by GenMC without ever exiting, so we don't do any leak checks. return Some((0, false)); diff --git a/src/shims/foreign_items.rs b/src/shims/foreign_items.rs index 2883541920..6658163f11 100644 --- a/src/shims/foreign_items.rs +++ b/src/shims/foreign_items.rs @@ -17,7 +17,6 @@ use rustc_target::callconv::FnAbi; use self::helpers::{ToHost, ToSoft}; use super::alloc::EvalContextExt as _; use super::backtrace::EvalContextExt as _; -use crate::concurrency::GenmcEvalContextExt as _; use crate::*; /// Type of dynamic symbols (for `dlsym` et al) @@ -440,22 +439,6 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { } } - /*** \/ GENMC VERIFIER CALLS \/ ****/ - "miri_genmc_verifier_assume" => { - let [condition] = - this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; - if this.machine.data_race.as_genmc_ref().is_some() { - this.handle_genmc_verifier_assume(condition)?; - } else { - tracing::warn!( - "GenMC: function `miri_genmc_verifier_assume` used, but GenMC mode is not active, skip ..." - ); - } - } - - // TODO GENMC: add other genmc functions - - /*** /\ GENMC VERIFIER CALLS /\ ****/ // Aborting the process. "exit" => { let [code] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; diff --git a/tests/genmc/pass/litmus/assume-ctrl.rs b/tests/genmc/pass/litmus/assume-ctrl.rs deleted file mode 100644 index eb3ac511d2..0000000000 --- a/tests/genmc/pass/litmus/assume-ctrl.rs +++ /dev/null @@ -1,46 +0,0 @@ -//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows - -#![no_main] - -#[path = "../../../utils/genmc.rs"] -mod genmc; - -#[path = "../../../utils/mod.rs"] -mod utils; - -use std::ffi::c_void; -use std::sync::atomic::{AtomicU64, Ordering}; - -static X: AtomicU64 = AtomicU64::new(0); -static Y: AtomicU64 = AtomicU64::new(0); - -use crate::genmc::*; -use crate::utils::miri_genmc_verifier_assume; - -#[unsafe(no_mangle)] -fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - let _ids = unsafe { create_pthreads_no_params([thread_1, thread_2]) }; - - 0 -} - -extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { - // TODO GENMC: do these have to be unsafe? - unsafe { - miri_genmc_verifier_assume(2 > Y.load(Ordering::Relaxed) || Y.load(Ordering::Relaxed) > 3); - } - X.store(1, Ordering::Relaxed); - std::ptr::null_mut() -} - -extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { - // TODO GENMC: do these have to be unsafe? - unsafe { - miri_genmc_verifier_assume(X.load(Ordering::Relaxed) < 3); - } - - Y.store(3, Ordering::Relaxed); - std::sync::atomic::fence(Ordering::SeqCst); - Y.store(4, Ordering::Relaxed); - std::ptr::null_mut() -} diff --git a/tests/genmc/pass/litmus/assume-ctrl.stderr b/tests/genmc/pass/litmus/assume-ctrl.stderr deleted file mode 100644 index 86a4c37e30..0000000000 --- a/tests/genmc/pass/litmus/assume-ctrl.stderr +++ /dev/null @@ -1,3 +0,0 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 4 -Number of blocked executions seen: 1 diff --git a/tests/utils/miri_extern.rs b/tests/utils/miri_extern.rs index 4ce7d27539..d6c43b1882 100644 --- a/tests/utils/miri_extern.rs +++ b/tests/utils/miri_extern.rs @@ -147,7 +147,4 @@ extern "Rust" { /// "symbolic" alignment checks. Will fail if the pointer is not actually aligned or `align` is /// not a power of two. Has no effect when alignment checks are concrete (which is the default). pub fn miri_promise_symbolic_alignment(ptr: *const (), align: usize); - - /// Blocks the current execution if the argument is false - pub fn miri_genmc_verifier_assume(condition: bool); } From 70c6db0488a02d4adb3f330032bbbab25d411d47 Mon Sep 17 00:00:00 2001 From: Patrick-6 Date: Sat, 26 Jul 2025 13:57:21 +0200 Subject: [PATCH 26/41] Remove Mutex handling, do cleanup. --- genmc-sys/build.rs | 1 - genmc-sys/src/lib.rs | 33 ----- genmc-sys/src_cpp/MiriInterface.hpp | 7 - genmc-sys/src_cpp/MiriInterface/Mutex.cpp | 133 ----------------- genmc-sys/src_cpp/ResultHandling.hpp | 17 --- src/concurrency/genmc/dummy.rs | 20 --- src/concurrency/genmc/mod.rs | 139 +----------------- src/machine.rs | 10 +- tests/genmc/_disabled/mutex_poison.rs | 76 ---------- .../atomic-primitives/arc.check_count.stderr | 8 - tests/genmc/pass/atomic-primitives/arc.rs | 55 ------- .../atomic-primitives/arc.try_upgrade.stderr | 8 - .../mutex_simple.reps1.stderr | 2 - .../mutex_simple.reps2.stderr | 2 - .../pass/atomic-primitives/mutex_simple.rs | 70 --------- 15 files changed, 4 insertions(+), 577 deletions(-) delete mode 100644 genmc-sys/src_cpp/MiriInterface/Mutex.cpp delete mode 100644 tests/genmc/_disabled/mutex_poison.rs delete mode 100644 tests/genmc/pass/atomic-primitives/arc.check_count.stderr delete mode 100644 tests/genmc/pass/atomic-primitives/arc.rs delete mode 100644 tests/genmc/pass/atomic-primitives/arc.try_upgrade.stderr delete mode 100644 tests/genmc/pass/atomic-primitives/mutex_simple.reps1.stderr delete mode 100644 tests/genmc/pass/atomic-primitives/mutex_simple.reps2.stderr delete mode 100644 tests/genmc/pass/atomic-primitives/mutex_simple.rs diff --git a/genmc-sys/build.rs b/genmc-sys/build.rs index f286b1962a..b26115a96c 100644 --- a/genmc-sys/build.rs +++ b/genmc-sys/build.rs @@ -212,7 +212,6 @@ fn compile_cpp_dependencies(genmc_path: &Path) { let cpp_files = [ "./src_cpp/MiriInterface/MiriInterface.cpp", - "./src_cpp/MiriInterface/Mutex.cpp", "./src_cpp/ResultHandling.cpp", ]; diff --git a/genmc-sys/src/lib.rs b/genmc-sys/src/lib.rs index 8ff61141bf..3e90d51f59 100644 --- a/genmc-sys/src/lib.rs +++ b/genmc-sys/src/lib.rs @@ -23,9 +23,6 @@ impl GenmcScalar { /// FIXME(genmc): remove this if a permanent fix is ever found. pub const DUMMY: Self = Self::from_u64(0xDEADBEEF); - pub const MUTEX_LOCKED_STATE: Self = Self::from_u64(1); - pub const MUTEX_UNLOCKED_STATE: Self = Self::from_u64(0); - pub const fn from_u64(value: u64) -> Self { Self { value, extra: 0, is_init: true } } @@ -199,15 +196,6 @@ mod ffi { isCoMaxWrite: bool, } - #[must_use] - #[derive(Debug)] - struct MutexLockResult { - /// If there was an error, it will be stored in `error`, otherwise it is `None`. - error: UniquePtr, - /// Indicate whether the lock was acquired by this thread. - is_lock_acquired: bool, - } - /**** /\ Result & Error types /\ ****/ unsafe extern "C++" { @@ -224,7 +212,6 @@ mod ffi { type StoreResult; type ReadModifyWriteResult; type CompareExchangeResult; - type MutexLockResult; type GenmcScalar; @@ -295,26 +282,6 @@ mod ffi { fn handleThreadFinish(self: Pin<&mut MiriGenMCShim>, thread_id: i32, ret_val: u64); fn handleThreadKill(self: Pin<&mut MiriGenMCShim>, thread_id: i32); - /**** Mutex handling ****/ - fn handleMutexLock( - self: Pin<&mut MiriGenMCShim>, - thread_id: i32, - address: u64, - size: u64, - ) -> MutexLockResult; - fn handleMutexTryLock( - self: Pin<&mut MiriGenMCShim>, - thread_id: i32, - address: u64, - size: u64, - ) -> MutexLockResult; - fn handleMutexUnlock( - self: Pin<&mut MiriGenMCShim>, - thread_id: i32, - address: u64, - size: u64, - ) -> StoreResult; - /**** Scheduling ****/ fn scheduleNext( self: Pin<&mut MiriGenMCShim>, diff --git a/genmc-sys/src_cpp/MiriInterface.hpp b/genmc-sys/src_cpp/MiriInterface.hpp index dabf205624..66acc75ad3 100644 --- a/genmc-sys/src_cpp/MiriInterface.hpp +++ b/genmc-sys/src_cpp/MiriInterface.hpp @@ -82,13 +82,6 @@ struct MiriGenMCShim : private GenMCDriver { void handleThreadFinish(ThreadId thread_id, uint64_t ret_val); void handleThreadKill(ThreadId thread_id); - /**** Mutex handling ****/ - auto handleMutexLock(ThreadId thread_id, uint64_t address, uint64_t size) - -> MutexLockResult; - auto handleMutexTryLock(ThreadId thread_id, uint64_t address, uint64_t size) - -> MutexLockResult; - auto handleMutexUnlock(ThreadId thread_id, uint64_t address, uint64_t size) -> StoreResult; - /**** Exploration related functionality ****/ auto scheduleNext(const int curr_thread_id, const ActionKind curr_thread_next_instr_kind) diff --git a/genmc-sys/src_cpp/MiriInterface/Mutex.cpp b/genmc-sys/src_cpp/MiriInterface/Mutex.cpp deleted file mode 100644 index 5b58db71c9..0000000000 --- a/genmc-sys/src_cpp/MiriInterface/Mutex.cpp +++ /dev/null @@ -1,133 +0,0 @@ -#include "MiriInterface.hpp" - -// Miri C++ helpers: -#include "Helper.hpp" - -// CXX.rs generated headers: -#include "genmc-sys/src/lib.rs.h" - -auto MiriGenMCShim::handleMutexLock(ThreadId thread_id, uint64_t address, uint64_t size) - -> MutexLockResult -{ - // TODO GENMC: this needs to be identical even in multithreading - ModuleID::ID annot_id; - if (annotation_id.contains(address)) { - annot_id = annotation_id.at(address); - } else { - annot_id = annotation_id_counter++; - annotation_id.insert(std::make_pair(address, annot_id)); - } - const auto aSize = ASize(size); - auto annot = std::move(Annotation( - AssumeType::Spinloop, - Annotation::ExprVP(NeExpr::create( - RegisterExpr::create(aSize.getBits(), annot_id), - ConcreteExpr::create(aSize.getBits(), SVal(1))) - .release()))); - - // Mutex starts out unlocked, so we always say the previous value is "unlocked". - auto oldValSetter = [this](SAddr addr) { this->handleOldVal(addr, GenmcScalar(0, 0)); }; - const auto ret = handleLoadResetIfNone( - oldValSetter, thread_id, address, size, annot, EventDeps()); - RETURN_IF_ERROR(ret, MutexLockResult); - - const auto *retVal = std::get_if(&ret); - if (!retVal) { - if (std::holds_alternative(ret)) { - // TODO TODO GENMC: what did I mean with this comment? - // TODO GENMC: is_read_opt == Mutex is acquired - // None --> Someone else has lock, this thread will be rescheduled later - // (currently block) 0 --> Got the lock 1 --> Someone else has lock, - // this thread will not be rescheduled later (block on Miri side) - return MutexLockResult(false); - } - ERROR("Unimplemented: mutex lock returned unexpected result."); - } - - const bool is_lock_acquired = *retVal == SVal(0); - if (is_lock_acquired) { - const auto ret = GenMCDriver::handleStore( - oldValSetter, incPos(thread_id), address, size, EventDeps()); - - const auto *err = std::get_if(&ret); - if (err != nullptr) { - return MutexLockResult::fromError( - "TODO GENMC: format error once std::format change is merged"); - } - ERROR_ON(!std::holds_alternative(ret), - "Unsupported: mutex lock store returned unexpected result."); - } else { - GenMCDriver::handleAssume(incPos(thread_id), AssumeType::Spinloop); - } - - return MutexLockResult(is_lock_acquired); -} - -auto MiriGenMCShim::handleMutexTryLock(ThreadId thread_id, uint64_t address, uint64_t size) - -> MutexLockResult -{ - const auto addr = SAddr(address); - const auto aSize = ASize(size); - - auto &currPos = threadsAction[thread_id].event; - // Mutex starts out unlocked, so we always say the previous value is "unlocked". - auto oldValSetter = [this](SAddr addr) { this->handleOldVal(addr, GenmcScalar(0, 0)); }; - const auto ret0 = GenMCDriver::handleLoad( - oldValSetter, ++currPos, addr, aSize); - RETURN_IF_ERROR(ret0, MutexLockResult); - - const auto *retVal = std::get_if(&ret0); - if (nullptr == retVal) { - // if (std::holds_alternative(ret0)) { - // // TODO TODO GENMC: what did I mean with this comment? - // // TODO GENMC: is_read_opt == Mutex is acquired - // // None --> Someone else has lock, this thread will be rescheduled later - // // (currently block) 0 --> Got the lock 1 --> Someone else has lock, - // // this thread will not be rescheduled later (block on Miri side) - // return MutexLockResult(false); - // } - ERROR("Unimplemented: mutex trylock load returned unexpected result."); - } - - const bool is_lock_acquired = *retVal == SVal(0); - if (!is_lock_acquired) - return MutexLockResult(false); /* Lock already held. */ - - const auto ret1 = GenMCDriver::handleStore( - oldValSetter, ++currPos, addr, aSize); - RETURN_IF_ERROR(ret1, MutexLockResult); - - if (!std::holds_alternative(ret1)) { - ERROR("Unimplemented: mutex trylock store returned unexpected result."); - } - // No error or unexpected result: lock is acquired. - return MutexLockResult(true); -} - -auto MiriGenMCShim::handleMutexUnlock(ThreadId thread_id, uint64_t address, uint64_t size) - -> StoreResult -{ - const auto pos = incPos(thread_id); - const auto addr = SAddr(address); - const auto aSize = ASize(size); - - const auto oldValSetter = [this](SAddr addr) { - // TODO GENMC(HACK): is this the best way to do it? - this->handleOldVal(addr, GenmcScalar(0xDEADBEEF, 0)); - }; - const auto ret = GenMCDriver::handleStore( - oldValSetter, pos, MemOrdering::Release, addr, aSize, AType::Signed, SVal(0), - EventDeps()); - RETURN_IF_ERROR(ret, StoreResult); - - if (!std::holds_alternative(ret)) { - ERROR("Unimplemented: mutex unlock store returned unexpected result."); - } - - // TODO GENMC: Mixed-accesses (`false` should be fine, since we never want to update Miri's - // memory for mutexes anyway) - // const bool isCoMaxWrite = false; - const auto &g = getExec().getGraph(); - const bool isCoMaxWrite = g.co_max(addr)->getPos() == pos; - return StoreResult::ok(isCoMaxWrite); -} diff --git a/genmc-sys/src_cpp/ResultHandling.hpp b/genmc-sys/src_cpp/ResultHandling.hpp index 146b7afd09..07c615ee15 100644 --- a/genmc-sys/src_cpp/ResultHandling.hpp +++ b/genmc-sys/src_cpp/ResultHandling.hpp @@ -166,21 +166,4 @@ struct CompareExchangeResult { } }; -struct MutexLockResult { - /// if not `nullptr`, it contains an error encountered during the handling of the mutex op. - std::unique_ptr error; - /// Indicate whether the lock was acquired by this thread. - bool is_lock_acquired; - - MutexLockResult(bool is_lock_acquired) : is_lock_acquired(is_lock_acquired), error(nullptr) - {} - - static auto fromError(std::string msg) -> MutexLockResult - { - auto res = MutexLockResult(false); - res.error = std::make_unique(msg); - return res; - } -}; - #endif /* GENMC_RESULT_HANDLING_HPP */ diff --git a/src/concurrency/genmc/dummy.rs b/src/concurrency/genmc/dummy.rs index 6f01321c1a..899138b164 100644 --- a/src/concurrency/genmc/dummy.rs +++ b/src/concurrency/genmc/dummy.rs @@ -253,26 +253,6 @@ impl GenmcCtx { } } -/// Other functionality not directly related to event handling -impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} -pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { - fn check_genmc_intercept_function( - &mut self, - _instance: rustc_middle::ty::Instance<'tcx>, - _args: &[rustc_const_eval::interpret::FnArg<'tcx, crate::Provenance>], - _dest: &crate::PlaceTy<'tcx>, - _ret: Option, - ) -> InterpResult<'tcx, bool> { - unreachable!() - } - - /**** Scheduling functionality ****/ - - fn genmc_schedule_thread(&mut self) -> InterpResult<'tcx, ThreadId> { - unreachable!() - } -} - impl VisitProvenance for GenmcCtx { fn visit_provenance(&self, _visit: &mut VisitWith<'_>) { unreachable!() diff --git a/src/concurrency/genmc/mod.rs b/src/concurrency/genmc/mod.rs index 0263482230..c62cebe24e 100644 --- a/src/concurrency/genmc/mod.rs +++ b/src/concurrency/genmc/mod.rs @@ -2,7 +2,6 @@ use std::cell::{Cell, RefCell}; use std::sync::Arc; use std::time::Duration; -use genmc_sys::cxx_extra::NonNullUniquePtr; use genmc_sys::{ ActionKind, GENMC_GLOBAL_ADDRESSES_MASK, GenmcScalar, MemOrdering, MiriGenMCShim, RMWBinOp, createGenmcHandle, @@ -20,9 +19,9 @@ use crate::concurrency::genmc::helper::{is_terminator_atomic_load, split_access} use crate::concurrency::genmc::warnings::WarningsCache; use crate::concurrency::thread::{EvalContextExt as _, ThreadState}; use crate::{ - AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, BlockReason, MachineCallback, - MemoryKind, MiriConfig, MiriInterpCx, MiriMachine, MiriMemoryKind, OpTy, Scalar, - TerminationInfo, ThreadId, ThreadManager, UnblockKind, VisitProvenance, VisitWith, callback, + AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, BlockReason, MemoryKind, + MiriConfig, MiriMachine, MiriMemoryKind, Scalar, TerminationInfo, ThreadId, ThreadManager, + VisitProvenance, VisitWith, }; mod config; @@ -979,138 +978,6 @@ impl GenmcCtx { /// Other functionality not directly related to event handling impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { - /// Given a `ty::Instance<'tcx>`, do any required special handling. Returns true if this `instance` should be skipped (i.e., no Mir should be executed for it). - fn check_genmc_intercept_function( - &mut self, - instance: rustc_middle::ty::Instance<'tcx>, - args: &[rustc_const_eval::interpret::FnArg<'tcx, crate::Provenance>], - dest: &crate::PlaceTy<'tcx>, - ret: Option, - ) -> InterpResult<'tcx, bool> { - let this = self.eval_context_mut(); - let genmc_ctx = this - .machine - .data_race - .as_genmc_ref() - .expect("This function should only be called in GenMC mode."); - - let get_mutex_call_infos = || { - // assert!(!args.is_empty()); - assert_eq!(args.len(), 1); - let arg = this.copy_fn_arg(&args[0]); - let addr = this.read_target_usize(&arg)?; - // FIXME(genmc): assert that we have at least 1 byte. - // FIXME(genmc): maybe use actual size of mutex here?. - - let thread_infos = genmc_ctx.thread_id_manager.borrow(); - let curr_thread = this.machine.threads.active_thread(); - let genmc_curr_thread = thread_infos.get_genmc_tid(curr_thread); - interp_ok((genmc_curr_thread, addr, 1)) - }; - - use rustc_span::sym; - if this.tcx.is_diagnostic_item(sym::sys_mutex_lock, instance.def_id()) { - info!("GenMC: handling Mutex::lock()"); - let (genmc_curr_thread, addr, size) = get_mutex_call_infos()?; - - let result = { - let mut mc = genmc_ctx.handle.borrow_mut(); - let pinned_mc = mc.as_mut(); - pinned_mc.handleMutexLock(genmc_curr_thread, addr, size) - }; - if let Some(error) = result.error.as_ref() { - throw_ub_format!("{}", error.to_string_lossy()); - } - // TODO GENMC(HACK): for determining if the Mutex lock blocks this thread. - if !result.is_lock_acquired { - fn create_callback<'tcx>( - genmc_curr_thread: i32, - addr: u64, - size: u64, - ) -> crate::DynUnblockCallback<'tcx> { - crate::callback!( - @capture<'tcx> { - // mutex_ref: MutexRef, - // retval_dest: Option<(Scalar, MPlaceTy<'tcx>)>, - genmc_curr_thread: i32, - addr: u64, - size: u64, - } - |this, unblock: crate::UnblockKind| { - assert_eq!(unblock, crate::UnblockKind::Ready); - let genmc_ctx = this.machine.data_race.as_genmc_ref().unwrap(); - - info!("GenMC: handling Mutex::lock: unblocking callback called!"); - let result = { - let mut mc = genmc_ctx.handle.borrow_mut(); - let pinned_mc = mc.as_mut(); - pinned_mc.handleMutexLock(genmc_curr_thread, addr, size) - }; - if let Some(error) = result.error.as_ref() { - throw_ub_format!("{}", error.to_string_lossy()); - } - // TODO GENMC(HACK): for determining if the Mutex lock blocks this thread. - if !result.is_lock_acquired { - // If this thread gets woken up without the mutex being made available, block the thread again. - this.block_thread( crate::BlockReason::Mutex, None, create_callback(genmc_curr_thread, addr, size)); - } - interp_ok(()) - } - ) - } - - info!("GenMC: handling Mutex::lock failed, blocking thread"); - // NOTE: We don't write anything back to Miri's memory, the Mutex state is handled only by GenMC. - - this.block_thread( - crate::BlockReason::Mutex, - None, - create_callback(genmc_curr_thread, addr, size), - ); - } else { - info!("GenMC: handling Mutex::lock: success: lock acquired."); - } - } else if this.tcx.is_diagnostic_item(sym::sys_mutex_try_lock, instance.def_id()) { - info!("GenMC: handling Mutex::try_lock()"); - let (genmc_curr_thread, addr, size) = get_mutex_call_infos()?; - - let result = { - let mut mc = genmc_ctx.handle.borrow_mut(); - let pinned_mc = mc.as_mut(); - pinned_mc.handleMutexTryLock(genmc_curr_thread, addr, size) - }; - if let Some(error) = result.error.as_ref() { - throw_ub_format!("{}", error.to_string_lossy()); - } - info!( - "GenMC: Mutex::try_lock(): writing resulting bool is_lock_acquired ({}) to place: {dest:?}", - result.is_lock_acquired - ); - - this.write_scalar(Scalar::from_bool(result.is_lock_acquired), dest)?; - // todo!("return whether lock was successful or not"); - } else if this.tcx.is_diagnostic_item(sym::sys_mutex_unlock, instance.def_id()) { - info!("GenMC: handling Mutex::unlock()"); - let (genmc_curr_thread, addr, size) = get_mutex_call_infos()?; - - let mut mc = genmc_ctx.handle.borrow_mut(); - let pinned_mc = mc.as_mut(); - let result = pinned_mc.handleMutexUnlock(genmc_curr_thread, addr, size); - if let Some(error) = result.error.as_ref() { - throw_ub_format!("{}", error.to_string_lossy()); - } - // NOTE: We don't write anything back to Miri's memory, the Mutex state is handled only by GenMC. - - // this.unblock_thread(, crate::BlockReason::Mutex)?; - } else { - return interp_ok(false); - }; - - this.return_to_block(ret)?; - - interp_ok(true) - } - /**** Scheduling functionality ****/ /// Ask for a scheduling decision. This should be called before every MIR instruction. diff --git a/src/machine.rs b/src/machine.rs index 43869b8351..cd72212e0b 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -30,9 +30,7 @@ use rustc_target::callconv::FnAbi; use crate::alloc_addresses::EvalContextExt; use crate::concurrency::cpu_affinity::{self, CpuAffinityMask}; use crate::concurrency::data_race::{self, NaReadType, NaWriteType}; -use crate::concurrency::{ - AllocDataRaceHandler, GenmcCtx, GenmcEvalContextExt as _, GlobalDataRaceHandler, weak_memory, -}; +use crate::concurrency::{AllocDataRaceHandler, GenmcCtx, GlobalDataRaceHandler, weak_memory}; use crate::*; /// First real-time signal. @@ -1129,12 +1127,6 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { return ecx.emulate_foreign_item(link_name, abi, &args, dest, ret, unwind); } - if ecx.machine.data_race.as_genmc_ref().is_some() - && ecx.check_genmc_intercept_function(instance, args, dest, ret)? - { - return interp_ok(None); - } - // Otherwise, load the MIR. interp_ok(Some((ecx.load_mir(instance.def, None)?, instance))) } diff --git a/tests/genmc/_disabled/mutex_poison.rs b/tests/genmc/_disabled/mutex_poison.rs deleted file mode 100644 index 64fe5c6db6..0000000000 --- a/tests/genmc/_disabled/mutex_poison.rs +++ /dev/null @@ -1,76 +0,0 @@ -//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows - -#![no_main] -#![feature(abort_unwind)] - -// #[path = "../../../utils/genmc.rs"] -// mod genmc; - -// use std::ffi::c_void; -use std::sync::{LockResult, Mutex}; - -// use crate::genmc::*; - -static LOCK: Mutex = Mutex::new(0); - -#[unsafe(no_mangle)] -fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - std::panic::abort_unwind(main_); - 0 -} - -fn main_() { - // let _ids = unsafe { create_pthreads_no_params( [thread_1, thread_2]) }; - // unsafe { join_pthreads(ids) }; - - let handle1 = std::thread::spawn(|| { - // let _err = std::panic::catch_unwind(|| { - let mut guard = LOCK.lock().unwrap(); - *guard = 0xDEADBEEF; - panic!(); - // }); - // drop(_err); - }); - - let handle2 = std::thread::spawn(|| { - // std::thread::sleep(std::time::Duration::from_millis(10)); // Let thread1 run first - match LOCK.lock() { - LockResult::Ok(mut value) => *value = 1234, - LockResult::Err(mut poison) => { - **poison.get_mut() = 42; - } - } - }); - - handle1.join().unwrap(); - handle2.join().unwrap(); - - // // Depending on the thread interleaving, the mutex might be poisoned. - // match LOCK.lock() { - // LockResult::Ok(value) => assert!(*value == 1234 || *value == 42), - // LockResult::Err(_poison) => {} - // } -} - -// // extern "C" fn thread_1(_: *mut c_void) -> *mut c_void { -// fn thread_1() { -// let _err = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { -// let mut guard = LOCK.lock().unwrap(); -// // Pretend whatever causes the crash fills the mutex with garbage values. -// *guard = 0xDEADBEEF; -// panic!(); -// })); -// Box::leak(Box::new(_err)); -// std::ptr::null_mut() -// } - -// // extern "C" fn thread_2(_: *mut c_void) -> *mut c_void { -// fn thread_2() { -// match LOCK.lock() { -// LockResult::Ok(mut value) => *value = 1234, -// LockResult::Err(mut poison) => { -// **poison.get_mut() = 42; -// } -// } -// std::ptr::null_mut() -// } diff --git a/tests/genmc/pass/atomic-primitives/arc.check_count.stderr b/tests/genmc/pass/atomic-primitives/arc.check_count.stderr deleted file mode 100644 index f7aacf6cda..0000000000 --- a/tests/genmc/pass/atomic-primitives/arc.check_count.stderr +++ /dev/null @@ -1,8 +0,0 @@ -warning: GenMC currently does not model the atomic failure ordering for `compare_exchange`. Failure ordering 'Relaxed' is treated like 'Acquire', which means that Miri might miss bugs related to this memory access (possible unsoundness)! - -warning: GenMC mode currently does not model spurious failures of `compare_exchange_weak`. This may lead to missed bugs (possible unsoundness)! - -warning: GenMC currently does not model the atomic failure ordering for `compare_exchange`. Failure ordering 'Acquire' is treated like 'Relaxed', which means that Miri might incorrectly detect errors related to this memory access (possible false positives). - -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 4 diff --git a/tests/genmc/pass/atomic-primitives/arc.rs b/tests/genmc/pass/atomic-primitives/arc.rs deleted file mode 100644 index 4ffe458775..0000000000 --- a/tests/genmc/pass/atomic-primitives/arc.rs +++ /dev/null @@ -1,55 +0,0 @@ -//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows -//@revisions: check_count try_upgrade - -// Check that various operations on `std::sync::Arc` are handled properly in GenMC mode. -// -// The number of explored executions in the expected output of this test may change if -// the implementation of `Arc` is ever changed, or additional optimizations are added to GenMC mode. -// -// The revision that tries to upgrade the `Weak` should never explore fewer executions compared to the revision that just accesses the `strong_count`, -// since `upgrade` needs to access the `strong_count` internally. -// There should always be more than 1 execution for both, since there is always the possibilility that the `Arc` has already been dropped, or it hasn't. - -#![no_main] - -use std::sync::Arc; -use std::thread; - -#[unsafe(no_mangle)] -fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - let data = Arc::new(42); - - // Clone the Arc, drop the original, check that memory still valid. - let data_clone = Arc::clone(&data); - drop(data); - assert!(*data_clone == 42); - - // Create a Weak reference. - let weak = Arc::downgrade(&data_clone); - - // Spawn a thread that uses the Arc. - let weak_ = weak.clone(); - let handle = thread::spawn(move || { - // Try to upgrade weak reference. - // Depending on execution schedule, this may fail or succeed depending on whether this runs before or after the `drop` in the main thread. - - #[cfg(check_count)] - let _strong_count = weak_.strong_count(); - - #[cfg(try_upgrade)] - if let Some(strong) = weak_.upgrade() { - assert_eq!(*strong, 42); - } - }); - - // Drop the last strong reference to the data. - drop(data_clone); - - // Wait for the thread to finish. - handle.join().unwrap(); - - // The upgrade should fail now (all Arcs dropped). - assert!(weak.upgrade().is_none()); - - 0 -} diff --git a/tests/genmc/pass/atomic-primitives/arc.try_upgrade.stderr b/tests/genmc/pass/atomic-primitives/arc.try_upgrade.stderr deleted file mode 100644 index 89b4da095c..0000000000 --- a/tests/genmc/pass/atomic-primitives/arc.try_upgrade.stderr +++ /dev/null @@ -1,8 +0,0 @@ -warning: GenMC currently does not model the atomic failure ordering for `compare_exchange`. Failure ordering 'Relaxed' is treated like 'Acquire', which means that Miri might miss bugs related to this memory access (possible unsoundness)! - -warning: GenMC mode currently does not model spurious failures of `compare_exchange_weak`. This may lead to missed bugs (possible unsoundness)! - -warning: GenMC currently does not model the atomic failure ordering for `compare_exchange`. Failure ordering 'Acquire' is treated like 'Relaxed', which means that Miri might incorrectly detect errors related to this memory access (possible false positives). - -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 7 diff --git a/tests/genmc/pass/atomic-primitives/mutex_simple.reps1.stderr b/tests/genmc/pass/atomic-primitives/mutex_simple.reps1.stderr deleted file mode 100644 index 01701dfe69..0000000000 --- a/tests/genmc/pass/atomic-primitives/mutex_simple.reps1.stderr +++ /dev/null @@ -1,2 +0,0 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 2 diff --git a/tests/genmc/pass/atomic-primitives/mutex_simple.reps2.stderr b/tests/genmc/pass/atomic-primitives/mutex_simple.reps2.stderr deleted file mode 100644 index be75e68fde..0000000000 --- a/tests/genmc/pass/atomic-primitives/mutex_simple.reps2.stderr +++ /dev/null @@ -1,2 +0,0 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 6 diff --git a/tests/genmc/pass/atomic-primitives/mutex_simple.rs b/tests/genmc/pass/atomic-primitives/mutex_simple.rs deleted file mode 100644 index 04030b90c2..0000000000 --- a/tests/genmc/pass/atomic-primitives/mutex_simple.rs +++ /dev/null @@ -1,70 +0,0 @@ -//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows -//@revisions: reps1 reps2 - -#![no_main] -#![feature(abort_unwind)] - -#[path = "../../../utils/genmc.rs"] -mod genmc; - -use std::ffi::c_void; -use std::sync::Mutex; - -use crate::genmc::*; - -#[cfg(not(reps2))] -const REPS: u64 = 1; -#[cfg(reps2)] -const REPS: u64 = 2; - -static LOCK: Mutex<[u64; 32]> = Mutex::new([1234; 32]); - -#[unsafe(no_mangle)] -fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - std::panic::abort_unwind(main_); - 0 -} - -fn main_() { - let mut guard = LOCK.lock().unwrap(); - for &v in guard.iter() { - assert!(v == 1234); // Check that mutex values are initialized correctly - } - guard[0] = 0; - guard[1] = 10; - assert!(guard[0] == 0 && guard[1] == 10); // Check if changes are accepted - - assert!(LOCK.try_lock().is_err()); // Trying to lock should fail if the lock is already held - - drop(guard); // Dropping the guard should unlock the mutex correctly. - { - assert!(LOCK.try_lock().is_ok()); // Trying to lock now should *not* fail since the lock is not held. - } - - let ids = unsafe { create_pthreads_no_params([thread_1, thread_2]) }; - unsafe { join_pthreads(ids) }; - - let guard = LOCK.lock().unwrap(); - assert!(guard[0] == REPS * 6); // Due to locking, no weird values should be here - assert!(guard[1] == 10); // Rest should be unchanged - for &v in guard.iter().skip(2) { - assert!(v == 1234); - } - drop(guard); -} - -extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { - for _ in 0..REPS { - let mut guard = LOCK.lock().unwrap(); - guard[0] += 2; - } - std::ptr::null_mut() -} - -extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { - for _ in 0..REPS { - let mut guard = LOCK.lock().unwrap(); - guard[0] += 4; - } - std::ptr::null_mut() -} From f888819e303056143f0304acb1f20932680a3764 Mon Sep 17 00:00:00 2001 From: Patrick-6 Date: Sat, 26 Jul 2025 14:26:30 +0200 Subject: [PATCH 27/41] Remove code for warning about missing GenMC support --- src/concurrency/genmc/mapping.rs | 11 ---- src/concurrency/genmc/mod.rs | 14 ---- src/concurrency/genmc/warnings.rs | 66 ------------------- tests/genmc/pass/thread/empty_main.stderr | 4 -- .../thread/miri_main_spawn_threads.stderr | 6 -- .../pass/thread/std_main_spawn_threads.stderr | 6 -- 6 files changed, 107 deletions(-) delete mode 100644 src/concurrency/genmc/warnings.rs diff --git a/src/concurrency/genmc/mapping.rs b/src/concurrency/genmc/mapping.rs index 714227488e..7a183e0d3e 100644 --- a/src/concurrency/genmc/mapping.rs +++ b/src/concurrency/genmc/mapping.rs @@ -36,17 +36,6 @@ impl AtomicFenceOrd { } impl AtomicRwOrd { - /// Split up an atomic read-write memory ordering into a separate read and write ordering. - pub(super) fn split_memory_orderings(self) -> (AtomicReadOrd, AtomicWriteOrd) { - match self { - AtomicRwOrd::Relaxed => (AtomicReadOrd::Relaxed, AtomicWriteOrd::Relaxed), - AtomicRwOrd::Acquire => (AtomicReadOrd::Acquire, AtomicWriteOrd::Relaxed), - AtomicRwOrd::Release => (AtomicReadOrd::Relaxed, AtomicWriteOrd::Release), - AtomicRwOrd::AcqRel => (AtomicReadOrd::Acquire, AtomicWriteOrd::Release), - AtomicRwOrd::SeqCst => (AtomicReadOrd::SeqCst, AtomicWriteOrd::SeqCst), - } - } - /// Split up the atomic success ordering of a read-modify-write operation into GenMC's representation. /// Note that both returned orderings are currently identical, because this is what GenMC expects. pub(super) fn to_genmc_memory_orderings(self) -> (MemOrdering, MemOrdering) { diff --git a/src/concurrency/genmc/mod.rs b/src/concurrency/genmc/mod.rs index c62cebe24e..1a1910ce6e 100644 --- a/src/concurrency/genmc/mod.rs +++ b/src/concurrency/genmc/mod.rs @@ -16,7 +16,6 @@ use self::helper::{genmc_scalar_to_scalar, scalar_to_genmc_scalar}; use self::mapping::{min_max_to_genmc_rmw_op, to_genmc_rmw_op}; use self::thread_id_map::ThreadIdMap; use crate::concurrency::genmc::helper::{is_terminator_atomic_load, split_access}; -use crate::concurrency::genmc::warnings::WarningsCache; use crate::concurrency::thread::{EvalContextExt as _, ThreadState}; use crate::{ AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, BlockReason, MemoryKind, @@ -30,7 +29,6 @@ mod helper; mod mapping; pub mod miri_genmc; mod thread_id_map; -mod warnings; pub use genmc_sys::GenmcParams; @@ -58,10 +56,6 @@ pub struct GenmcCtx { /// Keep track of global allocations, to ensure they keep the same address across different executions, even if the order of allocations changes. /// The `AllocId` for globals is stable across executions, so we can use it as an identifier. global_allocations: Arc, - // TODO GENMC: maybe make this a (base, size), maybe BTreeMap/sorted vector for reverse lookups - // GenMC needs to have access to that - // TODO: look at code of "pub struct GlobalStateInner" - warnings_cache: RefCell, exit_status: Cell>, } @@ -82,7 +76,6 @@ impl GenmcCtx { thread_id_manager: Default::default(), allow_data_races: Cell::new(false), global_allocations, - warnings_cache: Default::default(), exit_status: Cell::new(None), } } @@ -393,13 +386,6 @@ impl GenmcCtx { "atomic compare-exchange with data race checking disabled." ); - // FIXME(genmc): remove once GenMC supports failure memory ordering in `compare_exchange`. - self.warnings_cache.borrow_mut().warn_once_rmw_failure_ordering(&ecx.tcx, success, fail); - // FIXME(genmc): remove once GenMC implements spurious failures for `compare_exchange_weak`. - if can_fail_spuriously { - self.warnings_cache.borrow_mut().warn_once_compare_exchange_weak(&ecx.tcx); - } - let machine = &ecx.machine; let (success_load_ordering, success_store_ordering) = success.to_genmc_memory_orderings(); let fail_load_ordering = fail.convert(); diff --git a/src/concurrency/genmc/warnings.rs b/src/concurrency/genmc/warnings.rs deleted file mode 100644 index e8d3b2a922..0000000000 --- a/src/concurrency/genmc/warnings.rs +++ /dev/null @@ -1,66 +0,0 @@ -use rustc_data_structures::fx::FxHashSet; -use rustc_middle::query::TyCtxtAt; -use rustc_span::Span; - -use crate::{AtomicReadOrd, AtomicRwOrd}; - -#[derive(Default)] -pub struct WarningsCache { - emitted_compare_exchange_weak: FxHashSet, - emitted_compare_exchange_failure_ordering: FxHashSet<(Span, AtomicReadOrd, AtomicReadOrd)>, -} - -impl WarningsCache { - /// Warn about unsupported spurious failures of `compare_exchange_weak`, once per span, returning `true` if the warning was printed. - pub fn warn_once_compare_exchange_weak<'tcx>(&mut self, tcx: &TyCtxtAt<'tcx>) -> bool { - if self.emitted_compare_exchange_weak.insert(tcx.span) { - tcx.dcx().span_warn(tcx.span, "GenMC mode currently does not model spurious failures of `compare_exchange_weak`. This may lead to missed bugs (possible unsoundness)!"); - return true; - } - false - } - - /// Check if the given failure ordering is unsupported by GenMC. - /// Warning is printed only once per span and ordering combination. - /// Returns `true` if the warning was printed. - pub fn warn_once_rmw_failure_ordering<'tcx>( - &mut self, - tcx: &TyCtxtAt<'tcx>, - success_ordering: AtomicRwOrd, - failure_load_ordering: AtomicReadOrd, - ) -> bool { - let (success_load_ordering, _success_store_ordering) = - success_ordering.split_memory_orderings(); - let is_failure_ordering_weaker = match (success_load_ordering, failure_load_ordering) { - // Unsound: failure ordering is weaker than success ordering, but GenMC treats them as equally strong. - // Actual program execution might have behavior not modelled by GenMC: - (AtomicReadOrd::Acquire, AtomicReadOrd::Relaxed) - | (AtomicReadOrd::SeqCst, AtomicReadOrd::Relaxed) - | (AtomicReadOrd::SeqCst, AtomicReadOrd::Acquire) => true, - // Possible false positives: failure ordering is stronger than success ordering, but GenMC treats them as equally strong. - // We might explore executions that are not allowed by the program. - (AtomicReadOrd::Relaxed, AtomicReadOrd::Acquire) - | (AtomicReadOrd::Relaxed, AtomicReadOrd::SeqCst) - | (AtomicReadOrd::Acquire, AtomicReadOrd::SeqCst) => false, - // Correct: failure ordering is equally strong as success ordering: - (AtomicReadOrd::Relaxed, AtomicReadOrd::Relaxed) - | (AtomicReadOrd::Acquire, AtomicReadOrd::Acquire) - | (AtomicReadOrd::SeqCst, AtomicReadOrd::SeqCst) => return false, - }; - let key = (tcx.span, success_load_ordering, failure_load_ordering); - if self.emitted_compare_exchange_failure_ordering.insert(key) { - let error = if is_failure_ordering_weaker { - "miss bugs related to this memory access (possible unsoundness)!" - } else { - "incorrectly detect errors related to this memory access (possible false positives)." - }; - let msg = format!( - "GenMC currently does not model the atomic failure ordering for `compare_exchange`. Failure ordering '{failure_load_ordering:?}' is treated like '{success_load_ordering:?}', which means that Miri might {error}", - ); - // FIXME(genmc): this doesn't print a span: - tcx.dcx().span_warn(tcx.span, msg); - return true; - } - false - } -} diff --git a/tests/genmc/pass/thread/empty_main.stderr b/tests/genmc/pass/thread/empty_main.stderr index 1660e2431b..3b22247ee4 100644 --- a/tests/genmc/pass/thread/empty_main.stderr +++ b/tests/genmc/pass/thread/empty_main.stderr @@ -1,6 +1,2 @@ -warning: GenMC mode currently does not model spurious failures of `compare_exchange_weak`. This may lead to missed bugs (possible unsoundness)! - -warning: GenMC currently does not model the atomic failure ordering for `compare_exchange`. Failure ordering 'Relaxed' is treated like 'Acquire', which means that Miri might miss bugs related to this memory access (possible unsoundness)! - (GenMC) Verification complete. No errors were detected. Number of complete executions explored: 1 diff --git a/tests/genmc/pass/thread/miri_main_spawn_threads.stderr b/tests/genmc/pass/thread/miri_main_spawn_threads.stderr index 8e6a8b4056..3b22247ee4 100644 --- a/tests/genmc/pass/thread/miri_main_spawn_threads.stderr +++ b/tests/genmc/pass/thread/miri_main_spawn_threads.stderr @@ -1,8 +1,2 @@ -warning: GenMC currently does not model the atomic failure ordering for `compare_exchange`. Failure ordering 'Relaxed' is treated like 'Acquire', which means that Miri might miss bugs related to this memory access (possible unsoundness)! - -warning: GenMC mode currently does not model spurious failures of `compare_exchange_weak`. This may lead to missed bugs (possible unsoundness)! - -warning: GenMC currently does not model the atomic failure ordering for `compare_exchange`. Failure ordering 'Acquire' is treated like 'Relaxed', which means that Miri might incorrectly detect errors related to this memory access (possible false positives). - (GenMC) Verification complete. No errors were detected. Number of complete executions explored: 1 diff --git a/tests/genmc/pass/thread/std_main_spawn_threads.stderr b/tests/genmc/pass/thread/std_main_spawn_threads.stderr index ecfee88d60..3b22247ee4 100644 --- a/tests/genmc/pass/thread/std_main_spawn_threads.stderr +++ b/tests/genmc/pass/thread/std_main_spawn_threads.stderr @@ -1,8 +1,2 @@ -warning: GenMC mode currently does not model spurious failures of `compare_exchange_weak`. This may lead to missed bugs (possible unsoundness)! - -warning: GenMC currently does not model the atomic failure ordering for `compare_exchange`. Failure ordering 'Relaxed' is treated like 'Acquire', which means that Miri might miss bugs related to this memory access (possible unsoundness)! - -warning: GenMC currently does not model the atomic failure ordering for `compare_exchange`. Failure ordering 'Acquire' is treated like 'Relaxed', which means that Miri might incorrectly detect errors related to this memory access (possible false positives). - (GenMC) Verification complete. No errors were detected. Number of complete executions explored: 1 From 4ddc72e8cbdb14ca71039fefb3797e960a282b96 Mon Sep 17 00:00:00 2001 From: Patrick-6 Date: Sat, 26 Jul 2025 14:33:57 +0200 Subject: [PATCH 28/41] Remove graph printing --- genmc-sys/src/lib.rs | 1 - genmc-sys/src_cpp/MiriInterface.hpp | 2 -- src/bin/miri.rs | 2 -- src/concurrency/genmc/config.rs | 8 -------- src/concurrency/genmc/dummy.rs | 8 -------- src/concurrency/genmc/miri_genmc.rs | 10 ++-------- src/concurrency/genmc/mod.rs | 5 ----- 7 files changed, 2 insertions(+), 34 deletions(-) diff --git a/genmc-sys/src/lib.rs b/genmc-sys/src/lib.rs index 3e90d51f59..817a0764a8 100644 --- a/genmc-sys/src/lib.rs +++ b/genmc-sys/src/lib.rs @@ -308,7 +308,6 @@ mod ffi { /// If there are more executions, this method prepares for the next execution and returns `true`. fn isExplorationDone(self: Pin<&mut MiriGenMCShim>) -> bool; - fn printGraph(self: Pin<&mut MiriGenMCShim>); fn printEstimationResults(self: &MiriGenMCShim, elapsed_time_sec: f64); } } diff --git a/genmc-sys/src_cpp/MiriInterface.hpp b/genmc-sys/src_cpp/MiriInterface.hpp index 66acc75ad3..c1093af2f0 100644 --- a/genmc-sys/src_cpp/MiriInterface.hpp +++ b/genmc-sys/src_cpp/MiriInterface.hpp @@ -141,8 +141,6 @@ struct MiriGenMCShim : private GenMCDriver { /**** Printing and estimation mode functionality. ****/ - void printGraph() { GenMCDriver::debugPrintGraph(); } - void printEstimationResults(const double elapsed_time_sec) const { // FIXME(GenMC,cleanup): should this happen on the Rust side? diff --git a/src/bin/miri.rs b/src/bin/miri.rs index b1422f31fb..445aaf21d9 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -196,7 +196,6 @@ impl rustc_driver::Callbacks for MiriCompilerCalls { if genmc_config.do_estimation() && miri_genmc::run_genmc_mode( &config, - genmc_config, eval_entry_once, target_usize_max, miri_genmc::Mode::Estimation, @@ -210,7 +209,6 @@ impl rustc_driver::Callbacks for MiriCompilerCalls { let return_code = miri_genmc::run_genmc_mode( &config, - genmc_config, eval_entry_once, target_usize_max, miri_genmc::Mode::Verification, diff --git a/src/concurrency/genmc/config.rs b/src/concurrency/genmc/config.rs index dd1dacdcfa..3a38cc4199 100644 --- a/src/concurrency/genmc/config.rs +++ b/src/concurrency/genmc/config.rs @@ -6,15 +6,10 @@ use super::GenmcParams; #[derive(Debug, Default, Clone)] pub struct GenmcConfig { pub(super) params: GenmcParams, - print_exec_graphs: bool, do_estimation: bool, } impl GenmcConfig { - pub fn print_exec_graphs(&self) -> bool { - self.print_exec_graphs - } - pub fn do_estimation(&self) -> bool { self.do_estimation } @@ -43,9 +38,6 @@ impl GenmcConfig { }; if let Some(log_level) = trimmed_arg.strip_prefix("log=") { genmc_config.params.log_level = log_level.parse()?; - } else if trimmed_arg == "print-graphs" { - // TODO GENMC (DOCUMENTATION) - genmc_config.print_exec_graphs = true; } else if trimmed_arg == "estimate" { // TODO GENMC (DOCUMENTATION): naming, off/on by default? genmc_config.do_estimation = true; diff --git a/src/concurrency/genmc/dummy.rs b/src/concurrency/genmc/dummy.rs index 899138b164..01765d6bd1 100644 --- a/src/concurrency/genmc/dummy.rs +++ b/src/concurrency/genmc/dummy.rs @@ -52,10 +52,6 @@ impl GenmcCtx { unreachable!() } - pub fn print_genmc_graph(&self) { - unreachable!() - } - pub fn is_exploration_done(&self) -> bool { unreachable!() } @@ -271,10 +267,6 @@ impl GenmcConfig { } } - pub fn print_exec_graphs(&self) -> bool { - unreachable!() - } - pub fn do_estimation(&self) -> bool { unreachable!() } diff --git a/src/concurrency/genmc/miri_genmc.rs b/src/concurrency/genmc/miri_genmc.rs index eb769c5fd1..b2a8f11c7e 100644 --- a/src/concurrency/genmc/miri_genmc.rs +++ b/src/concurrency/genmc/miri_genmc.rs @@ -2,7 +2,7 @@ use std::fmt::Display; use std::rc::Rc; use std::time::Instant; -use crate::{GenmcConfig, GenmcCtx, MiriConfig}; +use crate::{GenmcCtx, MiriConfig}; #[derive(Clone, Copy, PartialEq, Eq)] pub enum Mode { @@ -28,7 +28,6 @@ impl Display for Mode { /// Returns `None` is an error is detected, or `Some(return_value)` with the return value of the last run of the program. pub fn run_genmc_mode( config: &MiriConfig, - genmc_config: &GenmcConfig, eval_entry: impl Fn(Rc) -> Option, target_usize_max: u64, mode: Mode, @@ -41,12 +40,7 @@ pub fn run_genmc_mode( // Execute the program until completion or an error happens: let result = eval_entry(genmc_ctx.clone()); - // We always print the graph when requested, even on errors. - // This may not be needed if Miri makes use of GenMC's error message at some point, since it already includes the graph. - // FIXME(genmc): Currently GenMC is missing some info from Miri to be able to fully print the execution graph. - if genmc_config.print_exec_graphs() { - genmc_ctx.print_genmc_graph(); - } + // FIXME(genmc) print execution graph here (if requested). // Return if there was an error, or get the return code of the program. let return_code = result?; diff --git a/src/concurrency/genmc/mod.rs b/src/concurrency/genmc/mod.rs index 1a1910ce6e..17baca7ab2 100644 --- a/src/concurrency/genmc/mod.rs +++ b/src/concurrency/genmc/mod.rs @@ -114,11 +114,6 @@ impl GenmcCtx { .expect("there should always be a message") } - pub fn print_genmc_graph(&self) { - let mut mc = self.handle.borrow_mut(); - mc.as_mut().printGraph(); - } - /// This function determines if we should continue exploring executions or if we are done. /// /// In GenMC mode, the input program should be repeatedly executed until this function returns `true` or an error is found. From f95019ceb45cbc9b947b52a62c2e7c280a968a01 Mon Sep 17 00:00:00 2001 From: Patrick-6 Date: Sat, 26 Jul 2025 14:45:50 +0200 Subject: [PATCH 29/41] Remove estimation mode. --- genmc-sys/src/lib.rs | 8 +-- genmc-sys/src_cpp/MiriInterface.hpp | 60 +++++++------------ .../src_cpp/MiriInterface/MiriInterface.cpp | 25 ++++---- src/bin/miri.rs | 18 +----- src/concurrency/genmc/config.rs | 18 ------ src/concurrency/genmc/dummy.rs | 20 +------ src/concurrency/genmc/miri_genmc.rs | 34 ++--------- src/concurrency/genmc/mod.rs | 11 +--- 8 files changed, 44 insertions(+), 150 deletions(-) diff --git a/genmc-sys/src/lib.rs b/genmc-sys/src/lib.rs index 817a0764a8..d749999a65 100644 --- a/genmc-sys/src/lib.rs +++ b/genmc-sys/src/lib.rs @@ -38,7 +38,6 @@ impl Default for GenmcParams { print_random_schedule_seed: false, log_level: Default::default(), do_symmetry_reduction: false, // TODO GENMC (PERFORMANCE): maybe make this default `true` - estimation_max: 1000, } } } @@ -78,7 +77,6 @@ mod ffi { pub print_random_schedule_seed: bool, pub log_level: LogLevel, pub do_symmetry_reduction: bool, - pub estimation_max: u32, } /// This is mostly equivalent to GenMC `VerbosityLevel`, but the debug log levels are always present (not conditionally compiled based on `ENABLE_GENMC_DEBUG`). @@ -215,11 +213,9 @@ mod ffi { type GenmcScalar; - // type OperatingMode; // Estimation(budget) or Verification - type MiriGenMCShim; - fn createGenmcHandle(config: &GenmcParams, do_estimation: bool) + fn createGenmcHandle(config: &GenmcParams) -> UniquePtr; fn getGlobalAllocStaticMask() -> u64; @@ -307,7 +303,5 @@ mod ffi { /// Check whether there are more executions to explore. /// If there are more executions, this method prepares for the next execution and returns `true`. fn isExplorationDone(self: Pin<&mut MiriGenMCShim>) -> bool; - - fn printEstimationResults(self: &MiriGenMCShim, elapsed_time_sec: f64); } } diff --git a/genmc-sys/src_cpp/MiriInterface.hpp b/genmc-sys/src_cpp/MiriInterface.hpp index c1093af2f0..7c6efc3f5e 100644 --- a/genmc-sys/src_cpp/MiriInterface.hpp +++ b/genmc-sys/src_cpp/MiriInterface.hpp @@ -35,7 +35,8 @@ using AnnotT = SExpr; // TODO GENMC: fix naming conventions -struct MiriGenMCShim : private GenMCDriver { +struct MiriGenMCShim : private GenMCDriver +{ public: MiriGenMCShim(std::shared_ptr conf, Mode mode /* = VerificationMode{} */) @@ -53,17 +54,17 @@ struct MiriGenMCShim : private GenMCDriver { /////////////////// [[nodiscard]] LoadResult handleLoad(ThreadId thread_id, uint64_t address, uint64_t size, - MemOrdering ord, GenmcScalar old_val); + MemOrdering ord, GenmcScalar old_val); [[nodiscard]] ReadModifyWriteResult handleReadModifyWrite(ThreadId thread_id, uint64_t address, uint64_t size, - MemOrdering loadOrd, MemOrdering store_ordering, RMWBinOp rmw_op, - GenmcScalar rhs_value, GenmcScalar old_val); + MemOrdering loadOrd, MemOrdering store_ordering, RMWBinOp rmw_op, + GenmcScalar rhs_value, GenmcScalar old_val); [[nodiscard]] CompareExchangeResult handleCompareExchange(ThreadId thread_id, uint64_t address, uint64_t size, - GenmcScalar expected_value, GenmcScalar new_value, - GenmcScalar old_val, MemOrdering success_load_ordering, - MemOrdering success_store_ordering, MemOrdering fail_load_ordering, - bool can_fail_spuriously); + GenmcScalar expected_value, GenmcScalar new_value, + GenmcScalar old_val, MemOrdering success_load_ordering, + MemOrdering success_store_ordering, MemOrdering fail_load_ordering, + bool can_fail_spuriously); [[nodiscard]] StoreResult handleStore(ThreadId thread_id, uint64_t address, uint64_t size, GenmcScalar value, GenmcScalar old_val, MemOrdering ord); @@ -139,32 +140,7 @@ struct MiriGenMCShim : private GenMCDriver { return nullptr; } - /**** Printing and estimation mode functionality. ****/ - - void printEstimationResults(const double elapsed_time_sec) const - { - // FIXME(GenMC,cleanup): should this happen on the Rust side? - const auto &res = getResult(); - const auto *conf = getConf(); - - long long mean = std::llround(res.estimationMean); - long long sd = std::llround(std::sqrt(res.estimationVariance)); - long double meanTimeSecs = - (long double)elapsed_time_sec / (res.explored + res.exploredBlocked); - PRINT(VerbosityLevel::Error) - << "Finished estimation in " << std::format("%.2f", elapsed_time_sec) - << " seconds.\n\n" - << std::format("Total executions estimate: {} (+- {})\n", mean, sd) - << "Time to completion estimate: " - << std::format("%.2f", meanTimeSecs * mean) << "s\n"; - GENMC_DEBUG(if (conf->printEstimationStats) PRINT(VerbosityLevel::Error) - << "Estimation moot: " << res.exploredMoot << "\n" - << "Estimation blocked: " << res.exploredBlocked << "\n" - << "Estimation complete: " << res.explored << "\n";); - } - - static std::unique_ptr createHandle(const GenmcParams &config, - bool estimation_mode); + static std::unique_ptr createHandle(const GenmcParams &config); private: /** @@ -181,7 +157,8 @@ struct MiriGenMCShim : private GenMCDriver { // TODO GENMC(CLEANUP): Pass this as a parameter: auto &g = getExec().getGraph(); auto *coLab = g.co_max(addr); - if (auto *wLab = llvm::dyn_cast(coLab)) { + if (auto *wLab = llvm::dyn_cast(coLab)) + { LOG(VerbosityLevel::Warning) << "handleOldVal: got WriteLabel, atomic: " << !wLab->isNotAtomic() << "\n"; @@ -192,8 +169,11 @@ struct MiriGenMCShim : private GenMCDriver { "reads-from label, but old value is `uninit`\n"; else if (wLab->isNotAtomic()) wLab->setVal(value.toSVal()); - } else if (const auto *wLab = llvm::dyn_cast(coLab)) { - if (value.is_init) { + } + else if (const auto *wLab = llvm::dyn_cast(coLab)) + { + if (value.is_init) + { auto result = initVals_.insert(std::make_pair(addr, value)); LOG(VerbosityLevel::Warning) << "handleOldVal: got InitLabel, insertion result: " @@ -207,7 +187,9 @@ struct MiriGenMCShim : private GenMCDriver { "value, but old " "value is `uninit`\n"; } - } else { + } + else + { BUG(); /* Invalid label */ } // either initLabel ==> update initValGetter @@ -260,7 +242,7 @@ struct MiriGenMCShim : private GenMCDriver { // NOTE: CXX doesn't seem to support exposing static methods to Rust, so we expose this // function instead -std::unique_ptr createGenmcHandle(const GenmcParams &config, bool estimation_mode); +std::unique_ptr createGenmcHandle(const GenmcParams &config); constexpr auto getGlobalAllocStaticMask() -> uint64_t { return SAddr::staticMask; } diff --git a/genmc-sys/src_cpp/MiriInterface/MiriInterface.cpp b/genmc-sys/src_cpp/MiriInterface/MiriInterface.cpp index ee08f5b6ef..0cee10233c 100644 --- a/genmc-sys/src_cpp/MiriInterface/MiriInterface.cpp +++ b/genmc-sys/src_cpp/MiriInterface/MiriInterface.cpp @@ -36,7 +36,7 @@ // FIXME(genmc,cxx): could directly return std::optional if CXX ever supports sharing it (see // https://github.com/dtolnay/cxx/issues/87). auto MiriGenMCShim::scheduleNext(const int curr_thread_id, - const ActionKind curr_thread_next_instr_kind) -> int64_t + const ActionKind curr_thread_next_instr_kind) -> int64_t { // The current thread is the only one where the `kind` could have changed since we last made // a scheduling decision. @@ -50,7 +50,7 @@ auto MiriGenMCShim::scheduleNext(const int curr_thread_id, /**** Functions available to Miri ****/ // NOLINTNEXTLINE(readability-convert-member-functions-to-static) -auto MiriGenMCShim::createHandle(const GenmcParams &config, bool estimation_mode) +auto MiriGenMCShim::createHandle(const GenmcParams &config) -> std::unique_ptr { auto conf = std::make_shared(); @@ -95,10 +95,8 @@ auto MiriGenMCShim::createHandle(const GenmcParams &config, bool estimation_mode // FIXME(genmc): expose this setting to Miri (useful for testing Miri-GenMC). conf->schedulePolicy = SchedulePolicy::WF; - conf->estimate = estimation_mode; - conf->estimationMax = config.estimation_max; - const auto mode = conf->estimate ? GenMCDriver::Mode(GenMCDriver::EstimationMode{}) - : GenMCDriver::Mode(GenMCDriver::VerificationMode{}); + conf->estimate = false; + const auto mode = GenMCDriver::Mode(GenMCDriver::VerificationMode{}); // Running Miri-GenMC without race detection is not supported. // Disabling this option also changes the behavior of the replay scheduler to only schedule @@ -119,7 +117,8 @@ auto MiriGenMCShim::createHandle(const GenmcParams &config, bool estimation_mode auto driver = std::make_unique(std::move(conf), mode); auto *driverPtr = driver.get(); - auto initValGetter = [driverPtr](const AAccess &access) { + auto initValGetter = [driverPtr](const AAccess &access) + { const auto addr = access.getAddr(); if (!driverPtr->initVals_.contains(addr)) { LOG(VerbosityLevel::Warning) @@ -147,10 +146,10 @@ auto MiriGenMCShim::createHandle(const GenmcParams &config, bool estimation_mode // This needs to be available to Miri, but clang-tidy wants it static // NOLINTNEXTLINE(misc-use-internal-linkage) -auto createGenmcHandle(const GenmcParams &config, bool estimation_mode) +auto createGenmcHandle(const GenmcParams &config) -> std::unique_ptr { - return MiriGenMCShim::createHandle(config, estimation_mode); + return MiriGenMCShim::createHandle(config); } /**** Execution start/end handling ****/ @@ -221,7 +220,7 @@ void MiriGenMCShim::handleThreadKill(ThreadId thread_id) /**** Memory access handling ****/ [[nodiscard]] auto MiriGenMCShim::handleLoad(ThreadId thread_id, uint64_t address, uint64_t size, - MemOrdering ord, GenmcScalar old_val) -> LoadResult + MemOrdering ord, GenmcScalar old_val) -> LoadResult { const auto addr = SAddr(address); const auto aSize = ASize(size); @@ -243,9 +242,9 @@ void MiriGenMCShim::handleThreadKill(ThreadId thread_id) } [[nodiscard]] auto MiriGenMCShim::handleReadModifyWrite(ThreadId thread_id, uint64_t address, - uint64_t size, MemOrdering loadOrd, - MemOrdering store_ordering, RMWBinOp rmw_op, - GenmcScalar rhs_value, GenmcScalar old_val) + uint64_t size, MemOrdering loadOrd, + MemOrdering store_ordering, RMWBinOp rmw_op, + GenmcScalar rhs_value, GenmcScalar old_val) -> ReadModifyWriteResult { const auto addr = SAddr(address); diff --git a/src/bin/miri.rs b/src/bin/miri.rs index 445aaf21d9..d19d3db670 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -186,32 +186,18 @@ impl rustc_driver::Callbacks for MiriCompilerCalls { optimizations is usually marginal at best."); } - if let Some(genmc_config) = &config.genmc_config { + if let Some(_genmc_config) = &config.genmc_config { let target_usize_max = tcx.target_usize_max(); let eval_entry_once = |genmc_ctx: Rc| { miri::eval_entry(tcx, entry_def_id, entry_type, &config, Some(genmc_ctx)) }; - // Estimate the execution space and runtime, if enabled. - if genmc_config.do_estimation() - && miri_genmc::run_genmc_mode( - &config, - eval_entry_once, - target_usize_max, - miri_genmc::Mode::Estimation, - ) - .is_none() - { - // We might already find an error during estimation, then we should stop here. - tcx.dcx().abort_if_errors(); - exit(rustc_driver::EXIT_FAILURE); - } + // FIXME(genmc): add estimation mode here. let return_code = miri_genmc::run_genmc_mode( &config, eval_entry_once, target_usize_max, - miri_genmc::Mode::Verification, ) .unwrap_or_else(|| { tcx.dcx().abort_if_errors(); diff --git a/src/concurrency/genmc/config.rs b/src/concurrency/genmc/config.rs index 3a38cc4199..df8843ec86 100644 --- a/src/concurrency/genmc/config.rs +++ b/src/concurrency/genmc/config.rs @@ -6,14 +6,9 @@ use super::GenmcParams; #[derive(Debug, Default, Clone)] pub struct GenmcConfig { pub(super) params: GenmcParams, - do_estimation: bool, } impl GenmcConfig { - pub fn do_estimation(&self) -> bool { - self.do_estimation - } - /// Function for parsing command line options for GenMC mode. /// /// All GenMC arguments start with the string "-Zmiri-genmc". @@ -38,19 +33,6 @@ impl GenmcConfig { }; if let Some(log_level) = trimmed_arg.strip_prefix("log=") { genmc_config.params.log_level = log_level.parse()?; - } else if trimmed_arg == "estimate" { - // TODO GENMC (DOCUMENTATION): naming, off/on by default? - genmc_config.do_estimation = true; - } else if let Some(estimation_max_str) = trimmed_arg.strip_prefix("estimation-max=") { - // TODO GENMC (DOCUMENTATION) - let Some(estimation_max) = - estimation_max_str.parse().ok().filter(|estimation_max| *estimation_max > 0) - else { - return Err(format!( - "-Zmiri-genmc-estimation-max expects a positive integer argument, but got '{estimation_max_str}'" - )); - }; - genmc_config.params.estimation_max = estimation_max; } else if trimmed_arg == "symmetry-reduction" { // TODO GENMC (PERFORMANCE): maybe make this the default, have an option to turn it off instead genmc_config.params.do_symmetry_reduction = true; diff --git a/src/concurrency/genmc/dummy.rs b/src/concurrency/genmc/dummy.rs index 01765d6bd1..0dc6b247d8 100644 --- a/src/concurrency/genmc/dummy.rs +++ b/src/concurrency/genmc/dummy.rs @@ -4,7 +4,7 @@ use rustc_middle::mir; use crate::{ AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, MemoryKind, MiriConfig, - MiriMachine, OpTy, Scalar, ThreadId, ThreadManager, VisitProvenance, VisitWith, + MiriMachine, Scalar, ThreadId, ThreadManager, VisitProvenance, VisitWith, }; #[derive(Debug)] @@ -16,20 +16,12 @@ pub struct GenmcConfig {} pub mod miri_genmc { use std::rc::Rc; - use crate::{GenmcConfig, GenmcCtx, MiriConfig}; - - #[derive(Clone, Copy, PartialEq, Eq)] - pub enum Mode { - Estimation, - Verification, - } + use crate::{GenmcCtx, MiriConfig}; pub fn run_genmc_mode( _config: &MiriConfig, - _genmc_config: &GenmcConfig, _eval_entry: impl Fn(Rc) -> Option, _target_usize_max: u64, - _mode: Mode, ) -> Option { unreachable!(); } @@ -40,10 +32,6 @@ impl GenmcCtx { unreachable!() } - pub fn print_estimation_result(&self) { - unreachable!() - } - pub fn get_blocked_execution_count(&self) -> usize { unreachable!() } @@ -266,8 +254,4 @@ impl GenmcConfig { Err(format!("GenMC is not supported on this target")) } } - - pub fn do_estimation(&self) -> bool { - unreachable!() - } } diff --git a/src/concurrency/genmc/miri_genmc.rs b/src/concurrency/genmc/miri_genmc.rs index b2a8f11c7e..dfd2e5702a 100644 --- a/src/concurrency/genmc/miri_genmc.rs +++ b/src/concurrency/genmc/miri_genmc.rs @@ -1,39 +1,19 @@ -use std::fmt::Display; use std::rc::Rc; -use std::time::Instant; use crate::{GenmcCtx, MiriConfig}; -#[derive(Clone, Copy, PartialEq, Eq)] -pub enum Mode { - Estimation, - Verification, -} - -impl Display for Mode { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str(match self { - Mode::Estimation => "Estimation", - Mode::Verification => "Verification", - }) - } -} - /// Do a complete run of the program in GenMC mode. /// This will call `eval_entry` multiple times, until either: /// - An error is detected -/// - All possible executions are explored (in `Mode::Verification`) -/// - Enough executions are explored to estimated the total number of executions (in `Mode::Estimation`) +/// - All possible executions are explored. /// -/// Returns `None` is an error is detected, or `Some(return_value)` with the return value of the last run of the program. +/// FIXME(genmc): add estimation mode setting. pub fn run_genmc_mode( config: &MiriConfig, eval_entry: impl Fn(Rc) -> Option, target_usize_max: u64, - mode: Mode, ) -> Option { - let time_start = Instant::now(); - let genmc_ctx = Rc::new(GenmcCtx::new(config, target_usize_max, mode)); + let genmc_ctx = Rc::new(GenmcCtx::new(config, target_usize_max)); for rep in 0u64.. { tracing::info!("Miri-GenMC loop {}", rep + 1); @@ -58,13 +38,7 @@ pub fn run_genmc_mode( continue; } - eprintln!("(GenMC) {mode} complete. No errors were detected.",); - - if mode == Mode::Estimation && return_code == 0 { - let elapsed_time = Instant::now().duration_since(time_start); - genmc_ctx.print_estimation_result(elapsed_time); - return Some(0); - } + eprintln!("(GenMC) Verification complete. No errors were detected."); let explored_execution_count = genmc_ctx.get_explored_execution_count(); let blocked_execution_count = genmc_ctx.get_blocked_execution_count(); diff --git a/src/concurrency/genmc/mod.rs b/src/concurrency/genmc/mod.rs index 17baca7ab2..d062cc918e 100644 --- a/src/concurrency/genmc/mod.rs +++ b/src/concurrency/genmc/mod.rs @@ -1,6 +1,5 @@ use std::cell::{Cell, RefCell}; use std::sync::Arc; -use std::time::Duration; use genmc_sys::{ ActionKind, GENMC_GLOBAL_ADDRESSES_MASK, GenmcScalar, MemOrdering, MiriGenMCShim, RMWBinOp, @@ -63,11 +62,11 @@ pub struct GenmcCtx { /// GenMC Context creation and administrative / query actions impl GenmcCtx { /// Create a new `GenmcCtx` from a given config. - pub fn new(miri_config: &MiriConfig, target_usize_max: u64, mode: miri_genmc::Mode) -> Self { + pub fn new(miri_config: &MiriConfig, target_usize_max: u64) -> Self { let genmc_config = miri_config.genmc_config.as_ref().unwrap(); info!("GenMC: Creating new GenMC Context"); - let handle = createGenmcHandle(&genmc_config.params, mode == miri_genmc::Mode::Estimation); + let handle = createGenmcHandle(&genmc_config.params); let non_null_handle = NonNullUniquePtr::new(handle).expect("GenMC should not return null"); let non_null_handle = RefCell::new(non_null_handle); let global_allocations = Arc::new(GlobalAllocationHandler::new(target_usize_max)); @@ -80,12 +79,6 @@ impl GenmcCtx { } } - pub fn print_estimation_result(&self, elapsed_time: Duration) { - let elapsed_time_sec = elapsed_time.as_secs_f64(); - let mc = self.handle.borrow(); - mc.as_ref().printEstimationResults(elapsed_time_sec); - } - pub fn get_blocked_execution_count(&self) -> u64 { let mc = self.handle.borrow(); mc.as_ref().getBlockedExecutionCount() From d0cfee5817bf25e0090ab9c75c3b53b5f6b05678 Mon Sep 17 00:00:00 2001 From: Patrick-6 Date: Sat, 26 Jul 2025 14:57:31 +0200 Subject: [PATCH 30/41] Simplify scheduling and next instruction type determination, do cleanup --- genmc-sys/src/lib.rs | 2 +- src/concurrency/data_race.rs | 3 +- src/concurrency/genmc/dummy.rs | 9 +++++ src/concurrency/genmc/helper.rs | 62 ++------------------------------- src/concurrency/genmc/mod.rs | 61 ++++---------------------------- src/concurrency/mod.rs | 2 +- src/concurrency/thread.rs | 15 ++------ 7 files changed, 25 insertions(+), 129 deletions(-) diff --git a/genmc-sys/src/lib.rs b/genmc-sys/src/lib.rs index d749999a65..6bb8ba3db7 100644 --- a/genmc-sys/src/lib.rs +++ b/genmc-sys/src/lib.rs @@ -37,7 +37,7 @@ impl Default for GenmcParams { Self { print_random_schedule_seed: false, log_level: Default::default(), - do_symmetry_reduction: false, // TODO GENMC (PERFORMANCE): maybe make this default `true` + do_symmetry_reduction: false, } } } diff --git a/src/concurrency/data_race.rs b/src/concurrency/data_race.rs index c9112cda08..5ddbe9e376 100644 --- a/src/concurrency/data_race.rs +++ b/src/concurrency/data_race.rs @@ -71,7 +71,7 @@ pub enum AtomicRwOrd { } /// Valid atomic read orderings, subset of atomic::Ordering. -#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] +#[derive(Copy, Clone, PartialEq, Eq, Debug)] pub enum AtomicReadOrd { Relaxed, Acquire, @@ -1016,7 +1016,6 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { if let Some(data_race) = this.machine.data_race.as_vclocks_ref() { data_race.acquire_clock(clock, &this.machine.threads); } - // TODO GENMC: does GenMC need to be informed about this? } } diff --git a/src/concurrency/genmc/dummy.rs b/src/concurrency/genmc/dummy.rs index 0dc6b247d8..6676add4d0 100644 --- a/src/concurrency/genmc/dummy.rs +++ b/src/concurrency/genmc/dummy.rs @@ -225,6 +225,15 @@ impl GenmcCtx { unreachable!() } + /**** Scheduling functionality ****/ + + pub fn schedule_thread<'tcx>( + &self, + _ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, + ) -> InterpResult<'tcx, ThreadId> { + unreachable!() + } + /**** Blocking instructions ****/ #[allow(unused)] diff --git a/src/concurrency/genmc/helper.rs b/src/concurrency/genmc/helper.rs index 7e3ce1cf30..345d00a562 100644 --- a/src/concurrency/genmc/helper.rs +++ b/src/concurrency/genmc/helper.rs @@ -1,14 +1,11 @@ use rustc_abi::Size; -use rustc_const_eval::interpret::{InterpCx, InterpResult, interp_ok}; -use rustc_middle::mir::{Terminator, TerminatorKind}; -use rustc_middle::ty::{self, ScalarInt, Ty}; +use rustc_const_eval::interpret::{InterpResult, interp_ok}; +use rustc_middle::ty::ScalarInt; use tracing::info; use super::GenmcScalar; use crate::alloc_addresses::EvalContextExt as _; -use crate::{ - BorTag, MiriInterpCx, MiriMachine, Pointer, Provenance, Scalar, ThreadId, throw_unsup_format, -}; +use crate::{BorTag, MiriInterpCx, Pointer, Provenance, Scalar, throw_unsup_format}; /// This function is used to split up a large memory access into aligned, non-overlapping chunks of a limited size. /// Returns an iterator over the chunks, yielding `(base address, size)` of each chunk, ordered by address. @@ -126,56 +123,3 @@ pub fn genmc_scalar_to_scalar<'tcx>( let (value_scalar_int, _got_truncated) = ScalarInt::truncate_from_uint(scalar.value, size); interp_ok(Scalar::Int(value_scalar_int)) } - -/// Check if a MIR terminator could be an atomic load operation. -/// Currently this check is very conservative; all atomics are seen as possibly being loads. -/// NOTE: This function panics if called with a thread that is not currently the active one. -pub fn is_terminator_atomic_load<'tcx>( - ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, - terminator: &Terminator<'tcx>, - thread_id: ThreadId, -) -> InterpResult<'tcx, bool> { - assert_eq!( - thread_id, - ecx.machine.threads.active_thread(), - "Can only call this function on the active thread." - ); - match &terminator.kind { - // All atomics are modeled as function calls to intrinsic functions. - // The one exception is thread joining, but those are also calls. - TerminatorKind::Call { func, .. } | TerminatorKind::TailCall { func, .. } => { - let frame = ecx.machine.threads.active_thread_stack().last().unwrap(); - let func_ty = func.ty(&frame.body().local_decls, *ecx.tcx); - info!("GenMC: terminator is a call with operand: {func:?}, ty of operand: {func_ty:?}"); - - has_function_atomic_load_semantics(ecx, func_ty) - } - _ => interp_ok(false), - } -} - -/// Check if a call or tail-call could have atomic load semantics. -fn has_function_atomic_load_semantics<'tcx>( - ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, - func_ty: Ty<'tcx>, -) -> InterpResult<'tcx, bool> { - let callee_def_id = match func_ty.kind() { - ty::FnDef(def_id, _args) => def_id, - _ => return interp_ok(true), // we don't know the callee, might be an intrinsic or pthread_join - }; - if ecx.tcx.is_foreign_item(*callee_def_id) { - // Some shims, like pthread_join, must be considered loads. So just consider them all loads, - // these calls are not *that* common. - return interp_ok(true); - } - - let Some(intrinsic_def) = ecx.tcx.intrinsic(callee_def_id) else { - // FIXME(genmc): Make this work for other platforms. - let item_name = ecx.tcx.item_name(*callee_def_id); - return interp_ok(matches!(item_name.as_str(), "pthread_join" | "WaitForSingleObject")); - }; - let intrinsice_name = intrinsic_def.name.as_str(); - info!("GenMC: intrinsic name: '{intrinsice_name}'"); - // FIXME(genmc): make this more precise (only loads). How can we make this maintainable? - interp_ok(intrinsice_name.starts_with("atomic_")) -} diff --git a/src/concurrency/genmc/mod.rs b/src/concurrency/genmc/mod.rs index d062cc918e..c200934c51 100644 --- a/src/concurrency/genmc/mod.rs +++ b/src/concurrency/genmc/mod.rs @@ -14,12 +14,11 @@ use self::global_allocations::{EvalContextExt as _, GlobalAllocationHandler}; use self::helper::{genmc_scalar_to_scalar, scalar_to_genmc_scalar}; use self::mapping::{min_max_to_genmc_rmw_op, to_genmc_rmw_op}; use self::thread_id_map::ThreadIdMap; -use crate::concurrency::genmc::helper::{is_terminator_atomic_load, split_access}; -use crate::concurrency::thread::{EvalContextExt as _, ThreadState}; +use crate::concurrency::genmc::helper::split_access; use crate::{ - AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, BlockReason, MemoryKind, - MiriConfig, MiriMachine, MiriMemoryKind, Scalar, TerminationInfo, ThreadId, ThreadManager, - VisitProvenance, VisitWith, + AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, MemoryKind, MiriConfig, + MiriMachine, MiriMemoryKind, Scalar, TerminationInfo, ThreadId, ThreadManager, VisitProvenance, + VisitWith, }; mod config; @@ -885,7 +884,7 @@ impl GenmcCtx { /**** Scheduling functionality ****/ - fn schedule_thread<'tcx>( + pub fn schedule_thread<'tcx>( &self, ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, ) -> InterpResult<'tcx, ThreadId> { @@ -912,11 +911,8 @@ impl GenmcCtx { return interp_ok(active_thread_id); } - if is_terminator_atomic_load(ecx, basic_block.terminator(), active_thread_id)? { - ActionKind::Load - } else { - ActionKind::NonLoad - } + // FIXME(genmc): determine terminator kind. + ActionKind::Load }; info!( @@ -949,49 +945,6 @@ impl GenmcCtx { } } -/// Other functionality not directly related to event handling -impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} -pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { - /**** Scheduling functionality ****/ - - /// Ask for a scheduling decision. This should be called before every MIR instruction. - /// - /// GenMC may realize that the execution is blocked, then this function will return a `InterpErrorKind::MachineStop` with error kind `TerminationInfo::GenmcBlockedExecution`). - /// - /// This is **not** an error by iself! Treat this as if the program ended normally: `handle_execution_end` should be called next, which will determine if were are any actual errors. - fn genmc_schedule_thread(&mut self) -> InterpResult<'tcx, ThreadId> { - let this = self.eval_context_mut(); - loop { - let genmc_ctx = this.machine.data_race.as_genmc_ref().unwrap(); - let next_thread_id = genmc_ctx.schedule_thread(this)?; - - match this.machine.threads.threads_ref()[next_thread_id].get_state() { - ThreadState::Blocked { - reason: block_reason @ (BlockReason::Mutex | BlockReason::GenmcAssume), - .. - } => { - info!( - "GenMC: schedule returned thread {next_thread_id:?}, which is blocked, so we unblock it now." - ); - this.unblock_thread(next_thread_id, *block_reason)?; - - // In some cases, like waiting on a Mutex::lock, the thread might still be blocked here: - if this.machine.threads.threads_ref()[next_thread_id] - .get_state() - .is_blocked_on(crate::BlockReason::Mutex) - { - info!("GenMC: Unblocked thread is blocked on a Mutex again!"); - continue; - } - } - _ => {} - } - - return interp_ok(next_thread_id); - } - } -} - impl VisitProvenance for GenmcCtx { fn visit_provenance(&self, _visit: &mut VisitWith<'_>) { // We don't have any tags. diff --git a/src/concurrency/mod.rs b/src/concurrency/mod.rs index 7c4e262492..4c8e0776d9 100644 --- a/src/concurrency/mod.rs +++ b/src/concurrency/mod.rs @@ -22,5 +22,5 @@ pub mod weak_memory; mod genmc; pub use self::data_race_handler::{AllocDataRaceHandler, GlobalDataRaceHandler}; -pub use self::genmc::{EvalContextExt as GenmcEvalContextExt, GenmcConfig, GenmcCtx, miri_genmc}; +pub use self::genmc::{GenmcConfig, GenmcCtx, miri_genmc}; pub use self::vector_clock::VClock; diff --git a/src/concurrency/thread.rs b/src/concurrency/thread.rs index d9a1ea2e82..0552a920dd 100644 --- a/src/concurrency/thread.rs +++ b/src/concurrency/thread.rs @@ -16,7 +16,7 @@ use rustc_middle::mir::Mutability; use rustc_middle::ty::layout::TyAndLayout; use rustc_span::Span; -use crate::concurrency::{GenmcEvalContextExt as _, GlobalDataRaceHandler}; +use crate::concurrency::GlobalDataRaceHandler; use crate::shims::tls; use crate::*; @@ -110,8 +110,6 @@ pub enum BlockReason { Eventfd, /// Blocked on unnamed_socket. UnnamedSocket, - /// Blocked on a GenMC `assume` statement (GenMC mode only). - GenmcAssume, } /// The state of a thread. @@ -530,11 +528,6 @@ impl<'tcx> ThreadManager<'tcx> { self.active_thread } - pub fn threads_ref(&self) -> &IndexVec> { - // TODO GENMC: should this implementation detail be exposed? - &self.threads - } - /// Get the total number of threads that were ever spawn by this program. pub fn get_total_thread_count(&self) -> usize { self.threads.len() @@ -588,8 +581,6 @@ impl<'tcx> ThreadManager<'tcx> { fn detach_thread(&mut self, id: ThreadId, allow_terminated_joined: bool) -> InterpResult<'tcx> { trace!("detaching {:?}", id); - tracing::info!("GenMC: TODO GENMC: does GenMC need special handling for detached threads?"); - let is_ub = if allow_terminated_joined && self.threads[id].state.is_terminated() { // "Detached" in particular means "not yet joined". Redundant detaching is still UB. self.threads[id].join_status == ThreadJoinStatus::Detached @@ -720,8 +711,8 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { let this = self.eval_context_mut(); // In GenMC mode, we let GenMC do the scheduling. - if this.machine.data_race.as_genmc_ref().is_some() { - let next_thread_id = this.genmc_schedule_thread()?; + if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() { + let next_thread_id = genmc_ctx.schedule_thread(this)?; let thread_manager = &mut this.machine.threads; thread_manager.active_thread = next_thread_id; From 7db233d26350bf29e7dcf6bf532481c357dd0828 Mon Sep 17 00:00:00 2001 From: Patrick-6 Date: Sat, 26 Jul 2025 15:07:22 +0200 Subject: [PATCH 31/41] Remove atomic fence support. --- genmc-sys/src/lib.rs | 1 - genmc-sys/src_cpp/MiriInterface.hpp | 2 - .../src_cpp/MiriInterface/MiriInterface.cpp | 6 --- src/concurrency/genmc/mapping.rs | 13 +---- src/concurrency/genmc/mod.rs | 19 ++----- tests/genmc/pass/litmus/2_2w_scfs.rs | 35 ------------- tests/genmc/pass/litmus/2_2w_scfs.stderr | 2 - tests/genmc/pass/litmus/2w2w_2sc_scf.rs | 34 ------------- tests/genmc/pass/litmus/2w2w_2sc_scf.stderr | 2 - tests/genmc/pass/litmus/IRIWish.rs | 47 ----------------- tests/genmc/pass/litmus/IRIWish.stderr | 2 - tests/genmc/pass/litmus/MPU2_rels_acqf.rs | 51 ------------------- tests/genmc/pass/litmus/MPU2_rels_acqf.stderr | 2 - tests/genmc/pass/litmus/MP_rels_acqf.rs | 40 --------------- tests/genmc/pass/litmus/MP_rels_acqf.stderr | 2 - tests/genmc/pass/litmus/SB_2sc_scf.rs | 35 ------------- tests/genmc/pass/litmus/SB_2sc_scf.stderr | 2 - tests/genmc/pass/litmus/Z6_acq.rs | 42 --------------- tests/genmc/pass/litmus/Z6_acq.stderr | 2 - tests/genmc/pass/litmus/atomicpo.rs | 34 ------------- tests/genmc/pass/litmus/cumul-release.rs | 42 --------------- tests/genmc/pass/litmus/cumul-release.stderr | 2 - tests/genmc/pass/thread/empty_main.rs | 3 -- tests/genmc/pass/thread/empty_main.stderr | 2 - .../pass/thread/miri_main_spawn_threads.rs | 15 ------ .../thread/miri_main_spawn_threads.stderr | 2 - .../pass/thread/std_main_spawn_threads.rs | 10 ---- .../pass/thread/std_main_spawn_threads.stderr | 2 - 28 files changed, 4 insertions(+), 447 deletions(-) delete mode 100644 tests/genmc/pass/litmus/2_2w_scfs.rs delete mode 100644 tests/genmc/pass/litmus/2_2w_scfs.stderr delete mode 100644 tests/genmc/pass/litmus/2w2w_2sc_scf.rs delete mode 100644 tests/genmc/pass/litmus/2w2w_2sc_scf.stderr delete mode 100644 tests/genmc/pass/litmus/IRIWish.rs delete mode 100644 tests/genmc/pass/litmus/IRIWish.stderr delete mode 100644 tests/genmc/pass/litmus/MPU2_rels_acqf.rs delete mode 100644 tests/genmc/pass/litmus/MPU2_rels_acqf.stderr delete mode 100644 tests/genmc/pass/litmus/MP_rels_acqf.rs delete mode 100644 tests/genmc/pass/litmus/MP_rels_acqf.stderr delete mode 100644 tests/genmc/pass/litmus/SB_2sc_scf.rs delete mode 100644 tests/genmc/pass/litmus/SB_2sc_scf.stderr delete mode 100644 tests/genmc/pass/litmus/Z6_acq.rs delete mode 100644 tests/genmc/pass/litmus/Z6_acq.stderr delete mode 100644 tests/genmc/pass/litmus/atomicpo.rs delete mode 100644 tests/genmc/pass/litmus/cumul-release.rs delete mode 100644 tests/genmc/pass/litmus/cumul-release.stderr delete mode 100644 tests/genmc/pass/thread/empty_main.rs delete mode 100644 tests/genmc/pass/thread/empty_main.stderr delete mode 100644 tests/genmc/pass/thread/miri_main_spawn_threads.rs delete mode 100644 tests/genmc/pass/thread/miri_main_spawn_threads.stderr delete mode 100644 tests/genmc/pass/thread/std_main_spawn_threads.rs delete mode 100644 tests/genmc/pass/thread/std_main_spawn_threads.stderr diff --git a/genmc-sys/src/lib.rs b/genmc-sys/src/lib.rs index 6bb8ba3db7..9f74cc962d 100644 --- a/genmc-sys/src/lib.rs +++ b/genmc-sys/src/lib.rs @@ -263,7 +263,6 @@ mod ffi { old_value: GenmcScalar, memory_ordering: MemOrdering, ) -> StoreResult; - fn handleFence(self: Pin<&mut MiriGenMCShim>, thread_id: i32, memory_ordering: MemOrdering); fn handleMalloc( self: Pin<&mut MiriGenMCShim>, diff --git a/genmc-sys/src_cpp/MiriInterface.hpp b/genmc-sys/src_cpp/MiriInterface.hpp index 7c6efc3f5e..36b7ab851e 100644 --- a/genmc-sys/src_cpp/MiriInterface.hpp +++ b/genmc-sys/src_cpp/MiriInterface.hpp @@ -69,8 +69,6 @@ struct MiriGenMCShim : private GenMCDriver GenmcScalar value, GenmcScalar old_val, MemOrdering ord); - void handleFence(ThreadId thread_id, MemOrdering ord); - /**** Memory (de)allocation ****/ uintptr_t handleMalloc(ThreadId thread_id, uint64_t size, uint64_t alignment); diff --git a/genmc-sys/src_cpp/MiriInterface/MiriInterface.cpp b/genmc-sys/src_cpp/MiriInterface/MiriInterface.cpp index 0cee10233c..d04d869328 100644 --- a/genmc-sys/src_cpp/MiriInterface/MiriInterface.cpp +++ b/genmc-sys/src_cpp/MiriInterface/MiriInterface.cpp @@ -362,12 +362,6 @@ void MiriGenMCShim::handleThreadKill(ThreadId thread_id) return StoreResult::ok(isCoMaxWrite); } -void MiriGenMCShim::handleFence(ThreadId thread_id, MemOrdering ord) -{ - const auto pos = incPos(thread_id); - GenMCDriver::handleFence(pos, ord, EventDeps()); -} - /**** Memory (de)allocation ****/ auto MiriGenMCShim::handleMalloc(ThreadId thread_id, uint64_t size, uint64_t alignment) -> uint64_t diff --git a/src/concurrency/genmc/mapping.rs b/src/concurrency/genmc/mapping.rs index 7a183e0d3e..5923c9b967 100644 --- a/src/concurrency/genmc/mapping.rs +++ b/src/concurrency/genmc/mapping.rs @@ -1,6 +1,6 @@ use genmc_sys::{MemOrdering, RMWBinOp}; -use crate::{AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd}; +use crate::{AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd}; // This file contains functionality to convert between Miri enums and their GenMC counterparts. @@ -24,17 +24,6 @@ impl AtomicWriteOrd { } } -impl AtomicFenceOrd { - pub(super) fn convert(self) -> MemOrdering { - match self { - AtomicFenceOrd::Acquire => MemOrdering::Acquire, - AtomicFenceOrd::Release => MemOrdering::Release, - AtomicFenceOrd::AcqRel => MemOrdering::AcquireRelease, - AtomicFenceOrd::SeqCst => MemOrdering::SequentiallyConsistent, - } - } -} - impl AtomicRwOrd { /// Split up the atomic success ordering of a read-modify-write operation into GenMC's representation. /// Note that both returned orderings are currently identical, because this is what GenMC expects. diff --git a/src/concurrency/genmc/mod.rs b/src/concurrency/genmc/mod.rs index c200934c51..2c423c72fd 100644 --- a/src/concurrency/genmc/mod.rs +++ b/src/concurrency/genmc/mod.rs @@ -218,23 +218,10 @@ impl GenmcCtx { pub(crate) fn atomic_fence<'tcx>( &self, - machine: &MiriMachine<'tcx>, - ordering: AtomicFenceOrd, + _machine: &MiriMachine<'tcx>, + _ordering: AtomicFenceOrd, ) -> InterpResult<'tcx> { - assert!(!self.allow_data_races.get(), "atomic fence with data race checking disabled."); - - let ordering = ordering.convert(); - - let thread_infos = self.thread_id_manager.borrow(); - let curr_thread = machine.threads.active_thread(); - let genmc_tid = thread_infos.get_genmc_tid(curr_thread); - - let mut mc = self.handle.borrow_mut(); - let pinned_mc = mc.as_mut(); - pinned_mc.handleFence(genmc_tid, ordering); - - // TODO GENMC: can this operation ever fail? - interp_ok(()) + throw_unsup_format!("FIXME(genmc): Add support for atomic fences.") } /// Inform GenMC about an atomic read-modify-write operation. diff --git a/tests/genmc/pass/litmus/2_2w_scfs.rs b/tests/genmc/pass/litmus/2_2w_scfs.rs deleted file mode 100644 index d318b4f4cb..0000000000 --- a/tests/genmc/pass/litmus/2_2w_scfs.rs +++ /dev/null @@ -1,35 +0,0 @@ -//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows - -#![no_main] - -#[path = "../../../utils/genmc.rs"] -mod genmc; - -use std::ffi::c_void; -use std::sync::atomic::{AtomicU64, Ordering}; - -static X: AtomicU64 = AtomicU64::new(0); -static Y: AtomicU64 = AtomicU64::new(0); - -use crate::genmc::*; - -#[unsafe(no_mangle)] -fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - let _ids = unsafe { create_pthreads_no_params([thread_1, thread_2]) }; - - 0 -} - -extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { - X.store(1, Ordering::Relaxed); - std::sync::atomic::fence(Ordering::SeqCst); - Y.store(2, Ordering::Relaxed); - std::ptr::null_mut() -} - -extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { - Y.store(1, Ordering::Relaxed); - std::sync::atomic::fence(Ordering::SeqCst); - X.store(2, Ordering::Relaxed); - std::ptr::null_mut() -} diff --git a/tests/genmc/pass/litmus/2_2w_scfs.stderr b/tests/genmc/pass/litmus/2_2w_scfs.stderr deleted file mode 100644 index 115b1986ce..0000000000 --- a/tests/genmc/pass/litmus/2_2w_scfs.stderr +++ /dev/null @@ -1,2 +0,0 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 3 diff --git a/tests/genmc/pass/litmus/2w2w_2sc_scf.rs b/tests/genmc/pass/litmus/2w2w_2sc_scf.rs deleted file mode 100644 index 7e833b1b6c..0000000000 --- a/tests/genmc/pass/litmus/2w2w_2sc_scf.rs +++ /dev/null @@ -1,34 +0,0 @@ -//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows - -#![no_main] - -#[path = "../../../utils/genmc.rs"] -mod genmc; - -use std::ffi::c_void; -use std::sync::atomic::{AtomicU64, Ordering}; - -static X: AtomicU64 = AtomicU64::new(0); -static Y: AtomicU64 = AtomicU64::new(0); - -use crate::genmc::*; - -#[unsafe(no_mangle)] -fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - let _ids = unsafe { create_pthreads_no_params([thread_1, thread_2]) }; - - 0 -} - -extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { - X.store(1, Ordering::SeqCst); - Y.store(2, Ordering::SeqCst); - std::ptr::null_mut() -} - -extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { - Y.store(1, Ordering::Relaxed); - std::sync::atomic::fence(Ordering::SeqCst); - X.store(2, Ordering::Relaxed); - std::ptr::null_mut() -} diff --git a/tests/genmc/pass/litmus/2w2w_2sc_scf.stderr b/tests/genmc/pass/litmus/2w2w_2sc_scf.stderr deleted file mode 100644 index 115b1986ce..0000000000 --- a/tests/genmc/pass/litmus/2w2w_2sc_scf.stderr +++ /dev/null @@ -1,2 +0,0 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 3 diff --git a/tests/genmc/pass/litmus/IRIWish.rs b/tests/genmc/pass/litmus/IRIWish.rs deleted file mode 100644 index 14197ccd23..0000000000 --- a/tests/genmc/pass/litmus/IRIWish.rs +++ /dev/null @@ -1,47 +0,0 @@ -//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows - -#![no_main] - -#[path = "../../../utils/genmc.rs"] -mod genmc; - -use std::ffi::c_void; -use std::sync::atomic::{AtomicU64, Ordering}; - -use crate::genmc::*; - -static X: AtomicU64 = AtomicU64::new(0); -static Y: AtomicU64 = AtomicU64::new(0); - -#[unsafe(no_mangle)] -fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - let thread_order = [thread_1, thread_2, thread_3, thread_4]; - let _ids = unsafe { create_pthreads_no_params(thread_order) }; - - 0 -} - -pub extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { - X.store(1, Ordering::Relaxed); - std::ptr::null_mut() -} - -pub extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { - let r1 = X.load(Ordering::Relaxed); - Y.store(r1, Ordering::Release); - std::ptr::null_mut() -} - -pub extern "C" fn thread_3(_value: *mut c_void) -> *mut c_void { - let _r1 = X.load(Ordering::Relaxed); - std::sync::atomic::fence(Ordering::AcqRel); - let _r2 = Y.load(Ordering::Relaxed); - std::ptr::null_mut() -} - -pub extern "C" fn thread_4(_value: *mut c_void) -> *mut c_void { - let _r1 = Y.load(Ordering::Relaxed); - std::sync::atomic::fence(Ordering::AcqRel); - let _r2 = X.load(Ordering::Relaxed); - std::ptr::null_mut() -} diff --git a/tests/genmc/pass/litmus/IRIWish.stderr b/tests/genmc/pass/litmus/IRIWish.stderr deleted file mode 100644 index bce94be916..0000000000 --- a/tests/genmc/pass/litmus/IRIWish.stderr +++ /dev/null @@ -1,2 +0,0 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 28 diff --git a/tests/genmc/pass/litmus/MPU2_rels_acqf.rs b/tests/genmc/pass/litmus/MPU2_rels_acqf.rs deleted file mode 100644 index d7fe499ff3..0000000000 --- a/tests/genmc/pass/litmus/MPU2_rels_acqf.rs +++ /dev/null @@ -1,51 +0,0 @@ -//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows - -#![no_main] - -#[path = "../../../utils/genmc.rs"] -mod genmc; - -use std::ffi::c_void; -use std::sync::atomic::{AtomicU64, Ordering}; - -// Note: the GenMC equivalent of this test (genmc/tests/correct/litmus/MPU2+rels+acqf/mpu2+rels+acqf.c) uses non-atomic accesses for `X` with disabled race detection. -static X: AtomicU64 = AtomicU64::new(0); -static Y: AtomicU64 = AtomicU64::new(0); - -use crate::genmc::*; - -#[unsafe(no_mangle)] -fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - let thread_order = [thread_1, thread_2, thread_3, thread_4]; - let _ids = unsafe { create_pthreads_no_params(thread_order) }; - - 0 -} - -extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { - X.store(1, Ordering::Relaxed); - - Y.store(0, Ordering::Release); - Y.store(1, Ordering::Relaxed); - std::ptr::null_mut() -} - -extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { - let expected = 2; - let _ = Y.compare_exchange(expected, 3, Ordering::Relaxed, Ordering::Relaxed); - std::ptr::null_mut() -} - -extern "C" fn thread_3(_value: *mut c_void) -> *mut c_void { - let expected = 1; - let _ = Y.compare_exchange(expected, 2, Ordering::Relaxed, Ordering::Relaxed); - std::ptr::null_mut() -} - -extern "C" fn thread_4(_value: *mut c_void) -> *mut c_void { - if Y.load(Ordering::Acquire) > 2 { - std::sync::atomic::fence(Ordering::Acquire); - X.store(2, Ordering::Relaxed); - } - std::ptr::null_mut() -} diff --git a/tests/genmc/pass/litmus/MPU2_rels_acqf.stderr b/tests/genmc/pass/litmus/MPU2_rels_acqf.stderr deleted file mode 100644 index f6d07e9c77..0000000000 --- a/tests/genmc/pass/litmus/MPU2_rels_acqf.stderr +++ /dev/null @@ -1,2 +0,0 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 36 diff --git a/tests/genmc/pass/litmus/MP_rels_acqf.rs b/tests/genmc/pass/litmus/MP_rels_acqf.rs deleted file mode 100644 index 5777a7196c..0000000000 --- a/tests/genmc/pass/litmus/MP_rels_acqf.rs +++ /dev/null @@ -1,40 +0,0 @@ -//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows -// -Zmiri-disable-data-race-detector - -#![no_main] - -#[path = "../../../utils/genmc.rs"] -mod genmc; - -use std::ffi::c_void; -use std::sync::atomic::{AtomicU64, Ordering}; - -// Note: the GenMC equivalent of this test (genmc/tests/correct/litmus/MP+rels+acqf/mp+rels+acqf.c) uses non-atomic accesses for `X` with disabled race detection. -static X: AtomicU64 = AtomicU64::new(0); -static Y: AtomicU64 = AtomicU64::new(0); - -use crate::genmc::*; - -#[unsafe(no_mangle)] -fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - let thread_order = [thread_1, thread_2]; - let _ids = unsafe { create_pthreads_no_params(thread_order) }; - - 0 -} - -extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { - X.store(1, Ordering::Relaxed); - - Y.store(0, Ordering::Release); - Y.store(1, Ordering::Relaxed); - std::ptr::null_mut() -} - -extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { - if Y.load(Ordering::Relaxed) != 0 { - std::sync::atomic::fence(Ordering::Acquire); - let _x = X.load(Ordering::Relaxed); - } - std::ptr::null_mut() -} diff --git a/tests/genmc/pass/litmus/MP_rels_acqf.stderr b/tests/genmc/pass/litmus/MP_rels_acqf.stderr deleted file mode 100644 index 0667962f99..0000000000 --- a/tests/genmc/pass/litmus/MP_rels_acqf.stderr +++ /dev/null @@ -1,2 +0,0 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 4 diff --git a/tests/genmc/pass/litmus/SB_2sc_scf.rs b/tests/genmc/pass/litmus/SB_2sc_scf.rs deleted file mode 100644 index be65dffcb0..0000000000 --- a/tests/genmc/pass/litmus/SB_2sc_scf.rs +++ /dev/null @@ -1,35 +0,0 @@ -//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows - -#![no_main] - -#[path = "../../../utils/genmc.rs"] -mod genmc; - -use std::ffi::c_void; -use std::sync::atomic::{AtomicU64, Ordering}; - -static X: AtomicU64 = AtomicU64::new(0); -static Y: AtomicU64 = AtomicU64::new(0); - -use crate::genmc::*; - -#[unsafe(no_mangle)] -fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - let thread_order = [thread_1, thread_2]; - let _ids = unsafe { create_pthreads_no_params(thread_order) }; - - 0 -} - -extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { - X.store(1, Ordering::SeqCst); - Y.load(Ordering::SeqCst); - std::ptr::null_mut() -} - -extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { - Y.store(1, Ordering::Relaxed); - std::sync::atomic::fence(Ordering::SeqCst); - X.load(Ordering::Relaxed); - std::ptr::null_mut() -} diff --git a/tests/genmc/pass/litmus/SB_2sc_scf.stderr b/tests/genmc/pass/litmus/SB_2sc_scf.stderr deleted file mode 100644 index 115b1986ce..0000000000 --- a/tests/genmc/pass/litmus/SB_2sc_scf.stderr +++ /dev/null @@ -1,2 +0,0 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 3 diff --git a/tests/genmc/pass/litmus/Z6_acq.rs b/tests/genmc/pass/litmus/Z6_acq.rs deleted file mode 100644 index 34c398bbb0..0000000000 --- a/tests/genmc/pass/litmus/Z6_acq.rs +++ /dev/null @@ -1,42 +0,0 @@ -//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows - -#![no_main] - -#[path = "../../../utils/genmc.rs"] -mod genmc; - -use std::ffi::c_void; -use std::sync::atomic::{AtomicU64, Ordering}; - -static X: AtomicU64 = AtomicU64::new(0); -static Y: AtomicU64 = AtomicU64::new(0); -static Z: AtomicU64 = AtomicU64::new(0); - -use crate::genmc::*; - -#[unsafe(no_mangle)] -fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - let _ids = unsafe { create_pthreads_no_params([thread_1, thread_2, thread_3]) }; - - 0 -} - -extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { - X.store(1, Ordering::Relaxed); - std::sync::atomic::fence(Ordering::SeqCst); - Y.store(1, Ordering::Relaxed); - std::ptr::null_mut() -} - -extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { - Y.load(Ordering::Acquire); - Z.store(1, Ordering::Relaxed); - std::ptr::null_mut() -} - -extern "C" fn thread_3(_value: *mut c_void) -> *mut c_void { - Z.store(2, Ordering::Relaxed); - std::sync::atomic::fence(Ordering::SeqCst); - X.load(Ordering::Relaxed); - std::ptr::null_mut() -} diff --git a/tests/genmc/pass/litmus/Z6_acq.stderr b/tests/genmc/pass/litmus/Z6_acq.stderr deleted file mode 100644 index 2be9a6c7fb..0000000000 --- a/tests/genmc/pass/litmus/Z6_acq.stderr +++ /dev/null @@ -1,2 +0,0 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 7 diff --git a/tests/genmc/pass/litmus/atomicpo.rs b/tests/genmc/pass/litmus/atomicpo.rs deleted file mode 100644 index 374c2aa099..0000000000 --- a/tests/genmc/pass/litmus/atomicpo.rs +++ /dev/null @@ -1,34 +0,0 @@ -//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows - -#![no_main] - -#[path = "../../../utils/genmc.rs"] -mod genmc; - -use std::ffi::c_void; -use std::sync::atomic::{AtomicU64, Ordering}; - -static X: AtomicU64 = AtomicU64::new(0); -static Y: AtomicU64 = AtomicU64::new(0); - -use crate::genmc::*; - -#[unsafe(no_mangle)] -fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - let _ids = unsafe { create_pthreads_no_params([thread_1, thread_2]) }; - - 0 -} - -extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { - X.store(1, Ordering::Relaxed); - std::sync::atomic::fence(Ordering::AcqRel); - Y.store(1, Ordering::Relaxed); - std::ptr::null_mut() -} - -extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { - Y.swap(1, Ordering::Relaxed); - X.swap(1, Ordering::Relaxed); - std::ptr::null_mut() -} diff --git a/tests/genmc/pass/litmus/cumul-release.rs b/tests/genmc/pass/litmus/cumul-release.rs deleted file mode 100644 index fffb3437fe..0000000000 --- a/tests/genmc/pass/litmus/cumul-release.rs +++ /dev/null @@ -1,42 +0,0 @@ -//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows - -#![no_main] - -#[path = "../../../utils/genmc.rs"] -mod genmc; - -use std::ffi::c_void; -use std::sync::atomic::{AtomicU64, Ordering}; - -use crate::genmc::*; - -static X: AtomicU64 = AtomicU64::new(0); -static Y: AtomicU64 = AtomicU64::new(0); -static Z: AtomicU64 = AtomicU64::new(0); - -#[unsafe(no_mangle)] -fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - let thread_order = [thread_1, thread_2, thread_3]; - let _ids = unsafe { create_pthreads_no_params(thread_order) }; - - 0 -} - -extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { - X.store(1, Ordering::Relaxed); - Y.store(1, Ordering::Release); - std::ptr::null_mut() -} - -extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { - let r1 = Y.load(Ordering::Relaxed); - Z.store(r1, Ordering::Relaxed); - std::ptr::null_mut() -} - -extern "C" fn thread_3(_value: *mut c_void) -> *mut c_void { - let _r2 = Z.load(Ordering::Relaxed); - std::sync::atomic::fence(Ordering::AcqRel); - let _r3 = X.load(Ordering::Relaxed); - std::ptr::null_mut() -} diff --git a/tests/genmc/pass/litmus/cumul-release.stderr b/tests/genmc/pass/litmus/cumul-release.stderr deleted file mode 100644 index 00394048ec..0000000000 --- a/tests/genmc/pass/litmus/cumul-release.stderr +++ /dev/null @@ -1,2 +0,0 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 8 diff --git a/tests/genmc/pass/thread/empty_main.rs b/tests/genmc/pass/thread/empty_main.rs deleted file mode 100644 index e2d7c3b562..0000000000 --- a/tests/genmc/pass/thread/empty_main.rs +++ /dev/null @@ -1,3 +0,0 @@ -//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows - -fn main() {} diff --git a/tests/genmc/pass/thread/empty_main.stderr b/tests/genmc/pass/thread/empty_main.stderr deleted file mode 100644 index 3b22247ee4..0000000000 --- a/tests/genmc/pass/thread/empty_main.stderr +++ /dev/null @@ -1,2 +0,0 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 1 diff --git a/tests/genmc/pass/thread/miri_main_spawn_threads.rs b/tests/genmc/pass/thread/miri_main_spawn_threads.rs deleted file mode 100644 index c7fd6dff28..0000000000 --- a/tests/genmc/pass/thread/miri_main_spawn_threads.rs +++ /dev/null @@ -1,15 +0,0 @@ -//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows - -#![no_main] - -const N: usize = 2; - -#[unsafe(no_mangle)] -fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - let handles: Vec<_> = (0..N).map(|_| std::thread::spawn(thread_func)).collect(); - handles.into_iter().for_each(|handle| handle.join().unwrap()); - - 0 -} - -fn thread_func() {} diff --git a/tests/genmc/pass/thread/miri_main_spawn_threads.stderr b/tests/genmc/pass/thread/miri_main_spawn_threads.stderr deleted file mode 100644 index 3b22247ee4..0000000000 --- a/tests/genmc/pass/thread/miri_main_spawn_threads.stderr +++ /dev/null @@ -1,2 +0,0 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 1 diff --git a/tests/genmc/pass/thread/std_main_spawn_threads.rs b/tests/genmc/pass/thread/std_main_spawn_threads.rs deleted file mode 100644 index 6477ac74ab..0000000000 --- a/tests/genmc/pass/thread/std_main_spawn_threads.rs +++ /dev/null @@ -1,10 +0,0 @@ -//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows - -const N: usize = 2; - -fn main() { - let handles: Vec<_> = (0..N).map(|_| std::thread::spawn(thread_func)).collect(); - handles.into_iter().for_each(|handle| handle.join().unwrap()); -} - -fn thread_func() {} diff --git a/tests/genmc/pass/thread/std_main_spawn_threads.stderr b/tests/genmc/pass/thread/std_main_spawn_threads.stderr deleted file mode 100644 index 3b22247ee4..0000000000 --- a/tests/genmc/pass/thread/std_main_spawn_threads.stderr +++ /dev/null @@ -1,2 +0,0 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 1 From 7b735e8a681aee3834cdf48955565aa890062cee Mon Sep 17 00:00:00 2001 From: Patrick-6 Date: Sat, 26 Jul 2025 15:36:48 +0200 Subject: [PATCH 32/41] Remove MIRI_LOG debug prints. --- genmc-sys/src_cpp/MiriInterface.hpp | 26 +++---------------- .../src_cpp/MiriInterface/MiriInterface.cpp | 15 +++-------- 2 files changed, 7 insertions(+), 34 deletions(-) diff --git a/genmc-sys/src_cpp/MiriInterface.hpp b/genmc-sys/src_cpp/MiriInterface.hpp index 36b7ab851e..0bbdda14ad 100644 --- a/genmc-sys/src_cpp/MiriInterface.hpp +++ b/genmc-sys/src_cpp/MiriInterface.hpp @@ -148,24 +148,12 @@ struct MiriGenMCShim : private GenMCDriver * */ void handleOldVal(const SAddr addr, GenmcScalar value) { - LOG(VerbosityLevel::Warning) - << "handleOldVal: " << addr << ", " << value.value << ", " << value.extra - << ", " << value.is_init << "\n"; - // TODO GENMC(CLEANUP): Pass this as a parameter: auto &g = getExec().getGraph(); auto *coLab = g.co_max(addr); if (auto *wLab = llvm::dyn_cast(coLab)) { - LOG(VerbosityLevel::Warning) - << "handleOldVal: got WriteLabel, atomic: " << !wLab->isNotAtomic() - << "\n"; - if (!value.is_init) - LOG(VerbosityLevel::Warning) - << "WARNING: TODO GENMC: handleOldVal tried to " - "overwrite value of NA " - "reads-from label, but old value is `uninit`\n"; - else if (wLab->isNotAtomic()) + if (value.is_init && wLab->isNotAtomic()) wLab->setVal(value.toSVal()); } else if (const auto *wLab = llvm::dyn_cast(coLab)) @@ -173,17 +161,9 @@ struct MiriGenMCShim : private GenMCDriver if (value.is_init) { auto result = initVals_.insert(std::make_pair(addr, value)); - LOG(VerbosityLevel::Warning) - << "handleOldVal: got InitLabel, insertion result: " - << result.first->second << ", " << result.second << "\n"; BUG_ON(result.second && - (*result.first).second != - value); /* Attempt to replace initial value */ - } else { - LOG(VerbosityLevel::Warning) - << "WARNING: TODO GENMC: handleOldVal tried set initial " - "value, but old " - "value is `uninit`\n"; + (*result.first).second != + value); /* Attempt to replace initial value */ } } else diff --git a/genmc-sys/src_cpp/MiriInterface/MiriInterface.cpp b/genmc-sys/src_cpp/MiriInterface/MiriInterface.cpp index d04d869328..a67f6f5be6 100644 --- a/genmc-sys/src_cpp/MiriInterface/MiriInterface.cpp +++ b/genmc-sys/src_cpp/MiriInterface/MiriInterface.cpp @@ -120,23 +120,16 @@ auto MiriGenMCShim::createHandle(const GenmcParams &config) auto initValGetter = [driverPtr](const AAccess &access) { const auto addr = access.getAddr(); - if (!driverPtr->initVals_.contains(addr)) { - LOG(VerbosityLevel::Warning) - << "WARNING: TODO GENMC: requested initial value for address " - << addr << ", but there is none.\n"; + if (!driverPtr->initVals_.contains(addr)) + { return SVal(0xCC00CC00); // BUG_ON(!driverPtr->initVals_.contains(addr)); } auto result = driverPtr->initVals_[addr]; - if (!result.is_init) { - LOG(VerbosityLevel::Warning) - << "WARNING: TODO GENMC: requested initial value for address " - << addr << ", but the memory is uninitialized.\n"; + if (!result.is_init) + { return SVal(0xFF00FF00); } - LOG(VerbosityLevel::Warning) - << "MiriGenMCShim: requested initial value for address " << addr - << " == " << addr.get() << ", returning: " << result << "\n"; return result.toSVal(); }; driver->getExec().getGraph().setInitValGetter(initValGetter); From 4e26d667fc1fd2d5427d4914d37cc9516927db35 Mon Sep 17 00:00:00 2001 From: Patrick-6 Date: Sat, 26 Jul 2025 16:03:21 +0200 Subject: [PATCH 33/41] Remove support for sending pointers to GenMC. --- genmc-sys/src/lib.rs | 9 +- genmc-sys/src_cpp/ResultHandling.cpp | 7 +- genmc-sys/src_cpp/ResultHandling.hpp | 20 +- src/concurrency/genmc/helper.rs | 54 +---- tests/genmc/pass/atomics/atomic_ptr.rs | 83 ------- tests/genmc/pass/atomics/atomic_ptr.stderr | 2 - .../pass/data-structures/ms_queue_dynamic.rs | 212 ------------------ .../data-structures/ms_queue_dynamic.stderr | 2 - .../data-structures/treiber_stack_dynamic.rs | 166 -------------- .../treiber_stack_dynamic.stderr | 2 - tests/genmc/pass/thread/thread_locals.rs | 55 ----- tests/genmc/pass/thread/thread_locals.stderr | 2 - 12 files changed, 25 insertions(+), 589 deletions(-) delete mode 100644 tests/genmc/pass/atomics/atomic_ptr.rs delete mode 100644 tests/genmc/pass/atomics/atomic_ptr.stderr delete mode 100644 tests/genmc/pass/data-structures/ms_queue_dynamic.rs delete mode 100644 tests/genmc/pass/data-structures/ms_queue_dynamic.stderr delete mode 100644 tests/genmc/pass/data-structures/treiber_stack_dynamic.rs delete mode 100644 tests/genmc/pass/data-structures/treiber_stack_dynamic.stderr delete mode 100644 tests/genmc/pass/thread/thread_locals.rs delete mode 100644 tests/genmc/pass/thread/thread_locals.stderr diff --git a/genmc-sys/src/lib.rs b/genmc-sys/src/lib.rs index 9f74cc962d..d41c7b3035 100644 --- a/genmc-sys/src/lib.rs +++ b/genmc-sys/src/lib.rs @@ -18,17 +18,13 @@ pub const GENMC_GLOBAL_ADDRESSES_MASK: u64 = 1 << 63; pub const GENMC_MAIN_THREAD_ID: i32 = 0; impl GenmcScalar { - pub const UNINIT: Self = Self { value: 0, extra: 0, is_init: false }; + pub const UNINIT: Self = Self { value: 0, is_init: false }; /// GenMC expects a value for all stores, but we cannot always provide one (e.g., non-atomic writes). /// FIXME(genmc): remove this if a permanent fix is ever found. pub const DUMMY: Self = Self::from_u64(0xDEADBEEF); pub const fn from_u64(value: u64) -> Self { - Self { value, extra: 0, is_init: true } - } - - pub const fn has_provenance(&self) -> bool { - self.extra != 0 + Self { value, is_init: true } } } @@ -142,7 +138,6 @@ mod ffi { #[derive(Debug, Clone, Copy)] struct GenmcScalar { value: u64, - extra: u64, is_init: bool, } diff --git a/genmc-sys/src_cpp/ResultHandling.cpp b/genmc-sys/src_cpp/ResultHandling.cpp index f5f306bfe1..800813eaeb 100644 --- a/genmc-sys/src_cpp/ResultHandling.cpp +++ b/genmc-sys/src_cpp/ResultHandling.cpp @@ -2,10 +2,9 @@ auto operator<<(llvm::raw_ostream &s, const GenmcScalar &v) -> llvm::raw_ostream & { - if (v.is_init) { - s << "{" << v.value << ", " << v.extra << "}"; - } else { + if (v.is_init) + s << "{" << v.value << "}"; + else s << "{UNINITIALIZED}"; - } return s; } diff --git a/genmc-sys/src_cpp/ResultHandling.hpp b/genmc-sys/src_cpp/ResultHandling.hpp index 07c615ee15..7d7a209a68 100644 --- a/genmc-sys/src_cpp/ResultHandling.hpp +++ b/genmc-sys/src_cpp/ResultHandling.hpp @@ -18,20 +18,19 @@ using ModelCheckerError = std::string; */ struct GenmcScalar { uint64_t value; - uint64_t extra; bool is_init; - explicit GenmcScalar() : value(0), extra(0), is_init(false) {} + explicit GenmcScalar() : value(0), is_init(false) {} explicit GenmcScalar(uint64_t value, uint64_t extra) - : value(value), extra(extra), is_init(true) + : value(value), is_init(true) {} - explicit GenmcScalar(SVal val) : value(val.get()), extra(val.getExtra()), is_init(true) {} + explicit GenmcScalar(SVal val) : value(val.get()), is_init(true) {} /** Convert to a GenMC SVal. Panics if the value is uninitialized. */ auto toSVal() const -> SVal { ERROR_ON(!is_init, "attempt to convert uninitialized GenmcScalar to an SVal\n"); - return SVal(value, extra); + return SVal(value); } bool operator==(const GenmcScalar &other) const @@ -44,8 +43,8 @@ struct GenmcScalar { if (is_init != other.is_init) return false; - // Compare the actual values. - return value == other.value && extra == other.extra; + // Compare the actual values + return value == other.value; } friend auto operator<<(llvm::raw_ostream &rhs, const GenmcScalar &v) -> llvm::raw_ostream &; @@ -86,7 +85,9 @@ struct LoadResult { read_value = rhs.read_value; if (rhs.error.get() != nullptr) { error = std::make_unique(*rhs.error); - } else { + } + else + { error = nullptr; } return *this; @@ -130,7 +131,8 @@ struct ReadModifyWriteResult { ReadModifyWriteResult(SVal old_value, SVal new_value, bool isCoMaxWrite) : old_value(GenmcScalar(old_value)), new_value(GenmcScalar(new_value)), isCoMaxWrite(isCoMaxWrite), error(nullptr) - {} + { + } static ReadModifyWriteResult fromError(std::string msg) { diff --git a/src/concurrency/genmc/helper.rs b/src/concurrency/genmc/helper.rs index 345d00a562..6c85811505 100644 --- a/src/concurrency/genmc/helper.rs +++ b/src/concurrency/genmc/helper.rs @@ -4,8 +4,7 @@ use rustc_middle::ty::ScalarInt; use tracing::info; use super::GenmcScalar; -use crate::alloc_addresses::EvalContextExt as _; -use crate::{BorTag, MiriInterpCx, Pointer, Provenance, Scalar, throw_unsup_format}; +use crate::{MiriInterpCx, Scalar, throw_unsup_format}; /// This function is used to split up a large memory access into aligned, non-overlapping chunks of a limited size. /// Returns an iterator over the chunks, yielding `(base address, size)` of each chunk, ordered by address. @@ -56,26 +55,19 @@ pub fn split_access(address: Size, size: Size) -> impl Iterator( - ecx: &MiriInterpCx<'tcx>, + _ecx: &MiriInterpCx<'tcx>, scalar: Scalar, ) -> InterpResult<'tcx, GenmcScalar> { interp_ok(match scalar { rustc_const_eval::interpret::Scalar::Int(scalar_int) => { // FIXME(genmc): Add u128 support once GenMC supports it. let value: u64 = scalar_int.to_uint(scalar_int.size()).try_into().unwrap(); - GenmcScalar { value, extra: 0, is_init: true } - } - rustc_const_eval::interpret::Scalar::Ptr(pointer, size) => { - let addr = Pointer::from(pointer).addr(); - if let Provenance::Wildcard = pointer.provenance { - throw_unsup_format!("Pointers with wildcard provenance not allowed in GenMC mode"); - } - let (alloc_id, _size, _prov_extra) = - rustc_const_eval::interpret::Machine::ptr_get_alloc(ecx, pointer, size.into()) - .unwrap(); - let base_addr = ecx.addr_from_alloc_id(alloc_id, None)?; - GenmcScalar { value: addr.bytes(), extra: base_addr, is_init: true } + GenmcScalar { value, is_init: true } } + rustc_const_eval::interpret::Scalar::Ptr(_pointer, _size) => + throw_unsup_format!( + "FIXME(genmc): Implement sending pointers (with provenance) to GenMC." + ), }) } @@ -84,39 +76,11 @@ pub fn scalar_to_genmc_scalar<'tcx>( /// Convert a `GenmcScalar` back into a Miri `Scalar`. /// For pointers, attempt to convert the stored base address of their allocation back into an `AllocId`. pub fn genmc_scalar_to_scalar<'tcx>( - ecx: &MiriInterpCx<'tcx>, + _ecx: &MiriInterpCx<'tcx>, scalar: GenmcScalar, size: Size, ) -> InterpResult<'tcx, Scalar> { - if scalar.extra != 0 { - // We have a pointer! - - let addr = Size::from_bytes(scalar.value); - let base_addr = scalar.extra; - - let alloc_size = 0; // TODO GENMC: what is the correct size here? Is 0 ok? - let only_exposed_allocations = false; - let Some(alloc_id) = - ecx.alloc_id_from_addr(base_addr, alloc_size, only_exposed_allocations) - else { - // TODO GENMC: what is the correct error in this case? Maybe give the pointer wildcard provenance instead? - throw_unsup_format!( - "Cannot get allocation id of pointer received from GenMC (base address: 0x{base_addr:x}, pointer address: 0x{:x})", - addr.bytes() - ); - }; - - // TODO GENMC: is using `size: Size` ok here? Can we ever have `size != sizeof pointer`? - - // FIXME: Currently GenMC mode incompatible with aliasing model checking - let tag = BorTag::default(); - let provenance = crate::machine::Provenance::Concrete { alloc_id, tag }; - let offset = addr; - let ptr = rustc_middle::mir::interpret::Pointer::new(provenance, offset); - - let size = size.bytes().try_into().unwrap(); - return interp_ok(Scalar::Ptr(ptr, size)); - } + // FIXME(genmc): Add GenmcScalar to Miri Pointer conversion. // NOTE: GenMC always returns 64 bit values, and the upper bits are not yet truncated. // FIXME(genmc): GenMC should be doing the truncation, not Miri. diff --git a/tests/genmc/pass/atomics/atomic_ptr.rs b/tests/genmc/pass/atomics/atomic_ptr.rs deleted file mode 100644 index a13b517d4a..0000000000 --- a/tests/genmc/pass/atomics/atomic_ptr.rs +++ /dev/null @@ -1,83 +0,0 @@ -//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows - -#![no_main] - -use std::sync::atomic::*; - -static mut X: u64 = 0; -static mut Y: u64 = 0; - -#[unsafe(no_mangle)] -fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - unsafe { - let atomic_ptr: AtomicPtr = AtomicPtr::new(&raw mut X); - - let x_ptr = atomic_ptr.load(Ordering::SeqCst); - *x_ptr = 10; - if X != 10 { - std::process::abort(); - } - atomic_ptr.store(&raw mut Y, Ordering::SeqCst); - Y = 42; - let y_ptr = atomic_ptr.load(Ordering::SeqCst); - if *y_ptr != 42 { - std::process::abort(); - } - *y_ptr = 1234; - if Y != 1234 { - std::process::abort(); - } else if X != 10 { - std::process::abort(); - } - let y_ptr_ = atomic_ptr.swap(&raw mut X, Ordering::SeqCst); - if y_ptr_ != y_ptr { - std::process::abort(); - } - // To make sure also the provenance info is correctly restored, we need to use the pointers: - if *y_ptr_ != *y_ptr { - std::process::abort(); - } - *y_ptr_ = *y_ptr; - - match atomic_ptr.compare_exchange( - y_ptr, // wrong, it should be `x_ptr`, so this should never succeed - std::ptr::dangling_mut(), - Ordering::SeqCst, - Ordering::SeqCst, - ) { - Ok(_ptr) => std::process::abort(), - Err(ptr) => - if ptr != x_ptr { - std::process::abort(); - } else if *ptr != *x_ptr { - std::process::abort(); - } else { - *ptr = *ptr; - }, - } - - let mut array: [u64; 10] = [0xAAAA; 10]; - match atomic_ptr.compare_exchange( - x_ptr, - &raw mut array[2], - Ordering::SeqCst, - Ordering::SeqCst, - ) { - Ok(ptr) => - if ptr != x_ptr { - std::process::abort(); - }, - Err(_ptr) => std::process::abort(), - } - let ptr = atomic_ptr.load(Ordering::SeqCst); - *ptr = 0xB; - if array[2] != 0xB { - std::process::abort(); - } - array[2] = 0xC; - if *ptr != 0xC { - std::process::abort(); - } - } - 0 -} diff --git a/tests/genmc/pass/atomics/atomic_ptr.stderr b/tests/genmc/pass/atomics/atomic_ptr.stderr deleted file mode 100644 index 3b22247ee4..0000000000 --- a/tests/genmc/pass/atomics/atomic_ptr.stderr +++ /dev/null @@ -1,2 +0,0 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 1 diff --git a/tests/genmc/pass/data-structures/ms_queue_dynamic.rs b/tests/genmc/pass/data-structures/ms_queue_dynamic.rs deleted file mode 100644 index dd02df5936..0000000000 --- a/tests/genmc/pass/data-structures/ms_queue_dynamic.rs +++ /dev/null @@ -1,212 +0,0 @@ -//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows - -// This test is a translations of the GenMC test `ms-queue-dynamic`, but with all code related to GenMC's hazard pointer API removed. -// The test leaks memory, so leak checks are disabled. - -#![no_main] -#![allow(static_mut_refs)] - -use std::alloc::{Layout, alloc, dealloc}; -use std::ffi::c_void; -use std::sync::atomic::AtomicPtr; -use std::sync::atomic::Ordering::*; - -use libc::{self, pthread_attr_t, pthread_t}; - -const MAX_THREADS: usize = 32; - -static mut QUEUE: MyStack = MyStack::new(); -static mut PARAMS: [u64; MAX_THREADS] = [0; MAX_THREADS]; -static mut INPUT: [u64; MAX_THREADS] = [0; MAX_THREADS]; -static mut OUTPUT: [Option; MAX_THREADS] = [None; MAX_THREADS]; - -#[repr(C)] -struct Node { - value: u64, - next: AtomicPtr, -} - -struct MyStack { - head: AtomicPtr, - tail: AtomicPtr, -} - -impl Node { - pub unsafe fn new_alloc() -> *mut Self { - alloc(Layout::new::()) as *mut Self - } - - pub unsafe fn free(node: *mut Self) { - dealloc(node as *mut u8, Layout::new::()) - } -} - -impl MyStack { - pub const fn new() -> Self { - let head = AtomicPtr::new(std::ptr::null_mut()); - let tail = AtomicPtr::new(std::ptr::null_mut()); - Self { head, tail } - } - - pub unsafe fn init_queue(&mut self, _num_threads: usize) { - let dummy = Node::new_alloc(); - - (*dummy).next = AtomicPtr::new(std::ptr::null_mut()); - self.head = AtomicPtr::new(dummy); - self.tail = AtomicPtr::new(dummy); - } - - pub unsafe fn clear_queue(&mut self, _num_threads: usize) { - let mut next; - let mut head = *self.head.get_mut(); - while !head.is_null() { - next = *(*head).next.get_mut(); - Node::free(head); - head = next; - } - } - - pub unsafe fn enqueue(&self, value: u64) { - let mut tail; - let node = Node::new_alloc(); - (*node).value = value; - (*node).next = AtomicPtr::new(std::ptr::null_mut()); - - loop { - tail = self.tail.load(Acquire); - let next = (*tail).next.load(Acquire); - if tail != self.tail.load(Acquire) { - continue; - } - - if next.is_null() { - if (*tail).next.compare_exchange(next, node, Release, Relaxed).is_ok() { - break; - } - } else { - let _ = self.tail.compare_exchange(tail, next, Release, Relaxed); - } - } - - let _ = self.tail.compare_exchange(tail, node, Release, Relaxed); - } - - pub unsafe fn dequeue(&self) -> Option { - loop { - let head = self.head.load(Acquire); - let tail = self.tail.load(Acquire); - - let next_ref = &(*head).next; - let next = next_ref.load(Acquire); - if self.head.load(Acquire) != head { - continue; - } - if head == tail { - if next.is_null() { - return None; - } - let _ = self.tail.compare_exchange(tail, next, Release, Relaxed); - } else { - let ret_val = (*next).value; - if self.head.compare_exchange(head, next, Release, Relaxed).is_ok() { - // NOTE: The popped `Node` is leaked. - return Some(ret_val); - } - } - } - } -} - -extern "C" fn thread_w(value: *mut c_void) -> *mut c_void { - unsafe { - let pid = *(value as *mut u64); - - INPUT[pid as usize] = pid * 10; - QUEUE.enqueue(INPUT[pid as usize]); - - std::ptr::null_mut() - } -} - -extern "C" fn thread_r(value: *mut c_void) -> *mut c_void { - unsafe { - let pid = *(value as *mut u64); - - OUTPUT[pid as usize] = QUEUE.dequeue(); - - std::ptr::null_mut() - } -} - -extern "C" fn thread_rw(value: *mut c_void) -> *mut c_void { - unsafe { - let pid = *(value as *mut u64); - - INPUT[pid as usize] = pid * 10; - QUEUE.enqueue(INPUT[pid as usize]); - - OUTPUT[pid as usize] = QUEUE.dequeue(); - - std::ptr::null_mut() - } -} - -#[unsafe(no_mangle)] -fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - let mut thread_ids: [pthread_t; MAX_THREADS] = [0; MAX_THREADS]; - - let attr: *const pthread_attr_t = std::ptr::null(); - - // TODO GENMC (TESTS): make different tests: - let readers = 0; - let writers = 0; - let rdwr = 2; - - let num_threads = readers + writers + rdwr; - - if num_threads > MAX_THREADS { - std::process::abort(); - } - - let mut i = 0; - unsafe { - MyStack::init_queue(&mut QUEUE, num_threads); - - for j in 0..num_threads { - PARAMS[j] = j as u64; - } - - /* Spawn threads */ - for _ in 0..writers { - let value: *mut c_void = (&raw mut PARAMS[i]) as *mut c_void; - if libc::pthread_create(&raw mut thread_ids[i], attr, thread_w, value) != 0 { - std::process::abort(); - } - i += 1; - } - for _ in 0..readers { - let value: *mut c_void = (&raw mut PARAMS[i]) as *mut c_void; - if libc::pthread_create(&raw mut thread_ids[i], attr, thread_r, value) != 0 { - std::process::abort(); - } - i += 1; - } - for _ in 0..rdwr { - let value: *mut c_void = (&raw mut PARAMS[i]) as *mut c_void; - if libc::pthread_create(&raw mut thread_ids[i], attr, thread_rw, value) != 0 { - std::process::abort(); - } - i += 1; - } - - for i in 0..num_threads { - if libc::pthread_join(thread_ids[i], std::ptr::null_mut()) != 0 { - std::process::abort(); - } - } - - MyStack::clear_queue(&mut QUEUE, num_threads); - } - - 0 -} diff --git a/tests/genmc/pass/data-structures/ms_queue_dynamic.stderr b/tests/genmc/pass/data-structures/ms_queue_dynamic.stderr deleted file mode 100644 index 0513e8df10..0000000000 --- a/tests/genmc/pass/data-structures/ms_queue_dynamic.stderr +++ /dev/null @@ -1,2 +0,0 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 222 diff --git a/tests/genmc/pass/data-structures/treiber_stack_dynamic.rs b/tests/genmc/pass/data-structures/treiber_stack_dynamic.rs deleted file mode 100644 index 04850b85a6..0000000000 --- a/tests/genmc/pass/data-structures/treiber_stack_dynamic.rs +++ /dev/null @@ -1,166 +0,0 @@ -//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows - -// This test is a translations of the GenMC test `treiber-stack-dynamic`, but with all code related to GenMC's hazard pointer API removed. -// The test leaks memory, so leak checks are disabled. - -#![no_main] -#![allow(static_mut_refs)] - -use std::alloc::{Layout, alloc, dealloc}; -use std::ffi::c_void; -use std::sync::atomic::{AtomicPtr, Ordering}; - -use libc::{self, pthread_attr_t, pthread_t}; - -const MAX_THREADS: usize = 32; - -static mut STACK: MyStack = MyStack::new(); -static mut THREADS: [pthread_t; MAX_THREADS] = [0; MAX_THREADS]; -static mut PARAMS: [u64; MAX_THREADS] = [0; MAX_THREADS]; - -#[repr(C)] -struct Node { - value: u64, - next: AtomicPtr, -} - -struct MyStack { - top: AtomicPtr, -} - -impl Node { - pub unsafe fn new_alloc() -> *mut Self { - alloc(Layout::new::()) as *mut Self - } - - pub unsafe fn free(node: *mut Self) { - dealloc(node as *mut u8, Layout::new::()) - } -} - -impl MyStack { - pub const fn new() -> Self { - Self { top: AtomicPtr::new(std::ptr::null_mut()) } - } - - pub unsafe fn init_stack(&mut self, _num_threads: usize) { - self.top = AtomicPtr::new(std::ptr::null_mut()); - } - - pub unsafe fn clear_stack(&mut self, _num_threads: usize) { - let mut next; - let mut top = *self.top.get_mut(); - while !top.is_null() { - next = *(*top).next.get_mut(); - Node::free(top); - top = next; - } - } - - pub unsafe fn push(&self, value: u64) { - let node = Node::new_alloc(); - (*node).value = value; - - loop { - let top = self.top.load(Ordering::Acquire); - (*node).next.store(top, Ordering::Relaxed); - if self.top.compare_exchange(top, node, Ordering::Release, Ordering::Relaxed).is_ok() { - break; - } - } - } - - pub unsafe fn pop(&self) -> u64 { - loop { - let top = self.top.load(Ordering::Acquire); - if top.is_null() { - return 0; - } - - let next = (*top).next.load(Ordering::Relaxed); - if self.top.compare_exchange(top, next, Ordering::Release, Ordering::Relaxed).is_ok() { - // NOTE: The popped `Node` is leaked. - return (*top).value; - } - } - } -} - -extern "C" fn thread_w(value: *mut c_void) -> *mut c_void { - unsafe { - let pid = *(value as *mut u64); - - STACK.push(pid); - } - std::ptr::null_mut() -} - -extern "C" fn thread_r(_value: *mut c_void) -> *mut c_void { - let _idx = unsafe { STACK.pop() }; - std::ptr::null_mut() -} - -extern "C" fn thread_rw(value: *mut c_void) -> *mut c_void { - unsafe { - let pid = *(value as *mut u64); - STACK.push(pid); - let _idx = STACK.pop(); - } - std::ptr::null_mut() -} - -#[unsafe(no_mangle)] -fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - let attr: *const pthread_attr_t = std::ptr::null(); - - // TODO GENMC: make different tests: - let readers = 1; - let writers = 2; - let rdwr = 0; - - let num_threads = readers + writers + rdwr; - - if num_threads > MAX_THREADS { - std::process::abort(); - } - - let mut i = 0; - unsafe { - MyStack::init_stack(&mut STACK, num_threads); - - for j in 0..num_threads { - PARAMS[j] = j as u64; - } - for _ in 0..readers { - let value: *mut c_void = (&raw mut PARAMS[i]) as *mut c_void; - if libc::pthread_create(&raw mut THREADS[i], attr, thread_r, value) != 0 { - std::process::abort(); - } - i += 1; - } - for _ in 0..writers { - let value: *mut c_void = (&raw mut PARAMS[i]) as *mut c_void; - if libc::pthread_create(&raw mut THREADS[i], attr, thread_w, value) != 0 { - std::process::abort(); - } - i += 1; - } - for _ in 0..rdwr { - let value: *mut c_void = (&raw mut PARAMS[i]) as *mut c_void; - if libc::pthread_create(&raw mut THREADS[i], attr, thread_rw, value) != 0 { - std::process::abort(); - } - i += 1; - } - - for i in 0..num_threads { - if libc::pthread_join(THREADS[i], std::ptr::null_mut()) != 0 { - std::process::abort(); - } - } - - MyStack::clear_stack(&mut STACK, num_threads); - } - - 0 -} diff --git a/tests/genmc/pass/data-structures/treiber_stack_dynamic.stderr b/tests/genmc/pass/data-structures/treiber_stack_dynamic.stderr deleted file mode 100644 index 15aa1e752a..0000000000 --- a/tests/genmc/pass/data-structures/treiber_stack_dynamic.stderr +++ /dev/null @@ -1,2 +0,0 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 22 diff --git a/tests/genmc/pass/thread/thread_locals.rs b/tests/genmc/pass/thread/thread_locals.rs deleted file mode 100644 index bb650c2076..0000000000 --- a/tests/genmc/pass/thread/thread_locals.rs +++ /dev/null @@ -1,55 +0,0 @@ -//@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows - -#![no_main] - -#[path = "../../../utils/genmc.rs"] -mod genmc; - -use std::alloc::{Layout, alloc}; -use std::cell::Cell; -use std::ffi::c_void; -use std::sync::atomic::{AtomicPtr, Ordering}; - -use crate::genmc::*; - -static X: AtomicPtr = AtomicPtr::new(std::ptr::null_mut()); - -thread_local! { - static R: Cell<*mut u64> = Cell::new(std::ptr::null_mut()); -} - -#[unsafe(no_mangle)] -fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - let _ids = unsafe { create_pthreads_no_params([thread_1, thread_2, thread_3]) }; - - 0 -} - -pub unsafe fn malloc() -> *mut u64 { - alloc(Layout::new::()) as *mut u64 -} - -extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { - unsafe { - R.set(malloc()); - let r_ptr = R.get(); - let _ = X.compare_exchange(std::ptr::null_mut(), r_ptr, Ordering::SeqCst, Ordering::SeqCst); - std::ptr::null_mut() - } -} - -extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { - unsafe { - R.set(malloc()); - std::ptr::null_mut() - } -} - -extern "C" fn thread_3(_value: *mut c_void) -> *mut c_void { - unsafe { - R.set(malloc()); - let r_ptr = R.get(); - let _ = X.compare_exchange(std::ptr::null_mut(), r_ptr, Ordering::SeqCst, Ordering::SeqCst); - std::ptr::null_mut() - } -} diff --git a/tests/genmc/pass/thread/thread_locals.stderr b/tests/genmc/pass/thread/thread_locals.stderr deleted file mode 100644 index 01701dfe69..0000000000 --- a/tests/genmc/pass/thread/thread_locals.stderr +++ /dev/null @@ -1,2 +0,0 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 2 From c885b2384c033afeb7b40ed97c2b0b7df46fdf26 Mon Sep 17 00:00:00 2001 From: Patrick-6 Date: Sat, 26 Jul 2025 16:42:21 +0200 Subject: [PATCH 34/41] More cleanup --- src/concurrency/genmc/mod.rs | 10 --------- src/concurrency/thread.rs | 43 +++++++++++++++++------------------- 2 files changed, 20 insertions(+), 33 deletions(-) diff --git a/src/concurrency/genmc/mod.rs b/src/concurrency/genmc/mod.rs index 2c423c72fd..6d0f5462c9 100644 --- a/src/concurrency/genmc/mod.rs +++ b/src/concurrency/genmc/mod.rs @@ -529,7 +529,6 @@ impl GenmcCtx { ); let machine = &ecx.machine; if memory_kind == MiriMemoryKind::Global.into() { - info!("GenMC: global memory allocation: {alloc_id:?}"); return ecx.get_global_allocation_address(&self.global_allocations, alloc_id); } let thread_infos = self.thread_id_manager.borrow(); @@ -537,10 +536,6 @@ impl GenmcCtx { let genmc_tid = thread_infos.get_genmc_tid(curr_thread); // GenMC doesn't support ZSTs, so we set the minimum size to 1 byte let genmc_size = size.bytes().max(1); - info!( - "GenMC: handle_alloc (thread: {curr_thread:?} ({genmc_tid:?}), size: {}, alignment: {alignment:?}, memory_kind: {memory_kind:?})", - size.bytes() - ); let alignment = alignment.bytes(); @@ -607,14 +602,9 @@ impl GenmcCtx { let genmc_parent_tid = thread_infos.get_genmc_tid(curr_thread_id); let genmc_new_tid = thread_infos.add_thread(new_thread_id); - info!( - "GenMC: handling thread creation (thread {curr_thread_id:?} ({genmc_parent_tid:?}) spawned thread {new_thread_id:?} ({genmc_new_tid:?}))" - ); - let mut mc = self.handle.borrow_mut(); mc.as_mut().handleThreadCreate(genmc_new_tid, genmc_parent_tid); - // TODO GENMC (ERROR HANDLING): can this ever fail? interp_ok(()) } diff --git a/src/concurrency/thread.rs b/src/concurrency/thread.rs index 0552a920dd..bca57ab848 100644 --- a/src/concurrency/thread.rs +++ b/src/concurrency/thread.rs @@ -113,8 +113,8 @@ pub enum BlockReason { } /// The state of a thread. -// TODO GENMC: is this ok to be pub? -pub enum ThreadState<'tcx> { +// FIXME(genmc,question): is this ok to be public? +pub(crate) enum ThreadState<'tcx> { /// The thread is enabled and can be executed. Enabled, /// The thread is blocked on something. @@ -136,16 +136,16 @@ impl<'tcx> std::fmt::Debug for ThreadState<'tcx> { } impl<'tcx> ThreadState<'tcx> { - // TODO GENMC: is it ok if these are pub? - pub fn is_enabled(&self) -> bool { + // FIXME(genmc,question): is it ok for these to be public? + pub(crate) fn is_enabled(&self) -> bool { matches!(self, ThreadState::Enabled) } - pub fn is_terminated(&self) -> bool { + pub(crate) fn is_terminated(&self) -> bool { matches!(self, ThreadState::Terminated) } - pub fn is_blocked_on(&self, reason: BlockReason) -> bool { + pub(crate) fn is_blocked_on(&self, reason: BlockReason) -> bool { matches!(*self, ThreadState::Blocked { reason: actual_reason, .. } if actual_reason == reason) } } @@ -211,8 +211,8 @@ impl<'tcx> Thread<'tcx> { self.thread_name.as_deref() } - pub fn get_state(&self) -> &ThreadState<'tcx> { - // TODO GENMC: should this implementation detail be exposed? + // FIXME(genmc,question): is this ok to be public? (it exposes implementation details) + pub(crate) fn get_state(&self) -> &ThreadState<'tcx> { &self.state } @@ -349,10 +349,10 @@ impl VisitProvenance for Frame<'_, Provenance, FrameExtra<'_>> { } } +// FIXME(genmc,question): is this ok to be public? /// The moment in time when a blocked thread should be woken up. -// TODO GENMC: is this ok to be pub? #[derive(Debug)] -pub enum Timeout { +pub(crate) enum Timeout { Monotonic(Instant), RealTime(SystemTime), } @@ -533,11 +533,6 @@ impl<'tcx> ThreadManager<'tcx> { self.threads.len() } - /// Get the total of threads that are currently enabled, i.e., could continue executing. - pub fn get_enabled_thread_count(&self) -> usize { - self.threads.iter().filter(|t| t.state.is_enabled()).count() - } - /// Get the total of threads that are currently live, i.e., not yet terminated. /// (They might be blocked.) pub fn get_live_thread_count(&self) -> usize { @@ -929,10 +924,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { assert!(thread.stack.is_empty(), "only threads with an empty stack can be terminated"); thread.state = ThreadState::Terminated; - // TODO GENMC (QUESTION): Can we move this down to where the GenmcCtx is? - if let Some(data_race) = this.machine.data_race.as_vclocks_mut() { - data_race.thread_terminated(&this.machine.threads); - } // Deallocate TLS. let gone_thread = this.active_thread(); { @@ -964,10 +955,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } } - // Inform GenMC that the thread finished. - // This needs to happen once all accesses to the thread are done, including freeing any TLS statics. - if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() { - genmc_ctx.handle_thread_finish(&this.machine.threads); + // FIXME(genmc,question): Is it correct to move this code here? + match &mut this.machine.data_race { + GlobalDataRaceHandler::None => {} + GlobalDataRaceHandler::Vclocks(data_race) => + data_race.thread_terminated(&this.machine.threads), + GlobalDataRaceHandler::Genmc(genmc_ctx) => { + // Inform GenMC that the thread finished. + // This needs to happen once all accesses to the thread are done, including freeing any TLS statics. + genmc_ctx.handle_thread_finish(&this.machine.threads) + } } // Unblock joining threads. From dd9b6663df210915fb29ea1c60c1ae16c5b944d5 Mon Sep 17 00:00:00 2001 From: Patrick-6 Date: Sat, 26 Jul 2025 16:56:11 +0200 Subject: [PATCH 35/41] Remove CXX NonNullUniquePtr. --- genmc-sys/src/cxx_extra.rs | 48 ----------------------------------- genmc-sys/src/lib.rs | 7 +++--- src/concurrency/genmc/mod.rs | 49 ++++++++++++++++++------------------ 3 files changed, 28 insertions(+), 76 deletions(-) delete mode 100644 genmc-sys/src/cxx_extra.rs diff --git a/genmc-sys/src/cxx_extra.rs b/genmc-sys/src/cxx_extra.rs deleted file mode 100644 index 55cfee57ea..0000000000 --- a/genmc-sys/src/cxx_extra.rs +++ /dev/null @@ -1,48 +0,0 @@ -#![allow(unused)] // TODO GENMC - -use std::pin::Pin; - -use cxx::UniquePtr; -use cxx::memory::UniquePtrTarget; - -#[repr(transparent)] -pub struct NonNullUniquePtr { - /// SAFETY: `inner` is never `null` - inner: UniquePtr, -} - -impl NonNullUniquePtr { - pub fn new(input: UniquePtr) -> Option { - if input.is_null() { - None - } else { - // SAFETY: `input` is not `null` - Some(unsafe { Self::new_unchecked(input) }) - } - } - - /// SAFETY: caller must ensure that `input` is not `null` - pub unsafe fn new_unchecked(input: UniquePtr) -> Self { - Self { inner: input } - } - - pub fn into_inner(self) -> UniquePtr { - self.inner - } - - pub fn as_mut(&mut self) -> Pin<&mut T> { - let ptr = self.inner.as_mut_ptr(); - - // SAFETY: `inner` is not `null` (type invariant) - let mut_reference = unsafe { ptr.as_mut().unwrap_unchecked() }; - // SAFETY: TODO GENMC (should be the same reason as in CXX crate, but there is no safety comment there) - unsafe { Pin::new_unchecked(mut_reference) } - } -} - -impl AsRef for NonNullUniquePtr { - fn as_ref(&self) -> &T { - // SAFETY: `inner` is not `null` (type invariant) - unsafe { self.inner.as_ref().unwrap_unchecked() } - } -} diff --git a/genmc-sys/src/lib.rs b/genmc-sys/src/lib.rs index d41c7b3035..44e9d43669 100644 --- a/genmc-sys/src/lib.rs +++ b/genmc-sys/src/lib.rs @@ -1,8 +1,8 @@ use std::str::FromStr; -pub use self::ffi::*; +pub use cxx::UniquePtr; -pub mod cxx_extra; +pub use self::ffi::*; /// Defined in "genmc/src/Support/SAddr.hpp". /// The first bit of all global addresses must be set to `1`, the rest are the actual address. @@ -210,8 +210,7 @@ mod ffi { type MiriGenMCShim; - fn createGenmcHandle(config: &GenmcParams) - -> UniquePtr; + fn createGenmcHandle(config: &GenmcParams) -> UniquePtr; fn getGlobalAllocStaticMask() -> u64; fn handleExecutionStart(self: Pin<&mut MiriGenMCShim>); diff --git a/src/concurrency/genmc/mod.rs b/src/concurrency/genmc/mod.rs index 6d0f5462c9..c6ea1495b7 100644 --- a/src/concurrency/genmc/mod.rs +++ b/src/concurrency/genmc/mod.rs @@ -3,7 +3,7 @@ use std::sync::Arc; use genmc_sys::{ ActionKind, GENMC_GLOBAL_ADDRESSES_MASK, GenmcScalar, MemOrdering, MiriGenMCShim, RMWBinOp, - createGenmcHandle, + UniquePtr, createGenmcHandle, }; use rustc_abi::{Align, Size}; use rustc_const_eval::interpret::{AllocId, InterpCx, InterpResult, interp_ok}; @@ -42,7 +42,7 @@ struct ExitStatus { } pub struct GenmcCtx { - handle: RefCell>, + handle: RefCell>, // TODO GENMC (PERFORMANCE): could use one RefCell for all internals instead of multiple thread_id_manager: RefCell, @@ -63,14 +63,10 @@ impl GenmcCtx { /// Create a new `GenmcCtx` from a given config. pub fn new(miri_config: &MiriConfig, target_usize_max: u64) -> Self { let genmc_config = miri_config.genmc_config.as_ref().unwrap(); - info!("GenMC: Creating new GenMC Context"); - - let handle = createGenmcHandle(&genmc_config.params); - let non_null_handle = NonNullUniquePtr::new(handle).expect("GenMC should not return null"); - let non_null_handle = RefCell::new(non_null_handle); + let handle = RefCell::new(createGenmcHandle(&genmc_config.params)); let global_allocations = Arc::new(GlobalAllocationHandler::new(target_usize_max)); Self { - handle: non_null_handle, + handle, thread_id_manager: Default::default(), allow_data_races: Cell::new(false), global_allocations, @@ -80,19 +76,23 @@ impl GenmcCtx { pub fn get_blocked_execution_count(&self) -> u64 { let mc = self.handle.borrow(); - mc.as_ref().getBlockedExecutionCount() + mc.as_ref().unwrap().getBlockedExecutionCount() } pub fn get_explored_execution_count(&self) -> u64 { let mc = self.handle.borrow(); - mc.as_ref().getExploredExecutionCount() + mc.as_ref().unwrap().getExploredExecutionCount() } /// Check if GenMC encountered an error that wasn't immediately returned during execution. /// Returns a string representation of the error if one occurred. pub fn try_get_error(&self) -> Option { let mc = self.handle.borrow(); - mc.as_ref().getErrorString().as_ref().map(|error| error.to_string_lossy().to_string()) + mc.as_ref() + .unwrap() + .getErrorString() + .as_ref() + .map(|error| error.to_string_lossy().to_string()) } /// Check if GenMC encountered an error that wasn't immediately returned during execution. @@ -100,6 +100,7 @@ impl GenmcCtx { pub fn get_result_message(&self) -> String { let mc = self.handle.borrow(); mc.as_ref() + .unwrap() .getResultMessage() .as_ref() .map(|error| error.to_string_lossy().to_string()) @@ -111,7 +112,7 @@ impl GenmcCtx { /// In GenMC mode, the input program should be repeatedly executed until this function returns `true` or an error is found. pub fn is_exploration_done(&self) -> bool { let mut mc = self.handle.borrow_mut(); - mc.as_mut().isExplorationDone() + mc.as_mut().unwrap().isExplorationDone() } pub fn get_exit_status(&self) -> Option<(i32, bool)> { @@ -136,7 +137,7 @@ impl GenmcCtx { self.exit_status.set(None); let mut mc = self.handle.borrow_mut(); - mc.as_mut().handleExecutionStart(); + mc.as_mut().unwrap().handleExecutionStart(); } /// Inform GenMC that the program's execution has ended. @@ -148,7 +149,7 @@ impl GenmcCtx { _ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, ) -> Result<(), String> { let mut mc = self.handle.borrow_mut(); - let result = mc.as_mut().handleExecutionEnd(); + let result = mc.as_mut().unwrap().handleExecutionEnd(); if let Some(msg) = result.as_ref() { Err(msg.to_string_lossy().to_string()) } else { @@ -383,7 +384,7 @@ impl GenmcCtx { let genmc_old_value = scalar_to_genmc_scalar(ecx, old_value)?; let mut mc = self.handle.borrow_mut(); - let pinned_mc = mc.as_mut(); + let pinned_mc = mc.as_mut().unwrap(); let cas_result = pinned_mc.handleCompareExchange( genmc_tid, genmc_address, @@ -540,7 +541,7 @@ impl GenmcCtx { let alignment = alignment.bytes(); let mut mc = self.handle.borrow_mut(); - let pinned_mc = mc.as_mut(); + let pinned_mc = mc.as_mut().unwrap(); let chosen_address = pinned_mc.handleMalloc(genmc_tid, genmc_size, alignment); // Non-global addresses should not be in the global address space or null. @@ -578,7 +579,7 @@ impl GenmcCtx { let genmc_address = address.bytes(); let mut mc = self.handle.borrow_mut(); - let pinned_mc = mc.as_mut(); + let pinned_mc = mc.as_mut().unwrap(); pinned_mc.handleFree(genmc_tid, genmc_address); // TODO GENMC (ERROR HANDLING): can this ever fail? @@ -603,7 +604,7 @@ impl GenmcCtx { let genmc_new_tid = thread_infos.add_thread(new_thread_id); let mut mc = self.handle.borrow_mut(); - mc.as_mut().handleThreadCreate(genmc_new_tid, genmc_parent_tid); + mc.as_mut().unwrap().handleThreadCreate(genmc_new_tid, genmc_parent_tid); interp_ok(()) } @@ -620,7 +621,7 @@ impl GenmcCtx { let genmc_child_tid = thread_infos.get_genmc_tid(child_thread_id); let mut mc = self.handle.borrow_mut(); - mc.as_mut().handleThreadJoin(genmc_curr_tid, genmc_child_tid); + mc.as_mut().unwrap().handleThreadJoin(genmc_curr_tid, genmc_child_tid); interp_ok(()) } @@ -640,7 +641,7 @@ impl GenmcCtx { ); let mut mc = self.handle.borrow_mut(); - mc.as_mut().handleThreadFinish(genmc_tid, ret_val); + mc.as_mut().unwrap().handleThreadFinish(genmc_tid, ret_val); } /// Handle a call to `libc::exit` or the exit of the main thread. @@ -666,7 +667,7 @@ impl GenmcCtx { let genmc_tid = thread_infos.get_genmc_tid(thread); let mut mc = self.handle.borrow_mut(); - mc.as_mut().handleThreadKill(genmc_tid); + mc.as_mut().unwrap().handleThreadKill(genmc_tid); } interp_ok(()) } @@ -712,7 +713,7 @@ impl GenmcCtx { let genmc_size = size.bytes(); let mut mc = self.handle.borrow_mut(); - let pinned_mc = mc.as_mut(); + let pinned_mc = mc.as_mut().unwrap(); let load_result = pinned_mc.handleLoad( genmc_tid, genmc_address, @@ -764,7 +765,7 @@ impl GenmcCtx { ); let mut mc = self.handle.borrow_mut(); - let pinned_mc = mc.as_mut(); + let pinned_mc = mc.as_mut().unwrap(); let store_result = pinned_mc.handleStore( genmc_tid, genmc_address, @@ -900,7 +901,7 @@ impl GenmcCtx { let curr_thread_info = thread_infos.get_genmc_tid(active_thread_id); let mut mc = self.handle.borrow_mut(); - let pinned_mc = mc.as_mut(); + let pinned_mc = mc.as_mut().unwrap(); let result = pinned_mc.scheduleNext(curr_thread_info, curr_thread_next_instr_kind); if result >= 0 { let next_thread_id = thread_infos From ad24c180d67e01a619389b087a5a8ea923ffb0bd Mon Sep 17 00:00:00 2001 From: Patrick-6 Date: Sat, 26 Jul 2025 17:28:22 +0200 Subject: [PATCH 36/41] Remove RMW / CAS support --- genmc-sys/src/lib.rs | 68 ----- genmc-sys/src_cpp/MiriInterface.hpp | 10 - .../src_cpp/MiriInterface/MiriInterface.cpp | 88 ------ genmc-sys/src_cpp/ResultHandling.hpp | 58 ---- src/concurrency/genmc/mapping.rs | 45 +--- src/concurrency/genmc/mod.rs | 254 +++--------------- tests/genmc/pass/atomics/cas_simple.rs | 47 ---- tests/genmc/pass/atomics/cas_simple.stderr | 2 - .../pass/atomics/rmw_edge_cases.i16_.stderr | 2 - .../pass/atomics/rmw_edge_cases.i32_.stderr | 2 - .../pass/atomics/rmw_edge_cases.i64_.stderr | 2 - .../pass/atomics/rmw_edge_cases.i8_.stderr | 2 - .../pass/atomics/rmw_edge_cases.isize_.stderr | 2 - tests/genmc/pass/atomics/rmw_edge_cases.rs | 88 ------ .../pass/atomics/rmw_edge_cases.u16_.stderr | 2 - .../pass/atomics/rmw_edge_cases.u32_.stderr | 2 - .../pass/atomics/rmw_edge_cases.u64_.stderr | 2 - .../pass/atomics/rmw_edge_cases.u8_.stderr | 2 - .../pass/atomics/rmw_edge_cases.usize_.stderr | 2 - tests/genmc/pass/atomics/rmw_simple.rs | 29 -- tests/genmc/pass/atomics/rmw_simple.stderr | 2 - tests/genmc/pass/litmus/LB_incMPs.rs | 48 ---- tests/genmc/pass/litmus/LB_incMPs.stderr | 2 - tests/genmc/pass/litmus/MPU_rels_acq.rs | 43 --- tests/genmc/pass/litmus/MPU_rels_acq.stderr | 2 - tests/genmc/pass/litmus/MP_incMPs.rs | 41 --- tests/genmc/pass/litmus/MP_incMPs.stderr | 2 - tests/genmc/pass/litmus/Z6_U0.rs | 40 --- tests/genmc/pass/litmus/Z6_U0.stderr | 2 - tests/genmc/pass/litmus/casdep.rs | 34 --- tests/genmc/pass/litmus/casdep.stderr | 2 - tests/genmc/pass/litmus/ccr.rs | 33 --- tests/genmc/pass/litmus/ccr.stderr | 2 - tests/genmc/pass/litmus/cii.rs | 36 --- tests/genmc/pass/litmus/cii.stderr | 2 - tests/genmc/pass/litmus/inc2w.rs | 36 --- tests/genmc/pass/litmus/inc2w.stderr | 2 - tests/genmc/pass/litmus/inc_inc_RR_W_RR.rs | 62 ----- .../genmc/pass/litmus/inc_inc_RR_W_RR.stderr | 2 - tests/genmc/pass/litmus/riwi.rs | 34 --- tests/genmc/pass/litmus/riwi.stderr | 2 - tests/genmc/pass/litmus/viktor-relseq.rs | 43 --- tests/genmc/pass/litmus/viktor-relseq.stderr | 2 - 43 files changed, 37 insertions(+), 1146 deletions(-) delete mode 100644 tests/genmc/pass/atomics/cas_simple.rs delete mode 100644 tests/genmc/pass/atomics/cas_simple.stderr delete mode 100644 tests/genmc/pass/atomics/rmw_edge_cases.i16_.stderr delete mode 100644 tests/genmc/pass/atomics/rmw_edge_cases.i32_.stderr delete mode 100644 tests/genmc/pass/atomics/rmw_edge_cases.i64_.stderr delete mode 100644 tests/genmc/pass/atomics/rmw_edge_cases.i8_.stderr delete mode 100644 tests/genmc/pass/atomics/rmw_edge_cases.isize_.stderr delete mode 100644 tests/genmc/pass/atomics/rmw_edge_cases.rs delete mode 100644 tests/genmc/pass/atomics/rmw_edge_cases.u16_.stderr delete mode 100644 tests/genmc/pass/atomics/rmw_edge_cases.u32_.stderr delete mode 100644 tests/genmc/pass/atomics/rmw_edge_cases.u64_.stderr delete mode 100644 tests/genmc/pass/atomics/rmw_edge_cases.u8_.stderr delete mode 100644 tests/genmc/pass/atomics/rmw_edge_cases.usize_.stderr delete mode 100644 tests/genmc/pass/atomics/rmw_simple.rs delete mode 100644 tests/genmc/pass/atomics/rmw_simple.stderr delete mode 100644 tests/genmc/pass/litmus/LB_incMPs.rs delete mode 100644 tests/genmc/pass/litmus/LB_incMPs.stderr delete mode 100644 tests/genmc/pass/litmus/MPU_rels_acq.rs delete mode 100644 tests/genmc/pass/litmus/MPU_rels_acq.stderr delete mode 100644 tests/genmc/pass/litmus/MP_incMPs.rs delete mode 100644 tests/genmc/pass/litmus/MP_incMPs.stderr delete mode 100644 tests/genmc/pass/litmus/Z6_U0.rs delete mode 100644 tests/genmc/pass/litmus/Z6_U0.stderr delete mode 100644 tests/genmc/pass/litmus/casdep.rs delete mode 100644 tests/genmc/pass/litmus/casdep.stderr delete mode 100644 tests/genmc/pass/litmus/ccr.rs delete mode 100644 tests/genmc/pass/litmus/ccr.stderr delete mode 100644 tests/genmc/pass/litmus/cii.rs delete mode 100644 tests/genmc/pass/litmus/cii.stderr delete mode 100644 tests/genmc/pass/litmus/inc2w.rs delete mode 100644 tests/genmc/pass/litmus/inc2w.stderr delete mode 100644 tests/genmc/pass/litmus/inc_inc_RR_W_RR.rs delete mode 100644 tests/genmc/pass/litmus/inc_inc_RR_W_RR.stderr delete mode 100644 tests/genmc/pass/litmus/riwi.rs delete mode 100644 tests/genmc/pass/litmus/riwi.stderr delete mode 100644 tests/genmc/pass/litmus/viktor-relseq.rs delete mode 100644 tests/genmc/pass/litmus/viktor-relseq.stderr diff --git a/genmc-sys/src/lib.rs b/genmc-sys/src/lib.rs index 44e9d43669..214a08fffb 100644 --- a/genmc-sys/src/lib.rs +++ b/genmc-sys/src/lib.rs @@ -120,21 +120,6 @@ mod ffi { SequentiallyConsistent = 6, } - #[derive(Debug)] - enum RMWBinOp { - Xchg = 0, - Add = 1, - Sub = 2, - And = 3, - Nand = 4, - Or = 5, - Xor = 6, - Max = 7, - Min = 8, - UMax = 9, - UMin = 10, - } - #[derive(Debug, Clone, Copy)] struct GenmcScalar { value: u64, @@ -163,39 +148,12 @@ mod ffi { isCoMaxWrite: bool, } - #[must_use] - #[derive(Debug)] - struct ReadModifyWriteResult { - /// If there was an error, it will be stored in `error`, otherwise it is `None`. - error: UniquePtr, - /// The value that was read by the RMW operation as the left operand. - old_value: GenmcScalar, - /// The value that was produced by the RMW operation. - new_value: GenmcScalar, - /// `true` if the write should also be reflected in Miri's memory representation. - isCoMaxWrite: bool, - } - - #[must_use] - #[derive(Debug)] - struct CompareExchangeResult { - /// If there was an error, it will be stored in `error`, otherwise it is `None`. - error: UniquePtr, - /// The value that was read by the compare-exchange. - old_value: GenmcScalar, - /// `true` if compare_exchange op was successful. - is_success: bool, - /// `true` if the write should also be reflected in Miri's memory representation. - isCoMaxWrite: bool, - } - /**** /\ Result & Error types /\ ****/ unsafe extern "C++" { include!("MiriInterface.hpp"); type MemOrdering; - type RMWBinOp; // Types for Scheduling queries: type ActionKind; @@ -203,8 +161,6 @@ mod ffi { // Result / Error types: type LoadResult; type StoreResult; - type ReadModifyWriteResult; - type CompareExchangeResult; type GenmcScalar; @@ -224,30 +180,6 @@ mod ffi { memory_ordering: MemOrdering, old_value: GenmcScalar, ) -> LoadResult; - fn handleReadModifyWrite( - self: Pin<&mut MiriGenMCShim>, - thread_id: i32, - address: u64, - size: u64, - load_ordering: MemOrdering, - store_ordering: MemOrdering, - rmw_op: RMWBinOp, - rhs_value: GenmcScalar, - old_value: GenmcScalar, - ) -> ReadModifyWriteResult; - fn handleCompareExchange( - self: Pin<&mut MiriGenMCShim>, - thread_id: i32, - address: u64, - size: u64, - expected_value: GenmcScalar, - new_value: GenmcScalar, - old_value: GenmcScalar, - success_load_ordering: MemOrdering, - success_store_ordering: MemOrdering, - fail_load_ordering: MemOrdering, - can_fail_spuriously: bool, - ) -> CompareExchangeResult; fn handleStore( self: Pin<&mut MiriGenMCShim>, thread_id: i32, diff --git a/genmc-sys/src_cpp/MiriInterface.hpp b/genmc-sys/src_cpp/MiriInterface.hpp index 0bbdda14ad..4e28a341d3 100644 --- a/genmc-sys/src_cpp/MiriInterface.hpp +++ b/genmc-sys/src_cpp/MiriInterface.hpp @@ -55,16 +55,6 @@ struct MiriGenMCShim : private GenMCDriver /////////////////// [[nodiscard]] LoadResult handleLoad(ThreadId thread_id, uint64_t address, uint64_t size, MemOrdering ord, GenmcScalar old_val); - [[nodiscard]] ReadModifyWriteResult - handleReadModifyWrite(ThreadId thread_id, uint64_t address, uint64_t size, - MemOrdering loadOrd, MemOrdering store_ordering, RMWBinOp rmw_op, - GenmcScalar rhs_value, GenmcScalar old_val); - [[nodiscard]] CompareExchangeResult - handleCompareExchange(ThreadId thread_id, uint64_t address, uint64_t size, - GenmcScalar expected_value, GenmcScalar new_value, - GenmcScalar old_val, MemOrdering success_load_ordering, - MemOrdering success_store_ordering, MemOrdering fail_load_ordering, - bool can_fail_spuriously); [[nodiscard]] StoreResult handleStore(ThreadId thread_id, uint64_t address, uint64_t size, GenmcScalar value, GenmcScalar old_val, MemOrdering ord); diff --git a/genmc-sys/src_cpp/MiriInterface/MiriInterface.cpp b/genmc-sys/src_cpp/MiriInterface/MiriInterface.cpp index a67f6f5be6..d43972bbac 100644 --- a/genmc-sys/src_cpp/MiriInterface/MiriInterface.cpp +++ b/genmc-sys/src_cpp/MiriInterface/MiriInterface.cpp @@ -234,94 +234,6 @@ void MiriGenMCShim::handleThreadKill(ThreadId thread_id) ERROR("Unimplemented: load returned unexpected result."); } -[[nodiscard]] auto MiriGenMCShim::handleReadModifyWrite(ThreadId thread_id, uint64_t address, - uint64_t size, MemOrdering loadOrd, - MemOrdering store_ordering, RMWBinOp rmw_op, - GenmcScalar rhs_value, GenmcScalar old_val) - -> ReadModifyWriteResult -{ - const auto addr = SAddr(address); - const auto aSize = ASize(size); - // `type` is only used for printing. - const auto type = AType::Unsigned; - - const auto rhsVal = rhs_value.toSVal(); - - const auto oldValSetter = [this, old_val](SAddr addr) { - this->handleOldVal(addr, old_val); - }; - const auto ret = handleLoadResetIfNone( - oldValSetter, thread_id, loadOrd, addr, aSize, type, rmw_op, rhsVal, EventDeps()); - RETURN_IF_ERROR(ret, ReadModifyWriteResult); - - const auto *retVal = std::get_if(&ret); - if (nullptr == retVal) { - ERROR("Unimplemented: read-modify-write returned unhandled result."); - } - const auto oldVal = *retVal; - const auto newVal = executeRMWBinOp(oldVal, rhsVal, size, rmw_op); - - const auto storePos = incPos(thread_id); - const auto storeRet = GenMCDriver::handleStore( - oldValSetter, storePos, store_ordering, addr, aSize, type, newVal); - RETURN_IF_ERROR(storeRet, ReadModifyWriteResult); - - const auto *storeRetVal = std::get_if(&ret); - ERROR_ON(nullptr == storeRetVal, "Unimplemented: load returned unexpected result."); - - // TODO GENMC(mixed-accesses): calculate this value - const auto &g = getExec().getGraph(); - const bool isCoMaxWrite = g.co_max(addr)->getPos() == storePos; - LOG(VerbosityLevel::Tip) << "TODO GENMC: calcuate isCoMaxWrite!!!\n"; - return ReadModifyWriteResult(oldVal, newVal, isCoMaxWrite); -} - -[[nodiscard]] auto MiriGenMCShim::handleCompareExchange( - ThreadId thread_id, uint64_t address, uint64_t size, GenmcScalar expected_value, - GenmcScalar new_value, GenmcScalar old_val, MemOrdering success_load_ordering, - MemOrdering success_store_ordering, MemOrdering fail_load_ordering, - bool can_fail_spuriously) -> CompareExchangeResult -{ - auto addr = SAddr(address); - auto aSize = ASize(size); - // `type` is only used for printing. - auto type = AType::Unsigned; - - auto expectedVal = expected_value.toSVal(); - auto newVal = new_value.toSVal(); - - // FIXME(GenMC): properly handle failure memory ordering. - - auto oldValSetter = [this, old_val](SAddr addr) { this->handleOldVal(addr, old_val); }; - const auto ret = handleLoadResetIfNone( - oldValSetter, thread_id, success_load_ordering, addr, aSize, type, expectedVal, - newVal); - RETURN_IF_ERROR(ret, CompareExchangeResult); - - const auto *retVal = std::get_if(&ret); - ERROR_ON(nullptr == retVal, "Unimplemented: load returned unexpected result."); - - const auto oldVal = *retVal; - if (oldVal != expectedVal) - return CompareExchangeResult::failure(oldVal); - - // FIXME(GenMC): Add support for modelling spurious failures. - - const auto storePos = incPos(thread_id); - const auto storeRet = GenMCDriver::handleStore( - oldValSetter, storePos, success_store_ordering, addr, aSize, type, newVal); - RETURN_IF_ERROR(storeRet, CompareExchangeResult); - - const auto *storeRetVal = std::get_if(&ret); - ERROR_ON(nullptr == storeRetVal, "Unimplemented: load returned unexpected result."); - - // TODO GENMC(mixed-accesses): calculate this value - const auto &g = getExec().getGraph(); - const bool isCoMaxWrite = g.co_max(addr)->getPos() == storePos; - LOG(VerbosityLevel::Tip) << "TODO GENMC: calcuate isCoMaxWrite!!!\n"; - return CompareExchangeResult::success(oldVal, isCoMaxWrite); -} - [[nodiscard]] auto MiriGenMCShim::handleStore(ThreadId thread_id, uint64_t address, uint64_t size, GenmcScalar value, GenmcScalar old_val, MemOrdering ord) -> StoreResult diff --git a/genmc-sys/src_cpp/ResultHandling.hpp b/genmc-sys/src_cpp/ResultHandling.hpp index 7d7a209a68..878cf3e5a6 100644 --- a/genmc-sys/src_cpp/ResultHandling.hpp +++ b/genmc-sys/src_cpp/ResultHandling.hpp @@ -110,62 +110,4 @@ struct StoreResult { } }; -struct ReadModifyWriteResult { - /// if not `nullptr`, it contains an error encountered during the handling of the RMW. - std::unique_ptr error; - /// The value that was read by the RMW operation as the left operand. - GenmcScalar old_value; - /// The value that was produced by the RMW operation. - GenmcScalar new_value; - /// `true` if the write should also be reflected in Miri's memory representation. - bool isCoMaxWrite; - -private: - ReadModifyWriteResult(std::string msg) : error(std::make_unique(msg)) - { - old_value = GenmcScalar(); - new_value = GenmcScalar(); - } - -public: - ReadModifyWriteResult(SVal old_value, SVal new_value, bool isCoMaxWrite) - : old_value(GenmcScalar(old_value)), new_value(GenmcScalar(new_value)), - isCoMaxWrite(isCoMaxWrite), error(nullptr) - { - } - - static ReadModifyWriteResult fromError(std::string msg) - { - return ReadModifyWriteResult(msg); - } -}; - -struct CompareExchangeResult { - /// if not `nullptr`, it contains an error encountered during the handling of the RMW op. - std::unique_ptr error; - /// The value that was read by the compare-exchange. - GenmcScalar old_value; - /// `true` if compare_exchange op was successful. - bool is_success; - /// `true` if the write should also be reflected in Miri's memory representation. - bool isCoMaxWrite; - - static CompareExchangeResult success(SVal old_value, bool isCoMaxWrite) - { - return CompareExchangeResult{nullptr, GenmcScalar(old_value), true, isCoMaxWrite}; - } - - static CompareExchangeResult failure(SVal old_value) - { - return CompareExchangeResult{nullptr, GenmcScalar(old_value), false, false}; - } - - static CompareExchangeResult fromError(std::string msg) - { - const auto dummy_scalar = GenmcScalar(); - return CompareExchangeResult{std::make_unique(msg), dummy_scalar, - false, false}; - } -}; - #endif /* GENMC_RESULT_HANDLING_HPP */ diff --git a/src/concurrency/genmc/mapping.rs b/src/concurrency/genmc/mapping.rs index 5923c9b967..a324874f4f 100644 --- a/src/concurrency/genmc/mapping.rs +++ b/src/concurrency/genmc/mapping.rs @@ -1,6 +1,6 @@ -use genmc_sys::{MemOrdering, RMWBinOp}; +use genmc_sys::MemOrdering; -use crate::{AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd}; +use crate::{AtomicReadOrd, AtomicWriteOrd}; // This file contains functionality to convert between Miri enums and their GenMC counterparts. @@ -23,44 +23,3 @@ impl AtomicWriteOrd { } } } - -impl AtomicRwOrd { - /// Split up the atomic success ordering of a read-modify-write operation into GenMC's representation. - /// Note that both returned orderings are currently identical, because this is what GenMC expects. - pub(super) fn to_genmc_memory_orderings(self) -> (MemOrdering, MemOrdering) { - match self { - AtomicRwOrd::Relaxed => (MemOrdering::Relaxed, MemOrdering::Relaxed), - AtomicRwOrd::Acquire => (MemOrdering::Acquire, MemOrdering::Acquire), - AtomicRwOrd::Release => (MemOrdering::Release, MemOrdering::Release), - AtomicRwOrd::AcqRel => (MemOrdering::AcquireRelease, MemOrdering::AcquireRelease), - AtomicRwOrd::SeqCst => - (MemOrdering::SequentiallyConsistent, MemOrdering::SequentiallyConsistent), - } - } -} - -/// Convert a possibly signed Miri min/max operation to its GenMC counterpart. -pub(super) fn min_max_to_genmc_rmw_op(min: bool, is_signed: bool) -> RMWBinOp { - // FIXME(genmc): is there a use for FMin/FMax? GenMC has (Min, UMin, FMin) - match (min, is_signed) { - (true, true) => RMWBinOp::Min, - (false, true) => RMWBinOp::Max, - (true, false) => RMWBinOp::UMin, - (false, false) => RMWBinOp::UMax, - } -} - -/// Convert a possibly negated binary operation to its GenMC counterpart. -pub(super) fn to_genmc_rmw_op(bin_op: rustc_middle::mir::BinOp, negate: bool) -> RMWBinOp { - match bin_op { - rustc_middle::mir::BinOp::Add => RMWBinOp::Add, - rustc_middle::mir::BinOp::Sub => RMWBinOp::Sub, - rustc_middle::mir::BinOp::BitOr if !negate => RMWBinOp::Or, - rustc_middle::mir::BinOp::BitXor if !negate => RMWBinOp::Xor, - rustc_middle::mir::BinOp::BitAnd if negate => RMWBinOp::Nand, - rustc_middle::mir::BinOp::BitAnd => RMWBinOp::And, - _ => { - panic!("unsupported atomic operation: bin_op: {bin_op:?}, negate: {negate}"); - } - } -} diff --git a/src/concurrency/genmc/mod.rs b/src/concurrency/genmc/mod.rs index c6ea1495b7..d3c305ec94 100644 --- a/src/concurrency/genmc/mod.rs +++ b/src/concurrency/genmc/mod.rs @@ -2,7 +2,7 @@ use std::cell::{Cell, RefCell}; use std::sync::Arc; use genmc_sys::{ - ActionKind, GENMC_GLOBAL_ADDRESSES_MASK, GenmcScalar, MemOrdering, MiriGenMCShim, RMWBinOp, + ActionKind, GENMC_GLOBAL_ADDRESSES_MASK, GenmcScalar, MemOrdering, MiriGenMCShim, UniquePtr, createGenmcHandle, }; use rustc_abi::{Align, Size}; @@ -12,7 +12,6 @@ use tracing::info; use self::global_allocations::{EvalContextExt as _, GlobalAllocationHandler}; use self::helper::{genmc_scalar_to_scalar, scalar_to_genmc_scalar}; -use self::mapping::{min_max_to_genmc_rmw_op, to_genmc_rmw_op}; use self::thread_id_map::ThreadIdMap; use crate::concurrency::genmc::helper::split_access; use crate::{ @@ -230,42 +229,20 @@ impl GenmcCtx { /// Returns `(old_val, Option)`. `new_val` might not be the latest write in coherence order, which is indicated by `None`. pub(crate) fn atomic_rmw_op<'tcx>( &self, - ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, - address: Size, - size: Size, - ordering: AtomicRwOrd, - (rmw_op, not): (mir::BinOp, bool), - rhs_scalar: Scalar, + _ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, + _address: Size, + _size: Size, + _ordering: AtomicRwOrd, + (_rmw_op, _not): (mir::BinOp, bool), + _rhs_scalar: Scalar, // The value that we would get, if we were to do a non-atomic load here. - old_value: Scalar, + _old_value: Scalar, ) -> InterpResult<'tcx, (Scalar, Option)> { assert!( !self.allow_data_races.get(), "atomic read-modify-write operation with data race checking disabled." ); - let (load_ordering, store_ordering) = ordering.to_genmc_memory_orderings(); - let genmc_rmw_op = to_genmc_rmw_op(rmw_op, not); - tracing::info!( - "GenMC: atomic_rmw_op (op: {rmw_op:?}, not: {not}, genmc_rmw_op: {genmc_rmw_op:?}): rhs value: {rhs_scalar:?}, orderings ({load_ordering:?}, {store_ordering:?})" - ); - - if matches!(rhs_scalar, Scalar::Ptr(..)) { - throw_unsup_format!( - "Right hand side of atomic read-modify-write operation cannot be a pointer" - ); - } - let genmc_rhs_scalar = scalar_to_genmc_scalar(ecx, rhs_scalar)?; - let genmc_old_value = scalar_to_genmc_scalar(ecx, old_value)?; - self.atomic_rmw_op_impl( - ecx, - address, - size, - load_ordering, - store_ordering, - genmc_rmw_op, - genmc_rhs_scalar, - genmc_old_value, - ) + throw_unsup_format!("FIXME(genmc): Add support for atomic RMW.") } /// Inform GenMC about an atomic `min` or `max` operation. @@ -273,142 +250,59 @@ impl GenmcCtx { /// Returns `(old_val, Option)`. `new_val` might not be the latest write in coherence order, which is indicated by `None`. pub(crate) fn atomic_min_max_op<'tcx>( &self, - ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, - address: Size, - size: Size, - ordering: AtomicRwOrd, - min: bool, - is_signed: bool, - rhs_scalar: Scalar, + _ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, + _address: Size, + _size: Size, + _ordering: AtomicRwOrd, + _min: bool, + _is_signed: bool, + _rhs_scalar: Scalar, // The value that we would get, if we were to do a non-atomic load here. - old_value: Scalar, + _old_value: Scalar, ) -> InterpResult<'tcx, (Scalar, Option)> { assert!( !self.allow_data_races.get(), "atomic min/max operation with data race checking disabled." ); - let (load_ordering, store_ordering) = ordering.to_genmc_memory_orderings(); - let genmc_rmw_op = min_max_to_genmc_rmw_op(min, is_signed); - tracing::info!( - "GenMC: atomic_min_max_op (min: {min}, signed: {is_signed}, genmc_rmw_op: {genmc_rmw_op:?}): rhs value: {rhs_scalar:?}, orderings ({load_ordering:?}, {store_ordering:?})" - ); - - // FIXME(genmc): can `rhs_scalar` be a pointer? Should this be allowed? - let genmc_rhs_scalar = scalar_to_genmc_scalar(ecx, rhs_scalar)?; - let genmc_old_value = scalar_to_genmc_scalar(ecx, old_value)?; - self.atomic_rmw_op_impl( - ecx, - address, - size, - load_ordering, - store_ordering, - genmc_rmw_op, - genmc_rhs_scalar, - genmc_old_value, - ) + throw_unsup_format!("FIXME(genmc): Add support for atomic min/max.") } /// Returns `(old_val, Option)`. `new_val` might not be the latest write in coherence order, which is indicated by `None`. pub(crate) fn atomic_exchange<'tcx>( &self, - ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, - address: Size, - size: Size, - rhs_scalar: Scalar, - ordering: AtomicRwOrd, + _ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, + _address: Size, + _size: Size, + _rhs_scalar: Scalar, + _ordering: AtomicRwOrd, // The value that we would get, if we were to do a non-atomic load here. - old_value: Scalar, + _old_value: Scalar, ) -> InterpResult<'tcx, (Scalar, Option)> { assert!( !self.allow_data_races.get(), "atomic swap operation with data race checking disabled." ); - - let (load_ordering, store_ordering) = ordering.to_genmc_memory_orderings(); - let genmc_rmw_op = RMWBinOp::Xchg; - tracing::info!( - "GenMC: atomic_exchange (op: {genmc_rmw_op:?}): new value: {rhs_scalar:?}, orderings ({load_ordering:?}, {store_ordering:?})" - ); - let genmc_rhs_scalar = scalar_to_genmc_scalar(ecx, rhs_scalar)?; - let genmc_old_value = scalar_to_genmc_scalar(ecx, old_value)?; - self.atomic_rmw_op_impl( - ecx, - address, - size, - load_ordering, - store_ordering, - genmc_rmw_op, - genmc_rhs_scalar, - genmc_old_value, - ) + throw_unsup_format!("FIXME(genmc): Add support for atomic swap.") } pub(crate) fn atomic_compare_exchange<'tcx>( &self, - ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, - address: Size, - size: Size, - expected_old_value: Scalar, - new_value: Scalar, - success: AtomicRwOrd, - fail: AtomicReadOrd, - can_fail_spuriously: bool, + _ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, + _address: Size, + _size: Size, + _expected_old_value: Scalar, + _new_value: Scalar, + _success: AtomicRwOrd, + _fail: AtomicReadOrd, + _can_fail_spuriously: bool, // The value that we would get, if we were to do a non-atomic load here. - old_value: Scalar, + _old_value: Scalar, ) -> InterpResult<'tcx, (Scalar, bool, bool)> { assert!( !self.allow_data_races.get(), "atomic compare-exchange with data race checking disabled." ); - - let machine = &ecx.machine; - let (success_load_ordering, success_store_ordering) = success.to_genmc_memory_orderings(); - let fail_load_ordering = fail.convert(); - - info!( - "GenMC: atomic_compare_exchange, address: {address:?}, size: {size:?} (expect: {expected_old_value:?}, new: {new_value:?}, old_value: {old_value:?}, {success:?}, {fail:?}), can fail spuriously: {can_fail_spuriously}" - ); - info!( - "GenMC: atomic_compare_exchange orderings: success: ({success_load_ordering:?}, {success_store_ordering:?}), failure load ordering: {fail_load_ordering:?}" - ); - - let thread_infos = self.thread_id_manager.borrow(); - let curr_thread = machine.threads.active_thread(); - let genmc_tid = thread_infos.get_genmc_tid(curr_thread); - - let genmc_address = address.bytes(); - let genmc_size = size.bytes(); - - let genmc_expected_value = scalar_to_genmc_scalar(ecx, expected_old_value)?; - let genmc_new_value = scalar_to_genmc_scalar(ecx, new_value)?; - let genmc_old_value = scalar_to_genmc_scalar(ecx, old_value)?; - - let mut mc = self.handle.borrow_mut(); - let pinned_mc = mc.as_mut().unwrap(); - let cas_result = pinned_mc.handleCompareExchange( - genmc_tid, - genmc_address, - genmc_size, - genmc_expected_value, - genmc_new_value, - genmc_old_value, - success_load_ordering, - success_store_ordering, - fail_load_ordering, - can_fail_spuriously, - ); - - if let Some(error) = cas_result.error.as_ref() { - throw_ub_format!("{}", error.to_string_lossy()); // TODO GENMC: proper error handling: find correct error here - } - - let return_scalar = genmc_scalar_to_scalar(ecx, cas_result.old_value, size)?; - info!( - "GenMC: atomic_compare_exchange: result: {cas_result:?}, returning scalar: {return_scalar:?}" - ); - // The write can only be a co-maximal write if the CAS succeeded. - assert!(cas_result.is_success || !cas_result.isCoMaxWrite); - interp_ok((return_scalar, cas_result.isCoMaxWrite, cas_result.is_success)) + throw_unsup_format!("FIXME(genmc): Add support for atomic compare_exchange.") } /// Inform GenMC about a non-atomic memory load @@ -782,84 +676,6 @@ impl GenmcCtx { interp_ok(store_result.isCoMaxWrite) } - fn atomic_rmw_op_impl<'tcx>( - &self, - ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, - address: Size, - size: Size, - load_ordering: MemOrdering, - store_ordering: MemOrdering, - genmc_rmw_op: RMWBinOp, - genmc_rhs_scalar: GenmcScalar, - genmc_old_value: GenmcScalar, - ) -> InterpResult<'tcx, (Scalar, Option)> { - assert!( - size.bytes() <= 8, - "TODO GENMC: no support for accesses larger than 8 bytes (got {} bytes)", - size.bytes() - ); - let machine = &ecx.machine; - assert_ne!(0, size.bytes()); - let thread_infos = self.thread_id_manager.borrow(); - let curr_thread_id = machine.threads.active_thread(); - let genmc_tid = thread_infos.get_genmc_tid(curr_thread_id); - - let genmc_address = address.bytes(); - let genmc_size = size.bytes(); - - info!( - "GenMC: atomic_rmw_op, thread: {curr_thread_id:?} ({genmc_tid:?}) (op: {genmc_rmw_op:?}, rhs value: {genmc_rhs_scalar:?}), address: {address:?}, size: {size:?}, orderings: ({load_ordering:?}, {store_ordering:?})", - ); - - let mut mc = self.handle.borrow_mut(); - let pinned_mc = mc.as_mut(); - let rmw_result = pinned_mc.handleReadModifyWrite( - genmc_tid, - genmc_address, - genmc_size, - load_ordering, - store_ordering, - genmc_rmw_op, - genmc_rhs_scalar, - genmc_old_value, - ); - - if let Some(error) = rmw_result.error.as_ref() { - throw_ub_format!("{}", error.to_string_lossy()); // TODO GENMC: proper error handling: find correct error here - } - - // Check that both RMW arguments have sane provenance. - match ( - genmc_rmw_op, - rmw_result.old_value.has_provenance(), - genmc_rhs_scalar.has_provenance(), - ) { - // compare-exchange should not swap a pointer and an integer. - // FIXME(GenMC): is this correct? - (RMWBinOp::Xchg, left, right) => - if left != right { - throw_ub_format!( - "atomic compare-exchange arguments should either both have pointer provenance ({left} != {right}). Both arguments should be pointers, or both integers." - ); - }, - // All other read-modify-write operations should never have a right-side argument that's a pointer. - (_, _, true) => - throw_ub_format!( - "atomic read-modify-write operation right argument has pointer provenance." - ), - _ => {} - } - - let old_value_scalar = genmc_scalar_to_scalar(ecx, rmw_result.old_value, size)?; - - let new_value_scalar = if rmw_result.isCoMaxWrite { - Some(genmc_scalar_to_scalar(ecx, rmw_result.new_value, size)?) - } else { - None - }; - interp_ok((old_value_scalar, new_value_scalar)) - } - /**** Scheduling functionality ****/ pub fn schedule_thread<'tcx>( diff --git a/tests/genmc/pass/atomics/cas_simple.rs b/tests/genmc/pass/atomics/cas_simple.rs deleted file mode 100644 index e9893ffe1b..0000000000 --- a/tests/genmc/pass/atomics/cas_simple.rs +++ /dev/null @@ -1,47 +0,0 @@ -//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows - -#![no_main] - -use std::sync::atomic::*; - -static VALUE: AtomicUsize = AtomicUsize::new(0); - -const SUCCESS_ORD: Ordering = Ordering::SeqCst; -const FAILURE_ORD: Ordering = Ordering::SeqCst; - -#[unsafe(no_mangle)] -fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - VALUE.store(1, SUCCESS_ORD); - - let current = 1; - let new_value = 2; - // Expect success: - match VALUE.compare_exchange(current, new_value, SUCCESS_ORD, FAILURE_ORD) { - Ok(old_value) => - if old_value != current { - std::process::abort(); - }, - Err(_value) => std::process::abort(), - } - - if new_value != VALUE.load(SUCCESS_ORD) { - std::process::abort() - } - - let dummy_value = 42; - let wrong_value = 1234; - - // Expect failure: - match VALUE.compare_exchange(wrong_value, dummy_value, SUCCESS_ORD, FAILURE_ORD) { - Ok(_old_value) => std::process::abort(), - Err(old_value) => - if old_value != new_value { - std::process::abort(); - }, - } - - if new_value != VALUE.load(SUCCESS_ORD) { - std::process::abort() - } - 0 -} diff --git a/tests/genmc/pass/atomics/cas_simple.stderr b/tests/genmc/pass/atomics/cas_simple.stderr deleted file mode 100644 index 3b22247ee4..0000000000 --- a/tests/genmc/pass/atomics/cas_simple.stderr +++ /dev/null @@ -1,2 +0,0 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 1 diff --git a/tests/genmc/pass/atomics/rmw_edge_cases.i16_.stderr b/tests/genmc/pass/atomics/rmw_edge_cases.i16_.stderr deleted file mode 100644 index 3b22247ee4..0000000000 --- a/tests/genmc/pass/atomics/rmw_edge_cases.i16_.stderr +++ /dev/null @@ -1,2 +0,0 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 1 diff --git a/tests/genmc/pass/atomics/rmw_edge_cases.i32_.stderr b/tests/genmc/pass/atomics/rmw_edge_cases.i32_.stderr deleted file mode 100644 index 3b22247ee4..0000000000 --- a/tests/genmc/pass/atomics/rmw_edge_cases.i32_.stderr +++ /dev/null @@ -1,2 +0,0 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 1 diff --git a/tests/genmc/pass/atomics/rmw_edge_cases.i64_.stderr b/tests/genmc/pass/atomics/rmw_edge_cases.i64_.stderr deleted file mode 100644 index 3b22247ee4..0000000000 --- a/tests/genmc/pass/atomics/rmw_edge_cases.i64_.stderr +++ /dev/null @@ -1,2 +0,0 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 1 diff --git a/tests/genmc/pass/atomics/rmw_edge_cases.i8_.stderr b/tests/genmc/pass/atomics/rmw_edge_cases.i8_.stderr deleted file mode 100644 index 3b22247ee4..0000000000 --- a/tests/genmc/pass/atomics/rmw_edge_cases.i8_.stderr +++ /dev/null @@ -1,2 +0,0 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 1 diff --git a/tests/genmc/pass/atomics/rmw_edge_cases.isize_.stderr b/tests/genmc/pass/atomics/rmw_edge_cases.isize_.stderr deleted file mode 100644 index 3b22247ee4..0000000000 --- a/tests/genmc/pass/atomics/rmw_edge_cases.isize_.stderr +++ /dev/null @@ -1,2 +0,0 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 1 diff --git a/tests/genmc/pass/atomics/rmw_edge_cases.rs b/tests/genmc/pass/atomics/rmw_edge_cases.rs deleted file mode 100644 index dbdef25071..0000000000 --- a/tests/genmc/pass/atomics/rmw_edge_cases.rs +++ /dev/null @@ -1,88 +0,0 @@ -//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows -//@revisions: u8_ u16_ u32_ u64_ usize_ i8_ i16_ i32_ i64_ isize_ - -// FIXME(genmc): ensure that 64 bit tests don't run on platforms without 64 bit atomics -// FIXME(genmc): add 128 bit tests for platforms that support it, once GenMC gets 128 bit atomic support - -// This test check for correct handling of some edge cases with atomic read-modify-write operations for all integer sizes. -// Atomic max and min should return the previous value, and store the result in the atomic. -// Atomic addition and subtraction should have wrapping semantics. - -#![no_main] - -#[cfg(u8_)] -type Int = u8; -#[cfg(u8_)] -type AtomicInt = AtomicU8; - -#[cfg(u16_)] -type Int = u16; -#[cfg(u16_)] -type AtomicInt = AtomicU16; - -#[cfg(u32_)] -type Int = u32; -#[cfg(u32_)] -type AtomicInt = AtomicU32; - -#[cfg(u64_)] -type Int = u64; -#[cfg(u64_)] -type AtomicInt = AtomicU64; - -#[cfg(usize_)] -type Int = usize; -#[cfg(usize_)] -type AtomicInt = AtomicUsize; - -#[cfg(i8_)] -type Int = i8; -#[cfg(i8_)] -type AtomicInt = AtomicI8; - -#[cfg(i16_)] -type Int = i16; -#[cfg(i16_)] -type AtomicInt = AtomicI16; - -#[cfg(i32_)] -type Int = i32; -#[cfg(i32_)] -type AtomicInt = AtomicI32; - -#[cfg(i64_)] -type Int = i64; -#[cfg(i64_)] -type AtomicInt = AtomicI64; - -#[cfg(isize_)] -type Int = isize; -#[cfg(isize_)] -type AtomicInt = AtomicIsize; - -use std::sync::atomic::*; - -const ORD: Ordering = Ordering::SeqCst; - -fn assert_eq(x: T, y: T) { - if x != y { - std::process::abort(); - } -} - -#[unsafe(no_mangle)] -fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - let x = AtomicInt::new(123); - assert_eq(123, x.fetch_max(0, ORD)); // `max` keeps existing value - assert_eq(123, x.fetch_max(Int::MAX, ORD)); // `max` stores the new value - assert_eq(Int::MAX, x.fetch_add(10, ORD)); // `fetch_add` should be wrapping - assert_eq(Int::MAX.wrapping_add(10), x.load(ORD)); - - x.store(42, ORD); - assert_eq(42, x.fetch_min(Int::MAX, ORD)); // `max` keeps existing value - assert_eq(42, x.fetch_min(Int::MIN, ORD)); // `max` stores the new value - assert_eq(Int::MIN, x.fetch_sub(10, ORD)); // `fetch_sub` should be wrapping - assert_eq(Int::MIN.wrapping_sub(10), x.load(ORD)); - - 0 -} diff --git a/tests/genmc/pass/atomics/rmw_edge_cases.u16_.stderr b/tests/genmc/pass/atomics/rmw_edge_cases.u16_.stderr deleted file mode 100644 index 3b22247ee4..0000000000 --- a/tests/genmc/pass/atomics/rmw_edge_cases.u16_.stderr +++ /dev/null @@ -1,2 +0,0 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 1 diff --git a/tests/genmc/pass/atomics/rmw_edge_cases.u32_.stderr b/tests/genmc/pass/atomics/rmw_edge_cases.u32_.stderr deleted file mode 100644 index 3b22247ee4..0000000000 --- a/tests/genmc/pass/atomics/rmw_edge_cases.u32_.stderr +++ /dev/null @@ -1,2 +0,0 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 1 diff --git a/tests/genmc/pass/atomics/rmw_edge_cases.u64_.stderr b/tests/genmc/pass/atomics/rmw_edge_cases.u64_.stderr deleted file mode 100644 index 3b22247ee4..0000000000 --- a/tests/genmc/pass/atomics/rmw_edge_cases.u64_.stderr +++ /dev/null @@ -1,2 +0,0 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 1 diff --git a/tests/genmc/pass/atomics/rmw_edge_cases.u8_.stderr b/tests/genmc/pass/atomics/rmw_edge_cases.u8_.stderr deleted file mode 100644 index 3b22247ee4..0000000000 --- a/tests/genmc/pass/atomics/rmw_edge_cases.u8_.stderr +++ /dev/null @@ -1,2 +0,0 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 1 diff --git a/tests/genmc/pass/atomics/rmw_edge_cases.usize_.stderr b/tests/genmc/pass/atomics/rmw_edge_cases.usize_.stderr deleted file mode 100644 index 3b22247ee4..0000000000 --- a/tests/genmc/pass/atomics/rmw_edge_cases.usize_.stderr +++ /dev/null @@ -1,2 +0,0 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 1 diff --git a/tests/genmc/pass/atomics/rmw_simple.rs b/tests/genmc/pass/atomics/rmw_simple.rs deleted file mode 100644 index c272549ea9..0000000000 --- a/tests/genmc/pass/atomics/rmw_simple.rs +++ /dev/null @@ -1,29 +0,0 @@ -//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows - -#![no_main] - -use std::sync::atomic::*; - -static VALUE: AtomicUsize = AtomicUsize::new(0); - -const ORD: Ordering = Ordering::SeqCst; - -fn assert_eq(x: usize, y: usize) { - if x != y { - std::process::abort(); - } -} - -#[unsafe(no_mangle)] -fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - VALUE.store(1, ORD); - - assert_eq(1, VALUE.fetch_add(7, ORD)); - assert_eq(8, VALUE.fetch_sub(2, ORD)); - assert_eq(6, VALUE.fetch_max(16, ORD)); - assert_eq(16, VALUE.fetch_min(4, ORD)); - assert_eq(4, VALUE.swap(42, ORD)); - - assert_eq(42, VALUE.load(ORD)); - 0 -} diff --git a/tests/genmc/pass/atomics/rmw_simple.stderr b/tests/genmc/pass/atomics/rmw_simple.stderr deleted file mode 100644 index 3b22247ee4..0000000000 --- a/tests/genmc/pass/atomics/rmw_simple.stderr +++ /dev/null @@ -1,2 +0,0 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 1 diff --git a/tests/genmc/pass/litmus/LB_incMPs.rs b/tests/genmc/pass/litmus/LB_incMPs.rs deleted file mode 100644 index 10022bd201..0000000000 --- a/tests/genmc/pass/litmus/LB_incMPs.rs +++ /dev/null @@ -1,48 +0,0 @@ -//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows - -#![no_main] - -#[path = "../../../utils/genmc.rs"] -mod genmc; - -use std::ffi::c_void; -use std::sync::atomic::{AtomicU64, Ordering}; - -static X: AtomicU64 = AtomicU64::new(0); -static Y: AtomicU64 = AtomicU64::new(0); -static W: AtomicU64 = AtomicU64::new(0); -static Z: AtomicU64 = AtomicU64::new(0); - -use crate::genmc::*; - -#[unsafe(no_mangle)] -fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - let thread_order = [thread_1, thread_2, thread_3, thread_4]; - let _ids = unsafe { create_pthreads_no_params(thread_order) }; - - 0 -} - -extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { - X.load(Ordering::Acquire); - Z.fetch_add(1, Ordering::AcqRel); - std::ptr::null_mut() -} - -extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { - Z.fetch_add(1, Ordering::AcqRel); - Y.store(1, Ordering::Release); - std::ptr::null_mut() -} - -extern "C" fn thread_3(_value: *mut c_void) -> *mut c_void { - Y.load(Ordering::Acquire); - W.fetch_add(1, Ordering::AcqRel); - std::ptr::null_mut() -} - -extern "C" fn thread_4(_value: *mut c_void) -> *mut c_void { - W.fetch_add(1, Ordering::AcqRel); - X.store(1, Ordering::Release); - std::ptr::null_mut() -} diff --git a/tests/genmc/pass/litmus/LB_incMPs.stderr b/tests/genmc/pass/litmus/LB_incMPs.stderr deleted file mode 100644 index c879e95a17..0000000000 --- a/tests/genmc/pass/litmus/LB_incMPs.stderr +++ /dev/null @@ -1,2 +0,0 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 15 diff --git a/tests/genmc/pass/litmus/MPU_rels_acq.rs b/tests/genmc/pass/litmus/MPU_rels_acq.rs deleted file mode 100644 index fa825b83ea..0000000000 --- a/tests/genmc/pass/litmus/MPU_rels_acq.rs +++ /dev/null @@ -1,43 +0,0 @@ -//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows - -#![no_main] - -#[path = "../../../utils/genmc.rs"] -mod genmc; - -use std::ffi::c_void; -use std::sync::atomic::{AtomicU64, Ordering}; - -// Note: the GenMC equivalent of this test (genmc/tests/correct/litmus/MPU+rels+acq/mpu+rels+acq.c) uses non-atomic accesses for `X` with disabled race detection. -static X: AtomicU64 = AtomicU64::new(0); -static Y: AtomicU64 = AtomicU64::new(0); - -use crate::genmc::*; - -#[unsafe(no_mangle)] -fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - let thread_order = [thread_1, thread_2, thread_3]; - let _ids = unsafe { create_pthreads_no_params(thread_order) }; - - 0 -} - -extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { - X.store(1, Ordering::Relaxed); - - Y.store(0, Ordering::Release); - Y.store(1, Ordering::Relaxed); - std::ptr::null_mut() -} - -extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { - Y.fetch_add(1, Ordering::Relaxed); - std::ptr::null_mut() -} - -extern "C" fn thread_3(_value: *mut c_void) -> *mut c_void { - if Y.load(Ordering::Acquire) > 1 { - X.store(2, Ordering::Relaxed); - } - std::ptr::null_mut() -} diff --git a/tests/genmc/pass/litmus/MPU_rels_acq.stderr b/tests/genmc/pass/litmus/MPU_rels_acq.stderr deleted file mode 100644 index c5e34b0064..0000000000 --- a/tests/genmc/pass/litmus/MPU_rels_acq.stderr +++ /dev/null @@ -1,2 +0,0 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 13 diff --git a/tests/genmc/pass/litmus/MP_incMPs.rs b/tests/genmc/pass/litmus/MP_incMPs.rs deleted file mode 100644 index 1fddb67eb3..0000000000 --- a/tests/genmc/pass/litmus/MP_incMPs.rs +++ /dev/null @@ -1,41 +0,0 @@ -//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows - -#![no_main] - -#[path = "../../../utils/genmc.rs"] -mod genmc; - -use std::ffi::c_void; -use std::sync::atomic::{AtomicU64, Ordering}; - -static X: AtomicU64 = AtomicU64::new(0); -static Y: AtomicU64 = AtomicU64::new(0); -static Z: AtomicU64 = AtomicU64::new(0); - -use crate::genmc::*; - -#[unsafe(no_mangle)] -fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - let thread_order = [thread_1, thread_2, thread_3]; - let _ids = unsafe { create_pthreads_no_params(thread_order) }; - - 0 -} - -extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { - Y.load(Ordering::Acquire); - Z.fetch_add(1, Ordering::AcqRel); - std::ptr::null_mut() -} - -extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { - Z.fetch_add(1, Ordering::AcqRel); - X.load(Ordering::Acquire); - std::ptr::null_mut() -} - -extern "C" fn thread_3(_value: *mut c_void) -> *mut c_void { - X.store(1, Ordering::Release); - Y.store(1, Ordering::Release); - std::ptr::null_mut() -} diff --git a/tests/genmc/pass/litmus/MP_incMPs.stderr b/tests/genmc/pass/litmus/MP_incMPs.stderr deleted file mode 100644 index 2be9a6c7fb..0000000000 --- a/tests/genmc/pass/litmus/MP_incMPs.stderr +++ /dev/null @@ -1,2 +0,0 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 7 diff --git a/tests/genmc/pass/litmus/Z6_U0.rs b/tests/genmc/pass/litmus/Z6_U0.rs deleted file mode 100644 index a7c9b93016..0000000000 --- a/tests/genmc/pass/litmus/Z6_U0.rs +++ /dev/null @@ -1,40 +0,0 @@ -//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows - -#![no_main] - -#[path = "../../../utils/genmc.rs"] -mod genmc; - -use std::ffi::c_void; -use std::sync::atomic::{AtomicU64, Ordering}; - -static X: AtomicU64 = AtomicU64::new(0); -static Y: AtomicU64 = AtomicU64::new(0); - -use crate::genmc::*; - -#[unsafe(no_mangle)] -fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - let thread_order = [thread_1, thread_2, thread_3]; - let _ids = unsafe { create_pthreads_no_params(thread_order) }; - - 0 -} - -extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { - X.store(1, Ordering::SeqCst); - Y.store(1, Ordering::Release); - std::ptr::null_mut() -} - -extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { - Y.fetch_add(1, Ordering::SeqCst); - Y.load(Ordering::Relaxed); - std::ptr::null_mut() -} - -extern "C" fn thread_3(_value: *mut c_void) -> *mut c_void { - Y.store(3, Ordering::SeqCst); - X.load(Ordering::SeqCst); - std::ptr::null_mut() -} diff --git a/tests/genmc/pass/litmus/Z6_U0.stderr b/tests/genmc/pass/litmus/Z6_U0.stderr deleted file mode 100644 index 15aa1e752a..0000000000 --- a/tests/genmc/pass/litmus/Z6_U0.stderr +++ /dev/null @@ -1,2 +0,0 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 22 diff --git a/tests/genmc/pass/litmus/casdep.rs b/tests/genmc/pass/litmus/casdep.rs deleted file mode 100644 index b5793f004d..0000000000 --- a/tests/genmc/pass/litmus/casdep.rs +++ /dev/null @@ -1,34 +0,0 @@ -//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows - -#![no_main] - -#[path = "../../../utils/genmc.rs"] -mod genmc; - -use std::ffi::c_void; -use std::sync::atomic::{AtomicU64, Ordering}; - -use crate::genmc::*; - -static X: AtomicU64 = AtomicU64::new(0); -static Y: AtomicU64 = AtomicU64::new(0); -static Z: AtomicU64 = AtomicU64::new(0); - -#[unsafe(no_mangle)] -fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - let _ids = unsafe { create_pthreads_no_params([thread_1, thread_2]) }; - - 0 -} - -extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { - let a = X.load(Ordering::Relaxed); - let _b = Y.compare_exchange(a, 1, Ordering::Relaxed, Ordering::Relaxed); - Z.store(a, Ordering::Relaxed); - std::ptr::null_mut() -} - -extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { - Y.store(2, Ordering::Relaxed); - std::ptr::null_mut() -} diff --git a/tests/genmc/pass/litmus/casdep.stderr b/tests/genmc/pass/litmus/casdep.stderr deleted file mode 100644 index 01701dfe69..0000000000 --- a/tests/genmc/pass/litmus/casdep.stderr +++ /dev/null @@ -1,2 +0,0 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 2 diff --git a/tests/genmc/pass/litmus/ccr.rs b/tests/genmc/pass/litmus/ccr.rs deleted file mode 100644 index 75bb654d88..0000000000 --- a/tests/genmc/pass/litmus/ccr.rs +++ /dev/null @@ -1,33 +0,0 @@ -//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows - -#![no_main] - -#[path = "../../../utils/genmc.rs"] -mod genmc; - -use std::ffi::c_void; -use std::sync::atomic::{AtomicU64, Ordering}; - -static X: AtomicU64 = AtomicU64::new(0); - -use crate::genmc::*; - -#[unsafe(no_mangle)] -fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - let _ids = unsafe { create_pthreads_no_params([thread_1, thread_2]) }; - - 0 -} - -extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { - let expected = 0; - let _ = X.compare_exchange(expected, 42, Ordering::Relaxed, Ordering::Relaxed); - std::ptr::null_mut() -} - -extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { - let expected = 0; - let _ = X.compare_exchange(expected, 17, Ordering::Relaxed, Ordering::Relaxed); - X.load(Ordering::Relaxed); - std::ptr::null_mut() -} diff --git a/tests/genmc/pass/litmus/ccr.stderr b/tests/genmc/pass/litmus/ccr.stderr deleted file mode 100644 index 01701dfe69..0000000000 --- a/tests/genmc/pass/litmus/ccr.stderr +++ /dev/null @@ -1,2 +0,0 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 2 diff --git a/tests/genmc/pass/litmus/cii.rs b/tests/genmc/pass/litmus/cii.rs deleted file mode 100644 index a34d809c1a..0000000000 --- a/tests/genmc/pass/litmus/cii.rs +++ /dev/null @@ -1,36 +0,0 @@ -//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows - -#![no_main] - -#[path = "../../../utils/genmc.rs"] -mod genmc; - -use std::ffi::c_void; -use std::sync::atomic::{AtomicU64, Ordering}; - -static X: AtomicU64 = AtomicU64::new(0); - -use crate::genmc::*; - -#[unsafe(no_mangle)] -fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - let _ids = unsafe { create_pthreads_no_params([thread_1, thread_2, thread_3]) }; - - 0 -} - -extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { - let expected = 1; - let _ = X.compare_exchange(expected, 2, Ordering::Relaxed, Ordering::Relaxed); - std::ptr::null_mut() -} - -extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { - X.fetch_add(1, Ordering::Relaxed); - std::ptr::null_mut() -} - -extern "C" fn thread_3(_value: *mut c_void) -> *mut c_void { - X.fetch_add(1, Ordering::Relaxed); - std::ptr::null_mut() -} diff --git a/tests/genmc/pass/litmus/cii.stderr b/tests/genmc/pass/litmus/cii.stderr deleted file mode 100644 index be75e68fde..0000000000 --- a/tests/genmc/pass/litmus/cii.stderr +++ /dev/null @@ -1,2 +0,0 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 6 diff --git a/tests/genmc/pass/litmus/inc2w.rs b/tests/genmc/pass/litmus/inc2w.rs deleted file mode 100644 index dbdbbffe1e..0000000000 --- a/tests/genmc/pass/litmus/inc2w.rs +++ /dev/null @@ -1,36 +0,0 @@ -//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows - -#![no_main] - -#[path = "../../../utils/genmc.rs"] -mod genmc; - -use std::ffi::c_void; -use std::sync::atomic::{AtomicU64, Ordering}; - -use crate::genmc::*; - -static X: AtomicU64 = AtomicU64::new(0); - -#[unsafe(no_mangle)] -fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - let thread_order = [thread_1, thread_2, thread_3]; - let _ids = unsafe { create_pthreads_no_params(thread_order) }; - - 0 -} - -pub extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { - X.fetch_add(1, Ordering::Relaxed); - std::ptr::null_mut() -} - -pub extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { - X.store(4, Ordering::Release); - std::ptr::null_mut() -} - -pub extern "C" fn thread_3(_value: *mut c_void) -> *mut c_void { - X.fetch_add(2, Ordering::Relaxed); - std::ptr::null_mut() -} diff --git a/tests/genmc/pass/litmus/inc2w.stderr b/tests/genmc/pass/litmus/inc2w.stderr deleted file mode 100644 index be75e68fde..0000000000 --- a/tests/genmc/pass/litmus/inc2w.stderr +++ /dev/null @@ -1,2 +0,0 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 6 diff --git a/tests/genmc/pass/litmus/inc_inc_RR_W_RR.rs b/tests/genmc/pass/litmus/inc_inc_RR_W_RR.rs deleted file mode 100644 index 00f00b5850..0000000000 --- a/tests/genmc/pass/litmus/inc_inc_RR_W_RR.rs +++ /dev/null @@ -1,62 +0,0 @@ -//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows - -#![no_main] - -#[path = "../../../utils/genmc.rs"] -mod genmc; - -use std::ffi::c_void; -use std::sync::atomic::{AtomicU64, Ordering}; - -use crate::genmc::*; - -static X: AtomicU64 = AtomicU64::new(0); - -static mut A: u64 = 0; -static mut B: u64 = 0; -static mut C: u64 = 0; -static mut D: u64 = 0; - -#[unsafe(no_mangle)] -fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - let thread_order = [thread_1, thread_2, thread_3, thread_4, thread_5]; - let ids = unsafe { create_pthreads_no_params(thread_order) }; - unsafe { join_pthreads(ids) }; - - if unsafe { A == 42 && B == 2 && C == 1 && D == 42 } { - std::process::abort(); - } - - 0 -} - -pub extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { - X.fetch_add(1, Ordering::Relaxed); - std::ptr::null_mut() -} - -pub extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { - X.fetch_add(1, Ordering::Relaxed); - std::ptr::null_mut() -} - -pub extern "C" fn thread_3(_value: *mut c_void) -> *mut c_void { - unsafe { - A = X.load(Ordering::Relaxed); - B = X.load(Ordering::Relaxed); - } - std::ptr::null_mut() -} - -pub extern "C" fn thread_4(_value: *mut c_void) -> *mut c_void { - X.store(42, Ordering::Relaxed); - std::ptr::null_mut() -} - -pub extern "C" fn thread_5(_value: *mut c_void) -> *mut c_void { - unsafe { - C = X.load(Ordering::Relaxed); - D = X.load(Ordering::Relaxed); - } - std::ptr::null_mut() -} diff --git a/tests/genmc/pass/litmus/inc_inc_RR_W_RR.stderr b/tests/genmc/pass/litmus/inc_inc_RR_W_RR.stderr deleted file mode 100644 index b5f8cd15b6..0000000000 --- a/tests/genmc/pass/litmus/inc_inc_RR_W_RR.stderr +++ /dev/null @@ -1,2 +0,0 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 600 diff --git a/tests/genmc/pass/litmus/riwi.rs b/tests/genmc/pass/litmus/riwi.rs deleted file mode 100644 index 9f158d6fea..0000000000 --- a/tests/genmc/pass/litmus/riwi.rs +++ /dev/null @@ -1,34 +0,0 @@ -//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows - -#![no_main] - -#[path = "../../../utils/genmc.rs"] -mod genmc; - -use std::ffi::c_void; -use std::sync::atomic::{AtomicU64, Ordering}; - -static X: AtomicU64 = AtomicU64::new(0); -static Y: AtomicU64 = AtomicU64::new(0); - -use crate::genmc::*; - -#[unsafe(no_mangle)] -fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - let thread_order = [thread_1, thread_2]; - let _ids = unsafe { create_pthreads_no_params(thread_order) }; - - 0 -} - -extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { - Y.load(Ordering::Relaxed); - X.fetch_add(1, Ordering::Relaxed); - std::ptr::null_mut() -} - -extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { - X.fetch_add(1, Ordering::Relaxed); - Y.store(1, Ordering::Release); - std::ptr::null_mut() -} diff --git a/tests/genmc/pass/litmus/riwi.stderr b/tests/genmc/pass/litmus/riwi.stderr deleted file mode 100644 index 115b1986ce..0000000000 --- a/tests/genmc/pass/litmus/riwi.stderr +++ /dev/null @@ -1,2 +0,0 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 3 diff --git a/tests/genmc/pass/litmus/viktor-relseq.rs b/tests/genmc/pass/litmus/viktor-relseq.rs deleted file mode 100644 index 5c10d39ba4..0000000000 --- a/tests/genmc/pass/litmus/viktor-relseq.rs +++ /dev/null @@ -1,43 +0,0 @@ -//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows - -#![no_main] - -#[path = "../../../utils/genmc.rs"] -mod genmc; - -use std::ffi::c_void; -use std::sync::atomic::{AtomicU64, Ordering}; - -static LOCK: AtomicU64 = AtomicU64::new(0); - -use crate::genmc::*; - -#[unsafe(no_mangle)] -fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - let thread_order = [thread_ra, thread_r, thread_rr, thread_rs]; - let _ids = unsafe { create_pthreads_no_params(thread_order) }; - - 0 -} - -extern "C" fn thread_ra(_value: *mut c_void) -> *mut c_void { - LOCK.fetch_add(1, Ordering::Acquire); - LOCK.fetch_add(1, Ordering::Relaxed); - std::ptr::null_mut() -} - -extern "C" fn thread_r(_value: *mut c_void) -> *mut c_void { - LOCK.fetch_add(1, Ordering::Relaxed); - LOCK.fetch_add(1, Ordering::Relaxed); - std::ptr::null_mut() -} - -extern "C" fn thread_rr(_value: *mut c_void) -> *mut c_void { - LOCK.fetch_add(1, Ordering::Release); - std::ptr::null_mut() -} - -extern "C" fn thread_rs(_value: *mut c_void) -> *mut c_void { - LOCK.fetch_add(1, Ordering::Relaxed); - std::ptr::null_mut() -} diff --git a/tests/genmc/pass/litmus/viktor-relseq.stderr b/tests/genmc/pass/litmus/viktor-relseq.stderr deleted file mode 100644 index d63ac5199d..0000000000 --- a/tests/genmc/pass/litmus/viktor-relseq.stderr +++ /dev/null @@ -1,2 +0,0 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 180 From 6495437f31a7cc700f4ceab1c64291de595464e0 Mon Sep 17 00:00:00 2001 From: Patrick-6 Date: Sun, 27 Jul 2025 19:29:54 +0200 Subject: [PATCH 37/41] C++ code cleanup --- genmc-sys/src_cpp/MiriInterface.hpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/genmc-sys/src_cpp/MiriInterface.hpp b/genmc-sys/src_cpp/MiriInterface.hpp index 4e28a341d3..d3ea4884fa 100644 --- a/genmc-sys/src_cpp/MiriInterface.hpp +++ b/genmc-sys/src_cpp/MiriInterface.hpp @@ -196,14 +196,6 @@ struct MiriGenMCShim : private GenMCDriver * in a given thread. */ std::vector threadsAction; - - /** - * Map of already used annotation ids (e.g., for mutexes). - * FIXME(GenMC): For multithreading support, this map and the counter below need to be - * synchronized across threads. - */ - std::unordered_map annotation_id{}; - ModuleID::ID annotation_id_counter = 0; }; /**** Functions available to Miri ****/ From d294612b310a54ed6a493ea74c184afba090556b Mon Sep 17 00:00:00 2001 From: Patrick-6 Date: Mon, 28 Jul 2025 10:55:30 +0200 Subject: [PATCH 38/41] Remove mixed atomic-non-atomic and 'read from initial value' support. --- genmc-sys/src_cpp/MiriInterface.hpp | 51 +++------------- .../src_cpp/MiriInterface/MiriInterface.cpp | 26 ++------- .../read_global_init.rs | 20 ------- .../read_global_init.stderr | 15 ----- .../wna_wrlx_rrlx.return1234.stderr | 15 ----- .../wna_wrlx_rrlx.return42.stderr | 15 ----- .../mixed-atomic-non-atomic/wna_wrlx_rrlx.rs | 58 ------------------- .../genmc/pass/atomics/stack_alloc_atomic.rs | 18 ------ .../pass/atomics/stack_alloc_atomic.stderr | 2 - 9 files changed, 12 insertions(+), 208 deletions(-) delete mode 100644 tests/genmc/fail/simple/mixed-atomic-non-atomic/read_global_init.rs delete mode 100644 tests/genmc/fail/simple/mixed-atomic-non-atomic/read_global_init.stderr delete mode 100644 tests/genmc/fail/simple/mixed-atomic-non-atomic/wna_wrlx_rrlx.return1234.stderr delete mode 100644 tests/genmc/fail/simple/mixed-atomic-non-atomic/wna_wrlx_rrlx.return42.stderr delete mode 100644 tests/genmc/fail/simple/mixed-atomic-non-atomic/wna_wrlx_rrlx.rs delete mode 100644 tests/genmc/pass/atomics/stack_alloc_atomic.rs delete mode 100644 tests/genmc/pass/atomics/stack_alloc_atomic.stderr diff --git a/genmc-sys/src_cpp/MiriInterface.hpp b/genmc-sys/src_cpp/MiriInterface.hpp index d3ea4884fa..7cf6902dbe 100644 --- a/genmc-sys/src_cpp/MiriInterface.hpp +++ b/genmc-sys/src_cpp/MiriInterface.hpp @@ -41,7 +41,8 @@ struct MiriGenMCShim : private GenMCDriver public: MiriGenMCShim(std::shared_ptr conf, Mode mode /* = VerificationMode{} */) : GenMCDriver(std::move(conf), nullptr, mode) - {} + { + } virtual ~MiriGenMCShim() {} @@ -56,8 +57,8 @@ struct MiriGenMCShim : private GenMCDriver [[nodiscard]] LoadResult handleLoad(ThreadId thread_id, uint64_t address, uint64_t size, MemOrdering ord, GenmcScalar old_val); [[nodiscard]] StoreResult handleStore(ThreadId thread_id, uint64_t address, uint64_t size, - GenmcScalar value, GenmcScalar old_val, - MemOrdering ord); + GenmcScalar value, GenmcScalar old_val, + MemOrdering ord); /**** Memory (de)allocation ****/ @@ -131,61 +132,25 @@ struct MiriGenMCShim : private GenMCDriver static std::unique_ptr createHandle(const GenmcParams &config); private: - /** - * @brief Try to insert the initial value of a memory location. - * @param addr - * @param value - * */ - void handleOldVal(const SAddr addr, GenmcScalar value) - { - // TODO GENMC(CLEANUP): Pass this as a parameter: - auto &g = getExec().getGraph(); - auto *coLab = g.co_max(addr); - if (auto *wLab = llvm::dyn_cast(coLab)) - { - if (value.is_init && wLab->isNotAtomic()) - wLab->setVal(value.toSVal()); - } - else if (const auto *wLab = llvm::dyn_cast(coLab)) - { - if (value.is_init) - { - auto result = initVals_.insert(std::make_pair(addr, value)); - BUG_ON(result.second && - (*result.first).second != - value); /* Attempt to replace initial value */ - } - } - else - { - BUG(); /* Invalid label */ - } - // either initLabel ==> update initValGetter - // or WriteLabel ==> Update its value in place (only if non-atomic) - } - /** * Helper function for loads that need to reset the event counter when no value is returned. * Same syntax as `GenMCDriver::handleLoad`, but this takes a thread id instead of an Event. * Automatically calls `incPos` and `decPos` where needed for the given thread. */ template - HandleResult handleLoadResetIfNone(std::function oldValSetter, - ThreadId tid, Ts &&...params) + HandleResult handleLoadResetIfNone(ThreadId tid, Ts &&...params) { const auto pos = incPos(tid); const auto ret = - GenMCDriver::handleLoad(oldValSetter, pos, std::forward(params)...); + GenMCDriver::handleLoad(pos, std::forward(params)...); // If we didn't get a value, we reset the index of the current thread. - if (!std::holds_alternative(ret)) { + if (!std::holds_alternative(ret)) + { decPos(tid); } return ret; } - // TODO GENMC(mixed-size accesses): - std::unordered_map initVals_{}; - /** * Currently, the interpreter is responsible for maintaining `ExecutionGraph` event indices. * The interpreter is also responsible for informing GenMC about the `ActionKind` of the diff --git a/genmc-sys/src_cpp/MiriInterface/MiriInterface.cpp b/genmc-sys/src_cpp/MiriInterface/MiriInterface.cpp index d43972bbac..2b4a294b04 100644 --- a/genmc-sys/src_cpp/MiriInterface/MiriInterface.cpp +++ b/genmc-sys/src_cpp/MiriInterface/MiriInterface.cpp @@ -119,18 +119,8 @@ auto MiriGenMCShim::createHandle(const GenmcParams &config) auto *driverPtr = driver.get(); auto initValGetter = [driverPtr](const AAccess &access) { - const auto addr = access.getAddr(); - if (!driverPtr->initVals_.contains(addr)) - { - return SVal(0xCC00CC00); - // BUG_ON(!driverPtr->initVals_.contains(addr)); - } - auto result = driverPtr->initVals_[addr]; - if (!result.is_init) - { - return SVal(0xFF00FF00); - } - return result.toSVal(); + // FIXME(genmc): Add proper support for initial values. + return SVal(0xff); }; driver->getExec().getGraph().setInitValGetter(initValGetter); @@ -220,11 +210,8 @@ void MiriGenMCShim::handleThreadKill(ThreadId thread_id) // `type` is only used for printing. const auto type = AType::Unsigned; - const auto oldValSetter = [this, old_val](SAddr addr) { - this->handleOldVal(addr, old_val); - }; const auto ret = handleLoadResetIfNone( - oldValSetter, thread_id, ord, addr, aSize, type); + thread_id, ord, addr, aSize, type); RETURN_IF_ERROR(ret, LoadResult); const auto *retVal = std::get_if(&ret); @@ -247,13 +234,8 @@ void MiriGenMCShim::handleThreadKill(ThreadId thread_id) auto val = value.toSVal(); - auto oldValSetter = [this, old_val](SAddr addr) { - this->handleOldVal(addr, - old_val); // TODO GENMC(HACK): is this the correct way to do it? - }; - const auto ret = GenMCDriver::handleStore( - oldValSetter, pos, ord, addr, aSize, type, val, EventDeps()); + pos, ord, addr, aSize, type, val, EventDeps()); RETURN_IF_ERROR(ret, StoreResult); diff --git a/tests/genmc/fail/simple/mixed-atomic-non-atomic/read_global_init.rs b/tests/genmc/fail/simple/mixed-atomic-non-atomic/read_global_init.rs deleted file mode 100644 index bac8293469..0000000000 --- a/tests/genmc/fail/simple/mixed-atomic-non-atomic/read_global_init.rs +++ /dev/null @@ -1,20 +0,0 @@ -//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows - -#![no_main] - -use std::sync::atomic::{AtomicU64, Ordering}; - -static X: AtomicU64 = AtomicU64::new(1234); - -#[unsafe(no_mangle)] -fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - // TODO GENMC: make this a "pass" test - if 1234 != unsafe { *X.as_ptr() } { - unsafe { std::hint::unreachable_unchecked() }; - } - if 1234 == X.load(Ordering::SeqCst) { - unsafe { std::hint::unreachable_unchecked() }; //~ ERROR: entering unreachable code - } - - 0 -} diff --git a/tests/genmc/fail/simple/mixed-atomic-non-atomic/read_global_init.stderr b/tests/genmc/fail/simple/mixed-atomic-non-atomic/read_global_init.stderr deleted file mode 100644 index ec779aa7c4..0000000000 --- a/tests/genmc/fail/simple/mixed-atomic-non-atomic/read_global_init.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error: Undefined Behavior: entering unreachable code - --> tests/genmc/fail/simple/mixed-atomic-non-atomic/read_global_init.rs:LL:CC - | -LL | unsafe { std::hint::unreachable_unchecked() }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here - | - = 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 - = note: BACKTRACE: - = note: inside `miri_start` at tests/genmc/fail/simple/mixed-atomic-non-atomic/read_global_init.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 - diff --git a/tests/genmc/fail/simple/mixed-atomic-non-atomic/wna_wrlx_rrlx.return1234.stderr b/tests/genmc/fail/simple/mixed-atomic-non-atomic/wna_wrlx_rrlx.return1234.stderr deleted file mode 100644 index 28a8b74c86..0000000000 --- a/tests/genmc/fail/simple/mixed-atomic-non-atomic/wna_wrlx_rrlx.return1234.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error: Undefined Behavior: entering unreachable code - --> tests/genmc/fail/simple/mixed-atomic-non-atomic/wna_wrlx_rrlx.rs:LL:CC - | -LL | std::hint::unreachable_unchecked(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here - | - = 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 - = note: BACKTRACE on thread `unnamed-ID`: - = note: inside `read_relaxed` at tests/genmc/fail/simple/mixed-atomic-non-atomic/wna_wrlx_rrlx.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 - diff --git a/tests/genmc/fail/simple/mixed-atomic-non-atomic/wna_wrlx_rrlx.return42.stderr b/tests/genmc/fail/simple/mixed-atomic-non-atomic/wna_wrlx_rrlx.return42.stderr deleted file mode 100644 index 28a8b74c86..0000000000 --- a/tests/genmc/fail/simple/mixed-atomic-non-atomic/wna_wrlx_rrlx.return42.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error: Undefined Behavior: entering unreachable code - --> tests/genmc/fail/simple/mixed-atomic-non-atomic/wna_wrlx_rrlx.rs:LL:CC - | -LL | std::hint::unreachable_unchecked(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here - | - = 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 - = note: BACKTRACE on thread `unnamed-ID`: - = note: inside `read_relaxed` at tests/genmc/fail/simple/mixed-atomic-non-atomic/wna_wrlx_rrlx.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 - diff --git a/tests/genmc/fail/simple/mixed-atomic-non-atomic/wna_wrlx_rrlx.rs b/tests/genmc/fail/simple/mixed-atomic-non-atomic/wna_wrlx_rrlx.rs deleted file mode 100644 index 2b4f803c6a..0000000000 --- a/tests/genmc/fail/simple/mixed-atomic-non-atomic/wna_wrlx_rrlx.rs +++ /dev/null @@ -1,58 +0,0 @@ -//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows -//@revisions: return1234 return42 - -#![no_main] - -use std::ffi::c_void; -use std::sync::atomic::{AtomicU64, Ordering}; - -use libc::{self, pthread_attr_t, pthread_t}; - -#[unsafe(no_mangle)] -fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - let mut t0: pthread_t = 0; - let mut t1: pthread_t = 0; - - let attr: *const pthread_attr_t = std::ptr::null(); - - let mut x: AtomicU64 = AtomicU64::new(1); - *x.get_mut() = 42; - // x.store(42, STORE_ORD); - - let value: *mut c_void = x.as_ptr() as *mut c_void; - - assert!(0 == unsafe { libc::pthread_create(&raw mut t0, attr, read_relaxed, value) }); - assert!(0 == unsafe { libc::pthread_create(&raw mut t1, attr, write_relaxed, value) }); - - assert!(0 == unsafe { libc::pthread_join(t0, std::ptr::null_mut()) }); - assert!(0 == unsafe { libc::pthread_join(t1, std::ptr::null_mut()) }); - - 0 -} - -extern "C" fn read_relaxed(value: *mut c_void) -> *mut c_void { - unsafe { - let x = (value as *const AtomicU64).as_ref().unwrap(); - let val = x.load(Ordering::Relaxed); - - let mut flag = false; - if cfg!(return1234) && 1234 == val { - flag = true; - } - if cfg!(return42) && 42 == val { - flag = true; - } - if flag { - std::hint::unreachable_unchecked(); //~ ERROR: entering unreachable code - } - std::ptr::null_mut() - } -} - -extern "C" fn write_relaxed(value: *mut c_void) -> *mut c_void { - unsafe { - let x = (value as *const AtomicU64).as_ref().unwrap(); - x.store(1234, Ordering::Relaxed); - std::ptr::null_mut() - } -} diff --git a/tests/genmc/pass/atomics/stack_alloc_atomic.rs b/tests/genmc/pass/atomics/stack_alloc_atomic.rs deleted file mode 100644 index d4c9968f43..0000000000 --- a/tests/genmc/pass/atomics/stack_alloc_atomic.rs +++ /dev/null @@ -1,18 +0,0 @@ -//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows - -#![no_main] - -use std::sync::atomic::*; - -const ORD: Ordering = Ordering::SeqCst; - -#[unsafe(no_mangle)] -fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - let x = AtomicU64::new(1234); - let a = x.load(ORD); - if a != 1234 { - std::process::abort(); - } - - 0 -} diff --git a/tests/genmc/pass/atomics/stack_alloc_atomic.stderr b/tests/genmc/pass/atomics/stack_alloc_atomic.stderr deleted file mode 100644 index 3b22247ee4..0000000000 --- a/tests/genmc/pass/atomics/stack_alloc_atomic.stderr +++ /dev/null @@ -1,2 +0,0 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 1 From d1b7febe2027c006a9d05f2b8a7b4ca452736cc9 Mon Sep 17 00:00:00 2001 From: Patrick-6 Date: Tue, 29 Jul 2025 13:54:40 +0200 Subject: [PATCH 39/41] Disable genmc feature by default. --- Cargo.toml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index de3d467c1b..38ceaf2688 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -72,8 +72,7 @@ name = "ui" harness = false [features] -# TODO GENMC (DEBUGGING): `genmc` feature should be turned off in upstream repo for now -default = ["stack-cache", "native-lib", "genmc"] +default = ["stack-cache", "native-lib"] genmc = ["dep:genmc-sys"] # this enables a GPL dependency! stack-cache = [] stack-cache-consistency-check = ["stack-cache"] From 5e071c938f4f9c7f6afca73a7291298b499c1ec9 Mon Sep 17 00:00:00 2001 From: Patrick-6 Date: Tue, 29 Jul 2025 14:04:37 +0200 Subject: [PATCH 40/41] Cleanup and formatting. --- Cargo.toml | 3 --- genmc-sys/.gitignore | 1 - genmc-sys/build.rs | 9 +++----- genmc-sys/src/lib.rs | 4 +++- genmc-sys/src_cpp/MiriInterface.hpp | 4 +--- .../src_cpp/MiriInterface/MiriInterface.cpp | 3 ++- miri-script/src/commands.rs | 18 +++++---------- miri-script/src/main.rs | 3 --- src/bin/miri.rs | 15 +++++-------- src/concurrency/genmc/config.rs | 4 ---- src/concurrency/genmc/miri_genmc.rs | 2 +- src/concurrency/genmc/mod.rs | 16 ++++++++------ src/concurrency/thread.rs | 1 + tests/ui.rs | 22 +++++-------------- 14 files changed, 36 insertions(+), 69 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 38ceaf2688..91dadf78a2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,9 +17,6 @@ name = "miri" test = false # we have no unit tests doctest = false # and no doc tests -[profile.bench] -debug = true - [dependencies] getrandom = { version = "0.3", features = ["std"] } rand = "0.9" diff --git a/genmc-sys/.gitignore b/genmc-sys/.gitignore index 1ba3f669fe..276a053cd0 100644 --- a/genmc-sys/.gitignore +++ b/genmc-sys/.gitignore @@ -1,2 +1 @@ genmc-src*/ -genmc*/ diff --git a/genmc-sys/build.rs b/genmc-sys/build.rs index b26115a96c..2262c1501f 100644 --- a/genmc-sys/build.rs +++ b/genmc-sys/build.rs @@ -26,9 +26,9 @@ mod downloading { use super::GENMC_DOWNLOAD_PATH; /// The GenMC repository the we get our commit from. - pub(crate) const GENMC_GITHUB_URL: &str = "https://github.com/MPI-SWS/genmc.git"; + pub(crate) const GENMC_GITHUB_URL: &str = "https://github.com/Patrick-6/genmc.git"; /// The GenMC commit we depend on. It must be available on the specified GenMC repository. - pub(crate) const GENMC_COMMIT: &str = "3438dd2c1202cd4a47ed7881d099abf23e4167ab"; + pub(crate) const GENMC_COMMIT: &str = "56997d85fc9c07f75431570d49ce4892b22b8e6e"; pub(crate) fn download_genmc() -> PathBuf { let Ok(genmc_download_path) = PathBuf::from_str(GENMC_DOWNLOAD_PATH); @@ -210,10 +210,7 @@ fn compile_cpp_dependencies(genmc_path: &Path) { // These definitions are parsed into a cmake list and then printed to the config.h file, so they are ';' separated. let definitions = llvm_definitions.split(";"); - let cpp_files = [ - "./src_cpp/MiriInterface/MiriInterface.cpp", - "./src_cpp/ResultHandling.cpp", - ]; + let cpp_files = ["./src_cpp/MiriInterface/MiriInterface.cpp", "./src_cpp/ResultHandling.cpp"]; let mut bridge = cxx_build::bridge("src/lib.rs"); // FIXME(genmc,cmake): Remove once the GenMC debug setting is available in the config.h file. diff --git a/genmc-sys/src/lib.rs b/genmc-sys/src/lib.rs index 214a08fffb..c8138c557b 100644 --- a/genmc-sys/src/lib.rs +++ b/genmc-sys/src/lib.rs @@ -69,10 +69,10 @@ mod ffi { /// (The fields of this struct are visible to both Rust and C++) #[derive(Clone, Debug)] struct GenmcParams { - // pub genmc_seed: u64; // OR: Option pub print_random_schedule_seed: bool, pub log_level: LogLevel, pub do_symmetry_reduction: bool, + // FIXME(GenMC): Add remaining parameters. } /// This is mostly equivalent to GenMC `VerbosityLevel`, but the debug log levels are always present (not conditionally compiled based on `ENABLE_GENMC_DEBUG`). @@ -128,6 +128,8 @@ mod ffi { /**** \/ Result & Error types \/ ****/ + // FIXME(genmc): Rework error handling (likely requires changes on the GenMC side). + #[must_use] #[derive(Debug)] struct LoadResult { diff --git a/genmc-sys/src_cpp/MiriInterface.hpp b/genmc-sys/src_cpp/MiriInterface.hpp index 7cf6902dbe..dfd74b3fa8 100644 --- a/genmc-sys/src_cpp/MiriInterface.hpp +++ b/genmc-sys/src_cpp/MiriInterface.hpp @@ -53,7 +53,6 @@ struct MiriGenMCShim : private GenMCDriver /**** Memory access handling ****/ - /////////////////// [[nodiscard]] LoadResult handleLoad(ThreadId thread_id, uint64_t address, uint64_t size, MemOrdering ord, GenmcScalar old_val); [[nodiscard]] StoreResult handleStore(ThreadId thread_id, uint64_t address, uint64_t size, @@ -165,8 +164,7 @@ struct MiriGenMCShim : private GenMCDriver /**** Functions available to Miri ****/ -// NOTE: CXX doesn't seem to support exposing static methods to Rust, so we expose this -// function instead +// NOTE: CXX doesn't support exposing static methods to Rust currently, so we expose this function instead. std::unique_ptr createGenmcHandle(const GenmcParams &config); constexpr auto getGlobalAllocStaticMask() -> uint64_t { return SAddr::staticMask; } diff --git a/genmc-sys/src_cpp/MiriInterface/MiriInterface.cpp b/genmc-sys/src_cpp/MiriInterface/MiriInterface.cpp index 2b4a294b04..6f3878a372 100644 --- a/genmc-sys/src_cpp/MiriInterface/MiriInterface.cpp +++ b/genmc-sys/src_cpp/MiriInterface/MiriInterface.cpp @@ -89,12 +89,13 @@ auto MiriGenMCShim::createHandle(const GenmcParams &config) // FIXME(genmc): implement symmetry reduction. ERROR_ON(config.do_symmetry_reduction, - "Symmetry reduction is currently unsupported in GenMC mode."); + "Symmetry reduction is currently unsupported in GenMC mode."); conf->symmetryReduction = config.do_symmetry_reduction; // FIXME(genmc): expose this setting to Miri (useful for testing Miri-GenMC). conf->schedulePolicy = SchedulePolicy::WF; + // FIXME(genmc): implement estimation mode. conf->estimate = false; const auto mode = GenMCDriver::Mode(GenMCDriver::VerificationMode{}); diff --git a/miri-script/src/commands.rs b/miri-script/src/commands.rs index c1aad7cadf..ee09b9b4b7 100644 --- a/miri-script/src/commands.rs +++ b/miri-script/src/commands.rs @@ -112,8 +112,8 @@ impl Command { Command::Check { features, flags } => Self::check(features, flags), Command::Test { bless, target, coverage, features, flags } => Self::test(bless, target, coverage, features, flags), - Command::Run { dep, verbose, target, edition, features, flags, flamegraph } => - Self::run(dep, verbose, target, edition, features, flags, flamegraph), + Command::Run { dep, verbose, target, edition, features, flags } => + Self::run(dep, verbose, target, edition, features, flags), Command::Doc { features, flags } => Self::doc(features, flags), Command::Fmt { flags } => Self::fmt(flags), Command::Clippy { features, flags } => Self::clippy(features, flags), @@ -463,7 +463,6 @@ impl Command { edition: Option, features: Vec, flags: Vec, - flamegraph: bool, ) -> Result<()> { let mut e = MiriEnv::new()?; @@ -499,18 +498,11 @@ impl Command { // The basic command that executes the Miri driver. let mut cmd = if dep { // We invoke the test suite as that has all the logic for running with dependencies. - let cmd = e - .cargo_cmd(".", "test", &features) + e.cargo_cmd(".", "test", &features) .args(&["--test", "ui"]) .args(quiet_flag) - .arg("--"); - if flamegraph { - cmd.args(&["--miri-run-dep-mode-flamegraph"]) - } else { - cmd.args(&["--miri-run-dep-mode"]) - } - } else if flamegraph { - cmd!(e.sh, "flamegraph -- {miri_bin}") + .arg("--") + .args(&["--miri-run-dep-mode"]) } else { cmd!(e.sh, "{miri_bin}") }; diff --git a/miri-script/src/main.rs b/miri-script/src/main.rs index f0d850df35..761ec5979f 100644 --- a/miri-script/src/main.rs +++ b/miri-script/src/main.rs @@ -96,9 +96,6 @@ pub enum Command { /// The flags set in `MIRIFLAGS` are added in front of these flags. #[arg(trailing_var_arg = true, allow_hyphen_values = true)] flags: Vec, - /// Run with flamegraph (requires cargo-flamegraph to be installed) - #[arg(long)] - flamegraph: bool, }, /// Build documentation. Doc { diff --git a/src/bin/miri.rs b/src/bin/miri.rs index d19d3db670..aeb4706e20 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -194,15 +194,12 @@ impl rustc_driver::Callbacks for MiriCompilerCalls { // FIXME(genmc): add estimation mode here. - let return_code = miri_genmc::run_genmc_mode( - &config, - eval_entry_once, - target_usize_max, - ) - .unwrap_or_else(|| { - tcx.dcx().abort_if_errors(); - rustc_driver::EXIT_FAILURE - }); + let return_code = + miri_genmc::run_genmc_mode(&config, eval_entry_once, target_usize_max) + .unwrap_or_else(|| { + tcx.dcx().abort_if_errors(); + rustc_driver::EXIT_FAILURE + }); exit(return_code); }; diff --git a/src/concurrency/genmc/config.rs b/src/concurrency/genmc/config.rs index df8843ec86..49869644f4 100644 --- a/src/concurrency/genmc/config.rs +++ b/src/concurrency/genmc/config.rs @@ -33,11 +33,7 @@ impl GenmcConfig { }; if let Some(log_level) = trimmed_arg.strip_prefix("log=") { genmc_config.params.log_level = log_level.parse()?; - } else if trimmed_arg == "symmetry-reduction" { - // TODO GENMC (PERFORMANCE): maybe make this the default, have an option to turn it off instead - genmc_config.params.do_symmetry_reduction = true; } else { - // TODO GENMC: how to properly handle this? return Err(format!("Invalid GenMC argument: \"-Zmiri-genmc-{trimmed_arg}\"")); } Ok(()) diff --git a/src/concurrency/genmc/miri_genmc.rs b/src/concurrency/genmc/miri_genmc.rs index dfd2e5702a..c270be7132 100644 --- a/src/concurrency/genmc/miri_genmc.rs +++ b/src/concurrency/genmc/miri_genmc.rs @@ -48,7 +48,7 @@ pub fn run_genmc_mode( eprintln!("Number of blocked executions seen: {blocked_execution_count}"); } - // TODO GENMC: what is an appropriate return code? (since there are possibly many) + // Return the return code of the last execution. return Some(return_code); } tracing::error!("GenMC mode did not finish in 2^64 iterations!"); diff --git a/src/concurrency/genmc/mod.rs b/src/concurrency/genmc/mod.rs index d3c305ec94..c0fce827d6 100644 --- a/src/concurrency/genmc/mod.rs +++ b/src/concurrency/genmc/mod.rs @@ -2,8 +2,8 @@ use std::cell::{Cell, RefCell}; use std::sync::Arc; use genmc_sys::{ - ActionKind, GENMC_GLOBAL_ADDRESSES_MASK, GenmcScalar, MemOrdering, MiriGenMCShim, - UniquePtr, createGenmcHandle, + ActionKind, GENMC_GLOBAL_ADDRESSES_MASK, GenmcScalar, MemOrdering, MiriGenMCShim, UniquePtr, + createGenmcHandle, }; use rustc_abi::{Align, Size}; use rustc_const_eval::interpret::{AllocId, InterpCx, InterpResult, interp_ok}; @@ -43,7 +43,7 @@ struct ExitStatus { pub struct GenmcCtx { handle: RefCell>, - // TODO GENMC (PERFORMANCE): could use one RefCell for all internals instead of multiple + // FIXME(genmc,performance): we could use one RefCell for all internals instead of multiple thread_id_manager: RefCell, /// Some actions Miri does are allowed to cause data races. @@ -54,6 +54,8 @@ pub struct GenmcCtx { /// The `AllocId` for globals is stable across executions, so we can use it as an identifier. global_allocations: Arc, + /// The exit status of the program. + /// `None` if no thread has called `exit` and the main thread isn't finished yet. exit_status: Cell>, } @@ -377,7 +379,6 @@ impl GenmcCtx { } if size.bytes() <= 8 { - // TODO GENMC(mixed atomic-non-atomics): anything to do here? let _is_co_max_write = self.atomic_store_impl( machine, address, @@ -476,7 +477,6 @@ impl GenmcCtx { let pinned_mc = mc.as_mut().unwrap(); pinned_mc.handleFree(genmc_tid, genmc_address); - // TODO GENMC (ERROR HANDLING): can this ever fail? interp_ok(()) } @@ -617,7 +617,8 @@ impl GenmcCtx { ); if let Some(error) = load_result.error.as_ref() { - throw_ub_format!("{}", error.to_string_lossy()); // TODO GENMC: proper error handling: find correct error here + // FIXME(genmc): error handling + throw_ub_format!("{}", error.to_string_lossy()); } if !load_result.has_value { @@ -670,7 +671,8 @@ impl GenmcCtx { ); if let Some(error) = store_result.error.as_ref() { - throw_ub_format!("{}", error.to_string_lossy()); // TODO GENMC: proper error handling: find correct error here + // FIXME(genmc): error handling + throw_ub_format!("{}", error.to_string_lossy()); } interp_ok(store_result.isCoMaxWrite) diff --git a/src/concurrency/thread.rs b/src/concurrency/thread.rs index bca57ab848..b47166758f 100644 --- a/src/concurrency/thread.rs +++ b/src/concurrency/thread.rs @@ -212,6 +212,7 @@ impl<'tcx> Thread<'tcx> { } // FIXME(genmc,question): is this ok to be public? (it exposes implementation details) + #[allow(unused)] // Note: only used if `genmc` feature is enabled. pub(crate) fn get_state(&self) -> &ThreadState<'tcx> { &self.state } diff --git a/tests/ui.rs b/tests/ui.rs index a790557938..73fbe2cc02 100644 --- a/tests/ui.rs +++ b/tests/ui.rs @@ -322,12 +322,10 @@ fn main() -> Result<()> { let mut args = std::env::args_os(); // Skip the program name and check whether this is a `./miri run-dep` invocation - if let Some(first) = args.nth(1) { - if first == "--miri-run-dep-mode" { - return run_dep_mode(target, args, /* flamegraph */ false); - } else if first == "--miri-run-dep-mode-flamegraph" { - return run_dep_mode(target, args, /* flamegraph */ true); - } + if let Some(first) = args.nth(1) + && first == "--miri-run-dep-mode" + { + return run_dep_mode(target, args); } ui(Mode::Pass, "tests/pass", &target, WithoutDependencies, tmpdir.path())?; @@ -357,11 +355,7 @@ fn main() -> Result<()> { Ok(()) } -fn run_dep_mode( - target: String, - args: impl Iterator, - flamegraph: bool, -) -> Result<()> { +fn run_dep_mode(target: String, args: impl Iterator) -> Result<()> { let mut config = miri_config(&target, "", Mode::RunDep, Some(WithDependencies { bless: false })); config.comment_defaults.base().custom.remove("edition"); // `./miri` adds an `--edition` in `args`, so don't set it twice @@ -376,11 +370,5 @@ fn run_dep_mode( // Build dependencies test_config.apply_custom(&mut cmd, &build_manager).unwrap(); - if flamegraph { - let mut flamegraph_cmd = Command::new("flamegraph"); - flamegraph_cmd.arg("--").arg(cmd.get_program()).args(cmd.get_args()); - cmd = flamegraph_cmd; - } - if cmd.spawn()?.wait()?.success() { Ok(()) } else { std::process::exit(1) } } From 11ef501108dec352eafb76aaa3714336f8297d24 Mon Sep 17 00:00:00 2001 From: Patrick-6 Date: Tue, 5 Aug 2025 10:29:40 +0200 Subject: [PATCH 41/41] Update GenMC commit. --- genmc-sys/build.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/genmc-sys/build.rs b/genmc-sys/build.rs index 2262c1501f..7ad529c7fa 100644 --- a/genmc-sys/build.rs +++ b/genmc-sys/build.rs @@ -28,7 +28,7 @@ mod downloading { /// The GenMC repository the we get our commit from. pub(crate) const GENMC_GITHUB_URL: &str = "https://github.com/Patrick-6/genmc.git"; /// The GenMC commit we depend on. It must be available on the specified GenMC repository. - pub(crate) const GENMC_COMMIT: &str = "56997d85fc9c07f75431570d49ce4892b22b8e6e"; + pub(crate) const GENMC_COMMIT: &str = "a46974cdc73f8bafe63b006b4709cdeb76e11138"; pub(crate) fn download_genmc() -> PathBuf { let Ok(genmc_download_path) = PathBuf::from_str(GENMC_DOWNLOAD_PATH);