|
1 | 1 | # **(WIP)** Documentation for Miri-GenMC
|
2 | 2 |
|
| 3 | +**NOTE: GenMC mode is not yet fully implemented, and has [several correctness issues](https://github.com/rust-lang/miri/issues/4572). Using GenMC mode currently requires manually compiling Miri, see [Usage](#usage).** |
| 4 | + |
| 5 | + |
3 | 6 | [GenMC](https://github.com/MPI-SWS/genmc) is a stateless model checker for exploring concurrent executions of a program.
|
4 | 7 | Miri-GenMC integrates that model checker into Miri.
|
5 | 8 |
|
6 |
| -**NOTE: Currently, no actual GenMC functionality is part of Miri, this is still WIP.** |
| 9 | +Miri in GenMC mode takes a program as input like regular Miri, but instead of running it once, the program is executed repeatedly, until all possible executions allowed by the Rust memory model are explored. |
| 10 | +This includes all possible thread interleavings and all allowed return values for atomic operations, including cases that are very rare to encounter on actual hardware. |
| 11 | +(However, this does not include other sources of non-determinism, such as the absolute addresses of allocations. |
| 12 | +It is hence still possible to have latent bugs in a test case even if they passed GenMC.) |
7 | 13 |
|
8 |
| -<!-- FIXME(genmc): add explanation. --> |
| 14 | +GenMC requires the input program to be bounded, i.e., have finitely many possible executions, otherwise it will not terminate. |
| 15 | +Any loops that may run infinitely must be replaced or bounded (see below). |
| 16 | + |
| 17 | +GenMC makes use of Dynamic Partial Order Reduction (DPOR) to reduce the number of executions that must be explored, but the runtime can still be super-exponential in the size of the input program (number of threads and amount of interaction between threads). |
| 18 | +Large programs may not be verifiable in a reasonable amount of time. |
9 | 19 |
|
10 | 20 | ## Usage
|
11 | 21 |
|
12 | 22 | For testing/developing Miri-GenMC:
|
| 23 | +- install all [dependencies required by GenMC](https://github.com/MPI-SWS/genmc?tab=readme-ov-file#dependencies) |
13 | 24 | - clone the Miri repo.
|
14 | 25 | - build Miri-GenMC with `./miri build --features=genmc`.
|
15 | 26 | - OR: install Miri-GenMC in the current system with `./miri install --features=genmc`
|
@@ -50,6 +61,68 @@ Note that `cargo miri test` in GenMC mode is currently not supported.
|
50 | 61 |
|
51 | 62 | <!-- FIXME(genmc): add tips for using Miri-GenMC more efficiently. -->
|
52 | 63 |
|
| 64 | +### Eliminating unbounded loops |
| 65 | + |
| 66 | +#### Limiting the number of explored executions |
| 67 | + |
| 68 | +The number of explored executions in a concurrent program can increase super-exponentially in the size of the program. |
| 69 | +Reducing the number of explored executions is the most impactful way to improve verification times. |
| 70 | +Some programs also contain possibly infinite loops, which are not supported by GenMC. |
| 71 | + |
| 72 | +One way to drastically improve verification performance is by bounding spinloops. |
| 73 | +A spinloop may loop a many times before it can finally make progress. |
| 74 | +If such a loop doesn't have any visible side effects, meaning it does not matter to the outcome of the program whether the loop ran once or a million time, then the loop can be limited to one iteration. |
| 75 | + |
| 76 | +The following code gives an example for how to replace a loop that waits for a boolean to be true. |
| 77 | +Since there are no side effects, replacing the loop with one iteration is safe. |
| 78 | + |
| 79 | +```rust |
| 80 | +#[cfg(miri)] |
| 81 | +unsafe extern "Rust" { |
| 82 | + // This is a special function that Miri provides. |
| 83 | + // It blocks the thread calling this function if the condition is false. |
| 84 | + pub unsafe fn miri_genmc_assume(condition: bool); |
| 85 | +} |
| 86 | + |
| 87 | +/// This functions loads an atomic boolean in a loop until it is true. |
| 88 | +/// GenMC will explore all executions where this does 1, 2, ..., ∞ loads, which means the verification will never terminate. |
| 89 | +fn spin_until_true(flag: &AtomicBool) { |
| 90 | + while (!flag.load(Relaxed)) { |
| 91 | + std::hint::spin_loop(); |
| 92 | + } |
| 93 | +} |
| 94 | + |
| 95 | +/// By replacing this loop with an assume statement, the only executions that will be explored are those with exactly 1 load. |
| 96 | +/// Incorrect use of assume statements can lead GenMC to miss important executions, so it is marked `unsafe`. |
| 97 | +fn spin_until_true_genmc(flag: &AtomicBool) { |
| 98 | + unsafe { miri_genmc_assume(flag.load(Relaxed)); } |
| 99 | +} |
| 100 | +``` |
| 101 | + |
| 102 | +Some loops do contain side effects, meaning the number of explored iterations affects the rest of the program. |
| 103 | +Replacing the loop with one iteration like we did above would mean we miss all those possible executions. |
| 104 | + |
| 105 | +In such a case, the loop can be limited to a fixed number of iterations instead. |
| 106 | +The choice of iteration limit trades off verification time for possibly missing bugs requiring more iterations. |
| 107 | + |
| 108 | +```rust |
| 109 | +/// The loop in this function has a side effect, which is to increment the counter for the number of iterations. |
| 110 | +/// Instead of replacing the loop entirely (which would miss all executions with `count > 0`), we limit the loop to at most 3 iterations. |
| 111 | +fn count_until_true_genmc(flag: &AtomicBool) -> u64 { |
| 112 | + let mut count = 0; |
| 113 | + while (!flag.load(Relaxed)) { |
| 114 | + // Any execution that takes more than 3 iterations will not be explored. |
| 115 | + unsafe { miri_genmc_assume(count < 3); } |
| 116 | + count += 1; |
| 117 | + std::hint::spin_loop(); |
| 118 | + } |
| 119 | + count |
| 120 | +} |
| 121 | +``` |
| 122 | + |
| 123 | +<!-- FIXME: update the code above once Miri supports a loop bounding features like GenMC's `--unroll=N`. --> |
| 124 | +<!-- FIXME: update this section once Miri-GenMC supports automatic program transformations (like spinloop-assume replacement). --> |
| 125 | + |
53 | 126 | ## Limitations
|
54 | 127 |
|
55 | 128 | Some or all of these limitations might get removed in the future:
|
|
0 commit comments