sync methods: pass around places, not pointer-typed operands

This commit is contained in:
Ralf Jung 2024-09-09 18:59:35 +02:00
parent 0aa3fd41f8
commit 51dbed2482
5 changed files with 67 additions and 93 deletions

View file

@ -1,7 +1,6 @@
use std::collections::VecDeque;
use rustc_index::Idx;
use rustc_middle::ty::layout::TyAndLayout;
use super::sync::EvalContextExtPriv as _;
use super::vector_clock::VClock;
@ -30,14 +29,12 @@ impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn init_once_get_or_create_id(
&mut self,
lock_op: &OpTy<'tcx>,
lock_layout: TyAndLayout<'tcx>,
lock: &MPlaceTy<'tcx>,
offset: u64,
) -> InterpResult<'tcx, InitOnceId> {
let this = self.eval_context_mut();
this.get_or_create_id(
lock_op,
lock_layout,
lock,
offset,
|ecx| &mut ecx.machine.sync.init_onces,
|_| Ok(Default::default()),

View file

@ -4,7 +4,7 @@ use std::time::Duration;
use rustc_data_structures::fx::FxHashMap;
use rustc_index::{Idx, IndexVec};
use rustc_middle::ty::layout::TyAndLayout;
use rustc_target::abi::Size;
use super::init_once::InitOnce;
use super::vector_clock::VClock;
@ -206,21 +206,21 @@ pub(super) trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
#[inline]
fn get_or_create_id<Id: SyncId + Idx, T>(
&mut self,
lock_op: &OpTy<'tcx>,
lock_layout: TyAndLayout<'tcx>,
lock: &MPlaceTy<'tcx>,
offset: u64,
get_objs: impl for<'a> Fn(&'a mut MiriInterpCx<'tcx>) -> &'a mut IndexVec<Id, T>,
create_obj: impl for<'a> FnOnce(&'a mut MiriInterpCx<'tcx>) -> InterpResult<'tcx, T>,
) -> InterpResult<'tcx, Option<Id>> {
let this = self.eval_context_mut();
let value_place =
this.deref_pointer_and_offset(lock_op, offset, lock_layout, this.machine.layouts.u32)?;
let offset = Size::from_bytes(offset);
assert!(lock.layout.size >= offset + this.machine.layouts.u32.size);
let id_place = lock.offset(offset, this.machine.layouts.u32, this)?;
let next_index = get_objs(this).next_index();
// Since we are lazy, this update has to be atomic.
let (old, success) = this
.atomic_compare_exchange_scalar(
&value_place,
&id_place,
&ImmTy::from_uint(0u32, this.machine.layouts.u32),
Scalar::from_u32(next_index.to_u32()),
AtomicRwOrd::Relaxed, // deliberately *no* synchronization
@ -258,18 +258,18 @@ pub(super) trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
/// - `obj` must be the new sync object.
fn create_id<Id: SyncId + Idx, T>(
&mut self,
lock_op: &OpTy<'tcx>,
lock_layout: TyAndLayout<'tcx>,
lock: &MPlaceTy<'tcx>,
offset: u64,
get_objs: impl for<'a> Fn(&'a mut MiriInterpCx<'tcx>) -> &'a mut IndexVec<Id, T>,
obj: T,
) -> InterpResult<'tcx, Id> {
let this = self.eval_context_mut();
let value_place =
this.deref_pointer_and_offset(lock_op, offset, lock_layout, this.machine.layouts.u32)?;
let offset = Size::from_bytes(offset);
assert!(lock.layout.size >= offset + this.machine.layouts.u32.size);
let id_place = lock.offset(offset, this.machine.layouts.u32, this)?;
let new_index = get_objs(this).push(obj);
this.write_scalar(Scalar::from_u32(new_index.to_u32()), &value_place)?;
this.write_scalar(Scalar::from_u32(new_index.to_u32()), &id_place)?;
Ok(new_index)
}
@ -302,15 +302,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
/// Eagerly create and initialize a new mutex.
fn mutex_create(
&mut self,
lock_op: &OpTy<'tcx>,
lock_layout: TyAndLayout<'tcx>,
lock: &MPlaceTy<'tcx>,
offset: u64,
data: Option<AdditionalMutexData>,
) -> InterpResult<'tcx, MutexId> {
let this = self.eval_context_mut();
this.create_id(
lock_op,
lock_layout,
lock,
offset,
|ecx| &mut ecx.machine.sync.mutexes,
Mutex { data, ..Default::default() },
@ -321,8 +319,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
/// `initialize_data` must return any additional data that a user wants to associate with the mutex.
fn mutex_get_or_create_id(
&mut self,
lock_op: &OpTy<'tcx>,
lock_layout: TyAndLayout<'tcx>,
lock: &MPlaceTy<'tcx>,
offset: u64,
initialize_data: impl for<'a> FnOnce(
&'a mut MiriInterpCx<'tcx>,
@ -330,8 +327,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
) -> InterpResult<'tcx, MutexId> {
let this = self.eval_context_mut();
this.get_or_create_id(
lock_op,
lock_layout,
lock,
offset,
|ecx| &mut ecx.machine.sync.mutexes,
|ecx| initialize_data(ecx).map(|data| Mutex { data, ..Default::default() }),
@ -350,8 +346,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn rwlock_get_or_create_id(
&mut self,
lock_op: &OpTy<'tcx>,
lock_layout: TyAndLayout<'tcx>,
lock: &MPlaceTy<'tcx>,
offset: u64,
initialize_data: impl for<'a> FnOnce(
&'a mut MiriInterpCx<'tcx>,
@ -360,8 +355,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
) -> InterpResult<'tcx, RwLockId> {
let this = self.eval_context_mut();
this.get_or_create_id(
lock_op,
lock_layout,
lock,
offset,
|ecx| &mut ecx.machine.sync.rwlocks,
|ecx| initialize_data(ecx).map(|data| RwLock { data, ..Default::default() }),
@ -380,14 +374,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn condvar_get_or_create_id(
&mut self,
lock_op: &OpTy<'tcx>,
lock_layout: TyAndLayout<'tcx>,
lock: &MPlaceTy<'tcx>,
offset: u64,
) -> InterpResult<'tcx, CondvarId> {
let this = self.eval_context_mut();
this.get_or_create_id(
lock_op,
lock_layout,
lock,
offset,
|ecx| &mut ecx.machine.sync.condvars,
|_| Ok(Default::default()),

View file

@ -14,12 +14,13 @@ use crate::*;
impl<'tcx> EvalContextExtPriv<'tcx> for crate::MiriInterpCx<'tcx> {}
trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn os_unfair_lock_getid(&mut self, lock_op: &OpTy<'tcx>) -> InterpResult<'tcx, MutexId> {
fn os_unfair_lock_getid(&mut self, lock_ptr: &OpTy<'tcx>) -> InterpResult<'tcx, MutexId> {
let this = self.eval_context_mut();
let lock = this.deref_pointer(lock_ptr)?;
// os_unfair_lock holds a 32-bit value, is initialized with zero and
// must be assumed to be opaque. Therefore, we can just store our
// internal mutex ID in the structure without anyone noticing.
this.mutex_get_or_create_id(lock_op, this.libc_ty_layout("os_unfair_lock"), 0, |_| Ok(None))
this.mutex_get_or_create_id(&lock, 0, |_| Ok(None))
}
}

View file

@ -18,10 +18,10 @@ fn mutexattr_kind_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u
fn mutexattr_get_kind<'tcx>(
ecx: &MiriInterpCx<'tcx>,
attr_op: &OpTy<'tcx>,
attr_ptr: &OpTy<'tcx>,
) -> InterpResult<'tcx, i32> {
ecx.deref_pointer_and_read(
attr_op,
attr_ptr,
mutexattr_kind_offset(ecx)?,
ecx.libc_ty_layout("pthread_mutexattr_t"),
ecx.machine.layouts.i32,
@ -31,11 +31,11 @@ fn mutexattr_get_kind<'tcx>(
fn mutexattr_set_kind<'tcx>(
ecx: &mut MiriInterpCx<'tcx>,
attr_op: &OpTy<'tcx>,
attr_ptr: &OpTy<'tcx>,
kind: i32,
) -> InterpResult<'tcx, ()> {
ecx.deref_pointer_and_write(
attr_op,
attr_ptr,
mutexattr_kind_offset(ecx)?,
Scalar::from_i32(kind),
ecx.libc_ty_layout("pthread_mutexattr_t"),
@ -94,15 +94,14 @@ fn mutex_id_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> {
/// Eagerly create and initialize a new mutex.
fn mutex_create<'tcx>(
ecx: &mut MiriInterpCx<'tcx>,
mutex_op: &OpTy<'tcx>,
mutex_ptr: &OpTy<'tcx>,
kind: i32,
) -> InterpResult<'tcx> {
// FIXME: might be worth changing mutex_create to take the mplace
// rather than the `OpTy`.
let address = ecx.read_pointer(mutex_op)?.addr().bytes();
let mutex = ecx.deref_pointer(mutex_ptr)?;
let address = mutex.ptr().addr().bytes();
let kind = translate_kind(ecx, kind)?;
let data = Some(AdditionalMutexData { address, kind });
ecx.mutex_create(mutex_op, ecx.libc_ty_layout("pthread_mutex_t"), mutex_id_offset(ecx)?, data)?;
ecx.mutex_create(&mutex, mutex_id_offset(ecx)?, data)?;
Ok(())
}
@ -112,24 +111,18 @@ fn mutex_create<'tcx>(
/// return an error if it has.
fn mutex_get_id<'tcx>(
ecx: &mut MiriInterpCx<'tcx>,
mutex_op: &OpTy<'tcx>,
mutex_ptr: &OpTy<'tcx>,
) -> InterpResult<'tcx, MutexId> {
let address = ecx.read_pointer(mutex_op)?.addr().bytes();
let mutex = ecx.deref_pointer(mutex_ptr)?;
let address = mutex.ptr().addr().bytes();
// FIXME: might be worth changing mutex_get_or_create_id to take the mplace
// rather than the `OpTy`.
let id = ecx.mutex_get_or_create_id(
mutex_op,
ecx.libc_ty_layout("pthread_mutex_t"),
mutex_id_offset(ecx)?,
|ecx| {
// This is called if a static initializer was used and the lock has not been assigned
// an ID yet. We have to determine the mutex kind from the static initializer.
let kind = kind_from_static_initializer(ecx, mutex_op)?;
let id = ecx.mutex_get_or_create_id(&mutex, mutex_id_offset(ecx)?, |ecx| {
// This is called if a static initializer was used and the lock has not been assigned
// an ID yet. We have to determine the mutex kind from the static initializer.
let kind = kind_from_static_initializer(ecx, &mutex)?;
Ok(Some(AdditionalMutexData { kind, address }))
},
)?;
Ok(Some(AdditionalMutexData { kind, address }))
})?;
// Check that the mutex has not been moved since last use.
let data = ecx.mutex_get_data(id).expect("data should be always exist for pthreads");
@ -143,20 +136,15 @@ fn mutex_get_id<'tcx>(
/// Returns the kind of a static initializer.
fn kind_from_static_initializer<'tcx>(
ecx: &MiriInterpCx<'tcx>,
mutex_op: &OpTy<'tcx>,
mutex: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx, MutexKind> {
// Only linux has static initializers other than PTHREAD_MUTEX_DEFAULT.
let kind = match &*ecx.tcx.sess.target.os {
"linux" => {
let offset = if ecx.pointer_size().bytes() == 8 { 16 } else { 12 };
ecx.deref_pointer_and_read(
mutex_op,
offset,
ecx.libc_ty_layout("pthread_mutex_t"),
ecx.machine.layouts.i32,
)?
.to_i32()?
let kind_place =
mutex.offset(Size::from_bytes(offset), ecx.machine.layouts.i32, ecx)?;
ecx.read_scalar(&kind_place)?.to_i32()?
}
| "illumos" | "solaris" | "macos" => ecx.eval_libc_i32("PTHREAD_MUTEX_DEFAULT"),
os => throw_unsup_format!("`pthread_mutex` is not supported on {os}"),
@ -211,16 +199,14 @@ fn rwlock_id_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> {
fn rwlock_get_id<'tcx>(
ecx: &mut MiriInterpCx<'tcx>,
rwlock_op: &OpTy<'tcx>,
rwlock_ptr: &OpTy<'tcx>,
) -> InterpResult<'tcx, RwLockId> {
let address = ecx.read_pointer(rwlock_op)?.addr().bytes();
let rwlock = ecx.deref_pointer(rwlock_ptr)?;
let address = rwlock.ptr().addr().bytes();
let id = ecx.rwlock_get_or_create_id(
rwlock_op,
ecx.libc_ty_layout("pthread_rwlock_t"),
rwlock_id_offset(ecx)?,
|_| Ok(Some(AdditionalRwLockData { address })),
)?;
let id = ecx.rwlock_get_or_create_id(&rwlock, rwlock_id_offset(ecx)?, |_| {
Ok(Some(AdditionalRwLockData { address }))
})?;
// Check that the rwlock has not been moved since last use.
let data = ecx.rwlock_get_data(id).expect("data should be always exist for pthreads");
@ -246,10 +232,10 @@ fn condattr_clock_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u
fn condattr_get_clock_id<'tcx>(
ecx: &MiriInterpCx<'tcx>,
attr_op: &OpTy<'tcx>,
attr_ptr: &OpTy<'tcx>,
) -> InterpResult<'tcx, i32> {
ecx.deref_pointer_and_read(
attr_op,
attr_ptr,
condattr_clock_offset(ecx)?,
ecx.libc_ty_layout("pthread_condattr_t"),
ecx.machine.layouts.i32,
@ -259,11 +245,11 @@ fn condattr_get_clock_id<'tcx>(
fn condattr_set_clock_id<'tcx>(
ecx: &mut MiriInterpCx<'tcx>,
attr_op: &OpTy<'tcx>,
attr_ptr: &OpTy<'tcx>,
clock_id: i32,
) -> InterpResult<'tcx, ()> {
ecx.deref_pointer_and_write(
attr_op,
attr_ptr,
condattr_clock_offset(ecx)?,
Scalar::from_i32(clock_id),
ecx.libc_ty_layout("pthread_condattr_t"),
@ -337,21 +323,18 @@ fn cond_clock_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> u64 {
fn cond_get_id<'tcx>(
ecx: &mut MiriInterpCx<'tcx>,
cond_op: &OpTy<'tcx>,
cond_ptr: &OpTy<'tcx>,
) -> InterpResult<'tcx, CondvarId> {
ecx.condvar_get_or_create_id(
cond_op,
ecx.libc_ty_layout("pthread_cond_t"),
cond_id_offset(ecx)?,
)
let cond = ecx.deref_pointer(cond_ptr)?;
ecx.condvar_get_or_create_id(&cond, cond_id_offset(ecx)?)
}
fn cond_reset_id<'tcx>(
ecx: &mut MiriInterpCx<'tcx>,
cond_op: &OpTy<'tcx>,
cond_ptr: &OpTy<'tcx>,
) -> InterpResult<'tcx, ()> {
ecx.deref_pointer_and_write(
cond_op,
cond_ptr,
cond_id_offset(ecx)?,
Scalar::from_i32(0),
ecx.libc_ty_layout("pthread_cond_t"),
@ -361,10 +344,10 @@ fn cond_reset_id<'tcx>(
fn cond_get_clock_id<'tcx>(
ecx: &MiriInterpCx<'tcx>,
cond_op: &OpTy<'tcx>,
cond_ptr: &OpTy<'tcx>,
) -> InterpResult<'tcx, i32> {
ecx.deref_pointer_and_read(
cond_op,
cond_ptr,
cond_clock_offset(ecx),
ecx.libc_ty_layout("pthread_cond_t"),
ecx.machine.layouts.i32,
@ -374,11 +357,11 @@ fn cond_get_clock_id<'tcx>(
fn cond_set_clock_id<'tcx>(
ecx: &mut MiriInterpCx<'tcx>,
cond_op: &OpTy<'tcx>,
cond_ptr: &OpTy<'tcx>,
clock_id: i32,
) -> InterpResult<'tcx, ()> {
ecx.deref_pointer_and_write(
cond_op,
cond_ptr,
cond_clock_offset(ecx),
Scalar::from_i32(clock_id),
ecx.libc_ty_layout("pthread_cond_t"),

View file

@ -10,9 +10,10 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Windows sync primitives are pointer sized.
// We only use the first 4 bytes for the id.
fn init_once_get_id(&mut self, init_once_op: &OpTy<'tcx>) -> InterpResult<'tcx, InitOnceId> {
fn init_once_get_id(&mut self, init_once_ptr: &OpTy<'tcx>) -> InterpResult<'tcx, InitOnceId> {
let this = self.eval_context_mut();
this.init_once_get_or_create_id(init_once_op, this.windows_ty_layout("INIT_ONCE"), 0)
let init_once = this.deref_pointer(init_once_ptr)?;
this.init_once_get_or_create_id(&init_once, 0)
}
/// Returns `true` if we were succssful, `false` if we would block.