Patchwork C++ PATCH for c++/59659 (compile-hog with list-initialization of member array)

login
register
mail settings
Submitter Jason Merrill
Date Jan. 15, 2014, 7:08 p.m.
Message ID <52D6DCB7.1090406@redhat.com>
Download mbox | patch
Permalink /patch/311245/
State New
Headers show

Comments

Jason Merrill - Jan. 15, 2014, 7:08 p.m.
In the original testcase, the user found that a large std::array of 
std::atomics was taking forever to compile.  There were two issues 
involved here:

1) Because atomics do have user-provided constructors, we were going 
down the constructor path and not realizing that it just gave us 
zero-initialization.  Fixed by calling maybe_constant_value and avoiding 
adding extra zero-initialization to the end of the CONSTRUCTOR.

2) If we aren't dealing with zero-initialization, when we 
value-initialize a bunch of elements at the end of an array, we really 
ought to use a loop rather than initializing them individually.  Fixed 
by using RANGE_EXPR and build_vec_init.

With this patch we do maybe_constant_value on all elements of an 
aggregate initializer, in hopes of turning more of them into constants 
even when we aren't initializing a constexpr or static variable.

Tested x86_64-pc-linux-gnu, applying to trunk.

Patch

commit 4759339f1cc6999d51fe82cdc9b7889286f58b3e
Author: Jason Merrill <jason@redhat.com>
Date:   Sat Jan 11 06:15:02 2014 -0500

    	PR c++/59659
    	* typeck2.c (massage_init_elt): New.
    	(process_init_constructor_record)
    	(process_init_constructor_union): Use it.
    	(process_init_constructor_array): Use it.  Use RANGE_EXPR.
    	(split_nonconstant_init_1): Handle it.
    	* semantics.c (cxx_eval_vec_init_1): Use force_rvalue.

diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index 9f87887..eb04266 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -8961,19 +8961,13 @@  cxx_eval_vec_init_1 (const constexpr_call *call, tree atype, tree init,
       else
 	{
 	  /* Copying an element.  */
-	  vec<tree, va_gc> *argvec;
 	  gcc_assert (same_type_ignoring_top_level_qualifiers_p
 		      (atype, TREE_TYPE (init)));
 	  eltinit = cp_build_array_ref (input_location, init, idx,
 					tf_warning_or_error);
 	  if (!real_lvalue_p (init))
 	    eltinit = move (eltinit);
-	  argvec = make_tree_vector ();
-	  argvec->quick_push (eltinit);
-	  eltinit = (build_special_member_call
-		     (NULL_TREE, complete_ctor_identifier, &argvec,
-		      elttype, LOOKUP_NORMAL, tf_warning_or_error));
-	  release_tree_vector (argvec);
+	  eltinit = force_rvalue (eltinit, tf_warning_or_error);
 	  eltinit = cxx_eval_constant_expression
 	    (call, eltinit, allow_non_constant, addr, non_constant_p, overflow_p);
 	}
diff --git a/gcc/cp/typeck2.c b/gcc/cp/typeck2.c
index d9c3647..25edbac 100644
--- a/gcc/cp/typeck2.c
+++ b/gcc/cp/typeck2.c
@@ -631,24 +631,40 @@  split_nonconstant_init_1 (tree dest, tree init)
 	      CONSTRUCTOR_ELTS (init)->ordered_remove (idx);
 	      --idx;
 
-	      if (array_type_p)
-		sub = build4 (ARRAY_REF, inner_type, dest, field_index,
-			      NULL_TREE, NULL_TREE);
+	      if (TREE_CODE (field_index) == RANGE_EXPR)
+		{
+		  /* Use build_vec_init to initialize a range.  */
+		  tree low = TREE_OPERAND (field_index, 0);
+		  tree hi = TREE_OPERAND (field_index, 1);
+		  sub = build4 (ARRAY_REF, inner_type, dest, low,
+				NULL_TREE, NULL_TREE);
+		  sub = cp_build_addr_expr (sub, tf_warning_or_error);
+		  tree max = size_binop (MINUS_EXPR, hi, low);
+		  code = build_vec_init (sub, max, value, false, 0,
+					 tf_warning_or_error);
+		  add_stmt (code);
+		}
 	      else
-		sub = build3 (COMPONENT_REF, inner_type, dest, field_index,
-			      NULL_TREE);
+		{
+		  if (array_type_p)
+		    sub = build4 (ARRAY_REF, inner_type, dest, field_index,
+				  NULL_TREE, NULL_TREE);
+		  else
+		    sub = build3 (COMPONENT_REF, inner_type, dest, field_index,
+				  NULL_TREE);
 
-	      code = build2 (INIT_EXPR, inner_type, sub, value);
-	      code = build_stmt (input_location, EXPR_STMT, code);
-	      code = maybe_cleanup_point_expr_void (code);
-	      add_stmt (code);
-	      if (type_build_dtor_call (inner_type))
-		{
-		  code = (build_special_member_call
-			  (sub, complete_dtor_identifier, NULL, inner_type,
-			   LOOKUP_NORMAL, tf_warning_or_error));
-		  if (!TYPE_HAS_TRIVIAL_DESTRUCTOR (inner_type))
-		    finish_eh_cleanup (code);
+		  code = build2 (INIT_EXPR, inner_type, sub, value);
+		  code = build_stmt (input_location, EXPR_STMT, code);
+		  code = maybe_cleanup_point_expr_void (code);
+		  add_stmt (code);
+		  if (type_build_dtor_call (inner_type))
+		    {
+		      code = (build_special_member_call
+			      (sub, complete_dtor_identifier, NULL, inner_type,
+			       LOOKUP_NORMAL, tf_warning_or_error));
+		      if (!TYPE_HAS_TRIVIAL_DESTRUCTOR (inner_type))
+			finish_eh_cleanup (code);
+		    }
 		}
 
 	      num_split_elts++;
@@ -1101,6 +1117,22 @@  picflag_from_initializer (tree init)
   return 0;
 }
 
+/* Adjust INIT for going into a CONSTRUCTOR.  */
+
+static tree
+massage_init_elt (tree type, tree init, tsubst_flags_t complain)
+{
+  init = digest_init_r (type, init, true, LOOKUP_IMPLICIT, complain);
+  /* Strip a simple TARGET_EXPR when we know this is an initializer.  */
+  if (TREE_CODE (init) == TARGET_EXPR
+      && !VOID_TYPE_P (TREE_TYPE (TARGET_EXPR_INITIAL (init))))
+    init = TARGET_EXPR_INITIAL (init);
+  /* When we defer constant folding within a statement, we may want to
+     defer this folding as well.  */
+  init = maybe_constant_init (init);
+  return init;
+}
+
 /* Subroutine of process_init_constructor, which will process an initializer
    INIT for an array or vector of type TYPE. Returns the flags (PICFLAG_*)
    which describe the initializers.  */
@@ -1158,8 +1190,7 @@  process_init_constructor_array (tree type, tree init,
       else
 	ce->index = size_int (i);
       gcc_assert (ce->value);
-      ce->value = digest_init_r (TREE_TYPE (type), ce->value, true,
-				 LOOKUP_IMPLICIT, complain);
+      ce->value = massage_init_elt (TREE_TYPE (type), ce->value, complain);
 
       if (ce->value != error_mark_node)
 	gcc_assert (same_type_ignoring_top_level_qualifiers_p
@@ -1168,33 +1199,42 @@  process_init_constructor_array (tree type, tree init,
       flags |= picflag_from_initializer (ce->value);
     }
 
-  /* No more initializers. If the array is unbounded, we are done. Otherwise,
-     we must add initializers ourselves.  */
-  if (!unbounded)
-    for (; i < len; ++i)
-      {
-	tree next;
+  /* No more initializers. If the array is unbounded, or we've initialized
+     all the elements, we are done. Otherwise, we must add initializers
+     ourselves.  */
+  if (!unbounded && i < len)
+    {
+      tree next;
 
-	if (type_build_ctor_call (TREE_TYPE (type)))
-	  {
-	    /* If this type needs constructors run for default-initialization,
-	      we can't rely on the back end to do it for us, so make the
-	      initialization explicit by list-initializing from {}.  */
-	    next = build_constructor (init_list_type_node, NULL);
-	    next = digest_init (TREE_TYPE (type), next, complain);
-	  }
-	else if (!zero_init_p (TREE_TYPE (type)))
-	  next = build_zero_init (TREE_TYPE (type),
-				  /*nelts=*/NULL_TREE,
-				  /*static_storage_p=*/false);
-	else
-	  /* The default zero-initialization is fine for us; don't
-	     add anything to the CONSTRUCTOR.  */
-	  break;
+      if (type_build_ctor_call (TREE_TYPE (type)))
+	{
+	  /* If this type needs constructors run for default-initialization,
+	     we can't rely on the back end to do it for us, so make the
+	     initialization explicit by list-initializing from {}.  */
+	  next = build_constructor (init_list_type_node, NULL);
+	  next = massage_init_elt (TREE_TYPE (type), next, complain);
+	  if (initializer_zerop (next))
+	    /* The default zero-initialization is fine for us; don't
+	       add anything to the CONSTRUCTOR.  */
+	    next = NULL_TREE;
+	}
+      else if (!zero_init_p (TREE_TYPE (type)))
+	next = build_zero_init (TREE_TYPE (type),
+				/*nelts=*/NULL_TREE,
+				/*static_storage_p=*/false);
+      else
+	/* The default zero-initialization is fine for us; don't
+	   add anything to the CONSTRUCTOR.  */
+	next = NULL_TREE;
 
-	flags |= picflag_from_initializer (next);
-	CONSTRUCTOR_APPEND_ELT (v, size_int (i), next);
-      }
+      if (next)
+	{
+	  flags |= picflag_from_initializer (next);
+	  tree index = build2 (RANGE_EXPR, sizetype, size_int (i),
+			       size_int (len - 1));
+	  CONSTRUCTOR_APPEND_ELT (v, index, next);
+	}
+    }
 
   CONSTRUCTOR_ELTS (init) = v;
   return flags;
@@ -1263,8 +1303,7 @@  process_init_constructor_record (tree type, tree init,
 	    }
 
 	  gcc_assert (ce->value);
-	  next = digest_init_r (type, ce->value, true,
-				LOOKUP_IMPLICIT, complain);
+	  next = massage_init_elt (type, ce->value, complain);
 	  ++idx;
 	}
       else if (type_build_ctor_call (TREE_TYPE (field)))
@@ -1274,18 +1313,7 @@  process_init_constructor_record (tree type, tree init,
 	     for us, so build up TARGET_EXPRs.  If the type in question is
 	     a class, just build one up; if it's an array, recurse.  */
 	  next = build_constructor (init_list_type_node, NULL);
-	  if (MAYBE_CLASS_TYPE_P (TREE_TYPE (field)))
-	    {
-	      next = finish_compound_literal (TREE_TYPE (field), next,
-					      complain);
-	      /* direct-initialize the target. No temporary is going
-		  to be involved.  */
-	      if (TREE_CODE (next) == TARGET_EXPR)
-		TARGET_EXPR_DIRECT_INIT_P (next) = true;
-	    }
-
-	  next = digest_init_r (TREE_TYPE (field), next, true,
-				LOOKUP_IMPLICIT, complain);
+	  next = massage_init_elt (TREE_TYPE (field), next, complain);
 
 	  /* Warn when some struct elements are implicitly initialized.  */
 	  warning (OPT_Wmissing_field_initializers,
@@ -1422,8 +1450,7 @@  process_init_constructor_union (tree type, tree init,
     }
 
   if (ce->value && ce->value != error_mark_node)
-    ce->value = digest_init_r (TREE_TYPE (ce->index), ce->value,
-			       true, LOOKUP_IMPLICIT, complain);
+    ce->value = massage_init_elt (TREE_TYPE (ce->index), ce->value, complain);
 
   return picflag_from_initializer (ce->value);
 }
diff --git a/gcc/testsuite/g++.dg/opt/value-init1.C b/gcc/testsuite/g++.dg/opt/value-init1.C
new file mode 100644
index 0000000..fd38b2e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/opt/value-init1.C
@@ -0,0 +1,13 @@ 
+// PR c++/59659
+// { dg-options "-fdump-tree-gimple -std=c++11" }
+// { dg-final { scan-tree-dump-times "i = 0" 0 "gimple" } }
+// { dg-final { cleanup-tree-dump "gimple" } }
+
+struct S { S () = default; S (int i); int i; };
+struct A { S s[100]; };
+
+void
+foo ()
+{
+  A a = {{}};
+}
diff --git a/gcc/testsuite/g++.dg/opt/value-init2.C b/gcc/testsuite/g++.dg/opt/value-init2.C
new file mode 100644
index 0000000..515cca0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/opt/value-init2.C
@@ -0,0 +1,13 @@ 
+// PR c++/59659
+// { dg-options "-fdump-tree-gimple" }
+// { dg-final { scan-tree-dump-times "S::S" 1 "gimple" } }
+// { dg-final { cleanup-tree-dump "gimple" } }
+
+struct S { S (); S (int i); int i; };
+struct A { S s[100]; };
+
+void
+foo ()
+{
+  A a = {{}};
+}