diff --git a/src/librustc/mir/repr.rs b/src/librustc/mir/repr.rs index fe86e3b1ed69..84187d941a6b 100644 --- a/src/librustc/mir/repr.rs +++ b/src/librustc/mir/repr.rs @@ -178,6 +178,10 @@ pub struct TempDecl<'tcx> { #[derive(Clone, Debug, RustcEncodable, RustcDecodable)] pub struct ArgDecl<'tcx> { pub ty: Ty<'tcx>, + + /// If true, this argument is a tuple after monomorphization, + /// and has to be collected from multiple actual arguments. + pub spread: bool } /////////////////////////////////////////////////////////////////////////// diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs index 5d9f827984e0..7fd959390de3 100644 --- a/src/librustc_mir/build/mod.rs +++ b/src/librustc_mir/build/mod.rs @@ -149,7 +149,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { pattern, &lvalue)); } - ArgDecl { ty: ty } + ArgDecl { ty: ty, spread: false } }) .collect(); diff --git a/src/librustc_mir/mir_map.rs b/src/librustc_mir/mir_map.rs index 13521de78af2..4717c54ca649 100644 --- a/src/librustc_mir/mir_map.rs +++ b/src/librustc_mir/mir_map.rs @@ -33,6 +33,7 @@ use rustc::util::common::ErrorReported; use rustc::util::nodemap::NodeMap; use rustc_front::hir; use rustc_front::intravisit::{self, Visitor}; +use syntax::abi::Abi; use syntax::ast; use syntax::attr::AttrMetaMethods; use syntax::codemap::Span; @@ -181,13 +182,20 @@ fn build_mir<'a,'tcx:'a>(cx: Cx<'a,'tcx>, let parameter_scope = cx.tcx().region_maps.lookup_code_extent( CodeExtentData::ParameterScope { fn_id: fn_id, body_id: body.id }); - Ok(build::construct(cx, - span, - implicit_arg_tys, - arguments, - parameter_scope, - fn_sig.output, - body)) + let mut mir = build::construct(cx, span, implicit_arg_tys, arguments, + parameter_scope, fn_sig.output, body); + + match cx.tcx().node_id_to_type(fn_id).sty { + ty::TyFnDef(_, _, f) if f.abi == Abi::RustCall => { + // RustCall pseudo-ABI untuples the last argument. + if let Some(arg_decl) = mir.arg_decls.last_mut() { + arg_decl.spread = true; + } + } + _ => {} + } + + Ok(mir) } fn closure_self_ty<'a, 'tcx>(tcx: &TyCtxt<'tcx>, diff --git a/src/librustc_trans/trans/mir/mod.rs b/src/librustc_trans/trans/mir/mod.rs index dac9310c8b9e..c9269e9f793f 100644 --- a/src/librustc_trans/trans/mir/mod.rs +++ b/src/librustc_trans/trans/mir/mod.rs @@ -147,15 +147,47 @@ pub fn trans_mir<'blk, 'tcx>(fcx: &'blk FunctionContext<'blk, 'tcx>) { fn arg_value_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>, mir: &mir::Mir<'tcx>) -> Vec> { - // FIXME tupled_args? I think I'd rather that mapping is done in MIR land though let fcx = bcx.fcx(); let tcx = bcx.tcx(); let mut idx = 0; let mut llarg_idx = fcx.fn_ty.ret.is_indirect() as usize; mir.arg_decls.iter().enumerate().map(|(arg_index, arg_decl)| { + let arg_ty = bcx.monomorphize(&arg_decl.ty); + if arg_decl.spread { + // This argument (e.g. the last argument in the "rust-call" ABI) + // is a tuple that was spread at the ABI level and now we have + // to reconstruct it into a tuple local variable, from multiple + // individual LLVM function arguments. + + let tupled_arg_tys = match arg_ty.sty { + ty::TyTuple(ref tys) => tys, + _ => unreachable!("spread argument isn't a tuple?!") + }; + + let llval = bcx.with_block(|bcx| { + let lltemp = base::alloc_ty(bcx, arg_ty, &format!("arg{}", arg_index)); + for (i, &tupled_arg_ty) in tupled_arg_tys.iter().enumerate() { + let dst = build::StructGEP(bcx, lltemp, i); + let arg = &fcx.fn_ty.args[idx]; + idx += 1; + if common::type_is_fat_ptr(tcx, tupled_arg_ty) { + // We pass fat pointers as two words, but inside the tuple + // they are the two sub-fields of a single aggregate field. + let meta = &fcx.fn_ty.args[idx]; + idx += 1; + arg.store_fn_arg(bcx, &mut llarg_idx, expr::get_dataptr(bcx, dst)); + meta.store_fn_arg(bcx, &mut llarg_idx, expr::get_meta(bcx, dst)); + } else { + arg.store_fn_arg(bcx, &mut llarg_idx, dst); + } + } + lltemp + }); + return LvalueRef::new_sized(llval, LvalueTy::from_ty(arg_ty)); + } + let arg = &fcx.fn_ty.args[idx]; idx += 1; - let arg_ty = bcx.monomorphize(&arg_decl.ty); let llval = if arg.is_indirect() { // Don't copy an indirect argument to an alloca, the caller // already put it in a temporary alloca and gave it up, unless