|
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