From 7187798577a9a9035d1d492d1686217ff5ca30d7 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 8 Jun 2004 09:29:57 -0700 Subject: [PATCH] gimple-low.c (struct lower_data): Replace the_return_label and one_return_stmt with return_statements. * gimple-low.c (struct lower_data): Replace the_return_label and one_return_stmt with return_statements. (lower_function_body): Process the entire list of return_statements. (lower_return_expr): Check source value before unifying return_exprs. * gimplify.c (gimplify_return_expr): Force the use of a temporary for !aggregate_value_p. * tree-gimple.c: Update RETURN_EXPR grammer. From-SVN: r82768 --- gcc/ChangeLog | 10 ++++++++ gcc/gimple-low.c | 62 +++++++++++++++++++++++++++++++++++------------ gcc/gimplify.c | 42 ++++++++++++++++++++++++++------ gcc/tree-gimple.c | 4 ++- 4 files changed, 94 insertions(+), 24 deletions(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 56ff307aead..1f8964f31b6 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,13 @@ +2004-06-08 Richard Henderson + + * gimple-low.c (struct lower_data): Replace the_return_label and + one_return_stmt with return_statements. + (lower_function_body): Process the entire list of return_statements. + (lower_return_expr): Check source value before unifying return_exprs. + * gimplify.c (gimplify_return_expr): Force the use of a temporary + for !aggregate_value_p. + * tree-gimple.c: Update RETURN_EXPR grammer. + 2004-06-08 Vladimir Makarov PR target/15598 diff --git a/gcc/gimple-low.c b/gcc/gimple-low.c index 458980f898f..aac3341b8cc 100644 --- a/gcc/gimple-low.c +++ b/gcc/gimple-low.c @@ -47,9 +47,9 @@ struct lower_data /* Block the current statement belongs to. */ tree block; - /* Label that unifies the return statements. */ - tree the_return_label; - tree one_return_stmt; + /* A TREE_LIST of label and return statements to be moved to the end + of the function. */ + tree return_statements; }; static void lower_stmt (tree_stmt_iterator *, struct lower_data *); @@ -76,8 +76,7 @@ lower_function_body (void) BLOCK_CHAIN (data.block) = NULL_TREE; TREE_ASM_WRITTEN (data.block) = 1; - data.the_return_label = NULL_TREE; - data.one_return_stmt = NULL_TREE; + data.return_statements = NULL_TREE; *body_p = alloc_stmt_list (); i = tsi_start (*body_p); @@ -86,13 +85,23 @@ lower_function_body (void) /* If we lowered any return statements, emit the representative at the end of the function. */ - if (data.one_return_stmt) + if (data.return_statements) { - tree t; - t = build (LABEL_EXPR, void_type_node, data.the_return_label); + tree t, x; i = tsi_last (*body_p); - tsi_link_after (&i, t, TSI_CONTINUE_LINKING); - tsi_link_after (&i, data.one_return_stmt, TSI_CONTINUE_LINKING); + + for (t = data.return_statements; t ; t = TREE_CHAIN (t)) + { + x = build (LABEL_EXPR, void_type_node, TREE_PURPOSE (t)); + tsi_link_after (&i, x, TSI_CONTINUE_LINKING); + + /* Remove the line number from the representative return statement. + It now fills in for many such returns. Failure to remove this + will result in incorrect results for coverage analysis. */ + x = TREE_VALUE (t); + SET_EXPR_LOCUS (x, NULL); + tsi_link_after (&i, x, TSI_CONTINUE_LINKING); + } } if (data.block != DECL_INITIAL (current_function_decl)) @@ -392,16 +401,37 @@ lower_cond_expr (tree_stmt_iterator *tsi, struct lower_data *data) static void lower_return_expr (tree_stmt_iterator *tsi, struct lower_data *data) { - tree stmt, label = data->the_return_label; + tree stmt = tsi_stmt (*tsi); + tree value, t, label; + + /* Extract the value being returned. */ + value = TREE_OPERAND (stmt, 0); + if (value && TREE_CODE (value) == MODIFY_EXPR) + value = TREE_OPERAND (value, 1); - if (!label) + /* Match this up with an existing return statement that's been created. */ + for (t = data->return_statements; t ; t = TREE_CHAIN (t)) { - data->the_return_label = label = create_artificial_label (); - data->one_return_stmt = tsi_stmt (*tsi); + tree tvalue = TREE_OPERAND (TREE_VALUE (t), 0); + if (tvalue && TREE_CODE (tvalue) == MODIFY_EXPR) + tvalue = TREE_OPERAND (tvalue, 1); + + if (value == tvalue) + { + label = TREE_PURPOSE (t); + goto found; + } } - stmt = build (GOTO_EXPR, void_type_node, label); - tsi_link_before (tsi, stmt, TSI_SAME_STMT); + /* Not found. Create a new label and record the return statement. */ + label = create_artificial_label (); + data->return_statements = tree_cons (label, stmt, data->return_statements); + + /* Generate a goto statement and remove the return statement. */ + found: + t = build (GOTO_EXPR, void_type_node, label); + SET_EXPR_LOCUS (t, EXPR_LOCUS (stmt)); + tsi_link_before (tsi, t, TSI_SAME_STMT); tsi_delink (tsi); } diff --git a/gcc/gimplify.c b/gcc/gimplify.c index 679103e31c1..c6378aa4d29 100644 --- a/gcc/gimplify.c +++ b/gcc/gimplify.c @@ -54,6 +54,7 @@ static struct gimplify_ctx tree conditional_cleanups; int conditions; tree exit_label; + tree return_temp; varray_type case_labels; /* The formal temporary table. Should this be persistent? */ htab_t temp_htab; @@ -888,7 +889,7 @@ static enum gimplify_status gimplify_return_expr (tree stmt, tree *pre_p) { tree ret_expr = TREE_OPERAND (stmt, 0); - tree result; + tree result_decl, result; if (!ret_expr || TREE_CODE (ret_expr) == RESULT_DECL) return GS_ALL_DONE; @@ -897,24 +898,51 @@ gimplify_return_expr (tree stmt, tree *pre_p) return GS_ERROR; if (VOID_TYPE_P (TREE_TYPE (TREE_TYPE (current_function_decl)))) - result = NULL_TREE; + result_decl = NULL_TREE; else { - result = TREE_OPERAND (ret_expr, 0); + result_decl = TREE_OPERAND (ret_expr, 0); #ifdef ENABLE_CHECKING if ((TREE_CODE (ret_expr) != MODIFY_EXPR && TREE_CODE (ret_expr) != INIT_EXPR) - || TREE_CODE (result) != RESULT_DECL) + || TREE_CODE (result_decl) != RESULT_DECL) abort (); #endif } - /* We need to pass the full MODIFY_EXPR down so that special handling - can replace it with something else. */ + /* If aggregate_value_p is true, then we can return the bare RESULT_DECL. + Recall that aggregate_value_p is FALSE for any aggregate type that is + returned in registers. If we're returning values in registers, then + we don't want to extend the lifetime of the RESULT_DECL, particularly + across another call. In addition, for those aggregates for which + hard_function_value generates a PARALLEL, we'll abort during normal + expansion of structure assignments; there's special code in expand_return + to handle this case that does not exist in expand_expr. */ + if (!result_decl + || aggregate_value_p (result_decl, TREE_TYPE (current_function_decl))) + result = result_decl; + else if (gimplify_ctxp->return_temp) + result = gimplify_ctxp->return_temp; + else + { + result = create_tmp_var (TREE_TYPE (result_decl), NULL); + gimplify_ctxp->return_temp = result; + } + + /* Smash the lhs of the MODIFY_EXPR to the temporary we plan to use. + Then gimplify the whole thing. */ + if (result != result_decl) + TREE_OPERAND (ret_expr, 0) = result; gimplify_stmt (&TREE_OPERAND (stmt, 0)); append_to_statement_list (TREE_OPERAND (stmt, 0), pre_p); - TREE_OPERAND (stmt, 0) = result; + /* If we didn't use a temporary, then the result is just the result_decl. + Otherwise we need a simple copy. This should already be gimple. */ + if (result == result_decl) + ret_expr = result; + else + ret_expr = build (MODIFY_EXPR, TREE_TYPE (result), result_decl, result); + TREE_OPERAND (stmt, 0) = ret_expr; return GS_ALL_DONE; } diff --git a/gcc/tree-gimple.c b/gcc/tree-gimple.c index 4643ac652f4..d3231c01fb6 100644 --- a/gcc/tree-gimple.c +++ b/gcc/tree-gimple.c @@ -79,7 +79,9 @@ Boston, MA 02111-1307, USA. */ GOTO_EXPR op0 -> LABEL_DECL | '*' ID | RETURN_EXPR - op0 -> RESULT_DECL | NULL_TREE + op0 -> NULL_TREE + | RESULT_DECL + | MODIFY_EXPR -> RESULT_DECL, varname | THROW_EXPR? do we need/want such a thing for opts, perhaps to generate an ERT_THROW region? I think so. Hmm...this would only work at the GIMPLE level, where we know that -- 2.30.2