Clean up function calling

Still not as clean as I'd like it, but better
This commit is contained in:
Ralf Jung 2018-08-24 14:40:55 +02:00
parent 66d64babed
commit aa645f30da
2 changed files with 98 additions and 117 deletions

View file

@ -43,17 +43,13 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
_ => (instance, place),
};
let fn_sig = instance.ty(*self.tcx).fn_sig(*self.tcx);
let fn_sig = self.tcx.normalize_erasing_late_bound_regions(self.param_env, &fn_sig);
let arg = OpTy {
op: Operand::Immediate(place.to_ref(&self)),
layout: self.layout_of(self.tcx.mk_mut_ptr(place.layout.ty))?,
};
// This should always be (), but getting it from the sig seems
// easier than creating a layout of ().
let dest = PlaceTy::null(&self, self.layout_of(fn_sig.output())?);
let ty = self.tcx.mk_tup((&[] as &[ty::Ty<'tcx>]).iter()); // return type is ()
let dest = PlaceTy::null(&self, self.layout_of(ty)?);
self.eval_fn_call(
instance,
@ -61,7 +57,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
Some(dest),
Some(target),
span,
fn_sig,
None,
)
}
}

View file

@ -15,9 +15,9 @@ use syntax::source_map::Span;
use rustc_target::spec::abi::Abi;
use rustc::mir::interpret::{EvalResult, Scalar};
use super::{EvalContext, Machine, Value, OpTy, Place, PlaceTy, ValTy, Operand, StackPopCleanup};
use rustc_data_structures::indexed_vec::Idx;
use super::{
EvalContext, Machine, Value, OpTy, Place, PlaceTy, ValTy, Operand, StackPopCleanup
};
mod drop;
@ -96,44 +96,44 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
let instance_ty = instance.ty(*self.tcx);
match instance_ty.sty {
ty::FnDef(..) => {
let real_sig = instance_ty.fn_sig(*self.tcx);
let sig = self.tcx.normalize_erasing_late_bound_regions(
ty::ParamEnv::reveal_all(),
self.param_env,
&sig,
);
let real_sig = instance_ty.fn_sig(*self.tcx);
let real_sig = self.tcx.normalize_erasing_late_bound_regions(
ty::ParamEnv::reveal_all(),
self.param_env,
&real_sig,
);
if !self.check_sig_compat(sig, real_sig)? {
return err!(FunctionPointerTyMismatch(real_sig, sig));
}
(instance, sig)
}
ref other => bug!("instance def ty: {:?}", other),
}
(instance, sig)
}
ty::FnDef(def_id, substs) => (
self.resolve(def_id, substs)?,
func.layout.ty.fn_sig(*self.tcx),
),
ty::FnDef(def_id, substs) => {
let sig = func.layout.ty.fn_sig(*self.tcx);
let sig = self.tcx.normalize_erasing_late_bound_regions(
self.param_env,
&sig,
);
(self.resolve(def_id, substs)?, sig)
},
_ => {
let msg = format!("can't handle callee of type {:?}", func.layout.ty);
return err!(Unimplemented(msg));
}
};
let args = self.eval_operands(args)?;
let sig = self.tcx.normalize_erasing_late_bound_regions(
ty::ParamEnv::reveal_all(),
&sig,
);
self.eval_fn_call(
fn_def,
&args[..],
dest,
ret,
terminator.source_info.span,
sig,
Some(sig),
)?;
}
@ -275,6 +275,8 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
return Ok(false);
}
/// Call this function -- pushing the stack frame and initializing the arguments.
/// `sig` is ptional in case of FnPtr/FnDef -- but mandatory for closures!
fn eval_fn_call(
&mut self,
instance: ty::Instance<'tcx>,
@ -282,12 +284,10 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
dest: Option<PlaceTy<'tcx>>,
ret: Option<mir::BasicBlock>,
span: Span,
sig: ty::FnSig<'tcx>,
sig: Option<ty::FnSig<'tcx>>,
) -> EvalResult<'tcx> {
trace!("eval_fn_call: {:#?}", instance);
if let Some(place) = dest {
assert_eq!(place.layout.ty, sig.output());
}
match instance.def {
ty::InstanceDef::Intrinsic(..) => {
// The intrinsic itself cannot diverge, so if we got here without a return
@ -303,58 +303,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
self.dump_place(*dest);
Ok(())
}
// FIXME: figure out why we can't just go through the shim
ty::InstanceDef::ClosureOnceShim { .. } => {
let mir = match M::find_fn(self, instance, args, dest, ret)? {
Some(mir) => mir,
None => return Ok(()),
};
let return_place = match dest {
Some(place) => *place,
None => Place::null(&self),
};
self.push_stack_frame(
instance,
span,
mir,
return_place,
StackPopCleanup::Goto(ret),
)?;
// Pass the arguments
let mut arg_locals = self.frame().mir.args_iter();
match sig.abi {
// closure as closure once
Abi::RustCall => {
for (arg_local, &op) in arg_locals.zip(args) {
let dest = self.eval_place(&mir::Place::Local(arg_local))?;
self.copy_op(op, dest)?;
}
}
// non capture closure as fn ptr
// need to inject zst ptr for closure object (aka do nothing)
// and need to pack arguments
Abi::Rust => {
trace!(
"args: {:#?}",
self.frame().mir.args_iter().zip(args.iter())
.map(|(local, arg)| (local, **arg, arg.layout.ty))
.collect::<Vec<_>>()
);
let local = arg_locals.nth(1).unwrap();
for (i, &op) in args.into_iter().enumerate() {
let dest = self.eval_place(&mir::Place::Local(local).field(
mir::Field::new(i),
op.layout.ty,
))?;
self.copy_op(op, dest)?;
}
}
_ => bug!("bad ABI for ClosureOnceShim: {:?}", sig.abi),
}
Ok(())
}
ty::InstanceDef::ClosureOnceShim { .. } |
ty::InstanceDef::FnPtrShim(..) |
ty::InstanceDef::DropGlue(..) |
ty::InstanceDef::CloneShim(..) |
@ -376,58 +325,94 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
StackPopCleanup::Goto(ret),
)?;
// Pass the arguments
let mut arg_locals = self.frame().mir.args_iter();
// If we didn't get a signture, ask `fn_sig`
let sig = sig.unwrap_or_else(|| {
let fn_sig = instance.ty(*self.tcx).fn_sig(*self.tcx);
self.tcx.normalize_erasing_late_bound_regions(self.param_env, &fn_sig)
});
assert_eq!(sig.inputs().len(), args.len());
// We can't test the types, as it is fine if the types are ABI-compatible but
// not equal.
// Figure out how to pass which arguments.
// FIXME: Somehow this is horribly full of special cases here, and codegen has
// none of that. What is going on?
trace!("ABI: {:?}", sig.abi);
trace!(
"args: {:#?}",
self.frame().mir.args_iter().zip(args.iter())
.map(|(local, arg)| (local, **arg, arg.layout.ty)).collect::<Vec<_>>()
args.iter()
.map(|arg| (arg.layout.ty, format!("{:?}", **arg)))
.collect::<Vec<_>>()
);
match sig.abi {
Abi::RustCall => {
assert_eq!(args.len(), 2);
trace!(
"locals: {:#?}",
mir.args_iter()
.map(|local|
(local, self.layout_of_local(self.cur_frame(), local).unwrap().ty)
)
.collect::<Vec<_>>()
);
match instance.def {
ty::InstanceDef::ClosureOnceShim { .. } if sig.abi == Abi::Rust => {
// this has an entirely ridicolous calling convention where it uses the
// "Rust" ABI, but arguments come in untupled and are supposed to be tupled
// for the callee! The function's first argument is a ZST, and then
// there comes a tuple for the rest.
let mut arg_locals = mir.args_iter();
{
// write first argument
let first_local = arg_locals.next().unwrap();
let dest = self.eval_place(&mir::Place::Local(first_local))?;
self.copy_op(args[0], dest)?;
{ // the ZST. nothing to write.
let arg_local = arg_locals.next().unwrap();
let dest = self.eval_place(&mir::Place::Local(arg_local))?;
assert!(dest.layout.is_zst());
}
// unpack and write all other args
let layout = args[1].layout;
if let ty::Tuple(_) = layout.ty.sty {
if layout.is_zst() {
// Nothing to do, no need to unpack zsts
return Ok(());
{ // the tuple argument.
let arg_local = arg_locals.next().unwrap();
let dest = self.eval_place(&mir::Place::Local(arg_local))?;
assert_eq!(dest.layout.fields.count(), args.len());
for (i, &op) in args.iter().enumerate() {
let dest_field = self.place_field(dest, i as u64)?;
self.copy_op(op, dest_field)?;
}
if self.frame().mir.args_iter().count() == layout.fields.count() + 1 {
for (i, arg_local) in arg_locals.enumerate() {
let arg = self.operand_field(args[1], i as u64)?;
let dest = self.eval_place(&mir::Place::Local(arg_local))?;
self.copy_op(arg, dest)?;
}
} else {
trace!("manual impl of rust-call ABI");
// called a manual impl of a rust-call function
let dest = self.eval_place(
&mir::Place::Local(arg_locals.next().unwrap()),
)?;
self.copy_op(args[1], dest)?;
}
} else {
bug!(
"rust-call ABI tuple argument was {:#?}",
layout
);
}
// that should be it
assert!(arg_locals.next().is_none());
}
_ => {
for (arg_local, &op) in arg_locals.zip(args) {
// overloaded-calls-simple.rs in miri's test suite demomstrates that there is
// no way to predict, from the ABI and instance.def, whether the function
// wants arguments passed with untupling or not. So we just make it
// depend on the number of arguments...
let untuple =
sig.abi == Abi::RustCall && !args.is_empty() && args.len() != mir.arg_count;
let (normal_args, untuple_arg) = if untuple {
let (tup, args) = args.split_last().unwrap();
trace!("eval_fn_call: Will pass last argument by untupling");
(args, Some(tup))
} else {
(&args[..], None)
};
// Pass the arguments.
let mut arg_locals = mir.args_iter();
// First the normal ones.
for &op in normal_args {
let arg_local = arg_locals.next().unwrap();
let dest = self.eval_place(&mir::Place::Local(arg_local))?;
self.copy_op(op, dest)?;
}
// The the ones to untuple.
if let Some(&untuple_arg) = untuple_arg {
for i in 0..untuple_arg.layout.fields.count() {
let arg_local = arg_locals.next().unwrap();
let dest = self.eval_place(&mir::Place::Local(arg_local))?;
let op = self.operand_field(untuple_arg, i as u64)?;
self.copy_op(op, dest)?;
}
}
// That should be it.
assert!(arg_locals.next().is_none());
}
}
Ok(())