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>).
@@ -133,9 +133,17 @@ struct StoreElement {
133133 // (partially) uninitialized data.
134134 val : Scalar < Provenance > ,
135135
136+ /// Metadata about loads from this store element,
137+ /// behind a RefCell to keep load op take &self
138+ load_info : RefCell < LoadInfo > ,
139+ }
140+
141+ #[ derive( Debug , Clone , PartialEq , Eq , Default ) ]
142+ struct LoadInfo {
136143 /// Timestamp of first loads from this store element by each thread
137- /// Behind a RefCell to keep load op take &self
138- loads : RefCell < FxHashMap < VectorIdx , VTimestamp > > ,
144+ timestamps : FxHashMap < VectorIdx , VTimestamp > ,
145+ /// Whether this store element has been read by an SC load
146+ sc_loaded : bool ,
139147}
140148
141149impl StoreBufferAlloc {
@@ -235,18 +243,23 @@ impl<'mir, 'tcx: 'mir> StoreBuffer {
235243 timestamp : 0 ,
236244 val : init,
237245 is_seqcst : false ,
238- loads : RefCell :: new ( FxHashMap :: default ( ) ) ,
246+ load_info : RefCell :: new ( LoadInfo :: default ( ) ) ,
239247 } ;
240248 ret. buffer . push_back ( store_elem) ;
241249 ret
242250 }
243251
244252 /// Reads from the last store in modification order
245- fn read_from_last_store ( & self , global : & DataRaceState , thread_mgr : & ThreadManager < ' _ , ' _ > ) {
253+ fn read_from_last_store (
254+ & self ,
255+ global : & DataRaceState ,
256+ thread_mgr : & ThreadManager < ' _ , ' _ > ,
257+ is_seqcst : bool ,
258+ ) {
246259 let store_elem = self . buffer . back ( ) ;
247260 if let Some ( store_elem) = store_elem {
248261 let ( index, clocks) = global. current_thread_state ( thread_mgr) ;
249- store_elem. load_impl ( index, & clocks) ;
262+ store_elem. load_impl ( index, & clocks, is_seqcst ) ;
250263 }
251264 }
252265
@@ -276,7 +289,7 @@ impl<'mir, 'tcx: 'mir> StoreBuffer {
276289 validate ( ) ?;
277290
278291 let ( index, clocks) = global. current_thread_state ( thread_mgr) ;
279- let loaded = store_elem. load_impl ( index, & clocks) ;
292+ let loaded = store_elem. load_impl ( index, & clocks, is_seqcst ) ;
280293 Ok ( ( loaded, recency) )
281294 }
282295
@@ -321,9 +334,9 @@ impl<'mir, 'tcx: 'mir> StoreBuffer {
321334 // then we can't read-from anything earlier in modification order.
322335 // C++20 §6.9.2.2 [intro.races] paragraph 18
323336 false
324- } else if store_elem. loads . borrow ( ) . iter ( ) . any ( | ( & load_index , & load_timestamp ) | {
325- load_timestamp <= clocks. clock [ load_index]
326- } ) {
337+ } else if store_elem. load_info . borrow ( ) . timestamps . iter ( ) . any (
338+ | ( & load_index , & load_timestamp) | load_timestamp <= clocks. clock [ load_index] ,
339+ ) {
327340 // CoRR: if there was a load from this store which happened-before the current load,
328341 // then we cannot read-from anything earlier in modification order.
329342 // C++20 §6.9.2.2 [intro.races] paragraph 16
@@ -340,12 +353,22 @@ impl<'mir, 'tcx: 'mir> StoreBuffer {
340353 // cannot read-before the last SC store executed before the fence.
341354 // C++17 §32.4 [atomics.order] paragraph 4
342355 false
343- } else if is_seqcst && store_elem. timestamp <= clocks. read_seqcst [ store_elem. store_index ] {
356+ } else if is_seqcst
357+ && store_elem. timestamp <= clocks. read_seqcst [ store_elem. store_index ]
358+ {
344359 // The current SC load cannot read-before the last store sequenced-before
345360 // the last SC fence.
346361 // C++17 §32.4 [atomics.order] paragraph 5
347362 false
348- } else { true } ;
363+ } else if is_seqcst && store_elem. load_info . borrow ( ) . sc_loaded {
364+ // The current SC load cannot read-before a store that an earlier SC load has observed.
365+ // See https://github.com/rust-lang/miri/issues/2301#issuecomment-1222720427
366+ // Consequences of C++20 §31.4 [atomics.order] paragraph 3.1, 3.3 (coherence-ordered before)
367+ // and 4.1 (coherence-ordered before between SC makes global total order S)
368+ false
369+ } else {
370+ true
371+ } ;
349372
350373 true
351374 } )
@@ -386,7 +409,7 @@ impl<'mir, 'tcx: 'mir> StoreBuffer {
386409 // access
387410 val,
388411 is_seqcst,
389- loads : RefCell :: new ( FxHashMap :: default ( ) ) ,
412+ load_info : RefCell :: new ( LoadInfo :: default ( ) ) ,
390413 } ;
391414 self . buffer . push_back ( store_elem) ;
392415 if self . buffer . len ( ) > STORE_BUFFER_LIMIT {
@@ -414,8 +437,15 @@ impl StoreElement {
414437 /// buffer regardless of subsequent loads by the same thread; if the earliest load of another
415438 /// thread doesn't happen before the current one, then no subsequent load by the other thread
416439 /// can happen before the current one.
417- fn load_impl ( & self , index : VectorIdx , clocks : & ThreadClockSet ) -> Scalar < Provenance > {
418- let _ = self . loads . borrow_mut ( ) . try_insert ( index, clocks. clock [ index] ) ;
440+ fn load_impl (
441+ & self ,
442+ index : VectorIdx ,
443+ clocks : & ThreadClockSet ,
444+ is_seqcst : bool ,
445+ ) -> Scalar < Provenance > {
446+ let mut load_info = self . load_info . borrow_mut ( ) ;
447+ load_info. sc_loaded |= is_seqcst;
448+ let _ = load_info. timestamps . try_insert ( index, clocks. clock [ index] ) ;
419449 self . val
420450 }
421451}
@@ -475,7 +505,7 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>:
475505 }
476506 let range = alloc_range ( base_offset, place. layout . size ) ;
477507 let buffer = alloc_buffers. get_or_create_store_buffer_mut ( range, init) ?;
478- buffer. read_from_last_store ( global, threads) ;
508+ buffer. read_from_last_store ( global, threads, atomic == AtomicRwOrd :: SeqCst ) ;
479509 buffer. buffered_write ( new_val, global, threads, atomic == AtomicRwOrd :: SeqCst ) ?;
480510 }
481511 Ok ( ( ) )
@@ -582,7 +612,11 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>:
582612 if let Some ( alloc_buffers) = this. get_alloc_extra ( alloc_id) ?. weak_memory . as_ref ( ) {
583613 let buffer = alloc_buffers
584614 . get_or_create_store_buffer ( alloc_range ( base_offset, size) , init) ?;
585- buffer. read_from_last_store ( global, & this. machine . threads ) ;
615+ buffer. read_from_last_store (
616+ global,
617+ & this. machine . threads ,
618+ atomic == AtomicReadOrd :: SeqCst ,
619+ ) ;
586620 }
587621 }
588622 Ok ( ( ) )
0 commit comments