@@ -193,75 +193,109 @@ 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:
227
+ /// - If yes, fetches the data from `alloc_extra.sync`, or calls `missing_data` if that fails
228
+ /// and stores that in `alloc_extra.sync`.
229
+ /// - Otherwise, calls `new_data` to initialize the primitive.
230
+ fn lazy_sync_get_data < T : ' static + Copy > (
231
+ & mut self ,
232
+ primitive : & MPlaceTy < ' tcx > ,
233
+ init_offset : Size ,
234
+ missing_data : impl FnOnce ( ) -> InterpResult < ' tcx , T > ,
235
+ new_data : impl FnOnce ( & mut MiriInterpCx < ' tcx > ) -> InterpResult < ' tcx , T > ,
236
+ ) -> InterpResult < ' tcx , T > {
237
+ let this = self . eval_context_mut ( ) ;
238
+
239
+ // Check if this is already initialized. Needs to be atomic because we can race with another
240
+ // thread initializing. Needs to be an RMW operation to ensure we read the *latest* value.
241
+ // So we just try to replace MUTEX_INIT_COOKIE with itself.
242
+ let init_cookie = Scalar :: from_u32 ( LAZY_INIT_COOKIE ) ;
243
+ let init_field = primitive. offset ( init_offset, this. machine . layouts . u32 , this) ?;
244
+ let ( _init, success) = this
245
+ . atomic_compare_exchange_scalar (
246
+ & init_field,
247
+ & ImmTy :: from_scalar ( init_cookie, this. machine . layouts . u32 ) ,
248
+ init_cookie,
249
+ AtomicRwOrd :: Relaxed ,
250
+ AtomicReadOrd :: Relaxed ,
251
+ /* can_fail_spuriously */ false ,
252
+ ) ?
253
+ . to_scalar_pair ( ) ;
254
+
255
+ if success. to_bool ( ) ? {
256
+ // If it is initialized, it must be found in the "sync primitive" table,
257
+ // or else it has been moved illegally.
258
+ let ( alloc, offset, _) = this. ptr_get_alloc_id ( primitive. ptr ( ) , 0 ) ?;
259
+ let ( alloc_extra, _machine) = this. get_alloc_extra_mut ( alloc) ?;
260
+ if let Some ( data) = alloc_extra. get_sync :: < T > ( offset) {
261
+ interp_ok ( * data)
262
+ } else {
263
+ let data = missing_data ( ) ?;
264
+ alloc_extra. sync . insert ( offset, Box :: new ( data) ) ;
265
+ interp_ok ( data)
266
+ }
267
+ } else {
268
+ let data = new_data ( this) ?;
269
+ this. lazy_sync_init ( primitive, init_offset, data) ?;
270
+ interp_ok ( data)
271
+ }
272
+ }
273
+
274
+ /// Get the synchronization primitive associated with the given pointer,
275
+ /// or initialize a new one.
276
+ fn get_sync_or_init < ' a , T : ' static > (
277
+ & ' a mut self ,
278
+ ptr : Pointer ,
279
+ new : impl FnOnce ( & ' a mut MiriMachine < ' tcx > ) -> InterpResult < ' tcx , T > ,
280
+ ) -> InterpResult < ' tcx , & ' a T >
281
+ where
282
+ ' tcx : ' a ,
283
+ {
284
+ let this = self . eval_context_mut ( ) ;
285
+ // Ensure there is memory behind this pointer, so that this allocation
286
+ // is truly the only place where the data could be stored.
287
+ this. check_ptr_access ( ptr, Size :: from_bytes ( 1 ) , CheckInAllocMsg :: InboundsTest ) ?;
288
+
289
+ let ( alloc, offset, _) = this. ptr_get_alloc_id ( ptr, 0 ) ?;
290
+ let ( alloc_extra, machine) = this. get_alloc_extra_mut ( alloc) ?;
291
+ // Due to borrow checker reasons, we have to do the lookup twice.
292
+ if alloc_extra. get_sync :: < T > ( offset) . is_none ( ) {
293
+ let new = new ( machine) ?;
294
+ alloc_extra. sync . insert ( offset, Box :: new ( new) ) ;
295
+ }
296
+ interp_ok ( alloc_extra. get_sync :: < T > ( offset) . unwrap ( ) )
297
+ }
298
+
265
299
#[ inline]
266
300
/// Get the id of the thread that currently owns this lock.
267
301
fn mutex_get_owner ( & mut self , id : MutexId ) -> ThreadId {
0 commit comments