107 lines
5.7 KiB
Markdown
107 lines
5.7 KiB
Markdown
# Memory Management in Rustc
|
||
|
||
Generally rustc tries to be pretty careful how it manages memory.
|
||
The compiler allocates _a lot_ of data structures throughout compilation,
|
||
and if we are not careful, it will take a lot of time and space to do so.
|
||
|
||
One of the main way the compiler manages this is using [arena]s and [interning].
|
||
|
||
[arena]: https://en.wikipedia.org/wiki/Region-based_memory_management
|
||
[interning]: https://en.wikipedia.org/wiki/String_interning
|
||
|
||
## Arenas and Interning
|
||
|
||
Since A LOT of data structures are created during compilation, for performance
|
||
reasons, we allocate them from a global memory pool.
|
||
Each are allocated once from a long-lived *arena*.
|
||
This is called _arena allocation_.
|
||
This system reduces allocations/deallocations of memory.
|
||
It also allows for easy comparison of types (more on types [here](./ty.md)) for equality:
|
||
for each interned type `X`, we implemented [`PartialEq` for X][peqimpl],
|
||
so we can just compare pointers.
|
||
The [`CtxtInterners`] type contains a bunch of maps of interned types and the arena itself.
|
||
|
||
[`CtxtInterners`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.CtxtInterners.html#structfield.arena
|
||
[peqimpl]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Ty.html#implementations
|
||
|
||
### Example: `ty::TyKind`
|
||
|
||
Taking the example of [`ty::TyKind`] which represents a type in the compiler (you
|
||
can read more [here](./ty.md)). Each time we want to construct a type, the
|
||
compiler doesn’t naively allocate from the buffer. Instead, we check if that
|
||
type was already constructed. If it was, we just get the same pointer we had
|
||
before, otherwise we make a fresh pointer. With this schema if we want to know
|
||
if two types are the same, all we need to do is compare the pointers which is
|
||
efficient. [`ty::TyKind`] should never be constructed on the stack, and it would be unusable
|
||
if done so.
|
||
You always allocate them from this arena and you always intern them so they are
|
||
unique.
|
||
|
||
At the beginning of the compilation we make a buffer and each time we need to allocate a type we use
|
||
some of this memory buffer. If we run out of space we get another one. The lifetime of that buffer
|
||
is `'tcx`. Our types are tied to that lifetime, so when compilation finishes all the memory related
|
||
to that buffer is freed and our `'tcx` references would be invalid.
|
||
|
||
In addition to types, there are a number of other arena-allocated data structures that you can
|
||
allocate, and which are found in this module. Here are a few examples:
|
||
|
||
- [`GenericArgs`], allocated with [`mk_args`] – this will intern a slice of types, often used
|
||
to specify the values to be substituted for generics args (e.g. `HashMap<i32, u32>` would be
|
||
represented as a slice `&'tcx [tcx.types.i32, tcx.types.u32]`).
|
||
- [`TraitRef`], typically passed by value – a **trait reference** consists of a reference to a trait
|
||
along with its various type parameters (including `Self`), like `i32: Display` (here, the def-id
|
||
would reference the `Display` trait, and the args would contain `i32`). Note that `def-id` is
|
||
defined and discussed in depth in the [`AdtDef and DefId`][adtdefid] section.
|
||
- [`Predicate`] defines something the trait system has to prove (see [traits] module).
|
||
|
||
[`GenericArgs`]: ./ty_module/generic_arguments.md#the-genericargs-type
|
||
[adtdefid]: ./ty_module/generic_arguments.md#adtdef-and-defid
|
||
[`TraitRef`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/type.TraitRef.html
|
||
[`AdtDef` and `DefId`]: ./ty.md#adts-representation
|
||
[`def-id`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/def_id/struct.DefId.html
|
||
[`GenericArgs`]: ./generic_arguments.html#GenericArgs
|
||
[`mk_args`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TyCtxt.html#method.mk_args
|
||
[adtdefid]: ./ty_module/generic_arguments.md#adtdef-and-defid
|
||
[`Predicate`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Predicate.html
|
||
[`TraitRef`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TraitRef.html
|
||
[`ty::TyKind`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/sty/type.TyKind.html
|
||
[traits]: ./traits/resolution.md
|
||
|
||
## The `tcx` and how it uses lifetimes
|
||
|
||
The typing context (`tcx`) is the central data structure in the compiler. It is the context that
|
||
you use to perform all manner of queries. The `struct` [`TyCtxt`] defines a reference to this shared
|
||
context:
|
||
|
||
```rust,ignore
|
||
tcx: TyCtxt<'tcx>
|
||
// ----
|
||
// |
|
||
// arena lifetime
|
||
```
|
||
|
||
As you can see, the `TyCtxt` type takes a lifetime parameter. When you see a reference with a
|
||
lifetime like `'tcx`, you know that it refers to arena-allocated data (or data that lives as long as
|
||
the arenas, anyhow).
|
||
|
||
[`TyCtxt`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TyCtxt.html
|
||
|
||
### A Note On Lifetimes
|
||
|
||
The Rust compiler is a fairly large program containing lots of big data
|
||
structures (e.g. the [Abstract Syntax Tree (AST)][ast], [High-Level Intermediate
|
||
Representation (`HIR`)][hir], and the type system) and as such, arenas and
|
||
references are heavily relied upon to minimize unnecessary memory use. This
|
||
manifests itself in the way people can plug into the compiler (i.e. the
|
||
[driver](./rustc-driver/intro.md)), preferring a "push"-style API (callbacks) instead
|
||
of the more Rust-ic "pull" style (think the `Iterator` trait).
|
||
|
||
Thread-local storage and interning are used a lot through the compiler to reduce
|
||
duplication while also preventing a lot of the ergonomic issues due to many
|
||
pervasive lifetimes. The [`rustc_middle::ty::tls`][tls] module is used to access these
|
||
thread-locals, although you should rarely need to touch it.
|
||
|
||
[ast]: ./ast-validation.md
|
||
[hir]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/index.html
|
||
[tls]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/tls/index.html
|