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>).
@@ -133,9 +133,17 @@ struct StoreElement {
133
133
// (partially) uninitialized data.
134
134
val : Scalar < Provenance > ,
135
135
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 {
136
143
/// 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 ,
139
147
}
140
148
141
149
impl StoreBufferAlloc {
@@ -235,18 +243,23 @@ impl<'mir, 'tcx: 'mir> StoreBuffer {
235
243
timestamp : 0 ,
236
244
val : init,
237
245
is_seqcst : false ,
238
- loads : RefCell :: new ( FxHashMap :: default ( ) ) ,
246
+ load_info : RefCell :: new ( LoadInfo :: default ( ) ) ,
239
247
} ;
240
248
ret. buffer . push_back ( store_elem) ;
241
249
ret
242
250
}
243
251
244
252
/// 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
+ ) {
246
259
let store_elem = self . buffer . back ( ) ;
247
260
if let Some ( store_elem) = store_elem {
248
261
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 ) ;
250
263
}
251
264
}
252
265
@@ -276,7 +289,7 @@ impl<'mir, 'tcx: 'mir> StoreBuffer {
276
289
validate ( ) ?;
277
290
278
291
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 ) ;
280
293
Ok ( ( loaded, recency) )
281
294
}
282
295
@@ -321,9 +334,9 @@ impl<'mir, 'tcx: 'mir> StoreBuffer {
321
334
// then we can't read-from anything earlier in modification order.
322
335
// C++20 §6.9.2.2 [intro.races] paragraph 18
323
336
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
+ ) {
327
340
// CoRR: if there was a load from this store which happened-before the current load,
328
341
// then we cannot read-from anything earlier in modification order.
329
342
// C++20 §6.9.2.2 [intro.races] paragraph 16
@@ -340,12 +353,22 @@ impl<'mir, 'tcx: 'mir> StoreBuffer {
340
353
// cannot read-before the last SC store executed before the fence.
341
354
// C++17 §32.4 [atomics.order] paragraph 4
342
355
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
+ {
344
359
// The current SC load cannot read-before the last store sequenced-before
345
360
// the last SC fence.
346
361
// C++17 §32.4 [atomics.order] paragraph 5
347
362
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
+ } ;
349
372
350
373
true
351
374
} )
@@ -386,7 +409,7 @@ impl<'mir, 'tcx: 'mir> StoreBuffer {
386
409
// access
387
410
val,
388
411
is_seqcst,
389
- loads : RefCell :: new ( FxHashMap :: default ( ) ) ,
412
+ load_info : RefCell :: new ( LoadInfo :: default ( ) ) ,
390
413
} ;
391
414
self . buffer . push_back ( store_elem) ;
392
415
if self . buffer . len ( ) > STORE_BUFFER_LIMIT {
@@ -414,8 +437,15 @@ impl StoreElement {
414
437
/// buffer regardless of subsequent loads by the same thread; if the earliest load of another
415
438
/// thread doesn't happen before the current one, then no subsequent load by the other thread
416
439
/// 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] ) ;
419
449
self . val
420
450
}
421
451
}
@@ -475,7 +505,7 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>:
475
505
}
476
506
let range = alloc_range ( base_offset, place. layout . size ) ;
477
507
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 ) ;
479
509
buffer. buffered_write ( new_val, global, threads, atomic == AtomicRwOrd :: SeqCst ) ?;
480
510
}
481
511
Ok ( ( ) )
@@ -582,7 +612,11 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>:
582
612
if let Some ( alloc_buffers) = this. get_alloc_extra ( alloc_id) ?. weak_memory . as_ref ( ) {
583
613
let buffer = alloc_buffers
584
614
. 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
+ ) ;
586
620
}
587
621
}
588
622
Ok ( ( ) )
0 commit comments