@@ -1214,19 +1214,117 @@ pub fn arrayalloca(cx: &Block, ty: Type, v: ValueRef) -> ValueRef {
1214
1214
pub fn make_return_slot_pointer ( fcx : & FunctionContext , output_type : ty:: t ) -> ValueRef {
1215
1215
let lloutputtype = type_of:: type_of ( fcx. ccx , output_type) ;
1216
1216
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" ) ;
1219
1223
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 ) ;
1223
1227
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
+ }
1227
1243
}
1244
+ }
1228
1245
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
+ }
1230
1328
}
1231
1329
1232
1330
// NB: must keep 4 fns in sync:
@@ -1261,13 +1359,15 @@ pub fn new_fn_ctxt<'a>(ccx: &'a CrateContext,
1261
1359
let substd_output_type = output_type. substp ( ccx. tcx ( ) , param_substs) ;
1262
1360
let uses_outptr = type_of:: return_uses_outptr ( ccx, substd_output_type) ;
1263
1361
let debug_context = debuginfo:: create_function_debug_context ( ccx, id, param_substs, llfndecl) ;
1362
+ let nested_returns = has_nested_returns ( ccx. tcx ( ) , id) ;
1264
1363
1265
1364
let mut fcx = FunctionContext {
1266
1365
llfn : llfndecl,
1267
1366
llenv : None ,
1268
1367
llretslotptr : Cell :: new ( None ) ,
1269
1368
alloca_insert_pt : Cell :: new ( None ) ,
1270
1369
llreturn : Cell :: new ( None ) ,
1370
+ needs_ret_allocas : nested_returns,
1271
1371
personality : Cell :: new ( None ) ,
1272
1372
caller_expects_out_pointer : uses_outptr,
1273
1373
llargs : RefCell :: new ( NodeMap :: new ( ) ) ,
@@ -1540,11 +1640,16 @@ pub fn finish_fn<'a>(fcx: &'a FunctionContext<'a>,
1540
1640
1541
1641
// Builds the return block for a function.
1542
1642
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 ) {
1544
1645
return RetVoid ( ret_cx) ;
1545
1646
}
1546
1647
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
+ } ;
1548
1653
let retptr = Value ( retslot) ;
1549
1654
match retptr. get_dominating_store ( ret_cx) {
1550
1655
// 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,
1678
1783
debuginfo:: start_emitting_source_locations( & fcx) ;
1679
1784
1680
1785
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" ) ) ,
1682
1787
None => {
1683
1788
assert ! ( type_is_zero_size( bcx. ccx( ) , block_ty) ) ;
1684
1789
expr:: Ignore
@@ -1692,7 +1797,7 @@ pub fn trans_closure(ccx: &CrateContext,
1692
1797
bcx = controlflow:: trans_block( bcx, body, dest) ;
1693
1798
1694
1799
match dest {
1695
- expr : : SaveIn ( slot) => {
1800
+ expr : : SaveIn ( slot) if fcx . needs_ret_allocas => {
1696
1801
Store ( bcx, slot, fcx. llretslotptr. get( ) . unwrap( ) ) ;
1697
1802
}
1698
1803
_ => { }
@@ -1862,12 +1967,14 @@ fn trans_enum_variant_or_tuple_like_struct(ccx: &CrateContext,
1862
1967
param_substs, None , & arena, TranslateItems ) ;
1863
1968
let bcx = init_function( & fcx, false, result_ty) ;
1864
1969
1970
+ assert!( !fcx. needs_ret_allocas) ;
1971
+
1865
1972
let arg_tys = ty:: ty_fn_args( ctor_ty) ;
1866
1973
1867
1974
let arg_datums = create_datums_for_fn_args( & fcx, arg_tys. as_slice( ) ) ;
1868
1975
1869
1976
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") ;
1871
1978
let repr = adt:: represent_type( ccx, result_ty) ;
1872
1979
for ( i, arg_datum) in arg_datums. move_iter( ) . enumerate( ) {
1873
1980
let lldestptr = adt:: trans_field_ptr( bcx,
@@ -1878,7 +1985,6 @@ fn trans_enum_variant_or_tuple_like_struct(ccx: &CrateContext,
1878
1985
arg_datum. store_to( bcx, lldestptr) ;
1879
1986
}
1880
1987
adt:: trans_set_discr( bcx, & * repr, dest, disr) ;
1881
- Store ( bcx, dest, fcx. llretslotptr. get( ) . unwrap( ) ) ;
1882
1988
}
1883
1989
1884
1990
finish_fn( & fcx, bcx, result_ty) ;
0 commit comments