@@ -688,6 +688,8 @@ impl MemoryCellClocks {
688688 self . write = ( index, thread_clocks. clock [ index] ) ;
689689 self . write_type = write_type;
690690 self . read . set_zero_vector ( ) ;
691+ // This is not an atomic location any more.
692+ self . atomic_ops = None ;
691693 Ok ( ( ) )
692694 }
693695}
@@ -754,14 +756,9 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> {
754756 let this = self . eval_context_mut ( ) ;
755757 this. atomic_access_check ( dest, AtomicAccessType :: Store ) ?;
756758
757- // Read the previous value so we can put it in the store buffer later.
758- // The program didn't actually do a read, so suppress the memory access hooks.
759- // This is also a very special exception where we just ignore an error -- if this read
760- // was UB e.g. because the memory is uninitialized, we don't want to know!
761- let old_val = this. run_for_validation_ref ( |this| this. read_scalar ( dest) ) . discard_err ( ) ;
762-
763759 // Inform GenMC about the atomic store.
764760 if let Some ( genmc_ctx) = this. machine . data_race . as_genmc_ref ( ) {
761+ let old_val = this. run_for_validation_ref ( |this| this. read_scalar ( dest) ) . discard_err ( ) ;
765762 if genmc_ctx. atomic_store (
766763 this,
767764 dest. ptr ( ) . addr ( ) ,
@@ -776,6 +773,9 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> {
776773 }
777774 return interp_ok ( ( ) ) ;
778775 }
776+
777+ // Read the previous value so we can put it in the store buffer later.
778+ let old_val = this. get_latest_nonatomic_val ( dest) ;
779779 this. allow_data_races_mut ( move |this| this. write_scalar ( val, dest) ) ?;
780780 this. validate_atomic_store ( dest, atomic) ?;
781781 this. buffered_atomic_write ( val, dest, atomic, old_val)
@@ -1536,6 +1536,35 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> {
15361536 )
15371537 }
15381538
1539+ /// Returns the most recent *non-atomic* value stored in the given place.
1540+ /// Errors if we don't need that (because we don't do store buffering) or if
1541+ /// the most recent value is in fact atomic.
1542+ fn get_latest_nonatomic_val ( & self , place : & MPlaceTy < ' tcx > ) -> Result < Option < Scalar > , ( ) > {
1543+ let this = self . eval_context_ref ( ) ;
1544+ // These cannot fail because `atomic_access_check` was done first.
1545+ let ( alloc_id, offset, _prov) = this. ptr_get_alloc_id ( place. ptr ( ) , 0 ) . unwrap ( ) ;
1546+ let alloc_meta = & this. get_alloc_extra ( alloc_id) . unwrap ( ) . data_race ;
1547+ if alloc_meta. as_weak_memory_ref ( ) . is_none ( ) {
1548+ // No reason to read old value if we don't track store buffers.
1549+ return Err ( ( ) ) ;
1550+ }
1551+ let data_race = alloc_meta. as_vclocks_ref ( ) . unwrap ( ) ;
1552+ // Only read old value if this is currently a non-atomic location.
1553+ for ( _range, clocks) in data_race. alloc_ranges . borrow_mut ( ) . iter ( offset, place. layout . size )
1554+ {
1555+ // If this had an atomic write that's not before the non-atomic write, that should
1556+ // already be in the store buffer. Initializing the store buffer now would use the
1557+ // wrong `sync_clock` so we better make sure that does not happen.
1558+ if clocks. atomic ( ) . is_some_and ( |atomic| !( atomic. write_vector <= clocks. write ( ) ) ) {
1559+ return Err ( ( ) ) ;
1560+ }
1561+ }
1562+ // The program didn't actually do a read, so suppress the memory access hooks.
1563+ // This is also a very special exception where we just ignore an error -- if this read
1564+ // was UB e.g. because the memory is uninitialized, we don't want to know!
1565+ Ok ( this. run_for_validation_ref ( |this| this. read_scalar ( place) ) . discard_err ( ) )
1566+ }
1567+
15391568 /// Generic atomic operation implementation
15401569 fn validate_atomic_op < A : Debug + Copy > (
15411570 & self ,
0 commit comments