Skip to content

Commit 71e19d5

Browse files
committed
librustc: Don't use an alloca per return if the function doesn't have nested returns.
1 parent 0ad97c0 commit 71e19d5

File tree

7 files changed

+150
-34
lines changed

7 files changed

+150
-34
lines changed

src/librustc/middle/trans/base.rs

+121-15
Original file line numberDiff line numberDiff line change
@@ -1214,19 +1214,117 @@ pub fn arrayalloca(cx: &Block, ty: Type, v: ValueRef) -> ValueRef {
12141214
pub fn make_return_slot_pointer(fcx: &FunctionContext, output_type: ty::t) -> ValueRef {
12151215
let lloutputtype = type_of::type_of(fcx.ccx, output_type);
12161216

1217-
// Let's create the stack slot
1218-
let slot = AllocaFcx(fcx, lloutputtype.ptr_to(), "llretslotptr");
1217+
// We create an alloca to hold a pointer of type `output_type`
1218+
// which will hold the pointer to the right alloca which has the
1219+
// final ret value
1220+
if fcx.needs_ret_allocas {
1221+
// Let's create the stack slot
1222+
let slot = AllocaFcx(fcx, lloutputtype.ptr_to(), "llretslotptr");
12191223

1220-
// and if we're using an out pointer, then store that in our newly made slot
1221-
if type_of::return_uses_outptr(fcx.ccx, output_type) {
1222-
let outptr = get_param(fcx.llfn, 0);
1224+
// and if we're using an out pointer, then store that in our newly made slot
1225+
if type_of::return_uses_outptr(fcx.ccx, output_type) {
1226+
let outptr = get_param(fcx.llfn, 0);
12231227

1224-
let b = fcx.ccx.builder();
1225-
b.position_before(fcx.alloca_insert_pt.get().unwrap());
1226-
b.store(outptr, slot);
1228+
let b = fcx.ccx.builder();
1229+
b.position_before(fcx.alloca_insert_pt.get().unwrap());
1230+
b.store(outptr, slot);
1231+
}
1232+
1233+
slot
1234+
1235+
// But if there are no nested returns, we skip the indirection and have a single
1236+
// retslot
1237+
} else {
1238+
if type_of::return_uses_outptr(fcx.ccx, output_type) {
1239+
get_param(fcx.llfn, 0)
1240+
} else {
1241+
AllocaFcx(fcx, lloutputtype, "sret_slot")
1242+
}
12271243
}
1244+
}
12281245

1229-
slot
1246+
struct CheckForNestedReturnsVisitor {
1247+
found: bool
1248+
}
1249+
1250+
impl Visitor<bool> for CheckForNestedReturnsVisitor {
1251+
fn visit_expr(&mut self, e: &ast::Expr, in_return: bool) {
1252+
match e.node {
1253+
ast::ExprRet(..) if in_return => {
1254+
self.found = true;
1255+
return;
1256+
}
1257+
ast::ExprRet(..) => visit::walk_expr(self, e, true),
1258+
_ => visit::walk_expr(self, e, in_return)
1259+
}
1260+
}
1261+
}
1262+
1263+
fn has_nested_returns(tcx: &ty::ctxt, id: ast::NodeId) -> bool {
1264+
match tcx.map.find(id) {
1265+
Some(ast_map::NodeItem(i)) => {
1266+
match i.node {
1267+
ast::ItemFn(_, _, _, _, blk) => {
1268+
let mut explicit = CheckForNestedReturnsVisitor { found: false };
1269+
let mut implicit = CheckForNestedReturnsVisitor { found: false };
1270+
visit::walk_item(&mut explicit, &*i, false);
1271+
visit::walk_expr_opt(&mut implicit, blk.expr, true);
1272+
explicit.found || implicit.found
1273+
}
1274+
_ => tcx.sess.bug("unexpected item variant in has_nested_returns")
1275+
}
1276+
}
1277+
Some(ast_map::NodeTraitMethod(trait_method)) => {
1278+
match *trait_method {
1279+
ast::Provided(m) => {
1280+
match m.node {
1281+
ast::MethDecl(_, _, _, _, _, _, blk, _) => {
1282+
let mut explicit = CheckForNestedReturnsVisitor { found: false };
1283+
let mut implicit = CheckForNestedReturnsVisitor { found: false };
1284+
visit::walk_method_helper(&mut explicit, &*m, false);
1285+
visit::walk_expr_opt(&mut implicit, blk.expr, true);
1286+
explicit.found || implicit.found
1287+
}
1288+
ast::MethMac(_) => tcx.sess.bug("unexpanded macro")
1289+
}
1290+
}
1291+
ast::Required(_) => tcx.sess.bug("unexpected variant: required trait method in \
1292+
has_nested_returns")
1293+
}
1294+
}
1295+
Some(ast_map::NodeMethod(m)) => {
1296+
match m.node {
1297+
ast::MethDecl(_, _, _, _, _, _, blk, _) => {
1298+
let mut explicit = CheckForNestedReturnsVisitor { found: false };
1299+
let mut implicit = CheckForNestedReturnsVisitor { found: false };
1300+
visit::walk_method_helper(&mut explicit, &*m, false);
1301+
visit::walk_expr_opt(&mut implicit, blk.expr, true);
1302+
explicit.found || implicit.found
1303+
}
1304+
ast::MethMac(_) => tcx.sess.bug("unexpanded macro")
1305+
}
1306+
}
1307+
Some(ast_map::NodeExpr(e)) => {
1308+
match e.node {
1309+
ast::ExprFnBlock(_, blk) | ast::ExprProc(_, blk) | ast::ExprUnboxedFn(_, blk) => {
1310+
let mut explicit = CheckForNestedReturnsVisitor { found: false };
1311+
let mut implicit = CheckForNestedReturnsVisitor { found: false };
1312+
visit::walk_expr(&mut explicit, &*e, false);
1313+
visit::walk_expr_opt(&mut implicit, blk.expr, true);
1314+
explicit.found || implicit.found
1315+
}
1316+
_ => tcx.sess.bug("unexpected expr variant in has_nested_returns")
1317+
}
1318+
}
1319+
1320+
Some(ast_map::NodeVariant(..)) | Some(ast_map::NodeStructCtor(..)) => false,
1321+
1322+
// glue, shims, etc
1323+
None if id == ast::DUMMY_NODE_ID => false,
1324+
1325+
_ => tcx.sess.bug(format!("unexpected variant in has_nested_returns: {}",
1326+
tcx.map.path_to_string(id)).as_slice())
1327+
}
12301328
}
12311329

12321330
// NB: must keep 4 fns in sync:
@@ -1261,13 +1359,15 @@ pub fn new_fn_ctxt<'a>(ccx: &'a CrateContext,
12611359
let substd_output_type = output_type.substp(ccx.tcx(), param_substs);
12621360
let uses_outptr = type_of::return_uses_outptr(ccx, substd_output_type);
12631361
let debug_context = debuginfo::create_function_debug_context(ccx, id, param_substs, llfndecl);
1362+
let nested_returns = has_nested_returns(ccx.tcx(), id);
12641363

12651364
let mut fcx = FunctionContext {
12661365
llfn: llfndecl,
12671366
llenv: None,
12681367
llretslotptr: Cell::new(None),
12691368
alloca_insert_pt: Cell::new(None),
12701369
llreturn: Cell::new(None),
1370+
needs_ret_allocas: nested_returns,
12711371
personality: Cell::new(None),
12721372
caller_expects_out_pointer: uses_outptr,
12731373
llargs: RefCell::new(NodeMap::new()),
@@ -1540,11 +1640,16 @@ pub fn finish_fn<'a>(fcx: &'a FunctionContext<'a>,
15401640

15411641
// Builds the return block for a function.
15421642
pub fn build_return_block(fcx: &FunctionContext, ret_cx: &Block, retty: ty::t) {
1543-
if fcx.llretslotptr.get().is_none() {
1643+
if fcx.llretslotptr.get().is_none() ||
1644+
(!fcx.needs_ret_allocas && fcx.caller_expects_out_pointer) {
15441645
return RetVoid(ret_cx);
15451646
}
15461647

1547-
let retslot = Load(ret_cx, fcx.llretslotptr.get().unwrap());
1648+
let retslot = if fcx.needs_ret_allocas {
1649+
Load(ret_cx, fcx.llretslotptr.get().unwrap())
1650+
} else {
1651+
fcx.llretslotptr.get().unwrap()
1652+
};
15481653
let retptr = Value(retslot);
15491654
match retptr.get_dominating_store(ret_cx) {
15501655
// If there's only a single store to the ret slot, we can directly return
@@ -1678,7 +1783,7 @@ pub fn trans_closure(ccx: &CrateContext,
16781783
debuginfo::start_emitting_source_locations(&fcx);
16791784

16801785
let dest = match fcx.llretslotptr.get() {
1681-
Some(_) => expr::SaveIn(alloca(bcx, type_of::type_of(bcx.ccx(), block_ty), "iret_slot")),
1786+
Some(_) => expr::SaveIn(fcx.get_ret_slot(bcx, block_ty, "iret_slot")),
16821787
None => {
16831788
assert!(type_is_zero_size(bcx.ccx(), block_ty));
16841789
expr::Ignore
@@ -1692,7 +1797,7 @@ pub fn trans_closure(ccx: &CrateContext,
16921797
bcx = controlflow::trans_block(bcx, body, dest);
16931798

16941799
match dest {
1695-
expr::SaveIn(slot) => {
1800+
expr::SaveIn(slot) if fcx.needs_ret_allocas => {
16961801
Store(bcx, slot, fcx.llretslotptr.get().unwrap());
16971802
}
16981803
_ => {}
@@ -1862,12 +1967,14 @@ fn trans_enum_variant_or_tuple_like_struct(ccx: &CrateContext,
18621967
param_substs, None, &arena, TranslateItems);
18631968
let bcx = init_function(&fcx, false, result_ty);
18641969

1970+
assert!(!fcx.needs_ret_allocas);
1971+
18651972
let arg_tys = ty::ty_fn_args(ctor_ty);
18661973

18671974
let arg_datums = create_datums_for_fn_args(&fcx, arg_tys.as_slice());
18681975

18691976
if !type_is_zero_size(fcx.ccx, result_ty) {
1870-
let dest = alloca(bcx, type_of::type_of(bcx.ccx(), result_ty), "eret_slot");
1977+
let dest = fcx.get_ret_slot(bcx, result_ty, "eret_slot");
18711978
let repr = adt::represent_type(ccx, result_ty);
18721979
for (i, arg_datum) in arg_datums.move_iter().enumerate() {
18731980
let lldestptr = adt::trans_field_ptr(bcx,
@@ -1878,7 +1985,6 @@ fn trans_enum_variant_or_tuple_like_struct(ccx: &CrateContext,
18781985
arg_datum.store_to(bcx, lldestptr);
18791986
}
18801987
adt::trans_set_discr(bcx, &*repr, dest, disr);
1881-
Store(bcx, dest, fcx.llretslotptr.get().unwrap());
18821988
}
18831989

18841990
finish_fn(&fcx, bcx, result_ty);

src/librustc/middle/trans/callee.rs

+3-8
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,7 @@ pub fn trans_unboxing_shim(bcx: &Block,
334334
let return_type = ty::ty_fn_ret(boxed_function_type);
335335
let fcx = new_fn_ctxt(ccx,
336336
llfn,
337-
-1,
337+
ast::DUMMY_NODE_ID,
338338
false,
339339
return_type,
340340
&empty_param_substs,
@@ -389,8 +389,9 @@ pub fn trans_unboxing_shim(bcx: &Block,
389389
for i in range(1, arg_types.len()) {
390390
llshimmedargs.push(get_param(fcx.llfn, fcx.arg_pos(i) as u32));
391391
}
392+
assert!(!fcx.needs_ret_allocas);
392393
let dest = match fcx.llretslotptr.get() {
393-
Some(_) => Some(expr::SaveIn(alloca(bcx, type_of::type_of(ccx, return_type), "ret_slot"))),
394+
Some(_) => Some(expr::SaveIn(fcx.get_ret_slot(bcx, return_type, "ret_slot"))),
394395
None => None
395396
};
396397
bcx = trans_call_inner(bcx,
@@ -404,12 +405,6 @@ pub fn trans_unboxing_shim(bcx: &Block,
404405
},
405406
ArgVals(llshimmedargs.as_slice()),
406407
dest).bcx;
407-
match dest {
408-
Some(expr::SaveIn(slot)) => {
409-
Store(bcx, slot, fcx.llretslotptr.get().unwrap());
410-
}
411-
_ => {}
412-
}
413408

414409
bcx = fcx.pop_and_trans_custom_cleanup_scope(bcx, arg_scope);
415410
finish_fn(&fcx, bcx, return_type);

src/librustc/middle/trans/closure.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -574,7 +574,7 @@ pub fn get_wrapper_for_bare_fn(ccx: &CrateContext,
574574

575575
let arena = TypedArena::new();
576576
let empty_param_substs = param_substs::empty();
577-
let fcx = new_fn_ctxt(ccx, llfn, -1, true, f.sig.output,
577+
let fcx = new_fn_ctxt(ccx, llfn, ast::DUMMY_NODE_ID, true, f.sig.output,
578578
&empty_param_substs, None, &arena, TranslateItems);
579579
let bcx = init_function(&fcx, true, f.sig.output);
580580

@@ -583,8 +583,9 @@ pub fn get_wrapper_for_bare_fn(ccx: &CrateContext,
583583
.as_slice());
584584
let mut llargs = Vec::new();
585585
match fcx.llretslotptr.get() {
586-
Some(llretslotptr) => {
587-
llargs.push(Load(bcx, llretslotptr));
586+
Some(llretptr) => {
587+
assert!(!fcx.needs_ret_allocas);
588+
llargs.push(llretptr);
588589
}
589590
None => {}
590591
}

src/librustc/middle/trans/common.rs

+15
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,13 @@ use middle::def;
2121
use middle::lang_items::LangItem;
2222
use middle::subst;
2323
use middle::subst::Subst;
24+
use middle::trans::base;
2425
use middle::trans::build;
2526
use middle::trans::cleanup;
2627
use middle::trans::datum;
2728
use middle::trans::debuginfo;
2829
use middle::trans::type_::Type;
30+
use middle::trans::type_of;
2931
use middle::ty;
3032
use middle::typeck;
3133
use util::ppaux::Repr;
@@ -254,6 +256,11 @@ pub struct FunctionContext<'a> {
254256
pub alloca_insert_pt: Cell<Option<ValueRef>>,
255257
pub llreturn: Cell<Option<BasicBlockRef>>,
256258

259+
// If the function has any nested return's, including something like:
260+
// fn foo() -> Option<Foo> { Some(Foo { x: return None }) }, then
261+
// we use a separate alloca for each return
262+
pub needs_ret_allocas: bool,
263+
257264
// The a value alloca'd for calls to upcalls.rust_personality. Used when
258265
// outputting the resume instruction.
259266
pub personality: Cell<Option<ValueRef>>,
@@ -345,6 +352,14 @@ impl<'a> FunctionContext<'a> {
345352
self.llreturn.get().unwrap()
346353
}
347354

355+
pub fn get_ret_slot(&self, bcx: &Block, ty: ty::t, name: &str) -> ValueRef {
356+
if self.needs_ret_allocas {
357+
base::alloca_no_lifetime(bcx, type_of::type_of(bcx.ccx(), ty), name)
358+
} else {
359+
self.llretslotptr.get().unwrap()
360+
}
361+
}
362+
348363
pub fn new_block(&'a self,
349364
is_lpad: bool,
350365
name: &str,

src/librustc/middle/trans/controlflow.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ use middle::trans::debuginfo;
2626
use middle::trans::expr;
2727
use middle::trans::meth;
2828
use middle::trans::type_::Type;
29-
use middle::trans::type_of;
3029
use middle::ty;
3130
use middle::typeck::MethodCall;
3231
use util::ppaux::Repr;
@@ -466,15 +465,15 @@ pub fn trans_ret<'a>(bcx: &'a Block<'a>,
466465
let dest = match (fcx.llretslotptr.get(), e) {
467466
(Some(_), Some(e)) => {
468467
let ret_ty = expr_ty(bcx, &*e);
469-
expr::SaveIn(alloca(bcx, type_of::type_of(bcx.ccx(), ret_ty), "ret_slot"))
468+
expr::SaveIn(fcx.get_ret_slot(bcx, ret_ty, "ret_slot"))
470469
}
471470
_ => expr::Ignore,
472471
};
473472
match e {
474473
Some(x) => {
475474
bcx = expr::trans_into(bcx, &*x, dest);
476475
match dest {
477-
expr::SaveIn(slot) => {
476+
expr::SaveIn(slot) if fcx.needs_ret_allocas => {
478477
Store(bcx, slot, fcx.llretslotptr.get().unwrap());
479478
}
480479
_ => {}

src/librustc/middle/trans/glue.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -467,7 +467,7 @@ fn make_generic_glue(ccx: &CrateContext,
467467

468468
let arena = TypedArena::new();
469469
let empty_param_substs = param_substs::empty();
470-
let fcx = new_fn_ctxt(ccx, llfn, -1, false, ty::mk_nil(),
470+
let fcx = new_fn_ctxt(ccx, llfn, ast::DUMMY_NODE_ID, false, ty::mk_nil(),
471471
&empty_param_substs, None, &arena, TranslateItems);
472472

473473
let bcx = init_function(&fcx, false, ty::mk_nil());

src/librustc/middle/trans/reflect.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,7 @@ impl<'a, 'b> Reflector<'a, 'b> {
310310
sym.as_slice());
311311
let arena = TypedArena::new();
312312
let empty_param_substs = param_substs::empty();
313-
let fcx = new_fn_ctxt(ccx, llfdecl, -1, false,
313+
let fcx = new_fn_ctxt(ccx, llfdecl, ast::DUMMY_NODE_ID, false,
314314
ty::mk_u64(), &empty_param_substs,
315315
None, &arena, TranslateItems);
316316
let bcx = init_function(&fcx, false, ty::mk_u64());
@@ -321,9 +321,9 @@ impl<'a, 'b> Reflector<'a, 'b> {
321321
let arg = get_param(llfdecl, fcx.arg_pos(0u) as c_uint);
322322
let arg = BitCast(bcx, arg, llptrty);
323323
let ret = adt::trans_get_discr(bcx, &*repr, arg, Some(Type::i64(ccx)));
324-
let ret_alloca = alloca(bcx, Type::i64(ccx), "ret_slot");
325-
Store(bcx, ret, ret_alloca);
326-
Store(bcx, ret_alloca, fcx.llretslotptr.get().unwrap());
324+
assert!(!fcx.needs_ret_allocas);
325+
let ret_slot = fcx.get_ret_slot(bcx, ty::mk_u64(), "ret_slot");
326+
Store(bcx, ret, ret_slot);
327327
match fcx.llreturn.get() {
328328
Some(llreturn) => Br(bcx, llreturn),
329329
None => {}

0 commit comments

Comments
 (0)