diff options
author | Patrick Palka <ppalka@redhat.com> | 2020-07-30 22:21:41 -0400 |
---|---|---|
committer | Patrick Palka <ppalka@redhat.com> | 2020-07-30 22:21:41 -0400 |
commit | 8c00059ce058ea2aec2933319e270f5443b8b909 (patch) | |
tree | 827a56233a6a312802401b1147f254516ea35b47 | |
parent | d48cca8f21c64bba215f163314562a81e97074e2 (diff) | |
download | gcc-8c00059ce058ea2aec2933319e270f5443b8b909.zip gcc-8c00059ce058ea2aec2933319e270f5443b8b909.tar.gz gcc-8c00059ce058ea2aec2933319e270f5443b8b909.tar.bz2 |
c++: decl_constant_value and unsharing [PR96197]
In the testcase from the PR we're seeing excessive memory use (> 5GB)
during constexpr evaluation, almost all of which is due to the call to
decl_constant_value in the VAR_DECL/CONST_DECL branch of
cxx_eval_constant_expression. We reach here every time we evaluate an
ARRAY_REF of a constexpr VAR_DECL, and from there decl_constant_value
makes an unshared copy of the VAR_DECL's initializer. But unsharing
here is unnecessary because callers of cxx_eval_constant_expression
already unshare its result when necessary.
To fix this excessive unsharing, this patch adds a new defaulted
parameter unshare_p to decl_really_constant_value and
decl_constant_value so that callers can control whether to unshare.
As a simplification, we can also move the call to unshare_expr in
constant_value_1 outside of the loop, since doing unshare_expr on a
DECL_P is a no-op.
Now that we no longer unshare the result of decl_constant_value and
decl_really_constant_value from cxx_eval_constant_expression, memory use
during constexpr evaluation for the testcase from the PR falls from ~5GB
to 15MB according to -ftime-report.
gcc/cp/ChangeLog:
PR c++/96197
* constexpr.c (cxx_eval_constant_expression) <case CONST_DECL>:
Pass false to decl_constant_value and decl_really_constant_value
so that they don't unshare their result.
* cp-tree.h (decl_constant_value): New declaration with an added
bool parameter.
(decl_really_constant_value): Add bool parameter defaulting to
true to existing declaration.
* init.c (constant_value_1): Add bool parameter which controls
whether to unshare the initializer before returning. Call
unshare_expr at most once.
(scalar_constant_value): Pass true to constant_value_1's new
bool parameter.
(decl_really_constant_value): Add bool parameter and forward it
to constant_value_1.
(decl_constant_value): Likewise, but instead define a new
overload with an added bool parameter.
gcc/testsuite/ChangeLog:
PR c++/96197
* g++.dg/cpp1y/constexpr-array8.C: New test.
-rw-r--r-- | gcc/cp/constexpr.c | 4 | ||||
-rw-r--r-- | gcc/cp/cp-tree.h | 3 | ||||
-rw-r--r-- | gcc/cp/init.c | 34 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/cpp1y/constexpr-array8.C | 18 |
4 files changed, 45 insertions, 14 deletions
diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c index 97dcc1b..b1c1d24 100644 --- a/gcc/cp/constexpr.c +++ b/gcc/cp/constexpr.c @@ -5695,9 +5695,9 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, TREE_CONSTANT (r) = true; } else if (ctx->strict) - r = decl_really_constant_value (t); + r = decl_really_constant_value (t, /*unshare_p=*/false); else - r = decl_constant_value (t); + r = decl_constant_value (t, /*unshare_p=*/false); if (TREE_CODE (r) == TARGET_EXPR && TREE_CODE (TARGET_EXPR_INITIAL (r)) == CONSTRUCTOR) r = TARGET_EXPR_INITIAL (r); diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index ea4871f..1e583ef 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -6773,7 +6773,8 @@ extern tree build_vec_delete (location_t, tree, tree, extern tree create_temporary_var (tree); extern void initialize_vtbl_ptrs (tree); extern tree scalar_constant_value (tree); -extern tree decl_really_constant_value (tree); +extern tree decl_constant_value (tree, bool); +extern tree decl_really_constant_value (tree, bool = true); extern int diagnose_uninitialized_cst_or_ref_member (tree, bool, bool); extern tree build_vtbl_address (tree); extern bool maybe_reject_flexarray_init (tree, tree); diff --git a/gcc/cp/init.c b/gcc/cp/init.c index ef4b3c4..cb9bd2d 100644 --- a/gcc/cp/init.c +++ b/gcc/cp/init.c @@ -2272,10 +2272,12 @@ build_offset_ref (tree type, tree member, bool address_p, recursively); otherwise, return DECL. If STRICT_P, the initializer is only returned if DECL is a constant-expression. If RETURN_AGGREGATE_CST_OK_P, it is ok to - return an aggregate constant. */ + return an aggregate constant. If UNSHARE_P, return an unshared + copy of the initializer. */ static tree -constant_value_1 (tree decl, bool strict_p, bool return_aggregate_cst_ok_p) +constant_value_1 (tree decl, bool strict_p, bool return_aggregate_cst_ok_p, + bool unshare_p) { while (TREE_CODE (decl) == CONST_DECL || decl_constant_var_p (decl) @@ -2343,9 +2345,9 @@ constant_value_1 (tree decl, bool strict_p, bool return_aggregate_cst_ok_p) && !DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (decl) && DECL_NONTRIVIALLY_INITIALIZED_P (decl)) break; - decl = unshare_expr (init); + decl = init; } - return decl; + return unshare_p ? unshare_expr (decl) : decl; } /* If DECL is a CONST_DECL, or a constant VAR_DECL initialized by constant @@ -2357,26 +2359,36 @@ tree scalar_constant_value (tree decl) { return constant_value_1 (decl, /*strict_p=*/true, - /*return_aggregate_cst_ok_p=*/false); + /*return_aggregate_cst_ok_p=*/false, + /*unshare_p=*/true); } -/* Like scalar_constant_value, but can also return aggregate initializers. */ +/* Like scalar_constant_value, but can also return aggregate initializers. + If UNSHARE_P, return an unshared copy of the initializer. */ tree -decl_really_constant_value (tree decl) +decl_really_constant_value (tree decl, bool unshare_p /*= true*/) { return constant_value_1 (decl, /*strict_p=*/true, - /*return_aggregate_cst_ok_p=*/true); + /*return_aggregate_cst_ok_p=*/true, + /*unshare_p=*/unshare_p); } -/* A more relaxed version of scalar_constant_value, used by the +/* A more relaxed version of decl_really_constant_value, used by the common C/C++ code. */ tree -decl_constant_value (tree decl) +decl_constant_value (tree decl, bool unshare_p) { return constant_value_1 (decl, /*strict_p=*/processing_template_decl, - /*return_aggregate_cst_ok_p=*/true); + /*return_aggregate_cst_ok_p=*/true, + /*unshare_p=*/unshare_p); +} + +tree +decl_constant_value (tree decl) +{ + return decl_constant_value (decl, /*unshare_p=*/true); } /* Common subroutines of build_new and build_vec_delete. */ diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-array8.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-array8.C new file mode 100644 index 0000000..339abb6 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-array8.C @@ -0,0 +1,18 @@ +// PR c++/96197 +// { dg-do compile { target c++14 } } + +struct S { + S* p = this; +}; + +constexpr S ary[5000] = {}; + +constexpr int foo() { + int count = 0; + for (int i = 0; i < 5000; i++) + if (ary[i].p != nullptr) + count++; + return count; +} + +constexpr int bar = foo(); |