diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs index 1e37f743b6f..f9b8b18a5ec 100644 --- a/src/librustc_trans/base.rs +++ b/src/librustc_trans/base.rs @@ -564,68 +564,6 @@ pub fn alloc_ty<'a, 'tcx>(bcx: &BlockAndBuilder<'a, 'tcx>, ty: Ty<'tcx>, name: & bcx.fcx().alloca(type_of::type_of(bcx.ccx(), ty), name) } -impl<'a, 'tcx> FunctionContext<'a, 'tcx> { - // Builds the return block for a function. - pub fn build_return_block(&self, ret_cx: &BlockAndBuilder<'a, 'tcx>) { - if self.llretslotptr.is_none() || self.fn_ty.ret.is_indirect() { - return ret_cx.ret_void(); - } - - let retslot = self.llretslotptr.unwrap(); - let retptr = Value(retslot); - let llty = self.fn_ty.ret.original_ty; - match (retptr.get_dominating_store(ret_cx), self.fn_ty.ret.cast) { - // If there's only a single store to the ret slot, we can directly return - // the value that was stored and omit the store and the alloca. - // However, we only want to do this when there is no cast needed. - (Some(s), None) => { - let mut retval = s.get_operand(0).unwrap().get(); - s.erase_from_parent(); - - if retptr.has_no_uses() { - retptr.erase_from_parent(); - } - - if self.fn_ty.ret.is_indirect() { - ret_cx.store(retval, get_param(self.llfn, 0)); - ret_cx.ret_void() - } else { - if llty == Type::i1(self.ccx) { - retval = ret_cx.trunc(retval, llty); - } - ret_cx.ret(retval) - } - } - (_, cast_ty) if self.fn_ty.ret.is_indirect() => { - // Otherwise, copy the return value to the ret slot. - assert_eq!(cast_ty, None); - let llsz = llsize_of(self.ccx, self.fn_ty.ret.ty); - let llalign = llalign_of_min(self.ccx, self.fn_ty.ret.ty); - call_memcpy(&ret_cx, get_param(self.llfn, 0), - retslot, llsz, llalign as u32); - ret_cx.ret_void() - } - (_, Some(cast_ty)) => { - let load = ret_cx.load(ret_cx.pointercast(retslot, cast_ty.ptr_to())); - let llalign = llalign_of_min(self.ccx, self.fn_ty.ret.ty); - unsafe { - llvm::LLVMSetAlignment(load, llalign); - } - ret_cx.ret(load) - } - (_, None) => { - let retval = if llty == Type::i1(self.ccx) { - let val = ret_cx.load_range_assert(retslot, 0, 2, llvm::False); - ret_cx.trunc(val, llty) - } else { - ret_cx.load(retslot) - }; - ret_cx.ret(retval) - } - } - } -} - pub fn trans_instance<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, instance: Instance<'tcx>) { let _s = if ccx.sess().trans_stats() { let mut instance_name = String::new(); @@ -683,9 +621,17 @@ pub fn trans_ctor_shim<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, let fcx = FunctionContext::new(ccx, llfndecl, fn_ty, None, false); let bcx = fcx.get_entry_block(); - if !fcx.fn_ty.ret.is_ignore() { - let dest = fcx.llretslotptr.unwrap(); + // But if there are no nested returns, we skip the indirection + // and have a single retslot + let dest = if fcx.fn_ty.ret.is_indirect() { + get_param(fcx.llfn, 0) + } else { + // We create an alloca to hold a pointer of type `ret.original_ty` + // which will hold the pointer to the right alloca which has the + // final ret value + fcx.alloca(fcx.fn_ty.ret.memory_ty(ccx), "sret_slot") + }; let dest_val = adt::MaybeSizedValue::sized(dest); // Can return unsized value let mut llarg_idx = fcx.fn_ty.ret.is_indirect() as usize; let mut arg_idx = 0; @@ -703,9 +649,32 @@ pub fn trans_ctor_shim<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, } } adt::trans_set_discr(&bcx, sig.output(), dest, disr); - } - fcx.build_return_block(&bcx); + if fcx.fn_ty.ret.is_indirect() { + bcx.ret_void(); + return; + } + + if let Some(cast_ty) = fcx.fn_ty.ret.cast { + let load = bcx.load(bcx.pointercast(dest, cast_ty.ptr_to())); + let llalign = llalign_of_min(fcx.ccx, fcx.fn_ty.ret.ty); + unsafe { + llvm::LLVMSetAlignment(load, llalign); + } + bcx.ret(load) + } else { + let llty = fcx.fn_ty.ret.original_ty; + let retval = if llty == Type::i1(fcx.ccx) { + let val = bcx.load_range_assert(dest, 0, 2, llvm::False); + bcx.trunc(val, llty) + } else { + bcx.load(dest) + }; + bcx.ret(retval) + } + } else { + bcx.ret_void(); + } } pub fn llvm_linkage_by_name(name: &str) -> Option { diff --git a/src/librustc_trans/callee.rs b/src/librustc_trans/callee.rs index fa24c5ecf2e..e0ecd1d8bf5 100644 --- a/src/librustc_trans/callee.rs +++ b/src/librustc_trans/callee.rs @@ -16,7 +16,7 @@ pub use self::CalleeData::*; -use llvm::{self, ValueRef, get_params}; +use llvm::{self, ValueRef, get_param, get_params}; use rustc::hir::def_id::DefId; use rustc::ty::subst::Substs; use rustc::traits; @@ -390,8 +390,8 @@ fn trans_fn_once_adapter_shim<'a, 'tcx>( let fn_ret = callee.ty.fn_ret(); let fn_ty = callee.direct_fn_type(bcx.ccx(), &[]); - let first_llarg = if fn_ty.ret.is_indirect() { - fcx.llretslotptr + let first_llarg = if fn_ty.ret.is_indirect() && !fcx.fn_ty.ret.is_ignore() { + Some(get_param(fcx.llfn, 0)) } else { None }; @@ -409,17 +409,16 @@ fn trans_fn_once_adapter_shim<'a, 'tcx>( } fn_ty.apply_attrs_callsite(llret); - if !fn_ty.ret.is_indirect() { - if let Some(llretslot) = fcx.llretslotptr { - fn_ty.ret.store(&bcx, llret, llretslot); - } - } - if fn_ret.0.is_never() { bcx.unreachable(); } self_scope.trans(&bcx); - fcx.build_return_block(&bcx); + + if fcx.fn_ty.ret.is_indirect() || fcx.fn_ty.ret.is_ignore() { + bcx.ret_void(); + } else { + bcx.ret(llret); + } ccx.instances().borrow_mut().insert(method_instance, lloncefn); @@ -539,9 +538,31 @@ fn trans_fn_pointer_shim<'a, 'tcx>( data: Fn(llfnpointer), ty: bare_fn_ty }; - callee.call(&bcx, &llargs[(self_idx + 1)..], fcx.llretslotptr, None); - fcx.build_return_block(&bcx); + let fn_ret = callee.ty.fn_ret(); + let fn_ty = callee.direct_fn_type(ccx, &[]); + + let mut args = Vec::new(); + + if fn_ty.ret.is_indirect() { + if !fn_ty.ret.is_ignore() { + args.push(get_param(fcx.llfn, 0)); + } + } + args.extend_from_slice(&llargs[(self_idx + 1)..]); + + let llret = bcx.call(llfnpointer, &args, None); + fn_ty.apply_attrs_callsite(llret); + + if fn_ret.0.is_never() { + bcx.unreachable(); + } + + if fn_ty.ret.is_indirect() || fcx.fn_ty.ret.is_ignore() { + bcx.ret_void(); + } else { + bcx.ret(llret); + } ccx.fn_pointer_shims().borrow_mut().insert(bare_fn_ty_maybe_ref, llfn); llfn diff --git a/src/librustc_trans/glue.rs b/src/librustc_trans/glue.rs index 936ab090112..4778f368554 100644 --- a/src/librustc_trans/glue.rs +++ b/src/librustc_trans/glue.rs @@ -201,7 +201,7 @@ pub fn implement_drop_glue<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, g: DropGlueKi // type, so we don't need to explicitly cast the function parameter. let bcx = make_drop_glue(bcx, get_param(llfn, 0), g); - fcx.build_return_block(&bcx); + bcx.ret_void(); } fn trans_custom_dtor<'a, 'tcx>(mut bcx: BlockAndBuilder<'a, 'tcx>, diff --git a/src/librustc_trans/meth.rs b/src/librustc_trans/meth.rs index a09838f282b..33b43e9a3ab 100644 --- a/src/librustc_trans/meth.rs +++ b/src/librustc_trans/meth.rs @@ -85,8 +85,24 @@ pub fn trans_object_shim<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>, let bcx = fcx.get_entry_block(); let llargs = get_params(fcx.llfn); - callee.call(&bcx, &llargs[fcx.fn_ty.ret.is_indirect() as usize..], fcx.llretslotptr, None); - fcx.build_return_block(&bcx); + let fn_ret = callee.ty.fn_ret(); + let fn_ty = callee.direct_fn_type(ccx, &[]); + + let mut args = Vec::new(); + + args.extend_from_slice(&llargs); + let llret = bcx.call(callee.reify(ccx), &args, None); + fn_ty.apply_attrs_callsite(llret); + + if fn_ret.0.is_never() { + bcx.unreachable(); + } + + if fn_ty.ret.is_indirect() || fcx.fn_ty.ret.is_ignore() { + bcx.ret_void(); + } else { + bcx.ret(llret); + } llfn }