]>
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}
;
8 use rustc
::hir
::def_id
::DefId
;
10 use rustc
::ty
::{self, Ty, TyCtxt}
;
14 Allocation
, AllocId
, InterpResult
, Scalar
, AllocationExtra
, AssertMessage
,
15 InterpCx
, PlaceTy
, OpTy
, ImmTy
, MemoryKind
, Pointer
, Memory
,
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
29 /// Indicates that we should stop unwinding,
30 /// as we've reached a catch frame
34 /// Whether this kind of memory is allowed to leak
35 pub trait MayLeak
: Copy
{
36 fn may_leak(self) -> bool
;
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
47 /// Inserts a new entry into the map.
48 fn insert(&mut self, k
: K
, v
: V
) -> Option
<V
>;
50 /// Removes an entry from the map.
51 fn remove
<Q
: ?Sized
+ Hash
+ Eq
>(&mut self, k
: &Q
) -> Option
<V
>
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
>;
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*.
63 vacant
: impl FnOnce() -> Result
<V
, E
>
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*.
72 vacant
: impl FnOnce() -> Result
<V
, E
>
73 ) -> Result
<&mut V
, E
>;
76 fn get(&self, k
: K
) -> Option
<&V
> {
77 self.get_or(k
, || Err(())).ok()
81 fn get_mut(&mut self, k
: K
) -> Option
<&mut V
> {
82 self.get_mut_or(k
, || Err(())).ok()
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;
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;
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
;
102 /// Extra data stored in every call frame.
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.
110 /// Extra data stored in every allocation.
111 type AllocExtra
: AllocationExtra
<Self::PointerTag
> + '
static;
113 /// Memory's allocation map
117 (MemoryKind
<Self::MemoryKinds
>, Allocation
<Self::PointerTag
, Self::AllocExtra
>)
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
>;
129 /// Whether memory accesses should be alignment-checked.
130 const CHECK_ALIGN
: bool
;
132 /// Whether to enforce the validity invariant
133 fn enforce_validity(ecx
: &InterpCx
<'mir
, 'tcx
, Self>) -> bool
;
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
>;
139 /// Entry point to all function calls.
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
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
>>>;
157 /// Execute `fn_val`. It is the hook's responsibility to advance the instruction
158 /// pointer as appropriate.
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
>;
167 /// Directly process an intrinsic without pushing a stack frame. It is the hook's
168 /// responsibility to advance the instruction pointer as appropriate.
170 ecx
: &mut InterpCx
<'mir
, 'tcx
, Self>,
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
>;
178 /// Called to evaluate `Assert` MIR terminators that trigger a panic.
180 ecx
: &mut InterpCx
<'mir
, 'tcx
, Self>,
182 msg
: &AssertMessage
<'tcx
>,
183 unwind
: Option
<mir
::BasicBlock
>,
184 ) -> InterpResult
<'tcx
>;
186 /// Called for read access to a foreign static item.
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.)
192 /// This allocation will then be fed to `tag_allocation` to initialize the "extra" state.
193 fn find_foreign_static(
196 ) -> InterpResult
<'tcx
, Cow
<'tcx
, Allocation
>>;
198 /// Called for all binary operations where the LHS has pointer type.
200 /// Returns a (value, overflowed) pair if the operation succeeded
202 ecx
: &InterpCx
<'mir
, 'tcx
, Self>,
204 left
: ImmTy
<'tcx
, Self::PointerTag
>,
205 right
: ImmTy
<'tcx
, Self::PointerTag
>,
206 ) -> InterpResult
<'tcx
, (Scalar
<Self::PointerTag
>, bool
, Ty
<'tcx
>)>;
208 /// Heap allocations via the `box` keyword.
210 ecx
: &mut InterpCx
<'mir
, 'tcx
, Self>,
211 dest
: PlaceTy
<'tcx
, Self::PointerTag
>,
212 ) -> InterpResult
<'tcx
>;
214 /// Called to read the specified `local` from the `frame`.
216 _ecx
: &InterpCx
<'mir
, 'tcx
, Self>,
217 frame
: &Frame
<'mir
, 'tcx
, Self::PointerTag
, Self::FrameExtra
>,
219 ) -> InterpResult
<'tcx
, Operand
<Self::PointerTag
>> {
220 frame
.locals
[local
].access()
223 /// Called before a `StaticKind::Static` value is accessed.
224 fn before_access_static(
225 _allocation
: &Allocation
,
226 ) -> InterpResult
<'tcx
> {
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.
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`.
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.)
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
,
250 alloc
: Cow
<'b
, Allocation
>,
251 kind
: Option
<MemoryKind
<Self::MemoryKinds
>>,
252 ) -> (Cow
<'b
, Allocation
<Self::PointerTag
, Self::AllocExtra
>>, Self::PointerTag
);
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
,
260 ) -> Self::PointerTag
;
262 /// Executes a retagging operation
265 _ecx
: &mut InterpCx
<'mir
, 'tcx
, Self>,
266 _kind
: mir
::RetagKind
,
267 _place
: PlaceTy
<'tcx
, Self::PointerTag
>,
268 ) -> InterpResult
<'tcx
> {
272 /// Called immediately before a new stack frame got pushed
273 fn stack_push(ecx
: &mut InterpCx
<'mir
, 'tcx
, Self>) -> InterpResult
<'tcx
, Self::FrameExtra
>;
275 /// Called immediately after a stack frame gets popped
277 _ecx
: &mut InterpCx
<'mir
, 'tcx
, Self>,
278 _extra
: Self::FrameExtra
,
280 ) -> InterpResult
<'tcx
, StackPopInfo
> {
281 // By default, we do not support unwinding from panics
282 Ok(StackPopInfo
::Normal
)
286 _mem
: &Memory
<'mir
, 'tcx
, Self>,
288 ) -> InterpResult
<'tcx
, Pointer
<Self::PointerTag
>> {
290 err_unsup
!(InvalidNullPointerUsage
)
292 err_unsup
!(ReadBytesAsPointer
)
297 _mem
: &Memory
<'mir
, 'tcx
, Self>,
298 _ptr
: Pointer
<Self::PointerTag
>,
299 ) -> InterpResult
<'tcx
, u64>;