|
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). |
35 |
| - |
36 |
| -A good example of a basic MIR pass is [`NoLandingPads`], which walks |
37 |
| -the MIR and removes all edges that are due to unwinding – this is |
38 |
| -used when configured with `panic=abort`, which never unwinds. As you |
39 |
| -can see from its source, a MIR pass is defined by first defining a |
40 |
| -dummy type, a struct with no fields, something like: |
| 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 analysis (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). |
| 41 | + |
| 42 | +A good example of a simple MIR pass is [`CleanupNonCodegenStatements`][cleanup-pass], which walks |
| 43 | +the MIR and removes all statements that are not relevant to code generation. As you can see from |
| 44 | +its [source][cleanup-source], it is defined by first defining a dummy type, a struct with no |
| 45 | +fields: |
41 | 46 |
|
42 | 47 | ```rust
|
43 |
| -struct MyPass; |
| 48 | +pub struct CleanupNonCodegenStatements; |
44 | 49 | ```
|
45 | 50 |
|
46 |
| -for which you then implement the `MirPass` trait. You can then insert |
47 |
| -this pass into the appropriate list of passes found in a query like |
48 |
| -`optimized_mir`, `mir_validated`, etc. (If this is an optimization, it |
49 |
| -should go into the `optimized_mir` list.) |
| 51 | +for which we then implement the `MirPass` trait: |
| 52 | + |
| 53 | +```rust |
| 54 | +impl<'tcx> MirPass<'tcx> for CleanupNonCodegenStatements { |
| 55 | + fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { |
| 56 | + ... |
| 57 | + } |
| 58 | +} |
| 59 | +``` |
| 60 | + |
| 61 | +We [register][pass-register] this pass inside the `mir_drops_elaborated_and_const_checked` query. |
| 62 | +(If this is an optimization, it should go into the `optimized_mir` list.) |
50 | 63 |
|
51 | 64 | If you are writing a pass, there's a good chance that you are going to
|
52 | 65 | want to use a [MIR visitor]. MIR visitors are a handy way to walk all
|
53 | 66 | the parts of the MIR, either to search for something or to make small
|
54 | 67 | edits.
|
55 | 68 |
|
56 |
| -### Stealing |
| 69 | +## Stealing |
57 | 70 |
|
58 |
| -The intermediate queries `mir_const()` and `mir_validated()` yield up |
59 |
| -a `&'tcx Steal<Mir<'tcx>>`, allocated using |
60 |
| -`tcx.alloc_steal_mir()`. This indicates that the result may be |
61 |
| -**stolen** by the next suite of optimizations – this is an |
| 71 | +The intermediate queries `mir_const()` and `mir_promoted()` yield up |
| 72 | +a `&'tcx Steal<Body<'tcx>>`, allocated using `tcx.alloc_steal_mir()`. |
| 73 | +This indicates that the result may be **stolen** by a subsequent query – this is an |
62 | 74 | optimization to avoid cloning the MIR. Attempting to use a stolen
|
63 | 75 | result will cause a panic in the compiler. Therefore, it is important
|
64 |
| -that you do not read directly from these intermediate queries except as |
65 |
| -part of the MIR processing pipeline. |
| 76 | +that you do not accidently read from these intermediate queries without |
| 77 | +the consideration of the dependency in the MIR processing pipeline. |
66 | 78 |
|
67 |
| -Because of this stealing mechanism, some care must also be taken to |
| 79 | +Because of this stealing mechanism, some care must be taken to |
68 | 80 | ensure that, before the MIR at a particular phase in the processing
|
69 | 81 | pipeline is stolen, anyone who may want to read from it has already
|
70 |
| -done so. Concretely, this means that if you have some query `foo(D)` |
| 82 | +done so. |
| 83 | + |
| 84 | +<!-- FIXME - What is force? Do we still have it in rustc? --> |
| 85 | +Concretely, this means that if you have a query `foo(D)` |
71 | 86 | that wants to access the result of `mir_const(D)` or
|
72 |
| -`mir_validated(D)`, you need to have the successor pass "force" |
| 87 | +`mir_promoted(D)`, you need to have the successor pass "force" |
73 | 88 | `foo(D)` using `ty::queries::foo::force(...)`. This will force a query
|
74 | 89 | to execute even though you don't directly require its result.
|
75 | 90 |
|
76 |
| -As an example, consider MIR const qualification. It wants to read the |
77 |
| -result produced by the `mir_const()` suite. However, that result will |
78 |
| -be **stolen** by the `mir_validated()` suite. If nothing was done, |
79 |
| -then `mir_const_qualif(D)` would succeed if it came before |
80 |
| -`mir_validated(D)`, but fail otherwise. Therefore, `mir_validated(D)` |
81 |
| -will **force** `mir_const_qualif` before it actually steals, thus |
82 |
| -ensuring that the reads have already happened (remember that |
83 |
| -[queries are memoized](../query.html), so executing a query twice |
84 |
| -simply loads from a cache the second time): |
85 |
| - |
86 |
| -```text |
87 |
| -mir_const(D) --read-by--> mir_const_qualif(D) |
88 |
| - | ^ |
89 |
| - stolen-by | |
90 |
| - | (forces) |
91 |
| - v | |
92 |
| -mir_validated(D) ------------+ |
| 91 | +> This mechanism is a bit dodgy. There is a discussion of more elegant |
| 92 | +alternatives in [rust-lang/rust#41710]. |
| 93 | + |
| 94 | +### Overview |
| 95 | + |
| 96 | +Below is an overview of the stealing dependency in the MIR processing pipeline[^part]: |
| 97 | + |
| 98 | +```mermaid |
| 99 | +flowchart BT |
| 100 | + mir_for_ctfe* --borrow--> id40 |
| 101 | + id5 --steal--> id40 |
| 102 | +
|
| 103 | + mir_borrowck* --borrow--> id3 |
| 104 | + id41 --steal part 1--> id3 |
| 105 | + id40 --steal part 0--> id3 |
| 106 | +
|
| 107 | + mir_const_qualif* -- borrow --> id2 |
| 108 | + id3 -- steal --> id2 |
| 109 | +
|
| 110 | + id2 -- steal --> id1 |
| 111 | +
|
| 112 | + id1([mir_built]) |
| 113 | + id2([mir_const]) |
| 114 | + id3([mir_promoted]) |
| 115 | + id40([mir_drops_elaborated_and_const_checked]) |
| 116 | + id41([promoted_mir]) |
| 117 | + id5([optimized_mir]) |
| 118 | +
|
| 119 | + style id1 fill:#bbf |
| 120 | + style id2 fill:#bbf |
| 121 | + style id3 fill:#bbf |
| 122 | + style id40 fill:#bbf |
| 123 | + style id41 fill:#bbf |
| 124 | + style id5 fill:#bbf |
93 | 125 | ```
|
94 | 126 |
|
95 |
| -This mechanism is a bit dodgy. There is a discussion of more elegant |
96 |
| -alternatives in [rust-lang/rust#41710]. |
| 127 | +The stadium-shape queries (e.g., `mir_built`) with a deep color are the primary queries in the |
| 128 | +pipeline, while the rectangle-shape queries (e.g., `mir_const_qualif*`[^star]) with a shallow color |
| 129 | +are those subsequent queries that need to read the results from `&'tcx Steal<Body<'tcx>>`. With the |
| 130 | +stealing mechanism, the rectangle-shape queries must be performed before any stadium-shape queries, |
| 131 | +that have an equal or larger height in the dependency tree, ever do. |
| 132 | + |
| 133 | +[^part]: The `mir_promoted` query will yield up a tuple |
| 134 | +`(&'tcx Steal<Body<'tcx>>, &'tcx Steal<IndexVec<Promoted, Body<'tcx>>>)`, `promoted_mir` will steal |
| 135 | +part 1 (`&'tcx Steal<IndexVec<Promoted, Body<'tcx>>>`) and `mir_drops_elaborated_and_const_checked` |
| 136 | +will steal part 0 (`&'tcx Steal<Body<'tcx>>`). And their stealing is irrelevant to each other, |
| 137 | +i.e., can be performed separately. |
| 138 | + |
| 139 | +[^star]: Note that the `*` suffix in the queries represent a set of queries with the same prefix. |
| 140 | +For example, `mir_borrowck*` represents `mir_borrowck`, `mir_borrowck_const_arg` and |
| 141 | +`mir_borrowck_opt_const_arg`. |
| 142 | + |
| 143 | +### Example |
| 144 | + |
| 145 | +As an example, consider MIR const qualification. It wants to read the result produced by the |
| 146 | +`mir_const` query. However, that result will be **stolen** by the `mir_promoted` query at some |
| 147 | +time in the pipeline. Before `mir_promoted` is ever queried, calling the `mir_const_qualif` query |
| 148 | +will succeed since `mir_const` will produce (if queried the first time) or cache (if queried |
| 149 | +multiple times) the `Steal` result and the result is **not** stolen yet. After `mir_promoted` is |
| 150 | +queried, the result would be stolen and calling the `mir_const_qualif` query to read the result |
| 151 | +would cause a panic. |
| 152 | + |
| 153 | +Therefore, with this stealing mechanism, `mir_promoted` should guarantee any `mir_const_qualif*` |
| 154 | +queries are called before it actually steals, thus ensuring that the reads have already happened |
| 155 | +(remember that [queries are memoized](../query.html), so executing a query twice |
| 156 | +simply loads from a cache the second time). |
97 | 157 |
|
98 | 158 | [rust-lang/rust#41710]: https://github.com/rust-lang/rust/issues/41710
|
| 159 | +[mirpass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/mir/trait.MirPass.html |
| 160 | +[lint1]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/check_packed_ref/struct.CheckPackedRef.html |
| 161 | +[lint2]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/check_const_item_mutation/struct.CheckConstItemMutation.html |
| 162 | +[lint3]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/function_item_references/struct.FunctionItemReferences.html |
| 163 | +[opt1]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/simplify/struct.SimplifyCfg.html |
| 164 | +[opt2]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/remove_unneeded_drops/struct.RemoveUnneededDrops.html |
99 | 165 | [mirtransform]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/
|
100 |
| -<!--- TODO: Change NoLandingPads. [#1232](https://github.com/rust-lang/rustc-dev-guide/issues/1232) --> |
101 |
| -[`NoLandingPads`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/no_landing_pads/struct.NoLandingPads.html |
| 166 | +[cleanup-pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/cleanup_post_borrowck/struct.CleanupNonCodegenStatements.html |
| 167 | +[cleanup-source]: https://github.com/rust-lang/rust/blob/e2b52ff73edc8b0b7c74bc28760d618187731fe8/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs#L27 |
| 168 | +[pass-register]: https://github.com/rust-lang/rust/blob/e2b52ff73edc8b0b7c74bc28760d618187731fe8/compiler/rustc_mir_transform/src/lib.rs#L413 |
102 | 169 | [MIR visitor]: ./visitor.html
|
0 commit comments