Currently we clobber objects at the end of the destructor to express
that the language guarantees nothing about the state of storage after an
object is destroyed. The same is true at the other end: when a
constructor begins the object has indeterminate value, so any stores to
the underlying storage before that can be considered dead. Like the
destructor clobber, this can be disabled with -fno-lifetime-dse.
Tested x86_64-pc-linux-gnu, applying to trunk.
commit b5ddc7d2b7bea43f0108e3eca1013ecec1ceda89
Author: Jason Merrill <ja...@redhat.com>
Date: Tue Feb 10 23:49:14 2015 -0500
* constexpr.c (cxx_eval_store_expression): Ignore clobbers.
(build_constexpr_constructor_member_initializers): Loop to find
the BIND_EXPR.
* decl.c (start_preparsed_function): Clobber the object at the
beginning of a constructor.
diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index 2952cbe..1f9ba3e 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -543,7 +543,16 @@ build_constexpr_constructor_member_initializers (tree type, tree body)
|| TREE_CODE (body) == EH_SPEC_BLOCK)
body = TREE_OPERAND (body, 0);
if (TREE_CODE (body) == STATEMENT_LIST)
- body = STATEMENT_LIST_HEAD (body)->stmt;
+ {
+ tree_stmt_iterator i = tsi_start (body);
+ while (true)
+ {
+ body = tsi_stmt (i);
+ if (TREE_CODE (body) == BIND_EXPR)
+ break;
+ tsi_next (&i);
+ }
+ }
body = BIND_EXPR_BODY (body);
if (TREE_CODE (body) == CLEANUP_POINT_EXPR)
{
@@ -2603,6 +2612,11 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t,
{
constexpr_ctx new_ctx = *ctx;
+ tree init = TREE_OPERAND (t, 1);
+ if (TREE_CLOBBER_P (init))
+ /* Just ignore clobbers. */
+ return void_node;
+
/* First we figure out where we're storing to. */
tree target = TREE_OPERAND (t, 0);
target = cxx_eval_constant_expression (ctx, target,
@@ -2684,9 +2698,8 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t,
new_ctx.object = target;
}
- tree init = cxx_eval_constant_expression (&new_ctx, TREE_OPERAND (t, 1),
- false,
- non_constant_p, overflow_p);
+ init = cxx_eval_constant_expression (&new_ctx, init, false,
+ non_constant_p, overflow_p);
if (target == object)
/* The hash table might have moved since the get earlier. */
ctx->values->put (object, init);
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index 0538570..965f07c 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -13708,6 +13708,20 @@ start_preparsed_function (tree decl1, tree attrs, int flags)
store_parm_decls (current_function_parms);
+ if (!processing_template_decl
+ && flag_lifetime_dse && DECL_CONSTRUCTOR_P (decl1))
+ {
+ /* Insert a clobber to let the back end know that the object storage
+ is dead when we enter the constructor. */
+ tree btype = CLASSTYPE_AS_BASE (current_class_type);
+ tree clobber = build_constructor (btype, NULL);
+ TREE_THIS_VOLATILE (clobber) = true;
+ tree bref = build_nop (build_reference_type (btype), current_class_ptr);
+ bref = convert_from_reference (bref);
+ tree exprstmt = build2 (MODIFY_EXPR, btype, bref, clobber);
+ finish_expr_stmt (exprstmt);
+ }
+
return true;
}
diff --git a/gcc/testsuite/g++.dg/opt/flifetime-dse2.C b/gcc/testsuite/g++.dg/opt/flifetime-dse2.C
new file mode 100644
index 0000000..16d9a74
--- /dev/null
+++ b/gcc/testsuite/g++.dg/opt/flifetime-dse2.C
@@ -0,0 +1,27 @@
+// { dg-options "-O3 -flifetime-dse" }
+// { dg-do run }
+
+typedef __SIZE_TYPE__ size_t;
+inline void * operator new (size_t, void *p) { return p; }
+
+struct A
+{
+ int i;
+ A() {}
+ ~A() {}
+};
+
+int main()
+{
+ int ar[1] = { 42 };
+ A* ap = new(ar) A;
+
+ // When the constructor starts the object has indeterminate value.
+ if (ap->i == 42) __builtin_abort();
+
+ ap->i = 42;
+ ap->~A();
+
+ // When the destructor ends the object no longer exists.
+ if (ar[0] == 42) __builtin_abort();
+}