mirror of
git://gcc.gnu.org/git/gcc.git
synced 2025-02-14 17:49:57 +08:00
Fix PR c++/70452 (regression in C++ parsing performance)
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
This commit is contained in:
parent
bf867841b4
commit
c0daf32d0c
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
17
gcc/testsuite/g++.dg/ext/constexpr-vla4.C
Normal file
17
gcc/testsuite/g++.dg/ext/constexpr-vla4.C
Normal file
@ -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" }
|
Loading…
Reference in New Issue
Block a user