aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatrick Palka <ppalka@redhat.com>2020-07-30 22:21:41 -0400
committerPatrick Palka <ppalka@redhat.com>2020-07-30 22:21:41 -0400
commit8c00059ce058ea2aec2933319e270f5443b8b909 (patch)
tree827a56233a6a312802401b1147f254516ea35b47
parentd48cca8f21c64bba215f163314562a81e97074e2 (diff)
downloadgcc-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.c4
-rw-r--r--gcc/cp/cp-tree.h3
-rw-r--r--gcc/cp/init.c34
-rw-r--r--gcc/testsuite/g++.dg/cpp1y/constexpr-array8.C18
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();