xref: /netbsd-src/external/gpl3/gcc.old/dist/gcc/tree-nrv.c (revision 8feb0f0b7eaff0608f8350bbfa3098827b4bb91b)
11debfc3dSmrg /* Language independent return value optimizations
2*8feb0f0bSmrg    Copyright (C) 2004-2020 Free Software Foundation, Inc.
31debfc3dSmrg 
41debfc3dSmrg This file is part of GCC.
51debfc3dSmrg 
61debfc3dSmrg GCC is free software; you can redistribute it and/or modify
71debfc3dSmrg it under the terms of the GNU General Public License as published by
81debfc3dSmrg the Free Software Foundation; either version 3, or (at your option)
91debfc3dSmrg any later version.
101debfc3dSmrg 
111debfc3dSmrg GCC is distributed in the hope that it will be useful,
121debfc3dSmrg but WITHOUT ANY WARRANTY; without even the implied warranty of
131debfc3dSmrg MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
141debfc3dSmrg GNU General Public License for more details.
151debfc3dSmrg 
161debfc3dSmrg You should have received a copy of the GNU General Public License
171debfc3dSmrg along with GCC; see the file COPYING3.  If not see
181debfc3dSmrg <http://www.gnu.org/licenses/>.  */
191debfc3dSmrg 
201debfc3dSmrg #include "config.h"
211debfc3dSmrg #include "system.h"
221debfc3dSmrg #include "coretypes.h"
231debfc3dSmrg #include "backend.h"
241debfc3dSmrg #include "tree.h"
251debfc3dSmrg #include "gimple.h"
261debfc3dSmrg #include "tree-pass.h"
271debfc3dSmrg #include "ssa.h"
281debfc3dSmrg #include "tree-pretty-print.h"
291debfc3dSmrg #include "gimple-iterator.h"
301debfc3dSmrg #include "gimple-walk.h"
311debfc3dSmrg #include "internal-fn.h"
321debfc3dSmrg 
331debfc3dSmrg /* This file implements return value optimizations for functions which
341debfc3dSmrg    return aggregate types.
351debfc3dSmrg 
361debfc3dSmrg    Basically this pass searches the function for return statements which
371debfc3dSmrg    return a local aggregate.  When converted to RTL such statements will
381debfc3dSmrg    generate a copy from the local aggregate to final return value destination
391debfc3dSmrg    mandated by the target's ABI.
401debfc3dSmrg 
411debfc3dSmrg    That copy can often be avoided by directly constructing the return value
421debfc3dSmrg    into the final destination mandated by the target's ABI.
431debfc3dSmrg 
441debfc3dSmrg    This is basically a generic equivalent to the C++ front-end's
451debfc3dSmrg    Named Return Value optimization.  */
461debfc3dSmrg 
471debfc3dSmrg struct nrv_data_t
481debfc3dSmrg {
491debfc3dSmrg   /* This is the temporary (a VAR_DECL) which appears in all of
501debfc3dSmrg      this function's RETURN_EXPR statements.  */
511debfc3dSmrg   tree var;
521debfc3dSmrg 
531debfc3dSmrg   /* This is the function's RESULT_DECL.  We will replace all occurrences
541debfc3dSmrg      of VAR with RESULT_DECL when we apply this optimization.  */
551debfc3dSmrg   tree result;
561debfc3dSmrg   int modified;
571debfc3dSmrg };
581debfc3dSmrg 
591debfc3dSmrg static tree finalize_nrv_r (tree *, int *, void *);
601debfc3dSmrg 
611debfc3dSmrg /* Callback for the tree walker.
621debfc3dSmrg 
631debfc3dSmrg    If TP refers to a RETURN_EXPR, then set the expression being returned
641debfc3dSmrg    to nrv_data->result.
651debfc3dSmrg 
661debfc3dSmrg    If TP refers to nrv_data->var, then replace nrv_data->var with
671debfc3dSmrg    nrv_data->result.
681debfc3dSmrg 
691debfc3dSmrg    If we reach a node where we know all the subtrees are uninteresting,
701debfc3dSmrg    then set *WALK_SUBTREES to zero.  */
711debfc3dSmrg 
721debfc3dSmrg static tree
finalize_nrv_r(tree * tp,int * walk_subtrees,void * data)731debfc3dSmrg finalize_nrv_r (tree *tp, int *walk_subtrees, void *data)
741debfc3dSmrg {
751debfc3dSmrg   struct walk_stmt_info *wi = (struct walk_stmt_info *) data;
761debfc3dSmrg   struct nrv_data_t *dp = (struct nrv_data_t *) wi->info;
771debfc3dSmrg 
781debfc3dSmrg   /* No need to walk into types.  */
791debfc3dSmrg   if (TYPE_P (*tp))
801debfc3dSmrg     *walk_subtrees = 0;
811debfc3dSmrg 
821debfc3dSmrg   /* Otherwise replace all occurrences of VAR with RESULT.  */
831debfc3dSmrg   else if (*tp == dp->var)
841debfc3dSmrg     {
851debfc3dSmrg       *tp = dp->result;
861debfc3dSmrg       dp->modified = 1;
871debfc3dSmrg     }
881debfc3dSmrg 
891debfc3dSmrg   /* Keep iterating.  */
901debfc3dSmrg   return NULL_TREE;
911debfc3dSmrg }
921debfc3dSmrg 
931debfc3dSmrg /* Main entry point for return value optimizations.
941debfc3dSmrg 
951debfc3dSmrg    If this function always returns the same local variable, and that
961debfc3dSmrg    local variable is an aggregate type, then replace the variable with
971debfc3dSmrg    the function's DECL_RESULT.
981debfc3dSmrg 
991debfc3dSmrg    This is the equivalent of the C++ named return value optimization
1001debfc3dSmrg    applied to optimized trees in a language independent form.  If we
1011debfc3dSmrg    ever encounter languages which prevent this kind of optimization,
1021debfc3dSmrg    then we could either have the languages register the optimization or
1031debfc3dSmrg    we could change the gating function to check the current language.  */
1041debfc3dSmrg 
1051debfc3dSmrg namespace {
1061debfc3dSmrg 
1071debfc3dSmrg const pass_data pass_data_nrv =
1081debfc3dSmrg {
1091debfc3dSmrg   GIMPLE_PASS, /* type */
1101debfc3dSmrg   "nrv", /* name */
1111debfc3dSmrg   OPTGROUP_NONE, /* optinfo_flags */
1121debfc3dSmrg   TV_TREE_NRV, /* tv_id */
1131debfc3dSmrg   ( PROP_ssa | PROP_cfg ), /* properties_required */
1141debfc3dSmrg   0, /* properties_provided */
1151debfc3dSmrg   0, /* properties_destroyed */
1161debfc3dSmrg   0, /* todo_flags_start */
1171debfc3dSmrg   0, /* todo_flags_finish */
1181debfc3dSmrg };
1191debfc3dSmrg 
1201debfc3dSmrg class pass_nrv : public gimple_opt_pass
1211debfc3dSmrg {
1221debfc3dSmrg public:
pass_nrv(gcc::context * ctxt)1231debfc3dSmrg   pass_nrv (gcc::context *ctxt)
1241debfc3dSmrg     : gimple_opt_pass (pass_data_nrv, ctxt)
1251debfc3dSmrg   {}
1261debfc3dSmrg 
1271debfc3dSmrg   /* opt_pass methods: */
gate(function *)1281debfc3dSmrg   virtual bool gate (function *) { return optimize > 0; }
1291debfc3dSmrg 
1301debfc3dSmrg   virtual unsigned int execute (function *);
1311debfc3dSmrg 
1321debfc3dSmrg }; // class pass_nrv
1331debfc3dSmrg 
1341debfc3dSmrg unsigned int
execute(function * fun)1351debfc3dSmrg pass_nrv::execute (function *fun)
1361debfc3dSmrg {
1371debfc3dSmrg   tree result = DECL_RESULT (current_function_decl);
1381debfc3dSmrg   tree result_type = TREE_TYPE (result);
1391debfc3dSmrg   tree found = NULL;
1401debfc3dSmrg   basic_block bb;
1411debfc3dSmrg   gimple_stmt_iterator gsi;
1421debfc3dSmrg   struct nrv_data_t data;
1431debfc3dSmrg 
1441debfc3dSmrg   /* If this function does not return an aggregate type in memory, then
1451debfc3dSmrg      there is nothing to do.  */
1461debfc3dSmrg   if (!aggregate_value_p (result, current_function_decl))
1471debfc3dSmrg     return 0;
1481debfc3dSmrg 
1491debfc3dSmrg   /* If a GIMPLE type is returned in memory, finalize_nrv_r might create
1501debfc3dSmrg      non-GIMPLE.  */
1511debfc3dSmrg   if (is_gimple_reg_type (result_type))
1521debfc3dSmrg     return 0;
1531debfc3dSmrg 
1541debfc3dSmrg   /* If the front end already did something like this, don't do it here.  */
1551debfc3dSmrg   if (DECL_NAME (result))
1561debfc3dSmrg     return 0;
1571debfc3dSmrg 
1581debfc3dSmrg   /* If the result has its address taken then it might be modified
1591debfc3dSmrg      by means not detected in the following loop.  Bail out in this
1601debfc3dSmrg      case.  */
1611debfc3dSmrg   if (TREE_ADDRESSABLE (result))
1621debfc3dSmrg     return 0;
1631debfc3dSmrg 
1641debfc3dSmrg   /* Look through each block for assignments to the RESULT_DECL.  */
1651debfc3dSmrg   FOR_EACH_BB_FN (bb, fun)
1661debfc3dSmrg     {
1671debfc3dSmrg       for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
1681debfc3dSmrg 	{
1691debfc3dSmrg 	  gimple *stmt = gsi_stmt (gsi);
1701debfc3dSmrg 	  tree ret_val;
1711debfc3dSmrg 
1721debfc3dSmrg 	  if (greturn *return_stmt = dyn_cast <greturn *> (stmt))
1731debfc3dSmrg 	    {
1741debfc3dSmrg 	      /* In a function with an aggregate return value, the
1751debfc3dSmrg 		 gimplifier has changed all non-empty RETURN_EXPRs to
1761debfc3dSmrg 		 return the RESULT_DECL.  */
1771debfc3dSmrg 	      ret_val = gimple_return_retval (return_stmt);
1781debfc3dSmrg 	      if (ret_val)
1791debfc3dSmrg 		gcc_assert (ret_val == result);
1801debfc3dSmrg 	    }
1811debfc3dSmrg 	  else if (gimple_has_lhs (stmt)
1821debfc3dSmrg 		   && gimple_get_lhs (stmt) == result)
1831debfc3dSmrg 	    {
1841debfc3dSmrg               tree rhs;
1851debfc3dSmrg 
1861debfc3dSmrg 	      if (!gimple_assign_copy_p (stmt))
1871debfc3dSmrg 		return 0;
1881debfc3dSmrg 
1891debfc3dSmrg 	      rhs = gimple_assign_rhs1 (stmt);
1901debfc3dSmrg 
1911debfc3dSmrg 	      /* Now verify that this return statement uses the same value
1921debfc3dSmrg 		 as any previously encountered return statement.  */
1931debfc3dSmrg 	      if (found != NULL)
1941debfc3dSmrg 		{
1951debfc3dSmrg 		  /* If we found a return statement using a different variable
1961debfc3dSmrg 		     than previous return statements, then we cannot perform
1971debfc3dSmrg 		     NRV optimizations.  */
1981debfc3dSmrg 		  if (found != rhs)
1991debfc3dSmrg 		    return 0;
2001debfc3dSmrg 		}
2011debfc3dSmrg 	      else
2021debfc3dSmrg 		found = rhs;
2031debfc3dSmrg 
2041debfc3dSmrg 	      /* The returned value must be a local automatic variable of the
2051debfc3dSmrg 		 same type and alignment as the function's result.  */
2061debfc3dSmrg 	      if (!VAR_P (found)
2071debfc3dSmrg 		  || TREE_THIS_VOLATILE (found)
2081debfc3dSmrg 		  || !auto_var_in_fn_p (found, current_function_decl)
2091debfc3dSmrg 		  || TREE_ADDRESSABLE (found)
2101debfc3dSmrg 		  || DECL_ALIGN (found) > DECL_ALIGN (result)
2111debfc3dSmrg 		  || !useless_type_conversion_p (result_type,
2121debfc3dSmrg 						 TREE_TYPE (found)))
2131debfc3dSmrg 		return 0;
2141debfc3dSmrg 	    }
2151debfc3dSmrg 	  else if (gimple_has_lhs (stmt))
2161debfc3dSmrg 	    {
2171debfc3dSmrg 	      tree addr = get_base_address (gimple_get_lhs (stmt));
2181debfc3dSmrg 	       /* If there's any MODIFY of component of RESULT,
2191debfc3dSmrg 		  then bail out.  */
2201debfc3dSmrg 	      if (addr && addr == result)
2211debfc3dSmrg 		return 0;
2221debfc3dSmrg 	    }
2231debfc3dSmrg 	}
2241debfc3dSmrg     }
2251debfc3dSmrg 
2261debfc3dSmrg   if (!found)
2271debfc3dSmrg     return 0;
2281debfc3dSmrg 
2291debfc3dSmrg   /* If dumping details, then note once and only the NRV replacement.  */
2301debfc3dSmrg   if (dump_file && (dump_flags & TDF_DETAILS))
2311debfc3dSmrg     {
2321debfc3dSmrg       fprintf (dump_file, "NRV Replaced: ");
2331debfc3dSmrg       print_generic_expr (dump_file, found, dump_flags);
2341debfc3dSmrg       fprintf (dump_file, "  with: ");
2351debfc3dSmrg       print_generic_expr (dump_file, result, dump_flags);
2361debfc3dSmrg       fprintf (dump_file, "\n");
2371debfc3dSmrg     }
2381debfc3dSmrg 
2391debfc3dSmrg   TREE_ADDRESSABLE (result) |= TREE_ADDRESSABLE (found);
2401debfc3dSmrg 
2411debfc3dSmrg   /* Now walk through the function changing all references to VAR to be
2421debfc3dSmrg      RESULT.  */
2431debfc3dSmrg   data.var = found;
2441debfc3dSmrg   data.result = result;
2451debfc3dSmrg   FOR_EACH_BB_FN (bb, fun)
2461debfc3dSmrg     {
2471debfc3dSmrg       for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); )
2481debfc3dSmrg 	{
2491debfc3dSmrg 	  gimple *stmt = gsi_stmt (gsi);
2501debfc3dSmrg 	  /* If this is a copy from VAR to RESULT, remove it.  */
2511debfc3dSmrg 	  if (gimple_assign_copy_p (stmt)
2521debfc3dSmrg 	      && gimple_assign_lhs (stmt) == result
2531debfc3dSmrg 	      && gimple_assign_rhs1 (stmt) == found)
2541debfc3dSmrg 	    {
2551debfc3dSmrg 	      unlink_stmt_vdef (stmt);
2561debfc3dSmrg 	      gsi_remove (&gsi, true);
2571debfc3dSmrg 	      release_defs (stmt);
2581debfc3dSmrg 	    }
2591debfc3dSmrg 	  else
2601debfc3dSmrg 	    {
2611debfc3dSmrg 	      struct walk_stmt_info wi;
2621debfc3dSmrg 	      memset (&wi, 0, sizeof (wi));
2631debfc3dSmrg 	      wi.info = &data;
2641debfc3dSmrg 	      data.modified = 0;
2651debfc3dSmrg 	      walk_gimple_op (stmt, finalize_nrv_r, &wi);
2661debfc3dSmrg 	      if (data.modified)
2671debfc3dSmrg 		update_stmt (stmt);
2681debfc3dSmrg 	      gsi_next (&gsi);
2691debfc3dSmrg 	    }
2701debfc3dSmrg 	}
2711debfc3dSmrg     }
2721debfc3dSmrg 
2731debfc3dSmrg   SET_DECL_VALUE_EXPR (found, result);
2741debfc3dSmrg   DECL_HAS_VALUE_EXPR_P (found) = 1;
2751debfc3dSmrg 
2761debfc3dSmrg   return 0;
2771debfc3dSmrg }
2781debfc3dSmrg 
2791debfc3dSmrg } // anon namespace
2801debfc3dSmrg 
2811debfc3dSmrg gimple_opt_pass *
make_pass_nrv(gcc::context * ctxt)2821debfc3dSmrg make_pass_nrv (gcc::context *ctxt)
2831debfc3dSmrg {
2841debfc3dSmrg   return new pass_nrv (ctxt);
2851debfc3dSmrg }
2861debfc3dSmrg 
2871debfc3dSmrg /* Determine (pessimistically) whether DEST is available for NRV
2881debfc3dSmrg    optimization, where DEST is expected to be the LHS of a modify
2891debfc3dSmrg    expression where the RHS is a function returning an aggregate.
2901debfc3dSmrg 
2911debfc3dSmrg    DEST is available if it is not clobbered or used by the call.  */
2921debfc3dSmrg 
2931debfc3dSmrg static bool
dest_safe_for_nrv_p(gcall * call)2941debfc3dSmrg dest_safe_for_nrv_p (gcall *call)
2951debfc3dSmrg {
2961debfc3dSmrg   tree dest = gimple_call_lhs (call);
2971debfc3dSmrg 
2981debfc3dSmrg   dest = get_base_address (dest);
2991debfc3dSmrg   if (! dest)
3001debfc3dSmrg     return false;
3011debfc3dSmrg 
3021debfc3dSmrg   if (TREE_CODE (dest) == SSA_NAME)
3031debfc3dSmrg     return true;
3041debfc3dSmrg 
3051debfc3dSmrg   if (call_may_clobber_ref_p (call, dest)
3061debfc3dSmrg       || ref_maybe_used_by_stmt_p (call, dest))
3071debfc3dSmrg     return false;
3081debfc3dSmrg 
3091debfc3dSmrg   return true;
3101debfc3dSmrg }
3111debfc3dSmrg 
3121debfc3dSmrg /* Walk through the function looking for GIMPLE_ASSIGNs with calls that
3131debfc3dSmrg    return in memory on the RHS.  For each of these, determine whether it is
3141debfc3dSmrg    safe to pass the address of the LHS as the return slot, and mark the
3151debfc3dSmrg    call appropriately if so.
3161debfc3dSmrg 
3171debfc3dSmrg    The NRV shares the return slot with a local variable in the callee; this
3181debfc3dSmrg    optimization shares the return slot with the target of the call within
3191debfc3dSmrg    the caller.  If the NRV is performed (which we can't know in general),
3201debfc3dSmrg    this optimization is safe if the address of the target has not
3211debfc3dSmrg    escaped prior to the call.  If it has, modifications to the local
3221debfc3dSmrg    variable will produce visible changes elsewhere, as in PR c++/19317.  */
3231debfc3dSmrg 
3241debfc3dSmrg namespace {
3251debfc3dSmrg 
3261debfc3dSmrg const pass_data pass_data_return_slot =
3271debfc3dSmrg {
3281debfc3dSmrg   GIMPLE_PASS, /* type */
3291debfc3dSmrg   "retslot", /* name */
3301debfc3dSmrg   OPTGROUP_NONE, /* optinfo_flags */
3311debfc3dSmrg   TV_NONE, /* tv_id */
3321debfc3dSmrg   PROP_ssa, /* properties_required */
3331debfc3dSmrg   0, /* properties_provided */
3341debfc3dSmrg   0, /* properties_destroyed */
3351debfc3dSmrg   0, /* todo_flags_start */
3361debfc3dSmrg   0, /* todo_flags_finish */
3371debfc3dSmrg };
3381debfc3dSmrg 
3391debfc3dSmrg class pass_return_slot : public gimple_opt_pass
3401debfc3dSmrg {
3411debfc3dSmrg public:
pass_return_slot(gcc::context * ctxt)3421debfc3dSmrg   pass_return_slot (gcc::context *ctxt)
3431debfc3dSmrg     : gimple_opt_pass (pass_data_return_slot, ctxt)
3441debfc3dSmrg   {}
3451debfc3dSmrg 
3461debfc3dSmrg   /* opt_pass methods: */
3471debfc3dSmrg   virtual unsigned int execute (function *);
3481debfc3dSmrg 
3491debfc3dSmrg }; // class pass_return_slot
3501debfc3dSmrg 
3511debfc3dSmrg unsigned int
execute(function * fun)3521debfc3dSmrg pass_return_slot::execute (function *fun)
3531debfc3dSmrg {
3541debfc3dSmrg   basic_block bb;
3551debfc3dSmrg 
3561debfc3dSmrg   FOR_EACH_BB_FN (bb, fun)
3571debfc3dSmrg     {
3581debfc3dSmrg       gimple_stmt_iterator gsi;
3591debfc3dSmrg       for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
3601debfc3dSmrg 	{
3611debfc3dSmrg 	  gcall *stmt;
3621debfc3dSmrg 	  bool slot_opt_p;
3631debfc3dSmrg 
3641debfc3dSmrg 	  stmt = dyn_cast <gcall *> (gsi_stmt (gsi));
3651debfc3dSmrg 	  if (stmt
3661debfc3dSmrg 	      && gimple_call_lhs (stmt)
3671debfc3dSmrg 	      && !gimple_call_return_slot_opt_p (stmt)
368*8feb0f0bSmrg 	      /* Ignore internal functions, those are expanded specially
369*8feb0f0bSmrg 		 and aggregate_value_p on their result might result in
370*8feb0f0bSmrg 		 undesirable warnings with some backends.  */
371*8feb0f0bSmrg 	      && !gimple_call_internal_p (stmt)
3721debfc3dSmrg 	      && aggregate_value_p (TREE_TYPE (gimple_call_lhs (stmt)),
3731debfc3dSmrg 				    gimple_call_fndecl (stmt)))
3741debfc3dSmrg 	    {
3751debfc3dSmrg 	      /* Check if the location being assigned to is
3761debfc3dSmrg 		 clobbered by the call.  */
3771debfc3dSmrg 	      slot_opt_p = dest_safe_for_nrv_p (stmt);
3781debfc3dSmrg 	      gimple_call_set_return_slot_opt (stmt, slot_opt_p);
3791debfc3dSmrg 	    }
3801debfc3dSmrg 	}
3811debfc3dSmrg     }
3821debfc3dSmrg   return 0;
3831debfc3dSmrg }
3841debfc3dSmrg 
3851debfc3dSmrg } // anon namespace
3861debfc3dSmrg 
3871debfc3dSmrg gimple_opt_pass *
make_pass_return_slot(gcc::context * ctxt)3881debfc3dSmrg make_pass_return_slot (gcc::context *ctxt)
3891debfc3dSmrg {
3901debfc3dSmrg   return new pass_return_slot (ctxt);
3911debfc3dSmrg }
392