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

Richard Biener richard.guenther@gmail.com
Mon Jul 23 10:18:00 GMT 2018


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



More information about the Gcc-patches mailing list