On Sun, Jul 22, 2018 at 9:31 PM Jakub Jelinek <ja...@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 <ja...@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