@@ -1122,7 +1122,7 @@ pub fn memcpy_ty(bcx: &Block, dst: ValueRef, src: ValueRef, t: ty::t) {
1122
1122
let llalign = llalign_of_min ( ccx, llty) ;
1123
1123
call_memcpy ( bcx, dst, src, llsz, llalign as u32 ) ;
1124
1124
} else {
1125
- Store ( bcx, Load ( bcx, src) , dst) ;
1125
+ store_ty ( bcx, Load ( bcx, src) , dst, t ) ;
1126
1126
}
1127
1127
}
1128
1128
@@ -1210,15 +1210,120 @@ pub fn arrayalloca(cx: &Block, ty: Type, v: ValueRef) -> ValueRef {
1210
1210
p
1211
1211
}
1212
1212
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
1219
1237
} 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 ( ) )
1222
1327
}
1223
1328
}
1224
1329
@@ -1254,13 +1359,15 @@ pub fn new_fn_ctxt<'a>(ccx: &'a CrateContext,
1254
1359
let substd_output_type = output_type. substp ( ccx. tcx ( ) , param_substs) ;
1255
1360
let uses_outptr = type_of:: return_uses_outptr ( ccx, substd_output_type) ;
1256
1361
let debug_context = debuginfo:: create_function_debug_context ( ccx, id, param_substs, llfndecl) ;
1362
+ let nested_returns = has_nested_returns ( ccx. tcx ( ) , id) ;
1257
1363
1258
1364
let mut fcx = FunctionContext {
1259
1365
llfn : llfndecl,
1260
1366
llenv : None ,
1261
- llretptr : Cell :: new ( None ) ,
1367
+ llretslotptr : Cell :: new ( None ) ,
1262
1368
alloca_insert_pt : Cell :: new ( None ) ,
1263
1369
llreturn : Cell :: new ( None ) ,
1370
+ needs_ret_allocas : nested_returns,
1264
1371
personality : Cell :: new ( None ) ,
1265
1372
caller_expects_out_pointer : uses_outptr,
1266
1373
llargs : RefCell :: new ( NodeMap :: new ( ) ) ,
@@ -1303,12 +1410,12 @@ pub fn init_function<'a>(fcx: &'a FunctionContext<'a>,
1303
1410
1304
1411
if !return_type_is_void ( fcx. ccx , substd_output_type) {
1305
1412
// 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 `.
1307
1414
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
1309
1416
// have been instructed to skip it for immediate return
1310
1417
// 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) ) ) ;
1312
1419
}
1313
1420
}
1314
1421
@@ -1533,13 +1640,18 @@ pub fn finish_fn<'a>(fcx: &'a FunctionContext<'a>,
1533
1640
1534
1641
// Builds the return block for a function.
1535
1642
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 ) {
1538
1645
return RetVoid ( ret_cx) ;
1539
1646
}
1540
1647
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) {
1543
1655
// If there's only a single store to the ret slot, we can directly return
1544
1656
// the value that was stored and omit the store and the alloca
1545
1657
Some ( s) => {
@@ -1550,17 +1662,29 @@ pub fn build_return_block(fcx: &FunctionContext, ret_cx: &Block, retty: ty::t) {
1550
1662
retptr. erase_from_parent ( ) ;
1551
1663
}
1552
1664
1553
- if ty:: type_is_bool ( retty) {
1665
+ let retval = if ty:: type_is_bool ( retty) {
1554
1666
Trunc ( ret_cx, retval, Type :: i1 ( fcx. ccx ) )
1555
1667
} else {
1556
1668
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) ;
1557
1676
}
1558
1677
}
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
+ }
1564
1688
}
1565
1689
1566
1690
#[ deriving( Clone , Eq , PartialEq ) ]
@@ -1658,10 +1782,10 @@ pub fn trans_closure(ccx: &CrateContext,
1658
1782
// emitting should be enabled.
1659
1783
debuginfo:: start_emitting_source_locations( & fcx) ;
1660
1784
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" ) ) ,
1663
1787
None => {
1664
- assert!( type_is_zero_size( bcx. ccx( ) , block_ty) )
1788
+ assert ! ( type_is_zero_size( bcx. ccx( ) , block_ty) ) ;
1665
1789
expr:: Ignore
1666
1790
}
1667
1791
} ;
@@ -1672,6 +1796,13 @@ pub fn trans_closure(ccx: &CrateContext,
1672
1796
// (trans_block, trans_expr, et cetera).
1673
1797
bcx = controlflow:: trans_block( bcx, body, dest) ;
1674
1798
1799
+ match dest {
1800
+ expr : : SaveIn ( slot) if fcx. needs_ret_allocas => {
1801
+ Store ( bcx, slot, fcx. llretslotptr. get( ) . unwrap( ) ) ;
1802
+ }
1803
+ _ => { }
1804
+ }
1805
+
1675
1806
match fcx. llreturn. get( ) {
1676
1807
Some ( _) => {
1677
1808
Br ( bcx, fcx. return_exit_block( ) ) ;
@@ -1836,21 +1967,24 @@ fn trans_enum_variant_or_tuple_like_struct(ccx: &CrateContext,
1836
1967
param_substs, None , & arena, TranslateItems ) ;
1837
1968
let bcx = init_function( & fcx, false, result_ty) ;
1838
1969
1970
+ assert!( !fcx. needs_ret_allocas) ;
1971
+
1839
1972
let arg_tys = ty:: ty_fn_args( ctor_ty) ;
1840
1973
1841
1974
let arg_datums = create_datums_for_fn_args( & fcx, arg_tys. as_slice( ) ) ;
1842
1975
1843
1976
if !type_is_zero_size( fcx. ccx, result_ty) {
1977
+ let dest = fcx. get_ret_slot( bcx, result_ty, "eret_slot") ;
1844
1978
let repr = adt:: represent_type( ccx, result_ty) ;
1845
1979
for ( i, arg_datum) in arg_datums. move_iter( ) . enumerate( ) {
1846
1980
let lldestptr = adt:: trans_field_ptr( bcx,
1847
1981
& * repr,
1848
- fcx . llretptr . get ( ) . unwrap ( ) ,
1982
+ dest ,
1849
1983
disr,
1850
1984
i) ;
1851
1985
arg_datum. store_to( bcx, lldestptr) ;
1852
1986
}
1853
- adt:: trans_set_discr( bcx, & * repr, fcx . llretptr . get ( ) . unwrap ( ) , disr) ;
1987
+ adt:: trans_set_discr( bcx, & * repr, dest , disr) ;
1854
1988
}
1855
1989
1856
1990
finish_fn( & fcx, bcx, result_ty) ;
0 commit comments