LCOV - code coverage report
Current view: top level - gcc - tree-nrv.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 94.7 % 113 107
Test Date: 2026-02-28 14:20:25 Functions: 100.0 % 7 7
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /* Language independent return value optimizations
       2              :    Copyright (C) 2004-2026 Free Software Foundation, Inc.
       3              : 
       4              : This file is part of GCC.
       5              : 
       6              : GCC is free software; you can redistribute it and/or modify
       7              : it under the terms of the GNU General Public License as published by
       8              : the Free Software Foundation; either version 3, or (at your option)
       9              : any later version.
      10              : 
      11              : GCC is distributed in the hope that it will be useful,
      12              : but WITHOUT ANY WARRANTY; without even the implied warranty of
      13              : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14              : GNU General Public License for more details.
      15              : 
      16              : You should have received a copy of the GNU General Public License
      17              : along with GCC; see the file COPYING3.  If not see
      18              : <http://www.gnu.org/licenses/>.  */
      19              : 
      20              : #include "config.h"
      21              : #include "system.h"
      22              : #include "coretypes.h"
      23              : #include "backend.h"
      24              : #include "tree.h"
      25              : #include "gimple.h"
      26              : #include "tree-pass.h"
      27              : #include "ssa.h"
      28              : #include "tree-pretty-print.h"
      29              : #include "gimple-iterator.h"
      30              : #include "gimple-walk.h"
      31              : #include "internal-fn.h"
      32              : 
      33              : /* This file implements return value optimizations for functions which
      34              :    return aggregate types.
      35              : 
      36              :    Basically this pass searches the function for return statements which
      37              :    return a local aggregate.  When converted to RTL such statements will
      38              :    generate a copy from the local aggregate to final return value destination
      39              :    mandated by the target's ABI.
      40              : 
      41              :    That copy can often be avoided by directly constructing the return value
      42              :    into the final destination mandated by the target's ABI.
      43              : 
      44              :    This is basically a generic equivalent to the C++ front-end's
      45              :    Named Return Value optimization.  */
      46              : 
      47              : struct nrv_data_t
      48              : {
      49              :   /* This is the temporary (a VAR_DECL) which appears in all of
      50              :      this function's RETURN_EXPR statements.  */
      51              :   tree var;
      52              : 
      53              :   /* This is the function's RESULT_DECL.  We will replace all occurrences
      54              :      of VAR with RESULT_DECL when we apply this optimization.  */
      55              :   tree result;
      56              :   int modified;
      57              : };
      58              : 
      59              : static tree finalize_nrv_r (tree *, int *, void *);
      60              : 
      61              : /* Callback for the tree walker.
      62              : 
      63              :    If TP refers to a RETURN_EXPR, then set the expression being returned
      64              :    to nrv_data->result.
      65              : 
      66              :    If TP refers to nrv_data->var, then replace nrv_data->var with
      67              :    nrv_data->result.
      68              : 
      69              :    If we reach a node where we know all the subtrees are uninteresting,
      70              :    then set *WALK_SUBTREES to zero.  */
      71              : 
      72              : static tree
      73       332841 : finalize_nrv_r (tree *tp, int *walk_subtrees, void *data)
      74              : {
      75       332841 :   struct walk_stmt_info *wi = (struct walk_stmt_info *) data;
      76       332841 :   struct nrv_data_t *dp = (struct nrv_data_t *) wi->info;
      77              : 
      78              :   /* No need to walk into types.  */
      79       332841 :   if (TYPE_P (*tp))
      80            0 :     *walk_subtrees = 0;
      81              : 
      82              :   /* Otherwise replace all occurrences of VAR with RESULT.  */
      83       332841 :   else if (*tp == dp->var)
      84              :     {
      85         5556 :       *tp = dp->result;
      86         5556 :       dp->modified = 1;
      87              :     }
      88              : 
      89              :   /* Keep iterating.  */
      90       332841 :   return NULL_TREE;
      91              : }
      92              : 
      93              : /* Main entry point for return value optimizations.
      94              : 
      95              :    If this function always returns the same local variable, and that
      96              :    local variable is an aggregate type, then replace the variable with
      97              :    the function's DECL_RESULT.
      98              : 
      99              :    This is the equivalent of the C++ named return value optimization
     100              :    applied to optimized trees in a language independent form.  If we
     101              :    ever encounter languages which prevent this kind of optimization,
     102              :    then we could either have the languages register the optimization or
     103              :    we could change the gating function to check the current language.  */
     104              : 
     105              : namespace {
     106              : 
     107              : const pass_data pass_data_nrv =
     108              : {
     109              :   GIMPLE_PASS, /* type */
     110              :   "nrv", /* name */
     111              :   OPTGROUP_NONE, /* optinfo_flags */
     112              :   TV_TREE_NRV, /* tv_id */
     113              :   ( PROP_ssa | PROP_cfg ), /* properties_required */
     114              :   0, /* properties_provided */
     115              :   0, /* properties_destroyed */
     116              :   0, /* todo_flags_start */
     117              :   0, /* todo_flags_finish */
     118              : };
     119              : 
     120              : class pass_nrv : public gimple_opt_pass
     121              : {
     122              : public:
     123       285722 :   pass_nrv (gcc::context *ctxt)
     124       571444 :     : gimple_opt_pass (pass_data_nrv, ctxt)
     125              :   {}
     126              : 
     127              :   /* opt_pass methods: */
     128      1472150 :   bool gate (function *) final override { return optimize > 0; }
     129              : 
     130              :   unsigned int execute (function *) final override;
     131              : 
     132              : }; // class pass_nrv
     133              : 
     134              : unsigned int
     135      1044019 : pass_nrv::execute (function *fun)
     136              : {
     137      1044019 :   tree result = DECL_RESULT (current_function_decl);
     138      1044019 :   tree result_type = TREE_TYPE (result);
     139      1044019 :   tree found = NULL;
     140      1044019 :   basic_block bb;
     141      1044019 :   gimple_stmt_iterator gsi;
     142      1044019 :   struct nrv_data_t data;
     143              : 
     144              :   /* If this function does not return an aggregate type in memory, then
     145              :      there is nothing to do.  */
     146      1044019 :   if (!aggregate_value_p (result, current_function_decl))
     147              :     return 0;
     148              : 
     149              :   /* If a GIMPLE type is returned in memory, finalize_nrv_r might create
     150              :      non-GIMPLE.  */
     151        53361 :   if (is_gimple_reg_type (result_type))
     152              :     return 0;
     153              : 
     154              :   /* If the front end already did something like this, don't do it here.  */
     155        43579 :   if (DECL_NAME (result))
     156              :     return 0;
     157              : 
     158              :   /* If the result has its address taken then it might be modified
     159              :      by means not detected in the following loop.  Bail out in this
     160              :      case.  */
     161        42773 :   if (TREE_ADDRESSABLE (result))
     162              :     return 0;
     163              : 
     164              :   /* Look through each block for assignments to the RESULT_DECL.  */
     165       547476 :   FOR_EACH_BB_FN (bb, fun)
     166              :     {
     167      3553599 :       for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
     168              :         {
     169      2504863 :           gimple *stmt = gsi_stmt (gsi);
     170              : 
     171      2504863 :           if (greturn *return_stmt = dyn_cast <greturn *> (stmt))
     172              :             {
     173              :               /* We cannot perform NRV optimizations in a function with an
     174              :                  aggregate return value if there is a return that does not
     175              :                  return RESULT_DECL.  We used to assert this scenario doesn't
     176              :                  happen: the gimplifier has changed all non-empty RETURN_EXPRs
     177              :                  to return the RESULT_DECL.  However, per PR119835 we may run
     178              :                  into this scenario for offloading compilation, and therefore
     179              :                  gracefully bail out.  */
     180         4456 :               if (tree ret_val = gimple_return_retval (return_stmt))
     181              :                 {
     182         4456 :                   if (ret_val != result)
     183              :                     return 0;
     184              :                 }
     185              :             }
     186      2500407 :           else if (gimple_has_lhs (stmt)
     187      1185386 :                    && gimple_get_lhs (stmt) == result)
     188              :             {
     189        11434 :               tree rhs;
     190              : 
     191        11434 :               if (!gimple_assign_copy_p (stmt))
     192              :                 return 0;
     193              : 
     194        10577 :               rhs = gimple_assign_rhs1 (stmt);
     195              : 
     196              :               /* Now verify that this return statement uses the same value
     197              :                  as any previously encountered return statement.  */
     198        10577 :               if (found != NULL)
     199              :                 {
     200              :                   /* If we found a return statement using a different variable
     201              :                      than previous return statements, then we cannot perform
     202              :                      NRV optimizations.  */
     203          739 :                   if (found != rhs)
     204              :                     return 0;
     205              :                 }
     206              :               else
     207              :                 found = rhs;
     208              : 
     209              :               /* The returned value must be a local automatic variable of the
     210              :                  same type and alignment as the function's result.  */
     211         9839 :               if (!VAR_P (found)
     212         9165 :                   || TREE_THIS_VOLATILE (found)
     213         9163 :                   || !auto_var_in_fn_p (found, current_function_decl)
     214         7867 :                   || TREE_ADDRESSABLE (found)
     215         7503 :                   || DECL_ALIGN (found) > DECL_ALIGN (result)
     216        15725 :                   || !useless_type_conversion_p (result_type,
     217         5886 :                                                  TREE_TYPE (found)))
     218         3953 :                 return 0;
     219              :             }
     220      5173006 :           else if (gimple_has_lhs (stmt))
     221              :             {
     222      1083281 :               tree addr = get_base_address (gimple_get_lhs (stmt));
     223              :                /* If there's any MODIFY of component of RESULT,
     224              :                   then bail out.  */
     225      1083281 :               if (addr && addr == result)
     226              :                 return 0;
     227              :             }
     228              :         }
     229              :     }
     230              : 
     231         4525 :   if (!found)
     232              :     return 0;
     233              : 
     234              :   /* If dumping details, then note once and only the NRV replacement.  */
     235         4414 :   if (dump_file && (dump_flags & TDF_DETAILS))
     236              :     {
     237            0 :       fprintf (dump_file, "NRV Replaced: ");
     238            0 :       print_generic_expr (dump_file, found, dump_flags);
     239            0 :       fprintf (dump_file, "  with: ");
     240            0 :       print_generic_expr (dump_file, result, dump_flags);
     241            0 :       fprintf (dump_file, "\n");
     242              :     }
     243              : 
     244         4414 :   TREE_ADDRESSABLE (result) |= TREE_ADDRESSABLE (found);
     245              : 
     246              :   /* Now walk through the function changing all references to VAR to be
     247              :      RESULT.  */
     248         4414 :   data.var = found;
     249         4414 :   data.result = result;
     250        28165 :   FOR_EACH_BB_FN (bb, fun)
     251              :     {
     252       144176 :       for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); )
     253              :         {
     254        96674 :           gimple *stmt = gsi_stmt (gsi);
     255              :           /* If this is a copy from VAR to RESULT, remove it.  */
     256        96674 :           if (gimple_assign_copy_p (stmt)
     257        21355 :               && gimple_assign_lhs (stmt) == result
     258       101089 :               && gimple_assign_rhs1 (stmt) == found)
     259              :             {
     260         4415 :               unlink_stmt_vdef (stmt);
     261         4415 :               gsi_remove (&gsi, true);
     262         4415 :               release_defs (stmt);
     263              :             }
     264              :           else
     265              :             {
     266        92259 :               struct walk_stmt_info wi;
     267        92259 :               memset (&wi, 0, sizeof (wi));
     268        92259 :               wi.info = &data;
     269        92259 :               data.modified = 0;
     270        92259 :               walk_gimple_op (stmt, finalize_nrv_r, &wi);
     271        92259 :               if (data.modified)
     272              :                 {
     273              :                   /* If this is a CLOBBER of VAR, remove it.  */
     274         5556 :                   if (gimple_clobber_p (stmt))
     275              :                     {
     276          650 :                       unlink_stmt_vdef (stmt);
     277          650 :                       gsi_remove (&gsi, true);
     278          650 :                       release_defs (stmt);
     279          650 :                       continue;
     280              :                     }
     281         4906 :                   update_stmt (stmt);
     282              :                 }
     283        91609 :               gsi_next (&gsi);
     284              :             }
     285              :         }
     286              :     }
     287              : 
     288         4414 :   SET_DECL_VALUE_EXPR (found, result);
     289         4414 :   DECL_HAS_VALUE_EXPR_P (found) = 1;
     290              : 
     291         4414 :   return 0;
     292              : }
     293              : 
     294              : } // anon namespace
     295              : 
     296              : gimple_opt_pass *
     297       285722 : make_pass_nrv (gcc::context *ctxt)
     298              : {
     299       285722 :   return new pass_nrv (ctxt);
     300              : }
     301              : 
     302              : /* Determine (pessimistically) whether DEST is available for NRV
     303              :    optimization, where DEST is expected to be the LHS of a modify
     304              :    expression where the RHS is a function returning an aggregate.
     305              : 
     306              :    DEST is available if it is not clobbered or used by the call.  */
     307              : 
     308              : static bool
     309         7364 : dest_safe_for_nrv_p (gcall *call)
     310              : {
     311         7364 :   tree dest = gimple_call_lhs (call);
     312              : 
     313         7364 :   dest = get_base_address (dest);
     314         7364 :   if (! dest)
     315              :     return false;
     316              : 
     317         7364 :   if (TREE_CODE (dest) == SSA_NAME)
     318              :     return true;
     319              : 
     320         3537 :   if (call_may_clobber_ref_p (call, dest, false)
     321         3537 :       || ref_maybe_used_by_stmt_p (call, dest, false))
     322          741 :     return false;
     323              : 
     324              :   return true;
     325              : }
     326              : 
     327              : /* Walk through the function looking for GIMPLE_ASSIGNs with calls that
     328              :    return in memory on the RHS.  For each of these, determine whether it is
     329              :    safe to pass the address of the LHS as the return slot, and mark the
     330              :    call appropriately if so.
     331              : 
     332              :    The NRV shares the return slot with a local variable in the callee; this
     333              :    optimization shares the return slot with the target of the call within
     334              :    the caller.  If the NRV is performed (which we can't know in general),
     335              :    this optimization is safe if the address of the target has not
     336              :    escaped prior to the call.  If it has, modifications to the local
     337              :    variable will produce visible changes elsewhere, as in PR c++/19317.  */
     338              : 
     339              : namespace {
     340              : 
     341              : const pass_data pass_data_return_slot =
     342              : {
     343              :   GIMPLE_PASS, /* type */
     344              :   "retslot", /* name */
     345              :   OPTGROUP_NONE, /* optinfo_flags */
     346              :   TV_NONE, /* tv_id */
     347              :   PROP_ssa, /* properties_required */
     348              :   0, /* properties_provided */
     349              :   0, /* properties_destroyed */
     350              :   0, /* todo_flags_start */
     351              :   0, /* todo_flags_finish */
     352              : };
     353              : 
     354              : class pass_return_slot : public gimple_opt_pass
     355              : {
     356              : public:
     357       285722 :   pass_return_slot (gcc::context *ctxt)
     358       571444 :     : gimple_opt_pass (pass_data_return_slot, ctxt)
     359              :   {}
     360              : 
     361              :   /* opt_pass methods: */
     362              :   unsigned int execute (function *) final override;
     363              : 
     364              : }; // class pass_return_slot
     365              : 
     366              : unsigned int
     367      1041408 : pass_return_slot::execute (function *fun)
     368              : {
     369      1041408 :   basic_block bb;
     370              : 
     371     12515677 :   FOR_EACH_BB_FN (bb, fun)
     372              :     {
     373     11474269 :       gimple_stmt_iterator gsi;
     374    107995431 :       for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
     375              :         {
     376     85046893 :           gcall *stmt;
     377     85046893 :           bool slot_opt_p;
     378              : 
     379     85046893 :           stmt = dyn_cast <gcall *> (gsi_stmt (gsi));
     380      5792667 :           if (stmt
     381      5792667 :               && gimple_call_lhs (stmt)
     382      2038167 :               && !gimple_call_return_slot_opt_p (stmt)
     383              :               /* Ignore internal functions, those are expanded specially
     384              :                  and aggregate_value_p on their result might result in
     385              :                  undesirable warnings with some backends.  */
     386      1853711 :               && !gimple_call_internal_p (stmt)
     387      1758055 :               && aggregate_value_p (TREE_TYPE (gimple_call_lhs (stmt)),
     388      1758055 :                                     gimple_call_fntype (stmt)))
     389              :             {
     390              :               /* Check if the location being assigned to is
     391              :                  clobbered by the call.  */
     392         7364 :               slot_opt_p = dest_safe_for_nrv_p (stmt);
     393     85054257 :               gimple_call_set_return_slot_opt (stmt, slot_opt_p);
     394              :             }
     395              :         }
     396              :     }
     397      1041408 :   return 0;
     398              : }
     399              : 
     400              : } // anon namespace
     401              : 
     402              : gimple_opt_pass *
     403       285722 : make_pass_return_slot (gcc::context *ctxt)
     404              : {
     405       285722 :   return new pass_return_slot (ctxt);
     406              : }
        

Generated by: LCOV version 2.4-beta

LCOV profile is generated on x86_64 machine using following configure options: configure --disable-bootstrap --enable-coverage=opt --enable-languages=c,c++,fortran,go,jit,lto,rust,m2 --enable-host-shared. GCC test suite is run with the built compiler.