blob: 0dfd4b9b80f98646cb98d6403bebaee958e737d9 [file] [log] [blame]
#![allow(unused)] // FIXME(GenMC): remove this
use std::cell::Cell;
use rustc_abi::{Align, Size};
use rustc_const_eval::interpret::{InterpCx, InterpResult, interp_ok};
use rustc_middle::mir;
use crate::{
AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, MemoryKind, MiriConfig,
MiriMachine, Scalar, ThreadId, ThreadManager, VisitProvenance, VisitWith,
};
mod config;
pub use self::config::GenmcConfig;
// FIXME(GenMC): add fields
pub struct GenmcCtx {
/// Some actions Miri does are allowed to cause data races.
/// GenMC will not be informed about certain actions (e.g. non-atomic loads) when this flag is set.
allow_data_races: Cell<bool>,
}
impl GenmcCtx {
/// Create a new `GenmcCtx` from a given config.
pub fn new(miri_config: &MiriConfig, genmc_config: &GenmcConfig) -> Self {
assert!(miri_config.genmc_mode);
todo!()
}
pub fn get_stuck_execution_count(&self) -> usize {
todo!()
}
pub fn print_genmc_graph(&self) {
todo!()
}
/// This function determines if we should continue exploring executions or if we are done.
///
/// In GenMC mode, the input program should be repeatedly executed until this function returns `true` or an error is found.
pub fn is_exploration_done(&self) -> bool {
todo!()
}
/// Inform GenMC that a new program execution has started.
/// This function should be called at the start of every execution.
pub(crate) fn handle_execution_start(&self) {
todo!()
}
/// Inform GenMC that the program's execution has ended.
///
/// This function must be called even when the execution got stuck (i.e., it returned a `InterpErrorKind::MachineStop` with error kind `TerminationInfo::GenmcStuckExecution`).
pub(crate) fn handle_execution_end<'tcx>(
&self,
ecx: &InterpCx<'tcx, MiriMachine<'tcx>>,
) -> Result<(), String> {
todo!()
}
/**** Memory access handling ****/
/// Select whether data race free actions should be allowed. This function should be used carefully!
///
/// If `true` is passed, allow for data races to happen without triggering an error, until this function is called again with argument `false`.
/// This allows for racy non-atomic memory accesses to be ignored (GenMC is not informed about them at all).
///
/// Certain operations are not permitted in GenMC mode with data races disabled and will cause a panic, e.g., atomic accesses or asking for scheduling decisions.
///
/// # Panics
/// If data race free is attempted to be set more than once (i.e., no nesting allowed).
pub(super) fn set_ongoing_action_data_race_free(&self, enable: bool) {
let old = self.allow_data_races.replace(enable);
assert_ne!(old, enable, "cannot nest allow_data_races");
}
pub(crate) fn atomic_load<'tcx>(
&self,
ecx: &InterpCx<'tcx, MiriMachine<'tcx>>,
address: Size,
size: Size,
ordering: AtomicReadOrd,
old_val: Option<Scalar>,
) -> InterpResult<'tcx, Scalar> {
assert!(!self.allow_data_races.get());
todo!()
}
pub(crate) fn atomic_store<'tcx>(
&self,
ecx: &InterpCx<'tcx, MiriMachine<'tcx>>,
address: Size,
size: Size,
value: Scalar,
ordering: AtomicWriteOrd,
) -> InterpResult<'tcx, ()> {
assert!(!self.allow_data_races.get());
todo!()
}
pub(crate) fn atomic_fence<'tcx>(
&self,
machine: &MiriMachine<'tcx>,
ordering: AtomicFenceOrd,
) -> InterpResult<'tcx, ()> {
assert!(!self.allow_data_races.get());
todo!()
}
/// Inform GenMC about an atomic read-modify-write operation.
///
/// Returns `(old_val, new_val)`.
pub(crate) fn atomic_rmw_op<'tcx>(
&self,
ecx: &InterpCx<'tcx, MiriMachine<'tcx>>,
address: Size,
size: Size,
ordering: AtomicRwOrd,
(rmw_op, not): (mir::BinOp, bool),
rhs_scalar: Scalar,
) -> InterpResult<'tcx, (Scalar, Scalar)> {
assert!(!self.allow_data_races.get());
todo!()
}
/// Inform GenMC about an atomic `min` or `max` operation.
///
/// Returns `(old_val, new_val)`.
pub(crate) fn atomic_min_max_op<'tcx>(
&self,
ecx: &InterpCx<'tcx, MiriMachine<'tcx>>,
address: Size,
size: Size,
ordering: AtomicRwOrd,
min: bool,
is_signed: bool,
rhs_scalar: Scalar,
) -> InterpResult<'tcx, (Scalar, Scalar)> {
assert!(!self.allow_data_races.get());
todo!()
}
pub(crate) fn atomic_exchange<'tcx>(
&self,
ecx: &InterpCx<'tcx, MiriMachine<'tcx>>,
address: Size,
size: Size,
rhs_scalar: Scalar,
ordering: AtomicRwOrd,
) -> InterpResult<'tcx, (Scalar, bool)> {
assert!(!self.allow_data_races.get());
todo!()
}
pub(crate) fn atomic_compare_exchange<'tcx>(
&self,
ecx: &InterpCx<'tcx, MiriMachine<'tcx>>,
address: Size,
size: Size,
expected_old_value: Scalar,
new_value: Scalar,
success: AtomicRwOrd,
fail: AtomicReadOrd,
can_fail_spuriously: bool,
) -> InterpResult<'tcx, (Scalar, bool)> {
assert!(!self.allow_data_races.get());
todo!()
}
/// Inform GenMC about a non-atomic memory load
///
/// NOTE: Unlike for *atomic* loads, we don't return a value here. Non-atomic values are still handled by Miri.
pub(crate) fn memory_load<'tcx>(
&self,
machine: &MiriMachine<'tcx>,
address: Size,
size: Size,
) -> InterpResult<'tcx, ()> {
todo!()
}
pub(crate) fn memory_store<'tcx>(
&self,
machine: &MiriMachine<'tcx>,
address: Size,
size: Size,
) -> InterpResult<'tcx, ()> {
todo!()
}
/**** Memory (de)allocation ****/
pub(crate) fn handle_alloc<'tcx>(
&self,
machine: &MiriMachine<'tcx>,
size: Size,
alignment: Align,
memory_kind: MemoryKind,
) -> InterpResult<'tcx, u64> {
todo!()
}
pub(crate) fn handle_dealloc<'tcx>(
&self,
machine: &MiriMachine<'tcx>,
address: Size,
size: Size,
align: Align,
kind: MemoryKind,
) -> InterpResult<'tcx, ()> {
todo!()
}
/**** Thread management ****/
pub(crate) fn handle_thread_create<'tcx>(
&self,
threads: &ThreadManager<'tcx>,
new_thread_id: ThreadId,
) -> InterpResult<'tcx, ()> {
assert!(!self.allow_data_races.get());
todo!()
}
pub(crate) fn handle_thread_join<'tcx>(
&self,
active_thread_id: ThreadId,
child_thread_id: ThreadId,
) -> InterpResult<'tcx, ()> {
assert!(!self.allow_data_races.get());
todo!()
}
pub(crate) fn handle_thread_stack_empty(&self, thread_id: ThreadId) {
todo!()
}
pub(crate) fn handle_thread_finish<'tcx>(
&self,
threads: &ThreadManager<'tcx>,
) -> InterpResult<'tcx, ()> {
assert!(!self.allow_data_races.get());
todo!()
}
/**** Scheduling functionality ****/
/// Ask for a scheduling decision. This should be called before every MIR instruction.
///
/// GenMC may realize that the execution got stuck, then this function will return a `InterpErrorKind::MachineStop` with error kind `TerminationInfo::GenmcStuckExecution`).
///
/// This is **not** an error by iself! Treat this as if the program ended normally: `handle_execution_end` should be called next, which will determine if were are any actual errors.
pub(crate) fn schedule_thread<'tcx>(
&self,
ecx: &InterpCx<'tcx, MiriMachine<'tcx>>,
) -> InterpResult<'tcx, ThreadId> {
assert!(!self.allow_data_races.get());
todo!()
}
/**** Blocking instructions ****/
pub(crate) fn handle_verifier_assume<'tcx>(
&self,
machine: &MiriMachine<'tcx>,
condition: bool,
) -> InterpResult<'tcx, ()> {
if condition { interp_ok(()) } else { self.handle_user_block(machine) }
}
}
impl VisitProvenance for GenmcCtx {
fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {
// We don't have any tags.
}
}
impl GenmcCtx {
fn handle_user_block<'tcx>(&self, machine: &MiriMachine<'tcx>) -> InterpResult<'tcx, ()> {
todo!()
}
}