Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update docs related to const-eval/Miri #676

Merged
merged 6 commits into from
May 25, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 24 additions & 17 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,26 @@ 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`.

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.

Constant evaluation returns a `Result` 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
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).
Constant evaluation can be done by calling the `const_eval_*` functions of `TyCtxt`.
They're the wrappers of the `const_eval` query.

The `const_eval_*` functions 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 [`ConstEvalResult`] with either the error, or the a
representation of the constant. `static` initializers are always represented as
[`miri`](./miri.html) virtual memory allocations (via [`ConstValue::ByRef`]).
Other constants get represented as [`ConstValue::Scalar`]
or [`ConstValue::Slice`] if possible. This means that the `const_eval_*`
functions cannot be used to create miri-pointers to the evaluated constant.
If you need the value of a constant inside Miri, you need to directly work with
[`eval_const_to_op`].

[`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
[`eval_const_to_op`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir/interpret/struct.InterpCx.html#method.eval_const_to_op
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_*` functions.

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_*`
functions, 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_*`.