Skip to content

Commit 3612192

Browse files
committed
Add some documentation for const eval and related topics
1 parent 45db49d commit 3612192

File tree

6 files changed

+99
-1
lines changed

6 files changed

+99
-1
lines changed

src/SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,6 @@
1919
- [MIR construction](./mir-construction.md)
2020
- [MIR borrowck](./mir-borrowck.md)
2121
- [MIR optimizations](./mir-optimizations.md)
22+
- [Constant evaluation](./const-eval.md)
2223
- [Generating LLVM IR](./trans.md)
2324
- [Glossary](./glossary.md)

src/const-eval.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Constant Evaluation
2+
3+
Constant evaluation is the process of computing values at compile time.
4+
Prominent examples are
5+
6+
* Array length
7+
* needs to be known to reserve stack or heap space
8+
* Enum variant discriminants
9+
* needs to be known to prevent two variants from having the same discriminant
10+
* Patterns
11+
* need to be known to check for overlapping patterns
12+
13+
Additionally constant evaluation can be used to reduce the workload or binary
14+
size at runtime by precomputing complex operations at compiletime and only
15+
storing the result.
16+
17+
Constant evaluation can be done by calling the `const_eval` query of `TyCtxt`.
18+
19+
The `const_eval` query takes a [`ParamEnv`](./param_env.md) of environment in
20+
which the constant is evaluated (e.g. the function within which the constant is
21+
used) and a `GlobalId`. The `GlobalId` is made up of an
22+
`Instance` referring to a constant or static or of an
23+
`Instance` of a function and an index into the function's `Promoted` table.
24+
25+
Constant evaluation returns a `Result` with either the error, or the simplest
26+
representation of the constant. "simplest" meaning if it is representable as an
27+
integer or fat pointer, it will directly yield the value (via `Value::ByVal` or
28+
`Value::ByValPair`), instead of referring to the [`miri`](./miri.md) virtual
29+
memory allocation (via `Value::ByRef`). This means that the `const_eval`
30+
function cannot be used to create miri-pointers to the evaluated constant or
31+
static. If you need that, you need to directly work with the functions in
32+
`src/librustc_mir/interpret/const_eval.rs`.

src/glossary.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ ICE | internal compiler error. When the compiler crashes.
1717
ICH | incremental compilation hash. ICHs are used as fingerprints for things such as HIR and crate metadata, to check if changes have been made. This is useful in incremental compilation to see if part of a crate has changed and should be recompiled.
1818
infcx | the inference context (see `librustc/infer`)
1919
MIR | the Mid-level IR that is created after type-checking for use by borrowck and trans ([see more](./mir.html))
20+
miri | an interpreter for MIR used for constant evaluation ([see more](./miri.html))
2021
obligation | something that must be proven by the trait system ([see more](trait-resolution.html))
2122
local crate | the crate currently being compiled.
2223
node-id or NodeId | an index identifying a particular node in the AST or HIR; gradually being phased out and replaced with `HirId`.

src/miri.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# Miri
2+
3+
Miri (**MIR** **I**nterpreter) is a virtual machine for executing MIR without
4+
compiling to machine code. It is usually invoked via `tcx.const_eval`.
5+
6+
Its core datastructures can be found in `src/librustc/mir/interpret`. This is
7+
mainly the error enum and the `Value` and `PrimVal` types. A `Value` can be
8+
either `ByVal` (a single `PrimVal`), `ByValPair` (two `PrimVal`s, usually fat
9+
pointers or two element tuples) or `ByRef`, which is used for anything else and
10+
refers to a virtual allocation. These allocations can be accessed via the
11+
methods on `tcx.interpret_interner`.
12+
13+
If you are expecting a numeric result, you can use `unwrap_u64` (panics on
14+
anything that can't be representad as a `u64`) or `to_raw_bits` which results
15+
in an `Option<u128>` yielding the `ByVal` if possible.
16+
17+
## Miri allocations
18+
19+
A miri allocation is either a byte sequence of the memory or an `Instance` in
20+
the case of function pointers. Byte sequences can additionally contain
21+
relocations that mark a group of bytes as a pointer to another allocation. The
22+
actual bytes at the relocation refer to the offset inside the other allocation.
23+
24+
## Miri interpretation
25+
26+
Although the main entry point to constant evaluation is the `tcx.const_eval`
27+
query, there are additional functions in `src/librustc_mir/interpret/const_eval`
28+
that allow accessing the fields of a `Value` (`ByRef` or otherwise). You should
29+
never have to access an `Allocation` directly except for translating it to the
30+
compilation target (atm just LLVM).
31+
32+
### Details
33+
34+
Miri starts by allocating a virtual stack frame for the current constant that
35+
is being evaluated. There's essentially no difference between a constant and a
36+
function with no arguments, except that constants do not allow local (named)
37+
variables at the time of writing this guide.
38+
39+
A stack frame is defined by the `Frame` type in
40+
`src/librustc_mir/interpret/eval_context.rs` and contains all the local
41+
variables memory (`None` at the start of evaluation).
42+
43+
Miri now calls the `step` method (in `src/librustc_mir/interpret/step.rs`) until
44+
it either returns an error or has no further statements to execute. Each
45+
statement will now initialize or modify the locals or the virtual memory
46+
referred to by a local. This might require evaluating other constants or
47+
statics, which just recursively invokes `tcx.const_eval`.

src/param_env.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Parameter Environment
2+
3+
When working with associated constants or generic types it is often relevant to
4+
have more information about the `Self` or generic parameters. Trait bounds and
5+
similar information is encoded in the `ParamEnv`. Often this is not enough
6+
information to obtain things like the type's `Layout`, but you can do all kinds
7+
of other checks on it (e.g. whether a type implements `Copy`) or you can
8+
evaluate an associated constant whose value does not depend on anything from the
9+
parameter environment.
10+
11+
Although you can obtain a valid `ParamEnv` for any item via
12+
`tcx.param_env(def_id)`, this `ParamEnv` can be too generic for your use case.
13+
Using the `ParamEnv` from the surrounding context can allow you to evaluate more
14+
things.
15+
16+
Another great thing about `ParamEnv` is that you can use it to eliminate the
17+
generic parameters from a `Ty` by calling `param_env.and(ty)`.

src/trait-resolution.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -433,7 +433,7 @@ before, and hence the cache lookup would succeed, yielding
433433
One subtle interaction is that the results of trait lookup will vary
434434
depending on what where clauses are in scope. Therefore, we actually
435435
have *two* caches, a local and a global cache. The local cache is
436-
attached to the `ParamEnv` and the global cache attached to the
436+
attached to the [`ParamEnv`](./param_env.md) and the global cache attached to the
437437
`tcx`. We use the local cache whenever the result might depend on the
438438
where clauses that are in scope. The determination of which cache to
439439
use is done by the method `pick_candidate_cache` in `select.rs`. At

0 commit comments

Comments
 (0)