41
41
//! on the data-race detection code.
42
42
43
43
use std:: {
44
+ borrow:: Cow ,
44
45
cell:: { Cell , Ref , RefCell , RefMut } ,
45
46
fmt:: Debug ,
46
47
mem,
@@ -167,7 +168,7 @@ pub struct DataRace;
167
168
/// explicitly to reduce memory usage for the
168
169
/// common case where no atomic operations
169
170
/// exists on the memory cell.
170
- #[ derive( Clone , PartialEq , Eq , Default , Debug ) ]
171
+ #[ derive( Clone , PartialEq , Eq , Debug ) ]
171
172
struct AtomicMemoryCellClocks {
172
173
/// The clock-vector of the timestamp of the last atomic
173
174
/// read operation performed by each thread.
@@ -186,6 +187,11 @@ struct AtomicMemoryCellClocks {
186
187
/// happen-before a thread if an acquire-load is
187
188
/// performed on the data.
188
189
sync_vector : VClock ,
190
+
191
+ /// The size of accesses to this atomic location.
192
+ /// We use this to detect non-synchronized mixed-size accesses. Since all accesses must be
193
+ /// aligned to their size, this is sufficient to detect imperfectly overlapping accesses.
194
+ size : Size ,
189
195
}
190
196
191
197
/// Type of write operation: allocating memory
@@ -241,6 +247,17 @@ struct MemoryCellClocks {
241
247
atomic_ops : Option < Box < AtomicMemoryCellClocks > > ,
242
248
}
243
249
250
+ impl AtomicMemoryCellClocks {
251
+ fn new ( size : Size ) -> Self {
252
+ AtomicMemoryCellClocks {
253
+ read_vector : Default :: default ( ) ,
254
+ write_vector : Default :: default ( ) ,
255
+ sync_vector : Default :: default ( ) ,
256
+ size,
257
+ }
258
+ }
259
+ }
260
+
244
261
impl MemoryCellClocks {
245
262
/// Create a new set of clocks representing memory allocated
246
263
/// at a given vector timestamp and index.
@@ -271,11 +288,39 @@ impl MemoryCellClocks {
271
288
self . atomic_ops . as_deref ( )
272
289
}
273
290
274
- /// Load or create the internal atomic memory metadata
275
- /// if it does not exist.
291
+ /// Load the internal atomic memory cells if they exist.
276
292
#[ inline]
277
- fn atomic_mut ( & mut self ) -> & mut AtomicMemoryCellClocks {
278
- self . atomic_ops . get_or_insert_with ( Default :: default)
293
+ fn atomic_mut_unwrap ( & mut self ) -> & mut AtomicMemoryCellClocks {
294
+ self . atomic_ops . as_deref_mut ( ) . unwrap ( )
295
+ }
296
+
297
+ /// Load or create the internal atomic memory metadata if it does not exist. Also ensures we do
298
+ /// not do mixed-size atomic accesses, and updates the recorded atomic access size.
299
+ fn atomic_access (
300
+ & mut self ,
301
+ thread_clocks : & ThreadClockSet ,
302
+ size : Size ,
303
+ ) -> Result < & mut AtomicMemoryCellClocks , DataRace > {
304
+ match self . atomic_ops {
305
+ Some ( ref mut atomic) => {
306
+ // We are good if the size is the same or all atomic accesses are before our current time.
307
+ if atomic. size == size {
308
+ Ok ( atomic)
309
+ } else if atomic. read_vector <= thread_clocks. clock
310
+ && atomic. write_vector <= thread_clocks. clock
311
+ {
312
+ // This is now the new size that must be used for accesses here.
313
+ atomic. size = size;
314
+ Ok ( atomic)
315
+ } else {
316
+ Err ( DataRace )
317
+ }
318
+ }
319
+ None => {
320
+ self . atomic_ops = Some ( Box :: new ( AtomicMemoryCellClocks :: new ( size) ) ) ;
321
+ Ok ( self . atomic_ops . as_mut ( ) . unwrap ( ) )
322
+ }
323
+ }
279
324
}
280
325
281
326
/// Update memory cell data-race tracking for atomic
@@ -285,8 +330,9 @@ impl MemoryCellClocks {
285
330
& mut self ,
286
331
thread_clocks : & mut ThreadClockSet ,
287
332
index : VectorIdx ,
333
+ access_size : Size ,
288
334
) -> Result < ( ) , DataRace > {
289
- self . atomic_read_detect ( thread_clocks, index) ?;
335
+ self . atomic_read_detect ( thread_clocks, index, access_size ) ?;
290
336
if let Some ( atomic) = self . atomic ( ) {
291
337
thread_clocks. clock . join ( & atomic. sync_vector ) ;
292
338
}
@@ -309,8 +355,9 @@ impl MemoryCellClocks {
309
355
& mut self ,
310
356
thread_clocks : & mut ThreadClockSet ,
311
357
index : VectorIdx ,
358
+ access_size : Size ,
312
359
) -> Result < ( ) , DataRace > {
313
- self . atomic_read_detect ( thread_clocks, index) ?;
360
+ self . atomic_read_detect ( thread_clocks, index, access_size ) ?;
314
361
if let Some ( atomic) = self . atomic ( ) {
315
362
thread_clocks. fence_acquire . join ( & atomic. sync_vector ) ;
316
363
}
@@ -323,9 +370,10 @@ impl MemoryCellClocks {
323
370
& mut self ,
324
371
thread_clocks : & ThreadClockSet ,
325
372
index : VectorIdx ,
373
+ access_size : Size ,
326
374
) -> Result < ( ) , DataRace > {
327
- self . atomic_write_detect ( thread_clocks, index) ?;
328
- let atomic = self . atomic_mut ( ) ;
375
+ self . atomic_write_detect ( thread_clocks, index, access_size ) ?;
376
+ let atomic = self . atomic_mut_unwrap ( ) ; // initialized by `atomic_write_detect`
329
377
atomic. sync_vector . clone_from ( & thread_clocks. clock ) ;
330
378
Ok ( ( ) )
331
379
}
@@ -336,14 +384,15 @@ impl MemoryCellClocks {
336
384
& mut self ,
337
385
thread_clocks : & ThreadClockSet ,
338
386
index : VectorIdx ,
387
+ access_size : Size ,
339
388
) -> Result < ( ) , DataRace > {
340
- self . atomic_write_detect ( thread_clocks, index) ?;
389
+ self . atomic_write_detect ( thread_clocks, index, access_size ) ?;
341
390
342
391
// The handling of release sequences was changed in C++20 and so
343
392
// the code here is different to the paper since now all relaxed
344
393
// stores block release sequences. The exception for same-thread
345
394
// relaxed stores has been removed.
346
- let atomic = self . atomic_mut ( ) ;
395
+ let atomic = self . atomic_mut_unwrap ( ) ;
347
396
atomic. sync_vector . clone_from ( & thread_clocks. fence_release ) ;
348
397
Ok ( ( ) )
349
398
}
@@ -354,9 +403,10 @@ impl MemoryCellClocks {
354
403
& mut self ,
355
404
thread_clocks : & ThreadClockSet ,
356
405
index : VectorIdx ,
406
+ access_size : Size ,
357
407
) -> Result < ( ) , DataRace > {
358
- self . atomic_write_detect ( thread_clocks, index) ?;
359
- let atomic = self . atomic_mut ( ) ;
408
+ self . atomic_write_detect ( thread_clocks, index, access_size ) ?;
409
+ let atomic = self . atomic_mut_unwrap ( ) ;
360
410
atomic. sync_vector . join ( & thread_clocks. clock ) ;
361
411
Ok ( ( ) )
362
412
}
@@ -367,9 +417,10 @@ impl MemoryCellClocks {
367
417
& mut self ,
368
418
thread_clocks : & ThreadClockSet ,
369
419
index : VectorIdx ,
420
+ access_size : Size ,
370
421
) -> Result < ( ) , DataRace > {
371
- self . atomic_write_detect ( thread_clocks, index) ?;
372
- let atomic = self . atomic_mut ( ) ;
422
+ self . atomic_write_detect ( thread_clocks, index, access_size ) ?;
423
+ let atomic = self . atomic_mut_unwrap ( ) ;
373
424
atomic. sync_vector . join ( & thread_clocks. fence_release ) ;
374
425
Ok ( ( ) )
375
426
}
@@ -380,9 +431,10 @@ impl MemoryCellClocks {
380
431
& mut self ,
381
432
thread_clocks : & ThreadClockSet ,
382
433
index : VectorIdx ,
434
+ access_size : Size ,
383
435
) -> Result < ( ) , DataRace > {
384
436
log:: trace!( "Atomic read with vectors: {:#?} :: {:#?}" , self , thread_clocks) ;
385
- let atomic = self . atomic_mut ( ) ;
437
+ let atomic = self . atomic_access ( thread_clocks , access_size ) ? ;
386
438
atomic. read_vector . set_at_index ( & thread_clocks. clock , index) ;
387
439
// Make sure the last non-atomic write and all non-atomic reads were before this access.
388
440
if self . write_was_before ( & thread_clocks. clock ) && self . read <= thread_clocks. clock {
@@ -398,9 +450,10 @@ impl MemoryCellClocks {
398
450
& mut self ,
399
451
thread_clocks : & ThreadClockSet ,
400
452
index : VectorIdx ,
453
+ access_size : Size ,
401
454
) -> Result < ( ) , DataRace > {
402
455
log:: trace!( "Atomic write with vectors: {:#?} :: {:#?}" , self , thread_clocks) ;
403
- let atomic = self . atomic_mut ( ) ;
456
+ let atomic = self . atomic_access ( thread_clocks , access_size ) ? ;
404
457
atomic. write_vector . set_at_index ( & thread_clocks. clock , index) ;
405
458
// Make sure the last non-atomic write and all non-atomic reads were before this access.
406
459
if self . write_was_before ( & thread_clocks. clock ) && self . read <= thread_clocks. clock {
@@ -802,26 +855,44 @@ impl VClockAlloc {
802
855
mem_clocks : & MemoryCellClocks ,
803
856
action : & str ,
804
857
is_atomic : bool ,
858
+ access_size : Size ,
805
859
ptr_dbg : Pointer < AllocId > ,
806
860
) -> InterpResult < ' tcx > {
807
861
let ( current_index, current_clocks) = global. current_thread_state ( thread_mgr) ;
862
+ let mut action = Cow :: Borrowed ( action) ;
808
863
let write_clock;
809
864
#[ rustfmt:: skip]
810
865
let ( other_action, other_thread, other_clock) =
811
866
if mem_clocks. write . 1 > current_clocks. clock [ mem_clocks. write . 0 ] {
812
867
write_clock = mem_clocks. write ( ) ;
813
- ( mem_clocks. write_type . get_descriptor ( ) , mem_clocks. write . 0 , & write_clock)
868
+ ( mem_clocks. write_type . get_descriptor ( ) . to_owned ( ) , mem_clocks. write . 0 , & write_clock)
814
869
} else if let Some ( idx) = Self :: find_gt_index ( & mem_clocks. read , & current_clocks. clock ) {
815
- ( "Read" , idx, & mem_clocks. read )
870
+ ( format ! ( "Read" ) , idx, & mem_clocks. read )
871
+ } else if is_atomic && let Some ( atomic) = mem_clocks. atomic ( ) && atomic. size != access_size {
872
+ // This is only a race if we are not synchronized with all atomic accesses, so find
873
+ // the one we are not synchronized with.
874
+ action = format ! ( "{}-byte (different-size) {action}" , access_size. bytes( ) ) . into ( ) ;
875
+ if let Some ( idx) = Self :: find_gt_index ( & atomic. write_vector , & current_clocks. clock )
876
+ {
877
+ ( format ! ( "{}-byte Atomic Store" , atomic. size. bytes( ) ) , idx, & atomic. write_vector )
878
+ } else if let Some ( idx) =
879
+ Self :: find_gt_index ( & atomic. read_vector , & current_clocks. clock )
880
+ {
881
+ ( format ! ( "{}-byte Atomic Load" , atomic. size. bytes( ) ) , idx, & atomic. read_vector )
882
+ } else {
883
+ unreachable ! (
884
+ "Failed to report data-race for mixed-size access: no race found"
885
+ )
886
+ }
816
887
} else if !is_atomic {
817
888
if let Some ( atomic) = mem_clocks. atomic ( ) {
818
889
if let Some ( idx) = Self :: find_gt_index ( & atomic. write_vector , & current_clocks. clock )
819
890
{
820
- ( "Atomic Store" , idx, & atomic. write_vector )
891
+ ( format ! ( "Atomic Store" ) , idx, & atomic. write_vector )
821
892
} else if let Some ( idx) =
822
893
Self :: find_gt_index ( & atomic. read_vector , & current_clocks. clock )
823
894
{
824
- ( "Atomic Load" , idx, & atomic. read_vector )
895
+ ( format ! ( "Atomic Load" ) , idx, & atomic. read_vector )
825
896
} else {
826
897
unreachable ! (
827
898
"Failed to report data-race for non-atomic operation: no race found"
@@ -905,7 +976,8 @@ impl VClockAlloc {
905
976
& machine. threads ,
906
977
mem_clocks,
907
978
"Read" ,
908
- false ,
979
+ /* is_atomic */ false ,
980
+ access_range. size ,
909
981
Pointer :: new ( alloc_id, Size :: from_bytes ( mem_clocks_range. start ) ) ,
910
982
) ;
911
983
}
@@ -944,7 +1016,8 @@ impl VClockAlloc {
944
1016
& machine. threads ,
945
1017
mem_clocks,
946
1018
write_type. get_descriptor ( ) ,
947
- false ,
1019
+ /* is_atomic */ false ,
1020
+ access_range. size ,
948
1021
Pointer :: new ( alloc_id, Size :: from_bytes ( mem_clocks_range. start ) ) ,
949
1022
) ;
950
1023
}
@@ -1072,9 +1145,9 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> {
1072
1145
"Atomic Load" ,
1073
1146
move |memory, clocks, index, atomic| {
1074
1147
if atomic == AtomicReadOrd :: Relaxed {
1075
- memory. load_relaxed ( & mut * clocks, index)
1148
+ memory. load_relaxed ( & mut * clocks, index, place . layout . size )
1076
1149
} else {
1077
- memory. load_acquire ( & mut * clocks, index)
1150
+ memory. load_acquire ( & mut * clocks, index, place . layout . size )
1078
1151
}
1079
1152
} ,
1080
1153
)
@@ -1095,9 +1168,9 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> {
1095
1168
"Atomic Store" ,
1096
1169
move |memory, clocks, index, atomic| {
1097
1170
if atomic == AtomicWriteOrd :: Relaxed {
1098
- memory. store_relaxed ( clocks, index)
1171
+ memory. store_relaxed ( clocks, index, place . layout . size )
1099
1172
} else {
1100
- memory. store_release ( clocks, index)
1173
+ memory. store_release ( clocks, index, place . layout . size )
1101
1174
}
1102
1175
} ,
1103
1176
)
@@ -1117,14 +1190,14 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> {
1117
1190
this. validate_overlapping_atomic ( place) ?;
1118
1191
this. validate_atomic_op ( place, atomic, "Atomic RMW" , move |memory, clocks, index, _| {
1119
1192
if acquire {
1120
- memory. load_acquire ( clocks, index) ?;
1193
+ memory. load_acquire ( clocks, index, place . layout . size ) ?;
1121
1194
} else {
1122
- memory. load_relaxed ( clocks, index) ?;
1195
+ memory. load_relaxed ( clocks, index, place . layout . size ) ?;
1123
1196
}
1124
1197
if release {
1125
- memory. rmw_release ( clocks, index)
1198
+ memory. rmw_release ( clocks, index, place . layout . size )
1126
1199
} else {
1127
- memory. rmw_relaxed ( clocks, index)
1200
+ memory. rmw_relaxed ( clocks, index, place . layout . size )
1128
1201
}
1129
1202
} )
1130
1203
}
@@ -1175,7 +1248,8 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> {
1175
1248
& this. machine . threads ,
1176
1249
mem_clocks,
1177
1250
description,
1178
- true ,
1251
+ /* is_atomic */ true ,
1252
+ place. layout . size ,
1179
1253
Pointer :: new (
1180
1254
alloc_id,
1181
1255
Size :: from_bytes ( mem_clocks_range. start ) ,
0 commit comments