]>
git.proxmox.com Git - rustc.git/blob - src/librustc_mir/interpret/machine.rs
1 //! This module contains everything needed to instantiate an interpreter.
2 //! This separation exists to ensure that no fancy miri features like
3 //! interpreting common C functions leak into CTFE.
5 use std
::borrow
::{Borrow, Cow}
;
9 use rustc_middle
::ty
::{self, Ty}
;
10 use rustc_span
::def_id
::DefId
;
13 AllocId
, Allocation
, AllocationExtra
, Frame
, ImmTy
, InterpCx
, InterpResult
, Memory
, MemoryKind
,
14 OpTy
, Operand
, PlaceTy
, Pointer
, Scalar
,
17 /// Data returned by Machine::stack_pop,
18 /// to provide further control over the popping of the stack frame
19 #[derive(Eq, PartialEq, Debug, Copy, Clone)]
20 pub enum StackPopJump
{
21 /// Indicates that no special handling should be
22 /// done - we'll either return normally or unwind
23 /// based on the terminator for the function
27 /// Indicates that we should *not* jump to the return/unwind address, as the callback already
28 /// took care of everything.
32 /// Whether this kind of memory is allowed to leak
33 pub trait MayLeak
: Copy
{
34 fn may_leak(self) -> bool
;
37 /// The functionality needed by memory to manage its allocations
38 pub trait AllocMap
<K
: Hash
+ Eq
, V
> {
39 /// Tests if the map contains the given key.
40 /// Deliberately takes `&mut` because that is sufficient, and some implementations
41 /// can be more efficient then (using `RefCell::get_mut`).
42 fn contains_key
<Q
: ?Sized
+ Hash
+ Eq
>(&mut self, k
: &Q
) -> bool
46 /// Inserts a new entry into the map.
47 fn insert(&mut self, k
: K
, v
: V
) -> Option
<V
>;
49 /// Removes an entry from the map.
50 fn remove
<Q
: ?Sized
+ Hash
+ Eq
>(&mut self, k
: &Q
) -> Option
<V
>
54 /// Returns data based on the keys and values in the map.
55 fn filter_map_collect
<T
>(&self, f
: impl FnMut(&K
, &V
) -> Option
<T
>) -> Vec
<T
>;
57 /// Returns a reference to entry `k`. If no such entry exists, call
58 /// `vacant` and either forward its error, or add its result to the map
59 /// and return a reference to *that*.
60 fn get_or
<E
>(&self, k
: K
, vacant
: impl FnOnce() -> Result
<V
, E
>) -> Result
<&V
, E
>;
62 /// Returns a mutable reference to entry `k`. If no such entry exists, call
63 /// `vacant` and either forward its error, or add its result to the map
64 /// and return a reference to *that*.
65 fn get_mut_or
<E
>(&mut self, k
: K
, vacant
: impl FnOnce() -> Result
<V
, E
>) -> Result
<&mut V
, E
>;
68 fn get(&self, k
: K
) -> Option
<&V
> {
69 self.get_or(k
, || Err(())).ok()
73 fn get_mut(&mut self, k
: K
) -> Option
<&mut V
> {
74 self.get_mut_or(k
, || Err(())).ok()
78 /// Methods of this trait signifies a point where CTFE evaluation would fail
79 /// and some use case dependent behaviour can instead be applied.
80 pub trait Machine
<'mir
, 'tcx
>: Sized
{
81 /// Additional memory kinds a machine wishes to distinguish from the builtin ones
82 type MemoryKind
: ::std
::fmt
::Debug
+ ::std
::fmt
::Display
+ MayLeak
+ Eq
+ '
static;
84 /// Tag tracked alongside every pointer. This is used to implement "Stacked Borrows"
85 /// <https://www.ralfj.de/blog/2018/08/07/stacked-borrows.html>.
86 /// The `default()` is used for pointers to consts, statics, vtables and functions.
87 type PointerTag
: ::std
::fmt
::Debug
+ Copy
+ Eq
+ Hash
+ '
static;
89 /// Machines can define extra (non-instance) things that represent values of function pointers.
90 /// For example, Miri uses this to return a function pointer from `dlsym`
91 /// that can later be called to execute the right thing.
92 type ExtraFnVal
: ::std
::fmt
::Debug
+ Copy
;
94 /// Extra data stored in every call frame.
97 /// Extra data stored in memory. A reference to this is available when `AllocExtra`
98 /// gets initialized, so you can e.g., have an `Rc` here if there is global state you
99 /// need access to in the `AllocExtra` hooks.
102 /// Extra data stored in every allocation.
103 type AllocExtra
: AllocationExtra
<Self::PointerTag
> + '
static;
105 /// Memory's allocation map
106 type MemoryMap
: AllocMap
<
108 (MemoryKind
<Self::MemoryKind
>, Allocation
<Self::PointerTag
, Self::AllocExtra
>),
112 /// The memory kind to use for copied global memory (held in `tcx`) --
113 /// or None if such memory should not be mutated and thus any such attempt will cause
114 /// a `ModifiedStatic` error to be raised.
115 /// Statics are copied under two circumstances: When they are mutated, and when
116 /// `tag_allocation` (see below) returns an owned allocation
117 /// that is added to the memory so that the work is not done twice.
118 const GLOBAL_KIND
: Option
<Self::MemoryKind
>;
120 /// Whether memory accesses should be alignment-checked.
121 fn enforce_alignment(memory_extra
: &Self::MemoryExtra
) -> bool
;
123 /// Whether to enforce the validity invariant
124 fn enforce_validity(ecx
: &InterpCx
<'mir
, 'tcx
, Self>) -> bool
;
126 /// Entry point to all function calls.
128 /// Returns either the mir to use for the call, or `None` if execution should
129 /// just proceed (which usually means this hook did all the work that the
130 /// called function should usually have done). In the latter case, it is
131 /// this hook's responsibility to advance the instruction pointer!
132 /// (This is to support functions like `__rust_maybe_catch_panic` that neither find a MIR
133 /// nor just jump to `ret`, but instead push their own stack frame.)
134 /// Passing `dest`and `ret` in the same `Option` proved very annoying when only one of them
136 fn find_mir_or_eval_fn(
137 ecx
: &mut InterpCx
<'mir
, 'tcx
, Self>,
138 instance
: ty
::Instance
<'tcx
>,
139 args
: &[OpTy
<'tcx
, Self::PointerTag
>],
140 ret
: Option
<(PlaceTy
<'tcx
, Self::PointerTag
>, mir
::BasicBlock
)>,
141 unwind
: Option
<mir
::BasicBlock
>,
142 ) -> InterpResult
<'tcx
, Option
<&'mir mir
::Body
<'tcx
>>>;
144 /// Execute `fn_val`. It is the hook's responsibility to advance the instruction
145 /// pointer as appropriate.
147 ecx
: &mut InterpCx
<'mir
, 'tcx
, Self>,
148 fn_val
: Self::ExtraFnVal
,
149 args
: &[OpTy
<'tcx
, Self::PointerTag
>],
150 ret
: Option
<(PlaceTy
<'tcx
, Self::PointerTag
>, mir
::BasicBlock
)>,
151 unwind
: Option
<mir
::BasicBlock
>,
152 ) -> InterpResult
<'tcx
>;
154 /// Directly process an intrinsic without pushing a stack frame. It is the hook's
155 /// responsibility to advance the instruction pointer as appropriate.
157 ecx
: &mut InterpCx
<'mir
, 'tcx
, Self>,
158 instance
: ty
::Instance
<'tcx
>,
159 args
: &[OpTy
<'tcx
, Self::PointerTag
>],
160 ret
: Option
<(PlaceTy
<'tcx
, Self::PointerTag
>, mir
::BasicBlock
)>,
161 unwind
: Option
<mir
::BasicBlock
>,
162 ) -> InterpResult
<'tcx
>;
164 /// Called to evaluate `Assert` MIR terminators that trigger a panic.
166 ecx
: &mut InterpCx
<'mir
, 'tcx
, Self>,
167 msg
: &mir
::AssertMessage
<'tcx
>,
168 unwind
: Option
<mir
::BasicBlock
>,
169 ) -> InterpResult
<'tcx
>;
171 /// Called to evaluate `Abort` MIR terminator.
172 fn abort(_ecx
: &mut InterpCx
<'mir
, 'tcx
, Self>) -> InterpResult
<'tcx
, !> {
173 throw_unsup_format
!("aborting execution is not supported")
176 /// Called for all binary operations where the LHS has pointer type.
178 /// Returns a (value, overflowed) pair if the operation succeeded
180 ecx
: &InterpCx
<'mir
, 'tcx
, Self>,
182 left
: ImmTy
<'tcx
, Self::PointerTag
>,
183 right
: ImmTy
<'tcx
, Self::PointerTag
>,
184 ) -> InterpResult
<'tcx
, (Scalar
<Self::PointerTag
>, bool
, Ty
<'tcx
>)>;
186 /// Heap allocations via the `box` keyword.
188 ecx
: &mut InterpCx
<'mir
, 'tcx
, Self>,
189 dest
: PlaceTy
<'tcx
, Self::PointerTag
>,
190 ) -> InterpResult
<'tcx
>;
192 /// Called to read the specified `local` from the `frame`.
195 _ecx
: &InterpCx
<'mir
, 'tcx
, Self>,
196 frame
: &Frame
<'mir
, 'tcx
, Self::PointerTag
, Self::FrameExtra
>,
198 ) -> InterpResult
<'tcx
, Operand
<Self::PointerTag
>> {
199 frame
.locals
[local
].access()
202 /// Called before a basic block terminator is executed.
203 /// You can use this to detect endlessly running programs.
205 fn before_terminator(_ecx
: &mut InterpCx
<'mir
, 'tcx
, Self>) -> InterpResult
<'tcx
> {
209 /// Called before a global allocation is accessed.
210 /// `def_id` is `Some` if this is the "lazy" allocation of a static.
212 fn before_access_global(
213 _memory_extra
: &Self::MemoryExtra
,
215 _allocation
: &Allocation
,
216 _static_def_id
: Option
<DefId
>,
218 ) -> InterpResult
<'tcx
> {
222 /// Called for *every* memory access to determine the real ID of the given allocation.
223 /// This provides a way for the machine to "redirect" certain allocations as it sees fit.
225 /// This is used by Miri to redirect extern statics to real allocations.
227 /// This function must be idempotent.
229 fn canonical_alloc_id(_mem
: &Memory
<'mir
, 'tcx
, Self>, id
: AllocId
) -> AllocId
{
233 /// Called when converting a `ty::Const` to an operand (in
234 /// `eval_const_to_op`).
236 /// Miri uses this callback for creating per thread allocations for thread
237 /// locals. In Rust, one way of creating a thread local is by marking a
238 /// static with `#[thread_local]`. On supported platforms this gets
239 /// translated to a LLVM thread local for which LLVM automatically ensures
240 /// that each thread gets its own copy. Since LLVM automatically handles
241 /// thread locals, the Rust compiler just treats thread local statics as
242 /// regular statics even though accessing a thread local static should be an
243 /// effectful computation that depends on the current thread. The long term
244 /// plan is to change MIR to make accesses to thread locals explicit
245 /// (https://github.com/rust-lang/rust/issues/70685). While the issue 70685
246 /// is not fixed, our current workaround in Miri is to use this function to
247 /// make per-thread copies of thread locals. Please note that we cannot make
248 /// these copies in `canonical_alloc_id` because that is too late: for
249 /// example, if one created a pointer in thread `t1` to a thread local and
250 /// sent it to another thread `t2`, resolving the access in
251 /// `canonical_alloc_id` would result in pointer pointing to `t2`'s thread
252 /// local and not `t1` as it should.
254 fn adjust_global_const(
255 _ecx
: &InterpCx
<'mir
, 'tcx
, Self>,
256 val
: mir
::interpret
::ConstValue
<'tcx
>,
257 ) -> InterpResult
<'tcx
, mir
::interpret
::ConstValue
<'tcx
>> {
261 /// Called to initialize the "extra" state of an allocation and make the pointers
262 /// it contains (in relocations) tagged. The way we construct allocations is
263 /// to always first construct it without extra and then add the extra.
264 /// This keeps uniform code paths for handling both allocations created by CTFE
265 /// for globals, and allocations created by Miri during evaluation.
267 /// `kind` is the kind of the allocation being tagged; it can be `None` when
268 /// it's a global and `GLOBAL_KIND` is `None`.
270 /// This should avoid copying if no work has to be done! If this returns an owned
271 /// allocation (because a copy had to be done to add tags or metadata), machine memory will
272 /// cache the result. (This relies on `AllocMap::get_or` being able to add the
273 /// owned allocation to the map even when the map is shared.)
275 /// Also return the "base" tag to use for this allocation: the one that is used for direct
276 /// accesses to this allocation. If `kind == STATIC_KIND`, this tag must be consistent
277 /// with `tag_global_base_pointer`.
278 fn init_allocation_extra
<'b
>(
279 memory_extra
: &Self::MemoryExtra
,
281 alloc
: Cow
<'b
, Allocation
>,
282 kind
: Option
<MemoryKind
<Self::MemoryKind
>>,
283 ) -> (Cow
<'b
, Allocation
<Self::PointerTag
, Self::AllocExtra
>>, Self::PointerTag
);
285 /// Called to notify the machine before a deallocation occurs.
286 fn before_deallocation(
287 _memory_extra
: &mut Self::MemoryExtra
,
289 ) -> InterpResult
<'tcx
> {
293 /// Return the "base" tag for the given *global* allocation: the one that is used for direct
294 /// accesses to this static/const/fn allocation. If `id` is not a global allocation,
295 /// this will return an unusable tag (i.e., accesses will be UB)!
297 /// Expects `id` to be already canonical, if needed.
298 fn tag_global_base_pointer(memory_extra
: &Self::MemoryExtra
, id
: AllocId
) -> Self::PointerTag
;
300 /// Executes a retagging operation
303 _ecx
: &mut InterpCx
<'mir
, 'tcx
, Self>,
304 _kind
: mir
::RetagKind
,
305 _place
: PlaceTy
<'tcx
, Self::PointerTag
>,
306 ) -> InterpResult
<'tcx
> {
310 /// Called immediately before a new stack frame gets pushed.
312 ecx
: &mut InterpCx
<'mir
, 'tcx
, Self>,
313 frame
: Frame
<'mir
, 'tcx
, Self::PointerTag
>,
314 ) -> InterpResult
<'tcx
, Frame
<'mir
, 'tcx
, Self::PointerTag
, Self::FrameExtra
>>;
316 /// Borrow the current thread's stack.
318 ecx
: &'a InterpCx
<'mir
, 'tcx
, Self>,
319 ) -> &'a
[Frame
<'mir
, 'tcx
, Self::PointerTag
, Self::FrameExtra
>];
321 /// Mutably borrow the current thread's stack.
323 ecx
: &'a
mut InterpCx
<'mir
, 'tcx
, Self>,
324 ) -> &'a
mut Vec
<Frame
<'mir
, 'tcx
, Self::PointerTag
, Self::FrameExtra
>>;
326 /// Called immediately after a stack frame got pushed and its locals got initialized.
327 fn after_stack_push(_ecx
: &mut InterpCx
<'mir
, 'tcx
, Self>) -> InterpResult
<'tcx
> {
331 /// Called immediately after a stack frame got popped, but before jumping back to the caller.
333 _ecx
: &mut InterpCx
<'mir
, 'tcx
, Self>,
334 _frame
: Frame
<'mir
, 'tcx
, Self::PointerTag
, Self::FrameExtra
>,
336 ) -> InterpResult
<'tcx
, StackPopJump
> {
337 // By default, we do not support unwinding from panics
338 Ok(StackPopJump
::Normal
)
342 _mem
: &Memory
<'mir
, 'tcx
, Self>,
344 ) -> InterpResult
<'tcx
, Pointer
<Self::PointerTag
>> {
346 // This is UB, seriously.
347 err_ub
!(InvalidIntPointerUsage(0))
349 // This is just something we cannot support during const-eval.
350 err_unsup
!(ReadBytesAsPointer
)
356 _mem
: &Memory
<'mir
, 'tcx
, Self>,
357 _ptr
: Pointer
<Self::PointerTag
>,
358 ) -> InterpResult
<'tcx
, u64>;