This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

C++ PATCH for named return value optimization


This has been sitting in my to-be-finished directory for several months;
sorry I didn't remember it in time for 3.0.

This is all pretty straightforward: if a function always returns the same
local variable in memory, it is stored in the return slot and the returns
become noops.  The latter is accomplished by setting a flag on the
RETURN_STMT.  I'm somewhat uncomfortable about that choice, but any tree
rewriting would break inlining.  Since RETURN_STMTs are replaced during the
inlining process, this approach is transparent to inlining.

Tested i686-pc-linux-gnu, applied to trunk.

2001-06-18  Jason Merrill  <jason_merrill@redhat.com>

	* c-common.h (RETURN_NULLIFIED_P): New macro.
	* c-semantics.c (genrtl_return_stmt): Check it.

	Implement the Named Return Value optimization.
	* cp-tree.h (struct cp_language_function): Add x_return_value.
	(current_function_return_value): Now a macro.
	* decl.c: Don't define it.
	(define_label, finish_case_label): Don't clear it.
	(init_decl_processing): Don't register it with GC.
	* semantics.c (genrtl_finish_function): Don't check it for
	no_return_label.  Copy the RTL from the return value to
	current_function_return_value and walk, calling...
	(nullify_returns_r): ...this new fn.
	* typeck.c (check_return_expr): Set current_function_return_value.

*** c-common.h.~1~	Mon Jun 18 13:23:43 2001
--- c-common.h	Fri Jun 15 17:18:41 2001
*************** Boston, MA 02111-1307, USA.  */
*** 32,37 ****
--- 32,38 ----
        SCOPE_BEGIN_P (in SCOPE_STMT)
        DECL_PRETTY_FUNCTION_P (in VAR_DECL)
        NEW_FOR_SCOPE_P (in FOR_STMT)
+       RETURN_NULLIFIED_P (in RETURN_STMT)
        ASM_INPUT_P (in ASM_STMT)
     1: C_DECLARED_LABEL_FLAG (in LABEL_DECL)
        STMT_IS_FULL_EXPR_P (in _STMT)
*************** extern tree strip_array_types           
*** 561,569 ****
  #define DO_COND(NODE)           TREE_OPERAND (DO_STMT_CHECK (NODE), 0)
  #define DO_BODY(NODE)           TREE_OPERAND (DO_STMT_CHECK (NODE), 1)
  
! /* RETURN_STMT accessor. This gives the expression associated with a
!    return statement. */
  #define RETURN_EXPR(NODE)       TREE_OPERAND (RETURN_STMT_CHECK (NODE), 0)
  
  /* EXPR_STMT accessor. This gives the expression associated with an
     expression statement. */
--- 562,572 ----
  #define DO_COND(NODE)           TREE_OPERAND (DO_STMT_CHECK (NODE), 0)
  #define DO_BODY(NODE)           TREE_OPERAND (DO_STMT_CHECK (NODE), 1)
  
! /* RETURN_STMT accessors. These give the expression associated with a
!    return statement, and whether it should be ignored when expanding
!    (as opposed to inlining).  */
  #define RETURN_EXPR(NODE)       TREE_OPERAND (RETURN_STMT_CHECK (NODE), 0)
+ #define RETURN_NULLIFIED_P(NODE) TREE_LANG_FLAG_0 (RETURN_STMT_CHECK (NODE))
  
  /* EXPR_STMT accessor. This gives the expression associated with an
     expression statement. */
*** c-semantics.c.~1~	Mon Jun 18 13:23:43 2001
--- c-semantics.c	Fri Jun 15 17:18:14 2001
*************** void
*** 460,466 ****
  genrtl_return_stmt (stmt)
       tree stmt;
  {
!   tree expr = RETURN_EXPR (stmt);
  
    emit_line_note (input_filename, lineno);
    if (!expr)
--- 460,474 ----
  genrtl_return_stmt (stmt)
       tree stmt;
  {
!   tree expr;
! 
!   /* If RETURN_NULLIFIED_P is set, the frontend has arranged to set up
!      the return value separately, so just return the return value
!      itself.  This is used for the C++ named return value optimization.  */
!   if (RETURN_NULLIFIED_P (stmt))
!     expr = DECL_RESULT (current_function_decl);
!   else
!     expr = RETURN_EXPR (stmt);
  
    emit_line_note (input_filename, lineno);
    if (!expr)
*** cp/cp-tree.h.~1~	Mon Jun 18 13:23:43 2001
--- cp/cp-tree.h	Fri Jun 15 17:19:17 2001
*************** struct cp_language_function
*** 863,868 ****
--- 863,869 ----
    tree x_eh_spec_block;
    tree x_in_charge_parm;
    tree x_vtt_parm;
+   tree x_return_value;
  
    tree *x_vcalls_possible_p;
  
*************** struct cp_language_function
*** 953,959 ****
  
  #define in_function_try_handler cp_function_chain->in_function_try_handler
  
! extern tree current_function_return_value;
  extern tree global_namespace;
  
  #define ansi_opname(CODE) \
--- 954,965 ----
  
  #define in_function_try_handler cp_function_chain->in_function_try_handler
  
! /* Expression always returned from function, or error_mark_node
!    otherwise, for use by the automatic named return value optimization.  */
! 
! #define current_function_return_value \
!   (cp_function_chain->x_return_value)
! 
  extern tree global_namespace;
  
  #define ansi_opname(CODE) \
*** cp/decl.c.~1~	Mon Jun 18 13:23:43 2001
--- cp/decl.c	Fri Jun 15 17:19:33 2001
*************** struct named_label_list
*** 285,297 ****
  
  #define named_labels cp_function_chain->x_named_labels
  
- /* Set to 0 at beginning of a function definition, and whenever
-    a label (case or named) is defined.  Set to value of expression
-    returned from function when that value can be transformed into
-    a named return value.  */
- 
- tree current_function_return_value;
- 
  /* Nonzero means use the ISO C94 dialect of C.  */
  
  int flag_isoc94;
--- 285,290 ----
*************** define_label (filename, line, name)
*** 5153,5159 ****
  	  ent->binding_level = current_binding_level;
  	}
        check_previous_gotos (decl);
-       current_function_return_value = NULL_TREE;
        return decl;
      }
  }
--- 5146,5151 ----
*************** finish_case_label (low_value, high_value
*** 5255,5261 ****
       own new (temporary) binding contour.  */
    for (p = current_binding_level; !(p->parm_flag); p = p->level_chain)
      p->more_cleanups_ok = 0;
-   current_function_return_value = NULL_TREE;
  
    return r;
  }
--- 5247,5252 ----
*************** init_decl_processing ()
*** 6624,6630 ****
    ggc_add_tree_root (&lastiddecl, 1);
  
    ggc_add_tree_root (&last_function_parm_tags, 1);
-   ggc_add_tree_root (&current_function_return_value, 1);
    ggc_add_tree_root (&current_function_parm_tags, 1);
    ggc_add_tree_root (&last_function_parms, 1);
    ggc_add_tree_root (&error_mark_list, 1);
--- 6615,6620 ----
*** cp/semantics.c.~1~	Mon Jun 18 13:23:45 2001
--- cp/semantics.c	Mon Jun 18 13:53:03 2001
***************
*** 49,54 ****
--- 49,55 ----
  
  static tree maybe_convert_cond PARAMS ((tree));
  static tree simplify_aggr_init_exprs_r PARAMS ((tree *, int *, void *));
+ static tree nullify_returns_r PARAMS ((tree *, int *, void *));
  static void deferred_type_access_control PARAMS ((void));
  static void emit_associated_thunks PARAMS ((tree));
  static void genrtl_try_block PARAMS ((tree));
*************** expand_body (fn)
*** 2464,2469 ****
--- 2465,2489 ----
    timevar_pop (TV_EXPAND);
  }
  
+ /* Helper function for walk_tree, used by genrtl_start_function to override
+    all the RETURN_STMTs for the named return value optimization.  */
+ 
+ static tree
+ nullify_returns_r (tp, walk_subtrees, data)
+      tree *tp;
+      int *walk_subtrees;
+      void *data ATTRIBUTE_UNUSED;
+ {
+   /* No need to walk into types.  */
+   if (TYPE_P (*tp))
+     *walk_subtrees = 0;
+   else if (TREE_CODE (*tp) == RETURN_STMT)
+     RETURN_NULLIFIED_P (*tp) = 1;
+ 
+   /* Keep iterating.  */
+   return NULL_TREE;
+ }
+ 
  /* Start generating the RTL for FN.  */
  
  static void
*************** genrtl_start_function (fn)
*** 2541,2546 ****
--- 2561,2582 ----
    /* Create a binding contour which can be used to catch
       cleanup-generated temporaries.  */
    expand_start_bindings (2);
+ 
+   /* Set up the named return value optimization, if we can.  */
+   if (current_function_return_value
+       && current_function_return_value != error_mark_node)
+     {
+       tree r = current_function_return_value;
+       /* This is only worth doing for fns that return in memory--and
+ 	 simpler, since we don't have to worry about promoted modes.  */
+       if (aggregate_value_p (TREE_TYPE (TREE_TYPE (fn))))
+ 	{
+ 	  COPY_DECL_RTL (DECL_RESULT (fn), r);
+ 	  DECL_ALIGN (r) = DECL_ALIGN (DECL_RESULT (fn));
+ 	  walk_tree_without_duplicates (&DECL_SAVED_TREE (fn),
+ 					nullify_returns_r, NULL_TREE);
+ 	}
+     }
  }
  
  /* Finish generating the RTL for FN.  */
*************** genrtl_finish_function (fn)
*** 2579,2585 ****
  
    if (!dtor_label && !DECL_CONSTRUCTOR_P (fn)
        && return_label != NULL_RTX
-       && current_function_return_value == NULL_TREE
        && ! DECL_NAME (DECL_RESULT (current_function_decl)))
      no_return_label = build_decl (LABEL_DECL, NULL_TREE, NULL_TREE);
  
--- 2615,2620 ----
*** cp/semantics.c.~2~	Fri Jun 15 17:18:14 2001
--- cp/semantics.c	Mon Jun 18 13:53:03 2001
*************** genrtl_start_function (fn)
*** 2571,2577 ****
  	 simpler, since we don't have to worry about promoted modes.  */
        if (aggregate_value_p (TREE_TYPE (TREE_TYPE (fn))))
  	{
! 	  DECL_RTL (r) = DECL_RTL (DECL_RESULT (fn));
  	  DECL_ALIGN (r) = DECL_ALIGN (DECL_RESULT (fn));
  	  walk_tree_without_duplicates (&DECL_SAVED_TREE (fn),
  					nullify_returns_r, NULL_TREE);
--- 2571,2577 ----
  	 simpler, since we don't have to worry about promoted modes.  */
        if (aggregate_value_p (TREE_TYPE (TREE_TYPE (fn))))
  	{
! 	  COPY_DECL_RTL (DECL_RESULT (fn), r);
  	  DECL_ALIGN (r) = DECL_ALIGN (DECL_RESULT (fn));
  	  walk_tree_without_duplicates (&DECL_SAVED_TREE (fn),
  					nullify_returns_r, NULL_TREE);
*** cp/typeck.c.~1~	Mon Jun 18 13:23:45 2001
--- cp/typeck.c	Fri Jun 15 17:18:14 2001
*************** check_return_expr (retval)
*** 6679,6684 ****
--- 6679,6710 ----
        && retval != current_class_ref)
      cp_warning ("`operator=' should return a reference to `*this'");
  
+   /* The fabled Named Return Value optimization: If this is a
+      value-returning function that always returns the same local
+      variable, remember it.
+ 
+      It might be nice to be more flexible, and choose the first suitable
+      variable even if the function sometimes returns something else, but
+      then we run the risk of clobbering the variable we chose if the other
+      returned expression uses the chosen variable somehow.  And people expect
+      this restriction, anyway.  (jason 2000-11-19) */
+ 
+   if (fn_returns_value_p && optimize)
+     {
+       if (retval != NULL_TREE
+ 	  && (current_function_return_value == NULL_TREE
+ 	      || current_function_return_value == retval)
+ 	  && TREE_CODE (retval) == VAR_DECL
+ 	  && DECL_CONTEXT (retval) == current_function_decl
+ 	  && ! TREE_STATIC (retval)
+ 	  && ! DECL_USER_ALIGN (retval)
+ 	  && same_type_p (TREE_TYPE (retval),
+ 			  TREE_TYPE (TREE_TYPE (current_function_decl))))
+ 	current_function_return_value = retval;
+       else
+ 	current_function_return_value = error_mark_node;
+     }
+ 
    /* We don't need to do any conversions when there's nothing being
       returned.  */
    if (!retval || retval == error_mark_node)


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]