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