diff --git a/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs b/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs index 46dcebc46e9c8..8492e01629be8 100644 --- a/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs @@ -90,7 +90,7 @@ impl<'tcx> interpret::Machine<'tcx> for DummyMachine { _instance: ty::Instance<'tcx>, _abi: &FnAbi<'tcx, Ty<'tcx>>, _args: &[interpret::FnArg<'tcx, Self::Provenance>], - _destination: &interpret::MPlaceTy<'tcx, Self::Provenance>, + _destination: &interpret::PlaceTy<'tcx, Self::Provenance>, _target: Option, _unwind: UnwindAction, ) -> interpret::InterpResult<'tcx, Option<(&'tcx Body<'tcx>, ty::Instance<'tcx>)>> { @@ -108,7 +108,7 @@ impl<'tcx> interpret::Machine<'tcx> for DummyMachine { _ecx: &mut InterpCx<'tcx, Self>, _instance: ty::Instance<'tcx>, _args: &[interpret::OpTy<'tcx, Self::Provenance>], - _destination: &interpret::MPlaceTy<'tcx, Self::Provenance>, + _destination: &interpret::PlaceTy<'tcx, Self::Provenance>, _target: Option, _unwind: UnwindAction, ) -> interpret::InterpResult<'tcx, Option>> { diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index ce8eceebdf8d2..a79ba6a634272 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -71,7 +71,12 @@ fn eval_body_using_ecx<'tcx, R: InterpretationResult<'tcx>>( // This can't use `init_stack_frame` since `body` is not a function, // so computing its ABI would fail. It's also not worth it since there are no arguments to pass. - ecx.push_stack_frame_raw(cid.instance, body, &ret, StackPopCleanup::Root { cleanup: false })?; + ecx.push_stack_frame_raw( + cid.instance, + body, + &ret.clone().into(), + StackPopCleanup::Root { cleanup: false }, + )?; ecx.storage_live_for_always_live_locals()?; // The main interpreter loop. diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index 7c7daed525b2d..9fe7a2336c32b 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -22,7 +22,7 @@ use crate::errors::{LongRunning, LongRunningWarn}; use crate::fluent_generated as fluent; use crate::interpret::{ self, AllocId, AllocInit, AllocRange, ConstAllocation, CtfeProvenance, FnArg, Frame, - GlobalAlloc, ImmTy, InterpCx, InterpResult, MPlaceTy, OpTy, Pointer, RangeSet, Scalar, + GlobalAlloc, ImmTy, InterpCx, InterpResult, OpTy, PlaceTy, Pointer, RangeSet, Scalar, compile_time_machine, interp_ok, throw_exhaust, throw_inval, throw_ub, throw_ub_custom, throw_unsup, throw_unsup_format, }; @@ -226,7 +226,7 @@ impl<'tcx> CompileTimeInterpCx<'tcx> { &mut self, instance: ty::Instance<'tcx>, args: &[FnArg<'tcx>], - _dest: &MPlaceTy<'tcx>, + _dest: &PlaceTy<'tcx>, _ret: Option, ) -> InterpResult<'tcx, Option>> { let def_id = instance.def_id(); @@ -343,7 +343,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { orig_instance: ty::Instance<'tcx>, _abi: &FnAbi<'tcx, Ty<'tcx>>, args: &[FnArg<'tcx>], - dest: &MPlaceTy<'tcx>, + dest: &PlaceTy<'tcx>, ret: Option, _unwind: mir::UnwindAction, // unwinding is not supported in consts ) -> InterpResult<'tcx, Option<(&'tcx mir::Body<'tcx>, ty::Instance<'tcx>)>> { @@ -385,7 +385,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { ecx: &mut InterpCx<'tcx, Self>, instance: ty::Instance<'tcx>, args: &[OpTy<'tcx>], - dest: &MPlaceTy<'tcx, Self::Provenance>, + dest: &PlaceTy<'tcx, Self::Provenance>, target: Option, _unwind: mir::UnwindAction, ) -> InterpResult<'tcx, Option>> { diff --git a/compiler/rustc_const_eval/src/interpret/call.rs b/compiler/rustc_const_eval/src/interpret/call.rs index 405208e94f4b8..789baea073463 100644 --- a/compiler/rustc_const_eval/src/interpret/call.rs +++ b/compiler/rustc_const_eval/src/interpret/call.rs @@ -339,7 +339,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { caller_fn_abi: &FnAbi<'tcx, Ty<'tcx>>, args: &[FnArg<'tcx, M::Provenance>], with_caller_location: bool, - destination: &MPlaceTy<'tcx, M::Provenance>, + destination: &PlaceTy<'tcx, M::Provenance>, mut stack_pop: StackPopCleanup, ) -> InterpResult<'tcx> { // Compute callee information. @@ -487,7 +487,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } // Protect return place for in-place return value passing. - M::protect_in_place_function_argument(self, &destination)?; + // We only need to protect anything if this is actually an in-memory place. + if let Left(mplace) = destination.as_mplace_or_local() { + M::protect_in_place_function_argument(self, &mplace)?; + } // Don't forget to mark "initially live" locals as live. self.storage_live_for_always_live_locals()?; @@ -512,7 +515,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { (caller_abi, caller_fn_abi): (ExternAbi, &FnAbi<'tcx, Ty<'tcx>>), args: &[FnArg<'tcx, M::Provenance>], with_caller_location: bool, - destination: &MPlaceTy<'tcx, M::Provenance>, + destination: &PlaceTy<'tcx, M::Provenance>, target: Option, unwind: mir::UnwindAction, ) -> InterpResult<'tcx> { @@ -776,10 +779,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // Note that we are using `pop_stack_frame_raw` and not `return_from_current_stack_frame`, // as the latter "executes" the goto to the return block, but we don't want to, // only the tail called function should return to the current return block. - M::before_stack_pop(self, self.frame())?; - - let StackPopInfo { return_action, return_to_block, return_place } = - self.pop_stack_frame_raw(false)?; + let StackPopInfo { return_action, return_to_block, return_place } = self + .pop_stack_frame_raw(false, |_this, _return_place| { + // This function's return value is just discarded, the tail-callee will fill in the return place instead. + interp_ok(()) + })?; assert_eq!(return_action, ReturnAction::Normal); @@ -850,7 +854,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { (ExternAbi::Rust, fn_abi), &[FnArg::Copy(arg.into())], false, - &ret, + &ret.into(), Some(target), unwind, ) @@ -891,28 +895,16 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { throw_ub_custom!(fluent::const_eval_unwind_past_top); } - M::before_stack_pop(self, self.frame())?; - - // Copy return value. Must of course happen *before* we deallocate the locals. - // Must be *after* `before_stack_pop` as otherwise the return place might still be protected. - let copy_ret_result = if !unwinding { - let op = self - .local_to_op(mir::RETURN_PLACE, None) - .expect("return place should always be live"); - let dest = self.frame().return_place.clone(); - let res = self.copy_op_allow_transmute(&op, &dest); - trace!("return value: {:?}", self.dump_place(&dest.into())); - // We delay actually short-circuiting on this error until *after* the stack frame is - // popped, since we want this error to be attributed to the caller, whose type defines - // this transmute. - res - } else { + // Get out the return value. Must happen *before* the frame is popped as we have to get the + // local's value out. + let return_op = + self.local_to_op(mir::RETURN_PLACE, None).expect("return place should always be live"); + // Do the actual pop + copy. + let stack_pop_info = self.pop_stack_frame_raw(unwinding, |this, return_place| { + this.copy_op_allow_transmute(&return_op, return_place)?; + trace!("return value: {:?}", this.dump_place(return_place)); interp_ok(()) - }; - - // All right, now it is time to actually pop the frame. - // An error here takes precedence over the copy error. - let (stack_pop_info, ()) = self.pop_stack_frame_raw(unwinding).and(copy_ret_result)?; + })?; match stack_pop_info.return_action { ReturnAction::Normal => {} @@ -924,7 +916,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // If we are not doing cleanup, also skip everything else. assert!(self.stack().is_empty(), "only the topmost frame should ever be leaked"); assert!(!unwinding, "tried to skip cleanup during unwinding"); - // Skip machine hook. + // Don't jump anywhere. return interp_ok(()); } } diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index 090b2a692cfc3..ee670b6245f09 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -17,9 +17,9 @@ use tracing::trace; use super::memory::MemoryKind; use super::util::ensure_monomorphic_enough; use super::{ - Allocation, CheckInAllocMsg, ConstAllocation, GlobalId, ImmTy, InterpCx, InterpResult, - MPlaceTy, Machine, OpTy, Pointer, PointerArithmetic, Provenance, Scalar, err_inval, - err_ub_custom, err_unsup_format, interp_ok, throw_inval, throw_ub_custom, throw_ub_format, + Allocation, CheckInAllocMsg, ConstAllocation, GlobalId, ImmTy, InterpCx, InterpResult, Machine, + OpTy, PlaceTy, Pointer, PointerArithmetic, Provenance, Scalar, err_inval, err_ub_custom, + err_unsup_format, interp_ok, throw_inval, throw_ub_custom, throw_ub_format, }; use crate::fluent_generated as fluent; @@ -112,7 +112,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { &mut self, instance: ty::Instance<'tcx>, args: &[OpTy<'tcx, M::Provenance>], - dest: &MPlaceTy<'tcx, M::Provenance>, + dest: &PlaceTy<'tcx, M::Provenance>, ret: Option, ) -> InterpResult<'tcx, bool> { let instance_args = instance.args; @@ -587,7 +587,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { &mut self, a: &ImmTy<'tcx, M::Provenance>, b: &ImmTy<'tcx, M::Provenance>, - dest: &MPlaceTy<'tcx, M::Provenance>, + dest: &PlaceTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx> { assert_eq!(a.layout.ty, b.layout.ty); assert_matches!(a.layout.ty.kind(), ty::Int(..) | ty::Uint(..)); @@ -801,7 +801,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { fn float_min_intrinsic( &mut self, args: &[OpTy<'tcx, M::Provenance>], - dest: &MPlaceTy<'tcx, M::Provenance>, + dest: &PlaceTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx, ()> where F: rustc_apfloat::Float + rustc_apfloat::FloatConvert + Into>, @@ -822,7 +822,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { fn float_max_intrinsic( &mut self, args: &[OpTy<'tcx, M::Provenance>], - dest: &MPlaceTy<'tcx, M::Provenance>, + dest: &PlaceTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx, ()> where F: rustc_apfloat::Float + rustc_apfloat::FloatConvert + Into>, @@ -843,7 +843,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { fn float_minimum_intrinsic( &mut self, args: &[OpTy<'tcx, M::Provenance>], - dest: &MPlaceTy<'tcx, M::Provenance>, + dest: &PlaceTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx, ()> where F: rustc_apfloat::Float + rustc_apfloat::FloatConvert + Into>, @@ -859,7 +859,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { fn float_maximum_intrinsic( &mut self, args: &[OpTy<'tcx, M::Provenance>], - dest: &MPlaceTy<'tcx, M::Provenance>, + dest: &PlaceTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx, ()> where F: rustc_apfloat::Float + rustc_apfloat::FloatConvert + Into>, @@ -875,7 +875,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { fn float_copysign_intrinsic( &mut self, args: &[OpTy<'tcx, M::Provenance>], - dest: &MPlaceTy<'tcx, M::Provenance>, + dest: &PlaceTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx, ()> where F: rustc_apfloat::Float + rustc_apfloat::FloatConvert + Into>, @@ -890,7 +890,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { fn float_abs_intrinsic( &mut self, args: &[OpTy<'tcx, M::Provenance>], - dest: &MPlaceTy<'tcx, M::Provenance>, + dest: &PlaceTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx, ()> where F: rustc_apfloat::Float + rustc_apfloat::FloatConvert + Into>, diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs index d13e17a481a46..b65d9444caf01 100644 --- a/compiler/rustc_const_eval/src/interpret/machine.rs +++ b/compiler/rustc_const_eval/src/interpret/machine.rs @@ -208,7 +208,7 @@ pub trait Machine<'tcx>: Sized { instance: ty::Instance<'tcx>, abi: &FnAbi<'tcx, Ty<'tcx>>, args: &[FnArg<'tcx, Self::Provenance>], - destination: &MPlaceTy<'tcx, Self::Provenance>, + destination: &PlaceTy<'tcx, Self::Provenance>, target: Option, unwind: mir::UnwindAction, ) -> InterpResult<'tcx, Option<(&'tcx mir::Body<'tcx>, ty::Instance<'tcx>)>>; @@ -220,7 +220,7 @@ pub trait Machine<'tcx>: Sized { fn_val: Self::ExtraFnVal, abi: &FnAbi<'tcx, Ty<'tcx>>, args: &[FnArg<'tcx, Self::Provenance>], - destination: &MPlaceTy<'tcx, Self::Provenance>, + destination: &PlaceTy<'tcx, Self::Provenance>, target: Option, unwind: mir::UnwindAction, ) -> InterpResult<'tcx>; @@ -234,7 +234,7 @@ pub trait Machine<'tcx>: Sized { ecx: &mut InterpCx<'tcx, Self>, instance: ty::Instance<'tcx>, args: &[OpTy<'tcx, Self::Provenance>], - destination: &MPlaceTy<'tcx, Self::Provenance>, + destination: &PlaceTy<'tcx, Self::Provenance>, target: Option, unwind: mir::UnwindAction, ) -> InterpResult<'tcx, Option>>; @@ -536,11 +536,9 @@ pub trait Machine<'tcx>: Sized { interp_ok(()) } - /// Called just before the return value is copied to the caller-provided return place. - fn before_stack_pop( - _ecx: &InterpCx<'tcx, Self>, - _frame: &Frame<'tcx, Self::Provenance, Self::FrameExtra>, - ) -> InterpResult<'tcx> { + /// Called just before the frame is removed from the stack (followed by return value copy and + /// local cleanup). + fn before_stack_pop(_ecx: &mut InterpCx<'tcx, Self>) -> InterpResult<'tcx> { interp_ok(()) } @@ -675,7 +673,7 @@ pub macro compile_time_machine(<$tcx: lifetime>) { fn_val: !, _abi: &FnAbi<$tcx, Ty<$tcx>>, _args: &[FnArg<$tcx>], - _destination: &MPlaceTy<$tcx, Self::Provenance>, + _destination: &PlaceTy<$tcx, Self::Provenance>, _target: Option, _unwind: mir::UnwindAction, ) -> InterpResult<$tcx> { diff --git a/compiler/rustc_const_eval/src/interpret/stack.rs b/compiler/rustc_const_eval/src/interpret/stack.rs index d7b03776bc481..2a2d1bb27547e 100644 --- a/compiler/rustc_const_eval/src/interpret/stack.rs +++ b/compiler/rustc_const_eval/src/interpret/stack.rs @@ -15,9 +15,9 @@ use rustc_span::Span; use tracing::{info_span, instrument, trace}; use super::{ - AllocId, CtfeProvenance, Immediate, InterpCx, InterpResult, MPlaceTy, Machine, MemPlace, - MemPlaceMeta, MemoryKind, Operand, Pointer, Provenance, ReturnAction, Scalar, - from_known_layout, interp_ok, throw_ub, throw_unsup, + AllocId, CtfeProvenance, Immediate, InterpCx, InterpResult, Machine, MemPlace, MemPlaceMeta, + MemoryKind, Operand, PlaceTy, Pointer, Provenance, ReturnAction, Scalar, from_known_layout, + interp_ok, throw_ub, throw_unsup, }; use crate::errors; @@ -76,8 +76,10 @@ pub struct Frame<'tcx, Prov: Provenance = CtfeProvenance, Extra = ()> { return_to_block: StackPopCleanup, /// The location where the result of the current stack frame should be written to, - /// and its layout in the caller. - pub return_place: MPlaceTy<'tcx, Prov>, + /// and its layout in the caller. This place is to be interpreted relative to the + /// *caller's* stack frame. We use a `PlaceTy` instead of an `MPlaceTy` since this + /// avoids having to move *all* return places into Miri's memory. + pub return_place: PlaceTy<'tcx, Prov>, /// The list of locals for this stack frame, stored in order as /// `[return_ptr, arguments..., variables..., temporaries...]`. @@ -129,7 +131,7 @@ pub struct StackPopInfo<'tcx, Prov: Provenance> { pub return_to_block: StackPopCleanup, /// [`return_place`](Frame::return_place) of the popped stack frame. - pub return_place: MPlaceTy<'tcx, Prov>, + pub return_place: PlaceTy<'tcx, Prov>, } /// State of a local variable including a memoized layout @@ -353,7 +355,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { &mut self, instance: ty::Instance<'tcx>, body: &'tcx mir::Body<'tcx>, - return_place: &MPlaceTy<'tcx, M::Provenance>, + return_place: &PlaceTy<'tcx, M::Provenance>, return_to_block: StackPopCleanup, ) -> InterpResult<'tcx> { trace!("body: {:#?}", body); @@ -404,9 +406,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// it. /// /// This also deallocates locals, if necessary. + /// `copy_ret_val` gets called after the frame has been taken from the stack but before the locals have been deallocated. /// - /// [`M::before_stack_pop`] should be called before calling this function. - /// [`M::after_stack_pop`] is called by this function automatically. + /// [`M::before_stack_pop`] and [`M::after_stack_pop`] are called by this function + /// automatically. /// /// The high-level version of this is `return_from_current_stack_frame`. /// @@ -415,47 +418,44 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { pub(super) fn pop_stack_frame_raw( &mut self, unwinding: bool, + copy_ret_val: impl FnOnce(&mut Self, &PlaceTy<'tcx, M::Provenance>) -> InterpResult<'tcx>, ) -> InterpResult<'tcx, StackPopInfo<'tcx, M::Provenance>> { - let cleanup = self.cleanup_current_frame_locals()?; - + M::before_stack_pop(self)?; let frame = self.stack_mut().pop().expect("tried to pop a stack frame, but there were none"); + // Copy return value (unless we are unwinding). + if !unwinding { + copy_ret_val(self, &frame.return_place)?; + } + let return_to_block = frame.return_to_block; let return_place = frame.return_place.clone(); - let return_action; - if cleanup { - return_action = M::after_stack_pop(self, frame, unwinding)?; - assert_ne!(return_action, ReturnAction::NoCleanup); - } else { - return_action = ReturnAction::NoCleanup; - }; - - interp_ok(StackPopInfo { return_action, return_to_block, return_place }) - } - - /// A private helper for [`pop_stack_frame_raw`](InterpCx::pop_stack_frame_raw). - /// Returns `true` if cleanup has been done, `false` otherwise. - fn cleanup_current_frame_locals(&mut self) -> InterpResult<'tcx, bool> { // Cleanup: deallocate locals. // Usually we want to clean up (deallocate locals), but in a few rare cases we don't. // We do this while the frame is still on the stack, so errors point to the callee. - let return_to_block = self.frame().return_to_block; let cleanup = match return_to_block { StackPopCleanup::Goto { .. } => true, StackPopCleanup::Root { cleanup, .. } => cleanup, }; - if cleanup { + let return_action = if cleanup { // We need to take the locals out, since we need to mutate while iterating. - let locals = mem::take(&mut self.frame_mut().locals); - for local in &locals { + for local in &frame.locals { self.deallocate_local(local.value)?; } - } - interp_ok(cleanup) + // Call the machine hook, which determines the next steps. + let return_action = M::after_stack_pop(self, frame, unwinding)?; + assert_ne!(return_action, ReturnAction::NoCleanup); + return_action + } else { + // We also skip the machine hook when there's no cleanup. This not a real "pop" anyway. + ReturnAction::NoCleanup + }; + + interp_ok(StackPopInfo { return_action, return_to_block, return_place }) } /// In the current stack frame, mark all locals as live that are not arguments and don't have diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index 363ceee1970ee..975325b0c1e09 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -506,7 +506,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let EvaluatedCalleeAndArgs { callee, args, fn_sig, fn_abi, with_caller_location } = self.eval_callee_and_args(terminator, func, args)?; - let destination = self.force_allocation(&self.eval_place(destination)?)?; + let destination = self.eval_place(destination)?; self.init_fn_call( callee, (fn_sig.abi, fn_abi), diff --git a/src/tools/miri/src/concurrency/thread.rs b/src/tools/miri/src/concurrency/thread.rs index 8aa65e6cb612b..6b760034ee805 100644 --- a/src/tools/miri/src/concurrency/thread.rs +++ b/src/tools/miri/src/concurrency/thread.rs @@ -218,34 +218,37 @@ impl<'tcx> Thread<'tcx> { } } - /// Return the top user-relevant frame, if there is one. + /// Return the top user-relevant frame, if there is one. `skip` indicates how many top frames + /// should be skipped. /// Note that the choice to return `None` here when there is no user-relevant frame is part of /// justifying the optimization that only pushes of user-relevant frames require updating the /// `top_user_relevant_frame` field. - fn compute_top_user_relevant_frame(&self) -> Option { + fn compute_top_user_relevant_frame(&self, skip: usize) -> Option { self.stack .iter() .enumerate() .rev() + .skip(skip) .find_map(|(idx, frame)| if frame.extra.is_user_relevant { Some(idx) } else { None }) } - /// Re-compute the top user-relevant frame from scratch. - pub fn recompute_top_user_relevant_frame(&mut self) { - self.top_user_relevant_frame = self.compute_top_user_relevant_frame(); + /// Re-compute the top user-relevant frame from scratch. `skip` indicates how many top frames + /// should be skipped. + pub fn recompute_top_user_relevant_frame(&mut self, skip: usize) { + self.top_user_relevant_frame = self.compute_top_user_relevant_frame(skip); } /// Set the top user-relevant frame to the given value. Must be equal to what /// `get_top_user_relevant_frame` would return! pub fn set_top_user_relevant_frame(&mut self, frame_idx: usize) { - debug_assert_eq!(Some(frame_idx), self.compute_top_user_relevant_frame()); + debug_assert_eq!(Some(frame_idx), self.compute_top_user_relevant_frame(0)); self.top_user_relevant_frame = Some(frame_idx); } /// Returns the topmost frame that is considered user-relevant, or the /// top of the stack if there is no such frame, or `None` if the stack is empty. pub fn top_user_relevant_frame(&self) -> Option { - debug_assert_eq!(self.top_user_relevant_frame, self.compute_top_user_relevant_frame()); + debug_assert_eq!(self.top_user_relevant_frame, self.compute_top_user_relevant_frame(0)); // This can be called upon creation of an allocation. We create allocations while setting up // parts of the Rust runtime when we do not have any stack frames yet, so we need to handle // empty stacks. diff --git a/src/tools/miri/src/eval.rs b/src/tools/miri/src/eval.rs index a90c6ab9d40e9..8fe034d258297 100644 --- a/src/tools/miri/src/eval.rs +++ b/src/tools/miri/src/eval.rs @@ -354,11 +354,10 @@ pub fn create_ecx<'tcx>( argvs.push(arg_place.to_ref(&ecx)); } // Make an array with all these pointers, in the Miri memory. - let argvs_layout = ecx.layout_of(Ty::new_array( - tcx, - Ty::new_imm_ptr(tcx, tcx.types.u8), - u64::try_from(argvs.len()).unwrap(), - ))?; + let u8_ptr_type = Ty::new_imm_ptr(tcx, tcx.types.u8); + let u8_ptr_ptr_type = Ty::new_imm_ptr(tcx, u8_ptr_type); + let argvs_layout = + ecx.layout_of(Ty::new_array(tcx, u8_ptr_type, u64::try_from(argvs.len()).unwrap()))?; let argvs_place = ecx.allocate(argvs_layout, MiriMemoryKind::Machine.into())?; for (idx, arg) in argvs.into_iter().enumerate() { let place = ecx.project_field(&argvs_place, idx)?; @@ -373,10 +372,8 @@ pub fn create_ecx<'tcx>( ecx.mark_immutable(&argc_place); ecx.machine.argc = Some(argc_place.ptr()); - let argv_place = ecx.allocate( - ecx.layout_of(Ty::new_imm_ptr(tcx, tcx.types.unit))?, - MiriMemoryKind::Machine.into(), - )?; + let argv_place = + ecx.allocate(ecx.layout_of(u8_ptr_ptr_type)?, MiriMemoryKind::Machine.into())?; ecx.write_pointer(argvs_place.ptr(), &argv_place)?; ecx.mark_immutable(&argv_place); ecx.machine.argv = Some(argv_place.ptr()); @@ -398,7 +395,9 @@ pub fn create_ecx<'tcx>( } ecx.mark_immutable(&cmd_place); } - ecx.mplace_to_ref(&argvs_place)? + let imm = argvs_place.to_ref(&ecx); + let layout = ecx.layout_of(u8_ptr_ptr_type)?; + ImmTy::from_immediate(imm, layout) }; // Return place (in static memory so that it does not count as leak). diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs index 8e7c9edfcc078..935afcb6e9290 100644 --- a/src/tools/miri/src/helpers.rs +++ b/src/tools/miri/src/helpers.rs @@ -470,7 +470,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { caller_fn_abi, &args.iter().map(|a| FnArg::Copy(a.clone().into())).collect::>(), /*with_caller_location*/ false, - &dest, + &dest.into(), stack_pop, ) } diff --git a/src/tools/miri/src/intrinsics/mod.rs b/src/tools/miri/src/intrinsics/mod.rs index 982fbc318110e..69baa472cd694 100644 --- a/src/tools/miri/src/intrinsics/mod.rs +++ b/src/tools/miri/src/intrinsics/mod.rs @@ -22,7 +22,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { &mut self, instance: ty::Instance<'tcx>, args: &[OpTy<'tcx>], - dest: &MPlaceTy<'tcx>, + dest: &PlaceTy<'tcx>, ret: Option, unwind: mir::UnwindAction, ) -> InterpResult<'tcx, Option>> { @@ -45,7 +45,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let intrinsic_name = this.tcx.item_name(instance.def_id()); let intrinsic_name = intrinsic_name.as_str(); - match this.emulate_intrinsic_by_name(intrinsic_name, instance.args, args, dest, ret)? { + // FIXME: avoid allocating memory + let dest = this.force_allocation(dest)?; + + match this.emulate_intrinsic_by_name(intrinsic_name, instance.args, args, &dest, ret)? { EmulateItemResult::NotSupported => { // We haven't handled the intrinsic, let's see if we can use a fallback body. if this.tcx.intrinsic(instance.def_id()).unwrap().must_be_overridden { diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index 1c6c7894cb456..ac1f9f3822cd5 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -1120,7 +1120,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { instance: ty::Instance<'tcx>, abi: &FnAbi<'tcx, Ty<'tcx>>, args: &[FnArg<'tcx, Provenance>], - dest: &MPlaceTy<'tcx>, + dest: &PlaceTy<'tcx>, ret: Option, unwind: mir::UnwindAction, ) -> InterpResult<'tcx, Option<(&'tcx mir::Body<'tcx>, ty::Instance<'tcx>)>> { @@ -1147,7 +1147,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { fn_val: DynSym, abi: &FnAbi<'tcx, Ty<'tcx>>, args: &[FnArg<'tcx, Provenance>], - dest: &MPlaceTy<'tcx>, + dest: &PlaceTy<'tcx>, ret: Option, unwind: mir::UnwindAction, ) -> InterpResult<'tcx> { @@ -1160,7 +1160,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { ecx: &mut MiriInterpCx<'tcx>, instance: ty::Instance<'tcx>, args: &[OpTy<'tcx>], - dest: &MPlaceTy<'tcx>, + dest: &PlaceTy<'tcx>, ret: Option, unwind: mir::UnwindAction, ) -> InterpResult<'tcx, Option>> { @@ -1639,15 +1639,21 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { interp_ok(()) } - fn before_stack_pop( - ecx: &InterpCx<'tcx, Self>, - frame: &Frame<'tcx, Self::Provenance, Self::FrameExtra>, - ) -> InterpResult<'tcx> { + fn before_stack_pop(ecx: &mut InterpCx<'tcx, Self>) -> InterpResult<'tcx> { + let frame = ecx.frame(); // We want this *before* the return value copy, because the return place itself is protected // until we do `end_call` here. if ecx.machine.borrow_tracker.is_some() { ecx.on_stack_pop(frame)?; } + if frame.extra.is_user_relevant { + // All that we store is whether or not the frame we just removed is local, so now we + // have no idea where the next topmost local frame is. So we recompute it. + // (If this ever becomes a bottleneck, we could have `push` store the previous + // user-relevant frame and restore that here.) + // We have to skip the frame that is just being popped. + ecx.active_thread_mut().recompute_top_user_relevant_frame(/* skip */ 1); + } // tracing-tree can autoamtically annotate scope changes, but it gets very confused by our // concurrency and what it prints is just plain wrong. So we print our own information // instead. (Cc https://github.com/rust-lang/miri/issues/2266) @@ -1661,15 +1667,8 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { frame: Frame<'tcx, Provenance, FrameExtra<'tcx>>, unwinding: bool, ) -> InterpResult<'tcx, ReturnAction> { - if frame.extra.is_user_relevant { - // All that we store is whether or not the frame we just removed is local, so now we - // have no idea where the next topmost local frame is. So we recompute it. - // (If this ever becomes a bottleneck, we could have `push` store the previous - // user-relevant frame and restore that here.) - ecx.active_thread_mut().recompute_top_user_relevant_frame(); - } let res = { - // Move `frame`` into a sub-scope so we control when it will be dropped. + // Move `frame` into a sub-scope so we control when it will be dropped. let mut frame = frame; let timing = frame.extra.timing.take(); let res = ecx.handle_stack_pop_unwind(frame.extra, unwinding); diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index 52c16a0c2e2dc..1195b741b19eb 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -43,7 +43,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { link_name: Symbol, abi: &FnAbi<'tcx, Ty<'tcx>>, args: &[OpTy<'tcx>], - dest: &MPlaceTy<'tcx>, + dest: &PlaceTy<'tcx>, ret: Option, unwind: mir::UnwindAction, ) -> InterpResult<'tcx, Option<(&'tcx mir::Body<'tcx>, ty::Instance<'tcx>)>> { @@ -69,8 +69,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { _ => {} } + // FIXME: avoid allocating memory + let dest = this.force_allocation(dest)?; + // The rest either implements the logic, or falls back to `lookup_exported_symbol`. - match this.emulate_foreign_item_inner(link_name, abi, args, dest)? { + match this.emulate_foreign_item_inner(link_name, abi, args, &dest)? { EmulateItemResult::NeedsReturn => { trace!("{:?}", this.dump_place(&dest.clone().into())); this.return_to_block(ret)?; @@ -111,7 +114,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { sym: DynSym, abi: &FnAbi<'tcx, Ty<'tcx>>, args: &[OpTy<'tcx>], - dest: &MPlaceTy<'tcx>, + dest: &PlaceTy<'tcx>, ret: Option, unwind: mir::UnwindAction, ) -> InterpResult<'tcx> { diff --git a/src/tools/miri/tests/fail/data_race/stack_pop_race.rs b/src/tools/miri/tests/fail/data_race/stack_pop_race.rs index 5138bcbf8f74d..e7632d43126ac 100644 --- a/src/tools/miri/tests/fail/data_race/stack_pop_race.rs +++ b/src/tools/miri/tests/fail/data_race/stack_pop_race.rs @@ -8,7 +8,7 @@ struct MakeSend(*const i32); unsafe impl Send for MakeSend {} fn main() { - race(0); + race(0); //~ERROR: Data race detected between (1) non-atomic read on thread `unnamed-1` and (2) deallocation on thread `main` } // Using an argument for the ptr to point to, since those do not get StorageDead. @@ -22,5 +22,4 @@ fn race(local: i32) { thread::yield_now(); // Deallocating the local (when `main` returns) // races with the read in the other thread. - // Make sure the error points at this function's end, not just the call site. -} //~ERROR: Data race detected between (1) non-atomic read on thread `unnamed-1` and (2) deallocation on thread `main` +} diff --git a/src/tools/miri/tests/fail/data_race/stack_pop_race.stderr b/src/tools/miri/tests/fail/data_race/stack_pop_race.stderr index 643426aba99eb..721b75630449f 100644 --- a/src/tools/miri/tests/fail/data_race/stack_pop_race.stderr +++ b/src/tools/miri/tests/fail/data_race/stack_pop_race.stderr @@ -1,8 +1,8 @@ error: Undefined Behavior: Data race detected between (1) non-atomic read on thread `unnamed-ID` and (2) deallocation on thread `main` at ALLOC. (2) just happened here --> tests/fail/data_race/stack_pop_race.rs:LL:CC | -LL | } - | ^ Data race detected between (1) non-atomic read on thread `unnamed-ID` and (2) deallocation on thread `main` at ALLOC. (2) just happened here +LL | race(0); + | ^^^^^^^ Data race detected between (1) non-atomic read on thread `unnamed-ID` and (2) deallocation on thread `main` at ALLOC. (2) just happened here | help: and (1) occurred earlier here --> tests/fail/data_race/stack_pop_race.rs:LL:CC @@ -12,12 +12,7 @@ LL | let _val = unsafe { *ptr.0 }; = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information = note: BACKTRACE (of the first span): - = note: inside `race` at tests/fail/data_race/stack_pop_race.rs:LL:CC -note: inside `main` - --> tests/fail/data_race/stack_pop_race.rs:LL:CC - | -LL | race(0); - | ^^^^^^^ + = note: inside `main` at tests/fail/data_race/stack_pop_race.rs:LL:CC note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/tail_calls/dangling-local-var.stderr b/src/tools/miri/tests/fail/tail_calls/dangling-local-var.stderr index 33e1e53ea0605..15f73c8a9ae15 100644 --- a/src/tools/miri/tests/fail/tail_calls/dangling-local-var.stderr +++ b/src/tools/miri/tests/fail/tail_calls/dangling-local-var.stderr @@ -14,8 +14,8 @@ LL | let local = 0; help: ALLOC was deallocated here: --> tests/fail/tail_calls/dangling-local-var.rs:LL:CC | -LL | become g(ptr) - | ^^^^^^^^^^^^^ +LL | f(std::ptr::null()); + | ^^^^^^^^^^^^^^^^^^^ = note: BACKTRACE (of the first span): = note: inside `g` at tests/fail/tail_calls/dangling-local-var.rs:LL:CC note: inside `main` diff --git a/src/tools/miri/tests/pass/alloc-access-tracking.rs b/src/tools/miri/tests/pass/alloc-access-tracking.rs index c47063bef03c7..0e88951dc43f9 100644 --- a/src/tools/miri/tests/pass/alloc-access-tracking.rs +++ b/src/tools/miri/tests/pass/alloc-access-tracking.rs @@ -1,7 +1,7 @@ #![no_std] #![no_main] -//@compile-flags: -Zmiri-track-alloc-id=21 -Zmiri-track-alloc-accesses -Cpanic=abort -//@normalize-stderr-test: "id 21" -> "id $$ALLOC" +//@compile-flags: -Zmiri-track-alloc-id=20 -Zmiri-track-alloc-accesses -Cpanic=abort +//@normalize-stderr-test: "id 20" -> "id $$ALLOC" //@only-target: linux # alloc IDs differ between OSes (due to extern static allocations) extern "Rust" {