Commit 6d729f28 by Jason Merrill Committed by Jason Merrill

re PR c++/43787 (memory copy of empty class (sizeof is one))

	PR c++/43787
gcc:
	* gimplify.c (gimplify_expr): Keep working if gimplify_modify_expr
	returns GS_OK.
	(gimplify_modify_expr_rhs): Return GS_OK if anything changed.
gcc/cp:
	* cp-gimplify.c (cp_gimplify_expr): Remove copies of empty classes.
	* call.c (build_over_call): Don't try to avoid INIT_EXPR copies here.

From-SVN: r159072
parent a2c9b836
2010-05-05 Jason Merrill <jason@redhat.com>
PR c++/43787
* gimplify.c (gimplify_expr): Keep working if gimplify_modify_expr
returns GS_OK.
(gimplify_modify_expr_rhs): Return GS_OK if anything changed.
2010-05-05 Alexandre Oliva <aoliva@redhat.com> 2010-05-05 Alexandre Oliva <aoliva@redhat.com>
Jakub Jelinek <jakub@redhat.com> Jakub Jelinek <jakub@redhat.com>
......
2010-05-05 Jason Merrill <jason@redhat.com>
PR c++/43787
* cp-gimplify.c (cp_gimplify_expr): Remove copies of empty classes.
* call.c (build_over_call): Don't try to avoid INIT_EXPR copies here.
2010-05-04 Paolo Carlini <paolo.carlini@oracle.com> 2010-05-04 Paolo Carlini <paolo.carlini@oracle.com>
PR c++/43028 PR c++/43028
......
...@@ -5778,20 +5778,8 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain) ...@@ -5778,20 +5778,8 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
{ {
tree to = stabilize_reference (cp_build_indirect_ref (fa, RO_NULL, tree to = stabilize_reference (cp_build_indirect_ref (fa, RO_NULL,
complain)); complain));
tree type = TREE_TYPE (to);
if (TREE_CODE (arg) != TARGET_EXPR val = build2 (INIT_EXPR, DECL_CONTEXT (fn), to, arg);
&& TREE_CODE (arg) != AGGR_INIT_EXPR
&& is_really_empty_class (type))
{
/* Avoid copying empty classes. */
val = build2 (COMPOUND_EXPR, void_type_node, to, arg);
TREE_NO_WARNING (val) = 1;
val = build2 (COMPOUND_EXPR, type, val, to);
TREE_NO_WARNING (val) = 1;
}
else
val = build2 (INIT_EXPR, DECL_CONTEXT (fn), to, arg);
return val; return val;
} }
} }
......
...@@ -569,6 +569,26 @@ cp_gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p) ...@@ -569,6 +569,26 @@ cp_gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
&& !useless_type_conversion_p (TREE_TYPE (op1), TREE_TYPE (op0))) && !useless_type_conversion_p (TREE_TYPE (op1), TREE_TYPE (op0)))
TREE_OPERAND (*expr_p, 1) = build1 (VIEW_CONVERT_EXPR, TREE_OPERAND (*expr_p, 1) = build1 (VIEW_CONVERT_EXPR,
TREE_TYPE (op0), op1); TREE_TYPE (op0), op1);
else if ((rhs_predicate_for (op0)) (op1)
&& !(TREE_CODE (op1) == CALL_EXPR
&& CALL_EXPR_RETURN_SLOT_OPT (op1))
&& is_really_empty_class (TREE_TYPE (op0)))
{
/* Remove any copies of empty classes. We check that the RHS
has a simple form so that TARGET_EXPRs and CONSTRUCTORs get
reduced properly, and we leave the return slot optimization
alone because it isn't a copy.
Also drop volatile variables on the RHS to avoid infinite
recursion from gimplify_expr trying to load the value. */
if (!TREE_SIDE_EFFECTS (op1)
|| (DECL_P (op1) && TREE_THIS_VOLATILE (op1)))
*expr_p = op0;
else
*expr_p = build2 (COMPOUND_EXPR, TREE_TYPE (*expr_p),
op0, op1);
}
} }
ret = GS_OK; ret = GS_OK;
break; break;
......
...@@ -4089,253 +4089,252 @@ gimplify_modify_expr_rhs (tree *expr_p, tree *from_p, tree *to_p, ...@@ -4089,253 +4089,252 @@ gimplify_modify_expr_rhs (tree *expr_p, tree *from_p, tree *to_p,
gimple_seq *pre_p, gimple_seq *post_p, gimple_seq *pre_p, gimple_seq *post_p,
bool want_value) bool want_value)
{ {
enum gimplify_status ret = GS_OK; enum gimplify_status ret = GS_UNHANDLED;
bool changed;
while (ret != GS_UNHANDLED) do
switch (TREE_CODE (*from_p)) {
{ changed = false;
case VAR_DECL: switch (TREE_CODE (*from_p))
/* If we're assigning from a read-only variable initialized with {
a constructor, do the direct assignment from the constructor, case VAR_DECL:
but only if neither source nor target are volatile since this /* If we're assigning from a read-only variable initialized with
latter assignment might end up being done on a per-field basis. */ a constructor, do the direct assignment from the constructor,
if (DECL_INITIAL (*from_p) but only if neither source nor target are volatile since this
&& TREE_READONLY (*from_p) latter assignment might end up being done on a per-field basis. */
&& !TREE_THIS_VOLATILE (*from_p) if (DECL_INITIAL (*from_p)
&& !TREE_THIS_VOLATILE (*to_p) && TREE_READONLY (*from_p)
&& TREE_CODE (DECL_INITIAL (*from_p)) == CONSTRUCTOR) && !TREE_THIS_VOLATILE (*from_p)
&& !TREE_THIS_VOLATILE (*to_p)
&& TREE_CODE (DECL_INITIAL (*from_p)) == CONSTRUCTOR)
{
tree old_from = *from_p;
enum gimplify_status subret;
/* Move the constructor into the RHS. */
*from_p = unshare_expr (DECL_INITIAL (*from_p));
/* Let's see if gimplify_init_constructor will need to put
it in memory. */
subret = gimplify_init_constructor (expr_p, NULL, NULL,
false, true);
if (subret == GS_ERROR)
{
/* If so, revert the change. */
*from_p = old_from;
}
else
{
ret = GS_OK;
changed = true;
}
}
break;
case INDIRECT_REF:
{ {
tree old_from = *from_p; /* If we have code like
/* Move the constructor into the RHS. */ *(const A*)(A*)&x
*from_p = unshare_expr (DECL_INITIAL (*from_p));
/* Let's see if gimplify_init_constructor will need to put where the type of "x" is a (possibly cv-qualified variant
it in memory. If so, revert the change. */ of "A"), treat the entire expression as identical to "x".
ret = gimplify_init_constructor (expr_p, NULL, NULL, false, true); This kind of code arises in C++ when an object is bound
if (ret == GS_ERROR) to a const reference, and if "x" is a TARGET_EXPR we want
to take advantage of the optimization below. */
tree t = gimple_fold_indirect_ref_rhs (TREE_OPERAND (*from_p, 0));
if (t)
{ {
*from_p = old_from; *from_p = t;
/* Fall through. */ ret = GS_OK;
changed = true;
} }
else break;
}
case TARGET_EXPR:
{
/* If we are initializing something from a TARGET_EXPR, strip the
TARGET_EXPR and initialize it directly, if possible. This can't
be done if the initializer is void, since that implies that the
temporary is set in some non-trivial way.
??? What about code that pulls out the temp and uses it
elsewhere? I think that such code never uses the TARGET_EXPR as
an initializer. If I'm wrong, we'll die because the temp won't
have any RTL. In that case, I guess we'll need to replace
references somehow. */
tree init = TARGET_EXPR_INITIAL (*from_p);
if (init
&& !VOID_TYPE_P (TREE_TYPE (init)))
{ {
*from_p = init;
ret = GS_OK; ret = GS_OK;
break; changed = true;
} }
} }
ret = GS_UNHANDLED; break;
break;
case INDIRECT_REF:
{
/* If we have code like
*(const A*)(A*)&x case COMPOUND_EXPR:
/* Remove any COMPOUND_EXPR in the RHS so the following cases will be
caught. */
gimplify_compound_expr (from_p, pre_p, true);
ret = GS_OK;
changed = true;
break;
where the type of "x" is a (possibly cv-qualified variant case CONSTRUCTOR:
of "A"), treat the entire expression as identical to "x". /* If we're initializing from a CONSTRUCTOR, break this into
This kind of code arises in C++ when an object is bound individual MODIFY_EXPRs. */
to a const reference, and if "x" is a TARGET_EXPR we want return gimplify_init_constructor (expr_p, pre_p, post_p, want_value,
to take advantage of the optimization below. */ false);
tree t = gimple_fold_indirect_ref_rhs (TREE_OPERAND (*from_p, 0));
if (t) case COND_EXPR:
/* If we're assigning to a non-register type, push the assignment
down into the branches. This is mandatory for ADDRESSABLE types,
since we cannot generate temporaries for such, but it saves a
copy in other cases as well. */
if (!is_gimple_reg_type (TREE_TYPE (*from_p)))
{ {
*from_p = t; /* This code should mirror the code in gimplify_cond_expr. */
ret = GS_OK; enum tree_code code = TREE_CODE (*expr_p);
tree cond = *from_p;
tree result = *to_p;
ret = gimplify_expr (&result, pre_p, post_p,
is_gimple_lvalue, fb_lvalue);
if (ret != GS_ERROR)
ret = GS_OK;
if (TREE_TYPE (TREE_OPERAND (cond, 1)) != void_type_node)
TREE_OPERAND (cond, 1)
= build2 (code, void_type_node, result,
TREE_OPERAND (cond, 1));
if (TREE_TYPE (TREE_OPERAND (cond, 2)) != void_type_node)
TREE_OPERAND (cond, 2)
= build2 (code, void_type_node, unshare_expr (result),
TREE_OPERAND (cond, 2));
TREE_TYPE (cond) = void_type_node;
recalculate_side_effects (cond);
if (want_value)
{
gimplify_and_add (cond, pre_p);
*expr_p = unshare_expr (result);
}
else
*expr_p = cond;
return ret;
} }
else
ret = GS_UNHANDLED;
break; break;
}
case TARGET_EXPR: case CALL_EXPR:
{ /* For calls that return in memory, give *to_p as the CALL_EXPR's
/* If we are initializing something from a TARGET_EXPR, strip the return slot so that we don't generate a temporary. */
TARGET_EXPR and initialize it directly, if possible. This can't if (!CALL_EXPR_RETURN_SLOT_OPT (*from_p)
be done if the initializer is void, since that implies that the && aggregate_value_p (*from_p, *from_p))
temporary is set in some non-trivial way.
??? What about code that pulls out the temp and uses it
elsewhere? I think that such code never uses the TARGET_EXPR as
an initializer. If I'm wrong, we'll die because the temp won't
have any RTL. In that case, I guess we'll need to replace
references somehow. */
tree init = TARGET_EXPR_INITIAL (*from_p);
if (init
&& !VOID_TYPE_P (TREE_TYPE (init)))
{ {
*from_p = init; bool use_target;
ret = GS_OK;
if (!(rhs_predicate_for (*to_p))(*from_p))
/* If we need a temporary, *to_p isn't accurate. */
use_target = false;
else if (TREE_CODE (*to_p) == RESULT_DECL
&& DECL_NAME (*to_p) == NULL_TREE
&& needs_to_live_in_memory (*to_p))
/* It's OK to use the return slot directly unless it's an NRV. */
use_target = true;
else if (is_gimple_reg_type (TREE_TYPE (*to_p))
|| (DECL_P (*to_p) && DECL_REGISTER (*to_p)))
/* Don't force regs into memory. */
use_target = false;
else if (TREE_CODE (*expr_p) == INIT_EXPR)
/* It's OK to use the target directly if it's being
initialized. */
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
use_target = true;
if (use_target)
{
CALL_EXPR_RETURN_SLOT_OPT (*from_p) = 1;
mark_addressable (*to_p);
}
} }
else break;
ret = GS_UNHANDLED;
}
break;
case COMPOUND_EXPR: case WITH_SIZE_EXPR:
/* Remove any COMPOUND_EXPR in the RHS so the following cases will be /* Likewise for calls that return an aggregate of non-constant size,
caught. */ since we would not be able to generate a temporary at all. */
gimplify_compound_expr (from_p, pre_p, true); if (TREE_CODE (TREE_OPERAND (*from_p, 0)) == CALL_EXPR)
ret = GS_OK; {
break; *from_p = TREE_OPERAND (*from_p, 0);
ret = GS_OK;
changed = true;
}
break;
case CONSTRUCTOR: /* If we're initializing from a container, push the initialization
/* If we're initializing from a CONSTRUCTOR, break this into inside it. */
individual MODIFY_EXPRs. */ case CLEANUP_POINT_EXPR:
return gimplify_init_constructor (expr_p, pre_p, post_p, want_value, case BIND_EXPR:
false); case STATEMENT_LIST:
case COND_EXPR:
/* If we're assigning to a non-register type, push the assignment
down into the branches. This is mandatory for ADDRESSABLE types,
since we cannot generate temporaries for such, but it saves a
copy in other cases as well. */
if (!is_gimple_reg_type (TREE_TYPE (*from_p)))
{ {
/* This code should mirror the code in gimplify_cond_expr. */ tree wrap = *from_p;
enum tree_code code = TREE_CODE (*expr_p); tree t;
tree cond = *from_p;
tree result = *to_p;
ret = gimplify_expr (&result, pre_p, post_p, ret = gimplify_expr (to_p, pre_p, post_p, is_gimple_min_lval,
is_gimple_lvalue, fb_lvalue); fb_lvalue);
if (ret != GS_ERROR) if (ret != GS_ERROR)
ret = GS_OK; ret = GS_OK;
if (TREE_TYPE (TREE_OPERAND (cond, 1)) != void_type_node) t = voidify_wrapper_expr (wrap, *expr_p);
TREE_OPERAND (cond, 1) gcc_assert (t == *expr_p);
= build2 (code, void_type_node, result,
TREE_OPERAND (cond, 1));
if (TREE_TYPE (TREE_OPERAND (cond, 2)) != void_type_node)
TREE_OPERAND (cond, 2)
= build2 (code, void_type_node, unshare_expr (result),
TREE_OPERAND (cond, 2));
TREE_TYPE (cond) = void_type_node;
recalculate_side_effects (cond);
if (want_value) if (want_value)
{ {
gimplify_and_add (cond, pre_p); gimplify_and_add (wrap, pre_p);
*expr_p = unshare_expr (result); *expr_p = unshare_expr (*to_p);
} }
else else
*expr_p = cond; *expr_p = wrap;
return ret; return GS_OK;
} }
else
ret = GS_UNHANDLED;
break;
case CALL_EXPR: case COMPOUND_LITERAL_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; tree complit = TREE_OPERAND (*expr_p, 1);
tree decl_s = COMPOUND_LITERAL_EXPR_DECL_EXPR (complit);
if (!(rhs_predicate_for (*to_p))(*from_p)) tree decl = DECL_EXPR_DECL (decl_s);
/* If we need a temporary, *to_p isn't accurate. */ tree init = DECL_INITIAL (decl);
use_target = false;
else if (TREE_CODE (*to_p) == RESULT_DECL /* struct T x = (struct T) { 0, 1, 2 } can be optimized
&& DECL_NAME (*to_p) == NULL_TREE into struct T x = { 0, 1, 2 } if the address of the
&& needs_to_live_in_memory (*to_p)) compound literal has never been taken. */
/* It's OK to use the return slot directly unless it's an NRV. */ if (!TREE_ADDRESSABLE (complit)
use_target = true; && !TREE_ADDRESSABLE (decl)
else if (is_gimple_reg_type (TREE_TYPE (*to_p)) && init)
|| (DECL_P (*to_p) && DECL_REGISTER (*to_p)))
/* Don't force regs into memory. */
use_target = false;
else if (TREE_CODE (*expr_p) == INIT_EXPR)
/* It's OK to use the target directly if it's being
initialized. */
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
use_target = true;
if (use_target)
{ {
CALL_EXPR_RETURN_SLOT_OPT (*from_p) = 1; *expr_p = copy_node (*expr_p);
mark_addressable (*to_p); TREE_OPERAND (*expr_p, 1) = init;
return GS_OK;
} }
} }
ret = GS_UNHANDLED; default:
break; break;
case WITH_SIZE_EXPR:
/* Likewise for calls that return an aggregate of non-constant size,
since we would not be able to generate a temporary at all. */
if (TREE_CODE (TREE_OPERAND (*from_p, 0)) == CALL_EXPR)
{
*from_p = TREE_OPERAND (*from_p, 0);
ret = GS_OK;
}
else
ret = GS_UNHANDLED;
break;
/* If we're initializing from a container, push the initialization
inside it. */
case CLEANUP_POINT_EXPR:
case BIND_EXPR:
case STATEMENT_LIST:
{
tree wrap = *from_p;
tree t;
ret = gimplify_expr (to_p, pre_p, post_p, is_gimple_min_lval,
fb_lvalue);
if (ret != GS_ERROR)
ret = GS_OK;
t = voidify_wrapper_expr (wrap, *expr_p);
gcc_assert (t == *expr_p);
if (want_value)
{
gimplify_and_add (wrap, pre_p);
*expr_p = unshare_expr (*to_p);
}
else
*expr_p = wrap;
return GS_OK;
}
case COMPOUND_LITERAL_EXPR:
{
tree complit = TREE_OPERAND (*expr_p, 1);
tree decl_s = COMPOUND_LITERAL_EXPR_DECL_EXPR (complit);
tree decl = DECL_EXPR_DECL (decl_s);
tree init = DECL_INITIAL (decl);
/* struct T x = (struct T) { 0, 1, 2 } can be optimized
into struct T x = { 0, 1, 2 } if the address of the
compound literal has never been taken. */
if (!TREE_ADDRESSABLE (complit)
&& !TREE_ADDRESSABLE (decl)
&& init)
{
*expr_p = copy_node (*expr_p);
TREE_OPERAND (*expr_p, 1) = init;
return GS_OK;
}
} }
}
default: while (changed);
ret = GS_UNHANDLED;
break;
}
return ret; return ret;
} }
...@@ -6616,8 +6615,16 @@ gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p, ...@@ -6616,8 +6615,16 @@ gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p,
case MODIFY_EXPR: case MODIFY_EXPR:
case INIT_EXPR: case INIT_EXPR:
ret = gimplify_modify_expr (expr_p, pre_p, post_p, {
fallback != fb_none); tree from = TREE_OPERAND (*expr_p, 1);
ret = gimplify_modify_expr (expr_p, pre_p, post_p,
fallback != fb_none);
/* Don't let the end of loop logic change GS_OK into GS_ALL_DONE
if the RHS has changed. */
if (ret == GS_OK && *expr_p == save_expr
&& TREE_OPERAND (*expr_p, 1) != from)
continue;
}
break; break;
case TRUTH_ANDIF_EXPR: case TRUTH_ANDIF_EXPR:
......
2010-05-05 Jason Merrill <jason@redhat.com>
PR c++/43787
* g++.dg/opt/empty1.C: New.
2010-05-05 Janus Weil <janus@gcc.gnu.org> 2010-05-05 Janus Weil <janus@gcc.gnu.org>
PR fortran/43696 PR fortran/43696
......
// PR c++/43787
// Test that we don't try to copy *x.
// { dg-do run }
class empty_t {};
int main()
{
empty_t* x = 0;
empty_t y = *x;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment