Skip to content

Commit 7dc4ae0

Browse files
David WoodJohnTitor
David Wood
authored andcommitted
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 <[email protected]>
1 parent 1474f1f commit 7dc4ae0

File tree

1 file changed

+61
-21
lines changed

1 file changed

+61
-21
lines changed

src/backend/monomorph.md

+61-21
Original file line numberDiff line numberDiff line change
@@ -61,33 +61,73 @@ units](../appendix/glossary.md#codegen-unit).
6161
## Polymorphization
6262

6363
As mentioned above, monomorphization produces fast code, but it comes at the
64-
cost of compile time and binary size. [MIR
65-
optimizations](../mir/optimizations.md) can help a bit with this. Another
66-
optimization currently under development is called _polymorphization_.
67-
68-
The general idea is that often we can share some code between monomorphized
69-
copies of code. More precisely, if a MIR block is not dependent on a type
70-
parameter, it may not need to be monomorphized into many copies. Consider the
71-
following example:
64+
cost of compile time and binary size. [MIR optimizations][miropt] can help a
65+
bit with this.
66+
67+
In addition to MIR optimizations, rustc attempts to determine when fewer
68+
copies of functions are necessary and avoid making those copies - known
69+
as "polymorphization". When a function-like item is found during
70+
monomorphization collection, the
71+
[`rustc_mir::monomorphize::polymorphize::unused_generic_params`][polymorph]
72+
query is invoked, which traverses the MIR of the item to determine on which
73+
generic parameters the item might not need duplicated.
74+
75+
Currently, polymorphization only looks for unused generic parameters. These
76+
are relatively rare in functions, but closures inherit the generic
77+
parameters of their parent function and it is common for closures to not
78+
use those inherited parameters. Without polymorphization, a copy of these
79+
closures would be created for each copy of the parent function. By
80+
creating fewer copies, less LLVM IR is generated and needs processed.
81+
82+
`unused_generic_params` returns a `FiniteBitSet<u64>` where a bit is set if
83+
the generic parameter of the corresponding index is unused. Any parameters
84+
after the first sixty-four are considered used.
85+
86+
The results of polymorphization analysis are used in the
87+
[`Instance::polymorphize`][inst_polymorph] function to replace the
88+
[`Instance`][inst]'s substitutions for the unused generic parameters with their
89+
identity substitutions.
90+
91+
Consider the example below:
7292

7393
```rust
74-
pub fn f() {
75-
g::<bool>();
76-
g::<usize>();
94+
fn foo<A, B>() {
95+
let x: Option<B> = None;
7796
}
7897

79-
fn g<T>() -> usize {
80-
let n = 1;
81-
let closure = || n;
82-
closure()
98+
fn main() {
99+
foo::<u16, u32>();
100+
foo::<u64, u32>();
83101
}
84102
```
85103

86-
In this case, we would currently collect `[f, g::<bool>, g::<usize>,
87-
g::<bool>::{{closure}}, g::<usize>::{{closure}}]`, but notice that the two
88-
closures would be identical -- they don't depend on the type parameter `T` of
89-
function `g`. So we only need to emit one copy of the closure.
104+
During monomorphization collection, `foo` will be collected with the
105+
substitutions `[u16, u32]` and `[u64, u32]` (from its invocations in `main`).
106+
`foo` has the identity substitutions `[A, B]` (or
107+
`[ty::Param(0), ty::Param(1)]`).
108+
109+
Polymorphization will identify `A` as being unused and it will be replaced in
110+
the substitutions with the identity parameter before being added to the set
111+
of collected items - thereby reducing the copies from two (`[u16, u32]` and
112+
`[u64, u32]`) to one (`[A, u32]`).
113+
114+
`unused_generic_params` will also invoked during code generation when the
115+
symbol name for `foo` is being computed for use in the callsites of `foo`
116+
(which have the regular substitutions present, otherwise there would be a
117+
symbol mismatch between the caller and the function).
118+
119+
As a result of polymorphization, items collected during monomorphization
120+
cannot be assumed to be monomorphic.
121+
122+
It is intended that polymorphization be extended to more advanced cases,
123+
such as where only the size/alignment of a generic parameter are required.
90124

91-
For more information, see [this thread on github][polymorph].
125+
More details on polymorphization are available in the
126+
[master's thesis][thesis] associated with polymorphization's initial
127+
implementation.
92128

93-
[polymorph]: https://github.com/rust-lang/rust/issues/46477
129+
[miropt]: ../mir/optimizations.md
130+
[polymorph]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir/monomorphize/polymorphize/fn.unused_generic_params.html
131+
[inst]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/instance/struct.Instance.html
132+
[inst_polymorph]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/instance/struct.Instance.html#method.polymorphize
133+
[thesis]: https://davidtw.co/media/masters_dissertation.pdf

0 commit comments

Comments
 (0)