6
6
//! but it is incapable of producing all possible weak behaviours allowed by the model. There are
7
7
//! certain weak behaviours observable on real hardware but not while using this.
8
8
//!
9
- //! Note that this implementation does not take into account of C++20's memory model revision to SC accesses
9
+ //! Note that this implementation does not fully take into account of C++20's memory model revision to SC accesses
10
10
//! and fences introduced by P0668 (<https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0668r5.html>).
11
11
//! This implementation is not fully correct under the revised C++20 model and may generate behaviours C++20
12
12
//! disallows (<https://github.com/rust-lang/miri/issues/2301>).
@@ -134,9 +134,17 @@ struct StoreElement {
134
134
// partially initialized data.
135
135
val : ScalarMaybeUninit < Provenance > ,
136
136
137
+ /// Metadata about loads from this store element,
138
+ /// behind a RefCell to keep load op take &self
139
+ load_info : RefCell < LoadInfo > ,
140
+ }
141
+
142
+ #[ derive( Debug , Clone , PartialEq , Eq , Default ) ]
143
+ struct LoadInfo {
137
144
/// Timestamp of first loads from this store element by each thread
138
- /// Behind a RefCell to keep load op take &self
139
- loads : RefCell < FxHashMap < VectorIdx , VTimestamp > > ,
145
+ timestamps : FxHashMap < VectorIdx , VTimestamp > ,
146
+ /// Whether this store element has been read by an SC load
147
+ sc_loaded : bool ,
140
148
}
141
149
142
150
impl StoreBufferAlloc {
@@ -236,18 +244,23 @@ impl<'mir, 'tcx: 'mir> StoreBuffer {
236
244
timestamp : 0 ,
237
245
val : init,
238
246
is_seqcst : false ,
239
- loads : RefCell :: new ( FxHashMap :: default ( ) ) ,
247
+ load_info : RefCell :: new ( LoadInfo :: default ( ) ) ,
240
248
} ;
241
249
ret. buffer . push_back ( store_elem) ;
242
250
ret
243
251
}
244
252
245
253
/// Reads from the last store in modification order
246
- fn read_from_last_store ( & self , global : & DataRaceState , thread_mgr : & ThreadManager < ' _ , ' _ > ) {
254
+ fn read_from_last_store (
255
+ & self ,
256
+ global : & DataRaceState ,
257
+ thread_mgr : & ThreadManager < ' _ , ' _ > ,
258
+ is_seqcst : bool ,
259
+ ) {
247
260
let store_elem = self . buffer . back ( ) ;
248
261
if let Some ( store_elem) = store_elem {
249
262
let ( index, clocks) = global. current_thread_state ( thread_mgr) ;
250
- store_elem. load_impl ( index, & clocks) ;
263
+ store_elem. load_impl ( index, & clocks, is_seqcst ) ;
251
264
}
252
265
}
253
266
@@ -277,7 +290,7 @@ impl<'mir, 'tcx: 'mir> StoreBuffer {
277
290
validate ( ) ?;
278
291
279
292
let ( index, clocks) = global. current_thread_state ( thread_mgr) ;
280
- let loaded = store_elem. load_impl ( index, & clocks) ;
293
+ let loaded = store_elem. load_impl ( index, & clocks, is_seqcst ) ;
281
294
Ok ( ( loaded, recency) )
282
295
}
283
296
@@ -322,9 +335,9 @@ impl<'mir, 'tcx: 'mir> StoreBuffer {
322
335
// then we can't read-from anything earlier in modification order.
323
336
// C++20 §6.9.2.2 [intro.races] paragraph 18
324
337
false
325
- } else if store_elem. loads . borrow ( ) . iter ( ) . any ( | ( & load_index , & load_timestamp ) | {
326
- load_timestamp <= clocks. clock [ load_index]
327
- } ) {
338
+ } else if store_elem. load_info . borrow ( ) . timestamps . iter ( ) . any (
339
+ | ( & load_index , & load_timestamp) | load_timestamp <= clocks. clock [ load_index] ,
340
+ ) {
328
341
// CoRR: if there was a load from this store which happened-before the current load,
329
342
// then we cannot read-from anything earlier in modification order.
330
343
// C++20 §6.9.2.2 [intro.races] paragraph 16
@@ -341,12 +354,22 @@ impl<'mir, 'tcx: 'mir> StoreBuffer {
341
354
// cannot read-before the last SC store executed before the fence.
342
355
// C++17 §32.4 [atomics.order] paragraph 4
343
356
false
344
- } else if is_seqcst && store_elem. timestamp <= clocks. read_seqcst [ store_elem. store_index ] {
357
+ } else if is_seqcst
358
+ && store_elem. timestamp <= clocks. read_seqcst [ store_elem. store_index ]
359
+ {
345
360
// The current SC load cannot read-before the last store sequenced-before
346
361
// the last SC fence.
347
362
// C++17 §32.4 [atomics.order] paragraph 5
348
363
false
349
- } else { true } ;
364
+ } else if is_seqcst && store_elem. load_info . borrow ( ) . sc_loaded {
365
+ // The current SC load cannot read-before a store that an earlier SC load has observed.
366
+ // See https://github.com/rust-lang/miri/issues/2301#issuecomment-1222720427
367
+ // Consequences of C++20 §31.4 [atomics.order] paragraph 3.1, 3.3 (coherence-ordered before)
368
+ // and 4.1 (coherence-ordered before between SC makes global total order S)
369
+ false
370
+ } else {
371
+ true
372
+ } ;
350
373
351
374
true
352
375
} )
@@ -387,7 +410,7 @@ impl<'mir, 'tcx: 'mir> StoreBuffer {
387
410
// access
388
411
val,
389
412
is_seqcst,
390
- loads : RefCell :: new ( FxHashMap :: default ( ) ) ,
413
+ load_info : RefCell :: new ( LoadInfo :: default ( ) ) ,
391
414
} ;
392
415
self . buffer . push_back ( store_elem) ;
393
416
if self . buffer . len ( ) > STORE_BUFFER_LIMIT {
@@ -419,8 +442,11 @@ impl StoreElement {
419
442
& self ,
420
443
index : VectorIdx ,
421
444
clocks : & ThreadClockSet ,
445
+ is_seqcst : bool ,
422
446
) -> ScalarMaybeUninit < Provenance > {
423
- let _ = self . loads . borrow_mut ( ) . try_insert ( index, clocks. clock [ index] ) ;
447
+ let mut load_info = self . load_info . borrow_mut ( ) ;
448
+ load_info. sc_loaded |= is_seqcst;
449
+ let _ = load_info. timestamps . try_insert ( index, clocks. clock [ index] ) ;
424
450
self . val
425
451
}
426
452
}
@@ -480,7 +506,7 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>:
480
506
}
481
507
let range = alloc_range ( base_offset, place. layout . size ) ;
482
508
let buffer = alloc_buffers. get_or_create_store_buffer_mut ( range, init) ?;
483
- buffer. read_from_last_store ( global, threads) ;
509
+ buffer. read_from_last_store ( global, threads, atomic == AtomicRwOrd :: SeqCst ) ;
484
510
buffer. buffered_write ( new_val, global, threads, atomic == AtomicRwOrd :: SeqCst ) ?;
485
511
}
486
512
Ok ( ( ) )
@@ -587,7 +613,11 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>:
587
613
if let Some ( alloc_buffers) = this. get_alloc_extra ( alloc_id) ?. weak_memory . as_ref ( ) {
588
614
let buffer = alloc_buffers
589
615
. get_or_create_store_buffer ( alloc_range ( base_offset, size) , init) ?;
590
- buffer. read_from_last_store ( global, & this. machine . threads ) ;
616
+ buffer. read_from_last_store (
617
+ global,
618
+ & this. machine . threads ,
619
+ atomic == AtomicReadOrd :: SeqCst ,
620
+ ) ;
591
621
}
592
622
}
593
623
Ok ( ( ) )
0 commit comments