|
| 1 | +# MIR definition and pass system |
| 2 | + |
| 3 | +This file contains the definition of the MIR datatypes along with the |
| 4 | +various types for the "MIR Pass" system, which lets you easily |
| 5 | +register and define new MIR transformations and analyses. |
| 6 | + |
| 7 | +Most of the code that operates on MIR can be found in the |
| 8 | +`librustc_mir` crate or other crates. The code found here in |
| 9 | +`librustc` is just the datatype definitions, alonging the functions |
| 10 | +which operate on MIR to be placed everywhere else. |
| 11 | + |
| 12 | +## MIR Data Types and visitor |
| 13 | + |
| 14 | +The main MIR data type is `rustc::mir::Mir`, defined in `mod.rs`. |
| 15 | +There is also the MIR visitor (in `visit.rs`) which allows you to walk |
| 16 | +the MIR and override what actions will be taken at various points (you |
| 17 | +can visit in either shared or mutable mode; the latter allows changing |
| 18 | +the MIR in place). Finally `traverse.rs` contains various traversal |
| 19 | +routines for visiting the MIR CFG in [different standard orders][traversal] |
| 20 | +(e.g. pre-order, reverse post-order, and so forth). |
| 21 | + |
| 22 | +[traversal]: https://en.wikipedia.org/wiki/Tree_traversal |
| 23 | + |
| 24 | +## MIR pass suites and their integration into the query system |
| 25 | + |
| 26 | +As a MIR *consumer*, you are expected to use one of the queries that |
| 27 | +returns a "final MIR". As of the time of this writing, there is only |
| 28 | +one: `optimized_mir(def_id)`, but more are expected to come in the |
| 29 | +future. For foreign def-ids, we simply read the MIR from the other |
| 30 | +crate's metadata. But for local query, this query will construct the |
| 31 | +MIR and then iteratively optimize it by putting it through various |
| 32 | +pipeline stages. This section describes those pipeline stages and how |
| 33 | +you can extend them. |
| 34 | + |
| 35 | +To produce the `optimized_mir(D)` for a given def-id `D`, the MIR |
| 36 | +passes through several suites of optimizations, each represented by a |
| 37 | +query. Each suite consists of multiple optimizations and |
| 38 | +transformations. These suites represent useful intermediate points |
| 39 | +where we want to access the MIR for type checking or other purposes: |
| 40 | + |
| 41 | +- `mir_build(D)` -- not a query, but this constructs the initial MIR |
| 42 | +- `mir_const(D)` -- applies some simple transformations to make MIR ready for constant evaluation; |
| 43 | +- `mir_validated(D)` -- applies some more transformations, making MIR ready for borrow checking; |
| 44 | +- `optimized_mir(D)` -- the final state, after all optimizations have been performed. |
| 45 | + |
| 46 | +### Stealing |
| 47 | + |
| 48 | +The intermediate queries `mir_const()` and `mir_validated()` yield up |
| 49 | +a `&'tcx Steal<Mir<'tcx>>`, allocated using |
| 50 | +`tcx.alloc_steal_mir()`. This indicates that the result may be |
| 51 | +**stolen** by the next suite of optimizations -- this is an |
| 52 | +optimization to avoid cloning the MIR. Attempting to use a stolen |
| 53 | +result will cause a panic in the compiler. Therefore, it is important |
| 54 | +that you not read directly from these intermediate queries except as |
| 55 | +part of the MIR processing pipeline. |
| 56 | + |
| 57 | +Because of this stealing mechanism, some care must also be taken to |
| 58 | +ensure that, before the MIR at a particular phase in the processing |
| 59 | +pipeline is stolen, anyone who may want to read from it has already |
| 60 | +done so. Concretely, this means that if you have some query `foo(D)` |
| 61 | +that wants to access the result of `mir_const(D)` or |
| 62 | +`mir_validated(D)`, you need to have the successor pass either "force" |
| 63 | +`foo(D)` using `ty::queries::foo::force(...)`. This will force a query |
| 64 | +to execute even though you don't directly require its result. |
| 65 | + |
| 66 | +As an example, consider MIR const qualification. It wants to read the |
| 67 | +result produced by the `mir_const()` suite. However, that result will |
| 68 | +be **stolen** by the `mir_validated()` suite. If nothing was done, |
| 69 | +then `mir_const_qualif(D)` would succeed if it came before |
| 70 | +`mir_validated(D)`, but fail otherwise. Therefore, `mir_validated(D)` |
| 71 | +will **force** `mir_const_qualif` before it actually steals, thus |
| 72 | +ensuring that the reads have already happened: |
| 73 | + |
| 74 | +``` |
| 75 | +mir_const(D) --read-by--> mir_const_qualif(D) |
| 76 | + | ^ |
| 77 | + stolen-by | |
| 78 | + | (forces) |
| 79 | + v | |
| 80 | +mir_validated(D) ------------+ |
| 81 | +``` |
| 82 | + |
| 83 | +### Implementing and registering a pass |
| 84 | + |
| 85 | +To create a new MIR pass, you simply implement the `MirPass` trait for |
| 86 | +some fresh singleton type `Foo`. Once you have implemented a trait for |
| 87 | +your type `Foo`, you then have to insert `Foo` into one of the suites; |
| 88 | +this is done in `librustc_driver/driver.rs` by invoking `push_pass(S, |
| 89 | +Foo)` with the appropriate suite substituted for `S`. |
| 90 | + |
0 commit comments