@@ -193,75 +193,104 @@ impl<'tcx> AllocExtra<'tcx> {
193
193
/// If `init` is set to this, we consider the primitive initialized.
194
194
pub const LAZY_INIT_COOKIE : u32 = 0xcafe_affe ;
195
195
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
-
259
196
// Public interface to synchronization primitives. Please note that in most
260
197
// cases, the function calls are infallible and it is the client's (shim
261
198
// implementation's) responsibility to detect and deal with erroneous
262
199
// situations.
263
200
impl < ' tcx > EvalContextExt < ' tcx > for crate :: MiriInterpCx < ' tcx > { }
264
201
pub 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
+
265
294
#[ inline]
266
295
/// Get the id of the thread that currently owns this lock.
267
296
fn mutex_get_owner ( & mut self , id : MutexId ) -> ThreadId {
0 commit comments