Expand on the documentation for polymorphization. (#803)
This commit elaborates on the existing documentation for polymorphization now that polymorphization has almost landed in rustc. Signed-off-by: David Wood <david@davidtw.co>
This commit is contained in:
parent
bc72178000
commit
50706fc2a2
|
|
@ -61,33 +61,73 @@ units](../appendix/glossary.md#codegen-unit).
|
||||||
## Polymorphization
|
## Polymorphization
|
||||||
|
|
||||||
As mentioned above, monomorphization produces fast code, but it comes at the
|
As mentioned above, monomorphization produces fast code, but it comes at the
|
||||||
cost of compile time and binary size. [MIR
|
cost of compile time and binary size. [MIR optimizations][miropt] can help a
|
||||||
optimizations](../mir/optimizations.md) can help a bit with this. Another
|
bit with this.
|
||||||
optimization currently under development is called _polymorphization_.
|
|
||||||
|
|
||||||
The general idea is that often we can share some code between monomorphized
|
In addition to MIR optimizations, rustc attempts to determine when fewer
|
||||||
copies of code. More precisely, if a MIR block is not dependent on a type
|
copies of functions are necessary and avoid making those copies - known
|
||||||
parameter, it may not need to be monomorphized into many copies. Consider the
|
as "polymorphization". When a function-like item is found during
|
||||||
following example:
|
monomorphization collection, the
|
||||||
|
[`rustc_mir::monomorphize::polymorphize::unused_generic_params`][polymorph]
|
||||||
|
query is invoked, which traverses the MIR of the item to determine on which
|
||||||
|
generic parameters the item might not need duplicated.
|
||||||
|
|
||||||
|
Currently, polymorphization only looks for unused generic parameters. These
|
||||||
|
are relatively rare in functions, but closures inherit the generic
|
||||||
|
parameters of their parent function and it is common for closures to not
|
||||||
|
use those inherited parameters. Without polymorphization, a copy of these
|
||||||
|
closures would be created for each copy of the parent function. By
|
||||||
|
creating fewer copies, less LLVM IR is generated and needs processed.
|
||||||
|
|
||||||
|
`unused_generic_params` returns a `FiniteBitSet<u64>` where a bit is set if
|
||||||
|
the generic parameter of the corresponding index is unused. Any parameters
|
||||||
|
after the first sixty-four are considered used.
|
||||||
|
|
||||||
|
The results of polymorphization analysis are used in the
|
||||||
|
[`Instance::polymorphize`][inst_polymorph] function to replace the
|
||||||
|
[`Instance`][inst]'s substitutions for the unused generic parameters with their
|
||||||
|
identity substitutions.
|
||||||
|
|
||||||
|
Consider the example below:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
pub fn f() {
|
fn foo<A, B>() {
|
||||||
g::<bool>();
|
let x: Option<B> = None;
|
||||||
g::<usize>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn g<T>() -> usize {
|
fn main() {
|
||||||
let n = 1;
|
foo::<u16, u32>();
|
||||||
let closure = || n;
|
foo::<u64, u32>();
|
||||||
closure()
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
In this case, we would currently collect `[f, g::<bool>, g::<usize>,
|
During monomorphization collection, `foo` will be collected with the
|
||||||
g::<bool>::{{closure}}, g::<usize>::{{closure}}]`, but notice that the two
|
substitutions `[u16, u32]` and `[u64, u32]` (from its invocations in `main`).
|
||||||
closures would be identical -- they don't depend on the type parameter `T` of
|
`foo` has the identity substitutions `[A, B]` (or
|
||||||
function `g`. So we only need to emit one copy of the closure.
|
`[ty::Param(0), ty::Param(1)]`).
|
||||||
|
|
||||||
For more information, see [this thread on github][polymorph].
|
Polymorphization will identify `A` as being unused and it will be replaced in
|
||||||
|
the substitutions with the identity parameter before being added to the set
|
||||||
|
of collected items - thereby reducing the copies from two (`[u16, u32]` and
|
||||||
|
`[u64, u32]`) to one (`[A, u32]`).
|
||||||
|
|
||||||
[polymorph]: https://github.com/rust-lang/rust/issues/46477
|
`unused_generic_params` will also invoked during code generation when the
|
||||||
|
symbol name for `foo` is being computed for use in the callsites of `foo`
|
||||||
|
(which have the regular substitutions present, otherwise there would be a
|
||||||
|
symbol mismatch between the caller and the function).
|
||||||
|
|
||||||
|
As a result of polymorphization, items collected during monomorphization
|
||||||
|
cannot be assumed to be monomorphic.
|
||||||
|
|
||||||
|
It is intended that polymorphization be extended to more advanced cases,
|
||||||
|
such as where only the size/alignment of a generic parameter are required.
|
||||||
|
|
||||||
|
More details on polymorphization are available in the
|
||||||
|
[master's thesis][thesis] associated with polymorphization's initial
|
||||||
|
implementation.
|
||||||
|
|
||||||
|
[miropt]: ../mir/optimizations.md
|
||||||
|
[polymorph]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir/monomorphize/polymorphize/fn.unused_generic_params.html
|
||||||
|
[inst]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/instance/struct.Instance.html
|
||||||
|
[inst_polymorph]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/instance/struct.Instance.html#method.polymorphize
|
||||||
|
[thesis]: https://davidtw.co/media/masters_dissertation.pdf
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue