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 NRVO with inlines


This patch extends my previous patch to work with inlined functions, by
setting the inliner up to replace the chosen NRV with the same VAR_DECL
that it uses for the RETURN_DECL.

The optimization will now change the trees for the function
to nullify returns and cleanups for the NRV, so that the later pieces which
unify the RETURN_DECL and the NRV are necessary for semantic correctness;
previously we only changed how the trees were expanded, not the trees
themselves.  I considered continuing with that, hooking also into
copy_body_r to avoid expanding RETURN_STMTs and CLEANUP_STMTs as
appropriate, but that didn't seem any cleaner than this approach.

Gaby's earlier patch that he sent me worked by methodically replacing all
instances of the NRV in the function with the RETURN_DECL; I wasn't
comfortable with that approach because it breaks debugging information for
the NRV.  I want to leave the chosen VAR_DECL where it is.

gcc/
2001-08-08  Jason Merrill  <jason_merrill@redhat.com>

	* c-common.h (RETURN_NULLIFIED_P): Lose.
	* c-semantics.c (genrtl_return_stmt): Don't check it.

gcc/cp/
2001-08-07  Jason Merrill  <jason_merrill@redhat.com>

	Support named return value optimization for inlines, too.
	* decl.c (finish_function): Nullify returns here.
	* semantics.c (genrtl_start_function): Not here.
	(cp_expand_stmt): Don't mess with CLEANUP_STMTs.
	(nullify_returns_r): No longer static.  Just clear RETURN_EXPR.
	Also nullify the CLEANUP_STMT for the nrv.
	* cp-tree.h: Declare it.
	* optimize.c (declare_return_variable): Replace the nrv with the
	return variable.
	* typeck.c (check_return_expr): Be more flexible on alignment check.
	Ignore cv-quals when checking for a matching type.

*** ./cp/cp-tree.h.~1~	Tue Aug  7 16:45:08 2001
--- ./cp/cp-tree.h	Tue Aug  7 16:42:36 2001
*************** extern tree finish_typeof			PARAMS ((tre
*** 4129,4134 ****
--- 4129,4135 ----
  extern void finish_decl_cleanup                 PARAMS ((tree, tree));
  extern void finish_named_return_value           PARAMS ((tree, tree));
  extern void expand_body                         PARAMS ((tree));
+ extern tree nullify_returns_r		      PARAMS ((tree *, int *, void *));
  extern void do_pushlevel                        PARAMS ((void));
  extern tree do_poplevel                         PARAMS ((void));
  extern void finish_mem_initializers             PARAMS ((tree));
*** ./cp/decl.c.~1~	Tue Aug  7 16:45:08 2001
--- ./cp/decl.c	Tue Aug  7 13:52:29 2001
*************** finish_function (flags)
*** 13955,13960 ****
--- 13955,13982 ----
      my_friendly_abort (122);
    poplevel (1, 0, 1);
  
+   /* Set up the named return value optimization, if we can.  Here, we
+      eliminate the copy from the nrv into the RESULT_DECL and any cleanup
+      for the nrv.  genrtl_start_function and declare_return_variable
+      handle making the nrv and RESULT_DECL share space.  */
+   if (current_function_return_value)
+     {
+       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 (r != error_mark_node
+ 	  && aggregate_value_p (TREE_TYPE (TREE_TYPE (fndecl))))
+ 	{
+ 	  DECL_ALIGN (r) = DECL_ALIGN (DECL_RESULT (fndecl));
+ 	  walk_tree_without_duplicates (&DECL_SAVED_TREE (fndecl),
+ 					nullify_returns_r, r);
+ 	}
+       else
+ 	/* Clear it so genrtl_start_function and declare_return_variable
+ 	   know we're not optimizing.  */
+ 	current_function_return_value = NULL_TREE;
+     }
+ 
    /* Remember that we were in class scope.  */
    if (current_class_name)
      ctype = current_class_type;
*** ./cp/optimize.c.~1~	Tue Aug  7 16:45:08 2001
--- ./cp/optimize.c	Tue Aug  7 12:49:00 2001
*************** declare_return_variable (id, use_stmt)
*** 602,607 ****
--- 602,624 ----
  		     (splay_tree_key) result,
  		     (splay_tree_value) var);
  
+   if (DECL_SAVED_FUNCTION_DATA (fn))
+     {
+       tree nrv = DECL_SAVED_FUNCTION_DATA (fn)->x_return_value;
+       if (nrv)
+ 	{
+ 	  /* We have a named return value; copy the name and source
+ 	     position so we can get reasonable debugging information, and
+ 	     register the return variable as its equivalent.  */
+ 	  DECL_NAME (var) = DECL_NAME (nrv);
+ 	  DECL_SOURCE_FILE (var) = DECL_SOURCE_FILE (nrv);
+ 	  DECL_SOURCE_LINE (var) = DECL_SOURCE_LINE (nrv);
+ 	  splay_tree_insert (id->decl_map,
+ 			     (splay_tree_key) nrv,
+ 			     (splay_tree_value) var);
+ 	}
+     }
+ 
    /* Build the USE_STMT.  */
    *use_stmt = build_stmt (EXPR_STMT, var);
  
*** ./cp/typeck.c.~1~	Tue Aug  7 16:45:08 2001
--- ./cp/typeck.c	Tue Aug  7 12:49:00 2001
*************** check_return_expr (retval)
*** 6668,6682 ****
        && 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 && flag_elide_constructors)
      {
--- 6668,6692 ----
        && retval != current_class_ref)
      cp_warning ("`operator=' should return a reference to `*this'");
  
!   /* The fabled Named Return Value optimization, as per [class.copy]/15:
! 
!      [...]      For  a function with a class return type, if the expression
!      in the return statement is the name of a local  object,  and  the  cv-
!      unqualified  type  of  the  local  object  is the same as the function
!      return type, an implementation is permitted to omit creating the  tem-
!      porary  object  to  hold  the function return value [...]
! 
!      So, 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)
! 
!      See finish_function, genrtl_start_function, and declare_return_variable
!      for other pieces of this optimization.  */
  
    if (fn_returns_value_p && flag_elide_constructors)
      {
*************** check_return_expr (retval)
*** 6687,6695 ****
  	  && DECL_CONTEXT (retval) == current_function_decl
  	  && ! TREE_STATIC (retval)
  	  && (DECL_ALIGN (retval)
! 	      == DECL_ALIGN (DECL_RESULT (current_function_decl)))
! 	  && 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;
--- 6697,6707 ----
  	  && DECL_CONTEXT (retval) == current_function_decl
  	  && ! TREE_STATIC (retval)
  	  && (DECL_ALIGN (retval)
! 	      >= DECL_ALIGN (DECL_RESULT (current_function_decl)))
! 	  && same_type_p ((TYPE_MAIN_VARIANT
! 			   (TREE_TYPE (retval))),
! 			  (TYPE_MAIN_VARIANT
! 			   (TREE_TYPE (TREE_TYPE (current_function_decl))))))
  	current_function_return_value = retval;
        else
  	current_function_return_value = error_mark_node;
*** ./cp/semantics.c.~1~	Tue Aug  7 16:45:08 2001
--- ./cp/semantics.c	Tue Aug  7 13:52:32 2001
***************
*** 50,56 ****
  
  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));
--- 50,55 ----
*************** cp_expand_stmt (t)
*** 2196,2206 ****
    switch (TREE_CODE (t))
      {
      case CLEANUP_STMT:
!       if (CLEANUP_DECL (t)
! 	  && CLEANUP_DECL (t) == current_function_return_value)
! 	/* Don't destroy the chosen named return value.  */;
!       else
! 	genrtl_decl_cleanup (CLEANUP_DECL (t), CLEANUP_EXPR (t));
        break;
  
      case CTOR_STMT:
--- 2195,2201 ----
    switch (TREE_CODE (t))
      {
      case CLEANUP_STMT:
!       genrtl_decl_cleanup (CLEANUP_DECL (t), CLEANUP_EXPR (t));
        break;
  
      case CTOR_STMT:
*************** expand_body (fn)
*** 2504,2523 ****
    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;
--- 2499,2524 ----
    timevar_pop (TV_EXPAND);
  }
  
! /* Helper function for walk_tree, used by finish_function to override all
!    the RETURN_STMTs and pertinent CLEANUP_STMTs for the named return
!    value optimization.  */
  
! tree
  nullify_returns_r (tp, walk_subtrees, data)
       tree *tp;
       int *walk_subtrees;
!      void *data;
  {
+   tree nrv = (tree) data;
+ 
    /* No need to walk into types.  */
    if (TYPE_P (*tp))
      *walk_subtrees = 0;
    else if (TREE_CODE (*tp) == RETURN_STMT)
!     RETURN_EXPR (*tp) = NULL_TREE;
!   else if (TREE_CODE (*tp) == CLEANUP_STMT
! 	   && CLEANUP_DECL (*tp) == nrv)
!     CLEANUP_EXPR (*tp) = NULL_TREE;
  
    /* Keep iterating.  */
    return NULL_TREE;
*************** genrtl_start_function (fn)
*** 2601,2621 ****
       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.  */
--- 2602,2610 ----
       cleanup-generated temporaries.  */
    expand_start_bindings (2);
  
!   /* Give our named return value the same RTL as our RESULT_DECL.  */
!   if (current_function_return_value)
!     COPY_DECL_RTL (DECL_RESULT (fn), current_function_return_value);
  }
  
  /* Finish generating the RTL for FN.  */
*** ./c-common.h.~1~	Tue Aug  7 16:45:08 2001
--- ./c-common.h	Fri Jul 27 12:58:07 2001
*************** Boston, MA 02111-1307, USA.  */
*** 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)
--- 32,37 ----
*************** extern tree strip_array_types           
*** 597,603 ****
     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. */
--- 596,601 ----
*** ./c-semantics.c.~1~	Tue Aug  7 16:45:08 2001
--- ./c-semantics.c	Tue Aug  7 12:21:19 2001
*************** genrtl_return_stmt (stmt)
*** 462,474 ****
  {
    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)
--- 462,468 ----
  {
    tree expr;
  
!   expr = RETURN_EXPR (stmt);
  
    emit_line_note (input_filename, lineno);
    if (!expr)

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