]> git.proxmox.com Git - rustc.git/blob - src/librustc_mir/interpret/machine.rs
New upstream version 1.43.0+dfsg1
[rustc.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::mir;
9 use rustc::ty::{self, Ty};
10 use rustc_span::Span;
11
12 use super::{
13 AllocId, Allocation, AllocationExtra, Frame, ImmTy, InterpCx, InterpResult, Memory, MemoryKind,
14 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 StackPopInfo {
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 stop unwinding,
28 /// as we've reached a catch frame
29 StopUnwinding,
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 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 MemoryKinds: ::std::fmt::Debug + 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 type PointerTag: ::std::fmt::Debug + Copy + Eq + Hash + 'static;
88
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;
93
94 /// Extra data stored in every call frame.
95 type FrameExtra;
96
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.
100 type MemoryExtra;
101
102 /// Extra data stored in every allocation.
103 type AllocExtra: AllocationExtra<Self::PointerTag> + 'static;
104
105 /// Memory's allocation map
106 type MemoryMap: AllocMap<
107 AllocId,
108 (MemoryKind<Self::MemoryKinds>, Allocation<Self::PointerTag, Self::AllocExtra>),
109 > + Default
110 + Clone;
111
112 /// The memory kind to use for copied statics -- or None if statics should not be mutated
113 /// and thus any such attempt will cause a `ModifiedStatic` error to be raised.
114 /// Statics are copied under two circumstances: When they are mutated, and when
115 /// `tag_allocation` or `find_foreign_static` (see below) returns an owned allocation
116 /// that is added to the memory so that the work is not done twice.
117 const STATIC_KIND: Option<Self::MemoryKinds>;
118
119 /// Whether memory accesses should be alignment-checked.
120 const CHECK_ALIGN: bool;
121
122 /// Whether to enforce the validity invariant
123 fn enforce_validity(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool;
124
125 /// Entry point to all function calls.
126 ///
127 /// Returns either the mir to use for the call, or `None` if execution should
128 /// just proceed (which usually means this hook did all the work that the
129 /// called function should usually have done). In the latter case, it is
130 /// this hook's responsibility to advance the instruction pointer!
131 /// (This is to support functions like `__rust_maybe_catch_panic` that neither find a MIR
132 /// nor just jump to `ret`, but instead push their own stack frame.)
133 /// Passing `dest`and `ret` in the same `Option` proved very annoying when only one of them
134 /// was used.
135 fn find_mir_or_eval_fn(
136 ecx: &mut InterpCx<'mir, 'tcx, Self>,
137 span: Span,
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>>>;
143
144 /// Execute `fn_val`. It is the hook's responsibility to advance the instruction
145 /// pointer as appropriate.
146 fn call_extra_fn(
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>;
153
154 /// Directly process an intrinsic without pushing a stack frame. It is the hook's
155 /// responsibility to advance the instruction pointer as appropriate.
156 fn call_intrinsic(
157 ecx: &mut InterpCx<'mir, 'tcx, Self>,
158 span: Span,
159 instance: ty::Instance<'tcx>,
160 args: &[OpTy<'tcx, Self::PointerTag>],
161 ret: Option<(PlaceTy<'tcx, Self::PointerTag>, mir::BasicBlock)>,
162 unwind: Option<mir::BasicBlock>,
163 ) -> InterpResult<'tcx>;
164
165 /// Called to evaluate `Assert` MIR terminators that trigger a panic.
166 fn assert_panic(
167 ecx: &mut InterpCx<'mir, 'tcx, Self>,
168 span: Span,
169 msg: &mir::AssertMessage<'tcx>,
170 unwind: Option<mir::BasicBlock>,
171 ) -> InterpResult<'tcx>;
172
173 /// Called for all binary operations where the LHS has pointer type.
174 ///
175 /// Returns a (value, overflowed) pair if the operation succeeded
176 fn binary_ptr_op(
177 ecx: &InterpCx<'mir, 'tcx, Self>,
178 bin_op: mir::BinOp,
179 left: ImmTy<'tcx, Self::PointerTag>,
180 right: ImmTy<'tcx, Self::PointerTag>,
181 ) -> InterpResult<'tcx, (Scalar<Self::PointerTag>, bool, Ty<'tcx>)>;
182
183 /// Heap allocations via the `box` keyword.
184 fn box_alloc(
185 ecx: &mut InterpCx<'mir, 'tcx, Self>,
186 dest: PlaceTy<'tcx, Self::PointerTag>,
187 ) -> InterpResult<'tcx>;
188
189 /// Called to read the specified `local` from the `frame`.
190 #[inline]
191 fn access_local(
192 _ecx: &InterpCx<'mir, 'tcx, Self>,
193 frame: &Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>,
194 local: mir::Local,
195 ) -> InterpResult<'tcx, Operand<Self::PointerTag>> {
196 frame.locals[local].access()
197 }
198
199 /// Called before a basic block terminator is executed.
200 /// You can use this to detect endlessly running programs.
201 #[inline]
202 fn before_terminator(_ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> {
203 Ok(())
204 }
205
206 /// Called before a `Static` value is accessed.
207 #[inline]
208 fn before_access_static(
209 _memory_extra: &Self::MemoryExtra,
210 _allocation: &Allocation,
211 ) -> InterpResult<'tcx> {
212 Ok(())
213 }
214
215 /// Called for *every* memory access to determine the real ID of the given allocation.
216 /// This provides a way for the machine to "redirect" certain allocations as it sees fit.
217 ///
218 /// This is used by Miri to redirect extern statics to real allocations.
219 ///
220 /// This function must be idempotent.
221 #[inline]
222 fn canonical_alloc_id(_mem: &Memory<'mir, 'tcx, Self>, id: AllocId) -> AllocId {
223 id
224 }
225
226 /// Called to initialize the "extra" state of an allocation and make the pointers
227 /// it contains (in relocations) tagged. The way we construct allocations is
228 /// to always first construct it without extra and then add the extra.
229 /// This keeps uniform code paths for handling both allocations created by CTFE
230 /// for statics, and allocations created by Miri during evaluation.
231 ///
232 /// `kind` is the kind of the allocation being tagged; it can be `None` when
233 /// it's a static and `STATIC_KIND` is `None`.
234 ///
235 /// This should avoid copying if no work has to be done! If this returns an owned
236 /// allocation (because a copy had to be done to add tags or metadata), machine memory will
237 /// cache the result. (This relies on `AllocMap::get_or` being able to add the
238 /// owned allocation to the map even when the map is shared.)
239 ///
240 /// Also return the "base" tag to use for this allocation: the one that is used for direct
241 /// accesses to this allocation. If `kind == STATIC_KIND`, this tag must be consistent
242 /// with `tag_static_base_pointer`.
243 fn init_allocation_extra<'b>(
244 memory_extra: &Self::MemoryExtra,
245 id: AllocId,
246 alloc: Cow<'b, Allocation>,
247 kind: Option<MemoryKind<Self::MemoryKinds>>,
248 ) -> (Cow<'b, Allocation<Self::PointerTag, Self::AllocExtra>>, Self::PointerTag);
249
250 /// Return the "base" tag for the given *static* allocation: the one that is used for direct
251 /// accesses to this static/const/fn allocation. If `id` is not a static allocation,
252 /// this will return an unusable tag (i.e., accesses will be UB)!
253 ///
254 /// Expects `id` to be already canonical, if needed.
255 fn tag_static_base_pointer(memory_extra: &Self::MemoryExtra, id: AllocId) -> Self::PointerTag;
256
257 /// Executes a retagging operation
258 #[inline]
259 fn retag(
260 _ecx: &mut InterpCx<'mir, 'tcx, Self>,
261 _kind: mir::RetagKind,
262 _place: PlaceTy<'tcx, Self::PointerTag>,
263 ) -> InterpResult<'tcx> {
264 Ok(())
265 }
266
267 /// Called immediately before a new stack frame got pushed.
268 fn stack_push(ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx, Self::FrameExtra>;
269
270 /// Called immediately after a stack frame gets popped
271 fn stack_pop(
272 _ecx: &mut InterpCx<'mir, 'tcx, Self>,
273 _extra: Self::FrameExtra,
274 _unwinding: bool,
275 ) -> InterpResult<'tcx, StackPopInfo> {
276 // By default, we do not support unwinding from panics
277 Ok(StackPopInfo::Normal)
278 }
279
280 fn int_to_ptr(
281 _mem: &Memory<'mir, 'tcx, Self>,
282 int: u64,
283 ) -> InterpResult<'tcx, Pointer<Self::PointerTag>> {
284 Err((if int == 0 {
285 err_unsup!(InvalidNullPointerUsage)
286 } else {
287 err_unsup!(ReadBytesAsPointer)
288 })
289 .into())
290 }
291
292 fn ptr_to_int(
293 _mem: &Memory<'mir, 'tcx, Self>,
294 _ptr: Pointer<Self::PointerTag>,
295 ) -> InterpResult<'tcx, u64>;
296 }