Folks at Google reported to me that some packages they are building rely on changes to an object persisting in the underlying storage after the object is destroyed, and the more aggressive DSE in GCC 4.9 breaks this. Such code has undefined behavior, but this seems like a reasonable thing to have a flag for, along the lines of -fno-strict-aliasing.

Tested x86_64-pc-linux-gnu, applying to trunk.
commit ca9986d97112f804e4cc416b15f9baf74d713aab
Author: Jason Merrill <ja...@redhat.com>
Date:   Tue Feb 10 23:45:19 2015 -0500

    gcc/
    	* common.opt (-flifetime-dse): New.
    gcc/cp/
    	* decl.c (begin_destructor_body): Condition clobber on
    	-flifetime-dse.

diff --git a/gcc/common.opt b/gcc/common.opt
index cf4e503..6e65757 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -1856,6 +1856,11 @@ fregmove
 Common Ignore
 Does nothing. Preserved for backward compatibility.
 
+flifetime-dse
+Common Report Var(flag_lifetime_dse) Init(1) Optimization
+Tell DSE that the storage for a C++ object is dead when the constructor
+starts and when the destructor finishes.
+
 flive-range-shrinkage
 Common Report Var(flag_live_range_shrinkage) Init(0) Optimization
 Relief of register pressure through live range shrinkage
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index 50b0624..810acd5 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -13936,15 +13936,19 @@ begin_destructor_body (void)
       initialize_vtbl_ptrs (current_class_ptr);
       finish_compound_stmt (compound_stmt);
 
-      /* Insert a cleanup to let the back end know that the object is dead
-	 when we exit the destructor, either normally or via exception.  */
-      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_decl_cleanup (NULL_TREE, exprstmt);
+      if (flag_lifetime_dse)
+	{
+	  /* Insert a cleanup to let the back end know that the object is dead
+	     when we exit the destructor, either normally or via exception.  */
+	  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_decl_cleanup (NULL_TREE, exprstmt);
+	}
 
       /* And insert cleanups for our bases and members so that they
 	 will be properly destroyed if we throw.  */
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 8a04790..5cce4f7 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -7888,6 +7888,16 @@ registers after writing to their lower 32-bit half.
 Enabled for Alpha, AArch64 and x86 at levels @option{-O2},
 @option{-O3}, @option{-Os}.
 
+@item -fno-lifetime-dse
+@opindex fno-lifetime-dse
+In C++ the value of an object is only affected by changes within its
+lifetime: when the constructor begins, the object has an indeterminate
+value, and any changes during the lifetime of the object are dead when
+the object is destroyed.  Normally dead store elimination will take
+advantage of this; if your code relies on the value of the object
+storage persisting beyond the lifetime of the object, you can use this
+flag to disable this optimization.
+
 @item -flive-range-shrinkage
 @opindex flive-range-shrinkage
 Attempt to decrease register pressure through register live range
diff --git a/gcc/testsuite/g++.dg/opt/flifetime-dse1.C b/gcc/testsuite/g++.dg/opt/flifetime-dse1.C
new file mode 100644
index 0000000..733d28a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/opt/flifetime-dse1.C
@@ -0,0 +1,23 @@
+// { dg-options "-O3 -fno-lifetime-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];
+
+  A* ap = new(ar) A;
+  ap->i = 42;
+  ap->~A();
+
+  if (ar[0] != 42) __builtin_abort();
+}

Reply via email to