On 8/6/20 1:50 PM, Patrick Palka wrote:
This patch eliminates an exponential dependence in cxx_eval_vec_init on
the array dimension of a VEC_INIT_EXPR when the RANGE_EXPR optimization
applies.  This is achieved by using a single constructor_elt (with index
RANGE_EXPR 0...max-1) per dimension instead of two constructor_elts
(with index 0 and RANGE_EXPR 1...max-1 respectively).  In doing so, we
can also get rid of the call to unshare_constructor since the element
initializer now gets used in exactly one spot.

The patch also removes the 'eltinit = new_ctx.ctor' assignment within the
RANGE_EXPR optimization since eltinit should already always be equal to
new_ctx.ctor here (modulo encountering an error when computing eltinit).
This was verified by running the testsuite against an appropriate assert.

Maybe keep that assert?

Finally, this patch reverses the sense of the ctx->quiet test that
controls whether to short-circuit evaluation upon seeing an error.  This
should speed up speculative evaluation of non-constant VEC_INIT_EXPRs
(since ctx->quiet is true then).  I'm not sure why we were testing
!ctx->quiet originally; it's inconsistent with how we short-circuit in
other spots.

Good question. That code seems to go back to the initial implementation of constexpr.

  I contrived the testcase array60.C below which verifies
that we now short-circuit quickly.

Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look OK to
commit?

gcc/cp/ChangeLog:

        * constexpr.c (cxx_eval_vec_init_1): Move the i == 0 test to the
        if statement that guards the RANGE_EXPR optimization.  Invert
        the ctx->quiet test. Apply the RANGE_EXPR optimization before we
        append the first element initializer.  Truncate ctx->ctor when
        performing the RANGE_EXPR optimization.  Make the built
        RANGE_EXPR start at index 0 instead of 1.  Don't call
        unshare_constructor.

gcc/testsuite/ChangeLog:

        * g++.dg/cpp0x/constexpr-array28.C: New test.
        * g++.dg/init/array60.C: New test.
---
  gcc/cp/constexpr.c                            | 34 ++++++++++---------
  .../g++.dg/cpp0x/constexpr-array28.C          | 14 ++++++++
  gcc/testsuite/g++.dg/init/array60.C           | 13 +++++++
  3 files changed, 45 insertions(+), 16 deletions(-)
  create mode 100644 gcc/testsuite/g++.dg/cpp0x/constexpr-array28.C
  create mode 100644 gcc/testsuite/g++.dg/init/array60.C

diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index ab747a58fa0..e67ce5da355 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -4205,7 +4205,7 @@ cxx_eval_vec_init_1 (const constexpr_ctx *ctx, tree 
atype, tree init,
          if (value_init || init == NULL_TREE)
            {
              eltinit = NULL_TREE;
-             reuse = i == 0;
+             reuse = true;
            }
          else
            eltinit = cp_build_array_ref (input_location, init, idx, complain);
@@ -4222,7 +4222,7 @@ cxx_eval_vec_init_1 (const constexpr_ctx *ctx, tree 
atype, tree init,
            return ctx->ctor;
          eltinit = cxx_eval_constant_expression (&new_ctx, init, lval,
                                                  non_constant_p, overflow_p);
-         reuse = i == 0;
+         reuse = true;
        }
        else
        {
@@ -4236,35 +4236,37 @@ cxx_eval_vec_init_1 (const constexpr_ctx *ctx, tree 
atype, tree init,
          eltinit = cxx_eval_constant_expression (&new_ctx, eltinit, lval,
                                                  non_constant_p, overflow_p);
        }
-      if (*non_constant_p && !ctx->quiet)
+      if (*non_constant_p && ctx->quiet)
        break;
-      if (new_ctx.ctor != ctx->ctor)
-       {
-         /* We appended this element above; update the value.  */
-         gcc_assert ((*p)->last().index == idx);
-         (*p)->last().value = eltinit;
-       }
-      else
-       CONSTRUCTOR_APPEND_ELT (*p, idx, eltinit);
+
        /* Reuse the result of cxx_eval_constant_expression call
         from the first iteration to all others if it is a constant
         initializer that doesn't require relocations.  */
-      if (reuse
+      if (i == 0
+         && reuse
          && max > 1
          && (eltinit == NULL_TREE
              || (initializer_constant_valid_p (eltinit, TREE_TYPE (eltinit))
                  == null_pointer_node)))
        {
-         if (new_ctx.ctor != ctx->ctor)
-           eltinit = new_ctx.ctor;
          tree range = build2 (RANGE_EXPR, size_type_node,
-                              build_int_cst (size_type_node, 1),
+                              build_int_cst (size_type_node, 0),
                               build_int_cst (size_type_node, max - 1));
-         CONSTRUCTOR_APPEND_ELT (*p, range, unshare_constructor (eltinit));
+         vec_safe_truncate (*p, 0);
+         CONSTRUCTOR_APPEND_ELT (*p, range, eltinit);
          break;
        }
        else if (i == 0)
        vec_safe_reserve (*p, max);
+
+      if (new_ctx.ctor != ctx->ctor)
+       {
+         /* We appended this element above; update the value.  */
+         gcc_assert ((*p)->last().index == idx);
+         (*p)->last().value = eltinit;

So if eltinit already == new_ctx.ctor, this doesn't change anything?

+       }
+      else
+       CONSTRUCTOR_APPEND_ELT (*p, idx, eltinit);
      }
if (!*non_constant_p)
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-array28.C 
b/gcc/testsuite/g++.dg/cpp0x/constexpr-array28.C
new file mode 100644
index 00000000000..f844926e32b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-array28.C
@@ -0,0 +1,14 @@
+// { dg-do compile { target c++11 } }
+
+struct A { int i = 42; };
+
+struct B
+{
+  A a[2][2][2][2][2][2][2][2][2][2][2][2][2][2][2][2][2][2][2][2][2][2][2][2];
+};
+
+void f() {
+  // Verify default initialization here does not scale exponentially
+  // with the number of array dimensions.
+  constexpr B b;
+}
diff --git a/gcc/testsuite/g++.dg/init/array60.C 
b/gcc/testsuite/g++.dg/init/array60.C
new file mode 100644
index 00000000000..22bd750f8e6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/init/array60.C
@@ -0,0 +1,13 @@
+struct A { int i; };
+
+struct B
+{
+  virtual void f();
+  A a[10000000];
+};
+
+extern B b;
+
+// Verify that speculative constexpr evaluation of this non-constant
+// initializer does not scale with the size of the array member 'a'.
+B b2 (b);


Reply via email to