remove irrelevant comments

This commit is contained in:
Niko Matsakis 2017-05-01 17:17:44 -04:00
parent 1dd9c3e52a
commit 851a880f52

View File

@ -32,111 +32,59 @@ MIR and then iteratively optimize it by putting it through various
pipeline stages. This section describes those pipeline stages and how
you can extend them.
Here is a diagram showing the various MIR queries involved in producing
the final `optimized_mir()` for a single def-id `D`. The arrows here
indicate how data flows from query to query.
To produce the `optimized_mir(D)` for a given def-id `D`, the MIR
passes through several suites of optimizations, each represented by a
query. Each suite consists of multiple optimizations and
transformations. These suites represent useful intermediate points
where we want to access the MIR for type checking or other purposes:
```
mir_build(D)
-> mir_pass((0,0,D)) ---+ each suite consists of many passes
-> ... |
-> mir_pass((0,N,D)) |
-> mir_suite((0,D)) ---+ ---+ there are several suites
-> ... |
-> mir_suite((M,D)) ---+
-> mir_optimized(D)
```
The MIR transformation pipeline is organized into **suites**. When
you ask for `mir_optimized(D)`, it will turn around and request the
result from the final **suite** of MIR passes
(`mir_suite((M,D))`). This will in turn (eventually) trigger the MIR
to be build and then passes through each of the optimization suites.
Each suite internally triggers one query for each of its passes
(`mir_pass(...)`).
The reason for the suites is that they represent points in the MIR
transformation pipeline where other bits of code are interested in
observing. For example, the `MIR_CONST` suite defines the point where
analysis for constant rvalues and expressions can take
place. `MIR_OPTIMIZED` naturally represents the point where we
actually generate machine code. Nobody should ever request the result
of an individual *pass*, at least outside of the transformation
pipeline: this allows us to add passes into the appropriate suite
without having to modify anything else in the compiler.
- `mir_build(D)` -- not a query, but this constructs the initial MIR
- `mir_const(D)` -- applies some simple transformations to make MIR ready for constant evaluation;
- `mir_validated(D)` -- applies some more transformations, making MIR ready for borrow checking;
- `optimized_mir(D)` -- the final state, after all optimizations have been performed.
### Stealing
Each of these intermediate queries yields up a `&'tcx
Steal<Mir<'tcx>>`, allocated using `tcx.alloc_steal_mir()`. This
indicates that the result may be **stolen** by the next pass -- this
is an optimization to avoid cloning the MIR. Attempting to use a
stolen result will cause a panic in the compiler. Therefore, it is
important that you not read directly from these intermediate queries
except as part of the MIR processing pipeline.
The intermediate queries `mir_const()` and `mir_validated()` yield up
a `&'tcx Steal<Mir<'tcx>>`, allocated using
`tcx.alloc_steal_mir()`. This indicates that the result may be
**stolen** by the next suite of optimizations -- this is an
optimization to avoid cloning the MIR. Attempting to use a stolen
result will cause a panic in the compiler. Therefore, it is important
that you not read directly from these intermediate queries except as
part of the MIR processing pipeline.
Because of this stealing mechanism, some care must also be taken to
ensure that, before the MIR at a particular phase in the processing
pipeline is stolen, anyone who may want to read from it has already
done so. Sometimes this requires **forcing** queries
(`ty::queries::foo::force(...)`) during an optimization pass -- this
will force a query to execute even though you don't directly require
its result. The query can then read the MIR it needs, and -- once it
is complete -- you can steal it.
done so. Concretely, this means that if you have some query `foo(D)`
that wants to access the result of `mir_const(D)` or
`mir_validated(D)`, you need to have the successor pass either "force"
`foo(D)` using `ty::queries::foo::force(...)`. This will force a query
to execute even though you don't directly require its result.
As an example, consider MIR const qualification. It wants to read the
result produced by the `MIR_CONST` suite. However, that result will be
**stolen** by the first pass in the next suite (that pass performs
const promotion):
```
mir_suite((MIR_CONST,D)) --read-by--> mir_const_qualif(D)
|
stolen-by
|
v
mir_pass((MIR_VALIDATED,0,D))
```
Therefore, the const promotion pass (the `mir_pass()` in the diagram)
result produced by the `mir_const()` suite. However, that result will
be **stolen** by the `mir_validated()` suite. If nothing was done,
then `mir_const_qualif(D)` would succeed if it came before
`mir_validated(D)`, but fail otherwise. Therefore, `mir_validated(D)`
will **force** `mir_const_qualif` before it actually steals, thus
ensuring that the reads have already happened (and the final result is
cached).
ensuring that the reads have already happened:
```
mir_const(D) --read-by--> mir_const_qualif(D)
| ^
stolen-by |
| (forces)
v |
mir_validated(D) ------------+
```
### Implementing and registering a pass
To create a new MIR pass, you have to implement one of the MIR pass
traits. There are several traits, and you want to pick the most
specific one that applies to your pass. They are described here in
order of preference. Once you have implemented a trait for your type
`Foo`, you then have to insert `Foo` into one of the suites; this is
done in `librustc_driver/driver.rs` by invoking `push_pass()` with the
appropriate suite.
To create a new MIR pass, you simply implement the `MirPass` trait for
some fresh singleton type `Foo`. Once you have implemented a trait for
your type `Foo`, you then have to insert `Foo` into one of the suites;
this is done in `librustc_driver/driver.rs` by invoking `push_pass(S,
Foo)` with the appropriate suite substituted for `S`.
**The `MirPass` trait.** For the most part, a MIR pass works by taking
as input the MIR for a single function and mutating it imperatively to
perform an optimization. To write such a pass, you can implement the
`MirPass` trait, which has a single callback that takes an `&mut Mir`.
**The `DefIdPass` trait.** When a `MirPass` trait is executed, the
system will automatically steal the result of the previous pass and
supply it to you. (See the section on queries and stealing below.)
Sometimes you don't want to steal the result of the previous pass
right away. In such cases, you can define a `DefIdPass`, which simply
gets a callback and lets you decide when to steal the previous result.
**The `Pass` trait.** The most primitive but flexible trait is `Pass`.
Unlike the other pass types, it returns a `Multi` result, which means
it scan be used for interprocedural passes which mutate more than one
MIR at a time (e.g., `inline`).
### The MIR Context
All of the passes when invoked take a `MirCtxt` object. This contains
various methods to find out (e.g.) the current pass suite and pass
index, the def-id you are operating on, and so forth. You can also
access the MIR for the current def-id using `read_previous_mir()`; the
"previous" refers to the fact that this will be the MIR that was
output by the previous pass. Finally, you can `steal_previous_mir()`
to steal the output of the current pass (in which case you get
ownership of the MIR).