66//! but it is incapable of producing all possible weak behaviours allowed by the model. There are
77//! certain weak behaviours observable on real hardware but not while using this.
88//!
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
1010//! and fences introduced by P0668 (<https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0668r5.html>).
1111//! This implementation is not fully correct under the revised C++20 model and may generate behaviours C++20
1212//! disallows (<https://github.com/rust-lang/miri/issues/2301>).
@@ -134,9 +134,17 @@ struct StoreElement {
134134 // partially initialized data.
135135 val : ScalarMaybeUninit < Provenance > ,
136136
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 {
137144 /// 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 ,
140148}
141149
142150impl StoreBufferAlloc {
@@ -236,18 +244,23 @@ impl<'mir, 'tcx: 'mir> StoreBuffer {
236244 timestamp : 0 ,
237245 val : init,
238246 is_seqcst : false ,
239- loads : RefCell :: new ( FxHashMap :: default ( ) ) ,
247+ load_info : RefCell :: new ( LoadInfo :: default ( ) ) ,
240248 } ;
241249 ret. buffer . push_back ( store_elem) ;
242250 ret
243251 }
244252
245253 /// 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+ ) {
247260 let store_elem = self . buffer . back ( ) ;
248261 if let Some ( store_elem) = store_elem {
249262 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 ) ;
251264 }
252265 }
253266
@@ -277,7 +290,7 @@ impl<'mir, 'tcx: 'mir> StoreBuffer {
277290 validate ( ) ?;
278291
279292 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 ) ;
281294 Ok ( ( loaded, recency) )
282295 }
283296
@@ -322,9 +335,9 @@ impl<'mir, 'tcx: 'mir> StoreBuffer {
322335 // then we can't read-from anything earlier in modification order.
323336 // C++20 §6.9.2.2 [intro.races] paragraph 18
324337 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+ ) {
328341 // CoRR: if there was a load from this store which happened-before the current load,
329342 // then we cannot read-from anything earlier in modification order.
330343 // C++20 §6.9.2.2 [intro.races] paragraph 16
@@ -341,12 +354,22 @@ impl<'mir, 'tcx: 'mir> StoreBuffer {
341354 // cannot read-before the last SC store executed before the fence.
342355 // C++17 §32.4 [atomics.order] paragraph 4
343356 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+ {
345360 // The current SC load cannot read-before the last store sequenced-before
346361 // the last SC fence.
347362 // C++17 §32.4 [atomics.order] paragraph 5
348363 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+ } ;
350373
351374 true
352375 } )
@@ -387,7 +410,7 @@ impl<'mir, 'tcx: 'mir> StoreBuffer {
387410 // access
388411 val,
389412 is_seqcst,
390- loads : RefCell :: new ( FxHashMap :: default ( ) ) ,
413+ load_info : RefCell :: new ( LoadInfo :: default ( ) ) ,
391414 } ;
392415 self . buffer . push_back ( store_elem) ;
393416 if self . buffer . len ( ) > STORE_BUFFER_LIMIT {
@@ -419,8 +442,11 @@ impl StoreElement {
419442 & self ,
420443 index : VectorIdx ,
421444 clocks : & ThreadClockSet ,
445+ is_seqcst : bool ,
422446 ) -> 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] ) ;
424450 self . val
425451 }
426452}
@@ -480,7 +506,7 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>:
480506 }
481507 let range = alloc_range ( base_offset, place. layout . size ) ;
482508 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 ) ;
484510 buffer. buffered_write ( new_val, global, threads, atomic == AtomicRwOrd :: SeqCst ) ?;
485511 }
486512 Ok ( ( ) )
@@ -587,7 +613,11 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>:
587613 if let Some ( alloc_buffers) = this. get_alloc_extra ( alloc_id) ?. weak_memory . as_ref ( ) {
588614 let buffer = alloc_buffers
589615 . 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+ ) ;
591621 }
592622 }
593623 Ok ( ( ) )
0 commit comments