Skip to content

Commit 6bbd1c4

Browse files
Jaic1oli-obk
authored andcommitted
Improve doc of MIR queries & passes
1 parent db1c4ca commit 6bbd1c4

File tree

2 files changed

+151
-67
lines changed

2 files changed

+151
-67
lines changed

Diff for: src/SUMMARY.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,8 @@
110110
- [The MIR (Mid-level IR)](./mir/index.md)
111111
- [MIR construction](./mir/construction.md)
112112
- [MIR visitor and traversal](./mir/visitor.md)
113-
- [MIR passes: getting the MIR for a function](./mir/passes.md)
114-
- [Identifiers in the compiler](./identifiers.md)
113+
- [MIR queries and passes: getting the MIR](./mir/passes.md)
114+
- [Identifiers in the Compiler](./identifiers.md)
115115
- [Closure expansion](./closure.md)
116116
- [Inline assembly](./asm.md)
117117

Diff for: src/mir/passes.md

+149-65
Original file line numberDiff line numberDiff line change
@@ -1,100 +1,184 @@
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).
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).
3541

3642
A basic example of a MIR pass is [`RemoveStorageMarkers`], which walks
3743
the MIR and removes all storage marks if they won't be emitted during codegen. As you
3844
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:
4046

4147
```rust
42-
struct MyPass;
48+
pub struct RemoveStorageMarkers;
4349
```
4450

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
4652
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
4854
should go into the `optimized_mir` list.)
4955

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+
5078
If you are writing a pass, there's a good chance that you are going to
5179
want to use a [MIR visitor]. MIR visitors are a handy way to walk all
5280
the parts of the MIR, either to search for something or to make small
5381
edits.
5482

55-
### Stealing
83+
## Stealing
5684

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
6188
optimization to avoid cloning the MIR. Attempting to use a stolen
6289
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.
6592

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
6794
ensure that, before the MIR at a particular phase in the processing
6895
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)`
70100
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"
72102
`foo(D)` using `ty::queries::foo::force(...)`. This will force a query
73103
to execute even though you don't directly require its result.
74104

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
92139
```
93140

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

97172
[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
98179
[mirtransform]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/
99180
[`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
100184
[MIR visitor]: ./visitor.html

0 commit comments

Comments
 (0)