Skip to content

Commit 83f0ba4

Browse files
committed
update backend chapters from nagisa's notes
1 parent 1f4d071 commit 83f0ba4

File tree

5 files changed

+151
-32
lines changed

5 files changed

+151
-32
lines changed

src/appendix/glossary.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ AST | the abstract syntax tree produced by the `rustc_ast`
1111
binder | a "binder" is a place where a variable or type is declared; for example, the `<T>` is a binder for the generic type parameter `T` in `fn foo<T>(..)`, and \|`a`\|` ...` is a binder for the parameter `a`. See [the background chapter for more](./background.html#free-vs-bound)
1212
bound variable | a "bound variable" is one that is declared within an expression/term. For example, the variable `a` is bound within the closure expression \|`a`\|` a * 2`. See [the background chapter for more](./background.html#free-vs-bound)
1313
codegen | the code to translate MIR into LLVM IR.
14-
codegen unit | when we produce LLVM IR, we group the Rust code into a number of codegen units (sometimes abbreviated as CGUs). Each of these units is processed by LLVM independently from one another, enabling parallelism. They are also the unit of incremental re-use.
14+
codegen unit | when we produce LLVM IR, we group the Rust code into a number of codegen units (sometimes abbreviated as CGUs). Each of these units is processed by LLVM independently from one another, enabling parallelism. They are also the unit of incremental re-use. ([see more](../backend/codegen.md))
1515
completeness | completeness is a technical term in type theory. Completeness means that every type-safe program also type-checks. Having both soundness and completeness is very hard, and usually soundness is more important. (see "soundness").
1616
control-flow graph | a representation of the control-flow of a program; see [the background chapter for more](./background.html#cfg)
1717
CTFE | Compile-Time Function Evaluation. This is the ability of the compiler to evaluate `const fn`s at compile time. This is part of the compiler's constant evaluation system. ([see more](../const-eval.html))

src/backend/backend.md

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,50 @@
11
# The Compiler Backend
22

3-
The _compiler backend_ refers to the parts of the compiler that turn rustc's
4-
MIR into actual executable code (e.g. an ELF or EXE binary) that can run on a
5-
processor. This is the last stage of compilation, and it has a few important
6-
parts:
3+
All of the preceding chapters of this guide have one thing in common: we never
4+
generated any executable machine code at all! With this chapter, all of that
5+
changes.
6+
7+
It's often useful to think of compilers as being composed of a _frontend_ and a
8+
_backend_ (though in rustc, there's not a sharp line between frontend and
9+
backend). The _frontend_ is responsible for taking raw source code, checking it
10+
for correctness, and getting it into a format usable by the backend. For rustc,
11+
this format is the MIR. The _backend_ refers to the parts of the compiler that
12+
turn rustc's MIR into actual executable code (e.g. an ELF or EXE binary) that
13+
can run on a processor. All of the previous chapters deal with rustc's
14+
frontend.
15+
16+
rustc's backend does the following:
717

818
0. First, we need to collect the set of things to generate code for. In
919
particular, we need to find out which concrete types to substitute for
1020
generic ones, since we need to generate code for the concrete types.
1121
Generating code for the concrete types (i.e. emitting a copy of the code for
1222
each concrete type) is called _monomorphization_, so the process of
1323
collecting all the concrete types is called _monomorphization collection_.
14-
1. Next, we need to actually lower the MIR (which is generic) to a codegen IR
15-
(usually LLVM IR; which is not generic) for each concrete type we collected.
16-
2. Finally, we need to invoke LLVM, which runs a bunch of optimization passes,
17-
generates executable code, and links together an executable binary.
24+
1. Next, we need to actually lower the MIR to a codegen IR
25+
(usually LLVM IR) for each concrete type we collected.
26+
2. Finally, we need to invoke the codegen backend (e.g. LLVM or Cranelift),
27+
which runs a bunch of optimization passes, generates executable code, and
28+
links together an executable binary.
29+
30+
[codegen1]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_ssa/base/fn.codegen_crate.html
31+
32+
The code for codegen is actually a bit complex due to a few factors:
33+
34+
- Support for multiple backends (LLVM and Cranelift). We try to share as much
35+
backend code between them as possible, so a lot of it is generic over the
36+
codegen implementation. This means that there are often a lot of layers of
37+
abstraction.
38+
- Codegen happens asynchronously in another thread for performance.
39+
- The actual codegen is done by a third-party library (either LLVM or Cranelift).
40+
41+
Generally, the [`rustc_codegen_ssa`][ssa] crate contains backend-agnastic code
42+
(i.e. independent of LLVM or Cranelift), while the [`rustc_codegen_llvm`][llvm]
43+
crate contains code specific to LLVM codegen.
44+
45+
[ssa]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_ssa/index.html
46+
[llvm]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_llvm/index.html
47+
48+
At a very high level, the entry point is
49+
[`rustc_codegen_ssa::base::codegen_crate`][codegen1]. This function starts the
50+
process discussed in the rest of this chapter.

src/backend/codegen.md

Lines changed: 44 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
# Code generation
22

33
Code generation or "codegen" is the part of the compiler that actually
4-
generates an executable binary. rustc uses LLVM for code generation.
4+
generates an executable binary. Usually, rustc uses LLVM for code generation;
5+
there is also support for [Cranelift]. The key is that rustc doesn't implement
6+
codegen itself. It's worth noting, though, that in the rust source code, many
7+
parts of the backend have `codegen` in their names (there are no hard
8+
boundaries).
9+
10+
[Cranelift]: https://github.com/bytecodealliance/wasmtime/tree/master/cranelift
511

612
> NOTE: If you are looking for hints on how to debug code generation bugs,
713
> please see [this section of the debugging chapter][debugging].
@@ -10,28 +16,16 @@ generates an executable binary. rustc uses LLVM for code generation.
1016

1117
## What is LLVM?
1218

13-
All of the preceding chapters of this guide have one thing in common: we never
14-
generated any executable machine code at all! With this chapter, all of that
15-
changes.
16-
17-
Like most compilers, rustc is composed of a "frontend" and a "backend". The
18-
"frontend" is responsible for taking raw source code, checking it for
19-
correctness, and getting it into a format `X` from which we can generate
20-
executable machine code. The "backend" then takes that format `X` and produces
21-
(possibly optimized) executable machine code for some platform. All of the
22-
previous chapters deal with rustc's frontend.
19+
[LLVM](https://llvm.org) is "a collection of modular and reusable compiler and
20+
toolchain technologies". In particular, the LLVM project contains a pluggable
21+
compiler backend (also called "LLVM"), which is used by many compiler projects,
22+
including the `clang` C compiler and our beloved `rustc`.
2323

24-
rustc's backend is [LLVM](https://llvm.org), "a collection of modular and
25-
reusable compiler and toolchain technologies". In particular, the LLVM project
26-
contains a pluggable compiler backend (also called "LLVM"), which is used by
27-
many compiler projects, including the `clang` C compiler and our beloved
28-
`rustc`.
29-
30-
LLVM's "format `X`" is called LLVM IR. It is basically assembly code with
24+
LLVM takes input in the form of LLVM IR. It is basically assembly code with
3125
additional low-level types and annotations added. These annotations are helpful
3226
for doing optimizations on the LLVM IR and outputted machine code. The end
33-
result of all this is (at long last) something executable (e.g. an ELF object
34-
or wasm).
27+
result of all this is (at long last) something executable (e.g. an ELF object,
28+
an EXE, or wasm).
3529

3630
There are a few benefits to using LLVM:
3731

@@ -49,6 +43,34 @@ There are a few benefits to using LLVM:
4943

5044
[spectre]: https://meltdownattack.com/
5145

52-
## Generating LLVM IR
46+
## Running LLVM, linking, and metadata generation
47+
48+
Once LLVM IR for all of the functions and statics, etc is built, it is time to
49+
start running LLVM and its optimisation passes. LLVM IR is grouped into
50+
"modules". Multiple "modules" can be codegened at the same time to aid in
51+
multi-core utilisation. These "modules" are what we refer to as _codegen
52+
units_. These units were established way back during monomorphisation
53+
collection phase.
54+
55+
Once LLVM produces objects from these modules, these objects are passed to the
56+
linker along with, optionally, the metadata object and an archive or an
57+
executable is produced.
58+
59+
It is not necessarily the codegen phase described above that runs the
60+
optimisations. With certain kinds of LTO, the optimisation might happen at the
61+
linking time instead. It is also possible for some optimisations to happen
62+
before objects are passed on to the linker and some to happen during the
63+
linking.
64+
65+
This all happens towards the very end of compilation. The code for this can be
66+
found in [`librustc_codegen_ssa::back`][ssaback] and
67+
[`librustc_codegen_llvm::back`][llvmback]. Sadly, this piece of code is not
68+
really well-separated into LLVM-dependent code; the [`rustc_codegen_ssa`][ssa]
69+
contains a fair amount of code specific to the LLVM backend.
70+
71+
Once these components are done with their work you end up with a number of
72+
files in your filesystem corresponding to the outputs you have requested.
5373

54-
TODO
74+
[ssa]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_ssa/index.html
75+
[ssaback]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_ssa/back/index.html
76+
[llvmback]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_llvm/back/index.html

src/backend/lowering-mir.md

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,58 @@
11
# Lowering MIR to a Codegen IR
22

3-
TODO
3+
Now that we have a list of symbols to generate from the collector, we need to
4+
generate some sort of codegen IR. In this chapter, we will assume LLVM IR,
5+
since that's what rustc usually uses. The actual monomorphisation is performed
6+
as we go, while we do the translation.
7+
8+
Recall that the backend is started by
9+
[`rustc_codegen_ssa::base::codegen_crate`][codegen1]. Eventually, this reaches
10+
[`rustc_codegen_ssa::mir::codegen_mir`][codegen2], which does the lowering from
11+
MIR to LLVM IR.
12+
13+
[codegen1]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_ssa/base/fn.codegen_crate.html
14+
[codegen2]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_ssa/mir/fn.codegen_mir.html
15+
16+
The code is split into modules which handle particular MIR primitives:
17+
18+
- [`librustc_codegen_ssa::mir::block`][mirblk] will deal with translating
19+
blocks and their terminators. The most complicated and also the most
20+
interesting thing this module does is generating code for function calls,
21+
including the necessary unwinding handling IR.
22+
- [`librustc_codegen_ssa::mir::statement`][mirst] translates MIR statements.
23+
- [`librustc_codegen_ssa::mir::operand`][mirop] translates MIR operands.
24+
- [`librustc_codegen_ssa::mir::place`][mirpl] translates MIR place references.
25+
- [`librustc_codegen_ssa::mir::rvalue`][mirrv] translates MIR r-values.
26+
27+
[mirblk]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_ssa/mir/block/index.html
28+
[mirst]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_ssa/mir/statement/index.html
29+
[mirop]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_ssa/mir/operand/index.html
30+
[mirpl]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_ssa/mir/place/index.html
31+
[mirrv]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_ssa/mir/rvalue/index.html
32+
33+
Before a function is translated a number of simple and primitive analysis
34+
passes will run to help us generate simpler and more efficient LLVM IR. An
35+
example of such an analysis pass would be figuring out which variables are
36+
SSA-like, so that we can translate them to SSA directly rather than relying on
37+
LLVM's `mem2reg` for those variables. The anayses can be found in
38+
[`rustc_codegen_ssa::mir::analyze`][mirana].
39+
40+
[mirana]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_ssa/mir/analyze/index.html
41+
42+
Usually a single MIR basic block will map to a LLVM basic block, with very few
43+
exceptions: intrinsic or function calls and less basic MIR statemenets like
44+
`assert` can result in multiple basic blocks. This is a perfect lede into the
45+
non-portable LLVM-specific part of the code generation. Intrinsic generation is
46+
fairly easy to understand as it involves very few abstraction levels in between
47+
and can be found in [`rustc_codegen_llvm::intrinsic`][llvmint].
48+
49+
[llvmint]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_llvm/intrinsic/index.html
50+
51+
Everything else will use the [builder interface][builder], this is the code that gets
52+
called in [`librustc_codegen_ssa::mir::*`][ssamir] modules that was discussed
53+
above.
54+
55+
[builder]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_llvm/builder/index.html
56+
[ssamir]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_ssa/mir/index.html
57+
58+
> TODO: discuss how constants are generated

src/backend/monomorph.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,15 @@ See [the collector rustdocs][collect] for more info.
4949

5050
[collect]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir/monomorphize/collector/index.html
5151

52+
The monomorphisation collector is run just before MIR lowering and codegen.
53+
[`rustc_codegen_ssa::base::codegen_crate`][codegen1] calls the
54+
[`collect_and_partition_mono_items`][mono] query, which does monomorphisation
55+
collection and then partitions them into [codegen
56+
units](../appendix/glossary.md).
57+
58+
[mono]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir/monomorphize/partitioning/fn.collect_and_partition_mono_items.html
59+
[codegen1]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_ssa/base/fn.codegen_crate.html
60+
5261
## Polymorphization
5362

5463
As mentioned above, monomorphisation produces fast code, but it comes at the

0 commit comments

Comments
 (0)