librustc: Use separate stack slot for each return.

This commit is contained in:
Luqman Aden 2014-07-29 12:25:06 -07:00
parent 9dcf89567e
commit 68cbd6c929
6 changed files with 81 additions and 41 deletions

View File

@ -1210,16 +1210,23 @@ pub fn arrayalloca(cx: &Block, ty: Type, v: ValueRef) -> ValueRef {
p
}
// Creates and returns space for, or returns the argument representing, the
// slot where the return value of the function must go.
pub fn make_return_pointer(fcx: &FunctionContext, output_type: ty::t)
-> ValueRef {
// Creates the alloca slot which holds the pointer to the slot for the final return value
pub fn make_return_slot_pointer(fcx: &FunctionContext, output_type: ty::t) -> ValueRef {
let lloutputtype = type_of::type_of(fcx.ccx, output_type);
// Let's create the stack slot
let slot = AllocaFcx(fcx, lloutputtype.ptr_to(), "llretslotptr");
// and if we're using an out pointer, then store that in our newly made slot
if type_of::return_uses_outptr(fcx.ccx, output_type) {
get_param(fcx.llfn, 0)
} else {
let lloutputtype = type_of::type_of(fcx.ccx, output_type);
AllocaFcx(fcx, lloutputtype, "__make_return_pointer")
let outptr = get_param(fcx.llfn, 0);
let b = fcx.ccx.builder();
b.position_before(fcx.alloca_insert_pt.get().unwrap());
b.store(outptr, slot);
}
slot
}
// NB: must keep 4 fns in sync:
@ -1258,7 +1265,7 @@ pub fn new_fn_ctxt<'a>(ccx: &'a CrateContext,
let mut fcx = FunctionContext {
llfn: llfndecl,
llenv: None,
llretptr: Cell::new(None),
llretslotptr: Cell::new(None),
alloca_insert_pt: Cell::new(None),
llreturn: Cell::new(None),
personality: Cell::new(None),
@ -1303,12 +1310,12 @@ pub fn init_function<'a>(fcx: &'a FunctionContext<'a>,
if !return_type_is_void(fcx.ccx, substd_output_type) {
// If the function returns nil/bot, there is no real return
// value, so do not set `llretptr`.
// value, so do not set `llretslotptr`.
if !skip_retptr || fcx.caller_expects_out_pointer {
// Otherwise, we normally allocate the llretptr, unless we
// Otherwise, we normally allocate the llretslotptr, unless we
// have been instructed to skip it for immediate return
// values.
fcx.llretptr.set(Some(make_return_pointer(fcx, substd_output_type)));
fcx.llretslotptr.set(Some(make_return_slot_pointer(fcx, substd_output_type)));
}
}
@ -1533,12 +1540,12 @@ pub fn finish_fn<'a>(fcx: &'a FunctionContext<'a>,
// Builds the return block for a function.
pub fn build_return_block(fcx: &FunctionContext, ret_cx: &Block, retty: ty::t) {
// Return the value if this function immediate; otherwise, return void.
if fcx.llretptr.get().is_none() || fcx.caller_expects_out_pointer {
if fcx.llretslotptr.get().is_none() {
return RetVoid(ret_cx);
}
let retptr = Value(fcx.llretptr.get().unwrap());
let retslot = Load(ret_cx, fcx.llretslotptr.get().unwrap());
let retptr = Value(retslot);
let retval = match retptr.get_dominating_store(ret_cx) {
// 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
@ -1557,10 +1564,15 @@ pub fn build_return_block(fcx: &FunctionContext, ret_cx: &Block, retty: ty::t) {
}
}
// Otherwise, load the return value from the ret slot
None => load_ty(ret_cx, fcx.llretptr.get().unwrap(), retty)
None => load_ty(ret_cx, retslot, retty)
};
Ret(ret_cx, retval);
if fcx.caller_expects_out_pointer {
store_ty(ret_cx, retval, get_param(fcx.llfn, 0), retty);
RetVoid(ret_cx);
} else {
Ret(ret_cx, retval);
}
}
#[deriving(Clone, Eq, PartialEq)]
@ -1658,10 +1670,10 @@ pub fn trans_closure(ccx: &CrateContext,
// emitting should be enabled.
debuginfo::start_emitting_source_locations(&fcx);
let dest = match fcx.llretptr.get() {
Some(e) => {expr::SaveIn(e)}
let dest = match fcx.llretslotptr.get() {
Some(_) => expr::SaveIn(alloca(bcx, type_of::type_of(bcx.ccx(), block_ty), "iret_slot")),
None => {
assert!(type_is_zero_size(bcx.ccx(), block_ty))
assert!(type_is_zero_size(bcx.ccx(), block_ty));
expr::Ignore
}
};
@ -1672,6 +1684,13 @@ pub fn trans_closure(ccx: &CrateContext,
// (trans_block, trans_expr, et cetera).
bcx = controlflow::trans_block(bcx, body, dest);
match dest {
expr::SaveIn(slot) => {
Store(bcx, slot, fcx.llretslotptr.get().unwrap());
}
_ => {}
}
match fcx.llreturn.get() {
Some(_) => {
Br(bcx, fcx.return_exit_block());
@ -1841,16 +1860,18 @@ fn trans_enum_variant_or_tuple_like_struct(ccx: &CrateContext,
let arg_datums = create_datums_for_fn_args(&fcx, arg_tys.as_slice());
if !type_is_zero_size(fcx.ccx, result_ty) {
let dest = alloca(bcx, type_of::type_of(bcx.ccx(), result_ty), "eret_slot");
let repr = adt::represent_type(ccx, result_ty);
for (i, arg_datum) in arg_datums.move_iter().enumerate() {
let lldestptr = adt::trans_field_ptr(bcx,
&*repr,
fcx.llretptr.get().unwrap(),
dest,
disr,
i);
arg_datum.store_to(bcx, lldestptr);
}
adt::trans_set_discr(bcx, &*repr, fcx.llretptr.get().unwrap(), disr);
adt::trans_set_discr(bcx, &*repr, dest, disr);
Store(bcx, dest, fcx.llretslotptr.get().unwrap());
}
finish_fn(&fcx, bcx, result_ty);

View File

@ -389,6 +389,10 @@ pub fn trans_unboxing_shim(bcx: &Block,
for i in range(1, arg_types.len()) {
llshimmedargs.push(get_param(fcx.llfn, fcx.arg_pos(i) as u32));
}
let dest = match fcx.llretslotptr.get() {
Some(_) => Some(expr::SaveIn(alloca(bcx, type_of::type_of(ccx, return_type), "ret_slot"))),
None => None
};
bcx = trans_call_inner(bcx,
None,
function_type,
@ -399,10 +403,13 @@ pub fn trans_unboxing_shim(bcx: &Block,
}
},
ArgVals(llshimmedargs.as_slice()),
match fcx.llretptr.get() {
None => None,
Some(llretptr) => Some(expr::SaveIn(llretptr)),
}).bcx;
dest).bcx;
match dest {
Some(expr::SaveIn(slot)) => {
Store(bcx, slot, fcx.llretslotptr.get().unwrap());
}
_ => {}
}
bcx = fcx.pop_and_trans_custom_cleanup_scope(bcx, arg_scope);
finish_fn(&fcx, bcx, return_type);

View File

@ -582,16 +582,16 @@ pub fn get_wrapper_for_bare_fn(ccx: &CrateContext,
ty::ty_fn_args(closure_ty)
.as_slice());
let mut llargs = Vec::new();
match fcx.llretptr.get() {
Some(llretptr) => {
llargs.push(llretptr);
match fcx.llretslotptr.get() {
Some(llretslotptr) => {
llargs.push(Load(bcx, llretslotptr));
}
None => {}
}
llargs.extend(args.iter().map(|arg| arg.val));
let retval = Call(bcx, fn_ptr, llargs.as_slice(), None);
if type_is_zero_size(ccx, f.sig.output) || fcx.llretptr.get().is_some() {
if type_is_zero_size(ccx, f.sig.output) || fcx.llretslotptr.get().is_some() {
RetVoid(bcx);
} else {
Ret(bcx, retval);

View File

@ -240,11 +240,11 @@ pub struct FunctionContext<'a> {
// The environment argument in a closure.
pub llenv: Option<ValueRef>,
// The place to store the return value. If the return type is immediate,
// this is an alloca in the function. Otherwise, it's the hidden first
// parameter to the function. After function construction, this should
// always be Some.
pub llretptr: Cell<Option<ValueRef>>,
// A pointer to where to store the return value. If the return type is
// immediate, this points to an alloca in the function. Otherwise, it's a
// pointer to the hidden first parameter of the function. After function
// construction, this should always be Some.
pub llretslotptr: Cell<Option<ValueRef>>,
// These pub elements: "hoisted basic blocks" containing
// administrative activities that have to happen in only one place in
@ -259,8 +259,8 @@ pub struct FunctionContext<'a> {
pub personality: Cell<Option<ValueRef>>,
// True if the caller expects this fn to use the out pointer to
// return. Either way, your code should write into llretptr, but if
// this value is false, llretptr will be a local alloca.
// return. Either way, your code should write into the slot llretslotptr
// points to, but if this value is false, that slot will be a local alloca.
pub caller_expects_out_pointer: bool,
// Maps arguments to allocas created for them in llallocas.

View File

@ -26,6 +26,7 @@
use middle::trans::expr;
use middle::trans::meth;
use middle::trans::type_::Type;
use middle::trans::type_of;
use middle::ty;
use middle::typeck::MethodCall;
use util::ppaux::Repr;
@ -462,13 +463,22 @@ pub fn trans_ret<'a>(bcx: &'a Block<'a>,
let _icx = push_ctxt("trans_ret");
let fcx = bcx.fcx;
let mut bcx = bcx;
let dest = match bcx.fcx.llretptr.get() {
None => expr::Ignore,
Some(retptr) => expr::SaveIn(retptr),
let dest = match (fcx.llretslotptr.get(), e) {
(Some(_), Some(e)) => {
let ret_ty = expr_ty(bcx, &*e);
expr::SaveIn(alloca(bcx, type_of::type_of(bcx.ccx(), ret_ty), "ret_slot"))
}
_ => expr::Ignore,
};
match e {
Some(x) => {
bcx = expr::trans_into(bcx, &*x, dest);
match dest {
expr::SaveIn(slot) => {
Store(bcx, slot, fcx.llretslotptr.get().unwrap());
}
_ => {}
}
}
_ => {}
}

View File

@ -321,7 +321,9 @@ pub fn visit_ty(&mut self, t: ty::t) {
let arg = get_param(llfdecl, fcx.arg_pos(0u) as c_uint);
let arg = BitCast(bcx, arg, llptrty);
let ret = adt::trans_get_discr(bcx, &*repr, arg, Some(Type::i64(ccx)));
Store(bcx, ret, fcx.llretptr.get().unwrap());
let ret_alloca = alloca(bcx, Type::i64(ccx), "ret_slot");
Store(bcx, ret, ret_alloca);
Store(bcx, ret_alloca, fcx.llretslotptr.get().unwrap());
match fcx.llreturn.get() {
Some(llreturn) => Br(bcx, llreturn),
None => {}