Skip to content

Commit 09d2500

Browse files
committed
WIP: Start working on Miri-GenMC interop.
- Add genmc-sys crate for building GenMC C++ code. - Implemented program features: - Handling of atomic loads, stores, allocations and deallocations. - Implement consistent address allocations for globals - Non-global addresses allocated by GenMC. - Limitation: No address randomization. - Limitation: No address reuse. - Handling of non-atomic memory accesses. - Limitation: need to split up NA reads and stores into max 8 byte chunks. - Limitation: no mixed size access support. - Limitation: Incomplete (unsound) handling of uninitialized memory. - Implement Pointer conversions to and from GenMC - Limitation: Aliasing model checking must be disabled. - Handling of Read-Modify-Write, Compare-Exchange and Atomic Exchange operations. - Limitation: Compare-Exchange currently ignores possibility of spurious failures. - Limitation: Compare-Exchange failure memory ordering is ignored. - Handling of thread creation and finishing. - Added Miri to GenMC thread id mapping. - Implement mutex lock, try_lock and unlock. - Make use of annotations to reduce number of blocked executions for programs with mutexes. - Add estimation mode for Miri-GenMC. - Limitation: Printing currently handled on C++ side (Should be moved once VerificationResult is available to Rust code) - Thread scheduling in Miri done through GenMC, add loop over eval_entry function. - Rebase GenMC to use new scheduler. - Added GenMC `__VERIFIER_assume` equivalent for Miri (`miri_genmc_verifier_assume`) - Add warnings for GenMC mode for unsupported features. - Add a lot of test, including translation of GenMC litmus tests. - Only run tests if 'genmc' feature enabled. - WIP: try implementing NonNullUniquePtr wrapper for CXX. - WIP: work on mixed atomic/non-atomics. - Partially working, some issues with globals - FIX: Make naming consistent with GenMC for blocked execution Squashed changes: - Add git support to build, move C++ files into genmc-sys crate - Implement building GenMC-Miri after GenMC codebase split. - Cleanup genmc-sys build, remove walkdir dependency. - Fix printing after LLVM IO removal. - WIP: temporarily point GenMC repo to private branch. - WIP: Work on building without LLVM dependency. - Fix build for Ubuntu 22 - Disable stacked borrows for GenMC tests - Get Miri-GenMC building again after rebasing onto GenMC release 0.12.0 - Translate some Loom tests for Miri-GenMC. - Add error for using native-lib with GenMC mode. - Remove on_stack_empty handling except for main thread. - Rename 'stuck' to 'blocked' to be in line with GenMC terminology. - Enable tests that were blocked on rust-lang #116707 - Remove redundant file in compilation step. - Clean up InterpResult<'tcx, ()> - Improve program exit handling, remove 'thread_stack_empty' hack, clean up terminator cache leftovers.
1 parent 3fc854d commit 09d2500

File tree

202 files changed

+6667
-254
lines changed

Some content is hidden

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

202 files changed

+6667
-254
lines changed

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,8 @@ name = "ui"
7272
harness = false
7373

7474
[features]
75-
default = ["stack-cache", "native-lib"]
75+
# TODO GENMC (DEBUGGING): `genmc` feature should be turned off in upstream repo for now
76+
default = ["stack-cache", "native-lib", "genmc"]
7677
genmc = ["dep:genmc-sys"] # this enables a GPL dependency!
7778
stack-cache = []
7879
stack-cache-consistency-check = ["stack-cache"]

genmc-sys/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
genmc-src*/
2+
genmc*/

genmc-sys/build.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,6 @@ fn compile_cpp_dependencies(genmc_path: &Path) {
224224
.include(genmc_include_dir)
225225
.include(llvm_include_dirs)
226226
.include("./src_cpp")
227-
.file("./src_cpp/MiriInterface.hpp")
228227
.file("./src_cpp/MiriInterface.cpp")
229228
.compile("genmc_interop");
230229

genmc-sys/src/cxx_extra.rs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#![allow(unused)] // TODO GENMC
2+
3+
use std::pin::Pin;
4+
5+
use cxx::UniquePtr;
6+
use cxx::memory::UniquePtrTarget;
7+
8+
#[repr(transparent)]
9+
pub struct NonNullUniquePtr<T: UniquePtrTarget> {
10+
/// SAFETY: `inner` is never `null`
11+
inner: UniquePtr<T>,
12+
}
13+
14+
impl<T: UniquePtrTarget> NonNullUniquePtr<T> {
15+
pub fn new(input: UniquePtr<T>) -> Option<Self> {
16+
if input.is_null() {
17+
None
18+
} else {
19+
// SAFETY: `input` is not `null`
20+
Some(unsafe { Self::new_unchecked(input) })
21+
}
22+
}
23+
24+
/// SAFETY: caller must ensure that `input` is not `null`
25+
pub unsafe fn new_unchecked(input: UniquePtr<T>) -> Self {
26+
Self { inner: input }
27+
}
28+
29+
pub fn into_inner(self) -> UniquePtr<T> {
30+
self.inner
31+
}
32+
33+
pub fn as_mut(&mut self) -> Pin<&mut T> {
34+
let ptr = self.inner.as_mut_ptr();
35+
36+
// SAFETY: `inner` is not `null` (type invariant)
37+
let mut_reference = unsafe { ptr.as_mut().unwrap_unchecked() };
38+
// SAFETY: TODO GENMC (should be the same reason as in CXX crate, but there is no safety comment there)
39+
unsafe { Pin::new_unchecked(mut_reference) }
40+
}
41+
}
42+
43+
impl<T: UniquePtrTarget> AsRef<T> for NonNullUniquePtr<T> {
44+
fn as_ref(&self) -> &T {
45+
// SAFETY: `inner` is not `null` (type invariant)
46+
unsafe { self.inner.as_ref().unwrap_unchecked() }
47+
}
48+
}

genmc-sys/src/lib.rs

Lines changed: 284 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,38 @@
11
pub use self::ffi::*;
22

3+
pub mod cxx_extra;
4+
5+
/// Defined in "genmc/src/Support/SAddr.hpp"
6+
/// FIXME: currently we use `getGlobalAllocStaticMask()` to ensure the constant is consistent between Miri and GenMC,
7+
/// but if https://github.com/dtolnay/cxx/issues/1051 is fixed we could share the constant directly.
8+
pub const GENMC_GLOBAL_ADDRESSES_MASK: u64 = 1 << 63;
9+
10+
#[repr(transparent)]
11+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12+
pub struct GenmcThreadId(pub i32);
13+
14+
pub const GENMC_MAIN_THREAD_ID: GenmcThreadId = GenmcThreadId(0);
15+
16+
impl GenmcScalar {
17+
pub const UNINIT: Self = Self { value: 0, extra: 0, is_init: false };
18+
pub const DUMMY: Self = Self::from_u64(0xDEADBEEF);
19+
20+
pub const MUTEX_LOCKED_STATE: Self = Self::from_u64(1);
21+
pub const MUTEX_UNLOCKED_STATE: Self = Self::from_u64(0);
22+
23+
pub const fn from_u64(value: u64) -> Self {
24+
Self { value, extra: 0, is_init: true }
25+
}
26+
}
27+
328
impl Default for GenmcParams {
429
fn default() -> Self {
530
Self {
631
print_random_schedule_seed: false,
7-
do_symmetry_reduction: false,
8-
// FIXME(GenMC): Add defaults for remaining parameters
32+
quiet: true,
33+
log_level_trace: false,
34+
do_symmetry_reduction: false, // TODO GENMC (PERFORMANCE): maybe make this default `true`
35+
estimation_max: 1000,
936
}
1037
}
1138
}
@@ -16,15 +43,268 @@ mod ffi {
1643
/// (The fields of this struct are visible to both Rust and C++)
1744
#[derive(Clone, Debug)]
1845
struct GenmcParams {
46+
// pub genmc_seed: u64; // OR: Option<u64>
1947
pub print_random_schedule_seed: bool,
48+
pub quiet: bool, // TODO GENMC: maybe make log-level more fine grained
49+
pub log_level_trace: bool,
2050
pub do_symmetry_reduction: bool,
21-
// FIXME(GenMC): Add remaining parameters.
51+
pub estimation_max: u32,
52+
}
53+
54+
#[derive(Debug)]
55+
enum ActionKind {
56+
/// Any Mir terminator that's atomic and has load semantics.
57+
Load,
58+
/// Anything that's not a `Load`.
59+
NonLoad,
60+
}
61+
62+
#[derive(Debug)]
63+
enum MemOrdering {
64+
NotAtomic = 0,
65+
Relaxed = 1,
66+
// In case we support consume
67+
Acquire = 3,
68+
Release = 4,
69+
AcquireRelease = 5,
70+
SequentiallyConsistent = 6,
71+
}
72+
73+
#[derive(Debug)]
74+
enum RMWBinOp {
75+
Xchg = 0,
76+
Add = 1,
77+
Sub = 2,
78+
And = 3,
79+
Nand = 4,
80+
Or = 5,
81+
Xor = 6,
82+
Max = 7,
83+
Min = 8,
84+
UMax = 9,
85+
UMin = 10,
86+
}
87+
88+
// TODO GENMC: do these have to be shared with the Rust side?
89+
#[derive(Debug)]
90+
enum StoreEventType {
91+
Normal,
92+
ReadModifyWrite,
93+
CompareExchange,
94+
MutexUnlockWrite,
95+
}
96+
97+
#[derive(Debug, Clone, Copy)]
98+
struct GenmcScalar {
99+
value: u64,
100+
extra: u64,
101+
is_init: bool,
102+
}
103+
104+
/**** \/ Result & Error types \/ ****/
105+
106+
#[must_use]
107+
#[derive(Debug)]
108+
struct ReadModifyWriteResult {
109+
old_value: GenmcScalar,
110+
new_value: GenmcScalar,
111+
isCoMaxWrite: bool,
112+
error: UniquePtr<CxxString>, // TODO GENMC: pass more error info here
113+
}
114+
115+
#[must_use]
116+
#[derive(Debug)]
117+
struct MutexLockResult {
118+
is_lock_acquired: bool,
119+
error: UniquePtr<CxxString>, // TODO GENMC: pass more error info here
120+
}
121+
122+
#[must_use]
123+
#[derive(Debug)]
124+
struct CompareExchangeResult {
125+
old_value: GenmcScalar, // TODO GENMC: handle bigger values
126+
is_success: bool,
127+
isCoMaxWrite: bool,
128+
error: UniquePtr<CxxString>, // TODO GENMC: pass more error info here
22129
}
130+
131+
#[must_use]
132+
#[derive(Debug)]
133+
struct LoadResult {
134+
is_read_opt: bool,
135+
read_value: GenmcScalar, // TODO GENMC: handle bigger values
136+
error: UniquePtr<CxxString>, // TODO GENMC: pass more error info here
137+
}
138+
139+
#[must_use]
140+
#[derive(Debug)]
141+
struct StoreResult {
142+
error: UniquePtr<CxxString>, // TODO GENMC: pass more error info here
143+
isCoMaxWrite: bool,
144+
}
145+
146+
#[must_use]
147+
#[derive(Debug)]
148+
enum VerificationError {
149+
VE_NonErrorBegin,
150+
VE_OK,
151+
VE_WWRace,
152+
VE_UnfreedMemory,
153+
VE_NonErrorLast,
154+
155+
VE_Safety,
156+
VE_Recovery,
157+
VE_Liveness,
158+
VE_RaceNotAtomic,
159+
VE_RaceFreeMalloc,
160+
VE_FreeNonMalloc,
161+
VE_DoubleFree,
162+
VE_Allocation,
163+
164+
VE_InvalidAccessBegin,
165+
VE_UninitializedMem,
166+
VE_AccessNonMalloc,
167+
VE_AccessFreed,
168+
VE_InvalidAccessEnd,
169+
170+
VE_InvalidCreate,
171+
VE_InvalidJoin,
172+
VE_InvalidUnlock,
173+
VE_InvalidBInit,
174+
VE_BarrierWellFormedness,
175+
VE_Annotation,
176+
VE_MixedSize,
177+
VE_LinearizabilityError,
178+
VE_SystemError,
179+
}
180+
181+
/**** /\ Result & Error types /\ ****/
182+
23183
unsafe extern "C++" {
24184
include!("MiriInterface.hpp");
25185

186+
type MemOrdering;
187+
type RMWBinOp;
188+
type StoreEventType;
189+
190+
// Types for Scheduling queries:
191+
type ActionKind;
192+
193+
// Result / Error types:
194+
type LoadResult;
195+
type StoreResult;
196+
type ReadModifyWriteResult;
197+
type CompareExchangeResult;
198+
type MutexLockResult;
199+
type VerificationError;
200+
201+
type GenmcScalar;
202+
203+
// type OperatingMode; // Estimation(budget) or Verification
204+
26205
type MiriGenMCShim;
27206

28-
fn createGenmcHandle(config: &GenmcParams) -> UniquePtr<MiriGenMCShim>;
207+
fn createGenmcHandle(config: &GenmcParams, do_estimation: bool)
208+
-> UniquePtr<MiriGenMCShim>;
209+
fn getGlobalAllocStaticMask() -> u64;
210+
211+
fn handleExecutionStart(self: Pin<&mut MiriGenMCShim>);
212+
fn handleExecutionEnd(self: Pin<&mut MiriGenMCShim>) -> UniquePtr<CxxString>;
213+
214+
fn handleLoad(
215+
self: Pin<&mut MiriGenMCShim>,
216+
thread_id: i32,
217+
address: u64,
218+
size: u64,
219+
memory_ordering: MemOrdering,
220+
old_value: GenmcScalar,
221+
) -> LoadResult;
222+
fn handleReadModifyWrite(
223+
self: Pin<&mut MiriGenMCShim>,
224+
thread_id: i32,
225+
address: u64,
226+
size: u64,
227+
load_ordering: MemOrdering,
228+
store_ordering: MemOrdering,
229+
rmw_op: RMWBinOp,
230+
rhs_value: GenmcScalar,
231+
old_value: GenmcScalar,
232+
) -> ReadModifyWriteResult;
233+
fn handleCompareExchange(
234+
self: Pin<&mut MiriGenMCShim>,
235+
thread_id: i32,
236+
address: u64,
237+
size: u64,
238+
expected_value: GenmcScalar,
239+
new_value: GenmcScalar,
240+
old_value: GenmcScalar,
241+
success_load_ordering: MemOrdering,
242+
success_store_ordering: MemOrdering,
243+
fail_load_ordering: MemOrdering,
244+
can_fail_spuriously: bool,
245+
) -> CompareExchangeResult;
246+
fn handleStore(
247+
self: Pin<&mut MiriGenMCShim>,
248+
thread_id: i32,
249+
address: u64,
250+
size: u64,
251+
value: GenmcScalar,
252+
old_value: GenmcScalar,
253+
memory_ordering: MemOrdering,
254+
store_event_type: StoreEventType,
255+
) -> StoreResult;
256+
fn handleFence(self: Pin<&mut MiriGenMCShim>, thread_id: i32, memory_ordering: MemOrdering);
257+
258+
fn handleMalloc(
259+
self: Pin<&mut MiriGenMCShim>,
260+
thread_id: i32,
261+
size: u64,
262+
alignment: u64,
263+
) -> u64;
264+
fn handleFree(self: Pin<&mut MiriGenMCShim>, thread_id: i32, address: u64, size: u64);
265+
266+
fn handleThreadCreate(self: Pin<&mut MiriGenMCShim>, thread_id: i32, parent_id: i32);
267+
fn handleThreadJoin(self: Pin<&mut MiriGenMCShim>, thread_id: i32, child_id: i32);
268+
fn handleThreadFinish(self: Pin<&mut MiriGenMCShim>, thread_id: i32, ret_val: u64);
269+
270+
/**** Blocking instructions ****/
271+
fn handleUserBlock(self: Pin<&mut MiriGenMCShim>, thread_id: i32);
272+
273+
/**** Mutex handling ****/
274+
fn handleMutexLock(
275+
self: Pin<&mut MiriGenMCShim>,
276+
thread_id: i32,
277+
address: u64,
278+
size: u64,
279+
) -> MutexLockResult;
280+
fn handleMutexTryLock(
281+
self: Pin<&mut MiriGenMCShim>,
282+
thread_id: i32,
283+
address: u64,
284+
size: u64,
285+
) -> MutexLockResult;
286+
fn handleMutexUnlock(
287+
self: Pin<&mut MiriGenMCShim>,
288+
thread_id: i32,
289+
address: u64,
290+
size: u64,
291+
) -> StoreResult;
292+
293+
/**** Scheduling ****/
294+
fn scheduleNext(
295+
self: Pin<&mut MiriGenMCShim>,
296+
curr_thread_id: i32,
297+
curr_thread_next_instr_kind: ActionKind,
298+
) -> i64;
299+
300+
fn getBlockedExecutionCount(self: &MiriGenMCShim) -> u64;
301+
fn getExploredExecutionCount(self: &MiriGenMCShim) -> u64;
302+
303+
/// Check whether there are more executions to explore.
304+
/// If there are more executions, this method prepares for the next execution and returns `true`.
305+
fn isExplorationDone(self: Pin<&mut MiriGenMCShim>) -> bool;
306+
307+
fn printGraph(self: Pin<&mut MiriGenMCShim>);
308+
fn printEstimationResults(self: &MiriGenMCShim, elapsed_time_sec: f64);
29309
}
30310
}

0 commit comments

Comments
 (0)