+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
/* 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 *);
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);
/* 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))
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);
}
\f
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;
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;
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;
}
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