|
1 |
| -# MIR passes |
| 1 | +# MIR queries and passes |
2 | 2 |
|
3 |
| -If you would like to get the MIR for a function (or constant, etc), |
4 |
| -you can use the `optimized_mir(def_id)` query. This will give you back |
5 |
| -the final, optimized MIR. For foreign def-ids, we simply read the MIR |
| 3 | +If you would like to get the MIR: |
| 4 | + |
| 5 | +- for a function - you can use the `optimized_mir(def_id)` query; |
| 6 | +- for a promoted - you can use the `promoted_mir(def_id)` query. |
| 7 | + |
| 8 | +These will give you back the final, optimized MIR. For foreign def-ids, we simply read the MIR |
6 | 9 | from the other crate's metadata. But for local def-ids, the query will
|
7 |
| -construct the MIR and then iteratively optimize it by applying a |
8 |
| -series of passes. This section describes how those passes work and how |
9 |
| -you can extend them. |
10 |
| - |
11 |
| -To produce the `optimized_mir(D)` for a given def-id `D`, the MIR |
12 |
| -passes through several suites of optimizations, each represented by a |
13 |
| -query. Each suite consists of multiple optimizations and |
14 |
| -transformations. These suites represent useful intermediate points |
15 |
| -where we want to access the MIR for type checking or other purposes: |
16 |
| - |
17 |
| -- `mir_build(D)` – not a query, but this constructs the initial MIR |
18 |
| -- `mir_const(D)` – applies some simple transformations to make MIR ready for |
19 |
| - constant evaluation; |
20 |
| -- `mir_validated(D)` – applies some more transformations, making MIR ready for |
21 |
| - borrow checking; |
22 |
| -- `optimized_mir(D)` – the final state, after all optimizations have been |
23 |
| - performed. |
24 |
| - |
25 |
| -### Implementing and registering a pass |
26 |
| - |
27 |
| -A `MirPass` is some bit of code that processes the MIR, typically – |
28 |
| -but not always – transforming it along the way somehow. For example, |
29 |
| -it might perform an optimization. The `MirPass` trait itself is found |
30 |
| -in [the `rustc_mir_transform` crate][mirtransform], and it |
31 |
| -basically consists of one method, `run_pass`, that simply gets an |
32 |
| -`&mut Mir` (along with the tcx and some information about where it |
33 |
| -came from). The MIR is therefore modified in place (which helps to |
34 |
| -keep things efficient). |
| 10 | +construct the optimized MIR by requesting a pipeline of upstream queries[^query]. |
| 11 | +Each query will contain a series of passes. |
| 12 | +This section describes how those queries and passes work and how you can extend them. |
| 13 | + |
| 14 | +To produce the optimized MIR for a given def-id `D`, `optimized_mir(D)` |
| 15 | +goes through several suites of passes, each grouped by a |
| 16 | +query. Each suite consists of passes which perform analysis, transformation or optimization. |
| 17 | +Each query represent a useful intermediate point |
| 18 | +where we can access the MIR dialect for type checking or other purposes: |
| 19 | + |
| 20 | +- `mir_built(D)` – it gives the initial MIR just after it's built; |
| 21 | +- `mir_const(D)` – it applies some simple transformation passes to make MIR ready for |
| 22 | + const qualification; |
| 23 | +- `mir_promoted(D)` - it extracts promotable temps into separate MIR bodies, and also makes MIR |
| 24 | + ready for borrow checking; |
| 25 | +- `mir_drops_elaborated_and_const_checked(D)` - it performs borrow checking, runs major |
| 26 | + transformation passes (such as drop elaboration) and makes MIR ready for optimization; |
| 27 | +- `optimized_mir(D)` – it performs all enabled optimizations and reaches the final state. |
| 28 | + |
| 29 | +[^query]: See the [Queries](../query.md) chapter for the general concept of query. |
| 30 | + |
| 31 | +## Implementing and registering a pass |
| 32 | + |
| 33 | +A `MirPass` is some bit of code that processes the MIR, typically transforming it along the way |
| 34 | +somehow. But it may also do other things like lingint (e.g., [`CheckPackedRef`][lint1], |
| 35 | +[`CheckConstItemMutation`][lint2], [`FunctionItemReferences`][lint3], which implement `MirLint`) or |
| 36 | +optimization (e.g., [`SimplifyCfg`][opt1], [`RemoveUnneededDrops`][opt2]). While most MIR passes |
| 37 | +are defined in the [`rustc_mir_transform`][mirtransform] crate, the `MirPass` trait itself is |
| 38 | +[found][mirpass] in the `rustc_middle` crate, and it basically consists of one primary method, |
| 39 | +`run_pass`, that simply gets an `&mut Body` (along with the `tcx`). |
| 40 | +The MIR is therefore modified in place (which helps to keep things efficient). |
35 | 41 |
|
36 | 42 | A basic example of a MIR pass is [`RemoveStorageMarkers`], which walks
|
37 | 43 | the MIR and removes all storage marks if they won't be emitted during codegen. As you
|
38 | 44 | can see from its source, a MIR pass is defined by first defining a
|
39 |
| -dummy type, a struct with no fields, something like: |
| 45 | +dummy type, a struct with no fields: |
40 | 46 |
|
41 | 47 | ```rust
|
42 |
| -struct MyPass; |
| 48 | +pub struct RemoveStorageMarkers; |
43 | 49 | ```
|
44 | 50 |
|
45 |
| -for which you then implement the `MirPass` trait. You can then insert |
| 51 | +for which we implement the `MirPass` trait. We can then insert |
46 | 52 | this pass into the appropriate list of passes found in a query like
|
47 |
| -`optimized_mir`, `mir_validated`, etc. (If this is an optimization, it |
| 53 | +`mir_built`, `optimized_mir`, etc. (If this is an optimization, it |
48 | 54 | should go into the `optimized_mir` list.)
|
49 | 55 |
|
| 56 | +Another example of a simple MIR pass is [`CleanupNonCodegenStatements`][cleanup-pass], which walks |
| 57 | +the MIR and removes all statements that are not relevant to code generation. As you can see from |
| 58 | +its [source][cleanup-source], it is defined by first defining a dummy type, a struct with no |
| 59 | +fields: |
| 60 | + |
| 61 | +```rust |
| 62 | +pub struct CleanupNonCodegenStatements; |
| 63 | +``` |
| 64 | + |
| 65 | +for which we then implement the `MirPass` trait: |
| 66 | + |
| 67 | +```rust |
| 68 | +impl<'tcx> MirPass<'tcx> for CleanupNonCodegenStatements { |
| 69 | + fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { |
| 70 | + ... |
| 71 | + } |
| 72 | +} |
| 73 | +``` |
| 74 | + |
| 75 | +We [register][pass-register] this pass inside the `mir_drops_elaborated_and_const_checked` query. |
| 76 | +(If this is an optimization, it should go into the `optimized_mir` list.) |
| 77 | + |
50 | 78 | If you are writing a pass, there's a good chance that you are going to
|
51 | 79 | want to use a [MIR visitor]. MIR visitors are a handy way to walk all
|
52 | 80 | the parts of the MIR, either to search for something or to make small
|
53 | 81 | edits.
|
54 | 82 |
|
55 |
| -### Stealing |
| 83 | +## Stealing |
56 | 84 |
|
57 |
| -The intermediate queries `mir_const()` and `mir_validated()` yield up |
58 |
| -a `&'tcx Steal<Mir<'tcx>>`, allocated using |
59 |
| -`tcx.alloc_steal_mir()`. This indicates that the result may be |
60 |
| -**stolen** by the next suite of optimizations – this is an |
| 85 | +The intermediate queries `mir_const()` and `mir_promoted()` yield up |
| 86 | +a `&'tcx Steal<Body<'tcx>>`, allocated using `tcx.alloc_steal_mir()`. |
| 87 | +This indicates that the result may be **stolen** by a subsequent query – this is an |
61 | 88 | optimization to avoid cloning the MIR. Attempting to use a stolen
|
62 | 89 | result will cause a panic in the compiler. Therefore, it is important
|
63 |
| -that you do not read directly from these intermediate queries except as |
64 |
| -part of the MIR processing pipeline. |
| 90 | +that you do not accidently read from these intermediate queries without |
| 91 | +the consideration of the dependency in the MIR processing pipeline. |
65 | 92 |
|
66 |
| -Because of this stealing mechanism, some care must also be taken to |
| 93 | +Because of this stealing mechanism, some care must be taken to |
67 | 94 | ensure that, before the MIR at a particular phase in the processing
|
68 | 95 | pipeline is stolen, anyone who may want to read from it has already
|
69 |
| -done so. Concretely, this means that if you have some query `foo(D)` |
| 96 | +done so. |
| 97 | + |
| 98 | +<!-- FIXME - What is force? Do we still have it in rustc? --> |
| 99 | +Concretely, this means that if you have a query `foo(D)` |
70 | 100 | that wants to access the result of `mir_const(D)` or
|
71 |
| -`mir_validated(D)`, you need to have the successor pass "force" |
| 101 | +`mir_promoted(D)`, you need to have the successor pass "force" |
72 | 102 | `foo(D)` using `ty::queries::foo::force(...)`. This will force a query
|
73 | 103 | to execute even though you don't directly require its result.
|
74 | 104 |
|
75 |
| -As an example, consider MIR const qualification. It wants to read the |
76 |
| -result produced by the `mir_const()` suite. However, that result will |
77 |
| -be **stolen** by the `mir_validated()` suite. If nothing was done, |
78 |
| -then `mir_const_qualif(D)` would succeed if it came before |
79 |
| -`mir_validated(D)`, but fail otherwise. Therefore, `mir_validated(D)` |
80 |
| -will **force** `mir_const_qualif` before it actually steals, thus |
81 |
| -ensuring that the reads have already happened (remember that |
82 |
| -[queries are memoized](../query.html), so executing a query twice |
83 |
| -simply loads from a cache the second time): |
84 |
| - |
85 |
| -```text |
86 |
| -mir_const(D) --read-by--> mir_const_qualif(D) |
87 |
| - | ^ |
88 |
| - stolen-by | |
89 |
| - | (forces) |
90 |
| - v | |
91 |
| -mir_validated(D) ------------+ |
| 105 | +> This mechanism is a bit dodgy. There is a discussion of more elegant |
| 106 | +alternatives in [rust-lang/rust#41710]. |
| 107 | + |
| 108 | +### Overview |
| 109 | + |
| 110 | +Below is an overview of the stealing dependency in the MIR processing pipeline[^part]: |
| 111 | + |
| 112 | +```mermaid |
| 113 | +flowchart BT |
| 114 | + mir_for_ctfe* --borrow--> id40 |
| 115 | + id5 --steal--> id40 |
| 116 | +
|
| 117 | + mir_borrowck* --borrow--> id3 |
| 118 | + id41 --steal part 1--> id3 |
| 119 | + id40 --steal part 0--> id3 |
| 120 | +
|
| 121 | + mir_const_qualif* -- borrow --> id2 |
| 122 | + id3 -- steal --> id2 |
| 123 | +
|
| 124 | + id2 -- steal --> id1 |
| 125 | +
|
| 126 | + id1([mir_built]) |
| 127 | + id2([mir_const]) |
| 128 | + id3([mir_promoted]) |
| 129 | + id40([mir_drops_elaborated_and_const_checked]) |
| 130 | + id41([promoted_mir]) |
| 131 | + id5([optimized_mir]) |
| 132 | +
|
| 133 | + style id1 fill:#bbf |
| 134 | + style id2 fill:#bbf |
| 135 | + style id3 fill:#bbf |
| 136 | + style id40 fill:#bbf |
| 137 | + style id41 fill:#bbf |
| 138 | + style id5 fill:#bbf |
92 | 139 | ```
|
93 | 140 |
|
94 |
| -This mechanism is a bit dodgy. There is a discussion of more elegant |
95 |
| -alternatives in [rust-lang/rust#41710]. |
| 141 | +The stadium-shape queries (e.g., `mir_built`) with a deep color are the primary queries in the |
| 142 | +pipeline, while the rectangle-shape queries (e.g., `mir_const_qualif*`[^star]) with a shallow color |
| 143 | +are those subsequent queries that need to read the results from `&'tcx Steal<Body<'tcx>>`. With the |
| 144 | +stealing mechanism, the rectangle-shape queries must be performed before any stadium-shape queries, |
| 145 | +that have an equal or larger height in the dependency tree, ever do. |
| 146 | + |
| 147 | +[^part]: The `mir_promoted` query will yield up a tuple |
| 148 | +`(&'tcx Steal<Body<'tcx>>, &'tcx Steal<IndexVec<Promoted, Body<'tcx>>>)`, `promoted_mir` will steal |
| 149 | +part 1 (`&'tcx Steal<IndexVec<Promoted, Body<'tcx>>>`) and `mir_drops_elaborated_and_const_checked` |
| 150 | +will steal part 0 (`&'tcx Steal<Body<'tcx>>`). And their stealing is irrelevant to each other, |
| 151 | +i.e., can be performed separately. |
| 152 | + |
| 153 | +[^star]: Note that the `*` suffix in the queries represent a set of queries with the same prefix. |
| 154 | +For example, `mir_borrowck*` represents `mir_borrowck`, `mir_borrowck_const_arg` and |
| 155 | +`mir_borrowck_opt_const_arg`. |
| 156 | + |
| 157 | +### Example |
| 158 | + |
| 159 | +As an example, consider MIR const qualification. It wants to read the result produced by the |
| 160 | +`mir_const` query. However, that result will be **stolen** by the `mir_promoted` query at some |
| 161 | +time in the pipeline. Before `mir_promoted` is ever queried, calling the `mir_const_qualif` query |
| 162 | +will succeed since `mir_const` will produce (if queried the first time) or cache (if queried |
| 163 | +multiple times) the `Steal` result and the result is **not** stolen yet. After `mir_promoted` is |
| 164 | +queried, the result would be stolen and calling the `mir_const_qualif` query to read the result |
| 165 | +would cause a panic. |
| 166 | + |
| 167 | +Therefore, with this stealing mechanism, `mir_promoted` should guarantee any `mir_const_qualif*` |
| 168 | +queries are called before it actually steals, thus ensuring that the reads have already happened |
| 169 | +(remember that [queries are memoized](../query.html), so executing a query twice |
| 170 | +simply loads from a cache the second time). |
96 | 171 |
|
97 | 172 | [rust-lang/rust#41710]: https://github.com/rust-lang/rust/issues/41710
|
| 173 | +[mirpass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/mir/trait.MirPass.html |
| 174 | +[lint1]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/check_packed_ref/struct.CheckPackedRef.html |
| 175 | +[lint2]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/check_const_item_mutation/struct.CheckConstItemMutation.html |
| 176 | +[lint3]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/function_item_references/struct.FunctionItemReferences.html |
| 177 | +[opt1]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/simplify/struct.SimplifyCfg.html |
| 178 | +[opt2]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/remove_unneeded_drops/struct.RemoveUnneededDrops.html |
98 | 179 | [mirtransform]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/
|
99 | 180 | [`RemoveStorageMarkers`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/remove_storage_markers/struct.RemoveStorageMarkers.html
|
| 181 | +[cleanup-pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/cleanup_post_borrowck/struct.CleanupNonCodegenStatements.html |
| 182 | +[cleanup-source]: https://github.com/rust-lang/rust/blob/e2b52ff73edc8b0b7c74bc28760d618187731fe8/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs#L27 |
| 183 | +[pass-register]: https://github.com/rust-lang/rust/blob/e2b52ff73edc8b0b7c74bc28760d618187731fe8/compiler/rustc_mir_transform/src/lib.rs#L413 |
100 | 184 | [MIR visitor]: ./visitor.html
|
0 commit comments