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
This commit is contained in:
Richard Henderson 2004-06-08 09:29:57 -07:00 committed by Richard Henderson
parent de101ad2f7
commit 7187798577
4 changed files with 94 additions and 24 deletions

View File

@ -1,3 +1,13 @@
2004-06-08 Richard Henderson <rth@redhat.com>
* 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 <vmakarov@redhat.com>
PR target/15598

View File

@ -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;
if (!label)
/* Extract the value being returned. */
value = TREE_OPERAND (stmt, 0);
if (value && TREE_CODE (value) == MODIFY_EXPR)
value = TREE_OPERAND (value, 1);
/* 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);
}

View File

@ -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;
}

View File

@ -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