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>).
13
13
//!
14
+ //! A modification is made to the paper's model to partially address C++20 changes.
15
+ //! Specifically, if an SC load reads from an atomic store of any ordering, then a later SC load cannot read from
16
+ //! an earlier store in the location's modification order. This is to prevent creating a backwards S edge from the second
17
+ //! load to the first, as a result of C++20's coherence-ordered before rules.
18
+ //!
14
19
//! Rust follows the C++20 memory model (except for the Consume ordering and some operations not performable through C++'s
15
20
//! std::atomic<T> API). It is therefore possible for this implementation to generate behaviours never observable when the
16
21
//! same program is compiled and run natively. Unfortunately, no literature exists at the time of writing which proposes
@@ -133,9 +138,17 @@ struct StoreElement {
133
138
// (partially) uninitialized data.
134
139
val: Scalar<Provenance>,
135
140
141
+ /// Metadata about loads from this store element,
142
+ /// behind a RefCell to keep load op take &self
143
+ load_info: RefCell<LoadInfo>,
144
+ }
145
+
146
+ #[derive(Debug, Clone, PartialEq, Eq, Default)]
147
+ struct LoadInfo {
136
148
/// 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>>,
149
+ timestamps: FxHashMap<VectorIdx, VTimestamp>,
150
+ /// Whether this store element has been read by an SC load
151
+ sc_loaded: bool,
139
152
}
140
153
141
154
impl StoreBufferAlloc {
@@ -235,18 +248,23 @@ impl<'mir, 'tcx: 'mir> StoreBuffer {
235
248
timestamp: 0,
236
249
val: init,
237
250
is_seqcst: false,
238
- loads : RefCell::new(FxHashMap ::default()),
251
+ load_info : RefCell::new(LoadInfo ::default()),
239
252
};
240
253
ret.buffer.push_back(store_elem);
241
254
ret
242
255
}
243
256
244
257
/// Reads from the last store in modification order
245
- fn read_from_last_store(&self, global: &DataRaceState, thread_mgr: &ThreadManager<'_, '_>) {
258
+ fn read_from_last_store(
259
+ &self,
260
+ global: &DataRaceState,
261
+ thread_mgr: &ThreadManager<'_, '_>,
262
+ is_seqcst: bool,
263
+ ) {
246
264
let store_elem = self.buffer.back();
247
265
if let Some(store_elem) = store_elem {
248
266
let (index, clocks) = global.current_thread_state(thread_mgr);
249
- store_elem.load_impl(index, &clocks);
267
+ store_elem.load_impl(index, &clocks, is_seqcst );
250
268
}
251
269
}
252
270
@@ -276,7 +294,7 @@ impl<'mir, 'tcx: 'mir> StoreBuffer {
276
294
validate()?;
277
295
278
296
let (index, clocks) = global.current_thread_state(thread_mgr);
279
- let loaded = store_elem.load_impl(index, &clocks);
297
+ let loaded = store_elem.load_impl(index, &clocks, is_seqcst );
280
298
Ok((loaded, recency))
281
299
}
282
300
@@ -293,6 +311,7 @@ impl<'mir, 'tcx: 'mir> StoreBuffer {
293
311
Ok(())
294
312
}
295
313
314
+ #[allow(clippy::if_same_then_else, clippy::needless_bool)]
296
315
/// Selects a valid store element in the buffer.
297
316
fn fetch_store<R: rand::Rng + ?Sized>(
298
317
&self,
@@ -319,34 +338,43 @@ impl<'mir, 'tcx: 'mir> StoreBuffer {
319
338
keep_searching = if store_elem.timestamp <= clocks.clock[store_elem.store_index] {
320
339
// CoWR: if a store happens-before the current load,
321
340
// then we can't read-from anything earlier in modification order.
322
- log::info!("Stopping due to coherent write-read");
341
+ // C++20 §6.9.2.2 [intro.races] paragraph 18
323
342
false
324
- } else if store_elem.loads .borrow().iter().any(|(&load_index, &load_timestamp)| {
325
- load_timestamp <= clocks.clock[load_index]
326
- } ) {
343
+ } else if store_elem.load_info .borrow().timestamps. iter().any(
344
+ |(&load_index, & load_timestamp)| load_timestamp <= clocks.clock[load_index],
345
+ ) {
327
346
// CoRR: if there was a load from this store which happened-before the current load,
328
347
// then we cannot read-from anything earlier in modification order.
329
- log::info!("Stopping due to coherent read-read");
348
+ // C++20 §6.9.2.2 [intro.races] paragraph 16
330
349
false
331
350
} else if store_elem.timestamp <= clocks.fence_seqcst[store_elem.store_index] {
332
- // The current load, which may be sequenced-after an SC fence, can only read-from
333
- // the last store sequenced-before an SC fence in another thread (or any stores
334
- // later than that SC fence)
335
- log::info!("Stopping due to coherent load sequenced after sc fence");
351
+ // The current load, which may be sequenced-after an SC fence, cannot read-before
352
+ // the last store sequenced-before an SC fence in another thread.
353
+ // C++17 §32.4 [atomics.order] paragraph 6
336
354
false
337
355
} else if store_elem.timestamp <= clocks.write_seqcst[store_elem.store_index]
338
356
&& store_elem.is_seqcst
339
357
{
340
- // The current non-SC load can only read-from the latest SC store (or any stores later than that
341
- // SC store)
342
- log::info!("Stopping due to needing to load from the last SC store");
358
+ // The current non-SC load, which may be sequenced-after an SC fence,
359
+ // cannot read-before the last SC store executed before the fence.
360
+ // C++17 §32.4 [atomics.order] paragraph 4
361
+ false
362
+ } else if is_seqcst
363
+ && store_elem.timestamp <= clocks.read_seqcst[store_elem.store_index]
364
+ {
365
+ // The current SC load cannot read-before the last store sequenced-before
366
+ // the last SC fence.
367
+ // C++17 §32.4 [atomics.order] paragraph 5
343
368
false
344
- } else if is_seqcst && store_elem.timestamp <= clocks.read_seqcst[store_elem.store_index] {
345
- // The current SC load can only read-from the last store sequenced-before
346
- // the last SC fence (or any stores later than the SC fence)
347
- log::info!("Stopping due to sc load needing to load from the last SC store before an SC fence");
369
+ } else if is_seqcst && store_elem.load_info.borrow().sc_loaded {
370
+ // The current SC load cannot read-before a store that an earlier SC load has observed.
371
+ // See https://github.com/rust-lang/miri/issues/2301#issuecomment-1222720427
372
+ // Consequences of C++20 §31.4 [atomics.order] paragraph 3.1, 3.3 (coherence-ordered before)
373
+ // and 4.1 (coherence-ordered before between SC makes global total order S)
348
374
false
349
- } else {true};
375
+ } else {
376
+ true
377
+ };
350
378
351
379
true
352
380
})
@@ -387,7 +415,7 @@ impl<'mir, 'tcx: 'mir> StoreBuffer {
387
415
// access
388
416
val,
389
417
is_seqcst,
390
- loads : RefCell::new(FxHashMap ::default()),
418
+ load_info : RefCell::new(LoadInfo ::default()),
391
419
};
392
420
self.buffer.push_back(store_elem);
393
421
if self.buffer.len() > STORE_BUFFER_LIMIT {
@@ -415,8 +443,15 @@ impl StoreElement {
415
443
/// buffer regardless of subsequent loads by the same thread; if the earliest load of another
416
444
/// thread doesn't happen before the current one, then no subsequent load by the other thread
417
445
/// can happen before the current one.
418
- fn load_impl(&self, index: VectorIdx, clocks: &ThreadClockSet) -> Scalar<Provenance> {
419
- let _ = self.loads.borrow_mut().try_insert(index, clocks.clock[index]);
446
+ fn load_impl(
447
+ &self,
448
+ index: VectorIdx,
449
+ clocks: &ThreadClockSet,
450
+ is_seqcst: bool,
451
+ ) -> Scalar<Provenance> {
452
+ let mut load_info = self.load_info.borrow_mut();
453
+ load_info.sc_loaded |= is_seqcst;
454
+ let _ = load_info.timestamps.try_insert(index, clocks.clock[index]);
420
455
self.val
421
456
}
422
457
}
@@ -476,7 +511,7 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>:
476
511
}
477
512
let range = alloc_range(base_offset, place.layout.size);
478
513
let buffer = alloc_buffers.get_or_create_store_buffer_mut(range, init)?;
479
- buffer.read_from_last_store(global, threads);
514
+ buffer.read_from_last_store(global, threads, atomic == AtomicRwOrd::SeqCst );
480
515
buffer.buffered_write(new_val, global, threads, atomic == AtomicRwOrd::SeqCst)?;
481
516
}
482
517
Ok(())
@@ -583,7 +618,11 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>:
583
618
if let Some(alloc_buffers) = this.get_alloc_extra(alloc_id)?.weak_memory.as_ref() {
584
619
let buffer = alloc_buffers
585
620
.get_or_create_store_buffer(alloc_range(base_offset, size), init)?;
586
- buffer.read_from_last_store(global, &this.machine.threads);
621
+ buffer.read_from_last_store(
622
+ global,
623
+ &this.machine.threads,
624
+ atomic == AtomicReadOrd::SeqCst,
625
+ );
587
626
}
588
627
}
589
628
Ok(())
0 commit comments