Skip to content

Commit

Permalink
Update docs related to const-eval
Browse files Browse the repository at this point in the history
  • Loading branch information
JohnTitor committed Apr 17, 2020
1 parent d7b4c7e commit 752c917
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 28 deletions.
32 changes: 19 additions & 13 deletions src/const-eval.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ specific item (constant/static/array length) this happens after the MIR for the
item is borrow-checked and optimized. In many cases trying to const evaluate an
item will trigger the computation of its MIR for the first time.

Prominent examples are
Prominent examples are:

* The initializer of a `static`
* Array length
Expand All @@ -20,19 +20,25 @@ Additionally constant evaluation can be used to reduce the workload or binary
size at runtime by precomputing complex operations at compiletime and only
storing the result.

Constant evaluation can be done by calling the `const_eval` query of `TyCtxt`.
Constant evaluation can be done by calling the `const_eval_*` queries of `TyCtxt`.

The `const_eval` query takes a [`ParamEnv`](./param_env.html) of environment in
which the constant is evaluated (e.g. the function within which the constant is
used) and a `GlobalId`. The `GlobalId` is made up of an
`Instance` referring to a constant or static or of an
`Instance` of a function and an index into the function's `Promoted` table.
The `const_eval_*` queries use a [`ParamEnv`](./param_env.html) of environment
in which the constant is evaluated (e.g. the function within which the constant is used)
and a [`GlobalId`]. The `GlobalId` is made up of an `Instance` referring to a constant
or static or of an `Instance` of a function and an index into the function's `Promoted` table.

Constant evaluation returns a `Result` with either the error, or the simplest
Constant evaluation returns a [`ConstEvalResult`] with either the error, or the simplest
representation of the constant. "simplest" meaning if it is representable as an
integer or fat pointer, it will directly yield the value (via `ConstValue::Scalar` or
`ConstValue::ScalarPair`), instead of referring to the [`miri`](./miri.html) virtual
memory allocation (via `ConstValue::ByRef`). This means that the `const_eval`
function cannot be used to create miri-pointers to the evaluated constant or
integer or fat pointer, it will directly yield the value (via [`ConstValue::Scalar`]
or [`ConstValue::Slice`]), instead of referring to the [`miri`](./miri.html) virtual
memory allocation (via [`ConstValue::ByRef`]). This means that the `const_eval_*`
functions cannot be used to create miri-pointers to the evaluated constant or
static. If you need that, you need to directly work with the functions in
[src/librustc_mir/const_eval.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir/const_eval/index.html).
[`src/librustc_mir/const_eval.rs`].

[`GlobalId`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/mir/interpret/struct.GlobalId.html
[`ConstValue::Scalar`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/mir/interpret/value/enum.ConstValue.html#variant.Scalar
[`ConstValue::Slice`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/mir/interpret/value/enum.ConstValue.html#variant.Slice
[`ConstValue::ByRef`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/mir/interpret/value/enum.ConstValue.html#variant.ByRef
[`ConstEvalResult`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/mir/interpret/error/type.ConstEvalResult.html
[`src/librustc_mir/const_eval.rs`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir/const_eval/index.html
30 changes: 15 additions & 15 deletions src/miri.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# Miri

Miri (**MIR** **I**nterpreter) is a virtual machine for executing MIR without
compiling to machine code. It is usually invoked via `tcx.const_eval`.
compiling to machine code. It is usually invoked via `tcx.const_eval_*` queries.

If you start out with a constant
If you start out with a constant:

```rust
const FOO: usize = 1 << 12;
Expand All @@ -12,7 +12,7 @@ const FOO: usize = 1 << 12;
rustc doesn't actually invoke anything until the constant is either used or
placed into metadata.

Once you have a use-site like
Once you have a use-site like:

```rust,ignore
type Foo = [u8; FOO - 42];
Expand All @@ -35,17 +35,17 @@ Invoking `tcx.const_eval(param_env.and(gid))` will now trigger the creation of
the MIR of the array length expression. The MIR will look something like this:

```mir
const Foo::{{initializer}}: usize = {
let mut _0: usize; // return pointer
Foo::{{constant}}#0: usize = {
let mut _0: usize;
let mut _1: (usize, bool);

bb0: {
_1 = CheckedSub(const Unevaluated(FOO, Slice([])), const 42usize);
assert(!(_1.1: bool), "attempt to subtract with overflow") -> bb1;
_1 = CheckedSub(const FOO, const 42usize);
assert(!move (_1.1: bool), "attempt to subtract with overflow") -> bb1;
}

bb1: {
_0 = (_1.0: usize);
_0 = move (_1.0: usize);
return;
}
}
Expand All @@ -55,16 +55,16 @@ Before the evaluation, a virtual memory location (in this case essentially a
`vec![u8; 4]` or `vec![u8; 8]`) is created for storing the evaluation result.

At the start of the evaluation, `_0` and `_1` are
`Operand::Immediate(Immediate::Scalar(ScalarMaybeUndef::Undef))`. This is quite
`Operand::Immediate(Immediate::Scalar(ScalarMaybeUndef::Undef))`. This is quite
a mouthful: [`Operand`] can represent either data stored somewhere in the
[interpreter memory](#memory) (`Operand::Indirect`), or (as an optimization)
immediate data stored in-line. And [`Immediate`] can either be a single
(potentially uninitialized) [scalar value][`Scalar`] (integer or thin pointer),
or a pair of two of them. In our case, the single scalar value is *not* (yet)
or a pair of two of them. In our case, the single scalar value is *not* (yet)
initialized.

When the initialization of `_1` is invoked, the value of the `FOO` constant is
required, and triggers another call to `tcx.const_eval`, which will not be shown
required, and triggers another call to `tcx.const_eval_*`, which will not be shown
here. If the evaluation of FOO is successful, `42` will be subtracted from its
value `4096` and the result stored in `_1` as
`Operand::Immediate(Immediate::ScalarPair(Scalar::Raw { data: 4054, .. },
Expand Down Expand Up @@ -200,8 +200,8 @@ division on pointer values.

## Interpretation

Although the main entry point to constant evaluation is the `tcx.const_eval`
query, there are additional functions in
Although the main entry point to constant evaluation is the `tcx.const_eval_*`
queries, there are additional functions in
[librustc_mir/const_eval.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir/const_eval/index.html)
that allow accessing the fields of a `ConstValue` (`ByRef` or otherwise). You should
never have to access an `Allocation` directly except for translating it to the
Expand All @@ -217,7 +217,7 @@ A stack frame is defined by the `Frame` type in
and contains all the local
variables memory (`None` at the start of evaluation). Each frame refers to the
evaluation of either the root constant or subsequent calls to `const fn`. The
evaluation of another constant simply calls `tcx.const_eval`, which produces an
evaluation of another constant simply calls `tcx.const_eval_*`, which produce an
entirely new and independent stack frame.

The frames are just a `Vec<Frame>`, there's no way to actually refer to a
Expand All @@ -229,4 +229,4 @@ Miri now calls the `step` method (in
) until it either returns an error or has no further statements to execute. Each
statement will now initialize or modify the locals or the virtual memory
referred to by a local. This might require evaluating other constants or
statics, which just recursively invokes `tcx.const_eval`.
statics, which just recursively invokes `tcx.const_eval_*`.

0 comments on commit 752c917

Please sign in to comment.