[C++] Implement P0595R1 - so far as __builtin_is_constant_evaluated rather than std::is_constant_evaluated magic builtin

Message ID 20180722193121.GC7166@tucnak
State New
Headers show
Series
  • [C++] Implement P0595R1 - so far as __builtin_is_constant_evaluated rather than std::is_constant_evaluated magic builtin
Related show

Commit Message

Jakub Jelinek July 22, 2018, 7:31 p.m.
Hi!

As part of the PR86590 discussions that the current libstdc++
__constant_string is extremely costly, because we don't fold the
__builtin_constant_p in the loop early enough and because Richard doesn't
want __builtin_early_constant_p, this patch is an attempt to implement
P0595R1 as a builtin (which will be likely needed anyway, so that libstdc++
will be able to use it even without -std=c++2a).

When evaluating (outermost) constant expressions with !ctx->quiet (i.e.
when we require constant expressions) as well in the special case of
reference initializers, or const non-volatile decl initializers, or
TREE_STATIC decl initializers, the builtin is folded into true (for the
ctx->quiet special cases if the constexpr evaluation doesn't turn a
constant expression, we retry without the special flag), otherwise
folding of it is deferred and it is flagged as non-constant expression,
and folding during gimplification folds it into false.

Not really sure about potential_constant_expression, for if it attempts
to evaluate the condition with ctx->quiet true and the patch doesn't set the
magic flag to fold the builtin to true in that case; it is unclear to me if
in case the condition includes std::is_constant_evaluated () call we need
to fold it also to true or not depending on what will we be evaluating later
on.

Bootstrapped/regtested on x86_64-linux.

2018-07-22  Jakub Jelinek  <jakub@redhat.com>

	P0595R1 - is_constant_evaluated
	* builtins.def (BUILT_IN_IS_CONSTANT_EVALUATED): New C++ builtin.
	* builtins.c (fold_builtin_0): Fold BUILT_IN_IS_CONSTANT_EVALUATED
	to 0.
cp/
	* cp-tree.h (maybe_constant_init): Add const_evaluated argument.
	* typeck2.c (store_init_value): Pass true as new argument to
	maybe_constant_init.
	* constexpr.c (struct constexpr_ctx): Add const_evaluated field.
	(cxx_eval_builtin_function_call): Handle
	BUILT_IN_IS_CONSTANT_EVALUATED.
	(cxx_eval_outermost_constant_expr): Add const_evaluated argument,
	initialize const_evaluated field in ctx.  If the result is
	TREE_CONSTANT and non_constant_p, retry with const_evaluated false
	if it was true.
	(is_sub_constant_expr): Initialize const_evaluated_field in ctx.
	(cxx_constant_value): Pass true as const_evaluated to
	cxx_eval_outermost_constant_expr.
	(maybe_constant_value): Pass false as const_evaluated to
	cxx_eval_outermost_constant_expr.
	(fold_non_dependent_expr): Likewise.
	(maybe_constant_init_1): Add const_evaluated argument, pass it
	down to cxx_eval_outermost_constant_expr.  Pass !allow_non_constant
	instead of false as strict to cxx_eval_outermost_constant_expr.
	(maybe_constant_init): Add const_evaluated argument, pass it down
	to maybe_constant_init_1.
	(cxx_constant_init): Pass true as const_evaluated to
	maybe_constant_init_1.
	* cp-gimplify.c (cp_fold): Don't fold BUILT_IN_IS_CONSTANT_EVALUATED
	calls.
lto/
	* lto-lang.c (c_dialect_cxx): Define.
ada/
	* gcc-interface/utils.c (c_dialect_cxx): Define.
brig/
	* brig-lang.c (c_dialect_cxx): Define.
testsuite/
	* g++.dg/cpp2a/is-constant-evaluated1.C: New test.


	Jakub

Comments

Richard Biener July 23, 2018, 10:17 a.m. | #1
On Sun, Jul 22, 2018 at 9:31 PM Jakub Jelinek <jakub@redhat.com> wrote:
>

> Hi!

>

> As part of the PR86590 discussions that the current libstdc++

> __constant_string is extremely costly, because we don't fold the

> __builtin_constant_p in the loop early enough and because Richard doesn't

> want __builtin_early_constant_p, this patch is an attempt to implement

> P0595R1 as a builtin (which will be likely needed anyway, so that libstdc++

> will be able to use it even without -std=c++2a).

>

> When evaluating (outermost) constant expressions with !ctx->quiet (i.e.

> when we require constant expressions) as well in the special case of

> reference initializers, or const non-volatile decl initializers, or

> TREE_STATIC decl initializers, the builtin is folded into true (for the

> ctx->quiet special cases if the constexpr evaluation doesn't turn a

> constant expression, we retry without the special flag), otherwise

> folding of it is deferred and it is flagged as non-constant expression,

> and folding during gimplification folds it into false.

>

> Not really sure about potential_constant_expression, for if it attempts

> to evaluate the condition with ctx->quiet true and the patch doesn't set the

> magic flag to fold the builtin to true in that case; it is unclear to me if

> in case the condition includes std::is_constant_evaluated () call we need

> to fold it also to true or not depending on what will we be evaluating later

> on.

>

> Bootstrapped/regtested on x86_64-linux.


Thanks for working on this.  I wonder if we can completely hide this
from the middle-end, without requiring defining of c_dialect_cxx.
There is the BUILT_IN_FRONTEND class so you could somewhere
manually inject a decl in that class for C++?

Richard.

> 2018-07-22  Jakub Jelinek  <jakub@redhat.com>

>

>         P0595R1 - is_constant_evaluated

>         * builtins.def (BUILT_IN_IS_CONSTANT_EVALUATED): New C++ builtin.

>         * builtins.c (fold_builtin_0): Fold BUILT_IN_IS_CONSTANT_EVALUATED

>         to 0.

> cp/

>         * cp-tree.h (maybe_constant_init): Add const_evaluated argument.

>         * typeck2.c (store_init_value): Pass true as new argument to

>         maybe_constant_init.

>         * constexpr.c (struct constexpr_ctx): Add const_evaluated field.

>         (cxx_eval_builtin_function_call): Handle

>         BUILT_IN_IS_CONSTANT_EVALUATED.

>         (cxx_eval_outermost_constant_expr): Add const_evaluated argument,

>         initialize const_evaluated field in ctx.  If the result is

>         TREE_CONSTANT and non_constant_p, retry with const_evaluated false

>         if it was true.

>         (is_sub_constant_expr): Initialize const_evaluated_field in ctx.

>         (cxx_constant_value): Pass true as const_evaluated to

>         cxx_eval_outermost_constant_expr.

>         (maybe_constant_value): Pass false as const_evaluated to

>         cxx_eval_outermost_constant_expr.

>         (fold_non_dependent_expr): Likewise.

>         (maybe_constant_init_1): Add const_evaluated argument, pass it

>         down to cxx_eval_outermost_constant_expr.  Pass !allow_non_constant

>         instead of false as strict to cxx_eval_outermost_constant_expr.

>         (maybe_constant_init): Add const_evaluated argument, pass it down

>         to maybe_constant_init_1.

>         (cxx_constant_init): Pass true as const_evaluated to

>         maybe_constant_init_1.

>         * cp-gimplify.c (cp_fold): Don't fold BUILT_IN_IS_CONSTANT_EVALUATED

>         calls.

> lto/

>         * lto-lang.c (c_dialect_cxx): Define.

> ada/

>         * gcc-interface/utils.c (c_dialect_cxx): Define.

> brig/

>         * brig-lang.c (c_dialect_cxx): Define.

> testsuite/

>         * g++.dg/cpp2a/is-constant-evaluated1.C: New test.

>

> --- gcc/builtins.def.jj 2018-06-20 08:15:34.179862153 +0200

> +++ gcc/builtins.def    2018-07-20 12:03:10.254453811 +0200

> @@ -974,6 +974,11 @@ DEF_EXT_LIB_BUILTIN    (BUILT_IN_PRINTF_

>  DEF_EXT_LIB_BUILTIN    (BUILT_IN_VFPRINTF_CHK, "__vfprintf_chk", BT_FN_INT_FILEPTR_INT_CONST_STRING_VALIST_ARG, ATTR_NONNULL_1_FORMAT_PRINTF_3_0)

>  DEF_EXT_LIB_BUILTIN    (BUILT_IN_VPRINTF_CHK, "__vprintf_chk", BT_FN_INT_INT_CONST_STRING_VALIST_ARG, ATTR_FORMAT_PRINTF_2_0)

>

> +/* C++ __builtin_is_constant_evaluated.  */

> +DEF_BUILTIN (BUILT_IN_IS_CONSTANT_EVALUATED, "__builtin_is_constant_evaluated",

> +            BUILT_IN_NORMAL, BT_FN_BOOL, BT_LAST, false, false, false,

> +            ATTR_CONST_NOTHROW_LEAF_LIST, true, c_dialect_cxx ())

> +

>  /* Profiling hooks.  */

>  DEF_BUILTIN (BUILT_IN_PROFILE_FUNC_ENTER, "__cyg_profile_func_enter", BUILT_IN_NORMAL, BT_FN_VOID_PTR_PTR, BT_LAST,

>              false, false, false, ATTR_NULL, true, true)

> --- gcc/builtins.c.jj   2018-07-16 23:24:51.306429546 +0200

> +++ gcc/builtins.c      2018-07-20 12:09:13.278818768 +0200

> @@ -9104,6 +9104,10 @@ fold_builtin_0 (location_t loc, tree fnd

>      case BUILT_IN_CLASSIFY_TYPE:

>        return fold_builtin_classify_type (NULL_TREE);

>

> +    case BUILT_IN_IS_CONSTANT_EVALUATED:

> +      /* The C++ FE can evaluate this to something other than false.  */

> +      return boolean_false_node;

> +

>      default:

>        break;

>      }

> --- gcc/cp/cp-tree.h.jj 2018-07-18 22:57:10.657780293 +0200

> +++ gcc/cp/cp-tree.h    2018-07-20 18:10:33.416409798 +0200

> @@ -7536,7 +7536,7 @@ extern bool require_potential_rvalue_con

>  extern tree cxx_constant_value                 (tree, tree = NULL_TREE);

>  extern tree cxx_constant_init                  (tree, tree = NULL_TREE);

>  extern tree maybe_constant_value               (tree, tree = NULL_TREE);

> -extern tree maybe_constant_init                        (tree, tree = NULL_TREE);

> +extern tree maybe_constant_init                        (tree, tree = NULL_TREE, bool = false);

>  extern tree fold_non_dependent_expr            (tree, tsubst_flags_t = tf_warning_or_error);

>  extern tree fold_simple                                (tree);

>  extern bool is_sub_constant_expr                (tree);

> --- gcc/cp/typeck2.c.jj 2018-06-29 09:38:17.786306395 +0200

> +++ gcc/cp/typeck2.c    2018-07-20 18:11:36.649492893 +0200

> @@ -837,7 +837,7 @@ store_init_value (tree decl, tree init,

>             value = cxx_constant_init (value, decl);

>         }

>        else

> -       value = maybe_constant_init (value, decl);

> +       value = maybe_constant_init (value, decl, true);

>        if (TREE_CODE (value) == CONSTRUCTOR && cp_has_mutable_p (type))

>         /* Poison this CONSTRUCTOR so it can't be copied to another

>            constexpr variable.  */

> --- gcc/cp/constexpr.c.jj       2018-06-25 14:51:23.094989194 +0200

> +++ gcc/cp/constexpr.c  2018-07-20 19:16:40.504036595 +0200

> @@ -1007,6 +1007,8 @@ struct constexpr_ctx {

>    /* Whether we are strictly conforming to constant expression rules or

>       trying harder to get a constant value.  */

>    bool strict;

> +  /* Whether __builtin_is_constant_evaluated () should be true.  */

> +  bool const_evaluated;

>  };

>

>  /* A table of all constexpr calls that have been evaluated by the

> @@ -1184,6 +1186,18 @@ cxx_eval_builtin_function_call (const co

>        return t;

>      }

>

> +  /* For __builtin_is_constant_evaluated, defer it if not ctx->const_evaluated,

> +     otherwise fold it to true.  */

> +  if (DECL_FUNCTION_CODE (fun) == BUILT_IN_IS_CONSTANT_EVALUATED)

> +    {

> +      if (!ctx->const_evaluated)

> +       {

> +         *non_constant_p = true;

> +         return t;

> +       }

> +      return boolean_true_node;

> +    }

> +

>    /* Be permissive for arguments to built-ins; __builtin_constant_p should

>       return constant false for a non-constant argument.  */

>    constexpr_ctx new_ctx = *ctx;

> @@ -4884,7 +4898,9 @@ instantiate_constexpr_fns (tree t)

>

>  static tree

>  cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant,

> -                                 bool strict = true, tree object = NULL_TREE)

> +                                 bool strict = true,

> +                                 bool const_evaluated = false,

> +                                 tree object = NULL_TREE)

>  {

>    auto_timevar time (TV_CONSTEXPR);

>

> @@ -4893,7 +4909,8 @@ cxx_eval_outermost_constant_expr (tree t

>    hash_map<tree,tree> map;

>

>    constexpr_ctx ctx = { NULL, &map, NULL, NULL, NULL, NULL,

> -                       allow_non_constant, strict };

> +                       allow_non_constant, strict,

> +                       const_evaluated || !allow_non_constant };

>

>    tree type = initialized_type (t);

>    tree r = t;

> @@ -4982,6 +4999,12 @@ cxx_eval_outermost_constant_expr (tree t

>      return error_mark_node;

>    else if (non_constant_p && TREE_CONSTANT (r))

>      {

> +      /* If __builtin_is_constant_evaluated () was evaluated to true

> +        and the result is not a valid constant expression, we need to

> +        punt.  */

> +      if (const_evaluated)

> +       return cxx_eval_outermost_constant_expr (t, true, strict,

> +                                                false, object);

>        /* This isn't actually constant, so unset TREE_CONSTANT.

>          Don't clear TREE_CONSTANT on ADDR_EXPR, as the middle-end requires

>          it to be set if it is invariant address, even when it is not

> @@ -5027,7 +5049,8 @@ is_sub_constant_expr (tree t)

>    bool overflow_p = false;

>    hash_map <tree, tree> map;

>

> -  constexpr_ctx ctx = { NULL, &map, NULL, NULL, NULL, NULL, true, true };

> +  constexpr_ctx ctx

> +    = { NULL, &map, NULL, NULL, NULL, NULL, true, true, false };

>

>    instantiate_constexpr_fns (t);

>    cxx_eval_constant_expression (&ctx, t, false, &non_constant_p,

> @@ -5042,7 +5065,7 @@ is_sub_constant_expr (tree t)

>  tree

>  cxx_constant_value (tree t, tree decl)

>  {

> -  return cxx_eval_outermost_constant_expr (t, false, true, decl);

> +  return cxx_eval_outermost_constant_expr (t, false, true, true, decl);

>  }

>

>  /* Helper routine for fold_simple function.  Either return simplified

> @@ -5148,7 +5171,7 @@ maybe_constant_value (tree t, tree decl)

>    if (tree *cached = cv_cache->get (t))

>      return *cached;

>

> -  r = cxx_eval_outermost_constant_expr (t, true, true, decl);

> +  r = cxx_eval_outermost_constant_expr (t, true, true, false, decl);

>    gcc_checking_assert (r == t

>                        || CONVERT_EXPR_P (t)

>                        || TREE_CODE (t) == VIEW_CONVERT_EXPR

> @@ -5222,7 +5245,8 @@ fold_non_dependent_expr (tree t,

>               return t;

>             }

>

> -         tree r = cxx_eval_outermost_constant_expr (t, true, true, NULL_TREE);

> +         tree r = cxx_eval_outermost_constant_expr (t, true, true, false,

> +                                                    NULL_TREE);

>           /* cp_tree_equal looks through NOPs, so allow them.  */

>           gcc_checking_assert (r == t

>                                || CONVERT_EXPR_P (t)

> @@ -5246,7 +5270,8 @@ fold_non_dependent_expr (tree t,

>     than wrapped in a TARGET_EXPR.  */

>

>  static tree

> -maybe_constant_init_1 (tree t, tree decl, bool allow_non_constant)

> +maybe_constant_init_1 (tree t, tree decl, bool allow_non_constant,

> +                      bool const_evaluated)

>  {

>    if (!t)

>      return t;

> @@ -5264,7 +5289,9 @@ maybe_constant_init_1 (tree t, tree decl

>    else if (CONSTANT_CLASS_P (t) && allow_non_constant)

>      /* No evaluation needed.  */;

>    else

> -    t = cxx_eval_outermost_constant_expr (t, allow_non_constant, false, decl);

> +    t = cxx_eval_outermost_constant_expr (t, allow_non_constant,

> +                                         !allow_non_constant,

> +                                         const_evaluated, decl);

>    if (TREE_CODE (t) == TARGET_EXPR)

>      {

>        tree init = TARGET_EXPR_INITIAL (t);

> @@ -5277,9 +5304,9 @@ maybe_constant_init_1 (tree t, tree decl

>  /* Wrapper for maybe_constant_init_1 which permits non constants.  */

>

>  tree

> -maybe_constant_init (tree t, tree decl)

> +maybe_constant_init (tree t, tree decl, bool const_evaluated)

>  {

> -  return maybe_constant_init_1 (t, decl, true);

> +  return maybe_constant_init_1 (t, decl, true, const_evaluated);

>  }

>

>  /* Wrapper for maybe_constant_init_1 which does not permit non constants.  */

> @@ -5287,7 +5314,7 @@ maybe_constant_init (tree t, tree decl)

>  tree

>  cxx_constant_init (tree t, tree decl)

>  {

> -  return maybe_constant_init_1 (t, decl, false);

> +  return maybe_constant_init_1 (t, decl, false, true);

>  }

>

>  #if 0

> --- gcc/cp/cp-gimplify.c.jj     2018-07-20 11:39:15.543037497 +0200

> +++ gcc/cp/cp-gimplify.c        2018-07-20 12:21:29.869568404 +0200

> @@ -2478,6 +2478,12 @@ cp_fold (tree x)

>             && DECL_DECLARED_CONSTEXPR_P (current_function_decl))

>           nw = 1;

>

> +       /* Defer folding __builtin_is_constant_evaluated.  */

> +       if (callee

> +           && DECL_BUILT_IN_CLASS (callee) == BUILT_IN_NORMAL

> +           && DECL_FUNCTION_CODE (callee) == BUILT_IN_IS_CONSTANT_EVALUATED)

> +         break;

> +

>         x = copy_node (x);

>

>         m = call_expr_nargs (x);

> --- gcc/lto/lto-lang.c.jj       2018-06-13 10:05:49.991124932 +0200

> +++ gcc/lto/lto-lang.c  2018-07-20 12:29:28.087207392 +0200

> @@ -246,6 +246,7 @@ static GTY(()) tree signed_size_type_nod

>  int flag_isoc94;

>  int flag_isoc99;

>  int flag_isoc11;

> +#define c_dialect_cxx() 0

>

>  /* Attribute handlers.  */

>

> --- gcc/ada/gcc-interface/utils.c.jj    2018-07-17 12:48:21.096585583 +0200

> +++ gcc/ada/gcc-interface/utils.c       2018-07-21 09:26:01.365363959 +0200

> @@ -6458,6 +6458,7 @@ def_builtin_1 (enum built_in_function fn

>  static int flag_isoc94 = 0;

>  static int flag_isoc99 = 0;

>  static int flag_isoc11 = 0;

> +#define c_dialect_cxx() 0

>

>  /* Install what the common builtins.def offers.  */

>

> --- gcc/brig/brig-lang.c.jj     2018-05-06 23:12:54.400623542 +0200

> +++ gcc/brig/brig-lang.c        2018-07-21 09:25:37.121341436 +0200

> @@ -587,6 +587,7 @@ static GTY(()) tree signed_size_type_nod

>  int flag_isoc94;

>  int flag_isoc99;

>  int flag_isoc11;

> +#define c_dialect_cxx() 0

>

>  static void

>  def_fn_type (builtin_type def, builtin_type ret, bool var, int n, ...)

> --- gcc/testsuite/g++.dg/cpp2a/is-constant-evaluated1.C.jj      2018-07-20 18:43:58.240798640 +0200

> +++ gcc/testsuite/g++.dg/cpp2a/is-constant-evaluated1.C 2018-07-20 19:09:44.712391964 +0200

> @@ -0,0 +1,64 @@

> +// P0595R1

> +// { dg-do compile { target c++14 } }

> +

> +template<int N> struct X { int v = N; };

> +X<__builtin_is_constant_evaluated ()> x; // type X<true>

> +int y = 4;

> +int a = __builtin_is_constant_evaluated () ? y : 1; // initializes a to 1

> +int b = __builtin_is_constant_evaluated () ? 2 : y; // initializes b to 2

> +int c = y + (__builtin_is_constant_evaluated () ? 2 : y); // initializes c to 2*y

> +

> +struct false_type { static constexpr bool value = false; };

> +struct true_type { static constexpr bool value = true; };

> +template<class T, class U>

> +struct is_same : false_type {};

> +template<class T>

> +struct is_same<T, T> : true_type {};

> +

> +constexpr int

> +foo (int x)

> +{

> +  const int n = __builtin_is_constant_evaluated () ? 13 : 17; // n == 13

> +  int m = __builtin_is_constant_evaluated () ? 13 : 17; // m might be 13 or 17 (see below)

> +  char arr[n] = {}; // char[13]

> +  return m + sizeof (arr) + x;

> +}

> +

> +constexpr int

> +bar ()

> +{

> +  const int n = __builtin_is_constant_evaluated() ? 13 : 17;

> +  X<n> x1;

> +  X<__builtin_is_constant_evaluated() ? 13 : 17> x2;

> +  static_assert (is_same<decltype (x1), decltype (x2)>::value, "x1/x2's type");

> +  return x1.v + x2.v;

> +}

> +

> +int p = foo (0); // m == 13; initialized to 26

> +int q = p + foo (0); // m == 17 for this call; initialized to 56

> +static_assert (bar () == 26, "bar");

> +

> +struct S { int a, b; };

> +

> +S s = { __builtin_is_constant_evaluated () ? 2 : 3, y };

> +S t = { __builtin_is_constant_evaluated () ? 2 : 3, 4 };

> +

> +static_assert (is_same<decltype (x), X<true> >::value, "x's type");

> +

> +int

> +main ()

> +{

> +  if (a != 1 || b != 2 || c != 8 || p != 26 || q != 56)

> +    __builtin_abort ();

> +  if (s.a != 3 || s.b != 4 || t.a != 2 || t.b != 4)

> +    __builtin_abort ();

> +  if (foo (y) != 34)

> +    __builtin_abort ();

> +#if __cplusplus >= 201703L

> +  if constexpr (foo (0) != 26)

> +    __builtin_abort ();

> +#endif

> +  constexpr int w = foo (0);

> +  if (w != 26)

> +    __builtin_abort ();

> +}

>

>         Jakub
Jakub Jelinek July 23, 2018, 10:28 a.m. | #2
On Mon, Jul 23, 2018 at 12:17:42PM +0200, Richard Biener wrote:
> > Bootstrapped/regtested on x86_64-linux.

> 

> Thanks for working on this.  I wonder if we can completely hide this

> from the middle-end, without requiring defining of c_dialect_cxx.

> There is the BUILT_IN_FRONTEND class so you could somewhere

> manually inject a decl in that class for C++?


But then I couldn't handle folding of the builtin in the middle-end to false,
which is what I need (because in the FE it needs to be either folded to
true, or its folding deferred until later).
Or maybe in the C++ gimplification langhook?

Seems we have a single BUILT_IN_FRONTEND builtin in the whole compiler,
__integer_pack, but it doesn't act as a normal builtin, given it is a
templatish magic.

	Jakub
Richard Biener July 23, 2018, 10:50 a.m. | #3
On Mon, Jul 23, 2018 at 12:28 PM Jakub Jelinek <jakub@redhat.com> wrote:
>

> On Mon, Jul 23, 2018 at 12:17:42PM +0200, Richard Biener wrote:

> > > Bootstrapped/regtested on x86_64-linux.

> >

> > Thanks for working on this.  I wonder if we can completely hide this

> > from the middle-end, without requiring defining of c_dialect_cxx.

> > There is the BUILT_IN_FRONTEND class so you could somewhere

> > manually inject a decl in that class for C++?

>

> But then I couldn't handle folding of the builtin in the middle-end to false,

> which is what I need (because in the FE it needs to be either folded to

> true, or its folding deferred until later).

> Or maybe in the C++ gimplification langhook?


Yes, I was thinking the C++ langhook or its fully_fold routine.

> Seems we have a single BUILT_IN_FRONTEND builtin in the whole compiler,

> __integer_pack, but it doesn't act as a normal builtin, given it is a

> templatish magic.


Yeah, I think at some point we considered removing BUILT_IN_FRONTEND ...

Nowadays internal-use builtins can easily be internal-functions but of couse
this one will eventually be used from libstdc++.

Richard.

>

>         Jakub
Jason Merrill July 31, 2018, 2:35 p.m. | #4
On Mon, Jul 23, 2018 at 8:50 PM, Richard Biener
<richard.guenther@gmail.com> wrote:
> On Mon, Jul 23, 2018 at 12:28 PM Jakub Jelinek <jakub@redhat.com> wrote:

>>

>> On Mon, Jul 23, 2018 at 12:17:42PM +0200, Richard Biener wrote:

>> > > Bootstrapped/regtested on x86_64-linux.

>> >

>> > Thanks for working on this.  I wonder if we can completely hide this

>> > from the middle-end, without requiring defining of c_dialect_cxx.

>> > There is the BUILT_IN_FRONTEND class so you could somewhere

>> > manually inject a decl in that class for C++?

>>

>> But then I couldn't handle folding of the builtin in the middle-end to false,

>> which is what I need (because in the FE it needs to be either folded to

>> true, or its folding deferred until later).

>> Or maybe in the C++ gimplification langhook?

>

> Yes, I was thinking the C++ langhook or its fully_fold routine.


fully_fold is too soon (until constexpr evaluation uses the
pre-genericize form), but the gimplification hook should work.

>> Seems we have a single BUILT_IN_FRONTEND builtin in the whole compiler,

>> __integer_pack, but it doesn't act as a normal builtin, given it is a

>> templatish magic.

>

> Yeah, I think at some point we considered removing BUILT_IN_FRONTEND ...

>

> Nowadays internal-use builtins can easily be internal-functions but of couse

> this one will eventually be used from libstdc++.


Immediately, I'd think.

Jason
Jakub Jelinek Aug. 1, 2018, 9:07 a.m. | #5
On Wed, Aug 01, 2018 at 12:35:09AM +1000, Jason Merrill wrote:
> On Mon, Jul 23, 2018 at 8:50 PM, Richard Biener

> <richard.guenther@gmail.com> wrote:

> > On Mon, Jul 23, 2018 at 12:28 PM Jakub Jelinek <jakub@redhat.com> wrote:

> >>

> >> On Mon, Jul 23, 2018 at 12:17:42PM +0200, Richard Biener wrote:

> >> > > Bootstrapped/regtested on x86_64-linux.

> >> >

> >> > Thanks for working on this.  I wonder if we can completely hide this

> >> > from the middle-end, without requiring defining of c_dialect_cxx.

> >> > There is the BUILT_IN_FRONTEND class so you could somewhere

> >> > manually inject a decl in that class for C++?

> >>

> >> But then I couldn't handle folding of the builtin in the middle-end to false,

> >> which is what I need (because in the FE it needs to be either folded to

> >> true, or its folding deferred until later).

> >> Or maybe in the C++ gimplification langhook?

> >

> > Yes, I was thinking the C++ langhook or its fully_fold routine.

> 

> fully_fold is too soon (until constexpr evaluation uses the

> pre-genericize form), but the gimplification hook should work.

> 

> >> Seems we have a single BUILT_IN_FRONTEND builtin in the whole compiler,

> >> __integer_pack, but it doesn't act as a normal builtin, given it is a

> >> templatish magic.

> >

> > Yeah, I think at some point we considered removing BUILT_IN_FRONTEND ...

> >

> > Nowadays internal-use builtins can easily be internal-functions but of couse

> > this one will eventually be used from libstdc++.

> 

> Immediately, I'd think.


Here is an updated patch that uses BUILT_IN_FRONTEND for this rather than
BUILT_IN_NORMAL builtin.

Bootstrapped/regtested on x86_64-linux and i686-linux.

2018-08-01  Jakub Jelinek  <jakub@redhat.com>

	P0595R1 - is_constant_evaluated
cp/
	* cp-tree.h (enum cp_built_in_function): New.
	(maybe_constant_init): Add const_evaluated argument.
	* typeck2.c (store_init_value): Pass true as new argument to
	maybe_constant_init.
	* constexpr.c (constexpr_fn_retval): Check also DECL_BUILT_IN_CLASS
	for BUILT_IN_UNREACHABLE.
	(struct constexpr_ctx): Add const_evaluated field.
	(cxx_eval_builtin_function_call): Use DECL_IS_BUILTIN_CONSTANT_P
	macro.  Handle CP_BUILT_IN_IS_CONSTANT_EVALUATED.  Check also
	DECL_BUILT_IN_CLASS for BUILT_IN_UNREACHABLE.
	(cxx_eval_outermost_constant_expr): Add const_evaluated argument,
	initialize const_evaluated field in ctx.  If the result is
	TREE_CONSTANT and non_constant_p, retry with const_evaluated false
	if it was true.
	(is_sub_constant_expr): Initialize const_evaluated_field in ctx.
	(cxx_constant_value): Pass true as const_evaluated to
	cxx_eval_outermost_constant_expr.
	(maybe_constant_value): Pass false as const_evaluated to
	cxx_eval_outermost_constant_expr.
	(fold_non_dependent_expr): Likewise.
	(maybe_constant_init_1): Add const_evaluated argument, pass it
	down to cxx_eval_outermost_constant_expr.  Pass !allow_non_constant
	instead of false as strict to cxx_eval_outermost_constant_expr.
	(maybe_constant_init): Add const_evaluated argument, pass it down
	to maybe_constant_init_1.
	(cxx_constant_init): Pass true as const_evaluated to
	maybe_constant_init_1.
	* cp-gimplify.c (cp_gimplify_expr): Handle CALL_EXPRs to
	CP_BUILT_IN_IS_CONSTANT_EVALUATED.
	(cp_fold): Don't fold CP_BUILT_IN_IS_CONSTANT_EVALUATED calls.
	* decl.c: Include langhooks.h.
	(cxx_init_decl_processing): Register __builtin_is_constant_evaluated
	built-in.
	* tree.c (builtin_valid_in_constant_expr_p): Return true for
	CP_BUILT_IN_IS_CONSTANT_EVALUATED.
	* pt.c (declare_integer_pack): Initialize DECL_FUNCTION_CODE.
testsuite/
	* g++.dg/cpp2a/is-constant-evaluated1.C: New test.

--- gcc/cp/decl.c.jj	2018-07-25 18:46:56.159149394 +0200
+++ gcc/cp/decl.c	2018-07-31 19:54:56.637180751 +0200
@@ -52,6 +52,7 @@ along with GCC; see the file COPYING3.
 #include "gimplify.h"
 #include "asan.h"
 #include "gcc-rich-location.h"
+#include "langhooks.h"
 
 /* Possible cases of bad specifiers type used by bad_specifiers. */
 enum bad_spec_place {
@@ -4125,6 +4126,13 @@ cxx_init_decl_processing (void)
 
   c_common_nodes_and_builtins ();
 
+  tree bool_ftype = build_function_type_list (boolean_type_node, NULL_TREE);
+  tree decl
+    = add_builtin_function ("__builtin_is_constant_evaluated",
+			    bool_ftype, CP_BUILT_IN_IS_CONSTANT_EVALUATED,
+			    BUILT_IN_FRONTEND, NULL, NULL_TREE);
+  set_call_expr_flags (decl, ECF_CONST | ECF_NOTHROW | ECF_LEAF);
+
   integer_two_node = build_int_cst (NULL_TREE, 2);
 
   /* Guess at the initial static decls size.  */
--- gcc/cp/typeck2.c.jj	2018-07-23 09:46:51.145991660 +0200
+++ gcc/cp/typeck2.c	2018-07-31 18:32:23.526305749 +0200
@@ -837,7 +837,7 @@ store_init_value (tree decl, tree init,
 	    value = cxx_constant_init (value, decl);
 	}
       else
-	value = maybe_constant_init (value, decl);
+	value = maybe_constant_init (value, decl, true);
       if (TREE_CODE (value) == CONSTRUCTOR && cp_has_mutable_p (type))
 	/* Poison this CONSTRUCTOR so it can't be copied to another
 	   constexpr variable.  */
--- gcc/cp/constexpr.c.jj	2018-07-23 09:46:51.193991708 +0200
+++ gcc/cp/constexpr.c	2018-07-31 20:06:04.176221114 +0200
@@ -713,6 +713,7 @@ constexpr_fn_retval (tree body)
 	{
 	  tree fun = get_function_named_in_call (body);
 	  if (fun != NULL_TREE
+	      && DECL_BUILT_IN_CLASS (fun) == BUILT_IN_NORMAL
 	      && DECL_FUNCTION_CODE (fun) == BUILT_IN_UNREACHABLE)
 	    return NULL_TREE;
 	}
@@ -1007,6 +1008,8 @@ struct constexpr_ctx {
   /* Whether we are strictly conforming to constant expression rules or
      trying harder to get a constant value.  */
   bool strict;
+  /* Whether __builtin_is_constant_evaluated () should be true.  */
+  bool const_evaluated;
 };
 
 /* A table of all constexpr calls that have been evaluated by the
@@ -1171,7 +1174,7 @@ cxx_eval_builtin_function_call (const co
   int i;
 
   /* Don't fold __builtin_constant_p within a constexpr function.  */
-  bool bi_const_p = (DECL_FUNCTION_CODE (fun) == BUILT_IN_CONSTANT_P);
+  bool bi_const_p = DECL_IS_BUILTIN_CONSTANT_P (fun);
 
   /* If we aren't requiring a constant expression, defer __builtin_constant_p
      in a constexpr function until we have values for the parameters.  */
@@ -1184,6 +1187,19 @@ cxx_eval_builtin_function_call (const co
       return t;
     }
 
+  /* For __builtin_is_constant_evaluated, defer it if not ctx->const_evaluated,
+     otherwise fold it to true.  */
+  if (DECL_BUILT_IN_CLASS (fun) == BUILT_IN_FRONTEND
+      && (int) DECL_FUNCTION_CODE (fun) == CP_BUILT_IN_IS_CONSTANT_EVALUATED)
+    {
+      if (!ctx->const_evaluated)
+	{
+	  *non_constant_p = true;
+	  return t;
+	}
+      return boolean_true_node;
+    }
+
   /* Be permissive for arguments to built-ins; __builtin_constant_p should
      return constant false for a non-constant argument.  */
   constexpr_ctx new_ctx = *ctx;
@@ -1217,7 +1233,8 @@ cxx_eval_builtin_function_call (const co
 	  /* Do not allow__builtin_unreachable in constexpr function.
 	     The __builtin_unreachable call with BUILTINS_LOCATION
 	     comes from cp_maybe_instrument_return.  */
-	  if (DECL_FUNCTION_CODE (fun) == BUILT_IN_UNREACHABLE
+	  if (DECL_BUILT_IN_CLASS (fun) == BUILT_IN_NORMAL
+	      && DECL_FUNCTION_CODE (fun) == BUILT_IN_UNREACHABLE
 	      && EXPR_LOCATION (t) == BUILTINS_LOCATION)
 	    error ("%<constexpr%> call flows off the end of the function");
 	  else
@@ -4884,7 +4901,9 @@ instantiate_constexpr_fns (tree t)
 
 static tree
 cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant,
-				  bool strict = true, tree object = NULL_TREE)
+				  bool strict = true,
+				  bool const_evaluated = false,
+				  tree object = NULL_TREE)
 {
   auto_timevar time (TV_CONSTEXPR);
 
@@ -4893,7 +4912,8 @@ cxx_eval_outermost_constant_expr (tree t
   hash_map<tree,tree> map;
 
   constexpr_ctx ctx = { NULL, &map, NULL, NULL, NULL, NULL,
-			allow_non_constant, strict };
+			allow_non_constant, strict,
+			const_evaluated || !allow_non_constant };
 
   tree type = initialized_type (t);
   tree r = t;
@@ -4982,6 +5002,12 @@ cxx_eval_outermost_constant_expr (tree t
     return error_mark_node;
   else if (non_constant_p && TREE_CONSTANT (r))
     {
+      /* If __builtin_is_constant_evaluated () was evaluated to true
+	 and the result is not a valid constant expression, we need to
+	 punt.  */
+      if (const_evaluated)
+	return cxx_eval_outermost_constant_expr (t, true, strict,
+						 false, object);
       /* This isn't actually constant, so unset TREE_CONSTANT.
 	 Don't clear TREE_CONSTANT on ADDR_EXPR, as the middle-end requires
 	 it to be set if it is invariant address, even when it is not
@@ -5027,7 +5053,8 @@ is_sub_constant_expr (tree t)
   bool overflow_p = false;
   hash_map <tree, tree> map;
 
-  constexpr_ctx ctx = { NULL, &map, NULL, NULL, NULL, NULL, true, true };
+  constexpr_ctx ctx
+    = { NULL, &map, NULL, NULL, NULL, NULL, true, true, false };
 
   instantiate_constexpr_fns (t);
   cxx_eval_constant_expression (&ctx, t, false, &non_constant_p,
@@ -5042,7 +5069,7 @@ is_sub_constant_expr (tree t)
 tree
 cxx_constant_value (tree t, tree decl)
 {
-  return cxx_eval_outermost_constant_expr (t, false, true, decl);
+  return cxx_eval_outermost_constant_expr (t, false, true, true, decl);
 }
 
 /* Helper routine for fold_simple function.  Either return simplified
@@ -5148,7 +5175,7 @@ maybe_constant_value (tree t, tree decl)
   if (tree *cached = cv_cache->get (t))
     return *cached;
 
-  r = cxx_eval_outermost_constant_expr (t, true, true, decl);
+  r = cxx_eval_outermost_constant_expr (t, true, true, false, decl);
   gcc_checking_assert (r == t
 		       || CONVERT_EXPR_P (t)
 		       || TREE_CODE (t) == VIEW_CONVERT_EXPR
@@ -5222,7 +5249,8 @@ fold_non_dependent_expr (tree t,
 	      return t;
 	    }
 
-	  tree r = cxx_eval_outermost_constant_expr (t, true, true, NULL_TREE);
+	  tree r = cxx_eval_outermost_constant_expr (t, true, true, false,
+						     NULL_TREE);
 	  /* cp_tree_equal looks through NOPs, so allow them.  */
 	  gcc_checking_assert (r == t
 			       || CONVERT_EXPR_P (t)
@@ -5246,7 +5274,8 @@ fold_non_dependent_expr (tree t,
    than wrapped in a TARGET_EXPR.  */
 
 static tree
-maybe_constant_init_1 (tree t, tree decl, bool allow_non_constant)
+maybe_constant_init_1 (tree t, tree decl, bool allow_non_constant,
+		       bool const_evaluated)
 {
   if (!t)
     return t;
@@ -5264,7 +5293,9 @@ maybe_constant_init_1 (tree t, tree decl
   else if (CONSTANT_CLASS_P (t) && allow_non_constant)
     /* No evaluation needed.  */;
   else
-    t = cxx_eval_outermost_constant_expr (t, allow_non_constant, false, decl);
+    t = cxx_eval_outermost_constant_expr (t, allow_non_constant,
+					  !allow_non_constant,
+					  const_evaluated, decl);
   if (TREE_CODE (t) == TARGET_EXPR)
     {
       tree init = TARGET_EXPR_INITIAL (t);
@@ -5277,9 +5308,9 @@ maybe_constant_init_1 (tree t, tree decl
 /* Wrapper for maybe_constant_init_1 which permits non constants.  */
 
 tree
-maybe_constant_init (tree t, tree decl)
+maybe_constant_init (tree t, tree decl, bool const_evaluated)
 {
-  return maybe_constant_init_1 (t, decl, true);
+  return maybe_constant_init_1 (t, decl, true, const_evaluated);
 }
 
 /* Wrapper for maybe_constant_init_1 which does not permit non constants.  */
@@ -5287,7 +5318,7 @@ maybe_constant_init (tree t, tree decl)
 tree
 cxx_constant_init (tree t, tree decl)
 {
-  return maybe_constant_init_1 (t, decl, false);
+  return maybe_constant_init_1 (t, decl, false, true);
 }
 
 #if 0
--- gcc/cp/cp-tree.h.jj	2018-07-25 18:46:56.156149388 +0200
+++ gcc/cp/cp-tree.h	2018-07-31 18:44:25.078455768 +0200
@@ -5966,6 +5966,13 @@ struct GTY((chain_next ("%h.next"))) tin
   static const unsigned short refcount_infinity = (unsigned short) ~0;
 };
 
+/* BUILT_IN_FRONTEND function codes.  */
+enum cp_built_in_function {
+  CP_BUILT_IN_IS_CONSTANT_EVALUATED,
+  CP_BUILT_IN_INTEGER_PACK,
+  CP_BUILT_IN_LAST
+};
+
 bool decl_spec_seq_has_spec_p (const cp_decl_specifier_seq *, cp_decl_spec);
 
 /* Return the type of the `this' parameter of FNTYPE.  */
@@ -7572,7 +7579,7 @@ extern bool require_potential_rvalue_con
 extern tree cxx_constant_value			(tree, tree = NULL_TREE);
 extern tree cxx_constant_init			(tree, tree = NULL_TREE);
 extern tree maybe_constant_value		(tree, tree = NULL_TREE);
-extern tree maybe_constant_init			(tree, tree = NULL_TREE);
+extern tree maybe_constant_init			(tree, tree = NULL_TREE, bool = false);
 extern tree fold_non_dependent_expr		(tree, tsubst_flags_t = tf_warning_or_error);
 extern tree fold_simple				(tree);
 extern bool is_sub_constant_expr                (tree);
--- gcc/cp/cp-gimplify.c.jj	2018-07-23 09:48:01.638068411 +0200
+++ gcc/cp/cp-gimplify.c	2018-07-31 20:05:48.925174439 +0200
@@ -793,6 +793,15 @@ cp_gimplify_expr (tree *expr_p, gimple_s
 		ret = GS_ERROR;
 	    }
 	}
+      if (ret != GS_ERROR)
+	{
+	  tree decl = cp_get_callee_fndecl_nofold (*expr_p);
+	  if (decl
+	      && DECL_BUILT_IN_CLASS (decl) == BUILT_IN_FRONTEND
+	      && ((int) DECL_FUNCTION_CODE (decl)
+		  == CP_BUILT_IN_IS_CONSTANT_EVALUATED))
+	    *expr_p = boolean_false_node;
+	}
       break;
 
     case RETURN_EXPR:
@@ -2483,6 +2492,13 @@ cp_fold (tree x)
 	    && DECL_DECLARED_CONSTEXPR_P (current_function_decl))
 	  nw = 1;
 
+	/* Defer folding __builtin_is_constant_evaluated.  */
+	if (callee
+	    && DECL_BUILT_IN_CLASS (callee) == BUILT_IN_FRONTEND
+	    && ((int) DECL_FUNCTION_CODE (callee)
+		== CP_BUILT_IN_IS_CONSTANT_EVALUATED))
+	  break;
+
 	x = copy_node (x);
 
 	m = call_expr_nargs (x);
--- gcc/cp/tree.c.jj	2018-07-17 20:08:09.482226661 +0200
+++ gcc/cp/tree.c	2018-07-31 19:14:26.520425530 +0200
@@ -415,10 +415,18 @@ cp_stabilize_reference (tree ref)
 bool
 builtin_valid_in_constant_expr_p (const_tree decl)
 {
-  if (!(TREE_CODE (decl) == FUNCTION_DECL
-	&& DECL_BUILT_IN_CLASS (decl) == BUILT_IN_NORMAL))
-    /* Not a built-in.  */
+  if (TREE_CODE (decl) != FUNCTION_DECL)
+    /* Not a function.  */
     return false;
+  if (DECL_BUILT_IN_CLASS (decl) != BUILT_IN_NORMAL)
+    {
+      if (DECL_BUILT_IN_CLASS (decl) == BUILT_IN_FRONTEND
+	  && ((int) DECL_FUNCTION_CODE (decl)
+	      == CP_BUILT_IN_IS_CONSTANT_EVALUATED))
+	return true;
+      /* Not a built-in.  */
+      return false;
+    }
   switch (DECL_FUNCTION_CODE (decl))
     {
       /* These always have constant results like the corresponding
--- gcc/cp/pt.c.jj	2018-07-18 22:57:10.806780476 +0200
+++ gcc/cp/pt.c	2018-07-31 19:55:48.090337614 +0200
@@ -27537,6 +27537,8 @@ declare_integer_pack (void)
 			       NULL_TREE, ECF_CONST);
   DECL_DECLARED_CONSTEXPR_P (ipfn) = true;
   DECL_BUILT_IN_CLASS (ipfn) = BUILT_IN_FRONTEND;
+  DECL_FUNCTION_CODE (ipfn)
+    = (enum built_in_function) (int) CP_BUILT_IN_INTEGER_PACK;
 }
 
 /* Set up the hash tables for template instantiations.  */
--- gcc/testsuite/g++.dg/cpp2a/is-constant-evaluated1.C.jj	2018-07-31 18:32:23.529305758 +0200
+++ gcc/testsuite/g++.dg/cpp2a/is-constant-evaluated1.C	2018-07-31 20:13:03.311489944 +0200
@@ -0,0 +1,66 @@
+// P0595R1
+// { dg-do compile { target c++14 } }
+
+template<int N> struct X { int v = N; };
+X<__builtin_is_constant_evaluated ()> x; // type X<true>
+int y = 4;
+int a = __builtin_is_constant_evaluated () ? y : 1; // initializes a to 1
+int b = __builtin_is_constant_evaluated () ? 2 : y; // initializes b to 2
+int c = y + (__builtin_is_constant_evaluated () ? 2 : y); // initializes c to 2*y
+int d = __builtin_is_constant_evaluated (); // initializes d to 1
+int e = d + __builtin_is_constant_evaluated (); // initializes e to 0
+
+struct false_type { static constexpr bool value = false; };
+struct true_type { static constexpr bool value = true; };
+template<class T, class U>
+struct is_same : false_type {};
+template<class T>
+struct is_same<T, T> : true_type {};
+
+constexpr int
+foo (int x)
+{
+  const int n = __builtin_is_constant_evaluated () ? 13 : 17; // n == 13
+  int m = __builtin_is_constant_evaluated () ? 13 : 17; // m might be 13 or 17 (see below)
+  char arr[n] = {}; // char[13]
+  return m + sizeof (arr) + x;
+}
+
+constexpr int
+bar ()
+{
+  const int n = __builtin_is_constant_evaluated() ? 13 : 17;
+  X<n> x1;
+  X<__builtin_is_constant_evaluated() ? 13 : 17> x2;
+  static_assert (is_same<decltype (x1), decltype (x2)>::value, "x1/x2's type");
+  return x1.v + x2.v;
+}
+
+int p = foo (0); // m == 13; initialized to 26
+int q = p + foo (0); // m == 17 for this call; initialized to 56
+static_assert (bar () == 26, "bar");
+
+struct S { int a, b; };
+
+S s = { __builtin_is_constant_evaluated () ? 2 : 3, y };
+S t = { __builtin_is_constant_evaluated () ? 2 : 3, 4 };
+
+static_assert (is_same<decltype (x), X<true> >::value, "x's type");
+
+int
+main ()
+{
+  if (a != 1 || b != 2 || c != 8 || d != 1 || e != 0 || p != 26 || q != 56)
+    __builtin_abort ();
+  if (s.a != 3 || s.b != 4 || t.a != 2 || t.b != 4)
+    __builtin_abort ();
+  if (foo (y) != 34)
+    __builtin_abort ();
+#if __cplusplus >= 201703L
+  if constexpr (foo (0) != 26)
+    __builtin_abort ();
+#endif
+  constexpr int w = foo (0);
+  if (w != 26)
+    __builtin_abort ();
+}


	Jakub
Jason Merrill Aug. 6, 2018, 1:49 p.m. | #6
On Wed, Aug 1, 2018 at 7:07 PM, Jakub Jelinek <jakub@redhat.com> wrote:
> On Wed, Aug 01, 2018 at 12:35:09AM +1000, Jason Merrill wrote:

>> On Mon, Jul 23, 2018 at 8:50 PM, Richard Biener

>> <richard.guenther@gmail.com> wrote:

>> > On Mon, Jul 23, 2018 at 12:28 PM Jakub Jelinek <jakub@redhat.com> wrote:

>> >>

>> >> On Mon, Jul 23, 2018 at 12:17:42PM +0200, Richard Biener wrote:

>> >> > > Bootstrapped/regtested on x86_64-linux.

>> >> >

>> >> > Thanks for working on this.  I wonder if we can completely hide this

>> >> > from the middle-end, without requiring defining of c_dialect_cxx.

>> >> > There is the BUILT_IN_FRONTEND class so you could somewhere

>> >> > manually inject a decl in that class for C++?

>> >>

>> >> But then I couldn't handle folding of the builtin in the middle-end to false,

>> >> which is what I need (because in the FE it needs to be either folded to

>> >> true, or its folding deferred until later).

>> >> Or maybe in the C++ gimplification langhook?

>> >

>> > Yes, I was thinking the C++ langhook or its fully_fold routine.

>>

>> fully_fold is too soon (until constexpr evaluation uses the

>> pre-genericize form), but the gimplification hook should work.

>>

>> >> Seems we have a single BUILT_IN_FRONTEND builtin in the whole compiler,

>> >> __integer_pack, but it doesn't act as a normal builtin, given it is a

>> >> templatish magic.

>> >

>> > Yeah, I think at some point we considered removing BUILT_IN_FRONTEND ...

>> >

>> > Nowadays internal-use builtins can easily be internal-functions but of couse

>> > this one will eventually be used from libstdc++.

>>

>> Immediately, I'd think.

>

> Here is an updated patch that uses BUILT_IN_FRONTEND for this rather than

> BUILT_IN_NORMAL builtin.

>

> Bootstrapped/regtested on x86_64-linux and i686-linux.

>

> 2018-08-01  Jakub Jelinek  <jakub@redhat.com>

>

>         P0595R1 - is_constant_evaluated

> cp/

>         * cp-tree.h (enum cp_built_in_function): New.

>         (maybe_constant_init): Add const_evaluated argument.


We should document that this new parameter (here and in
cxx_eval_outermost_constant_expr) means "required to be
constant-evaluated" as per P0595 even though allow_non_constant is
true.  And the parameter names should make its magical nature more
obvious; perhaps "pretend_const_required".  OK with those changes.

Jason

Patch

--- gcc/builtins.def.jj	2018-06-20 08:15:34.179862153 +0200
+++ gcc/builtins.def	2018-07-20 12:03:10.254453811 +0200
@@ -974,6 +974,11 @@  DEF_EXT_LIB_BUILTIN    (BUILT_IN_PRINTF_
 DEF_EXT_LIB_BUILTIN    (BUILT_IN_VFPRINTF_CHK, "__vfprintf_chk", BT_FN_INT_FILEPTR_INT_CONST_STRING_VALIST_ARG, ATTR_NONNULL_1_FORMAT_PRINTF_3_0)
 DEF_EXT_LIB_BUILTIN    (BUILT_IN_VPRINTF_CHK, "__vprintf_chk", BT_FN_INT_INT_CONST_STRING_VALIST_ARG, ATTR_FORMAT_PRINTF_2_0)
 
+/* C++ __builtin_is_constant_evaluated.  */
+DEF_BUILTIN (BUILT_IN_IS_CONSTANT_EVALUATED, "__builtin_is_constant_evaluated",
+	     BUILT_IN_NORMAL, BT_FN_BOOL, BT_LAST, false, false, false,
+	     ATTR_CONST_NOTHROW_LEAF_LIST, true, c_dialect_cxx ())
+
 /* Profiling hooks.  */
 DEF_BUILTIN (BUILT_IN_PROFILE_FUNC_ENTER, "__cyg_profile_func_enter", BUILT_IN_NORMAL, BT_FN_VOID_PTR_PTR, BT_LAST,
 	     false, false, false, ATTR_NULL, true, true)
--- gcc/builtins.c.jj	2018-07-16 23:24:51.306429546 +0200
+++ gcc/builtins.c	2018-07-20 12:09:13.278818768 +0200
@@ -9104,6 +9104,10 @@  fold_builtin_0 (location_t loc, tree fnd
     case BUILT_IN_CLASSIFY_TYPE:
       return fold_builtin_classify_type (NULL_TREE);
 
+    case BUILT_IN_IS_CONSTANT_EVALUATED:
+      /* The C++ FE can evaluate this to something other than false.  */
+      return boolean_false_node;     
+
     default:
       break;
     }
--- gcc/cp/cp-tree.h.jj	2018-07-18 22:57:10.657780293 +0200
+++ gcc/cp/cp-tree.h	2018-07-20 18:10:33.416409798 +0200
@@ -7536,7 +7536,7 @@  extern bool require_potential_rvalue_con
 extern tree cxx_constant_value			(tree, tree = NULL_TREE);
 extern tree cxx_constant_init			(tree, tree = NULL_TREE);
 extern tree maybe_constant_value		(tree, tree = NULL_TREE);
-extern tree maybe_constant_init			(tree, tree = NULL_TREE);
+extern tree maybe_constant_init			(tree, tree = NULL_TREE, bool = false);
 extern tree fold_non_dependent_expr		(tree, tsubst_flags_t = tf_warning_or_error);
 extern tree fold_simple				(tree);
 extern bool is_sub_constant_expr                (tree);
--- gcc/cp/typeck2.c.jj	2018-06-29 09:38:17.786306395 +0200
+++ gcc/cp/typeck2.c	2018-07-20 18:11:36.649492893 +0200
@@ -837,7 +837,7 @@  store_init_value (tree decl, tree init,
 	    value = cxx_constant_init (value, decl);
 	}
       else
-	value = maybe_constant_init (value, decl);
+	value = maybe_constant_init (value, decl, true);
       if (TREE_CODE (value) == CONSTRUCTOR && cp_has_mutable_p (type))
 	/* Poison this CONSTRUCTOR so it can't be copied to another
 	   constexpr variable.  */
--- gcc/cp/constexpr.c.jj	2018-06-25 14:51:23.094989194 +0200
+++ gcc/cp/constexpr.c	2018-07-20 19:16:40.504036595 +0200
@@ -1007,6 +1007,8 @@  struct constexpr_ctx {
   /* Whether we are strictly conforming to constant expression rules or
      trying harder to get a constant value.  */
   bool strict;
+  /* Whether __builtin_is_constant_evaluated () should be true.  */
+  bool const_evaluated;
 };
 
 /* A table of all constexpr calls that have been evaluated by the
@@ -1184,6 +1186,18 @@  cxx_eval_builtin_function_call (const co
       return t;
     }
 
+  /* For __builtin_is_constant_evaluated, defer it if not ctx->const_evaluated,
+     otherwise fold it to true.  */
+  if (DECL_FUNCTION_CODE (fun) == BUILT_IN_IS_CONSTANT_EVALUATED)
+    {
+      if (!ctx->const_evaluated)
+	{
+	  *non_constant_p = true;
+	  return t;
+	}
+      return boolean_true_node;
+    }
+
   /* Be permissive for arguments to built-ins; __builtin_constant_p should
      return constant false for a non-constant argument.  */
   constexpr_ctx new_ctx = *ctx;
@@ -4884,7 +4898,9 @@  instantiate_constexpr_fns (tree t)
 
 static tree
 cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant,
-				  bool strict = true, tree object = NULL_TREE)
+				  bool strict = true,
+				  bool const_evaluated = false,
+				  tree object = NULL_TREE)
 {
   auto_timevar time (TV_CONSTEXPR);
 
@@ -4893,7 +4909,8 @@  cxx_eval_outermost_constant_expr (tree t
   hash_map<tree,tree> map;
 
   constexpr_ctx ctx = { NULL, &map, NULL, NULL, NULL, NULL,
-			allow_non_constant, strict };
+			allow_non_constant, strict,
+			const_evaluated || !allow_non_constant };
 
   tree type = initialized_type (t);
   tree r = t;
@@ -4982,6 +4999,12 @@  cxx_eval_outermost_constant_expr (tree t
     return error_mark_node;
   else if (non_constant_p && TREE_CONSTANT (r))
     {
+      /* If __builtin_is_constant_evaluated () was evaluated to true
+	 and the result is not a valid constant expression, we need to
+	 punt.  */
+      if (const_evaluated)
+	return cxx_eval_outermost_constant_expr (t, true, strict,
+						 false, object);
       /* This isn't actually constant, so unset TREE_CONSTANT.
 	 Don't clear TREE_CONSTANT on ADDR_EXPR, as the middle-end requires
 	 it to be set if it is invariant address, even when it is not
@@ -5027,7 +5049,8 @@  is_sub_constant_expr (tree t)
   bool overflow_p = false;
   hash_map <tree, tree> map;
 
-  constexpr_ctx ctx = { NULL, &map, NULL, NULL, NULL, NULL, true, true };
+  constexpr_ctx ctx
+    = { NULL, &map, NULL, NULL, NULL, NULL, true, true, false };
 
   instantiate_constexpr_fns (t);
   cxx_eval_constant_expression (&ctx, t, false, &non_constant_p,
@@ -5042,7 +5065,7 @@  is_sub_constant_expr (tree t)
 tree
 cxx_constant_value (tree t, tree decl)
 {
-  return cxx_eval_outermost_constant_expr (t, false, true, decl);
+  return cxx_eval_outermost_constant_expr (t, false, true, true, decl);
 }
 
 /* Helper routine for fold_simple function.  Either return simplified
@@ -5148,7 +5171,7 @@  maybe_constant_value (tree t, tree decl)
   if (tree *cached = cv_cache->get (t))
     return *cached;
 
-  r = cxx_eval_outermost_constant_expr (t, true, true, decl);
+  r = cxx_eval_outermost_constant_expr (t, true, true, false, decl);
   gcc_checking_assert (r == t
 		       || CONVERT_EXPR_P (t)
 		       || TREE_CODE (t) == VIEW_CONVERT_EXPR
@@ -5222,7 +5245,8 @@  fold_non_dependent_expr (tree t,
 	      return t;
 	    }
 
-	  tree r = cxx_eval_outermost_constant_expr (t, true, true, NULL_TREE);
+	  tree r = cxx_eval_outermost_constant_expr (t, true, true, false,
+						     NULL_TREE);
 	  /* cp_tree_equal looks through NOPs, so allow them.  */
 	  gcc_checking_assert (r == t
 			       || CONVERT_EXPR_P (t)
@@ -5246,7 +5270,8 @@  fold_non_dependent_expr (tree t,
    than wrapped in a TARGET_EXPR.  */
 
 static tree
-maybe_constant_init_1 (tree t, tree decl, bool allow_non_constant)
+maybe_constant_init_1 (tree t, tree decl, bool allow_non_constant,
+		       bool const_evaluated)
 {
   if (!t)
     return t;
@@ -5264,7 +5289,9 @@  maybe_constant_init_1 (tree t, tree decl
   else if (CONSTANT_CLASS_P (t) && allow_non_constant)
     /* No evaluation needed.  */;
   else
-    t = cxx_eval_outermost_constant_expr (t, allow_non_constant, false, decl);
+    t = cxx_eval_outermost_constant_expr (t, allow_non_constant,
+					  !allow_non_constant,
+					  const_evaluated, decl);
   if (TREE_CODE (t) == TARGET_EXPR)
     {
       tree init = TARGET_EXPR_INITIAL (t);
@@ -5277,9 +5304,9 @@  maybe_constant_init_1 (tree t, tree decl
 /* Wrapper for maybe_constant_init_1 which permits non constants.  */
 
 tree
-maybe_constant_init (tree t, tree decl)
+maybe_constant_init (tree t, tree decl, bool const_evaluated)
 {
-  return maybe_constant_init_1 (t, decl, true);
+  return maybe_constant_init_1 (t, decl, true, const_evaluated);
 }
 
 /* Wrapper for maybe_constant_init_1 which does not permit non constants.  */
@@ -5287,7 +5314,7 @@  maybe_constant_init (tree t, tree decl)
 tree
 cxx_constant_init (tree t, tree decl)
 {
-  return maybe_constant_init_1 (t, decl, false);
+  return maybe_constant_init_1 (t, decl, false, true);
 }
 
 #if 0
--- gcc/cp/cp-gimplify.c.jj	2018-07-20 11:39:15.543037497 +0200
+++ gcc/cp/cp-gimplify.c	2018-07-20 12:21:29.869568404 +0200
@@ -2478,6 +2478,12 @@  cp_fold (tree x)
 	    && DECL_DECLARED_CONSTEXPR_P (current_function_decl))
 	  nw = 1;
 
+	/* Defer folding __builtin_is_constant_evaluated.  */
+	if (callee
+	    && DECL_BUILT_IN_CLASS (callee) == BUILT_IN_NORMAL
+	    && DECL_FUNCTION_CODE (callee) == BUILT_IN_IS_CONSTANT_EVALUATED)
+	  break;
+
 	x = copy_node (x);
 
 	m = call_expr_nargs (x);
--- gcc/lto/lto-lang.c.jj	2018-06-13 10:05:49.991124932 +0200
+++ gcc/lto/lto-lang.c	2018-07-20 12:29:28.087207392 +0200
@@ -246,6 +246,7 @@  static GTY(()) tree signed_size_type_nod
 int flag_isoc94;
 int flag_isoc99;
 int flag_isoc11;
+#define c_dialect_cxx() 0
 
 /* Attribute handlers.  */
 
--- gcc/ada/gcc-interface/utils.c.jj	2018-07-17 12:48:21.096585583 +0200
+++ gcc/ada/gcc-interface/utils.c	2018-07-21 09:26:01.365363959 +0200
@@ -6458,6 +6458,7 @@  def_builtin_1 (enum built_in_function fn
 static int flag_isoc94 = 0;
 static int flag_isoc99 = 0;
 static int flag_isoc11 = 0;
+#define c_dialect_cxx() 0
 
 /* Install what the common builtins.def offers.  */
 
--- gcc/brig/brig-lang.c.jj	2018-05-06 23:12:54.400623542 +0200
+++ gcc/brig/brig-lang.c	2018-07-21 09:25:37.121341436 +0200
@@ -587,6 +587,7 @@  static GTY(()) tree signed_size_type_nod
 int flag_isoc94;
 int flag_isoc99;
 int flag_isoc11;
+#define c_dialect_cxx() 0
 
 static void
 def_fn_type (builtin_type def, builtin_type ret, bool var, int n, ...)
--- gcc/testsuite/g++.dg/cpp2a/is-constant-evaluated1.C.jj	2018-07-20 18:43:58.240798640 +0200
+++ gcc/testsuite/g++.dg/cpp2a/is-constant-evaluated1.C	2018-07-20 19:09:44.712391964 +0200
@@ -0,0 +1,64 @@ 
+// P0595R1
+// { dg-do compile { target c++14 } }
+
+template<int N> struct X { int v = N; };
+X<__builtin_is_constant_evaluated ()> x; // type X<true>
+int y = 4;
+int a = __builtin_is_constant_evaluated () ? y : 1; // initializes a to 1
+int b = __builtin_is_constant_evaluated () ? 2 : y; // initializes b to 2
+int c = y + (__builtin_is_constant_evaluated () ? 2 : y); // initializes c to 2*y
+  
+struct false_type { static constexpr bool value = false; };
+struct true_type { static constexpr bool value = true; };
+template<class T, class U>
+struct is_same : false_type {};
+template<class T>
+struct is_same<T, T> : true_type {};
+
+constexpr int
+foo (int x)
+{
+  const int n = __builtin_is_constant_evaluated () ? 13 : 17; // n == 13
+  int m = __builtin_is_constant_evaluated () ? 13 : 17; // m might be 13 or 17 (see below)
+  char arr[n] = {}; // char[13]
+  return m + sizeof (arr) + x;
+}
+
+constexpr int
+bar ()
+{
+  const int n = __builtin_is_constant_evaluated() ? 13 : 17;
+  X<n> x1;
+  X<__builtin_is_constant_evaluated() ? 13 : 17> x2;
+  static_assert (is_same<decltype (x1), decltype (x2)>::value, "x1/x2's type");
+  return x1.v + x2.v;
+}
+
+int p = foo (0); // m == 13; initialized to 26
+int q = p + foo (0); // m == 17 for this call; initialized to 56
+static_assert (bar () == 26, "bar");
+
+struct S { int a, b; };
+
+S s = { __builtin_is_constant_evaluated () ? 2 : 3, y };
+S t = { __builtin_is_constant_evaluated () ? 2 : 3, 4 };
+
+static_assert (is_same<decltype (x), X<true> >::value, "x's type");
+
+int
+main ()
+{
+  if (a != 1 || b != 2 || c != 8 || p != 26 || q != 56)
+    __builtin_abort ();
+  if (s.a != 3 || s.b != 4 || t.a != 2 || t.b != 4)
+    __builtin_abort ();
+  if (foo (y) != 34)
+    __builtin_abort ();
+#if __cplusplus >= 201703L
+  if constexpr (foo (0) != 26)
+    __builtin_abort ();
+#endif
+  constexpr int w = foo (0);
+  if (w != 26)
+    __builtin_abort ();
+}