]> git.proxmox.com Git - rustc.git/blob - src/librustc_mir/interpret/machine.rs
New upstream version 1.41.1+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::hir::def_id::DefId;
9 use rustc::mir;
10 use rustc::ty::{self, Ty, TyCtxt};
11 use syntax_pos::Span;
12
13 use super::{
14 Allocation, AllocId, InterpResult, Scalar, AllocationExtra, AssertMessage,
15 InterpCx, PlaceTy, OpTy, ImmTy, MemoryKind, Pointer, Memory,
16 Frame, Operand,
17 };
18
19 /// Data returned by Machine::stack_pop,
20 /// to provide further control over the popping of the stack frame
21 #[derive(Eq, PartialEq, Debug, Copy, Clone)]
22 pub enum StackPopInfo {
23 /// Indicates that no special handling should be
24 /// done - we'll either return normally or unwind
25 /// based on the terminator for the function
26 /// we're leaving.
27 Normal,
28
29 /// Indicates that we should stop unwinding,
30 /// as we've reached a catch frame
31 StopUnwinding
32 }
33
34 /// Whether this kind of memory is allowed to leak
35 pub trait MayLeak: Copy {
36 fn may_leak(self) -> bool;
37 }
38
39 /// The functionality needed by memory to manage its allocations
40 pub trait AllocMap<K: Hash + Eq, V> {
41 /// Tests if the map contains the given key.
42 /// Deliberately takes `&mut` because that is sufficient, and some implementations
43 /// can be more efficient then (using `RefCell::get_mut`).
44 fn contains_key<Q: ?Sized + Hash + Eq>(&mut self, k: &Q) -> bool
45 where K: Borrow<Q>;
46
47 /// Inserts a new entry into the map.
48 fn insert(&mut self, k: K, v: V) -> Option<V>;
49
50 /// Removes an entry from the map.
51 fn remove<Q: ?Sized + Hash + Eq>(&mut self, k: &Q) -> Option<V>
52 where 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>(
61 &self,
62 k: K,
63 vacant: impl FnOnce() -> Result<V, E>
64 ) -> Result<&V, E>;
65
66 /// Returns a mutable reference to entry `k`. If no such entry exists, call
67 /// `vacant` and either forward its error, or add its result to the map
68 /// and return a reference to *that*.
69 fn get_mut_or<E>(
70 &mut self,
71 k: K,
72 vacant: impl FnOnce() -> Result<V, E>
73 ) -> Result<&mut V, E>;
74
75 /// Read-only lookup.
76 fn get(&self, k: K) -> Option<&V> {
77 self.get_or(k, || Err(())).ok()
78 }
79
80 /// Mutable lookup.
81 fn get_mut(&mut self, k: K) -> Option<&mut V> {
82 self.get_mut_or(k, || Err(())).ok()
83 }
84 }
85
86 /// Methods of this trait signifies a point where CTFE evaluation would fail
87 /// and some use case dependent behaviour can instead be applied.
88 pub trait Machine<'mir, 'tcx>: Sized {
89 /// Additional memory kinds a machine wishes to distinguish from the builtin ones
90 type MemoryKinds: ::std::fmt::Debug + MayLeak + Eq + 'static;
91
92 /// Tag tracked alongside every pointer. This is used to implement "Stacked Borrows"
93 /// <https://www.ralfj.de/blog/2018/08/07/stacked-borrows.html>.
94 /// The `default()` is used for pointers to consts, statics, vtables and functions.
95 type PointerTag: ::std::fmt::Debug + Copy + Eq + Hash + 'static;
96
97 /// Machines can define extra (non-instance) things that represent values of function pointers.
98 /// For example, Miri uses this to return a function pointer from `dlsym`
99 /// that can later be called to execute the right thing.
100 type ExtraFnVal: ::std::fmt::Debug + Copy;
101
102 /// Extra data stored in every call frame.
103 type FrameExtra;
104
105 /// Extra data stored in memory. A reference to this is available when `AllocExtra`
106 /// gets initialized, so you can e.g., have an `Rc` here if there is global state you
107 /// need access to in the `AllocExtra` hooks.
108 type MemoryExtra;
109
110 /// Extra data stored in every allocation.
111 type AllocExtra: AllocationExtra<Self::PointerTag> + 'static;
112
113 /// Memory's allocation map
114 type MemoryMap:
115 AllocMap<
116 AllocId,
117 (MemoryKind<Self::MemoryKinds>, Allocation<Self::PointerTag, Self::AllocExtra>)
118 > +
119 Default +
120 Clone;
121
122 /// The memory kind to use for copied statics -- or None if statics should not be mutated
123 /// and thus any such attempt will cause a `ModifiedStatic` error to be raised.
124 /// Statics are copied under two circumstances: When they are mutated, and when
125 /// `tag_allocation` or `find_foreign_static` (see below) returns an owned allocation
126 /// that is added to the memory so that the work is not done twice.
127 const STATIC_KIND: Option<Self::MemoryKinds>;
128
129 /// Whether memory accesses should be alignment-checked.
130 const CHECK_ALIGN: bool;
131
132 /// Whether to enforce the validity invariant
133 fn enforce_validity(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool;
134
135 /// Called before a basic block terminator is executed.
136 /// You can use this to detect endlessly running programs.
137 fn before_terminator(ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx>;
138
139 /// Entry point to all function calls.
140 ///
141 /// Returns either the mir to use for the call, or `None` if execution should
142 /// just proceed (which usually means this hook did all the work that the
143 /// called function should usually have done). In the latter case, it is
144 /// this hook's responsibility to advance the instruction pointer!
145 /// (This is to support functions like `__rust_maybe_catch_panic` that neither find a MIR
146 /// nor just jump to `ret`, but instead push their own stack frame.)
147 /// Passing `dest`and `ret` in the same `Option` proved very annoying when only one of them
148 /// was used.
149 fn find_mir_or_eval_fn(
150 ecx: &mut InterpCx<'mir, 'tcx, Self>,
151 instance: ty::Instance<'tcx>,
152 args: &[OpTy<'tcx, Self::PointerTag>],
153 ret: Option<(PlaceTy<'tcx, Self::PointerTag>, mir::BasicBlock)>,
154 unwind: Option<mir::BasicBlock>,
155 ) -> InterpResult<'tcx, Option<&'mir mir::Body<'tcx>>>;
156
157 /// Execute `fn_val`. It is the hook's responsibility to advance the instruction
158 /// pointer as appropriate.
159 fn call_extra_fn(
160 ecx: &mut InterpCx<'mir, 'tcx, Self>,
161 fn_val: Self::ExtraFnVal,
162 args: &[OpTy<'tcx, Self::PointerTag>],
163 ret: Option<(PlaceTy<'tcx, Self::PointerTag>, mir::BasicBlock)>,
164 unwind: Option<mir::BasicBlock>,
165 ) -> InterpResult<'tcx>;
166
167 /// Directly process an intrinsic without pushing a stack frame. It is the hook's
168 /// responsibility to advance the instruction pointer as appropriate.
169 fn call_intrinsic(
170 ecx: &mut InterpCx<'mir, 'tcx, Self>,
171 span: Span,
172 instance: ty::Instance<'tcx>,
173 args: &[OpTy<'tcx, Self::PointerTag>],
174 ret: Option<(PlaceTy<'tcx, Self::PointerTag>, mir::BasicBlock)>,
175 unwind: Option<mir::BasicBlock>,
176 ) -> InterpResult<'tcx>;
177
178 /// Called to evaluate `Assert` MIR terminators that trigger a panic.
179 fn assert_panic(
180 ecx: &mut InterpCx<'mir, 'tcx, Self>,
181 span: Span,
182 msg: &AssertMessage<'tcx>,
183 unwind: Option<mir::BasicBlock>,
184 ) -> InterpResult<'tcx>;
185
186 /// Called for read access to a foreign static item.
187 ///
188 /// This will only be called once per static and machine; the result is cached in
189 /// the machine memory. (This relies on `AllocMap::get_or` being able to add the
190 /// owned allocation to the map even when the map is shared.)
191 ///
192 /// This allocation will then be fed to `tag_allocation` to initialize the "extra" state.
193 fn find_foreign_static(
194 tcx: TyCtxt<'tcx>,
195 def_id: DefId,
196 ) -> InterpResult<'tcx, Cow<'tcx, Allocation>>;
197
198 /// Called for all binary operations where the LHS has pointer type.
199 ///
200 /// Returns a (value, overflowed) pair if the operation succeeded
201 fn binary_ptr_op(
202 ecx: &InterpCx<'mir, 'tcx, Self>,
203 bin_op: mir::BinOp,
204 left: ImmTy<'tcx, Self::PointerTag>,
205 right: ImmTy<'tcx, Self::PointerTag>,
206 ) -> InterpResult<'tcx, (Scalar<Self::PointerTag>, bool, Ty<'tcx>)>;
207
208 /// Heap allocations via the `box` keyword.
209 fn box_alloc(
210 ecx: &mut InterpCx<'mir, 'tcx, Self>,
211 dest: PlaceTy<'tcx, Self::PointerTag>,
212 ) -> InterpResult<'tcx>;
213
214 /// Called to read the specified `local` from the `frame`.
215 fn access_local(
216 _ecx: &InterpCx<'mir, 'tcx, Self>,
217 frame: &Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>,
218 local: mir::Local,
219 ) -> InterpResult<'tcx, Operand<Self::PointerTag>> {
220 frame.locals[local].access()
221 }
222
223 /// Called before a `StaticKind::Static` value is accessed.
224 fn before_access_static(
225 _allocation: &Allocation,
226 ) -> InterpResult<'tcx> {
227 Ok(())
228 }
229
230 /// Called to initialize the "extra" state of an allocation and make the pointers
231 /// it contains (in relocations) tagged. The way we construct allocations is
232 /// to always first construct it without extra and then add the extra.
233 /// This keeps uniform code paths for handling both allocations created by CTFE
234 /// for statics, and allocations ceated by Miri during evaluation.
235 ///
236 /// `kind` is the kind of the allocation being tagged; it can be `None` when
237 /// it's a static and `STATIC_KIND` is `None`.
238 ///
239 /// This should avoid copying if no work has to be done! If this returns an owned
240 /// allocation (because a copy had to be done to add tags or metadata), machine memory will
241 /// cache the result. (This relies on `AllocMap::get_or` being able to add the
242 /// owned allocation to the map even when the map is shared.)
243 ///
244 /// Also return the "base" tag to use for this allocation: the one that is used for direct
245 /// accesses to this allocation. If `kind == STATIC_KIND`, this tag must be consistent
246 /// with `tag_static_base_pointer`.
247 fn init_allocation_extra<'b>(
248 memory_extra: &Self::MemoryExtra,
249 id: AllocId,
250 alloc: Cow<'b, Allocation>,
251 kind: Option<MemoryKind<Self::MemoryKinds>>,
252 ) -> (Cow<'b, Allocation<Self::PointerTag, Self::AllocExtra>>, Self::PointerTag);
253
254 /// Return the "base" tag for the given *static* allocation: the one that is used for direct
255 /// accesses to this static/const/fn allocation. If `id` is not a static allocation,
256 /// this will return an unusable tag (i.e., accesses will be UB)!
257 fn tag_static_base_pointer(
258 memory_extra: &Self::MemoryExtra,
259 id: AllocId,
260 ) -> Self::PointerTag;
261
262 /// Executes a retagging operation
263 #[inline]
264 fn retag(
265 _ecx: &mut InterpCx<'mir, 'tcx, Self>,
266 _kind: mir::RetagKind,
267 _place: PlaceTy<'tcx, Self::PointerTag>,
268 ) -> InterpResult<'tcx> {
269 Ok(())
270 }
271
272 /// Called immediately before a new stack frame got pushed
273 fn stack_push(ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx, Self::FrameExtra>;
274
275 /// Called immediately after a stack frame gets popped
276 fn stack_pop(
277 _ecx: &mut InterpCx<'mir, 'tcx, Self>,
278 _extra: Self::FrameExtra,
279 _unwinding: bool
280 ) -> InterpResult<'tcx, StackPopInfo> {
281 // By default, we do not support unwinding from panics
282 Ok(StackPopInfo::Normal)
283 }
284
285 fn int_to_ptr(
286 _mem: &Memory<'mir, 'tcx, Self>,
287 int: u64,
288 ) -> InterpResult<'tcx, Pointer<Self::PointerTag>> {
289 Err((if int == 0 {
290 err_unsup!(InvalidNullPointerUsage)
291 } else {
292 err_unsup!(ReadBytesAsPointer)
293 }).into())
294 }
295
296 fn ptr_to_int(
297 _mem: &Memory<'mir, 'tcx, Self>,
298 _ptr: Pointer<Self::PointerTag>,
299 ) -> InterpResult<'tcx, u64>;
300 }