Skip to content

Commit 0aefe34

Browse files
authored
Merge pull request #4611 from Patrick-6/miri-genmc-temporal-mixing
Add support for temporal mixing of atomic and non-atomic accesses in GenMC mode
2 parents 49439ec + 57a7380 commit 0aefe34

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+978
-191
lines changed

genmc-sys/build.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ mod downloading {
2828
/// The GenMC repository the we get our commit from.
2929
pub(crate) const GENMC_GITHUB_URL: &str = "https://gitlab.inf.ethz.ch/public-plf/genmc.git";
3030
/// The GenMC commit we depend on. It must be available on the specified GenMC repository.
31-
pub(crate) const GENMC_COMMIT: &str = "cd01c12032bdd71df742b41c7817f99acc72e7ab";
31+
pub(crate) const GENMC_COMMIT: &str = "ce775ccd7866db820fa12ffca66463087a11dd96";
3232

3333
/// Ensure that a local GenMC repo is present and set to the correct commit.
3434
/// Return the path of the GenMC repo and whether the checked out commit was changed.

genmc-sys/cpp/include/MiriInterface.hpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -214,9 +214,10 @@ struct MiriGenmcShim : private GenMCDriver {
214214
* Automatically calls `inc_pos` and `dec_pos` where needed for the given thread.
215215
*/
216216
template <EventLabel::EventLabelKind k, typename... Ts>
217-
auto handle_load_reset_if_none(ThreadId tid, Ts&&... params) -> HandleResult<SVal> {
217+
auto handle_load_reset_if_none(ThreadId tid, std::optional<SVal> old_val, Ts&&... params)
218+
-> HandleResult<SVal> {
218219
const auto pos = inc_pos(tid);
219-
const auto ret = GenMCDriver::handleLoad<k>(pos, std::forward<Ts>(params)...);
220+
const auto ret = GenMCDriver::handleLoad<k>(pos, old_val, std::forward<Ts>(params)...);
220221
// If we didn't get a value, we have to reset the index of the current thread.
221222
if (!std::holds_alternative<SVal>(ret)) {
222223
dec_pos(tid);
@@ -274,6 +275,12 @@ inline SVal to_sval(GenmcScalar scalar) {
274275
ERROR_ON(!scalar.is_init, "Cannot convert an uninitialized `GenmcScalar` into an `SVal`\n");
275276
return SVal(scalar.value, scalar.extra);
276277
}
278+
279+
inline std::optional<SVal> try_to_sval(GenmcScalar scalar) {
280+
if (scalar.is_init)
281+
return { SVal(scalar.value, scalar.extra) };
282+
return std::nullopt;
283+
}
277284
} // namespace GenmcScalarExt
278285

279286
namespace LoadResultExt {

genmc-sys/cpp/src/MiriInterface/EventHandling.cpp

Lines changed: 20 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ void MiriGenmcShim::handle_assume_block(ThreadId thread_id) {
4949
const auto type = AType::Unsigned;
5050
const auto ret = handle_load_reset_if_none<EventLabel::EventLabelKind::Read>(
5151
thread_id,
52+
GenmcScalarExt::try_to_sval(old_val),
5253
ord,
5354
SAddr(address),
5455
ASize(size),
@@ -74,6 +75,7 @@ void MiriGenmcShim::handle_assume_block(ThreadId thread_id) {
7475
const auto pos = inc_pos(thread_id);
7576
const auto ret = GenMCDriver::handleStore<EventLabel::EventLabelKind::Write>(
7677
pos,
78+
GenmcScalarExt::try_to_sval(old_val),
7779
ord,
7880
SAddr(address),
7981
ASize(size),
@@ -84,15 +86,13 @@ void MiriGenmcShim::handle_assume_block(ThreadId thread_id) {
8486

8587
if (const auto* err = std::get_if<VerificationError>(&ret))
8688
return StoreResultExt::from_error(format_error(*err));
87-
if (!std::holds_alternative<std::monostate>(ret))
88-
ERROR("store returned unexpected result");
8989

90-
// FIXME(genmc,mixed-accesses): Use the value that GenMC returns from handleStore (once
91-
// available).
92-
const auto& g = getExec().getGraph();
93-
return StoreResultExt::ok(
94-
/* is_coherence_order_maximal_write */ g.co_max(SAddr(address))->getPos() == pos
90+
const bool* is_coherence_order_maximal_write = std::get_if<bool>(&ret);
91+
ERROR_ON(
92+
nullptr == is_coherence_order_maximal_write,
93+
"Unimplemented: Store returned unexpected result."
9594
);
95+
return StoreResultExt::ok(*is_coherence_order_maximal_write);
9696
}
9797

9898
void MiriGenmcShim::handle_fence(ThreadId thread_id, MemOrdering ord) {
@@ -117,6 +117,7 @@ void MiriGenmcShim::handle_fence(ThreadId thread_id, MemOrdering ord) {
117117
// `FaiRead` and `FaiWrite`.
118118
const auto load_ret = handle_load_reset_if_none<EventLabel::EventLabelKind::FaiRead>(
119119
thread_id,
120+
GenmcScalarExt::try_to_sval(old_val),
120121
ordering,
121122
SAddr(address),
122123
ASize(size),
@@ -139,6 +140,7 @@ void MiriGenmcShim::handle_fence(ThreadId thread_id, MemOrdering ord) {
139140
const auto storePos = inc_pos(thread_id);
140141
const auto store_ret = GenMCDriver::handleStore<EventLabel::EventLabelKind::FaiWrite>(
141142
storePos,
143+
GenmcScalarExt::try_to_sval(old_val),
142144
ordering,
143145
SAddr(address),
144146
ASize(size),
@@ -148,16 +150,15 @@ void MiriGenmcShim::handle_fence(ThreadId thread_id, MemOrdering ord) {
148150
if (const auto* err = std::get_if<VerificationError>(&store_ret))
149151
return ReadModifyWriteResultExt::from_error(format_error(*err));
150152

151-
const auto* store_ret_val = std::get_if<std::monostate>(&store_ret);
152-
ERROR_ON(nullptr == store_ret_val, "Unimplemented: RMW store returned unexpected result.");
153-
154-
// FIXME(genmc,mixed-accesses): Use the value that GenMC returns from handleStore (once
155-
// available).
156-
const auto& g = getExec().getGraph();
153+
const bool* is_coherence_order_maximal_write = std::get_if<bool>(&store_ret);
154+
ERROR_ON(
155+
nullptr == is_coherence_order_maximal_write,
156+
"Unimplemented: RMW store returned unexpected result."
157+
);
157158
return ReadModifyWriteResultExt::ok(
158159
/* old_value: */ read_old_val,
159160
new_value,
160-
/* is_coherence_order_maximal_write */ g.co_max(SAddr(address))->getPos() == storePos
161+
*is_coherence_order_maximal_write
161162
);
162163
}
163164

@@ -183,6 +184,7 @@ void MiriGenmcShim::handle_fence(ThreadId thread_id, MemOrdering ord) {
183184

184185
const auto load_ret = handle_load_reset_if_none<EventLabel::EventLabelKind::CasRead>(
185186
thread_id,
187+
GenmcScalarExt::try_to_sval(old_val),
186188
success_ordering,
187189
SAddr(address),
188190
ASize(size),
@@ -203,6 +205,7 @@ void MiriGenmcShim::handle_fence(ThreadId thread_id, MemOrdering ord) {
203205
const auto storePos = inc_pos(thread_id);
204206
const auto store_ret = GenMCDriver::handleStore<EventLabel::EventLabelKind::CasWrite>(
205207
storePos,
208+
GenmcScalarExt::try_to_sval(old_val),
206209
success_ordering,
207210
SAddr(address),
208211
ASize(size),
@@ -211,19 +214,12 @@ void MiriGenmcShim::handle_fence(ThreadId thread_id, MemOrdering ord) {
211214
);
212215
if (const auto* err = std::get_if<VerificationError>(&store_ret))
213216
return CompareExchangeResultExt::from_error(format_error(*err));
214-
const auto* store_ret_val = std::get_if<std::monostate>(&store_ret);
217+
const bool* is_coherence_order_maximal_write = std::get_if<bool>(&store_ret);
215218
ERROR_ON(
216-
nullptr == store_ret_val,
219+
nullptr == is_coherence_order_maximal_write,
217220
"Unimplemented: compare-exchange store returned unexpected result."
218221
);
219-
220-
// FIXME(genmc,mixed-accesses): Use the value that GenMC returns from handleStore (once
221-
// available).
222-
const auto& g = getExec().getGraph();
223-
return CompareExchangeResultExt::success(
224-
read_old_val,
225-
/* is_coherence_order_maximal_write */ g.co_max(SAddr(address))->getPos() == storePos
226-
);
222+
return CompareExchangeResultExt::success(read_old_val, *is_coherence_order_maximal_write);
227223
}
228224

229225
/**** Memory (de)allocation ****/

tests/genmc/fail/atomics/atomic_ptr_double_free.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,6 @@ unsafe fn free(ptr: *mut u64) {
2222

2323
#[unsafe(no_mangle)]
2424
fn miri_start(_argc: isize, _argv: *const *const u8) -> isize {
25-
// FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses.
26-
X.store(std::ptr::null_mut(), SeqCst);
27-
2825
unsafe {
2926
let ids = [
3027
spawn_pthread_closure(|| {

tests/genmc/fail/data_race/atomic_ptr_alloc_race.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,6 @@ static X: AtomicPtr<u64> = AtomicPtr::new(std::ptr::null_mut());
2323

2424
#[unsafe(no_mangle)]
2525
fn miri_start(_argc: isize, _argv: *const *const u8) -> isize {
26-
// FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses.
27-
X.store(std::ptr::null_mut(), SeqCst);
28-
2926
unsafe {
3027
let ids = [
3128
spawn_pthread_closure(|| {

tests/genmc/fail/data_race/atomic_ptr_dealloc_write_race.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,6 @@ static mut Y: u64 = 0;
2020

2121
#[unsafe(no_mangle)]
2222
fn miri_start(_argc: isize, _argv: *const *const u8) -> isize {
23-
// FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses.
24-
X.store(std::ptr::null_mut(), SeqCst);
25-
2623
unsafe {
2724
let ids = [
2825
spawn_pthread_closure(|| {

tests/genmc/fail/data_race/atomic_ptr_write_dealloc_race.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,6 @@ static Y: AtomicPtr<u64> = AtomicPtr::new(std::ptr::null_mut());
2424

2525
#[unsafe(no_mangle)]
2626
fn miri_start(_argc: isize, _argv: *const *const u8) -> isize {
27-
// FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses.
28-
X.store(std::ptr::null_mut(), SeqCst);
29-
3027
unsafe {
3128
let ids = [
3229
spawn_pthread_closure(|| {

tests/genmc/fail/loom/buggy_inc.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,6 @@ impl BuggyInc {
4242
fn miri_start(_argc: isize, _argv: *const *const u8) -> isize {
4343
unsafe {
4444
static BUGGY_INC: BuggyInc = BuggyInc::new();
45-
// FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses.
46-
BUGGY_INC.num.store(0, Relaxed);
47-
4845
let ids = [
4946
spawn_pthread_closure(|| {
5047
BUGGY_INC.inc();

tests/genmc/pass/atomics/atomic_ptr_ops.rs

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -37,46 +37,54 @@ unsafe fn pointers_equal(a: *mut u64, b: *mut u64) {
3737

3838
unsafe fn test_load_store_exchange() {
3939
let atomic_ptr: AtomicPtr<u64> = AtomicPtr::new(&raw mut X);
40-
// FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses.
41-
// FIXME(genmc): Add test cases with temporal mixing of atomics/non-atomics.
42-
atomic_ptr.store(&raw mut X, SeqCst);
4340

44-
// Load can read the initial value.
41+
// Atomic load can read the initial value.
4542
pointers_equal(atomic_ptr.load(SeqCst), &raw mut X);
46-
// Store works as expected.
43+
// Atomic store works as expected.
4744
atomic_ptr.store(&raw mut Y, SeqCst);
4845
pointers_equal(atomic_ptr.load(SeqCst), &raw mut Y);
46+
// We can read the value of the atomic store non-atomically.
47+
pointers_equal(*atomic_ptr.as_ptr(), &raw mut Y);
48+
// We can read the value of a non-atomic store atomically.
49+
*atomic_ptr.as_ptr() = &raw mut X;
50+
pointers_equal(atomic_ptr.load(SeqCst), &raw mut X);
4951

5052
// Atomic swap must return the old value and store the new one.
53+
*atomic_ptr.as_ptr() = &raw mut Y; // Test that we can read this non-atomic store using `swap`.
5154
pointers_equal(atomic_ptr.swap(&raw mut X, SeqCst), &raw mut Y);
55+
pointers_equal(*atomic_ptr.as_ptr(), &raw mut X);
5256
pointers_equal(atomic_ptr.load(SeqCst), &raw mut X);
5357

5458
// Failing compare_exchange (wrong expected pointer).
5559
match atomic_ptr.compare_exchange(&raw mut Y, std::ptr::null_mut(), SeqCst, SeqCst) {
5660
Ok(_ptr) => std::process::abort(),
5761
Err(ptr) => pointers_equal(ptr, &raw mut X),
5862
}
63+
// Non-atomic read value should also be unchanged by a failing compare_exchange.
64+
pointers_equal(*atomic_ptr.as_ptr(), &raw mut X);
65+
5966
// Failing compare_exchange (null).
6067
match atomic_ptr.compare_exchange(std::ptr::null_mut(), std::ptr::null_mut(), SeqCst, SeqCst) {
6168
Ok(_ptr) => std::process::abort(),
6269
Err(ptr) => pointers_equal(ptr, &raw mut X),
6370
}
71+
// Non-atomic read value should also be unchanged by a failing compare_exchange.
72+
pointers_equal(*atomic_ptr.as_ptr(), &raw mut X);
73+
6474
// Successful compare_exchange.
6575
match atomic_ptr.compare_exchange(&raw mut X, &raw mut Y, SeqCst, SeqCst) {
6676
Ok(ptr) => pointers_equal(ptr, &raw mut X),
6777
Err(_ptr) => std::process::abort(),
6878
}
6979
// compare_exchange should update the pointer.
7080
pointers_equal(atomic_ptr.load(SeqCst), &raw mut Y);
81+
pointers_equal(*atomic_ptr.as_ptr(), &raw mut Y);
7182
}
7283

7384
unsafe fn test_add_sub() {
7485
const LEN: usize = 16;
7586
let mut array: [u64; LEN] = std::array::from_fn(|i| i as u64);
76-
7787
let atomic_ptr: AtomicPtr<u64> = AtomicPtr::new(&raw mut array[0]);
78-
// FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses.
79-
atomic_ptr.store(&raw mut array[0], SeqCst);
8088

8189
// Each element of the array should be reachable using `fetch_ptr_add`.
8290
// All pointers must stay valid.
@@ -110,10 +118,7 @@ unsafe fn test_and_or_xor() {
110118

111119
let mut array = AlignedArray(std::array::from_fn(|i| i as u64 * 10));
112120
let array_ptr = &raw mut array.0[0];
113-
114121
let atomic_ptr: AtomicPtr<u64> = AtomicPtr::new(array_ptr);
115-
// FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses.
116-
atomic_ptr.store(array_ptr, SeqCst);
117122

118123
// Test no-op arguments.
119124
assert_equals(array_ptr, atomic_ptr.fetch_or(0, SeqCst));

tests/genmc/pass/atomics/atomic_ptr_roundtrip.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,6 @@ static mut X: [u8; 16] = [0; 16];
2323

2424
#[unsafe(no_mangle)]
2525
fn miri_start(_argc: isize, _argv: *const *const u8) -> isize {
26-
// FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses.
27-
PTR.store(std::ptr::null_mut(), SeqCst);
28-
2926
unsafe {
3027
let ids = [
3128
spawn_pthread_closure(|| {

0 commit comments

Comments
 (0)