mirror of
git://gcc.gnu.org/git/gcc.git
synced 2025-02-23 00:10:05 +08:00
re PR c++/19317 (removing a temporary return value when we cannot)
PR c++/19317 Leave the return slot target in the MODIFY_EXPR rather than making it an argument, but only use it if the CALL_EXPR has a flag set. * tree.h (CALL_EXPR_HAS_RETURN_SLOT_ADDR): Rename to CALL_EXPR_RETURN_SLOT_OPT. * calls.c (expand_call): Adjust. * tree-inline.c (expand_call_inline): Adjust. * tree-pretty-print.c (dump_generic_node): Adjust. And set the flag as appropriate. * gimplify.c (gimplify_modify_expr_rhs): Set CALL_EXPR_HAS_RETURN_SLOT_ADDR where the LHS is obviously safe. * tree-nrv.c (execute_return_slot_opt): Set CALL_EXPR_HAS_RETURN_SLOT_ADDR based on escape analysis. * tree-pass.h: Declare pass_return_slot. * tree-optimize.c (init_tree_optimization_passes): Add it. * cp/semantics.c (simplify_aggr_init_expr): Use CALL_EXPR_RETURN_SLOT_OPT, not CALL_EXPR_HAS_RETURN_SLOT_ADDR. From-SVN: r101269
This commit is contained in:
parent
f565b0a1a5
commit
fa47911c56
@ -1,3 +1,22 @@
|
||||
2005-06-23 Jason Merrill <jason@redhat.com>
|
||||
|
||||
PR c++/19317
|
||||
Leave the return slot target in the MODIFY_EXPR rather than making
|
||||
it an argument, but only use it if the CALL_EXPR has a flag set.
|
||||
* tree.h (CALL_EXPR_HAS_RETURN_SLOT_ADDR): Rename to
|
||||
CALL_EXPR_RETURN_SLOT_OPT.
|
||||
* calls.c (expand_call): Adjust.
|
||||
* tree-inline.c (expand_call_inline): Adjust.
|
||||
* tree-pretty-print.c (dump_generic_node): Adjust.
|
||||
|
||||
And set the flag as appropriate.
|
||||
* gimplify.c (gimplify_modify_expr_rhs): Set
|
||||
CALL_EXPR_HAS_RETURN_SLOT_ADDR where the LHS is obviously safe.
|
||||
* tree-nrv.c (execute_return_slot_opt): Set
|
||||
CALL_EXPR_HAS_RETURN_SLOT_ADDR based on escape analysis.
|
||||
* tree-pass.h: Declare pass_return_slot.
|
||||
* tree-optimize.c (init_tree_optimization_passes): Add it.
|
||||
|
||||
2005-06-23 David Edelsohn <edelsohn@gnu.org>
|
||||
|
||||
PR target/21760
|
||||
|
12
gcc/calls.c
12
gcc/calls.c
@ -1964,17 +1964,7 @@ expand_call (tree exp, rtx target, int ignore)
|
||||
{
|
||||
struct_value_size = int_size_in_bytes (TREE_TYPE (exp));
|
||||
|
||||
if (CALL_EXPR_HAS_RETURN_SLOT_ADDR (exp))
|
||||
{
|
||||
/* The structure value address arg is already in actparms.
|
||||
Pull it out. It might be nice to just leave it there, but
|
||||
we need to set structure_value_addr. */
|
||||
tree return_arg = TREE_VALUE (actparms);
|
||||
actparms = TREE_CHAIN (actparms);
|
||||
structure_value_addr = expand_expr (return_arg, NULL_RTX,
|
||||
VOIDmode, EXPAND_NORMAL);
|
||||
}
|
||||
else if (target && MEM_P (target))
|
||||
if (target && MEM_P (target) && CALL_EXPR_RETURN_SLOT_OPT (exp))
|
||||
structure_value_addr = XEXP (target, 0);
|
||||
else
|
||||
{
|
||||
|
@ -1,3 +1,9 @@
|
||||
2005-06-23 Jason Merrill <jason@redhat.com>
|
||||
|
||||
PR c++/19317
|
||||
* semantics.c (simplify_aggr_init_expr): Use
|
||||
CALL_EXPR_RETURN_SLOT_OPT, not CALL_EXPR_HAS_RETURN_SLOT_ADDR.
|
||||
|
||||
2005-06-23 Volker Reichelt <reichelt@igpm.rwth-aachen.de>
|
||||
|
||||
* pt.c (register_specialization): Remove superfluous assertion.
|
||||
|
@ -2904,27 +2904,15 @@ simplify_aggr_init_expr (tree *tp)
|
||||
style = arg;
|
||||
}
|
||||
|
||||
if (style == ctor || style == arg)
|
||||
if (style == ctor)
|
||||
{
|
||||
/* Pass the address of the slot. If this is a constructor, we
|
||||
replace the first argument; otherwise, we tack on a new one. */
|
||||
/* Replace the first argument to the ctor with the address of the
|
||||
slot. */
|
||||
tree addr;
|
||||
|
||||
if (style == ctor)
|
||||
args = TREE_CHAIN (args);
|
||||
|
||||
args = TREE_CHAIN (args);
|
||||
cxx_mark_addressable (slot);
|
||||
addr = build1 (ADDR_EXPR, build_pointer_type (type), slot);
|
||||
if (style == arg)
|
||||
{
|
||||
/* The return type might have different cv-quals from the slot. */
|
||||
tree fntype = TREE_TYPE (TREE_TYPE (fn));
|
||||
|
||||
gcc_assert (TREE_CODE (fntype) == FUNCTION_TYPE
|
||||
|| TREE_CODE (fntype) == METHOD_TYPE);
|
||||
addr = convert (build_pointer_type (TREE_TYPE (fntype)), addr);
|
||||
}
|
||||
|
||||
args = tree_cons (NULL_TREE, addr, args);
|
||||
}
|
||||
|
||||
@ -2933,9 +2921,13 @@ simplify_aggr_init_expr (tree *tp)
|
||||
fn, args, NULL_TREE);
|
||||
|
||||
if (style == arg)
|
||||
/* Tell the backend that we've added our return slot to the argument
|
||||
list. */
|
||||
CALL_EXPR_HAS_RETURN_SLOT_ADDR (call_expr) = 1;
|
||||
{
|
||||
/* Just mark it addressable here, and leave the rest to
|
||||
expand_call{,_inline}. */
|
||||
cxx_mark_addressable (slot);
|
||||
CALL_EXPR_RETURN_SLOT_OPT (call_expr) = true;
|
||||
call_expr = build2 (MODIFY_EXPR, TREE_TYPE (call_expr), slot, call_expr);
|
||||
}
|
||||
else if (style == pcc)
|
||||
{
|
||||
/* If we're using the non-reentrant PCC calling convention, then we
|
||||
@ -2945,6 +2937,7 @@ simplify_aggr_init_expr (tree *tp)
|
||||
call_expr = build_aggr_init (slot, call_expr,
|
||||
DIRECT_BIND | LOOKUP_ONLYCONVERTING);
|
||||
pop_deferring_access_checks ();
|
||||
call_expr = build (COMPOUND_EXPR, TREE_TYPE (slot), call_expr, slot);
|
||||
}
|
||||
|
||||
*tp = call_expr;
|
||||
|
@ -3015,6 +3015,46 @@ gimplify_modify_expr_rhs (tree *expr_p, tree *from_p, tree *to_p, tree *pre_p,
|
||||
ret = GS_UNHANDLED;
|
||||
break;
|
||||
|
||||
case CALL_EXPR:
|
||||
/* For calls that return in memory, give *to_p as the CALL_EXPR's
|
||||
return slot so that we don't generate a temporary. */
|
||||
if (!CALL_EXPR_RETURN_SLOT_OPT (*from_p)
|
||||
&& aggregate_value_p (*from_p, *from_p))
|
||||
{
|
||||
bool use_target;
|
||||
|
||||
if (TREE_CODE (*to_p) == RESULT_DECL
|
||||
&& needs_to_live_in_memory (*to_p))
|
||||
/* It's always OK to use the return slot directly. */
|
||||
use_target = true;
|
||||
else if (!is_gimple_non_addressable (*to_p))
|
||||
/* Don't use the original target if it's already addressable;
|
||||
if its address escapes, and the called function uses the
|
||||
NRV optimization, a conforming program could see *to_p
|
||||
change before the called function returns; see c++/19317.
|
||||
When optimizing, the return_slot pass marks more functions
|
||||
as safe after we have escape info. */
|
||||
use_target = false;
|
||||
else if (DECL_GIMPLE_FORMAL_TEMP_P (*to_p))
|
||||
/* Don't use the original target if it's a formal temp; we
|
||||
don't want to take their addresses. */
|
||||
use_target = false;
|
||||
else if (is_gimple_reg_type (TREE_TYPE (*to_p)))
|
||||
/* Also don't force regs into memory. */
|
||||
use_target = false;
|
||||
else
|
||||
use_target = true;
|
||||
|
||||
if (use_target)
|
||||
{
|
||||
CALL_EXPR_RETURN_SLOT_OPT (*from_p) = 1;
|
||||
lang_hooks.mark_addressable (*to_p);
|
||||
}
|
||||
}
|
||||
|
||||
ret = GS_UNHANDLED;
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = GS_UNHANDLED;
|
||||
break;
|
||||
|
28
gcc/testsuite/g++.dg/opt/nrv9.C
Normal file
28
gcc/testsuite/g++.dg/opt/nrv9.C
Normal file
@ -0,0 +1,28 @@
|
||||
// PR c++/19317
|
||||
// If we do both NRV and caller-side return slot opt for ma = ma.f,
|
||||
// constructing fa incorrectly sets ma.i to 0.
|
||||
|
||||
extern "C" void abort();
|
||||
|
||||
struct A
|
||||
{
|
||||
int i;
|
||||
int pad[32]; // force return in memory
|
||||
A(): i(0) {}
|
||||
A(int ia): i(ia) {}
|
||||
};
|
||||
|
||||
A ga(42);
|
||||
|
||||
A f()
|
||||
{
|
||||
A la;
|
||||
if (ga.i != 42)
|
||||
abort();
|
||||
return la;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
ga = f ();
|
||||
}
|
@ -2026,13 +2026,6 @@ expand_call_inline (basic_block bb, tree stmt, tree *tp, void *data)
|
||||
|
||||
/* Initialize the parameters. */
|
||||
args = TREE_OPERAND (t, 1);
|
||||
if (CALL_EXPR_HAS_RETURN_SLOT_ADDR (t))
|
||||
{
|
||||
return_slot_addr = TREE_VALUE (args);
|
||||
args = TREE_CHAIN (args);
|
||||
}
|
||||
else
|
||||
return_slot_addr = NULL_TREE;
|
||||
|
||||
initialize_inlined_parameters (id, args, TREE_OPERAND (t, 2), fn, bb);
|
||||
|
||||
@ -2046,10 +2039,10 @@ expand_call_inline (basic_block bb, tree stmt, tree *tp, void *data)
|
||||
gcc_assert (TREE_CODE (DECL_INITIAL (fn)) == BLOCK);
|
||||
|
||||
/* Find the lhs to which the result of this call is assigned. */
|
||||
modify_dest = stmt;
|
||||
if (TREE_CODE (modify_dest) == MODIFY_EXPR)
|
||||
return_slot_addr = NULL;
|
||||
if (TREE_CODE (stmt) == MODIFY_EXPR)
|
||||
{
|
||||
modify_dest = TREE_OPERAND (modify_dest, 0);
|
||||
modify_dest = TREE_OPERAND (stmt, 0);
|
||||
|
||||
/* The function which we are inlining might not return a value,
|
||||
in which case we should issue a warning that the function
|
||||
@ -2059,6 +2052,11 @@ expand_call_inline (basic_block bb, tree stmt, tree *tp, void *data)
|
||||
uninitialized variable. */
|
||||
if (DECL_P (modify_dest))
|
||||
TREE_NO_WARNING (modify_dest) = 1;
|
||||
if (CALL_EXPR_RETURN_SLOT_OPT (t))
|
||||
{
|
||||
return_slot_addr = build_fold_addr_expr (modify_dest);
|
||||
modify_dest = NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
modify_dest = NULL;
|
||||
|
@ -218,3 +218,79 @@ struct tree_opt_pass pass_nrv =
|
||||
TODO_dump_func | TODO_ggc_collect, /* todo_flags_finish */
|
||||
0 /* letter */
|
||||
};
|
||||
|
||||
/* Walk through the function looking for MODIFY_EXPRs with calls that
|
||||
return in memory on the RHS. For each of these, determine whether it is
|
||||
safe to pass the address of the LHS as the return slot, and mark the
|
||||
call appropriately if so.
|
||||
|
||||
The NRV shares the return slot with a local variable in the callee; this
|
||||
optimization shares the return slot with the target of the call within
|
||||
the caller. If the NRV is performed (which we can't know in general),
|
||||
this optimization is safe if the address of the target has not
|
||||
escaped prior to the call. If it has, modifications to the local
|
||||
variable will produce visible changes elsewhere, as in PR c++/19317. */
|
||||
|
||||
static void
|
||||
execute_return_slot_opt (void)
|
||||
{
|
||||
basic_block bb;
|
||||
|
||||
FOR_EACH_BB (bb)
|
||||
{
|
||||
block_stmt_iterator i;
|
||||
for (i = bsi_start (bb); !bsi_end_p (i); bsi_next (&i))
|
||||
{
|
||||
tree stmt = bsi_stmt (i);
|
||||
tree call;
|
||||
|
||||
if (TREE_CODE (stmt) == MODIFY_EXPR
|
||||
&& (call = TREE_OPERAND (stmt, 1),
|
||||
TREE_CODE (call) == CALL_EXPR)
|
||||
&& !CALL_EXPR_RETURN_SLOT_OPT (call)
|
||||
&& aggregate_value_p (call, call))
|
||||
{
|
||||
def_operand_p def_p;
|
||||
ssa_op_iter op_iter;
|
||||
|
||||
/* We determine whether or not the LHS address escapes by
|
||||
asking whether it is call clobbered. When the LHS isn't a
|
||||
simple decl, we need to check the VDEFs, so it's simplest
|
||||
to just loop through all the DEFs. */
|
||||
FOR_EACH_SSA_DEF_OPERAND (def_p, stmt, op_iter, SSA_OP_ALL_DEFS)
|
||||
{
|
||||
tree def = DEF_FROM_PTR (def_p);
|
||||
if (TREE_CODE (def) == SSA_NAME)
|
||||
def = SSA_NAME_VAR (def);
|
||||
if (is_call_clobbered (def))
|
||||
goto unsafe;
|
||||
}
|
||||
|
||||
/* No defs are call clobbered, so the optimization is safe. */
|
||||
CALL_EXPR_RETURN_SLOT_OPT (call) = 1;
|
||||
/* This is too late to mark the target addressable like we do
|
||||
in gimplify_modify_expr_rhs, but that's OK; anything that
|
||||
wasn't already addressable was handled there. */
|
||||
|
||||
unsafe:;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct tree_opt_pass pass_return_slot =
|
||||
{
|
||||
"retslot", /* name */
|
||||
NULL, /* gate */
|
||||
execute_return_slot_opt, /* execute */
|
||||
NULL, /* sub */
|
||||
NULL, /* next */
|
||||
0, /* static_pass_number */
|
||||
0, /* tv_id */
|
||||
PROP_ssa | PROP_alias, /* properties_required */
|
||||
0, /* properties_provided */
|
||||
0, /* properties_destroyed */
|
||||
0, /* todo_flags_start */
|
||||
0, /* todo_flags_finish */
|
||||
0 /* letter */
|
||||
};
|
||||
|
@ -397,6 +397,7 @@ init_tree_optimization_passes (void)
|
||||
NEXT_PASS (pass_build_ssa);
|
||||
NEXT_PASS (pass_build_pta);
|
||||
NEXT_PASS (pass_may_alias);
|
||||
NEXT_PASS (pass_return_slot);
|
||||
NEXT_PASS (pass_del_pta);
|
||||
NEXT_PASS (pass_rename_ssa_copies);
|
||||
NEXT_PASS (pass_early_warn_uninitialized);
|
||||
|
@ -224,6 +224,7 @@ extern struct tree_opt_pass pass_create_structure_vars;
|
||||
extern struct tree_opt_pass pass_build_pta;
|
||||
extern struct tree_opt_pass pass_del_pta;
|
||||
extern struct tree_opt_pass pass_uncprop;
|
||||
extern struct tree_opt_pass pass_return_slot;
|
||||
extern struct tree_opt_pass pass_reassoc;
|
||||
|
||||
/* IPA Passes */
|
||||
|
@ -1003,8 +1003,8 @@ dump_generic_node (pretty_printer *buffer, tree node, int spc, int flags,
|
||||
pp_character (buffer, ']');
|
||||
}
|
||||
|
||||
if (CALL_EXPR_HAS_RETURN_SLOT_ADDR (node))
|
||||
pp_string (buffer, " [return slot addr]");
|
||||
if (CALL_EXPR_RETURN_SLOT_OPT (node))
|
||||
pp_string (buffer, " [return slot optimization]");
|
||||
if (CALL_EXPR_TAILCALL (node))
|
||||
pp_string (buffer, " [tail call]");
|
||||
break;
|
||||
|
@ -337,7 +337,7 @@ struct tree_common GTY(())
|
||||
|
||||
TREE_PRIVATE in
|
||||
..._DECL
|
||||
CALL_EXPR_HAS_RETURN_SLOT_ADDR in
|
||||
CALL_EXPR_RETURN_SLOT_OPT in
|
||||
CALL_EXPR
|
||||
DECL_BY_REFERENCE in
|
||||
PARM_DECL, RESULT_DECL
|
||||
@ -980,9 +980,9 @@ extern void tree_operand_check_failed (int, enum tree_code,
|
||||
an exception. In a CALL_EXPR, nonzero means the call cannot throw. */
|
||||
#define TREE_NOTHROW(NODE) ((NODE)->common.nothrow_flag)
|
||||
|
||||
/* In a CALL_EXPR, means that the address of the return slot is part of the
|
||||
argument list. */
|
||||
#define CALL_EXPR_HAS_RETURN_SLOT_ADDR(NODE) ((NODE)->common.private_flag)
|
||||
/* In a CALL_EXPR, means that it's safe to use the target of the call
|
||||
expansion as the return slot for a call that returns in memory. */
|
||||
#define CALL_EXPR_RETURN_SLOT_OPT(NODE) ((NODE)->common.private_flag)
|
||||
|
||||
/* In a RESULT_DECL or PARM_DECL, means that it is passed by invisible
|
||||
reference (and the TREE_TYPE is a pointer to the true type). */
|
||||
|
Loading…
Reference in New Issue
Block a user