From: Patrick Palka <ppalka@redhat.com>
To: Patrick Palka <ppalka@redhat.com>
Cc: Jason Merrill <jason@redhat.com>, gcc-patches@gcc.gnu.org
Subject: Re: [PATCH 2/2] c++: speculative constexpr and is_constant_evaluated [PR108243]
Date: Fri, 10 Feb 2023 11:51:02 -0500 (EST) [thread overview]
Message-ID: <af7fe73a-80e9-cd5c-1df3-c627b289d6a2@idea> (raw)
In-Reply-To: <5b99d4cf-4c6c-d2b1-d7d6-c447bf8f393e@idea>
On Fri, 10 Feb 2023, Patrick Palka wrote:
> On Thu, 9 Feb 2023, Patrick Palka wrote:
>
> > On Thu, 9 Feb 2023, Jason Merrill wrote:
> >
> > > On 2/9/23 09:36, Patrick Palka wrote:
> > > > On Sun, 5 Feb 2023, Jason Merrill wrote:
> > > >
> > > > > On 2/3/23 15:51, Patrick Palka wrote:
> > > > > > On Mon, 30 Jan 2023, Jason Merrill wrote:
> > > > > >
> > > > > > > On 1/27/23 17:02, Patrick Palka wrote:
> > > > > > > > This PR illustrates that __builtin_is_constant_evaluated currently
> > > > > > > > acts
> > > > > > > > as an optimization barrier for our speculative constexpr evaluation,
> > > > > > > > since we don't want to prematurely fold the builtin to false if the
> > > > > > > > expression in question would be later manifestly constant evaluated
> > > > > > > > (in
> > > > > > > > which case it must be folded to true).
> > > > > > > >
> > > > > > > > This patch fixes this by permitting __builtin_is_constant_evaluated
> > > > > > > > to get folded as false during cp_fold_function, since at that point
> > > > > > > > we're sure we're doing manifestly constant evaluation. To that end
> > > > > > > > we add a flags parameter to cp_fold that controls what mce_value the
> > > > > > > > CALL_EXPR case passes to maybe_constant_value.
> > > > > > > >
> > > > > > > > bootstrapped and rgetsted no x86_64-pc-linux-gnu, does this look OK
> > > > > > > > for
> > > > > > > > trunk?
> > > > > > > >
> > > > > > > > PR c++/108243
> > > > > > > >
> > > > > > > > gcc/cp/ChangeLog:
> > > > > > > >
> > > > > > > > * cp-gimplify.cc (enum fold_flags): Define.
> > > > > > > > (cp_fold_data::genericize): Replace this data member with ...
> > > > > > > > (cp_fold_data::fold_flags): ... this.
> > > > > > > > (cp_fold_r): Adjust cp_fold_data use and cp_fold_calls.
> > > > > > > > (cp_fold_function): Likewise.
> > > > > > > > (cp_fold_maybe_rvalue): Likewise.
> > > > > > > > (cp_fully_fold_init): Likewise.
> > > > > > > > (cp_fold): Add fold_flags parameter. Don't cache if flags
> > > > > > > > isn't empty.
> > > > > > > > <case CALL_EXPR>: Pass mce_false to maybe_constant_value
> > > > > > > > if if ff_genericize is set.
> > > > > > > >
> > > > > > > > gcc/testsuite/ChangeLog:
> > > > > > > >
> > > > > > > > * g++.dg/opt/pr108243.C: New test.
> > > > > > > > ---
> > > > > > > > gcc/cp/cp-gimplify.cc | 76
> > > > > > > > ++++++++++++++++++-----------
> > > > > > > > gcc/testsuite/g++.dg/opt/pr108243.C | 29 +++++++++++
> > > > > > > > 2 files changed, 76 insertions(+), 29 deletions(-)
> > > > > > > > create mode 100644 gcc/testsuite/g++.dg/opt/pr108243.C
> > > > > > > >
> > > > > > > > diff --git a/gcc/cp/cp-gimplify.cc b/gcc/cp/cp-gimplify.cc
> > > > > > > > index a35cedd05cc..d023a63768f 100644
> > > > > > > > --- a/gcc/cp/cp-gimplify.cc
> > > > > > > > +++ b/gcc/cp/cp-gimplify.cc
> > > > > > > > @@ -43,12 +43,20 @@ along with GCC; see the file COPYING3. If not
> > > > > > > > see
> > > > > > > > #include "omp-general.h"
> > > > > > > > #include "opts.h"
> > > > > > > > +/* Flags for cp_fold and cp_fold_r. */
> > > > > > > > +
> > > > > > > > +enum fold_flags {
> > > > > > > > + ff_none = 0,
> > > > > > > > + /* Whether we're being called from cp_fold_function. */
> > > > > > > > + ff_genericize = 1 << 0,
> > > > > > > > +};
> > > > > > > > +
> > > > > > > > /* Forward declarations. */
> > > > > > > > static tree cp_genericize_r (tree *, int *, void *);
> > > > > > > > static tree cp_fold_r (tree *, int *, void *);
> > > > > > > > static void cp_genericize_tree (tree*, bool);
> > > > > > > > -static tree cp_fold (tree);
> > > > > > > > +static tree cp_fold (tree, fold_flags);
> > > > > > > > /* Genericize a TRY_BLOCK. */
> > > > > > > > @@ -996,9 +1004,8 @@ struct cp_genericize_data
> > > > > > > > struct cp_fold_data
> > > > > > > > {
> > > > > > > > hash_set<tree> pset;
> > > > > > > > - bool genericize; // called from cp_fold_function?
> > > > > > > > -
> > > > > > > > - cp_fold_data (bool g): genericize (g) {}
> > > > > > > > + fold_flags flags;
> > > > > > > > + cp_fold_data (fold_flags flags): flags (flags) {}
> > > > > > > > };
> > > > > > > > static tree
> > > > > > > > @@ -1039,7 +1046,7 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees,
> > > > > > > > void
> > > > > > > > *data_)
> > > > > > > > break;
> > > > > > > > }
> > > > > > > > - *stmt_p = stmt = cp_fold (*stmt_p);
> > > > > > > > + *stmt_p = stmt = cp_fold (*stmt_p, data->flags);
> > > > > > > > if (data->pset.add (stmt))
> > > > > > > > {
> > > > > > > > @@ -1119,12 +1126,12 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees,
> > > > > > > > void
> > > > > > > > *data_)
> > > > > > > > here rather than in cp_genericize to avoid problems with the
> > > > > > > > invisible
> > > > > > > > reference transition. */
> > > > > > > > case INIT_EXPR:
> > > > > > > > - if (data->genericize)
> > > > > > > > + if (data->flags & ff_genericize)
> > > > > > > > cp_genericize_init_expr (stmt_p);
> > > > > > > > break;
> > > > > > > > case TARGET_EXPR:
> > > > > > > > - if (data->genericize)
> > > > > > > > + if (data->flags & ff_genericize)
> > > > > > > > cp_genericize_target_expr (stmt_p);
> > > > > > > > /* Folding might replace e.g. a COND_EXPR with a
> > > > > > > > TARGET_EXPR;
> > > > > > > > in
> > > > > > > > @@ -1157,7 +1164,7 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees,
> > > > > > > > void
> > > > > > > > *data_)
> > > > > > > > void
> > > > > > > > cp_fold_function (tree fndecl)
> > > > > > > > {
> > > > > > > > - cp_fold_data data (/*genericize*/true);
> > > > > > > > + cp_fold_data data (ff_genericize);
> > > > > > > > cp_walk_tree (&DECL_SAVED_TREE (fndecl), cp_fold_r, &data,
> > > > > > > > NULL);
> > > > > > > > }
> > > > > > > > @@ -2375,7 +2382,7 @@ cp_fold_maybe_rvalue (tree x, bool rval)
> > > > > > > > {
> > > > > > > > while (true)
> > > > > > > > {
> > > > > > > > - x = cp_fold (x);
> > > > > > > > + x = cp_fold (x, ff_none);
> > > > > > > > if (rval)
> > > > > > > > x = mark_rvalue_use (x);
> > > > > > > > if (rval && DECL_P (x)
> > > > > > > > @@ -2434,7 +2441,7 @@ cp_fully_fold_init (tree x)
> > > > > > > > if (processing_template_decl)
> > > > > > > > return x;
> > > > > > > > x = cp_fully_fold (x);
> > > > > > > > - cp_fold_data data (/*genericize*/false);
> > > > > > > > + cp_fold_data data (ff_none);
> > > > > > > > cp_walk_tree (&x, cp_fold_r, &data, NULL);
> > > > > > > > return x;
> > > > > > > > }
> > > > > > > > @@ -2469,7 +2476,7 @@ clear_fold_cache (void)
> > > > > > > > Function returns X or its folded variant. */
> > > > > > > > static tree
> > > > > > > > -cp_fold (tree x)
> > > > > > > > +cp_fold (tree x, fold_flags flags)
> > > > > > > > {
> > > > > > > > tree op0, op1, op2, op3;
> > > > > > > > tree org_x = x, r = NULL_TREE;
> > > > > > > > @@ -2490,8 +2497,11 @@ cp_fold (tree x)
> > > > > > > > if (fold_cache == NULL)
> > > > > > > > fold_cache = hash_map<tree, tree>::create_ggc (101);
> > > > > > > > - if (tree *cached = fold_cache->get (x))
> > > > > > > > - return *cached;
> > > > > > > > + bool cache_p = (flags == ff_none);
> > > > > > > > +
> > > > > > > > + if (cache_p)
> > > > > > > > + if (tree *cached = fold_cache->get (x))
> > > > > > > > + return *cached;
> > > > > > > > uid_sensitive_constexpr_evaluation_checker c;
> > > > > > > > @@ -2526,7 +2536,7 @@ cp_fold (tree x)
> > > > > > > > Don't create a new tree if op0 != TREE_OPERAND (x, 0),
> > > > > > > > the
> > > > > > > > folding of the operand should be in the caches and if in
> > > > > > > > cp_fold_r
> > > > > > > > it will modify it in place. */
> > > > > > > > - op0 = cp_fold (TREE_OPERAND (x, 0));
> > > > > > > > + op0 = cp_fold (TREE_OPERAND (x, 0), flags);
> > > > > > > > if (op0 == error_mark_node)
> > > > > > > > x = error_mark_node;
> > > > > > > > break;
> > > > > > > > @@ -2571,7 +2581,7 @@ cp_fold (tree x)
> > > > > > > > {
> > > > > > > > tree p = maybe_undo_parenthesized_ref (x);
> > > > > > > > if (p != x)
> > > > > > > > - return cp_fold (p);
> > > > > > > > + return cp_fold (p, flags);
> > > > > > > > }
> > > > > > > > goto unary;
> > > > > > > > @@ -2763,8 +2773,8 @@ cp_fold (tree x)
> > > > > > > > case COND_EXPR:
> > > > > > > > loc = EXPR_LOCATION (x);
> > > > > > > > op0 = cp_fold_rvalue (TREE_OPERAND (x, 0));
> > > > > > > > - op1 = cp_fold (TREE_OPERAND (x, 1));
> > > > > > > > - op2 = cp_fold (TREE_OPERAND (x, 2));
> > > > > > > > + op1 = cp_fold (TREE_OPERAND (x, 1), flags);
> > > > > > > > + op2 = cp_fold (TREE_OPERAND (x, 2), flags);
> > > > > > > > if (TREE_CODE (TREE_TYPE (x)) == BOOLEAN_TYPE)
> > > > > > > > {
> > > > > > > > @@ -2854,7 +2864,7 @@ cp_fold (tree x)
> > > > > > > > {
> > > > > > > > if (!same_type_p (TREE_TYPE (x), TREE_TYPE (r)))
> > > > > > > > r = build_nop (TREE_TYPE (x), r);
> > > > > > > > - x = cp_fold (r);
> > > > > > > > + x = cp_fold (r, flags);
> > > > > > > > break;
> > > > > > > > }
> > > > > > > > }
> > > > > > > > @@ -2908,7 +2918,7 @@ cp_fold (tree x)
> > > > > > > > int m = call_expr_nargs (x);
> > > > > > > > for (int i = 0; i < m; i++)
> > > > > > > > {
> > > > > > > > - r = cp_fold (CALL_EXPR_ARG (x, i));
> > > > > > > > + r = cp_fold (CALL_EXPR_ARG (x, i), flags);
> > > > > > > > if (r != CALL_EXPR_ARG (x, i))
> > > > > > > > {
> > > > > > > > if (r == error_mark_node)
> > > > > > > > @@ -2931,7 +2941,7 @@ cp_fold (tree x)
> > > > > > > > if (TREE_CODE (r) != CALL_EXPR)
> > > > > > > > {
> > > > > > > > - x = cp_fold (r);
> > > > > > > > + x = cp_fold (r, flags);
> > > > > > > > break;
> > > > > > > > }
> > > > > > > > @@ -2944,7 +2954,15 @@ cp_fold (tree x)
> > > > > > > > constant, but the call followed by an INDIRECT_REF is. */
> > > > > > > > if (callee && DECL_DECLARED_CONSTEXPR_P (callee)
> > > > > > > > && !flag_no_inline)
> > > > > > > > - r = maybe_constant_value (x);
> > > > > > > > + {
> > > > > > > > + mce_value manifestly_const_eval = mce_unknown;
> > > > > > > > + if (flags & ff_genericize)
> > > > > > > > + /* At genericization time it's safe to fold
> > > > > > > > + __builtin_is_constant_evaluated to false. */
> > > > > > > > + manifestly_const_eval = mce_false;
> > > > > > > > + r = maybe_constant_value (x, /*decl=*/NULL_TREE,
> > > > > > > > + manifestly_const_eval);
> > > > > > > > + }
> > > > > > > > optimize = sv;
> > > > > > > > if (TREE_CODE (r) != CALL_EXPR)
> > > > > > > > @@ -2971,7 +2989,7 @@ cp_fold (tree x)
> > > > > > > > vec<constructor_elt, va_gc> *nelts = NULL;
> > > > > > > > FOR_EACH_VEC_SAFE_ELT (elts, i, p)
> > > > > > > > {
> > > > > > > > - tree op = cp_fold (p->value);
> > > > > > > > + tree op = cp_fold (p->value, flags);
> > > > > > > > if (op != p->value)
> > > > > > > > {
> > > > > > > > if (op == error_mark_node)
> > > > > > > > @@ -3002,7 +3020,7 @@ cp_fold (tree x)
> > > > > > > > for (int i = 0; i < n; i++)
> > > > > > > > {
> > > > > > > > - tree op = cp_fold (TREE_VEC_ELT (x, i));
> > > > > > > > + tree op = cp_fold (TREE_VEC_ELT (x, i), flags);
> > > > > > > > if (op != TREE_VEC_ELT (x, i))
> > > > > > > > {
> > > > > > > > if (!changed)
> > > > > > > > @@ -3019,10 +3037,10 @@ cp_fold (tree x)
> > > > > > > > case ARRAY_RANGE_REF:
> > > > > > > > loc = EXPR_LOCATION (x);
> > > > > > > > - op0 = cp_fold (TREE_OPERAND (x, 0));
> > > > > > > > - op1 = cp_fold (TREE_OPERAND (x, 1));
> > > > > > > > - op2 = cp_fold (TREE_OPERAND (x, 2));
> > > > > > > > - op3 = cp_fold (TREE_OPERAND (x, 3));
> > > > > > > > + op0 = cp_fold (TREE_OPERAND (x, 0), flags);
> > > > > > > > + op1 = cp_fold (TREE_OPERAND (x, 1), flags);
> > > > > > > > + op2 = cp_fold (TREE_OPERAND (x, 2), flags);
> > > > > > > > + op3 = cp_fold (TREE_OPERAND (x, 3), flags);
> > > > > > > > if (op0 != TREE_OPERAND (x, 0)
> > > > > > > > || op1 != TREE_OPERAND (x, 1)
> > > > > > > > @@ -3050,7 +3068,7 @@ cp_fold (tree x)
> > > > > > > > /* A SAVE_EXPR might contain e.g. (0 * i) + (0 * j),
> > > > > > > > which,
> > > > > > > > after
> > > > > > > > folding, evaluates to an invariant. In that case no need to
> > > > > > > > wrap
> > > > > > > > this folded tree with a SAVE_EXPR. */
> > > > > > > > - r = cp_fold (TREE_OPERAND (x, 0));
> > > > > > > > + r = cp_fold (TREE_OPERAND (x, 0), flags);
> > > > > > > > if (tree_invariant_p (r))
> > > > > > > > x = r;
> > > > > > > > break;
> > > > > > > > @@ -3069,7 +3087,7 @@ cp_fold (tree x)
> > > > > > > > copy_warning (x, org_x);
> > > > > > > > }
> > > > > > > > - if (!c.evaluation_restricted_p ())
> > > > > > > > + if (cache_p && !c.evaluation_restricted_p ())
> > > > > > > > {
> > > > > > > > fold_cache->put (org_x, x);
> > > > > > > > /* Prevent that we try to fold an already folded result
> > > > > > > > again.
> > > > > > > > */
> > > > > > > > diff --git a/gcc/testsuite/g++.dg/opt/pr108243.C
> > > > > > > > b/gcc/testsuite/g++.dg/opt/pr108243.C
> > > > > > > > new file mode 100644
> > > > > > > > index 00000000000..4c45dbba13c
> > > > > > > > --- /dev/null
> > > > > > > > +++ b/gcc/testsuite/g++.dg/opt/pr108243.C
> > > > > > > > @@ -0,0 +1,29 @@
> > > > > > > > +// PR c++/108243
> > > > > > > > +// { dg-do compile { target c++11 } }
> > > > > > > > +// { dg-additional-options "-O -fdump-tree-original" }
> > > > > > > > +
> > > > > > > > +constexpr int foo() {
> > > > > > > > + return __builtin_is_constant_evaluated() + 1;
> > > > > > > > +}
> > > > > > > > +
> > > > > > > > +#if __cpp_if_consteval
> > > > > > > > +constexpr int bar() {
> > > > > > > > + if consteval {
> > > > > > > > + return 5;
> > > > > > > > + } else {
> > > > > > > > + return 4;
> > > > > > > > + }
> > > > > > > > +}
> > > > > > > > +#endif
> > > > > > > > +
> > > > > > > > +int p, q;
> > > > > > > > +
> > > > > > > > +int main() {
> > > > > > > > + p = foo();
> > > > > > > > +#if __cpp_if_consteval
> > > > > > > > + q = bar();
> > > > > > > > +#endif
> > > > > > > > +}
> > > > > > > > +
> > > > > > > > +// { dg-final { scan-tree-dump-not "= foo" "original" } }
> > > > > > > > +// { dg-final { scan-tree-dump-not "= bar" "original" } }
> > > > > > >
> > > > > > > Let's also test a static initializer that can't be fully
> > > > > > > constant-evaluated.
> > > > > >
> > > > > > D'oh, doing so revealed that cp_fold_function doesn't reach static
> > > > > > initializers; that's taken care of by cp_fully_fold_init. So it seems
> > > > > > we need to make cp_fold when called from the latter entry point to also
> > > > > > assume m_c_e is false. We can't re-use ff_genericize here because that
> > > > > > flag has additional effects in cp_fold_r, so it seems we need another
> > > > > > flag that that only affects the manifestly constant-eval stuff; I called
> > > > > > it ff_mce_false. How does the following look?
> > > > > >
> > > > > > -- >8 --
> > > > > >
> > > > > > Subject: [PATCH 2/2] c++: speculative constexpr and
> > > > > > is_constant_evaluated
> > > > > > [PR108243]
> > > > > >
> > > > > > This PR illustrates that __builtin_is_constant_evaluated currently acts
> > > > > > as an optimization barrier for our speculative constexpr evaluation,
> > > > > > since we don't want to prematurely fold the builtin to false if the
> > > > > > expression in question would be later manifestly constant evaluated (in
> > > > > > which case it must be folded to true).
> > > > > >
> > > > > > This patch fixes this by permitting __builtin_is_constant_evaluated
> > > > > > to get folded as false during cp_fold_function and cp_fully_fold_init,
> > > > > > since at these points we're sure we're done with manifestly constant
> > > > > > evaluation. To that end we add a flags parameter to cp_fold that
> > > > > > controls whether we pass mce_false or mce_unknown to
> > > > > > maybe_constant_value
> > > > > > when folding a CALL_EXPR.
> > > > > >
> > > > > > PR c++/108243
> > > > > > PR c++/97553
> > > > > >
> > > > > > gcc/cp/ChangeLog:
> > > > > >
> > > > > > * cp-gimplify.cc (enum fold_flags): Define.
> > > > > > (cp_fold_data::genericize): Replace this data member with ...
> > > > > > (cp_fold_data::fold_flags): ... this.
> > > > > > (cp_fold_r): Adjust use of cp_fold_data and calls to cp_fold.
> > > > > > (cp_fold_function): Likewise.
> > > > > > (cp_fold_maybe_rvalue): Likewise.
> > > > > > (cp_fully_fold_init): Likewise.
> > > > > > (cp_fold): Add fold_flags parameter. Don't cache if flags
> > > > > > isn't empty.
> > > > > > <case CALL_EXPR>: If ff_genericize is set, fold
> > > > > > __builtin_is_constant_evaluated to false and pass mce_false to
> > > > > > maybe_constant_value.
> > > > > >
> > > > > > gcc/testsuite/ChangeLog:
> > > > > >
> > > > > > * g++.dg/opt/is_constant_evaluated1.C: New test.
> > > > > > * g++.dg/opt/is_constant_evaluated2.C: New test.
> > > > > > ---
> > > > > > gcc/cp/cp-gimplify.cc | 88
> > > > > > ++++++++++++-------
> > > > > > .../g++.dg/opt/is_constant_evaluated1.C | 14 +++
> > > > > > .../g++.dg/opt/is_constant_evaluated2.C | 32 +++++++
> > > > > > 3 files changed, 104 insertions(+), 30 deletions(-)
> > > > > > create mode 100644 gcc/testsuite/g++.dg/opt/is_constant_evaluated1.C
> > > > > > create mode 100644 gcc/testsuite/g++.dg/opt/is_constant_evaluated2.C
> > > > > >
> > > > > > diff --git a/gcc/cp/cp-gimplify.cc b/gcc/cp/cp-gimplify.cc
> > > > > > index 9929d29981a..590ed787997 100644
> > > > > > --- a/gcc/cp/cp-gimplify.cc
> > > > > > +++ b/gcc/cp/cp-gimplify.cc
> > > > > > @@ -43,12 +43,26 @@ along with GCC; see the file COPYING3. If not see
> > > > > > #include "omp-general.h"
> > > > > > #include "opts.h"
> > > > > > +/* Flags for cp_fold and cp_fold_r. */
> > > > > > +
> > > > > > +enum fold_flags {
> > > > > > + ff_none = 0,
> > > > > > + /* Whether we're being called from cp_fold_function. */
> > > > > > + ff_genericize = 1 << 0,
> > > > > > + /* Whether we're folding late enough that we could assume
> > > > > > + we're definitely not in a manifestly constant-evaluated
> > > > > > + context. */
> > > > >
> > > > > It's not necessarily a matter of late enough; we could fold sooner and
> > > > > still
> > > > > know that, as in cp_fully_fold_init. We could do the same at other
> > > > > full-expression points, but we don't because we want to delay folding as
> > > > > much
> > > > > as possible. So let's say "folding at a point where we know we're..."
> > > > >
> > > > > > + ff_mce_false = 1 << 1,
> > > > > > +};
> > > > > > +
> > > > > > +using fold_flags_t = int;
> > > > > > +
> > > > > > /* Forward declarations. */
> > > > > > static tree cp_genericize_r (tree *, int *, void *);
> > > > > > static tree cp_fold_r (tree *, int *, void *);
> > > > > > static void cp_genericize_tree (tree*, bool);
> > > > > > -static tree cp_fold (tree);
> > > > > > +static tree cp_fold (tree, fold_flags_t);
> > > > > > /* Genericize a TRY_BLOCK. */
> > > > > > @@ -1012,9 +1026,8 @@ struct cp_genericize_data
> > > > > > struct cp_fold_data
> > > > > > {
> > > > > > hash_set<tree> pset;
> > > > > > - bool genericize; // called from cp_fold_function?
> > > > > > -
> > > > > > - cp_fold_data (bool g): genericize (g) {}
> > > > > > + fold_flags_t flags;
> > > > > > + cp_fold_data (fold_flags_t flags): flags (flags) {}
> > > > > > };
> > > > > > static tree
> > > > > > @@ -1055,7 +1068,7 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void
> > > > > > *data_)
> > > > > > break;
> > > > > > }
> > > > > > - *stmt_p = stmt = cp_fold (*stmt_p);
> > > > > > + *stmt_p = stmt = cp_fold (*stmt_p, data->flags);
> > > > > > if (data->pset.add (stmt))
> > > > > > {
> > > > > > @@ -1135,12 +1148,12 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees,
> > > > > > void
> > > > > > *data_)
> > > > > > here rather than in cp_genericize to avoid problems with the
> > > > > > invisible
> > > > > > reference transition. */
> > > > > > case INIT_EXPR:
> > > > > > - if (data->genericize)
> > > > > > + if (data->flags & ff_genericize)
> > > > > > cp_genericize_init_expr (stmt_p);
> > > > > > break;
> > > > > > case TARGET_EXPR:
> > > > > > - if (data->genericize)
> > > > > > + if (data->flags & ff_genericize)
> > > > > > cp_genericize_target_expr (stmt_p);
> > > > > > /* Folding might replace e.g. a COND_EXPR with a TARGET_EXPR;
> > > > > > in
> > > > > > @@ -1173,7 +1186,7 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void
> > > > > > *data_)
> > > > > > void
> > > > > > cp_fold_function (tree fndecl)
> > > > > > {
> > > > > > - cp_fold_data data (/*genericize*/true);
> > > > > > + cp_fold_data data (ff_genericize | ff_mce_false);
> > > > >
> > > > > Here would be a good place for a comment about passing mce_false because
> > > > > all
> > > > > manifestly-constant-evaluated expressions will have been
> > > > > constant-evaluated
> > > > > already if possible.
> > > > >
> > > > > > cp_walk_tree (&DECL_SAVED_TREE (fndecl), cp_fold_r, &data, NULL);
> > > > > > }
> > > > > > @@ -2391,7 +2404,7 @@ cp_fold_maybe_rvalue (tree x, bool rval)
> > > > > > {
> > > > > > while (true)
> > > > > > {
> > > > > > - x = cp_fold (x);
> > > > > > + x = cp_fold (x, ff_none);
> > > > > > if (rval)
> > > > > > x = mark_rvalue_use (x);
> > > > > > if (rval && DECL_P (x)
> > > > > > @@ -2450,7 +2463,7 @@ cp_fully_fold_init (tree x)
> > > > > > if (processing_template_decl)
> > > > > > return x;
> > > > > > x = cp_fully_fold (x);
> > > > > > - cp_fold_data data (/*genericize*/false);
> > > > > > + cp_fold_data data (ff_mce_false);
> > > > > > cp_walk_tree (&x, cp_fold_r, &data, NULL);
> > > > > > return x;
> > > > > > }
> > > > > > @@ -2485,7 +2498,7 @@ clear_fold_cache (void)
> > > > > > Function returns X or its folded variant. */
> > > > > > static tree
> > > > > > -cp_fold (tree x)
> > > > > > +cp_fold (tree x, fold_flags_t flags)
> > > > > > {
> > > > > > tree op0, op1, op2, op3;
> > > > > > tree org_x = x, r = NULL_TREE;
> > > > > > @@ -2506,8 +2519,11 @@ cp_fold (tree x)
> > > > > > if (fold_cache == NULL)
> > > > > > fold_cache = hash_map<tree, tree>::create_ggc (101);
> > > > > > - if (tree *cached = fold_cache->get (x))
> > > > > > - return *cached;
> > > > > > + bool cache_p = (flags == ff_none);
> > > > > > +
> > > > > > + if (cache_p)
> > > > > > + if (tree *cached = fold_cache->get (x))
> > > > > > + return *cached;
> > > > > > uid_sensitive_constexpr_evaluation_checker c;
> > > > > > @@ -2542,7 +2558,7 @@ cp_fold (tree x)
> > > > > > Don't create a new tree if op0 != TREE_OPERAND (x, 0),
> > > > > > the
> > > > > > folding of the operand should be in the caches and if in
> > > > > > cp_fold_r
> > > > > > it will modify it in place. */
> > > > > > - op0 = cp_fold (TREE_OPERAND (x, 0));
> > > > > > + op0 = cp_fold (TREE_OPERAND (x, 0), flags);
> > > > > > if (op0 == error_mark_node)
> > > > > > x = error_mark_node;
> > > > > > break;
> > > > > > @@ -2587,7 +2603,7 @@ cp_fold (tree x)
> > > > > > {
> > > > > > tree p = maybe_undo_parenthesized_ref (x);
> > > > > > if (p != x)
> > > > > > - return cp_fold (p);
> > > > > > + return cp_fold (p, flags);
> > > > > > }
> > > > > > goto unary;
> > > > > > @@ -2779,8 +2795,8 @@ cp_fold (tree x)
> > > > > > case COND_EXPR:
> > > > > > loc = EXPR_LOCATION (x);
> > > > > > op0 = cp_fold_rvalue (TREE_OPERAND (x, 0));
> > > > > > - op1 = cp_fold (TREE_OPERAND (x, 1));
> > > > > > - op2 = cp_fold (TREE_OPERAND (x, 2));
> > > > > > + op1 = cp_fold (TREE_OPERAND (x, 1), flags);
> > > > > > + op2 = cp_fold (TREE_OPERAND (x, 2), flags);
> > > > > > if (TREE_CODE (TREE_TYPE (x)) == BOOLEAN_TYPE)
> > > > > > {
> > > > > > @@ -2870,7 +2886,7 @@ cp_fold (tree x)
> > > > > > {
> > > > > > if (!same_type_p (TREE_TYPE (x), TREE_TYPE (r)))
> > > > > > r = build_nop (TREE_TYPE (x), r);
> > > > > > - x = cp_fold (r);
> > > > > > + x = cp_fold (r, flags);
> > > > > > break;
> > > > > > }
> > > > > > }
> > > > > > @@ -2890,8 +2906,12 @@ cp_fold (tree x)
> > > > > > {
> > > > > > switch (DECL_FE_FUNCTION_CODE (callee))
> > > > > > {
> > > > > > - /* Defer folding __builtin_is_constant_evaluated. */
> > > > > > case CP_BUILT_IN_IS_CONSTANT_EVALUATED:
> > > > > > + /* Defer folding __builtin_is_constant_evaluated unless
> > > > > > + we can assume this isn't a manifestly constant-evaluated
> > > > >
> > > > > s/can assume/know/
> > > > >
> > > > > OK with those comment changes.
> > > >
> > > > Thanks a lot. Unfortunately I think the patch has a significant problem
> > > > that only just occurred to me -- disabling the cp_fold cache when the
> > > > flag ff_mce_false is set effectively makes cp_fold_function and
> > > > cp_fully_fold_init quadratic in the size of the expression (since
> > > > cp_fold_r calls cp_fold on each subtree, and cp_fold when the cache is
> > > > disabled will end up fully walking each subtree). Note that the reason
> > > > we must disable the cache is because cp_fold with ff_mce_false might
> > > > give a different folded result than without that flag if the expression
> > > > contains a suitable CALL_EXPR subexpression.
> > >
> > > Good point.
> > >
> > > > One approach to fix this complexity issue would be to parameterize the
> > > > cache according to the flags that were passed to cp_fold, which would
> > > > allow us to keep the cache enabled when ff_mce_false is set. A downside
> > > > to this approach is that the size of the cp_fold cache would essentially
> > > > double since for each tree we'd now have two cache entries, one for
> > > > flags=ff_none and another for flags=ff_mce_false.
> > >
> > > We could also clear the cache before cp_fold_function since the two folds
> > > shouldn't overlap (much).
> >
> > Makes sense, but IIUC we'd also have to clear it before (and after)
> > cp_fully_fold_init too, which unlike cp_fold_function may get called
> > in the middle of a function body.
>
> Ah sorry, I think I misunderstood your idea. Clearing the cache between
> cp_fold_function would definitely help with controlling the size of the
> cache, and indeed there shouldn't be much overlap because there isn't
> much sharing of expression trees across function bodies.
>
> However, I was curious about how big the fold_cache gets in practice,
> and it turns out it doesn't get very big at all since we regularly clear
> the fold_cache via clear_cv_and_fold_caches anyway. According to my
> experiments it doesn't get larger than about ~10k elements. So a
> doubling of that is pretty much insignificant.
>
> So ISTM parameterizing the cache is the way to go. How does the
> following look?
>
> -- >8 --
>
> Subject: [PATCH] c++: speculative constexpr and is_constant_evaluated
> [PR108243]
>
> PR c++/108243
> PR c++/97553
>
> gcc/cp/ChangeLog:
>
> * cp-gimplify.cc (enum fold_flags): Define.
> (fold_flags_t): Declare.
> (cp_fold_data::genericize): Replace this data member with ...
> (cp_fold_data::fold_flags): ... this.
> (cp_fold_r): Adjust use of cp_fold_data and calls to cp_fold.
> (cp_fold_function): Likewise.
> (cp_fold_maybe_rvalue): Likewise.
> (cp_fully_fold_init): Likewise.
> (fold_cache): Replace with ...
> (fold_caches): ... this 2-element array of caches.
> (get_fold_cache): Define.
> (clear_fold_cache): Adjust.
> (cp_fold): Add flags parameter. Call get_fold_cache.
> <case CALL_EXPR>: If ff_mce_false is set, fold
> __builtin_is_constant_evaluated to false and pass mce_false to
> maybe_constant_value.
>
> gcc/testsuite/ChangeLog:
>
> * g++.dg/opt/is_constant_evaluated1.C: New test.
> * g++.dg/opt/is_constant_evaluated2.C: New test.
> ---
> gcc/cp/cp-gimplify.cc | 103 +++++++++++++-----
> .../g++.dg/opt/is_constant_evaluated1.C | 15 +++
> .../g++.dg/opt/is_constant_evaluated2.C | 32 ++++++
> 3 files changed, 120 insertions(+), 30 deletions(-)
> create mode 100644 gcc/testsuite/g++.dg/opt/is_constant_evaluated1.C
> create mode 100644 gcc/testsuite/g++.dg/opt/is_constant_evaluated2.C
>
> diff --git a/gcc/cp/cp-gimplify.cc b/gcc/cp/cp-gimplify.cc
> index 9929d29981a..01e624bc9de 100644
> --- a/gcc/cp/cp-gimplify.cc
> +++ b/gcc/cp/cp-gimplify.cc
> @@ -43,12 +43,26 @@ along with GCC; see the file COPYING3. If not see
> #include "omp-general.h"
> #include "opts.h"
>
> +/* Flags for cp_fold and cp_fold_r. */
> +
> +enum fold_flags {
> + ff_none = 0,
> + /* Whether we're being called from cp_fold_function. */
> + ff_genericize = 1 << 0,
> + /* Whether we're folding a point where we know we're
> + definitely not in a manifestly constant-evaluated
> + context. */
> + ff_mce_false = 1 << 1,
> +};
> +
> +using fold_flags_t = int;
> +
> /* Forward declarations. */
>
> static tree cp_genericize_r (tree *, int *, void *);
> static tree cp_fold_r (tree *, int *, void *);
> static void cp_genericize_tree (tree*, bool);
> -static tree cp_fold (tree);
> +static tree cp_fold (tree, fold_flags_t);
>
> /* Genericize a TRY_BLOCK. */
>
> @@ -1012,9 +1026,8 @@ struct cp_genericize_data
> struct cp_fold_data
> {
> hash_set<tree> pset;
> - bool genericize; // called from cp_fold_function?
> -
> - cp_fold_data (bool g): genericize (g) {}
> + fold_flags_t flags;
> + cp_fold_data (fold_flags_t flags): flags (flags) {}
> };
>
> static tree
> @@ -1055,7 +1068,7 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_)
> break;
> }
>
> - *stmt_p = stmt = cp_fold (*stmt_p);
> + *stmt_p = stmt = cp_fold (*stmt_p, data->flags);
>
> if (data->pset.add (stmt))
> {
> @@ -1135,12 +1148,12 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_)
> here rather than in cp_genericize to avoid problems with the invisible
> reference transition. */
> case INIT_EXPR:
> - if (data->genericize)
> + if (data->flags & ff_genericize)
> cp_genericize_init_expr (stmt_p);
> break;
>
> case TARGET_EXPR:
> - if (data->genericize)
> + if (data->flags & ff_genericize)
> cp_genericize_target_expr (stmt_p);
>
> /* Folding might replace e.g. a COND_EXPR with a TARGET_EXPR; in
> @@ -1173,7 +1186,10 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_)
> void
> cp_fold_function (tree fndecl)
> {
> - cp_fold_data data (/*genericize*/true);
> + /* By now all manifestly-constant-evaluated expressions will have
> + been constant-evaluated already if possible, so we can safely
> + pass ff_mce_false. */
> + cp_fold_data data (ff_genericize | ff_mce_false);
> cp_walk_tree (&DECL_SAVED_TREE (fndecl), cp_fold_r, &data, NULL);
> }
>
> @@ -2391,7 +2407,7 @@ cp_fold_maybe_rvalue (tree x, bool rval)
> {
> while (true)
> {
> - x = cp_fold (x);
> + x = cp_fold (x, ff_none);
> if (rval)
> x = mark_rvalue_use (x);
> if (rval && DECL_P (x)
> @@ -2450,7 +2466,7 @@ cp_fully_fold_init (tree x)
> if (processing_template_decl)
> return x;
> x = cp_fully_fold (x);
> - cp_fold_data data (/*genericize*/false);
> + cp_fold_data data (ff_mce_false);
> cp_walk_tree (&x, cp_fold_r, &data, NULL);
> return x;
> }
> @@ -2466,15 +2482,29 @@ c_fully_fold (tree x, bool /*in_init*/, bool */*maybe_const*/, bool lval)
> return cp_fold_maybe_rvalue (x, !lval);
> }
>
> -static GTY((deletable)) hash_map<tree, tree> *fold_cache;
> +static GTY((deletable)) hash_map<tree, tree> *fold_caches[2];
> +
> +/* Subroutine of cp_fold. Returns which fold cache to use according
> + to the given flags. We need multiple caches since the result of
> + folding may depend on which flags are used. */
> +
> +static hash_map<tree, tree> *&
> +get_fold_cache (fold_flags_t flags)
> +{
> + if (flags & ff_mce_false)
> + return fold_caches[1];
> + else
> + return fold_caches[0];
> +}
>
> /* Dispose of the whole FOLD_CACHE. */
>
> void
> clear_fold_cache (void)
> {
> - if (fold_cache != NULL)
> - fold_cache->empty ();
> + for (auto& fold_cache : fold_caches)
> + if (fold_cache != NULL)
> + fold_cache->empty ();
> }
>
> /* This function tries to fold an expression X.
> @@ -2485,7 +2515,7 @@ clear_fold_cache (void)
> Function returns X or its folded variant. */
>
> static tree
> -cp_fold (tree x)
> +cp_fold (tree x, fold_flags_t flags)
> {
> tree op0, op1, op2, op3;
> tree org_x = x, r = NULL_TREE;
> @@ -2503,6 +2533,7 @@ cp_fold (tree x)
> if (DECL_P (x) || CONSTANT_CLASS_P (x))
> return x;
>
> + auto& fold_cache = get_fold_cache (flags);
> if (fold_cache == NULL)
> fold_cache = hash_map<tree, tree>::create_ggc (101);
>
> @@ -2542,7 +2573,7 @@ cp_fold (tree x)
> Don't create a new tree if op0 != TREE_OPERAND (x, 0), the
> folding of the operand should be in the caches and if in cp_fold_r
> it will modify it in place. */
> - op0 = cp_fold (TREE_OPERAND (x, 0));
> + op0 = cp_fold (TREE_OPERAND (x, 0), flags);
> if (op0 == error_mark_node)
> x = error_mark_node;
> break;
> @@ -2587,7 +2618,7 @@ cp_fold (tree x)
> {
> tree p = maybe_undo_parenthesized_ref (x);
> if (p != x)
> - return cp_fold (p);
> + return cp_fold (p, flags);
> }
> goto unary;
>
> @@ -2779,8 +2810,8 @@ cp_fold (tree x)
> case COND_EXPR:
> loc = EXPR_LOCATION (x);
> op0 = cp_fold_rvalue (TREE_OPERAND (x, 0));
Whoops, we should also propagate the flags through calls to
cp_fold_rvalue and cp_fold_maybe_rvalue from cp_fold. The below
version fixes this by adding static overloads of these functions that
additionally take and propagate a fold_flags parameter.
-- >8 --
Subject: [PATCH] c++: speculative constexpr and is_constant_evaluated
[PR108243]
PR c++/108243
PR c++/97553
gcc/cp/ChangeLog:
* cp-gimplify.cc (enum fold_flags): Define.
(fold_flags_t): Declare.
(cp_fold_data::genericize): Replace this data member with ...
(cp_fold_data::fold_flags): ... this.
(cp_fold_r): Adjust use of cp_fold_data and calls to cp_fold.
(cp_fold_function): Likewise.
(cp_fold_maybe_rvalue): Add an static overload that takes
and propagates a fold_flags_t parameter, and define the existing
public overload in terms of it.
(cp_fold_rvalue): Likewise.
(cp_fully_fold_init): Adjust use of cp_fold_data.
(fold_cache): Replace with ...
(fold_caches): ... this 2-element array of caches.
(get_fold_cache): Define.
(clear_fold_cache): Adjust.
(cp_fold): Add fold_flags_t parameter. Call get_fold_cache.
Pass flags to cp_fold, cp_fold_rvalue and cp_fold_maybe_rvalue.
<case CALL_EXPR>: If ff_mce_false is set, fold
__builtin_is_constant_evaluated to false and pass mce_false to
maybe_constant_value.
gcc/testsuite/ChangeLog:
* g++.dg/opt/is_constant_evaluated1.C: New test.
* g++.dg/opt/is_constant_evaluated2.C: New test.
---
gcc/cp/cp-gimplify.cc | 139 ++++++++++++------
.../g++.dg/opt/is_constant_evaluated1.C | 15 ++
.../g++.dg/opt/is_constant_evaluated2.C | 32 ++++
3 files changed, 144 insertions(+), 42 deletions(-)
create mode 100644 gcc/testsuite/g++.dg/opt/is_constant_evaluated1.C
create mode 100644 gcc/testsuite/g++.dg/opt/is_constant_evaluated2.C
diff --git a/gcc/cp/cp-gimplify.cc b/gcc/cp/cp-gimplify.cc
index 9929d29981a..edece6b7a8a 100644
--- a/gcc/cp/cp-gimplify.cc
+++ b/gcc/cp/cp-gimplify.cc
@@ -43,12 +43,26 @@ along with GCC; see the file COPYING3. If not see
#include "omp-general.h"
#include "opts.h"
+/* Flags for cp_fold and cp_fold_r. */
+
+enum fold_flags {
+ ff_none = 0,
+ /* Whether we're being called from cp_fold_function. */
+ ff_genericize = 1 << 0,
+ /* Whether we're folding a point where we know we're
+ definitely not in a manifestly constant-evaluated
+ context. */
+ ff_mce_false = 1 << 1,
+};
+
+using fold_flags_t = int;
+
/* Forward declarations. */
static tree cp_genericize_r (tree *, int *, void *);
static tree cp_fold_r (tree *, int *, void *);
static void cp_genericize_tree (tree*, bool);
-static tree cp_fold (tree);
+static tree cp_fold (tree, fold_flags_t);
/* Genericize a TRY_BLOCK. */
@@ -1012,9 +1026,8 @@ struct cp_genericize_data
struct cp_fold_data
{
hash_set<tree> pset;
- bool genericize; // called from cp_fold_function?
-
- cp_fold_data (bool g): genericize (g) {}
+ fold_flags_t flags;
+ cp_fold_data (fold_flags_t flags): flags (flags) {}
};
static tree
@@ -1055,7 +1068,7 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_)
break;
}
- *stmt_p = stmt = cp_fold (*stmt_p);
+ *stmt_p = stmt = cp_fold (*stmt_p, data->flags);
if (data->pset.add (stmt))
{
@@ -1135,12 +1148,12 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_)
here rather than in cp_genericize to avoid problems with the invisible
reference transition. */
case INIT_EXPR:
- if (data->genericize)
+ if (data->flags & ff_genericize)
cp_genericize_init_expr (stmt_p);
break;
case TARGET_EXPR:
- if (data->genericize)
+ if (data->flags & ff_genericize)
cp_genericize_target_expr (stmt_p);
/* Folding might replace e.g. a COND_EXPR with a TARGET_EXPR; in
@@ -1173,7 +1186,10 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_)
void
cp_fold_function (tree fndecl)
{
- cp_fold_data data (/*genericize*/true);
+ /* By now all manifestly-constant-evaluated expressions will have
+ been constant-evaluated already if possible, so we can safely
+ pass ff_mce_false. */
+ cp_fold_data data (ff_genericize | ff_mce_false);
cp_walk_tree (&DECL_SAVED_TREE (fndecl), cp_fold_r, &data, NULL);
}
@@ -2386,12 +2402,12 @@ cxx_omp_disregard_value_expr (tree decl, bool shared)
/* Fold expression X which is used as an rvalue if RVAL is true. */
-tree
-cp_fold_maybe_rvalue (tree x, bool rval)
+static tree
+cp_fold_maybe_rvalue (tree x, bool rval, fold_flags_t flags)
{
while (true)
{
- x = cp_fold (x);
+ x = cp_fold (x, flags);
if (rval)
x = mark_rvalue_use (x);
if (rval && DECL_P (x)
@@ -2409,12 +2425,24 @@ cp_fold_maybe_rvalue (tree x, bool rval)
return x;
}
+tree
+cp_fold_maybe_rvalue (tree x, bool rval)
+{
+ return cp_fold_maybe_rvalue (x, rval, ff_none);
+}
+
/* Fold expression X which is used as an rvalue. */
+static tree
+cp_fold_rvalue (tree x, fold_flags_t flags)
+{
+ return cp_fold_maybe_rvalue (x, true, flags);
+}
+
tree
cp_fold_rvalue (tree x)
{
- return cp_fold_maybe_rvalue (x, true);
+ return cp_fold_rvalue (x, ff_none);
}
/* Perform folding on expression X. */
@@ -2450,7 +2478,7 @@ cp_fully_fold_init (tree x)
if (processing_template_decl)
return x;
x = cp_fully_fold (x);
- cp_fold_data data (/*genericize*/false);
+ cp_fold_data data (ff_mce_false);
cp_walk_tree (&x, cp_fold_r, &data, NULL);
return x;
}
@@ -2466,15 +2494,29 @@ c_fully_fold (tree x, bool /*in_init*/, bool */*maybe_const*/, bool lval)
return cp_fold_maybe_rvalue (x, !lval);
}
-static GTY((deletable)) hash_map<tree, tree> *fold_cache;
+static GTY((deletable)) hash_map<tree, tree> *fold_caches[2];
+
+/* Subroutine of cp_fold. Returns which fold cache to use according
+ to the given flags. We need multiple caches since the result of
+ folding may depend on which flags are used. */
+
+static hash_map<tree, tree> *&
+get_fold_cache (fold_flags_t flags)
+{
+ if (flags & ff_mce_false)
+ return fold_caches[1];
+ else
+ return fold_caches[0];
+}
/* Dispose of the whole FOLD_CACHE. */
void
clear_fold_cache (void)
{
- if (fold_cache != NULL)
- fold_cache->empty ();
+ for (auto& fold_cache : fold_caches)
+ if (fold_cache != NULL)
+ fold_cache->empty ();
}
/* This function tries to fold an expression X.
@@ -2485,7 +2527,7 @@ clear_fold_cache (void)
Function returns X or its folded variant. */
static tree
-cp_fold (tree x)
+cp_fold (tree x, fold_flags_t flags)
{
tree op0, op1, op2, op3;
tree org_x = x, r = NULL_TREE;
@@ -2503,6 +2545,7 @@ cp_fold (tree x)
if (DECL_P (x) || CONSTANT_CLASS_P (x))
return x;
+ auto& fold_cache = get_fold_cache (flags);
if (fold_cache == NULL)
fold_cache = hash_map<tree, tree>::create_ggc (101);
@@ -2517,7 +2560,7 @@ cp_fold (tree x)
case CLEANUP_POINT_EXPR:
/* Strip CLEANUP_POINT_EXPR if the expression doesn't have side
effects. */
- r = cp_fold_rvalue (TREE_OPERAND (x, 0));
+ r = cp_fold_rvalue (TREE_OPERAND (x, 0), flags);
if (!TREE_SIDE_EFFECTS (r))
x = r;
break;
@@ -2542,14 +2585,14 @@ cp_fold (tree x)
Don't create a new tree if op0 != TREE_OPERAND (x, 0), the
folding of the operand should be in the caches and if in cp_fold_r
it will modify it in place. */
- op0 = cp_fold (TREE_OPERAND (x, 0));
+ op0 = cp_fold (TREE_OPERAND (x, 0), flags);
if (op0 == error_mark_node)
x = error_mark_node;
break;
}
loc = EXPR_LOCATION (x);
- op0 = cp_fold_maybe_rvalue (TREE_OPERAND (x, 0), rval_ops);
+ op0 = cp_fold_maybe_rvalue (TREE_OPERAND (x, 0), rval_ops, flags);
if (code == CONVERT_EXPR
&& SCALAR_TYPE_P (TREE_TYPE (x))
@@ -2577,7 +2620,7 @@ cp_fold (tree x)
break;
case EXCESS_PRECISION_EXPR:
- op0 = cp_fold_maybe_rvalue (TREE_OPERAND (x, 0), rval_ops);
+ op0 = cp_fold_maybe_rvalue (TREE_OPERAND (x, 0), rval_ops, flags);
x = fold_convert_loc (EXPR_LOCATION (x), TREE_TYPE (x), op0);
break;
@@ -2587,13 +2630,13 @@ cp_fold (tree x)
{
tree p = maybe_undo_parenthesized_ref (x);
if (p != x)
- return cp_fold (p);
+ return cp_fold (p, flags);
}
goto unary;
case ADDR_EXPR:
loc = EXPR_LOCATION (x);
- op0 = cp_fold_maybe_rvalue (TREE_OPERAND (x, 0), false);
+ op0 = cp_fold_maybe_rvalue (TREE_OPERAND (x, 0), false, flags);
/* Cope with user tricks that amount to offsetof. */
if (op0 != error_mark_node
@@ -2630,7 +2673,7 @@ cp_fold (tree x)
unary:
loc = EXPR_LOCATION (x);
- op0 = cp_fold_maybe_rvalue (TREE_OPERAND (x, 0), rval_ops);
+ op0 = cp_fold_maybe_rvalue (TREE_OPERAND (x, 0), rval_ops, flags);
finish_unary:
if (op0 != TREE_OPERAND (x, 0))
@@ -2657,7 +2700,7 @@ cp_fold (tree x)
break;
case UNARY_PLUS_EXPR:
- op0 = cp_fold_rvalue (TREE_OPERAND (x, 0));
+ op0 = cp_fold_rvalue (TREE_OPERAND (x, 0), flags);
if (op0 == error_mark_node)
x = error_mark_node;
else
@@ -2711,8 +2754,8 @@ cp_fold (tree x)
case RANGE_EXPR: case COMPLEX_EXPR:
loc = EXPR_LOCATION (x);
- op0 = cp_fold_maybe_rvalue (TREE_OPERAND (x, 0), rval_ops);
- op1 = cp_fold_rvalue (TREE_OPERAND (x, 1));
+ op0 = cp_fold_maybe_rvalue (TREE_OPERAND (x, 0), rval_ops, flags);
+ op1 = cp_fold_rvalue (TREE_OPERAND (x, 1), flags);
/* decltype(nullptr) has only one value, so optimize away all comparisons
with that type right away, keeping them in the IL causes troubles for
@@ -2778,9 +2821,9 @@ cp_fold (tree x)
case VEC_COND_EXPR:
case COND_EXPR:
loc = EXPR_LOCATION (x);
- op0 = cp_fold_rvalue (TREE_OPERAND (x, 0));
- op1 = cp_fold (TREE_OPERAND (x, 1));
- op2 = cp_fold (TREE_OPERAND (x, 2));
+ op0 = cp_fold_rvalue (TREE_OPERAND (x, 0), flags);
+ op1 = cp_fold (TREE_OPERAND (x, 1), flags);
+ op2 = cp_fold (TREE_OPERAND (x, 2), flags);
if (TREE_CODE (TREE_TYPE (x)) == BOOLEAN_TYPE)
{
@@ -2870,7 +2913,7 @@ cp_fold (tree x)
{
if (!same_type_p (TREE_TYPE (x), TREE_TYPE (r)))
r = build_nop (TREE_TYPE (x), r);
- x = cp_fold (r);
+ x = cp_fold (r, flags);
break;
}
}
@@ -2890,8 +2933,12 @@ cp_fold (tree x)
{
switch (DECL_FE_FUNCTION_CODE (callee))
{
- /* Defer folding __builtin_is_constant_evaluated. */
case CP_BUILT_IN_IS_CONSTANT_EVALUATED:
+ /* Defer folding __builtin_is_constant_evaluated unless
+ we know this isn't a manifestly constant-evaluated
+ context. */
+ if (flags & ff_mce_false)
+ x = boolean_false_node;
break;
case CP_BUILT_IN_SOURCE_LOCATION:
x = fold_builtin_source_location (x);
@@ -2924,7 +2971,7 @@ cp_fold (tree x)
int m = call_expr_nargs (x);
for (int i = 0; i < m; i++)
{
- r = cp_fold (CALL_EXPR_ARG (x, i));
+ r = cp_fold (CALL_EXPR_ARG (x, i), flags);
if (r != CALL_EXPR_ARG (x, i))
{
if (r == error_mark_node)
@@ -2947,7 +2994,7 @@ cp_fold (tree x)
if (TREE_CODE (r) != CALL_EXPR)
{
- x = cp_fold (r);
+ x = cp_fold (r, flags);
break;
}
@@ -2960,7 +3007,15 @@ cp_fold (tree x)
constant, but the call followed by an INDIRECT_REF is. */
if (callee && DECL_DECLARED_CONSTEXPR_P (callee)
&& !flag_no_inline)
- r = maybe_constant_value (x);
+ {
+ mce_value manifestly_const_eval = mce_unknown;
+ if (flags & ff_mce_false)
+ /* Allow folding __builtin_is_constant_evaluated to false during
+ constexpr evaluation of this call. */
+ manifestly_const_eval = mce_false;
+ r = maybe_constant_value (x, /*decl=*/NULL_TREE,
+ manifestly_const_eval);
+ }
optimize = sv;
if (TREE_CODE (r) != CALL_EXPR)
@@ -2987,7 +3042,7 @@ cp_fold (tree x)
vec<constructor_elt, va_gc> *nelts = NULL;
FOR_EACH_VEC_SAFE_ELT (elts, i, p)
{
- tree op = cp_fold (p->value);
+ tree op = cp_fold (p->value, flags);
if (op != p->value)
{
if (op == error_mark_node)
@@ -3018,7 +3073,7 @@ cp_fold (tree x)
for (int i = 0; i < n; i++)
{
- tree op = cp_fold (TREE_VEC_ELT (x, i));
+ tree op = cp_fold (TREE_VEC_ELT (x, i), flags);
if (op != TREE_VEC_ELT (x, i))
{
if (!changed)
@@ -3035,10 +3090,10 @@ cp_fold (tree x)
case ARRAY_RANGE_REF:
loc = EXPR_LOCATION (x);
- op0 = cp_fold (TREE_OPERAND (x, 0));
- op1 = cp_fold (TREE_OPERAND (x, 1));
- op2 = cp_fold (TREE_OPERAND (x, 2));
- op3 = cp_fold (TREE_OPERAND (x, 3));
+ op0 = cp_fold (TREE_OPERAND (x, 0), flags);
+ op1 = cp_fold (TREE_OPERAND (x, 1), flags);
+ op2 = cp_fold (TREE_OPERAND (x, 2), flags);
+ op3 = cp_fold (TREE_OPERAND (x, 3), flags);
if (op0 != TREE_OPERAND (x, 0)
|| op1 != TREE_OPERAND (x, 1)
@@ -3066,7 +3121,7 @@ cp_fold (tree x)
/* A SAVE_EXPR might contain e.g. (0 * i) + (0 * j), which, after
folding, evaluates to an invariant. In that case no need to wrap
this folded tree with a SAVE_EXPR. */
- r = cp_fold (TREE_OPERAND (x, 0));
+ r = cp_fold (TREE_OPERAND (x, 0), flags);
if (tree_invariant_p (r))
x = r;
break;
diff --git a/gcc/testsuite/g++.dg/opt/is_constant_evaluated1.C b/gcc/testsuite/g++.dg/opt/is_constant_evaluated1.C
new file mode 100644
index 00000000000..983410b9e83
--- /dev/null
+++ b/gcc/testsuite/g++.dg/opt/is_constant_evaluated1.C
@@ -0,0 +1,15 @@
+// PR c++/108243
+// { dg-do compile { target c++11 } }
+// { dg-additional-options "-O -fdump-tree-original" }
+
+struct A {
+ constexpr A(int n, int m) : n(n), m(m) { }
+ int n, m;
+};
+
+A* f(int n) {
+ static A a = {n, __builtin_is_constant_evaluated()};
+ return &a;
+}
+
+// { dg-final { scan-tree-dump-not "is_constant_evaluated" "original" } }
diff --git a/gcc/testsuite/g++.dg/opt/is_constant_evaluated2.C b/gcc/testsuite/g++.dg/opt/is_constant_evaluated2.C
new file mode 100644
index 00000000000..ed964e20a7a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/opt/is_constant_evaluated2.C
@@ -0,0 +1,32 @@
+// PR c++/97553
+// { dg-do compile { target c++11 } }
+// { dg-additional-options "-O -fdump-tree-original" }
+
+constexpr int foo() {
+ return __builtin_is_constant_evaluated() + 1;
+}
+
+#if __cpp_if_consteval
+constexpr int bar() {
+ if consteval {
+ return 5;
+ } else {
+ return 4;
+ }
+}
+#endif
+
+int p, q;
+
+int main() {
+ p = foo();
+#if __cpp_if_consteval
+ q = bar();
+#endif
+}
+
+// { dg-final { scan-tree-dump "p = 1" "original" } }
+// { dg-final { scan-tree-dump-not "= foo" "original" } }
+
+// { dg-final { scan-tree-dump "q = 4" "original" { target c++23 } } }
+// { dg-final { scan-tree-dump-not "= bar" "original" { target c++23 } } }
--
2.39.1.433.g23c56f7bd5
> - op1 = cp_fold (TREE_OPERAND (x, 1));
> - op2 = cp_fold (TREE_OPERAND (x, 2));
> + op1 = cp_fold (TREE_OPERAND (x, 1), flags);
> + op2 = cp_fold (TREE_OPERAND (x, 2), flags);
>
> if (TREE_CODE (TREE_TYPE (x)) == BOOLEAN_TYPE)
> {
> @@ -2870,7 +2901,7 @@ cp_fold (tree x)
> {
> if (!same_type_p (TREE_TYPE (x), TREE_TYPE (r)))
> r = build_nop (TREE_TYPE (x), r);
> - x = cp_fold (r);
> + x = cp_fold (r, flags);
> break;
> }
> }
> @@ -2890,8 +2921,12 @@ cp_fold (tree x)
> {
> switch (DECL_FE_FUNCTION_CODE (callee))
> {
> - /* Defer folding __builtin_is_constant_evaluated. */
> case CP_BUILT_IN_IS_CONSTANT_EVALUATED:
> + /* Defer folding __builtin_is_constant_evaluated unless
> + we know this isn't a manifestly constant-evaluated
> + context. */
> + if (flags & ff_mce_false)
> + x = boolean_false_node;
> break;
> case CP_BUILT_IN_SOURCE_LOCATION:
> x = fold_builtin_source_location (x);
> @@ -2924,7 +2959,7 @@ cp_fold (tree x)
> int m = call_expr_nargs (x);
> for (int i = 0; i < m; i++)
> {
> - r = cp_fold (CALL_EXPR_ARG (x, i));
> + r = cp_fold (CALL_EXPR_ARG (x, i), flags);
> if (r != CALL_EXPR_ARG (x, i))
> {
> if (r == error_mark_node)
> @@ -2947,7 +2982,7 @@ cp_fold (tree x)
>
> if (TREE_CODE (r) != CALL_EXPR)
> {
> - x = cp_fold (r);
> + x = cp_fold (r, flags);
> break;
> }
>
> @@ -2960,7 +2995,15 @@ cp_fold (tree x)
> constant, but the call followed by an INDIRECT_REF is. */
> if (callee && DECL_DECLARED_CONSTEXPR_P (callee)
> && !flag_no_inline)
> - r = maybe_constant_value (x);
> + {
> + mce_value manifestly_const_eval = mce_unknown;
> + if (flags & ff_mce_false)
> + /* Allow folding __builtin_is_constant_evaluated to false during
> + constexpr evaluation of this call. */
> + manifestly_const_eval = mce_false;
> + r = maybe_constant_value (x, /*decl=*/NULL_TREE,
> + manifestly_const_eval);
> + }
> optimize = sv;
>
> if (TREE_CODE (r) != CALL_EXPR)
> @@ -2987,7 +3030,7 @@ cp_fold (tree x)
> vec<constructor_elt, va_gc> *nelts = NULL;
> FOR_EACH_VEC_SAFE_ELT (elts, i, p)
> {
> - tree op = cp_fold (p->value);
> + tree op = cp_fold (p->value, flags);
> if (op != p->value)
> {
> if (op == error_mark_node)
> @@ -3018,7 +3061,7 @@ cp_fold (tree x)
>
> for (int i = 0; i < n; i++)
> {
> - tree op = cp_fold (TREE_VEC_ELT (x, i));
> + tree op = cp_fold (TREE_VEC_ELT (x, i), flags);
> if (op != TREE_VEC_ELT (x, i))
> {
> if (!changed)
> @@ -3035,10 +3078,10 @@ cp_fold (tree x)
> case ARRAY_RANGE_REF:
>
> loc = EXPR_LOCATION (x);
> - op0 = cp_fold (TREE_OPERAND (x, 0));
> - op1 = cp_fold (TREE_OPERAND (x, 1));
> - op2 = cp_fold (TREE_OPERAND (x, 2));
> - op3 = cp_fold (TREE_OPERAND (x, 3));
> + op0 = cp_fold (TREE_OPERAND (x, 0), flags);
> + op1 = cp_fold (TREE_OPERAND (x, 1), flags);
> + op2 = cp_fold (TREE_OPERAND (x, 2), flags);
> + op3 = cp_fold (TREE_OPERAND (x, 3), flags);
>
> if (op0 != TREE_OPERAND (x, 0)
> || op1 != TREE_OPERAND (x, 1)
> @@ -3066,7 +3109,7 @@ cp_fold (tree x)
> /* A SAVE_EXPR might contain e.g. (0 * i) + (0 * j), which, after
> folding, evaluates to an invariant. In that case no need to wrap
> this folded tree with a SAVE_EXPR. */
> - r = cp_fold (TREE_OPERAND (x, 0));
> + r = cp_fold (TREE_OPERAND (x, 0), flags);
> if (tree_invariant_p (r))
> x = r;
> break;
> diff --git a/gcc/testsuite/g++.dg/opt/is_constant_evaluated1.C b/gcc/testsuite/g++.dg/opt/is_constant_evaluated1.C
> new file mode 100644
> index 00000000000..983410b9e83
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/opt/is_constant_evaluated1.C
> @@ -0,0 +1,15 @@
> +// PR c++/108243
> +// { dg-do compile { target c++11 } }
> +// { dg-additional-options "-O -fdump-tree-original" }
> +
> +struct A {
> + constexpr A(int n, int m) : n(n), m(m) { }
> + int n, m;
> +};
> +
> +A* f(int n) {
> + static A a = {n, __builtin_is_constant_evaluated()};
> + return &a;
> +}
> +
> +// { dg-final { scan-tree-dump-not "is_constant_evaluated" "original" } }
> diff --git a/gcc/testsuite/g++.dg/opt/is_constant_evaluated2.C b/gcc/testsuite/g++.dg/opt/is_constant_evaluated2.C
> new file mode 100644
> index 00000000000..ed964e20a7a
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/opt/is_constant_evaluated2.C
> @@ -0,0 +1,32 @@
> +// PR c++/97553
> +// { dg-do compile { target c++11 } }
> +// { dg-additional-options "-O -fdump-tree-original" }
> +
> +constexpr int foo() {
> + return __builtin_is_constant_evaluated() + 1;
> +}
> +
> +#if __cpp_if_consteval
> +constexpr int bar() {
> + if consteval {
> + return 5;
> + } else {
> + return 4;
> + }
> +}
> +#endif
> +
> +int p, q;
> +
> +int main() {
> + p = foo();
> +#if __cpp_if_consteval
> + q = bar();
> +#endif
> +}
> +
> +// { dg-final { scan-tree-dump "p = 1" "original" } }
> +// { dg-final { scan-tree-dump-not "= foo" "original" } }
> +
> +// { dg-final { scan-tree-dump "q = 4" "original" { target c++23 } } }
> +// { dg-final { scan-tree-dump-not "= bar" "original" { target c++23 } } }
> --
> 2.39.1.433.g23c56f7bd5
>
>
> >
> > >
> > > > Another approach would be to split out the trial constexpr evaluation
> > > > part of cp_fold's CALL_EXPR handling, parameterize that, and call it
> > > > directly from cp_fold_r. With this approach we wouldn't perform as much
> > > > folding, e.g.
> > > >
> > > > int n = 41 + !std::is_constant_evaluated();
> > > >
> > > > would get folded to 1 + 41 rather than 42. But I suspect this would
> > > > give us 95% of the reapable benefits of the above approach.
> > > >
> > > > I think I'm leaning towards this second approach, which the below patch
> > > > implements instead. What do you think? Bootstrapped and regtested on
> > > > x86_64-pc-linux-gnu.
> > >
> > > That sounds reasonable, but...
> > >
> > > > -- >8 --
> > > >
> > > > Subject: [PATCH] c++: speculative constexpr and is_constant_evaluated
> > > > [PR108243]
> > > >
> > > > This PR illustrates that __builtin_is_constant_evaluated currently acts
> > > > as an optimization barrier for our speculative constexpr evaluation,
> > > > since we don't want to prematurely fold the builtin to false before the
> > > > expression in question undergoes constant evaluation as in a manifestly
> > > > constant-evaluated context (in which case the builtin must instead be
> > > > folded to true).
> > > >
> > > > This patch fixes this by permitting __builtin_is_constant_evaluated
> > > > to get folded to false from cp_fold_r, where we know we're done with
> > > > proper constant evaluation (of manifestly constant-evaluated contexts).
> > > >
> > > > PR c++/108243
> > > > PR c++/97553
> > > >
> > > > gcc/cp/ChangeLog:
> > > >
> > > > * cp-gimplify.cc
> > > > (cp_fold_r): Remove redundant *stmt_p assignments. After
> > > > calling cp_fold, call maybe_fold_constexpr_call with mce_false.
> > > > (cp_fold) <case CALL_EXPR>: Split out trial constexpr evaluation
> > > > into ...
> > > > (maybe_fold_constexpr_call): ... here.
> > > >
> > > > gcc/testsuite/ChangeLog:
> > > >
> > > > * g++.dg/opt/is_constant_evaluated1.C: New test.
> > > > * g++.dg/opt/is_constant_evaluated2.C: New test.
> > > > ---
> > > > gcc/cp/cp-gimplify.cc | 55 +++++++++++++++----
> > > > .../g++.dg/opt/is_constant_evaluated1.C | 20 +++++++
> > > > .../g++.dg/opt/is_constant_evaluated2.C | 32 +++++++++++
> > > > 3 files changed, 95 insertions(+), 12 deletions(-)
> > > > create mode 100644 gcc/testsuite/g++.dg/opt/is_constant_evaluated1.C
> > > > create mode 100644 gcc/testsuite/g++.dg/opt/is_constant_evaluated2.C
> > > >
> > > > diff --git a/gcc/cp/cp-gimplify.cc b/gcc/cp/cp-gimplify.cc
> > > > index 9929d29981a..dca55056b2c 100644
> > > > --- a/gcc/cp/cp-gimplify.cc
> > > > +++ b/gcc/cp/cp-gimplify.cc
> > > > @@ -49,6 +49,7 @@ static tree cp_genericize_r (tree *, int *, void *);
> > > > static tree cp_fold_r (tree *, int *, void *);
> > > > static void cp_genericize_tree (tree*, bool);
> > > > static tree cp_fold (tree);
> > > > +static tree maybe_fold_constexpr_call (tree, mce_value);
> > > > /* Genericize a TRY_BLOCK. */
> > > > @@ -1034,7 +1035,7 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void
> > > > *data_)
> > > > error_at (PTRMEM_CST_LOCATION (stmt),
> > > > "taking address of an immediate function %qD",
> > > > PTRMEM_CST_MEMBER (stmt));
> > > > - stmt = *stmt_p = build_zero_cst (TREE_TYPE (stmt));
> > > > + stmt = build_zero_cst (TREE_TYPE (stmt));
> > > > break;
> > > > }
> > > > break;
> > > > @@ -1046,7 +1047,7 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void
> > > > *data_)
> > > > error_at (EXPR_LOCATION (stmt),
> > > > "taking address of an immediate function %qD",
> > > > TREE_OPERAND (stmt, 0));
> > > > - stmt = *stmt_p = build_zero_cst (TREE_TYPE (stmt));
> > > > + stmt = build_zero_cst (TREE_TYPE (stmt));
> > > > break;
> > > > }
> > > > break;
> > > > @@ -1055,7 +1056,17 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void
> > > > *data_)
> > > > break;
> > > > }
> > > > - *stmt_p = stmt = cp_fold (*stmt_p);
> > > > + stmt = cp_fold (stmt);
> > > > +
> > > > + if (TREE_CODE (stmt) == CALL_EXPR)
> > > > + /* Since cp_fold_r is called (from cp_genericize, cp_fold_function
> > > > + and cp_fully_fold_init) only after the overall expression has been
> > > > + considered for constant-evaluation, we can by now safely fold any
> > > > + remaining __builtin_is_constant_evaluated calls to false, so try
> > > > + constexpr expansion with mce_false. */
> > > > + stmt = maybe_fold_constexpr_call (stmt, mce_false);
> > > > +
> > > > + *stmt_p = stmt;
> > > > if (data->pset.add (stmt))
> > > > {
> > > > @@ -2952,15 +2963,10 @@ cp_fold (tree x)
> > > > }
> > > > optimize = nw;
> > > > -
> > > > - /* Invoke maybe_constant_value for functions declared
> > > > - constexpr and not called with AGGR_INIT_EXPRs.
> > > > - TODO:
> > > > - Do constexpr expansion of expressions where the call itself is not
> > > > - constant, but the call followed by an INDIRECT_REF is. */
> > > > - if (callee && DECL_DECLARED_CONSTEXPR_P (callee)
> > > > - && !flag_no_inline)
> > > > - r = maybe_constant_value (x);
> > > > + /* Pass mce_unknown to defer folding __builtin_is_constant_evaluated
> > > > + since we don't know if we're in a manifestly constant-evaluated
> > > > + context that hasn't yet been constant-evaluated. */
> > > > + r = maybe_fold_constexpr_call (x, mce_unknown);
> > >
> > > It seems unfortunate to try to fold both here and in cp_fold_r.
> >
> > Yes, though I'm afraid some duplication of work is pretty much
> > unavoidable. Even if in cp_fold_r we did something like
> >
> > if (TREE_CODE (stmt) == CALL_EXPR)
> > /* cp_fold_call_expr is the entire CALL_EXPR case of cp_fold. */
> > stmt = cp_fold_call_expr (stmt, mce_false);
> > else
> > stmt = cp_fold (stmt);
> >
> > instead of
> >
> > stmt = cp_fold (stmt);
> > if (TREE_CODE (stmt) == CALL_EXPR)
> > stmt = maybe_fold_constexpr_call (stmt, mce_false);
> >
> > we would still end up doing cp_fold on the CALL_EXPR if it's a
> > subexpression of some larger expression (since cp_fold is recursive).
> >
> > And even if we went with the original approach of parameterizing cp_fold
> > according to manifestly_const_eval totally, we would still end up trying
> > to fold some constexpr calls twice, first with flags=ff_none (during
> > some on-the-spot folding) and again with flags=ff_mce_false (during
> > cp_fold_function), I think.
> >
> > >
> > > Does this patch still fold __builtin_is_constant_evaluated() even though it no
> > > longer touches the cp_fold builtin handling?
> >
> > Indeed it doesn't, instead __builtin_is_constant_evaluated() will
> > continue to get folded during gimplification. I thought folding it
> > might not be benefical with this approach, but on second thought we
> > should do it for consistency at least. The following incremental
> > patch seems to work:
> >
> > diff --git a/gcc/cp/cp-gimplify.cc b/gcc/cp/cp-gimplify.cc
> > index dca55056b2c..250147bde0f 100644
> > --- a/gcc/cp/cp-gimplify.cc
> > +++ b/gcc/cp/cp-gimplify.cc
> > @@ -3124,6 +3124,14 @@ maybe_fold_constexpr_call (tree x, mce_value manifestly_const_eval)
> > if (TREE_CODE (r) != CALL_EXPR)
> > return r;
> > }
> > + if (fndecl_built_in_p (callee, CP_BUILT_IN_IS_CONSTANT_EVALUATED,
> > + BUILT_IN_FRONTEND))
> > + {
> > + if (manifestly_const_eval == mce_true)
> > + return boolean_true_node;
> > + else if (manifestly_const_eval == mce_false)
> > + return boolean_false_node;
> > + }
> > return x;
> > }
> >
> >
> > >
> > > > optimize = sv;
> > > > if (TREE_CODE (r) != CALL_EXPR)
> > > > @@ -3096,6 +3102,31 @@ cp_fold (tree x)
> > > > return x;
> > > > }
> > > > +/* If the CALL_EXPR X calls a constexpr function, try expanding it via
> > > > + constexpr evaluation. Returns the expanded result or X if constexpr
> > > > + evaluation wasn't possible.
> > > > +
> > > > + TODO: Do constexpr expansion of expressions where the call itself
> > > > + is not constant, but the call followed by an INDIRECT_REF is. */
> > > > +
> > > > +static tree
> > > > +maybe_fold_constexpr_call (tree x, mce_value manifestly_const_eval)
> > > > +{
> > > > + if (flag_no_inline)
> > > > + return x;
> > > > + tree callee = get_callee_fndecl (x);
> > > > + if (!callee)
> > > > + return x;
> > > > + if (DECL_DECLARED_CONSTEXPR_P (callee))
> > > > + {
> > > > + tree r = maybe_constant_value (x, /*decl=*/NULL_TREE,
> > > > + manifestly_const_eval);
> > > > + if (TREE_CODE (r) != CALL_EXPR)
> > > > + return r;
> > > > + }
> > > > + return x;
> > > > +}
> > > > +
> > > > /* Look up "hot", "cold", "likely" or "unlikely" in attribute list LIST.
> > > > */
> > > > tree
> > > > diff --git a/gcc/testsuite/g++.dg/opt/is_constant_evaluated1.C
> > > > b/gcc/testsuite/g++.dg/opt/is_constant_evaluated1.C
> > > > new file mode 100644
> > > > index 00000000000..2123f20e3e5
> > > > --- /dev/null
> > > > +++ b/gcc/testsuite/g++.dg/opt/is_constant_evaluated1.C
> > > > @@ -0,0 +1,20 @@
> > > > +// PR c++/108243
> > > > +// { dg-do compile { target c++11 } }
> > > > +// { dg-additional-options "-O -fdump-tree-original" }
> > > > +
> > > > +struct A {
> > > > + constexpr A(int n, int m) : n(n), m(m) { }
> > > > + int n, m;
> > > > +};
> > > > +
> > > > +constexpr int foo(int n) {
> > > > + return n + !__builtin_is_constant_evaluated();
> > > > +}
> > > > +
> > > > +A* f(int n) {
> > > > + static A a = {n, foo(41)};
> > > > + return &a;
> > > > +}
> > > > +
> > > > +// { dg-final { scan-tree-dump "42" "original" } }
> > > > +// { dg-final { scan-tree-dump-not "foo \\(41\\)" "original" } }
> > > > diff --git a/gcc/testsuite/g++.dg/opt/is_constant_evaluated2.C
> > > > b/gcc/testsuite/g++.dg/opt/is_constant_evaluated2.C
> > > > new file mode 100644
> > > > index 00000000000..ed964e20a7a
> > > > --- /dev/null
> > > > +++ b/gcc/testsuite/g++.dg/opt/is_constant_evaluated2.C
> > > > @@ -0,0 +1,32 @@
> > > > +// PR c++/97553
> > > > +// { dg-do compile { target c++11 } }
> > > > +// { dg-additional-options "-O -fdump-tree-original" }
> > > > +
> > > > +constexpr int foo() {
> > > > + return __builtin_is_constant_evaluated() + 1;
> > > > +}
> > > > +
> > > > +#if __cpp_if_consteval
> > > > +constexpr int bar() {
> > > > + if consteval {
> > > > + return 5;
> > > > + } else {
> > > > + return 4;
> > > > + }
> > > > +}
> > > > +#endif
> > > > +
> > > > +int p, q;
> > > > +
> > > > +int main() {
> > > > + p = foo();
> > > > +#if __cpp_if_consteval
> > > > + q = bar();
> > > > +#endif
> > > > +}
> > > > +
> > > > +// { dg-final { scan-tree-dump "p = 1" "original" } }
> > > > +// { dg-final { scan-tree-dump-not "= foo" "original" } }
> > > > +
> > > > +// { dg-final { scan-tree-dump "q = 4" "original" { target c++23 } } }
> > > > +// { dg-final { scan-tree-dump-not "= bar" "original" { target c++23 } } }
> > >
> > >
> >
>
next prev parent reply other threads:[~2023-02-10 16:51 UTC|newest]
Thread overview: 15+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-01-27 22:02 [PATCH 1/2] c++: make manifestly_const_eval tri-state Patrick Palka
2023-01-27 22:02 ` [PATCH 2/2] c++: speculative constexpr and is_constant_evaluated [PR108243] Patrick Palka
2023-01-27 22:05 ` Patrick Palka
2023-01-30 20:05 ` Jason Merrill
2023-02-03 20:51 ` Patrick Palka
2023-02-03 20:57 ` Patrick Palka
2023-02-05 20:11 ` Jason Merrill
2023-02-09 17:36 ` Patrick Palka
2023-02-09 23:36 ` Jason Merrill
2023-02-10 1:32 ` Patrick Palka
2023-02-10 14:48 ` Patrick Palka
2023-02-10 16:51 ` Patrick Palka [this message]
2023-02-14 23:02 ` Jason Merrill
2023-01-30 20:02 ` [PATCH 1/2] c++: make manifestly_const_eval tri-state Jason Merrill
2023-02-03 21:21 ` Patrick Palka
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=af7fe73a-80e9-cd5c-1df3-c627b289d6a2@idea \
--to=ppalka@redhat.com \
--cc=gcc-patches@gcc.gnu.org \
--cc=jason@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).