Skip to content

Commit e2273d9

Browse files
committed
auto merge of #16081 : luqmana/rust/nr, r=pcwalton
Fixes #15763
2 parents c7d0b52 + 71e19d5 commit e2273d9

File tree

14 files changed

+406
-52
lines changed

14 files changed

+406
-52
lines changed

src/libcore/intrinsics.rs

+7
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,13 @@ extern "rust-intrinsic" {
310310
/// ```
311311
pub fn transmute<T,U>(e: T) -> U;
312312

313+
/// Gives the address for the return value of the enclosing function.
314+
///
315+
/// Using this instrinsic in a function that does not use an out pointer
316+
/// will trigger a compiler error.
317+
#[cfg(not(stage0))]
318+
pub fn return_address() -> *const u8;
319+
313320
/// Returns `true` if a type requires drop glue.
314321
pub fn needs_drop<T>() -> bool;
315322

src/librustc/middle/trans/base.rs

+162-28
Original file line numberDiff line numberDiff line change
@@ -1122,7 +1122,7 @@ pub fn memcpy_ty(bcx: &Block, dst: ValueRef, src: ValueRef, t: ty::t) {
11221122
let llalign = llalign_of_min(ccx, llty);
11231123
call_memcpy(bcx, dst, src, llsz, llalign as u32);
11241124
} else {
1125-
Store(bcx, Load(bcx, src), dst);
1125+
store_ty(bcx, Load(bcx, src), dst, t);
11261126
}
11271127
}
11281128

@@ -1210,15 +1210,120 @@ pub fn arrayalloca(cx: &Block, ty: Type, v: ValueRef) -> ValueRef {
12101210
p
12111211
}
12121212

1213-
// Creates and returns space for, or returns the argument representing, the
1214-
// slot where the return value of the function must go.
1215-
pub fn make_return_pointer(fcx: &FunctionContext, output_type: ty::t)
1216-
-> ValueRef {
1217-
if type_of::return_uses_outptr(fcx.ccx, output_type) {
1218-
get_param(fcx.llfn, 0)
1213+
// Creates the alloca slot which holds the pointer to the slot for the final return value
1214+
pub fn make_return_slot_pointer(fcx: &FunctionContext, output_type: ty::t) -> ValueRef {
1215+
let lloutputtype = type_of::type_of(fcx.ccx, output_type);
1216+
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");
1223+
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);
1227+
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
12191237
} else {
1220-
let lloutputtype = type_of::type_of(fcx.ccx, output_type);
1221-
AllocaFcx(fcx, lloutputtype, "__make_return_pointer")
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+
}
1243+
}
1244+
}
1245+
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())
12221327
}
12231328
}
12241329

@@ -1254,13 +1359,15 @@ pub fn new_fn_ctxt<'a>(ccx: &'a CrateContext,
12541359
let substd_output_type = output_type.substp(ccx.tcx(), param_substs);
12551360
let uses_outptr = type_of::return_uses_outptr(ccx, substd_output_type);
12561361
let debug_context = debuginfo::create_function_debug_context(ccx, id, param_substs, llfndecl);
1362+
let nested_returns = has_nested_returns(ccx.tcx(), id);
12571363

12581364
let mut fcx = FunctionContext {
12591365
llfn: llfndecl,
12601366
llenv: None,
1261-
llretptr: Cell::new(None),
1367+
llretslotptr: Cell::new(None),
12621368
alloca_insert_pt: Cell::new(None),
12631369
llreturn: Cell::new(None),
1370+
needs_ret_allocas: nested_returns,
12641371
personality: Cell::new(None),
12651372
caller_expects_out_pointer: uses_outptr,
12661373
llargs: RefCell::new(NodeMap::new()),
@@ -1303,12 +1410,12 @@ pub fn init_function<'a>(fcx: &'a FunctionContext<'a>,
13031410

13041411
if !return_type_is_void(fcx.ccx, substd_output_type) {
13051412
// If the function returns nil/bot, there is no real return
1306-
// value, so do not set `llretptr`.
1413+
// value, so do not set `llretslotptr`.
13071414
if !skip_retptr || fcx.caller_expects_out_pointer {
1308-
// Otherwise, we normally allocate the llretptr, unless we
1415+
// Otherwise, we normally allocate the llretslotptr, unless we
13091416
// have been instructed to skip it for immediate return
13101417
// values.
1311-
fcx.llretptr.set(Some(make_return_pointer(fcx, substd_output_type)));
1418+
fcx.llretslotptr.set(Some(make_return_slot_pointer(fcx, substd_output_type)));
13121419
}
13131420
}
13141421

@@ -1533,13 +1640,18 @@ pub fn finish_fn<'a>(fcx: &'a FunctionContext<'a>,
15331640

15341641
// Builds the return block for a function.
15351642
pub fn build_return_block(fcx: &FunctionContext, ret_cx: &Block, retty: ty::t) {
1536-
// Return the value if this function immediate; otherwise, return void.
1537-
if fcx.llretptr.get().is_none() || fcx.caller_expects_out_pointer {
1643+
if fcx.llretslotptr.get().is_none() ||
1644+
(!fcx.needs_ret_allocas && fcx.caller_expects_out_pointer) {
15381645
return RetVoid(ret_cx);
15391646
}
15401647

1541-
let retptr = Value(fcx.llretptr.get().unwrap());
1542-
let retval = match retptr.get_dominating_store(ret_cx) {
1648+
let retslot = if fcx.needs_ret_allocas {
1649+
Load(ret_cx, fcx.llretslotptr.get().unwrap())
1650+
} else {
1651+
fcx.llretslotptr.get().unwrap()
1652+
};
1653+
let retptr = Value(retslot);
1654+
match retptr.get_dominating_store(ret_cx) {
15431655
// If there's only a single store to the ret slot, we can directly return
15441656
// the value that was stored and omit the store and the alloca
15451657
Some(s) => {
@@ -1550,17 +1662,29 @@ pub fn build_return_block(fcx: &FunctionContext, ret_cx: &Block, retty: ty::t) {
15501662
retptr.erase_from_parent();
15511663
}
15521664

1553-
if ty::type_is_bool(retty) {
1665+
let retval = if ty::type_is_bool(retty) {
15541666
Trunc(ret_cx, retval, Type::i1(fcx.ccx))
15551667
} else {
15561668
retval
1669+
};
1670+
1671+
if fcx.caller_expects_out_pointer {
1672+
store_ty(ret_cx, retval, get_param(fcx.llfn, 0), retty);
1673+
return RetVoid(ret_cx);
1674+
} else {
1675+
return Ret(ret_cx, retval);
15571676
}
15581677
}
1559-
// Otherwise, load the return value from the ret slot
1560-
None => load_ty(ret_cx, fcx.llretptr.get().unwrap(), retty)
1561-
};
1562-
1563-
Ret(ret_cx, retval);
1678+
// Otherwise, copy the return value to the ret slot
1679+
None => {
1680+
if fcx.caller_expects_out_pointer {
1681+
memcpy_ty(ret_cx, get_param(fcx.llfn, 0), retslot, retty);
1682+
return RetVoid(ret_cx);
1683+
} else {
1684+
return Ret(ret_cx, load_ty(ret_cx, retslot, retty));
1685+
}
1686+
}
1687+
}
15641688
}
15651689

15661690
#[deriving(Clone, Eq, PartialEq)]
@@ -1658,10 +1782,10 @@ pub fn trans_closure(ccx: &CrateContext,
16581782
// emitting should be enabled.
16591783
debuginfo::start_emitting_source_locations(&fcx);
16601784

1661-
let dest = match fcx.llretptr.get() {
1662-
Some(e) => {expr::SaveIn(e)}
1785+
let dest = match fcx.llretslotptr.get() {
1786+
Some(_) => expr::SaveIn(fcx.get_ret_slot(bcx, block_ty, "iret_slot")),
16631787
None => {
1664-
assert!(type_is_zero_size(bcx.ccx(), block_ty))
1788+
assert!(type_is_zero_size(bcx.ccx(), block_ty));
16651789
expr::Ignore
16661790
}
16671791
};
@@ -1672,6 +1796,13 @@ pub fn trans_closure(ccx: &CrateContext,
16721796
// (trans_block, trans_expr, et cetera).
16731797
bcx = controlflow::trans_block(bcx, body, dest);
16741798

1799+
match dest {
1800+
expr::SaveIn(slot) if fcx.needs_ret_allocas => {
1801+
Store(bcx, slot, fcx.llretslotptr.get().unwrap());
1802+
}
1803+
_ => {}
1804+
}
1805+
16751806
match fcx.llreturn.get() {
16761807
Some(_) => {
16771808
Br(bcx, fcx.return_exit_block());
@@ -1836,21 +1967,24 @@ fn trans_enum_variant_or_tuple_like_struct(ccx: &CrateContext,
18361967
param_substs, None, &arena, TranslateItems);
18371968
let bcx = init_function(&fcx, false, result_ty);
18381969

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

18411974
let arg_datums = create_datums_for_fn_args(&fcx, arg_tys.as_slice());
18421975

18431976
if !type_is_zero_size(fcx.ccx, result_ty) {
1977+
let dest = fcx.get_ret_slot(bcx, result_ty, "eret_slot");
18441978
let repr = adt::represent_type(ccx, result_ty);
18451979
for (i, arg_datum) in arg_datums.move_iter().enumerate() {
18461980
let lldestptr = adt::trans_field_ptr(bcx,
18471981
&*repr,
1848-
fcx.llretptr.get().unwrap(),
1982+
dest,
18491983
disr,
18501984
i);
18511985
arg_datum.store_to(bcx, lldestptr);
18521986
}
1853-
adt::trans_set_discr(bcx, &*repr, fcx.llretptr.get().unwrap(), disr);
1987+
adt::trans_set_discr(bcx, &*repr, dest, disr);
18541988
}
18551989

18561990
finish_fn(&fcx, bcx, result_ty);

src/librustc/middle/trans/callee.rs

+10-6
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,6 +389,11 @@ 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);
393+
let dest = match fcx.llretslotptr.get() {
394+
Some(_) => Some(expr::SaveIn(fcx.get_ret_slot(bcx, return_type, "ret_slot"))),
395+
None => None
396+
};
392397
bcx = trans_call_inner(bcx,
393398
None,
394399
function_type,
@@ -399,10 +404,7 @@ pub fn trans_unboxing_shim(bcx: &Block,
399404
}
400405
},
401406
ArgVals(llshimmedargs.as_slice()),
402-
match fcx.llretptr.get() {
403-
None => None,
404-
Some(llretptr) => Some(expr::SaveIn(llretptr)),
405-
}).bcx;
407+
dest).bcx;
406408

407409
bcx = fcx.pop_and_trans_custom_cleanup_scope(bcx, arg_scope);
408410
finish_fn(&fcx, bcx, return_type);
@@ -758,9 +760,11 @@ pub fn trans_call_inner<'a>(
758760
assert!(abi == synabi::RustIntrinsic);
759761
assert!(dest.is_some());
760762

763+
let call_info = call_info.expect("no call info for intrinsic call?");
761764
return intrinsic::trans_intrinsic_call(bcx, node, callee_ty,
762765
arg_cleanup_scope, args,
763-
dest.unwrap(), substs);
766+
dest.unwrap(), substs,
767+
call_info);
764768
}
765769
NamedTupleConstructor(substs, disr) => {
766770
assert!(dest.is_some());

src/librustc/middle/trans/closure.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -574,24 +574,25 @@ 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

581581
let args = create_datums_for_fn_args(&fcx,
582582
ty::ty_fn_args(closure_ty)
583583
.as_slice());
584584
let mut llargs = Vec::new();
585-
match fcx.llretptr.get() {
585+
match fcx.llretslotptr.get() {
586586
Some(llretptr) => {
587+
assert!(!fcx.needs_ret_allocas);
587588
llargs.push(llretptr);
588589
}
589590
None => {}
590591
}
591592
llargs.extend(args.iter().map(|arg| arg.val));
592593

593594
let retval = Call(bcx, fn_ptr, llargs.as_slice(), None);
594-
if type_is_zero_size(ccx, f.sig.output) || fcx.llretptr.get().is_some() {
595+
if type_is_zero_size(ccx, f.sig.output) || fcx.llretslotptr.get().is_some() {
595596
RetVoid(bcx);
596597
} else {
597598
Ret(bcx, retval);

0 commit comments

Comments
 (0)