]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/interpret/machine.rs
Merge commit 'e214ea82ad0a751563acf67e1cd9279cf302db3a' into clippyup
[rust.git] / 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.
4
5 use std::borrow::{Borrow, Cow};
6 use std::hash::Hash;
7
8 use rustc_middle::mir;
9 use rustc_middle::ty::{self, Ty};
10 use rustc_span::def_id::DefId;
11
12 use super::{
13     AllocId, Allocation, AllocationExtra, CheckInAllocMsg, Frame, ImmTy, InterpCx, InterpResult,
14     Memory, MemoryKind, OpTy, Operand, PlaceTy, Pointer, Scalar,
15 };
16
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
24     /// we're leaving.
25     Normal,
26
27     /// Indicates that we should *not* jump to the return/unwind address, as the callback already
28     /// took care of everything.
29     NoJump,
30 }
31
32 /// Whether this kind of memory is allowed to leak
33 pub trait MayLeak: Copy {
34     fn may_leak(self) -> bool;
35 }
36
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
43     where
44         K: Borrow<Q>;
45
46     /// Inserts a new entry into the map.
47     fn insert(&mut self, k: K, v: V) -> Option<V>;
48
49     /// Removes an entry from the map.
50     fn remove<Q: ?Sized + Hash + Eq>(&mut self, k: &Q) -> Option<V>
51     where
52         K: Borrow<Q>;
53
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>;
56
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>;
61
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>;
66
67     /// Read-only lookup.
68     fn get(&self, k: K) -> Option<&V> {
69         self.get_or(k, || Err(())).ok()
70     }
71
72     /// Mutable lookup.
73     fn get_mut(&mut self, k: K) -> Option<&mut V> {
74         self.get_mut_or(k, || Err(())).ok()
75     }
76 }
77
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;
83
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     /// The `Debug` formatting is used for displaying pointers; we cannot use `Display`
88     /// as `()` does not implement that, but it should be "nice" output.
89     type PointerTag: ::std::fmt::Debug + Copy + Eq + Hash + 'static;
90
91     /// Machines can define extra (non-instance) things that represent values of function pointers.
92     /// For example, Miri uses this to return a function pointer from `dlsym`
93     /// that can later be called to execute the right thing.
94     type ExtraFnVal: ::std::fmt::Debug + Copy;
95
96     /// Extra data stored in every call frame.
97     type FrameExtra;
98
99     /// Extra data stored in memory. A reference to this is available when `AllocExtra`
100     /// gets initialized, so you can e.g., have an `Rc` here if there is global state you
101     /// need access to in the `AllocExtra` hooks.
102     type MemoryExtra;
103
104     /// Extra data stored in every allocation.
105     type AllocExtra: AllocationExtra<Self::PointerTag> + 'static;
106
107     /// Memory's allocation map
108     type MemoryMap: AllocMap<
109             AllocId,
110             (MemoryKind<Self::MemoryKind>, Allocation<Self::PointerTag, Self::AllocExtra>),
111         > + Default
112         + Clone;
113
114     /// The memory kind to use for copied global memory (held in `tcx`) --
115     /// or None if such memory should not be mutated and thus any such attempt will cause
116     /// a `ModifiedStatic` error to be raised.
117     /// Statics are copied under two circumstances: When they are mutated, and when
118     /// `tag_allocation` (see below) returns an owned allocation
119     /// that is added to the memory so that the work is not done twice.
120     const GLOBAL_KIND: Option<Self::MemoryKind>;
121
122     /// Whether memory accesses should be alignment-checked.
123     fn enforce_alignment(memory_extra: &Self::MemoryExtra) -> bool;
124
125     /// Whether to enforce the validity invariant
126     fn enforce_validity(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool;
127
128     /// Entry point to all function calls.
129     ///
130     /// Returns either the mir to use for the call, or `None` if execution should
131     /// just proceed (which usually means this hook did all the work that the
132     /// called function should usually have done). In the latter case, it is
133     /// this hook's responsibility to advance the instruction pointer!
134     /// (This is to support functions like `__rust_maybe_catch_panic` that neither find a MIR
135     /// nor just jump to `ret`, but instead push their own stack frame.)
136     /// Passing `dest`and `ret` in the same `Option` proved very annoying when only one of them
137     /// was used.
138     fn find_mir_or_eval_fn(
139         ecx: &mut InterpCx<'mir, 'tcx, Self>,
140         instance: ty::Instance<'tcx>,
141         args: &[OpTy<'tcx, Self::PointerTag>],
142         ret: Option<(PlaceTy<'tcx, Self::PointerTag>, mir::BasicBlock)>,
143         unwind: Option<mir::BasicBlock>,
144     ) -> InterpResult<'tcx, Option<&'mir mir::Body<'tcx>>>;
145
146     /// Execute `fn_val`.  It is the hook's responsibility to advance the instruction
147     /// pointer as appropriate.
148     fn call_extra_fn(
149         ecx: &mut InterpCx<'mir, 'tcx, Self>,
150         fn_val: Self::ExtraFnVal,
151         args: &[OpTy<'tcx, Self::PointerTag>],
152         ret: Option<(PlaceTy<'tcx, Self::PointerTag>, mir::BasicBlock)>,
153         unwind: Option<mir::BasicBlock>,
154     ) -> InterpResult<'tcx>;
155
156     /// Directly process an intrinsic without pushing a stack frame. It is the hook's
157     /// responsibility to advance the instruction pointer as appropriate.
158     fn call_intrinsic(
159         ecx: &mut InterpCx<'mir, 'tcx, Self>,
160         instance: ty::Instance<'tcx>,
161         args: &[OpTy<'tcx, Self::PointerTag>],
162         ret: Option<(PlaceTy<'tcx, Self::PointerTag>, mir::BasicBlock)>,
163         unwind: Option<mir::BasicBlock>,
164     ) -> InterpResult<'tcx>;
165
166     /// Called to evaluate `Assert` MIR terminators that trigger a panic.
167     fn assert_panic(
168         ecx: &mut InterpCx<'mir, 'tcx, Self>,
169         msg: &mir::AssertMessage<'tcx>,
170         unwind: Option<mir::BasicBlock>,
171     ) -> InterpResult<'tcx>;
172
173     /// Called to evaluate `Abort` MIR terminator.
174     fn abort(_ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx, !> {
175         throw_unsup_format!("aborting execution is not supported")
176     }
177
178     /// Called for all binary operations where the LHS has pointer type.
179     ///
180     /// Returns a (value, overflowed) pair if the operation succeeded
181     fn binary_ptr_op(
182         ecx: &InterpCx<'mir, 'tcx, Self>,
183         bin_op: mir::BinOp,
184         left: ImmTy<'tcx, Self::PointerTag>,
185         right: ImmTy<'tcx, Self::PointerTag>,
186     ) -> InterpResult<'tcx, (Scalar<Self::PointerTag>, bool, Ty<'tcx>)>;
187
188     /// Heap allocations via the `box` keyword.
189     fn box_alloc(
190         ecx: &mut InterpCx<'mir, 'tcx, Self>,
191         dest: PlaceTy<'tcx, Self::PointerTag>,
192     ) -> InterpResult<'tcx>;
193
194     /// Called to read the specified `local` from the `frame`.
195     #[inline]
196     fn access_local(
197         _ecx: &InterpCx<'mir, 'tcx, Self>,
198         frame: &Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>,
199         local: mir::Local,
200     ) -> InterpResult<'tcx, Operand<Self::PointerTag>> {
201         frame.locals[local].access()
202     }
203
204     /// Called before a basic block terminator is executed.
205     /// You can use this to detect endlessly running programs.
206     #[inline]
207     fn before_terminator(_ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> {
208         Ok(())
209     }
210
211     /// Called before a global allocation is accessed.
212     /// `def_id` is `Some` if this is the "lazy" allocation of a static.
213     #[inline]
214     fn before_access_global(
215         _memory_extra: &Self::MemoryExtra,
216         _alloc_id: AllocId,
217         _allocation: &Allocation,
218         _static_def_id: Option<DefId>,
219         _is_write: bool,
220     ) -> InterpResult<'tcx> {
221         Ok(())
222     }
223
224     /// Called for *every* memory access to determine the real ID of the given allocation.
225     /// This provides a way for the machine to "redirect" certain allocations as it sees fit.
226     ///
227     /// This is used by Miri to redirect extern statics to real allocations.
228     ///
229     /// This function must be idempotent.
230     #[inline]
231     fn canonical_alloc_id(_mem: &Memory<'mir, 'tcx, Self>, id: AllocId) -> AllocId {
232         id
233     }
234
235     /// Called when converting a `ty::Const` to an operand (in
236     /// `eval_const_to_op`).
237     ///
238     /// Miri uses this callback for creating per thread allocations for thread
239     /// locals. In Rust, one way of creating a thread local is by marking a
240     /// static with `#[thread_local]`. On supported platforms this gets
241     /// translated to a LLVM thread local for which LLVM automatically ensures
242     /// that each thread gets its own copy. Since LLVM automatically handles
243     /// thread locals, the Rust compiler just treats thread local statics as
244     /// regular statics even though accessing a thread local static should be an
245     /// effectful computation that depends on the current thread. The long term
246     /// plan is to change MIR to make accesses to thread locals explicit
247     /// (https://github.com/rust-lang/rust/issues/70685). While the issue 70685
248     /// is not fixed, our current workaround in Miri is to use this function to
249     /// make per-thread copies of thread locals. Please note that we cannot make
250     /// these copies in `canonical_alloc_id` because that is too late: for
251     /// example, if one created a pointer in thread `t1` to a thread local and
252     /// sent it to another thread `t2`, resolving the access in
253     /// `canonical_alloc_id` would result in pointer pointing to `t2`'s thread
254     /// local and not `t1` as it should.
255     #[inline]
256     fn adjust_global_const(
257         _ecx: &InterpCx<'mir, 'tcx, Self>,
258         val: mir::interpret::ConstValue<'tcx>,
259     ) -> InterpResult<'tcx, mir::interpret::ConstValue<'tcx>> {
260         Ok(val)
261     }
262
263     /// Called to initialize the "extra" state of an allocation and make the pointers
264     /// it contains (in relocations) tagged.  The way we construct allocations is
265     /// to always first construct it without extra and then add the extra.
266     /// This keeps uniform code paths for handling both allocations created by CTFE
267     /// for globals, and allocations created by Miri during evaluation.
268     ///
269     /// `kind` is the kind of the allocation being tagged; it can be `None` when
270     /// it's a global and `GLOBAL_KIND` is `None`.
271     ///
272     /// This should avoid copying if no work has to be done! If this returns an owned
273     /// allocation (because a copy had to be done to add tags or metadata), machine memory will
274     /// cache the result. (This relies on `AllocMap::get_or` being able to add the
275     /// owned allocation to the map even when the map is shared.)
276     ///
277     /// Also return the "base" tag to use for this allocation: the one that is used for direct
278     /// accesses to this allocation. If `kind == STATIC_KIND`, this tag must be consistent
279     /// with `tag_global_base_pointer`.
280     fn init_allocation_extra<'b>(
281         memory_extra: &Self::MemoryExtra,
282         id: AllocId,
283         alloc: Cow<'b, Allocation>,
284         kind: Option<MemoryKind<Self::MemoryKind>>,
285     ) -> (Cow<'b, Allocation<Self::PointerTag, Self::AllocExtra>>, Self::PointerTag);
286
287     /// Called to notify the machine before a deallocation occurs.
288     fn before_deallocation(
289         _memory_extra: &mut Self::MemoryExtra,
290         _id: AllocId,
291     ) -> InterpResult<'tcx> {
292         Ok(())
293     }
294
295     /// Return the "base" tag for the given *global* allocation: the one that is used for direct
296     /// accesses to this static/const/fn allocation. If `id` is not a global allocation,
297     /// this will return an unusable tag (i.e., accesses will be UB)!
298     ///
299     /// Expects `id` to be already canonical, if needed.
300     fn tag_global_base_pointer(memory_extra: &Self::MemoryExtra, id: AllocId) -> Self::PointerTag;
301
302     /// Executes a retagging operation
303     #[inline]
304     fn retag(
305         _ecx: &mut InterpCx<'mir, 'tcx, Self>,
306         _kind: mir::RetagKind,
307         _place: PlaceTy<'tcx, Self::PointerTag>,
308     ) -> InterpResult<'tcx> {
309         Ok(())
310     }
311
312     /// Called immediately before a new stack frame gets pushed.
313     fn init_frame_extra(
314         ecx: &mut InterpCx<'mir, 'tcx, Self>,
315         frame: Frame<'mir, 'tcx, Self::PointerTag>,
316     ) -> InterpResult<'tcx, Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>>;
317
318     /// Borrow the current thread's stack.
319     fn stack(
320         ecx: &'a InterpCx<'mir, 'tcx, Self>,
321     ) -> &'a [Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>];
322
323     /// Mutably borrow the current thread's stack.
324     fn stack_mut(
325         ecx: &'a mut InterpCx<'mir, 'tcx, Self>,
326     ) -> &'a mut Vec<Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>>;
327
328     /// Called immediately after a stack frame got pushed and its locals got initialized.
329     fn after_stack_push(_ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> {
330         Ok(())
331     }
332
333     /// Called immediately after a stack frame got popped, but before jumping back to the caller.
334     fn after_stack_pop(
335         _ecx: &mut InterpCx<'mir, 'tcx, Self>,
336         _frame: Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>,
337         _unwinding: bool,
338     ) -> InterpResult<'tcx, StackPopJump> {
339         // By default, we do not support unwinding from panics
340         Ok(StackPopJump::Normal)
341     }
342
343     fn int_to_ptr(
344         _mem: &Memory<'mir, 'tcx, Self>,
345         int: u64,
346     ) -> InterpResult<'tcx, Pointer<Self::PointerTag>> {
347         Err((if int == 0 {
348             // This is UB, seriously.
349             err_ub!(DanglingIntPointer(0, CheckInAllocMsg::InboundsTest))
350         } else {
351             // This is just something we cannot support during const-eval.
352             err_unsup!(ReadBytesAsPointer)
353         })
354         .into())
355     }
356
357     fn ptr_to_int(
358         _mem: &Memory<'mir, 'tcx, Self>,
359         _ptr: Pointer<Self::PointerTag>,
360     ) -> InterpResult<'tcx, u64>;
361 }
362
363 // A lot of the flexibility above is just needed for `Miri`, but all "compile-time" machines
364 // (CTFE and ConstProp) use the same instance.  Here, we share that code.
365 pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) {
366     type PointerTag = ();
367     type ExtraFnVal = !;
368
369     type MemoryKind = !;
370     type MemoryMap = rustc_data_structures::fx::FxHashMap<AllocId, (MemoryKind<!>, Allocation)>;
371     const GLOBAL_KIND: Option<!> = None; // no copying of globals from `tcx` to machine memory
372
373     type AllocExtra = ();
374     type FrameExtra = ();
375
376     #[inline(always)]
377     fn enforce_alignment(_memory_extra: &Self::MemoryExtra) -> bool {
378         // We do not check for alignment to avoid having to carry an `Align`
379         // in `ConstValue::ByRef`.
380         false
381     }
382
383     #[inline(always)]
384     fn enforce_validity(_ecx: &InterpCx<$mir, $tcx, Self>) -> bool {
385         false // for now, we don't enforce validity
386     }
387
388     #[inline(always)]
389     fn call_extra_fn(
390         _ecx: &mut InterpCx<$mir, $tcx, Self>,
391         fn_val: !,
392         _args: &[OpTy<$tcx>],
393         _ret: Option<(PlaceTy<$tcx>, mir::BasicBlock)>,
394         _unwind: Option<mir::BasicBlock>,
395     ) -> InterpResult<$tcx> {
396         match fn_val {}
397     }
398
399     #[inline(always)]
400     fn init_allocation_extra<'b>(
401         _memory_extra: &Self::MemoryExtra,
402         _id: AllocId,
403         alloc: Cow<'b, Allocation>,
404         _kind: Option<MemoryKind<!>>,
405     ) -> (Cow<'b, Allocation<Self::PointerTag>>, Self::PointerTag) {
406         // We do not use a tag so we can just cheaply forward the allocation
407         (alloc, ())
408     }
409
410     #[inline(always)]
411     fn tag_global_base_pointer(
412         _memory_extra: &Self::MemoryExtra,
413         _id: AllocId,
414     ) -> Self::PointerTag {
415         ()
416     }
417
418     #[inline(always)]
419     fn init_frame_extra(
420         _ecx: &mut InterpCx<$mir, $tcx, Self>,
421         frame: Frame<$mir, $tcx>,
422     ) -> InterpResult<$tcx, Frame<$mir, $tcx>> {
423         Ok(frame)
424     }
425 }