Skip to content

Commit db76438

Browse files
committed
Improve doc of MIR queries & passes
1 parent d3daa1f commit db76438

File tree

2 files changed

+142
-75
lines changed

2 files changed

+142
-75
lines changed

src/SUMMARY.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@
9494
- [The MIR (Mid-level IR)](./mir/index.md)
9595
- [MIR construction](./mir/construction.md)
9696
- [MIR visitor and traversal](./mir/visitor.md)
97-
- [MIR passes: getting the MIR for a function](./mir/passes.md)
97+
- [MIR queries and passes](./mir/passes.md)
9898
- [Identifiers in the Compiler](./identifiers.md)
9999
- [Closure expansion](./closure.md)
100100
- [Inline assembly](./asm.md)

src/mir/passes.md

Lines changed: 141 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,102 +1,169 @@
1-
# MIR passes
1+
# MIR queries and passes
22

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
69
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:
4146

4247
```rust
43-
struct MyPass;
48+
pub struct CleanupNonCodegenStatements;
4449
```
4550

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.)
5063

5164
If you are writing a pass, there's a good chance that you are going to
5265
want to use a [MIR visitor]. MIR visitors are a handy way to walk all
5366
the parts of the MIR, either to search for something or to make small
5467
edits.
5568

56-
### Stealing
69+
## Stealing
5770

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
6274
optimization to avoid cloning the MIR. Attempting to use a stolen
6375
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.
6678

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
6880
ensure that, before the MIR at a particular phase in the processing
6981
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)`
7186
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"
7388
`foo(D)` using `ty::queries::foo::force(...)`. This will force a query
7489
to execute even though you don't directly require its result.
7590

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
93125
```
94126

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).
97157

98158
[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
99165
[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
102169
[MIR visitor]: ./visitor.html

0 commit comments

Comments
 (0)