]> gcc.gnu.org Git - gcc.git/commitdiff
Fix PR c++/70452 (regression in C++ parsing performance)
authorPatrick Palka <ppalka@gcc.gnu.org>
Tue, 5 Apr 2016 16:40:00 +0000 (16:40 +0000)
committerPatrick Palka <ppalka@gcc.gnu.org>
Tue, 5 Apr 2016 16:40:00 +0000 (16:40 +0000)
gcc/cp/ChangeLog:

PR c++/70452
* constexpr.c (struct fundef_copy): New struct.
(struct fundef_copies_table_t): New struct.
(fundef_copies_table): New static variable.
(maybe_initialize_fundef_copies_table): New static function.
(get_fundef_copy): New static function.
(save_fundef_copy): New static function.
(cxx_eval_call_expression): Use get_fundef_copy, and
save_fundef_copy.
(constexpr_call_table): Add "deletable" GTY marker.

gcc/testsuite/ChangeLog:

PR c++/70452
* g++.dg/ext/constexpr-vla4.C: New test.

From-SVN: r234753

gcc/cp/ChangeLog
gcc/cp/constexpr.c
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/ext/constexpr-vla4.C [new file with mode: 0644]

index 77ac83db02fee1ab051e1cac8c7ca581c0272654..77c0b5b20896f1c171244a8a8d38e42828f852a7 100644 (file)
@@ -1,3 +1,16 @@
+2016-04-05  Patrick Palka  <ppalka@gcc.gnu.org>
+
+       PR c++/70452
+       * constexpr.c (struct fundef_copy): New struct.
+       (struct fundef_copies_table_t): New struct.
+       (fundef_copies_table): New static variable.
+       (maybe_initialize_fundef_copies_table): New static function.
+       (get_fundef_copy): New static function.
+       (save_fundef_copy): New static function.
+       (cxx_eval_call_expression): Use get_fundef_copy, and
+       save_fundef_copy.
+       (constexpr_call_table): Add "deletable" GTY marker.
+
 2016-04-05  Patrick Palka  <ppalka@gcc.gnu.org>
 
        PR c++/70452
index 0607b08860cf28f24a5f4832ec7a9af1ae235fd7..1c2701bbcd4478f5a2601271d20ceeaa496efdc5 100644 (file)
@@ -915,7 +915,7 @@ struct constexpr_ctx {
 /* A table of all constexpr calls that have been evaluated by the
    compiler in this translation unit.  */
 
-static GTY (()) hash_table<constexpr_call_hasher> *constexpr_call_table;
+static GTY ((deletable)) hash_table<constexpr_call_hasher> *constexpr_call_table;
 
 static tree cxx_eval_constant_expression (const constexpr_ctx *, tree,
                                          bool, bool *, bool *, tree * = NULL);
@@ -965,6 +965,78 @@ maybe_initialize_constexpr_call_table (void)
     constexpr_call_table = hash_table<constexpr_call_hasher>::create_ggc (101);
 }
 
+/* The representation of a single node in the per-function freelist maintained
+   by FUNDEF_COPIES_TABLE.  */
+
+struct fundef_copy
+{
+  tree body;
+  tree parms;
+  tree res;
+  fundef_copy *prev;
+};
+
+/* During constexpr CALL_EXPR evaluation, to avoid issues with sharing when
+   a function happens to get called recursively, we unshare the callee
+   function's body and evaluate this unshared copy instead of evaluating the
+   original body.
+
+   FUNDEF_COPIES_TABLE is a per-function freelist of these unshared function
+   copies.  The underlying data structure of FUNDEF_COPIES_TABLE is a hash_map
+   that's keyed off of the original FUNCTION_DECL and whose value is the chain
+   of this function's unused copies awaiting reuse.  */
+
+struct fundef_copies_table_t
+{
+  hash_map<tree, fundef_copy *> *map;
+};
+
+static GTY((deletable)) fundef_copies_table_t fundef_copies_table;
+
+/* Initialize FUNDEF_COPIES_TABLE if it's not initialized.  */
+
+static void
+maybe_initialize_fundef_copies_table ()
+{
+  if (fundef_copies_table.map == NULL)
+    fundef_copies_table.map = hash_map<tree, fundef_copy *>::create_ggc (101);
+}
+
+/* Reuse a copy or create a new unshared copy of the function FUN.
+   Return this copy.  */
+
+static fundef_copy *
+get_fundef_copy (tree fun)
+{
+  maybe_initialize_fundef_copies_table ();
+
+  fundef_copy *copy;
+  fundef_copy **slot = &fundef_copies_table.map->get_or_insert (fun, NULL);
+  if (*slot == NULL)
+    {
+      copy = ggc_alloc<fundef_copy> ();
+      copy->body = copy_fn (fun, copy->parms, copy->res);
+      copy->prev = NULL;
+    }
+  else
+    {
+      copy = *slot;
+      *slot = (*slot)->prev;
+    }
+
+  return copy;
+}
+
+/* Save the copy COPY of function FUN for later reuse by get_fundef_copy().  */
+
+static void
+save_fundef_copy (tree fun, fundef_copy *copy)
+{
+  fundef_copy **slot = &fundef_copies_table.map->get_or_insert (fun, NULL);
+  copy->prev = *slot;
+  *slot = copy;
+}
+
 /* We have an expression tree T that represents a call, either CALL_EXPR
    or AGGR_INIT_EXPR.  If the call is lexically to a named function,
    retrun the _DECL for that function.  */
@@ -1365,10 +1437,13 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
       if (!result || result == error_mark_node)
        {
          gcc_assert (DECL_SAVED_TREE (fun));
-         tree parms, res;
+         tree body, parms, res;
 
-         /* Unshare the whole function body.  */
-         tree body = copy_fn (fun, parms, res);
+         /* Reuse or create a new unshared copy of this function's body.  */
+         fundef_copy *copy = get_fundef_copy (fun);
+         body = copy->body;
+         parms = copy->parms;
+         res = copy->res;
 
          /* Associate the bindings with the remapped parms.  */
          tree bound = new_call.bindings;
@@ -1397,8 +1472,14 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
          else
            ctx->values->put (res, NULL_TREE);
 
+         /* Track the callee's evaluated SAVE_EXPRs so that we can forget
+            their values after the call.  */
+         constexpr_ctx ctx_with_save_exprs = *ctx;
+         hash_set<tree> save_exprs;
+         ctx_with_save_exprs.save_exprs = &save_exprs;
+
          tree jump_target = NULL_TREE;
-         cxx_eval_constant_expression (ctx, body,
+         cxx_eval_constant_expression (&ctx_with_save_exprs, body,
                                        lval, non_constant_p, overflow_p,
                                        &jump_target);
 
@@ -1423,6 +1504,11 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
                }
            }
 
+         /* Forget the saved values of the callee's SAVE_EXPRs.  */
+         for (hash_set<tree>::iterator iter = save_exprs.begin();
+              iter != save_exprs.end(); ++iter)
+           ctx_with_save_exprs.values->remove (*iter);
+
          /* Remove the parms/result from the values map.  Is it worth
             bothering to do this when the map itself is only live for
             one constexpr evaluation?  If so, maybe also clear out
@@ -1432,6 +1518,9 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
            ctx->values->remove (slot);
          for (tree parm = parms; parm; parm = TREE_CHAIN (parm))
            ctx->values->remove (parm);
+
+         /* Make the unshared function copy we used available for re-use.  */
+         save_fundef_copy (fun, copy);
        }
 
       if (result == error_mark_node)
index 7012de5007bdbb5844dd37b969c2a330289fcdd0..957823d7fe23ee195d36f6cf2c49f53e343067a7 100644 (file)
@@ -1,3 +1,8 @@
+2016-04-05  Patrick Palka  <ppalka@gcc.gnu.org>
+
+       PR c++/70452
+       * g++.dg/ext/constexpr-vla4.C: New test.
+
 2016-04-05  Uros Bizjak  <ubizjak@gmail.com>
 
        PR target/70510
diff --git a/gcc/testsuite/g++.dg/ext/constexpr-vla4.C b/gcc/testsuite/g++.dg/ext/constexpr-vla4.C
new file mode 100644 (file)
index 0000000..428a8fd
--- /dev/null
@@ -0,0 +1,17 @@
+// PR c++/70452
+// { dg-do compile { target c++14 } }
+
+constexpr int
+foo (int n, bool p)
+{
+  __extension__ int a [n] = { 0 };
+  if (n == 3)
+    foo (n - 2, false);
+  if (n == 3)
+    foo(n - 1, true);
+  if (p)
+    return a[1];
+  return 0;
+}
+
+constexpr int i2 = foo (3, false); // { dg-bogus "array subscript out of bound" }
This page took 0.07312 seconds and 5 git commands to generate.