Hi! While in C++11, builtins returning two results, one of them by dereferencing a pointer argument can't be constexpr, in my understanding in C++14 generalized constexprs they can.
So, this patch tweaks cxx_eval_builtin_function_call so that it handles how builtins.c folds these builtins (i.e. COMPOUND_EXPR with first operand being *arg = const1 and second operand const2, optionally all wrapped into a NON_LVALUE_EXPR. In addition, I've noticed that the lval argument is passed down to evaluation of arguments, that doesn't make sense to me, IMHO arguments should be always evakyated as rvalues (and for non-builtins they are). sincos (which has stores 2 results through pointers) is folded earlier into cexpi and thus worked already before. Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk? 2016-07-15 Jakub Jelinek <ja...@redhat.com> PR c++/50060 * constexpr.c (cxx_eval_builtin_function_call): Pass false as lval when evaluating call arguments, make the lval argument nameless. For C++14 and later, if new_call is COMPOUND_EXPR with a assignment and constant, evaluate the assignment and return the constant. * g++.dg/cpp1y/constexpr-50060.C: New test. --- gcc/cp/constexpr.c.jj 2016-07-11 22:18:01.000000000 +0200 +++ gcc/cp/constexpr.c 2016-07-15 15:27:50.820085561 +0200 @@ -1078,8 +1078,7 @@ get_nth_callarg (tree t, int n) static tree cxx_eval_builtin_function_call (const constexpr_ctx *ctx, tree t, tree fun, - bool lval, - bool *non_constant_p, bool *overflow_p) + bool, bool *non_constant_p, bool *overflow_p) { const int nargs = call_expr_nargs (t); tree *args = (tree *) alloca (nargs * sizeof (tree)); @@ -1105,7 +1104,7 @@ cxx_eval_builtin_function_call (const co for (i = 0; i < nargs; ++i) { args[i] = cxx_eval_constant_expression (&new_ctx, CALL_EXPR_ARG (t, i), - lval, &dummy1, &dummy2); + 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. */ @@ -1119,6 +1118,27 @@ cxx_eval_builtin_function_call (const co /* Fold away the NOP_EXPR from fold_builtin_n. */ new_call = fold (new_call); force_folding_builtin_constant_p = save_ffbcp; + + if (cxx_dialect >= cxx14) + { + tree r = new_call; + if (TREE_CODE (r) == NON_LVALUE_EXPR) + r = TREE_OPERAND (r, 0); + if (TREE_CODE (r) == COMPOUND_EXPR + && TREE_CODE (TREE_OPERAND (r, 0)) == MODIFY_EXPR + && reduced_constant_expression_p (TREE_OPERAND (TREE_OPERAND (r, 0), + 1))) + { + /* The frexp, modf, remquo and lgamma_r builtins (and their variants) + with &var as last argument are folded into + (var = const1), const2, sometimes wrapped into + NON_LVALUE_EXPR. */ + cxx_eval_constant_expression (&new_ctx, TREE_OPERAND (r, 0), + false, non_constant_p, overflow_p); + new_call = TREE_OPERAND (r, 1); + } + } + VERIFY_CONSTANT (new_call); return new_call; } --- gcc/testsuite/g++.dg/cpp1y/constexpr-50060.C.jj 2016-07-15 15:34:12.469124944 +0200 +++ gcc/testsuite/g++.dg/cpp1y/constexpr-50060.C 2016-07-15 15:59:36.285303078 +0200 @@ -0,0 +1,100 @@ +// PR c++/50060 +// { dg-do compile { target c++14 } } + +// sincos and lgamma_r aren't available in -std=c++14, +// only in -std=gnu++14. Use __builtin_* in that case. +extern "C" void sincos (double, double *, double *); +extern "C" double frexp (double, int *); +extern "C" double modf (double, double *); +extern "C" double remquo (double, double, int *); +extern "C" double lgamma_r (double, int *); + +constexpr double +f0 (double x) +{ + double y {}; + double z {}; + __builtin_sincos (x, &y, &z); + return y; +} + +constexpr double +f1 (double x) +{ + double y {}; + double z {}; + __builtin_sincos (x, &y, &z); + return z; +} + +constexpr double +f2 (double x) +{ + int y {}; + return frexp (x, &y); +} + +constexpr int +f3 (double x) +{ + int y {}; + frexp (x, &y); + return y; +} + +constexpr double +f4 (double x) +{ + double y {}; + return modf (x, &y); +} + +constexpr double +f5 (double x) +{ + double y {}; + modf (x, &y); + return y; +} + +constexpr double +f6 (double x, double y) +{ + int z {}; + return remquo (x, y, &z); +} + +constexpr int +f7 (double x, double y) +{ + int z {}; + remquo (x, y, &z); + return z; +} + +constexpr double +f8 (double x) +{ + int y {}; + return __builtin_lgamma_r (x, &y); +} + +constexpr int +f9 (double x) +{ + int y {}; + __builtin_lgamma_r (x, &y); + return y; +} + +static_assert (f0 (0.0) == 0.0, ""); +static_assert (f1 (0.0) == 1.0, ""); +static_assert (f2 (6.5) == 0.8125, ""); +static_assert (f3 (6.5) == 3, ""); +static_assert (f4 (-7.25) == -0.25, ""); +static_assert (f5 (-7.25) == -7.0, ""); +static_assert (f6 (3.0, 2.0) == -1.0, ""); +static_assert (f7 (3.0, 2.0) == 2, ""); +static_assert (f8 (0.75) >= 0.20 && f8 (0.75) <= 0.21, ""); +static_assert (f8 (0.75) >= 0.20 && f8 (0.75) <= 0.21, ""); +static_assert (f9 (0.75) == 1, ""); Jakub