Hi! This is the implementation of __builtin_early_constant_p builtin which is at all optimization levels quite similar to __builtin_constant_p at -O0, except that the FE folding might be slightly different between -O0 and -O1+. In any case, the builtin is folded to 0 already during the FEs if the argument can't be proven constant or non-constant, like with __builtin_constant_p at -O0. The users can use it for stuff where __builtin_constant_p doesn't really work (e.g. the kernel ilog2 case), for e.g. C macro constexpr-like programming.
Is this ok for stage1, or do you find it not useful at all? In any case, bootstrapped/regtested on x86_64-linux and i686-linux. 2018-03-07 Jakub Jelinek <ja...@redhat.com> PR c++/78420 * builtins.def (BUILT_IN_EARLY_CONSTANT_P): New built-in. * builtins.h (force_folding_builtin_constant_p): Change from bool to int. * builtins.c (force_folding_builtin_constant_p): Likewise. (expand_builtin): Handle BUILT_IN_EARLY_CONSTANT_P like BUILT_IN_CONSTANT_P. (fold_builtin_constant_p): Check force_folding_builtin_constant_p > 0 instead of force_folding_builtin_constant_p. (fold_builtin_1) <case BUILT_IN_CONSTANT_P>: Don't return integer_zero_node if force_folding_builtin_constant_p < 0. <case BUILT_IN_EARLY_CONSTANT_P>: New case. (is_simple_builtin): Handle BUILT_IN_EARLY_CONSTANT_P like BUILT_IN_CONSTANT_P. * doc/extend.texi (__builtin_early_constant_p): Document. c-family/ * c-common.c (check_builtin_function_arguments): Handle BUILT_IN_EARLY_CONSTANT_P like BUILT_IN_CONSTANT_P. c/ * c-parser.c (c_parser_get_builtin_args): Adjust for force_folding_builtin_constant_p now being int rather than bool. cp/ * cp-tree.h (DECL_IS_BUILTIN_CONSTANT_P): Handle BUILT_IN_EARLY_CONSTANT_P like BUILT_IN_CONSTANT_P. * call.c (magic_varargs_p): Likewise. * tree.c (builtin_valid_in_constant_expr_p): Likewise. * constexpr.c (cxx_eval_builtin_function_call): Likewise. Adjust for force_folding_builtin_constant_p now being int rather than bool. * cp-gimplify.c: Include builtins.h. (cp_fold): For __builtin*_constant_p adjust temporarily force_folding_builtin_constant_p rather than optimize. testsuite/ * g++.dg/delayedfold/builtin-constant4.C: New test. * g++.dg/delayedfold/builtin-constant3.C: New test. * g++.dg/ext/builtin14.C: New test. * g++.dg/ext/builtin12.C: New test. * g++.dg/ext/builtin13.C: New test. * g++.dg/cpp1y/constexpr-78420.C: New test. * g++.dg/cpp0x/constexpr-builtin5.C: New test. * g++.dg/cpp0x/constexpr-builtin4.C: New test. * gcc.dg/torture/pr78420.c: New test. --- gcc/builtins.def.jj 2018-01-03 10:19:55.103533949 +0100 +++ gcc/builtins.def 2018-03-07 15:55:20.756617250 +0100 @@ -857,6 +857,7 @@ DEF_EXT_LIB_BUILTIN (BUILT_IN_DCGETTE DEF_EXT_LIB_BUILTIN (BUILT_IN_DGETTEXT, "dgettext", BT_FN_STRING_CONST_STRING_CONST_STRING, ATTR_FORMAT_ARG_2) DEF_GCC_BUILTIN (BUILT_IN_DWARF_CFA, "dwarf_cfa", BT_FN_PTR, ATTR_NULL) DEF_GCC_BUILTIN (BUILT_IN_DWARF_SP_COLUMN, "dwarf_sp_column", BT_FN_UINT, ATTR_NULL) +DEF_GCC_BUILTIN (BUILT_IN_EARLY_CONSTANT_P, "early_constant_p", BT_FN_INT_VAR, ATTR_CONST_NOTHROW_LEAF_LIST) DEF_GCC_BUILTIN (BUILT_IN_EH_RETURN, "eh_return", BT_FN_VOID_PTRMODE_PTR, ATTR_NORETURN_NOTHROW_LEAF_LIST) DEF_GCC_BUILTIN (BUILT_IN_EH_RETURN_DATA_REGNO, "eh_return_data_regno", BT_FN_INT_INT, ATTR_LEAF_LIST) DEF_EXT_LIB_BUILTIN (BUILT_IN_EXECL, "execl", BT_FN_INT_CONST_STRING_CONST_STRING_VAR, ATTR_SENTINEL_NOTHROW_LIST) --- gcc/builtins.h.jj 2018-01-04 00:43:16.103702766 +0100 +++ gcc/builtins.h 2018-03-07 16:31:54.898348803 +0100 @@ -46,8 +46,10 @@ extern struct target_builtins *this_targ #define this_target_builtins (&default_target_builtins) #endif -/* Non-zero if __builtin_constant_p should be folded right away. */ -extern bool force_folding_builtin_constant_p; +/* 1 if __builtin_{,early_}constant_p should be folded right away, + -1 if __builtin_{,early_}constant_p should not be folded to 0 when + fold_builtin_constant_p returned NULL. */ +extern int force_folding_builtin_constant_p; extern bool is_builtin_fn (tree); extern bool called_as_built_in (tree); --- gcc/builtins.c.jj 2018-02-19 20:35:54.418015330 +0100 +++ gcc/builtins.c 2018-03-07 16:32:12.326353626 +0100 @@ -91,8 +91,10 @@ const char * built_in_names[(int) END_BU initialized to NULL_TREE. */ builtin_info_type builtin_info[(int)END_BUILTINS]; -/* Non-zero if __builtin_constant_p should be folded right away. */ -bool force_folding_builtin_constant_p; +/* 1 if __builtin_{,early_}constant_p should be folded right away, + -1 if __builtin_{,early_}constant_p should not be folded to 0 when + fold_builtin_constant_p returned NULL. */ +int force_folding_builtin_constant_p; static rtx c_readstr (const char *, scalar_int_mode); static int target_char_cast (tree, char *); @@ -6872,6 +6874,7 @@ expand_builtin (tree exp, rtx target, rt return expand_builtin_classify_type (exp); case BUILT_IN_CONSTANT_P: + case BUILT_IN_EARLY_CONSTANT_P: return const0_rtx; case BUILT_IN_FRAME_ADDRESS: @@ -7932,7 +7935,7 @@ fold_builtin_constant_p (tree arg) || POINTER_TYPE_P (TREE_TYPE (arg)) || cfun == 0 || folding_initializer - || force_folding_builtin_constant_p) + || force_folding_builtin_constant_p > 0) return integer_zero_node; return NULL_TREE; @@ -8966,12 +8969,21 @@ fold_builtin_1 (location_t loc, tree fnd /* Gimplification will pull the CALL_EXPR for the builtin out of an if condition. When not optimizing, we'll not CSE it back. To avoid link error types of regressions, return false now. */ - if (!val && !optimize) + if (!val && !optimize && force_folding_builtin_constant_p >= 0) val = integer_zero_node; return val; } + case BUILT_IN_EARLY_CONSTANT_P: + /* Like __builtin_constant_p, but always behave like that builtin + behaves at -O0. */ + if (tree val = fold_builtin_constant_p (arg0)) + return val; + if (force_folding_builtin_constant_p >= 0) + return integer_zero_node; + return NULL_TREE; + case BUILT_IN_CLASSIFY_TYPE: return fold_builtin_classify_type (arg0); @@ -10576,6 +10588,7 @@ is_simple_builtin (tree decl) { /* Builtins that expand to constants. */ case BUILT_IN_CONSTANT_P: + case BUILT_IN_EARLY_CONSTANT_P: case BUILT_IN_EXPECT: case BUILT_IN_OBJECT_SIZE: case BUILT_IN_UNREACHABLE: --- gcc/doc/extend.texi.jj 2018-02-22 22:25:53.312995803 +0100 +++ gcc/doc/extend.texi 2018-03-07 18:16:36.870891566 +0100 @@ -11924,6 +11924,17 @@ built-in in this case, because it has no optimization. @end deftypefn +@deftypefn {Built-in Function} int __builtin_early_constant_p (@var{exp}) +This built-in function is like @code{__builtin_constant_p} when used in +static initializers or C++ @code{constexpr} contexts, in other contexts +it acts similarly to @code{__builtin_constant_p} at @option{-O0}, in that +if the argument after folding is proven a constant, it returns 1, otherwise +it returns 0. If the argument needs function inlining and/or constant +propagation, or other inter-statement optimizations in order to be proven +constant, @code{__builtin_early_constant_p} will return 0 while +@code{__builtin_constant_p} might still return 1. +@end deftypefn + @deftypefn {Built-in Function} long __builtin_expect (long @var{exp}, long @var{c}) @opindex fprofile-arcs You may use @code{__builtin_expect} to provide the compiler with --- gcc/c/c-parser.c.jj 2018-03-06 21:56:59.043571905 +0100 +++ gcc/c/c-parser.c 2018-03-07 16:33:47.927380098 +0100 @@ -7449,7 +7449,7 @@ c_parser_get_builtin_args (c_parser *par location_t loc = c_parser_peek_token (parser)->location; vec<c_expr_t, va_gc> *cexpr_list; c_expr_t expr; - bool saved_force_folding_builtin_constant_p; + int saved_force_folding_builtin_constant_p; *ret_cexpr_list = NULL; if (c_parser_next_token_is_not (parser, CPP_OPEN_PAREN)) @@ -7469,7 +7469,8 @@ c_parser_get_builtin_args (c_parser *par saved_force_folding_builtin_constant_p = force_folding_builtin_constant_p; - force_folding_builtin_constant_p |= choose_expr_p; + if (choose_expr_p) + force_folding_builtin_constant_p = 1; expr = c_parser_expr_no_commas (parser, NULL); force_folding_builtin_constant_p = saved_force_folding_builtin_constant_p; @@ -9160,7 +9161,9 @@ c_parser_postfix_expression_after_primar if (TREE_CODE (expr.value) == INTEGER_CST && TREE_CODE (orig_expr.value) == FUNCTION_DECL && DECL_BUILT_IN_CLASS (orig_expr.value) == BUILT_IN_NORMAL - && DECL_FUNCTION_CODE (orig_expr.value) == BUILT_IN_CONSTANT_P) + && (DECL_FUNCTION_CODE (orig_expr.value) == BUILT_IN_CONSTANT_P + || (DECL_FUNCTION_CODE (orig_expr.value) + == BUILT_IN_EARLY_CONSTANT_P))) expr.original_code = C_MAYBE_CONST_EXPR; expr.original_type = NULL; if (exprlist) --- gcc/c-family/c-common.c.jj 2018-01-17 22:00:12.041228270 +0100 +++ gcc/c-family/c-common.c 2018-03-07 15:58:18.350661272 +0100 @@ -5790,6 +5790,7 @@ check_builtin_function_arguments (locati } case BUILT_IN_CONSTANT_P: + case BUILT_IN_EARLY_CONSTANT_P: return builtin_function_validate_nargs (loc, fndecl, nargs, 1); case BUILT_IN_ISFINITE: --- gcc/cp/cp-tree.h.jj 2018-03-02 00:15:54.099781049 +0100 +++ gcc/cp/cp-tree.h 2018-03-07 16:00:32.795694600 +0100 @@ -2846,11 +2846,13 @@ struct GTY(()) lang_decl { #define DECL_HAS_IN_CHARGE_PARM_P(NODE) \ (LANG_DECL_FN_CHECK (NODE)->has_in_charge_parm_p) -/* Nonzero if DECL is a declaration of __builtin_constant_p. */ +/* Nonzero if DECL is a declaration of __builtin_{,early_}constant_p. */ #define DECL_IS_BUILTIN_CONSTANT_P(NODE) \ (TREE_CODE (NODE) == FUNCTION_DECL \ && DECL_BUILT_IN_CLASS (NODE) == BUILT_IN_NORMAL \ - && DECL_FUNCTION_CODE (NODE) == BUILT_IN_CONSTANT_P) + && (DECL_FUNCTION_CODE (NODE) == BUILT_IN_CONSTANT_P \ + || (DECL_FUNCTION_CODE (NODE) \ + == BUILT_IN_EARLY_CONSTANT_P))) /* Nonzero for _DECL means that this decl appears in (or will appear in) as a member in a RECORD_TYPE or UNION_TYPE node. It is also for --- gcc/cp/constexpr.c.jj 2018-03-07 10:11:42.758757442 +0100 +++ gcc/cp/constexpr.c 2018-03-07 16:35:25.906407232 +0100 @@ -1168,8 +1168,10 @@ cxx_eval_builtin_function_call (const co tree new_call; int i; - /* Don't fold __builtin_constant_p within a constexpr function. */ - bool bi_const_p = (DECL_FUNCTION_CODE (fun) == BUILT_IN_CONSTANT_P); + /* Don't fold __builtin_{,early_}constant_p within a constexpr function. */ + bool bi_const_p + = (DECL_FUNCTION_CODE (fun) == BUILT_IN_CONSTANT_P + || DECL_FUNCTION_CODE (fun) == BUILT_IN_EARLY_CONSTANT_P); if (bi_const_p && current_function_decl @@ -1179,8 +1181,8 @@ cxx_eval_builtin_function_call (const co return t; } - /* Be permissive for arguments to built-ins; __builtin_constant_p should - return constant false for a non-constant argument. */ + /* Be permissive for arguments to built-ins; __builtin_{,early_}constant_p + should return constant false for a non-constant argument. */ constexpr_ctx new_ctx = *ctx; new_ctx.quiet = true; bool dummy1 = false, dummy2 = false; @@ -1189,13 +1191,13 @@ cxx_eval_builtin_function_call (const co args[i] = cxx_eval_constant_expression (&new_ctx, CALL_EXPR_ARG (t, i), false, &dummy1, &dummy2); if (bi_const_p) - /* For __built_in_constant_p, fold all expressions with constant values - even if they aren't C++ constant-expressions. */ + /* For __built_in_{,early_}constant_p, fold all expressions with + constant values even if they aren't C++ constant-expressions. */ args[i] = cp_fully_fold (args[i]); } - bool save_ffbcp = force_folding_builtin_constant_p; - force_folding_builtin_constant_p = true; + int save_ffbcp = force_folding_builtin_constant_p; + force_folding_builtin_constant_p = 1; new_call = fold_builtin_call_array (EXPR_LOCATION (t), TREE_TYPE (t), CALL_EXPR_FN (t), nargs, args); force_folding_builtin_constant_p = save_ffbcp; --- gcc/cp/call.c.jj 2018-03-06 08:01:37.880883501 +0100 +++ gcc/cp/call.c 2018-03-07 15:58:38.457666266 +0100 @@ -7455,6 +7455,7 @@ magic_varargs_p (tree fn) { case BUILT_IN_CLASSIFY_TYPE: case BUILT_IN_CONSTANT_P: + case BUILT_IN_EARLY_CONSTANT_P: case BUILT_IN_NEXT_ARG: case BUILT_IN_VA_START: return 1; --- gcc/cp/cp-gimplify.c.jj 2018-03-02 12:24:10.918325074 +0100 +++ gcc/cp/cp-gimplify.c 2018-03-07 17:20:36.524970697 +0100 @@ -34,6 +34,7 @@ along with GCC; see the file COPYING3. #include "stringpool.h" #include "attribs.h" #include "asan.h" +#include "builtins.h" /* Forward declarations. */ @@ -2374,17 +2375,18 @@ cp_fold (tree x) case CALL_EXPR: { - int i, m, sv = optimize, nw = sv, changed = 0; + int i, m, sv = force_folding_builtin_constant_p, nw = sv, changed = 0; tree callee = get_callee_fndecl (x); /* Some built-in function calls will be evaluated at compile-time in - fold (). Set optimize to 1 when folding __builtin_constant_p inside - a constexpr function so that fold_builtin_1 doesn't fold it to 0. */ - if (callee && DECL_BUILT_IN (callee) && !optimize + fold (). Set force_folding_builtin_constant_p to -1 when folding + __builtin_{,early_}constant_p inside a constexpr function so that + fold_builtin_1 doesn't fold it to 0. */ + if (callee && DECL_BUILT_IN (callee) && DECL_IS_BUILTIN_CONSTANT_P (callee) && current_function_decl && DECL_DECLARED_CONSTEXPR_P (current_function_decl)) - nw = 1; + nw = -1; x = copy_node (x); @@ -2406,9 +2408,9 @@ cp_fold (tree x) if (x == error_mark_node) break; - optimize = nw; + force_folding_builtin_constant_p = nw; r = fold (x); - optimize = sv; + force_folding_builtin_constant_p = sv; if (TREE_CODE (r) != CALL_EXPR) { @@ -2416,7 +2418,7 @@ cp_fold (tree x) break; } - optimize = nw; + force_folding_builtin_constant_p = nw; /* Invoke maybe_constant_value for functions declared constexpr and not called with AGGR_INIT_EXPRs. @@ -2426,7 +2428,7 @@ cp_fold (tree x) if (callee && DECL_DECLARED_CONSTEXPR_P (callee) && !flag_no_inline) r = maybe_constant_value (x); - optimize = sv; + force_folding_builtin_constant_p = sv; if (TREE_CODE (r) != CALL_EXPR) { --- gcc/cp/tree.c.jj 2018-03-07 10:11:41.477753951 +0100 +++ gcc/cp/tree.c 2018-03-07 16:01:09.987703817 +0100 @@ -417,6 +417,7 @@ builtin_valid_in_constant_expr_p (const_ /* These have constant results even if their operands are non-constant. */ case BUILT_IN_CONSTANT_P: + case BUILT_IN_EARLY_CONSTANT_P: case BUILT_IN_ATOMIC_ALWAYS_LOCK_FREE: return true; default: --- gcc/testsuite/g++.dg/delayedfold/builtin-constant3.C.jj 2018-03-07 16:55:48.747733809 +0100 +++ gcc/testsuite/g++.dg/delayedfold/builtin-constant3.C 2018-03-07 16:56:42.519747846 +0100 @@ -0,0 +1,8 @@ +// { dg-do compile { target c++11 } } +// { dg-options "-O2" } + +#define SA(X) static_assert ((X),#X) + +int i; + +SA(__builtin_early_constant_p (i == 42 && false)); --- gcc/testsuite/g++.dg/delayedfold/builtin-constant4.C.jj 2018-03-07 16:55:54.807735391 +0100 +++ gcc/testsuite/g++.dg/delayedfold/builtin-constant4.C 2018-03-07 16:56:51.663750235 +0100 @@ -0,0 +1,14 @@ +// { dg-do run } +// { dg-options "-O2" } + +int i; + +#define CV(X) (__builtin_early_constant_p (X) ? (X) : -1) + +int ar[] = { CV (i == 42 && false) }; + +int main() +{ + if (ar[0] != 0) + __builtin_abort(); +} --- gcc/testsuite/g++.dg/ext/builtin12.C.jj 2018-03-07 16:53:48.015702300 +0100 +++ gcc/testsuite/g++.dg/ext/builtin12.C 2018-03-07 16:54:11.079708323 +0100 @@ -0,0 +1,14 @@ +// PR c++/19628 +// Verify that __builtin_early_constant_p may appear in a constant-expression. + +// { dg-do run } + +int main() +{ + switch (3) { + case (__builtin_early_constant_p(7) ? 3 : 8): + return 0; + default: + return 1; + } +} --- gcc/testsuite/g++.dg/ext/builtin13.C.jj 2018-03-07 16:53:50.842703039 +0100 +++ gcc/testsuite/g++.dg/ext/builtin13.C 2018-03-07 16:54:20.441710763 +0100 @@ -0,0 +1,16 @@ +// PR c++/19628 +// Verify that __builtin_early_constant_p may appear in a constant-expression. + +// { dg-do compile } + +template <int I> +int f(int x[__builtin_early_constant_p(I)]) +{ + return x[0]; +} + +int g() +{ + int a[1] = { 7 }; + return f<32>(a); +} --- gcc/testsuite/g++.dg/ext/builtin14.C.jj 2018-03-07 16:53:54.348703950 +0100 +++ gcc/testsuite/g++.dg/ext/builtin14.C 2018-03-07 16:54:27.073712490 +0100 @@ -0,0 +1,3 @@ +// PR c++/21619 +// { dg-options "" } +int f[__builtin_early_constant_p(&"Hello"[0])?1:-1]; --- gcc/testsuite/g++.dg/cpp1y/constexpr-78420.C.jj 2018-03-07 17:03:06.464825167 +0100 +++ gcc/testsuite/g++.dg/cpp1y/constexpr-78420.C 2018-03-07 17:09:10.426865686 +0100 @@ -0,0 +1,36 @@ +// PR c++/78420 +// { dg-do run { target c++14 } } +// { dg-options "-O2" } + +constexpr int +foo (const int *a, const int *b) +{ + if (__builtin_early_constant_p (a < b)) + return a < b; + return -1; +} + +int ar[3], i; +static_assert (foo (&ar[1], &ar[2]) == 1); +static_assert (foo (&ar[2], &ar[1]) == 0); +static_assert (foo (&ar[0], &i) == -1); + +int +main () +{ + int *p = &ar[1]; + int *q = &ar[2]; + if (foo (p, q) != -1) + __builtin_abort (); + constexpr int r = foo (&ar[1], &ar[2]); + if (r != 1) + __builtin_abort (); + if (!__builtin_early_constant_p (1)) + __builtin_abort (); + const int two = 2; + if (!__builtin_early_constant_p (two)) + __builtin_abort (); + int three = 3; + if (__builtin_early_constant_p (three)) + __builtin_abort (); +} --- gcc/testsuite/g++.dg/cpp0x/constexpr-builtin4.C.jj 2018-03-07 16:50:24.222649109 +0100 +++ gcc/testsuite/g++.dg/cpp0x/constexpr-builtin4.C 2018-03-07 16:50:49.049655592 +0100 @@ -0,0 +1,5 @@ +// PR c++/54021 +// { dg-do compile { target c++11 } } + +#define __builtin_constant_p __builtin_early_constant_p +#include "constexpr-builtin2.C" --- gcc/testsuite/g++.dg/cpp0x/constexpr-builtin5.C.jj 2018-03-07 16:50:56.574657555 +0100 +++ gcc/testsuite/g++.dg/cpp0x/constexpr-builtin5.C 2018-03-07 16:51:20.688663849 +0100 @@ -0,0 +1,6 @@ +// PR c++/65656 +// { dg-options "-std=c++11 -O" } + +int main(int argc, char *argv[]) { + constexpr bool x = __builtin_early_constant_p(argc); +} --- gcc/testsuite/gcc.dg/torture/pr78420.c.jj 2018-03-07 17:15:27.305907637 +0100 +++ gcc/testsuite/gcc.dg/torture/pr78420.c 2018-03-07 17:51:03.862453805 +0100 @@ -0,0 +1,29 @@ +/* PR c++/78420 */ +/* { dg-do run } */ + +int a = __builtin_early_constant_p (1); +const int b = 2; +int c = __builtin_early_constant_p (b); +int d = __builtin_early_constant_p (b + 1); +int e; +int f = __builtin_early_constant_p (e); + +int +main () +{ + const int g = 3; + if (a != 1 || b != 2 || c || d || e || f) + __builtin_abort (); + int h = 3; + if (!__builtin_early_constant_p (7) +#ifdef __OPTIMIZE__ + || !__builtin_early_constant_p (g) + || !__builtin_early_constant_p (g + 1) +#else + || __builtin_early_constant_p (g) + || __builtin_early_constant_p (g + 1) +#endif + || __builtin_early_constant_p (h)) + __builtin_abort (); + return 0; +} Jakub