Update docs related to const-eval/Miri (#676)
* Update docs related to const-eval Co-authored-by: Ralf Jung <post@ralfj.de>
This commit is contained in:
parent
c777d434f5
commit
85d815d11d
|
|
@ -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
|
||||
|
|
@ -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`.
|
||||
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` 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_*` 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 `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 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
src/miri.md
30
src/miri.md
|
|
@ -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;
|
||||
|
|
@ -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];
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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, .. },
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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_*`.
|
||||
|
|
|
|||
Loading…
Reference in New Issue