1
Fork 0

Refactor call terminator to always hold a destination place

This commit is contained in:
Jakob Degen 2022-04-16 09:27:54 -04:00
parent 222c5724ec
commit 09b0936db2
67 changed files with 422 additions and 412 deletions

View file

@ -60,7 +60,7 @@ fn eval_body_using_ecx<'mir, 'tcx>(
ecx.push_stack_frame(
cid.instance,
body,
Some(&ret.into()),
&ret.into(),
StackPopCleanup::Root { cleanup: false },
)?;

View file

@ -265,7 +265,8 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
instance: ty::Instance<'tcx>,
_abi: Abi,
args: &[OpTy<'tcx>],
_ret: Option<(&PlaceTy<'tcx>, mir::BasicBlock)>,
_dest: &PlaceTy<'tcx>,
_ret: Option<mir::BasicBlock>,
_unwind: StackPopUnwind, // unwinding is not supported in consts
) -> InterpResult<'tcx, Option<(&'mir mir::Body<'tcx>, ty::Instance<'tcx>)>> {
debug!("find_mir_or_eval_fn: {:?}", instance);
@ -293,6 +294,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
new_instance,
_abi,
args,
_dest,
_ret,
_unwind,
)?
@ -307,17 +309,18 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
ecx: &mut InterpCx<'mir, 'tcx, Self>,
instance: ty::Instance<'tcx>,
args: &[OpTy<'tcx>],
ret: Option<(&PlaceTy<'tcx>, mir::BasicBlock)>,
dest: &PlaceTy<'tcx, Self::PointerTag>,
target: Option<mir::BasicBlock>,
_unwind: StackPopUnwind,
) -> InterpResult<'tcx> {
// Shared intrinsics.
if ecx.emulate_intrinsic(instance, args, ret)? {
if ecx.emulate_intrinsic(instance, args, dest, target)? {
return Ok(());
}
let intrinsic_name = ecx.tcx.item_name(instance.def_id());
// CTFE-specific intrinsics.
let Some((dest, ret)) = ret else {
let Some(ret) = target else {
return Err(ConstEvalErrKind::NeedsRfc(format!(
"calling intrinsic `{}`",
intrinsic_name

View file

@ -105,7 +105,7 @@ pub struct Frame<'mir, 'tcx, Tag: Provenance = AllocId, Extra = ()> {
/// The location where the result of the current stack frame should be written to,
/// and its layout in the caller.
pub return_place: Option<PlaceTy<'tcx, Tag>>,
pub return_place: PlaceTy<'tcx, Tag>,
/// The list of locals for this stack frame, stored in order as
/// `[return_ptr, arguments..., variables..., temporaries...]`.
@ -676,7 +676,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
&mut self,
instance: ty::Instance<'tcx>,
body: &'mir mir::Body<'tcx>,
return_place: Option<&PlaceTy<'tcx, M::PointerTag>>,
return_place: &PlaceTy<'tcx, M::PointerTag>,
return_to_block: StackPopCleanup,
) -> InterpResult<'tcx> {
trace!("body: {:#?}", body);
@ -685,7 +685,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
body,
loc: Err(body.span), // Span used for errors caused during preamble.
return_to_block,
return_place: return_place.copied(),
return_place: *return_place,
// empty local array, we fill it in below, after we are inside the stack frame and
// all methods actually know about the frame
locals: IndexVec::new(),
@ -807,14 +807,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
self.stack_mut().pop().expect("tried to pop a stack frame, but there were none");
if !unwinding {
// Copy the return value to the caller's stack frame.
if let Some(ref return_place) = frame.return_place {
let op = self.access_local(&frame, mir::RETURN_PLACE, None)?;
self.copy_op_transmute(&op, return_place)?;
trace!("{:?}", self.dump_place(**return_place));
} else {
throw_ub!(Unreachable);
}
let op = self.access_local(&frame, mir::RETURN_PLACE, None)?;
self.copy_op_transmute(&op, &frame.return_place)?;
trace!("{:?}", self.dump_place(*frame.return_place));
}
let return_to_block = frame.return_to_block;
@ -1055,7 +1050,7 @@ where
body.hash_stable(hcx, hasher);
instance.hash_stable(hcx, hasher);
return_to_block.hash_stable(hcx, hasher);
return_place.as_ref().map(|r| &**r).hash_stable(hcx, hasher);
return_place.hash_stable(hcx, hasher);
locals.hash_stable(hcx, hasher);
loc.hash_stable(hcx, hasher);
extra.hash_stable(hcx, hasher);

View file

@ -115,13 +115,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
&mut self,
instance: ty::Instance<'tcx>,
args: &[OpTy<'tcx, M::PointerTag>],
ret: Option<(&PlaceTy<'tcx, M::PointerTag>, mir::BasicBlock)>,
dest: &PlaceTy<'tcx, M::PointerTag>,
ret: Option<mir::BasicBlock>,
) -> InterpResult<'tcx, bool> {
let substs = instance.substs;
let intrinsic_name = self.tcx.item_name(instance.def_id());
// First handle intrinsics without return place.
let (dest, ret) = match ret {
let ret = match ret {
None => match intrinsic_name {
sym::transmute => throw_ub_format!("transmuting to uninhabited type"),
sym::abort => M::abort(self, "the program aborted execution".to_owned())?,

View file

@ -169,7 +169,8 @@ pub trait Machine<'mir, 'tcx>: Sized {
instance: ty::Instance<'tcx>,
abi: Abi,
args: &[OpTy<'tcx, Self::PointerTag>],
ret: Option<(&PlaceTy<'tcx, Self::PointerTag>, mir::BasicBlock)>,
destination: &PlaceTy<'tcx, Self::PointerTag>,
target: Option<mir::BasicBlock>,
unwind: StackPopUnwind,
) -> InterpResult<'tcx, Option<(&'mir mir::Body<'tcx>, ty::Instance<'tcx>)>>;
@ -180,7 +181,8 @@ pub trait Machine<'mir, 'tcx>: Sized {
fn_val: Self::ExtraFnVal,
abi: Abi,
args: &[OpTy<'tcx, Self::PointerTag>],
ret: Option<(&PlaceTy<'tcx, Self::PointerTag>, mir::BasicBlock)>,
destination: &PlaceTy<'tcx, Self::PointerTag>,
target: Option<mir::BasicBlock>,
unwind: StackPopUnwind,
) -> InterpResult<'tcx>;
@ -190,7 +192,8 @@ pub trait Machine<'mir, 'tcx>: Sized {
ecx: &mut InterpCx<'mir, 'tcx, Self>,
instance: ty::Instance<'tcx>,
args: &[OpTy<'tcx, Self::PointerTag>],
ret: Option<(&PlaceTy<'tcx, Self::PointerTag>, mir::BasicBlock)>,
destination: &PlaceTy<'tcx, Self::PointerTag>,
target: Option<mir::BasicBlock>,
unwind: StackPopUnwind,
) -> InterpResult<'tcx>;
@ -470,7 +473,8 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) {
fn_val: !,
_abi: Abi,
_args: &[OpTy<$tcx>],
_ret: Option<(&PlaceTy<$tcx>, mir::BasicBlock)>,
_destination: &PlaceTy<$tcx, Self::PointerTag>,
_target: Option<mir::BasicBlock>,
_unwind: StackPopUnwind,
) -> InterpResult<$tcx> {
match fn_val {}

View file

@ -57,7 +57,15 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
self.go_to_block(target_block);
}
Call { ref func, ref args, destination, ref cleanup, from_hir_call: _, fn_span: _ } => {
Call {
ref func,
ref args,
destination,
target,
ref cleanup,
from_hir_call: _,
fn_span: _,
} => {
let old_stack = self.frame_idx();
let old_loc = self.frame().loc;
let func = self.eval_operand(func, None)?;
@ -91,20 +99,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
),
};
let dest_place;
let ret = match destination {
Some((dest, ret)) => {
dest_place = self.eval_place(dest)?;
Some((&dest_place, ret))
}
None => None,
};
let destination = self.eval_place(destination)?;
self.eval_fn_call(
fn_val,
(fn_sig.abi, fn_abi),
&args,
with_caller_location,
ret,
&destination,
target,
match (cleanup, fn_abi.can_unwind) {
(Some(cleanup), true) => StackPopUnwind::Cleanup(*cleanup),
(None, true) => StackPopUnwind::Skip,
@ -299,7 +301,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
(caller_abi, caller_fn_abi): (Abi, &FnAbi<'tcx, Ty<'tcx>>),
args: &[OpTy<'tcx, M::PointerTag>],
with_caller_location: bool,
ret: Option<(&PlaceTy<'tcx, M::PointerTag>, mir::BasicBlock)>,
destination: &PlaceTy<'tcx, M::PointerTag>,
target: Option<mir::BasicBlock>,
mut unwind: StackPopUnwind,
) -> InterpResult<'tcx> {
trace!("eval_fn_call: {:#?}", fn_val);
@ -307,7 +310,15 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
let instance = match fn_val {
FnVal::Instance(instance) => instance,
FnVal::Other(extra) => {
return M::call_extra_fn(self, extra, caller_abi, args, ret, unwind);
return M::call_extra_fn(
self,
extra,
caller_abi,
args,
destination,
target,
unwind,
);
}
};
@ -315,7 +326,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
ty::InstanceDef::Intrinsic(def_id) => {
assert!(self.tcx.is_intrinsic(def_id));
// caller_fn_abi is not relevant here, we interpret the arguments directly for each intrinsic.
M::call_intrinsic(self, instance, args, ret, unwind)
M::call_intrinsic(self, instance, args, destination, target, unwind)
}
ty::InstanceDef::VtableShim(..)
| ty::InstanceDef::ReifyShim(..)
@ -326,7 +337,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
| ty::InstanceDef::Item(_) => {
// We need MIR for this fn
let Some((body, instance)) =
M::find_mir_or_eval_fn(self, instance, caller_abi, args, ret, unwind)? else {
M::find_mir_or_eval_fn(self, instance, caller_abi, args, destination, target, unwind)? else {
return Ok(());
};
@ -362,8 +373,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
self.push_stack_frame(
instance,
body,
ret.map(|p| p.0),
StackPopCleanup::Goto { ret: ret.map(|p| p.1), unwind },
destination,
StackPopCleanup::Goto { ret: target, unwind },
)?;
// If an error is raised here, pop the frame again to get an accurate backtrace.
@ -540,7 +551,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
(caller_abi, caller_fn_abi),
&args,
with_caller_location,
ret,
destination,
target,
unwind,
)
}
@ -582,7 +594,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
(Abi::Rust, fn_abi),
&[arg.into()],
false,
Some((&dest.into(), target)),
&dest.into(),
Some(target),
match unwind {
Some(cleanup) => StackPopUnwind::Cleanup(cleanup),
None => StackPopUnwind::Skip,

View file

@ -788,7 +788,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
} else {
let terminator = self.source[loc.block].terminator_mut();
let target = match terminator.kind {
TerminatorKind::Call { destination: Some((_, target)), .. } => target,
TerminatorKind::Call { target: Some(target), .. } => target,
ref kind => {
span_bug!(terminator.source_info.span, "{:?} not promotable", kind);
}
@ -814,7 +814,8 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
func,
args,
cleanup: None,
destination: Some((Place::from(new_temp), new_target)),
destination: Place::from(new_temp),
target: Some(new_target),
from_hir_call,
fn_span,
},
@ -1054,11 +1055,9 @@ pub fn is_const_fn_in_array_repeat_expression<'tcx>(
{
if let Operand::Constant(box Constant { literal, .. }) = func {
if let ty::FnDef(def_id, _) = *literal.ty().kind() {
if let Some((destination_place, _)) = destination {
if destination_place == place {
if ccx.tcx.is_const_fn(def_id) {
return true;
}
if destination == place {
if ccx.tcx.is_const_fn(def_id) {
return true;
}
}
}

View file

@ -673,7 +673,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
self.check_edge(location, *unwind, EdgeKind::Unwind);
}
}
TerminatorKind::Call { func, args, destination, cleanup, .. } => {
TerminatorKind::Call { func, args, destination, target, cleanup, .. } => {
let func_ty = func.ty(&self.body.local_decls, self.tcx);
match func_ty.kind() {
ty::FnPtr(..) | ty::FnDef(..) => {}
@ -682,7 +682,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
format!("encountered non-callable type {} in `Call` terminator", func_ty),
),
}
if let Some((_, target)) = destination {
if let Some(target) = target {
self.check_edge(location, *target, EdgeKind::Normal);
}
if let Some(cleanup) = cleanup {
@ -693,9 +693,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
// passed by a reference to the callee. Consequently they must be non-overlapping.
// Currently this simply checks for duplicate places.
self.place_cache.clear();
if let Some((destination, _)) = destination {
self.place_cache.push(destination.as_ref());
}
self.place_cache.push(destination.as_ref());
for arg in args {
if let Operand::Move(place) = arg {
self.place_cache.push(place.as_ref());