@@ -193,75 +193,104 @@ impl<'tcx> AllocExtra<'tcx> {
193193/// If `init` is set to this, we consider the primitive initialized.
194194pub const LAZY_INIT_COOKIE : u32 = 0xcafe_affe ;
195195
196- /// Helper for lazily initialized `alloc_extra.sync` data:
197- /// this forces an immediate init.
198- pub fn lazy_sync_init < ' tcx , T : ' static + Copy > (
199- ecx : & mut MiriInterpCx < ' tcx > ,
200- primitive : & MPlaceTy < ' tcx > ,
201- init_offset : Size ,
202- data : T ,
203- ) -> InterpResult < ' tcx > {
204- let ( alloc, offset, _) = ecx. ptr_get_alloc_id ( primitive. ptr ( ) , 0 ) ?;
205- let ( alloc_extra, _machine) = ecx. get_alloc_extra_mut ( alloc) ?;
206- alloc_extra. sync . insert ( offset, Box :: new ( data) ) ;
207- // Mark this as "initialized".
208- let init_field = primitive. offset ( init_offset, ecx. machine . layouts . u32 , ecx) ?;
209- ecx. write_scalar_atomic (
210- Scalar :: from_u32 ( LAZY_INIT_COOKIE ) ,
211- & init_field,
212- AtomicWriteOrd :: Relaxed ,
213- ) ?;
214- interp_ok ( ( ) )
215- }
216-
217- /// Helper for lazily initialized `alloc_extra.sync` data:
218- /// Checks if the primitive is initialized, and return its associated data if so.
219- /// Otherwise, calls `new_data` to initialize the primitive.
220- pub fn lazy_sync_get_data < ' tcx , T : ' static + Copy > (
221- ecx : & mut MiriInterpCx < ' tcx > ,
222- primitive : & MPlaceTy < ' tcx > ,
223- init_offset : Size ,
224- name : & str ,
225- new_data : impl FnOnce ( & mut MiriInterpCx < ' tcx > ) -> InterpResult < ' tcx , T > ,
226- ) -> InterpResult < ' tcx , T > {
227- // Check if this is already initialized. Needs to be atomic because we can race with another
228- // thread initializing. Needs to be an RMW operation to ensure we read the *latest* value.
229- // So we just try to replace MUTEX_INIT_COOKIE with itself.
230- let init_cookie = Scalar :: from_u32 ( LAZY_INIT_COOKIE ) ;
231- let init_field = primitive. offset ( init_offset, ecx. machine . layouts . u32 , ecx) ?;
232- let ( _init, success) = ecx
233- . atomic_compare_exchange_scalar (
234- & init_field,
235- & ImmTy :: from_scalar ( init_cookie, ecx. machine . layouts . u32 ) ,
236- init_cookie,
237- AtomicRwOrd :: Relaxed ,
238- AtomicReadOrd :: Relaxed ,
239- /* can_fail_spuriously */ false ,
240- ) ?
241- . to_scalar_pair ( ) ;
242-
243- if success. to_bool ( ) ? {
244- // If it is initialized, it must be found in the "sync primitive" table,
245- // or else it has been moved illegally.
246- let ( alloc, offset, _) = ecx. ptr_get_alloc_id ( primitive. ptr ( ) , 0 ) ?;
247- let alloc_extra = ecx. get_alloc_extra ( alloc) ?;
248- let data = alloc_extra
249- . get_sync :: < T > ( offset)
250- . ok_or_else ( || err_ub_format ! ( "`{name}` can't be moved after first use" ) ) ?;
251- interp_ok ( * data)
252- } else {
253- let data = new_data ( ecx) ?;
254- lazy_sync_init ( ecx, primitive, init_offset, data) ?;
255- interp_ok ( data)
256- }
257- }
258-
259196// Public interface to synchronization primitives. Please note that in most
260197// cases, the function calls are infallible and it is the client's (shim
261198// implementation's) responsibility to detect and deal with erroneous
262199// situations.
263200impl < ' tcx > EvalContextExt < ' tcx > for crate :: MiriInterpCx < ' tcx > { }
264201pub trait EvalContextExt < ' tcx > : crate :: MiriInterpCxExt < ' tcx > {
202+ /// Helper for lazily initialized `alloc_extra.sync` data:
203+ /// this forces an immediate init.
204+ fn lazy_sync_init < T : ' static + Copy > (
205+ & mut self ,
206+ primitive : & MPlaceTy < ' tcx > ,
207+ init_offset : Size ,
208+ data : T ,
209+ ) -> InterpResult < ' tcx > {
210+ let this = self . eval_context_mut ( ) ;
211+
212+ let ( alloc, offset, _) = this. ptr_get_alloc_id ( primitive. ptr ( ) , 0 ) ?;
213+ let ( alloc_extra, _machine) = this. get_alloc_extra_mut ( alloc) ?;
214+ alloc_extra. sync . insert ( offset, Box :: new ( data) ) ;
215+ // Mark this as "initialized".
216+ let init_field = primitive. offset ( init_offset, this. machine . layouts . u32 , this) ?;
217+ this. write_scalar_atomic (
218+ Scalar :: from_u32 ( LAZY_INIT_COOKIE ) ,
219+ & init_field,
220+ AtomicWriteOrd :: Relaxed ,
221+ ) ?;
222+ interp_ok ( ( ) )
223+ }
224+
225+ /// Helper for lazily initialized `alloc_extra.sync` data:
226+ /// Checks if the primitive is initialized, and return its associated data if so.
227+ /// Otherwise, calls `new_data` to initialize the primitive.
228+ fn lazy_sync_get_data < T : ' static + Copy > (
229+ & mut self ,
230+ primitive : & MPlaceTy < ' tcx > ,
231+ init_offset : Size ,
232+ name : & str ,
233+ new_data : impl FnOnce ( & mut MiriInterpCx < ' tcx > ) -> InterpResult < ' tcx , T > ,
234+ ) -> InterpResult < ' tcx , T > {
235+ let this = self . eval_context_mut ( ) ;
236+
237+ // Check if this is already initialized. Needs to be atomic because we can race with another
238+ // thread initializing. Needs to be an RMW operation to ensure we read the *latest* value.
239+ // So we just try to replace MUTEX_INIT_COOKIE with itself.
240+ let init_cookie = Scalar :: from_u32 ( LAZY_INIT_COOKIE ) ;
241+ let init_field = primitive. offset ( init_offset, this. machine . layouts . u32 , this) ?;
242+ let ( _init, success) = this
243+ . atomic_compare_exchange_scalar (
244+ & init_field,
245+ & ImmTy :: from_scalar ( init_cookie, this. machine . layouts . u32 ) ,
246+ init_cookie,
247+ AtomicRwOrd :: Relaxed ,
248+ AtomicReadOrd :: Relaxed ,
249+ /* can_fail_spuriously */ false ,
250+ ) ?
251+ . to_scalar_pair ( ) ;
252+
253+ if success. to_bool ( ) ? {
254+ // If it is initialized, it must be found in the "sync primitive" table,
255+ // or else it has been moved illegally.
256+ let ( alloc, offset, _) = this. ptr_get_alloc_id ( primitive. ptr ( ) , 0 ) ?;
257+ let alloc_extra = this. get_alloc_extra ( alloc) ?;
258+ let data = alloc_extra
259+ . get_sync :: < T > ( offset)
260+ . ok_or_else ( || err_ub_format ! ( "`{name}` can't be moved after first use" ) ) ?;
261+ interp_ok ( * data)
262+ } else {
263+ let data = new_data ( this) ?;
264+ this. lazy_sync_init ( primitive, init_offset, data) ?;
265+ interp_ok ( data)
266+ }
267+ }
268+
269+ /// Get the synchronization primitive associated with the given pointer,
270+ /// or initialize a new one.
271+ fn get_sync_or_init < ' a , T : ' static > (
272+ & ' a mut self ,
273+ ptr : Pointer ,
274+ new : impl FnOnce ( & ' a mut MiriMachine < ' tcx > ) -> InterpResult < ' tcx , T > ,
275+ ) -> InterpResult < ' tcx , & ' a T >
276+ where
277+ ' tcx : ' a ,
278+ {
279+ let this = self . eval_context_mut ( ) ;
280+ // Ensure there is memory behind this pointer, so that this allocation
281+ // is truly the only place where the data could be stored.
282+ this. check_ptr_access ( ptr, Size :: from_bytes ( 1 ) , CheckInAllocMsg :: InboundsTest ) ?;
283+
284+ let ( alloc, offset, _) = this. ptr_get_alloc_id ( ptr, 0 ) ?;
285+ let ( alloc_extra, machine) = this. get_alloc_extra_mut ( alloc) ?;
286+ // Due to borrow checker reasons, we have to do the lookup twice.
287+ if alloc_extra. get_sync :: < T > ( offset) . is_none ( ) {
288+ let new = new ( machine) ?;
289+ alloc_extra. sync . insert ( offset, Box :: new ( new) ) ;
290+ }
291+ interp_ok ( alloc_extra. get_sync :: < T > ( offset) . unwrap ( ) )
292+ }
293+
265294 #[ inline]
266295 /// Get the id of the thread that currently owns this lock.
267296 fn mutex_get_owner ( & mut self , id : MutexId ) -> ThreadId {
0 commit comments