Skip to content

Commit bcd7aa5

Browse files
committed
clean up imperfect overlap detection in weak-mem emulation
1 parent 5fd979f commit bcd7aa5

File tree

6 files changed

+28
-90
lines changed

6 files changed

+28
-90
lines changed

src/concurrency/data_race.rs

-38
Original file line numberDiff line numberDiff line change
@@ -339,15 +339,6 @@ impl MemoryCellClocks {
339339
Ok(())
340340
}
341341

342-
/// Checks if the memory cell access is ordered with all prior atomic reads and writes
343-
fn race_free_with_atomic(&self, thread_clocks: &ThreadClockSet) -> bool {
344-
if let Some(atomic) = self.atomic() {
345-
atomic.read_vector <= thread_clocks.clock && atomic.write_vector <= thread_clocks.clock
346-
} else {
347-
true
348-
}
349-
}
350-
351342
/// Update memory cell data-race tracking for atomic
352343
/// load relaxed semantics, is a no-op if this memory was
353344
/// not used previously as atomic memory.
@@ -542,7 +533,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> {
542533
// the *value* (including the associated provenance if this is an AtomicPtr) at this location.
543534
// Only metadata on the location itself is used.
544535
let scalar = this.allow_data_races_ref(move |this| this.read_scalar(place))?;
545-
this.validate_overlapping_atomic(place)?;
546536
this.buffered_atomic_read(place, atomic, scalar, || {
547537
this.validate_atomic_load(place, atomic)
548538
})
@@ -558,7 +548,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> {
558548
let this = self.eval_context_mut();
559549
this.atomic_access_check(dest)?;
560550

561-
this.validate_overlapping_atomic(dest)?;
562551
this.allow_data_races_mut(move |this| this.write_scalar(val, dest))?;
563552
this.validate_atomic_store(dest, atomic)?;
564553
// FIXME: it's not possible to get the value before write_scalar. A read_scalar will cause
@@ -581,7 +570,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> {
581570
let this = self.eval_context_mut();
582571
this.atomic_access_check(place)?;
583572

584-
this.validate_overlapping_atomic(place)?;
585573
let old = this.allow_data_races_mut(|this| this.read_immediate(place))?;
586574

587575
// Atomics wrap around on overflow.
@@ -606,7 +594,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> {
606594
let this = self.eval_context_mut();
607595
this.atomic_access_check(place)?;
608596

609-
this.validate_overlapping_atomic(place)?;
610597
let old = this.allow_data_races_mut(|this| this.read_scalar(place))?;
611598
this.allow_data_races_mut(|this| this.write_scalar(new, place))?;
612599

@@ -628,7 +615,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> {
628615
let this = self.eval_context_mut();
629616
this.atomic_access_check(place)?;
630617

631-
this.validate_overlapping_atomic(place)?;
632618
let old = this.allow_data_races_mut(|this| this.read_immediate(place))?;
633619
let lt = this.wrapping_binary_op(mir::BinOp::Lt, &old, &rhs)?.to_scalar().to_bool()?;
634620

@@ -668,7 +654,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> {
668654
let this = self.eval_context_mut();
669655
this.atomic_access_check(place)?;
670656

671-
this.validate_overlapping_atomic(place)?;
672657
// Failure ordering cannot be stronger than success ordering, therefore first attempt
673658
// to read with the failure ordering and if successful then try again with the success
674659
// read ordering and write in the success case.
@@ -927,26 +912,6 @@ impl VClockAlloc {
927912
}))?
928913
}
929914

930-
/// Detect racing atomic read and writes (not data races)
931-
/// on every byte of the current access range
932-
pub(super) fn race_free_with_atomic(
933-
&self,
934-
range: AllocRange,
935-
global: &GlobalState,
936-
thread_mgr: &ThreadManager<'_, '_>,
937-
) -> bool {
938-
if global.race_detecting() {
939-
let (_, thread_clocks) = global.current_thread_state(thread_mgr);
940-
let alloc_ranges = self.alloc_ranges.borrow();
941-
for (_, mem_clocks) in alloc_ranges.iter(range.start, range.size) {
942-
if !mem_clocks.race_free_with_atomic(&thread_clocks) {
943-
return false;
944-
}
945-
}
946-
}
947-
true
948-
}
949-
950915
/// Detect data-races for an unsynchronized read operation, will not perform
951916
/// data-race detection if `race_detecting()` is false, either due to no threads
952917
/// being created or if it is temporarily disabled during a racy read or write
@@ -1138,7 +1103,6 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> {
11381103
atomic: AtomicReadOrd,
11391104
) -> InterpResult<'tcx> {
11401105
let this = self.eval_context_ref();
1141-
this.validate_overlapping_atomic(place)?;
11421106
this.validate_atomic_op(
11431107
place,
11441108
atomic,
@@ -1161,7 +1125,6 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> {
11611125
atomic: AtomicWriteOrd,
11621126
) -> InterpResult<'tcx> {
11631127
let this = self.eval_context_mut();
1164-
this.validate_overlapping_atomic(place)?;
11651128
this.validate_atomic_op(
11661129
place,
11671130
atomic,
@@ -1187,7 +1150,6 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> {
11871150
let acquire = matches!(atomic, Acquire | AcqRel | SeqCst);
11881151
let release = matches!(atomic, Release | AcqRel | SeqCst);
11891152
let this = self.eval_context_mut();
1190-
this.validate_overlapping_atomic(place)?;
11911153
this.validate_atomic_op(place, atomic, "Atomic RMW", move |memory, clocks, index, _| {
11921154
if acquire {
11931155
memory.load_acquire(clocks, index, place.layout.size)?;

src/concurrency/weak_memory.rs

+4-40
Original file line numberDiff line numberDiff line change
@@ -169,14 +169,6 @@ impl StoreBufferAlloc {
169169
Self { store_buffers: RefCell::new(RangeObjectMap::new()) }
170170
}
171171

172-
/// Checks if the range imperfectly overlaps with existing buffers
173-
/// Used to determine if mixed-size atomic accesses
174-
fn is_overlapping(&self, range: AllocRange) -> bool {
175-
let buffers = self.store_buffers.borrow();
176-
let access_type = buffers.access_type(range);
177-
matches!(access_type, AccessType::ImperfectlyOverlapping(_))
178-
}
179-
180172
/// When a non-atomic access happens on a location that has been atomically accessed
181173
/// before without data race, we can determine that the non-atomic access fully happens
182174
/// after all the prior atomic accesses so the location no longer needs to exhibit
@@ -190,6 +182,8 @@ impl StoreBufferAlloc {
190182
buffers.remove_from_pos(pos);
191183
}
192184
AccessType::ImperfectlyOverlapping(pos_range) => {
185+
// We rely on the data-race check making sure this is synchronized.
186+
// Therefore we can forget about the old data here.
193187
buffers.remove_pos_range(pos_range);
194188
}
195189
AccessType::Empty(_) => {
@@ -215,7 +209,7 @@ impl StoreBufferAlloc {
215209
pos
216210
}
217211
AccessType::ImperfectlyOverlapping(pos_range) => {
218-
// Once we reach here we would've already checked that this access is not racy
212+
// Once we reach here we would've already checked that this access is not racy.
219213
let mut buffers = self.store_buffers.borrow_mut();
220214
buffers.remove_pos_range(pos_range.clone());
221215
buffers.insert_at_pos(pos_range.start, range, StoreBuffer::new(init));
@@ -240,6 +234,7 @@ impl StoreBufferAlloc {
240234
pos
241235
}
242236
AccessType::ImperfectlyOverlapping(pos_range) => {
237+
// Once we reach here we would've already checked that this access is not racy.
243238
buffers.remove_pos_range(pos_range.clone());
244239
buffers.insert_at_pos(pos_range.start, range, StoreBuffer::new(init));
245240
pos_range.start
@@ -473,37 +468,6 @@ impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir,
473468
pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>:
474469
crate::MiriInterpCxExt<'mir, 'tcx>
475470
{
476-
// If weak memory emulation is enabled, check if this atomic op imperfectly overlaps with a previous
477-
// atomic read or write. If it does, then we require it to be ordered (non-racy) with all previous atomic
478-
// accesses on all the bytes in range
479-
fn validate_overlapping_atomic(
480-
&self,
481-
place: &MPlaceTy<'tcx, Provenance>,
482-
) -> InterpResult<'tcx> {
483-
let this = self.eval_context_ref();
484-
let (alloc_id, base_offset, ..) = this.ptr_get_alloc_id(place.ptr())?;
485-
if let crate::AllocExtra {
486-
weak_memory: Some(alloc_buffers),
487-
data_race: Some(alloc_clocks),
488-
..
489-
} = this.get_alloc_extra(alloc_id)?
490-
{
491-
let range = alloc_range(base_offset, place.layout.size);
492-
if alloc_buffers.is_overlapping(range)
493-
&& !alloc_clocks.race_free_with_atomic(
494-
range,
495-
this.machine.data_race.as_ref().unwrap(),
496-
&this.machine.threads,
497-
)
498-
{
499-
throw_unsup_format!(
500-
"racy imperfectly overlapping atomic access is not possible in the C++20 memory model, and not supported by Miri's weak memory emulation"
501-
);
502-
}
503-
}
504-
Ok(())
505-
}
506-
507471
fn buffered_atomic_rmw(
508472
&mut self,
509473
new_val: Scalar<Provenance>,

tests/fail/weak_memory/racing_mixed_size.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ fn split_u32_ptr(dword: *const u32) -> *const [u16; 2] {
1919

2020
// Wine's SRWLock implementation does this, which is definitely undefined in C++ memory model
2121
// https://github.com/wine-mirror/wine/blob/303f8042f9db508adaca02ef21f8de4992cb9c03/dlls/ntdll/sync.c#L543-L566
22-
// Though it probably works just fine on x86
22+
// It probably works just fine on x86, but Intel does document this as "don't do it!"
2323
pub fn main() {
2424
let x = static_atomic_u32(0);
2525
let j1 = spawn(move || {
@@ -31,7 +31,7 @@ pub fn main() {
3131
let x_split = split_u32_ptr(x_ptr);
3232
unsafe {
3333
let hi = ptr::addr_of!((*x_split)[0]);
34-
std::intrinsics::atomic_load_relaxed(hi); //~ ERROR: imperfectly overlapping
34+
std::intrinsics::atomic_load_relaxed(hi); //~ ERROR: different-size
3535
}
3636
});
3737

tests/fail/weak_memory/racing_mixed_size.stderr

+10-4
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
1-
error: unsupported operation: racy imperfectly overlapping atomic access is not possible in the C++20 memory model, and not supported by Miri's weak memory emulation
1+
error: Undefined Behavior: Data race detected between (1) 4-byte Atomic Store on thread `<unnamed>` and (2) 2-byte (different-size) Atomic Load on thread `<unnamed>` at ALLOC. (2) just happened here
22
--> $DIR/racing_mixed_size.rs:LL:CC
33
|
44
LL | std::intrinsics::atomic_load_relaxed(hi);
5-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ racy imperfectly overlapping atomic access is not possible in the C++20 memory model, and not supported by Miri's weak memory emulation
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Data race detected between (1) 4-byte Atomic Store on thread `<unnamed>` and (2) 2-byte (different-size) Atomic Load on thread `<unnamed>` at ALLOC. (2) just happened here
66
|
7-
= help: this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support
8-
= note: BACKTRACE:
7+
help: and (1) occurred earlier here
8+
--> $DIR/racing_mixed_size.rs:LL:CC
9+
|
10+
LL | x.store(1, Relaxed);
11+
| ^^^^^^^^^^^^^^^^^^^
12+
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
13+
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
14+
= note: BACKTRACE (of the first span):
915
= note: inside closure at $DIR/racing_mixed_size.rs:LL:CC
1016

1117
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

tests/fail/weak_memory/racing_mixed_size_read.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ fn split_u32_ptr(dword: *const u32) -> *const [u16; 2] {
1616

1717
// Racing mixed size reads may cause two loads to read-from
1818
// the same store but observe different values, which doesn't make
19-
// sense under the formal model so we forbade this.
19+
// sense under the formal model so we forbid this.
2020
pub fn main() {
2121
let x = static_atomic(0);
2222

@@ -29,7 +29,7 @@ pub fn main() {
2929
let x_split = split_u32_ptr(x_ptr);
3030
unsafe {
3131
let hi = x_split as *const u16 as *const AtomicU16;
32-
(*hi).load(Relaxed); //~ ERROR: imperfectly overlapping
32+
(*hi).load(Relaxed); //~ ERROR: different-size
3333
}
3434
});
3535

tests/fail/weak_memory/racing_mixed_size_read.stderr

+10-4
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
1-
error: unsupported operation: racy imperfectly overlapping atomic access is not possible in the C++20 memory model, and not supported by Miri's weak memory emulation
1+
error: Undefined Behavior: Data race detected between (1) 4-byte Atomic Load on thread `<unnamed>` and (2) 2-byte (different-size) Atomic Load on thread `<unnamed>` at ALLOC. (2) just happened here
22
--> $DIR/racing_mixed_size_read.rs:LL:CC
33
|
44
LL | (*hi).load(Relaxed);
5-
| ^^^^^^^^^^^^^^^^^^^ racy imperfectly overlapping atomic access is not possible in the C++20 memory model, and not supported by Miri's weak memory emulation
5+
| ^^^^^^^^^^^^^^^^^^^ Data race detected between (1) 4-byte Atomic Load on thread `<unnamed>` and (2) 2-byte (different-size) Atomic Load on thread `<unnamed>` at ALLOC. (2) just happened here
66
|
7-
= help: this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support
8-
= note: BACKTRACE:
7+
help: and (1) occurred earlier here
8+
--> $DIR/racing_mixed_size_read.rs:LL:CC
9+
|
10+
LL | x.load(Relaxed);
11+
| ^^^^^^^^^^^^^^^
12+
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
13+
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
14+
= note: BACKTRACE (of the first span):
915
= note: inside closure at $DIR/racing_mixed_size_read.rs:LL:CC
1016

1117
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

0 commit comments

Comments
 (0)