This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
C++ PATCH for named return value optimization
- To: gcc-patches at gcc dot gnu dot org
- Subject: C++ PATCH for named return value optimization
- From: Jason Merrill <jason_merrill at redhat dot com>
- Date: 18 Jun 2001 17:10:01 +0100
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 (¤t_function_return_value, 1);
ggc_add_tree_root (¤t_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)