@@ -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.
15351642pub 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) ;
0 commit comments